/** * Synor DEX API Gateway * Unified trading interface for all DEX services * * Port: 17530 (REST), 17531 (WebSocket) * Endpoints: * GET /health - Health check * GET /status - System status * GET /v1/oracle/* - Oracle proxy * GET /v1/perps/* - Perps proxy * GET /v1/aggregator/* - Aggregator proxy * GET /v1/markets - All markets overview */ const http = require('http'); const PORT = process.env.PORT || 17530; const ORACLE_URL = process.env.ORACLE_URL || 'http://localhost:17500'; const PERPS_URL = process.env.PERPS_URL || 'http://localhost:17510'; const AGGREGATOR_URL = process.env.AGGREGATOR_URL || 'http://localhost:17520'; // Service status tracking const serviceStatus = { oracle: { healthy: false, lastCheck: 0 }, perps: { healthy: false, lastCheck: 0 }, aggregator: { healthy: false, lastCheck: 0 }, }; // Fetch helper async function fetchJSON(url, options = {}) { return new Promise((resolve, reject) => { const urlObj = new URL(url); const reqOptions = { hostname: urlObj.hostname, port: urlObj.port, path: urlObj.pathname + urlObj.search, method: options.method || 'GET', headers: options.headers || { 'Content-Type': 'application/json' }, timeout: 5000, }; const req = http.request(reqOptions, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { resolve({ status: res.statusCode, data: JSON.parse(data) }); } catch { resolve({ status: res.statusCode, data }); } }); }); req.on('error', reject); req.on('timeout', () => { req.destroy(); reject(new Error('Timeout')); }); if (options.body) { req.write(JSON.stringify(options.body)); } req.end(); }); } // Health check for services async function checkServiceHealth() { const checks = [ { name: 'oracle', url: `${ORACLE_URL}/health` }, { name: 'perps', url: `${PERPS_URL}/health` }, { name: 'aggregator', url: `${AGGREGATOR_URL}/health` }, ]; for (const check of checks) { try { const result = await fetchJSON(check.url); serviceStatus[check.name] = { healthy: result.status === 200, lastCheck: Date.now(), latency: Date.now() - serviceStatus[check.name].lastCheck, }; } catch { serviceStatus[check.name] = { healthy: false, lastCheck: Date.now() }; } } } // Check health every 30 seconds setInterval(checkServiceHealth, 30000); checkServiceHealth(); // Proxy request to service async function proxyRequest(targetUrl, req, res) { try { let body = ''; for await (const chunk of req) { body += chunk; } const result = await fetchJSON(targetUrl, { method: req.method, body: body ? JSON.parse(body) : undefined, }); res.writeHead(result.status); res.end(JSON.stringify(result.data)); } catch (err) { res.writeHead(502); res.end(JSON.stringify({ error: 'Service unavailable', message: err.message })); } } // HTTP Server const server = http.createServer(async (req, res) => { res.setHeader('Content-Type', 'application/json'); res.setHeader('Access-Control-Allow-Origin', '*'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); if (req.method === 'OPTIONS') { res.writeHead(200); res.end(); return; } const url = new URL(req.url, `http://localhost:${PORT}`); const path = url.pathname; // Health check if (path === '/health') { const allHealthy = Object.values(serviceStatus).every(s => s.healthy); res.writeHead(allHealthy ? 200 : 503); res.end(JSON.stringify({ status: allHealthy ? 'healthy' : 'degraded', timestamp: Date.now(), })); return; } // System status if (path === '/status') { const allHealthy = Object.values(serviceStatus).every(s => s.healthy); res.writeHead(200); res.end(JSON.stringify({ status: allHealthy ? 'operational' : 'degraded', services: serviceStatus, version: '1.0.0', timestamp: Date.now(), })); return; } // API documentation if (path === '/' || path === '/docs') { res.writeHead(200); res.end(JSON.stringify({ name: 'Synor DEX API', version: '1.0.0', endpoints: { '/health': 'Health check', '/status': 'System status with all services', '/v1/oracle/prices': 'Get all price feeds', '/v1/oracle/price/:symbol': 'Get price for symbol', '/v1/oracle/twap/:symbol': 'Get TWAP for symbol', '/v1/perps/markets': 'Get all perpetual markets', '/v1/perps/positions/:address': 'Get user positions', '/v1/perps/funding': 'Get funding rates', '/v1/aggregator/sources': 'Get liquidity sources', '/v1/aggregator/quote': 'Get swap quote', '/v1/aggregator/swap': 'Execute swap', '/v1/markets': 'Combined market overview', }, })); return; } // Proxy to Oracle if (path.startsWith('/v1/oracle/')) { const targetPath = path.replace('/v1/oracle', ''); const targetUrl = `${ORACLE_URL}${targetPath}${url.search}`; await proxyRequest(targetUrl, req, res); return; } // Proxy to Perps if (path.startsWith('/v1/perps/')) { const targetPath = path.replace('/v1/perps', ''); const targetUrl = `${PERPS_URL}${targetPath}${url.search}`; await proxyRequest(targetUrl, req, res); return; } // Proxy to Aggregator if (path.startsWith('/v1/aggregator/')) { const targetPath = path.replace('/v1/aggregator', ''); const targetUrl = `${AGGREGATOR_URL}${targetPath}${url.search}`; await proxyRequest(targetUrl, req, res); return; } // Combined markets overview if (path === '/v1/markets') { try { const [perpsResult, oracleResult, sourcesResult] = await Promise.allSettled([ fetchJSON(`${PERPS_URL}/markets`), fetchJSON(`${ORACLE_URL}/prices`), fetchJSON(`${AGGREGATOR_URL}/sources`), ]); res.writeHead(200); res.end(JSON.stringify({ perpetuals: perpsResult.status === 'fulfilled' ? perpsResult.value.data : null, prices: oracleResult.status === 'fulfilled' ? oracleResult.value.data : null, liquiditySources: sourcesResult.status === 'fulfilled' ? sourcesResult.value.data : null, timestamp: Date.now(), })); } catch (err) { res.writeHead(500); res.end(JSON.stringify({ error: err.message })); } return; } // Default res.writeHead(404); res.end(JSON.stringify({ error: 'Not found', path })); }); server.listen(PORT, '0.0.0.0', () => { console.log(`DEX API Gateway running on port ${PORT}`); console.log(''); console.log('Service URLs:'); console.log(` Oracle: ${ORACLE_URL}`); console.log(` Perps: ${PERPS_URL}`); console.log(` Aggregator: ${AGGREGATOR_URL}`); console.log(''); console.log('Endpoints:'); console.log(' GET /health - Health check'); console.log(' GET /status - System status'); console.log(' GET /docs - API documentation'); console.log(' GET /v1/oracle/* - Oracle proxy'); console.log(' GET /v1/perps/* - Perps proxy'); console.log(' GET /v1/aggregator/* - Aggregator proxy'); console.log(' GET /v1/markets - Combined market overview'); });