refactor: extract websocket server

This commit is contained in:
MythEclipse
2026-05-19 14:45:39 +07:00
parent d52803ae20
commit 109a6825bc
2 changed files with 88 additions and 38 deletions
+10 -38
View File
@@ -9,7 +9,6 @@ import express, {
type Response, type Response,
} from "express"; } from "express";
import helmet from "helmet"; import helmet from "helmet";
import { WebSocketServer } from "ws";
import { config } from "./config"; import { config } from "./config";
import { AppError } from "./errors"; import { AppError } from "./errors";
import { createChildLogger, logger } from "./logger"; import { createChildLogger, logger } from "./logger";
@@ -37,7 +36,7 @@ import {
exposePcmBroadcastGlobal, exposePcmBroadcastGlobal,
exposeVideoBroadcastGlobal, exposeVideoBroadcastGlobal,
} from "./ws/broadcastGlobals"; } from "./ws/broadcastGlobals";
import { createVoiceAudioBridge } from "./ws/voiceAudioBridge"; import { startWebSocketServer } from "./ws/server";
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename); const __dirname = path.dirname(__filename);
@@ -61,8 +60,6 @@ export async function startWebserver(
const server = http.createServer(app); const server = http.createServer(app);
const wsPath = "/ws"; const wsPath = "/ws";
const wss = new WebSocketServer({ server, path: wsPath });
wsLogger.info({ port, wsPath }, "WebSocket server listening");
// Create broadcaster instance // Create broadcaster instance
const broadcaster = createBroadcaster(); const broadcaster = createBroadcaster();
@@ -205,40 +202,15 @@ export async function startWebserver(
exposeVideoBroadcastGlobal(() => broadcaster.getClients(), wsLogger); exposeVideoBroadcastGlobal(() => broadcaster.getClients(), wsLogger);
exposeActiveUserGlobal(activeUsers, broadcastUserState); exposeActiveUserGlobal(activeUsers, broadcastUserState);
const voiceAudioBridge = createVoiceAudioBridge(wsLogger); startWebSocketServer({
server,
wss.on("connection", (ws) => { port,
wsLogger.info({ port, wsPath }, "New WebSocket connection"); wsPath,
broadcaster.addClient(ws); broadcaster,
activeUsers,
ws.send( getSharedUIState,
JSON.stringify({ mediaController,
type: "user_state", logger: wsLogger,
users: Array.from(activeUsers.entries()).map(([id, data]) => ({
id,
...data,
})),
}),
);
ws.send(JSON.stringify({ type: "ui_state", state: getSharedUIState() }));
ws.send(
JSON.stringify({
type: "media_state",
state: mediaController.getState(),
}),
);
ws.on("message", (data: Buffer | ArrayBuffer | Buffer[]) => {
if (!Buffer.isBuffer(data)) return;
voiceAudioBridge.handleBrowserAudio(data);
});
ws.on("close", () => {
broadcaster.removeClient(ws);
});
ws.on("error", () => {
broadcaster.removeClient(ws);
});
}); });
app.use( app.use(
+78
View File
@@ -0,0 +1,78 @@
import type { Server as HttpServer } from "node:http";
import { WebSocketServer } from "ws";
import type { createChildLogger } from "../logger";
import type { MediaController } from "../media/mediaController";
import type { ModerationBroadcaster } from "../moderation/types";
import { createVoiceAudioBridge } from "./voiceAudioBridge";
type Logger = ReturnType<typeof createChildLogger>;
type ActiveUsers = Map<
string,
{ username: string; avatar: string; speaking: boolean }
>;
export interface WebSocketServerOptions {
server: HttpServer;
port: number;
wsPath: string;
broadcaster: ModerationBroadcaster;
activeUsers: ActiveUsers;
getSharedUIState: () => unknown;
mediaController: MediaController;
logger: Logger;
}
export function startWebSocketServer(options: WebSocketServerOptions) {
const wss = new WebSocketServer({
server: options.server,
path: options.wsPath,
});
const voiceAudioBridge = createVoiceAudioBridge(options.logger);
options.logger.info(
{ port: options.port, wsPath: options.wsPath },
"WebSocket server listening",
);
wss.on("connection", (ws) => {
options.logger.info(
{ port: options.port, wsPath: options.wsPath },
"New WebSocket connection",
);
options.broadcaster.addClient(ws);
ws.send(
JSON.stringify({
type: "user_state",
users: Array.from(options.activeUsers.entries()).map(([id, data]) => ({
id,
...data,
})),
}),
);
ws.send(
JSON.stringify({ type: "ui_state", state: options.getSharedUIState() }),
);
ws.send(
JSON.stringify({
type: "media_state",
state: options.mediaController.getState(),
}),
);
ws.on("message", (data: Buffer | ArrayBuffer | Buffer[]) => {
if (!Buffer.isBuffer(data)) return;
voiceAudioBridge.handleBrowserAudio(data);
});
ws.on("close", () => {
options.broadcaster.removeClient(ws);
});
ws.on("error", () => {
options.broadcaster.removeClient(ws);
});
});
return wss;
}