lila/apps/api/src/ws/auth.ts
lila 745c5c4e3a feat(api): add WebSocket foundation and multiplayer game store
- Add ws/ directory: server setup, auth, router, connections map
- WebSocket auth rejects upgrade with 401 if no Better Auth session
- Router parses WsClientMessageSchema, dispatches to handlers,
  two-layer error handling (AppError -> WsErrorSchema, unknown -> 500)
- connections.ts: in-memory Map<lobbyId, Map<userId, WebSocket>>
  with addConnection, removeConnection, broadcastToLobby
- LobbyGameStore interface + InMemoryLobbyGameStore implementation
  following existing GameSessionStore pattern
- multiplayerGameService: generateMultiplayerQuestions() decoupled
  from single-player flow, hardcoded defaults en->it nouns easy 3 rounds
- handleLobbyJoin and handleLobbyLeave implemented
- WsErrorSchema added to shared schemas
- server.ts switched to createServer + setupWebSocket
2026-04-17 09:36:16 +02:00

32 lines
896 B
TypeScript

import type { IncomingMessage } from "http";
import type { Duplex } from "stream";
import type { WebSocketServer, WebSocket } from "ws";
import { fromNodeHeaders } from "better-auth/node";
import { auth } from "../lib/auth.js";
export const handleUpgrade = async (
request: IncomingMessage,
socket: Duplex,
head: Buffer,
wss: WebSocketServer,
): Promise<void> => {
try {
const session = await auth.api.getSession({
headers: fromNodeHeaders(request.headers),
});
if (!session) {
socket.write("HTTP/1.1 401 Unauthorized\r\n\r\n");
socket.destroy();
return;
}
wss.handleUpgrade(request, socket, head, (ws: WebSocket) => {
wss.emit("connection", ws, request, session);
});
} catch (err) {
console.error("WebSocket auth error:", err);
socket.write("HTTP/1.1 500 Internal Server Error\r\n\r\n");
socket.destroy();
}
};