surveillance.gateway.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. /**
  2. * Lego 09 — Surveillance Gateway
  3. * Lego 11 — Socket Event Schema: monitor:subscribe / monitor:data
  4. *
  5. * Registers the Socket.io namespace on port 3000 (shared server).
  6. * On client subscribe → sends the latest snapshot immediately.
  7. * Every 500ms tick → SurveillanceService calls back → we broadcast to all.
  8. */
  9. import {
  10. WebSocketGateway,
  11. WebSocketServer,
  12. SubscribeMessage,
  13. OnGatewayInit,
  14. OnGatewayConnection,
  15. OnGatewayDisconnect,
  16. } from '@nestjs/websockets';
  17. import { Logger, OnModuleInit } from '@nestjs/common';
  18. import { Server, Socket } from 'socket.io';
  19. import { SurveillanceService, MonitorPayload, MonitorStatus } from './surveillance.service';
  20. @WebSocketGateway({
  21. cors: { origin: '*' }, // Angular dev server on any port
  22. namespace: '/monitor',
  23. })
  24. export class SurveillanceGateway
  25. implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect, OnModuleInit
  26. {
  27. @WebSocketServer()
  28. private server!: Server;
  29. private readonly logger = new Logger(SurveillanceGateway.name);
  30. constructor(private readonly surveillanceService: SurveillanceService) {}
  31. // ─── Lifecycle ─────────────────────────────────────────────────────────────
  32. onModuleInit() {
  33. // Wire the service callback → broadcasts to all connected clients
  34. this.surveillanceService.registerMetricsCallback((metrics: MonitorPayload[]) => {
  35. this.broadcast(metrics);
  36. });
  37. // Wire webhook probe results → broadcast monitor:status to all clients
  38. this.surveillanceService.registerStatusCallback((status: MonitorStatus) => {
  39. if (this.server) this.server.emit('monitor:status', status);
  40. });
  41. }
  42. afterInit(server: Server) {
  43. this.logger.log('🔌 SurveillanceGateway initialized on /monitor namespace');
  44. }
  45. handleConnection(client: Socket) {
  46. this.logger.log(`📡 Client connected: ${client.id}`);
  47. // Immediately push the current snapshot so the UI isn't blank on load
  48. const snapshot = this.surveillanceService.getLatestMetrics();
  49. if (snapshot.length > 0) {
  50. client.emit('monitor:data', snapshot);
  51. }
  52. // Push the latest webhook status immediately so the UI doesn't wait 10 s
  53. client.emit('monitor:status', this.surveillanceService.getLatestStatus());
  54. }
  55. handleDisconnect(client: Socket) {
  56. this.logger.log(`🔌 Client disconnected: ${client.id}`);
  57. }
  58. // ─── Event Handlers ────────────────────────────────────────────────────────
  59. /**
  60. * Lego 11 — monitor:subscribe
  61. * UI emits this to start (or re-confirm) the resource tracking stream.
  62. * We acknowledge and immediately push the latest snapshot.
  63. */
  64. @SubscribeMessage('monitor:subscribe')
  65. handleSubscribe(client: Socket) {
  66. this.logger.log(`🟢 monitor:subscribe from ${client.id}`);
  67. const snapshot = this.surveillanceService.getLatestMetrics();
  68. client.emit('monitor:data', snapshot);
  69. return { event: 'monitor:subscribed', data: { ok: true } };
  70. }
  71. // ─── Broadcast ─────────────────────────────────────────────────────────────
  72. private broadcast(metrics: MonitorPayload[]) {
  73. if (this.server) {
  74. this.server.emit('monitor:data', metrics);
  75. }
  76. }
  77. }