Re_Backend/src/realtime/socket.ts

117 lines
4.2 KiB
TypeScript

import { Server } from 'socket.io';
let io: Server | null = null;
// Track online users per request: { requestId: Set<userId> }
const onlineUsersPerRequest = new Map<string, Set<string>>();
export function initSocket(httpServer: any) {
const defaultOrigins = [
'http://localhost:3000',
'http://127.0.0.1:3000',
'http://localhost:5173',
'http://127.0.0.1:5173'
];
const configured = (process.env.FRONTEND_ORIGIN || '').split(',').map(s => s.trim()).filter(Boolean);
const origins = configured.length ? configured : defaultOrigins;
io = new Server(httpServer, {
cors: {
origin: origins,
methods: ['GET', 'POST'],
credentials: true
},
path: '/socket.io',
transports: ['websocket', 'polling']
});
io.on('connection', (socket: any) => {
let currentRequestId: string | null = null;
let currentUserId: string | null = null;
// Join user's personal notification room
socket.on('join:user', (data: { userId: string }) => {
const userId = typeof data === 'string' ? data : data.userId;
socket.join(`user:${userId}`);
currentUserId = userId;
console.log(`[Socket] User ${userId} joined personal notification room`);
});
socket.on('join:request', (data: { requestId: string; userId?: string }) => {
const requestId = typeof data === 'string' ? data : data.requestId;
const userId = typeof data === 'object' ? data.userId : null;
socket.join(`request:${requestId}`);
currentRequestId = requestId;
currentUserId = userId || null;
if (userId) {
// Track this user as online for this request
if (!onlineUsersPerRequest.has(requestId)) {
onlineUsersPerRequest.set(requestId, new Set());
}
onlineUsersPerRequest.get(requestId)!.add(userId);
const onlineUsers = Array.from(onlineUsersPerRequest.get(requestId) || []);
// Broadcast presence to others in the room
socket.to(`request:${requestId}`).emit('presence:join', { userId, requestId });
// Send current online users to the newly joined socket
socket.emit('presence:online', { requestId, userIds: onlineUsers });
}
});
// Handle request for current online users (when component loads after join)
socket.on('request:online-users', (data: { requestId: string }) => {
const requestId = typeof data === 'string' ? data : data.requestId;
const onlineUsers = Array.from(onlineUsersPerRequest.get(requestId) || []);
socket.emit('presence:online', { requestId, userIds: onlineUsers });
});
socket.on('leave:request', (requestId: string) => {
socket.leave(`request:${requestId}`);
if (currentUserId && onlineUsersPerRequest.has(requestId)) {
onlineUsersPerRequest.get(requestId)!.delete(currentUserId);
if (onlineUsersPerRequest.get(requestId)!.size === 0) {
onlineUsersPerRequest.delete(requestId);
}
// Broadcast leave to others
socket.to(`request:${requestId}`).emit('presence:leave', { userId: currentUserId, requestId });
}
if (requestId === currentRequestId) {
currentRequestId = null;
currentUserId = null;
}
});
socket.on('disconnect', () => {
// Handle user going offline when connection drops
if (currentRequestId && currentUserId && onlineUsersPerRequest.has(currentRequestId)) {
onlineUsersPerRequest.get(currentRequestId)!.delete(currentUserId);
if (onlineUsersPerRequest.get(currentRequestId)!.size === 0) {
onlineUsersPerRequest.delete(currentRequestId);
}
// Broadcast disconnect to others in the room
socket.to(`request:${currentRequestId}`).emit('presence:leave', { userId: currentUserId, requestId: currentRequestId });
}
});
});
return io;
}
export function emitToRequestRoom(requestId: string, event: string, payload: any) {
if (!io) return;
io.to(`request:${requestId}`).emit(event, payload);
}
export function emitToUser(userId: string, event: string, payload: any) {
if (!io) return;
io.to(`user:${userId}`).emit(event, payload);
console.log(`[Socket] Emitted '${event}' to user ${userId}`);
}