import { WebSocketGateway, WebSocketServer, SubscribeMessage, OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, } from '@nestjs/websockets'; import { Logger } from '@nestjs/common'; import { Server, Socket } from 'socket.io'; /** * WebSocketsGateway * * This gateway handles all WebSocket connections and events. * It implements the events specified in the specifications: * - project:updated * - project:collaboratorAdded * - group:created * - group:updated * - group:personAdded * - group:personRemoved * - notification:new */ @WebSocketGateway({ cors: { origin: process.env.FRONTEND_URL || 'http://localhost:3001', credentials: true, }, }) export class WebSocketsGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { @WebSocketServer() server: Server; private logger = new Logger('WebSocketsGateway'); private connectedClients = new Map(); // socketId -> userId /** * After gateway initialization */ afterInit(server: Server) { this.logger.log('WebSocket Gateway initialized'); } /** * Handle new client connections */ handleConnection(client: Socket, ...args: any[]) { const userId = client.handshake.query.userId as string; if (userId) { this.connectedClients.set(client.id, userId); client.join(`user:${userId}`); this.logger.log(`Client connected: ${client.id}, User ID: ${userId}`); } else { this.logger.warn(`Client connected without user ID: ${client.id}`); } } /** * Handle client disconnections */ handleDisconnect(client: Socket) { this.connectedClients.delete(client.id); this.logger.log(`Client disconnected: ${client.id}`); } /** * Join a project room to receive project-specific events */ @SubscribeMessage('project:join') handleJoinProject(client: Socket, projectId: string) { client.join(`project:${projectId}`); this.logger.log(`Client ${client.id} joined project room: ${projectId}`); return { success: true }; } /** * Leave a project room */ @SubscribeMessage('project:leave') handleLeaveProject(client: Socket, projectId: string) { client.leave(`project:${projectId}`); this.logger.log(`Client ${client.id} left project room: ${projectId}`); return { success: true }; } /** * Emit project updated event */ emitProjectUpdated(projectId: string, data: any) { this.server.to(`project:${projectId}`).emit('project:updated', data); this.logger.log(`Emitted project:updated for project ${projectId}`); } /** * Emit collaborator added event */ emitCollaboratorAdded(projectId: string, data: any) { this.server.to(`project:${projectId}`).emit('project:collaboratorAdded', data); this.logger.log(`Emitted project:collaboratorAdded for project ${projectId}`); } /** * Emit group created event */ emitGroupCreated(projectId: string, data: any) { this.server.to(`project:${projectId}`).emit('group:created', data); this.logger.log(`Emitted group:created for project ${projectId}`); } /** * Emit group updated event */ emitGroupUpdated(projectId: string, data: any) { this.server.to(`project:${projectId}`).emit('group:updated', data); this.logger.log(`Emitted group:updated for project ${projectId}`); } /** * Emit person added to group event */ emitPersonAddedToGroup(projectId: string, data: any) { this.server.to(`project:${projectId}`).emit('group:personAdded', data); this.logger.log(`Emitted group:personAdded for project ${projectId}`); } /** * Emit person removed from group event */ emitPersonRemovedFromGroup(projectId: string, data: any) { this.server.to(`project:${projectId}`).emit('group:personRemoved', data); this.logger.log(`Emitted group:personRemoved for project ${projectId}`); } /** * Emit notification to a specific user */ emitNotification(userId: string, data: any) { this.server.to(`user:${userId}`).emit('notification:new', data); this.logger.log(`Emitted notification:new for user ${userId}`); } /** * Emit notification to all users in a project */ emitProjectNotification(projectId: string, data: any) { this.server.to(`project:${projectId}`).emit('notification:new', data); this.logger.log(`Emitted notification:new for project ${projectId}`); } }