309 lines
8.9 KiB
TypeScript
309 lines
8.9 KiB
TypeScript
/**
|
|
* 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<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 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<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 || []
|
|
}
|
|
}
|
|
});
|
|
} |