This commit is contained in:
2025-05-29 01:00:20 +00:00
parent 2024ea5a69
commit d8d1bdcd41
14 changed files with 190 additions and 791 deletions

View File

@ -19,7 +19,6 @@ import {
createWebSocketRoute as createWebSocketPatternRoute,
createLoadBalancerRoute as createLoadBalancerPatternRoute,
createApiGatewayRoute,
createStaticFileServerRoute,
addRateLimiting,
addBasicAuth,
addJwtAuth
@ -29,7 +28,6 @@ export {
createWebSocketPatternRoute,
createLoadBalancerPatternRoute,
createApiGatewayRoute,
createStaticFileServerRoute,
addRateLimiting,
addBasicAuth,
addJwtAuth

View File

@ -11,7 +11,6 @@
* - HTTPS passthrough routes (createHttpsPassthroughRoute)
* - Complete HTTPS servers with redirects (createCompleteHttpsServer)
* - Load balancer routes (createLoadBalancerRoute)
* - Static file server routes (createStaticFileRoute)
* - API routes (createApiRoute)
* - WebSocket routes (createWebSocketRoute)
* - Port mapping routes (createPortMappingRoute, createOffsetPortMappingRoute)
@ -119,11 +118,8 @@ export function createHttpToHttpsRedirect(
// Create route action
const action: IRouteAction = {
type: 'redirect',
redirect: {
to: `https://{domain}:${httpsPort}{path}`,
status: 301
}
type: 'socket-handler',
socketHandler: SocketHandlers.httpRedirect(`https://{domain}:${httpsPort}{path}`, 301)
};
// Create the route config
@ -267,60 +263,6 @@ export function createLoadBalancerRoute(
};
}
/**
* Create a static file server route
* @param domains Domain(s) to match
* @param rootDir Root directory path for static files
* @param options Additional route options
* @returns Route configuration object
*/
export function createStaticFileRoute(
domains: string | string[],
rootDir: string,
options: {
indexFiles?: string[];
serveOnHttps?: boolean;
certificate?: 'auto' | { key: string; cert: string };
httpPort?: number | number[];
httpsPort?: number | number[];
name?: string;
[key: string]: any;
} = {}
): IRouteConfig {
// Create route match
const match: IRouteMatch = {
ports: options.serveOnHttps
? (options.httpsPort || 443)
: (options.httpPort || 80),
domains
};
// Create route action
const action: IRouteAction = {
type: 'static',
static: {
root: rootDir,
index: options.indexFiles || ['index.html', 'index.htm']
}
};
// Add TLS configuration if serving on HTTPS
if (options.serveOnHttps) {
action.tls = {
mode: 'terminate',
certificate: options.certificate || 'auto'
};
}
// Create the route config
return {
match,
action,
name: options.name || `Static Files for ${Array.isArray(domains) ? domains.join(', ') : domains}`,
...options
};
}
/**
* Create an API route configuration
* @param domains Domain(s) to match
@ -853,7 +795,7 @@ export const SocketHandlers = {
/**
* Simple echo server handler
*/
echo: (socket: plugins.net.Socket) => {
echo: (socket: plugins.net.Socket, context: IRouteContext) => {
socket.write('ECHO SERVER READY\n');
socket.on('data', data => socket.write(data));
},
@ -861,7 +803,7 @@ export const SocketHandlers = {
/**
* TCP proxy handler
*/
proxy: (targetHost: string, targetPort: number) => (socket: plugins.net.Socket) => {
proxy: (targetHost: string, targetPort: number) => (socket: plugins.net.Socket, context: IRouteContext) => {
const target = plugins.net.connect(targetPort, targetHost);
socket.pipe(target);
target.pipe(socket);
@ -876,7 +818,7 @@ export const SocketHandlers = {
/**
* Line-based protocol handler
*/
lineProtocol: (handler: (line: string, socket: plugins.net.Socket) => void) => (socket: plugins.net.Socket) => {
lineProtocol: (handler: (line: string, socket: plugins.net.Socket) => void) => (socket: plugins.net.Socket, context: IRouteContext) => {
let buffer = '';
socket.on('data', (data) => {
buffer += data.toString();
@ -893,7 +835,7 @@ export const SocketHandlers = {
/**
* Simple HTTP response handler (for testing)
*/
httpResponse: (statusCode: number, body: string) => (socket: plugins.net.Socket) => {
httpResponse: (statusCode: number, body: string) => (socket: plugins.net.Socket, context: IRouteContext) => {
const response = [
`HTTP/1.1 ${statusCode} ${statusCode === 200 ? 'OK' : 'Error'}`,
'Content-Type: text/plain',
@ -905,5 +847,74 @@ export const SocketHandlers = {
socket.write(response);
socket.end();
},
/**
* Block connection immediately
*/
block: (message?: string) => (socket: plugins.net.Socket, context: IRouteContext) => {
const finalMessage = message || `Connection blocked from ${context.clientIp}`;
if (finalMessage) {
socket.write(finalMessage);
}
socket.end();
},
/**
* HTTP block response
*/
httpBlock: (statusCode: number = 403, message?: string) => (socket: plugins.net.Socket, context: IRouteContext) => {
const defaultMessage = `Access forbidden for ${context.domain || context.clientIp}`;
const finalMessage = message || defaultMessage;
const response = [
`HTTP/1.1 ${statusCode} ${finalMessage}`,
'Content-Type: text/plain',
`Content-Length: ${finalMessage.length}`,
'Connection: close',
'',
finalMessage
].join('\r\n');
socket.write(response);
socket.end();
},
/**
* HTTP redirect handler
*/
httpRedirect: (locationTemplate: string, statusCode: number = 301) => (socket: plugins.net.Socket, context: IRouteContext) => {
let buffer = '';
socket.once('data', (data) => {
buffer += data.toString();
const lines = buffer.split('\r\n');
const requestLine = lines[0];
const [method, path] = requestLine.split(' ');
const domain = context.domain || 'localhost';
const port = context.port;
let finalLocation = locationTemplate
.replace('{domain}', domain)
.replace('{port}', String(port))
.replace('{path}', path)
.replace('{clientIp}', context.clientIp);
const message = `Redirecting to ${finalLocation}`;
const response = [
`HTTP/1.1 ${statusCode} ${statusCode === 301 ? 'Moved Permanently' : 'Found'}`,
`Location: ${finalLocation}`,
'Content-Type: text/plain',
`Content-Length: ${message.length}`,
'Connection: close',
'',
message
].join('\r\n');
socket.write(response);
socket.end();
});
}
};

View File

@ -7,6 +7,7 @@
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
@ -112,11 +113,11 @@ export function createHttpToHttpsRedirect(
ports: 80
},
action: {
type: 'redirect',
redirect: {
to: options.preservePath ? 'https://{domain}{path}' : 'https://{domain}',
status: options.redirectCode || 301
}
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}`
};
@ -214,57 +215,6 @@ export function createApiGatewayRoute(
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