fix(strcuture): refactor responsibilities

This commit is contained in:
2025-05-19 17:28:05 +00:00
parent 8fb67922a5
commit 465148d553
62 changed files with 1414 additions and 2066 deletions

9
ts/routing/index.ts Normal file
View File

@ -0,0 +1,9 @@
/**
* Routing functionality module
*/
// Export types and models from HttpProxy
export * from '../proxies/http-proxy/models/http-types.js';
// Export router functionality
export * from './router/index.js';

View File

@ -0,0 +1,6 @@
/**
* This file re-exports HTTP types from the HttpProxy module
* for backward compatibility. All HTTP types are now consolidated
* in the HttpProxy module.
*/
export * from '../../proxies/http-proxy/models/http-types.js';

View File

@ -0,0 +1,12 @@
/**
* HTTP routing
*/
// Export selectively to avoid ambiguity between duplicate type names
export { ProxyRouter } from './proxy-router.js';
export type { IPathPatternConfig } from './proxy-router.js';
// Re-export the RouterResult and PathPatternConfig from proxy-router.js (legacy names maintained for compatibility)
export type { PathPatternConfig as ProxyPathPatternConfig, RouterResult as ProxyRouterResult } from './proxy-router.js';
export { RouteRouter } from './route-router.js';
export type { PathPatternConfig as RoutePathPatternConfig, RouterResult as RouteRouterResult } from './route-router.js';

View File

