import express from 'express'; import pool from '../db/pool.js'; import { requireAuth } from '../middleware/auth.js'; const router = express.Router(); router.use(requireAuth); // GET / – list all registered cluster nodes with online status router.get('/', async (req, res, next) => { try { const r = await pool.query( `SELECT *, EXTRACT(EPOCH FROM (NOW() - last_seen)) AS stale_seconds FROM cluster_nodes ORDER BY registered_at ASC` ); res.json(r.rows.map(row => ({ ...row, // online = last heartbeat within 2 minutes online: Number(row.stale_seconds) < 120, }))); } catch (err) { next(err); } }); // POST /heartbeat – upsert this node's registration router.post('/heartbeat', async (req, res, next) => { try { const { hostname, ip_address, role = 'worker', version, api_url, cpu_usage, mem_used_mb, mem_total_mb, metadata, } = req.body; if (!hostname) return res.status(400).json({ error: 'hostname is required' }); const r = await pool.query( `INSERT INTO cluster_nodes (hostname, ip_address, role, version, api_url, cpu_usage, mem_used_mb, mem_total_mb, last_seen, metadata) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,NOW(),$9) ON CONFLICT (hostname) DO UPDATE SET ip_address = EXCLUDED.ip_address, role = EXCLUDED.role, version = EXCLUDED.version, api_url = EXCLUDED.api_url, cpu_usage = EXCLUDED.cpu_usage, mem_used_mb = EXCLUDED.mem_used_mb, mem_total_mb = EXCLUDED.mem_total_mb, last_seen = NOW(), metadata = EXCLUDED.metadata RETURNING *`, [ hostname, ip_address || null, role, version || null, api_url || null, cpu_usage != null ? cpu_usage : null, mem_used_mb != null ? mem_used_mb : null, mem_total_mb != null ? mem_total_mb : null, metadata != null ? JSON.stringify(metadata) : null, ] ); res.json(r.rows[0]); } catch (err) { next(err); } }); // DELETE /:id – deregister a node router.delete('/:id', async (req, res, next) => { try { const r = await pool.query( 'DELETE FROM cluster_nodes WHERE id = $1 RETURNING id', [req.params.id] ); if (r.rowCount === 0) return res.status(404).json({ error: 'Node not found' }); res.json({ ok: true }); } catch (err) { next(err); } }); export default router;