@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
|
@@ -21,7 +21,9 @@ import type {
|
|
|
21
21
|
EntitlementStats,
|
|
22
22
|
} from './types.js';
|
|
23
23
|
import type { AuthenticatedRequest } from '../auth/types.js';
|
|
24
|
-
import { getCache, type CacheInstance } from '../cache-plugin.js';
|
|
24
|
+
import { getCache, hasCache, type CacheInstance } from '../cache-plugin.js';
|
|
25
|
+
import { hasPostgres, getPostgres } from '../postgres-plugin.js';
|
|
26
|
+
import { postgresEntitlementSource } from './sources/index.js';
|
|
25
27
|
|
|
26
28
|
// Plugin state
|
|
27
29
|
let primarySource: EntitlementSource | null = null;
|
|
@@ -35,67 +37,103 @@ let cacheEnabled = true;
|
|
|
35
37
|
let cacheVersion = 1;
|
|
36
38
|
|
|
37
39
|
/**
|
|
38
|
-
* Create the Entitlements plugin
|
|
40
|
+
* Create the Entitlements plugin with smart defaults
|
|
41
|
+
*
|
|
42
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
43
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
39
44
|
*/
|
|
40
|
-
export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plugin {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
function log(message: string, data?: Record<string, unknown>) {
|
|
48
|
-
if (debug) {
|
|
49
|
-
console.log(`[EntitlementsPlugin] ${message}`, data || '');
|
|
45
|
+
export function createEntitlementsPlugin(config: Partial<EntitlementsPluginConfig> = {}): Plugin {
|
|
46
|
+
function log(message: string, data?: Record<string, unknown>, isError = false) {
|
|
47
|
+
const prefix = '[EntitlementsPlugin]';
|
|
48
|
+
if (isError) {
|
|
49
|
+
console.error(`${prefix} ${message}`, data || '');
|
|
50
|
+
} else if (config.debug) {
|
|
51
|
+
console.log(`${prefix} ${message}`, data || '');
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
// Cache key helpers
|
|
54
|
-
const keys = {
|
|
55
|
-
entitlements: (email: string) => `${cacheKeyPrefix}user:${email.toLowerCase()}`,
|
|
56
|
-
mapping: (source: string, id: string) => `${cacheKeyPrefix}mapping:${source}:${id}`,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
55
|
return {
|
|
60
56
|
id: 'entitlements',
|
|
61
57
|
name: 'Entitlements',
|
|
62
58
|
version: '1.0.0',
|
|
63
59
|
|
|
64
60
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
61
|
+
const logger = registry.getLogger('entitlements');
|
|
62
|
+
|
|
63
|
+
// Check for postgres in registry (needed for default source)
|
|
64
|
+
if (!hasPostgres()) {
|
|
65
|
+
logger.warn('No Database! Entitlements plugin disabled.');
|
|
66
|
+
registry.registerHealthCheck({
|
|
67
|
+
name: 'entitlements-source',
|
|
68
|
+
type: 'custom',
|
|
69
|
+
check: async () => ({
|
|
70
|
+
healthy: false,
|
|
71
|
+
details: {
|
|
72
|
+
error: 'PostgreSQL not available',
|
|
73
|
+
state: 'disabled',
|
|
74
|
+
},
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Smart defaults - get dependencies from registry
|
|
81
|
+
const source = config.source ?? postgresEntitlementSource({
|
|
82
|
+
pool: () => getPostgres().getPool(),
|
|
83
|
+
autoCreateTables: true,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const debug = config.debug ?? false;
|
|
87
|
+
const apiPrefix = config.api?.prefix ?? '/entitlements';
|
|
88
|
+
const apiEnabled = config.api?.enabled ?? true;
|
|
89
|
+
const enableWriteApi = config.api?.enableWrite ?? true;
|
|
90
|
+
|
|
65
91
|
log('Starting entitlements plugin');
|
|
66
92
|
|
|
67
93
|
// Initialize primary source
|
|
68
|
-
await
|
|
69
|
-
primarySource =
|
|
70
|
-
log('Primary source initialized', { source:
|
|
94
|
+
await source.initialize();
|
|
95
|
+
primarySource = source;
|
|
96
|
+
log('Primary source initialized', { source: source.name });
|
|
71
97
|
|
|
72
98
|
// Initialize additional sources
|
|
73
99
|
additionalSources = config.additionalSources || [];
|
|
74
|
-
for (const
|
|
75
|
-
await
|
|
76
|
-
log('Additional source initialized', { source:
|
|
100
|
+
for (const additionalSource of additionalSources) {
|
|
101
|
+
await additionalSource.initialize();
|
|
102
|
+
log('Additional source initialized', { source: additionalSource.name });
|
|
77
103
|
}
|
|
78
104
|
|
|
79
105
|
// Store config
|
|
80
|
-
pluginConfig = config;
|
|
106
|
+
pluginConfig = { ...config, source, debug };
|
|
81
107
|
|
|
82
|
-
// Setup caching if enabled
|
|
108
|
+
// Setup caching if enabled and available
|
|
83
109
|
cacheEnabled = config.cache?.enabled !== false;
|
|
84
110
|
if (cacheEnabled) {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
111
|
+
if (hasCache()) {
|
|
112
|
+
try {
|
|
113
|
+
const instanceName = config.cache?.instanceName || 'default';
|
|
114
|
+
cacheInstance = getCache(instanceName);
|
|
115
|
+
cacheKeyPrefix = config.cache?.keyPrefix || 'entitlements:';
|
|
116
|
+
cacheTtl = config.cache?.ttl || 300;
|
|
117
|
+
cacheMappingTtl = config.cache?.mappingTtl || cacheTtl * 2;
|
|
118
|
+
log('Cache configured', { instanceName, prefix: cacheKeyPrefix, ttl: cacheTtl });
|
|
119
|
+
} catch {
|
|
120
|
+
log('Cache instance not available, running without caching');
|
|
121
|
+
cacheEnabled = false;
|
|
122
|
+
cacheInstance = null;
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
log('Cache plugin not available, running without caching');
|
|
94
126
|
cacheEnabled = false;
|
|
95
127
|
cacheInstance = null;
|
|
96
128
|
}
|
|
97
129
|
}
|
|
98
130
|
|
|
131
|
+
// Cache key helpers
|
|
132
|
+
const keys = {
|
|
133
|
+
entitlements: (email: string) => `${cacheKeyPrefix}user:${email.toLowerCase()}`,
|
|
134
|
+
mapping: (sourceId: string, id: string) => `${cacheKeyPrefix}mapping:${sourceId}:${id}`,
|
|
135
|
+
};
|
|
136
|
+
|
|
99
137
|
// Register health check
|
|
100
138
|
registry.registerHealthCheck({
|
|
101
139
|
name: 'entitlements-source',
|
|
@@ -104,8 +142,8 @@ export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plug
|
|
|
104
142
|
try {
|
|
105
143
|
// Use source's isHealthy() method if available (avoids API calls)
|
|
106
144
|
// Otherwise just check that source is initialized
|
|
107
|
-
if (
|
|
108
|
-
const healthy = await
|
|
145
|
+
if (source.isHealthy) {
|
|
146
|
+
const healthy = await source.isHealthy();
|
|
109
147
|
return { healthy };
|
|
110
148
|
}
|
|
111
149
|
// Source is healthy if initialized (we got here means it started)
|
|
@@ -145,9 +183,9 @@ export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plug
|
|
|
145
183
|
try {
|
|
146
184
|
const sources = [
|
|
147
185
|
{
|
|
148
|
-
name:
|
|
149
|
-
description:
|
|
150
|
-
readonly:
|
|
186
|
+
name: source.name,
|
|
187
|
+
description: source.description,
|
|
188
|
+
readonly: source.readonly ?? false,
|
|
151
189
|
primary: true,
|
|
152
190
|
},
|
|
153
191
|
...additionalSources.map((s) => ({
|
|
@@ -159,8 +197,8 @@ export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plug
|
|
|
159
197
|
];
|
|
160
198
|
|
|
161
199
|
res.json({
|
|
162
|
-
readonly:
|
|
163
|
-
writeEnabled: enableWriteApi && !
|
|
200
|
+
readonly: source.readonly ?? false,
|
|
201
|
+
writeEnabled: enableWriteApi && !source.readonly,
|
|
164
202
|
cacheEnabled,
|
|
165
203
|
cacheTtl,
|
|
166
204
|
sources,
|
|
@@ -311,7 +349,7 @@ export function createEntitlementsPlugin(config: EntitlementsPluginConfig): Plug
|
|
|
311
349
|
});
|
|
312
350
|
|
|
313
351
|
// Write endpoints (grant/revoke) - only if enabled and source is writable
|
|
314
|
-
if (enableWriteApi && !
|
|
352
|
+
if (enableWriteApi && !source.readonly) {
|
|
315
353
|
// Grant entitlement
|
|
316
354
|
registry.addRoute({
|
|
317
355
|
method: 'post',
|
|
@@ -50,8 +50,6 @@ export type {
|
|
|
50
50
|
EntitlementStats,
|
|
51
51
|
} from './types.js';
|
|
52
52
|
|
|
53
|
-
// UI Components
|
|
54
|
-
export
|
|
55
|
-
|
|
56
|
-
export { EntitlementsManagementPage } from './EntitlementsManagementPage.js';
|
|
57
|
-
export type { EntitlementsManagementPageProps } from './EntitlementsManagementPage.js';
|
|
53
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
54
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
55
|
+
|
|
@@ -200,10 +200,17 @@ export interface EntitlementsApiConfig {
|
|
|
200
200
|
|
|
201
201
|
/**
|
|
202
202
|
* Entitlements plugin configuration
|
|
203
|
+
*
|
|
204
|
+
* All properties are optional - plugin will use smart defaults:
|
|
205
|
+
* - source: Postgres entitlement source using registry's postgres instance
|
|
206
|
+
* - cache: Uses cache from registry if available
|
|
207
|
+
* - api.prefix: '/entitlements'
|
|
208
|
+
* - api.enabled: true
|
|
209
|
+
* - debug: false
|
|
203
210
|
*/
|
|
204
211
|
export interface EntitlementsPluginConfig {
|
|
205
|
-
/** Primary entitlement source */
|
|
206
|
-
source
|
|
212
|
+
/** Primary entitlement source (default: postgres source from registry) */
|
|
213
|
+
source?: EntitlementSource;
|
|
207
214
|
|
|
208
215
|
/** Additional sources to query (results are merged) */
|
|
209
216
|
additionalSources?: EntitlementSource[];
|
package/src/plugins/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ export { createLogsPlugin } from './logs-plugin.js';
|
|
|
14
14
|
export type { LogsPluginConfig } from './logs-plugin.js';
|
|
15
15
|
|
|
16
16
|
export { createMaintenancePlugin } from './maintenance-plugin.js';
|
|
17
|
-
export type { MaintenancePluginConfig } from './maintenance-plugin.js';
|
|
17
|
+
export type { MaintenancePluginConfig, SeedTask, SeedTaskHandler } from './maintenance-plugin.js';
|
|
18
18
|
export {
|
|
19
19
|
MaintenanceManagementPage,
|
|
20
20
|
MaintenanceStatusWidget,
|
|
@@ -11,6 +11,8 @@ import React, { useEffect, useRef, useState } from 'react';
|
|
|
11
11
|
export interface SeedExecutorProps {
|
|
12
12
|
apiPrefix: string;
|
|
13
13
|
seedName: string;
|
|
14
|
+
seedType?: string;
|
|
15
|
+
seedOptions?: any;
|
|
14
16
|
onComplete: () => void;
|
|
15
17
|
onCancel: () => void;
|
|
16
18
|
}
|
|
@@ -26,6 +28,8 @@ interface OutputLine {
|
|
|
26
28
|
export const SeedExecutor: React.FC<SeedExecutorProps> = ({
|
|
27
29
|
apiPrefix,
|
|
28
30
|
seedName,
|
|
31
|
+
seedType = 'file',
|
|
32
|
+
seedOptions,
|
|
29
33
|
onComplete,
|
|
30
34
|
onCancel,
|
|
31
35
|
}) => {
|
|
@@ -63,7 +67,11 @@ export const SeedExecutor: React.FC<SeedExecutorProps> = ({
|
|
|
63
67
|
headers: {
|
|
64
68
|
'Content-Type': 'application/json',
|
|
65
69
|
},
|
|
66
|
-
body: JSON.stringify({
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
name: seedName,
|
|
72
|
+
type: seedType,
|
|
73
|
+
options: seedOptions,
|
|
74
|
+
}),
|
|
67
75
|
});
|
|
68
76
|
|
|
69
77
|
if (!response.ok) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Seed List Component
|
|
3
3
|
*
|
|
4
|
-
* Displays available seed scripts with metadata.
|
|
4
|
+
* Displays available seed scripts and custom tasks with metadata.
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
7
7
|
*/
|
|
@@ -10,10 +10,11 @@ import React, { useEffect, useState } from 'react';
|
|
|
10
10
|
|
|
11
11
|
export interface SeedListProps {
|
|
12
12
|
apiPrefix: string;
|
|
13
|
-
onExecute: (seedName: string) => void;
|
|
13
|
+
onExecute: (seedName: string, type?: string, options?: any) => void;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
interface SeedFile {
|
|
17
|
+
type: 'file';
|
|
17
18
|
name: string;
|
|
18
19
|
path: string;
|
|
19
20
|
size: number;
|
|
@@ -21,8 +22,18 @@ interface SeedFile {
|
|
|
21
22
|
modifiedAt: string;
|
|
22
23
|
}
|
|
23
24
|
|
|
25
|
+
interface CustomTask {
|
|
26
|
+
type: 'task';
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
description: string;
|
|
30
|
+
options?: Record<string, any>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type SeedItem = SeedFile | CustomTask;
|
|
34
|
+
|
|
24
35
|
export const SeedList: React.FC<SeedListProps> = ({ apiPrefix, onExecute }) => {
|
|
25
|
-
const [seeds, setSeeds] = useState<
|
|
36
|
+
const [seeds, setSeeds] = useState<SeedItem[]>([]);
|
|
26
37
|
const [loading, setLoading] = useState(true);
|
|
27
38
|
const [error, setError] = useState<string | null>(null);
|
|
28
39
|
|
|
@@ -57,6 +68,14 @@ export const SeedList: React.FC<SeedListProps> = ({ apiPrefix, onExecute }) => {
|
|
|
57
68
|
return new Date(dateString).toLocaleString();
|
|
58
69
|
};
|
|
59
70
|
|
|
71
|
+
const isFileType = (item: SeedItem): item is SeedFile => {
|
|
72
|
+
return item.type === 'file';
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const isTaskType = (item: SeedItem): item is CustomTask => {
|
|
76
|
+
return item.type === 'task';
|
|
77
|
+
};
|
|
78
|
+
|
|
60
79
|
if (loading) {
|
|
61
80
|
return <div style={{ padding: '20px' }}>Loading seeds...</div>;
|
|
62
81
|
}
|
|
@@ -72,55 +91,83 @@ export const SeedList: React.FC<SeedListProps> = ({ apiPrefix, onExecute }) => {
|
|
|
72
91
|
if (seeds.length === 0) {
|
|
73
92
|
return (
|
|
74
93
|
<div style={{ padding: '20px', color: '#666' }}>
|
|
75
|
-
No seed scripts
|
|
94
|
+
No seed scripts or tasks found.
|
|
76
95
|
</div>
|
|
77
96
|
);
|
|
78
97
|
}
|
|
79
98
|
|
|
80
99
|
return (
|
|
81
100
|
<div style={{ padding: '20px' }}>
|
|
82
|
-
<h3>Available
|
|
101
|
+
<h3>Available Seeds & Tasks ({seeds.length})</h3>
|
|
83
102
|
<table style={{ width: '100%', borderCollapse: 'collapse', marginTop: '16px' }}>
|
|
84
103
|
<thead>
|
|
85
104
|
<tr style={{ borderBottom: '2px solid #ddd', textAlign: 'left' }}>
|
|
105
|
+
<th style={{ padding: '12px' }}>Type</th>
|
|
86
106
|
<th style={{ padding: '12px' }}>Name</th>
|
|
87
|
-
<th style={{ padding: '12px' }}>
|
|
88
|
-
<th style={{ padding: '12px' }}>
|
|
107
|
+
<th style={{ padding: '12px' }}>Description</th>
|
|
108
|
+
<th style={{ padding: '12px' }}>Details</th>
|
|
89
109
|
<th style={{ padding: '12px' }}>Action</th>
|
|
90
110
|
</tr>
|
|
91
111
|
</thead>
|
|
92
112
|
<tbody>
|
|
93
|
-
{seeds.map((seed) =>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
>
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
{seeds.map((seed) => {
|
|
114
|
+
const itemKey = isFileType(seed) ? seed.name : seed.id;
|
|
115
|
+
return (
|
|
116
|
+
<tr key={itemKey} style={{ borderBottom: '1px solid #eee' }}>
|
|
117
|
+
<td style={{ padding: '12px' }}>
|
|
118
|
+
<span
|
|
119
|
+
style={{
|
|
120
|
+
display: 'inline-block',
|
|
121
|
+
padding: '4px 8px',
|
|
122
|
+
borderRadius: '4px',
|
|
123
|
+
fontSize: '12px',
|
|
124
|
+
fontWeight: 'bold',
|
|
125
|
+
backgroundColor: isFileType(seed) ? '#e3f2fd' : '#f3e5f5',
|
|
126
|
+
color: isFileType(seed) ? '#1976d2' : '#7b1fa2',
|
|
127
|
+
}}
|
|
128
|
+
>
|
|
129
|
+
{isFileType(seed) ? 'FILE' : 'TASK'}
|
|
130
|
+
</span>
|
|
131
|
+
</td>
|
|
132
|
+
<td style={{ padding: '12px', fontFamily: 'monospace' }}>
|
|
133
|
+
{isFileType(seed) ? seed.name : seed.name}
|
|
134
|
+
</td>
|
|
135
|
+
<td style={{ padding: '12px', fontSize: '14px', color: '#666' }}>
|
|
136
|
+
{isTaskType(seed) ? seed.description : '-'}
|
|
137
|
+
</td>
|
|
138
|
+
<td style={{ padding: '12px', fontSize: '14px', color: '#666' }}>
|
|
139
|
+
{isFileType(seed)
|
|
140
|
+
? `${formatFileSize(seed.size)} • ${formatDate(seed.modifiedAt)}`
|
|
141
|
+
: '-'}
|
|
142
|
+
</td>
|
|
143
|
+
<td style={{ padding: '12px' }}>
|
|
144
|
+
<button
|
|
145
|
+
onClick={() => {
|
|
146
|
+
const displayName = isFileType(seed) ? seed.name : seed.name;
|
|
147
|
+
if (confirm(`Execute ${displayName}?`)) {
|
|
148
|
+
if (isFileType(seed)) {
|
|
149
|
+
onExecute(seed.name, 'file');
|
|
150
|
+
} else {
|
|
151
|
+
onExecute(seed.id, 'task', seed.options);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}}
|
|
155
|
+
style={{
|
|
156
|
+
padding: '6px 12px',
|
|
157
|
+
backgroundColor: '#1976d2',
|
|
158
|
+
color: 'white',
|
|
159
|
+
border: 'none',
|
|
160
|
+
borderRadius: '4px',
|
|
161
|
+
cursor: 'pointer',
|
|
162
|
+
}}
|
|
163
|
+
data-testid={`execute-${itemKey}`}
|
|
164
|
+
>
|
|
165
|
+
Execute
|
|
166
|
+
</button>
|
|
167
|
+
</td>
|
|
168
|
+
</tr>
|
|
169
|
+
);
|
|
170
|
+
})}
|
|
124
171
|
</tbody>
|
|
125
172
|
</table>
|
|
126
173
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Seed Management Page
|
|
3
3
|
*
|
|
4
|
-
* Main page for managing and executing seed scripts.
|
|
4
|
+
* Main page for managing and executing seed scripts and custom tasks.
|
|
5
5
|
*
|
|
6
6
|
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
7
7
|
*/
|
|
@@ -23,9 +23,13 @@ export const SeedManagementPage: React.FC<SeedManagementPageProps> = ({
|
|
|
23
23
|
}) => {
|
|
24
24
|
const [activeTab, setActiveTab] = useState<Tab>('list');
|
|
25
25
|
const [selectedSeed, setSelectedSeed] = useState<string | null>(null);
|
|
26
|
+
const [selectedType, setSelectedType] = useState<string>('file');
|
|
27
|
+
const [selectedOptions, setSelectedOptions] = useState<any>(undefined);
|
|
26
28
|
|
|
27
|
-
const handleExecute = (seedName: string) => {
|
|
29
|
+
const handleExecute = (seedName: string, type: string = 'file', options?: any) => {
|
|
28
30
|
setSelectedSeed(seedName);
|
|
31
|
+
setSelectedType(type);
|
|
32
|
+
setSelectedOptions(options);
|
|
29
33
|
setActiveTab('execute');
|
|
30
34
|
};
|
|
31
35
|
|
|
@@ -36,7 +40,7 @@ export const SeedManagementPage: React.FC<SeedManagementPageProps> = ({
|
|
|
36
40
|
return (
|
|
37
41
|
<PluginManagementPage
|
|
38
42
|
title="Seed Management"
|
|
39
|
-
description="Manage and execute database seed scripts"
|
|
43
|
+
description="Manage and execute database seed scripts and custom tasks"
|
|
40
44
|
>
|
|
41
45
|
<div style={{ marginBottom: '20px' }}>
|
|
42
46
|
<button
|
|
@@ -51,7 +55,7 @@ export const SeedManagementPage: React.FC<SeedManagementPageProps> = ({
|
|
|
51
55
|
cursor: 'pointer',
|
|
52
56
|
}}
|
|
53
57
|
>
|
|
54
|
-
Available Seeds
|
|
58
|
+
Available Seeds & Tasks
|
|
55
59
|
</button>
|
|
56
60
|
<button
|
|
57
61
|
onClick={() => setActiveTab('history')}
|
|
@@ -76,6 +80,8 @@ export const SeedManagementPage: React.FC<SeedManagementPageProps> = ({
|
|
|
76
80
|
<SeedExecutor
|
|
77
81
|
apiPrefix={apiPrefix}
|
|
78
82
|
seedName={selectedSeed}
|
|
83
|
+
seedType={selectedType}
|
|
84
|
+
seedOptions={selectedOptions}
|
|
79
85
|
onComplete={handleExecutionComplete}
|
|
80
86
|
onCancel={() => setActiveTab('list')}
|
|
81
87
|
/>
|
|
@@ -40,33 +40,33 @@ const MAX_OUTPUT_SIZE = 100 * 1024;
|
|
|
40
40
|
/**
|
|
41
41
|
* Validate script path to prevent path traversal attacks
|
|
42
42
|
*
|
|
43
|
-
* @param
|
|
43
|
+
* @param scriptPath - Relative path to the script (e.g., "database/001.init.mjs" or "01-Setup/seed-data.mjs")
|
|
44
44
|
* @param scriptsPath - Base path for scripts directory
|
|
45
|
-
* @returns Resolved path if valid, null if invalid
|
|
45
|
+
* @returns Resolved absolute path if valid, null if invalid
|
|
46
46
|
*/
|
|
47
|
-
export function validateScriptPath(
|
|
48
|
-
// Only allow
|
|
49
|
-
if (
|
|
47
|
+
export function validateScriptPath(scriptPath: string, scriptsPath: string): string | null {
|
|
48
|
+
// Only allow .mjs files
|
|
49
|
+
if (!scriptPath.endsWith('.mjs')) {
|
|
50
50
|
return null;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// Resolve paths
|
|
54
54
|
const basePath = resolve(scriptsPath);
|
|
55
|
-
const
|
|
55
|
+
const resolvedScriptPath = resolve(basePath, scriptPath);
|
|
56
56
|
|
|
57
57
|
// Ensure resolved path is within scriptsPath (prevent path traversal)
|
|
58
58
|
// Use relative() for platform-agnostic check (works on Windows and Unix)
|
|
59
|
-
const relativePath = relative(basePath,
|
|
59
|
+
const relativePath = relative(basePath, resolvedScriptPath);
|
|
60
60
|
if (relativePath.startsWith('..') || relativePath.includes('../') || relativePath.includes('..\\')) {
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// Ensure file exists
|
|
65
|
-
if (!existsSync(
|
|
65
|
+
if (!existsSync(resolvedScriptPath)) {
|
|
66
66
|
return null;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
return
|
|
69
|
+
return resolvedScriptPath;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
72
|
/**
|
|
@@ -95,30 +95,64 @@ export class SeedExecutor {
|
|
|
95
95
|
*
|
|
96
96
|
* @param scriptPath - Absolute path to the script
|
|
97
97
|
* @param res - Express response object (for SSE streaming)
|
|
98
|
+
* @param databaseUrl - Optional database URL to pass to the script
|
|
99
|
+
* @param projectRoot - Optional project root directory (defaults to script's directory)
|
|
98
100
|
* @returns Promise resolving to execution result
|
|
99
101
|
*/
|
|
100
|
-
async execute(scriptPath: string, res: Response): Promise<SeedExecutionResult> {
|
|
102
|
+
async execute(scriptPath: string, res: Response, databaseUrl?: string, projectRoot?: string): Promise<SeedExecutionResult> {
|
|
101
103
|
this.startTime = Date.now();
|
|
102
104
|
this.outputBuffer = '';
|
|
103
105
|
this.errorBuffer = '';
|
|
104
106
|
this.outputSize = 0;
|
|
105
107
|
|
|
106
108
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
107
|
-
//
|
|
108
|
-
// Use
|
|
109
|
-
|
|
109
|
+
// Determine if we need tsx (for .ts/.mts files that import TS modules)
|
|
110
|
+
// Use tsx for .mjs files too since they might import from TS source
|
|
111
|
+
const needsTsx = scriptPath.match(/\.(mjs|ts|mts)$/);
|
|
112
|
+
const execCommand = needsTsx ? 'tsx' : process.execPath;
|
|
113
|
+
const execArgs = needsTsx ? [scriptPath] : [scriptPath];
|
|
114
|
+
|
|
115
|
+
// Spawn process with TypeScript support if needed
|
|
116
|
+
this.child = spawn(execCommand, execArgs, {
|
|
110
117
|
env: {
|
|
118
|
+
...process.env, // Inherit all env vars for tsx to work properly
|
|
111
119
|
NODE_ENV: process.env.NODE_ENV || 'development',
|
|
112
|
-
DATABASE_URI: process.env.DATABASE_URI,
|
|
113
|
-
DATABASE_URL: process.env.DATABASE_URL,
|
|
120
|
+
DATABASE_URI: databaseUrl || process.env.DATABASE_URI,
|
|
121
|
+
DATABASE_URL: databaseUrl || process.env.DATABASE_URL,
|
|
114
122
|
PAYLOAD_PUBLIC_SERVER_URL: process.env.PAYLOAD_PUBLIC_SERVER_URL,
|
|
115
123
|
NEXT_PUBLIC_SERVER_URL: process.env.NEXT_PUBLIC_SERVER_URL,
|
|
116
124
|
API_URL: process.env.API_URL,
|
|
125
|
+
PAYLOAD_SECRET: process.env.PAYLOAD_SECRET || 'dev-secret-key-change-in-production',
|
|
117
126
|
},
|
|
118
|
-
stdio: ['
|
|
119
|
-
cwd: resolve(scriptPath, '..'), // Run from scripts directory
|
|
127
|
+
stdio: ['pipe', 'pipe', 'pipe'], // stdin: pipe, stdout: pipe, stderr: pipe
|
|
128
|
+
cwd: projectRoot || resolve(scriptPath, '..'), // Run from project root (or scripts directory if not provided)
|
|
120
129
|
});
|
|
121
130
|
|
|
131
|
+
// Auto-answer interactive prompts (like Drizzle's schema push confirmation)
|
|
132
|
+
// Send 'y\n' every 500ms to handle any prompts that appear
|
|
133
|
+
const stdinInterval = setInterval(() => {
|
|
134
|
+
if (this.child && this.child.stdin && !this.child.stdin.destroyed) {
|
|
135
|
+
try {
|
|
136
|
+
this.child.stdin.write('y\n');
|
|
137
|
+
} catch (err) {
|
|
138
|
+
// Ignore errors - stdin might be closed
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}, 500);
|
|
142
|
+
|
|
143
|
+
// Clear interval when process exits
|
|
144
|
+
const cleanupInterval = () => {
|
|
145
|
+
clearInterval(stdinInterval);
|
|
146
|
+
// Close stdin to signal EOF
|
|
147
|
+
if (this.child && this.child.stdin && !this.child.stdin.destroyed) {
|
|
148
|
+
try {
|
|
149
|
+
this.child.stdin.end();
|
|
150
|
+
} catch (err) {
|
|
151
|
+
// Ignore errors
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
122
156
|
// Handle stdout
|
|
123
157
|
this.child.stdout?.on('data', (data: Buffer) => {
|
|
124
158
|
const text = data.toString();
|
|
@@ -165,6 +199,8 @@ export class SeedExecutor {
|
|
|
165
199
|
|
|
166
200
|
// Handle process exit
|
|
167
201
|
this.child.on('exit', (code, signal) => {
|
|
202
|
+
cleanupInterval(); // Clean up stdin interval
|
|
203
|
+
|
|
168
204
|
const exitCode = code ?? (signal ? 1 : 0);
|
|
169
205
|
const duration = Date.now() - this.startTime;
|
|
170
206
|
|
|
@@ -188,6 +224,8 @@ export class SeedExecutor {
|
|
|
188
224
|
|
|
189
225
|
// Handle spawn errors
|
|
190
226
|
this.child.on('error', (err: Error) => {
|
|
227
|
+
cleanupInterval(); // Clean up stdin interval
|
|
228
|
+
|
|
191
229
|
sendSSEEvent(res, {
|
|
192
230
|
type: 'error',
|
|
193
231
|
data: err.message,
|
|
@@ -199,6 +237,7 @@ export class SeedExecutor {
|
|
|
199
237
|
|
|
200
238
|
// Handle SSE connection close - terminate child process
|
|
201
239
|
res.on('close', () => {
|
|
240
|
+
cleanupInterval(); // Clean up stdin interval
|
|
202
241
|
if (this.child && !this.child.killed) {
|
|
203
242
|
this.child.kill('SIGTERM');
|
|
204
243
|
}
|