/** * Trading Platform - Backend API * ========================== * * Main entry point for the Express.js backend API. */ import express, { Express, Request, Response } from 'express'; import { createServer } from 'http'; import cors from 'cors'; import helmet from 'helmet'; import compression from 'compression'; import morgan from 'morgan'; import { config } from './config/index.js'; import { logger } from './shared/utils/logger.js'; import { setupSwagger } from './config/swagger.config.js'; // WebSocket import { wsManager, tradingStreamService } from './core/websocket/index.js'; // Background Jobs import { distributionJob } from './modules/investment/jobs/index.js'; // Portfolio WebSocket import { portfolioWebSocket } from './modules/portfolio/websocket/portfolio.websocket.js'; // Import routes import { authRouter } from './modules/auth/auth.routes.js'; import { usersRouter } from './modules/users/users.routes.js'; import { educationRouter } from './modules/education/education.routes.js'; import { tradingRouter } from './modules/trading/trading.routes.js'; import { investmentRouter } from './modules/investment/investment.routes.js'; import { paymentsRouter } from './modules/payments/payments.routes.js'; import { adminRouter } from './modules/admin/admin.routes.js'; import { mlRouter } from './modules/ml/ml.routes.js'; import { llmRouter } from './modules/llm/llm.routes.js'; import { portfolioRouter } from './modules/portfolio/portfolio.routes.js'; import { agentsRouter } from './modules/agents/agents.routes.js'; import { notificationRouter } from './modules/notifications/notification.routes.js'; // Service clients for health checks import { tradingAgentsClient, mlEngineClient, llmAgentClient } from './shared/clients/index.js'; // Health aggregator import { getSystemHealth, getQuickHealth } from './shared/utils/health-aggregator.js'; // Import middleware import { errorHandler } from './core/middleware/error-handler.js'; import { notFoundHandler } from './core/middleware/not-found.js'; import { rateLimiter } from './core/middleware/rate-limiter.js'; const app: Express = express(); // Trust proxy (for rate limiting behind reverse proxy) app.set('trust proxy', 1); // Security middleware app.use(helmet()); // CORS app.use(cors({ origin: config.cors.origins, credentials: true, methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'], exposedHeaders: ['X-Token-Expires-At'], // FASE 4: Expose token expiry for proactive refresh })); // Compression app.use(compression()); // Request logging app.use(morgan('combined', { stream: { write: (message) => logger.info(message.trim()) } })); // Body parsing app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Rate limiting app.use(rateLimiter); // Swagger documentation setupSwagger(app, '/api/v1'); // Quick health check (before auth) - just checks if backend is running app.get('/health', (req: Request, res: Response) => { const quickHealth = getQuickHealth(); res.json({ ...quickHealth, version: config.app.version, environment: config.app.env, }); }); // Full system health check - checks all services including database app.get('/health/full', async (req: Request, res: Response) => { try { const systemHealth = await getSystemHealth(); const statusCode = systemHealth.status === 'healthy' ? 200 : systemHealth.status === 'degraded' ? 200 : 503; res.status(statusCode).json(systemHealth); } catch (error) { logger.error('Health check failed:', error); res.status(500).json({ status: 'unhealthy', error: 'Health check failed', timestamp: new Date().toISOString(), }); } }); // Services health check (legacy, kept for compatibility) app.get('/health/services', async (req: Request, res: Response) => { try { const systemHealth = await getSystemHealth(); // Transform to legacy format for backwards compatibility const services: Record = {}; for (const service of systemHealth.services) { services[service.name] = { status: service.status, latency: service.latency_ms, error: service.error, }; } res.json({ status: systemHealth.status, services, timestamp: systemHealth.timestamp, }); } catch (error) { logger.error('Services health check failed:', error); res.status(500).json({ status: 'unhealthy', error: 'Health check failed', timestamp: new Date().toISOString(), }); } }); // API routes const apiRouter = express.Router(); apiRouter.use('/auth', authRouter); apiRouter.use('/users', usersRouter); apiRouter.use('/education', educationRouter); apiRouter.use('/trading', tradingRouter); apiRouter.use('/investment', investmentRouter); apiRouter.use('/payments', paymentsRouter); apiRouter.use('/admin', adminRouter); apiRouter.use('/ml', mlRouter); apiRouter.use('/llm', llmRouter); apiRouter.use('/portfolio', portfolioRouter); apiRouter.use('/agents', agentsRouter); apiRouter.use('/notifications', notificationRouter); // Mount API router app.use('/api/v1', apiRouter); // 404 handler app.use(notFoundHandler); // Error handler app.use(errorHandler); // Create HTTP server const httpServer = createServer(app); // Initialize WebSocket wsManager.initialize(httpServer); tradingStreamService.initialize(); portfolioWebSocket.initialize(); // Start background jobs distributionJob.start(); // WebSocket stats endpoint app.get('/api/v1/ws/stats', (req: Request, res: Response) => { res.json({ success: true, data: tradingStreamService.getStats(), }); }); // Start server const PORT = config.app.port; httpServer.listen(PORT, () => { logger.info(`🚀 Trading Platform API server running on port ${PORT}`); logger.info(`📡 WebSocket server running on ws://localhost:${PORT}/ws`); logger.info(`📚 Environment: ${config.app.env}`); logger.info(`📖 Docs available at http://localhost:${PORT}/api/v1/docs`); }); // Graceful shutdown const gracefulShutdown = () => { logger.info('Shutting down gracefully...'); distributionJob.stop(); portfolioWebSocket.shutdown(); tradingStreamService.shutdown(); wsManager.shutdown(); httpServer.close(() => { logger.info('HTTP server closed'); process.exit(0); }); }; process.on('SIGTERM', () => { logger.info('SIGTERM received.'); gracefulShutdown(); }); process.on('SIGINT', () => { logger.info('SIGINT received.'); gracefulShutdown(); }); export { app };