@qwickapps/server 1.7.0 → 1.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +66 -0
- package/README.md +13 -116
- package/dist/src/core/control-panel.d.ts.map +1 -1
- package/dist/src/core/control-panel.js +6 -4
- package/dist/src/core/control-panel.js.map +1 -1
- package/dist/src/core/gateway.d.ts.map +1 -1
- package/dist/src/core/gateway.js +24 -2
- package/dist/src/core/gateway.js.map +1 -1
- package/dist/src/core/plugin-registry.d.ts +15 -2
- package/dist/src/core/plugin-registry.d.ts.map +1 -1
- package/dist/src/core/plugin-registry.js.map +1 -1
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/plugins/api-keys/api-keys-plugin.d.ts +5 -2
- package/dist/src/plugins/api-keys/api-keys-plugin.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/api-keys-plugin.js +61 -19
- package/dist/src/plugins/api-keys/api-keys-plugin.js.map +1 -1
- package/dist/src/plugins/api-keys/index.d.ts +0 -4
- package/dist/src/plugins/api-keys/index.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/index.js +2 -3
- package/dist/src/plugins/api-keys/index.js.map +1 -1
- package/dist/src/plugins/api-keys/stores/postgres-store.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/stores/postgres-store.js +29 -0
- package/dist/src/plugins/api-keys/stores/postgres-store.js.map +1 -1
- package/dist/src/plugins/api-keys/types.d.ts +9 -3
- package/dist/src/plugins/api-keys/types.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/types.js.map +1 -1
- package/dist/src/plugins/auth/auth-plugin.d.ts.map +1 -1
- package/dist/src/plugins/auth/auth-plugin.js +4 -2
- package/dist/src/plugins/auth/auth-plugin.js.map +1 -1
- package/dist/src/plugins/auth/env-config.d.ts.map +1 -1
- package/dist/src/plugins/auth/env-config.js +1 -0
- package/dist/src/plugins/auth/env-config.js.map +1 -1
- package/dist/src/plugins/auth/index.d.ts +0 -4
- package/dist/src/plugins/auth/index.d.ts.map +1 -1
- package/dist/src/plugins/auth/index.js +2 -3
- package/dist/src/plugins/auth/index.js.map +1 -1
- package/dist/src/plugins/bans/bans-plugin.d.ts +5 -2
- package/dist/src/plugins/bans/bans-plugin.d.ts.map +1 -1
- package/dist/src/plugins/bans/bans-plugin.js +71 -25
- package/dist/src/plugins/bans/bans-plugin.js.map +1 -1
- package/dist/src/plugins/bans/index.d.ts +1 -5
- package/dist/src/plugins/bans/index.d.ts.map +1 -1
- package/dist/src/plugins/bans/index.js +3 -4
- package/dist/src/plugins/bans/index.js.map +1 -1
- package/dist/src/plugins/bans/stores/in-memory-store.d.ts +34 -0
- package/dist/src/plugins/bans/stores/in-memory-store.d.ts.map +1 -0
- package/dist/src/plugins/bans/stores/in-memory-store.js +97 -0
- package/dist/src/plugins/bans/stores/in-memory-store.js.map +1 -0
- package/dist/src/plugins/bans/stores/index.d.ts +1 -0
- package/dist/src/plugins/bans/stores/index.d.ts.map +1 -1
- package/dist/src/plugins/bans/stores/index.js +1 -0
- package/dist/src/plugins/bans/stores/index.js.map +1 -1
- package/dist/src/plugins/bans/types.d.ts +13 -6
- package/dist/src/plugins/bans/types.d.ts.map +1 -1
- package/dist/src/plugins/cache-plugin.d.ts +35 -16
- package/dist/src/plugins/cache-plugin.d.ts.map +1 -1
- package/dist/src/plugins/cache-plugin.js +299 -20
- package/dist/src/plugins/cache-plugin.js.map +1 -1
- package/dist/src/plugins/cms/cms-plugin.d.ts.map +1 -1
- package/dist/src/plugins/cms/cms-plugin.js +3 -1
- package/dist/src/plugins/cms/cms-plugin.js.map +1 -1
- package/dist/src/plugins/devices/devices-plugin.d.ts +5 -2
- package/dist/src/plugins/devices/devices-plugin.d.ts.map +1 -1
- package/dist/src/plugins/devices/devices-plugin.js +62 -26
- package/dist/src/plugins/devices/devices-plugin.js.map +1 -1
- package/dist/src/plugins/devices/index.d.ts +0 -4
- package/dist/src/plugins/devices/index.d.ts.map +1 -1
- package/dist/src/plugins/devices/index.js +2 -3
- package/dist/src/plugins/devices/index.js.map +1 -1
- package/dist/src/plugins/entitlements/entitlements-plugin.d.ts +5 -2
- package/dist/src/plugins/entitlements/entitlements-plugin.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/entitlements-plugin.js +78 -41
- package/dist/src/plugins/entitlements/entitlements-plugin.js.map +1 -1
- package/dist/src/plugins/entitlements/index.d.ts +1 -5
- package/dist/src/plugins/entitlements/index.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/index.js +3 -4
- package/dist/src/plugins/entitlements/index.js.map +1 -1
- package/dist/src/plugins/entitlements/sources/in-memory-source.d.ts +9 -0
- package/dist/src/plugins/entitlements/sources/in-memory-source.d.ts.map +1 -0
- package/dist/src/plugins/entitlements/sources/in-memory-source.js +65 -0
- package/dist/src/plugins/entitlements/sources/in-memory-source.js.map +1 -0
- package/dist/src/plugins/entitlements/sources/index.d.ts +1 -0
- package/dist/src/plugins/entitlements/sources/index.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/sources/index.js +1 -0
- package/dist/src/plugins/entitlements/sources/index.js.map +1 -1
- package/dist/src/plugins/entitlements/types.d.ts +9 -2
- package/dist/src/plugins/entitlements/types.d.ts.map +1 -1
- package/dist/src/plugins/health-plugin.d.ts.map +1 -1
- package/dist/src/plugins/health-plugin.js +1 -0
- package/dist/src/plugins/health-plugin.js.map +1 -1
- package/dist/src/plugins/index.d.ts +4 -4
- package/dist/src/plugins/index.d.ts.map +1 -1
- package/dist/src/plugins/index.js +4 -4
- package/dist/src/plugins/index.js.map +1 -1
- package/dist/src/plugins/logs-plugin.d.ts.map +1 -1
- package/dist/src/plugins/logs-plugin.js +49 -1
- package/dist/src/plugins/logs-plugin.js.map +1 -1
- package/dist/src/plugins/maintenance-plugin.d.ts.map +1 -1
- package/dist/src/plugins/maintenance-plugin.js +39 -0
- package/dist/src/plugins/maintenance-plugin.js.map +1 -1
- package/dist/src/plugins/notifications/index.d.ts +0 -4
- package/dist/src/plugins/notifications/index.d.ts.map +1 -1
- package/dist/src/plugins/notifications/index.js +2 -3
- package/dist/src/plugins/notifications/index.js.map +1 -1
- package/dist/src/plugins/notifications/notifications-plugin.d.ts +5 -2
- package/dist/src/plugins/notifications/notifications-plugin.d.ts.map +1 -1
- package/dist/src/plugins/notifications/notifications-plugin.js +46 -13
- package/dist/src/plugins/notifications/notifications-plugin.js.map +1 -1
- package/dist/src/plugins/parental/index.d.ts +0 -4
- package/dist/src/plugins/parental/index.d.ts.map +1 -1
- package/dist/src/plugins/parental/index.js +2 -3
- package/dist/src/plugins/parental/index.js.map +1 -1
- package/dist/src/plugins/parental/parental-plugin.d.ts +5 -2
- package/dist/src/plugins/parental/parental-plugin.d.ts.map +1 -1
- package/dist/src/plugins/parental/parental-plugin.js +60 -24
- package/dist/src/plugins/parental/parental-plugin.js.map +1 -1
- package/dist/src/plugins/postgres-plugin.d.ts +3 -1
- package/dist/src/plugins/postgres-plugin.d.ts.map +1 -1
- package/dist/src/plugins/postgres-plugin.js +18 -8
- package/dist/src/plugins/postgres-plugin.js.map +1 -1
- package/dist/src/plugins/preferences/index.d.ts +0 -4
- package/dist/src/plugins/preferences/index.d.ts.map +1 -1
- package/dist/src/plugins/preferences/index.js +2 -3
- package/dist/src/plugins/preferences/index.js.map +1 -1
- package/dist/src/plugins/preferences/preferences-plugin.d.ts +5 -2
- package/dist/src/plugins/preferences/preferences-plugin.d.ts.map +1 -1
- package/dist/src/plugins/preferences/preferences-plugin.js +63 -19
- package/dist/src/plugins/preferences/preferences-plugin.js.map +1 -1
- package/dist/src/plugins/profiles/index.d.ts +0 -4
- package/dist/src/plugins/profiles/index.d.ts.map +1 -1
- package/dist/src/plugins/profiles/index.js +2 -3
- package/dist/src/plugins/profiles/index.js.map +1 -1
- package/dist/src/plugins/profiles/profiles-plugin.d.ts +5 -2
- package/dist/src/plugins/profiles/profiles-plugin.d.ts.map +1 -1
- package/dist/src/plugins/profiles/profiles-plugin.js +60 -26
- package/dist/src/plugins/profiles/profiles-plugin.js.map +1 -1
- package/dist/src/plugins/profiles/types.d.ts +9 -2
- package/dist/src/plugins/profiles/types.d.ts.map +1 -1
- package/dist/src/plugins/qwickbrain/index.d.ts +0 -4
- package/dist/src/plugins/qwickbrain/index.d.ts.map +1 -1
- package/dist/src/plugins/qwickbrain/index.js +2 -3
- package/dist/src/plugins/qwickbrain/index.js.map +1 -1
- package/dist/src/plugins/qwickbrain/qwickbrain-plugin.d.ts.map +1 -1
- package/dist/src/plugins/qwickbrain/qwickbrain-plugin.js +117 -0
- package/dist/src/plugins/qwickbrain/qwickbrain-plugin.js.map +1 -1
- package/dist/src/plugins/rate-limit/index.d.ts +0 -4
- package/dist/src/plugins/rate-limit/index.d.ts.map +1 -1
- package/dist/src/plugins/rate-limit/index.js +2 -3
- package/dist/src/plugins/rate-limit/index.js.map +1 -1
- package/dist/src/plugins/subscriptions/index.d.ts +0 -4
- package/dist/src/plugins/subscriptions/index.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/index.js +2 -3
- package/dist/src/plugins/subscriptions/index.js.map +1 -1
- package/dist/src/plugins/subscriptions/subscriptions-plugin.d.ts +5 -2
- package/dist/src/plugins/subscriptions/subscriptions-plugin.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/subscriptions-plugin.js +63 -29
- package/dist/src/plugins/subscriptions/subscriptions-plugin.js.map +1 -1
- package/dist/src/plugins/subscriptions/types.d.ts +8 -2
- package/dist/src/plugins/subscriptions/types.d.ts.map +1 -1
- package/dist/src/plugins/tenants/index.d.ts +1 -1
- package/dist/src/plugins/tenants/index.d.ts.map +1 -1
- package/dist/src/plugins/tenants/index.js +1 -1
- package/dist/src/plugins/tenants/index.js.map +1 -1
- package/dist/src/plugins/tenants/stores/in-memory-store.d.ts +59 -0
- package/dist/src/plugins/tenants/stores/in-memory-store.d.ts.map +1 -0
- package/dist/src/plugins/tenants/stores/in-memory-store.js +257 -0
- package/dist/src/plugins/tenants/stores/in-memory-store.js.map +1 -0
- package/dist/src/plugins/tenants/stores/index.d.ts +8 -0
- package/dist/src/plugins/tenants/stores/index.d.ts.map +1 -0
- package/dist/src/plugins/tenants/stores/index.js +8 -0
- package/dist/src/plugins/tenants/stores/index.js.map +1 -0
- package/dist/src/plugins/tenants/tenants-plugin.d.ts +5 -2
- package/dist/src/plugins/tenants/tenants-plugin.d.ts.map +1 -1
- package/dist/src/plugins/tenants/tenants-plugin.js +93 -60
- package/dist/src/plugins/tenants/tenants-plugin.js.map +1 -1
- package/dist/src/plugins/tenants/types.d.ts +8 -2
- package/dist/src/plugins/tenants/types.d.ts.map +1 -1
- package/dist/src/plugins/usage/index.d.ts +0 -4
- package/dist/src/plugins/usage/index.d.ts.map +1 -1
- package/dist/src/plugins/usage/index.js +2 -3
- package/dist/src/plugins/usage/index.js.map +1 -1
- package/dist/src/plugins/usage/usage-plugin.d.ts +5 -2
- package/dist/src/plugins/usage/usage-plugin.d.ts.map +1 -1
- package/dist/src/plugins/usage/usage-plugin.js +57 -23
- package/dist/src/plugins/usage/usage-plugin.js.map +1 -1
- package/dist/src/plugins/users/index.d.ts +1 -1
- package/dist/src/plugins/users/index.d.ts.map +1 -1
- package/dist/src/plugins/users/index.js +1 -1
- package/dist/src/plugins/users/index.js.map +1 -1
- package/dist/src/plugins/users/stores/in-memory-store.d.ts +36 -0
- package/dist/src/plugins/users/stores/in-memory-store.d.ts.map +1 -0
- package/dist/src/plugins/users/stores/in-memory-store.js +122 -0
- package/dist/src/plugins/users/stores/in-memory-store.js.map +1 -0
- package/dist/src/plugins/users/stores/index.d.ts +1 -0
- package/dist/src/plugins/users/stores/index.d.ts.map +1 -1
- package/dist/src/plugins/users/stores/index.js +1 -0
- package/dist/src/plugins/users/stores/index.js.map +1 -1
- package/dist/src/plugins/users/types.d.ts +7 -2
- package/dist/src/plugins/users/types.d.ts.map +1 -1
- package/dist/src/plugins/users/users-plugin.d.ts +5 -2
- package/dist/src/plugins/users/users-plugin.d.ts.map +1 -1
- package/dist/src/plugins/users/users-plugin.js +56 -23
- package/dist/src/plugins/users/users-plugin.js.map +1 -1
- package/dist/ui/src/api/controlPanelApi.d.ts +10 -1
- package/dist/ui/src/api/controlPanelApi.d.ts.map +1 -1
- package/dist/ui/src/api/controlPanelApi.js.map +1 -1
- package/dist/ui/src/dashboard/PluginWidgetRenderer.d.ts +3 -1
- package/dist/ui/src/dashboard/PluginWidgetRenderer.d.ts.map +1 -1
- package/dist/ui/src/dashboard/PluginWidgetRenderer.js +5 -1
- package/dist/ui/src/dashboard/PluginWidgetRenderer.js.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.d.ts.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.js +13 -1
- package/dist/ui/src/dashboard/builtInWidgets.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.d.ts +11 -0
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js +77 -0
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.d.ts +10 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.js +14 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOpsWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.d.ts +10 -0
- package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.js +14 -0
- package/dist/ui/src/dashboard/widgets/EnvironmentConfigWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.d.ts +11 -0
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js +96 -0
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts +10 -0
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js +55 -0
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/ServiceControlWidget.d.ts +10 -0
- package/dist/ui/src/dashboard/widgets/ServiceControlWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/ServiceControlWidget.js +14 -0
- package/dist/ui/src/dashboard/widgets/ServiceControlWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/index.d.ts +6 -0
- package/dist/ui/src/dashboard/widgets/index.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/index.js +6 -0
- package/dist/ui/src/dashboard/widgets/index.js.map +1 -1
- package/dist/ui/src/pages/DashboardPage.js +1 -1
- package/dist/ui/src/pages/DashboardPage.js.map +1 -1
- package/dist-ui/assets/index-0gzisPdy.js +528 -0
- package/dist-ui/assets/{index-lm1yX6UD.js.map → index-0gzisPdy.js.map} +1 -1
- package/dist-ui/index.html +1 -1
- package/dist-ui-lib/index.js +3109 -2774
- package/dist-ui-lib/index.js.map +1 -1
- package/dist-ui-lib/src/api/controlPanelApi.d.ts +10 -1
- package/dist-ui-lib/src/dashboard/PluginWidgetRenderer.d.ts +3 -1
- package/dist-ui-lib/src/dashboard/widgets/CacheMaintenanceWidget.d.ts +10 -0
- package/dist-ui-lib/src/dashboard/widgets/DatabaseOpsWidget.d.ts +9 -0
- package/dist-ui-lib/src/dashboard/widgets/EnvironmentConfigWidget.d.ts +9 -0
- package/dist-ui-lib/src/dashboard/widgets/LogsMaintenanceWidget.d.ts +10 -0
- package/dist-ui-lib/src/dashboard/widgets/SeedManagementWidget.d.ts +9 -0
- package/dist-ui-lib/src/dashboard/widgets/ServiceControlWidget.d.ts +9 -0
- package/dist-ui-lib/src/dashboard/widgets/index.d.ts +6 -0
- package/package.json +12 -6
- package/src/core/control-panel.ts +6 -4
- package/src/core/gateway.ts +25 -2
- package/src/core/plugin-registry.ts +15 -2
- package/src/index.ts +53 -0
- package/src/plugins/api-keys/api-keys-plugin.ts +64 -20
- package/src/plugins/api-keys/index.ts +2 -5
- package/src/plugins/api-keys/stores/postgres-store.ts +30 -0
- package/src/plugins/api-keys/types.ts +9 -3
- package/src/plugins/auth/auth-plugin.ts +4 -2
- package/src/plugins/auth/env-config.ts +1 -0
- package/src/plugins/auth/index.ts +3 -5
- package/src/plugins/bans/bans-plugin.ts +71 -26
- package/src/plugins/bans/index.ts +4 -6
- package/src/plugins/bans/stores/in-memory-store.ts +106 -0
- package/src/plugins/bans/stores/index.ts +1 -0
- package/src/plugins/bans/types.ts +13 -6
- package/src/plugins/cache-plugin.test.ts +2 -2
- package/src/plugins/cache-plugin.ts +331 -30
- package/src/plugins/cms/cms-plugin.ts +3 -1
- package/src/plugins/devices/devices-plugin.ts +62 -27
- package/src/plugins/devices/index.ts +3 -5
- package/src/plugins/entitlements/entitlements-plugin.ts +81 -43
- package/src/plugins/entitlements/index.ts +4 -6
- package/src/plugins/entitlements/sources/in-memory-source.ts +76 -0
- package/src/plugins/entitlements/sources/index.ts +1 -0
- package/src/plugins/entitlements/types.ts +9 -2
- package/src/plugins/health-plugin.ts +1 -0
- package/src/plugins/index.ts +4 -1
- package/src/plugins/logs-plugin.ts +55 -1
- package/src/plugins/maintenance-plugin.ts +43 -0
- package/src/plugins/notifications/index.ts +3 -5
- package/src/plugins/notifications/notifications-plugin.ts +49 -19
- package/src/plugins/parental/index.ts +3 -5
- package/src/plugins/parental/parental-plugin.ts +63 -25
- package/src/plugins/postgres-plugin.test.ts +2 -2
- package/src/plugins/postgres-plugin.ts +20 -9
- package/src/plugins/preferences/index.ts +3 -5
- package/src/plugins/preferences/preferences-plugin.ts +66 -20
- package/src/plugins/profiles/index.ts +3 -5
- package/src/plugins/profiles/profiles-plugin.ts +60 -27
- package/src/plugins/profiles/types.ts +9 -2
- package/src/plugins/qwickbrain/index.ts +3 -5
- package/src/plugins/qwickbrain/qwickbrain-plugin.ts +135 -0
- package/src/plugins/rate-limit/index.ts +3 -5
- package/src/plugins/subscriptions/index.ts +3 -5
- package/src/plugins/subscriptions/subscriptions-plugin.ts +63 -30
- package/src/plugins/subscriptions/types.ts +8 -2
- package/src/plugins/tenants/index.ts +1 -1
- package/src/plugins/tenants/stores/in-memory-store.ts +335 -0
- package/src/plugins/tenants/stores/index.ts +13 -0
- package/src/plugins/tenants/tenants-plugin.ts +97 -62
- package/src/plugins/tenants/types.ts +8 -2
- package/src/plugins/usage/index.ts +3 -5
- package/src/plugins/usage/usage-plugin.ts +60 -26
- package/src/plugins/users/index.ts +1 -1
- package/src/plugins/users/stores/in-memory-store.ts +140 -0
- package/src/plugins/users/stores/index.ts +1 -0
- package/src/plugins/users/types.ts +7 -2
- package/src/plugins/users/users-plugin.ts +56 -24
- package/src/testing/index.ts +1 -0
- package/src/testing/pg-mem-pool.ts +33 -0
- package/ui/src/api/controlPanelApi.ts +10 -1
- package/ui/src/dashboard/PluginWidgetRenderer.tsx +8 -0
- package/ui/src/dashboard/builtInWidgets.tsx +19 -1
- package/ui/src/dashboard/widgets/CacheMaintenanceWidget.tsx +195 -0
- package/ui/src/dashboard/widgets/DatabaseOpsWidget.tsx +29 -0
- package/ui/src/dashboard/widgets/EnvironmentConfigWidget.tsx +29 -0
- package/ui/src/dashboard/widgets/LogsMaintenanceWidget.tsx +247 -0
- package/ui/src/dashboard/widgets/SeedManagementWidget.tsx +128 -0
- package/ui/src/dashboard/widgets/ServiceControlWidget.tsx +29 -0
- package/ui/src/dashboard/widgets/index.ts +6 -0
- package/ui/src/pages/DashboardPage.tsx +2 -2
- package/ui/src/pages/MaintenancePage.tsx +1 -1
- package/dist-ui/assets/index-lm1yX6UD.js +0 -528
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cache Plugin
|
|
3
3
|
*
|
|
4
|
-
* Provides
|
|
5
|
-
*
|
|
4
|
+
* Provides caching capabilities with Redis or in-memory storage.
|
|
5
|
+
* Supports Redis via 'ioredis' library or zero-dependency in-memory LRU cache.
|
|
6
6
|
*
|
|
7
7
|
* ## Features
|
|
8
|
-
* -
|
|
8
|
+
* - Dual-mode: Redis (persistent, distributed) or Memory (fast, local)
|
|
9
|
+
* - Connection management with automatic reconnection (Redis)
|
|
10
|
+
* - LRU eviction with TTL expiration (Memory)
|
|
9
11
|
* - Key prefixing for multi-tenant/multi-app scenarios
|
|
10
|
-
* - TTL-based caching with
|
|
12
|
+
* - TTL-based caching with get/set operations
|
|
11
13
|
* - Automatic health checks
|
|
12
14
|
* - Multiple named instances support
|
|
13
15
|
* - Graceful shutdown
|
|
14
16
|
*
|
|
15
|
-
* ## Usage
|
|
17
|
+
* ## Usage (Redis)
|
|
16
18
|
*
|
|
17
19
|
* ```typescript
|
|
18
20
|
* import { createGateway, createCachePlugin, getCache } from '@qwickapps/server';
|
|
@@ -21,6 +23,7 @@
|
|
|
21
23
|
* // ... config
|
|
22
24
|
* plugins: [
|
|
23
25
|
* createCachePlugin({
|
|
26
|
+
* type: 'redis',
|
|
24
27
|
* url: process.env.REDIS_URL,
|
|
25
28
|
* keyPrefix: 'myapp:',
|
|
26
29
|
* }),
|
|
@@ -33,12 +36,24 @@
|
|
|
33
36
|
* const user = await cache.get<User>('user:123');
|
|
34
37
|
* ```
|
|
35
38
|
*
|
|
39
|
+
* ## Usage (In-Memory)
|
|
40
|
+
*
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // Zero external dependencies - perfect for demos and testing
|
|
43
|
+
* createCachePlugin({
|
|
44
|
+
* type: 'memory',
|
|
45
|
+
* keyPrefix: 'demo:',
|
|
46
|
+
* defaultTtl: 3600,
|
|
47
|
+
* maxMemoryEntries: 5000,
|
|
48
|
+
* })
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
36
51
|
* ## Multiple Caches
|
|
37
52
|
*
|
|
38
53
|
* ```typescript
|
|
39
54
|
* // Register multiple caches with different names
|
|
40
55
|
* createCachePlugin({ url: primaryUrl, keyPrefix: 'session:' }, 'sessions');
|
|
41
|
-
* createCachePlugin({
|
|
56
|
+
* createCachePlugin({ type: 'memory', keyPrefix: 'cache:' }, 'content');
|
|
42
57
|
*
|
|
43
58
|
* // Access by name
|
|
44
59
|
* const sessions = getCache('sessions');
|
|
@@ -48,6 +63,7 @@
|
|
|
48
63
|
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
49
64
|
*/
|
|
50
65
|
|
|
66
|
+
import type { Request, Response } from 'express';
|
|
51
67
|
import type { Plugin, PluginConfig, PluginRegistry } from '../core/plugin-registry.js';
|
|
52
68
|
|
|
53
69
|
// Dynamic import for ioredis (optional peer dependency)
|
|
@@ -58,8 +74,11 @@ type RedisOptions = import('ioredis').RedisOptions;
|
|
|
58
74
|
* Configuration for the cache plugin
|
|
59
75
|
*/
|
|
60
76
|
export interface CachePluginConfig {
|
|
61
|
-
/**
|
|
62
|
-
|
|
77
|
+
/** Cache type: 'redis' or 'memory' (default: 'redis' if url provided, 'memory' otherwise) */
|
|
78
|
+
type?: 'redis' | 'memory';
|
|
79
|
+
|
|
80
|
+
/** Redis connection URL (required for type='redis', e.g., redis://localhost:6379) */
|
|
81
|
+
url?: string;
|
|
63
82
|
|
|
64
83
|
/** Key prefix for all cache operations (default: '') */
|
|
65
84
|
keyPrefix?: string;
|
|
@@ -67,34 +86,37 @@ export interface CachePluginConfig {
|
|
|
67
86
|
/** Default TTL in seconds for set operations (default: 3600 = 1 hour) */
|
|
68
87
|
defaultTtl?: number;
|
|
69
88
|
|
|
70
|
-
/** Maximum number of
|
|
89
|
+
/** Maximum number of entries for memory cache (default: 10000, only used for type='memory') */
|
|
90
|
+
maxMemoryEntries?: number;
|
|
91
|
+
|
|
92
|
+
/** Maximum number of retry attempts (default: 3, only used for type='redis') */
|
|
71
93
|
maxRetries?: number;
|
|
72
94
|
|
|
73
|
-
/** Retry delay in milliseconds (default: 1000) */
|
|
95
|
+
/** Retry delay in milliseconds (default: 1000, only used for type='redis') */
|
|
74
96
|
retryDelayMs?: number;
|
|
75
97
|
|
|
76
|
-
/** Connection timeout in milliseconds (default: 5000) */
|
|
98
|
+
/** Connection timeout in milliseconds (default: 5000, only used for type='redis') */
|
|
77
99
|
connectTimeoutMs?: number;
|
|
78
100
|
|
|
79
|
-
/** Command timeout in milliseconds (default: 5000) */
|
|
101
|
+
/** Command timeout in milliseconds (default: 5000, only used for type='redis') */
|
|
80
102
|
commandTimeoutMs?: number;
|
|
81
103
|
|
|
82
104
|
/** Register a health check for this cache (default: true) */
|
|
83
105
|
healthCheck?: boolean;
|
|
84
106
|
|
|
85
|
-
/** Name for the health check (default: 'redis') */
|
|
107
|
+
/** Name for the health check (default: 'redis' or 'memory') */
|
|
86
108
|
healthCheckName?: string;
|
|
87
109
|
|
|
88
110
|
/** Health check interval in milliseconds (default: 30000) */
|
|
89
111
|
healthCheckInterval?: number;
|
|
90
112
|
|
|
91
|
-
/** Called when connection is ready */
|
|
113
|
+
/** Called when connection is ready (only used for type='redis') */
|
|
92
114
|
onConnect?: () => void;
|
|
93
115
|
|
|
94
|
-
/** Called on connection errors */
|
|
116
|
+
/** Called on connection errors (only used for type='redis') */
|
|
95
117
|
onError?: (error: Error) => void;
|
|
96
118
|
|
|
97
|
-
/** Enable lazy connect - don't connect until first command (default: false) */
|
|
119
|
+
/** Enable lazy connect - don't connect until first command (default: false, only used for type='redis') */
|
|
98
120
|
lazyConnect?: boolean;
|
|
99
121
|
}
|
|
100
122
|
|
|
@@ -219,6 +241,94 @@ export interface CacheInstance {
|
|
|
219
241
|
close(): Promise<void>;
|
|
220
242
|
}
|
|
221
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Simple LRU Cache implementation for in-memory fallback
|
|
246
|
+
*/
|
|
247
|
+
class LRUCache<T> {
|
|
248
|
+
private cache = new Map<string, { value: T; expiresAt: number }>();
|
|
249
|
+
private maxSize: number;
|
|
250
|
+
|
|
251
|
+
constructor(maxSize: number) {
|
|
252
|
+
this.maxSize = maxSize;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
get(key: string): T | null {
|
|
256
|
+
const entry = this.cache.get(key);
|
|
257
|
+
if (!entry) return null;
|
|
258
|
+
|
|
259
|
+
// Check expiration
|
|
260
|
+
if (entry.expiresAt <= Date.now()) {
|
|
261
|
+
this.cache.delete(key);
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Move to end (most recently used)
|
|
266
|
+
this.cache.delete(key);
|
|
267
|
+
this.cache.set(key, entry);
|
|
268
|
+
|
|
269
|
+
return entry.value;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
set(key: string, value: T, ttlMs: number): void {
|
|
273
|
+
// Remove oldest entries if at capacity
|
|
274
|
+
while (this.cache.size >= this.maxSize) {
|
|
275
|
+
const firstKey = this.cache.keys().next().value;
|
|
276
|
+
if (firstKey) {
|
|
277
|
+
this.cache.delete(firstKey);
|
|
278
|
+
} else {
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
this.cache.set(key, {
|
|
284
|
+
value,
|
|
285
|
+
expiresAt: Date.now() + ttlMs,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
delete(key: string): boolean {
|
|
290
|
+
return this.cache.delete(key);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
has(key: string): boolean {
|
|
294
|
+
const entry = this.cache.get(key);
|
|
295
|
+
if (!entry) return false;
|
|
296
|
+
if (entry.expiresAt <= Date.now()) {
|
|
297
|
+
this.cache.delete(key);
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
keys(pattern: string): string[] {
|
|
304
|
+
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
|
|
305
|
+
const result: string[] = [];
|
|
306
|
+
for (const key of this.cache.keys()) {
|
|
307
|
+
if (regex.test(key)) {
|
|
308
|
+
const entry = this.cache.get(key);
|
|
309
|
+
if (entry && entry.expiresAt > Date.now()) {
|
|
310
|
+
result.push(key);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return result;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
size(): number {
|
|
318
|
+
// Clean up expired entries
|
|
319
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
320
|
+
if (entry.expiresAt <= Date.now()) {
|
|
321
|
+
this.cache.delete(key);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return this.cache.size;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
clear(): void {
|
|
328
|
+
this.cache.clear();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
222
332
|
// Global registry of cache instances by name
|
|
223
333
|
const instances = new Map<string, CacheInstance>();
|
|
224
334
|
|
|
@@ -274,7 +384,11 @@ export function createCachePlugin(
|
|
|
274
384
|
config: CachePluginConfig,
|
|
275
385
|
instanceName = 'default'
|
|
276
386
|
): Plugin {
|
|
387
|
+
// Determine cache type
|
|
388
|
+
const cacheType = config.type || (config.url ? 'redis' : 'memory');
|
|
389
|
+
|
|
277
390
|
let client: Redis | null = null;
|
|
391
|
+
let lru: LRUCache<string> | null = null;
|
|
278
392
|
const prefix = config.keyPrefix ?? '';
|
|
279
393
|
const defaultTtl = config.defaultTtl ?? 3600;
|
|
280
394
|
const pluginId = `cache:${instanceName}`;
|
|
@@ -283,7 +397,129 @@ export function createCachePlugin(
|
|
|
283
397
|
const unprefixKey = (key: string): string =>
|
|
284
398
|
prefix && key.startsWith(prefix) ? key.slice(prefix.length) : key;
|
|
285
399
|
|
|
286
|
-
const
|
|
400
|
+
const createMemoryInstance = (): CacheInstance => {
|
|
401
|
+
const maxEntries = config.maxMemoryEntries || 10000;
|
|
402
|
+
lru = new LRUCache<string>(maxEntries);
|
|
403
|
+
|
|
404
|
+
return {
|
|
405
|
+
async get<T = unknown>(key: string): Promise<T | null> {
|
|
406
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
407
|
+
const value = lru.get(prefixKey(key));
|
|
408
|
+
if (value === null) return null;
|
|
409
|
+
try {
|
|
410
|
+
return JSON.parse(value) as T;
|
|
411
|
+
} catch {
|
|
412
|
+
return value as unknown as T;
|
|
413
|
+
}
|
|
414
|
+
},
|
|
415
|
+
|
|
416
|
+
async getRaw(key: string): Promise<string | null> {
|
|
417
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
418
|
+
return lru.get(prefixKey(key));
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
async set<T = unknown>(key: string, value: T, ttlSeconds?: number): Promise<void> {
|
|
422
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
423
|
+
const ttl = (ttlSeconds ?? defaultTtl) * 1000; // Convert to ms
|
|
424
|
+
const serialized = typeof value === 'string' ? value : JSON.stringify(value);
|
|
425
|
+
lru.set(prefixKey(key), serialized, ttl);
|
|
426
|
+
},
|
|
427
|
+
|
|
428
|
+
async setRaw(key: string, value: string, ttlSeconds?: number): Promise<void> {
|
|
429
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
430
|
+
const ttl = (ttlSeconds ?? defaultTtl) * 1000;
|
|
431
|
+
lru.set(prefixKey(key), value, ttl);
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
async delete(key: string): Promise<boolean> {
|
|
435
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
436
|
+
return lru.delete(prefixKey(key));
|
|
437
|
+
},
|
|
438
|
+
|
|
439
|
+
async deletePattern(pattern: string): Promise<number> {
|
|
440
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
441
|
+
const keys = lru.keys(prefixKey(pattern));
|
|
442
|
+
keys.forEach(k => lru!.delete(k));
|
|
443
|
+
return keys.length;
|
|
444
|
+
},
|
|
445
|
+
|
|
446
|
+
async exists(key: string): Promise<boolean> {
|
|
447
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
448
|
+
return lru.has(prefixKey(key));
|
|
449
|
+
},
|
|
450
|
+
|
|
451
|
+
async expire(key: string, ttlSeconds: number): Promise<boolean> {
|
|
452
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
453
|
+
const value = lru.get(prefixKey(key));
|
|
454
|
+
if (value === null) return false;
|
|
455
|
+
lru.set(prefixKey(key), value, ttlSeconds * 1000);
|
|
456
|
+
return true;
|
|
457
|
+
},
|
|
458
|
+
|
|
459
|
+
async ttl(key: string): Promise<number> {
|
|
460
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
461
|
+
// Memory cache doesn't track TTL separately
|
|
462
|
+
return lru.has(prefixKey(key)) ? -1 : -2;
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
async incr(key: string, delta = 1): Promise<number> {
|
|
466
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
467
|
+
const current = lru.get(prefixKey(key));
|
|
468
|
+
const value = current ? parseInt(current, 10) + delta : delta;
|
|
469
|
+
lru.set(prefixKey(key), value.toString(), defaultTtl * 1000);
|
|
470
|
+
return value;
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
async keys(pattern: string): Promise<string[]> {
|
|
474
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
475
|
+
return lru.keys(prefixKey(pattern)).map(unprefixKey);
|
|
476
|
+
},
|
|
477
|
+
|
|
478
|
+
async scanKeys(pattern: string): Promise<string[]> {
|
|
479
|
+
// Memory cache can use same implementation as keys()
|
|
480
|
+
return this.keys(pattern);
|
|
481
|
+
},
|
|
482
|
+
|
|
483
|
+
async flush(): Promise<number> {
|
|
484
|
+
if (!lru) throw new Error('Cache not initialized');
|
|
485
|
+
if (!prefix) {
|
|
486
|
+
throw new Error('Cannot flush without a keyPrefix configured');
|
|
487
|
+
}
|
|
488
|
+
const keys = lru.keys(`${prefix}*`);
|
|
489
|
+
keys.forEach(k => lru!.delete(k));
|
|
490
|
+
return keys.length;
|
|
491
|
+
},
|
|
492
|
+
|
|
493
|
+
async getStats(): Promise<{ connected: boolean; keyCount: number; usedMemory?: string }> {
|
|
494
|
+
if (!lru) {
|
|
495
|
+
return { connected: false, keyCount: 0 };
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
connected: true,
|
|
499
|
+
keyCount: lru.size(),
|
|
500
|
+
usedMemory: undefined,
|
|
501
|
+
};
|
|
502
|
+
},
|
|
503
|
+
|
|
504
|
+
getClient(): Redis {
|
|
505
|
+
throw new Error('Memory cache does not have a Redis client');
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
async close(): Promise<void> {
|
|
509
|
+
if (lru) {
|
|
510
|
+
lru.clear();
|
|
511
|
+
lru = null;
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
};
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
const createRedisInstance = async (): Promise<CacheInstance> => {
|
|
518
|
+
// Validate Redis URL is provided
|
|
519
|
+
if (!config.url) {
|
|
520
|
+
throw new Error('Redis URL is required for Redis cache type');
|
|
521
|
+
}
|
|
522
|
+
|
|
287
523
|
// Dynamic import of ioredis
|
|
288
524
|
const { default: Redis } = await import('ioredis');
|
|
289
525
|
|
|
@@ -468,42 +704,50 @@ export function createCachePlugin(
|
|
|
468
704
|
|
|
469
705
|
return {
|
|
470
706
|
id: pluginId,
|
|
471
|
-
name:
|
|
707
|
+
name: `${cacheType === 'memory' ? 'Memory' : 'Redis'} Cache (${instanceName})`,
|
|
472
708
|
version: '1.0.0',
|
|
473
709
|
|
|
474
710
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
475
711
|
const logger = registry.getLogger(pluginId);
|
|
476
712
|
|
|
477
|
-
// Create and register the instance
|
|
478
|
-
const instance =
|
|
713
|
+
// Create and register the instance based on type
|
|
714
|
+
const instance = cacheType === 'memory'
|
|
715
|
+
? createMemoryInstance()
|
|
716
|
+
: await createRedisInstance();
|
|
479
717
|
instances.set(instanceName, instance);
|
|
480
718
|
|
|
481
|
-
// Test connection
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
719
|
+
// Test connection (skip for memory, test for Redis)
|
|
720
|
+
if (cacheType === 'redis') {
|
|
721
|
+
try {
|
|
722
|
+
await instance.getClient().ping();
|
|
723
|
+
logger.debug(`Cache "${instanceName}" connected`);
|
|
724
|
+
} catch (err) {
|
|
725
|
+
logger.error(`Cache "${instanceName}" connection failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
726
|
+
throw err;
|
|
727
|
+
}
|
|
728
|
+
} else {
|
|
729
|
+
logger.debug(`Cache "${instanceName}" initialized (in-memory)`);
|
|
489
730
|
}
|
|
490
731
|
|
|
491
732
|
// Register health check if enabled
|
|
492
733
|
if (config.healthCheck !== false) {
|
|
493
734
|
registry.registerHealthCheck({
|
|
494
|
-
name: config.healthCheckName ?? 'redis',
|
|
735
|
+
name: config.healthCheckName ?? (cacheType === 'memory' ? 'memory-cache' : 'redis'),
|
|
495
736
|
type: 'custom',
|
|
496
737
|
interval: config.healthCheckInterval ?? 30000,
|
|
497
738
|
timeout: 5000,
|
|
498
739
|
check: async () => {
|
|
499
740
|
const start = Date.now();
|
|
500
741
|
try {
|
|
501
|
-
|
|
742
|
+
if (cacheType === 'redis') {
|
|
743
|
+
await instance.getClient().ping();
|
|
744
|
+
}
|
|
502
745
|
const stats = await instance.getStats();
|
|
503
746
|
return {
|
|
504
747
|
healthy: true,
|
|
505
748
|
latency: Date.now() - start,
|
|
506
749
|
details: {
|
|
750
|
+
type: cacheType,
|
|
507
751
|
connected: stats.connected,
|
|
508
752
|
keyCount: stats.keyCount,
|
|
509
753
|
usedMemory: stats.usedMemory,
|
|
@@ -521,6 +765,63 @@ export function createCachePlugin(
|
|
|
521
765
|
},
|
|
522
766
|
});
|
|
523
767
|
}
|
|
768
|
+
|
|
769
|
+
// Register maintenance routes (only for default instance to avoid conflicts)
|
|
770
|
+
if (instanceName === 'default') {
|
|
771
|
+
// GET /stats - Cache statistics
|
|
772
|
+
registry.addRoute({
|
|
773
|
+
method: 'get',
|
|
774
|
+
path: '/stats',
|
|
775
|
+
pluginId: 'cache',
|
|
776
|
+
handler: async (_req: Request, res: Response) => {
|
|
777
|
+
try {
|
|
778
|
+
const stats = await instance.getStats();
|
|
779
|
+
return res.json(stats);
|
|
780
|
+
} catch (error) {
|
|
781
|
+
logger.error('Failed to get cache stats', { error });
|
|
782
|
+
return res.status(500).json({
|
|
783
|
+
error: 'Failed to get cache stats',
|
|
784
|
+
message: error instanceof Error ? error.message : String(error),
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
},
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
// POST /flush - Clear cache
|
|
791
|
+
registry.addRoute({
|
|
792
|
+
method: 'post',
|
|
793
|
+
path: '/flush',
|
|
794
|
+
pluginId: 'cache',
|
|
795
|
+
handler: async (_req: Request, res: Response) => {
|
|
796
|
+
try {
|
|
797
|
+
const deletedCount = await instance.flush();
|
|
798
|
+
logger.info(`Flushed cache: ${deletedCount} keys deleted`);
|
|
799
|
+
return res.json({
|
|
800
|
+
success: true,
|
|
801
|
+
message: `Cache flushed successfully`,
|
|
802
|
+
deletedCount,
|
|
803
|
+
});
|
|
804
|
+
} catch (error) {
|
|
805
|
+
logger.error('Failed to flush cache', { error });
|
|
806
|
+
return res.status(500).json({
|
|
807
|
+
error: 'Failed to flush cache',
|
|
808
|
+
message: error instanceof Error ? error.message : String(error),
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
},
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
// Register maintenance widget
|
|
815
|
+
registry.addWidget({
|
|
816
|
+
id: 'cache-maintenance',
|
|
817
|
+
title: 'Cache Management',
|
|
818
|
+
component: 'CacheMaintenanceWidget',
|
|
819
|
+
type: 'maintenance',
|
|
820
|
+
priority: 60,
|
|
821
|
+
showByDefault: true,
|
|
822
|
+
pluginId: 'cache',
|
|
823
|
+
});
|
|
824
|
+
}
|
|
524
825
|
},
|
|
525
826
|
|
|
526
827
|
async onStop(): Promise<void> {
|
|
@@ -50,6 +50,7 @@ export function createCMSPlugin(config: CMSPluginConfig): Plugin {
|
|
|
50
50
|
id: 'cms-status',
|
|
51
51
|
title: 'Payload CMS',
|
|
52
52
|
component: 'CMSStatusWidget',
|
|
53
|
+
type: 'status',
|
|
53
54
|
priority: 15, // After ServiceHealthWidget (10)
|
|
54
55
|
showByDefault: true,
|
|
55
56
|
pluginId: 'cms',
|
|
@@ -62,8 +63,9 @@ export function createCMSPlugin(config: CMSPluginConfig): Plugin {
|
|
|
62
63
|
id: 'cms-maintenance',
|
|
63
64
|
title: 'CMS Service Control',
|
|
64
65
|
component: 'CMSMaintenanceWidget',
|
|
66
|
+
type: 'maintenance',
|
|
65
67
|
priority: 10,
|
|
66
|
-
showByDefault:
|
|
68
|
+
showByDefault: true, // Show by default on maintenance page
|
|
67
69
|
pluginId: 'cms',
|
|
68
70
|
});
|
|
69
71
|
}
|
|
@@ -28,6 +28,9 @@ import {
|
|
|
28
28
|
isTokenExpired,
|
|
29
29
|
getTokenExpiration,
|
|
30
30
|
} from './token-utils.js';
|
|
31
|
+
import { hasPostgres, getPostgres } from '../postgres-plugin.js';
|
|
32
|
+
import { postgresDeviceStore } from './stores/index.js';
|
|
33
|
+
import { computeDeviceAdapter } from './adapters/index.js';
|
|
31
34
|
|
|
32
35
|
// Store instances for helper access
|
|
33
36
|
let currentStore: DeviceStore | null = null;
|
|
@@ -35,16 +38,18 @@ let currentAdapter: DeviceAdapter | null = null;
|
|
|
35
38
|
let currentConfig: DevicesPluginConfig | null = null;
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
|
-
* Create the Devices plugin
|
|
41
|
+
* Create the Devices plugin with smart defaults
|
|
42
|
+
*
|
|
43
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
44
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
39
45
|
*/
|
|
40
|
-
export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log(`[DevicesPlugin] ${message}`, data || '');
|
|
46
|
+
export function createDevicesPlugin(config: Partial<DevicesPluginConfig> = {}): Plugin {
|
|
47
|
+
function log(message: string, data?: Record<string, unknown>, isError = false) {
|
|
48
|
+
const prefix = '[DevicesPlugin]';
|
|
49
|
+
if (isError) {
|
|
50
|
+
console.error(`${prefix} ${message}`, data || '');
|
|
51
|
+
} else if (config.debug) {
|
|
52
|
+
console.log(`${prefix} ${message}`, data || '');
|
|
48
53
|
}
|
|
49
54
|
}
|
|
50
55
|
|
|
@@ -54,16 +59,46 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
54
59
|
version: '1.0.0',
|
|
55
60
|
|
|
56
61
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
57
|
-
|
|
62
|
+
const logger = registry.getLogger('devices');
|
|
63
|
+
|
|
64
|
+
// Check for postgres in registry
|
|
65
|
+
if (!hasPostgres()) {
|
|
66
|
+
logger.warn('No Database! Devices plugin disabled.');
|
|
67
|
+
registry.registerHealthCheck({
|
|
68
|
+
name: 'devices-store',
|
|
69
|
+
type: 'custom',
|
|
70
|
+
check: async () => ({
|
|
71
|
+
healthy: false,
|
|
72
|
+
details: {
|
|
73
|
+
error: 'PostgreSQL not available',
|
|
74
|
+
state: 'disabled',
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Smart defaults - get dependencies from registry
|
|
82
|
+
const store = config.store ?? postgresDeviceStore({
|
|
83
|
+
pool: () => getPostgres().getPool(),
|
|
84
|
+
autoCreateTables: true,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const adapter = config.adapter ?? computeDeviceAdapter();
|
|
88
|
+
const debug = config.debug ?? false;
|
|
89
|
+
const defaultTokenValidityDays = config.defaultTokenValidityDays ?? 90;
|
|
90
|
+
const apiPrefix = config.api?.prefix ?? '/devices';
|
|
91
|
+
|
|
92
|
+
log('Starting devices plugin', { adapter: adapter.name });
|
|
58
93
|
|
|
59
94
|
// Initialize the store (creates tables if needed)
|
|
60
|
-
await
|
|
95
|
+
await store.initialize();
|
|
61
96
|
log('Devices plugin migrations complete');
|
|
62
97
|
|
|
63
98
|
// Store references for helper access
|
|
64
|
-
currentStore =
|
|
65
|
-
currentAdapter =
|
|
66
|
-
currentConfig = config;
|
|
99
|
+
currentStore = store;
|
|
100
|
+
currentAdapter = adapter;
|
|
101
|
+
currentConfig = { ...config, store, adapter, debug, defaultTokenValidityDays };
|
|
67
102
|
|
|
68
103
|
// Register health check
|
|
69
104
|
registry.registerHealthCheck({
|
|
@@ -71,12 +106,12 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
71
106
|
type: 'custom',
|
|
72
107
|
check: async () => {
|
|
73
108
|
try {
|
|
74
|
-
await
|
|
109
|
+
await store.search({ limit: 1 });
|
|
75
110
|
return {
|
|
76
111
|
healthy: true,
|
|
77
112
|
details: {
|
|
78
|
-
adapter:
|
|
79
|
-
tokenPrefix:
|
|
113
|
+
adapter: adapter.name,
|
|
114
|
+
tokenPrefix: adapter.tokenPrefix,
|
|
80
115
|
},
|
|
81
116
|
};
|
|
82
117
|
} catch {
|
|
@@ -106,7 +141,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
106
141
|
sortOrder: (req.query.sortOrder as DeviceSearchParams['sortOrder']) || 'desc',
|
|
107
142
|
};
|
|
108
143
|
|
|
109
|
-
const result = await
|
|
144
|
+
const result = await store.search(params);
|
|
110
145
|
res.json(result);
|
|
111
146
|
} catch (error) {
|
|
112
147
|
console.error('[DevicesPlugin] Search error:', error);
|
|
@@ -122,7 +157,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
122
157
|
pluginId: 'devices',
|
|
123
158
|
handler: async (req: Request, res: Response) => {
|
|
124
159
|
try {
|
|
125
|
-
const device = await
|
|
160
|
+
const device = await store.getById(req.params.id);
|
|
126
161
|
if (!device) {
|
|
127
162
|
return res.status(404).json({ error: 'Device not found' });
|
|
128
163
|
}
|
|
@@ -150,7 +185,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
150
185
|
};
|
|
151
186
|
|
|
152
187
|
// Validate using adapter
|
|
153
|
-
const validation =
|
|
188
|
+
const validation = adapter.validateDeviceInput(input);
|
|
154
189
|
if (!validation.valid) {
|
|
155
190
|
return res.status(400).json({
|
|
156
191
|
error: 'Validation failed',
|
|
@@ -181,7 +216,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
181
216
|
metadata: req.body.metadata,
|
|
182
217
|
};
|
|
183
218
|
|
|
184
|
-
const device = await
|
|
219
|
+
const device = await store.update(req.params.id, input);
|
|
185
220
|
if (!device) {
|
|
186
221
|
return res.status(404).json({ error: 'Device not found' });
|
|
187
222
|
}
|
|
@@ -200,19 +235,19 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
200
235
|
pluginId: 'devices',
|
|
201
236
|
handler: async (req: Request, res: Response) => {
|
|
202
237
|
try {
|
|
203
|
-
const device = await
|
|
238
|
+
const device = await store.getById(req.params.id);
|
|
204
239
|
if (!device) {
|
|
205
240
|
return res.status(404).json({ error: 'Device not found' });
|
|
206
241
|
}
|
|
207
242
|
|
|
208
|
-
const deleted = await
|
|
243
|
+
const deleted = await store.delete(req.params.id);
|
|
209
244
|
if (!deleted) {
|
|
210
245
|
return res.status(404).json({ error: 'Device not found' });
|
|
211
246
|
}
|
|
212
247
|
|
|
213
248
|
// Call adapter hook
|
|
214
|
-
if (
|
|
215
|
-
await
|
|
249
|
+
if (adapter.onDeviceDeleted) {
|
|
250
|
+
await adapter.onDeviceDeleted(device);
|
|
216
251
|
}
|
|
217
252
|
|
|
218
253
|
res.status(204).send();
|
|
@@ -230,7 +265,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
230
265
|
pluginId: 'devices',
|
|
231
266
|
handler: async (req: Request, res: Response) => {
|
|
232
267
|
try {
|
|
233
|
-
const device = await
|
|
268
|
+
const device = await store.getById(req.params.id);
|
|
234
269
|
if (!device) {
|
|
235
270
|
return res.status(404).json({ error: 'Device not found' });
|
|
236
271
|
}
|
|
@@ -295,7 +330,7 @@ export function createDevicesPlugin(config: DevicesPluginConfig): Plugin {
|
|
|
295
330
|
|
|
296
331
|
async onStop(): Promise<void> {
|
|
297
332
|
log('Stopping devices plugin');
|
|
298
|
-
await
|
|
333
|
+
if (currentStore) { await currentStore.shutdown(); };
|
|
299
334
|
currentStore = null;
|
|
300
335
|
currentAdapter = null;
|
|
301
336
|
currentConfig = null;
|