import type { Server as HttpServer } from "node:http"; import { instrument } from "@socket.io/admin-ui"; import { Server } from "socket.io"; import { createLogger } from "../logger/logger.controller.js"; import { auth } from "../utils/auth.utils.js"; import { allowedOrigins } from "../utils/cors.utils.js"; import { registerEmitter } from "./roomEmitter.socket.js"; import { registerHasRoomMembers } from "./socket.manager.js"; import { isRoomKey, roomConfigs } from "./socket.roomConfig.js"; const log = createLogger({ module: "socket.io", subModule: "setup" }); export const setupSocketIORoutes = (baseUrl: string, server: HttpServer) => { const io = new Server(server, { path: `${baseUrl}/api/socket.io`, cors: { origin: allowedOrigins, credentials: true, }, }); registerHasRoomMembers((roomId) => { return (io.sockets.adapter.rooms.get(roomId)?.size ?? 0) > 0; }); registerEmitter((roomId, payloads) => { io.to(roomId).emit("room-update", { roomId, payloads, }); }); io.use(async (socket, next) => { try { //const cookieHeader = socket.handshake.headers.cookie; const headers = new Headers(); for (const [key, value] of Object.entries(socket.request.headers)) { if (typeof value === "string") { headers.set(key, value); } else if (Array.isArray(value)) { headers.set(key, value.join(", ")); } } const session = await auth.api.getSession({ headers, }); if (!session) { return next(); // allow connection, but no auth } if (session) { socket.user = session.user; socket.session = session as any; } next(); } catch (err) { next(); } }); io.on("connection", (s) => { log.info({}, `User connected: ${s.id}`); s.emit("welcome", { serverTime: Date.now(), availableRooms: ["logs", "labels"], version: "1.0.0", }); s.on("join-room", async ({ room, params }) => { if (!isRoomKey(room)) return; const config = roomConfigs[room]; if (!config) { return s.emit("room-error", { roomId: room, message: `Unknown room: ${room}`, }); } const actualRoom = config.buildRoom ? config.buildRoom(params) : (room as any); const allowed = config.canJoin ? await config.canJoin({ socket: s, user: s.user, room, actualRoom, params, }) : true; if (!allowed) { return s.emit("room-error", { roomId: room, message: `Not authorized to be in room: ${room}`, }); } await s.join(actualRoom); s.emit("room-joined", { room, roomId: actualRoom, params, }); if (config.seed) { const payloads = await config.seed({ room, actualRoom, params, user: s.user, }); s.emit("room-update", { room, roomId: actualRoom, type: "snapshot", payloads, }); } log.info( { room, actualRoom, params }, `User joined ${actualRoom}: ${s.id}`, ); }); // s.on("leave-room", (room) => { // s.leave(room); // log.info({}, `${s.id} left room: ${JSON.stringify(room)}`); // }); s.on("leave-room", async ({ room, params }) => { if (!isRoomKey(room)) return; const config = roomConfigs[room]; if (!config) return; const actualRoom = config.buildRoom ? config.buildRoom(params) : (room as any); await s.leave(actualRoom); s.emit("room-left", { room, roomId: actualRoom, params, }); log.info( { room, actualRoom, params }, `${s.id} left room: ${actualRoom}`, ); }); }); io.on("disconnect", (s) => { log.info({}, "User disconnected:", s.id); }); // admin stuff const admin = io.of("/admin"); admin.on("connection", (s) => { log.info({}, `User connected: ${s.id}`); }); admin.on("disconnect", (s) => { log.info({}, "User disconnected:", s.id); }); instrument(io, { auth: false, //namespaceName: "/admin", }); };