import WebSocket from 'ws'; import { ClientMessage } from '../utils/protocol'; import { safeSend, ServerMessageType } from '../utils/protocol'; import { isOnline } from '../redis/presenceClient'; import { enqueueOfflineMessage } from '../redis/messageClient'; import { registry } from '../services/connectionRegistry'; import { logger } from '../utils/logger'; /** * Handles WebRTC signaling: * - offer / answer / ice-candidate are forwarded to the target peer. * - If the peer is offline, the signal is dropped (SDP signals don't * queue — they are ephemeral). Only chat messages queue offline. * * WebRTC fallback to WS relay: * When a client detects iceConnectionState === 'failed' or 'disconnected', * it switches to type: 'relay' messages instead. * The server relays those via handleRelay in connectionHandler.ts. * This handler is only responsible for SDP/ICE forwarding. */ export async function handleSignaling( _senderWs: WebSocket, fromUid: string, msg: ClientMessage ): Promise { const toUid = msg.payload.toUid as string | undefined; if (!toUid || typeof toUid !== 'string') { logger.warn('Signaling message missing toUid', { fromUid, type: msg.type }); return; } // Security: block self-signaling if (toUid === fromUid) return; const target = registry.getByUid(toUid); if (!target) { // Peer not on this process — check Redis const online = await isOnline(toUid).catch(() => false); if (!online) { // Peer offline — signal is ephemeral, inform the sender safeSend(_senderWs, 'peer-offline', { uid: toUid }); } // In a multi-node deployment: publish to Redis pub/sub here // for cross-node delivery. Single-node (HF): nothing to do. return; } // Forward the signal verbatim to the peer const forwardType = msg.type as ServerMessageType; safeSend( target.ws, forwardType, { fromUid, sdp: msg.payload.sdp, candidate: msg.payload.candidate, }, msg.requestId ); logger.debug('Signal forwarded', { type: msg.type, fromUid, toUid }); } /** * Notify a user that a specific peer has come online. * Called from connectionHandler after the user connects. */ export function notifyPeerOnline(targetUid: string, peerUid: string): void { const target = registry.getByUid(targetUid); if (!target) return; safeSend(target.ws, 'peer-online', { uid: peerUid }); } /** * Notify a user that a specific peer has gone offline. * Called from connectionHandler on disconnect. */ export function notifyPeerOffline(targetUid: string, peerUid: string): void { const target = registry.getByUid(targetUid); if (!target) return; safeSend(target.ws, 'peer-offline', { uid: peerUid }); }