const express = require('express'); const fs = require('fs'); const path = require('path'); const app = express(); const PORT = 9201; const ALERTS_FILE = path.join(__dirname, 'alerts.json'); app.use(express.json()); // Load/save alerts function loadAlerts() { try { return JSON.parse(fs.readFileSync(ALERTS_FILE, 'utf8')); } catch { return []; } } function saveAlerts(alerts) { fs.writeFileSync(ALERTS_FILE, JSON.stringify(alerts, null, 2)); } // SSE clients const sseClients = new Set(); // Serve dashboard app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'index.html')); }); // List alerts app.get('/api/alerts', (req, res) => { res.json(loadAlerts().slice(-50).reverse()); }); // Push alert app.post('/api/alerts', (req, res) => { const { message, priority = 'info' } = req.body; if (!message) return res.status(400).json({ error: 'message required' }); const alert = { id: Date.now().toString(36) + Math.random().toString(36).slice(2, 6), message, priority, timestamp: new Date().toISOString() }; const alerts = loadAlerts(); alerts.push(alert); // Keep last 100 if (alerts.length > 100) alerts.splice(0, alerts.length - 100); saveAlerts(alerts); // Notify SSE clients for (const client of sseClients) { client.write(`data: ${JSON.stringify(alert)}\n\n`); } res.status(201).json(alert); }); // SSE stream app.get('/api/alerts/stream', (req, res) => { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', }); res.write('data: {"type":"connected"}\n\n'); sseClients.add(res); req.on('close', () => sseClients.delete(res)); }); app.listen(PORT, '0.0.0.0', () => { console.log(`Alert dashboard running on http://0.0.0.0:${PORT}`); });