@ -0,0 +1,437 @@
import * as plugins from '../../plugins.js';
import type { IReverseProxyConfig } from '../../proxies/http-proxy/models/types.js';
/**
* Optional path pattern configuration that can be added to proxy configs
*/
export interface PathPatternConfig {
pathPattern?: string;
}
// Backward compatibility
export type IPathPatternConfig = PathPatternConfig;
/**
* Interface for router result with additional metadata
*/
export interface RouterResult {
config: IReverseProxyConfig;
pathMatch?: string;
pathParams?: Record<string, string>;
pathRemainder?: string;
}
// Backward compatibility
export type IRouterResult = RouterResult;
/**
* Router for HTTP reverse proxy requests
*
* Supports the following domain matching patterns:
* - Exact matches: "example.com"
* - Wildcard subdomains: "*.example.com" (matches any subdomain of example.com)
* - TLD wildcards: "example.*" (matches example.com, example.org, etc.)
* - Complex wildcards: "*.lossless*" (matches any subdomain of any lossless domain)
* - Default fallback: "*" (matches any unmatched domain)
*
* Also supports path pattern matching for each domain:
* - Exact path: "/api/users"
* - Wildcard paths: "/api/*"
* - Path parameters: "/users/:id/profile"
*/
export class ProxyRouter {
// Store original configs for reference
private reverseProxyConfigs: IReverseProxyConfig[] = [];
// Default config to use when no match is found (optional)
private defaultConfig?: IReverseProxyConfig;
// Store path patterns separately since they're not in the original interface
private pathPatterns: Map<IReverseProxyConfig, string> = new Map();
// Logger interface
private logger: {
error: (message: string, data?: any) => void;
warn: (message: string, data?: any) => void;
info: (message: string, data?: any) => void;
debug: (message: string, data?: any) => void;
};
constructor(
configs?: IReverseProxyConfig[],
logger?: {
error: (message: string, data?: any) => void;
warn: (message: string, data?: any) => void;
info: (message: string, data?: any) => void;
debug: (message: string, data?: any) => void;
}
) {
this.logger = logger || console;
if (configs) {
this.setNewProxyConfigs(configs);
}
}
/**
* Sets a new set of reverse configs to be routed to
* @param reverseCandidatesArg Array of reverse proxy configurations
*/
public setNewProxyConfigs(reverseCandidatesArg: IReverseProxyConfig[]): void {
this.reverseProxyConfigs = [...reverseCandidatesArg];
// Find default config if any (config with "*" as hostname)
this.defaultConfig = this.reverseProxyConfigs.find(config => config.hostName === '*');
this.logger.info(`Router initialized with ${this.reverseProxyConfigs.length} configs (${this.getHostnames().length} unique hosts)`);
}
/**
* Routes a request based on hostname and path
* @param req The incoming HTTP request
* @returns The matching proxy config or undefined if no match found
*/
public routeReq(req: plugins.http.IncomingMessage): IReverseProxyConfig {
const result = this.routeReqWithDetails(req);
return result ? result.config : undefined;
}
/**
* Routes a request with detailed matching information
* @param req The incoming HTTP request
* @returns Detailed routing result including matched config and path information
*/
public routeReqWithDetails(req: plugins.http.IncomingMessage): RouterResult | undefined {
// Extract and validate host header
const originalHost = req.headers.host;
if (!originalHost) {
this.logger.error('No host header found in request');
return this.defaultConfig ? { config: this.defaultConfig } : undefined;
}
// Parse URL for path matching
const parsedUrl = plugins.url.parse(req.url || '/');
const urlPath = parsedUrl.pathname || '/';
// Extract hostname without port
const hostWithoutPort = originalHost.split(':')[0].toLowerCase();
// First try exact hostname match
const exactConfig = this.findConfigForHost(hostWithoutPort, urlPath);
if (exactConfig) {
return exactConfig;
}
// Try various wildcard patterns
if (hostWithoutPort.includes('.')) {
const domainParts = hostWithoutPort.split('.');
// Try wildcard subdomain (*.example.com)
if (domainParts.length > 2) {
const wildcardDomain = `*.${domainParts.slice(1).join('.')}`;
const wildcardConfig = this.findConfigForHost(wildcardDomain, urlPath);
if (wildcardConfig) {
return wildcardConfig;
}
}
// Try TLD wildcard (example.*)
const baseDomain = domainParts.slice(0, -1).join('.');
const tldWildcardDomain = `${baseDomain}.*`;
const tldWildcardConfig = this.findConfigForHost(tldWildcardDomain, urlPath);
if (tldWildcardConfig) {
return tldWildcardConfig;
}
// Try complex wildcard patterns
const wildcardPatterns = this.findWildcardMatches(hostWithoutPort);
for (const pattern of wildcardPatterns) {
const wildcardConfig = this.findConfigForHost(pattern, urlPath);
if (wildcardConfig) {
return wildcardConfig;
}
}
}
// Fall back to default config if available
if (this.defaultConfig) {
this.logger.warn(`No specific config found for host: ${hostWithoutPort}, using default`);
return { config: this.defaultConfig };
}
this.logger.error(`No config found for host: ${hostWithoutPort}`);
return undefined;
}
/**
* Find potential wildcard patterns that could match a given hostname
* Handles complex patterns like "*.lossless*" or other partial matches
* @param hostname The hostname to find wildcard matches for
* @returns Array of potential wildcard patterns that could match
*/
private findWildcardMatches(hostname: string): string[] {
const patterns: string[] = [];
const hostnameParts = hostname.split('.');
// Find all configured hostnames that contain wildcards
const wildcardConfigs = this.reverseProxyConfigs.filter(
config => config.hostName.includes('*')
);
// Extract unique wildcard patterns
const wildcardPatterns = [...new Set(
wildcardConfigs.map(config => config.hostName.toLowerCase())
)];
// For each wildcard pattern, check if it could match the hostname
// using simplified regex pattern matching
for (const pattern of wildcardPatterns) {
// Skip the default wildcard '*'
if (pattern === '*') continue;
// Skip already checked patterns (*.domain.com and domain.*)
if (pattern.startsWith('*.') && pattern.indexOf('*', 2) === -1) continue;
if (pattern.endsWith('.*') && pattern.indexOf('*') === pattern.length - 1) continue;
// Convert wildcard pattern to regex
const regexPattern = pattern
.replace(/\./g, '\\.') // Escape dots
.replace(/\*/g, '.*'); // Convert * to .* for regex
// Create regex object with case insensitive flag
const regex = new RegExp(`^${regexPattern}$`, 'i');
// If hostname matches this complex pattern, add it to the list
if (regex.test(hostname)) {
patterns.push(pattern);
}
}
return patterns;
}
/**
* Find a config for a specific host and path
*/
private findConfigForHost(hostname: string, path: string): RouterResult | undefined {
// Find all configs for this hostname
const configs = this.reverseProxyConfigs.filter(
config => config.hostName.toLowerCase() === hostname.toLowerCase()
);
if (configs.length === 0) {
return undefined;
}
// First try configs with path patterns
const configsWithPaths = configs.filter(config => this.pathPatterns.has(config));
// Sort by path pattern specificity - more specific first
configsWithPaths.sort((a, b) => {
const aPattern = this.pathPatterns.get(a) || '';
const bPattern = this.pathPatterns.get(b) || '';
// Exact patterns come before wildcard patterns
const aHasWildcard = aPattern.includes('*');
const bHasWildcard = bPattern.includes('*');
if (aHasWildcard && !bHasWildcard) return 1;
if (!aHasWildcard && bHasWildcard) return -1;
// Longer patterns are considered more specific
return bPattern.length - aPattern.length;
});
// Check each config with path pattern
for (const config of configsWithPaths) {
const pathPattern = this.pathPatterns.get(config);
if (pathPattern) {
const pathMatch = this.matchPath(path, pathPattern);
if (pathMatch) {
return {
config,
pathMatch: pathMatch.matched,
pathParams: pathMatch.params,
pathRemainder: pathMatch.remainder
};
}
}
}
// If no path pattern matched, use the first config without a path pattern
const configWithoutPath = configs.find(config => !this.pathPatterns.has(config));
if (configWithoutPath) {
return { config: configWithoutPath };
}
return undefined;
}
/**
* Matches a URL path against a pattern
* Supports:
* - Exact matches: /users/profile
* - Wildcards: /api/* (matches any path starting with /api/)
* - Path parameters: /users/:id (captures id as a parameter)
*
* @param path The URL path to match
* @param pattern The pattern to match against
* @returns Match result with params and remainder, or null if no match
*/
private matchPath(path: string, pattern: string): {
matched: string;
params: Record<string, string>;
remainder: string;
} | null {
// Handle exact match
if (path === pattern) {
return {
matched: pattern,
params: {},
remainder: ''
};
}
// Handle wildcard match
if (pattern.endsWith('/*')) {
const prefix = pattern.slice(0, -2);
if (path === prefix || path.startsWith(`${prefix}/`)) {
return {
matched: prefix,
params: {},
remainder: path.slice(prefix.length)
};
}
return null;
}
// Handle path parameters
const patternParts = pattern.split('/').filter(p => p);
const pathParts = path.split('/').filter(p => p);
// Too few path parts to match
if (pathParts.length < patternParts.length) {
return null;
}
const params: Record<string, string> = {};
// Compare each part
for (let i = 0; i < patternParts.length; i++) {
const patternPart = patternParts[i];
const pathPart = pathParts[i];
// Handle parameter
if (patternPart.startsWith(':')) {
const paramName = patternPart.slice(1);
params[paramName] = pathPart;
continue;
}
// Handle wildcard at the end
if (patternPart === '*' && i === patternParts.length - 1) {
break;
}
// Handle exact match for this part
if (patternPart !== pathPart) {
return null;
}
}
// Calculate the remainder - the unmatched path parts
const remainderParts = pathParts.slice(patternParts.length);
const remainder = remainderParts.length ? '/' + remainderParts.join('/') : '';
// Calculate the matched path
const matchedParts = patternParts.map((part, i) => {
return part.startsWith(':') ? pathParts[i] : part;
});
const matched = '/' + matchedParts.join('/');
return {
matched,
params,
remainder
};
}
/**
* Gets all currently active proxy configurations
* @returns Array of all active configurations
*/
public getProxyConfigs(): IReverseProxyConfig[] {
return [...this.reverseProxyConfigs];
}
/**
* Gets all hostnames that this router is configured to handle
* @returns Array of hostnames
*/
public getHostnames(): string[] {
const hostnames = new Set<string>();
for (const config of this.reverseProxyConfigs) {
if (config.hostName !== '*') {
hostnames.add(config.hostName.toLowerCase());
}
}
return Array.from(hostnames);
}
/**
* Adds a single new proxy configuration
* @param config The configuration to add
* @param pathPattern Optional path pattern for route matching
*/
public addProxyConfig(
config: IReverseProxyConfig,
pathPattern?: string
): void {
this.reverseProxyConfigs.push(config);
// Store path pattern if provided
if (pathPattern) {
this.pathPatterns.set(config, pathPattern);
}
}
/**
* Sets a path pattern for an existing config
* @param config The existing configuration
* @param pathPattern The path pattern to set
* @returns Boolean indicating if the config was found and updated
*/
public setPathPattern(
config: IReverseProxyConfig,
pathPattern: string
): boolean {
const exists = this.reverseProxyConfigs.includes(config);
if (exists) {
this.pathPatterns.set(config, pathPattern);
return true;
}
return false;
}
/**
* Removes a proxy configuration by hostname
* @param hostname The hostname to remove
* @returns Boolean indicating whether any configs were removed
*/
public removeProxyConfig(hostname: string): boolean {
const initialCount = this.reverseProxyConfigs.length;
// Find configs to remove
const configsToRemove = this.reverseProxyConfigs.filter(
config => config.hostName === hostname
);
// Remove them from the patterns map
for (const config of configsToRemove) {
this.pathPatterns.delete(config);
}
// Filter them out of the configs array
this.reverseProxyConfigs = this.reverseProxyConfigs.filter(
config => config.hostName !== hostname
);
return this.reverseProxyConfigs.length !== initialCount;
}
}

