BREAKING CHANGE(documentation): Update readme documentation to comprehensively describe the new unified route-based configuration system in v14.0.0

This commit is contained in:
Philipp Kunz 2025-05-10 00:06:53 +00:00
parent 28022ebe87
commit bb66b98f1d
4 changed files with 264 additions and 85 deletions

View File

@ -1,5 +1,14 @@
# Changelog
## 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
- Added detailed description of IRouteConfig, IRouteMatch, and IRouteAction interfaces
- Improved explanation of port, domain, path, client IP, and TLS version matching features
- Included examples of helper functions (createHttpRoute, createHttpsRoute, etc.) with usage of template variables
- Enhanced migration guide from legacy configurations to the new match/action pattern
- Updated examples and tests to reflect the new documentation structure
## 2025-05-09 - 13.1.3 - fix(documentation)
Update readme.md to provide a unified and comprehensive overview of SmartProxy, with reorganized sections, enhanced diagrams, and detailed usage examples for various proxy scenarios.

252
readme.md
View File

@ -202,60 +202,187 @@ await proxy.stop();
## Route-Based Configuration System
The new route-based configuration in v14.0.0 follows a match/action pattern, making it more powerful and flexible:
SmartProxy v14.0.0 introduces a new unified route configuration system based on the `IRouteConfig` interface. This system follows a match/action pattern that makes routing more powerful, flexible, and declarative.
### IRouteConfig Interface
The `IRouteConfig` interface is the core building block of SmartProxy's configuration system. Each route definition consists of match criteria and an action to perform on matched traffic:
```typescript
// Basic structure of a route configuration
{
// What traffic to match
match: {
ports: 443, // Required: port(s) to listen on
domains: 'example.com', // Optional: domain(s) to match
path: '/api', // Optional: URL path pattern
clientIp: ['10.0.0.*'], // Optional: client IP patterns to match
tlsVersion: ['TLSv1.2', 'TLSv1.3'] // Optional: TLS versions to match
},
interface IRouteConfig {
// What traffic to match (required)
match: IRouteMatch;
// What to do with matched traffic
action: {
type: 'forward', // 'forward', 'redirect', or 'block'
// What to do with matched traffic (required)
action: IRouteAction;
// Metadata (all optional)
name?: string; // Human-readable name for this route
description?: string; // Description of the route's purpose
priority?: number; // Controls matching order (higher = matched first)
tags?: string[]; // Arbitrary tags for categorization
}
```
#### Match Criteria (IRouteMatch)
The `match` property defines criteria for identifying which incoming traffic should be handled by this route:
```typescript
interface IRouteMatch {
// Listen on these ports (required)
ports: TPortRange; // number | number[] | Array<{ from: number; to: number }>
// Optional domain patterns to match (default: all domains)
domains?: string | string[]; // Supports wildcards like '*.example.com'
// Advanced matching criteria (all optional)
path?: string; // Match specific URL paths, supports glob patterns
clientIp?: string[]; // Match specific client IPs, supports glob patterns
tlsVersion?: string[]; // Match specific TLS versions e.g. ['TLSv1.2', 'TLSv1.3']
}
```
**Port Specification:**
- **Single port:** `ports: 80`
- **Multiple ports:** `ports: [80, 443]`
- **Port ranges:** `ports: [{ from: 8000, to: 8100 }]`
- **Mixed format:** `ports: [80, 443, { from: 8000, to: 8100 }]`
**Domain Matching:**
- **Single domain:** `domains: 'example.com'`
- **Multiple domains:** `domains: ['example.com', 'api.example.com']`
- **Wildcard domains:** `domains: '*.example.com'` (matches any subdomain)
- **Root domain + subdomains:** `domains: ['example.com', '*.example.com']`
**Path Matching:**
- **Exact path:** `path: '/api'` (matches only /api exactly)
- **Prefix match:** `path: '/api/*'` (matches /api and any paths under it)
- **Multiple patterns:** Use multiple routes with different priorities
**Client IP Matching:**
- **Exact IP:** `clientIp: ['192.168.1.1']`
- **Subnet wildcards:** `clientIp: ['10.0.0.*', '192.168.1.*']`
- **CIDR notation:** `clientIp: ['10.0.0.0/24']`
**TLS Version Matching:**
- `tlsVersion: ['TLSv1.2', 'TLSv1.3']` (only match these TLS versions)
#### Action Configuration (IRouteAction)
The `action` property defines what to do with traffic that matches the criteria:
```typescript
interface IRouteAction {
// Action type (required)
type: 'forward' | 'redirect' | 'block';
// For 'forward' actions
target: {
host: 'localhost', // Target host(s) for forwarding
port: 8080 // Target port
},
target?: IRouteTarget;
// TLS handling (for 'forward' actions)
tls: {
mode: 'terminate', // 'passthrough', 'terminate', 'terminate-and-reencrypt'
certificate: 'auto' // 'auto' for Let's Encrypt or {key, cert}
},
// TLS handling for 'forward' actions
tls?: IRouteTls;
// For 'redirect' actions
redirect: {
to: 'https://{domain}{path}', // URL pattern with variables
status: 301 // HTTP status code
},
redirect?: IRouteRedirect;
// Security controls for any action
security: {
allowedIps: ['10.0.0.*'], // IP allowlist
blockedIps: ['1.2.3.4'], // IP blocklist
maxConnections: 100 // Connection limits
},
// Security options for any action
security?: IRouteSecurity;
// Advanced options
advanced: {
timeout: 30000, // Connection timeout
headers: { // Custom headers
'X-Forwarded-For': '{clientIp}'
},
keepAlive: true // Connection pooling
advanced?: IRouteAdvanced;
}
},
```
// Metadata (optional)
**Forward Action:**
When `type: 'forward'`, the traffic is forwarded to the specified target:
```typescript
interface IRouteTarget {
host: string | string[]; // Target host(s) - string array enables round-robin
port: number; // Target port
preservePort?: boolean; // Use incoming port as target port (default: false)
}
```
**TLS Configuration:**
When forwarding with TLS, you can configure how TLS is handled:
```typescript
interface IRouteTls {
mode: 'passthrough' | 'terminate' | 'terminate-and-reencrypt';
certificate?: 'auto' | { // 'auto' = use ACME (Let's Encrypt)
key: string; // TLS private key content
cert: string; // TLS certificate content
};
}
```
**TLS Modes:**
- **passthrough:** Forward raw encrypted TLS traffic without decryption
- **terminate:** Terminate TLS and forward as HTTP
- **terminate-and-reencrypt:** Terminate TLS and create a new TLS connection to the backend
**Redirect Action:**
When `type: 'redirect'`, the client is redirected:
```typescript
interface IRouteRedirect {
to: string; // URL or template with variables
status: 301 | 302 | 307 | 308; // HTTP status code
}
```
**Block Action:**
When `type: 'block'`, the connection is immediately closed.
**Security Options:**
For any action type, you can add security controls:
```typescript
interface IRouteSecurity {
allowedIps?: string[]; // IP allowlist with glob pattern support
blockedIps?: string[]; // IP blocklist with glob pattern support
maxConnections?: number; // Maximum concurrent connections
authentication?: { // Optional authentication (future support)
type: 'basic' | 'digest' | 'oauth';
// Auth-specific options
};
}
```
**Advanced Options:**
Additional advanced configurations:
```typescript
interface IRouteAdvanced {
timeout?: number; // Connection timeout in milliseconds
headers?: Record<string, string>; // Custom HTTP headers
keepAlive?: boolean; // Enable connection pooling
// Additional advanced options
}
```
#### Template Variables
String values in redirect URLs and headers can include variables:
- `{domain}`: The requested domain name
- `{port}`: The incoming port number
- `{path}`: The requested URL path
- `{query}`: The query string
- `{clientIp}`: The client's IP address
- `{sni}`: The SNI hostname
Example with template variables:
```typescript
redirect: {
to: 'https://{domain}{path}?source=redirect',
status: 301
}
```
#### Route Metadata and Prioritization
You can add metadata to routes to help with organization and control matching priority:
```typescript
{
name: 'API Server', // Human-readable name
description: 'Main API endpoints', // Description
priority: 100, // Matching priority (higher = matched first)
@ -263,6 +390,49 @@ The new route-based configuration in v14.0.0 follows a match/action pattern, mak
}
```
Routes with higher priority values are matched first, allowing you to create specialized routes that take precedence over more general ones.
#### Complete Route Configuration Example
```typescript
// Example of a complete route configuration
{
match: {
ports: 443,
domains: ['api.example.com', 'api-v2.example.com'],
path: '/secure/*',
clientIp: ['10.0.0.*', '192.168.1.*']
},
action: {
type: 'forward',
target: {
host: ['10.0.0.1', '10.0.0.2'], // Round-robin between these hosts
port: 8080
},
tls: {
mode: 'terminate',
certificate: 'auto' // Use Let's Encrypt
},
security: {
allowedIps: ['10.0.0.*'],
maxConnections: 100
},
advanced: {
timeout: 30000,
headers: {
'X-Original-Host': '{domain}',
'X-Client-IP': '{clientIp}'
},
keepAlive: true
}
},
name: 'Secure API Route',
description: 'Route for secure API endpoints with authentication',
priority: 100,
tags: ['api', 'secure', 'internal']
}
```
### Using Helper Functions
While you can create route configurations manually, SmartProxy provides helper functions to make it easier:

