diff --git a/changelog.md b/changelog.md index 6c0c702..c5a5d5f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,12 @@ # Changelog +## 2025-05-10 - 15.1.0 - feat(smartproxy) +Update documentation and route helper functions; add createPortRange, createSecurityConfig, createStaticFileRoute, and createTestRoute helpers to the readme and tests. Refactor test examples to use the new helper API and remove legacy connection handling files (including the old connection handler and PortRangeManager) to fully embrace the unified route‐based configuration. + +- Added new helper functions (createPortRange, createSecurityConfig, createStaticFileRoute, createTestRoute) in readme and route helpers. +- Refactored tests (test.forwarding.examples.ts, test.forwarding.unit.ts, etc.) to update references to the new API. +- Removed legacy connection handler and PortRangeManager files to simplify code and align with route‐based configuration. + ## 2025-05-10 - 15.0.0 - BREAKING CHANGE(documentation) Update readme documentation to comprehensively describe the new unified route-based configuration system in v14.0.0 diff --git a/readme.md b/readme.md index 6b690d4..e533898 100644 --- a/readme.md +++ b/readme.md @@ -463,6 +463,10 @@ Available helper functions: - `createBlockRoute()` - Create a route to block specific traffic - `createLoadBalancerRoute()` - Create a route for load balancing - `createHttpsServer()` - Create a complete HTTPS server setup with HTTP redirect +- `createPortRange()` - Helper to create port range configurations from various formats +- `createSecurityConfig()` - Helper to create security configuration objects +- `createStaticFileRoute()` - Create a route for serving static files +- `createTestRoute()` - Create a test route for debugging and testing purposes ## What You Can Do with SmartProxy diff --git a/test/test.forwarding.examples.ts b/test/test.forwarding.examples.ts index 439b8b9..3331529 100644 --- a/test/test.forwarding.examples.ts +++ b/test/test.forwarding.examples.ts @@ -1,112 +1,197 @@ -import * as plugins from '../ts/plugins.js'; +import * as path from 'path'; import { tap, expect } from '@push.rocks/tapbundle'; import { SmartProxy } from '../ts/proxies/smart-proxy/index.js'; -import type { TForwardingType } from '../ts/forwarding/config/forwarding-types.js'; -import type { IDomainConfig } from '../ts/forwarding/config/domain-config.js'; import { - httpOnly, - httpsPassthrough, - tlsTerminateToHttp, - tlsTerminateToHttps -} from '../ts/forwarding/config/forwarding-types.js'; + createHttpRoute, + createHttpsRoute, + createPassthroughRoute, + createRedirectRoute, + createHttpToHttpsRedirect, + createBlockRoute, + createLoadBalancerRoute, + createHttpsServer, + createPortRange, + createSecurityConfig, + createStaticFileRoute, + createTestRoute +} from '../ts/proxies/smart-proxy/route-helpers/index.js'; +import type { IRouteConfig } from '../ts/proxies/smart-proxy/models/route-types.js'; -// Test to demonstrate various forwarding configurations -tap.test('Forwarding configuration examples', async (tools) => { +// Test to demonstrate various route configurations using the new helpers +tap.test('Route-based configuration examples', async (tools) => { // Example 1: HTTP-only configuration - const httpOnlyConfig: IDomainConfig = { - domains: ['http.example.com'], - forwarding: httpOnly({ - target: { - host: 'localhost', - port: 3000 - }, - security: { - allowedIps: ['*'] // Allow all - } - }) - }; - console.log(httpOnlyConfig.forwarding, 'HTTP-only configuration created successfully'); - expect(httpOnlyConfig.forwarding.type).toEqual('http-only'); + const httpOnlyRoute = createHttpRoute({ + domains: 'http.example.com', + target: { + host: 'localhost', + port: 3000 + }, + security: { + allowedIps: ['*'] // Allow all + }, + name: 'Basic HTTP Route' + }); - // Example 2: HTTPS Passthrough (SNI) - const httpsPassthroughConfig: IDomainConfig = { - domains: ['pass.example.com'], - forwarding: httpsPassthrough({ - target: { - host: ['10.0.0.1', '10.0.0.2'], // Round-robin target IPs - port: 443 - }, - security: { - allowedIps: ['*'] // Allow all - } - }) - }; - expect(httpsPassthroughConfig.forwarding).toBeTruthy(); - expect(httpsPassthroughConfig.forwarding.type).toEqual('https-passthrough'); - expect(Array.isArray(httpsPassthroughConfig.forwarding.target.host)).toBeTrue(); + console.log('HTTP-only route created successfully:', httpOnlyRoute.name); + expect(httpOnlyRoute.action.type).toEqual('forward'); + expect(httpOnlyRoute.match.domains).toEqual('http.example.com'); + + // Example 2: HTTPS Passthrough (SNI) configuration + const httpsPassthroughRoute = createPassthroughRoute({ + domains: 'pass.example.com', + target: { + host: ['10.0.0.1', '10.0.0.2'], // Round-robin target IPs + port: 443 + }, + security: { + allowedIps: ['*'] // Allow all + }, + name: 'HTTPS Passthrough Route' + }); + + expect(httpsPassthroughRoute).toBeTruthy(); + expect(httpsPassthroughRoute.action.tls?.mode).toEqual('passthrough'); + expect(Array.isArray(httpsPassthroughRoute.action.target?.host)).toBeTrue(); // Example 3: HTTPS Termination to HTTP Backend - const terminateToHttpConfig: IDomainConfig = { - domains: ['secure.example.com'], - forwarding: tlsTerminateToHttp({ - target: { - host: 'localhost', - port: 8080 - }, - http: { - redirectToHttps: true, // Redirect HTTP requests to HTTPS - headers: { - 'X-Forwarded-Proto': 'https' - } - }, - acme: { - enabled: true, - maintenance: true, - production: false // Use staging ACME server for testing - }, - security: { - allowedIps: ['*'] // Allow all - } - }) - }; - expect(terminateToHttpConfig.forwarding).toBeTruthy(); - expect(terminateToHttpConfig.forwarding.type).toEqual('https-terminate-to-http'); - expect(terminateToHttpConfig.forwarding.http?.redirectToHttps).toBeTrue(); + const terminateToHttpRoute = createHttpsRoute({ + domains: 'secure.example.com', + target: { + host: 'localhost', + port: 8080 + }, + tlsMode: 'terminate', + certificate: 'auto', + headers: { + 'X-Forwarded-Proto': 'https' + }, + security: { + allowedIps: ['*'] // Allow all + }, + name: 'HTTPS Termination to HTTP Backend' + }); - // Example 4: HTTPS Termination to HTTPS Backend - const terminateToHttpsConfig: IDomainConfig = { - domains: ['proxy.example.com'], - forwarding: tlsTerminateToHttps({ - target: { - host: 'internal-api.local', - port: 8443 - }, - https: { - forwardSni: true // Forward original SNI info - }, - security: { - allowedIps: ['10.0.0.0/24', '192.168.1.0/24'], - maxConnections: 1000 - }, - advanced: { - timeout: 3600000, // 1 hour in ms - headers: { - 'X-Original-Host': '{sni}' - } - } - }) - }; - expect(terminateToHttpsConfig.forwarding).toBeTruthy(); - expect(terminateToHttpsConfig.forwarding.type).toEqual('https-terminate-to-https'); - expect(terminateToHttpsConfig.forwarding.https?.forwardSni).toBeTrue(); - expect(terminateToHttpsConfig.forwarding.security?.allowedIps?.length).toEqual(2); + // Create the HTTP to HTTPS redirect for this domain + const httpToHttpsRedirect = createHttpToHttpsRedirect({ + domains: 'secure.example.com', + name: 'HTTP to HTTPS Redirect for secure.example.com' + }); - // Skip the SmartProxy integration test for now and just verify our configuration objects work - console.log('All forwarding configurations were created successfully'); + expect(terminateToHttpRoute).toBeTruthy(); + expect(terminateToHttpRoute.action.tls?.mode).toEqual('terminate'); + expect(terminateToHttpRoute.action.advanced?.headers?.['X-Forwarded-Proto']).toEqual('https'); + expect(httpToHttpsRedirect.action.type).toEqual('redirect'); - // This is just to verify that our test passes - expect(true).toBeTrue(); + // Example 4: Load Balancer with HTTPS + const loadBalancerRoute = createLoadBalancerRoute({ + domains: 'proxy.example.com', + targets: ['internal-api-1.local', 'internal-api-2.local'], + targetPort: 8443, + tlsMode: 'terminate-and-reencrypt', + certificate: 'auto', + headers: { + 'X-Original-Host': '{domain}' + }, + security: { + allowedIps: ['10.0.0.0/24', '192.168.1.0/24'], + maxConnections: 1000 + }, + name: 'Load Balanced HTTPS Route' + }); + + expect(loadBalancerRoute).toBeTruthy(); + expect(loadBalancerRoute.action.tls?.mode).toEqual('terminate-and-reencrypt'); + expect(Array.isArray(loadBalancerRoute.action.target?.host)).toBeTrue(); + expect(loadBalancerRoute.action.security?.allowedIps?.length).toEqual(2); + + // Example 5: Block specific IPs + const blockRoute = createBlockRoute({ + ports: [80, 443], + clientIp: ['192.168.5.0/24'], + name: 'Block Suspicious IPs', + priority: 1000 // High priority to ensure it's evaluated first + }); + + expect(blockRoute.action.type).toEqual('block'); + expect(blockRoute.match.clientIp?.length).toEqual(1); + expect(blockRoute.priority).toEqual(1000); + + // Example 6: Complete HTTPS Server with HTTP Redirect + const httpsServerRoutes = createHttpsServer({ + domains: 'complete.example.com', + target: { + host: 'localhost', + port: 8080 + }, + certificate: 'auto', + name: 'Complete HTTPS Server' + }); + + expect(Array.isArray(httpsServerRoutes)).toBeTrue(); + expect(httpsServerRoutes.length).toEqual(2); // HTTPS route and HTTP redirect + expect(httpsServerRoutes[0].action.tls?.mode).toEqual('terminate'); + expect(httpsServerRoutes[1].action.type).toEqual('redirect'); + + // Example 7: Static File Server + const staticFileRoute = createStaticFileRoute({ + domains: 'static.example.com', + targetDirectory: '/var/www/static', + tlsMode: 'terminate', + certificate: 'auto', + headers: { + 'Cache-Control': 'public, max-age=86400' + }, + name: 'Static File Server' + }); + + expect(staticFileRoute.action.advanced?.staticFiles?.directory).toEqual('/var/www/static'); + expect(staticFileRoute.action.advanced?.headers?.['Cache-Control']).toEqual('public, max-age=86400'); + + // Example 8: Test Route for Debugging + const testRoute = createTestRoute({ + ports: 8000, + domains: 'test.example.com', + response: { + status: 200, + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ status: 'ok', message: 'API is working!' }) + } + }); + + expect(testRoute.match.ports).toEqual(8000); + expect(testRoute.action.advanced?.testResponse?.status).toEqual(200); + + // Create a SmartProxy instance with all routes + const allRoutes: IRouteConfig[] = [ + httpOnlyRoute, + httpsPassthroughRoute, + terminateToHttpRoute, + httpToHttpsRedirect, + loadBalancerRoute, + blockRoute, + ...httpsServerRoutes, + staticFileRoute, + testRoute + ]; + + // We're not actually starting the SmartProxy in this test, + // just verifying that the configuration is valid + const smartProxy = new SmartProxy({ + routes: allRoutes, + acme: { + email: 'admin@example.com', + termsOfServiceAgreed: true, + directoryUrl: 'https://acme-staging-v02.api.letsencrypt.org/directory' + } + }); + + console.log(`Smart Proxy configured with ${allRoutes.length} routes`); + + // Verify our example proxy was created correctly + expect(smartProxy).toBeTruthy(); }); export default tap.start(); \ No newline at end of file diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 4f10d8e..33163bf 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@push.rocks/smartproxy', - version: '15.0.0', + version: '15.1.0', description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.' } diff --git a/ts/proxies/smart-proxy/connection-handler.ts b/ts/proxies/smart-proxy/connection-handler.ts.bak similarity index 100% rename from ts/proxies/smart-proxy/connection-handler.ts rename to ts/proxies/smart-proxy/connection-handler.ts.bak diff --git a/ts/proxies/smart-proxy/domain-config-manager.ts b/ts/proxies/smart-proxy/domain-config-manager.ts.bak similarity index 100% rename from ts/proxies/smart-proxy/domain-config-manager.ts rename to ts/proxies/smart-proxy/domain-config-manager.ts.bak diff --git a/ts/proxies/smart-proxy/models/route-types.ts b/ts/proxies/smart-proxy/models/route-types.ts index 21814a8..179f6ed 100644 --- a/ts/proxies/smart-proxy/models/route-types.ts +++ b/ts/proxies/smart-proxy/models/route-types.ts @@ -23,10 +23,10 @@ export type TPortRange = number | number[] | Array<{ from: number; to: number }> export interface IRouteMatch { // Listen on these ports (required) ports: TPortRange; - + // Optional domain patterns to match (default: all domains) domains?: string | string[]; - + // Advanced matching criteria path?: string; // Match specific paths clientIp?: string[]; // Match specific client IPs @@ -61,6 +61,25 @@ export interface IRouteRedirect { status: 301 | 302 | 307 | 308; } +/** + * Authentication options + */ +export interface IRouteAuthentication { + type: 'basic' | 'digest' | 'oauth' | 'jwt'; + credentials?: { + username: string; + password: string; + }[]; + realm?: string; + jwtSecret?: string; + jwtIssuer?: string; + oauthProvider?: string; + oauthClientId?: string; + oauthClientSecret?: string; + oauthRedirectUri?: string; + [key: string]: any; // Allow additional auth-specific options +} + /** * Security options for route actions */ @@ -68,10 +87,28 @@ export interface IRouteSecurity { allowedIps?: string[]; blockedIps?: string[]; maxConnections?: number; - authentication?: { - type: 'basic' | 'digest' | 'oauth'; - // Auth-specific options would go here - }; + authentication?: IRouteAuthentication; +} + +/** + * Static file server configuration + */ +export interface IRouteStaticFiles { + directory: string; + indexFiles?: string[]; + cacheControl?: string; + expires?: number; + followSymlinks?: boolean; + disableDirectoryListing?: boolean; +} + +/** + * Test route response configuration + */ +export interface IRouteTestResponse { + status: number; + headers: Record; + body: string; } /** @@ -81,6 +118,8 @@ export interface IRouteAdvanced { timeout?: number; headers?: Record; keepAlive?: boolean; + staticFiles?: IRouteStaticFiles; + testResponse?: IRouteTestResponse; // Additional advanced options would go here } @@ -90,19 +129,19 @@ export interface IRouteAdvanced { export interface IRouteAction { // Basic routing type: TRouteActionType; - + // Target for forwarding target?: IRouteTarget; - + // TLS handling tls?: IRouteTls; - + // For redirects redirect?: IRouteRedirect; - + // Security options security?: IRouteSecurity; - + // Advanced options advanced?: IRouteAdvanced; } @@ -113,10 +152,10 @@ export interface IRouteAction { export interface IRouteConfig { // What to match match: IRouteMatch; - + // What to do with matched traffic action: IRouteAction; - + // Optional metadata name?: string; // Human-readable name for this route description?: string; // Description of the route's purpose @@ -130,7 +169,7 @@ export interface IRouteConfig { export interface IRoutedSmartProxyOptions { // The unified configuration array (required) routes: IRouteConfig[]; - + // Global/default settings defaults?: { target?: { @@ -141,10 +180,10 @@ export interface IRoutedSmartProxyOptions { tls?: IRouteTls; // ...other defaults }; - + // Other global settings remain (acme, etc.) acme?: IAcmeOptions; - + // Connection timeouts and other global settings initialDataTimeout?: number; socketTimeout?: number; @@ -152,13 +191,13 @@ export interface IRoutedSmartProxyOptions { maxConnectionLifetime?: number; inactivityTimeout?: number; gracefulShutdownTimeout?: number; - + // Socket optimization settings noDelay?: boolean; keepAlive?: boolean; keepAliveInitialDelay?: number; maxPendingDataSize?: number; - + // Enhanced features disableInactivityCheck?: boolean; enableKeepAliveProbes?: boolean; @@ -166,16 +205,16 @@ export interface IRoutedSmartProxyOptions { enableTlsDebugLogging?: boolean; enableRandomizedTimeouts?: boolean; allowSessionTicket?: boolean; - + // Rate limiting and security maxConnectionsPerIP?: number; connectionRateLimitPerMinute?: number; - + // Enhanced keep-alive settings keepAliveTreatment?: 'standard' | 'extended' | 'immortal'; keepAliveInactivityMultiplier?: number; extendedKeepAliveLifetime?: number; - + /** * Optional certificate provider callback. Return 'http01' to use HTTP-01 challenges, * or a static certificate object for immediate provisioning. diff --git a/ts/proxies/smart-proxy/network-proxy-bridge.ts b/ts/proxies/smart-proxy/network-proxy-bridge.ts index 7f6c1da..0951c6d 100644 --- a/ts/proxies/smart-proxy/network-proxy-bridge.ts +++ b/ts/proxies/smart-proxy/network-proxy-bridge.ts @@ -377,16 +377,10 @@ export class NetworkProxyBridge { publicKey: certCert, destinationIps: targetHosts, destinationPorts: [targetPort], - proxyConfig: { - targetIsTls: route.action.tls.mode === 'terminate-and-reencrypt', - allowHTTP1: true, - // Apply any other NetworkProxy-specific settings - ...(route.action.advanced ? { - preserveHost: true, - timeout: route.action.advanced.timeout, - headers: route.action.advanced.headers - } : {}) - } + // Use backendProtocol for TLS re-encryption: + backendProtocol: route.action.tls.mode === 'terminate-and-reencrypt' ? 'http2' : 'http1', + // Add rewriteHostHeader for host header handling: + rewriteHostHeader: route.action.advanced?.headers ? true : false }; configs.push(config); diff --git a/ts/proxies/smart-proxy/port-range-manager.ts b/ts/proxies/smart-proxy/port-range-manager.ts.bak similarity index 100% rename from ts/proxies/smart-proxy/port-range-manager.ts rename to ts/proxies/smart-proxy/port-range-manager.ts.bak diff --git a/ts/proxies/smart-proxy/route-connection-handler.ts b/ts/proxies/smart-proxy/route-connection-handler.ts index 73898c5..cd7c3d8 100644 --- a/ts/proxies/smart-proxy/route-connection-handler.ts +++ b/ts/proxies/smart-proxy/route-connection-handler.ts @@ -741,20 +741,7 @@ export class RouteConnectionHandler { this.connectionManager.incrementTerminationStat('outgoing', 'connection_failed'); } - // If we have a forwarding handler for this domain, let it handle the error - if (domainConfig) { - try { - const forwardingHandler = this.domainConfigManager.getForwardingHandler(domainConfig); - forwardingHandler.emit('connection_error', { - socket, - error: err, - connectionId - }); - } catch (handlerErr) { - // If getting the handler fails, just log and continue with normal cleanup - console.log(`Error getting forwarding handler for error handling: ${handlerErr}`); - } - } + // Route-based configuration doesn't use domain handlers // Clean up the connection this.connectionManager.initiateCleanupOnce(record, `connection_failed_${code}`); @@ -887,8 +874,8 @@ export class RouteConnectionHandler { `${ serverName ? ` (SNI: ${serverName})` - : domainConfig - ? ` (Port-based for domain: ${domainConfig.domains.join(', ')})` + : record.lockedDomain + ? ` (Domain: ${record.lockedDomain})` : '' }` + ` TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${ @@ -901,8 +888,8 @@ export class RouteConnectionHandler { `${ serverName ? ` (SNI: ${serverName})` - : domainConfig - ? ` (Port-based for domain: ${domainConfig.domains.join(', ')})` + : record.lockedDomain + ? ` (Domain: ${record.lockedDomain})` : '' }` ); diff --git a/ts/proxies/smart-proxy/route-helpers.ts b/ts/proxies/smart-proxy/route-helpers.ts index 604578a..6e1e52a 100644 --- a/ts/proxies/smart-proxy/route-helpers.ts +++ b/ts/proxies/smart-proxy/route-helpers.ts @@ -1,12 +1,13 @@ -import type { - IRouteConfig, - IRouteMatch, - IRouteAction, +import type { + IRouteConfig, + IRouteMatch, + IRouteAction, IRouteTarget, IRouteTls, IRouteRedirect, IRouteSecurity, - IRouteAdvanced + IRouteAdvanced, + TPortRange } from './models/route-types.js'; /** @@ -30,7 +31,7 @@ export function createRoute( } /** - * Create a basic HTTP route configuration + * Create a basic HTTP route configuration */ export function createHttpRoute( options: { @@ -206,7 +207,7 @@ export function createHttpToHttpsRedirect( } ): IRouteConfig { const domainArray = Array.isArray(options.domains) ? options.domains : [options.domains]; - + return createRedirectRoute({ ports: 80, domains: options.domains, @@ -270,7 +271,7 @@ export function createLoadBalancerRoute( ): IRouteConfig { const useTls = options.tlsMode !== undefined; const defaultPort = useTls ? 443 : 80; - + return createRoute( { ports: options.ports || defaultPort, @@ -321,7 +322,7 @@ export function createHttpsServer( ): IRouteConfig[] { const routes: IRouteConfig[] = []; const domainArray = Array.isArray(options.domains) ? options.domains : [options.domains]; - + // Add HTTPS route routes.push(createHttpsRoute({ domains: options.domains, @@ -330,7 +331,7 @@ export function createHttpsServer( security: options.security, name: options.name || `HTTPS Server for ${domainArray.join(', ')}` })); - + // Add HTTP to HTTPS redirect if requested if (options.addHttpRedirect !== false) { routes.push(createHttpToHttpsRedirect({ @@ -339,6 +340,158 @@ export function createHttpsServer( priority: 100 })); } - + return routes; +} + +/** + * Create a port range configuration from various input formats + */ +export function createPortRange( + ports: number | number[] | string | Array<{ from: number; to: number }> +): TPortRange { + // If it's a string like "80,443" or "8000-9000", parse it + if (typeof ports === 'string') { + if (ports.includes('-')) { + // Handle range like "8000-9000" + const [start, end] = ports.split('-').map(p => parseInt(p.trim(), 10)); + return [{ from: start, to: end }]; + } else if (ports.includes(',')) { + // Handle comma-separated list like "80,443,8080" + return ports.split(',').map(p => parseInt(p.trim(), 10)); + } else { + // Handle single port as string + return parseInt(ports.trim(), 10); + } + } + + // Otherwise return as is + return ports; +} + +/** + * Create a security configuration object + */ +export function createSecurityConfig( + options: { + allowedIps?: string[]; + blockedIps?: string[]; + maxConnections?: number; + authentication?: { + type: 'basic' | 'digest' | 'oauth'; + // Auth-specific options + [key: string]: any; + }; + } +): IRouteSecurity { + return { + ...(options.allowedIps ? { allowedIps: options.allowedIps } : {}), + ...(options.blockedIps ? { blockedIps: options.blockedIps } : {}), + ...(options.maxConnections ? { maxConnections: options.maxConnections } : {}), + ...(options.authentication ? { authentication: options.authentication } : {}) + }; +} + +/** + * Create a static file server route + */ +export function createStaticFileRoute( + options: { + ports?: number | number[]; // Default: 80 + domains: string | string[]; + path?: string; + targetDirectory: string; + tlsMode?: 'terminate' | 'terminate-and-reencrypt'; + certificate?: 'auto' | { key: string; cert: string }; + headers?: Record; + security?: IRouteSecurity; + name?: string; + description?: string; + priority?: number; + tags?: string[]; + } +): IRouteConfig { + const useTls = options.tlsMode !== undefined; + const defaultPort = useTls ? 443 : 80; + + return createRoute( + { + ports: options.ports || defaultPort, + domains: options.domains, + ...(options.path ? { path: options.path } : {}) + }, + { + type: 'forward', + target: { + host: 'localhost', // Static file serving is typically handled locally + port: 0, // Special value indicating a static file server + preservePort: false + }, + ...(useTls ? { + tls: { + mode: options.tlsMode!, + certificate: options.certificate || 'auto' + } + } : {}), + advanced: { + ...(options.headers ? { headers: options.headers } : {}), + staticFiles: { + directory: options.targetDirectory, + indexFiles: ['index.html', 'index.htm'] + } + }, + ...(options.security ? { security: options.security } : {}) + }, + { + name: options.name || 'Static File Server', + description: options.description || `Serving static files from ${options.targetDirectory}`, + priority: options.priority, + tags: options.tags + } + ); +} + +/** + * Create a test route for debugging purposes + */ +export function createTestRoute( + options: { + ports?: number | number[]; // Default: 8000 + domains?: string | string[]; + path?: string; + response?: { + status?: number; + headers?: Record; + body?: string; + }; + name?: string; + } +): IRouteConfig { + return createRoute( + { + ports: options.ports || 8000, + ...(options.domains ? { domains: options.domains } : {}), + ...(options.path ? { path: options.path } : {}) + }, + { + type: 'forward', + target: { + host: 'test', // Special value indicating a test route + port: 0 + }, + advanced: { + testResponse: { + status: options.response?.status || 200, + headers: options.response?.headers || { 'Content-Type': 'text/plain' }, + body: options.response?.body || 'Test route is working!' + } + } + }, + { + name: options.name || 'Test Route', + description: 'Route for testing and debugging', + priority: 500, + tags: ['test', 'debug'] + } + ); } \ No newline at end of file diff --git a/ts/proxies/smart-proxy/route-helpers/index.ts b/ts/proxies/smart-proxy/route-helpers/index.ts new file mode 100644 index 0000000..a2771d4 --- /dev/null +++ b/ts/proxies/smart-proxy/route-helpers/index.ts @@ -0,0 +1,9 @@ +/** + * Route helpers for SmartProxy + * + * This module provides helper functions for creating various types of route configurations + * to be used with the SmartProxy system. + */ + +// Re-export all functions from the route-helpers.ts file +export * from '../route-helpers.js'; \ No newline at end of file diff --git a/ts/proxies/smart-proxy/route-manager.ts b/ts/proxies/smart-proxy/route-manager.ts index 629e0ee..a216b29 100644 --- a/ts/proxies/smart-proxy/route-manager.ts +++ b/ts/proxies/smart-proxy/route-manager.ts @@ -7,8 +7,7 @@ import type { } from './models/route-types.js'; import type { ISmartProxyOptions, - IRoutedSmartProxyOptions, - IDomainConfig + IRoutedSmartProxyOptions } from './models/interfaces.js'; import { isRoutedOptions, diff --git a/ts/proxies/smart-proxy/smart-proxy.ts b/ts/proxies/smart-proxy/smart-proxy.ts index 96255b2..551f12d 100644 --- a/ts/proxies/smart-proxy/smart-proxy.ts +++ b/ts/proxies/smart-proxy/smart-proxy.ts @@ -6,7 +6,7 @@ import { SecurityManager } from './security-manager.js'; import { TlsManager } from './tls-manager.js'; import { NetworkProxyBridge } from './network-proxy-bridge.js'; import { TimeoutManager } from './timeout-manager.js'; -import { PortRangeManager } from './port-range-manager.js'; +// import { PortRangeManager } from './port-range-manager.js'; import { RouteManager } from './route-manager.js'; import { RouteConnectionHandler } from './route-connection-handler.js'; @@ -22,7 +22,7 @@ import type { ISmartProxyOptions, IRoutedSmartProxyOptions } from './models/interfaces.js'; -import { isRoutedOptions } from './models/interfaces.js'; +import { isRoutedOptions, isLegacyOptions } from './models/interfaces.js'; import type { IRouteConfig } from './models/route-types.js'; /** @@ -49,7 +49,7 @@ export class SmartProxy extends plugins.EventEmitter { private tlsManager: TlsManager; private networkProxyBridge: NetworkProxyBridge; private timeoutManager: TimeoutManager; - private portRangeManager: PortRangeManager; + // private portRangeManager: PortRangeManager; private routeManager: RouteManager; private routeConnectionHandler: RouteConnectionHandler; @@ -133,7 +133,7 @@ export class SmartProxy extends plugins.EventEmitter { autoRenew: true, certificateStore: './certs', skipConfiguredCerts: false, - httpsRedirectPort: this.settings.fromPort || 443, + httpsRedirectPort: 443, renewCheckIntervalHours: 24, domainForwards: [] }; @@ -152,7 +152,7 @@ export class SmartProxy extends plugins.EventEmitter { this.routeManager = new RouteManager(this.settings); // Create port range manager - this.portRangeManager = new PortRangeManager(this.settings); + // this.portRangeManager = new PortRangeManager(this.settings); // Create other required components this.tlsManager = new TlsManager(this.settings); @@ -220,22 +220,22 @@ export class SmartProxy extends plugins.EventEmitter { if (this.port80Handler) { const acme = this.settings.acme!; - // Setup domain forwards based on configuration type + // Setup domain forwards const domainForwards = acme.domainForwards?.map(f => { - if (isLegacyOptions(this.settings)) { - // If using legacy mode, check if domain config exists - const domainConfig = this.settings.domainConfigs.find( - dc => dc.domains.some(d => d === f.domain) - ); + // Check if a matching route exists + const matchingRoute = this.settings.routes.find( + route => Array.isArray(route.match.domains) + ? route.match.domains.some(d => d === f.domain) + : route.match.domains === f.domain + ); - if (domainConfig?.forwarding) { - return { - domain: f.domain, - forwardConfig: f.forwardConfig, - acmeForwardConfig: f.acmeForwardConfig, - sslRedirect: f.sslRedirect || domainConfig.forwarding.http?.redirectToHttps || false + if (matchingRoute) { + return { + domain: f.domain, + forwardConfig: f.forwardConfig, + acmeForwardConfig: f.acmeForwardConfig, + sslRedirect: f.sslRedirect || false }; - } } else { // In route mode, look for matching route const route = this.routeManager.findMatchingRoute({ @@ -332,10 +332,8 @@ export class SmartProxy extends plugins.EventEmitter { const isNetworkProxyPort = this.settings.useNetworkProxy?.includes(port); console.log( `SmartProxy -> OK: Now listening on port ${port}${ - isLegacyOptions(this.settings) && this.settings.sniEnabled && !isNetworkProxyPort ? - ' (SNI passthrough enabled)' : - '' - }${isNetworkProxyPort ? ' (NetworkProxy forwarding enabled)' : ''}` + isNetworkProxyPort ? ' (NetworkProxy forwarding enabled)' : '' + }` ); }); @@ -723,17 +721,7 @@ export class SmartProxy extends plugins.EventEmitter { domains.push(...eligibleDomains); } - // For legacy mode, also get domains from domain configs - if (isLegacyOptions(this.settings)) { - for (const config of this.settings.domainConfigs) { - // Skip domains that can't be used with ACME - const eligibleDomains = config.domains.filter(domain => - !domain.includes('*') && this.isValidDomain(domain) - ); - - domains.push(...eligibleDomains); - } - } + // Legacy mode is no longer supported return domains; }