import type { IncomingMessage } from "node:http"; import type { WebSocket, WebSocketServer } from "ws"; import { verifyMeetingToken } from "./token"; import { broadcast, getParticipant, isKicked, joinRoom, kickFromRoom, leaveRoom, type Participant, } from "./rooms"; import type { ClientToServerMessage, ParticipantSummary } from "./protocol"; function participantSummary(p: Participant): ParticipantSummary { return { participantId: p.participantId, name: p.name, role: p.role }; } export function attachSignalingServer(wss: WebSocketServer) { wss.on("connection", (ws: WebSocket, req: IncomingMessage) => { const url = new URL(req.url ?? "", "http://internal"); const token = url.searchParams.get("token") ?? ""; const payload = verifyMeetingToken(token); if (!payload) { ws.close(4000, "invalid-token"); return; } if (isKicked(payload.eventSlug, payload.participantId)) { ws.close(4001, "removed-by-host"); return; } const self: Participant = { ws, participantId: payload.participantId, name: payload.name, role: payload.role, }; const existing = joinRoom(payload.eventSlug, self); ws.send( JSON.stringify({ type: "welcome", participantId: self.participantId, role: self.role, participants: existing.map(participantSummary), }), ); broadcast(payload.eventSlug, { type: "peer-joined", ...participantSummary(self) }, { exclude: self.participantId, }); ws.on("message", (raw) => { let message: ClientToServerMessage; try { message = JSON.parse(raw.toString()); } catch { return; } if (message.type === "offer" || message.type === "answer" || message.type === "ice-candidate") { const target = getParticipant(payload.eventSlug, message.to); if (target && target.ws.readyState === target.ws.OPEN) { target.ws.send( JSON.stringify({ type: message.type, from: self.participantId, payload: message.payload }), ); } return; } if (message.type === "kick") { if (self.role !== "host") return; // server-side authority, not client-claimed const target = getParticipant(payload.eventSlug, message.targetParticipantId); if (!target) return; kickFromRoom(payload.eventSlug, target.participantId); target.ws.send(JSON.stringify({ type: "kicked" })); target.ws.close(4001, "removed-by-host"); broadcast(payload.eventSlug, { type: "peer-left", participantId: target.participantId }); } }); ws.on("close", () => { leaveRoom(payload.eventSlug, self.participantId); broadcast(payload.eventSlug, { type: "peer-left", participantId: self.participantId }); }); }); }