@mostajs/setup 1.4.4 → 1.4.5
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.
|
@@ -12,10 +12,79 @@ export function createUploadJarHandlers() {
|
|
|
12
12
|
async function GET() {
|
|
13
13
|
try {
|
|
14
14
|
const { listJarFiles, getJdbcDialectStatus } = await import('@mostajs/orm');
|
|
15
|
+
// Scan active bridges: BridgeManager bridges + PID files + port probing
|
|
16
|
+
const bridges = [];
|
|
17
|
+
// 1. Check BridgeManager known bridges
|
|
18
|
+
try {
|
|
19
|
+
const { BridgeManager } = await import('@mostajs/orm');
|
|
20
|
+
const manager = BridgeManager.getInstance();
|
|
21
|
+
for (const b of manager.list()) {
|
|
22
|
+
bridges.push({ port: b.port, pid: b.pid, status: 'active', jdbcUrl: b.jdbcUrl });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch { /* ignore */ }
|
|
26
|
+
// 2. Scan PID files for orphan bridges
|
|
27
|
+
try {
|
|
28
|
+
const { existsSync, readdirSync, readFileSync } = await import('fs');
|
|
29
|
+
const { join } = await import('path');
|
|
30
|
+
const jarDir = process.env.MOSTA_JAR_DIR || join(process.cwd(), 'jar_files');
|
|
31
|
+
if (existsSync(jarDir)) {
|
|
32
|
+
const pidFiles = readdirSync(jarDir).filter((f) => f.startsWith('.bridge-') && f.endsWith('.pid'));
|
|
33
|
+
for (const file of pidFiles) {
|
|
34
|
+
const portMatch = file.match(/\.bridge-(\d+)\.pid/);
|
|
35
|
+
if (!portMatch)
|
|
36
|
+
continue;
|
|
37
|
+
const port = parseInt(portMatch[1]);
|
|
38
|
+
// Skip if already known from BridgeManager
|
|
39
|
+
if (bridges.some(b => b.port === port))
|
|
40
|
+
continue;
|
|
41
|
+
const pidStr = readFileSync(join(jarDir, file), 'utf-8').trim();
|
|
42
|
+
const pid = parseInt(pidStr);
|
|
43
|
+
if (isNaN(pid))
|
|
44
|
+
continue;
|
|
45
|
+
// Check if process is alive
|
|
46
|
+
let alive = false;
|
|
47
|
+
try {
|
|
48
|
+
process.kill(pid, 0);
|
|
49
|
+
alive = true;
|
|
50
|
+
}
|
|
51
|
+
catch { /* dead */ }
|
|
52
|
+
if (alive) {
|
|
53
|
+
// Probe health
|
|
54
|
+
let jdbcUrl;
|
|
55
|
+
try {
|
|
56
|
+
const res = await fetch(`http://localhost:${port}/health`, { signal: AbortSignal.timeout(1000) });
|
|
57
|
+
if (res.ok) {
|
|
58
|
+
const h = await res.json();
|
|
59
|
+
jdbcUrl = h.jdbcUrl;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch { /* not responding */ }
|
|
63
|
+
bridges.push({ port, pid, status: jdbcUrl ? 'active' : 'orphan', jdbcUrl });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch { /* ignore */ }
|
|
69
|
+
// 3. Probe common bridge ports for unknown bridges (no PID file)
|
|
70
|
+
const basePort = parseInt(process.env.MOSTA_BRIDGE_PORT_BASE || '8765');
|
|
71
|
+
for (let port = basePort; port < basePort + 5; port++) {
|
|
72
|
+
if (bridges.some(b => b.port === port))
|
|
73
|
+
continue;
|
|
74
|
+
try {
|
|
75
|
+
const res = await fetch(`http://localhost:${port}/health`, { signal: AbortSignal.timeout(500) });
|
|
76
|
+
if (res.ok) {
|
|
77
|
+
const h = await res.json();
|
|
78
|
+
bridges.push({ port, pid: 0, status: 'active', jdbcUrl: h.jdbcUrl });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch { /* not a bridge */ }
|
|
82
|
+
}
|
|
15
83
|
return Response.json({
|
|
16
84
|
ok: true,
|
|
17
85
|
jars: listJarFiles(),
|
|
18
86
|
dialects: getJdbcDialectStatus(),
|
|
87
|
+
bridges,
|
|
19
88
|
});
|
|
20
89
|
}
|
|
21
90
|
catch (err) {
|
|
@@ -81,23 +150,39 @@ export function createUploadJarHandlers() {
|
|
|
81
150
|
const { action } = body;
|
|
82
151
|
if (action === 'stop') {
|
|
83
152
|
const bridgePort = body.port || 8765;
|
|
84
|
-
|
|
153
|
+
const bridgePid = body.pid || 0;
|
|
85
154
|
try {
|
|
155
|
+
// 1. Try BridgeManager
|
|
86
156
|
const { BridgeManager } = await import('@mostajs/orm');
|
|
87
157
|
const manager = BridgeManager.getInstance();
|
|
88
158
|
const bridges = manager.list();
|
|
89
159
|
const bridge = bridges.find((b) => b.port === bridgePort);
|
|
90
160
|
if (bridge) {
|
|
91
161
|
await manager.stop(bridge.key);
|
|
92
|
-
return Response.json({ ok: true, message: `Bridge arrete sur le port ${bridgePort}` });
|
|
93
162
|
}
|
|
94
|
-
//
|
|
163
|
+
// 2. Kill by PID if provided
|
|
164
|
+
if (bridgePid > 0) {
|
|
165
|
+
try {
|
|
166
|
+
process.kill(bridgePid, 'SIGKILL');
|
|
167
|
+
}
|
|
168
|
+
catch { /* already dead */ }
|
|
169
|
+
}
|
|
170
|
+
// 3. Fallback: kill process on port
|
|
95
171
|
const { execSync } = await import('child_process');
|
|
96
172
|
try {
|
|
97
173
|
execSync(`fuser -k ${bridgePort}/tcp 2>/dev/null`, { stdio: 'ignore' });
|
|
98
174
|
}
|
|
99
175
|
catch { /* port may already be free */ }
|
|
100
|
-
|
|
176
|
+
// 4. Clean PID file
|
|
177
|
+
try {
|
|
178
|
+
const { existsSync, unlinkSync } = await import('fs');
|
|
179
|
+
const { join } = await import('path');
|
|
180
|
+
const pidFile = join(process.env.MOSTA_JAR_DIR || join(process.cwd(), 'jar_files'), `.bridge-${bridgePort}.pid`);
|
|
181
|
+
if (existsSync(pidFile))
|
|
182
|
+
unlinkSync(pidFile);
|
|
183
|
+
}
|
|
184
|
+
catch { /* non-critical */ }
|
|
185
|
+
return Response.json({ ok: true, message: `Bridge arrete (port ${bridgePort})` });
|
|
101
186
|
}
|
|
102
187
|
catch (err) {
|
|
103
188
|
const msg = err instanceof Error ? err.message : 'Erreur';
|
|
@@ -215,24 +215,31 @@ async function fetchRetry(url, init, retries = 3, delay = 2000) {
|
|
|
215
215
|
}
|
|
216
216
|
return fetch(url, init);
|
|
217
217
|
}
|
|
218
|
-
// ── JAR Upload Sub-component ─────────────────────────────────
|
|
219
218
|
function JarUploadInline({ dialect, jarEndpoint, dbConfig }) {
|
|
220
219
|
const [uploading, setUploading] = useState(false);
|
|
221
220
|
const [bridgeLoading, setBridgeLoading] = useState(false);
|
|
222
221
|
const [bridgePort, setBridgePort] = useState(null);
|
|
223
222
|
const [message, setMessage] = useState(null);
|
|
224
223
|
const [jarStatus, setJarStatus] = useState(null);
|
|
225
|
-
|
|
224
|
+
const [bridges, setBridges] = useState([]);
|
|
225
|
+
const [killingPort, setKillingPort] = useState(null);
|
|
226
|
+
const loadStatus = useCallback(() => {
|
|
226
227
|
fetch(jarEndpoint)
|
|
227
228
|
.then(r => r.json())
|
|
228
229
|
.then(data => {
|
|
229
230
|
if (data.ok) {
|
|
230
231
|
const s = data.dialects?.find((d) => d.dialect === dialect);
|
|
231
232
|
setJarStatus(s || { hasJar: false, jarFile: null });
|
|
233
|
+
setBridges(data.bridges || []);
|
|
234
|
+
// If a bridge is active, track its port
|
|
235
|
+
const active = (data.bridges || []).find((b) => b.status === 'active');
|
|
236
|
+
if (active)
|
|
237
|
+
setBridgePort(active.port);
|
|
232
238
|
}
|
|
233
239
|
})
|
|
234
240
|
.catch(() => { });
|
|
235
241
|
}, [dialect, jarEndpoint]);
|
|
242
|
+
useEffect(() => { loadStatus(); }, [loadStatus]);
|
|
236
243
|
const handleUpload = async (e) => {
|
|
237
244
|
const file = e.target.files?.[0];
|
|
238
245
|
if (!file || !file.name.endsWith('.jar'))
|
|
@@ -273,6 +280,7 @@ function JarUploadInline({ dialect, jarEndpoint, dbConfig }) {
|
|
|
273
280
|
if (result.ok) {
|
|
274
281
|
setBridgePort(result.port || 8765);
|
|
275
282
|
setMessage({ ok: true, text: `Bridge JDBC lance sur le port ${result.port || 8765}` });
|
|
283
|
+
loadStatus();
|
|
276
284
|
}
|
|
277
285
|
else {
|
|
278
286
|
setMessage({ ok: false, text: result.error || 'Echec lancement du bridge' });
|
|
@@ -285,19 +293,21 @@ function JarUploadInline({ dialect, jarEndpoint, dbConfig }) {
|
|
|
285
293
|
setBridgeLoading(false);
|
|
286
294
|
}
|
|
287
295
|
};
|
|
288
|
-
const handleStopBridge = async () => {
|
|
289
|
-
|
|
296
|
+
const handleStopBridge = async (port, pid) => {
|
|
297
|
+
setKillingPort(port);
|
|
290
298
|
setMessage(null);
|
|
291
299
|
try {
|
|
292
300
|
const res = await fetch(jarEndpoint, {
|
|
293
301
|
method: 'PATCH',
|
|
294
302
|
headers: { 'Content-Type': 'application/json' },
|
|
295
|
-
body: JSON.stringify({ action: 'stop', port
|
|
303
|
+
body: JSON.stringify({ action: 'stop', port, pid }),
|
|
296
304
|
});
|
|
297
305
|
const result = await res.json();
|
|
298
306
|
if (result.ok) {
|
|
299
|
-
|
|
307
|
+
if (bridgePort === port)
|
|
308
|
+
setBridgePort(null);
|
|
300
309
|
setMessage({ ok: true, text: result.message || 'Bridge arrete' });
|
|
310
|
+
loadStatus();
|
|
301
311
|
}
|
|
302
312
|
else {
|
|
303
313
|
setMessage({ ok: false, text: result.error || 'Echec arret du bridge' });
|
|
@@ -307,10 +317,13 @@ function JarUploadInline({ dialect, jarEndpoint, dbConfig }) {
|
|
|
307
317
|
setMessage({ ok: false, text: 'Erreur reseau' });
|
|
308
318
|
}
|
|
309
319
|
finally {
|
|
310
|
-
|
|
320
|
+
setKillingPort(null);
|
|
311
321
|
}
|
|
312
322
|
};
|
|
313
|
-
return (_jsxs("div", { style: S.jarBox, children: [_jsxs("div", { style: S.flex(8), children: [_jsx("span", { style: S.jarTitle, children: "Driver JDBC" }), jarStatus?.hasJar ? (_jsx("span", { style: { ...S.badge('installed'), marginLeft: 0 }, children: jarStatus.jarFile })) : (_jsx("span", { style: { fontSize: 12, color: '#6b7280' }, children: "Aucun JAR installe" })), bridgePort && (_jsxs("span", { style: { fontSize: 11, color: '#059669', fontWeight: 600 }, children: ["Bridge actif port ", bridgePort] }))] }), _jsxs("div", { style: { ...S.flex(8), marginTop: 8, flexWrap: 'wrap' }, children: [_jsxs("label", { style: { ...S.btn('primary', uploading), cursor: uploading ? 'wait' : 'pointer', fontSize: 12, padding: '6px 12px' }, children: [uploading ? 'Upload...' : 'Uploader un .jar', _jsx("input", { type: "file", accept: ".jar", onChange: handleUpload, disabled: uploading, style: { display: 'none' } })] }), jarStatus?.hasJar &&
|
|
323
|
+
return (_jsxs("div", { style: S.jarBox, children: [_jsxs("div", { style: S.flex(8), children: [_jsx("span", { style: S.jarTitle, children: "Driver JDBC" }), jarStatus?.hasJar ? (_jsx("span", { style: { ...S.badge('installed'), marginLeft: 0 }, children: jarStatus.jarFile })) : (_jsx("span", { style: { fontSize: 12, color: '#6b7280' }, children: "Aucun JAR installe" })), bridgePort && (_jsxs("span", { style: { fontSize: 11, color: '#059669', fontWeight: 600 }, children: ["Bridge actif port ", bridgePort] }))] }), _jsxs("div", { style: { ...S.flex(8), marginTop: 8, flexWrap: 'wrap' }, children: [_jsxs("label", { style: { ...S.btn('primary', uploading), cursor: uploading ? 'wait' : 'pointer', fontSize: 12, padding: '6px 12px' }, children: [uploading ? 'Upload...' : 'Uploader un .jar', _jsx("input", { type: "file", accept: ".jar", onChange: handleUpload, disabled: uploading, style: { display: 'none' } })] }), jarStatus?.hasJar && bridges.length === 0 && (_jsx("button", { style: { ...S.btn('primary', bridgeLoading), fontSize: 12, padding: '6px 12px', backgroundColor: bridgeLoading ? '#6b7280' : '#059669' }, onClick: handleStartBridge, disabled: bridgeLoading, children: bridgeLoading ? 'Lancement...' : 'Lancer le bridge' })), _jsx("span", { style: { fontSize: 11, color: '#9ca3af' }, children: "Ex: hsqldb*.jar, ojdbc*.jar, db2jcc*.jar" })] }), bridges.length > 0 && (_jsxs("div", { style: { marginTop: 8, padding: '8px 10px', backgroundColor: '#f0fdf4', borderRadius: 6, border: '1px solid #bbf7d0' }, children: [_jsx("div", { style: { fontSize: 11, fontWeight: 600, color: '#166534', marginBottom: 4 }, children: "Bridges actifs" }), bridges.map(b => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '4px 0', fontSize: 12 }, children: [_jsx("span", { style: { width: 8, height: 8, borderRadius: '50%', backgroundColor: b.status === 'active' ? '#22c55e' : '#f59e0b', flexShrink: 0 } }), _jsxs("span", { style: { color: '#374151', fontFamily: 'monospace', fontSize: 11 }, children: [":", b.port, " ", b.pid > 0 ? `(PID ${b.pid})` : '', " ", b.jdbcUrl ? `— ${b.jdbcUrl}` : ''] }), _jsx("button", { style: {
|
|
324
|
+
...S.btn('primary', killingPort === b.port), fontSize: 11, padding: '2px 8px',
|
|
325
|
+
backgroundColor: killingPort === b.port ? '#6b7280' : '#dc2626', marginLeft: 'auto',
|
|
326
|
+
}, onClick: () => handleStopBridge(b.port, b.pid), disabled: killingPort === b.port, children: killingPort === b.port ? '...' : 'Kill' })] }, b.port))), bridges.length === 0 || bridges.some(b => b.status === 'active') ? null : (_jsx("button", { style: { ...S.btn('primary', bridgeLoading), fontSize: 12, padding: '6px 12px', backgroundColor: '#059669', marginTop: 6 }, onClick: handleStartBridge, disabled: bridgeLoading, children: bridgeLoading ? 'Lancement...' : 'Lancer le bridge' }))] })), message && (_jsx("p", { style: { fontSize: 12, color: message.ok ? '#059669' : '#dc2626', marginTop: 8 }, children: message.text }))] }));
|
|
314
327
|
}
|
|
315
328
|
// ── Main Component ───────────────────────────────────────────
|
|
316
329
|
export default function SetupWizard({ t: tProp, onComplete, endpoints = {}, dbNamePrefix = 'mydb', persistState = true, }) {
|
package/package.json
CHANGED