codenuk_backend_mine/services/api-gateway/src/server.js

855 lines
29 KiB
JavaScript

require('dotenv').config();
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');
// Import middleware
const corsMiddleware = require('./middleware/cors');
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();
// Apply CORS middleware before other middleware
app.use(corsMiddleware);
const server = http.createServer(app);
const PORT = process.env.PORT || 8000;
// Initialize Socket.IO with CORS
const io = socketIo(server, {
cors: {
origin: [
'http://localhost:3001', // Frontend (CodeNuk)
'http://localhost:3000', // Alternative frontend port
'http://localhost:8008', // Dashboard service
'http://localhost:8000', // API Gateway
process.env.FRONTEND_URL
].filter(Boolean),
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',
GIT_INTEGRATION_URL: process.env.GIT_INTEGRATION_URL || 'http://localhost:8012',
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',
AI_MOCKUP_URL: process.env.AI_MOCKUP_URL || 'http://localhost:8021',
};
// ========================================
// 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 is already configured via corsMiddleware above
// 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('/api/github', express.json({ limit: '10mb' }));
app.use('/api/mockup', 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 (disabled by default via env)
const isRateLimitDisabled = (process.env.GATEWAY_DISABLE_RATE_LIMIT || process.env.DISABLE_RATE_LIMIT || 'true').toLowerCase() === 'true';
const createServiceLimiter = (maxRequests = 1000) => {
if (isRateLimitDisabled) {
return (req, res, next) => next();
}
return 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',
git_integration: process.env.GIT_INTEGRATION_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',
ai_mockup: process.env.AI_MOCKUP_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',
// Forward Authorization header so protected auth-admin routes work
'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(`📦 [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) => {
// Allow unauthenticated read operations
if (req.method === 'GET') {
return next();
}
// Allow unauthenticated POST to create a template at the root endpoint
// Mounted path is /api/templates, so req.path === '/' for the root
if (req.method === 'POST' && (req.path === '/' || req.originalUrl === '/api/templates')) {
return next();
}
// For other write operations, require authentication and forward user context
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'
});
}
}
});
}
);
// Admin endpoints (Template Manager) - expose /api/admin via gateway
console.log('🔧 Registering /api/admin proxy route...');
app.use('/api/admin',
createServiceLimiter(300),
// Public proxy from gateway perspective; downstream service enforces JWT admin check
(req, res, next) => {
console.log(`🟠 [ADMIN PROXY] ${req.method} ${req.originalUrl}`);
return next();
},
(req, res, next) => {
const adminServiceUrl = serviceTargets.TEMPLATE_MANAGER_URL;
const targetUrl = `${adminServiceUrl}${req.originalUrl}`;
console.log(`🔥 [ADMIN PROXY] ${req.method} ${req.originalUrl}${targetUrl}`);
res.setTimeout(15000, () => {
console.error('❌ [ADMIN PROXY] Response timeout');
if (!res.headersSent) {
res.status(504).json({ error: 'Gateway timeout', service: 'template-manager(admin)' });
}
});
const options = {
method: req.method,
url: targetUrl,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'API-Gateway/1.0',
'Connection': 'keep-alive',
// Forward Authorization header for admin JWT check
'Authorization': req.headers.authorization
},
timeout: 8000,
validateStatus: () => true,
maxRedirects: 0
};
if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') {
options.data = req.body || {};
console.log(`📦 [ADMIN PROXY] Request body:`, JSON.stringify(req.body));
}
axios(options)
.then(response => {
console.log(`✅ [ADMIN PROXY] Response: ${response.status} for ${req.method} ${req.originalUrl}`);
if (!res.headersSent) {
res.status(response.status).json(response.data);
}
})
.catch(error => {
console.error(`❌ [ADMIN 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: 'Admin endpoints unavailable',
message: error.code || error.message,
service: 'template-manager(admin)'
});
}
}
});
}
);
// 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),
// Public proxy: features endpoints do not require auth
(req, res, next) => {
console.log(`🟢 [FEATURES PROXY] Public access → ${req.method} ${req.originalUrl}`);
return 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'
});
}
}
});
}
);
// Git Integration Service - Direct HTTP forwarding
console.log('🔧 Registering /api/github proxy route...');
app.use('/api/github',
createServiceLimiter(200),
// Conditionally require auth: allow public GETs, require token for write ops
(req, res, next) => {
// Allow unauthenticated access for read-only requests and specific public endpoints
if (req.method === 'GET') {
return next();
}
// Allowlist certain POST endpoints that must be public to initiate flows
const url = req.originalUrl || '';
const isPublicGithubEndpoint = (
url.startsWith('/api/github/test-access') ||
url.startsWith('/api/github/auth/github') ||
url.startsWith('/api/github/auth/github/callback') ||
url.startsWith('/api/github/auth/github/status')
);
if (isPublicGithubEndpoint) {
return next();
}
return authMiddleware.verifyToken(req, res, () => authMiddleware.forwardUserContext(req, res, next));
},
(req, res, next) => {
const gitServiceUrl = serviceTargets.GIT_INTEGRATION_URL;
console.log(`🔥 [GIT PROXY] ${req.method} ${req.originalUrl}${gitServiceUrl}${req.originalUrl}`);
// Set response timeout to prevent hanging
res.setTimeout(15000, () => {
console.error('❌ [GIT PROXY] Response timeout');
if (!res.headersSent) {
res.status(504).json({ error: 'Gateway timeout', service: 'git-integration' });
}
});
const options = {
method: req.method,
url: `${gitServiceUrl}${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(`📦 [GIT PROXY] Request body:`, JSON.stringify(req.body));
}
axios(options)
.then(response => {
console.log(`✅ [GIT PROXY] Response: ${response.status} for ${req.method} ${req.originalUrl}`);
if (!res.headersSent) {
res.status(response.status).json(response.data);
}
})
.catch(error => {
console.error(`❌ [GIT 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: 'Git integration service unavailable',
message: error.code || error.message,
service: 'git-integration'
});
}
}
});
}
);
// AI Mockup Service - Direct HTTP forwarding
console.log('🔧 Registering /api/mockup proxy route...');
app.use('/api/mockup',
createServiceLimiter(200),
// Public proxy: AI mockup endpoints do not require auth for basic generation
(req, res, next) => {
console.log(`🎨 [AI MOCKUP PROXY] ${req.method} ${req.originalUrl}`);
return next();
},
(req, res, next) => {
const aiMockupServiceUrl = serviceTargets.AI_MOCKUP_URL;
// Strip the /api/mockup prefix so /api/mockup/health -> /health at target
const rewrittenPath = (req.originalUrl || '').replace(/^\/api\/mockup/, '');
const targetUrl = `${aiMockupServiceUrl}${rewrittenPath}`;
console.log(`🔥 [AI MOCKUP PROXY] ${req.method} ${req.originalUrl}${targetUrl}`);
res.setTimeout(30000, () => {
console.error('❌ [AI MOCKUP PROXY] Response timeout');
if (!res.headersSent) {
res.status(504).json({ error: 'Gateway timeout', service: 'ai-mockup' });
}
});
const options = {
method: req.method,
url: targetUrl,
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: 25000,
validateStatus: () => true,
maxRedirects: 0,
responseType: 'text'
};
if (req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH') {
options.data = req.body || {};
console.log(`📦 [AI MOCKUP PROXY] Request body:`, JSON.stringify(req.body));
}
axios(options)
.then(response => {
console.log(`✅ [AI MOCKUP PROXY] Response: ${response.status} for ${req.method} ${req.originalUrl}`);
if (res.headersSent) return;
const contentType = response.headers['content-type'] || '';
// Forward key headers
if (contentType) res.setHeader('Content-Type', contentType);
res.setHeader('X-Gateway-Request-ID', req.requestId);
res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');
// If response is SVG or XML or plain text, send as-is; else JSON
if (contentType.includes('image/svg') || contentType.includes('xml') || contentType.includes('text/plain') || typeof response.data === 'string') {
res.status(response.status).send(response.data);
} else {
res.status(response.status).json(response.data);
}
})
.catch(error => {
console.error(`❌ [AI MOCKUP PROXY ERROR]:`, error.message);
if (!res.headersSent) {
if (error.response) {
const ct = error.response.headers?.['content-type'] || '';
if (ct.includes('image/svg') || ct.includes('xml') || typeof error.response.data === 'string') {
res.status(error.response.status).send(error.response.data);
} else {
res.status(error.response.status).json(error.response.data);
}
} else {
res.status(502).json({
error: 'AI Mockup service unavailable',
message: error.code || error.message,
service: 'ai-mockup'
});
}
}
});
}
);
// 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',
github: '/api/github',
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',
mockup: '/api/mockup'
},
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',
github: '/api/github',
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',
mockup: '/api/mockup'
},
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;