/** * LandveX Master Gateway * * Unifies all backend services: * - Intelligence Lab (port 3002) * - Operations API (port 3005) * - Communications (port 3010) * - Apollo CRM (port 3001) * - AAMOS Ledger (port 3250) * - AAMOS Incidents (port 3303) */ import http from 'http'; import { GATEWAY_CONFIG, SERVICES } from './config'; import { requireAuth, requireRole, verifyApiKey } from './auth'; import { proxyRequest, checkServiceHealth } from './proxy'; // Service health status const serviceHealth: Record = {}; // Rate limiting (simple in-memory) const rateLimits: Record = {}; function checkRateLimit(identifier: string, maxRequests: number): boolean { const now = Date.now(); const windowStart = Math.floor(now / GATEWAY_CONFIG.rateLimitWindow) * GATEWAY_CONFIG.rateLimitWindow; if (!rateLimits[identifier] || rateLimits[identifier].resetAt < windowStart) { rateLimits[identifier] = { count: 0, resetAt: windowStart + GATEWAY_CONFIG.rateLimitWindow }; } rateLimits[identifier].count++; return rateLimits[identifier].count <= maxRequests; } // CORS headers function setCorsHeaders(res: http.ServerResponse): void { res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-API-Key'); } // Main server const server = http.createServer(async (req, res) => { setCorsHeaders(res); if (req.method === 'OPTIONS') { res.statusCode = 204; res.end(); return; } const url = req.url || '/'; // Health check if (url === '/health') { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify({ status: 'ok', version: '0.1.0-gateway', environment: GATEWAY_CONFIG.env, services: Object.entries(serviceHealth).map(([name, health]) => ({ name, healthy: health.healthy, latency: health.latency, lastCheck: health.lastCheck, error: health.error, })), })); return; } // API Key auth for external API access const apiKey = req.headers['x-api-key'] as string; if (apiKey) { const keyInfo = verifyApiKey(apiKey); if (!keyInfo.valid) { res.statusCode = 401; res.end(JSON.stringify({ error: 'Invalid API key' })); return; } if (!checkRateLimit(apiKey, keyInfo.rateLimit || 100)) { res.statusCode = 429; res.end(JSON.stringify({ error: 'Rate limit exceeded' })); return; } } // Route to services if (url.startsWith('/api/v1/government') || url.startsWith('/api/v1/customers')) { proxyRequest(req, res, SERVICES.operations); return; } else if (url.startsWith('/api/v1/communications')) { proxyRequest(req, res, SERVICES.communications); return; } else if (url.startsWith('/api/v1/missions') || url.startsWith('/api/v1/artifacts')) { proxyRequest(req, res, SERVICES.intelligence); return; } else if (url.startsWith('/api/apollo')) { proxyRequest(req, res, SERVICES.apollo); } else if (url.startsWith('/api/ledger')) { proxyRequest(req, res, SERVICES.ledger); } else if (url.startsWith('/api/incidents')) { proxyRequest(req, res, SERVICES.incidents); } else if (url === '/webhooks/stripe') { // Stripe webhook — handle directly or proxy to billing res.statusCode = 200; res.end(JSON.stringify({ received: true })); } else { res.statusCode = 404; res.end(JSON.stringify({ error: 'Not Found', path: url })); } }); // Health check loop async function updateHealth() { for (const [name, service] of Object.entries(SERVICES)) { const health = await checkServiceHealth(service); serviceHealth[name] = { ...health, lastCheck: Date.now(), }; } } // Initial health check updateHealth(); setInterval(updateHealth, 30000); // Every 30 seconds server.listen(GATEWAY_CONFIG.port, () => { console.log(`🚀 LandveX Gateway running on port ${GATEWAY_CONFIG.port}`); console.log(`📡 Services:`); Object.entries(SERVICES).forEach(([key, service]) => { console.log(` - ${service.name}: http://${service.host}:${service.port}`); }); });