@iota-big3/sdk-gateway
Version:
Universal API Gateway with protocol translation, intelligent routing, rate limiting, health checking, and caching
161 lines • 5.51 kB
JavaScript
"use strict";
/**
* @iota-big3/sdk-gateway
* Clean Routing Engine - Phase 2d Rebuild
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.RoutingEngine = void 0;
const tslib_1 = require("tslib");
const crypto = tslib_1.__importStar(require("crypto"));
const events_1 = require("events");
class RoutingEngine extends events_1.EventEmitter {
constructor() {
super();
this.routes = new Map();
this.endpointHealth = new Map();
this.roundRobinCounters = new Map();
this.isEnabled = true;
this.currentIndex = 0; // Added for adaptive strategy
}
addRoute(route) {
if (!this.isEnabled) {
return;
}
this.routes.set(route.path, route);
// Initialize health status for all endpoints
route.endpoints.forEach(endpoint => {
this.endpointHealth.set(endpoint.id, true);
});
this.emit('route:added', {
route,
timestamp: Date.now()
});
}
removeRoute(path) {
if (!this.isEnabled) {
return;
}
const route = this.routes.get(path);
if (route) {
this.routes.delete(path);
this.roundRobinCounters.delete(path);
// Clean up endpoint health tracking
route.endpoints.forEach(endpoint => {
this.endpointHealth.delete(endpoint.id);
});
this.emit('route:removed', {
route,
timestamp: Date.now()
});
}
}
findRoute(request) {
if (!this.isEnabled) {
return null;
}
// Find exact match first
let route = this.routes.get(request.path);
// If no exact match, try pattern matching
if (!route) {
for (const [path, routeConfig] of Array.from(this.routes)) {
if (this.matchesPattern(request.path, path)) {
route = routeConfig;
break;
}
}
}
if (!route) {
return null;
}
// Get healthy endpoints
const healthyEndpoints = route.endpoints.filter(endpoint => this.endpointHealth.get(endpoint.id) !== false);
if (healthyEndpoints.length === 0) {
return null;
}
// Select endpoint based on load balance strategy
const endpoint = this.selectEndpoint(healthyEndpoints, route.loadBalance?.strategy || 'round-robin');
return {
endpoint,
route,
reason: `Selected by ${route.loadBalance?.strategy || 'round-robin'} strategy`
};
}
matchesPattern(path, pattern) {
// Simple pattern matching (supports * wildcard)
if (pattern.includes('*')) {
const regex = new RegExp('^' + pattern.replace('*', '.*') + '$');
return regex.test(path);
}
return path === pattern;
}
selectEndpoint(endpoints, strategy) {
if (!endpoints || endpoints.length === 0) {
throw new Error('No endpoints available');
}
// Ensure we always have at least one endpoint
const firstEndpoint = endpoints[0];
if (!firstEndpoint) {
throw new Error('No valid endpoints available');
}
// Use default strategy if not specified
const activeStrategy = strategy || 'round-robin';
switch (activeStrategy) {
case 'round-robin': {
// Simple round-robin
const index = this.currentIndex % endpoints.length;
this.currentIndex++;
return endpoints[index] || firstEndpoint;
}
case 'random': {
const randomIndex = Math.floor(crypto.randomInt(0, Number.MAX_SAFE_INTEGER) / Number.MAX_SAFE_INTEGER * endpoints.length);
return endpoints[randomIndex] || firstEndpoint;
}
case 'least-connections':
// For now, just return first endpoint
// In real implementation, would track active connections
return firstEndpoint;
case 'weighted':
// For now, just return first endpoint
// In real implementation, would use endpoint weights
return firstEndpoint;
default:
return firstEndpoint;
}
}
_selectAdaptiveEndpoint(endpoints) {
// For now, simple implementation
// In real implementation, would use metrics to select best endpoint
return endpoints.length > 0 && endpoints[0] ? endpoints[0] : null;
}
updateEndpointHealth(endpointId, isHealthy) {
this.endpointHealth.set(endpointId, isHealthy);
this.emit('endpoint:health:updated', {
endpointId,
isHealthy,
timestamp: Date.now()
});
}
getAllRoutes() {
return Array.from(this.routes.values());
}
/**
* Get all routes (alias for getAllRoutes)
*/
getRoutes() {
return this.getAllRoutes();
}
/**
* Remove route by ID
*/
removeRouteById(routeId) {
for (const [path, route] of this.routes.entries()) {
if (route.id === routeId) {
this.removeRoute(path);
return true;
}
}
return false;
}
}
exports.RoutingEngine = RoutingEngine;
//# sourceMappingURL=routing-engine.js.map