diff --git a/ts/proxies/smart-proxy/domain-config-manager.ts b/ts/proxies/smart-proxy/domain-config-manager.ts index 1f01d97..976f60a 100644 --- a/ts/proxies/smart-proxy/domain-config-manager.ts +++ b/ts/proxies/smart-proxy/domain-config-manager.ts @@ -3,6 +3,8 @@ import type { IDomainConfig, ISmartProxyOptions } from './models/interfaces.js'; import type { TForwardingType, IForwardConfig } from '../../forwarding/config/forwarding-types.js'; import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js'; import { ForwardingHandlerFactory } from '../../forwarding/factory/forwarding-factory.js'; +import type { IRouteConfig } from './models/route-types.js'; +import { RouteManager } from './route-manager.js'; /** * Manages domain configurations and target selection @@ -14,13 +16,112 @@ export class DomainConfigManager { // Cache forwarding handlers for each domain config private forwardingHandlers: Map = new Map(); - constructor(private settings: ISmartProxyOptions) {} - + // Store derived domain configs from routes + private derivedDomainConfigs: IDomainConfig[] = []; + + // Reference to RouteManager for route-based configuration + private routeManager?: RouteManager; + + constructor(private settings: ISmartProxyOptions) { + // Initialize with derived domain configs if using route-based configuration + if (settings.routes && !settings.domainConfigs) { + this.generateDomainConfigsFromRoutes(); + } + } + + /** + * Set the route manager reference for route-based queries + */ + public setRouteManager(routeManager: RouteManager): void { + this.routeManager = routeManager; + + // Regenerate domain configs from routes if needed + if (this.settings.routes && (!this.settings.domainConfigs || this.settings.domainConfigs.length === 0)) { + this.generateDomainConfigsFromRoutes(); + } + } + + /** + * Generate domain configs from routes + */ + public generateDomainConfigsFromRoutes(): void { + this.derivedDomainConfigs = []; + + if (!this.settings.routes) return; + + for (const route of this.settings.routes) { + if (route.action.type !== 'forward' || !route.match.domains) continue; + + // Convert route to domain config + const domainConfig = this.routeToDomainConfig(route); + if (domainConfig) { + this.derivedDomainConfigs.push(domainConfig); + } + } + } + + /** + * Convert a route to a domain config + */ + private routeToDomainConfig(route: IRouteConfig): IDomainConfig | null { + if (route.action.type !== 'forward' || !route.action.target) return null; + + // Get domains from route + const domains = Array.isArray(route.match.domains) ? + route.match.domains : + (route.match.domains ? [route.match.domains] : []); + + if (domains.length === 0) return null; + + // Determine forwarding type based on TLS mode + let forwardingType: TForwardingType = 'http-only'; + if (route.action.tls) { + switch (route.action.tls.mode) { + case 'passthrough': + forwardingType = 'https-passthrough'; + break; + case 'terminate': + forwardingType = 'https-terminate-to-http'; + break; + case 'terminate-and-reencrypt': + forwardingType = 'https-terminate-to-https'; + break; + } + } + + // Create domain config + return { + domains, + forwarding: { + type: forwardingType, + target: { + host: route.action.target.host, + port: route.action.target.port + }, + security: route.action.security ? { + allowedIps: route.action.security.allowedIps, + blockedIps: route.action.security.blockedIps, + maxConnections: route.action.security.maxConnections + } : undefined, + https: route.action.tls && route.action.tls.certificate !== 'auto' ? { + customCert: route.action.tls.certificate + } : undefined, + advanced: route.action.advanced + } + }; + } + /** * Updates the domain configurations */ public updateDomainConfigs(newDomainConfigs: IDomainConfig[]): void { - this.settings.domainConfigs = newDomainConfigs; + // If we're using domainConfigs property, update it + if (this.settings.domainConfigs) { + this.settings.domainConfigs = newDomainConfigs; + } else { + // Otherwise update our derived configs + this.derivedDomainConfigs = newDomainConfigs; + } // Reset target indices for removed configs const currentConfigSet = new Set(newDomainConfigs); @@ -60,7 +161,8 @@ export class DomainConfigManager { * Get all domain configurations */ public getDomainConfigs(): IDomainConfig[] { - return this.settings.domainConfigs; + // Use domainConfigs from settings if available, otherwise use derived configs + return this.settings.domainConfigs || this.derivedDomainConfigs; } /** @@ -69,23 +171,64 @@ export class DomainConfigManager { public findDomainConfig(serverName: string): IDomainConfig | undefined { if (!serverName) return undefined; - return this.settings.domainConfigs.find((config) => - config.domains.some((d) => plugins.minimatch(serverName, d)) - ); + // Get domain configs from the appropriate source + const domainConfigs = this.getDomainConfigs(); + + // Check for direct match + for (const config of domainConfigs) { + if (config.domains.some(d => plugins.minimatch(serverName, d))) { + return config; + } + } + + // No match found + return undefined; } /** * Find domain config for a specific port */ public findDomainConfigForPort(port: number): IDomainConfig | undefined { - return this.settings.domainConfigs.find( - (domain) => { - const portRanges = domain.forwarding?.advanced?.portRanges; - return portRanges && - portRanges.length > 0 && - this.isPortInRanges(port, portRanges); + // Get domain configs from the appropriate source + const domainConfigs = this.getDomainConfigs(); + + // Check if any domain config has a matching port range + for (const domain of domainConfigs) { + const portRanges = domain.forwarding?.advanced?.portRanges; + if (portRanges && portRanges.length > 0 && this.isPortInRanges(port, portRanges)) { + return domain; } - ); + } + + // If we're in route-based mode, also check routes for this port + if (this.settings.routes && (!this.settings.domainConfigs || this.settings.domainConfigs.length === 0)) { + const routesForPort = this.settings.routes.filter(route => { + // Check if this port is in the route's ports + if (typeof route.match.ports === 'number') { + return route.match.ports === port; + } else if (Array.isArray(route.match.ports)) { + return route.match.ports.some(p => { + if (typeof p === 'number') { + return p === port; + } else if (p.from && p.to) { + return port >= p.from && port <= p.to; + } + return false; + }); + } + return false; + }); + + // If we found any routes for this port, convert the first one to a domain config + if (routesForPort.length > 0 && routesForPort[0].action.type === 'forward') { + const domainConfig = this.routeToDomainConfig(routesForPort[0]); + if (domainConfig) { + return domainConfig; + } + } + } + + return undefined; } /** diff --git a/ts/proxies/smart-proxy/models/interfaces.ts b/ts/proxies/smart-proxy/models/interfaces.ts index c2ad26b..c709d8e 100644 --- a/ts/proxies/smart-proxy/models/interfaces.ts +++ b/ts/proxies/smart-proxy/models/interfaces.ts @@ -62,14 +62,15 @@ export interface IDomainConfig { } /** - * Helper functions for type checking - now always assume route-based config + * Helper functions for type checking configuration types */ export function isLegacyOptions(options: any): boolean { - return false; // No longer supporting legacy options + return !!(options.domainConfigs && options.domainConfigs.length > 0 && + (!options.routes || options.routes.length === 0)); } export function isRoutedOptions(options: any): boolean { - return true; // Always assume routed options + return !!(options.routes && options.routes.length > 0); } /** diff --git a/ts/proxies/smart-proxy/smart-proxy.ts b/ts/proxies/smart-proxy/smart-proxy.ts index f05f1b7..6568c24 100644 --- a/ts/proxies/smart-proxy/smart-proxy.ts +++ b/ts/proxies/smart-proxy/smart-proxy.ts @@ -113,12 +113,18 @@ export class SmartProxy extends plugins.EventEmitter { this.timeoutManager ); - // Create domain config manager and port range manager (for backward compatibility) - this.domainConfigManager = new DomainConfigManager(this.settings); - this.portRangeManager = new PortRangeManager(this.settings); - - // Create the new route manager + // Create the new route manager first this.routeManager = new RouteManager(this.settings); + + // Create domain config manager and port range manager + this.domainConfigManager = new DomainConfigManager(this.settings); + + // Share the route manager with the domain config manager + if (typeof this.domainConfigManager.setRouteManager === 'function') { + this.domainConfigManager.setRouteManager(this.routeManager); + } + + this.portRangeManager = new PortRangeManager(this.settings); // Create other required components this.tlsManager = new TlsManager(this.settings); @@ -178,10 +184,16 @@ export class SmartProxy extends plugins.EventEmitter { return; } - // If using legacy format, make sure domainConfigs are initialized + // Initialize domain config based on configuration type if (isLegacyOptions(this.settings)) { - // Initialize domain config manager with the processed configs - this.domainConfigManager.updateDomainConfigs(this.settings.domainConfigs); + // Initialize domain config manager with the legacy domain configs + this.domainConfigManager.updateDomainConfigs(this.settings.domainConfigs || []); + } else if (isRoutedOptions(this.settings)) { + // For pure route-based configuration, the domain config is already initialized + // in the constructor, but we might need to regenerate it + if (typeof this.domainConfigManager.generateDomainConfigsFromRoutes === 'function') { + this.domainConfigManager.generateDomainConfigsFromRoutes(); + } } // Initialize Port80Handler if enabled