'use strict'; /** * health.js — HTTP health endpoint + metrics. * * GET /health -> JSON { status, activeSessions, uptime, lastError, metrics } * GET /metrics -> JSON { detailed metrics } */ /* eslint-disable no-console */ const http = require('http'); class HealthServer { /** * @param {object} opts * @param {number} opts.port - Port to listen on (0 = disabled) * @param {Function} opts.getMetrics - Callback that returns metrics object * @param {object} [opts.logger] - pino logger */ constructor(opts) { this.port = opts.port; this.getMetrics = opts.getMetrics; this.logger = opts.logger || null; this.server = null; this.startTime = Date.now(); } start() { if (this.port === 0) { if (this.logger) this.logger.info('Health server disabled (port=0)'); return Promise.resolve(); } return new Promise((resolve, reject) => { this.server = http.createServer((req, res) => { this._handleRequest(req, res); }); this.server.on('error', (err) => { if (this.logger) { this.logger.error({ err }, 'Health server error'); } else { console.error('Health server error:', err.message); } reject(err); }); this.server.listen(this.port, '127.0.0.1', () => { if (this.logger) { this.logger.info({ port: this.port }, 'Health server listening'); } resolve(); }); }); } stop() { return new Promise((resolve) => { if (!this.server) { resolve(); return; } this.server.close(() => resolve()); }); } _handleRequest(req, res) { const url = new URL(req.url, `http://localhost:${this.port}`); if (req.method !== 'GET') { res.writeHead(405, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Method not allowed' })); return; } let body; switch (url.pathname) { case '/health': body = this._buildHealthResponse(); break; case '/metrics': body = this._buildMetricsResponse(); break; default: res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found' })); return; } res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(body, null, 2)); } _buildHealthResponse() { const metrics = this.getMetrics(); const status = metrics.circuit && metrics.circuit.state === 'open' ? 'degraded' : 'healthy'; return { status, uptime: Math.floor((Date.now() - this.startTime) / 1000), activeSessions: metrics.activeSessions || 0, lastError: metrics.lastError || null, metrics: { updates_sent: metrics.updatesSent || 0, updates_failed: metrics.updatesFailed || 0, circuit_state: metrics.circuit ? metrics.circuit.state : 'unknown', queue_depth: metrics.queueDepth || 0, }, }; } _buildMetricsResponse() { return this.getMetrics(); } } module.exports = { HealthServer };