View File

@ -0,0 +1,482 @@
import * as plugins from '../../plugins.js';
import type { IRouteConfig } from '../../proxies/smart-proxy/models/route-types.js';
import type { ILogger } from '../../proxies/http-proxy/models/types.js';
/**
* Optional path pattern configuration that can be added to proxy configs
*/
export interface PathPatternConfig {
pathPattern?: string;
}
/**
* Interface for router result with additional metadata
*/
export interface RouterResult {
route: IRouteConfig;
pathMatch?: string;
pathParams?: Record<string, string>;
pathRemainder?: string;
}
/**
* Router for HTTP reverse proxy requests based on route configurations
*
* Supports the following domain matching patterns:
* - Exact matches: "example.com"
* - Wildcard subdomains: "*.example.com" (matches any subdomain of example.com)
* - TLD wildcards: "example.*" (matches example.com, example.org, etc.)
* - Complex wildcards: "*.lossless*" (matches any subdomain of any lossless domain)
* - Default fallback: "*" (matches any unmatched domain)
*
* Also supports path pattern matching for each domain:
* - Exact path: "/api/users"
* - Wildcard paths: "/api/*"
* - Path parameters: "/users/:id/profile"
*/
export class RouteRouter {
// Store original routes for reference
private routes: IRouteConfig[] = [];
// Default route to use when no match is found (optional)
private defaultRoute?: IRouteConfig;
// Store path patterns separately since they're not in the original interface
private pathPatterns: Map<IRouteConfig, string> = new Map();
// Logger interface
private logger: ILogger;
constructor(
routes?: IRouteConfig[],
logger?: ILogger
) {
this.logger = logger || {
error: console.error,
warn: console.warn,
info: console.info,
debug: console.debug
};
if (routes) {
this.setRoutes(routes);
}
}
/**
* Sets a new set of routes to be routed to
* @param routes Array of route configurations
*/
public setRoutes(routes: IRouteConfig[]): void {
this.routes = [...routes];
// Sort routes by priority
this.routes.sort((a, b) => {
const priorityA = a.priority ?? 0;
const priorityB = b.priority ?? 0;
return priorityB - priorityA;
});
// Find default route if any (route with "*" as domain)
this.defaultRoute = this.routes.find(route => {
const domains = Array.isArray(route.match.domains)
? route.match.domains
: [route.match.domains];
return domains.includes('*');
});
// Extract path patterns from route match.path
for (const route of this.routes) {
if (route.match.path) {
this.pathPatterns.set(route, route.match.path);
}
}
const uniqueDomains = this.getHostnames();
this.logger.info(`Router initialized with ${this.routes.length} routes (${uniqueDomains.length} unique hosts)`);
}
/**
* Routes a request based on hostname and path
* @param req The incoming HTTP request
* @returns The matching route or undefined if no match found
*/
public routeReq(req: plugins.http.IncomingMessage): IRouteConfig | undefined {
const result = this.routeReqWithDetails(req);
return result ? result.route : undefined;
}
/**
* Routes a request with detailed matching information
* @param req The incoming HTTP request
* @returns Detailed routing result including matched route and path information
*/
public routeReqWithDetails(req: plugins.http.IncomingMessage): RouterResult | undefined {
// Extract and validate host header
const originalHost = req.headers.host;
if (!originalHost) {
this.logger.error('No host header found in request');
return this.defaultRoute ? { route: this.defaultRoute } : undefined;
}
// Parse URL for path matching
const parsedUrl = plugins.url.parse(req.url || '/');
const urlPath = parsedUrl.pathname || '/';
// Extract hostname without port
const hostWithoutPort = originalHost.split(':')[0].toLowerCase();
// First try exact hostname match
const exactRoute = this.findRouteForHost(hostWithoutPort, urlPath);
if (exactRoute) {
return exactRoute;
}
// Try various wildcard patterns
if (hostWithoutPort.includes('.')) {
const domainParts = hostWithoutPort.split('.');
// Try wildcard subdomain (*.example.com)
if (domainParts.length > 2) {
const wildcardDomain = `*.${domainParts.slice(1).join('.')}`;
const wildcardRoute = this.findRouteForHost(wildcardDomain, urlPath);
if (wildcardRoute) {
return wildcardRoute;
}
}
// Try TLD wildcard (example.*)
const baseDomain = domainParts.slice(0, -1).join('.');
const tldWildcardDomain = `${baseDomain}.*`;
const tldWildcardRoute = this.findRouteForHost(tldWildcardDomain, urlPath);
if (tldWildcardRoute) {
return tldWildcardRoute;
}
// Try complex wildcard patterns
const wildcardPatterns = this.findWildcardMatches(hostWithoutPort);
for (const pattern of wildcardPatterns) {
const wildcardRoute = this.findRouteForHost(pattern, urlPath);
if (wildcardRoute) {
return wildcardRoute;
}
}
}
// Fall back to default route if available
if (this.defaultRoute) {
this.logger.warn(`No specific route found for host: ${hostWithoutPort}, using default`);
return { route: this.defaultRoute };
}
this.logger.error(`No route found for host: ${hostWithoutPort}`);
return undefined;
}
/**
* Find potential wildcard patterns that could match a given hostname
* Handles complex patterns like "*.lossless*" or other partial matches
* @param hostname The hostname to find wildcard matches for
* @returns Array of potential wildcard patterns that could match
*/
private findWildcardMatches(hostname: string): string[] {
const patterns: string[] = [];
// Find all routes with wildcard domains
for (const route of this.routes) {
if (!route.match.domains) continue;
const domains = Array.isArray(route.match.domains)
? route.match.domains
: [route.match.domains];
// Filter to only wildcard domains
const wildcardDomains = domains.filter(domain => domain.includes('*'));
// Convert each wildcard domain to a regex pattern and check if it matches
for (const domain of wildcardDomains) {
// Skip the default wildcard '*'
if (domain === '*') continue;
// Skip already checked patterns (*.domain.com and domain.*)
if (domain.startsWith('*.') && domain.indexOf('*', 2) === -1) continue;
if (domain.endsWith('.*') && domain.indexOf('*') === domain.length - 1) continue;
// Convert wildcard pattern to regex
const regexPattern = domain
.replace(/\./g, '\\.') // Escape dots
.replace(/\*/g, '.*'); // Convert * to .* for regex
// Create regex object with case insensitive flag
const regex = new RegExp(`^${regexPattern}$`, 'i');
// If hostname matches this complex pattern, add it to the list
if (regex.test(hostname)) {
patterns.push(domain);
}
}
}
return patterns;
}
/**
* Find a route for a specific host and path
*/
private findRouteForHost(hostname: string, path: string): RouterResult | undefined {
// Find all routes for this hostname
const matchingRoutes = this.routes.filter(route => {
if (!route.match.domains) return false;
const domains = Array.isArray(route.match.domains)
? route.match.domains
: [route.match.domains];
return domains.some(domain => domain.toLowerCase() === hostname.toLowerCase());
});
if (matchingRoutes.length === 0) {
return undefined;
}
// First try routes with path patterns
const routesWithPaths = matchingRoutes.filter(route => this.pathPatterns.has(route));
// Already sorted by priority during setRoutes
// Check each route with path pattern
for (const route of routesWithPaths) {
const pathPattern = this.pathPatterns.get(route);
if (pathPattern) {
const pathMatch = this.matchPath(path, pathPattern);
if (pathMatch) {
return {
route,
pathMatch: pathMatch.matched,
pathParams: pathMatch.params,
pathRemainder: pathMatch.remainder
};
}
}
}
// If no path pattern matched, use the first route without a path pattern
const routeWithoutPath = matchingRoutes.find(route => !this.pathPatterns.has(route));
if (routeWithoutPath) {
return { route: routeWithoutPath };
}
return undefined;
}
/**
* Matches a URL path against a pattern
* Supports:
* - Exact matches: /users/profile
* - Wildcards: /api/* (matches any path starting with /api/)
* - Path parameters: /users/:id (captures id as a parameter)
*
* @param path The URL path to match
* @param pattern The pattern to match against
* @returns Match result with params and remainder, or null if no match
*/
private matchPath(path: string, pattern: string): {
matched: string;
params: Record<string, string>;
remainder: string;
} | null {
// Handle exact match
if (path === pattern) {
return {
matched: pattern,
params: {},
remainder: ''
};
}
// Handle wildcard match
if (pattern.endsWith('/*')) {
const prefix = pattern.slice(0, -2);
if (path === prefix || path.startsWith(`${prefix}/`)) {
return {
matched: prefix,
params: {},
remainder: path.slice(prefix.length)
};
}
return null;
}
// Handle path parameters
const patternParts = pattern.split('/').filter(p => p);
const pathParts = path.split('/').filter(p => p);
// Too few path parts to match
if (pathParts.length < patternParts.length) {
return null;
}
const params: Record<string, string> = {};
// Compare each part
for (let i = 0; i < patternParts.length; i++) {
const patternPart = patternParts[i];
const pathPart = pathParts[i];
// Handle parameter
if (patternPart.startsWith(':')) {
const paramName = patternPart.slice(1);
params[paramName] = pathPart;
continue;
}
// Handle wildcard at the end
if (patternPart === '*' && i === patternParts.length - 1) {
break;
}
// Handle exact match for this part
if (patternPart !== pathPart) {
return null;
}
}
// Calculate the remainder - the unmatched path parts
const remainderParts = pathParts.slice(patternParts.length);
const remainder = remainderParts.length ? '/' + remainderParts.join('/') : '';
// Calculate the matched path
const matchedParts = patternParts.map((part, i) => {
return part.startsWith(':') ? pathParts[i] : part;
});
const matched = '/' + matchedParts.join('/');
return {
matched,
params,
remainder
};
}
/**
* Gets all currently active route configurations
* @returns Array of all active routes
*/
public getRoutes(): IRouteConfig[] {
return [...this.routes];
}
/**
* Gets all hostnames that this router is configured to handle
* @returns Array of hostnames
*/
public getHostnames(): string[] {
const hostnames = new Set<string>();
for (const route of this.routes) {
if (!route.match.domains) continue;
const domains = Array.isArray(route.match.domains)
? route.match.domains
: [route.match.domains];
for (const domain of domains) {
if (domain !== '*') {
hostnames.add(domain.toLowerCase());
}
}
}
return Array.from(hostnames);
}
/**
* Adds a single new route configuration
* @param route The route configuration to add
*/
public addRoute(route: IRouteConfig): void {
this.routes.push(route);
// Store path pattern if present
if (route.match.path) {
this.pathPatterns.set(route, route.match.path);
}
// Re-sort routes by priority
this.routes.sort((a, b) => {
const priorityA = a.priority ?? 0;
const priorityB = b.priority ?? 0;
return priorityB - priorityA;
});
}
/**
* Removes routes by domain pattern
* @param domain The domain pattern to remove routes for
* @returns Boolean indicating whether any routes were removed
*/
public removeRoutesByDomain(domain: string): boolean {
const initialCount = this.routes.length;
// Find routes to remove
const routesToRemove = this.routes.filter(route => {
if (!route.match.domains) return false;
const domains = Array.isArray(route.match.domains)
? route.match.domains
: [route.match.domains];
return domains.includes(domain);
});
// Remove them from the patterns map
for (const route of routesToRemove) {
this.pathPatterns.delete(route);
}
// Filter them out of the routes array
this.routes = this.routes.filter(route => {
if (!route.match.domains) return true;
const domains = Array.isArray(route.match.domains)
? route.match.domains
: [route.match.domains];
return !domains.includes(domain);
});
return this.routes.length !== initialCount;
}
/**
* Legacy method for compatibility with ProxyRouter
* Converts IReverseProxyConfig to IRouteConfig and calls setRoutes
*
* @param configs Array of legacy proxy configurations
*/
public setNewProxyConfigs(configs: any[]): void {
// Convert legacy configs to routes and add them
const routes: IRouteConfig[] = configs.map(config => {
// Create a basic route configuration from the legacy config
return {
match: {
ports: config.destinationPorts[0], // Just use the first port
domains: config.hostName
},
action: {
type: 'forward',
target: {
host: config.destinationIps,
port: config.destinationPorts[0]
},
tls: {
mode: 'terminate',
certificate: {
key: config.privateKey,
cert: config.publicKey
}
}
},
name: `Legacy Config - ${config.hostName}`,
enabled: true
};
});
this.setRoutes(routes);
}
}