import express, { Express, Request, Response } from 'express'; import { Server } from 'http'; import { logger } from '../utils/logger'; import { config } from '../config'; export interface HttpServerCallbacks { onJoinRequest: (sessionId: string, meetingUrl: string, botName?: string, instanceId?: string, gatewayWsUrl?: string) => Promise; onLeaveRequest: (sessionId: string) => Promise; onStatusRequest: (sessionId: string) => { state: string; error?: string } | null; } /** * HTTP server for the Bot Launcher API. * Provides endpoints to deploy/control bots. */ export class HttpServer { private _app: Express; private _server: Server | null = null; private _callbacks: HttpServerCallbacks; constructor(callbacks: HttpServerCallbacks) { this._callbacks = callbacks; this._app = express(); this._setupMiddleware(); this._setupRoutes(); } /** * Start the HTTP server. */ async start(): Promise { return new Promise((resolve) => { this._server = this._app.listen(config.port, () => { logger.info(`HTTP server listening on port ${config.port}`); resolve(); }); }); } /** * Stop the HTTP server. */ async stop(): Promise { return new Promise((resolve, reject) => { if (!this._server) { resolve(); return; } this._server.close((err) => { if (err) { reject(err); } else { logger.info('HTTP server stopped'); resolve(); } }); }); } private _setupMiddleware(): void { this._app.use(express.json()); // Request logging this._app.use((req, res, next) => { logger.debug(`${req.method} ${req.path}`); next(); }); } private _setupRoutes(): void { // Health check this._app.get('/health', (req: Request, res: Response) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // Deploy a new bot this._app.post('/api/bot', async (req: Request, res: Response) => { try { const { sessionId, meetingUrl, botName, instanceId, gatewayWsUrl } = req.body; if (!sessionId || !meetingUrl) { res.status(400).json({ error: 'Missing required fields: sessionId, meetingUrl' }); return; } await this._callbacks.onJoinRequest(sessionId, meetingUrl, botName, instanceId, gatewayWsUrl); res.json({ success: true, sessionId, message: 'Bot deployment started', }); } catch (error) { logger.error('Error deploying bot:', error); res.status(500).json({ error: (error as Error).message }); } }); // Leave meeting this._app.post('/api/bot/:sessionId/leave', async (req: Request, res: Response) => { try { const { sessionId } = req.params; await this._callbacks.onLeaveRequest(sessionId); res.json({ success: true, sessionId, message: 'Bot leave initiated', }); } catch (error) { logger.error('Error leaving meeting:', error); res.status(500).json({ error: (error as Error).message }); } }); // Get bot status this._app.get('/api/bot/:sessionId/status', (req: Request, res: Response) => { const { sessionId } = req.params; const status = this._callbacks.onStatusRequest(sessionId); if (!status) { res.status(404).json({ error: 'Session not found' }); return; } res.json({ sessionId, ...status, }); }); // List all active bots this._app.get('/api/bots', (req: Request, res: Response) => { // This would need access to the session manager res.json({ message: 'Not implemented' }); }); } }