import express from 'express'; import http from 'http'; import { WebSocketServer, WebSocket } from 'ws'; import path from 'path'; import { fileURLToPath } from 'url'; import { logger } from '../util/logger.js'; import { statusRoutes } from './routes/status.js'; import { controlRoutes } from './routes/control.js'; import { linkRoutes } from './routes/links.js'; import { debugRoutes } from './routes/debug.js'; import type { Bot } from '../bot/Bot.js'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); export class Server { private app = express(); private server: http.Server; private wss: WebSocketServer; private clients: Set = new Set(); private bot: Bot; constructor(bot: Bot, private port: number = 3000) { this.bot = bot; this.app.use(express.json()); this.app.get('/', (_req, res) => { res.sendFile(path.join(__dirname, '..', '..', 'src', 'server', 'index.html')); }); // Mount routes this.app.use('/api', statusRoutes(bot)); this.app.use('/api', controlRoutes(bot)); this.app.use('/api/links', linkRoutes(bot)); this.app.use('/api/debug', debugRoutes(bot, this)); this.server = http.createServer(this.app); this.wss = new WebSocketServer({ server: this.server }); this.wss.on('connection', (ws) => { this.clients.add(ws); ws.send(JSON.stringify({ type: 'status', data: bot.getStatus() })); ws.on('close', () => this.clients.delete(ws)); }); // Subscribe to bot events bot.on('status-update', () => this.broadcastStatus()); bot.on('log', (level: string, message: string) => this.broadcastLog(level, message)); } broadcastStatus(): void { const msg = JSON.stringify({ type: 'status', data: this.bot.getStatus() }); for (const client of this.clients) { if (client.readyState === WebSocket.OPEN) { client.send(msg); } } } broadcastLog(level: string, message: string): void { const msg = JSON.stringify({ type: 'log', data: { level, message, time: new Date().toISOString() }, }); for (const client of this.clients) { if (client.readyState === WebSocket.OPEN) { client.send(msg); } } } broadcastDebug(action: string, data: Record): void { const msg = JSON.stringify({ type: 'debug', data: { action, ...data } }); for (const client of this.clients) { if (client.readyState === WebSocket.OPEN) { client.send(msg); } } } async start(): Promise { return new Promise((resolve) => { this.server.listen(this.port, () => { logger.info({ port: this.port }, `Dashboard running at http://localhost:${this.port}`); resolve(); }); }); } async stop(): Promise { for (const client of this.clients) { client.close(); } return new Promise((resolve) => { this.server.close(() => resolve()); }); } }