/** * 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 } from '../models/route-types.js'; import { createHttpRoute, createHttpsTerminateRoute, createHttpsPassthroughRoute, createCompleteHttpsServer } from './route-helpers.js'; import { mergeRouteConfigs } from './route-utils.js'; /** * 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 = { 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 static file server route pattern * @param domains Domain(s) to match * @param rootDirectory Root directory for static files * @param options Additional route options * @returns Static file server route configuration */ export function createStaticFileServerRoute( domains: string | string[], rootDirectory: string, options: { useTls?: boolean; certificate?: 'auto' | { key: string; cert: string }; indexFiles?: string[]; cacheControl?: string; path?: string; [key: string]: any; } = {} ): IRouteConfig { // Create base route with static action const baseRoute: IRouteConfig = { match: { domains, ports: options.useTls ? 443 : 80, path: options.path || '/' }, action: { type: 'static', static: { root: rootDirectory, index: options.indexFiles || ['index.html', 'index.htm'], headers: { 'Cache-Control': options.cacheControl || 'public, max-age=3600' } } }, name: options.name || `Static Server: ${Array.isArray(domains) ? domains.join(', ') : domains}`, priority: options.priority || 50 }; // Add TLS configuration if requested if (options.useTls) { baseRoute.action.tls = { mode: 'terminate', certificate: options.certificate || 'auto' }; } return baseRoute; } /** * 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 = { 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 = { 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 || [] } } }); }