@qwickapps/server 1.7.2 → 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 +33 -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.map +1 -1
- package/dist/src/plugins/api-keys/api-keys-plugin.js +53 -1
- package/dist/src/plugins/api-keys/api-keys-plugin.js.map +1 -1
- package/dist/src/plugins/api-keys/index.d.ts +1 -1
- package/dist/src/plugins/api-keys/index.d.ts.map +1 -1
- 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 +13 -1
- 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/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/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/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/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 +2 -2
- 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 +58 -1
- package/src/plugins/api-keys/index.ts +1 -0
- package/src/plugins/api-keys/stores/postgres-store.ts +90 -67
- package/src/plugins/api-keys/types.ts +14 -1
- package/src/plugins/diagnostics-plugin.ts +77 -0
- 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/postgres-plugin.ts +410 -5
- 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-0gzisPdy.js +0 -528
- package/dist-ui/assets/index-0gzisPdy.js.map +0 -1
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database Operations Widget Component
|
|
3
|
+
* Displays database status and provides manual initialization/recreation controls
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2025 QwickApps.com. All rights reserved.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useEffect, useState } from 'react';
|
|
9
|
+
import {
|
|
10
|
+
Card,
|
|
11
|
+
CardContent,
|
|
12
|
+
Typography,
|
|
13
|
+
Button,
|
|
14
|
+
Alert,
|
|
15
|
+
Box,
|
|
16
|
+
Chip,
|
|
17
|
+
Dialog,
|
|
18
|
+
DialogTitle,
|
|
19
|
+
DialogContent,
|
|
20
|
+
DialogActions,
|
|
21
|
+
TextField,
|
|
22
|
+
CircularProgress,
|
|
23
|
+
} from '@mui/material';
|
|
24
|
+
|
|
25
|
+
export interface DatabaseOperationsWidgetProps {
|
|
26
|
+
// No props needed - will use default values
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface DatabaseStatus {
|
|
30
|
+
status: 'healthy' | 'error' | 'unknown';
|
|
31
|
+
connected: boolean;
|
|
32
|
+
database?: string;
|
|
33
|
+
user?: string;
|
|
34
|
+
host?: string;
|
|
35
|
+
port?: number;
|
|
36
|
+
errorMessage?: string;
|
|
37
|
+
autoInitializeEnabled: boolean;
|
|
38
|
+
adminCredentialsProvided: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ConfirmationDialogProps {
|
|
42
|
+
open: boolean;
|
|
43
|
+
title: string;
|
|
44
|
+
message: string;
|
|
45
|
+
confirmText: string;
|
|
46
|
+
requiredInput: string;
|
|
47
|
+
onConfirm: () => void;
|
|
48
|
+
onCancel: () => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const ConfirmationDialog: React.FC<ConfirmationDialogProps> = ({
|
|
52
|
+
open,
|
|
53
|
+
title,
|
|
54
|
+
message,
|
|
55
|
+
confirmText,
|
|
56
|
+
requiredInput,
|
|
57
|
+
onConfirm,
|
|
58
|
+
onCancel,
|
|
59
|
+
}) => {
|
|
60
|
+
const [inputValue, setInputValue] = useState('');
|
|
61
|
+
const isValid = inputValue === requiredInput;
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<Dialog open={open} onClose={onCancel} maxWidth="sm" fullWidth>
|
|
65
|
+
<DialogTitle>{title}</DialogTitle>
|
|
66
|
+
<DialogContent>
|
|
67
|
+
<Typography variant="body2" sx={{ mb: 2 }}>
|
|
68
|
+
{message}
|
|
69
|
+
</Typography>
|
|
70
|
+
<Typography variant="body2" sx={{ mb: 1, fontWeight: 'bold' }}>
|
|
71
|
+
Type "{requiredInput}" to confirm:
|
|
72
|
+
</Typography>
|
|
73
|
+
<TextField
|
|
74
|
+
autoFocus
|
|
75
|
+
fullWidth
|
|
76
|
+
value={inputValue}
|
|
77
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
78
|
+
placeholder={requiredInput}
|
|
79
|
+
sx={{ fontFamily: 'monospace' }}
|
|
80
|
+
/>
|
|
81
|
+
</DialogContent>
|
|
82
|
+
<DialogActions>
|
|
83
|
+
<Button onClick={onCancel}>Cancel</Button>
|
|
84
|
+
<Button
|
|
85
|
+
onClick={() => {
|
|
86
|
+
if (isValid) {
|
|
87
|
+
onConfirm();
|
|
88
|
+
setInputValue('');
|
|
89
|
+
}
|
|
90
|
+
}}
|
|
91
|
+
disabled={!isValid}
|
|
92
|
+
variant="contained"
|
|
93
|
+
color="error"
|
|
94
|
+
>
|
|
95
|
+
{confirmText}
|
|
96
|
+
</Button>
|
|
97
|
+
</DialogActions>
|
|
98
|
+
</Dialog>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
interface AdminCredentialsDialogProps {
|
|
103
|
+
open: boolean;
|
|
104
|
+
onSubmit: (credentials: { adminUser: string; adminPassword: string }) => void;
|
|
105
|
+
onCancel: () => void;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const AdminCredentialsDialog: React.FC<AdminCredentialsDialogProps> = ({
|
|
109
|
+
open,
|
|
110
|
+
onSubmit,
|
|
111
|
+
onCancel,
|
|
112
|
+
}) => {
|
|
113
|
+
const [adminUser, setAdminUser] = useState('postgres');
|
|
114
|
+
const [adminPassword, setAdminPassword] = useState('');
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<Dialog open={open} onClose={onCancel} maxWidth="sm" fullWidth>
|
|
118
|
+
<DialogTitle>Admin Credentials Required</DialogTitle>
|
|
119
|
+
<DialogContent>
|
|
120
|
+
<Typography variant="body2" sx={{ mb: 2 }}>
|
|
121
|
+
Provide PostgreSQL admin credentials to perform this operation:
|
|
122
|
+
</Typography>
|
|
123
|
+
<TextField
|
|
124
|
+
fullWidth
|
|
125
|
+
label="Admin User"
|
|
126
|
+
value={adminUser}
|
|
127
|
+
onChange={(e) => setAdminUser(e.target.value)}
|
|
128
|
+
sx={{ mb: 2 }}
|
|
129
|
+
placeholder="postgres"
|
|
130
|
+
/>
|
|
131
|
+
<TextField
|
|
132
|
+
fullWidth
|
|
133
|
+
type="password"
|
|
134
|
+
label="Admin Password"
|
|
135
|
+
value={adminPassword}
|
|
136
|
+
onChange={(e) => setAdminPassword(e.target.value)}
|
|
137
|
+
placeholder="Enter admin password"
|
|
138
|
+
/>
|
|
139
|
+
</DialogContent>
|
|
140
|
+
<DialogActions>
|
|
141
|
+
<Button onClick={onCancel}>Cancel</Button>
|
|
142
|
+
<Button
|
|
143
|
+
onClick={() => {
|
|
144
|
+
if (adminUser && adminPassword) {
|
|
145
|
+
onSubmit({ adminUser, adminPassword });
|
|
146
|
+
setAdminUser('postgres');
|
|
147
|
+
setAdminPassword('');
|
|
148
|
+
}
|
|
149
|
+
}}
|
|
150
|
+
disabled={!adminUser || !adminPassword}
|
|
151
|
+
variant="contained"
|
|
152
|
+
>
|
|
153
|
+
Continue
|
|
154
|
+
</Button>
|
|
155
|
+
</DialogActions>
|
|
156
|
+
</Dialog>
|
|
157
|
+
);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const DatabaseOperationsWidget: React.FC<DatabaseOperationsWidgetProps> = () => {
|
|
161
|
+
// Use default values since props cannot be passed through WidgetContribution
|
|
162
|
+
const apiPrefix = '/api/postgres:default';
|
|
163
|
+
const instanceName = 'default';
|
|
164
|
+
const [status, setStatus] = useState<DatabaseStatus | null>(null);
|
|
165
|
+
const [loading, setLoading] = useState(true);
|
|
166
|
+
const [error, setError] = useState<string | null>(null);
|
|
167
|
+
const [operating, setOperating] = useState(false);
|
|
168
|
+
const [showConfirmDialog, setShowConfirmDialog] = useState(false);
|
|
169
|
+
const [confirmOperation, setConfirmOperation] = useState<'initialize' | 'recreate'>('initialize');
|
|
170
|
+
const [showAdminDialog, setShowAdminDialog] = useState(false);
|
|
171
|
+
const [adminCredentials, setAdminCredentials] = useState<{
|
|
172
|
+
adminUser: string;
|
|
173
|
+
adminPassword: string;
|
|
174
|
+
} | null>(null);
|
|
175
|
+
|
|
176
|
+
const fetchStatus = async () => {
|
|
177
|
+
try {
|
|
178
|
+
const response = await fetch(`${apiPrefix}/status?instance=${instanceName}`);
|
|
179
|
+
if (!response.ok) throw new Error('Failed to fetch database status');
|
|
180
|
+
const data = await response.json();
|
|
181
|
+
setStatus(data);
|
|
182
|
+
setError(null);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
185
|
+
} finally {
|
|
186
|
+
setLoading(false);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
fetchStatus();
|
|
192
|
+
const interval = setInterval(fetchStatus, 30000);
|
|
193
|
+
return () => clearInterval(interval);
|
|
194
|
+
}, [apiPrefix, instanceName]);
|
|
195
|
+
|
|
196
|
+
const handleInitialize = async (creds?: { adminUser: string; adminPassword: string }) => {
|
|
197
|
+
setOperating(true);
|
|
198
|
+
try {
|
|
199
|
+
const response = await fetch(`${apiPrefix}/initialize`, {
|
|
200
|
+
method: 'POST',
|
|
201
|
+
headers: { 'Content-Type': 'application/json' },
|
|
202
|
+
body: JSON.stringify({
|
|
203
|
+
instance: instanceName,
|
|
204
|
+
...creds,
|
|
205
|
+
}),
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
210
|
+
throw new Error(errorData.message || 'Failed to initialize database');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
await fetchStatus();
|
|
214
|
+
alert('Database initialized successfully');
|
|
215
|
+
} catch (err) {
|
|
216
|
+
alert(`Initialization failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
217
|
+
} finally {
|
|
218
|
+
setOperating(false);
|
|
219
|
+
setShowConfirmDialog(false);
|
|
220
|
+
setShowAdminDialog(false);
|
|
221
|
+
setAdminCredentials(null);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const handleRecreate = async (creds?: { adminUser: string; adminPassword: string }) => {
|
|
226
|
+
setOperating(true);
|
|
227
|
+
try {
|
|
228
|
+
const response = await fetch(`${apiPrefix}/recreate`, {
|
|
229
|
+
method: 'POST',
|
|
230
|
+
headers: { 'Content-Type': 'application/json' },
|
|
231
|
+
body: JSON.stringify({
|
|
232
|
+
instance: instanceName,
|
|
233
|
+
...creds,
|
|
234
|
+
}),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
const errorData = await response.json().catch(() => ({ message: 'Unknown error' }));
|
|
239
|
+
throw new Error(errorData.message || 'Failed to recreate database');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
await fetchStatus();
|
|
243
|
+
alert('Database recreated successfully');
|
|
244
|
+
} catch (err) {
|
|
245
|
+
alert(`Recreation failed: ${err instanceof Error ? err.message : 'Unknown error'}`);
|
|
246
|
+
} finally {
|
|
247
|
+
setOperating(false);
|
|
248
|
+
setShowConfirmDialog(false);
|
|
249
|
+
setShowAdminDialog(false);
|
|
250
|
+
setAdminCredentials(null);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const startOperation = (operation: 'initialize' | 'recreate') => {
|
|
255
|
+
setConfirmOperation(operation);
|
|
256
|
+
|
|
257
|
+
if (!status?.adminCredentialsProvided) {
|
|
258
|
+
setShowAdminDialog(true);
|
|
259
|
+
} else if (operation === 'recreate') {
|
|
260
|
+
setShowConfirmDialog(true);
|
|
261
|
+
} else {
|
|
262
|
+
handleInitialize();
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const handleAdminCredentialsSubmit = (creds: { adminUser: string; adminPassword: string }) => {
|
|
267
|
+
setAdminCredentials(creds);
|
|
268
|
+
setShowAdminDialog(false);
|
|
269
|
+
|
|
270
|
+
if (confirmOperation === 'recreate') {
|
|
271
|
+
setShowConfirmDialog(true);
|
|
272
|
+
} else {
|
|
273
|
+
handleInitialize(creds);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
const handleConfirmDialogConfirm = () => {
|
|
278
|
+
if (confirmOperation === 'recreate') {
|
|
279
|
+
handleRecreate(adminCredentials || undefined);
|
|
280
|
+
} else {
|
|
281
|
+
handleInitialize(adminCredentials || undefined);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
if (loading) {
|
|
286
|
+
return (
|
|
287
|
+
<Card>
|
|
288
|
+
<CardContent>
|
|
289
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
|
290
|
+
<CircularProgress size={20} />
|
|
291
|
+
<Typography variant="body2">Loading database status...</Typography>
|
|
292
|
+
</Box>
|
|
293
|
+
</CardContent>
|
|
294
|
+
</Card>
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (error || !status) {
|
|
299
|
+
return (
|
|
300
|
+
<Card>
|
|
301
|
+
<CardContent>
|
|
302
|
+
<Typography variant="h6" gutterBottom>
|
|
303
|
+
Database ({instanceName})
|
|
304
|
+
</Typography>
|
|
305
|
+
<Alert severity="error">{error || 'Failed to load database status'}</Alert>
|
|
306
|
+
</CardContent>
|
|
307
|
+
</Card>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const requiredInput = status.database
|
|
312
|
+
? `RECREATE ${status.database.toUpperCase()} DATABASE`
|
|
313
|
+
: 'RECREATE DATABASE';
|
|
314
|
+
|
|
315
|
+
const statusColor = status.connected ? 'success' : 'error';
|
|
316
|
+
const statusLabel = status.connected ? 'CONNECTED' : 'ERROR';
|
|
317
|
+
|
|
318
|
+
return (
|
|
319
|
+
<>
|
|
320
|
+
<Card>
|
|
321
|
+
<CardContent>
|
|
322
|
+
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
|
|
323
|
+
<Typography variant="h6">Database ({instanceName})</Typography>
|
|
324
|
+
<Chip label={statusLabel} color={statusColor} size="small" />
|
|
325
|
+
</Box>
|
|
326
|
+
|
|
327
|
+
<Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
|
|
328
|
+
{operating
|
|
329
|
+
? 'Processing database operation...'
|
|
330
|
+
: status.connected
|
|
331
|
+
? `Connected to ${status.database}`
|
|
332
|
+
: status.errorMessage || 'Database connection error'}
|
|
333
|
+
</Typography>
|
|
334
|
+
|
|
335
|
+
<Box sx={{ mb: 2 }}>
|
|
336
|
+
<Typography variant="caption" color="text.secondary">
|
|
337
|
+
Connection
|
|
338
|
+
</Typography>
|
|
339
|
+
<Typography variant="body2" fontWeight="bold">
|
|
340
|
+
{statusLabel}
|
|
341
|
+
</Typography>
|
|
342
|
+
</Box>
|
|
343
|
+
|
|
344
|
+
<Box sx={{ mb: 2 }}>
|
|
345
|
+
<Typography variant="caption" color="text.secondary">
|
|
346
|
+
Database
|
|
347
|
+
</Typography>
|
|
348
|
+
<Typography variant="body2" fontWeight="bold">
|
|
349
|
+
{status.database || 'N/A'}
|
|
350
|
+
</Typography>
|
|
351
|
+
</Box>
|
|
352
|
+
|
|
353
|
+
<Box sx={{ mb: 2 }}>
|
|
354
|
+
<Typography variant="caption" color="text.secondary">
|
|
355
|
+
Host
|
|
356
|
+
</Typography>
|
|
357
|
+
<Typography variant="body2" fontWeight="bold">
|
|
358
|
+
{status.host || 'N/A'}:{status.port || 'N/A'}
|
|
359
|
+
</Typography>
|
|
360
|
+
</Box>
|
|
361
|
+
|
|
362
|
+
{!status.connected && !operating && (
|
|
363
|
+
<Box sx={{ display: 'flex', gap: 1, mt: 2 }}>
|
|
364
|
+
<Button
|
|
365
|
+
variant="contained"
|
|
366
|
+
color="primary"
|
|
367
|
+
onClick={() => startOperation('initialize')}
|
|
368
|
+
size="small"
|
|
369
|
+
>
|
|
370
|
+
Initialize Database
|
|
371
|
+
</Button>
|
|
372
|
+
<Button
|
|
373
|
+
variant="contained"
|
|
374
|
+
color="error"
|
|
375
|
+
onClick={() => startOperation('recreate')}
|
|
376
|
+
size="small"
|
|
377
|
+
>
|
|
378
|
+
Recreate Database
|
|
379
|
+
</Button>
|
|
380
|
+
</Box>
|
|
381
|
+
)}
|
|
382
|
+
</CardContent>
|
|
383
|
+
</Card>
|
|
384
|
+
|
|
385
|
+
<AdminCredentialsDialog
|
|
386
|
+
open={showAdminDialog}
|
|
387
|
+
onSubmit={handleAdminCredentialsSubmit}
|
|
388
|
+
onCancel={() => {
|
|
389
|
+
setShowAdminDialog(false);
|
|
390
|
+
setAdminCredentials(null);
|
|
391
|
+
}}
|
|
392
|
+
/>
|
|
393
|
+
|
|
394
|
+
<ConfirmationDialog
|
|
395
|
+
open={showConfirmDialog}
|
|
396
|
+
title="Confirm Database Recreation"
|
|
397
|
+
message={`This will drop and recreate the database "${status.database}". All data will be lost. This action cannot be undone.`}
|
|
398
|
+
confirmText="Recreate"
|
|
399
|
+
requiredInput={requiredInput}
|
|
400
|
+
onConfirm={handleConfirmDialogConfirm}
|
|
401
|
+
onCancel={() => {
|
|
402
|
+
setShowConfirmDialog(false);
|
|
403
|
+
setAdminCredentials(null);
|
|
404
|
+
}}
|
|
405
|
+
/>
|
|
406
|
+
</>
|
|
407
|
+
);
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
export default DatabaseOperationsWidget;
|
|
@@ -61,7 +61,8 @@ export function LogsMaintenanceWidget() {
|
|
|
61
61
|
|
|
62
62
|
const fetchSources = async () => {
|
|
63
63
|
try {
|
|
64
|
-
const
|
|
64
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
65
|
+
const response = await fetch(`${basePath}/api/logs/sources`);
|
|
65
66
|
if (!response.ok) throw new Error('Failed to fetch log sources');
|
|
66
67
|
const data = await response.json();
|
|
67
68
|
setSources(data.sources || []);
|
|
@@ -79,7 +80,8 @@ export function LogsMaintenanceWidget() {
|
|
|
79
80
|
setLoading(true);
|
|
80
81
|
setError(null);
|
|
81
82
|
try {
|
|
82
|
-
const
|
|
83
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
84
|
+
const response = await fetch(`${basePath}/api/logs/stats?source=${selectedSource}`);
|
|
83
85
|
if (!response.ok) throw new Error('Failed to fetch log stats');
|
|
84
86
|
const data = await response.json();
|
|
85
87
|
setStats(data);
|
|
@@ -108,7 +110,8 @@ export function LogsMaintenanceWidget() {
|
|
|
108
110
|
setSuccess(null);
|
|
109
111
|
|
|
110
112
|
try {
|
|
111
|
-
const
|
|
113
|
+
const basePath = (window as any).__APP_BASE_PATH__ || '';
|
|
114
|
+
const response = await fetch(`${basePath}/api/logs/clear`, {
|
|
112
115
|
method: 'POST',
|
|
113
116
|
headers: { 'Content-Type': 'application/json' },
|
|
114
117
|
body: JSON.stringify({ source: selectedSource }),
|