BREAKING_CHANGE(core): remove legacy forwarding module in favor of route-based system
- Removed the forwarding namespace export from main index - Removed TForwardingType and all forwarding handlers - Consolidated route helper functions into route-helpers.ts - All functionality is now available through the route-based system - Users must migrate from forwarding.* imports to direct route helper imports
This commit is contained in:
@@ -14,23 +14,12 @@ export * from './route-validators.js';
|
||||
// Export route utilities for route operations
|
||||
export * from './route-utils.js';
|
||||
|
||||
// Export route patterns with renamed exports to avoid conflicts
|
||||
import {
|
||||
createWebSocketRoute as createWebSocketPatternRoute,
|
||||
createLoadBalancerRoute as createLoadBalancerPatternRoute,
|
||||
createApiGatewayRoute,
|
||||
addRateLimiting,
|
||||
addBasicAuth,
|
||||
addJwtAuth
|
||||
} from './route-patterns.js';
|
||||
|
||||
// Export additional functions from route-helpers that weren't already exported
|
||||
export {
|
||||
createWebSocketPatternRoute,
|
||||
createLoadBalancerPatternRoute,
|
||||
createApiGatewayRoute,
|
||||
addRateLimiting,
|
||||
addBasicAuth,
|
||||
addJwtAuth
|
||||
};
|
||||
} from './route-helpers.js';
|
||||
|
||||
// Migration utilities have been removed as they are no longer needed
|
@@ -20,6 +20,7 @@
|
||||
|
||||
import * as plugins from '../../../plugins.js';
|
||||
import type { IRouteConfig, IRouteMatch, IRouteAction, IRouteTarget, TPortRange, IRouteContext } from '../models/route-types.js';
|
||||
import { mergeRouteConfigs } from './route-utils.js';
|
||||
|
||||
/**
|
||||
* Create an HTTP-only route configuration
|
||||
@@ -211,26 +212,62 @@ export function createCompleteHttpsServer(
|
||||
/**
|
||||
* Create a load balancer route (round-robin between multiple backend hosts)
|
||||
* @param domains Domain(s) to match
|
||||
* @param hosts Array of backend hosts to load balance between
|
||||
* @param port Backend port
|
||||
* @param options Additional route options
|
||||
* @param backendsOrHosts Array of backend servers OR array of host strings (legacy)
|
||||
* @param portOrOptions Port number (legacy) OR options object
|
||||
* @param options Additional route options (legacy)
|
||||
* @returns Route configuration object
|
||||
*/
|
||||
export function createLoadBalancerRoute(
|
||||
domains: string | string[],
|
||||
hosts: string[],
|
||||
port: number,
|
||||
options: {
|
||||
backendsOrHosts: Array<{ host: string; port: number }> | string[],
|
||||
portOrOptions?: number | {
|
||||
tls?: {
|
||||
mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt';
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
};
|
||||
useTls?: boolean;
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
algorithm?: 'round-robin' | 'least-connections' | 'ip-hash';
|
||||
healthCheck?: {
|
||||
path: string;
|
||||
interval: number;
|
||||
timeout: number;
|
||||
unhealthyThreshold: number;
|
||||
healthyThreshold: number;
|
||||
};
|
||||
[key: string]: any;
|
||||
},
|
||||
options?: {
|
||||
tls?: {
|
||||
mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt';
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
};
|
||||
[key: string]: any;
|
||||
} = {}
|
||||
}
|
||||
): IRouteConfig {
|
||||
// Handle legacy signature: (domains, hosts[], port, options)
|
||||
let backends: Array<{ host: string; port: number }>;
|
||||
let finalOptions: any;
|
||||
|
||||
if (Array.isArray(backendsOrHosts) && backendsOrHosts.length > 0 && typeof backendsOrHosts[0] === 'string') {
|
||||
// Legacy signature
|
||||
const hosts = backendsOrHosts as string[];
|
||||
const port = portOrOptions as number;
|
||||
backends = hosts.map(host => ({ host, port }));
|
||||
finalOptions = options || {};
|
||||
} else {
|
||||
// New signature
|
||||
backends = backendsOrHosts as Array<{ host: string; port: number }>;
|
||||
finalOptions = (portOrOptions as any) || {};
|
||||
}
|
||||
|
||||
// Extract hosts and ensure all backends use the same port
|
||||
const port = backends[0].port;
|
||||
const hosts = backends.map(backend => backend.host);
|
||||
|
||||
// Create route match
|
||||
const match: IRouteMatch = {
|
||||
ports: options.match?.ports || (options.tls ? 443 : 80),
|
||||
ports: finalOptions.match?.ports || (finalOptions.tls || finalOptions.useTls ? 443 : 80),
|
||||
domains
|
||||
};
|
||||
|
||||
@@ -247,10 +284,18 @@ export function createLoadBalancerRoute(
|
||||
};
|
||||
|
||||
// Add TLS configuration if provided
|
||||
if (options.tls) {
|
||||
if (finalOptions.tls || finalOptions.useTls) {
|
||||
action.tls = {
|
||||
mode: options.tls.mode,
|
||||
certificate: options.tls.certificate || 'auto'
|
||||
mode: finalOptions.tls?.mode || 'terminate',
|
||||
certificate: finalOptions.tls?.certificate || finalOptions.certificate || 'auto'
|
||||
};
|
||||
}
|
||||
|
||||
// Add load balancing options
|
||||
if (finalOptions.algorithm || finalOptions.healthCheck) {
|
||||
action.loadBalancing = {
|
||||
algorithm: finalOptions.algorithm || 'round-robin',
|
||||
healthCheck: finalOptions.healthCheck
|
||||
};
|
||||
}
|
||||
|
||||
@@ -258,8 +303,8 @@ export function createLoadBalancerRoute(
|
||||
return {
|
||||
match,
|
||||
action,
|
||||
name: options.name || `Load Balancer for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
|
||||
...options
|
||||
name: finalOptions.name || `Load Balancer for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
|
||||
...finalOptions
|
||||
};
|
||||
}
|
||||
|
||||
@@ -339,16 +384,26 @@ export function createApiRoute(
|
||||
/**
|
||||
* Create a WebSocket route configuration
|
||||
* @param domains Domain(s) to match
|
||||
* @param wsPath WebSocket path (e.g., "/ws")
|
||||
* @param target Target WebSocket server host and port
|
||||
* @param options Additional route options
|
||||
* @param targetOrPath Target server OR WebSocket path (legacy)
|
||||
* @param targetOrOptions Target server (legacy) OR options
|
||||
* @param options Additional route options (legacy)
|
||||
* @returns Route configuration object
|
||||
*/
|
||||
export function createWebSocketRoute(
|
||||
domains: string | string[],
|
||||
wsPath: string,
|
||||
target: { host: string | string[]; port: number },
|
||||
options: {
|
||||
targetOrPath: { host: string | string[]; port: number } | string,
|
||||
targetOrOptions?: { host: string | string[]; port: number } | {
|
||||
useTls?: boolean;
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
path?: string;
|
||||
httpPort?: number | number[];
|
||||
httpsPort?: number | number[];
|
||||
pingInterval?: number;
|
||||
pingTimeout?: number;
|
||||
name?: string;
|
||||
[key: string]: any;
|
||||
},
|
||||
options?: {
|
||||
useTls?: boolean;
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
httpPort?: number | number[];
|
||||
@@ -357,16 +412,33 @@ export function createWebSocketRoute(
|
||||
pingTimeout?: number;
|
||||
name?: string;
|
||||
[key: string]: any;
|
||||
} = {}
|
||||
}
|
||||
): IRouteConfig {
|
||||
// Handle different signatures
|
||||
let target: { host: string | string[]; port: number };
|
||||
let wsPath: string;
|
||||
let finalOptions: any;
|
||||
|
||||
if (typeof targetOrPath === 'string') {
|
||||
// Legacy signature: (domains, path, target, options)
|
||||
wsPath = targetOrPath;
|
||||
target = targetOrOptions as { host: string | string[]; port: number };
|
||||
finalOptions = options || {};
|
||||
} else {
|
||||
// New signature: (domains, target, options)
|
||||
target = targetOrPath;
|
||||
finalOptions = (targetOrOptions as any) || {};
|
||||
wsPath = finalOptions.path || '/ws';
|
||||
}
|
||||
|
||||
// Normalize WebSocket path
|
||||
const normalizedPath = wsPath.startsWith('/') ? wsPath : `/${wsPath}`;
|
||||
|
||||
// Create route match
|
||||
const match: IRouteMatch = {
|
||||
ports: options.useTls
|
||||
? (options.httpsPort || 443)
|
||||
: (options.httpPort || 80),
|
||||
ports: finalOptions.useTls
|
||||
? (finalOptions.httpsPort || 443)
|
||||
: (finalOptions.httpPort || 80),
|
||||
domains,
|
||||
path: normalizedPath
|
||||
};
|
||||
@@ -377,16 +449,16 @@ export function createWebSocketRoute(
|
||||
targets: [target],
|
||||
websocket: {
|
||||
enabled: true,
|
||||
pingInterval: options.pingInterval || 30000, // 30 seconds
|
||||
pingTimeout: options.pingTimeout || 5000 // 5 seconds
|
||||
pingInterval: finalOptions.pingInterval || 30000, // 30 seconds
|
||||
pingTimeout: finalOptions.pingTimeout || 5000 // 5 seconds
|
||||
}
|
||||
};
|
||||
|
||||
// Add TLS configuration if using HTTPS
|
||||
if (options.useTls) {
|
||||
if (finalOptions.useTls) {
|
||||
action.tls = {
|
||||
mode: 'terminate',
|
||||
certificate: options.certificate || 'auto'
|
||||
certificate: finalOptions.certificate || 'auto'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -394,9 +466,9 @@ export function createWebSocketRoute(
|
||||
return {
|
||||
match,
|
||||
action,
|
||||
name: options.name || `WebSocket Route ${normalizedPath} for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
|
||||
priority: options.priority || 100, // Higher priority for WebSocket routes
|
||||
...options
|
||||
name: finalOptions.name || `WebSocket Route ${normalizedPath} for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
|
||||
priority: finalOptions.priority || 100, // Higher priority for WebSocket routes
|
||||
...finalOptions
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1030,3 +1102,152 @@ export const SocketHandlers = {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an API Gateway route pattern
|
||||
* @param domains Domain(s) to match
|
||||
* @param apiBasePath Base path for API endpoints (e.g., '/api')
|
||||
* @param target Target host and port
|
||||
* @param options Additional route options
|
||||
* @returns API route configuration
|
||||
*/
|
||||
export function createApiGatewayRoute(
|
||||
domains: string | string[],
|
||||
apiBasePath: string,
|
||||
target: { host: string | string[]; port: number },
|
||||
options: {
|
||||
useTls?: boolean;
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
addCorsHeaders?: boolean;
|
||||
[key: string]: any;
|
||||
} = {}
|
||||
): IRouteConfig {
|
||||
// Normalize apiBasePath to ensure it starts with / and doesn't end with /
|
||||
const normalizedPath = apiBasePath.startsWith('/')
|
||||
? apiBasePath
|
||||
: `/${apiBasePath}`;
|
||||
|
||||
// Add wildcard to path to match all API endpoints
|
||||
const apiPath = normalizedPath.endsWith('/')
|
||||
? `${normalizedPath}*`
|
||||
: `${normalizedPath}/*`;
|
||||
|
||||
// Create base route
|
||||
const baseRoute = options.useTls
|
||||
? createHttpsTerminateRoute(domains, target, {
|
||||
certificate: options.certificate || 'auto'
|
||||
})
|
||||
: createHttpRoute(domains, target);
|
||||
|
||||
// Add API-specific configurations
|
||||
const apiRoute: Partial<IRouteConfig> = {
|
||||
match: {
|
||||
...baseRoute.match,
|
||||
path: apiPath
|
||||
},
|
||||
name: options.name || `API Gateway: ${apiPath} -> ${Array.isArray(target.host) ? target.host.join(', ') : target.host}:${target.port}`,
|
||||
priority: options.priority || 100 // Higher priority for specific path matching
|
||||
};
|
||||
|
||||
// Add CORS headers if requested
|
||||
if (options.addCorsHeaders) {
|
||||
apiRoute.headers = {
|
||||
response: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
'Access-Control-Max-Age': '86400'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return mergeRouteConfigs(baseRoute, apiRoute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rate limiting route pattern
|
||||
* @param baseRoute Base route to add rate limiting to
|
||||
* @param rateLimit Rate limiting configuration
|
||||
* @returns Route with rate limiting
|
||||
*/
|
||||
export function addRateLimiting(
|
||||
baseRoute: IRouteConfig,
|
||||
rateLimit: {
|
||||
maxRequests: number;
|
||||
window: number; // Time window in seconds
|
||||
keyBy?: 'ip' | 'path' | 'header';
|
||||
headerName?: string; // Required if keyBy is 'header'
|
||||
errorMessage?: string;
|
||||
}
|
||||
): IRouteConfig {
|
||||
return mergeRouteConfigs(baseRoute, {
|
||||
security: {
|
||||
rateLimit: {
|
||||
enabled: true,
|
||||
maxRequests: rateLimit.maxRequests,
|
||||
window: rateLimit.window,
|
||||
keyBy: rateLimit.keyBy || 'ip',
|
||||
headerName: rateLimit.headerName,
|
||||
errorMessage: rateLimit.errorMessage || 'Rate limit exceeded. Please try again later.'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a basic authentication route pattern
|
||||
* @param baseRoute Base route to add authentication to
|
||||
* @param auth Authentication configuration
|
||||
* @returns Route with basic authentication
|
||||
*/
|
||||
export function addBasicAuth(
|
||||
baseRoute: IRouteConfig,
|
||||
auth: {
|
||||
users: Array<{ username: string; password: string }>;
|
||||
realm?: string;
|
||||
excludePaths?: string[];
|
||||
}
|
||||
): IRouteConfig {
|
||||
return mergeRouteConfigs(baseRoute, {
|
||||
security: {
|
||||
basicAuth: {
|
||||
enabled: true,
|
||||
users: auth.users,
|
||||
realm: auth.realm || 'Restricted Area',
|
||||
excludePaths: auth.excludePaths || []
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JWT authentication route pattern
|
||||
* @param baseRoute Base route to add JWT authentication to
|
||||
* @param jwt JWT authentication configuration
|
||||
* @returns Route with JWT authentication
|
||||
*/
|
||||
export function addJwtAuth(
|
||||
baseRoute: IRouteConfig,
|
||||
jwt: {
|
||||
secret: string;
|
||||
algorithm?: string;
|
||||
issuer?: string;
|
||||
audience?: string;
|
||||
expiresIn?: number; // Time in seconds
|
||||
excludePaths?: string[];
|
||||
}
|
||||
): IRouteConfig {
|
||||
return mergeRouteConfigs(baseRoute, {
|
||||
security: {
|
||||
jwtAuth: {
|
||||
enabled: true,
|
||||
secret: jwt.secret,
|
||||
algorithm: jwt.algorithm || 'HS256',
|
||||
issuer: jwt.issuer,
|
||||
audience: jwt.audience,
|
||||
expiresIn: jwt.expiresIn,
|
||||
excludePaths: jwt.excludePaths || []
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -1,403 +0,0 @@
|
||||
/**
|
||||
* Route Patterns
|
||||
*
|
||||
* This file provides pre-defined route patterns for common use cases.
|
||||
* These patterns can be used as templates for creating route configurations.
|
||||
*/
|
||||
|
||||
import type { IRouteConfig, IRouteMatch, IRouteAction, IRouteTarget } from '../models/route-types.js';
|
||||
import { mergeRouteConfigs } from './route-utils.js';
|
||||
import { SocketHandlers } from './route-helpers.js';
|
||||
|
||||
/**
|
||||
* Create a basic HTTP route configuration
|
||||
*/
|
||||
export function createHttpRoute(
|
||||
domains: string | string[],
|
||||
target: { host: string | string[]; port: number | 'preserve' | ((ctx: any) => number) },
|
||||
options: Partial<IRouteConfig> = {}
|
||||
): IRouteConfig {
|
||||
const route: IRouteConfig = {
|
||||
match: {
|
||||
domains,
|
||||
ports: 80
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{
|
||||
host: target.host,
|
||||
port: target.port
|
||||
}]
|
||||
},
|
||||
name: options.name || `HTTP: ${Array.isArray(domains) ? domains.join(', ') : domains}`
|
||||
};
|
||||
|
||||
return mergeRouteConfigs(route, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTTPS route with TLS termination
|
||||
*/
|
||||
export function createHttpsTerminateRoute(
|
||||
domains: string | string[],
|
||||
target: { host: string | string[]; port: number | 'preserve' | ((ctx: any) => number) },
|
||||
options: Partial<IRouteConfig> & {
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
reencrypt?: boolean;
|
||||
} = {}
|
||||
): IRouteConfig {
|
||||
const route: IRouteConfig = {
|
||||
match: {
|
||||
domains,
|
||||
ports: 443
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{
|
||||
host: target.host,
|
||||
port: target.port
|
||||
}],
|
||||
tls: {
|
||||
mode: options.reencrypt ? 'terminate-and-reencrypt' : 'terminate',
|
||||
certificate: options.certificate || 'auto'
|
||||
}
|
||||
},
|
||||
name: options.name || `HTTPS (terminate): ${Array.isArray(domains) ? domains.join(', ') : domains}`
|
||||
};
|
||||
|
||||
return mergeRouteConfigs(route, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTTPS route with TLS passthrough
|
||||
*/
|
||||
export function createHttpsPassthroughRoute(
|
||||
domains: string | string[],
|
||||
target: { host: string | string[]; port: number | 'preserve' | ((ctx: any) => number) },
|
||||
options: Partial<IRouteConfig> = {}
|
||||
): IRouteConfig {
|
||||
const route: IRouteConfig = {
|
||||
match: {
|
||||
domains,
|
||||
ports: 443
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
targets: [{
|
||||
host: target.host,
|
||||
port: target.port
|
||||
}],
|
||||
tls: {
|
||||
mode: 'passthrough'
|
||||
}
|
||||
},
|
||||
name: options.name || `HTTPS (passthrough): ${Array.isArray(domains) ? domains.join(', ') : domains}`
|
||||
};
|
||||
|
||||
return mergeRouteConfigs(route, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTTP to HTTPS redirect route
|
||||
*/
|
||||
export function createHttpToHttpsRedirect(
|
||||
domains: string | string[],
|
||||
options: Partial<IRouteConfig> & {
|
||||
redirectCode?: 301 | 302 | 307 | 308;
|
||||
preservePath?: boolean;
|
||||
} = {}
|
||||
): IRouteConfig {
|
||||
const route: IRouteConfig = {
|
||||
match: {
|
||||
domains,
|
||||
ports: 80
|
||||
},
|
||||
action: {
|
||||
type: 'socket-handler',
|
||||
socketHandler: SocketHandlers.httpRedirect(
|
||||
options.preservePath ? 'https://{domain}{path}' : 'https://{domain}',
|
||||
options.redirectCode || 301
|
||||
)
|
||||
},
|
||||
name: options.name || `HTTP to HTTPS redirect: ${Array.isArray(domains) ? domains.join(', ') : domains}`
|
||||
};
|
||||
|
||||
return mergeRouteConfigs(route, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a complete HTTPS server with redirect from HTTP
|
||||
*/
|
||||
export function createCompleteHttpsServer(
|
||||
domains: string | string[],
|
||||
target: { host: string | string[]; port: number | 'preserve' | ((ctx: any) => number) },
|
||||
options: Partial<IRouteConfig> & {
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
tlsMode?: 'terminate' | 'passthrough' | 'terminate-and-reencrypt';
|
||||
redirectCode?: 301 | 302 | 307 | 308;
|
||||
} = {}
|
||||
): IRouteConfig[] {
|
||||
// Create the TLS route based on the selected mode
|
||||
const tlsRoute = options.tlsMode === 'passthrough'
|
||||
? createHttpsPassthroughRoute(domains, target, options)
|
||||
: createHttpsTerminateRoute(domains, target, {
|
||||
...options,
|
||||
reencrypt: options.tlsMode === 'terminate-and-reencrypt'
|
||||
});
|
||||
|
||||
// Create the HTTP to HTTPS redirect route
|
||||
const redirectRoute = createHttpToHttpsRedirect(domains, {
|
||||
redirectCode: options.redirectCode,
|
||||
preservePath: true
|
||||
});
|
||||
|
||||
return [tlsRoute, redirectRoute];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an API Gateway route pattern
|
||||
* @param domains Domain(s) to match
|
||||
* @param apiBasePath Base path for API endpoints (e.g., '/api')
|
||||
* @param target Target host and port
|
||||
* @param options Additional route options
|
||||
* @returns API route configuration
|
||||
*/
|
||||
export function createApiGatewayRoute(
|
||||
domains: string | string[],
|
||||
apiBasePath: string,
|
||||
target: { host: string | string[]; port: number },
|
||||
options: {
|
||||
useTls?: boolean;
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
addCorsHeaders?: boolean;
|
||||
[key: string]: any;
|
||||
} = {}
|
||||
): IRouteConfig {
|
||||
// Normalize apiBasePath to ensure it starts with / and doesn't end with /
|
||||
const normalizedPath = apiBasePath.startsWith('/')
|
||||
? apiBasePath
|
||||
: `/${apiBasePath}`;
|
||||
|
||||
// Add wildcard to path to match all API endpoints
|
||||
const apiPath = normalizedPath.endsWith('/')
|
||||
? `${normalizedPath}*`
|
||||
: `${normalizedPath}/*`;
|
||||
|
||||
// Create base route
|
||||
const baseRoute = options.useTls
|
||||
? createHttpsTerminateRoute(domains, target, {
|
||||
certificate: options.certificate || 'auto'
|
||||
})
|
||||
: createHttpRoute(domains, target);
|
||||
|
||||
// Add API-specific configurations
|
||||
const apiRoute: Partial<IRouteConfig> = {
|
||||
match: {
|
||||
...baseRoute.match,
|
||||
path: apiPath
|
||||
},
|
||||
name: options.name || `API Gateway: ${apiPath} -> ${Array.isArray(target.host) ? target.host.join(', ') : target.host}:${target.port}`,
|
||||
priority: options.priority || 100 // Higher priority for specific path matching
|
||||
};
|
||||
|
||||
// Add CORS headers if requested
|
||||
if (options.addCorsHeaders) {
|
||||
apiRoute.headers = {
|
||||
response: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
'Access-Control-Max-Age': '86400'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return mergeRouteConfigs(baseRoute, apiRoute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WebSocket route pattern
|
||||
* @param domains Domain(s) to match
|
||||
* @param target WebSocket server host and port
|
||||
* @param options Additional route options
|
||||
* @returns WebSocket route configuration
|
||||
*/
|
||||
export function createWebSocketRoute(
|
||||
domains: string | string[],
|
||||
target: { host: string | string[]; port: number },
|
||||
options: {
|
||||
useTls?: boolean;
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
path?: string;
|
||||
[key: string]: any;
|
||||
} = {}
|
||||
): IRouteConfig {
|
||||
// Create base route
|
||||
const baseRoute = options.useTls
|
||||
? createHttpsTerminateRoute(domains, target, {
|
||||
certificate: options.certificate || 'auto'
|
||||
})
|
||||
: createHttpRoute(domains, target);
|
||||
|
||||
// Add WebSocket-specific configurations
|
||||
const wsRoute: Partial<IRouteConfig> = {
|
||||
match: {
|
||||
...baseRoute.match,
|
||||
path: options.path || '/ws',
|
||||
headers: {
|
||||
'Upgrade': 'websocket'
|
||||
}
|
||||
},
|
||||
action: {
|
||||
...baseRoute.action,
|
||||
websocket: {
|
||||
enabled: true,
|
||||
pingInterval: options.pingInterval || 30000, // 30 seconds
|
||||
pingTimeout: options.pingTimeout || 5000 // 5 seconds
|
||||
}
|
||||
},
|
||||
name: options.name || `WebSocket: ${Array.isArray(domains) ? domains.join(', ') : domains} -> ${Array.isArray(target.host) ? target.host.join(', ') : target.host}:${target.port}`,
|
||||
priority: options.priority || 100 // Higher priority for WebSocket routes
|
||||
};
|
||||
|
||||
return mergeRouteConfigs(baseRoute, wsRoute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a load balancer route pattern
|
||||
* @param domains Domain(s) to match
|
||||
* @param backends Array of backend servers
|
||||
* @param options Additional route options
|
||||
* @returns Load balancer route configuration
|
||||
*/
|
||||
export function createLoadBalancerRoute(
|
||||
domains: string | string[],
|
||||
backends: Array<{ host: string; port: number }>,
|
||||
options: {
|
||||
useTls?: boolean;
|
||||
certificate?: 'auto' | { key: string; cert: string };
|
||||
algorithm?: 'round-robin' | 'least-connections' | 'ip-hash';
|
||||
healthCheck?: {
|
||||
path: string;
|
||||
interval: number;
|
||||
timeout: number;
|
||||
unhealthyThreshold: number;
|
||||
healthyThreshold: number;
|
||||
};
|
||||
[key: string]: any;
|
||||
} = {}
|
||||
): IRouteConfig {
|
||||
// Extract hosts and ensure all backends use the same port
|
||||
const port = backends[0].port;
|
||||
const hosts = backends.map(backend => backend.host);
|
||||
|
||||
// Create route with multiple hosts for load balancing
|
||||
const baseRoute = options.useTls
|
||||
? createHttpsTerminateRoute(domains, { host: hosts, port }, {
|
||||
certificate: options.certificate || 'auto'
|
||||
})
|
||||
: createHttpRoute(domains, { host: hosts, port });
|
||||
|
||||
// Add load balancing specific configurations
|
||||
const lbRoute: Partial<IRouteConfig> = {
|
||||
action: {
|
||||
...baseRoute.action,
|
||||
loadBalancing: {
|
||||
algorithm: options.algorithm || 'round-robin',
|
||||
healthCheck: options.healthCheck
|
||||
}
|
||||
},
|
||||
name: options.name || `Load Balancer: ${Array.isArray(domains) ? domains.join(', ') : domains}`,
|
||||
priority: options.priority || 50
|
||||
};
|
||||
|
||||
return mergeRouteConfigs(baseRoute, lbRoute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a rate limiting route pattern
|
||||
* @param baseRoute Base route to add rate limiting to
|
||||
* @param rateLimit Rate limiting configuration
|
||||
* @returns Route with rate limiting
|
||||
*/
|
||||
export function addRateLimiting(
|
||||
baseRoute: IRouteConfig,
|
||||
rateLimit: {
|
||||
maxRequests: number;
|
||||
window: number; // Time window in seconds
|
||||
keyBy?: 'ip' | 'path' | 'header';
|
||||
headerName?: string; // Required if keyBy is 'header'
|
||||
errorMessage?: string;
|
||||
}
|
||||
): IRouteConfig {
|
||||
return mergeRouteConfigs(baseRoute, {
|
||||
security: {
|
||||
rateLimit: {
|
||||
enabled: true,
|
||||
maxRequests: rateLimit.maxRequests,
|
||||
window: rateLimit.window,
|
||||
keyBy: rateLimit.keyBy || 'ip',
|
||||
headerName: rateLimit.headerName,
|
||||
errorMessage: rateLimit.errorMessage || 'Rate limit exceeded. Please try again later.'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a basic authentication route pattern
|
||||
* @param baseRoute Base route to add authentication to
|
||||
* @param auth Authentication configuration
|
||||
* @returns Route with basic authentication
|
||||
*/
|
||||
export function addBasicAuth(
|
||||
baseRoute: IRouteConfig,
|
||||
auth: {
|
||||
users: Array<{ username: string; password: string }>;
|
||||
realm?: string;
|
||||
excludePaths?: string[];
|
||||
}
|
||||
): IRouteConfig {
|
||||
return mergeRouteConfigs(baseRoute, {
|
||||
security: {
|
||||
basicAuth: {
|
||||
enabled: true,
|
||||
users: auth.users,
|
||||
realm: auth.realm || 'Restricted Area',
|
||||
excludePaths: auth.excludePaths || []
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JWT authentication route pattern
|
||||
* @param baseRoute Base route to add JWT authentication to
|
||||
* @param jwt JWT authentication configuration
|
||||
* @returns Route with JWT authentication
|
||||
*/
|
||||
export function addJwtAuth(
|
||||
baseRoute: IRouteConfig,
|
||||
jwt: {
|
||||
secret: string;
|
||||
algorithm?: string;
|
||||
issuer?: string;
|
||||
audience?: string;
|
||||
expiresIn?: number; // Time in seconds
|
||||
excludePaths?: string[];
|
||||
}
|
||||
): IRouteConfig {
|
||||
return mergeRouteConfigs(baseRoute, {
|
||||
security: {
|
||||
jwtAuth: {
|
||||
enabled: true,
|
||||
secret: jwt.secret,
|
||||
algorithm: jwt.algorithm || 'HS256',
|
||||
issuer: jwt.issuer,
|
||||
audience: jwt.audience,
|
||||
expiresIn: jwt.expiresIn,
|
||||
excludePaths: jwt.excludePaths || []
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user