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

+81
View File
@@ -0,0 +1,81 @@
import express from "express";
import http from "node:http";
import path from "node:path";
import { WebSocketServer } from "ws";
import type { BotManager } from "../bot/manager.js";
import type { MusicProvider } from "../music/provider.js";
import type { BotDatabase } from "../data/database.js";
import type { BotConfig } from "../data/config.js";
import type { Logger } from "../logger.js";
import { createBotRouter } from "./api/bot.js";
import { createMusicRouter } from "./api/music.js";
import { createPlayerRouter } from "./api/player.js";
import { createAuthRouter } from "./api/auth.js";
import { setupWebSocket } from "./websocket.js";
export interface WebServerOptions {
port: number;
botManager: BotManager;
neteaseProvider: MusicProvider;
qqProvider: MusicProvider;
database: BotDatabase;
config: BotConfig;
logger: Logger;
staticDir?: string;
}
export interface WebServer {
start(): Promise<void>;
stop(): void;
}
export function createWebServer(options: WebServerOptions): WebServer {
const app = express();
const server = http.createServer(app);
const logger = options.logger.child({ component: "web" });
app.use(express.json());
app.use(
"/api/bot",
createBotRouter(options.botManager, options.config, logger)
);
app.use(
"/api/music",
createMusicRouter(options.neteaseProvider, options.qqProvider, logger)
);
app.use("/api/player", createPlayerRouter(options.botManager, logger));
app.use(
"/api/auth",
createAuthRouter(options.neteaseProvider, options.qqProvider, logger)
);
app.get("/api/health", (_req, res) => {
res.json({ status: "ok", version: "0.1.0" });
});
if (options.staticDir) {
app.use(express.static(options.staticDir));
app.get("*", (_req, res) => {
res.sendFile(path.join(options.staticDir!, "index.html"));
});
}
const wss = new WebSocketServer({ server, path: "/ws" });
setupWebSocket(wss, options.botManager, logger);
return {
async start(): Promise<void> {
return new Promise((resolve) => {
server.listen(options.port, () => {
logger.info({ port: options.port }, "Web server started");
resolve();
});
});
},
stop(): void {
wss.close();
server.close();
},
};
}