import { Server, Socket } from "socket.io"; import jwt from "jsonwebtoken"; import { eventHandlerMap } from "../events/eventHandaler"; import { existingUserData } from "../../shared/services/AuthService"; interface UserPayload { userId: string; organization: string; [key: string]: any; } interface UserSocketInfo { socketId: string; userId: string; organization: string; } const connectedUsersByOrg: { [organization: string]: UserSocketInfo[] } = {}; export const SocketServer = async (io: Server) => { const namespaceNames = ["/edge", "/project", "/collection"]; const onlineUsers: { [organization: string]: Set } = {}; namespaceNames.forEach((nspName) => { const namespace = io.of(nspName); namespace.use(async (socket: Socket, next) => { try { const accessToken = socket.handshake.auth.accessToken as string; const refreshToken = socket.handshake.auth.refreshToken as string; if (!accessToken) return next(new Error("No access token provided")); const jwt_secret = process.env.JWT_SECRET as string; try { const decoded = jwt.verify(accessToken, jwt_secret) as UserPayload; const mailExistance = await existingUserData(decoded.email, decoded.organization); if (!mailExistance) { return next(new Error("Unauthorized user - not in system")); } (socket as any).user = decoded; return next(); } catch (err: any) { if (err.name === "TokenExpiredError") { if (!refreshToken) { return next(new Error("Token expired and no refresh token provided")); } try { const refreshSecret = process.env.REFRESH_JWT_SECRET as string; const decodedRefresh = jwt.verify(refreshToken, refreshSecret) as UserPayload; const mailExistance = await existingUserData(decodedRefresh.email, decodedRefresh.organization); if (!mailExistance) { return next(new Error("Unauthorized user - not in system")); } // Generate new access token const newAccessToken = jwt.sign( { email: decodedRefresh.email, organization: decodedRefresh.organization }, jwt_secret, { expiresIn: "3h" } // adjust expiry ); socket.emit("newAccessToken", newAccessToken); (socket as any).user = decodedRefresh; return next(); } catch (refreshErr) { return next(new Error("Refresh token expired or invalid")); } } return next(new Error("Invalid token")); } } catch (err) { return next(new Error("Authentication error")); } }); namespace.on("connection", (socket: Socket) => { const user = (socket as any).user as UserPayload; const { organization, userId } = user; if (!onlineUsers[organization]) onlineUsers[organization] = new Set(); onlineUsers[organization].add(socket.id); if (!connectedUsersByOrg[organization]) connectedUsersByOrg[organization] = []; connectedUsersByOrg[organization].push({ socketId: socket.id, userId, organization }); console.log(`✅ Connected to namespace ${nspName}: ${socket.id}`); // Common event handler socket.onAny((event: string, data: any, callback: any) => { const handler = eventHandlerMap[event]; if (handler) { handler(event, socket, io, data, connectedUsersByOrg, callback); } else { console.warn(` No handler found for event: ${event}`); } }); socket.on("disconnect", () => { onlineUsers[organization]?.delete(socket.id); if (onlineUsers[organization]?.size === 0) { delete onlineUsers[organization]; } connectedUsersByOrg[organization] = connectedUsersByOrg[organization]?.filter( (u) => u.socketId !== socket.id ); }); }); }); return io; };