- Implemented `WebSocketsGateway` for handling Socket.IO connections, events, and rooms. - Added `WebSocketsService` to act as a facade for emitting WebSocket events (projects, groups, notifications). - Registered `WebSocketsModule` and integrated it into `AppModule`. - Enabled real-time updates in `ProjectsService` and `GroupsService` with relevant WebSocket events (create, update, delete, collaborator/group actions).
152 lines
4.3 KiB
TypeScript
152 lines
4.3 KiB
TypeScript
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<string, string>(); // 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}`);
|
|
}
|
|
} |