@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
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory Tenant Store for Demo/Testing
|
|
3
|
+
*
|
|
4
|
+
* Implements the TenantStore interface with in-memory storage.
|
|
5
|
+
* Pre-populated with demo tenants and memberships.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Tenant,
|
|
10
|
+
TenantStore,
|
|
11
|
+
CreateTenantInput,
|
|
12
|
+
UpdateTenantInput,
|
|
13
|
+
TenantSearchParams,
|
|
14
|
+
TenantListResponse,
|
|
15
|
+
TenantMembership,
|
|
16
|
+
CreateTenantMembershipInput,
|
|
17
|
+
UpdateTenantMembershipInput,
|
|
18
|
+
TenantWithMembership,
|
|
19
|
+
TenantType,
|
|
20
|
+
} from '../types.js';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Default demo user ID used for pre-populated tenants.
|
|
24
|
+
* Matches the user ID assigned by basic auth guard.
|
|
25
|
+
*/
|
|
26
|
+
export const DEMO_USER_ID = 'basic-auth-user';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Logger interface for in-memory stores.
|
|
30
|
+
*/
|
|
31
|
+
export interface InMemoryStoreLogger {
|
|
32
|
+
info: (message: string) => void;
|
|
33
|
+
debug?: (message: string) => void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Options for creating an in-memory tenant store.
|
|
38
|
+
*/
|
|
39
|
+
export interface InMemoryTenantStoreOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Demo user ID for pre-populated tenants.
|
|
42
|
+
* @default 'basic-auth-user'
|
|
43
|
+
*/
|
|
44
|
+
demoUserId?: string;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Optional logger for store operations.
|
|
48
|
+
* If not provided, uses console.log as fallback.
|
|
49
|
+
*/
|
|
50
|
+
logger?: InMemoryStoreLogger;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Creates an in-memory tenant store for demo/testing purposes.
|
|
55
|
+
* Pre-populated with 4 demo tenants (organization, group, department, user).
|
|
56
|
+
*
|
|
57
|
+
* This store is NOT suitable for production use - data is lost on restart.
|
|
58
|
+
* Use postgresTenantsStore for production deployments.
|
|
59
|
+
*
|
|
60
|
+
* @param options - Configuration options for the store
|
|
61
|
+
* @returns TenantStore implementation with in-memory storage
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* import { createInMemoryTenantStore } from '@qwickapps/server';
|
|
66
|
+
*
|
|
67
|
+
* const store = createInMemoryTenantStore({
|
|
68
|
+
* demoUserId: 'test-user-123',
|
|
69
|
+
* logger: console,
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* await store.initialize();
|
|
73
|
+
* const tenants = await store.getTenantsForUser('test-user-123');
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function createInMemoryTenantStore(
|
|
77
|
+
options: InMemoryTenantStoreOptions = {}
|
|
78
|
+
): TenantStore {
|
|
79
|
+
const demoUserId = options.demoUserId || DEMO_USER_ID;
|
|
80
|
+
const logger = options.logger || {
|
|
81
|
+
info: (msg: string) => console.log(msg),
|
|
82
|
+
debug: (msg: string) => console.log(msg),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const tenants = new Map<string, Tenant>();
|
|
86
|
+
const memberships = new Map<string, TenantMembership>();
|
|
87
|
+
let tenantIdCounter = 1;
|
|
88
|
+
let membershipIdCounter = 1;
|
|
89
|
+
|
|
90
|
+
// Pre-populate demo tenants
|
|
91
|
+
const demoTenants: Array<{ name: string; type: TenantType; owner_id: string }> = [
|
|
92
|
+
{ name: 'Acme Corporation', type: 'organization', owner_id: demoUserId },
|
|
93
|
+
{ name: 'Engineering Team', type: 'group', owner_id: demoUserId },
|
|
94
|
+
{ name: 'Finance Department', type: 'department', owner_id: demoUserId },
|
|
95
|
+
{ name: 'Demo User Workspace', type: 'user', owner_id: demoUserId },
|
|
96
|
+
];
|
|
97
|
+
|
|
98
|
+
demoTenants.forEach((t) => {
|
|
99
|
+
const id = `tenant-${tenantIdCounter++}`;
|
|
100
|
+
const tenant: Tenant = {
|
|
101
|
+
id,
|
|
102
|
+
name: t.name,
|
|
103
|
+
type: t.type,
|
|
104
|
+
owner_id: t.owner_id,
|
|
105
|
+
metadata: {},
|
|
106
|
+
created_at: new Date(),
|
|
107
|
+
updated_at: new Date(),
|
|
108
|
+
};
|
|
109
|
+
tenants.set(id, tenant);
|
|
110
|
+
|
|
111
|
+
// Auto-create membership for owner
|
|
112
|
+
const membershipId = `membership-${membershipIdCounter++}`;
|
|
113
|
+
const membership: TenantMembership = {
|
|
114
|
+
id: membershipId,
|
|
115
|
+
tenant_id: id,
|
|
116
|
+
user_id: t.owner_id,
|
|
117
|
+
role: 'owner',
|
|
118
|
+
joined_at: new Date(),
|
|
119
|
+
};
|
|
120
|
+
memberships.set(membershipId, membership);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
name: 'in-memory',
|
|
125
|
+
|
|
126
|
+
async initialize(): Promise<void> {
|
|
127
|
+
logger.info('[InMemoryTenantStore] Initialized with demo tenants');
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
async getById(id: string): Promise<Tenant | null> {
|
|
131
|
+
return tenants.get(id) || null;
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
async getByIds(ids: string[]): Promise<Tenant[]> {
|
|
135
|
+
return ids.map((id) => tenants.get(id)).filter((t): t is Tenant => t !== undefined);
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
async getByName(name: string): Promise<Tenant | null> {
|
|
139
|
+
for (const tenant of tenants.values()) {
|
|
140
|
+
if (tenant.name.toLowerCase() === name.toLowerCase()) {
|
|
141
|
+
return tenant;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return null;
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
async create(input: CreateTenantInput): Promise<Tenant> {
|
|
148
|
+
const id = `tenant-${tenantIdCounter++}`;
|
|
149
|
+
const tenant: Tenant = {
|
|
150
|
+
id,
|
|
151
|
+
name: input.name,
|
|
152
|
+
type: input.type,
|
|
153
|
+
owner_id: input.owner_id,
|
|
154
|
+
metadata: input.metadata || {},
|
|
155
|
+
created_at: new Date(),
|
|
156
|
+
updated_at: new Date(),
|
|
157
|
+
};
|
|
158
|
+
tenants.set(id, tenant);
|
|
159
|
+
|
|
160
|
+
// Auto-create membership for owner
|
|
161
|
+
const membershipId = `membership-${membershipIdCounter++}`;
|
|
162
|
+
const membership: TenantMembership = {
|
|
163
|
+
id: membershipId,
|
|
164
|
+
tenant_id: id,
|
|
165
|
+
user_id: input.owner_id,
|
|
166
|
+
role: 'owner',
|
|
167
|
+
joined_at: new Date(),
|
|
168
|
+
};
|
|
169
|
+
memberships.set(membershipId, membership);
|
|
170
|
+
|
|
171
|
+
return tenant;
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
async update(id: string, input: UpdateTenantInput): Promise<Tenant | null> {
|
|
175
|
+
const tenant = tenants.get(id);
|
|
176
|
+
if (!tenant) return null;
|
|
177
|
+
|
|
178
|
+
const updated: Tenant = {
|
|
179
|
+
...tenant,
|
|
180
|
+
...input,
|
|
181
|
+
id: tenant.id, // Preserve ID
|
|
182
|
+
created_at: tenant.created_at, // Preserve created_at
|
|
183
|
+
updated_at: new Date(),
|
|
184
|
+
};
|
|
185
|
+
tenants.set(id, updated);
|
|
186
|
+
return updated;
|
|
187
|
+
},
|
|
188
|
+
|
|
189
|
+
async delete(id: string): Promise<boolean> {
|
|
190
|
+
// Delete all memberships for this tenant
|
|
191
|
+
for (const [key, membership] of memberships.entries()) {
|
|
192
|
+
if (membership.tenant_id === id) {
|
|
193
|
+
memberships.delete(key);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return tenants.delete(id);
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
async search(params: TenantSearchParams = {}): Promise<TenantListResponse> {
|
|
200
|
+
let result = Array.from(tenants.values());
|
|
201
|
+
|
|
202
|
+
if (params.query) {
|
|
203
|
+
const query = params.query.toLowerCase();
|
|
204
|
+
result = result.filter((t) => t.name.toLowerCase().includes(query));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (params.type) {
|
|
208
|
+
result = result.filter((t) => t.type === params.type);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (params.owner_id) {
|
|
212
|
+
result = result.filter((t) => t.owner_id === params.owner_id);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const sortBy = params.sortBy || 'created_at';
|
|
216
|
+
const sortOrder = params.sortOrder || 'desc';
|
|
217
|
+
|
|
218
|
+
result.sort((a, b) => {
|
|
219
|
+
const aVal = a[sortBy as keyof Tenant];
|
|
220
|
+
const bVal = b[sortBy as keyof Tenant];
|
|
221
|
+
if (aVal == null || bVal == null) return 0;
|
|
222
|
+
if (aVal < bVal) return sortOrder === 'asc' ? -1 : 1;
|
|
223
|
+
if (aVal > bVal) return sortOrder === 'asc' ? 1 : -1;
|
|
224
|
+
return 0;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const total = result.length;
|
|
228
|
+
const page = params.page || 1;
|
|
229
|
+
const limit = params.limit || 20;
|
|
230
|
+
const offset = (page - 1) * limit;
|
|
231
|
+
result = result.slice(offset, offset + limit);
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
tenants: result,
|
|
235
|
+
total,
|
|
236
|
+
page,
|
|
237
|
+
limit,
|
|
238
|
+
totalPages: Math.ceil(total / limit),
|
|
239
|
+
};
|
|
240
|
+
},
|
|
241
|
+
|
|
242
|
+
async getTenantsForUser(userId: string): Promise<TenantWithMembership[]> {
|
|
243
|
+
const userMemberships = Array.from(memberships.values()).filter(
|
|
244
|
+
(m) => m.user_id === userId
|
|
245
|
+
);
|
|
246
|
+
const results: TenantWithMembership[] = [];
|
|
247
|
+
for (const membership of userMemberships) {
|
|
248
|
+
const tenant = tenants.get(membership.tenant_id);
|
|
249
|
+
if (tenant) {
|
|
250
|
+
results.push({
|
|
251
|
+
...tenant,
|
|
252
|
+
user_role: membership.role,
|
|
253
|
+
membership,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return results;
|
|
258
|
+
},
|
|
259
|
+
|
|
260
|
+
async getTenantForUser(
|
|
261
|
+
tenantId: string,
|
|
262
|
+
userId: string
|
|
263
|
+
): Promise<TenantWithMembership | null> {
|
|
264
|
+
const membership = await this.getMembership(tenantId, userId);
|
|
265
|
+
if (!membership) return null;
|
|
266
|
+
|
|
267
|
+
const tenant = tenants.get(tenantId);
|
|
268
|
+
if (!tenant) return null;
|
|
269
|
+
|
|
270
|
+
return {
|
|
271
|
+
...tenant,
|
|
272
|
+
user_role: membership.role,
|
|
273
|
+
membership,
|
|
274
|
+
};
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
async getMembers(tenantId: string): Promise<TenantMembership[]> {
|
|
278
|
+
return Array.from(memberships.values()).filter((m) => m.tenant_id === tenantId);
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
async getMembership(tenantId: string, userId: string): Promise<TenantMembership | null> {
|
|
282
|
+
for (const membership of memberships.values()) {
|
|
283
|
+
if (membership.tenant_id === tenantId && membership.user_id === userId) {
|
|
284
|
+
return membership;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return null;
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
async addMember(input: CreateTenantMembershipInput): Promise<TenantMembership> {
|
|
291
|
+
const id = `membership-${membershipIdCounter++}`;
|
|
292
|
+
const membership: TenantMembership = {
|
|
293
|
+
id,
|
|
294
|
+
tenant_id: input.tenant_id,
|
|
295
|
+
user_id: input.user_id,
|
|
296
|
+
role: input.role,
|
|
297
|
+
joined_at: new Date(),
|
|
298
|
+
};
|
|
299
|
+
memberships.set(id, membership);
|
|
300
|
+
return membership;
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
async updateMember(
|
|
304
|
+
tenantId: string,
|
|
305
|
+
userId: string,
|
|
306
|
+
input: UpdateTenantMembershipInput
|
|
307
|
+
): Promise<TenantMembership | null> {
|
|
308
|
+
for (const membership of memberships.values()) {
|
|
309
|
+
if (membership.tenant_id === tenantId && membership.user_id === userId) {
|
|
310
|
+
const updated: TenantMembership = {
|
|
311
|
+
...membership,
|
|
312
|
+
...input,
|
|
313
|
+
};
|
|
314
|
+
memberships.set(membership.id, updated);
|
|
315
|
+
return updated;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return null;
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
async removeMember(tenantId: string, userId: string): Promise<boolean> {
|
|
322
|
+
for (const [key, membership] of memberships.entries()) {
|
|
323
|
+
if (membership.tenant_id === tenantId && membership.user_id === userId) {
|
|
324
|
+
memberships.delete(key);
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return false;
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
async shutdown(): Promise<void> {
|
|
332
|
+
logger.info('[InMemoryTenantStore] Shutdown');
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tenant Stores Index
|
|
3
|
+
*
|
|
4
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { postgresTenantStore } from './postgres-store.js';
|
|
8
|
+
export {
|
|
9
|
+
createInMemoryTenantStore as inMemoryTenantStore,
|
|
10
|
+
DEMO_USER_ID,
|
|
11
|
+
type InMemoryStoreLogger,
|
|
12
|
+
type InMemoryTenantStoreOptions,
|
|
13
|
+
} from './in-memory-store.js';
|
|
@@ -23,58 +23,26 @@ import type {
|
|
|
23
23
|
} from './types.js';
|
|
24
24
|
import { getUserById } from '../users/users-plugin.js';
|
|
25
25
|
import { getAuthenticatedUser, isAuthenticated } from '../auth/index.js';
|
|
26
|
+
import { hasPostgres, getPostgres } from '../postgres-plugin.js';
|
|
27
|
+
import { postgresTenantStore } from './stores/index.js';
|
|
26
28
|
|
|
27
29
|
// Store instance and registry for helper access
|
|
28
30
|
let currentStore: TenantStore | null = null;
|
|
29
31
|
let currentRegistry: PluginRegistry | null = null;
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
|
-
* Create the Tenants plugin
|
|
34
|
+
* Create the Tenants plugin with smart defaults
|
|
35
|
+
*
|
|
36
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
37
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
33
38
|
*/
|
|
34
|
-
export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
console.log(`[TenantsPlugin] ${message}`, data || '');
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Helper to check if user has access to a tenant
|
|
47
|
-
*/
|
|
48
|
-
async function canAccessTenant(userId: string, tenantId: string): Promise<boolean> {
|
|
49
|
-
try {
|
|
50
|
-
const membership = await config.store.getTenantForUser(tenantId, userId);
|
|
51
|
-
return membership !== null;
|
|
52
|
-
} catch {
|
|
53
|
-
return false;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Helper to check if user has admin/owner role in a tenant
|
|
59
|
-
*/
|
|
60
|
-
async function canManageTenant(userId: string, tenantId: string): Promise<boolean> {
|
|
61
|
-
try {
|
|
62
|
-
const membership = await config.store.getTenantForUser(tenantId, userId);
|
|
63
|
-
return membership !== null && ['owner', 'admin'].includes(membership.user_role);
|
|
64
|
-
} catch {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Helper to check if user is owner of a tenant
|
|
71
|
-
*/
|
|
72
|
-
async function isOwnerOfTenant(userId: string, tenantId: string): Promise<boolean> {
|
|
73
|
-
try {
|
|
74
|
-
const membership = await config.store.getTenantForUser(tenantId, userId);
|
|
75
|
-
return membership !== null && membership.user_role === 'owner';
|
|
76
|
-
} catch {
|
|
77
|
-
return false;
|
|
39
|
+
export function createTenantsPlugin(config: Partial<TenantsPluginConfig> = {}): Plugin {
|
|
40
|
+
function log(message: string, data?: Record<string, unknown>, isError = false) {
|
|
41
|
+
const prefix = '[TenantsPlugin]';
|
|
42
|
+
if (isError) {
|
|
43
|
+
console.error(`${prefix} ${message}`, data || '');
|
|
44
|
+
} else if (config.debug) {
|
|
45
|
+
console.log(`${prefix} ${message}`, data || '');
|
|
78
46
|
}
|
|
79
47
|
}
|
|
80
48
|
|
|
@@ -84,14 +52,43 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
84
52
|
version: '1.0.0',
|
|
85
53
|
|
|
86
54
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
55
|
+
const logger = registry.getLogger('tenants');
|
|
56
|
+
|
|
57
|
+
// Check for postgres in registry
|
|
58
|
+
if (!hasPostgres()) {
|
|
59
|
+
logger.warn('No Database! Tenants plugin disabled.');
|
|
60
|
+
registry.registerHealthCheck({
|
|
61
|
+
name: 'tenants-store',
|
|
62
|
+
type: 'custom',
|
|
63
|
+
check: async () => ({
|
|
64
|
+
healthy: false,
|
|
65
|
+
details: {
|
|
66
|
+
error: 'PostgreSQL not available',
|
|
67
|
+
state: 'disabled',
|
|
68
|
+
},
|
|
69
|
+
}),
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Smart defaults - get dependencies from registry
|
|
75
|
+
const store = config.store ?? postgresTenantStore({
|
|
76
|
+
pool: () => getPostgres().getPool(),
|
|
77
|
+
autoCreateTables: true,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const debug = config.debug ?? false;
|
|
81
|
+
const apiPrefix = config.apiPrefix ?? '/tenants';
|
|
82
|
+
const apiEnabled = config.apiEnabled ?? true;
|
|
83
|
+
|
|
87
84
|
log('Starting tenants plugin');
|
|
88
85
|
|
|
89
86
|
// Initialize the store (creates tables if needed)
|
|
90
|
-
await
|
|
87
|
+
await store.initialize();
|
|
91
88
|
log('Tenants plugin migrations complete');
|
|
92
89
|
|
|
93
90
|
// Store references for helper access
|
|
94
|
-
currentStore =
|
|
91
|
+
currentStore = store;
|
|
95
92
|
currentRegistry = registry;
|
|
96
93
|
|
|
97
94
|
// Register health check
|
|
@@ -101,7 +98,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
101
98
|
check: async () => {
|
|
102
99
|
try {
|
|
103
100
|
// Simple health check - try to search with limit 1
|
|
104
|
-
await
|
|
101
|
+
await store.search({ limit: 1 });
|
|
105
102
|
return { healthy: true };
|
|
106
103
|
} catch {
|
|
107
104
|
return { healthy: false };
|
|
@@ -109,6 +106,42 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
109
106
|
},
|
|
110
107
|
});
|
|
111
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Helper to check if user has access to a tenant
|
|
111
|
+
*/
|
|
112
|
+
async function canAccessTenant(userId: string, tenantId: string): Promise<boolean> {
|
|
113
|
+
try {
|
|
114
|
+
const membership = await store.getTenantForUser(tenantId, userId);
|
|
115
|
+
return membership !== null;
|
|
116
|
+
} catch {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Helper to check if user has admin/owner role in a tenant
|
|
123
|
+
*/
|
|
124
|
+
async function canManageTenant(userId: string, tenantId: string): Promise<boolean> {
|
|
125
|
+
try {
|
|
126
|
+
const membership = await store.getTenantForUser(tenantId, userId);
|
|
127
|
+
return membership !== null && ['owner', 'admin'].includes(membership.user_role);
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Helper to check if user is owner of a tenant
|
|
135
|
+
*/
|
|
136
|
+
async function isOwnerOfTenant(userId: string, tenantId: string): Promise<boolean> {
|
|
137
|
+
try {
|
|
138
|
+
const membership = await store.getTenantForUser(tenantId, userId);
|
|
139
|
+
return membership !== null && membership.user_role === 'owner';
|
|
140
|
+
} catch {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
112
145
|
if (!apiEnabled) return;
|
|
113
146
|
|
|
114
147
|
// ========================================================================
|
|
@@ -118,7 +151,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
118
151
|
// List/Search tenants
|
|
119
152
|
registry.addRoute({
|
|
120
153
|
method: 'get',
|
|
121
|
-
path: apiPrefix,
|
|
154
|
+
path: apiPrefix || '/',
|
|
122
155
|
pluginId: 'tenants',
|
|
123
156
|
handler: async (req: Request, res: Response) => {
|
|
124
157
|
try {
|
|
@@ -140,7 +173,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
140
173
|
|
|
141
174
|
// Users can only search their own tenants
|
|
142
175
|
// Get all tenants user belongs to
|
|
143
|
-
const userTenants = await
|
|
176
|
+
const userTenants = await store.getTenantsForUser(user.id);
|
|
144
177
|
res.json({
|
|
145
178
|
tenants: userTenants,
|
|
146
179
|
total: userTenants.length,
|
|
@@ -186,7 +219,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
186
219
|
});
|
|
187
220
|
}
|
|
188
221
|
|
|
189
|
-
const tenant = await
|
|
222
|
+
const tenant = await store.getById(req.params.id);
|
|
190
223
|
if (!tenant) {
|
|
191
224
|
return res.status(404).json({ error: 'Tenant not found' });
|
|
192
225
|
}
|
|
@@ -201,7 +234,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
201
234
|
// Create tenant
|
|
202
235
|
registry.addRoute({
|
|
203
236
|
method: 'post',
|
|
204
|
-
path: apiPrefix,
|
|
237
|
+
path: apiPrefix || '/',
|
|
205
238
|
pluginId: 'tenants',
|
|
206
239
|
handler: async (req: Request, res: Response) => {
|
|
207
240
|
try {
|
|
@@ -252,10 +285,10 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
252
285
|
});
|
|
253
286
|
}
|
|
254
287
|
|
|
255
|
-
const tenant = await
|
|
288
|
+
const tenant = await store.create(input);
|
|
256
289
|
|
|
257
290
|
// Automatically add creator as owner
|
|
258
|
-
await
|
|
291
|
+
await store.addMember({
|
|
259
292
|
tenant_id: tenant.id,
|
|
260
293
|
user_id: user.id,
|
|
261
294
|
role: 'owner',
|
|
@@ -306,7 +339,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
306
339
|
metadata: req.body.metadata,
|
|
307
340
|
};
|
|
308
341
|
|
|
309
|
-
const tenant = await
|
|
342
|
+
const tenant = await store.update(req.params.id, input);
|
|
310
343
|
if (!tenant) {
|
|
311
344
|
return res.status(404).json({ error: 'Tenant not found' });
|
|
312
345
|
}
|
|
@@ -351,7 +384,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
351
384
|
});
|
|
352
385
|
}
|
|
353
386
|
|
|
354
|
-
const deleted = await
|
|
387
|
+
const deleted = await store.delete(req.params.id);
|
|
355
388
|
if (!deleted) {
|
|
356
389
|
return res.status(404).json({ error: 'Tenant not found' });
|
|
357
390
|
}
|
|
@@ -400,7 +433,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
400
433
|
});
|
|
401
434
|
}
|
|
402
435
|
|
|
403
|
-
const tenants = await
|
|
436
|
+
const tenants = await store.getTenantsForUser(req.params.userId);
|
|
404
437
|
res.json({ tenants, total: tenants.length });
|
|
405
438
|
} catch (error) {
|
|
406
439
|
console.error('[TenantsPlugin] Get user tenants error:', error);
|
|
@@ -444,7 +477,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
444
477
|
});
|
|
445
478
|
}
|
|
446
479
|
|
|
447
|
-
const members = await
|
|
480
|
+
const members = await store.getMembers(req.params.tenantId);
|
|
448
481
|
res.json({ members, total: members.length });
|
|
449
482
|
} catch (error) {
|
|
450
483
|
console.error('[TenantsPlugin] Get members error:', error);
|
|
@@ -506,7 +539,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
506
539
|
});
|
|
507
540
|
}
|
|
508
541
|
|
|
509
|
-
const membership = await
|
|
542
|
+
const membership = await store.addMember(input);
|
|
510
543
|
log('Member added to tenant', {
|
|
511
544
|
tenantId: input.tenant_id,
|
|
512
545
|
userId: input.user_id,
|
|
@@ -568,7 +601,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
568
601
|
});
|
|
569
602
|
}
|
|
570
603
|
|
|
571
|
-
const membership = await
|
|
604
|
+
const membership = await store.updateMember(req.params.tenantId, req.params.userId, input);
|
|
572
605
|
if (!membership) {
|
|
573
606
|
return res.status(404).json({ error: 'Membership not found' });
|
|
574
607
|
}
|
|
@@ -618,7 +651,7 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
618
651
|
});
|
|
619
652
|
}
|
|
620
653
|
|
|
621
|
-
const deleted = await
|
|
654
|
+
const deleted = await store.removeMember(req.params.tenantId, req.params.userId);
|
|
622
655
|
if (!deleted) {
|
|
623
656
|
return res.status(404).json({ error: 'Membership not found' });
|
|
624
657
|
}
|
|
@@ -641,7 +674,9 @@ export function createTenantsPlugin(config: TenantsPluginConfig): Plugin {
|
|
|
641
674
|
|
|
642
675
|
async onStop(): Promise<void> {
|
|
643
676
|
log('Stopping tenants plugin');
|
|
644
|
-
|
|
677
|
+
if (currentStore) {
|
|
678
|
+
await currentStore.shutdown();
|
|
679
|
+
}
|
|
645
680
|
currentStore = null;
|
|
646
681
|
currentRegistry = null;
|
|
647
682
|
},
|
|
@@ -236,10 +236,16 @@ export interface PostgresTenantStoreConfig {
|
|
|
236
236
|
|
|
237
237
|
/**
|
|
238
238
|
* Tenants plugin configuration
|
|
239
|
+
*
|
|
240
|
+
* All properties are optional - plugin will use smart defaults:
|
|
241
|
+
* - store: Postgres tenant store using registry's postgres instance
|
|
242
|
+
* - apiPrefix: '/tenants'
|
|
243
|
+
* - apiEnabled: true
|
|
244
|
+
* - debug: false
|
|
239
245
|
*/
|
|
240
246
|
export interface TenantsPluginConfig {
|
|
241
|
-
/** Tenant store implementation */
|
|
242
|
-
store
|
|
247
|
+
/** Tenant store implementation (default: postgres tenant store from registry) */
|
|
248
|
+
store?: TenantStore;
|
|
243
249
|
/** API route prefix (default: '/tenants') */
|
|
244
250
|
apiPrefix?: string;
|
|
245
251
|
/** Enable API endpoints (default: true) */
|
|
@@ -38,8 +38,6 @@ export type {
|
|
|
38
38
|
// Stores
|
|
39
39
|
export { postgresUsageStore } from './stores/index.js';
|
|
40
40
|
|
|
41
|
-
// UI Components
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
export { UsageManagementPage } from './UsageManagementPage.js';
|
|
45
|
-
export type { UsageManagementPageProps } from './UsageManagementPage.js';
|
|
41
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
42
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
43
|
+
|