/** * Lego 09 — Surveillance Gateway * Lego 11 — Socket Event Schema: monitor:subscribe / monitor:data * * Registers the Socket.io namespace on port 3000 (shared server). * On client subscribe → sends the latest snapshot immediately. * Every 500ms tick → SurveillanceService calls back → we broadcast to all. */ import { WebSocketGateway, WebSocketServer, SubscribeMessage, OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect, } from '@nestjs/websockets'; import { Logger, OnModuleInit } from '@nestjs/common'; import { Server, Socket } from 'socket.io'; import { SurveillanceService, MonitorPayload, MonitorStatus } from './surveillance.service'; @WebSocketGateway({ cors: { origin: '*' }, // Angular dev server on any port namespace: '/monitor', }) export class SurveillanceGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect, OnModuleInit { @WebSocketServer() private server!: Server; private readonly logger = new Logger(SurveillanceGateway.name); constructor(private readonly surveillanceService: SurveillanceService) {} // ─── Lifecycle ───────────────────────────────────────────────────────────── onModuleInit() { // Wire the service callback → broadcasts to all connected clients this.surveillanceService.registerMetricsCallback((metrics: MonitorPayload[]) => { this.broadcast(metrics); }); // Wire webhook probe results → broadcast monitor:status to all clients this.surveillanceService.registerStatusCallback((status: MonitorStatus) => { if (this.server) this.server.emit('monitor:status', status); }); } afterInit(server: Server) { this.logger.log('🔌 SurveillanceGateway initialized on /monitor namespace'); } handleConnection(client: Socket) { this.logger.log(`📡 Client connected: ${client.id}`); // Immediately push the current snapshot so the UI isn't blank on load const snapshot = this.surveillanceService.getLatestMetrics(); if (snapshot.length > 0) { client.emit('monitor:data', snapshot); } // Push the latest webhook status immediately so the UI doesn't wait 10 s client.emit('monitor:status', this.surveillanceService.getLatestStatus()); } handleDisconnect(client: Socket) { this.logger.log(`🔌 Client disconnected: ${client.id}`); } // ─── Event Handlers ──────────────────────────────────────────────────────── /** * Lego 11 — monitor:subscribe * UI emits this to start (or re-confirm) the resource tracking stream. * We acknowledge and immediately push the latest snapshot. */ @SubscribeMessage('monitor:subscribe') handleSubscribe(client: Socket) { this.logger.log(`🟢 monitor:subscribe from ${client.id}`); const snapshot = this.surveillanceService.getLatestMetrics(); client.emit('monitor:data', snapshot); return { event: 'monitor:subscribed', data: { ok: true } }; } // ─── Broadcast ───────────────────────────────────────────────────────────── private broadcast(metrics: MonitorPayload[]) { if (this.server) { this.server.emit('monitor:data', metrics); } } }