Files
dashcaddy/dashcaddy-api/routes/events.js
T
Hermes 954be9e868
CI / Test & Lint (push) Has been cancelled
CI / Security audit (push) Has been cancelled
feat: auto-restart policies, SSL monitoring, DNS propagation, dependency tracking, config drift detection
2026-06-10 14:43:46 -07:00

155 lines
5.1 KiB
JavaScript

const express = require('express');
/**
* Server-Sent Events route factory
* Pushes real-time updates to connected dashboard clients
* @param {Object} deps - Dependencies
* @param {Object} deps.resourceMonitor - Container resource monitor
* @param {Object} deps.healthChecker - Health checker
* @param {Object} deps.updateManager - Update manager
* @param {Function} deps.logError - Error logging function
* @param {Object} deps.dependencyManager - Dependency manager for restart chain events
* @returns {express.Router}
*/
module.exports = function({ resourceMonitor, healthChecker, updateManager, logError, dependencyManager, autoRestartManager, driftDetector, sslMonitor, dnsPropagationChecker }) {
const router = express.Router();
const clients = new Set();
function broadcast(event, data) {
const msg = `event: ${event}\ndata: ${JSON.stringify(data)}\n\n`;
for (const res of clients) {
try { res.write(msg); } catch (_) { clients.delete(res); }
}
}
// --- Wire up EventEmitter listeners ---
// Resource monitor events
if (resourceMonitor) {
resourceMonitor.on('alert', (data) => {
broadcast('resource-alert', data);
});
resourceMonitor.on('auto-restart', (data) => {
broadcast('auto-restart', data);
});
}
// Health checker events
if (healthChecker) {
healthChecker.on('status-check', (data) => {
broadcast('status-change', {
serviceId: data.serviceId,
name: data.name,
status: data.status,
responseTime: data.responseTime,
timestamp: data.timestamp
});
});
healthChecker.on('incident-created', (data) => {
broadcast('incident', { type: 'created', ...data });
});
healthChecker.on('incident-resolved', (data) => {
broadcast('incident', { type: 'resolved', ...data });
});
}
// Update manager events
if (updateManager) {
updateManager.on('update-available', (data) => {
broadcast('update-available', data);
});
updateManager.on('update-start', (data) => {
broadcast('update-start', data);
});
updateManager.on('update-complete', (data) => {
broadcast('update-complete', data);
});
updateManager.on('update-failed', (data) => {
broadcast('update-failed', data);
});
updateManager.on('auto-update-start', (data) => {
broadcast('auto-update-start', data);
});
updateManager.on('auto-update-complete', (data) => {
broadcast('auto-update-complete', data);
});
}
// Dependency manager events
if (dependencyManager) {
dependencyManager.on('dependency-restart-start', (data) => {
broadcast('dependency-restart-start', data);
});
dependencyManager.on('dependency-restart-progress', (data) => {
broadcast('dependency-restart-progress', data);
});
dependencyManager.on('dependency-restart-complete', (data) => {
broadcast('dependency-restart-complete', data);
});
dependencyManager.on('dependency-restart-failed', (data) => {
broadcast('dependency-restart-failed', data);
});
}
// Auto-restart manager events
if (autoRestartManager) {
autoRestartManager.on('auto-restart-attempt', (data) => broadcast('auto-restart-attempt', data));
autoRestartManager.on('auto-restart-success', (data) => broadcast('auto-restart-success', data));
autoRestartManager.on('auto-restart-failed', (data) => broadcast('auto-restart-failed', data));
autoRestartManager.on('auto-restart-max-reached', (data) => broadcast('auto-restart-max-reached', data));
}
// Config drift detector events
if (driftDetector) {
driftDetector.on('drift-detected', (data) => broadcast('drift-detected', data));
}
// SSL monitor events
if (sslMonitor) {
sslMonitor.on('cert-expiring', (data) => broadcast('cert-expiring', data));
sslMonitor.on('cert-critical', (data) => broadcast('cert-critical', data));
}
// DNS propagation checker events
if (dnsPropagationChecker) {
dnsPropagationChecker.on('propagation-check', (data) => broadcast('dns-propagation-check', data));
dnsPropagationChecker.on('propagation-complete', (data) => broadcast('dns-propagation-complete', data));
dnsPropagationChecker.on('propagation-timeout', (data) => broadcast('dns-propagation-timeout', data));
}
// SSE endpoint
router.get('/stream', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no',
});
// Send initial connected event
res.write(`event: connected\ndata: ${JSON.stringify({ clients: clients.size + 1 })}\n\n`);
clients.add(res);
// Heartbeat every 30s
const heartbeat = setInterval(() => {
try { res.write(': heartbeat\n\n'); } catch (_) { cleanup(); }
}, 30000);
function cleanup() {
clearInterval(heartbeat);
clients.delete(res);
}
req.on('close', cleanup);
req.on('error', cleanup);
});
// Client count (useful for debugging)
router.get('/clients', (req, res) => {
res.json({ success: true, count: clients.size });
});
return router;
};