@qwickapps/server 1.8.1 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/dist/src/core/control-panel.d.ts.map +1 -1
- package/dist/src/core/control-panel.js +10 -7
- package/dist/src/core/control-panel.js.map +1 -1
- package/dist/src/core/gateway.d.ts.map +1 -1
- package/dist/src/core/gateway.js +46 -47
- package/dist/src/core/gateway.js.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysManagementPage.js +1 -1
- package/dist/src/plugins/api-keys/ApiKeysManagementPage.js.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/api-keys/ApiKeysStatusWidget.js +1 -1
- package/dist/src/plugins/api-keys/ApiKeysStatusWidget.js.map +1 -1
- package/dist/src/plugins/auth/AuthManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/auth/AuthManagementPage.js +1 -1
- package/dist/src/plugins/auth/AuthManagementPage.js.map +1 -1
- package/dist/src/plugins/auth/AuthStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/auth/AuthStatusWidget.js +1 -1
- package/dist/src/plugins/auth/AuthStatusWidget.js.map +1 -1
- package/dist/src/plugins/auth/auth-plugin.d.ts.map +1 -1
- package/dist/src/plugins/auth/auth-plugin.js +9 -0
- package/dist/src/plugins/auth/auth-plugin.js.map +1 -1
- package/dist/src/plugins/bans/BansManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/bans/BansManagementPage.js +1 -1
- package/dist/src/plugins/bans/BansManagementPage.js.map +1 -1
- package/dist/src/plugins/bans/BansStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/bans/BansStatusWidget.js +1 -1
- package/dist/src/plugins/bans/BansStatusWidget.js.map +1 -1
- package/dist/src/plugins/cache/CacheManagementPage.js +1 -1
- package/dist/src/plugins/cache/CacheManagementPage.js.map +1 -1
- package/dist/src/plugins/cache/CacheStatusWidget.js +1 -1
- package/dist/src/plugins/cache/CacheStatusWidget.js.map +1 -1
- package/dist/src/plugins/devices/DevicesManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/devices/DevicesManagementPage.js +1 -1
- package/dist/src/plugins/devices/DevicesManagementPage.js.map +1 -1
- package/dist/src/plugins/devices/DevicesStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/devices/DevicesStatusWidget.js +1 -1
- package/dist/src/plugins/devices/DevicesStatusWidget.js.map +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsManagementPage.js +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsManagementPage.js.map +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsStatusWidget.js +1 -1
- package/dist/src/plugins/diagnostics/DiagnosticsStatusWidget.js.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsManagementPage.js +1 -1
- package/dist/src/plugins/entitlements/EntitlementsManagementPage.js.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/entitlements/EntitlementsStatusWidget.js +1 -1
- package/dist/src/plugins/entitlements/EntitlementsStatusWidget.js.map +1 -1
- package/dist/src/plugins/health/HealthManagementPage.js +1 -1
- package/dist/src/plugins/health/HealthManagementPage.js.map +1 -1
- package/dist/src/plugins/health/HealthStatusWidget.js +1 -1
- package/dist/src/plugins/health/HealthStatusWidget.js.map +1 -1
- package/dist/src/plugins/logs/LogsManagementPage.js +1 -1
- package/dist/src/plugins/logs/LogsManagementPage.js.map +1 -1
- package/dist/src/plugins/logs/LogsStatusWidget.js +1 -1
- package/dist/src/plugins/logs/LogsStatusWidget.js.map +1 -1
- package/dist/src/plugins/maintenance/MaintenanceManagementPage.js +1 -1
- package/dist/src/plugins/maintenance/MaintenanceManagementPage.js.map +1 -1
- package/dist/src/plugins/maintenance/MaintenanceStatusWidget.js +1 -1
- package/dist/src/plugins/maintenance/MaintenanceStatusWidget.js.map +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.js +1 -1
- package/dist/src/plugins/maintenance/SeedManagementPage.js.map +1 -1
- package/dist/src/plugins/maintenance/seed-executor.js +2 -2
- package/dist/src/plugins/maintenance/seed-executor.js.map +1 -1
- package/dist/src/plugins/maintenance-plugin.d.ts +2 -0
- package/dist/src/plugins/maintenance-plugin.d.ts.map +1 -1
- package/dist/src/plugins/maintenance-plugin.js +402 -2
- package/dist/src/plugins/maintenance-plugin.js.map +1 -1
- package/dist/src/plugins/notifications/NotificationsManagementPage.js +1 -1
- package/dist/src/plugins/notifications/NotificationsManagementPage.js.map +1 -1
- package/dist/src/plugins/notifications/NotificationsStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/notifications/NotificationsStatusWidget.js +1 -1
- package/dist/src/plugins/notifications/NotificationsStatusWidget.js.map +1 -1
- package/dist/src/plugins/parental/ParentalManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/parental/ParentalManagementPage.js +1 -1
- package/dist/src/plugins/parental/ParentalManagementPage.js.map +1 -1
- package/dist/src/plugins/parental/ParentalStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/parental/ParentalStatusWidget.js +1 -1
- package/dist/src/plugins/parental/ParentalStatusWidget.js.map +1 -1
- package/dist/src/plugins/postgres/PostgresManagementPage.js +1 -1
- package/dist/src/plugins/postgres/PostgresManagementPage.js.map +1 -1
- package/dist/src/plugins/postgres/PostgresStatusWidget.js +1 -1
- package/dist/src/plugins/postgres/PostgresStatusWidget.js.map +1 -1
- package/dist/src/plugins/preferences/PreferencesManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/preferences/PreferencesManagementPage.js +1 -1
- package/dist/src/plugins/preferences/PreferencesManagementPage.js.map +1 -1
- package/dist/src/plugins/preferences/PreferencesStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/preferences/PreferencesStatusWidget.js +1 -1
- package/dist/src/plugins/preferences/PreferencesStatusWidget.js.map +1 -1
- package/dist/src/plugins/profiles/ProfilesManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/profiles/ProfilesManagementPage.js +1 -1
- package/dist/src/plugins/profiles/ProfilesManagementPage.js.map +1 -1
- package/dist/src/plugins/profiles/ProfilesStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/profiles/ProfilesStatusWidget.js +1 -1
- package/dist/src/plugins/profiles/ProfilesStatusWidget.js.map +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainManagementPage.js +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainManagementPage.js.map +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainStatusWidget.js +1 -1
- package/dist/src/plugins/qwickbrain/QwickbrainStatusWidget.js.map +1 -1
- package/dist/src/plugins/rate-limit/RateLimitManagementPage.js +1 -1
- package/dist/src/plugins/rate-limit/RateLimitManagementPage.js.map +1 -1
- package/dist/src/plugins/rate-limit/RateLimitStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/rate-limit/RateLimitStatusWidget.js +1 -1
- package/dist/src/plugins/rate-limit/RateLimitStatusWidget.js.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsManagementPage.js +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsManagementPage.js.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsStatusWidget.js +1 -1
- package/dist/src/plugins/subscriptions/SubscriptionsStatusWidget.js.map +1 -1
- package/dist/src/plugins/usage/UsageManagementPage.d.ts.map +1 -1
- package/dist/src/plugins/usage/UsageManagementPage.js +1 -1
- package/dist/src/plugins/usage/UsageManagementPage.js.map +1 -1
- package/dist/src/plugins/usage/UsageStatusWidget.d.ts.map +1 -1
- package/dist/src/plugins/usage/UsageStatusWidget.js +1 -1
- package/dist/src/plugins/usage/UsageStatusWidget.js.map +1 -1
- package/dist/src/plugins/users/UsersManagementPage.js +1 -1
- package/dist/src/plugins/users/UsersManagementPage.js.map +1 -1
- package/dist/src/plugins/users/UsersStatusWidget.js +1 -1
- package/dist/src/plugins/users/UsersStatusWidget.js.map +1 -1
- package/dist/ui/src/api/clientBuilder.d.ts +3 -3
- package/dist/ui/src/api/clientBuilder.js +5 -5
- package/dist/ui/src/api/clientBuilder.js.map +1 -1
- package/dist/ui/src/api/controlPanelApi.js +19 -19
- package/dist/ui/src/api/controlPanelApi.js.map +1 -1
- package/dist/ui/src/components/ControlPanelApp.d.ts.map +1 -1
- package/dist/ui/src/components/ControlPanelApp.js +5 -4
- package/dist/ui/src/components/ControlPanelApp.js.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.d.ts.map +1 -1
- package/dist/ui/src/dashboard/builtInWidgets.js +3 -1
- package/dist/ui/src/dashboard/builtInWidgets.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js +8 -8
- package/dist/ui/src/dashboard/widgets/CMSMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js +2 -2
- package/dist/ui/src/dashboard/widgets/CMSStatusWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js +4 -4
- package/dist/ui/src/dashboard/widgets/CacheMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js +2 -1
- package/dist/ui/src/dashboard/widgets/DatabaseOperationsWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js +6 -6
- package/dist/ui/src/dashboard/widgets/LogsMaintenanceWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.d.ts +8 -0
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.d.ts.map +1 -0
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.js +132 -0
- package/dist/ui/src/dashboard/widgets/MigrationManagementWidget.js.map +1 -0
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js +6 -6
- package/dist/ui/src/dashboard/widgets/SeedManagementWidget.js.map +1 -1
- package/dist/ui/src/dashboard/widgets/index.d.ts +1 -0
- package/dist/ui/src/dashboard/widgets/index.d.ts.map +1 -1
- package/dist/ui/src/dashboard/widgets/index.js +1 -0
- package/dist/ui/src/dashboard/widgets/index.js.map +1 -1
- package/dist-ui/assets/{index-D-4HKPkw.js → index-Cez_jyhl.js} +111 -108
- package/dist-ui/assets/{index-D-4HKPkw.js.map → index-Cez_jyhl.js.map} +1 -1
- package/dist-ui/assets/{index-DRG9n0cx.css → index-De-dCl_t.css} +1 -1
- package/dist-ui/index.html +2 -2
- package/dist-ui-lib/index.js +2623 -2441
- package/dist-ui-lib/index.js.map +1 -1
- package/dist-ui-lib/src/api/clientBuilder.d.ts +3 -3
- package/dist-ui-lib/src/dashboard/widgets/MigrationManagementWidget.d.ts +7 -0
- package/dist-ui-lib/src/dashboard/widgets/index.d.ts +1 -0
- package/package.json +27 -26
- package/src/core/control-panel.ts +10 -7
- package/src/core/gateway.ts +48 -51
- package/src/plugins/api-keys/ApiKeysManagementPage.tsx +1 -1
- package/src/plugins/api-keys/ApiKeysStatusWidget.tsx +1 -1
- package/src/plugins/auth/AuthManagementPage.tsx +1 -1
- package/src/plugins/auth/AuthStatusWidget.tsx +1 -1
- package/src/plugins/auth/auth-plugin.ts +9 -0
- package/src/plugins/bans/BansManagementPage.tsx +1 -1
- package/src/plugins/bans/BansStatusWidget.tsx +1 -1
- package/src/plugins/cache/CacheManagementPage.tsx +1 -1
- package/src/plugins/cache/CacheStatusWidget.tsx +1 -1
- package/src/plugins/devices/DevicesManagementPage.tsx +1 -1
- package/src/plugins/devices/DevicesStatusWidget.tsx +1 -1
- package/src/plugins/diagnostics/DiagnosticsManagementPage.tsx +1 -1
- package/src/plugins/diagnostics/DiagnosticsStatusWidget.tsx +1 -1
- package/src/plugins/entitlements/EntitlementsManagementPage.tsx +1 -1
- package/src/plugins/entitlements/EntitlementsStatusWidget.tsx +1 -1
- package/src/plugins/health/HealthManagementPage.tsx +1 -1
- package/src/plugins/health/HealthStatusWidget.tsx +1 -1
- package/src/plugins/logs/LogsManagementPage.tsx +1 -1
- package/src/plugins/logs/LogsStatusWidget.tsx +1 -1
- package/src/plugins/maintenance/MaintenanceManagementPage.tsx +1 -1
- package/src/plugins/maintenance/MaintenanceStatusWidget.tsx +1 -1
- package/src/plugins/maintenance/SeedManagementPage.tsx +1 -1
- package/src/plugins/maintenance/seed-executor.ts +2 -2
- package/src/plugins/maintenance-plugin.ts +474 -2
- package/src/plugins/notifications/NotificationsManagementPage.tsx +1 -1
- package/src/plugins/notifications/NotificationsStatusWidget.tsx +1 -1
- package/src/plugins/parental/ParentalManagementPage.tsx +1 -1
- package/src/plugins/parental/ParentalStatusWidget.tsx +1 -1
- package/src/plugins/postgres/PostgresManagementPage.tsx +1 -1
- package/src/plugins/postgres/PostgresStatusWidget.tsx +1 -1
- package/src/plugins/preferences/PreferencesManagementPage.tsx +1 -1
- package/src/plugins/preferences/PreferencesStatusWidget.tsx +1 -1
- package/src/plugins/profiles/ProfilesManagementPage.tsx +1 -1
- package/src/plugins/profiles/ProfilesStatusWidget.tsx +1 -1
- package/src/plugins/qwickbrain/QwickbrainManagementPage.tsx +1 -1
- package/src/plugins/qwickbrain/QwickbrainStatusWidget.tsx +1 -1
- package/src/plugins/rate-limit/RateLimitManagementPage.tsx +1 -1
- package/src/plugins/rate-limit/RateLimitStatusWidget.tsx +1 -1
- package/src/plugins/subscriptions/SubscriptionsManagementPage.tsx +1 -1
- package/src/plugins/subscriptions/SubscriptionsStatusWidget.tsx +1 -1
- package/src/plugins/usage/UsageManagementPage.tsx +1 -1
- package/src/plugins/usage/UsageStatusWidget.tsx +1 -1
- package/src/plugins/users/UsersManagementPage.tsx +1 -1
- package/src/plugins/users/UsersStatusWidget.tsx +1 -1
- package/ui/src/App.tsx +4 -3
- package/ui/src/api/clientBuilder.ts +5 -5
- package/ui/src/api/controlPanelApi.ts +19 -19
- package/ui/src/components/ControlPanelApp.tsx +5 -4
- package/ui/src/dashboard/builtInWidgets.tsx +3 -0
- package/ui/src/dashboard/widgets/CMSMaintenanceWidget.tsx +8 -8
- package/ui/src/dashboard/widgets/CMSStatusWidget.tsx +2 -2
- package/ui/src/dashboard/widgets/CacheMaintenanceWidget.tsx +4 -4
- package/ui/src/dashboard/widgets/DatabaseOperationsWidget.tsx +2 -1
- package/ui/src/dashboard/widgets/LogsMaintenanceWidget.tsx +6 -6
- package/ui/src/dashboard/widgets/MigrationManagementWidget.tsx +319 -0
- package/ui/src/dashboard/widgets/SeedManagementWidget.tsx +6 -6
- package/ui/src/dashboard/widgets/index.ts +1 -0
- package/ui/src/hooks/useJobStream.ts +2 -2
- package/ui/src/pages/ContentOpsJobsPage.tsx +3 -3
- package/ui/src/pages/PluginPage.tsx +1 -1
- package/ui/src/pages/TenantsManagementPage.tsx +1 -1
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration Management Widget
|
|
3
|
+
*
|
|
4
|
+
* Allows executing Payload CMS database migrations from the control panel.
|
|
5
|
+
* Part of the maintenance plugin.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useState, useEffect } from 'react';
|
|
9
|
+
import {
|
|
10
|
+
Card,
|
|
11
|
+
CardContent,
|
|
12
|
+
Typography,
|
|
13
|
+
Button,
|
|
14
|
+
CircularProgress,
|
|
15
|
+
Alert,
|
|
16
|
+
Box,
|
|
17
|
+
Table,
|
|
18
|
+
TableBody,
|
|
19
|
+
TableCell,
|
|
20
|
+
TableContainer,
|
|
21
|
+
TableHead,
|
|
22
|
+
TableRow,
|
|
23
|
+
Paper,
|
|
24
|
+
Chip,
|
|
25
|
+
Dialog,
|
|
26
|
+
DialogTitle,
|
|
27
|
+
DialogContent,
|
|
28
|
+
DialogActions,
|
|
29
|
+
} from '@mui/material';
|
|
30
|
+
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
|
31
|
+
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
|
32
|
+
import ErrorIcon from '@mui/icons-material/Error';
|
|
33
|
+
import HistoryIcon from '@mui/icons-material/History';
|
|
34
|
+
|
|
35
|
+
interface MigrationExecution {
|
|
36
|
+
id: string;
|
|
37
|
+
status: 'running' | 'completed' | 'failed';
|
|
38
|
+
started_at: string;
|
|
39
|
+
completed_at?: string;
|
|
40
|
+
exit_code?: number;
|
|
41
|
+
output?: string;
|
|
42
|
+
error?: string;
|
|
43
|
+
duration_ms?: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function MigrationManagementWidget() {
|
|
47
|
+
const [error, setError] = useState<string | null>(null);
|
|
48
|
+
const [executing, setExecuting] = useState(false);
|
|
49
|
+
const [output, setOutput] = useState<string>('');
|
|
50
|
+
const [history, setHistory] = useState<MigrationExecution[]>([]);
|
|
51
|
+
const [historyLoading, setHistoryLoading] = useState(true);
|
|
52
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
53
|
+
const [selectedExecution, setSelectedExecution] = useState<MigrationExecution | null>(null);
|
|
54
|
+
const [historyLimit] = useState(10);
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
fetchHistory();
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
const fetchHistory = async () => {
|
|
61
|
+
try {
|
|
62
|
+
setHistoryLoading(true);
|
|
63
|
+
const basePath = (window as any).__API_BASE_PATH__ || '';
|
|
64
|
+
const response = await fetch(`${basePath}/maintenance/migrations/history?limit=${historyLimit}`);
|
|
65
|
+
if (!response.ok) throw new Error('Failed to fetch migration history');
|
|
66
|
+
const data = await response.json();
|
|
67
|
+
setHistory(data.executions || []);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
console.error('Failed to fetch migration history:', err);
|
|
70
|
+
} finally {
|
|
71
|
+
setHistoryLoading(false);
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleExecute = async () => {
|
|
76
|
+
setExecuting(true);
|
|
77
|
+
setOutput('Starting migrations...\n');
|
|
78
|
+
setError(null);
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const basePath = (window as any).__API_BASE_PATH__ || '';
|
|
82
|
+
const eventSource = new EventSource(`${basePath}/maintenance/migrations/execute`, {
|
|
83
|
+
withCredentials: true,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
eventSource.addEventListener('message', (e) => {
|
|
87
|
+
try {
|
|
88
|
+
const data = JSON.parse(e.data);
|
|
89
|
+
|
|
90
|
+
if (data.type === 'output') {
|
|
91
|
+
setOutput(prev => prev + data.data);
|
|
92
|
+
} else if (data.type === 'error') {
|
|
93
|
+
setOutput(prev => prev + '[ERROR] ' + data.data);
|
|
94
|
+
} else if (data.type === 'complete') {
|
|
95
|
+
setOutput(prev => prev + `\n\n✓ Migrations completed in ${data.duration}ms (exit code: ${data.exitCode})`);
|
|
96
|
+
setExecuting(false);
|
|
97
|
+
eventSource.close();
|
|
98
|
+
fetchHistory(); // Refresh history
|
|
99
|
+
}
|
|
100
|
+
} catch (err) {
|
|
101
|
+
console.error('Failed to parse SSE message:', err);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
eventSource.onerror = (err) => {
|
|
106
|
+
console.error('SSE error:', err);
|
|
107
|
+
setError('Connection lost or migration failed. Check console for details.');
|
|
108
|
+
setExecuting(false);
|
|
109
|
+
eventSource.close();
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
} catch (err) {
|
|
113
|
+
setError(err instanceof Error ? err.message : 'Migration execution failed');
|
|
114
|
+
setExecuting(false);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const formatDate = (dateString: string) => {
|
|
119
|
+
return new Date(dateString).toLocaleString();
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const formatDuration = (ms?: number) => {
|
|
123
|
+
if (!ms) return '-';
|
|
124
|
+
if (ms < 1000) return `${ms}ms`;
|
|
125
|
+
return `${(ms / 1000).toFixed(2)}s`;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const getStatusChip = (status: string) => {
|
|
129
|
+
switch (status) {
|
|
130
|
+
case 'completed':
|
|
131
|
+
return <Chip label="Success" color="success" size="small" icon={<CheckCircleIcon />} />;
|
|
132
|
+
case 'failed':
|
|
133
|
+
return <Chip label="Failed" color="error" size="small" icon={<ErrorIcon />} />;
|
|
134
|
+
case 'running':
|
|
135
|
+
return <Chip label="Running" color="primary" size="small" />;
|
|
136
|
+
default:
|
|
137
|
+
return <Chip label={status} size="small" />;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const handleViewDetails = (execution: MigrationExecution) => {
|
|
142
|
+
setSelectedExecution(execution);
|
|
143
|
+
setDialogOpen(true);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<>
|
|
148
|
+
<Card>
|
|
149
|
+
<CardContent>
|
|
150
|
+
<Typography variant="h6" gutterBottom>
|
|
151
|
+
Database Migrations
|
|
152
|
+
</Typography>
|
|
153
|
+
<Typography variant="body2" color="text.secondary" gutterBottom>
|
|
154
|
+
Execute Payload CMS database schema migrations
|
|
155
|
+
</Typography>
|
|
156
|
+
|
|
157
|
+
{error && (
|
|
158
|
+
<Alert severity="error" sx={{ mt: 2, mb: 2 }}>
|
|
159
|
+
{error}
|
|
160
|
+
</Alert>
|
|
161
|
+
)}
|
|
162
|
+
|
|
163
|
+
<Box sx={{ mt: 2, mb: 2 }}>
|
|
164
|
+
<Button
|
|
165
|
+
variant="contained"
|
|
166
|
+
color="primary"
|
|
167
|
+
onClick={handleExecute}
|
|
168
|
+
disabled={executing}
|
|
169
|
+
startIcon={executing ? <CircularProgress size={20} /> : <PlayArrowIcon />}
|
|
170
|
+
fullWidth
|
|
171
|
+
>
|
|
172
|
+
{executing ? 'Running Migrations...' : 'Run Migrations'}
|
|
173
|
+
</Button>
|
|
174
|
+
</Box>
|
|
175
|
+
|
|
176
|
+
{output && (
|
|
177
|
+
<Paper
|
|
178
|
+
elevation={0}
|
|
179
|
+
sx={{
|
|
180
|
+
p: 2,
|
|
181
|
+
bgcolor: '#1e1e1e',
|
|
182
|
+
color: '#d4d4d4',
|
|
183
|
+
fontFamily: 'monospace',
|
|
184
|
+
fontSize: '0.875rem',
|
|
185
|
+
maxHeight: '400px',
|
|
186
|
+
overflow: 'auto',
|
|
187
|
+
whiteSpace: 'pre-wrap',
|
|
188
|
+
wordBreak: 'break-word',
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
{output}
|
|
192
|
+
</Paper>
|
|
193
|
+
)}
|
|
194
|
+
|
|
195
|
+
<Typography variant="h6" sx={{ mt: 4, mb: 2 }}>
|
|
196
|
+
<HistoryIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
197
|
+
Recent Executions
|
|
198
|
+
</Typography>
|
|
199
|
+
|
|
200
|
+
{historyLoading ? (
|
|
201
|
+
<Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
|
|
202
|
+
<CircularProgress />
|
|
203
|
+
</Box>
|
|
204
|
+
) : history.length === 0 ? (
|
|
205
|
+
<Alert severity="info">No migration executions yet</Alert>
|
|
206
|
+
) : (
|
|
207
|
+
<TableContainer component={Paper} variant="outlined">
|
|
208
|
+
<Table size="small">
|
|
209
|
+
<TableHead>
|
|
210
|
+
<TableRow>
|
|
211
|
+
<TableCell>Status</TableCell>
|
|
212
|
+
<TableCell>Started</TableCell>
|
|
213
|
+
<TableCell>Duration</TableCell>
|
|
214
|
+
<TableCell>Exit Code</TableCell>
|
|
215
|
+
<TableCell align="right">Actions</TableCell>
|
|
216
|
+
</TableRow>
|
|
217
|
+
</TableHead>
|
|
218
|
+
<TableBody>
|
|
219
|
+
{history.map((execution) => (
|
|
220
|
+
<TableRow key={execution.id}>
|
|
221
|
+
<TableCell>{getStatusChip(execution.status)}</TableCell>
|
|
222
|
+
<TableCell>{formatDate(execution.started_at)}</TableCell>
|
|
223
|
+
<TableCell>{formatDuration(execution.duration_ms)}</TableCell>
|
|
224
|
+
<TableCell>{execution.exit_code ?? '-'}</TableCell>
|
|
225
|
+
<TableCell align="right">
|
|
226
|
+
<Button
|
|
227
|
+
size="small"
|
|
228
|
+
onClick={() => handleViewDetails(execution)}
|
|
229
|
+
>
|
|
230
|
+
View Details
|
|
231
|
+
</Button>
|
|
232
|
+
</TableCell>
|
|
233
|
+
</TableRow>
|
|
234
|
+
))}
|
|
235
|
+
</TableBody>
|
|
236
|
+
</Table>
|
|
237
|
+
</TableContainer>
|
|
238
|
+
)}
|
|
239
|
+
</CardContent>
|
|
240
|
+
</Card>
|
|
241
|
+
|
|
242
|
+
{/* Execution Details Dialog */}
|
|
243
|
+
<Dialog
|
|
244
|
+
open={dialogOpen}
|
|
245
|
+
onClose={() => setDialogOpen(false)}
|
|
246
|
+
maxWidth="md"
|
|
247
|
+
fullWidth
|
|
248
|
+
>
|
|
249
|
+
<DialogTitle>
|
|
250
|
+
Migration Execution Details
|
|
251
|
+
</DialogTitle>
|
|
252
|
+
<DialogContent>
|
|
253
|
+
{selectedExecution && (
|
|
254
|
+
<>
|
|
255
|
+
<Box sx={{ mb: 2 }}>
|
|
256
|
+
<Typography variant="subtitle2">Status:</Typography>
|
|
257
|
+
{getStatusChip(selectedExecution.status)}
|
|
258
|
+
</Box>
|
|
259
|
+
<Box sx={{ mb: 2 }}>
|
|
260
|
+
<Typography variant="subtitle2">Started:</Typography>
|
|
261
|
+
<Typography variant="body2">{formatDate(selectedExecution.started_at)}</Typography>
|
|
262
|
+
</Box>
|
|
263
|
+
{selectedExecution.completed_at && (
|
|
264
|
+
<Box sx={{ mb: 2 }}>
|
|
265
|
+
<Typography variant="subtitle2">Completed:</Typography>
|
|
266
|
+
<Typography variant="body2">{formatDate(selectedExecution.completed_at)}</Typography>
|
|
267
|
+
</Box>
|
|
268
|
+
)}
|
|
269
|
+
<Box sx={{ mb: 2 }}>
|
|
270
|
+
<Typography variant="subtitle2">Duration:</Typography>
|
|
271
|
+
<Typography variant="body2">{formatDuration(selectedExecution.duration_ms)}</Typography>
|
|
272
|
+
</Box>
|
|
273
|
+
<Box sx={{ mb: 2 }}>
|
|
274
|
+
<Typography variant="subtitle2">Exit Code:</Typography>
|
|
275
|
+
<Typography variant="body2">{selectedExecution.exit_code ?? 'N/A'}</Typography>
|
|
276
|
+
</Box>
|
|
277
|
+
{selectedExecution.output && (
|
|
278
|
+
<Box sx={{ mb: 2 }}>
|
|
279
|
+
<Typography variant="subtitle2">Output:</Typography>
|
|
280
|
+
<Paper
|
|
281
|
+
elevation={0}
|
|
282
|
+
sx={{
|
|
283
|
+
p: 2,
|
|
284
|
+
bgcolor: '#1e1e1e',
|
|
285
|
+
color: '#d4d4d4',
|
|
286
|
+
fontFamily: 'monospace',
|
|
287
|
+
fontSize: '0.75rem',
|
|
288
|
+
maxHeight: '300px',
|
|
289
|
+
overflow: 'auto',
|
|
290
|
+
whiteSpace: 'pre-wrap',
|
|
291
|
+
wordBreak: 'break-word',
|
|
292
|
+
}}
|
|
293
|
+
>
|
|
294
|
+
{selectedExecution.output}
|
|
295
|
+
</Paper>
|
|
296
|
+
</Box>
|
|
297
|
+
)}
|
|
298
|
+
{selectedExecution.error && (
|
|
299
|
+
<Box sx={{ mb: 2 }}>
|
|
300
|
+
<Typography variant="subtitle2">Error:</Typography>
|
|
301
|
+
<Alert severity="error">
|
|
302
|
+
<pre style={{ margin: 0, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
|
|
303
|
+
{selectedExecution.error}
|
|
304
|
+
</pre>
|
|
305
|
+
</Alert>
|
|
306
|
+
</Box>
|
|
307
|
+
)}
|
|
308
|
+
</>
|
|
309
|
+
)}
|
|
310
|
+
</DialogContent>
|
|
311
|
+
<DialogActions>
|
|
312
|
+
<Button onClick={() => setDialogOpen(false)}>
|
|
313
|
+
Close
|
|
314
|
+
</Button>
|
|
315
|
+
</DialogActions>
|
|
316
|
+
</Dialog>
|
|
317
|
+
</>
|
|
318
|
+
);
|
|
319
|
+
}
|
|
@@ -91,8 +91,8 @@ export function SeedManagementWidget() {
|
|
|
91
91
|
|
|
92
92
|
const fetchSeeds = async () => {
|
|
93
93
|
try {
|
|
94
|
-
const basePath = (window as any).
|
|
95
|
-
const response = await fetch(`${basePath}/
|
|
94
|
+
const basePath = (window as any).__API_BASE_PATH__ || '';
|
|
95
|
+
const response = await fetch(`${basePath}/maintenance/seeds/discover`);
|
|
96
96
|
if (!response.ok) throw new Error('Failed to fetch seeds');
|
|
97
97
|
const data = await response.json();
|
|
98
98
|
setSeeds(data.seeds || []);
|
|
@@ -172,8 +172,8 @@ export function SeedManagementWidget() {
|
|
|
172
172
|
|
|
173
173
|
// Execute a single seed and handle SSE stream
|
|
174
174
|
const executeSeed = async (seedKey: string, friendlyName: string, seedType: string): Promise<ExecutionResult> => {
|
|
175
|
-
const basePath = (window as any).
|
|
176
|
-
const response = await fetch(`${basePath}/
|
|
175
|
+
const basePath = (window as any).__API_BASE_PATH__ || '';
|
|
176
|
+
const response = await fetch(`${basePath}/maintenance/seeds/execute`, {
|
|
177
177
|
method: 'POST',
|
|
178
178
|
headers: { 'Content-Type': 'application/json' },
|
|
179
179
|
body: JSON.stringify({ name: seedKey, type: seedType }),
|
|
@@ -237,8 +237,8 @@ export function SeedManagementWidget() {
|
|
|
237
237
|
setResetDialogOpen(false);
|
|
238
238
|
|
|
239
239
|
try {
|
|
240
|
-
const basePath = (window as any).
|
|
241
|
-
const response = await fetch(`${basePath}/
|
|
240
|
+
const basePath = (window as any).__API_BASE_PATH__ || '';
|
|
241
|
+
const response = await fetch(`${basePath}/maintenance/database/reset`, {
|
|
242
242
|
method: 'POST',
|
|
243
243
|
headers: { 'Content-Type': 'application/json' },
|
|
244
244
|
});
|
|
@@ -11,6 +11,7 @@ export { NotificationsStatsWidget } from './NotificationsStatsWidget';
|
|
|
11
11
|
export { CMSStatusWidget } from './CMSStatusWidget';
|
|
12
12
|
export { CMSMaintenanceWidget } from './CMSMaintenanceWidget';
|
|
13
13
|
export { SeedManagementWidget } from './SeedManagementWidget';
|
|
14
|
+
export { MigrationManagementWidget } from './MigrationManagementWidget';
|
|
14
15
|
export { ServiceControlWidget } from './ServiceControlWidget';
|
|
15
16
|
export { EnvironmentConfigWidget } from './EnvironmentConfigWidget';
|
|
16
17
|
export { DatabaseOpsWidget } from './DatabaseOpsWidget';
|
|
@@ -61,7 +61,7 @@ export function useJobStream(options: UseJobStreamOptions = {}): UseJobStreamRet
|
|
|
61
61
|
// Fetch initial jobs via REST API
|
|
62
62
|
const refresh = useCallback(async () => {
|
|
63
63
|
try {
|
|
64
|
-
const response = await fetch(`${api.getBaseUrl()}/
|
|
64
|
+
const response = await fetch(`${api.getBaseUrl()}/contentops/jobs`, {
|
|
65
65
|
credentials: 'same-origin',
|
|
66
66
|
});
|
|
67
67
|
if (!response.ok) {
|
|
@@ -85,7 +85,7 @@ export function useJobStream(options: UseJobStreamOptions = {}): UseJobStreamRet
|
|
|
85
85
|
|
|
86
86
|
try {
|
|
87
87
|
setReconnecting(true);
|
|
88
|
-
const eventSource = new EventSource(`${api.getBaseUrl()}/
|
|
88
|
+
const eventSource = new EventSource(`${api.getBaseUrl()}/contentops/jobs/stream`, {
|
|
89
89
|
withCredentials: true,
|
|
90
90
|
});
|
|
91
91
|
|
|
@@ -79,7 +79,7 @@ export function ContentOpsJobsPage() {
|
|
|
79
79
|
setActionLoading(jobId);
|
|
80
80
|
setActionError(null);
|
|
81
81
|
|
|
82
|
-
const response = await fetch(`${api.getBaseUrl()}/
|
|
82
|
+
const response = await fetch(`${api.getBaseUrl()}/contentops/jobs/${jobId}/retry`, {
|
|
83
83
|
method: 'POST',
|
|
84
84
|
credentials: 'include',
|
|
85
85
|
});
|
|
@@ -102,7 +102,7 @@ export function ContentOpsJobsPage() {
|
|
|
102
102
|
setActionLoading(jobId);
|
|
103
103
|
setActionError(null);
|
|
104
104
|
|
|
105
|
-
const response = await fetch(`${api.getBaseUrl()}/
|
|
105
|
+
const response = await fetch(`${api.getBaseUrl()}/contentops/jobs/${jobId}/cancel`, {
|
|
106
106
|
method: 'POST',
|
|
107
107
|
credentials: 'include',
|
|
108
108
|
});
|
|
@@ -125,7 +125,7 @@ export function ContentOpsJobsPage() {
|
|
|
125
125
|
setActionLoading(jobId);
|
|
126
126
|
setActionError(null);
|
|
127
127
|
|
|
128
|
-
const response = await fetch(`${api.getBaseUrl()}/
|
|
128
|
+
const response = await fetch(`${api.getBaseUrl()}/contentops/jobs/${jobId}`, {
|
|
129
129
|
method: 'DELETE',
|
|
130
130
|
credentials: 'include',
|
|
131
131
|
});
|
|
@@ -47,7 +47,7 @@ export function PluginPage({ pluginId, title, route }: PluginPageProps) {
|
|
|
47
47
|
const fetchPageData = async () => {
|
|
48
48
|
try {
|
|
49
49
|
// Try to fetch plugin-specific page data
|
|
50
|
-
const response = await fetch(`${api.getBaseUrl()}
|
|
50
|
+
const response = await fetch(`${api.getBaseUrl()}${route}`, {
|
|
51
51
|
credentials: 'same-origin',
|
|
52
52
|
});
|
|
53
53
|
if (response.ok) {
|
|
@@ -80,7 +80,7 @@ export function TenantsManagementPage({
|
|
|
80
80
|
title = 'Tenant Management',
|
|
81
81
|
subtitle = 'Manage organizations, groups, and departments',
|
|
82
82
|
headerActions,
|
|
83
|
-
apiBaseUrl = '/
|
|
83
|
+
apiBaseUrl = '/qapi/tenants',
|
|
84
84
|
}: TenantsManagementPageProps) {
|
|
85
85
|
// Tenants state
|
|
86
86
|
const [tenants, setTenants] = useState<Tenant[]>([]);
|