const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const cors = require('cors'); const helmet = require('helmet'); const morgan = require('morgan'); const rateLimit = require('express-rate-limit'); const { createProxyMiddleware } = require('http-proxy-middleware'); const jwt = require('jsonwebtoken'); const axios = require('axios'); require('dotenv').config(); // Import middleware const authMiddleware = require('./middleware/authentication'); const serviceHealthMiddleware = require('./middleware/serviceHealth'); const requestLogger = require('./middleware/requestLogger'); const websocketAuth = require('./middleware/webSocket'); // Import route handlers const serviceRouter = require('./routes/serviceRouter'); const healthRouter = require('./routes/healthRouter'); const websocketRouter = require('./routes/websocketRouter'); const app = express(); const server = http.createServer(app); const PORT = process.env.PORT || 8000; // Initialize Socket.IO with CORS const io = socketIo(server, { cors: { origin: process.env.FRONTEND_URL || "*", credentials: true, methods: ['GET', 'POST'] }, transports: ['websocket', 'polling'] }); // Make io available globally for other modules global.io = io; // Service targets configuration const serviceTargets = { USER_AUTH_URL: process.env.USER_AUTH_URL || 'http://localhost:8011', TEMPLATE_MANAGER_URL: process.env.TEMPLATE_MANAGER_URL || 'http://localhost:8009', REQUIREMENT_PROCESSOR_URL: process.env.REQUIREMENT_PROCESSOR_URL || 'http://localhost:8001', TECH_STACK_SELECTOR_URL: process.env.TECH_STACK_SELECTOR_URL || 'http://localhost:8002', ARCHITECTURE_DESIGNER_URL: process.env.ARCHITECTURE_DESIGNER_URL || 'http://localhost:8003', CODE_GENERATOR_URL: process.env.CODE_GENERATOR_URL || 'http://localhost:8004', TEST_GENERATOR_URL: process.env.TEST_GENERATOR_URL || 'http://localhost:8005', DEPLOYMENT_MANAGER_URL: process.env.DEPLOYMENT_MANAGER_URL || 'http://localhost:8006', DASHBOARD_URL: process.env.DASHBOARD_URL || 'http://localhost:8008', SELF_IMPROVING_GENERATOR_URL: process.env.SELF_IMPROVING_GENERATOR_URL || 'http://localhost:8007', }; // ======================================== // MIDDLEWARE SETUP // ======================================== // Security middleware app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"], scriptSrc: ["'self'"], imgSrc: ["'self'", "data:", "https:"], connectSrc: ["'self'", "ws:", "wss:"] } } })); // CORS configuration app.use(cors({ origin: process.env.FRONTEND_URL || "*", credentials: true, optionsSuccessStatus: 200, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: [ 'Origin', 'X-Requested-With', 'Content-Type', 'Accept', 'Authorization', 'X-Gateway-Request-ID', 'X-Gateway-Timestamp', 'X-Forwarded-By', 'X-Forwarded-For', 'X-Forwarded-Proto', 'X-Forwarded-Host', 'X-Session-Token', 'X-Platform', 'X-App-Version' ], exposedHeaders: [ 'X-Gateway-Request-ID', 'X-Gateway-Timestamp', 'X-Forwarded-By', 'X-Forwarded-For', 'X-Forwarded-Proto', 'X-Forwarded-Host' ] })); // Request parsing middleware - only for non-proxy routes app.use('/api/websocket', express.json({ limit: '10mb' })); app.use('/api/gateway', express.json({ limit: '10mb' })); app.use('/api/auth', express.json({ limit: '10mb' })); app.use('/api/templates', express.json({ limit: '10mb' })); app.use('/api/features', express.json({ limit: '10mb' })); app.use('/health', express.json({ limit: '10mb' })); // Trust proxy for accurate IP addresses app.set('trust proxy', 1); // Request ID middleware for tracing app.use((req, res, next) => { req.requestId = `gw-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; res.setHeader('X-Request-ID', req.requestId); next(); }); app.use(morgan(process.env.NODE_ENV === 'production' ? 'combined' : 'dev')); // Custom request logger for service tracking app.use(requestLogger.logRequest); // Rate limiting configuration const createServiceLimiter = (maxRequests = 1000) => rateLimit({ windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS) || 15 * 60 * 1000, max: maxRequests, message: { success: false, message: 'Too many requests, please try again later.', retry_after: 900 }, standardHeaders: true, legacyHeaders: false }); // Health check endpoint (before rate limiting and authentication) app.get('/health', (req, res) => { res.json({ success: true, service: 'api-gateway', status: 'healthy', timestamp: new Date().toISOString(), version: process.env.npm_package_version || '1.0.0', environment: process.env.NODE_ENV || 'development', uptime: process.uptime(), services: { user_auth: process.env.USER_AUTH_URL ? 'configured' : 'not configured', template_manager: process.env.TEMPLATE_MANAGER_URL ? 'configured' : 'not configured', requirement_processor: process.env.REQUIREMENT_PROCESSOR_URL ? 'configured' : 'not configured', tech_stack_selector: process.env.TECH_STACK_SELECTOR_URL ? 'configured' : 'not configured', architecture_designer: process.env.ARCHITECTURE_DESIGNER_URL ? 'configured' : 'not configured', code_generator: process.env.CODE_GENERATOR_URL ? 'configured' : 'not configured', test_generator: process.env.TEST_GENERATOR_URL ? 'configured' : 'not configured', deployment_manager: process.env.DEPLOYMENT_MANAGER_URL ? 'configured' : 'not configured', dashboard: process.env.DASHBOARD_URL ? 'configured' : 'not configured', self_improving_generator: process.env.SELF_IMPROVING_GENERATOR_URL ? 'configured' : 'not configured' }, websocket: 'enabled' }); }); // Service health monitoring routes app.use('/health', healthRouter.router); // WebSocket connection handling const websocketHandlers = websocketAuth(io); // Auth Service - Fixed proxy with proper connection handling console.log('🔧 Registering /api/auth proxy route...'); app.use('/api/auth', (req, res, next) => { const authServiceUrl = serviceTargets.USER_AUTH_URL; console.log(`🔥 [AUTH PROXY] ${req.method} ${req.originalUrl} → ${authServiceUrl}${req.originalUrl}`); // Set response timeout to prevent hanging res.setTimeout(15000, () => { console.error('❌ [AUTH PROXY] Response timeout'); if (!res.headersSent) { res.status(504).json({ error: 'Gateway timeout', service: 'user-auth' }); } }); const options = { method: req.method, url: `${authServiceUrl}${req.originalUrl}`, headers: { 'Content-Type': 'application/json', 'User-Agent': 'API-Gateway/1.0', 'Connection': 'keep-alive' }, timeout: 8000, validateStatus: () => true, maxRedirects: 0 }; // Always include request body for POST/PUT/PATCH requests if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') { options.data = req.body || {}; console.log(`📦 [AUTH PROXY] Request body:`, JSON.stringify(req.body)); } axios(options) .then(response => { console.log(`✅ [AUTH PROXY] Response: ${response.status} for ${req.method} ${req.originalUrl}`); if (!res.headersSent) { res.status(response.status).json(response.data); } }) .catch(error => { console.error(`❌ [AUTH PROXY ERROR]:`, error.message); if (!res.headersSent) { if (error.response) { res.status(error.response.status).json(error.response.data); } else { res.status(502).json({ error: 'Auth service unavailable', message: error.code || error.message, service: 'user-auth' }); } } }); }); // WebSocket API routes for managing connections app.use('/api/websocket', websocketRouter); // Apply rate limiting to other API routes app.use('/api', createServiceLimiter(1000)); // Template Manager Service - Direct HTTP forwarding console.log('🔧 Registering /api/templates proxy route...'); app.use('/api/templates', createServiceLimiter(200), // Conditionally require auth: allow public GETs, require token for write ops (req, res, next) => { if (req.method === 'GET') { return next(); } return authMiddleware.verifyToken(req, res, () => authMiddleware.forwardUserContext(req, res, next)); }, (req, res, next) => { const templateServiceUrl = serviceTargets.TEMPLATE_MANAGER_URL; console.log(`🔥 [TEMPLATE PROXY] ${req.method} ${req.originalUrl} → ${templateServiceUrl}${req.originalUrl}`); // Set response timeout to prevent hanging res.setTimeout(15000, () => { console.error('❌ [TEMPLATE PROXY] Response timeout'); if (!res.headersSent) { res.status(504).json({ error: 'Gateway timeout', service: 'template-manager' }); } }); const options = { method: req.method, url: `${templateServiceUrl}${req.originalUrl}`, headers: { 'Content-Type': 'application/json', 'User-Agent': 'API-Gateway/1.0', 'Connection': 'keep-alive', // Forward user context from auth middleware 'X-User-ID': req.user?.id || req.user?.userId, 'X-User-Role': req.user?.role, 'Authorization': req.headers.authorization }, timeout: 8000, validateStatus: () => true, maxRedirects: 0 }; // Always include request body for POST/PUT/PATCH requests if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') { options.data = req.body || {}; console.log(`📦 [TEMPLATE PROXY] Request body:`, JSON.stringify(req.body)); } axios(options) .then(response => { console.log(`✅ [TEMPLATE PROXY] Response: ${response.status} for ${req.method} ${req.originalUrl}`); if (!res.headersSent) { res.status(response.status).json(response.data); } }) .catch(error => { console.error(`❌ [TEMPLATE PROXY ERROR]:`, error.message); if (!res.headersSent) { if (error.response) { res.status(error.response.status).json(error.response.data); } else { res.status(502).json({ error: 'Template service unavailable', message: error.code || error.message, service: 'template-manager' }); } } }); } ); // Requirement Processor Service app.use('/api/requirements', createServiceLimiter(300), authMiddleware.verifyToken, authMiddleware.forwardUserContext, serviceRouter.createServiceProxy(serviceTargets.REQUIREMENT_PROCESSOR_URL, 'requirement-processor') ); // Tech Stack Selector Service app.use('/api/tech-stack', createServiceLimiter(200), authMiddleware.verifyToken, authMiddleware.forwardUserContext, serviceRouter.createServiceProxy(serviceTargets.TECH_STACK_SELECTOR_URL, 'tech-stack-selector') ); // Architecture Designer Service app.use('/api/architecture', createServiceLimiter(150), authMiddleware.verifyToken, authMiddleware.forwardUserContext, serviceRouter.createServiceProxy(serviceTargets.ARCHITECTURE_DESIGNER_URL, 'architecture-designer') ); // Code Generator Service app.use('/api/codegen', createServiceLimiter(100), authMiddleware.verifyToken, authMiddleware.forwardUserContext, serviceRouter.createServiceProxy(serviceTargets.CODE_GENERATOR_URL, 'code-generator') ); // Test Generator Service app.use('/api/tests', createServiceLimiter(150), authMiddleware.verifyToken, authMiddleware.forwardUserContext, serviceRouter.createServiceProxy(serviceTargets.TEST_GENERATOR_URL, 'test-generator') ); // Deployment Manager Service app.use('/api/deploy', createServiceLimiter(100), authMiddleware.verifyToken, authMiddleware.forwardUserContext, serviceRouter.createServiceProxy(serviceTargets.DEPLOYMENT_MANAGER_URL, 'deployment-manager') ); // Dashboard Service app.use('/api/dashboard', createServiceLimiter(300), authMiddleware.verifyToken, authMiddleware.forwardUserContext, serviceRouter.createServiceProxy(serviceTargets.DASHBOARD_URL, 'dashboard') ); // Self-Improving Generator Service app.use('/api/self-improving', createServiceLimiter(50), authMiddleware.verifyToken, authMiddleware.forwardUserContext, serviceRouter.createServiceProxy(serviceTargets.SELF_IMPROVING_GENERATOR_URL, 'self-improving-generator') ); // Features (Template Manager) - expose /api/features via gateway console.log('🔧 Registering /api/features proxy route...'); app.use('/api/features', createServiceLimiter(300), (req, res, next) => { if (req.method === 'GET') { return next(); } return authMiddleware.verifyToken(req, res, () => authMiddleware.forwardUserContext(req, res, next)); }, (req, res, next) => { const templateServiceUrl = serviceTargets.TEMPLATE_MANAGER_URL; console.log(`🔥 [FEATURES PROXY] ${req.method} ${req.originalUrl} → ${templateServiceUrl}${req.originalUrl}`); res.setTimeout(15000, () => { console.error('❌ [FEATURES PROXY] Response timeout'); if (!res.headersSent) { res.status(504).json({ error: 'Gateway timeout', service: 'template-manager' }); } }); const options = { method: req.method, url: `${templateServiceUrl}${req.originalUrl}`, headers: { 'Content-Type': 'application/json', 'User-Agent': 'API-Gateway/1.0', 'Connection': 'keep-alive', 'Authorization': req.headers.authorization, 'X-User-ID': req.user?.id || req.user?.userId, 'X-User-Role': req.user?.role, }, timeout: 8000, validateStatus: () => true, maxRedirects: 0 }; if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') { options.data = req.body || {}; console.log(`📦 [FEATURES PROXY] Request body:`, JSON.stringify(req.body)); } axios(options) .then(response => { console.log(`✅ [FEATURES PROXY] Response: ${response.status} for ${req.method} ${req.originalUrl}`); if (!res.headersSent) { res.status(response.status).json(response.data); } }) .catch(error => { console.error(`❌ [FEATURES PROXY ERROR]:`, error.message); if (!res.headersSent) { if (error.response) { res.status(error.response.status).json(error.response.data); } else { res.status(502).json({ error: 'Template feature service unavailable', message: error.code || error.message, service: 'template-manager' }); } } }); } ); // Gateway management endpoints app.get('/api/gateway/info', authMiddleware.verifyToken, (req, res) => { res.json({ success: true, gateway: { name: 'CodeNuk API Gateway', version: process.env.npm_package_version || '1.0.0', environment: process.env.NODE_ENV || 'development', uptime: process.uptime() }, services: { total_services: Object.keys(serviceTargets).length, operational_services: Object.keys(serviceTargets).length, service_urls: serviceTargets }, features: { websocket_enabled: true, authentication: true, rate_limiting: true, health_monitoring: true, request_logging: true, cors_enabled: true }, websocket: { connected_clients: io.engine.clientsCount, transport_types: ['websocket', 'polling'] } }); }); // Service status endpoint app.get('/api/gateway/services', authMiddleware.verifyToken, serviceHealthMiddleware.getServiceStatus); // Root endpoint app.get('/', (req, res) => { res.json({ success: true, message: 'CodeNuk API Gateway', version: process.env.npm_package_version || '1.0.0', description: 'Central gateway for all CodeNuk microservices', documentation: { health: '/health', gateway_info: '/api/gateway/info', service_status: '/api/gateway/services' }, services: { auth: '/api/auth', templates: '/api/templates', requirements: '/api/requirements', tech_stack: '/api/tech-stack', architecture: '/api/architecture', codegen: '/api/codegen', tests: '/api/tests', deploy: '/api/deploy', dashboard: '/api/dashboard', self_improving: '/api/self-improving' }, websocket: { endpoint: '/socket.io/', authentication_required: true } }); }); // 404 handler app.use('*', (req, res) => { res.status(404).json({ success: false, message: 'Endpoint not found', available_services: { auth: '/api/auth', templates: '/api/templates', requirements: '/api/requirements', tech_stack: '/api/tech-stack', architecture: '/api/architecture', codegen: '/api/codegen', tests: '/api/tests', deploy: '/api/deploy', dashboard: '/api/dashboard', self_improving: '/api/self-improving' }, documentation: '/api/gateway/info' }); }); // Global error handler app.use((error, req, res, next) => { console.error(`[${req.requestId}] Gateway Error:`, error); // Handle proxy errors if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') { return res.status(503).json({ success: false, message: 'Service temporarily unavailable', error: 'The requested service is currently unavailable', request_id: req.requestId }); } // Handle timeout errors if (error.code === 'ETIMEDOUT' || error.message.includes('timeout')) { return res.status(504).json({ success: false, message: 'Service timeout', error: 'The service took too long to respond', request_id: req.requestId }); } // Handle authentication errors if (error.name === 'JsonWebTokenError' || error.name === 'TokenExpiredError') { return res.status(401).json({ success: false, message: 'Authentication failed', error: error.message }); } // Generic error handler res.status(error.status || 500).json({ success: false, message: error.message || 'Internal gateway error', error: process.env.NODE_ENV === 'development' ? { stack: error.stack, name: error.name } : undefined, request_id: req.requestId }); }); // Start server and initialize health monitoring const startServer = async () => { try { console.log('🚀 Starting CodeNuk API Gateway...'); // Initialize service health monitoring await serviceHealthMiddleware.initializeHealthMonitoring(); server.listen(PORT, '0.0.0.0', () => { console.log(`✅ API Gateway running on port ${PORT}`); console.log(`🌍 Environment: ${process.env.NODE_ENV || 'development'}`); console.log(`📋 Health check: http://localhost:${PORT}/health`); console.log(`📖 Gateway info: http://localhost:${PORT}/api/gateway/info`); console.log(`🔗 WebSocket enabled on: ws://localhost:${PORT}`); // Log service configuration console.log('⚙️ Configured Services:'); Object.entries(serviceTargets).forEach(([name, url]) => { console.log(` - ${name}: ${url}`); }); console.log('🔧 Features:'); console.log(` - Rate Limiting: Enabled`); console.log(` - Authentication: JWT with Auth Service`); console.log(` - WebSocket: Real-time notifications`); console.log(` - Health Monitoring: All services`); console.log(` - Request Logging: Enabled`); }); } catch (error) { console.error('❌ Failed to start API Gateway:', error); process.exit(1); } }; // Graceful shutdown process.on('SIGTERM', () => { console.log('📴 SIGTERM received, shutting down gracefully...'); server.close(() => { console.log('✅ API Gateway shut down successfully'); process.exit(0); }); }); process.on('SIGINT', () => { console.log('📴 SIGINT received, shutting down gracefully...'); server.close(() => { console.log('✅ API Gateway shut down successfully'); process.exit(0); }); }); // Start the server startServer(); module.exports = app;