@qwickapps/server 1.7.1 → 1.8.0
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 +46 -0
- package/dist/src/core/control-panel.js +5 -5
- 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 +117 -15
- package/dist/src/core/gateway.js.map +1 -1
- package/dist/src/core/plugin-registry.d.ts +70 -0
- package/dist/src/core/plugin-registry.d.ts.map +1 -1
- package/dist/src/core/plugin-registry.js +94 -0
- package/dist/src/core/plugin-registry.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.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 +113 -19
- package/dist/src/plugins/api-keys/api-keys-plugin.js.map +1 -1
- package/dist/src/plugins/api-keys/index.d.ts +1 -5
- 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 +83 -65
- package/dist/src/plugins/api-keys/stores/postgres-store.js.map +1 -1
- package/dist/src/plugins/api-keys/types.d.ts +22 -4
- 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/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 +0 -4
- package/dist/src/plugins/bans/index.d.ts.map +1 -1
- package/dist/src/plugins/bans/index.js +2 -3
- package/dist/src/plugins/bans/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/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/diagnostics-plugin.d.ts.map +1 -1
- package/dist/src/plugins/diagnostics-plugin.js +73 -0
- package/dist/src/plugins/diagnostics-plugin.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 +0 -4
- package/dist/src/plugins/entitlements/index.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/index.js +2 -3
- package/dist/src/plugins/entitlements/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/index.d.ts +1 -1
- package/dist/src/plugins/index.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/SeedExecutor.d.ts +2 -0
- package/dist/src/plugins/maintenance/SeedExecutor.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/SeedExecutor.js +6 -2
- package/dist/src/plugins/maintenance/SeedExecutor.js.map +1 -1
- package/dist/src/plugins/maintenance/SeedList.d.ts +2 -2
- package/dist/src/plugins/maintenance/SeedList.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/SeedList.js +39 -14
- package/dist/src/plugins/maintenance/SeedList.js.map +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.d.ts +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.js +9 -5
- package/dist/src/plugins/maintenance/SeedManagementPage.js.map +1 -1
- package/dist/src/plugins/maintenance/seed-executor.d.ts +6 -4
- package/dist/src/plugins/maintenance/seed-executor.d.ts.map +1 -1
- package/dist/src/plugins/maintenance/seed-executor.js +53 -17
- package/dist/src/plugins/maintenance/seed-executor.js.map +1 -1
- package/dist/src/plugins/maintenance-plugin.d.ts +24 -0
- package/dist/src/plugins/maintenance-plugin.d.ts.map +1 -1
- package/dist/src/plugins/maintenance-plugin.js +222 -34
- 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 +45 -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 +12 -0
- package/dist/src/plugins/postgres-plugin.d.ts.map +1 -1
- package/dist/src/plugins/postgres-plugin.js +319 -5
- 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/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 +91 -58
- 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/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/components/ControlPanelApp.d.ts.map +1 -1
- package/dist/ui/src/components/ControlPanelApp.js +4 -3
- package/dist/ui/src/components/ControlPanelApp.js.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.d.ts.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.js +3 -1
- package/dist/ui/src/dashboard/builtInWidgets.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js +17 -4
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js +5 -1
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js +4 -2
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.d.ts +12 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js +174 -0
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js +6 -3
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts +1 -1
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js +256 -16
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/index.d.ts +1 -0
- package/dist/ui/src/dashboard/widgets/index.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/index.js +1 -0
- package/dist/ui/src/dashboard/widgets/index.js.map +1 -1
- package/dist-ui/assets/index-BkGp7ZKd.js +529 -0
- package/dist-ui/assets/index-BkGp7ZKd.js.map +1 -0
- package/dist-ui/index.html +1 -1
- package/dist-ui-lib/index.js +3735 -3187
- package/dist-ui-lib/index.js.map +1 -1
- package/dist-ui-lib/src/dashboard/widgets/DatabaseOperationsWidget.d.ts +11 -0
- package/dist-ui-lib/src/dashboard/widgets/SeedManagementWidget.d.ts +1 -1
- package/dist-ui-lib/src/dashboard/widgets/index.d.ts +1 -0
- package/package.json +8 -5
- package/src/core/control-panel.ts +5 -5
- package/src/core/gateway.ts +135 -15
- package/src/core/plugin-registry.ts +171 -0
- package/src/index.ts +2 -0
- package/src/plugins/api-keys/api-keys-plugin.ts +121 -20
- package/src/plugins/api-keys/index.ts +3 -5
- package/src/plugins/api-keys/stores/postgres-store.ts +90 -67
- package/src/plugins/api-keys/types.ts +23 -4
- package/src/plugins/auth/index.ts +3 -5
- package/src/plugins/bans/bans-plugin.ts +71 -26
- package/src/plugins/bans/index.ts +3 -5
- package/src/plugins/bans/types.ts +13 -6
- package/src/plugins/devices/devices-plugin.ts +62 -27
- package/src/plugins/devices/index.ts +3 -5
- package/src/plugins/diagnostics-plugin.ts +77 -0
- package/src/plugins/entitlements/entitlements-plugin.ts +81 -43
- package/src/plugins/entitlements/index.ts +3 -5
- package/src/plugins/entitlements/types.ts +9 -2
- package/src/plugins/index.ts +1 -1
- package/src/plugins/maintenance/SeedExecutor.tsx +9 -1
- package/src/plugins/maintenance/SeedList.tsx +85 -38
- package/src/plugins/maintenance/SeedManagementPage.tsx +10 -4
- package/src/plugins/maintenance/seed-executor.ts +56 -17
- package/src/plugins/maintenance-plugin.ts +267 -36
- package/src/plugins/notifications/index.ts +3 -5
- package/src/plugins/notifications/notifications-plugin.ts +48 -19
- package/src/plugins/parental/index.ts +3 -5
- package/src/plugins/parental/parental-plugin.ts +63 -25
- package/src/plugins/postgres-plugin.ts +410 -5
- 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/tenants-plugin.ts +95 -60
- 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/types.ts +7 -2
- package/src/plugins/users/users-plugin.ts +56 -24
- package/ui/src/App.tsx +3 -3
- package/ui/src/components/ControlPanelApp.tsx +4 -3
- package/ui/src/dashboard/builtInWidgets.tsx +3 -0
- package/ui/src/dashboard/widgets/CMSMaintenanceWidget.tsx +17 -4
- package/ui/src/dashboard/widgets/CMSStatusWidget.tsx +5 -1
- package/ui/src/dashboard/widgets/CacheMaintenanceWidget.tsx +4 -2
- package/ui/src/dashboard/widgets/DatabaseOperationsWidget.tsx +410 -0
- package/ui/src/dashboard/widgets/LogsMaintenanceWidget.tsx +6 -3
- package/ui/src/dashboard/widgets/SeedManagementWidget.tsx +533 -49
- package/ui/src/dashboard/widgets/index.ts +1 -0
- package/dist-ui/assets/index-8y0jDGcd.js +0 -528
- package/dist-ui/assets/index-8y0jDGcd.js.map +0 -1
|
@@ -17,6 +17,8 @@ import type {
|
|
|
17
17
|
UsageStatus,
|
|
18
18
|
UsageSummary,
|
|
19
19
|
} from './types.js';
|
|
20
|
+
import { hasPostgres, getPostgres } from '../postgres-plugin.js';
|
|
21
|
+
import { postgresUsageStore } from './stores/index.js';
|
|
20
22
|
|
|
21
23
|
// Import subscription helpers if available
|
|
22
24
|
let getFeatureLimitFn: ((userId: string, featureCode: string) => Promise<number | null>) | null = null;
|
|
@@ -44,15 +46,18 @@ function getTomorrowMidnight(): Date {
|
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
/**
|
|
47
|
-
* Create the Usage plugin
|
|
49
|
+
* Create the Usage plugin with smart defaults
|
|
50
|
+
*
|
|
51
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
52
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
48
53
|
*/
|
|
49
|
-
export function createUsagePlugin(config: UsagePluginConfig): Plugin {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (debug) {
|
|
55
|
-
console.log(
|
|
54
|
+
export function createUsagePlugin(config: Partial<UsagePluginConfig> = {}): Plugin {
|
|
55
|
+
function log(message: string, data?: Record<string, unknown>, isError = false) {
|
|
56
|
+
const prefix = '[UsagePlugin]';
|
|
57
|
+
if (isError) {
|
|
58
|
+
console.error(`${prefix} ${message}`, data || '');
|
|
59
|
+
} else if (config.debug) {
|
|
60
|
+
console.log(`${prefix} ${message}`, data || '');
|
|
56
61
|
}
|
|
57
62
|
}
|
|
58
63
|
|
|
@@ -62,15 +67,48 @@ export function createUsagePlugin(config: UsagePluginConfig): Plugin {
|
|
|
62
67
|
version: '1.0.0',
|
|
63
68
|
|
|
64
69
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
70
|
+
const logger = registry.getLogger('usage');
|
|
71
|
+
|
|
72
|
+
// Check for postgres in registry
|
|
73
|
+
if (!hasPostgres()) {
|
|
74
|
+
logger.warn('No Database! Usage plugin disabled.');
|
|
75
|
+
registry.registerHealthCheck({
|
|
76
|
+
name: 'usage-store',
|
|
77
|
+
type: 'custom',
|
|
78
|
+
check: async () => ({
|
|
79
|
+
healthy: false,
|
|
80
|
+
details: {
|
|
81
|
+
error: 'PostgreSQL not available',
|
|
82
|
+
state: 'disabled',
|
|
83
|
+
},
|
|
84
|
+
}),
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Smart defaults - get dependencies from registry
|
|
90
|
+
const store = config.store ?? postgresUsageStore({
|
|
91
|
+
pool: () => getPostgres().getPool(),
|
|
92
|
+
autoCreateTables: true,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const debug = config.debug ?? false;
|
|
96
|
+
const apiPrefix = config.api?.prefix ?? '/'; // Framework adds /usage prefix automatically
|
|
97
|
+
const apiEnabled = config.api?.enabled ?? true;
|
|
98
|
+
const dailyRetentionDays = config.cleanup?.dailyRetentionDays ?? 90;
|
|
99
|
+
const monthlyRetentionMonths = config.cleanup?.monthlyRetentionMonths ?? 24;
|
|
100
|
+
const runOnStartup = config.cleanup?.runOnStartup ?? false;
|
|
101
|
+
const cleanupIntervalHours = config.cleanup?.cleanupIntervalHours ?? 0;
|
|
102
|
+
|
|
65
103
|
log('Starting usage plugin');
|
|
66
104
|
|
|
67
105
|
// Initialize the store (creates tables if needed)
|
|
68
|
-
await
|
|
106
|
+
await store.initialize();
|
|
69
107
|
log('Usage plugin migrations complete');
|
|
70
108
|
|
|
71
109
|
// Store references for helper access
|
|
72
|
-
currentStore =
|
|
73
|
-
currentConfig = config;
|
|
110
|
+
currentStore = store;
|
|
111
|
+
currentConfig = { ...config, store, debug };
|
|
74
112
|
|
|
75
113
|
// Try to get the feature limit function from subscriptions plugin
|
|
76
114
|
try {
|
|
@@ -96,25 +134,19 @@ export function createUsagePlugin(config: UsagePluginConfig): Plugin {
|
|
|
96
134
|
});
|
|
97
135
|
|
|
98
136
|
// Run cleanup on startup if configured
|
|
99
|
-
if (
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
const dailyDeleted = await config.store.cleanupOldDaily(dailyDays);
|
|
104
|
-
const monthlyDeleted = await config.store.cleanupOldMonthly(monthlyMonths);
|
|
137
|
+
if (runOnStartup) {
|
|
138
|
+
const dailyDeleted = await store.cleanupOldDaily(dailyRetentionDays);
|
|
139
|
+
const monthlyDeleted = await store.cleanupOldMonthly(monthlyRetentionMonths);
|
|
105
140
|
log('Startup cleanup complete', { dailyDeleted, monthlyDeleted });
|
|
106
141
|
}
|
|
107
142
|
|
|
108
143
|
// Set up periodic cleanup if configured
|
|
109
|
-
if (
|
|
110
|
-
const intervalMs =
|
|
144
|
+
if (cleanupIntervalHours > 0) {
|
|
145
|
+
const intervalMs = cleanupIntervalHours * 60 * 60 * 1000;
|
|
111
146
|
cleanupIntervalId = setInterval(async () => {
|
|
112
147
|
try {
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
const dailyDeleted = await config.store.cleanupOldDaily(dailyDays);
|
|
117
|
-
const monthlyDeleted = await config.store.cleanupOldMonthly(monthlyMonths);
|
|
148
|
+
const dailyDeleted = await store.cleanupOldDaily(dailyRetentionDays);
|
|
149
|
+
const monthlyDeleted = await store.cleanupOldMonthly(monthlyRetentionMonths);
|
|
118
150
|
log('Periodic cleanup complete', { dailyDeleted, monthlyDeleted });
|
|
119
151
|
} catch (error) {
|
|
120
152
|
console.error('[UsagePlugin] Cleanup error:', error);
|
|
@@ -123,7 +155,7 @@ export function createUsagePlugin(config: UsagePluginConfig): Plugin {
|
|
|
123
155
|
}
|
|
124
156
|
|
|
125
157
|
// Add API routes if enabled
|
|
126
|
-
if (
|
|
158
|
+
if (apiEnabled) {
|
|
127
159
|
// Get daily usage summary
|
|
128
160
|
registry.addRoute({
|
|
129
161
|
method: 'get',
|
|
@@ -211,7 +243,9 @@ export function createUsagePlugin(config: UsagePluginConfig): Plugin {
|
|
|
211
243
|
cleanupIntervalId = null;
|
|
212
244
|
}
|
|
213
245
|
|
|
214
|
-
|
|
246
|
+
if (currentStore) {
|
|
247
|
+
await currentStore.shutdown();
|
|
248
|
+
}
|
|
215
249
|
currentStore = null;
|
|
216
250
|
currentConfig = null;
|
|
217
251
|
log('Usage plugin stopped');
|
|
@@ -269,10 +269,15 @@ export interface UsersUiConfig {
|
|
|
269
269
|
|
|
270
270
|
/**
|
|
271
271
|
* Users plugin configuration
|
|
272
|
+
*
|
|
273
|
+
* All properties are optional - plugin will use smart defaults:
|
|
274
|
+
* - store: Postgres user store using registry's postgres instance
|
|
275
|
+
* - api.prefix: '/users'
|
|
276
|
+
* - debug: false
|
|
272
277
|
*/
|
|
273
278
|
export interface UsersPluginConfig {
|
|
274
|
-
/** User storage backend */
|
|
275
|
-
store
|
|
279
|
+
/** User storage backend (default: postgres user store from registry) */
|
|
280
|
+
store?: UserStore;
|
|
276
281
|
/** Sync configuration (optional) */
|
|
277
282
|
sync?: UserSyncConfig;
|
|
278
283
|
/** API configuration */
|
|
@@ -30,22 +30,26 @@ import { getEntitlements } from '../entitlements/entitlements-plugin.js';
|
|
|
30
30
|
import { getPreferences } from '../preferences/preferences-plugin.js';
|
|
31
31
|
import { getActiveBan } from '../bans/bans-plugin.js';
|
|
32
32
|
import { autoCreateUserTenant } from '../tenants/index.js';
|
|
33
|
+
import { hasPostgres, getPostgres } from '../postgres-plugin.js';
|
|
34
|
+
import { postgresUserStore } from './stores/index.js';
|
|
33
35
|
|
|
34
36
|
// Store instance for helper access
|
|
35
37
|
let currentStore: UserStore | null = null;
|
|
36
38
|
let currentRegistry: PluginRegistry | null = null;
|
|
37
39
|
|
|
38
40
|
/**
|
|
39
|
-
* Create the Users plugin
|
|
41
|
+
* Create the Users 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.
|
|
40
45
|
*/
|
|
41
|
-
export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
console.log(`[UsersPlugin] ${message}`, data || '');
|
|
46
|
+
export function createUsersPlugin(config: Partial<UsersPluginConfig> = {}): Plugin {
|
|
47
|
+
function log(message: string, data?: Record<string, unknown>, isError = false) {
|
|
48
|
+
const prefix = '[UsersPlugin]';
|
|
49
|
+
if (isError) {
|
|
50
|
+
console.error(`${prefix} ${message}`, data || '');
|
|
51
|
+
} else if (config.debug) {
|
|
52
|
+
console.log(`${prefix} ${message}`, data || '');
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -55,14 +59,42 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
55
59
|
version: '1.0.0',
|
|
56
60
|
|
|
57
61
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
62
|
+
const logger = registry.getLogger('users');
|
|
63
|
+
|
|
64
|
+
// Check for postgres in registry
|
|
65
|
+
if (!hasPostgres()) {
|
|
66
|
+
logger.warn('No Database! Users plugin disabled.');
|
|
67
|
+
registry.registerHealthCheck({
|
|
68
|
+
name: 'users-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 ?? postgresUserStore({
|
|
83
|
+
pool: () => getPostgres().getPool(),
|
|
84
|
+
autoCreateTables: true,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const debug = config.debug ?? false;
|
|
88
|
+
const apiPrefix = config.api?.prefix ?? '/users';
|
|
89
|
+
|
|
58
90
|
log('Starting users plugin');
|
|
59
91
|
|
|
60
92
|
// Initialize the store (creates tables if needed)
|
|
61
|
-
await
|
|
93
|
+
await store.initialize();
|
|
62
94
|
log('Users plugin migrations complete');
|
|
63
95
|
|
|
64
96
|
// Store references for helper access
|
|
65
|
-
currentStore =
|
|
97
|
+
currentStore = store;
|
|
66
98
|
currentRegistry = registry;
|
|
67
99
|
|
|
68
100
|
// Register health check
|
|
@@ -72,7 +104,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
72
104
|
check: async () => {
|
|
73
105
|
try {
|
|
74
106
|
// Simple health check - try to search with limit 1
|
|
75
|
-
await
|
|
107
|
+
await store.search({ limit: 1 });
|
|
76
108
|
return { healthy: true };
|
|
77
109
|
} catch {
|
|
78
110
|
return { healthy: false };
|
|
@@ -98,7 +130,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
98
130
|
sortOrder: (req.query.sortOrder as UserSearchParams['sortOrder']) || 'desc',
|
|
99
131
|
};
|
|
100
132
|
|
|
101
|
-
const result = await
|
|
133
|
+
const result = await store.search(params);
|
|
102
134
|
res.json(result);
|
|
103
135
|
} catch (error) {
|
|
104
136
|
console.error('[UsersPlugin] Search error:', error);
|
|
@@ -114,7 +146,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
114
146
|
pluginId: 'users',
|
|
115
147
|
handler: async (req: Request, res: Response) => {
|
|
116
148
|
try {
|
|
117
|
-
const user = await
|
|
149
|
+
const user = await store.getById(req.params.id);
|
|
118
150
|
if (!user) {
|
|
119
151
|
return res.status(404).json({ error: 'User not found' });
|
|
120
152
|
}
|
|
@@ -147,12 +179,12 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
147
179
|
}
|
|
148
180
|
|
|
149
181
|
// Check if user already exists
|
|
150
|
-
const existing = await
|
|
182
|
+
const existing = await store.getByEmail(input.email);
|
|
151
183
|
if (existing) {
|
|
152
184
|
return res.status(409).json({ error: 'User with this email already exists' });
|
|
153
185
|
}
|
|
154
186
|
|
|
155
|
-
const user = await
|
|
187
|
+
const user = await store.create(input);
|
|
156
188
|
|
|
157
189
|
// Auto-create personal tenant if tenants plugin available
|
|
158
190
|
if (registry.hasPlugin('tenants')) {
|
|
@@ -187,7 +219,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
187
219
|
}
|
|
188
220
|
|
|
189
221
|
// Check if user already exists
|
|
190
|
-
const existing = await
|
|
222
|
+
const existing = await store.getByEmail(email);
|
|
191
223
|
if (existing) {
|
|
192
224
|
return res.status(409).json({ error: 'User with this email already exists' });
|
|
193
225
|
}
|
|
@@ -200,7 +232,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
200
232
|
expiresAt.setDate(expiresAt.getDate() + 7);
|
|
201
233
|
|
|
202
234
|
// Create user with invited status
|
|
203
|
-
const user = await
|
|
235
|
+
const user = await store.create({
|
|
204
236
|
email,
|
|
205
237
|
name,
|
|
206
238
|
metadata: role ? { role } : undefined,
|
|
@@ -251,7 +283,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
251
283
|
return res.status(400).json({ error: 'Invitation token is required' });
|
|
252
284
|
}
|
|
253
285
|
|
|
254
|
-
const user = await
|
|
286
|
+
const user = await store.acceptInvitation(token);
|
|
255
287
|
|
|
256
288
|
if (!user) {
|
|
257
289
|
return res.status(404).json({
|
|
@@ -290,7 +322,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
290
322
|
metadata: req.body.metadata,
|
|
291
323
|
};
|
|
292
324
|
|
|
293
|
-
const user = await
|
|
325
|
+
const user = await store.update(req.params.id, input);
|
|
294
326
|
if (!user) {
|
|
295
327
|
return res.status(404).json({ error: 'User not found' });
|
|
296
328
|
}
|
|
@@ -309,7 +341,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
309
341
|
pluginId: 'users',
|
|
310
342
|
handler: async (req: Request, res: Response) => {
|
|
311
343
|
try {
|
|
312
|
-
const deleted = await
|
|
344
|
+
const deleted = await store.delete(req.params.id);
|
|
313
345
|
if (!deleted) {
|
|
314
346
|
return res.status(404).json({ error: 'User not found' });
|
|
315
347
|
}
|
|
@@ -328,7 +360,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
328
360
|
pluginId: 'users',
|
|
329
361
|
handler: async (req: Request, res: Response) => {
|
|
330
362
|
try {
|
|
331
|
-
const user = await
|
|
363
|
+
const user = await store.getById(req.params.id);
|
|
332
364
|
if (!user) {
|
|
333
365
|
return res.status(404).json({ error: 'User not found' });
|
|
334
366
|
}
|
|
@@ -391,7 +423,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
391
423
|
pluginId: 'users',
|
|
392
424
|
handler: async (_req: Request, res: Response) => {
|
|
393
425
|
try {
|
|
394
|
-
const allUsers = await
|
|
426
|
+
const allUsers = await store.search({ limit: 10000 });
|
|
395
427
|
const now = Date.now();
|
|
396
428
|
const sevenDaysAgo = now - 7 * 24 * 60 * 60 * 1000;
|
|
397
429
|
|
|
@@ -419,7 +451,7 @@ export function createUsersPlugin(config: UsersPluginConfig): Plugin {
|
|
|
419
451
|
|
|
420
452
|
async onStop(): Promise<void> {
|
|
421
453
|
log('Stopping users plugin');
|
|
422
|
-
await
|
|
454
|
+
if (currentStore) { await currentStore.shutdown(); };
|
|
423
455
|
currentStore = null;
|
|
424
456
|
currentRegistry = null;
|
|
425
457
|
log('Users plugin stopped');
|
package/ui/src/App.tsx
CHANGED
|
@@ -71,9 +71,9 @@ declare global {
|
|
|
71
71
|
*/
|
|
72
72
|
const basePath = window.__APP_BASE_PATH__ ?? '';
|
|
73
73
|
|
|
74
|
-
// API
|
|
75
|
-
//
|
|
76
|
-
api.setBaseUrl(
|
|
74
|
+
// Set API base URL to match the control panel mount path
|
|
75
|
+
// When proxied through a gateway at /cpanel, API calls need to go to /cpanel/api
|
|
76
|
+
api.setBaseUrl(basePath);
|
|
77
77
|
|
|
78
78
|
// Footer content with QwickApps Server branding
|
|
79
79
|
const footerContent = (
|
|
@@ -147,9 +147,10 @@ export function ControlPanelApp({
|
|
|
147
147
|
// Combine built-in widget components with custom ones
|
|
148
148
|
const allWidgetComponents = [...getBuiltInWidgetComponents(), ...widgetComponents];
|
|
149
149
|
|
|
150
|
-
// Configure API base URL -
|
|
151
|
-
//
|
|
152
|
-
|
|
150
|
+
// Configure API base URL - read from injected __APP_BASE_PATH__ if available
|
|
151
|
+
// Server injects window.__APP_BASE_PATH__ when control panel is mounted at a base path
|
|
152
|
+
// Example: mounted at /cpanel → API calls go to /cpanel/api (not /api)
|
|
153
|
+
const apiBasePath = (window as any).__APP_BASE_PATH__ || '';
|
|
153
154
|
api.setBaseUrl(apiBasePath);
|
|
154
155
|
|
|
155
156
|
// Fetch version from API
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
ServiceControlWidget,
|
|
22
22
|
EnvironmentConfigWidget,
|
|
23
23
|
DatabaseOpsWidget,
|
|
24
|
+
DatabaseOperationsWidget,
|
|
24
25
|
LogsMaintenanceWidget,
|
|
25
26
|
CacheMaintenanceWidget,
|
|
26
27
|
} from './widgets';
|
|
@@ -42,6 +43,7 @@ export const builtInWidgetComponents: Record<string, React.ComponentType> = {
|
|
|
42
43
|
ServiceControlWidget: ServiceControlWidget,
|
|
43
44
|
EnvironmentConfigWidget: EnvironmentConfigWidget,
|
|
44
45
|
DatabaseOpsWidget: DatabaseOpsWidget,
|
|
46
|
+
DatabaseOperationsWidget: DatabaseOperationsWidget,
|
|
45
47
|
LogsMaintenanceWidget: LogsMaintenanceWidget,
|
|
46
48
|
CacheMaintenanceWidget: CacheMaintenanceWidget,
|
|
47
49
|
PreferencesPage: PreferencesPage,
|
|
@@ -66,6 +68,7 @@ export function getBuiltInWidgetComponents(): WidgetComponent[] {
|
|
|
66
68
|
{ name: 'ServiceControlWidget', component: ServiceControlWidget },
|
|
67
69
|
{ name: 'EnvironmentConfigWidget', component: EnvironmentConfigWidget },
|
|
68
70
|
{ name: 'DatabaseOpsWidget', component: DatabaseOpsWidget },
|
|
71
|
+
{ name: 'DatabaseOperationsWidget', component: DatabaseOperationsWidget },
|
|
69
72
|
{ name: 'LogsMaintenanceWidget', component: LogsMaintenanceWidget },
|
|
70
73
|
{ name: 'CacheMaintenanceWidget', component: CacheMaintenanceWidget },
|
|
71
74
|
{ name: 'PreferencesPage', component: PreferencesPage },
|
|
@@ -49,7 +49,11 @@ export function CMSMaintenanceWidget() {
|
|
|
49
49
|
|
|
50
50
|
const fetchStatus = async () => {
|
|
51
51
|
try {
|
|
52
|
-
const
|
|
52
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
53
|
+
const response = await fetch(`${basePath}/api/cms/status`);
|
|
54
|
+
if (!response.ok) {
|
|
55
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
56
|
+
}
|
|
53
57
|
const data = await response.json();
|
|
54
58
|
setStatus(data);
|
|
55
59
|
} catch (err) {
|
|
@@ -59,7 +63,11 @@ export function CMSMaintenanceWidget() {
|
|
|
59
63
|
|
|
60
64
|
const fetchSeeds = async () => {
|
|
61
65
|
try {
|
|
62
|
-
const
|
|
66
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
67
|
+
const response = await fetch(`${basePath}/api/cms/seeds`);
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
70
|
+
}
|
|
63
71
|
const data = await response.json();
|
|
64
72
|
setSeeds(data.seeds || []);
|
|
65
73
|
} catch (err) {
|
|
@@ -80,7 +88,8 @@ export function CMSMaintenanceWidget() {
|
|
|
80
88
|
setError(null);
|
|
81
89
|
setSuccess(null);
|
|
82
90
|
try {
|
|
83
|
-
const
|
|
91
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
92
|
+
const response = await fetch(`${basePath}/api/cms/restart`, { method: 'POST' });
|
|
84
93
|
const data = await response.json();
|
|
85
94
|
|
|
86
95
|
if (response.ok) {
|
|
@@ -100,9 +109,13 @@ export function CMSMaintenanceWidget() {
|
|
|
100
109
|
setSuccess(null);
|
|
101
110
|
|
|
102
111
|
try {
|
|
103
|
-
const
|
|
112
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
113
|
+
const response = await fetch(`${basePath}/api/cms/seeds/${seedName}/execute`, {
|
|
104
114
|
method: 'POST',
|
|
105
115
|
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
118
|
+
}
|
|
106
119
|
const data = await response.json();
|
|
107
120
|
|
|
108
121
|
if (data.success) {
|
|
@@ -35,7 +35,11 @@ export function CMSStatusWidget() {
|
|
|
35
35
|
|
|
36
36
|
const fetchStatus = async () => {
|
|
37
37
|
try {
|
|
38
|
-
const
|
|
38
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
39
|
+
const response = await fetch(`${basePath}/api/cms/status`);
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
42
|
+
}
|
|
39
43
|
const data = await response.json();
|
|
40
44
|
setStatus(data);
|
|
41
45
|
setError(null);
|
|
@@ -47,7 +47,8 @@ export function CacheMaintenanceWidget() {
|
|
|
47
47
|
setLoading(true);
|
|
48
48
|
setError(null);
|
|
49
49
|
try {
|
|
50
|
-
const
|
|
50
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
51
|
+
const response = await fetch(`${basePath}/api/cache:default/stats`);
|
|
51
52
|
if (!response.ok) {
|
|
52
53
|
if (response.status === 404) {
|
|
53
54
|
throw new Error('Cache plugin not configured');
|
|
@@ -75,7 +76,8 @@ export function CacheMaintenanceWidget() {
|
|
|
75
76
|
setSuccess(null);
|
|
76
77
|
|
|
77
78
|
try {
|
|
78
|
-
const
|
|
79
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
80
|
+
const response = await fetch(`${basePath}/api/cache:default/flush`, {
|
|
79
81
|
method: 'POST',
|
|
80
82
|
headers: { 'Content-Type': 'application/json' },
|
|
81
83
|
});
|