63 lines · 1.5 KB · TypeScript
RawDownload
/** * SSE (Server-Sent Events) route for real-time event streaming. * * Routes: * - GET /api/events: SSE stream with keep-alive pings */ import { createLogger } from '@shared/utils/logger'; import type { FastifyInstance, FastifyReply } from 'fastify'; const logger = createLogger('HTTP:events'); const KEEPALIVE_INTERVAL_MS = 30_000; /** All connected SSE clients */const clients = new Set<FastifyReply>(); /** * Registers the SSE events endpoint. */export function registerEventRoutes(app: FastifyInstance): void {  app.get('/api/events', async (request, reply) => {    reply.raw.writeHead(200, {      'Content-Type': 'text/event-stream',      'Cache-Control': 'no-cache',      Connection: 'keep-alive',    });     clients.add(reply);    logger.info(`SSE client connected (total: ${clients.size})`);     // Keep-alive ping    const timer = setInterval(() => {      reply.raw.write(':ping\n\n');    }, KEEPALIVE_INTERVAL_MS);     // Cleanup on disconnect    request.raw.on('close', () => {      clearInterval(timer);      clients.delete(reply);      logger.info(`SSE client disconnected (total: ${clients.size})`);    });     // Prevent Fastify from ending the response    await reply;  });} /** * Broadcasts an event to all connected SSE clients. */export function broadcastEvent(channel: string, data: unknown): void {  const payload = `event: ${channel}\ndata: ${JSON.stringify(data)}\n\n`;   for (const client of clients) {    try {      client.raw.write(payload);    } catch {      clients.delete(client);    }  }}