View File

@ -16,7 +16,7 @@ import {
} from '../ts/proxies/smart-proxy/index.js';
// Import test helpers
import { getCertificate } from './helpers/certificates.js';
import { loadTestCertificates } from './helpers/certificates.js';
tap.test('Routes: Should create basic HTTP route', async () => {
// Create a simple HTTP route
@ -31,12 +31,12 @@ tap.test('Routes: Should create basic HTTP route', async () => {
});
// Validate the route configuration
expect(httpRoute.match.ports).to.equal(8080);
expect(httpRoute.match.domains).to.equal('example.com');
expect(httpRoute.action.type).to.equal('forward');
expect(httpRoute.action.target?.host).to.equal('localhost');
expect(httpRoute.action.target?.port).to.equal(3000);
expect(httpRoute.name).to.equal('Basic HTTP Route');
expect(httpRoute.match.ports).toEqual(8080);
expect(httpRoute.match.domains).toEqual('example.com');
expect(httpRoute.action.type).toEqual('forward');
expect(httpRoute.action.target?.host).toEqual('localhost');
expect(httpRoute.action.target?.port).toEqual(3000);
expect(httpRoute.name).toEqual('Basic HTTP Route');
});
tap.test('Routes: Should create HTTPS route with TLS termination', async () => {
@ -52,14 +52,14 @@ tap.test('Routes: Should create HTTPS route with TLS termination', async () => {
});
// Validate the route configuration
expect(httpsRoute.match.ports).to.equal(443); // Default HTTPS port
expect(httpsRoute.match.domains).to.equal('secure.example.com');
expect(httpsRoute.action.type).to.equal('forward');
expect(httpsRoute.action.tls?.mode).to.equal('terminate');
expect(httpsRoute.action.tls?.certificate).to.equal('auto');
expect(httpsRoute.action.target?.host).to.equal('localhost');
expect(httpsRoute.action.target?.port).to.equal(8080);
expect(httpsRoute.name).to.equal('HTTPS Route');
expect(httpsRoute.match.ports).toEqual(443); // Default HTTPS port
expect(httpsRoute.match.domains).toEqual('secure.example.com');
expect(httpsRoute.action.type).toEqual('forward');
expect(httpsRoute.action.tls?.mode).toEqual('terminate');
expect(httpsRoute.action.tls?.certificate).toEqual('auto');
expect(httpsRoute.action.target?.host).toEqual('localhost');
expect(httpsRoute.action.target?.port).toEqual(8080);
expect(httpsRoute.name).toEqual('HTTPS Route');
});
tap.test('Routes: Should create HTTP to HTTPS redirect', async () => {
@ -70,11 +70,11 @@ tap.test('Routes: Should create HTTP to HTTPS redirect', async () => {
});
// Validate the route configuration
expect(redirectRoute.match.ports).to.equal(80);
expect(redirectRoute.match.domains).to.equal('example.com');
expect(redirectRoute.action.type).to.equal('redirect');
expect(redirectRoute.action.redirect?.to).to.equal('https://{domain}{path}');
expect(redirectRoute.action.redirect?.status).to.equal(301);
expect(redirectRoute.match.ports).toEqual(80);
expect(redirectRoute.match.domains).toEqual('example.com');
expect(redirectRoute.action.type).toEqual('redirect');
expect(redirectRoute.action.redirect?.to).toEqual('https://{domain}{path}');
expect(redirectRoute.action.redirect?.status).toEqual(301);
});
tap.test('Routes: Should create complete HTTPS server with redirects', async () => {
@ -90,20 +90,20 @@ tap.test('Routes: Should create complete HTTPS server with redirects', async ()
});
// Validate that we got two routes (HTTPS route and HTTP redirect)
expect(routes.length).to.equal(2);
expect(routes.length).toEqual(2);
// Validate HTTPS route
const httpsRoute = routes[0];
expect(httpsRoute.match.ports).to.equal(443);
expect(httpsRoute.match.domains).to.equal('example.com');
expect(httpsRoute.action.type).to.equal('forward');
expect(httpsRoute.action.tls?.mode).to.equal('terminate');
expect(httpsRoute.match.ports).toEqual(443);
expect(httpsRoute.match.domains).toEqual('example.com');
expect(httpsRoute.action.type).toEqual('forward');
expect(httpsRoute.action.tls?.mode).toEqual('terminate');
// Validate HTTP redirect route
const redirectRoute = routes[1];
expect(redirectRoute.match.ports).to.equal(80);
expect(redirectRoute.action.type).to.equal('redirect');
expect(redirectRoute.action.redirect?.to).to.equal('https://{domain}{path}');
expect(redirectRoute.match.ports).toEqual(80);
expect(redirectRoute.action.type).toEqual('redirect');
expect(redirectRoute.action.redirect?.to).toEqual('https://{domain}{path}');
});
tap.test('Routes: Should create load balancer route', async () => {
@ -118,18 +118,18 @@ tap.test('Routes: Should create load balancer route', async () => {
});
// Validate the route configuration
expect(lbRoute.match.domains).to.equal('app.example.com');
expect(lbRoute.action.type).to.equal('forward');
expect(Array.isArray(lbRoute.action.target?.host)).to.equal(true);
expect((lbRoute.action.target?.host as string[]).length).to.equal(3);
expect((lbRoute.action.target?.host as string[])[0]).to.equal('10.0.0.1');
expect(lbRoute.action.target?.port).to.equal(8080);
expect(lbRoute.action.tls?.mode).to.equal('terminate');
expect(lbRoute.match.domains).toEqual('app.example.com');
expect(lbRoute.action.type).toEqual('forward');
expect(Array.isArray(lbRoute.action.target?.host)).toBeTrue();
expect((lbRoute.action.target?.host as string[]).length).toEqual(3);
expect((lbRoute.action.target?.host as string[])[0]).toEqual('10.0.0.1');
expect(lbRoute.action.target?.port).toEqual(8080);
expect(lbRoute.action.tls?.mode).toEqual('terminate');
});
tap.test('SmartProxy: Should create instance with route-based config', async () => {
// Create TLS certificates for testing
const cert = await getCertificate();
const certs = loadTestCertificates();
// Create a SmartProxy instance with route-based configuration
const proxy = new SmartProxy({
@ -150,8 +150,8 @@ tap.test('SmartProxy: Should create instance with route-based config', async ()
port: 8443
},
certificate: {
key: cert.key,
cert: cert.cert
key: certs.privateKey,
cert: certs.publicKey
},
name: 'HTTPS Route'
})
@ -173,9 +173,9 @@ tap.test('SmartProxy: Should create instance with route-based config', async ()
});
// Simply verify the instance was created successfully
expect(proxy).to.be.an('object');
expect(proxy.start).to.be.a('function');
expect(proxy.stop).to.be.a('function');
expect(typeof proxy).toEqual('object');
expect(typeof proxy.start).toEqual('function');
expect(typeof proxy.stop).toEqual('function');
});
export default tap.start();

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartproxy',
version: '13.1.3',
description: 'A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.'
version: '15.0.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.'
}