70 lines
1.8 KiB
JavaScript
70 lines
1.8 KiB
JavaScript
const express = require('express');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const app = express();
|
|
const PORT = 9202;
|
|
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}`);
|
|
});
|