import { Router } from 'express'; import { agentRequest } from '../agentClient.js'; import { getNode, listNodes, recordNodeHealth } from '../store.js'; export const proxyRouter = Router(); proxyRouter.get('/health', async (_req, res) => { const nodes = listNodes().filter((node) => node.enabled); const checks = { database: 'ok' }; let ok = true; await Promise.all(nodes.map(async (node) => { try { const health = await agentRequest(node, '/health', { timeout: 8000 }); checks[node.name] = health?.ok ? 'ok' : 'degraded'; recordNodeHealth(node.id, checks[node.name]); ok = ok && Boolean(health?.ok); } catch { checks[node.name] = 'error'; recordNodeHealth(node.id, 'error'); ok = false; } })); res.json({ ok, checks }); }); proxyRouter.get('/vms', async (_req, res) => { const nodes = listNodes().filter((node) => node.enabled); const rows = []; await Promise.all(nodes.map(async (node) => { try { const vms = await agentRequest(node, '/vms'); for (const vm of vms) { rows.push({ ...vm, id: `${node.id}:${vm.name}`, nodeId: node.id, nodeName: node.name }); } recordNodeHealth(node.id, 'ok'); } catch { recordNodeHealth(node.id, 'error'); } })); res.json(rows.sort((a, b) => `${a.nodeName}/${a.name}`.localeCompare(`${b.nodeName}/${b.name}`))); }); proxyRouter.get('/jobs', async (_req, res) => { const nodes = listNodes().filter((node) => node.enabled); const rows = []; await Promise.all(nodes.map(async (node) => { try { const jobs = await agentRequest(node, '/jobs'); for (const job of jobs) rows.push({ ...job, nodeId: node.id, nodeName: node.name }); } catch { recordNodeHealth(node.id, 'error'); } })); res.json(rows.sort((a, b) => String(b.startedAt || '').localeCompare(String(a.startedAt || '')))); }); proxyRouter.get('/snapshots/:nodeId/:vmName', async (req, res, next) => { try { const node = requireNode(req.params.nodeId); res.json(await agentRequest(node, `/snapshots/${encodeURIComponent(req.params.vmName)}`)); } catch (error) { next(error); } }); proxyRouter.get('/snapshots/:nodeId/:vmName/:snapshotId/files', async (req, res, next) => { try { const node = requireNode(req.params.nodeId); res.json(await agentRequest(node, `/snapshots/${encodeURIComponent(req.params.vmName)}/${encodeURIComponent(req.params.snapshotId)}/files`)); } catch (error) { next(error); } }); proxyRouter.post('/backup/:nodeId/:vmName', async (req, res, next) => { try { const node = requireNode(req.params.nodeId); res.status(202).json(await agentRequest(node, `/backup/${encodeURIComponent(req.params.vmName)}`, { method: 'POST', body: req.body || {} })); } catch (error) { next(error); } }); proxyRouter.post('/restore/:nodeId/:vmName', async (req, res, next) => { try { const node = requireNode(req.params.nodeId); res.status(202).json(await agentRequest(node, `/restore/${encodeURIComponent(req.params.vmName)}`, { method: 'POST', body: req.body || {} })); } catch (error) { next(error); } }); proxyRouter.get('/settings/:nodeId', async (req, res, next) => { try { const node = requireNode(req.params.nodeId); res.json(await agentRequest(node, '/settings')); } catch (error) { next(error); } }); proxyRouter.put('/settings/:nodeId', async (req, res, next) => { try { const node = requireNode(req.params.nodeId); res.json(await agentRequest(node, '/settings', { method: 'PUT', body: req.body || {} })); } catch (error) { next(error); } }); function requireNode(id) { const node = getNode(id); if (!node) { const error = new Error('Node not found.'); error.status = 404; throw error; } return node; }