@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
|
@@ -24,6 +24,9 @@ import type {
|
|
|
24
24
|
CreateRestrictionInput,
|
|
25
25
|
LogActivityInput,
|
|
26
26
|
} from './types.js';
|
|
27
|
+
import { hasPostgres, getPostgres } from '../postgres-plugin.js';
|
|
28
|
+
import { postgresParentalStore } from './stores/index.js';
|
|
29
|
+
import { kidsAdapter } from './adapters/index.js';
|
|
27
30
|
|
|
28
31
|
// Store instances for helper access
|
|
29
32
|
let currentStore: ParentalStore | null = null;
|
|
@@ -54,17 +57,18 @@ function isWithinSchedule(schedule: Record<string, { start: string; end: string
|
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
/**
|
|
57
|
-
* Create the Parental plugin
|
|
60
|
+
* Create the Parental plugin with smart defaults
|
|
61
|
+
*
|
|
62
|
+
* Config is optional - plugin will use defaults and get dependencies from registry.
|
|
63
|
+
* Gracefully handles missing dependencies with clear log messages.
|
|
58
64
|
*/
|
|
59
|
-
export function createParentalPlugin(config: ParentalPluginConfig): Plugin {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (debug) {
|
|
67
|
-
console.log(`[ParentalPlugin] ${message}`, data || '');
|
|
65
|
+
export function createParentalPlugin(config: Partial<ParentalPluginConfig> = {}): Plugin {
|
|
66
|
+
function log(message: string, data?: Record<string, unknown>, isError = false) {
|
|
67
|
+
const prefix = '[ParentalPlugin]';
|
|
68
|
+
if (isError) {
|
|
69
|
+
console.error(`${prefix} ${message}`, data || '');
|
|
70
|
+
} else if (config.debug) {
|
|
71
|
+
console.log(`${prefix} ${message}`, data || '');
|
|
68
72
|
}
|
|
69
73
|
}
|
|
70
74
|
|
|
@@ -74,16 +78,48 @@ export function createParentalPlugin(config: ParentalPluginConfig): Plugin {
|
|
|
74
78
|
version: '1.0.0',
|
|
75
79
|
|
|
76
80
|
async onStart(_pluginConfig: PluginConfig, registry: PluginRegistry): Promise<void> {
|
|
81
|
+
const logger = registry.getLogger('parental');
|
|
82
|
+
|
|
83
|
+
// Check for postgres in registry
|
|
84
|
+
if (!hasPostgres()) {
|
|
85
|
+
logger.warn('No Database! Parental plugin disabled.');
|
|
86
|
+
registry.registerHealthCheck({
|
|
87
|
+
name: 'parental-store',
|
|
88
|
+
type: 'custom',
|
|
89
|
+
check: async () => ({
|
|
90
|
+
healthy: false,
|
|
91
|
+
details: {
|
|
92
|
+
error: 'PostgreSQL not available',
|
|
93
|
+
state: 'disabled',
|
|
94
|
+
},
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Smart defaults - get dependencies from registry
|
|
101
|
+
const store = config.store ?? postgresParentalStore({
|
|
102
|
+
pool: () => getPostgres().getPool(),
|
|
103
|
+
autoCreateTables: true,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const adapter = config.adapter ?? kidsAdapter();
|
|
107
|
+
const debug = config.debug ?? false;
|
|
108
|
+
const apiPrefix = config.api?.prefix ?? '/'; // Framework adds /parental prefix automatically
|
|
109
|
+
const apiEnabled = config.api?.enabled ?? true;
|
|
110
|
+
const maxPinAttempts = config.maxPinAttempts ?? 5;
|
|
111
|
+
const pinLockoutMinutes = config.pinLockoutMinutes ?? 30;
|
|
112
|
+
|
|
77
113
|
log('Starting parental plugin');
|
|
78
114
|
|
|
79
115
|
// Initialize the store (creates tables if needed)
|
|
80
|
-
await
|
|
116
|
+
await store.initialize();
|
|
81
117
|
log('Parental store initialized');
|
|
82
118
|
|
|
83
119
|
// Store references for helper access
|
|
84
|
-
currentStore =
|
|
85
|
-
currentAdapter =
|
|
86
|
-
currentConfig = config;
|
|
120
|
+
currentStore = store;
|
|
121
|
+
currentAdapter = adapter;
|
|
122
|
+
currentConfig = { ...config, store, adapter, debug, maxPinAttempts, pinLockoutMinutes };
|
|
87
123
|
|
|
88
124
|
// Register health check
|
|
89
125
|
registry.registerHealthCheck({
|
|
@@ -99,7 +135,7 @@ export function createParentalPlugin(config: ParentalPluginConfig): Plugin {
|
|
|
99
135
|
});
|
|
100
136
|
|
|
101
137
|
// Add API routes if enabled
|
|
102
|
-
if (
|
|
138
|
+
if (apiEnabled) {
|
|
103
139
|
// ═══════════════════════════════════════════════════════════════════════
|
|
104
140
|
// Guardian Settings Routes
|
|
105
141
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -140,7 +176,7 @@ export function createParentalPlugin(config: ParentalPluginConfig): Plugin {
|
|
|
140
176
|
// Hash PIN if provided
|
|
141
177
|
const processedInput = {
|
|
142
178
|
...input,
|
|
143
|
-
adapter_type:
|
|
179
|
+
adapter_type: adapter.name,
|
|
144
180
|
pin: input.pin ? hashPin(input.pin) : undefined,
|
|
145
181
|
};
|
|
146
182
|
|
|
@@ -290,8 +326,8 @@ export function createParentalPlugin(config: ParentalPluginConfig): Plugin {
|
|
|
290
326
|
const input = req.body as CreateRestrictionInput;
|
|
291
327
|
|
|
292
328
|
// Validate with adapter
|
|
293
|
-
if (
|
|
294
|
-
const validation =
|
|
329
|
+
if (adapter.validateRestriction) {
|
|
330
|
+
const validation = adapter.validateRestriction(input);
|
|
295
331
|
if (!validation.valid) {
|
|
296
332
|
return res.status(400).json({ error: 'Invalid restriction', errors: validation.errors });
|
|
297
333
|
}
|
|
@@ -416,7 +452,7 @@ export function createParentalPlugin(config: ParentalPluginConfig): Plugin {
|
|
|
416
452
|
const input = req.body as LogActivityInput;
|
|
417
453
|
const activity = await logActivity({
|
|
418
454
|
...input,
|
|
419
|
-
adapter_type:
|
|
455
|
+
adapter_type: adapter.name,
|
|
420
456
|
});
|
|
421
457
|
res.status(201).json(activity);
|
|
422
458
|
} catch (error) {
|
|
@@ -441,10 +477,10 @@ export function createParentalPlugin(config: ParentalPluginConfig): Plugin {
|
|
|
441
477
|
|
|
442
478
|
// Format details with adapter if available
|
|
443
479
|
const formattedActivities = activities.map((activity) => {
|
|
444
|
-
if (
|
|
480
|
+
if (adapter.formatActivityDetails) {
|
|
445
481
|
return {
|
|
446
482
|
...activity,
|
|
447
|
-
formatted_details:
|
|
483
|
+
formatted_details: adapter.formatActivityDetails(activity),
|
|
448
484
|
};
|
|
449
485
|
}
|
|
450
486
|
return activity;
|
|
@@ -466,9 +502,9 @@ export function createParentalPlugin(config: ParentalPluginConfig): Plugin {
|
|
|
466
502
|
handler: async (_req: Request, res: Response) => {
|
|
467
503
|
try {
|
|
468
504
|
res.json({
|
|
469
|
-
name:
|
|
470
|
-
activity_types:
|
|
471
|
-
default_daily_limit:
|
|
505
|
+
name: adapter.name,
|
|
506
|
+
activity_types: adapter.getActivityTypes(),
|
|
507
|
+
default_daily_limit: adapter.getDefaultDailyLimit(),
|
|
472
508
|
});
|
|
473
509
|
} catch (error) {
|
|
474
510
|
console.error('[ParentalPlugin] Get adapter info error:', error);
|
|
@@ -483,7 +519,9 @@ export function createParentalPlugin(config: ParentalPluginConfig): Plugin {
|
|
|
483
519
|
|
|
484
520
|
async onStop(): Promise<void> {
|
|
485
521
|
log('Stopping parental plugin');
|
|
486
|
-
|
|
522
|
+
if (currentStore) {
|
|
523
|
+
await currentStore.shutdown();
|
|
524
|
+
}
|
|
487
525
|
currentStore = null;
|
|
488
526
|
currentAdapter = null;
|
|
489
527
|
currentConfig = null;
|
|
@@ -90,6 +90,26 @@ export interface PostgresPluginConfig {
|
|
|
90
90
|
|
|
91
91
|
/** Called on pool errors */
|
|
92
92
|
onError?: (error: Error) => void;
|
|
93
|
+
|
|
94
|
+
// Database initialization options
|
|
95
|
+
|
|
96
|
+
/** Admin user for database operations (e.g., 'postgres') */
|
|
97
|
+
adminUser?: string;
|
|
98
|
+
|
|
99
|
+
/** Admin password for database operations */
|
|
100
|
+
adminPassword?: string;
|
|
101
|
+
|
|
102
|
+
/** Admin database to connect to for operations (default: 'postgres') */
|
|
103
|
+
adminDatabase?: string;
|
|
104
|
+
|
|
105
|
+
/** Database name to create/ensure exists (parsed from url if not provided) */
|
|
106
|
+
databaseName?: string;
|
|
107
|
+
|
|
108
|
+
/** User who should own the database (parsed from url if not provided) */
|
|
109
|
+
databaseOwner?: string;
|
|
110
|
+
|
|
111
|
+
/** Automatically initialize database if connection fails (default: true if admin credentials provided) */
|
|
112
|
+
autoInitialize?: boolean;
|
|
93
113
|
}
|
|
94
114
|
|
|
95
115
|
/**
|
|
@@ -140,6 +160,127 @@ export interface PostgresInstance {
|
|
|
140
160
|
// Global registry of PostgreSQL instances by name
|
|
141
161
|
const instances = new Map<string, PostgresInstance>();
|
|
142
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Parse database connection URL to extract components
|
|
165
|
+
*/
|
|
166
|
+
function parseConnectionUrl(url: string): {
|
|
167
|
+
user: string;
|
|
168
|
+
password: string;
|
|
169
|
+
host: string;
|
|
170
|
+
port: number;
|
|
171
|
+
database: string;
|
|
172
|
+
} {
|
|
173
|
+
const match = url.match(/postgresql:\/\/([^:]+):([^@]+)@([^:]+):(\d+)\/(.+)/);
|
|
174
|
+
if (!match) {
|
|
175
|
+
throw new Error('Invalid PostgreSQL connection URL format');
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
user: match[1],
|
|
179
|
+
password: match[2],
|
|
180
|
+
host: match[3],
|
|
181
|
+
port: parseInt(match[4], 10),
|
|
182
|
+
database: match[5],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Helper to create an admin pool for database operations
|
|
188
|
+
*/
|
|
189
|
+
function createAdminPool(config: {
|
|
190
|
+
adminUser: string;
|
|
191
|
+
adminPassword: string;
|
|
192
|
+
host: string;
|
|
193
|
+
port: number;
|
|
194
|
+
adminDatabase?: string;
|
|
195
|
+
}): pg.Pool {
|
|
196
|
+
return new Pool({
|
|
197
|
+
user: config.adminUser,
|
|
198
|
+
password: config.adminPassword,
|
|
199
|
+
host: config.host,
|
|
200
|
+
port: config.port,
|
|
201
|
+
database: config.adminDatabase || 'postgres',
|
|
202
|
+
max: 1,
|
|
203
|
+
idleTimeoutMillis: 5000,
|
|
204
|
+
connectionTimeoutMillis: 5000,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Ensure database user exists with password
|
|
210
|
+
*/
|
|
211
|
+
async function ensureUserExists(
|
|
212
|
+
adminPool: pg.Pool,
|
|
213
|
+
user: string,
|
|
214
|
+
password: string
|
|
215
|
+
): Promise<void> {
|
|
216
|
+
const result = await adminPool.query(
|
|
217
|
+
`SELECT 1 FROM pg_roles WHERE rolname = $1`,
|
|
218
|
+
[user]
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
if (result.rows.length === 0) {
|
|
222
|
+
await adminPool.query(
|
|
223
|
+
`CREATE USER ${user} WITH PASSWORD '${password}'`
|
|
224
|
+
);
|
|
225
|
+
} else {
|
|
226
|
+
await adminPool.query(
|
|
227
|
+
`ALTER USER ${user} WITH PASSWORD '${password}'`
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Ensure database exists with correct owner
|
|
234
|
+
*/
|
|
235
|
+
async function ensureDatabaseExists(
|
|
236
|
+
adminPool: pg.Pool,
|
|
237
|
+
database: string,
|
|
238
|
+
owner: string
|
|
239
|
+
): Promise<void> {
|
|
240
|
+
const result = await adminPool.query(
|
|
241
|
+
`SELECT 1 FROM pg_database WHERE datname = $1`,
|
|
242
|
+
[database]
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
if (result.rows.length === 0) {
|
|
246
|
+
await adminPool.query(
|
|
247
|
+
`CREATE DATABASE ${database} OWNER ${owner}`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Grant all permissions to user on database
|
|
254
|
+
*/
|
|
255
|
+
async function grantPermissions(
|
|
256
|
+
adminPool: pg.Pool,
|
|
257
|
+
database: string,
|
|
258
|
+
user: string
|
|
259
|
+
): Promise<void> {
|
|
260
|
+
const tempPool = new Pool({
|
|
261
|
+
user: adminPool.options.user as string,
|
|
262
|
+
password: adminPool.options.password as string,
|
|
263
|
+
host: adminPool.options.host as string,
|
|
264
|
+
port: adminPool.options.port as number,
|
|
265
|
+
database,
|
|
266
|
+
max: 1,
|
|
267
|
+
idleTimeoutMillis: 5000,
|
|
268
|
+
connectionTimeoutMillis: 5000,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
try {
|
|
272
|
+
await tempPool.query(`
|
|
273
|
+
GRANT ALL ON SCHEMA public TO ${user};
|
|
274
|
+
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${user};
|
|
275
|
+
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${user};
|
|
276
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${user};
|
|
277
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${user};
|
|
278
|
+
`);
|
|
279
|
+
} finally {
|
|
280
|
+
await tempPool.end();
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
143
284
|
/**
|
|
144
285
|
* Get a PostgreSQL instance by name
|
|
145
286
|
*
|
|
@@ -305,15 +446,279 @@ export function createPostgresPlugin(
|
|
|
305
446
|
const instance = createInstance();
|
|
306
447
|
instances.set(instanceName, instance);
|
|
307
448
|
|
|
308
|
-
//
|
|
449
|
+
// Register maintenance widget FIRST (before connection attempt)
|
|
450
|
+
// This ensures the widget is available even if database connection fails
|
|
451
|
+
registry.addWidget({
|
|
452
|
+
id: `postgres-operations-${instanceName}`,
|
|
453
|
+
title: `Database Operations (${instanceName})`,
|
|
454
|
+
component: 'DatabaseOperationsWidget',
|
|
455
|
+
type: 'maintenance',
|
|
456
|
+
priority: 50,
|
|
457
|
+
showByDefault: true,
|
|
458
|
+
pluginId: pluginId,
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// Three-phase initialization: connect → auto-repair → error state
|
|
462
|
+
|
|
463
|
+
// PHASE 1: Try to connect with DATABASE_URI
|
|
309
464
|
try {
|
|
310
465
|
await instance.query('SELECT 1');
|
|
311
|
-
logger.
|
|
312
|
-
} catch (
|
|
313
|
-
|
|
314
|
-
|
|
466
|
+
logger.info(`PostgreSQL "${instanceName}" connected successfully`);
|
|
467
|
+
} catch (connectionError) {
|
|
468
|
+
const errorMsg = connectionError instanceof Error ? connectionError.message : String(connectionError);
|
|
469
|
+
logger.warn(`PostgreSQL "${instanceName}" connection failed: ${errorMsg}`);
|
|
470
|
+
|
|
471
|
+
// PHASE 2: Auto-repair if admin credentials provided
|
|
472
|
+
const shouldAutoRepair =
|
|
473
|
+
config.adminUser &&
|
|
474
|
+
config.adminPassword &&
|
|
475
|
+
config.autoInitialize !== false &&
|
|
476
|
+
config.url;
|
|
477
|
+
|
|
478
|
+
if (shouldAutoRepair) {
|
|
479
|
+
logger.info(`Attempting auto-repair for "${instanceName}"...`);
|
|
480
|
+
|
|
481
|
+
try {
|
|
482
|
+
const connParams = parseConnectionUrl(config.url!);
|
|
483
|
+
const adminPool = createAdminPool({
|
|
484
|
+
adminUser: config.adminUser!,
|
|
485
|
+
adminPassword: config.adminPassword!,
|
|
486
|
+
host: connParams.host,
|
|
487
|
+
port: connParams.port,
|
|
488
|
+
adminDatabase: config.adminDatabase,
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
try {
|
|
492
|
+
// Ensure user exists
|
|
493
|
+
logger.debug(`Ensuring user "${connParams.user}" exists...`);
|
|
494
|
+
await ensureUserExists(adminPool, connParams.user, connParams.password);
|
|
495
|
+
|
|
496
|
+
// Ensure database exists
|
|
497
|
+
logger.debug(`Ensuring database "${connParams.database}" exists...`);
|
|
498
|
+
await ensureDatabaseExists(adminPool, connParams.database, connParams.user);
|
|
499
|
+
|
|
500
|
+
// Grant permissions
|
|
501
|
+
logger.debug(`Granting permissions to "${connParams.user}" on "${connParams.database}"...`);
|
|
502
|
+
await grantPermissions(adminPool, connParams.database, connParams.user);
|
|
503
|
+
|
|
504
|
+
logger.info(`Auto-repair completed successfully for "${instanceName}"`);
|
|
505
|
+
|
|
506
|
+
// Try connection again after repair
|
|
507
|
+
await instance.query('SELECT 1');
|
|
508
|
+
logger.info(`PostgreSQL "${instanceName}" connected after auto-repair`);
|
|
509
|
+
} finally {
|
|
510
|
+
await adminPool.end();
|
|
511
|
+
}
|
|
512
|
+
} catch (repairError) {
|
|
513
|
+
const repairMsg = repairError instanceof Error ? repairError.message : String(repairError);
|
|
514
|
+
// Log error but don't throw - allow plugin to continue starting
|
|
515
|
+
// This ensures the widget and API routes are available for manual database initialization
|
|
516
|
+
logger.error(
|
|
517
|
+
`PostgreSQL connection failed and auto-repair unsuccessful. ` +
|
|
518
|
+
`Original error: ${errorMsg}. Repair error: ${repairMsg}. ` +
|
|
519
|
+
`Use the maintenance UI to manually initialize the database.`
|
|
520
|
+
);
|
|
521
|
+
}
|
|
522
|
+
} else {
|
|
523
|
+
// PHASE 3: No auto-repair available, remain in error state
|
|
524
|
+
const missingConfig = [];
|
|
525
|
+
if (!config.adminUser) missingConfig.push('adminUser');
|
|
526
|
+
if (!config.adminPassword) missingConfig.push('adminPassword');
|
|
527
|
+
|
|
528
|
+
const hint = missingConfig.length > 0
|
|
529
|
+
? ` Provide ${missingConfig.join(', ')} in config to enable auto-repair.`
|
|
530
|
+
: ' Set autoInitialize=true to enable auto-repair.';
|
|
531
|
+
|
|
532
|
+
// Log error but don't throw - allow plugin to continue starting
|
|
533
|
+
// This ensures the widget and API routes are available for manual database initialization
|
|
534
|
+
logger.error(
|
|
535
|
+
`PostgreSQL connection failed: ${errorMsg}.${hint} ` +
|
|
536
|
+
`Use the maintenance UI to manually initialize the database.`
|
|
537
|
+
);
|
|
538
|
+
}
|
|
315
539
|
}
|
|
316
540
|
|
|
541
|
+
// Register API routes for database operations
|
|
542
|
+
registry.addRoute({
|
|
543
|
+
method: 'get',
|
|
544
|
+
path: '/status',
|
|
545
|
+
pluginId: pluginId,
|
|
546
|
+
handler: async (req: import('express').Request, res: import('express').Response) => {
|
|
547
|
+
try {
|
|
548
|
+
const requestedInstance = (req.query.instance as string) || 'default';
|
|
549
|
+
const targetInstance = instances.get(requestedInstance);
|
|
550
|
+
|
|
551
|
+
if (!targetInstance) {
|
|
552
|
+
return res.status(404).json({
|
|
553
|
+
status: 'error',
|
|
554
|
+
connected: false,
|
|
555
|
+
errorMessage: `PostgreSQL instance "${requestedInstance}" not found`,
|
|
556
|
+
autoInitializeEnabled: false,
|
|
557
|
+
adminCredentialsProvided: false,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
let connParams: ReturnType<typeof parseConnectionUrl> | null = null;
|
|
562
|
+
if (config.url) {
|
|
563
|
+
try {
|
|
564
|
+
connParams = parseConnectionUrl(config.url);
|
|
565
|
+
} catch (err) {
|
|
566
|
+
// URL parsing failed, ignore
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
try {
|
|
571
|
+
await targetInstance.query('SELECT 1');
|
|
572
|
+
res.json({
|
|
573
|
+
status: 'healthy',
|
|
574
|
+
connected: true,
|
|
575
|
+
database: connParams?.database,
|
|
576
|
+
user: connParams?.user,
|
|
577
|
+
host: connParams?.host,
|
|
578
|
+
port: connParams?.port,
|
|
579
|
+
autoInitializeEnabled: config.autoInitialize !== false,
|
|
580
|
+
adminCredentialsProvided: !!(config.adminUser && config.adminPassword),
|
|
581
|
+
});
|
|
582
|
+
} catch (err) {
|
|
583
|
+
res.json({
|
|
584
|
+
status: 'error',
|
|
585
|
+
connected: false,
|
|
586
|
+
database: connParams?.database,
|
|
587
|
+
user: connParams?.user,
|
|
588
|
+
host: connParams?.host,
|
|
589
|
+
port: connParams?.port,
|
|
590
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
591
|
+
autoInitializeEnabled: config.autoInitialize !== false,
|
|
592
|
+
adminCredentialsProvided: !!(config.adminUser && config.adminPassword),
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
} catch (err) {
|
|
596
|
+
res.status(500).json({
|
|
597
|
+
status: 'error',
|
|
598
|
+
connected: false,
|
|
599
|
+
errorMessage: err instanceof Error ? err.message : 'Unknown error',
|
|
600
|
+
autoInitializeEnabled: false,
|
|
601
|
+
adminCredentialsProvided: false,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
registry.addRoute({
|
|
608
|
+
method: 'post',
|
|
609
|
+
path: '/initialize',
|
|
610
|
+
pluginId: pluginId,
|
|
611
|
+
handler: async (req: import('express').Request, res: import('express').Response) => {
|
|
612
|
+
try {
|
|
613
|
+
const { instance: requestedInstance, adminUser, adminPassword } = req.body;
|
|
614
|
+
const targetInstance = requestedInstance || 'default';
|
|
615
|
+
|
|
616
|
+
if (!instances.has(targetInstance)) {
|
|
617
|
+
return res.status(404).json({ message: `Instance "${targetInstance}" not found` });
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (!config.url) {
|
|
621
|
+
return res.status(400).json({ message: 'No database URL configured' });
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const connParams = parseConnectionUrl(config.url);
|
|
625
|
+
const effectiveAdminUser = adminUser || config.adminUser;
|
|
626
|
+
const effectiveAdminPassword = adminPassword || config.adminPassword;
|
|
627
|
+
|
|
628
|
+
if (!effectiveAdminUser || !effectiveAdminPassword) {
|
|
629
|
+
return res.status(400).json({
|
|
630
|
+
message: 'Admin credentials required. Provide adminUser and adminPassword.',
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
const adminPool = createAdminPool({
|
|
635
|
+
adminUser: effectiveAdminUser,
|
|
636
|
+
adminPassword: effectiveAdminPassword,
|
|
637
|
+
host: connParams.host,
|
|
638
|
+
port: connParams.port,
|
|
639
|
+
adminDatabase: config.adminDatabase,
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
try {
|
|
643
|
+
await ensureUserExists(adminPool, connParams.user, connParams.password);
|
|
644
|
+
await ensureDatabaseExists(adminPool, connParams.database, connParams.user);
|
|
645
|
+
await grantPermissions(adminPool, connParams.database, connParams.user);
|
|
646
|
+
|
|
647
|
+
logger.info(`Database "${connParams.database}" initialized successfully`);
|
|
648
|
+
res.json({ message: `Database "${connParams.database}" initialized successfully` });
|
|
649
|
+
} finally {
|
|
650
|
+
await adminPool.end();
|
|
651
|
+
}
|
|
652
|
+
} catch (err) {
|
|
653
|
+
logger.error('Database initialization failed', { error: err });
|
|
654
|
+
res.status(500).json({
|
|
655
|
+
message: err instanceof Error ? err.message : 'Unknown error',
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
registry.addRoute({
|
|
662
|
+
method: 'post',
|
|
663
|
+
path: '/recreate',
|
|
664
|
+
pluginId: pluginId,
|
|
665
|
+
handler: async (req: import('express').Request, res: import('express').Response) => {
|
|
666
|
+
try {
|
|
667
|
+
const { instance: requestedInstance, adminUser, adminPassword } = req.body;
|
|
668
|
+
const targetInstance = requestedInstance || 'default';
|
|
669
|
+
|
|
670
|
+
if (!instances.has(targetInstance)) {
|
|
671
|
+
return res.status(404).json({ message: `Instance "${targetInstance}" not found` });
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
if (!config.url) {
|
|
675
|
+
return res.status(400).json({ message: 'No database URL configured' });
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const connParams = parseConnectionUrl(config.url);
|
|
679
|
+
const effectiveAdminUser = adminUser || config.adminUser;
|
|
680
|
+
const effectiveAdminPassword = adminPassword || config.adminPassword;
|
|
681
|
+
|
|
682
|
+
if (!effectiveAdminUser || !effectiveAdminPassword) {
|
|
683
|
+
return res.status(400).json({
|
|
684
|
+
message: 'Admin credentials required. Provide adminUser and adminPassword.',
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const adminPool = createAdminPool({
|
|
689
|
+
adminUser: effectiveAdminUser,
|
|
690
|
+
adminPassword: effectiveAdminPassword,
|
|
691
|
+
host: connParams.host,
|
|
692
|
+
port: connParams.port,
|
|
693
|
+
adminDatabase: config.adminDatabase,
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
try {
|
|
697
|
+
// Drop database if exists
|
|
698
|
+
await adminPool.query(`DROP DATABASE IF EXISTS ${connParams.database}`);
|
|
699
|
+
|
|
700
|
+
// Recreate database
|
|
701
|
+
await adminPool.query(
|
|
702
|
+
`CREATE DATABASE ${connParams.database} OWNER ${connParams.user}`
|
|
703
|
+
);
|
|
704
|
+
|
|
705
|
+
// Grant permissions
|
|
706
|
+
await grantPermissions(adminPool, connParams.database, connParams.user);
|
|
707
|
+
|
|
708
|
+
logger.info(`Database "${connParams.database}" recreated successfully`);
|
|
709
|
+
res.json({ message: `Database "${connParams.database}" recreated successfully` });
|
|
710
|
+
} finally {
|
|
711
|
+
await adminPool.end();
|
|
712
|
+
}
|
|
713
|
+
} catch (err) {
|
|
714
|
+
logger.error('Database recreation failed', { error: err });
|
|
715
|
+
res.status(500).json({
|
|
716
|
+
message: err instanceof Error ? err.message : 'Unknown error',
|
|
717
|
+
});
|
|
718
|
+
}
|
|
719
|
+
},
|
|
720
|
+
});
|
|
721
|
+
|
|
317
722
|
// Register health check if enabled
|
|
318
723
|
if (config.healthCheck !== false) {
|
|
319
724
|
registry.registerHealthCheck({
|
|
@@ -29,8 +29,6 @@ export type {
|
|
|
29
29
|
// Stores
|
|
30
30
|
export { postgresPreferencesStore, deepMerge } from './stores/index.js';
|
|
31
31
|
|
|
32
|
-
// UI Components
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
export { PreferencesManagementPage } from './PreferencesManagementPage.js';
|
|
36
|
-
export type { PreferencesManagementPageProps } from './PreferencesManagementPage.js';
|
|
32
|
+
// UI Components are exported from main package index (@qwickapps/server)
|
|
33
|
+
// Do NOT export here to avoid loading UI dependencies when importing plugins
|
|
34
|
+
|