@qwickapps/server 1.8.1 → 1.9.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 +21 -0
- package/README.md +4 -4
- package/dist/src/core/control-panel.d.ts.map +1 -1
- package/dist/src/core/control-panel.js +10 -7
- package/dist/src/core/control-panel.js.map +1 -1
- package/dist/src/core/gateway.d.ts.map +1 -1
- package/dist/src/core/gateway.js +46 -47
- package/dist/src/core/gateway.js.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysManagementPage.js +1 -1
- package/dist/src/plugins/api-keys/ApiKeysManagementPage.js.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysStatusWidget.js +1 -1
- package/dist/src/plugins/api-keys/ApiKeysStatusWidget.js.map +1 -1
- package/dist/src/plugins/auth/AuthManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/auth/AuthManagementPage.js +1 -1
- package/dist/src/plugins/auth/AuthManagementPage.js.map +1 -1
- package/dist/src/plugins/auth/AuthStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/auth/AuthStatusWidget.js +1 -1
- package/dist/src/plugins/auth/AuthStatusWidget.js.map +1 -1
- package/dist/src/plugins/auth/auth-plugin.d.ts.map +1 -1
- package/dist/src/plugins/auth/auth-plugin.js +9 -0
- package/dist/src/plugins/auth/auth-plugin.js.map +1 -1
- package/dist/src/plugins/bans/BansManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/bans/BansManagementPage.js +1 -1
- package/dist/src/plugins/bans/BansManagementPage.js.map +1 -1
- package/dist/src/plugins/bans/BansStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/bans/BansStatusWidget.js +1 -1
- package/dist/src/plugins/bans/BansStatusWidget.js.map +1 -1
- package/dist/src/plugins/cache/CacheManagementPage.js +1 -1
- package/dist/src/plugins/cache/CacheManagementPage.js.map +1 -1
- package/dist/src/plugins/cache/CacheStatusWidget.js +1 -1
- package/dist/src/plugins/cache/CacheStatusWidget.js.map +1 -1
- package/dist/src/plugins/devices/DevicesManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/devices/DevicesManagementPage.js +1 -1
- package/dist/src/plugins/devices/DevicesManagementPage.js.map +1 -1
- package/dist/src/plugins/devices/DevicesStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/devices/DevicesStatusWidget.js +1 -1
- package/dist/src/plugins/devices/DevicesStatusWidget.js.map +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsManagementPage.js +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsManagementPage.js.map +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsStatusWidget.js +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsStatusWidget.js.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsManagementPage.js +1 -1
- package/dist/src/plugins/entitlements/EntitlementsManagementPage.js.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsStatusWidget.js +1 -1
- package/dist/src/plugins/entitlements/EntitlementsStatusWidget.js.map +1 -1
- package/dist/src/plugins/health/HealthManagementPage.js +1 -1
- package/dist/src/plugins/health/HealthManagementPage.js.map +1 -1
- package/dist/src/plugins/health/HealthStatusWidget.js +1 -1
- package/dist/src/plugins/health/HealthStatusWidget.js.map +1 -1
- package/dist/src/plugins/logs/LogsManagementPage.js +1 -1
- package/dist/src/plugins/logs/LogsManagementPage.js.map +1 -1
- package/dist/src/plugins/logs/LogsStatusWidget.js +1 -1
- package/dist/src/plugins/logs/LogsStatusWidget.js.map +1 -1
- package/dist/src/plugins/maintenance/MaintenanceManagementPage.js +1 -1
- package/dist/src/plugins/maintenance/MaintenanceManagementPage.js.map +1 -1
- package/dist/src/plugins/maintenance/MaintenanceStatusWidget.js +1 -1
- package/dist/src/plugins/maintenance/MaintenanceStatusWidget.js.map +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.js +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.js.map +1 -1
- package/dist/src/plugins/maintenance/seed-executor.js +2 -2
- package/dist/src/plugins/maintenance/seed-executor.js.map +1 -1
- package/dist/src/plugins/maintenance-plugin.d.ts +2 -0
- package/dist/src/plugins/maintenance-plugin.d.ts.map +1 -1
- package/dist/src/plugins/maintenance-plugin.js +402 -2
- package/dist/src/plugins/maintenance-plugin.js.map +1 -1
- package/dist/src/plugins/notifications/NotificationsManagementPage.js +1 -1
- package/dist/src/plugins/notifications/NotificationsManagementPage.js.map +1 -1
- package/dist/src/plugins/notifications/NotificationsStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/notifications/NotificationsStatusWidget.js +1 -1
- package/dist/src/plugins/notifications/NotificationsStatusWidget.js.map +1 -1
- package/dist/src/plugins/parental/ParentalManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/parental/ParentalManagementPage.js +1 -1
- package/dist/src/plugins/parental/ParentalManagementPage.js.map +1 -1
- package/dist/src/plugins/parental/ParentalStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/parental/ParentalStatusWidget.js +1 -1
- package/dist/src/plugins/parental/ParentalStatusWidget.js.map +1 -1
- package/dist/src/plugins/postgres/PostgresManagementPage.js +1 -1
- package/dist/src/plugins/postgres/PostgresManagementPage.js.map +1 -1
- package/dist/src/plugins/postgres/PostgresStatusWidget.js +1 -1
- package/dist/src/plugins/postgres/PostgresStatusWidget.js.map +1 -1
- package/dist/src/plugins/preferences/PreferencesManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/preferences/PreferencesManagementPage.js +1 -1
- package/dist/src/plugins/preferences/PreferencesManagementPage.js.map +1 -1
- package/dist/src/plugins/preferences/PreferencesStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/preferences/PreferencesStatusWidget.js +1 -1
- package/dist/src/plugins/preferences/PreferencesStatusWidget.js.map +1 -1
- package/dist/src/plugins/profiles/ProfilesManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/profiles/ProfilesManagementPage.js +1 -1
- package/dist/src/plugins/profiles/ProfilesManagementPage.js.map +1 -1
- package/dist/src/plugins/profiles/ProfilesStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/profiles/ProfilesStatusWidget.js +1 -1
- package/dist/src/plugins/profiles/ProfilesStatusWidget.js.map +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainManagementPage.js +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainManagementPage.js.map +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainStatusWidget.js +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainStatusWidget.js.map +1 -1
- package/dist/src/plugins/rate-limit/RateLimitManagementPage.js +1 -1
- package/dist/src/plugins/rate-limit/RateLimitManagementPage.js.map +1 -1
- package/dist/src/plugins/rate-limit/RateLimitStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/rate-limit/RateLimitStatusWidget.js +1 -1
- package/dist/src/plugins/rate-limit/RateLimitStatusWidget.js.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsManagementPage.js +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsManagementPage.js.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsStatusWidget.js +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsStatusWidget.js.map +1 -1
- package/dist/src/plugins/usage/UsageManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/usage/UsageManagementPage.js +1 -1
- package/dist/src/plugins/usage/UsageManagementPage.js.map +1 -1
- package/dist/src/plugins/usage/UsageStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/usage/UsageStatusWidget.js +1 -1
- package/dist/src/plugins/usage/UsageStatusWidget.js.map +1 -1
- package/dist/src/plugins/users/UsersManagementPage.js +1 -1
- package/dist/src/plugins/users/UsersManagementPage.js.map +1 -1
- package/dist/src/plugins/users/UsersStatusWidget.js +1 -1
- package/dist/src/plugins/users/UsersStatusWidget.js.map +1 -1
- package/dist/ui/src/api/clientBuilder.d.ts +3 -3
- package/dist/ui/src/api/clientBuilder.js +5 -5
- package/dist/ui/src/api/clientBuilder.js.map +1 -1
- package/dist/ui/src/api/controlPanelApi.js +19 -19
- package/dist/ui/src/api/controlPanelApi.js.map +1 -1
- package/dist/ui/src/components/ControlPanelApp.d.ts.map +1 -1
- package/dist/ui/src/components/ControlPanelApp.js +5 -4
- package/dist/ui/src/components/ControlPanelApp.js.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.d.ts.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.js +3 -1
- package/dist/ui/src/dashboard/builtInWidgets.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js +8 -8
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js +2 -2
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js +4 -4
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js +2 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js +6 -6
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.d.ts +8 -0
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.js +132 -0
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js +6 -6
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/index.d.ts +1 -0
- package/dist/ui/src/dashboard/widgets/index.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/index.js +1 -0
- package/dist/ui/src/dashboard/widgets/index.js.map +1 -1
- package/dist-ui/assets/{index-DRG9n0cx.css → index-De-dCl_t.css} +1 -1
- package/dist-ui/assets/{index-D-4HKPkw.js → index-DnEQCOGR.js} +112 -109
- package/dist-ui/assets/{index-D-4HKPkw.js.map → index-DnEQCOGR.js.map} +1 -1
- package/dist-ui/index.html +2 -2
- package/dist-ui-lib/index.js +2623 -2441
- package/dist-ui-lib/index.js.map +1 -1
- package/dist-ui-lib/src/api/clientBuilder.d.ts +3 -3
- package/dist-ui-lib/src/dashboard/widgets/MigrationManagementWidget.d.ts +7 -0
- package/dist-ui-lib/src/dashboard/widgets/index.d.ts +1 -0
- package/package.json +5 -2
- package/src/core/control-panel.ts +10 -7
- package/src/core/gateway.ts +48 -51
- package/src/plugins/api-keys/ApiKeysManagementPage.tsx +1 -1
- package/src/plugins/api-keys/ApiKeysStatusWidget.tsx +1 -1
- package/src/plugins/auth/AuthManagementPage.tsx +1 -1
- package/src/plugins/auth/AuthStatusWidget.tsx +1 -1
- package/src/plugins/auth/adapters/supertokens-adapter.ts +9 -13
- package/src/plugins/auth/auth-plugin.ts +10 -0
- package/src/plugins/auth/env-config.ts +1 -0
- package/src/plugins/auth/types.ts +2 -0
- package/src/plugins/bans/BansManagementPage.tsx +1 -1
- package/src/plugins/bans/BansStatusWidget.tsx +1 -1
- package/src/plugins/cache/CacheManagementPage.tsx +1 -1
- package/src/plugins/cache/CacheStatusWidget.tsx +1 -1
- package/src/plugins/devices/DevicesManagementPage.tsx +1 -1
- package/src/plugins/devices/DevicesStatusWidget.tsx +1 -1
- package/src/plugins/diagnostics/DiagnosticsManagementPage.tsx +1 -1
- package/src/plugins/diagnostics/DiagnosticsStatusWidget.tsx +1 -1
- package/src/plugins/entitlements/EntitlementsManagementPage.tsx +1 -1
- package/src/plugins/entitlements/EntitlementsStatusWidget.tsx +1 -1
- package/src/plugins/health/HealthManagementPage.tsx +1 -1
- package/src/plugins/health/HealthStatusWidget.tsx +1 -1
- package/src/plugins/logs/LogsManagementPage.tsx +1 -1
- package/src/plugins/logs/LogsStatusWidget.tsx +1 -1
- package/src/plugins/maintenance/MaintenanceManagementPage.tsx +1 -1
- package/src/plugins/maintenance/MaintenanceStatusWidget.tsx +1 -1
- package/src/plugins/maintenance/SeedManagementPage.tsx +1 -1
- package/src/plugins/maintenance/seed-executor.ts +7 -6
- package/src/plugins/maintenance-plugin.ts +501 -5
- package/src/plugins/notifications/NotificationsManagementPage.tsx +1 -1
- package/src/plugins/notifications/NotificationsStatusWidget.tsx +1 -1
- package/src/plugins/parental/ParentalManagementPage.tsx +1 -1
- package/src/plugins/parental/ParentalStatusWidget.tsx +1 -1
- package/src/plugins/postgres/PostgresManagementPage.tsx +1 -1
- package/src/plugins/postgres/PostgresStatusWidget.tsx +1 -1
- package/src/plugins/preferences/PreferencesManagementPage.tsx +1 -1
- package/src/plugins/preferences/PreferencesStatusWidget.tsx +1 -1
- package/src/plugins/profiles/ProfilesManagementPage.tsx +1 -1
- package/src/plugins/profiles/ProfilesStatusWidget.tsx +1 -1
- package/src/plugins/qwickbrain/QwickbrainManagementPage.tsx +1 -1
- package/src/plugins/qwickbrain/QwickbrainStatusWidget.tsx +1 -1
- package/src/plugins/rate-limit/RateLimitManagementPage.tsx +1 -1
- package/src/plugins/rate-limit/RateLimitStatusWidget.tsx +1 -1
- package/src/plugins/subscriptions/SubscriptionsManagementPage.tsx +1 -1
- package/src/plugins/subscriptions/SubscriptionsStatusWidget.tsx +1 -1
- package/src/plugins/usage/UsageManagementPage.tsx +1 -1
- package/src/plugins/usage/UsageStatusWidget.tsx +1 -1
- package/src/plugins/users/UsersManagementPage.tsx +1 -1
- package/src/plugins/users/UsersStatusWidget.tsx +1 -1
- package/ui/src/App.tsx +4 -3
- package/ui/src/api/clientBuilder.ts +5 -5
- package/ui/src/api/controlPanelApi.ts +19 -19
- package/ui/src/components/ControlPanelApp.tsx +5 -4
- package/ui/src/dashboard/builtInWidgets.tsx +3 -0
- package/ui/src/dashboard/widgets/AuthStatusWidget.tsx +3 -8
- package/ui/src/dashboard/widgets/CMSMaintenanceWidget.tsx +8 -8
- package/ui/src/dashboard/widgets/CMSStatusWidget.tsx +2 -2
- package/ui/src/dashboard/widgets/CacheMaintenanceWidget.tsx +4 -4
- package/ui/src/dashboard/widgets/DatabaseOperationsWidget.tsx +2 -1
- package/ui/src/dashboard/widgets/LogsMaintenanceWidget.tsx +6 -6
- package/ui/src/dashboard/widgets/MigrationManagementWidget.tsx +319 -0
- package/ui/src/dashboard/widgets/SeedManagementWidget.tsx +6 -6
- package/ui/src/dashboard/widgets/index.ts +1 -0
- package/ui/src/hooks/useJobStream.ts +2 -2
- package/ui/src/pages/ContentOpsJobsPage.tsx +3 -3
- package/ui/src/pages/PluginPage.tsx +1 -1
- package/ui/src/pages/TenantsManagementPage.tsx +1 -1
|
@@ -18,15 +18,15 @@ export interface RouteManifestEntry {
|
|
|
18
18
|
/**
|
|
19
19
|
* Build typed API client from server manifest
|
|
20
20
|
*
|
|
21
|
-
* Fetches the manifest from /
|
|
21
|
+
* Fetches the manifest from /client-manifest (baseUrl already includes /qapi) and generates a nested
|
|
22
22
|
* client object with methods for each route.
|
|
23
23
|
*
|
|
24
|
-
* @param baseUrl - Base URL
|
|
24
|
+
* @param baseUrl - Base URL for QwickApps Server APIs (e.g., '/qapi' or 'http://localhost:3000/qapi')
|
|
25
25
|
* @returns Promise resolving to the generated client
|
|
26
26
|
*
|
|
27
27
|
* @example
|
|
28
28
|
* ```typescript
|
|
29
|
-
* const client = await buildClientFromManifest<APIClient>('
|
|
29
|
+
* const client = await buildClientFromManifest<APIClient>('/qapi');
|
|
30
30
|
* const logs = await client.logs.query({ limit: 10 });
|
|
31
31
|
* ```
|
|
32
32
|
*/
|
|
@@ -10,6 +10,7 @@ export { NotificationsStatsWidget } from './NotificationsStatsWidget';
|
|
|
10
10
|
export { CMSStatusWidget } from './CMSStatusWidget';
|
|
11
11
|
export { CMSMaintenanceWidget } from './CMSMaintenanceWidget';
|
|
12
12
|
export { SeedManagementWidget } from './SeedManagementWidget';
|
|
13
|
+
export { MigrationManagementWidget } from './MigrationManagementWidget';
|
|
13
14
|
export { ServiceControlWidget } from './ServiceControlWidget';
|
|
14
15
|
export { EnvironmentConfigWidget } from './EnvironmentConfigWidget';
|
|
15
16
|
export { DatabaseOpsWidget } from './DatabaseOpsWidget';
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qwickapps/server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Plugin-based application server framework for building websites, APIs, admin dashboards, and full-stack products",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"main": "dist/src/index.js",
|
|
6
7
|
"types": "dist/src/index.d.ts",
|
|
7
8
|
"exports": {
|
|
@@ -59,6 +60,8 @@
|
|
|
59
60
|
"express": "^4.18.2",
|
|
60
61
|
"helmet": "^7.1.0",
|
|
61
62
|
"http-proxy-middleware": "^3.0.3",
|
|
63
|
+
"prop-types": "^15.8.1",
|
|
64
|
+
"react-is": "^18.2.0",
|
|
62
65
|
"zod": "^3.22.4"
|
|
63
66
|
},
|
|
64
67
|
"devDependencies": {
|
|
@@ -90,7 +93,7 @@
|
|
|
90
93
|
"react": "^18.2.0",
|
|
91
94
|
"react-dom": "^18.2.0",
|
|
92
95
|
"react-router-dom": "^6.30.1",
|
|
93
|
-
"supertokens-node": "^
|
|
96
|
+
"supertokens-node": "^18.0.0",
|
|
94
97
|
"tsx": "^4.20.6",
|
|
95
98
|
"typescript": "^5.3.3",
|
|
96
99
|
"vite": "^6.0.0",
|
|
@@ -200,10 +200,11 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
|
|
|
200
200
|
next();
|
|
201
201
|
});
|
|
202
202
|
|
|
203
|
-
// CRITICAL:
|
|
204
|
-
// This
|
|
205
|
-
//
|
|
206
|
-
|
|
203
|
+
// CRITICAL: QwickApps Server APIs always mount at /qapi regardless of mountPath
|
|
204
|
+
// This avoids conflicts with application-level APIs (e.g., Payload CMS uses /api)
|
|
205
|
+
// /qapi = QwickApps framework APIs (SuperTokens, health, postgres, cache, etc.)
|
|
206
|
+
// /api = Application/CMS APIs (Payload, custom app routes)
|
|
207
|
+
app.use('/qapi', router);
|
|
207
208
|
|
|
208
209
|
// Built-in routes
|
|
209
210
|
|
|
@@ -238,7 +239,7 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
|
|
|
238
239
|
|
|
239
240
|
manifest[key] = {
|
|
240
241
|
method: route.method.toUpperCase(),
|
|
241
|
-
path:
|
|
242
|
+
path: route.path,
|
|
242
243
|
auth: route.auth?.required || false,
|
|
243
244
|
};
|
|
244
245
|
}
|
|
@@ -284,8 +285,10 @@ export function createControlPanel(options: CreateControlPanelOptions): ControlP
|
|
|
284
285
|
const effectivePath = forwardedPrefix || mountPath;
|
|
285
286
|
const normalizedPath = effectivePath === '/' ? '' : effectivePath;
|
|
286
287
|
|
|
287
|
-
// Inject base
|
|
288
|
-
|
|
288
|
+
// Inject base paths as global variables before other scripts
|
|
289
|
+
// __APP_BASE_PATH__: UI mount path for routing (e.g., '/cpanel')
|
|
290
|
+
// __API_BASE_PATH__: Base URL for QwickApps Server APIs (always '/qapi')
|
|
291
|
+
const basePathScript = `<script>window.__APP_BASE_PATH__="${normalizedPath}";window.__API_BASE_PATH__="/qapi";</script>`;
|
|
289
292
|
let html = indexHtmlTemplate.replace('<head>', `<head>\n ${basePathScript}`);
|
|
290
293
|
|
|
291
294
|
// Rewrite asset paths if mounted at a subpath
|
package/src/core/gateway.ts
CHANGED
|
@@ -1311,54 +1311,37 @@ export function createGateway(config: GatewayConfig): GatewayInstance {
|
|
|
1311
1311
|
);
|
|
1312
1312
|
}
|
|
1313
1313
|
|
|
1314
|
-
// 2.
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
//
|
|
1319
|
-
//
|
|
1320
|
-
|
|
1321
|
-
const cpApiPath = `${cpPath}/api`;
|
|
1322
|
-
const apiProxy = createProxyMiddleware({
|
|
1314
|
+
// 2. Create HTTP server (but don't listen yet - need server object for WS)
|
|
1315
|
+
server = createServer(app);
|
|
1316
|
+
|
|
1317
|
+
// 3. Setup mounted apps WITH QwickApps Server API proxy injected at the right position
|
|
1318
|
+
// QwickApps Server APIs (/qapi/*) need to be registered BEFORE root path proxy
|
|
1319
|
+
// to prevent being caught by frontend catch-all routing
|
|
1320
|
+
const qwickAppsApiProxy = createProxyMiddleware({
|
|
1323
1321
|
target: `http://localhost:${cpPort}`,
|
|
1324
1322
|
changeOrigin: true,
|
|
1325
|
-
pathRewrite: (path) => `/api${path}`, // Express removed {cpPath}/api, add back just /api (control panel APIs are at /api/*)
|
|
1326
1323
|
on: {
|
|
1327
1324
|
proxyReq: (proxyReq, req) => {
|
|
1328
|
-
|
|
1329
|
-
proxyReq.setHeader('X-Forwarded-Prefix', cpPath);
|
|
1330
|
-
logger.debug(`[API Proxy] Forwarding ${req.method} ${req.url} to control panel`);
|
|
1325
|
+
logger.debug(`[QwickApps API] Forwarding ${req.method} ${req.url} to QwickApps Server`);
|
|
1331
1326
|
},
|
|
1332
1327
|
proxyRes: (proxyRes, req) => {
|
|
1333
|
-
logger.debug(`[API
|
|
1328
|
+
logger.debug(`[QwickApps API] Response for ${req.url}: ${proxyRes.statusCode}`);
|
|
1334
1329
|
},
|
|
1335
1330
|
error: (err: Error, req: IncomingMessage, res: ServerResponse | Socket) => {
|
|
1336
|
-
logger.error(`[API
|
|
1331
|
+
logger.error(`[QwickApps API] Error for ${req.url}`, { error: err.message });
|
|
1337
1332
|
if (res && 'writeHead' in res && !res.headersSent) {
|
|
1338
1333
|
res.writeHead(503, { 'Content-Type': 'application/json' });
|
|
1339
1334
|
res.end(JSON.stringify({
|
|
1340
1335
|
error: 'Service Unavailable',
|
|
1341
|
-
message: '
|
|
1336
|
+
message: 'QwickApps Server APIs are currently unavailable.',
|
|
1342
1337
|
details: nodeEnv === 'development' ? err.message : undefined,
|
|
1343
1338
|
}));
|
|
1344
1339
|
}
|
|
1345
1340
|
},
|
|
1346
1341
|
},
|
|
1347
1342
|
});
|
|
1348
|
-
app.use(cpApiPath, apiProxy);
|
|
1349
|
-
logger.debug(`Setting up proxy: ${cpApiPath} -> http://localhost:${cpPort}/api`);
|
|
1350
|
-
mountedApps.push({ path: cpApiPath, type: 'proxy', target: `http://localhost:${cpPort}` });
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
// 3. Create HTTP server (but don't listen yet - need server object for WS)
|
|
1354
|
-
server = createServer(app);
|
|
1355
1343
|
|
|
1356
|
-
|
|
1357
|
-
if (cpEnabled) {
|
|
1358
|
-
const cpPort = parseInt(process.env.CPANEL_PORT || String(gatewayPort + 1), 10);
|
|
1359
|
-
|
|
1360
|
-
// Proxy /cpanel to control panel UI
|
|
1361
|
-
// Express strips cpPath prefix, so we need custom proxy to add it back
|
|
1344
|
+
// Create control panel UI proxy (will be registered before root proxy)
|
|
1362
1345
|
const cpUiProxy = createProxyMiddleware({
|
|
1363
1346
|
target: `http://localhost:${cpPort}`,
|
|
1364
1347
|
changeOrigin: true,
|
|
@@ -1378,23 +1361,46 @@ export function createGateway(config: GatewayConfig): GatewayInstance {
|
|
|
1378
1361
|
},
|
|
1379
1362
|
},
|
|
1380
1363
|
});
|
|
1381
|
-
app.use(cpPath, cpUiProxy);
|
|
1382
1364
|
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1365
|
+
const apps = config.apps || [];
|
|
1366
|
+
for (const appConfig of apps) {
|
|
1367
|
+
// IMPORTANT: Insert QwickApps Server and Control Panel proxies BEFORE root path proxy
|
|
1368
|
+
// This ensures /qapi/* and /cpanel requests don't get caught by frontend catch-all routing
|
|
1369
|
+
if (appConfig.path === '/') {
|
|
1370
|
+
// 1. Register QwickApps Server API proxy at /qapi/* BEFORE the root proxy
|
|
1371
|
+
// These are framework APIs: SuperTokens auth, health checks, postgres, cache, etc.
|
|
1372
|
+
// Payload CMS uses natural Next.js /api/* path
|
|
1373
|
+
app.use((req, res, next) => {
|
|
1374
|
+
if (req.path.startsWith('/qapi/')) {
|
|
1375
|
+
return qwickAppsApiProxy(req, res, next);
|
|
1376
|
+
}
|
|
1377
|
+
next();
|
|
1378
|
+
});
|
|
1379
|
+
logger.debug(`Setting up proxy: /qapi/* -> http://localhost:${cpPort} (QwickApps Server APIs)`);
|
|
1380
|
+
mountedApps.push({ path: '/qapi', type: 'proxy', target: `http://localhost:${cpPort}` });
|
|
1381
|
+
|
|
1382
|
+
// 2. Register Control Panel UI proxy BEFORE the root proxy
|
|
1383
|
+
app.use(cpPath, cpUiProxy);
|
|
1384
|
+
mountedApps.push({ path: cpPath, type: 'proxy', target: `http://localhost:${cpPort}` });
|
|
1385
|
+
|
|
1386
|
+
// 3. Setup WebSocket upgrade handling for control panel UI
|
|
1387
|
+
server!.on('upgrade', (req: IncomingMessage, socket: Duplex, head: Buffer) => {
|
|
1388
|
+
if (req.url?.startsWith(cpPath)) {
|
|
1389
|
+
cpUiProxy.upgrade?.(req, socket as Socket, head);
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1387
1392
|
}
|
|
1388
|
-
});
|
|
1389
1393
|
|
|
1390
|
-
|
|
1394
|
+
// Now register the app itself
|
|
1395
|
+
if (appConfig.source.type === 'proxy') {
|
|
1396
|
+
setupProxyApp(appConfig, server);
|
|
1397
|
+
} else {
|
|
1398
|
+
setupStaticApp(appConfig);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1391
1401
|
}
|
|
1392
1402
|
|
|
1393
|
-
//
|
|
1394
|
-
const apps = config.apps || [];
|
|
1395
|
-
|
|
1396
|
-
// If plugins need maintenance, add root-level maintenance page
|
|
1397
|
-
// but keep /cpanel and /diagnostics accessible
|
|
1403
|
+
// 6. Add maintenance page if needed (after all proxies are set up)
|
|
1398
1404
|
if (cpEnabled) {
|
|
1399
1405
|
const pluginsNeedingMaintenance = controlPanelInstance!.getPluginRegistry().getPluginsNeedingMaintenance();
|
|
1400
1406
|
if (pluginsNeedingMaintenance.length > 0) {
|
|
@@ -1429,16 +1435,7 @@ export function createGateway(config: GatewayConfig): GatewayInstance {
|
|
|
1429
1435
|
}
|
|
1430
1436
|
}
|
|
1431
1437
|
|
|
1432
|
-
// Setup
|
|
1433
|
-
for (const appConfig of apps) {
|
|
1434
|
-
if (appConfig.source.type === 'proxy') {
|
|
1435
|
-
setupProxyApp(appConfig, server);
|
|
1436
|
-
} else {
|
|
1437
|
-
setupStaticApp(appConfig);
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
|
|
1441
|
-
// 6. Setup frontend app at root
|
|
1438
|
+
// 7. Setup frontend app at root
|
|
1442
1439
|
setupFrontendApp();
|
|
1443
1440
|
|
|
1444
1441
|
// 7. Start listening (LAST STEP - after all middleware is registered)
|
|
@@ -24,7 +24,7 @@ interface ApiKey {
|
|
|
24
24
|
usageCount: number;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export function ApiKeysManagementPage({ apiPrefix = '/
|
|
27
|
+
export function ApiKeysManagementPage({ apiPrefix = '/qapi/api-keys' }: ApiKeysManagementPageProps) {
|
|
28
28
|
const [activeTab, setActiveTab] = useState<'all' | 'active' | 'expired' | 'config'>('all');
|
|
29
29
|
const [keys, setKeys] = useState<ApiKey[]>([]);
|
|
30
30
|
const [loading, setLoading] = useState(true);
|
|
@@ -10,7 +10,7 @@ export interface ApiKeysStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function ApiKeysStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function ApiKeysStatusWidget({ apiPrefix = '/qapi/api-keys' }: ApiKeysStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalKeys: number;
|
|
16
16
|
activeKeys: number;
|
|
@@ -30,7 +30,7 @@ interface AuthSession {
|
|
|
30
30
|
ipAddress?: string;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export function AuthManagementPage({ apiPrefix = '/
|
|
33
|
+
export function AuthManagementPage({ apiPrefix = '/qapi/auth' }: AuthManagementPageProps) {
|
|
34
34
|
const [activeTab, setActiveTab] = useState<'overview' | 'providers' | 'sessions' | 'config'>('overview');
|
|
35
35
|
const [providers, setProviders] = useState<AuthProvider[]>([]);
|
|
36
36
|
const [sessions, setSessions] = useState<AuthSession[]>([]);
|
|
@@ -10,7 +10,7 @@ export interface AuthStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function AuthStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function AuthStatusWidget({ apiPrefix = '/qapi/auth' }: AuthStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalProviders: number;
|
|
16
16
|
activeProviders: number;
|
|
@@ -47,14 +47,12 @@ export function supertokensAdapter(config: SupertokensAdapterConfig): AuthAdapte
|
|
|
47
47
|
// Store response on request for later use in getUser()
|
|
48
48
|
(req as SupertokensExtendedRequest)[REQUEST_RES_KEY] = res;
|
|
49
49
|
|
|
50
|
-
// Skip if already initialized with error
|
|
50
|
+
// Skip if already initialized with error — let auth-checking middleware
|
|
51
|
+
// decide whether to block the request based on authRequired config.
|
|
52
|
+
// Returning 500 here blocks ALL routes (including /auth/config/status)
|
|
53
|
+
// even when authRequired is false.
|
|
51
54
|
if (initializationError) {
|
|
52
|
-
return
|
|
53
|
-
error: 'Auth Configuration Error',
|
|
54
|
-
message:
|
|
55
|
-
'Supertokens is not properly configured. Install supertokens-node package: npm install supertokens-node',
|
|
56
|
-
details: initializationError.message,
|
|
57
|
-
});
|
|
55
|
+
return next();
|
|
58
56
|
}
|
|
59
57
|
|
|
60
58
|
// Lazy initialize Supertokens
|
|
@@ -163,12 +161,10 @@ export function supertokensAdapter(config: SupertokensAdapterConfig): AuthAdapte
|
|
|
163
161
|
initializationError =
|
|
164
162
|
error instanceof Error ? error : new Error('Failed to initialize Supertokens');
|
|
165
163
|
console.error('[SupertokensAdapter] Initialization error:', error);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
details: initializationError.message,
|
|
171
|
-
});
|
|
164
|
+
// Let the auth-checking middleware decide whether to block this request.
|
|
165
|
+
// Non-auth routes (e.g. /auth/config/status) must remain accessible
|
|
166
|
+
// so the UI can display the configuration error state.
|
|
167
|
+
return next();
|
|
172
168
|
}
|
|
173
169
|
}
|
|
174
170
|
|
|
@@ -73,6 +73,16 @@ export function createAuthPlugin(config: AuthPluginConfig): Plugin {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
// Register SuperTokens middleware on router for auth paths
|
|
77
|
+
// Uses config.apiBasePath so Gateway forwards requests to the correct path
|
|
78
|
+
// e.g. SUPERTOKENS_API_BASE_PATH=/qapi/auth → router.use('/qapi/auth', ...)
|
|
79
|
+
const authBasePath = config.apiBasePath ?? '/auth';
|
|
80
|
+
if (Array.isArray(primaryMiddleware)) {
|
|
81
|
+
router.use(authBasePath, ...primaryMiddleware);
|
|
82
|
+
} else {
|
|
83
|
+
router.use(authBasePath, primaryMiddleware);
|
|
84
|
+
}
|
|
85
|
+
|
|
76
86
|
// Add the auth checking middleware to router (not app)
|
|
77
87
|
// This ensures it processes requests to /api/* routes
|
|
78
88
|
router.use(createAuthMiddleware());
|
|
@@ -402,6 +402,7 @@ export function createAuthPluginFromEnv(options?: AuthEnvPluginOptions): Plugin
|
|
|
402
402
|
excludePaths,
|
|
403
403
|
authRequired,
|
|
404
404
|
debug,
|
|
405
|
+
apiBasePath: getEnv('SUPERTOKENS_API_BASE_PATH') ?? getEnv('AUTH_API_BASE_PATH'),
|
|
405
406
|
onUnauthorized: options?.onUnauthorized,
|
|
406
407
|
onAuthenticated: options?.onAuthenticated,
|
|
407
408
|
};
|
|
@@ -180,6 +180,8 @@ export interface AuthPluginConfig {
|
|
|
180
180
|
excludePaths?: string[];
|
|
181
181
|
/** Whether auth is required for all routes (default: true) */
|
|
182
182
|
authRequired?: boolean;
|
|
183
|
+
/** API base path for auth routes registered on the Express router (default: '/auth') */
|
|
184
|
+
apiBasePath?: string;
|
|
183
185
|
/** Custom unauthorized handler */
|
|
184
186
|
onUnauthorized?: (req: Request, res: Response) => void;
|
|
185
187
|
/**
|
|
@@ -23,7 +23,7 @@ interface Ban {
|
|
|
23
23
|
createdBy?: string;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function BansManagementPage({ apiPrefix = '/
|
|
26
|
+
export function BansManagementPage({ apiPrefix = '/qapi/bans' }: BansManagementPageProps) {
|
|
27
27
|
const [activeTab, setActiveTab] = useState<'all' | 'active' | 'expired' | 'config'>('all');
|
|
28
28
|
const [bans, setBans] = useState<Ban[]>([]);
|
|
29
29
|
const [loading, setLoading] = useState(true);
|
|
@@ -10,7 +10,7 @@ export interface BansStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function BansStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function BansStatusWidget({ apiPrefix = '/qapi/bans' }: BansStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalBans: number;
|
|
16
16
|
activeBans: number;
|
|
@@ -32,7 +32,7 @@ interface CacheInfo {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export const CacheManagementPage: React.FC<CacheManagementPageProps> = ({
|
|
35
|
-
apiPrefix = '/
|
|
35
|
+
apiPrefix = '/qapi/plugins/cache',
|
|
36
36
|
}) => {
|
|
37
37
|
const [keys, setKeys] = useState<CacheKey[]>([]);
|
|
38
38
|
const [info, setInfo] = useState<CacheInfo | null>(null);
|
|
@@ -26,7 +26,7 @@ interface CacheStats {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export const CacheStatusWidget: React.FC<CacheStatusWidgetProps> = ({
|
|
29
|
-
apiPrefix = '/
|
|
29
|
+
apiPrefix = '/qapi/plugins/cache',
|
|
30
30
|
}) => {
|
|
31
31
|
const [stats, setStats] = useState<CacheStats | null>(null);
|
|
32
32
|
const [loading, setLoading] = useState(true);
|
|
@@ -23,7 +23,7 @@ interface Device {
|
|
|
23
23
|
lastActiveAt?: string;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export function DevicesManagementPage({ apiPrefix = '/
|
|
26
|
+
export function DevicesManagementPage({ apiPrefix = '/qapi/devices' }: DevicesManagementPageProps) {
|
|
27
27
|
const [activeTab, setActiveTab] = useState<'all' | 'active' | 'pending' | 'config'>('all');
|
|
28
28
|
const [devices, setDevices] = useState<Device[]>([]);
|
|
29
29
|
const [loading, setLoading] = useState(true);
|
|
@@ -10,7 +10,7 @@ export interface DevicesStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function DevicesStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function DevicesStatusWidget({ apiPrefix = '/qapi/devices' }: DevicesStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalDevices: number;
|
|
16
16
|
activeDevices: number;
|
|
@@ -38,7 +38,7 @@ interface DiagnosticsReport {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export const DiagnosticsManagementPage: React.FC<DiagnosticsManagementPageProps> = ({
|
|
41
|
-
apiPrefix = '/
|
|
41
|
+
apiPrefix = '/qapi/plugins/diagnostics',
|
|
42
42
|
}) => {
|
|
43
43
|
const [report, setReport] = useState<DiagnosticsReport | null>(null);
|
|
44
44
|
const [loading, setLoading] = useState(true);
|
|
@@ -26,7 +26,7 @@ interface DiagnosticsStats {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export const DiagnosticsStatusWidget: React.FC<DiagnosticsStatusWidgetProps> = ({
|
|
29
|
-
apiPrefix = '/
|
|
29
|
+
apiPrefix = '/qapi/plugins/diagnostics',
|
|
30
30
|
}) => {
|
|
31
31
|
const [stats, setStats] = useState<DiagnosticsStats | null>(null);
|
|
32
32
|
const [loading, setLoading] = useState(true);
|
|
@@ -22,7 +22,7 @@ interface Entitlement {
|
|
|
22
22
|
grantedBy?: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export function EntitlementsManagementPage({ apiPrefix = '/
|
|
25
|
+
export function EntitlementsManagementPage({ apiPrefix = '/qapi/entitlements' }: EntitlementsManagementPageProps) {
|
|
26
26
|
const [activeTab, setActiveTab] = useState<'all' | 'active' | 'expired' | 'config'>('all');
|
|
27
27
|
const [entitlements, setEntitlements] = useState<Entitlement[]>([]);
|
|
28
28
|
const [loading, setLoading] = useState(true);
|
|
@@ -10,7 +10,7 @@ export interface EntitlementsStatusWidgetProps {
|
|
|
10
10
|
apiPrefix?: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function EntitlementsStatusWidget({ apiPrefix = '/
|
|
13
|
+
export function EntitlementsStatusWidget({ apiPrefix = '/qapi/entitlements' }: EntitlementsStatusWidgetProps) {
|
|
14
14
|
const [stats, setStats] = useState<{
|
|
15
15
|
totalEntitlements: number;
|
|
16
16
|
activeEntitlements: number;
|
|
@@ -33,7 +33,7 @@ interface HealthSummary {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export const HealthManagementPage: React.FC<HealthManagementPageProps> = ({
|
|
36
|
-
apiPrefix = '/
|
|
36
|
+
apiPrefix = '/qapi/plugins/health',
|
|
37
37
|
}) => {
|
|
38
38
|
const [summary, setSummary] = useState<HealthSummary | null>(null);
|
|
39
39
|
const [selectedCheck, setSelectedCheck] = useState<HealthCheckResult | null>(null);
|
|
@@ -33,7 +33,7 @@ interface HealthSummary {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
export const HealthStatusWidget: React.FC<HealthStatusWidgetProps> = ({
|
|
36
|
-
apiPrefix = '/
|
|
36
|
+
apiPrefix = '/qapi/plugins/health',
|
|
37
37
|
}) => {
|
|
38
38
|
const [summary, setSummary] = useState<HealthSummary | null>(null);
|
|
39
39
|
const [loading, setLoading] = useState(true);
|
|
@@ -42,7 +42,7 @@ interface LogStats {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export const LogsManagementPage: React.FC<LogsManagementPageProps> = ({
|
|
45
|
-
apiPrefix = '/
|
|
45
|
+
apiPrefix = '/qapi/plugins/logs',
|
|
46
46
|
}) => {
|
|
47
47
|
const [logs, setLogs] = useState<LogEntry[]>([]);
|
|
48
48
|
const [sources, setSources] = useState<LogSource[]>([]);
|
|
@@ -28,7 +28,7 @@ interface LogStats {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export const LogsStatusWidget: React.FC<LogsStatusWidgetProps> = ({
|
|
31
|
-
apiPrefix = '/
|
|
31
|
+
apiPrefix = '/qapi/plugins/logs',
|
|
32
32
|
}) => {
|
|
33
33
|
const [stats, setStats] = useState<LogStats | null>(null);
|
|
34
34
|
const [loading, setLoading] = useState(true);
|
|
@@ -30,7 +30,7 @@ interface MaintenanceStatus {
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export const MaintenanceManagementPage: React.FC<MaintenanceManagementPageProps> = ({
|
|
33
|
-
apiPrefix = '/
|
|
33
|
+
apiPrefix = '/qapi/plugins/maintenance',
|
|
34
34
|
}) => {
|
|
35
35
|
const [status, setStatus] = useState<MaintenanceStatus | null>(null);
|
|
36
36
|
const [loading, setLoading] = useState(true);
|
|
@@ -26,7 +26,7 @@ interface MaintenanceStatus {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
export const MaintenanceStatusWidget: React.FC<MaintenanceStatusWidgetProps> = ({
|
|
29
|
-
apiPrefix = '/
|
|
29
|
+
apiPrefix = '/qapi/plugins/maintenance',
|
|
30
30
|
}) => {
|
|
31
31
|
const [status, setStatus] = useState<MaintenanceStatus | null>(null);
|
|
32
32
|
const [loading, setLoading] = useState(true);
|
|
@@ -19,7 +19,7 @@ export interface SeedManagementPageProps {
|
|
|
19
19
|
type Tab = 'list' | 'execute' | 'history';
|
|
20
20
|
|
|
21
21
|
export const SeedManagementPage: React.FC<SeedManagementPageProps> = ({
|
|
22
|
-
apiPrefix = '/
|
|
22
|
+
apiPrefix = '/qapi/plugins/maintenance',
|
|
23
23
|
}) => {
|
|
24
24
|
const [activeTab, setActiveTab] = useState<Tab>('list');
|
|
25
25
|
const [selectedSeed, setSelectedSeed] = useState<string | null>(null);
|
|
@@ -45,8 +45,8 @@ const MAX_OUTPUT_SIZE = 100 * 1024;
|
|
|
45
45
|
* @returns Resolved absolute path if valid, null if invalid
|
|
46
46
|
*/
|
|
47
47
|
export function validateScriptPath(scriptPath: string, scriptsPath: string): string | null {
|
|
48
|
-
// Only allow .mjs files
|
|
49
|
-
if (!scriptPath.endsWith('.mjs')) {
|
|
48
|
+
// Only allow .mjs and .ts files
|
|
49
|
+
if (!scriptPath.endsWith('.mjs') && !scriptPath.endsWith('.ts')) {
|
|
50
50
|
return null;
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -106,11 +106,12 @@ export class SeedExecutor {
|
|
|
106
106
|
this.outputSize = 0;
|
|
107
107
|
|
|
108
108
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
109
|
+
// .ts/.mts files require tsx for TypeScript compilation.
|
|
110
|
+
// .mjs files are native ESM and run directly with node — tsx is not
|
|
111
|
+
// available in the system PATH in Docker production builds.
|
|
112
|
+
const needsTsx = scriptPath.match(/\.(ts|mts)$/);
|
|
112
113
|
const execCommand = needsTsx ? 'tsx' : process.execPath;
|
|
113
|
-
const execArgs =
|
|
114
|
+
const execArgs = [scriptPath];
|
|
114
115
|
|
|
115
116
|
// Spawn process with TypeScript support if needed
|
|
116
117
|
this.child = spawn(execCommand, execArgs, {
|