/** * Stock Bot Orchestrator Service * Coordinates between Rust core, data feeds, and analytics */ import { getLogger } from '@stock-bot/logger'; import { initializeStockConfig } from '@stock-bot/stock-config'; import { Hono } from 'hono'; import { cors } from 'hono/cors'; import { createRoutes } from './routes/create-routes'; import { createContainer } from './simple-container'; // Initialize configuration with service-specific overrides const config = initializeStockConfig('orchestrator'); const logger = getLogger('orchestrator'); // Get service-specific config const serviceConfig = config.services?.orchestrator || { port: 2004, defaultMode: 'paper', paperTradingCapital: 100000, enableWebSocket: true }; const PORT = serviceConfig.port; // Log the configuration logger.info('Service configuration:', { port: PORT, defaultMode: serviceConfig.defaultMode, enableWebSocket: serviceConfig.enableWebSocket, backtesting: serviceConfig.backtesting, strategies: serviceConfig.strategies }); async function main() { let server: any; // Declare server in outer scope for shutdown try { // Initialize container with all services using configuration const services = await createContainer(config); // Initialize Hono app const app = new Hono(); // CORS middleware - use config for origins app.use('*', cors({ origin: config.services?.webApi?.cors?.origins || ['http://localhost:4200', 'http://localhost:3000', 'http://localhost:5173', 'http://localhost:5174'], allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], allowHeaders: ['Content-Type', 'Authorization'], credentials: config.services?.webApi?.cors?.credentials ?? true, })); // Logging middleware app.use('*', async (c, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; services.logger.debug(`${c.req.method} ${c.req.url} - ${ms}ms`); }); // Create and mount routes (without Socket.IO for now) const routes = createRoutes(services); app.route('/', routes); // Start Bun server try { server = Bun.serve({ port: PORT, hostname: '0.0.0.0', // Explicitly bind to all interfaces fetch: async (req) => { services.logger.debug(`Incoming request: ${req.method} ${req.url}`); try { const response = await app.fetch(req); return response; } catch (error) { services.logger.error('Request handling error:', error); return new Response('Internal Server Error', { status: 500 }); } }, error: (error) => { services.logger.error('Server error:', error); return new Response('Internal Server Error', { status: 500 }); }, }); services.logger.info(`Orchestrator service started on port ${server.port}`); services.logger.info(`Server hostname: ${server.hostname}`); services.logger.info(`Server URL: http://${server.hostname}:${server.port}`); // Test that server is actually listening setTimeout(async () => { try { const testResponse = await fetch(`http://localhost:${server.port}/health`); services.logger.info(`Server self-test: ${testResponse.status} ${testResponse.statusText}`); } catch (error) { services.logger.error('Server self-test failed:', error); } }, 1000); } catch (error) { services.logger.error('Failed to start Bun server:', error); throw error; } services.logger.info('Service metadata:', { version: '1.0.0', description: 'Trading System Orchestrator', defaultMode: serviceConfig.defaultMode, enableWebSocket: serviceConfig.enableWebSocket, endpoints: { health: '/health', orders: '/api/orders', positions: '/api/positions', analytics: '/api/analytics', backtest: '/api/backtest', } }); // Note: Socket.IO with Bun requires a different setup // For now, we'll disable Socket.IO to avoid the CORS error if (serviceConfig.enableWebSocket) { services.logger.info('WebSocket support is enabled but Socket.IO integration with Bun requires additional setup'); } // Graceful shutdown process.on('SIGINT', async () => { services.logger.info('Orchestrator service shutting down...'); // Cleanup any active trading sessions const modeManager = services.custom?.ModeManager; if (modeManager) { await modeManager.shutdown(); } if (server) { server.stop(); } process.exit(0); }); } catch (error) { logger.error('Failed to start orchestrator service:', error); process.exit(1); } } // Start the service main().catch(error => { logger.error('Unhandled error:', error); process.exit(1); });