/** * Server — HTTP Server * * AUTO-GENERATED by Phoenix VCS * Provides health check, metrics, and module endpoints. */ import { createServer, IncomingMessage, ServerResponse } from 'node:http'; import * as connectionHandling from './connection-handling.js'; import * as rooms from './rooms.js'; // ─── Metrics ───────────────────────────────────────────────────────────────── const _svcMetrics = { requests_total: 0, requests_by_path: {} as Record, errors_total: 0, uptime_start: Date.now(), }; // ─── Module Registry ───────────────────────────────────────────────────────── const _svcModules = { 'connection-handling': connectionHandling, 'rooms': rooms, }; // ─── Router ────────────────────────────────────────────────────────────────── type Handler = (req: IncomingMessage, res: ServerResponse) => void | Promise; const routes: Record = { '/health': (_req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ status: 'ok', service: 'Server', uptime: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), modules: Object.keys(_svcModules), })); }, '/metrics': (_req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ ..._svcMetrics, uptime_seconds: Math.floor((Date.now() - _svcMetrics.uptime_start) / 1000), }, null, 2)); }, '/modules': (_req, res) => { const info = Object.entries(_svcModules).map(([name, mod]) => { const phoenix = (mod as Record)._phoenix as Record | undefined; return { name, risk_tier: phoenix?.risk_tier ?? 'unknown', exports: Object.keys(mod).filter(k => k !== '_phoenix'), }; }); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(info, null, 2)); }, }; // ─── Server ────────────────────────────────────────────────────────────────── function handleRequest(req: IncomingMessage, res: ServerResponse): void { const url = req.url ?? '/'; const path = url.split('?')[0]; _svcMetrics.requests_total++; _svcMetrics.requests_by_path[path] = (_svcMetrics.requests_by_path[path] ?? 0) + 1; const handler = routes[path]; if (handler) { try { handler(req, res); } catch (err) { _svcMetrics.errors_total++; res.writeHead(500, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: String(err) })); } } else { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not Found', path, available: Object.keys(routes), })); } } export function startServer(port?: number): { server: ReturnType; port: number; ready: Promise } { const requestedPort = port ?? parseInt(process.env.SERVER_PORT ?? process.env.PORT ?? '3001', 10); const server = createServer(handleRequest); let actualPort = requestedPort; const ready = new Promise(resolve => { server.listen(requestedPort, () => { const addr = server.address(); if (addr && typeof addr === 'object') actualPort = addr.port; result.port = actualPort; console.log(`Server listening on http://localhost:${actualPort}`); console.log(` /health — health check`); console.log(` /metrics — request metrics`); console.log(` /modules — registered modules`); resolve(); }); }); const result = { server, port: actualPort, ready }; return result; } // Start when run directly const isMain = process.argv[1]?.endsWith('/server/server.js') || process.argv[1]?.endsWith('/server/server.ts'); if (isMain) { startServer(); }