feat: add web backend — Express REST API, WebSocket, full wiring in entry point

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
saopig1andClaude Opus 4.6 committed 2026-03-30 00:47:19 +08:00
1 parent e5d5185f14
commit 0b54809113
7 files changed
+633

No files matched your search

+70
View File
@@ -0,0 +1,70 @@
import { WebSocketServer, WebSocket } from "ws";
import type { BotManager } from "../bot/manager.js";
import type { Logger } from "../logger.js";
export function setupWebSocket(
wss: WebSocketServer,
botManager: BotManager,
logger: Logger
): void {
const clients = new Set<WebSocket>();
wss.on("connection", (ws) => {
clients.add(ws);
logger.debug("WebSocket client connected");
const bots = botManager.getAllBots().map((b) => b.getStatus());
ws.send(JSON.stringify({ type: "init", bots }));
ws.on("close", () => {
clients.delete(ws);
logger.debug("WebSocket client disconnected");
});
ws.on("error", (err) => {
logger.error({ err }, "WebSocket error");
clients.delete(ws);
});
});
const broadcast = (data: object) => {
const message = JSON.stringify(data);
for (const client of clients) {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
}
}
};
const attachBotListeners = () => {
for (const bot of botManager.getAllBots()) {
bot.removeAllListeners("stateChange");
bot.removeAllListeners("connected");
bot.removeAllListeners("disconnected");
bot.on("stateChange", () => {
broadcast({
type: "stateChange",
botId: bot.id,
status: bot.getStatus(),
queue: bot.getQueue(),
});
});
bot.on("connected", () => {
broadcast({
type: "botConnected",
botId: bot.id,
status: bot.getStatus(),
});
});
bot.on("disconnected", () => {
broadcast({ type: "botDisconnected", botId: bot.id });
});
}
};
setInterval(attachBotListeners, 5000);
attachBotListeners();
}