@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
|
@@ -10,13 +10,103 @@
|
|
|
10
10
|
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { readdirSync, statSync } from 'fs';
|
|
14
|
-
import { resolve } from 'path';
|
|
13
|
+
import { readdirSync, statSync, readFileSync } from 'fs';
|
|
14
|
+
import { resolve, join, relative } from 'path';
|
|
15
15
|
import type { Request, Response } from 'express';
|
|
16
16
|
import type { Plugin, PluginConfig, PluginRegistry } from '../core/plugin-registry.js';
|
|
17
17
|
import { SeedExecutor, validateScriptPath } from './maintenance/seed-executor.js';
|
|
18
18
|
import { getPostgres, hasPostgres } from './postgres-plugin.js';
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Extract description from JSDoc comment at top of file
|
|
22
|
+
*/
|
|
23
|
+
function extractDescription(filePath: string): string | undefined {
|
|
24
|
+
try {
|
|
25
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
26
|
+
// Match JSDoc comment at start of file (after any whitespace)
|
|
27
|
+
const jsdocMatch = content.match(/^\s*\/\*\*\s*\n([\s\S]*?)\*\//);
|
|
28
|
+
|
|
29
|
+
if (jsdocMatch) {
|
|
30
|
+
// Extract lines and clean up asterisks and whitespace
|
|
31
|
+
const lines = jsdocMatch[1]
|
|
32
|
+
.split('\n')
|
|
33
|
+
.map(line => line.replace(/^\s*\*\s?/, '').trim())
|
|
34
|
+
.filter(line => line.length > 0);
|
|
35
|
+
|
|
36
|
+
// Skip the first line if it's just the title (will be shown separately)
|
|
37
|
+
// Return subsequent lines as the description
|
|
38
|
+
return lines.slice(1).join(' ').trim() || undefined;
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
// Failed to read file or parse description
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Recursively scan directory for .mjs files
|
|
49
|
+
*/
|
|
50
|
+
function scanSeedScripts(dir: string, basePath: string = dir): any[] {
|
|
51
|
+
const results: any[] = [];
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
55
|
+
|
|
56
|
+
for (const entry of entries) {
|
|
57
|
+
const fullPath = join(dir, entry.name);
|
|
58
|
+
|
|
59
|
+
if (entry.isDirectory()) {
|
|
60
|
+
// Recursively scan subdirectories
|
|
61
|
+
results.push(...scanSeedScripts(fullPath, basePath));
|
|
62
|
+
} else if (entry.isFile() && entry.name.endsWith('.mjs')) {
|
|
63
|
+
// Found a .mjs file
|
|
64
|
+
const stats = statSync(fullPath);
|
|
65
|
+
const relativePath = relative(basePath, fullPath);
|
|
66
|
+
const description = extractDescription(fullPath);
|
|
67
|
+
|
|
68
|
+
results.push({
|
|
69
|
+
type: 'file',
|
|
70
|
+
name: entry.name,
|
|
71
|
+
path: relativePath, // Relative path from scripts directory
|
|
72
|
+
fullPath: fullPath, // Absolute path
|
|
73
|
+
size: stats.size,
|
|
74
|
+
createdAt: stats.birthtime,
|
|
75
|
+
modifiedAt: stats.mtime,
|
|
76
|
+
description, // Add description from JSDoc
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
} catch (err) {
|
|
81
|
+
// Directory not accessible, return empty array
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return results;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Custom seed task handler
|
|
89
|
+
*/
|
|
90
|
+
export interface SeedTaskHandler {
|
|
91
|
+
(options?: Record<string, any>, res?: Response): Promise<void>;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Custom seed task definition
|
|
96
|
+
*/
|
|
97
|
+
export interface SeedTask {
|
|
98
|
+
/** Unique task identifier */
|
|
99
|
+
id: string;
|
|
100
|
+
/** Display name */
|
|
101
|
+
name: string;
|
|
102
|
+
/** Description of what this task does */
|
|
103
|
+
description: string;
|
|
104
|
+
/** Task handler function */
|
|
105
|
+
handler: SeedTaskHandler;
|
|
106
|
+
/** Optional task options/parameters */
|
|
107
|
+
options?: Record<string, any>;
|
|
108
|
+
}
|
|
109
|
+
|
|
20
110
|
export interface MaintenancePluginConfig {
|
|
21
111
|
/** Path to scripts directory (default: './scripts') */
|
|
22
112
|
scriptsPath?: string;
|
|
@@ -41,6 +131,9 @@ export interface MaintenancePluginConfig {
|
|
|
41
131
|
|
|
42
132
|
/** Enable database operations (default: true) */
|
|
43
133
|
enableDatabaseOps?: boolean;
|
|
134
|
+
|
|
135
|
+
/** Custom seed tasks */
|
|
136
|
+
customTasks?: SeedTask[];
|
|
44
137
|
}
|
|
45
138
|
|
|
46
139
|
/**
|
|
@@ -60,6 +153,38 @@ export function createMaintenancePlugin(config: MaintenancePluginConfig = {}): P
|
|
|
60
153
|
const logger = registry.getLogger('maintenance');
|
|
61
154
|
logger.info('Maintenance plugin starting...');
|
|
62
155
|
|
|
156
|
+
// Initialize seed_executions table if PostgreSQL is available
|
|
157
|
+
if (hasPostgres()) {
|
|
158
|
+
try {
|
|
159
|
+
const db = getPostgres();
|
|
160
|
+
await db.queryRaw(`
|
|
161
|
+
CREATE TABLE IF NOT EXISTS seed_executions (
|
|
162
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
163
|
+
name TEXT NOT NULL,
|
|
164
|
+
status TEXT NOT NULL CHECK (status IN ('running', 'completed', 'failed')),
|
|
165
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
166
|
+
completed_at TIMESTAMPTZ,
|
|
167
|
+
exit_code INTEGER,
|
|
168
|
+
output TEXT,
|
|
169
|
+
error TEXT,
|
|
170
|
+
duration_ms INTEGER,
|
|
171
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
172
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
173
|
+
)
|
|
174
|
+
`);
|
|
175
|
+
|
|
176
|
+
// Create index on status for faster queries
|
|
177
|
+
await db.queryRaw(`
|
|
178
|
+
CREATE INDEX IF NOT EXISTS idx_seed_executions_status
|
|
179
|
+
ON seed_executions(status)
|
|
180
|
+
`);
|
|
181
|
+
|
|
182
|
+
logger.debug('Seed executions table initialized');
|
|
183
|
+
} catch (error) {
|
|
184
|
+
logger.error('Failed to initialize seed_executions table', { error });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
63
188
|
// Clean up orphaned executions from previous crashes
|
|
64
189
|
if (hasPostgres()) {
|
|
65
190
|
try {
|
|
@@ -112,7 +237,7 @@ export function createMaintenancePlugin(config: MaintenancePluginConfig = {}): P
|
|
|
112
237
|
logger.warn('Seed management requires PostgreSQL plugin for execution history');
|
|
113
238
|
}
|
|
114
239
|
|
|
115
|
-
// GET /seeds/discover - List available seed scripts
|
|
240
|
+
// GET /seeds/discover - List available seed scripts and custom tasks
|
|
116
241
|
registry.addRoute({
|
|
117
242
|
method: 'get',
|
|
118
243
|
path: '/seeds/discover',
|
|
@@ -120,25 +245,30 @@ export function createMaintenancePlugin(config: MaintenancePluginConfig = {}): P
|
|
|
120
245
|
handler: (_req: Request, res: Response) => {
|
|
121
246
|
try {
|
|
122
247
|
const resolvedPath = resolve(scriptsPath);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
248
|
+
let seedFiles: any[] = [];
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
// Recursively scan for .mjs files
|
|
252
|
+
seedFiles = scanSeedScripts(resolvedPath);
|
|
253
|
+
|
|
254
|
+
// Sort by relative path (natural ordering respects numbered prefixes)
|
|
255
|
+
// Example: "01-Setup/001.init.mjs" comes before "02-Production/001.seed.mjs"
|
|
256
|
+
seedFiles.sort((a, b) => a.path.localeCompare(b.path, undefined, { numeric: true }));
|
|
257
|
+
} catch (err) {
|
|
258
|
+
// Scripts directory may not exist, which is fine if we have custom tasks
|
|
259
|
+
logger.debug('Scripts directory not found or not readable');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Add custom tasks
|
|
263
|
+
const customTasks = (config.customTasks || []).map((task) => ({
|
|
264
|
+
type: 'task',
|
|
265
|
+
id: task.id,
|
|
266
|
+
name: task.name,
|
|
267
|
+
description: task.description,
|
|
268
|
+
options: task.options,
|
|
269
|
+
}));
|
|
270
|
+
|
|
271
|
+
res.json({ seeds: [...seedFiles, ...customTasks] });
|
|
142
272
|
} catch (error) {
|
|
143
273
|
logger.error('Failed to discover seed scripts', { error });
|
|
144
274
|
res.status(500).json({
|
|
@@ -149,23 +279,45 @@ export function createMaintenancePlugin(config: MaintenancePluginConfig = {}): P
|
|
|
149
279
|
},
|
|
150
280
|
});
|
|
151
281
|
|
|
152
|
-
// POST /seeds/execute - Execute a seed script
|
|
282
|
+
// POST /seeds/execute - Execute a seed script or custom task
|
|
153
283
|
registry.addRoute({
|
|
154
284
|
method: 'post',
|
|
155
285
|
path: '/seeds/execute',
|
|
156
286
|
pluginId: 'maintenance',
|
|
157
287
|
handler: async (req: Request, res: Response) => {
|
|
158
288
|
try {
|
|
159
|
-
const { name } = req.body;
|
|
289
|
+
const { name, type, options } = req.body;
|
|
160
290
|
|
|
161
291
|
if (!name || typeof name !== 'string') {
|
|
162
292
|
return res.status(400).json({ error: 'Script name is required' });
|
|
163
293
|
}
|
|
164
294
|
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
295
|
+
// Ensure seed_executions table exists (lazy initialization)
|
|
296
|
+
if (hasPostgres()) {
|
|
297
|
+
const db = getPostgres();
|
|
298
|
+
try {
|
|
299
|
+
await db.queryRaw(`
|
|
300
|
+
CREATE TABLE IF NOT EXISTS seed_executions (
|
|
301
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
302
|
+
name TEXT NOT NULL,
|
|
303
|
+
status TEXT NOT NULL CHECK (status IN ('running', 'completed', 'failed')),
|
|
304
|
+
started_at TIMESTAMPTZ NOT NULL,
|
|
305
|
+
completed_at TIMESTAMPTZ,
|
|
306
|
+
exit_code INTEGER,
|
|
307
|
+
output TEXT,
|
|
308
|
+
error TEXT,
|
|
309
|
+
duration_ms INTEGER,
|
|
310
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
311
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
312
|
+
)
|
|
313
|
+
`);
|
|
314
|
+
await db.queryRaw(`
|
|
315
|
+
CREATE INDEX IF NOT EXISTS idx_seed_executions_status
|
|
316
|
+
ON seed_executions(status)
|
|
317
|
+
`);
|
|
318
|
+
} catch (err) {
|
|
319
|
+
logger.debug('Table initialization check', { err });
|
|
320
|
+
}
|
|
169
321
|
}
|
|
170
322
|
|
|
171
323
|
// Check for concurrent execution
|
|
@@ -204,10 +356,39 @@ export function createMaintenancePlugin(config: MaintenancePluginConfig = {}): P
|
|
|
204
356
|
executionId = result?.id || null;
|
|
205
357
|
}
|
|
206
358
|
|
|
207
|
-
|
|
208
|
-
|
|
359
|
+
const startTime = Date.now();
|
|
360
|
+
let exitCode = 0;
|
|
361
|
+
let output = '';
|
|
362
|
+
let error = '';
|
|
363
|
+
|
|
209
364
|
try {
|
|
210
|
-
|
|
365
|
+
// Execute based on type
|
|
366
|
+
if (type === 'task') {
|
|
367
|
+
// Find custom task
|
|
368
|
+
const task = (config.customTasks || []).find((t) => t.id === name);
|
|
369
|
+
if (!task) {
|
|
370
|
+
throw new Error(`Custom task not found: ${name}`);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Execute custom task handler with SSE streaming
|
|
374
|
+
await task.handler(options || {}, res);
|
|
375
|
+
} else {
|
|
376
|
+
// Execute file-based seed script
|
|
377
|
+
const scriptPath = validateScriptPath(name, scriptsPath);
|
|
378
|
+
if (!scriptPath) {
|
|
379
|
+
throw new Error('Invalid script name or file not found');
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const executor = new SeedExecutor();
|
|
383
|
+
// Project root is one level up from scripts directory
|
|
384
|
+
const projectRoot = resolve(scriptsPath, '..');
|
|
385
|
+
const result = await executor.execute(scriptPath, res, config.databaseUrl, projectRoot);
|
|
386
|
+
exitCode = result.exitCode;
|
|
387
|
+
output = result.output;
|
|
388
|
+
error = result.error;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const duration = Date.now() - startTime;
|
|
211
392
|
|
|
212
393
|
// Update execution record
|
|
213
394
|
if (hasPostgres() && executionId) {
|
|
@@ -218,11 +399,11 @@ export function createMaintenancePlugin(config: MaintenancePluginConfig = {}): P
|
|
|
218
399
|
output = $3, error = $4, duration_ms = $5, updated_at = NOW()
|
|
219
400
|
WHERE id = $6`,
|
|
220
401
|
[
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
402
|
+
exitCode === 0 ? 'completed' : 'failed',
|
|
403
|
+
exitCode,
|
|
404
|
+
output,
|
|
405
|
+
error,
|
|
406
|
+
duration,
|
|
226
407
|
executionId,
|
|
227
408
|
]
|
|
228
409
|
);
|
|
@@ -262,6 +443,56 @@ export function createMaintenancePlugin(config: MaintenancePluginConfig = {}): P
|
|
|
262
443
|
},
|
|
263
444
|
});
|
|
264
445
|
|
|
446
|
+
// POST /database/reset - Drop and recreate database schema (local/dev only)
|
|
447
|
+
registry.addRoute({
|
|
448
|
+
method: 'post',
|
|
449
|
+
path: '/database/reset',
|
|
450
|
+
pluginId: 'maintenance',
|
|
451
|
+
handler: async (req: Request, res: Response) => {
|
|
452
|
+
try {
|
|
453
|
+
// Security: Only allow in local or development environments
|
|
454
|
+
const nodeEnv = process.env.NODE_ENV?.toLowerCase();
|
|
455
|
+
if (nodeEnv !== 'local' && nodeEnv !== 'development') {
|
|
456
|
+
return res.status(403).json({
|
|
457
|
+
error: 'Database reset is only available in local or development environments',
|
|
458
|
+
currentEnv: nodeEnv || 'production',
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (!hasPostgres()) {
|
|
463
|
+
return res.status(503).json({
|
|
464
|
+
error: 'PostgreSQL plugin required for database reset',
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const db = getPostgres();
|
|
469
|
+
|
|
470
|
+
// Drop and recreate public schema (removes all tables, data, etc.)
|
|
471
|
+
await db.queryRaw('DROP SCHEMA IF EXISTS public CASCADE');
|
|
472
|
+
await db.queryRaw('CREATE SCHEMA public');
|
|
473
|
+
await db.queryRaw('GRANT ALL ON SCHEMA public TO public');
|
|
474
|
+
await db.queryRaw('GRANT ALL ON SCHEMA public TO postgres');
|
|
475
|
+
await db.queryRaw('GRANT ALL ON SCHEMA public TO qwickapps');
|
|
476
|
+
|
|
477
|
+
// Grant default privileges for future tables and sequences
|
|
478
|
+
await db.queryRaw('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO qwickapps');
|
|
479
|
+
await db.queryRaw('ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO qwickapps');
|
|
480
|
+
|
|
481
|
+
res.json({
|
|
482
|
+
success: true,
|
|
483
|
+
message: 'Database schema has been reset. All tables and data have been deleted.',
|
|
484
|
+
timestamp: new Date().toISOString(),
|
|
485
|
+
});
|
|
486
|
+
} catch (error) {
|
|
487
|
+
logger.error('Database reset failed', { error });
|
|
488
|
+
res.status(500).json({
|
|
489
|
+
error: 'Failed to reset database',
|
|
490
|
+
message: error instanceof Error ? error.message : String(error),
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
},
|
|
494
|
+
});
|
|
495
|
+
|
|
265
496
|
// GET /seeds/history - List execution history
|
|
266
497
|
registry.addRoute({
|
|
267
498
|
method: 'get',
|
|
@@ -90,8 +90,6 @@ export type {
|
|
|
90
90
|
NotificationsManagerInterface,
|
|
91
91
|
} from './types.js';
|
|
92
92
|
|
|
93
|
-
// UI Components
|
|
94
|
-
export
|
|
95
|
-
|
|
96
|
-
export type { NotificationsStatusWidgetProps } from './NotificationsStatusWidget.js';
|
|
97
|
-
export type { NotificationsManagementPageProps } from './NotificationsManagementPage.js';
|
|
93
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
94
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
95
|
+
|
|
@@ -96,7 +96,10 @@ function validateId(id: string | undefined, paramName: string): { valid: boolean
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
|
-
* Create the Notifications plugin
|
|
99
|
+
* Create the Notifications plugin with smart defaults
|
|
100
|
+
*
|
|
101
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
102
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
100
103
|
*
|
|
101
104
|
* @param config Plugin configuration
|
|
102
105
|
* @returns Plugin instance
|
|
@@ -112,11 +115,7 @@ function validateId(id: string | undefined, paramName: string): { valid: boolean
|
|
|
112
115
|
* });
|
|
113
116
|
* ```
|
|
114
117
|
*/
|
|
115
|
-
export function createNotificationsPlugin(config: NotificationsPluginConfig): Plugin {
|
|
116
|
-
const apiPrefix = config.api?.prefix || '/'; // Framework adds /notifications prefix automatically
|
|
117
|
-
const streamEnabled = config.api?.stream !== false;
|
|
118
|
-
const statsEnabled = config.api?.stats !== false;
|
|
119
|
-
|
|
118
|
+
export function createNotificationsPlugin(config: Partial<NotificationsPluginConfig> = {}): Plugin {
|
|
120
119
|
let manager: NotificationsManager | null = null;
|
|
121
120
|
|
|
122
121
|
return {
|
|
@@ -129,10 +128,19 @@ export function createNotificationsPlugin(config: NotificationsPluginConfig): Pl
|
|
|
129
128
|
|
|
130
129
|
// Check for postgres plugin dependency
|
|
131
130
|
if (!hasPostgres()) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
131
|
+
logger.warn('No Database! Notifications plugin disabled.');
|
|
132
|
+
registry.registerHealthCheck({
|
|
133
|
+
name: 'notifications',
|
|
134
|
+
type: 'custom',
|
|
135
|
+
check: async () => ({
|
|
136
|
+
healthy: false,
|
|
137
|
+
details: {
|
|
138
|
+
error: 'PostgreSQL not available',
|
|
139
|
+
state: 'disabled',
|
|
140
|
+
},
|
|
141
|
+
}),
|
|
142
|
+
});
|
|
143
|
+
return;
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
// Get database connection string from postgres plugin
|
|
@@ -150,22 +158,43 @@ export function createNotificationsPlugin(config: NotificationsPluginConfig): Pl
|
|
|
150
158
|
}
|
|
151
159
|
|
|
152
160
|
if (!connectionString) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
161
|
+
logger.warn('Could not determine PostgreSQL connection string. Notifications plugin disabled.');
|
|
162
|
+
registry.registerHealthCheck({
|
|
163
|
+
name: 'notifications',
|
|
164
|
+
type: 'custom',
|
|
165
|
+
check: async () => ({
|
|
166
|
+
healthy: false,
|
|
167
|
+
details: {
|
|
168
|
+
error: 'Connection string not available',
|
|
169
|
+
state: 'disabled',
|
|
170
|
+
},
|
|
171
|
+
}),
|
|
172
|
+
});
|
|
173
|
+
return;
|
|
157
174
|
}
|
|
158
175
|
|
|
176
|
+
// Smart defaults
|
|
177
|
+
const channels = config.channels ?? [];
|
|
178
|
+
const apiPrefix = config.api?.prefix ?? '/'; // Framework adds /notifications prefix automatically
|
|
179
|
+
const streamEnabled = config.api?.stream ?? true;
|
|
180
|
+
const statsEnabled = config.api?.stats ?? true;
|
|
181
|
+
|
|
159
182
|
logger.debug('Initializing notifications manager', {
|
|
160
|
-
channels:
|
|
183
|
+
channels: channels,
|
|
161
184
|
heartbeatInterval: config.heartbeat?.interval,
|
|
162
185
|
});
|
|
163
186
|
|
|
187
|
+
// Create full config with defaults for required fields
|
|
188
|
+
const fullConfig: NotificationsPluginConfig = {
|
|
189
|
+
...config,
|
|
190
|
+
channels, // Required field with default
|
|
191
|
+
};
|
|
192
|
+
|
|
164
193
|
// Create and initialize manager
|
|
165
194
|
manager = new NotificationsManager(
|
|
166
195
|
connectionString,
|
|
167
|
-
|
|
168
|
-
|
|
196
|
+
channels,
|
|
197
|
+
fullConfig,
|
|
169
198
|
logger
|
|
170
199
|
);
|
|
171
200
|
|
|
@@ -182,7 +211,7 @@ export function createNotificationsPlugin(config: NotificationsPluginConfig): Pl
|
|
|
182
211
|
healthy: health?.isHealthy ?? false,
|
|
183
212
|
details: {
|
|
184
213
|
connected: health?.isConnected,
|
|
185
|
-
channels:
|
|
214
|
+
channels: channels,
|
|
186
215
|
activeClients: manager?.getStats().currentConnections ?? 0,
|
|
187
216
|
lastEventAt: health?.lastEventAt?.toISOString(),
|
|
188
217
|
isReconnecting: health?.isReconnecting,
|
|
@@ -273,7 +302,7 @@ export function createNotificationsPlugin(config: NotificationsPluginConfig): Pl
|
|
|
273
302
|
const stats = manager.getStats();
|
|
274
303
|
res.json({
|
|
275
304
|
...stats,
|
|
276
|
-
channels:
|
|
305
|
+
channels: channels,
|
|
277
306
|
lastEventAt: stats.connectionHealth.lastEventAt?.toISOString(),
|
|
278
307
|
lastReconnectionAt: stats.lastReconnectionAt?.toISOString(),
|
|
279
308
|
});
|
|
@@ -54,8 +54,6 @@ export { postgresParentalStore } from './stores/index.js';
|
|
|
54
54
|
export { kidsAdapter } from './adapters/index.js';
|
|
55
55
|
export type { KidsAdapterConfig } from './adapters/index.js';
|
|
56
56
|
|
|
57
|
-
// UI Components
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
export { ParentalManagementPage } from './ParentalManagementPage.js';
|
|
61
|
-
export type { ParentalManagementPageProps } from './ParentalManagementPage.js';
|
|
57
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
58
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
59
|
+
|