@qwickapps/server 1.8.1 → 1.8.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 +7 -0
- package/dist/src/core/control-panel.d.ts.map +1 -1
- package/dist/src/core/control-panel.js +10 -7
- 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 +46 -47
- package/dist/src/core/gateway.js.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysManagementPage.js +1 -1
- package/dist/src/plugins/api-keys/ApiKeysManagementPage.js.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysStatusWidget.js +1 -1
- package/dist/src/plugins/api-keys/ApiKeysStatusWidget.js.map +1 -1
- package/dist/src/plugins/auth/AuthManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/auth/AuthManagementPage.js +1 -1
- package/dist/src/plugins/auth/AuthManagementPage.js.map +1 -1
- package/dist/src/plugins/auth/AuthStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/auth/AuthStatusWidget.js +1 -1
- package/dist/src/plugins/auth/AuthStatusWidget.js.map +1 -1
- package/dist/src/plugins/auth/auth-plugin.d.ts.map +1 -1
- package/dist/src/plugins/auth/auth-plugin.js +9 -0
- package/dist/src/plugins/auth/auth-plugin.js.map +1 -1
- package/dist/src/plugins/bans/BansManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/bans/BansManagementPage.js +1 -1
- package/dist/src/plugins/bans/BansManagementPage.js.map +1 -1
- package/dist/src/plugins/bans/BansStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/bans/BansStatusWidget.js +1 -1
- package/dist/src/plugins/bans/BansStatusWidget.js.map +1 -1
- package/dist/src/plugins/cache/CacheManagementPage.js +1 -1
- package/dist/src/plugins/cache/CacheManagementPage.js.map +1 -1
- package/dist/src/plugins/cache/CacheStatusWidget.js +1 -1
- package/dist/src/plugins/cache/CacheStatusWidget.js.map +1 -1
- package/dist/src/plugins/devices/DevicesManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/devices/DevicesManagementPage.js +1 -1
- package/dist/src/plugins/devices/DevicesManagementPage.js.map +1 -1
- package/dist/src/plugins/devices/DevicesStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/devices/DevicesStatusWidget.js +1 -1
- package/dist/src/plugins/devices/DevicesStatusWidget.js.map +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsManagementPage.js +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsManagementPage.js.map +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsStatusWidget.js +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsStatusWidget.js.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsManagementPage.js +1 -1
- package/dist/src/plugins/entitlements/EntitlementsManagementPage.js.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsStatusWidget.js +1 -1
- package/dist/src/plugins/entitlements/EntitlementsStatusWidget.js.map +1 -1
- package/dist/src/plugins/health/HealthManagementPage.js +1 -1
- package/dist/src/plugins/health/HealthManagementPage.js.map +1 -1
- package/dist/src/plugins/health/HealthStatusWidget.js +1 -1
- package/dist/src/plugins/health/HealthStatusWidget.js.map +1 -1
- package/dist/src/plugins/logs/LogsManagementPage.js +1 -1
- package/dist/src/plugins/logs/LogsManagementPage.js.map +1 -1
- package/dist/src/plugins/logs/LogsStatusWidget.js +1 -1
- package/dist/src/plugins/logs/LogsStatusWidget.js.map +1 -1
- package/dist/src/plugins/maintenance/MaintenanceManagementPage.js +1 -1
- package/dist/src/plugins/maintenance/MaintenanceManagementPage.js.map +1 -1
- package/dist/src/plugins/maintenance/MaintenanceStatusWidget.js +1 -1
- package/dist/src/plugins/maintenance/MaintenanceStatusWidget.js.map +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.js +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.js.map +1 -1
- package/dist/src/plugins/maintenance/seed-executor.js +2 -2
- package/dist/src/plugins/maintenance/seed-executor.js.map +1 -1
- package/dist/src/plugins/maintenance-plugin.d.ts +2 -0
- package/dist/src/plugins/maintenance-plugin.d.ts.map +1 -1
- package/dist/src/plugins/maintenance-plugin.js +402 -2
- package/dist/src/plugins/maintenance-plugin.js.map +1 -1
- package/dist/src/plugins/notifications/NotificationsManagementPage.js +1 -1
- package/dist/src/plugins/notifications/NotificationsManagementPage.js.map +1 -1
- package/dist/src/plugins/notifications/NotificationsStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/notifications/NotificationsStatusWidget.js +1 -1
- package/dist/src/plugins/notifications/NotificationsStatusWidget.js.map +1 -1
- package/dist/src/plugins/parental/ParentalManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/parental/ParentalManagementPage.js +1 -1
- package/dist/src/plugins/parental/ParentalManagementPage.js.map +1 -1
- package/dist/src/plugins/parental/ParentalStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/parental/ParentalStatusWidget.js +1 -1
- package/dist/src/plugins/parental/ParentalStatusWidget.js.map +1 -1
- package/dist/src/plugins/postgres/PostgresManagementPage.js +1 -1
- package/dist/src/plugins/postgres/PostgresManagementPage.js.map +1 -1
- package/dist/src/plugins/postgres/PostgresStatusWidget.js +1 -1
- package/dist/src/plugins/postgres/PostgresStatusWidget.js.map +1 -1
- package/dist/src/plugins/preferences/PreferencesManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/preferences/PreferencesManagementPage.js +1 -1
- package/dist/src/plugins/preferences/PreferencesManagementPage.js.map +1 -1
- package/dist/src/plugins/preferences/PreferencesStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/preferences/PreferencesStatusWidget.js +1 -1
- package/dist/src/plugins/preferences/PreferencesStatusWidget.js.map +1 -1
- package/dist/src/plugins/profiles/ProfilesManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/profiles/ProfilesManagementPage.js +1 -1
- package/dist/src/plugins/profiles/ProfilesManagementPage.js.map +1 -1
- package/dist/src/plugins/profiles/ProfilesStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/profiles/ProfilesStatusWidget.js +1 -1
- package/dist/src/plugins/profiles/ProfilesStatusWidget.js.map +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainManagementPage.js +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainManagementPage.js.map +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainStatusWidget.js +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainStatusWidget.js.map +1 -1
- package/dist/src/plugins/rate-limit/RateLimitManagementPage.js +1 -1
- package/dist/src/plugins/rate-limit/RateLimitManagementPage.js.map +1 -1
- package/dist/src/plugins/rate-limit/RateLimitStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/rate-limit/RateLimitStatusWidget.js +1 -1
- package/dist/src/plugins/rate-limit/RateLimitStatusWidget.js.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsManagementPage.js +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsManagementPage.js.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsStatusWidget.js +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsStatusWidget.js.map +1 -1
- package/dist/src/plugins/usage/UsageManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/usage/UsageManagementPage.js +1 -1
- package/dist/src/plugins/usage/UsageManagementPage.js.map +1 -1
- package/dist/src/plugins/usage/UsageStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/usage/UsageStatusWidget.js +1 -1
- package/dist/src/plugins/usage/UsageStatusWidget.js.map +1 -1
- package/dist/src/plugins/users/UsersManagementPage.js +1 -1
- package/dist/src/plugins/users/UsersManagementPage.js.map +1 -1
- package/dist/src/plugins/users/UsersStatusWidget.js +1 -1
- package/dist/src/plugins/users/UsersStatusWidget.js.map +1 -1
- package/dist/ui/src/api/clientBuilder.d.ts +3 -3
- package/dist/ui/src/api/clientBuilder.js +5 -5
- package/dist/ui/src/api/clientBuilder.js.map +1 -1
- package/dist/ui/src/api/controlPanelApi.js +19 -19
- package/dist/ui/src/api/controlPanelApi.js.map +1 -1
- package/dist/ui/src/components/ControlPanelApp.d.ts.map +1 -1
- package/dist/ui/src/components/ControlPanelApp.js +5 -4
- 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.js +8 -8
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js +2 -2
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js +4 -4
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js +2 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js +6 -6
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.d.ts +8 -0
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.js +132 -0
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js +6 -6
- 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-D-4HKPkw.js → index-Cez_jyhl.js} +111 -108
- package/dist-ui/assets/{index-D-4HKPkw.js.map → index-Cez_jyhl.js.map} +1 -1
- package/dist-ui/assets/{index-DRG9n0cx.css → index-De-dCl_t.css} +1 -1
- package/dist-ui/index.html +2 -2
- package/dist-ui-lib/index.js +2623 -2441
- package/dist-ui-lib/index.js.map +1 -1
- package/dist-ui-lib/src/api/clientBuilder.d.ts +3 -3
- package/dist-ui-lib/src/dashboard/widgets/MigrationManagementWidget.d.ts +7 -0
- package/dist-ui-lib/src/dashboard/widgets/index.d.ts +1 -0
- package/package.json +27 -26
- package/src/core/control-panel.ts +10 -7
- package/src/core/gateway.ts +48 -51
- package/src/plugins/api-keys/ApiKeysManagementPage.tsx +1 -1
- package/src/plugins/api-keys/ApiKeysStatusWidget.tsx +1 -1
- package/src/plugins/auth/AuthManagementPage.tsx +1 -1
- package/src/plugins/auth/AuthStatusWidget.tsx +1 -1
- package/src/plugins/auth/auth-plugin.ts +9 -0
- package/src/plugins/bans/BansManagementPage.tsx +1 -1
- package/src/plugins/bans/BansStatusWidget.tsx +1 -1
- package/src/plugins/cache/CacheManagementPage.tsx +1 -1
- package/src/plugins/cache/CacheStatusWidget.tsx +1 -1
- package/src/plugins/devices/DevicesManagementPage.tsx +1 -1
- package/src/plugins/devices/DevicesStatusWidget.tsx +1 -1
- package/src/plugins/diagnostics/DiagnosticsManagementPage.tsx +1 -1
- package/src/plugins/diagnostics/DiagnosticsStatusWidget.tsx +1 -1
- package/src/plugins/entitlements/EntitlementsManagementPage.tsx +1 -1
- package/src/plugins/entitlements/EntitlementsStatusWidget.tsx +1 -1
- package/src/plugins/health/HealthManagementPage.tsx +1 -1
- package/src/plugins/health/HealthStatusWidget.tsx +1 -1
- package/src/plugins/logs/LogsManagementPage.tsx +1 -1
- package/src/plugins/logs/LogsStatusWidget.tsx +1 -1
- package/src/plugins/maintenance/MaintenanceManagementPage.tsx +1 -1
- package/src/plugins/maintenance/MaintenanceStatusWidget.tsx +1 -1
- package/src/plugins/maintenance/SeedManagementPage.tsx +1 -1
- package/src/plugins/maintenance/seed-executor.ts +2 -2
- package/src/plugins/maintenance-plugin.ts +474 -2
- package/src/plugins/notifications/NotificationsManagementPage.tsx +1 -1
- package/src/plugins/notifications/NotificationsStatusWidget.tsx +1 -1
- package/src/plugins/parental/ParentalManagementPage.tsx +1 -1
- package/src/plugins/parental/ParentalStatusWidget.tsx +1 -1
- package/src/plugins/postgres/PostgresManagementPage.tsx +1 -1
- package/src/plugins/postgres/PostgresStatusWidget.tsx +1 -1
- package/src/plugins/preferences/PreferencesManagementPage.tsx +1 -1
- package/src/plugins/preferences/PreferencesStatusWidget.tsx +1 -1
- package/src/plugins/profiles/ProfilesManagementPage.tsx +1 -1
- package/src/plugins/profiles/ProfilesStatusWidget.tsx +1 -1
- package/src/plugins/qwickbrain/QwickbrainManagementPage.tsx +1 -1
- package/src/plugins/qwickbrain/QwickbrainStatusWidget.tsx +1 -1
- package/src/plugins/rate-limit/RateLimitManagementPage.tsx +1 -1
- package/src/plugins/rate-limit/RateLimitStatusWidget.tsx +1 -1
- package/src/plugins/subscriptions/SubscriptionsManagementPage.tsx +1 -1
- package/src/plugins/subscriptions/SubscriptionsStatusWidget.tsx +1 -1
- package/src/plugins/usage/UsageManagementPage.tsx +1 -1
- package/src/plugins/usage/UsageStatusWidget.tsx +1 -1
- package/src/plugins/users/UsersManagementPage.tsx +1 -1
- package/src/plugins/users/UsersStatusWidget.tsx +1 -1
- package/ui/src/App.tsx +4 -3
- package/ui/src/api/clientBuilder.ts +5 -5
- package/ui/src/api/controlPanelApi.ts +19 -19
- package/ui/src/components/ControlPanelApp.tsx +5 -4
- package/ui/src/dashboard/builtInWidgets.tsx +3 -0
- package/ui/src/dashboard/widgets/CMSMaintenanceWidget.tsx +8 -8
- package/ui/src/dashboard/widgets/CMSStatusWidget.tsx +2 -2
- package/ui/src/dashboard/widgets/CacheMaintenanceWidget.tsx +4 -4
- package/ui/src/dashboard/widgets/DatabaseOperationsWidget.tsx +2 -1
- package/ui/src/dashboard/widgets/LogsMaintenanceWidget.tsx +6 -6
- package/ui/src/dashboard/widgets/MigrationManagementWidget.tsx +319 -0
- package/ui/src/dashboard/widgets/SeedManagementWidget.tsx +6 -6
- package/ui/src/dashboard/widgets/index.ts +1 -0
- package/ui/src/hooks/useJobStream.ts +2 -2
- package/ui/src/pages/ContentOpsJobsPage.tsx +3 -3
- package/ui/src/pages/PluginPage.tsx +1 -1
- package/ui/src/pages/TenantsManagementPage.tsx +1 -1
|
@@ -59,8 +59,8 @@ function scanSeedScripts(dir: string, basePath: string = dir): any[] {
|
|
|
59
59
|
if (entry.isDirectory()) {
|
|
60
60
|
// Recursively scan subdirectories
|
|
61
61
|
results.push(...scanSeedScripts(fullPath, basePath));
|
|
62
|
-
} else if (entry.isFile() && entry.name.endsWith('.mjs')) {
|
|
63
|
-
// Found a .mjs file
|
|
62
|
+
} else if (entry.isFile() && (entry.name.endsWith('.mjs') || entry.name.endsWith('.ts'))) {
|
|
63
|
+
// Found a .mjs or .ts file
|
|
64
64
|
const stats = statSync(fullPath);
|
|
65
65
|
const relativePath = relative(basePath, fullPath);
|
|
66
66
|
const description = extractDescription(fullPath);
|
|
@@ -132,6 +132,9 @@ export interface MaintenancePluginConfig {
|
|
|
132
132
|
/** Enable database operations (default: true) */
|
|
133
133
|
enableDatabaseOps?: boolean;
|
|
134
134
|
|
|
135
|
+
/** Enable migration management (default: true) */
|
|
136
|
+
enableMigrations?: boolean;
|
|
137
|
+
|
|
135
138
|
/** Custom seed tasks */
|
|
136
139
|
customTasks?: SeedTask[];
|
|
137
140
|
}
|
|
@@ -599,6 +602,462 @@ export function createMaintenancePlugin(config: MaintenancePluginConfig = {}): P
|
|
|
599
602
|
});
|
|
600
603
|
}
|
|
601
604
|
|
|
605
|
+
// Migration Management Routes
|
|
606
|
+
if (config.enableMigrations !== false) {
|
|
607
|
+
// Ensure migration_executions table exists
|
|
608
|
+
registry.addRoute({
|
|
609
|
+
method: 'post',
|
|
610
|
+
path: '/migrations/_init',
|
|
611
|
+
pluginId: 'maintenance',
|
|
612
|
+
handler: async (req: Request, res: Response) => {
|
|
613
|
+
try {
|
|
614
|
+
if (hasPostgres()) {
|
|
615
|
+
const db = getPostgres();
|
|
616
|
+
await db.queryRaw(`
|
|
617
|
+
CREATE TABLE IF NOT EXISTS migration_executions (
|
|
618
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
619
|
+
status TEXT NOT NULL CHECK (status IN ('running', 'completed', 'failed')),
|
|
620
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
621
|
+
completed_at TIMESTAMPTZ,
|
|
622
|
+
exit_code INTEGER,
|
|
623
|
+
output TEXT,
|
|
624
|
+
error TEXT,
|
|
625
|
+
duration_ms INTEGER,
|
|
626
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
627
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
628
|
+
)
|
|
629
|
+
`);
|
|
630
|
+
await db.queryRaw(`
|
|
631
|
+
CREATE INDEX IF NOT EXISTS idx_migration_executions_status
|
|
632
|
+
ON migration_executions(status)
|
|
633
|
+
`);
|
|
634
|
+
await db.queryRaw(`
|
|
635
|
+
CREATE INDEX IF NOT EXISTS idx_migration_executions_started_at
|
|
636
|
+
ON migration_executions(started_at DESC)
|
|
637
|
+
`);
|
|
638
|
+
}
|
|
639
|
+
res.json({ success: true });
|
|
640
|
+
} catch (error) {
|
|
641
|
+
logger.error('Failed to initialize migration_executions table', { error });
|
|
642
|
+
res.status(500).json({ error: error instanceof Error ? error.message : String(error) });
|
|
643
|
+
}
|
|
644
|
+
},
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
// GET /migrations/status - Get migration status
|
|
648
|
+
registry.addRoute({
|
|
649
|
+
method: 'get',
|
|
650
|
+
path: '/migrations/status',
|
|
651
|
+
pluginId: 'maintenance',
|
|
652
|
+
handler: async (req: Request, res: Response) => {
|
|
653
|
+
try {
|
|
654
|
+
// Check if there are any pending migrations by checking Payload
|
|
655
|
+
// This is a simple implementation - just returns basic status
|
|
656
|
+
res.json({
|
|
657
|
+
available: true,
|
|
658
|
+
lastChecked: new Date().toISOString(),
|
|
659
|
+
});
|
|
660
|
+
} catch (error) {
|
|
661
|
+
logger.error('Failed to get migration status', { error });
|
|
662
|
+
res.status(500).json({
|
|
663
|
+
error: error instanceof Error ? error.message : String(error),
|
|
664
|
+
});
|
|
665
|
+
}
|
|
666
|
+
},
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// GET /migrations/execute - Execute Payload migrations with SSE output
|
|
670
|
+
registry.addRoute({
|
|
671
|
+
method: 'get',
|
|
672
|
+
path: '/migrations/execute',
|
|
673
|
+
pluginId: 'maintenance',
|
|
674
|
+
handler: async (req: Request, res: Response) => {
|
|
675
|
+
const MIGRATION_LOCK_ID = 123456789;
|
|
676
|
+
const MIGRATION_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
|
677
|
+
let lockAcquired = false;
|
|
678
|
+
|
|
679
|
+
try {
|
|
680
|
+
// Ensure table exists (lazy initialization)
|
|
681
|
+
if (hasPostgres()) {
|
|
682
|
+
const db = getPostgres();
|
|
683
|
+
try {
|
|
684
|
+
await db.queryRaw(`
|
|
685
|
+
CREATE TABLE IF NOT EXISTS migration_executions (
|
|
686
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
687
|
+
status TEXT NOT NULL CHECK (status IN ('running', 'completed', 'failed')),
|
|
688
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
689
|
+
completed_at TIMESTAMPTZ,
|
|
690
|
+
exit_code INTEGER,
|
|
691
|
+
output TEXT,
|
|
692
|
+
error TEXT,
|
|
693
|
+
duration_ms INTEGER,
|
|
694
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
695
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
696
|
+
)
|
|
697
|
+
`);
|
|
698
|
+
} catch (err) {
|
|
699
|
+
logger.debug('Table initialization check', { err });
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Acquire advisory lock for concurrency control
|
|
704
|
+
if (hasPostgres()) {
|
|
705
|
+
const db = getPostgres();
|
|
706
|
+
const lockResult = await db.queryOne<{ pg_try_advisory_lock: boolean }>(
|
|
707
|
+
'SELECT pg_try_advisory_lock($1) as pg_try_advisory_lock',
|
|
708
|
+
[MIGRATION_LOCK_ID]
|
|
709
|
+
);
|
|
710
|
+
|
|
711
|
+
lockAcquired = lockResult?.pg_try_advisory_lock || false;
|
|
712
|
+
|
|
713
|
+
if (!lockAcquired) {
|
|
714
|
+
return res.status(409).json({
|
|
715
|
+
error: 'Migrations are already running. Please wait for them to complete.',
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Set SSE headers
|
|
721
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
722
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
723
|
+
res.setHeader('Connection', 'keep-alive');
|
|
724
|
+
res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering
|
|
725
|
+
res.setHeader('Content-Encoding', 'identity'); // Disable compression
|
|
726
|
+
res.flushHeaders();
|
|
727
|
+
|
|
728
|
+
// Create execution record in database
|
|
729
|
+
let executionId: string | null = null;
|
|
730
|
+
if (hasPostgres()) {
|
|
731
|
+
const db = getPostgres();
|
|
732
|
+
const result = await db.queryOne<{ id: string }>(
|
|
733
|
+
`INSERT INTO migration_executions (status, started_at)
|
|
734
|
+
VALUES ($1, NOW())
|
|
735
|
+
RETURNING id`,
|
|
736
|
+
['running']
|
|
737
|
+
);
|
|
738
|
+
executionId = result?.id || null;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const startTime = Date.now();
|
|
742
|
+
let exitCode: number | undefined = undefined;
|
|
743
|
+
let output = '';
|
|
744
|
+
let error = '';
|
|
745
|
+
let migrationProcess: any = null;
|
|
746
|
+
let timeoutHandle: NodeJS.Timeout | null = null;
|
|
747
|
+
|
|
748
|
+
// Cleanup function to ensure resources are released
|
|
749
|
+
const cleanup = async (reason: string) => {
|
|
750
|
+
logger.debug(`Migration cleanup: ${reason}`, { executionId });
|
|
751
|
+
|
|
752
|
+
// Clear timeout if set
|
|
753
|
+
if (timeoutHandle) {
|
|
754
|
+
clearTimeout(timeoutHandle);
|
|
755
|
+
timeoutHandle = null;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
// Kill process if still running
|
|
759
|
+
if (migrationProcess && !migrationProcess.killed) {
|
|
760
|
+
migrationProcess.kill('SIGTERM');
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Update execution record if not already completed
|
|
764
|
+
if (hasPostgres() && executionId && exitCode === undefined) {
|
|
765
|
+
const db = getPostgres();
|
|
766
|
+
const duration = Date.now() - startTime;
|
|
767
|
+
await db.query(
|
|
768
|
+
`UPDATE migration_executions
|
|
769
|
+
SET status = $1, completed_at = NOW(),
|
|
770
|
+
error = $2, duration_ms = $3, updated_at = NOW()
|
|
771
|
+
WHERE id = $4 AND status = 'running'`,
|
|
772
|
+
['failed', reason, duration, executionId]
|
|
773
|
+
).catch(err => logger.error('Failed to update execution on cleanup', { err }));
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Release advisory lock
|
|
777
|
+
if (hasPostgres() && lockAcquired) {
|
|
778
|
+
const db = getPostgres();
|
|
779
|
+
await db.query(
|
|
780
|
+
'SELECT pg_advisory_unlock($1)',
|
|
781
|
+
[MIGRATION_LOCK_ID]
|
|
782
|
+
).catch(err => logger.error('Failed to release advisory lock', { err }));
|
|
783
|
+
lockAcquired = false;
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
// Handle client disconnect
|
|
788
|
+
res.on('close', () => {
|
|
789
|
+
cleanup('Client disconnected before completion').catch(err =>
|
|
790
|
+
logger.error('Cleanup failed on disconnect', { err })
|
|
791
|
+
);
|
|
792
|
+
});
|
|
793
|
+
|
|
794
|
+
try {
|
|
795
|
+
// Execute Payload migration command
|
|
796
|
+
const { spawn } = await import('child_process');
|
|
797
|
+
|
|
798
|
+
migrationProcess = spawn('npx', ['payload', 'migrate', '--force-accept-warning'], {
|
|
799
|
+
cwd: process.cwd(),
|
|
800
|
+
env: {
|
|
801
|
+
...process.env,
|
|
802
|
+
CI: 'true', // Force non-interactive mode
|
|
803
|
+
NODE_ENV: 'production', // Disable dev mode prompts
|
|
804
|
+
},
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
// Automatically answer 'y' to any interactive prompts
|
|
808
|
+
if (migrationProcess.stdin) {
|
|
809
|
+
migrationProcess.stdin.write('y\n');
|
|
810
|
+
migrationProcess.stdin.end();
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// Set timeout to prevent hanging migrations
|
|
814
|
+
timeoutHandle = setTimeout(() => {
|
|
815
|
+
logger.warn('Migration timeout - killing process', { executionId, timeout: MIGRATION_TIMEOUT_MS });
|
|
816
|
+
if (migrationProcess && !migrationProcess.killed) {
|
|
817
|
+
migrationProcess.kill('SIGTERM');
|
|
818
|
+
error += '\n[TIMEOUT] Migration exceeded maximum execution time and was terminated.';
|
|
819
|
+
}
|
|
820
|
+
}, MIGRATION_TIMEOUT_MS);
|
|
821
|
+
|
|
822
|
+
// Stream stdout
|
|
823
|
+
migrationProcess.stdout?.on('data', (data: Buffer) => {
|
|
824
|
+
const chunk = data.toString();
|
|
825
|
+
output += chunk;
|
|
826
|
+
if (!res.writableEnded) {
|
|
827
|
+
res.write(`data: ${JSON.stringify({
|
|
828
|
+
type: 'output',
|
|
829
|
+
data: chunk,
|
|
830
|
+
timestamp: new Date().toISOString()
|
|
831
|
+
})}\n\n`);
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
|
|
835
|
+
// Stream stderr
|
|
836
|
+
migrationProcess.stderr?.on('data', (data: Buffer) => {
|
|
837
|
+
const chunk = data.toString();
|
|
838
|
+
error += chunk;
|
|
839
|
+
if (!res.writableEnded) {
|
|
840
|
+
res.write(`data: ${JSON.stringify({
|
|
841
|
+
type: 'error',
|
|
842
|
+
data: chunk,
|
|
843
|
+
timestamp: new Date().toISOString()
|
|
844
|
+
})}\n\n`);
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
// Wait for process to complete
|
|
849
|
+
await new Promise<void>((resolve, reject) => {
|
|
850
|
+
migrationProcess.on('close', (code: number | null) => {
|
|
851
|
+
exitCode = code || 0;
|
|
852
|
+
resolve();
|
|
853
|
+
});
|
|
854
|
+
migrationProcess.on('error', (err: Error) => {
|
|
855
|
+
reject(err);
|
|
856
|
+
});
|
|
857
|
+
});
|
|
858
|
+
|
|
859
|
+
// Clear timeout since process completed
|
|
860
|
+
if (timeoutHandle) {
|
|
861
|
+
clearTimeout(timeoutHandle);
|
|
862
|
+
timeoutHandle = null;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
const duration = Date.now() - startTime;
|
|
866
|
+
|
|
867
|
+
// Update execution record
|
|
868
|
+
if (hasPostgres() && executionId) {
|
|
869
|
+
const db = getPostgres();
|
|
870
|
+
await db.query(
|
|
871
|
+
`UPDATE migration_executions
|
|
872
|
+
SET status = $1, completed_at = NOW(), exit_code = $2,
|
|
873
|
+
output = $3, error = $4, duration_ms = $5, updated_at = NOW()
|
|
874
|
+
WHERE id = $6`,
|
|
875
|
+
[
|
|
876
|
+
exitCode === 0 ? 'completed' : 'failed',
|
|
877
|
+
exitCode,
|
|
878
|
+
output,
|
|
879
|
+
error,
|
|
880
|
+
duration,
|
|
881
|
+
executionId,
|
|
882
|
+
]
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Send completion event
|
|
887
|
+
if (!res.writableEnded) {
|
|
888
|
+
res.write(`data: ${JSON.stringify({
|
|
889
|
+
type: 'complete',
|
|
890
|
+
exitCode,
|
|
891
|
+
duration,
|
|
892
|
+
timestamp: new Date().toISOString()
|
|
893
|
+
})}\n\n`);
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
res.end();
|
|
897
|
+
|
|
898
|
+
// Release advisory lock after successful completion
|
|
899
|
+
if (hasPostgres() && lockAcquired) {
|
|
900
|
+
const db = getPostgres();
|
|
901
|
+
await db.query('SELECT pg_advisory_unlock($1)', [MIGRATION_LOCK_ID]);
|
|
902
|
+
lockAcquired = false;
|
|
903
|
+
}
|
|
904
|
+
} catch (error) {
|
|
905
|
+
logger.error('Migration execution failed', { error });
|
|
906
|
+
|
|
907
|
+
// Send error event via SSE
|
|
908
|
+
if (!res.writableEnded) {
|
|
909
|
+
res.write(`data: ${JSON.stringify({
|
|
910
|
+
type: 'error',
|
|
911
|
+
data: error instanceof Error ? error.message : String(error),
|
|
912
|
+
timestamp: new Date().toISOString()
|
|
913
|
+
})}\n\n`);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
// Update execution record as failed
|
|
917
|
+
if (hasPostgres() && executionId) {
|
|
918
|
+
const db = getPostgres();
|
|
919
|
+
const duration = Date.now() - startTime;
|
|
920
|
+
await db.query(
|
|
921
|
+
`UPDATE migration_executions
|
|
922
|
+
SET status = $1, completed_at = NOW(),
|
|
923
|
+
error = $2, duration_ms = $3, updated_at = NOW()
|
|
924
|
+
WHERE id = $4`,
|
|
925
|
+
['failed', error instanceof Error ? error.message : String(error), duration, executionId]
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
res.end();
|
|
930
|
+
|
|
931
|
+
// Release advisory lock after error
|
|
932
|
+
if (hasPostgres() && lockAcquired) {
|
|
933
|
+
const db = getPostgres();
|
|
934
|
+
await db.query('SELECT pg_advisory_unlock($1)', [MIGRATION_LOCK_ID]);
|
|
935
|
+
lockAcquired = false;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
} catch (error) {
|
|
939
|
+
logger.error('Migration execution setup failed', { error });
|
|
940
|
+
|
|
941
|
+
// Release advisory lock if acquired
|
|
942
|
+
if (hasPostgres() && lockAcquired) {
|
|
943
|
+
const db = getPostgres();
|
|
944
|
+
await db.query('SELECT pg_advisory_unlock($1)', [MIGRATION_LOCK_ID])
|
|
945
|
+
.catch(err => logger.error('Failed to release lock on setup error', { err }));
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
res.status(500).json({
|
|
949
|
+
error: error instanceof Error ? error.message : String(error),
|
|
950
|
+
});
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
// GET /migrations/history - Get migration execution history
|
|
956
|
+
registry.addRoute({
|
|
957
|
+
method: 'get',
|
|
958
|
+
path: '/migrations/history',
|
|
959
|
+
pluginId: 'maintenance',
|
|
960
|
+
handler: async (req: Request, res: Response) => {
|
|
961
|
+
try {
|
|
962
|
+
if (!hasPostgres()) {
|
|
963
|
+
return res.json({ executions: [] });
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const db = getPostgres();
|
|
967
|
+
const { limit = '10', offset = '0', status, search } = req.query;
|
|
968
|
+
|
|
969
|
+
let query = 'SELECT * FROM migration_executions WHERE 1=1';
|
|
970
|
+
const params: (string | number)[] = [];
|
|
971
|
+
let paramIndex = 1;
|
|
972
|
+
|
|
973
|
+
if (status && typeof status === 'string') {
|
|
974
|
+
query += ` AND status = $${paramIndex}`;
|
|
975
|
+
params.push(status);
|
|
976
|
+
paramIndex++;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (search && typeof search === 'string') {
|
|
980
|
+
query += ` AND (output ILIKE $${paramIndex} OR error ILIKE $${paramIndex})`;
|
|
981
|
+
params.push(`%${search}%`);
|
|
982
|
+
paramIndex++;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
query += ` ORDER BY started_at DESC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`;
|
|
986
|
+
params.push(parseInt(limit as string, 10), parseInt(offset as string, 10));
|
|
987
|
+
|
|
988
|
+
const executions = await db.query(query, params);
|
|
989
|
+
|
|
990
|
+
// Get total count
|
|
991
|
+
let countQuery = 'SELECT COUNT(*) as count FROM migration_executions WHERE 1=1';
|
|
992
|
+
const countParams: string[] = [];
|
|
993
|
+
let countParamIndex = 1;
|
|
994
|
+
|
|
995
|
+
if (status && typeof status === 'string') {
|
|
996
|
+
countQuery += ` AND status = $${countParamIndex}`;
|
|
997
|
+
countParams.push(status);
|
|
998
|
+
countParamIndex++;
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
if (search && typeof search === 'string') {
|
|
1002
|
+
countQuery += ` AND (output ILIKE $${countParamIndex} OR error ILIKE $${countParamIndex})`;
|
|
1003
|
+
countParams.push(`%${search}%`);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
const countResult = await db.queryOne<{ count: string }>(countQuery, countParams);
|
|
1007
|
+
const total = parseInt(countResult?.count || '0', 10);
|
|
1008
|
+
|
|
1009
|
+
res.json({
|
|
1010
|
+
executions,
|
|
1011
|
+
pagination: {
|
|
1012
|
+
total,
|
|
1013
|
+
limit: parseInt(limit as string, 10),
|
|
1014
|
+
offset: parseInt(offset as string, 10),
|
|
1015
|
+
},
|
|
1016
|
+
});
|
|
1017
|
+
} catch (error) {
|
|
1018
|
+
logger.error('Failed to fetch migration history', { error });
|
|
1019
|
+
res.status(500).json({
|
|
1020
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
},
|
|
1024
|
+
});
|
|
1025
|
+
|
|
1026
|
+
// GET /migrations/history/:id - Get detailed migration execution result
|
|
1027
|
+
registry.addRoute({
|
|
1028
|
+
method: 'get',
|
|
1029
|
+
path: '/migrations/history/:id',
|
|
1030
|
+
pluginId: 'maintenance',
|
|
1031
|
+
handler: async (req: Request, res: Response) => {
|
|
1032
|
+
try {
|
|
1033
|
+
if (!hasPostgres()) {
|
|
1034
|
+
return res.status(404).json({ error: 'Execution not found' });
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
const { id } = req.params;
|
|
1038
|
+
const db = getPostgres();
|
|
1039
|
+
|
|
1040
|
+
const execution = await db.queryOne(
|
|
1041
|
+
'SELECT * FROM migration_executions WHERE id = $1',
|
|
1042
|
+
[id]
|
|
1043
|
+
);
|
|
1044
|
+
|
|
1045
|
+
if (!execution) {
|
|
1046
|
+
return res.status(404).json({ error: 'Execution not found' });
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
res.json({ execution });
|
|
1050
|
+
} catch (error) {
|
|
1051
|
+
logger.error('Failed to fetch migration execution detail', { error });
|
|
1052
|
+
res.status(500).json({
|
|
1053
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1054
|
+
message: error instanceof Error ? error.message : String(error),
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
});
|
|
1059
|
+
}
|
|
1060
|
+
|
|
602
1061
|
// Register maintenance widgets
|
|
603
1062
|
if (config.enableSeeds !== false) {
|
|
604
1063
|
registry.addWidget({
|
|
@@ -612,6 +1071,19 @@ export function createMaintenancePlugin(config: MaintenancePluginConfig = {}): P
|
|
|
612
1071
|
});
|
|
613
1072
|
}
|
|
614
1073
|
|
|
1074
|
+
// Register migration management widget
|
|
1075
|
+
if (config.enableMigrations !== false) {
|
|
1076
|
+
registry.addWidget({
|
|
1077
|
+
id: 'migration-management',
|
|
1078
|
+
title: 'Database Migrations',
|
|
1079
|
+
component: 'MigrationManagementWidget',
|
|
1080
|
+
type: 'maintenance',
|
|
1081
|
+
priority: 15,
|
|
1082
|
+
showByDefault: true, // Show by default on maintenance page
|
|
1083
|
+
pluginId: 'maintenance',
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
|
|
615
1087
|
// TODO: Register service control routes
|
|
616
1088
|
if (config.enableServiceControl !== false) {
|
|
617
1089
|
logger.debug('Service control enabled');
|
|
@@ -32,7 +32,7 @@ interface NotificationsStats {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export const NotificationsManagementPage: React.FC<NotificationsManagementPageProps> = ({
|
|
35
|
-
apiPrefix = '/
|
|
35
|
+
apiPrefix = '/qapi/notifications',
|
|
36
36
|
}) => {
|
|
37
37
|
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
38
38
|
const [stats, setStats] = useState<NotificationsStats | null>(null);
|
|
@@ -10,7 +10,7 @@ export interface NotificationsStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function NotificationsStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function NotificationsStatusWidget({ apiPrefix = '/qapi/notifications' }: NotificationsStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalNotifications: number;
|
|
16
16
|
pendingNotifications: number;
|
|
@@ -23,7 +23,7 @@ interface ParentalControl {
|
|
|
23
23
|
updatedAt: string;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function ParentalManagementPage({ apiPrefix = '/
|
|
26
|
+
export function ParentalManagementPage({ apiPrefix = '/qapi/parental' }: ParentalManagementPageProps) {
|
|
27
27
|
const [activeTab, setActiveTab] = useState<'all' | 'active' | 'violations' | 'config'>('all');
|
|
28
28
|
const [controls, setControls] = useState<ParentalControl[]>([]);
|
|
29
29
|
const [loading, setLoading] = useState(true);
|
|
@@ -10,7 +10,7 @@ export interface ParentalStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function ParentalStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function ParentalStatusWidget({ apiPrefix = '/qapi/parental' }: ParentalStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalControls: number;
|
|
16
16
|
activeControls: number;
|
|
@@ -42,7 +42,7 @@ interface PoolConfig {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export const PostgresManagementPage: React.FC<PostgresManagementPageProps> = ({
|
|
45
|
-
apiPrefix = '/
|
|
45
|
+
apiPrefix = '/qapi/plugins/postgres',
|
|
46
46
|
}) => {
|
|
47
47
|
const [connections, setConnections] = useState<ConnectionInfo[]>([]);
|
|
48
48
|
const [queryLogs, setQueryLogs] = useState<QueryLog[]>([]);
|
|
@@ -28,7 +28,7 @@ interface PostgresStats {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export const PostgresStatusWidget: React.FC<PostgresStatusWidgetProps> = ({
|
|
31
|
-
apiPrefix = '/
|
|
31
|
+
apiPrefix = '/qapi/plugins/postgres',
|
|
32
32
|
}) => {
|
|
33
33
|
const [stats, setStats] = useState<PostgresStats | null>(null);
|
|
34
34
|
const [loading, setLoading] = useState(true);
|
|
@@ -29,7 +29,7 @@ interface Preference {
|
|
|
29
29
|
updatedAt: string;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export function PreferencesManagementPage({ apiPrefix = '/
|
|
32
|
+
export function PreferencesManagementPage({ apiPrefix = '/qapi/preferences' }: PreferencesManagementPageProps) {
|
|
33
33
|
const [activeTab, setActiveTab] = useState<'overview' | 'user' | 'global' | 'config'>('overview');
|
|
34
34
|
const [preferenceSets, setPreferenceSets] = useState<PreferenceSet[]>([]);
|
|
35
35
|
const [preferences, setPreferences] = useState<Preference[]>([]);
|
|
@@ -10,7 +10,7 @@ export interface PreferencesStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function PreferencesStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function PreferencesStatusWidget({ apiPrefix = '/qapi/preferences' }: PreferencesStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalPreferences: number;
|
|
16
16
|
activeUsers: number;
|
|
@@ -22,7 +22,7 @@ interface Profile {
|
|
|
22
22
|
createdAt: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export function ProfilesManagementPage({ apiPrefix = '/
|
|
25
|
+
export function ProfilesManagementPage({ apiPrefix = '/qapi/profiles' }: ProfilesManagementPageProps) {
|
|
26
26
|
const [activeTab, setActiveTab] = useState<'all' | 'complete' | 'incomplete' | 'config'>('all');
|
|
27
27
|
const [profiles, setProfiles] = useState<Profile[]>([]);
|
|
28
28
|
const [loading, setLoading] = useState(true);
|
|
@@ -10,7 +10,7 @@ export interface ProfilesStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function ProfilesStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function ProfilesStatusWidget({ apiPrefix = '/qapi/profiles' }: ProfilesStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalProfiles: number;
|
|
16
16
|
completeProfiles: number;
|
|
@@ -30,7 +30,7 @@ interface QwickbrainStats {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export const QwickbrainManagementPage: React.FC<QwickbrainManagementPageProps> = ({
|
|
33
|
-
apiPrefix = '/
|
|
33
|
+
apiPrefix = '/qapi/qwickbrain',
|
|
34
34
|
}) => {
|
|
35
35
|
const [repositories, setRepositories] = useState<Repository[]>([]);
|
|
36
36
|
const [stats, setStats] = useState<QwickbrainStats | null>(null);
|
|
@@ -10,7 +10,7 @@ export interface QwickbrainStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function QwickbrainStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function QwickbrainStatusWidget({ apiPrefix = '/qapi/qwickbrain' }: QwickbrainStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalDocuments: number;
|
|
16
16
|
indexedRepositories: number;
|
|
@@ -31,7 +31,7 @@ interface RateLimitStats {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
export const RateLimitManagementPage: React.FC<RateLimitManagementPageProps> = ({
|
|
34
|
-
apiPrefix = '/
|
|
34
|
+
apiPrefix = '/qapi/rate-limit',
|
|
35
35
|
}) => {
|
|
36
36
|
const [rules, setRules] = useState<RateLimitRule[]>([]);
|
|
37
37
|
const [stats, setStats] = useState<RateLimitStats | null>(null);
|
|
@@ -10,7 +10,7 @@ export interface RateLimitStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function RateLimitStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function RateLimitStatusWidget({ apiPrefix = '/qapi/rate-limit' }: RateLimitStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalRequests: number;
|
|
16
16
|
blockedRequests: number;
|
|
@@ -23,7 +23,7 @@ interface Subscription {
|
|
|
23
23
|
amount?: number;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function SubscriptionsManagementPage({ apiPrefix = '/
|
|
26
|
+
export function SubscriptionsManagementPage({ apiPrefix = '/qapi/subscriptions' }: SubscriptionsManagementPageProps) {
|
|
27
27
|
const [activeTab, setActiveTab] = useState<'all' | 'active' | 'expiring' | 'config'>('all');
|
|
28
28
|
const [subscriptions, setSubscriptions] = useState<Subscription[]>([]);
|
|
29
29
|
const [loading, setLoading] = useState(true);
|
|
@@ -10,7 +10,7 @@ export interface SubscriptionsStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function SubscriptionsStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function SubscriptionsStatusWidget({ apiPrefix = '/qapi/subscriptions' }: SubscriptionsStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalSubscriptions: number;
|
|
16
16
|
activeSubscriptions: number;
|
|
@@ -21,7 +21,7 @@ interface UsageEvent {
|
|
|
21
21
|
metadata?: Record<string, unknown>;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function UsageManagementPage({ apiPrefix = '/
|
|
24
|
+
export function UsageManagementPage({ apiPrefix = '/qapi/usage' }: UsageManagementPageProps) {
|
|
25
25
|
const [activeTab, setActiveTab] = useState<'recent' | 'features' | 'users' | 'config'>('recent');
|
|
26
26
|
const [events, setEvents] = useState<UsageEvent[]>([]);
|
|
27
27
|
const [loading, setLoading] = useState(true);
|