Compare commits

...

6 Commits

Author SHA1 Message Date
09fc71f051 13.1.3
Some checks failed
Default (tags) / security (push) Successful in 34s
Default (tags) / test (push) Failing after 1m32s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-09 22:58:42 +00:00
e508078ecf 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. 2025-05-09 22:58:42 +00:00
7f614584b8 13.1.2
Some checks failed
Default (tags) / security (push) Successful in 35s
Default (tags) / test (push) Failing after 1m32s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-09 22:52:57 +00:00
e1a25b749c fix(docs): Update readme to reflect updated interface and type naming conventions 2025-05-09 22:52:57 +00:00
c34462b781 13.1.1
Some checks failed
Default (tags) / security (push) Successful in 43s
Default (tags) / test (push) Failing after 1m32s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-09 22:46:54 +00:00
f8647516b5 fix(typescript): Refactor types and interfaces to use consistent I prefix and update related tests 2025-05-09 22:46:53 +00:00
47 changed files with 870 additions and 633 deletions

View File

@ -1,5 +1,28 @@
# Changelog
## 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.
- Reorganized key sections to clearly list Primary API, Helper Functions, Specialized Components, and Core Utilities.
- Added detailed Quick Start examples covering API Gateway, automatic HTTPS, load balancing, wildcard subdomain support, and comprehensive proxy server setups.
- Included updated architecture flow diagrams and explanations of Unified Forwarding System and ACME integration.
- Improved clarity and consistency across documentation, with revised formatting and expanded descriptions.
## 2025-05-09 - 13.1.2 - fix(docs)
Update readme to reflect updated interface and type naming conventions
- Changed 'Interfaces' section to 'Interfaces and Types' with updated file references
- Replaced 'SmartProxyOptions', 'AcmeOptions', 'ForwardConfig' with their new names 'ISmartProxyOptions', 'IAcmeOptions', 'IForwardConfig', etc.
- Clarified API reference and project architecture documentation
## 2025-05-09 - 13.1.1 - fix(typescript)
Refactor types and interfaces to use consistent 'I' prefix and update related tests
- Replaced DomainConfig with IDomainConfig and SmartProxyOptions with ISmartProxyOptions in various modules
- Renamed SmartProxyCertProvisionObject to TSmartProxyCertProvisionObject for clarity
- Standardized type names (e.g. ForwardConfig → IForwardConfig, Logger → ILogger) across proxy, forwarding, and certificate modules
- Updated tests and helper functions to reflect new type names and ensure compatibility
## 2025-05-09 - 13.1.0 - feat(docs)
Update README to reflect new modular architecture and expanded core utilities: add Project Architecture Overview, update export paths and API references, and mark plan tasks as completed

View File

@ -1,6 +1,6 @@
{
"name": "@push.rocks/smartproxy",
"version": "13.1.0",
"version": "13.1.3",
"private": false,
"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.",
"main": "dist_ts/index.js",

691
readme.md
View File

@ -1,12 +1,13 @@
# @push.rocks/smartproxy
A high-performance proxy toolkit for Node.js, offering:
- HTTP/HTTPS reverse proxy with TLS termination and WebSocket support
- Automatic ACME certificate management (HTTP-01)
- Low-level port forwarding via nftables
- HTTP-to-HTTPS and custom URL redirects
- Advanced TCP/SNI-based proxying with IP filtering and rules
- Unified forwarding configuration system for all proxy types
A unified high-performance proxy toolkit for Node.js, with **SmartProxy** as the central API to handle all your proxy needs:
- **Unified Configuration API**: One consistent way to configure various proxy types
- **SSL/TLS Support**: Automatic HTTPS with Let's Encrypt certificate provisioning
- **Simplified Domain Management**: Easy routing based on domain names with wildcard support
- **Advanced SNI Handling**: Smart TCP/SNI-based forwarding with IP filtering
- **Multiple Forwarding Types**: HTTP-only, HTTPS passthrough, TLS termination options
- **Security Features**: IP allowlists, connection limits, timeouts, and more
## Project Architecture Overview
@ -51,34 +52,52 @@ SmartProxy has been restructured using a modern, modular architecture to improve
└── /redirects # Redirect handlers
```
## Exports
The following classes and interfaces are provided:
## Main Components
### Primary API (Recommended)
- **SmartProxy** (`ts/proxies/smart-proxy/smart-proxy.ts`)
The central unified API for all proxy needs, featuring:
- Domain-based routing with SNI inspection
- Automatic certificate management
- Multiple forwarding types in one configuration
- Advanced security controls
- Flexible backend targeting options
### Helper Functions
- **createDomainConfig**
Create domain configuration with clean syntax
- **httpOnly**, **httpsPassthrough**, **tlsTerminateToHttp**, **tlsTerminateToHttps**
Helper functions to create different forwarding configurations
### Specialized Components
- **NetworkProxy** (`ts/proxies/network-proxy/network-proxy.ts`)
HTTP/HTTPS reverse proxy with TLS termination, WebSocket support,
connection pooling, and optional ACME integration.
HTTP/HTTPS reverse proxy with TLS termination and WebSocket support
- **Port80Handler** (`ts/http/port80/port80-handler.ts`)
ACME HTTP-01 challenge handler and certificate manager.
ACME HTTP-01 challenge handler for Let's Encrypt certificates
- **NfTablesProxy** (`ts/proxies/nftables-proxy/nftables-proxy.ts`)
Low-level port forwarding using nftables NAT rules.
Low-level port forwarding using nftables NAT rules
- **Redirect**, **SslRedirect** (`ts/http/redirects/redirect-handler.ts`)
HTTP/HTTPS redirect server and shortcut for HTTP→HTTPS.
- **SmartProxy** (`ts/proxies/smart-proxy/smart-proxy.ts`)
TCP/SNI-based proxy with dynamic routing, IP filtering, and unified certificates.
HTTP-to-HTTPS redirects with customizable rules
- **SniHandler** (`ts/tls/sni/sni-handler.ts`)
Static utilities to extract SNI hostnames from TLS handshakes.
- **Forwarding Handlers** (`ts/forwarding/handlers/*.ts`)
Unified forwarding handlers for different connection types (HTTP, HTTPS passthrough, TLS termination).
- **Core Utilities**
- **ValidationUtils** (`ts/core/utils/validation-utils.ts`) for domain, port, and configuration validation
- **IpUtils** (`ts/core/utils/ip-utils.ts`) for IP address validation and filtering
Utilities for SNI extraction from TLS handshakes
- **Interfaces**
- `SmartProxyOptions`, `DomainConfig` (`ts/proxies/smart-proxy/models/interfaces.ts`)
- `NetworkProxyOptions` (`ts/proxies/network-proxy/models/types.ts`)
- `AcmeOptions`, `DomainOptions` (`ts/core/models/common-types.ts`)
- `NfTableProxySettings` (`ts/proxies/nftables-proxy/models/interfaces.ts`)
- `ForwardConfig`, `ForwardingType` (`ts/forwarding/config/forwarding-types.ts`)
### Core Utilities
- **ValidationUtils** (`ts/core/utils/validation-utils.ts`)
Domain, port, and configuration validation
- **IpUtils** (`ts/core/utils/ip-utils.ts`)
IP address validation and filtering with glob patterns
### Interfaces and Types
- `ISmartProxyOptions`, `IDomainConfig` (`ts/proxies/smart-proxy/models/interfaces.ts`)
- `IForwardConfig`, `TForwardingType` (`ts/forwarding/config/forwarding-types.ts`)
- `INetworkProxyOptions` (`ts/proxies/network-proxy/models/types.ts`)
- `IAcmeOptions`, `IDomainOptions` (`ts/certificate/models/certificate-types.ts`)
- `INfTableProxySettings` (`ts/proxies/nftables-proxy/models/interfaces.ts`)
## Installation
Install via npm:
@ -86,15 +105,142 @@ Install via npm:
npm install @push.rocks/smartproxy
```
## Quick Start
## Quick Start with SmartProxy
SmartProxy is the recommended way to use this library, providing a unified API for all proxy scenarios.
```typescript
import { SmartProxy, createDomainConfig, httpOnly, tlsTerminateToHttp, httpsPassthrough } from '@push.rocks/smartproxy';
// Create a new SmartProxy instance with all your domain configurations in one place
const proxy = new SmartProxy({
// Listen on port 443 for incoming connections
fromPort: 443,
// Configure domains and their forwarding rules
domainConfigs: [
// Basic HTTP forwarding for api.example.com
createDomainConfig('api.example.com', httpOnly({
target: { host: 'localhost', port: 3000 }
})),
// HTTPS termination with automatic Let's Encrypt certificates
createDomainConfig('secure.example.com', tlsTerminateToHttp({
target: { host: 'localhost', port: 8080 },
acme: {
enabled: true,
production: true
}
})),
// Multiple domains with wildcard support
createDomainConfig(['example.com', '*.example.com'], httpsPassthrough({
target: {
// Load balancing across multiple backend servers
host: ['192.168.1.10', '192.168.1.11'],
port: 443
},
security: {
// IP filtering for enhanced security
allowedIps: ['10.0.0.*', '192.168.1.*'],
blockedIps: ['1.2.3.4']
}
}))
],
// Enable SNI-based routing
sniEnabled: true,
// Automatic Let's Encrypt integration
acme: {
enabled: true,
contactEmail: 'admin@example.com',
useProduction: true
}
});
// Listen for certificate events
proxy.on('certificate', evt => {
console.log(`Certificate for ${evt.domain} ready, expires: ${evt.expiryDate}`);
});
// Start the proxy
await proxy.start();
// Dynamically add or update domain configurations later
await proxy.updateDomainConfigs([
createDomainConfig('new-domain.com', tlsTerminateToHttp({
target: { host: 'localhost', port: 9000 }
}))
]);
// Later, gracefully shut down
await proxy.stop();
```
### What You Can Do with SmartProxy
1. **Domain-Based Routing**
```typescript
// Route requests for different domains to different backend servers
createDomainConfig('api.example.com', httpOnly({
target: { host: 'api-server', port: 3000 }
}))
```
2. **Automatic SSL with Let's Encrypt**
```typescript
// Get and automatically renew certificates
createDomainConfig('secure.example.com', tlsTerminateToHttp({
target: { host: 'localhost', port: 8080 },
acme: { enabled: true, production: true }
}))
```
3. **Load Balancing**
```typescript
// Distribute traffic across multiple backend servers
createDomainConfig('app.example.com', httpOnly({
target: {
host: ['10.0.0.1', '10.0.0.2', '10.0.0.3'],
port: 8080
}
}))
```
4. **Security Controls**
```typescript
// Restrict access based on IP addresses
createDomainConfig('admin.example.com', httpOnly({
target: { host: 'localhost', port: 8080 },
security: {
allowedIps: ['10.0.0.*', '192.168.1.*'],
maxConnections: 100
}
}))
```
5. **Wildcard Domains**
```typescript
// Handle all subdomains with one config
createDomainConfig(['example.com', '*.example.com'], httpsPassthrough({
target: { host: 'backend-server', port: 443 }
}))
```
## Other Components
While SmartProxy provides a unified API for most needs, you can also use individual components:
### NetworkProxy
For HTTP/HTTPS reverse proxy with TLS termination and WebSocket support:
### 1. HTTP(S) Reverse Proxy (NetworkProxy)
```typescript
import { NetworkProxy } from '@push.rocks/smartproxy';
import * as fs from 'fs';
const proxy = new NetworkProxy({ port: 443 });
await proxy.start();
await proxy.updateProxyConfigs([
{
hostName: 'example.com',
@ -104,163 +250,56 @@ await proxy.updateProxyConfigs([
privateKey: fs.readFileSync('key.pem', 'utf8'),
}
]);
// Add default headers to all responses
await proxy.addDefaultHeaders({
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains'
});
// ...
await proxy.stop();
```
### 2. HTTP→HTTPS Redirect (Redirect / SslRedirect)
```typescript
import { Redirect, SslRedirect } from '@push.rocks/smartproxy';
import * as fs from 'fs';
### Port80Handler
For standalone ACME certificate management:
// Custom redirect rules
const redirect = new Redirect({
httpPort: 80,
httpsPort: 443,
sslOptions: {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
},
rules: [
{
fromProtocol: 'http',
fromHost: '*',
toProtocol: 'https',
toHost: '$1',
statusCode: 301
}
]
});
await redirect.start();
// Quick HTTP→HTTPS helper on port 80
const quick = new SslRedirect(80);
await quick.start();
```
### 3. Automatic Certificates (ACME Port80Handler)
```typescript
import { Port80Handler } from '@push.rocks/smartproxy';
// Configure ACME on port 80 with contact email
const acme = new Port80Handler({
port: 80,
contactEmail: 'admin@example.com',
useProduction: true,
renewThresholdDays: 30
});
acme.on('certificate-issued', evt => {
console.log(`Certificate ready for ${evt.domain}, expires ${evt.expiryDate}`);
useProduction: true
});
acme.on('certificate-issued', evt => console.log(`Certificate ready: ${evt.domain}`));
await acme.start();
acme.addDomain({
domainName: 'example.com',
sslRedirect: true,
acmeMaintenance: true
});
```
### 4. Low-Level Port Forwarding (NfTablesProxy)
### NfTablesProxy
For low-level port forwarding using nftables:
```typescript
import { NfTablesProxy } from '@push.rocks/smartproxy';
// Forward port 80→8080 with source IP preservation
const nft = new NfTablesProxy({
fromPort: 80,
toPort: 8080,
toHost: 'localhost',
preserveSourceIP: true,
deleteOnExit: true
preserveSourceIP: true
});
await nft.start();
// ...
await nft.stop();
```
### 5. TCP/SNI Proxy (SmartProxy)
### Redirect / SslRedirect
For HTTP-to-HTTPS redirects:
```typescript
import { SmartProxy } from '@push.rocks/smartproxy';
import { createDomainConfig, httpOnly, tlsTerminateToHttp, httpsPassthrough } from '@push.rocks/smartproxy';
import { SslRedirect } from '@push.rocks/smartproxy';
const smart = new SmartProxy({
fromPort: 443,
toPort: 8443,
domainConfigs: [
// HTTPS passthrough example
createDomainConfig(['example.com', '*.example.com'],
httpsPassthrough({
target: {
host: '127.0.0.1',
port: 443
},
security: {
allowedIps: ['*']
}
})
),
// HTTPS termination example
createDomainConfig('secure.example.com',
tlsTerminateToHttp({
target: {
host: 'localhost',
port: 3000
},
acme: {
enabled: true,
production: true
}
})
)
],
sniEnabled: true
});
smart.on('certificate', evt => console.log(evt));
await smart.start();
// Update domains later
await smart.updateDomainConfigs([/* new configs */]);
```
### 6. SNI Utilities (SniHandler)
```js
import { SniHandler } from '@push.rocks/smartproxy';
// Extract SNI from a TLS ClientHello buffer
const sni = SniHandler.extractSNI(buffer);
// Reassemble fragmented ClientHello
const complete = SniHandler.handleFragmentedClientHello(buf, connId);
```
### 7. Core Utilities (ValidationUtils, IpUtils)
```typescript
import { ValidationUtils, IpUtils } from '@push.rocks/smartproxy';
// Validate a domain name
const isValidDomain = ValidationUtils.isValidDomainName('example.com');
// Check if an IP is allowed based on filters
const isAllowed = IpUtils.isIPAuthorized(
'192.168.1.1',
['192.168.1.*'], // allowed IPs
['192.168.1.100'] // blocked IPs
);
// Convert CIDR to glob patterns
const globPatterns = IpUtils.cidrToGlobPatterns('10.0.0.0/24');
// Quick HTTP→HTTPS helper on port 80
const redirect = new SslRedirect(80);
await redirect.start();
```
## API Reference
For full configuration options and type definitions, see the TypeScript interfaces:
- `NetworkProxyOptions` (`ts/proxies/network-proxy/models/types.ts`)
- `AcmeOptions`, `DomainOptions` (`ts/core/models/common-types.ts`)
- `ForwardConfig` (`ts/forwarding/config/forwarding-types.ts`)
- `NfTableProxySettings` (`ts/proxies/nftables-proxy/models/interfaces.ts`)
- `SmartProxyOptions`, `DomainConfig` (`ts/proxies/smart-proxy/models/interfaces.ts`)
- `INetworkProxyOptions` (`ts/proxies/network-proxy/models/types.ts`)
- `IAcmeOptions`, `IDomainOptions` (`ts/certificate/models/certificate-types.ts`)
- `IForwardConfig` (`ts/forwarding/config/forwarding-types.ts`)
- `INfTableProxySettings` (`ts/proxies/nftables-proxy/models/interfaces.ts`)
- `ISmartProxyOptions`, `IDomainConfig` (`ts/proxies/smart-proxy/models/interfaces.ts`)
## Architecture & Flow Diagrams
@ -479,118 +518,290 @@ Listen for certificate events via EventEmitter:
Provide a `certProvisionFunction(domain)` in SmartProxy settings to supply static certs or return `'http01'`.
## Unified Forwarding System
## SmartProxy: Common Use Cases
The SmartProxy Unified Forwarding System provides a clean, use-case driven approach to configuring different types of traffic forwarding. It replaces disparate configuration mechanisms with a unified interface.
The SmartProxy component offers a clean, unified approach to handle virtually any proxy scenario.
### Forwarding Types
### 1. API Gateway / Backend Routing
The system supports four primary forwarding types:
Create a flexible API gateway to route traffic to different microservices based on domain:
```typescript
import { SmartProxy, createDomainConfig, httpOnly, tlsTerminateToHttp } from '@push.rocks/smartproxy';
const apiGateway = new SmartProxy({
fromPort: 443,
domainConfigs: [
// Users API
createDomainConfig('users.api.example.com', tlsTerminateToHttp({
target: { host: 'users-service', port: 3000 },
acme: { enabled: true, production: true }
})),
// Products API
createDomainConfig('products.api.example.com', tlsTerminateToHttp({
target: { host: 'products-service', port: 3001 },
acme: { enabled: true, production: true }
})),
// Admin dashboard gets extra security
createDomainConfig('admin.example.com', tlsTerminateToHttp({
target: { host: 'admin-dashboard', port: 8080 },
security: {
allowedIps: ['10.0.0.*', '192.168.1.*'] // Only allow internal network
}
}))
],
sniEnabled: true
});
await apiGateway.start();
```
### 2. Automatic HTTPS for Development
Easily add HTTPS to your local development environment with automatic certificates:
```typescript
import { SmartProxy, createDomainConfig, tlsTerminateToHttp } from '@push.rocks/smartproxy';
const devProxy = new SmartProxy({
fromPort: 443,
domainConfigs: [
createDomainConfig('dev.local', tlsTerminateToHttp({
target: { host: 'localhost', port: 3000 },
// For development, use self-signed or existing certificates
https: {
customCert: {
key: fs.readFileSync('dev-cert.key', 'utf8'),
cert: fs.readFileSync('dev-cert.pem', 'utf8')
}
},
// Auto-redirect HTTP to HTTPS
http: {
enabled: true,
redirectToHttps: true
}
}))
]
});
await devProxy.start();
```
### 3. Load Balancing Multiple Servers
Distribute traffic across multiple backend servers with round-robin load balancing:
```typescript
import { SmartProxy, createDomainConfig, tlsTerminateToHttp } from '@push.rocks/smartproxy';
const loadBalancer = new SmartProxy({
fromPort: 443,
domainConfigs: [
createDomainConfig('app.example.com', tlsTerminateToHttp({
target: {
// Round-robin across multiple servers
host: [
'10.0.0.10',
'10.0.0.11',
'10.0.0.12'
],
port: 8080
},
acme: { enabled: true, production: true }
}))
]
});
await loadBalancer.start();
```
### 4. Wildcard Subdomain Handling
Support multiple or dynamically created subdomains with one configuration:
```typescript
import { SmartProxy, createDomainConfig, tlsTerminateToHttp } from '@push.rocks/smartproxy';
const multiTenantProxy = new SmartProxy({
fromPort: 443,
domainConfigs: [
// Handle all customer subdomains with one config
createDomainConfig('*.example.com', tlsTerminateToHttp({
target: { host: 'tenant-router', port: 8080 },
acme: { enabled: true, production: true },
// Pass original hostname to backend for tenant identification
advanced: {
headers: {
'X-Original-Host': '{sni}'
}
}
}))
],
sniEnabled: true
});
await multiTenantProxy.start();
```
### 5. Comprehensive Proxy Server
Create a complete proxy solution with multiple services on a single server:
```typescript
import { SmartProxy, createDomainConfig, httpOnly, tlsTerminateToHttp, tlsTerminateToHttps, httpsPassthrough } from '@push.rocks/smartproxy';
const enterpriseProxy = new SmartProxy({
fromPort: 443,
domainConfigs: [
// Web application with automatic HTTPS
createDomainConfig('app.example.com', tlsTerminateToHttp({
target: { host: 'web-app', port: 8080 },
acme: { enabled: true, production: true },
http: { enabled: true, redirectToHttps: true }
})),
// Legacy system that needs HTTPS passthrough
createDomainConfig('legacy.example.com', httpsPassthrough({
target: { host: 'legacy-server', port: 443 }
})),
// Internal APIs with IP restrictions
createDomainConfig('api.internal.example.com', tlsTerminateToHttp({
target: { host: 'api-gateway', port: 3000 },
security: {
allowedIps: ['10.0.0.0/16', '192.168.0.0/16'],
maxConnections: 500
}
})),
// External services with customer certificate
createDomainConfig('external.example.com', tlsTerminateToHttps({
target: { host: 'external-service', port: 8443 },
https: {
customCert: {
key: fs.readFileSync('external-key.pem', 'utf8'),
cert: fs.readFileSync('external-cert.pem', 'utf8')
}
}
}))
],
sniEnabled: true,
// Enable connection timeouts for security
inactivityTimeout: 30000,
// Using global certificate management
acme: {
enabled: true,
contactEmail: 'admin@example.com',
useProduction: true,
renewThresholdDays: 30
}
});
await enterpriseProxy.start();
```
## Unified Forwarding System Details
SmartProxy's unified forwarding system supports four primary forwarding types:
1. **HTTP-only (`http-only`)**: Forwards HTTP traffic to a backend server.
2. **HTTPS Passthrough (`https-passthrough`)**: Passes through raw TLS traffic without termination (SNI forwarding).
3. **HTTPS Termination to HTTP (`https-terminate-to-http`)**: Terminates TLS and forwards the decrypted traffic to an HTTP backend.
4. **HTTPS Termination to HTTPS (`https-terminate-to-https`)**: Terminates TLS and creates a new TLS connection to an HTTPS backend.
### Basic Configuration
### Configuration Format
Each domain is configured with a forwarding type and target:
```typescript
{
domains: ['example.com'],
domains: ['example.com'], // Single domain or array of domains (with wildcard support)
forwarding: {
type: 'http-only',
type: 'http-only', // One of the four forwarding types
target: {
host: 'localhost',
port: 3000
host: 'localhost', // Backend server (string or array for load balancing)
port: 3000 // Backend port
}
// Additional options as needed
}
}
```
### Helper Functions
Helper functions are provided for common configurations:
Helper functions provide a cleaner syntax for creating configurations:
```typescript
import { createDomainConfig, httpOnly, tlsTerminateToHttp,
tlsTerminateToHttps, httpsPassthrough } from '@push.rocks/smartproxy';
// Instead of manually specifying the type and format
const config = createDomainConfig('example.com', httpOnly({
target: { host: 'localhost', port: 3000 }
}));
// HTTP-only
await domainManager.addDomainConfig(
createDomainConfig('example.com', httpOnly({
target: { host: 'localhost', port: 3000 }
}))
);
// HTTPS termination to HTTP
await domainManager.addDomainConfig(
createDomainConfig('secure.example.com', tlsTerminateToHttp({
target: { host: 'localhost', port: 3000 },
acme: { production: true }
}))
);
// HTTPS termination to HTTPS
await domainManager.addDomainConfig(
createDomainConfig('api.example.com', tlsTerminateToHttps({
target: { host: 'internal-api', port: 8443 },
http: { redirectToHttps: true }
}))
);
// HTTPS passthrough (SNI)
await domainManager.addDomainConfig(
createDomainConfig('passthrough.example.com', httpsPassthrough({
target: { host: '10.0.0.5', port: 443 }
}))
);
// Available helper functions:
// - httpOnly() - For HTTP-only traffic
// - httpsPassthrough() - For SNI-based passthrough
// - tlsTerminateToHttp() - For HTTPS termination to HTTP
// - tlsTerminateToHttps() - For HTTPS termination to HTTPS
```
### Advanced Configuration
### Advanced Configuration Options
For more complex scenarios, additional options can be specified:
```typescript
{
domains: ['api.example.com'],
forwarding: {
type: 'https-terminate-to-https',
target: {
host: ['10.0.0.10', '10.0.0.11'], // Round-robin load balancing
port: 8443
createDomainConfig('api.example.com', tlsTerminateToHttps({
// Target configuration with load balancing
target: {
host: ['10.0.0.10', '10.0.0.11'], // Round-robin load balancing
port: 8443
},
// HTTP options
http: {
enabled: true, // Listen on HTTP port
redirectToHttps: true // Automatically redirect to HTTPS
},
// HTTPS/TLS options
https: {
customCert: { // Provide your own certificate
key: '-----BEGIN PRIVATE KEY-----\n...',
cert: '-----BEGIN CERTIFICATE-----\n...'
},
http: {
enabled: true,
redirectToHttps: true
forwardSni: true // Forward original SNI to backend
},
// Let's Encrypt ACME integration
acme: {
enabled: true, // Enable automatic certificates
production: true, // Use production Let's Encrypt
maintenance: true // Auto-renew certificates
},
// Security settings
security: {
allowedIps: ['10.0.0.*'], // IP allowlist (glob patterns)
blockedIps: ['1.2.3.4'], // IP blocklist
maxConnections: 100 // Connection limits
},
// Advanced settings
advanced: {
timeout: 30000, // Connection timeout in ms
headers: { // Custom headers to backend
'X-Forwarded-For': '{clientIp}',
'X-Original-Host': '{sni}' // Template variables available
},
https: {
// Custom certificate instead of ACME-provisioned
customCert: {
key: '-----BEGIN PRIVATE KEY-----\n...',
cert: '-----BEGIN CERTIFICATE-----\n...'
}
},
security: {
allowedIps: ['10.0.0.*', '192.168.1.*'],
blockedIps: ['1.2.3.4'],
maxConnections: 100
},
advanced: {
timeout: 30000,
headers: {
'X-Forwarded-For': '{clientIp}',
'X-Original-Host': '{sni}'
}
}
keepAlive: true // Keep connections alive
}
}
}))
```
### Extended Configuration Options
#### ForwardConfig
#### IForwardConfig
- `type`: 'http-only' | 'https-passthrough' | 'https-terminate-to-http' | 'https-terminate-to-https'
- `target`: { host: string | string[], port: number }
- `http?`: { enabled?: boolean, redirectToHttps?: boolean, headers?: Record<string, string> }
@ -601,7 +812,7 @@ For more complex scenarios, additional options can be specified:
## Configuration Options
### NetworkProxy (NetworkProxyOptions)
### NetworkProxy (INetworkProxyOptions)
- `port` (number, required)
- `backendProtocol` ('http1'|'http2', default 'http1')
- `maxConnections` (number, default 10000)
@ -610,11 +821,11 @@ For more complex scenarios, additional options can be specified:
- `cors` (object)
- `connectionPoolSize` (number, default 50)
- `logLevel` ('error'|'warn'|'info'|'debug')
- `acme` (AcmeOptions)
- `acme` (IAcmeOptions)
- `useExternalPort80Handler` (boolean)
- `portProxyIntegration` (boolean)
### Port80Handler (AcmeOptions)
### Port80Handler (IAcmeOptions)
- `enabled` (boolean, default true)
- `port` (number, default 80)
- `contactEmail` (string)
@ -623,9 +834,9 @@ For more complex scenarios, additional options can be specified:
- `autoRenew` (boolean, default true)
- `certificateStore` (string)
- `skipConfiguredCerts` (boolean)
- `domainForwards` (DomainForwardConfig[])
- `domainForwards` (IDomainForwardConfig[])
### NfTablesProxy (NfTableProxySettings)
### NfTablesProxy (INfTableProxySettings)
- `fromPort` / `toPort` (number|range|array)
- `toHost` (string, default 'localhost')
- `preserveSourceIP`, `deleteOnExit`, `protocol`, `enableLogging`, `ipv6Support` (booleans)
@ -634,16 +845,16 @@ For more complex scenarios, additional options can be specified:
- `qos`, `netProxyIntegration` (objects)
### Redirect / SslRedirect
- Constructor options: `httpPort`, `httpsPort`, `sslOptions`, `rules` (RedirectRule[])
- Constructor options: `httpPort`, `httpsPort`, `sslOptions`, `rules` (IRedirectRule[])
### SmartProxy (SmartProxyOptions)
### SmartProxy (ISmartProxyOptions)
- `fromPort`, `toPort` (number)
- `domainConfigs` (DomainConfig[]) - Using unified forwarding configuration
- `domainConfigs` (IDomainConfig[]) - Using unified forwarding configuration
- `sniEnabled`, `preserveSourceIP` (booleans)
- `defaultAllowedIPs`, `defaultBlockedIPs` (string[]) - Default IP allowlists/blocklists
- Timeouts: `initialDataTimeout`, `socketTimeout`, `inactivityTimeout`, etc.
- Socket opts: `noDelay`, `keepAlive`, `enableKeepAliveProbes`
- `acme` (AcmeOptions), `certProvisionFunction` (callback)
- `acme` (IAcmeOptions), `certProvisionFunction` (callback)
- `useNetworkProxy` (number[]), `networkProxyPort` (number)
- `globalPortRanges` (Array<{ from: number; to: number }>)

View File

@ -1,9 +1,10 @@
import { tap, expect } from '@push.rocks/tapbundle';
import * as plugins from '../ts/plugins.js';
import { CertProvisioner } from '../ts/certificate/providers/cert-provisioner.js';
import type { DomainConfig } from '../ts/forwarding/config/forwarding-types.js';
import type { SmartProxyCertProvisionObject } from '../ts/certificate/models/certificate-types.js';
import type { CertificateData } from '../ts/certificate/models/certificate-types.js';
import type { IDomainConfig } from '../ts/forwarding/config/domain-config.js';
import type { ICertificateData } from '../ts/certificate/models/certificate-types.js';
// Import SmartProxyCertProvisionObject type alias
import type { TSmartProxyCertProvisionObject } from '../ts/certificate/providers/cert-provisioner.js';
// Fake Port80Handler stub
class FakePort80Handler extends plugins.EventEmitter {
@ -19,15 +20,15 @@ class FakePort80Handler extends plugins.EventEmitter {
// Fake NetworkProxyBridge stub
class FakeNetworkProxyBridge {
public appliedCerts: CertificateData[] = [];
applyExternalCertificate(cert: CertificateData) {
public appliedCerts: ICertificateData[] = [];
applyExternalCertificate(cert: ICertificateData) {
this.appliedCerts.push(cert);
}
}
tap.test('CertProvisioner handles static provisioning', async () => {
const domain = 'static.com';
const domainConfigs: DomainConfig[] = [{
const domainConfigs: IDomainConfig[] = [{
domains: [domain],
forwarding: {
type: 'https-terminate-to-https',
@ -37,7 +38,7 @@ tap.test('CertProvisioner handles static provisioning', async () => {
const fakePort80 = new FakePort80Handler();
const fakeBridge = new FakeNetworkProxyBridge();
// certProvider returns static certificate
const certProvider = async (d: string): Promise<SmartProxyCertProvisionObject> => {
const certProvider = async (d: string): Promise<TSmartProxyCertProvisionObject> => {
expect(d).toEqual(domain);
return {
domainName: domain,
@ -75,7 +76,7 @@ tap.test('CertProvisioner handles static provisioning', async () => {
tap.test('CertProvisioner handles http01 provisioning', async () => {
const domain = 'http01.com';
const domainConfigs: DomainConfig[] = [{
const domainConfigs: IDomainConfig[] = [{
domains: [domain],
forwarding: {
type: 'https-terminate-to-http',
@ -85,7 +86,7 @@ tap.test('CertProvisioner handles http01 provisioning', async () => {
const fakePort80 = new FakePort80Handler();
const fakeBridge = new FakeNetworkProxyBridge();
// certProvider returns http01 directive
const certProvider = async (): Promise<SmartProxyCertProvisionObject> => 'http01';
const certProvider = async (): Promise<TSmartProxyCertProvisionObject> => 'http01';
const prov = new CertProvisioner(
domainConfigs,
fakePort80 as any,
@ -106,7 +107,7 @@ tap.test('CertProvisioner handles http01 provisioning', async () => {
tap.test('CertProvisioner on-demand http01 renewal', async () => {
const domain = 'renew.com';
const domainConfigs: DomainConfig[] = [{
const domainConfigs: IDomainConfig[] = [{
domains: [domain],
forwarding: {
type: 'https-terminate-to-http',
@ -115,7 +116,7 @@ tap.test('CertProvisioner on-demand http01 renewal', async () => {
}];
const fakePort80 = new FakePort80Handler();
const fakeBridge = new FakeNetworkProxyBridge();
const certProvider = async (): Promise<SmartProxyCertProvisionObject> => 'http01';
const certProvider = async (): Promise<TSmartProxyCertProvisionObject> => 'http01';
const prov = new CertProvisioner(
domainConfigs,
fakePort80 as any,
@ -132,7 +133,7 @@ tap.test('CertProvisioner on-demand http01 renewal', async () => {
tap.test('CertProvisioner on-demand static provisioning', async () => {
const domain = 'ondemand.com';
const domainConfigs: DomainConfig[] = [{
const domainConfigs: IDomainConfig[] = [{
domains: [domain],
forwarding: {
type: 'https-terminate-to-https',
@ -141,7 +142,7 @@ tap.test('CertProvisioner on-demand static provisioning', async () => {
}];
const fakePort80 = new FakePort80Handler();
const fakeBridge = new FakeNetworkProxyBridge();
const certProvider = async (): Promise<SmartProxyCertProvisionObject> => ({
const certProvider = async (): Promise<TSmartProxyCertProvisionObject> => ({
domainName: domain,
publicKey: 'PKEY',
privateKey: 'PRIV',

View File

@ -2,8 +2,8 @@ import * as plugins from '../ts/plugins.js';
import { tap, expect } from '@push.rocks/tapbundle';
import { SmartProxy } from '../ts/proxies/smart-proxy/index.js';
import type { DomainConfig } from '../ts/forwarding/config/forwarding-types.js';
import type { ForwardingType } from '../ts/forwarding/config/forwarding-types.js';
import type { TForwardingType } from '../ts/forwarding/config/forwarding-types.js';
import type { IDomainConfig } from '../ts/forwarding/config/domain-config.js';
import {
httpOnly,
httpsPassthrough,
@ -14,7 +14,7 @@ import {
// Test to demonstrate various forwarding configurations
tap.test('Forwarding configuration examples', async (tools) => {
// Example 1: HTTP-only configuration
const httpOnlyConfig: DomainConfig = {
const httpOnlyConfig: IDomainConfig = {
domains: ['http.example.com'],
forwarding: httpOnly({
target: {
@ -30,7 +30,7 @@ tap.test('Forwarding configuration examples', async (tools) => {
expect(httpOnlyConfig.forwarding.type).toEqual('http-only');
// Example 2: HTTPS Passthrough (SNI)
const httpsPassthroughConfig: DomainConfig = {
const httpsPassthroughConfig: IDomainConfig = {
domains: ['pass.example.com'],
forwarding: httpsPassthrough({
target: {
@ -47,7 +47,7 @@ tap.test('Forwarding configuration examples', async (tools) => {
expect(Array.isArray(httpsPassthroughConfig.forwarding.target.host)).toBeTrue();
// Example 3: HTTPS Termination to HTTP Backend
const terminateToHttpConfig: DomainConfig = {
const terminateToHttpConfig: IDomainConfig = {
domains: ['secure.example.com'],
forwarding: tlsTerminateToHttp({
target: {
@ -75,7 +75,7 @@ tap.test('Forwarding configuration examples', async (tools) => {
expect(terminateToHttpConfig.forwarding.http?.redirectToHttps).toBeTrue();
// Example 4: HTTPS Termination to HTTPS Backend
const terminateToHttpsConfig: DomainConfig = {
const terminateToHttpsConfig: IDomainConfig = {
domains: ['proxy.example.com'],
forwarding: tlsTerminateToHttps({
target: {

View File

@ -1,6 +1,6 @@
import { tap, expect } from '@push.rocks/tapbundle';
import * as plugins from '../ts/plugins.js';
import type { ForwardConfig, ForwardingType } from '../ts/forwarding/config/forwarding-types.js';
import type { IForwardConfig, TForwardingType } from '../ts/forwarding/config/forwarding-types.js';
// First, import the components directly to avoid issues with compiled modules
import { ForwardingHandlerFactory } from '../ts/forwarding/factory/forwarding-factory.js';
@ -17,7 +17,7 @@ const helpers = {
tap.test('ForwardingHandlerFactory - apply defaults based on type', async () => {
// HTTP-only defaults
const httpConfig: ForwardConfig = {
const httpConfig: IForwardConfig = {
type: 'http-only',
target: { host: 'localhost', port: 3000 }
};
@ -26,7 +26,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedHttpConfig.http?.enabled).toEqual(true);
// HTTPS-passthrough defaults
const passthroughConfig: ForwardConfig = {
const passthroughConfig: IForwardConfig = {
type: 'https-passthrough',
target: { host: 'localhost', port: 443 }
};
@ -36,7 +36,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedPassthroughConfig.http?.enabled).toEqual(false);
// HTTPS-terminate-to-http defaults
const terminateToHttpConfig: ForwardConfig = {
const terminateToHttpConfig: IForwardConfig = {
type: 'https-terminate-to-http',
target: { host: 'localhost', port: 3000 }
};
@ -48,7 +48,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedTerminateToHttpConfig.acme?.maintenance).toEqual(true);
// HTTPS-terminate-to-https defaults
const terminateToHttpsConfig: ForwardConfig = {
const terminateToHttpsConfig: IForwardConfig = {
type: 'https-terminate-to-https',
target: { host: 'localhost', port: 8443 }
};
@ -62,7 +62,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
tap.test('ForwardingHandlerFactory - validate configuration', async () => {
// Valid configuration
const validConfig: ForwardConfig = {
const validConfig: IForwardConfig = {
type: 'http-only',
target: { host: 'localhost', port: 3000 }
};
@ -77,7 +77,7 @@ tap.test('ForwardingHandlerFactory - validate configuration', async () => {
expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig1)).toThrow();
// Invalid configuration - invalid port
const invalidConfig2: ForwardConfig = {
const invalidConfig2: IForwardConfig = {
type: 'http-only',
target: { host: 'localhost', port: 0 }
};
@ -85,7 +85,7 @@ tap.test('ForwardingHandlerFactory - validate configuration', async () => {
expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig2)).toThrow();
// Invalid configuration - HTTP disabled for HTTP-only
const invalidConfig3: ForwardConfig = {
const invalidConfig3: IForwardConfig = {
type: 'http-only',
target: { host: 'localhost', port: 3000 },
http: { enabled: false }
@ -94,7 +94,7 @@ tap.test('ForwardingHandlerFactory - validate configuration', async () => {
expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig3)).toThrow();
// Invalid configuration - HTTP enabled for HTTPS passthrough
const invalidConfig4: ForwardConfig = {
const invalidConfig4: IForwardConfig = {
type: 'https-passthrough',
target: { host: 'localhost', port: 443 },
http: { enabled: true }

View File

@ -1,6 +1,6 @@
import { tap, expect } from '@push.rocks/tapbundle';
import * as plugins from '../ts/plugins.js';
import type { ForwardConfig } from '../ts/forwarding/config/forwarding-types.js';
import type { IForwardConfig } from '../ts/forwarding/config/forwarding-types.js';
// First, import the components directly to avoid issues with compiled modules
import { ForwardingHandlerFactory } from '../ts/forwarding/factory/forwarding-factory.js';
@ -17,7 +17,7 @@ const helpers = {
tap.test('ForwardingHandlerFactory - apply defaults based on type', async () => {
// HTTP-only defaults
const httpConfig: ForwardConfig = {
const httpConfig: IForwardConfig = {
type: 'http-only',
target: { host: 'localhost', port: 3000 }
};
@ -26,7 +26,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedHttpConfig.http?.enabled).toEqual(true);
// HTTPS-passthrough defaults
const passthroughConfig: ForwardConfig = {
const passthroughConfig: IForwardConfig = {
type: 'https-passthrough',
target: { host: 'localhost', port: 443 }
};
@ -36,7 +36,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedPassthroughConfig.http?.enabled).toEqual(false);
// HTTPS-terminate-to-http defaults
const terminateToHttpConfig: ForwardConfig = {
const terminateToHttpConfig: IForwardConfig = {
type: 'https-terminate-to-http',
target: { host: 'localhost', port: 3000 }
};
@ -48,7 +48,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
expect(expandedTerminateToHttpConfig.acme?.maintenance).toEqual(true);
// HTTPS-terminate-to-https defaults
const terminateToHttpsConfig: ForwardConfig = {
const terminateToHttpsConfig: IForwardConfig = {
type: 'https-terminate-to-https',
target: { host: 'localhost', port: 8443 }
};
@ -62,7 +62,7 @@ tap.test('ForwardingHandlerFactory - apply defaults based on type', async () =>
tap.test('ForwardingHandlerFactory - validate configuration', async () => {
// Valid configuration
const validConfig: ForwardConfig = {
const validConfig: IForwardConfig = {
type: 'http-only',
target: { host: 'localhost', port: 3000 }
};
@ -77,7 +77,7 @@ tap.test('ForwardingHandlerFactory - validate configuration', async () => {
expect(() => ForwardingHandlerFactory.validateConfig(invalidConfig1)).toThrow();
// Invalid configuration - invalid port
const invalidConfig2: ForwardConfig = {
const invalidConfig2: IForwardConfig = {
type: 'http-only',
target: { host: 'localhost', port: 0 }
};

View File

@ -282,10 +282,20 @@ tap.test('should support optional source IP preservation in chained proxies', as
// Test round-robin behavior for multiple target hosts in a domain config.
tap.test('should use round robin for multiple target hosts in domain config', async () => {
// Create a domain config with multiple hosts in the target
const domainConfig = {
const domainConfig: {
domains: string[];
forwarding: {
type: 'http-only';
target: {
host: string[];
port: number;
};
http: { enabled: boolean };
}
} = {
domains: ['rr.test'],
forwarding: {
type: 'http-only',
type: 'http-only' as const,
target: {
host: ['hostA', 'hostB'], // Array of hosts for round-robin
port: 80

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartproxy',
version: '13.1.0',
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.'
}

View File

@ -1,6 +1,6 @@
import * as fs from 'fs';
import * as path from 'path';
import type { AcmeOptions } from '../models/certificate-types.js';
import type { IAcmeOptions } from '../models/certificate-types.js';
import { ensureCertificateDirectory } from '../utils/certificate-helpers.js';
// We'll need to update this import when we move the Port80Handler
import { Port80Handler } from '../../http/port80/port80-handler.js';
@ -12,7 +12,7 @@ import { Port80Handler } from '../../http/port80/port80-handler.js';
* @returns A new Port80Handler instance
*/
export function buildPort80Handler(
options: AcmeOptions
options: IAcmeOptions
): Port80Handler {
if (options.certificateStore) {
ensureCertificateDirectory(options.certificateStore);
@ -32,7 +32,7 @@ export function createDefaultAcmeOptions(
email: string,
certificateStore: string,
useProduction: boolean = false
): AcmeOptions {
): IAcmeOptions {
return {
accountEmail: email,
enabled: true,

View File

@ -1,12 +1,12 @@
import * as plugins from '../../plugins.js';
import type { AcmeOptions, CertificateData } from '../models/certificate-types.js';
import type { IAcmeOptions, ICertificateData } from '../models/certificate-types.js';
import { CertificateEvents } from '../events/certificate-events.js';
/**
* Manages ACME challenges and certificate validation
*/
export class AcmeChallengeHandler extends plugins.EventEmitter {
private options: AcmeOptions;
private options: IAcmeOptions;
private client: any; // ACME client from plugins
private pendingChallenges: Map<string, any>;
@ -14,7 +14,7 @@ export class AcmeChallengeHandler extends plugins.EventEmitter {
* Creates a new ACME challenge handler
* @param options ACME configuration options
*/
constructor(options: AcmeOptions) {
constructor(options: IAcmeOptions) {
super();
this.options = options;
this.pendingChallenges = new Map();

View File

@ -25,8 +25,8 @@ export * from './storage/file-storage.js';
// Convenience function to create a certificate provisioner with common settings
import { CertProvisioner } from './providers/cert-provisioner.js';
import { buildPort80Handler } from './acme/acme-factory.js';
import type { AcmeOptions, DomainForwardConfig } from './models/certificate-types.js';
import type { DomainConfig } from '../forwarding/config/domain-config.js';
import type { IAcmeOptions, IDomainForwardConfig } from './models/certificate-types.js';
import type { IDomainConfig } from '../forwarding/config/domain-config.js';
/**
* Creates a complete certificate provisioning system with default settings
@ -37,8 +37,8 @@ import type { DomainConfig } from '../forwarding/config/domain-config.js';
* @returns Configured CertProvisioner
*/
export function createCertificateProvisioner(
domainConfigs: DomainConfig[],
acmeOptions: AcmeOptions,
domainConfigs: IDomainConfig[],
acmeOptions: IAcmeOptions,
networkProxyBridge: any, // Placeholder until NetworkProxyBridge is migrated
certProvider?: any // Placeholder until cert provider type is properly defined
): CertProvisioner {

View File

@ -84,4 +84,5 @@ export interface IAcmeOptions {
certificateStore?: string; // Directory to store certificates
skipConfiguredCerts?: boolean; // Skip domains with existing certificates
domainForwards?: IDomainForwardConfig[]; // Domain-specific forwarding configs
}
}

View File

@ -1,34 +1,34 @@
import * as plugins from '../../plugins.js';
import type { DomainConfig } from '../../forwarding/config/domain-config.js';
import type { CertificateData, DomainForwardConfig, DomainOptions } from '../models/certificate-types.js';
import type { IDomainConfig } from '../../forwarding/config/domain-config.js';
import type { ICertificateData, IDomainForwardConfig, IDomainOptions } from '../models/certificate-types.js';
import { Port80HandlerEvents, CertProvisionerEvents } from '../events/certificate-events.js';
import { Port80Handler } from '../../http/port80/port80-handler.js';
// We need to define this interface until we migrate NetworkProxyBridge
interface NetworkProxyBridge {
applyExternalCertificate(certData: CertificateData): void;
interface INetworkProxyBridge {
applyExternalCertificate(certData: ICertificateData): void;
}
// This will be imported after NetworkProxyBridge is migrated
// import type { NetworkProxyBridge } from '../../proxies/smart-proxy/network-proxy-bridge.js';
// For backward compatibility
export type ISmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
export type TSmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
/**
* Type for static certificate provisioning
*/
export type CertProvisionObject = plugins.tsclass.network.ICert | 'http01' | 'dns01';
export type TCertProvisionObject = plugins.tsclass.network.ICert | 'http01' | 'dns01';
/**
* CertProvisioner manages certificate provisioning and renewal workflows,
* unifying static certificates and HTTP-01 challenges via Port80Handler.
*/
export class CertProvisioner extends plugins.EventEmitter {
private domainConfigs: DomainConfig[];
private domainConfigs: IDomainConfig[];
private port80Handler: Port80Handler;
private networkProxyBridge: NetworkProxyBridge;
private certProvisionFunction?: (domain: string) => Promise<CertProvisionObject>;
private forwardConfigs: DomainForwardConfig[];
private networkProxyBridge: INetworkProxyBridge;
private certProvisionFunction?: (domain: string) => Promise<TCertProvisionObject>;
private forwardConfigs: IDomainForwardConfig[];
private renewThresholdDays: number;
private renewCheckIntervalHours: number;
private autoRenew: boolean;
@ -47,14 +47,14 @@ export class CertProvisioner extends plugins.EventEmitter {
* @param forwardConfigs Domain forwarding configurations for ACME challenges
*/
constructor(
domainConfigs: DomainConfig[],
domainConfigs: IDomainConfig[],
port80Handler: Port80Handler,
networkProxyBridge: NetworkProxyBridge,
certProvider?: (domain: string) => Promise<CertProvisionObject>,
networkProxyBridge: INetworkProxyBridge,
certProvider?: (domain: string) => Promise<TCertProvisionObject>,
renewThresholdDays: number = 30,
renewCheckIntervalHours: number = 24,
autoRenew: boolean = true,
forwardConfigs: DomainForwardConfig[] = []
forwardConfigs: IDomainForwardConfig[] = []
) {
super();
this.domainConfigs = domainConfigs;
@ -92,11 +92,11 @@ export class CertProvisioner extends plugins.EventEmitter {
*/
private setupEventSubscriptions(): void {
// We need to reimplement subscribeToPort80Handler here
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (data: CertificateData) => {
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (data: ICertificateData) => {
this.emit(CertProvisionerEvents.CERTIFICATE_ISSUED, { ...data, source: 'http01', isRenewal: false });
});
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (data: CertificateData) => {
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (data: ICertificateData) => {
this.emit(CertProvisionerEvents.CERTIFICATE_RENEWED, { ...data, source: 'http01', isRenewal: true });
});
@ -110,7 +110,7 @@ export class CertProvisioner extends plugins.EventEmitter {
*/
private setupForwardingConfigs(): void {
for (const config of this.forwardConfigs) {
const domainOptions: DomainOptions = {
const domainOptions: IDomainOptions = {
domainName: config.domain,
sslRedirect: config.sslRedirect || false,
acmeMaintenance: false,
@ -138,7 +138,7 @@ export class CertProvisioner extends plugins.EventEmitter {
*/
private async provisionDomain(domain: string): Promise<void> {
const isWildcard = domain.includes('*');
let provision: CertProvisionObject = 'http01';
let provision: TCertProvisionObject = 'http01';
// Try to get a certificate from the provision function
if (this.certProvisionFunction) {
@ -174,7 +174,7 @@ export class CertProvisioner extends plugins.EventEmitter {
// Static certificate (e.g., DNS-01 provisioned or user-provided)
this.provisionMap.set(domain, 'static');
const certObj = provision as plugins.tsclass.network.ICert;
const certData: CertificateData = {
const certData: ICertificateData = {
domain: certObj.domainName,
certificate: certObj.publicKey,
privateKey: certObj.privateKey,
@ -235,7 +235,7 @@ export class CertProvisioner extends plugins.EventEmitter {
if (provision !== 'http01' && provision !== 'dns01') {
const certObj = provision as plugins.tsclass.network.ICert;
const certData: CertificateData = {
const certData: ICertificateData = {
domain: certObj.domainName,
certificate: certObj.publicKey,
privateKey: certObj.privateKey,
@ -267,7 +267,7 @@ export class CertProvisioner extends plugins.EventEmitter {
const isWildcard = domain.includes('*');
// Determine provisioning method
let provision: CertProvisionObject = 'http01';
let provision: TCertProvisionObject = 'http01';
if (this.certProvisionFunction) {
provision = await this.certProvisionFunction(domain);
@ -288,7 +288,7 @@ export class CertProvisioner extends plugins.EventEmitter {
} else {
// Static certificate (e.g., DNS-01 provisioned) supports wildcards
const certObj = provision as plugins.tsclass.network.ICert;
const certData: CertificateData = {
const certData: ICertificateData = {
domain: certObj.domainName,
certificate: certObj.publicKey,
privateKey: certObj.privateKey,
@ -311,7 +311,7 @@ export class CertProvisioner extends plugins.EventEmitter {
sslRedirect?: boolean;
acmeMaintenance?: boolean;
}): Promise<void> {
const domainOptions: DomainOptions = {
const domainOptions: IDomainOptions = {
domainName: domain,
sslRedirect: options?.sslRedirect || true,
acmeMaintenance: options?.acmeMaintenance || true

View File

@ -1,7 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
import * as plugins from '../../plugins.js';
import type { CertificateData, Certificates } from '../models/certificate-types.js';
import type { ICertificateData, ICertificates } from '../models/certificate-types.js';
import { ensureCertificateDirectory } from '../utils/certificate-helpers.js';
/**
@ -21,10 +21,10 @@ export class FileStorage {
/**
* Save a certificate to the file system
* @param domain Domain name
* @param domain Domain name
* @param certData Certificate data to save
*/
public async saveCertificate(domain: string, certData: CertificateData): Promise<void> {
public async saveCertificate(domain: string, certData: ICertificateData): Promise<void> {
const sanitizedDomain = this.sanitizeDomain(domain);
const certDir = path.join(this.storageDir, sanitizedDomain);
ensureCertificateDirectory(certDir);
@ -57,7 +57,7 @@ export class FileStorage {
* @param domain Domain name
* @returns Certificate data if found, null otherwise
*/
public async loadCertificate(domain: string): Promise<CertificateData | null> {
public async loadCertificate(domain: string): Promise<ICertificateData | null> {
const sanitizedDomain = this.sanitizeDomain(domain);
const certDir = path.join(this.storageDir, sanitizedDomain);

View File

@ -1,7 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
import type { Certificates } from '../models/certificate-types.js';
import type { ICertificates } from '../models/certificate-types.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
@ -9,7 +9,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
* Loads the default SSL certificates from the assets directory
* @returns The certificate key pair
*/
export function loadDefaultCertificates(): Certificates {
export function loadDefaultCertificates(): ICertificates {
try {
// Need to adjust path from /ts/certificate/utils to /assets/certs
const certPath = path.join(__dirname, '..', '..', '..', 'assets', 'certs');

View File

@ -6,7 +6,7 @@ import type {
} from './types.js';
import type {
ForwardConfig as IForwardConfig
IForwardConfig
} from '../forwarding/config/forwarding-types.js';
/**

View File

@ -1,14 +1,14 @@
import type { ForwardConfig } from './forwarding-types.js';
import type { IForwardConfig } from './forwarding-types.js';
/**
* Domain configuration with unified forwarding configuration
*/
export interface DomainConfig {
export interface IDomainConfig {
// Core properties - domain patterns
domains: string[];
// Unified forwarding configuration
forwarding: ForwardConfig;
forwarding: IForwardConfig;
}
/**
@ -16,8 +16,8 @@ export interface DomainConfig {
*/
export function createDomainConfig(
domains: string | string[],
forwarding: ForwardConfig
): DomainConfig {
forwarding: IForwardConfig
): IDomainConfig {
// Normalize domains to an array
const domainArray = Array.isArray(domains) ? domains : [domains];
@ -25,7 +25,4 @@ export function createDomainConfig(
domains: domainArray,
forwarding
};
}
// Backwards compatibility
export interface IDomainConfig extends DomainConfig {}
}

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js';
import type { DomainConfig } from './domain-config.js';
import type { IDomainConfig } from './domain-config.js';
import { ForwardingHandler } from '../handlers/base-handler.js';
import { ForwardingHandlerEvents } from './forwarding-types.js';
import { ForwardingHandlerFactory } from '../factory/forwarding-factory.js';
@ -21,14 +21,14 @@ export enum DomainManagerEvents {
* Manages domains and their forwarding handlers
*/
export class DomainManager extends plugins.EventEmitter {
private domainConfigs: DomainConfig[] = [];
private domainConfigs: IDomainConfig[] = [];
private domainHandlers: Map<string, ForwardingHandler> = new Map();
/**
* Create a new DomainManager
* @param initialDomains Optional initial domain configurations
*/
constructor(initialDomains?: DomainConfig[]) {
constructor(initialDomains?: IDomainConfig[]) {
super();
if (initialDomains) {
@ -40,7 +40,7 @@ export class DomainManager extends plugins.EventEmitter {
* Set or replace all domain configurations
* @param configs Array of domain configurations
*/
public async setDomainConfigs(configs: DomainConfig[]): Promise<void> {
public async setDomainConfigs(configs: IDomainConfig[]): Promise<void> {
// Clear existing handlers
this.domainHandlers.clear();
@ -57,7 +57,7 @@ export class DomainManager extends plugins.EventEmitter {
* Add a new domain configuration
* @param config The domain configuration to add
*/
public async addDomainConfig(config: DomainConfig): Promise<void> {
public async addDomainConfig(config: IDomainConfig): Promise<void> {
// Check if any of these domains already exist
for (const domain of config.domains) {
if (this.domainHandlers.has(domain)) {
@ -193,7 +193,7 @@ export class DomainManager extends plugins.EventEmitter {
* Create handlers for a domain configuration
* @param config The domain configuration
*/
private async createHandlersForDomain(config: DomainConfig): Promise<void> {
private async createHandlersForDomain(config: IDomainConfig): Promise<void> {
try {
// Create a handler for this forwarding configuration
const handler = ForwardingHandlerFactory.createHandler(config.forwarding);
@ -221,7 +221,7 @@ export class DomainManager extends plugins.EventEmitter {
* @param handler The handler
* @param config The domain configuration for this handler
*/
private setupHandlerEvents(handler: ForwardingHandler, config: DomainConfig): void {
private setupHandlerEvents(handler: ForwardingHandler, config: IDomainConfig): void {
// Forward relevant events
handler.on(ForwardingHandlerEvents.CERTIFICATE_NEEDED, (data) => {
this.emit(DomainManagerEvents.CERTIFICATE_NEEDED, {
@ -277,7 +277,7 @@ export class DomainManager extends plugins.EventEmitter {
* Get all domain configurations
* @returns Array of domain configurations
*/
public getDomainConfigs(): DomainConfig[] {
public getDomainConfigs(): IDomainConfig[] {
return [...this.domainConfigs];
}
}

View File

@ -3,7 +3,7 @@ import type * as plugins from '../../plugins.js';
/**
* The primary forwarding types supported by SmartProxy
*/
export type ForwardingType =
export type TForwardingType =
| 'http-only' // HTTP forwarding only (no HTTPS)
| 'https-passthrough' // Pass-through TLS traffic (SNI forwarding)
| 'https-terminate-to-http' // Terminate TLS and forward to HTTP backend
@ -12,7 +12,7 @@ export type ForwardingType =
/**
* Target configuration for forwarding
*/
export interface TargetConfig {
export interface ITargetConfig {
host: string | string[]; // Support single host or round-robin
port: number;
}
@ -20,7 +20,7 @@ export interface TargetConfig {
/**
* HTTP-specific options for forwarding
*/
export interface HttpOptions {
export interface IHttpOptions {
enabled?: boolean; // Whether HTTP is enabled
redirectToHttps?: boolean; // Redirect HTTP to HTTPS
headers?: Record<string, string>; // Custom headers for HTTP responses
@ -29,7 +29,7 @@ export interface HttpOptions {
/**
* HTTPS-specific options for forwarding
*/
export interface HttpsOptions {
export interface IHttpsOptions {
customCert?: { // Use custom cert instead of auto-provisioned
key: string;
cert: string;
@ -40,8 +40,8 @@ export interface HttpsOptions {
/**
* ACME certificate handling options
*/
export interface AcmeForwardingOptions {
enabled?: boolean; // Enable ACME certificate provisioning
export interface IAcmeForwardingOptions {
enabled?: boolean; // Enable ACME certificate provisioning
maintenance?: boolean; // Auto-renew certificates
production?: boolean; // Use production ACME servers
forwardChallenges?: { // Forward ACME challenges
@ -54,7 +54,7 @@ export interface AcmeForwardingOptions {
/**
* Security options for forwarding
*/
export interface SecurityOptions {
export interface ISecurityOptions {
allowedIps?: string[]; // IPs allowed to connect
blockedIps?: string[]; // IPs blocked from connecting
maxConnections?: number; // Max simultaneous connections
@ -63,7 +63,7 @@ export interface SecurityOptions {
/**
* Advanced options for forwarding
*/
export interface AdvancedOptions {
export interface IAdvancedOptions {
portRanges?: Array<{ from: number; to: number }>; // Allowed port ranges
networkProxyPort?: number; // Custom NetworkProxy port if using terminate mode
keepAlive?: boolean; // Enable TCP keepalive
@ -74,21 +74,21 @@ export interface AdvancedOptions {
/**
* Unified forwarding configuration interface
*/
export interface ForwardConfig {
export interface IForwardConfig {
// Define the primary forwarding type - use-case driven approach
type: ForwardingType;
type: TForwardingType;
// Target configuration
target: TargetConfig;
target: ITargetConfig;
// Protocol options
http?: HttpOptions;
https?: HttpsOptions;
acme?: AcmeForwardingOptions;
http?: IHttpOptions;
https?: IHttpsOptions;
acme?: IAcmeForwardingOptions;
// Security and advanced options
security?: SecurityOptions;
advanced?: AdvancedOptions;
security?: ISecurityOptions;
advanced?: IAdvancedOptions;
}
/**
@ -118,8 +118,8 @@ export interface IForwardingHandler extends plugins.EventEmitter {
* Helper function types for common forwarding patterns
*/
export const httpOnly = (
partialConfig: Partial<ForwardConfig> & Pick<ForwardConfig, 'target'>
): ForwardConfig => ({
partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
): IForwardConfig => ({
type: 'http-only',
target: partialConfig.target,
http: { enabled: true, ...(partialConfig.http || {}) },
@ -128,8 +128,8 @@ export const httpOnly = (
});
export const tlsTerminateToHttp = (
partialConfig: Partial<ForwardConfig> & Pick<ForwardConfig, 'target'>
): ForwardConfig => ({
partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
): IForwardConfig => ({
type: 'https-terminate-to-http',
target: partialConfig.target,
https: { ...(partialConfig.https || {}) },
@ -140,8 +140,8 @@ export const tlsTerminateToHttp = (
});
export const tlsTerminateToHttps = (
partialConfig: Partial<ForwardConfig> & Pick<ForwardConfig, 'target'>
): ForwardConfig => ({
partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
): IForwardConfig => ({
type: 'https-terminate-to-https',
target: partialConfig.target,
https: { ...(partialConfig.https || {}) },
@ -152,20 +152,11 @@ export const tlsTerminateToHttps = (
});
export const httpsPassthrough = (
partialConfig: Partial<ForwardConfig> & Pick<ForwardConfig, 'target'>
): ForwardConfig => ({
partialConfig: Partial<IForwardConfig> & Pick<IForwardConfig, 'target'>
): IForwardConfig => ({
type: 'https-passthrough',
target: partialConfig.target,
https: { forwardSni: true, ...(partialConfig.https || {}) },
...(partialConfig.security ? { security: partialConfig.security } : {}),
...(partialConfig.advanced ? { advanced: partialConfig.advanced } : {})
});
// Backwards compatibility interfaces with 'I' prefix
export interface ITargetConfig extends TargetConfig {}
export interface IHttpOptions extends HttpOptions {}
export interface IHttpsOptions extends HttpsOptions {}
export interface IAcmeForwardingOptions extends AcmeForwardingOptions {}
export interface ISecurityOptions extends SecurityOptions {}
export interface IAdvancedOptions extends AdvancedOptions {}
export interface IForwardConfig extends ForwardConfig {}
});

View File

@ -1,5 +1,5 @@
import type { ForwardConfig } from '../config/forwarding-types.js';
import type { ForwardingHandler } from '../handlers/base-handler.js';
import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandler } from '../handlers/base-handler.js';
import { HttpForwardingHandler } from '../handlers/http-handler.js';
import { HttpsPassthroughHandler } from '../handlers/https-passthrough-handler.js';
import { HttpsTerminateToHttpHandler } from '../handlers/https-terminate-to-http-handler.js';
@ -14,35 +14,35 @@ export class ForwardingHandlerFactory {
* @param config The forwarding configuration
* @returns The appropriate forwarding handler
*/
public static createHandler(config: ForwardConfig): ForwardingHandler {
public static createHandler(config: IForwardConfig): ForwardingHandler {
// Create the appropriate handler based on the forwarding type
switch (config.type) {
case 'http-only':
return new HttpForwardingHandler(config);
case 'https-passthrough':
return new HttpsPassthroughHandler(config);
case 'https-terminate-to-http':
return new HttpsTerminateToHttpHandler(config);
case 'https-terminate-to-https':
return new HttpsTerminateToHttpsHandler(config);
default:
// Type system should prevent this, but just in case:
throw new Error(`Unknown forwarding type: ${(config as any).type}`);
}
}
/**
* Apply default values to a forwarding configuration based on its type
* @param config The original forwarding configuration
* @returns A configuration with defaults applied
*/
public static applyDefaults(config: ForwardConfig): ForwardConfig {
public static applyDefaults(config: IForwardConfig): IForwardConfig {
// Create a deep copy of the configuration
const result: ForwardConfig = JSON.parse(JSON.stringify(config));
const result: IForwardConfig = JSON.parse(JSON.stringify(config));
// Apply defaults based on forwarding type
switch (config.type) {
@ -112,7 +112,7 @@ export class ForwardingHandlerFactory {
* @param config The configuration to validate
* @throws Error if the configuration is invalid
*/
public static validateConfig(config: ForwardConfig): void {
public static validateConfig(config: IForwardConfig): void {
// Validate common properties
if (!config.target) {
throw new Error('Forwarding configuration must include a target');

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js';
import type {
ForwardConfig,
IForwardConfig,
IForwardingHandler
} from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
@ -13,7 +13,7 @@ export abstract class ForwardingHandler extends plugins.EventEmitter implements
* Create a new ForwardingHandler
* @param config The forwarding configuration
*/
constructor(protected config: ForwardConfig) {
constructor(protected config: IForwardConfig) {
super();
}

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js';
import type { ForwardConfig } from '../config/forwarding-types.js';
import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
/**
@ -11,14 +11,23 @@ export class HttpForwardingHandler extends ForwardingHandler {
* Create a new HTTP forwarding handler
* @param config The forwarding configuration
*/
constructor(config: ForwardConfig) {
constructor(config: IForwardConfig) {
super(config);
// Validate that this is an HTTP-only configuration
if (config.type !== 'http-only') {
throw new Error(`Invalid configuration type for HttpForwardingHandler: ${config.type}`);
}
}
/**
* Initialize the handler
* HTTP handler doesn't need special initialization
*/
public async initialize(): Promise<void> {
// Basic initialization from parent class
await super.initialize();
}
/**
* Handle a raw socket connection

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js';
import type { ForwardConfig } from '../config/forwarding-types.js';
import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
/**
@ -11,14 +11,23 @@ export class HttpsPassthroughHandler extends ForwardingHandler {
* Create a new HTTPS passthrough handler
* @param config The forwarding configuration
*/
constructor(config: ForwardConfig) {
constructor(config: IForwardConfig) {
super(config);
// Validate that this is an HTTPS passthrough configuration
if (config.type !== 'https-passthrough') {
throw new Error(`Invalid configuration type for HttpsPassthroughHandler: ${config.type}`);
}
}
/**
* Initialize the handler
* HTTPS passthrough handler doesn't need special initialization
*/
public async initialize(): Promise<void> {
// Basic initialization from parent class
await super.initialize();
}
/**
* Handle a TLS/SSL socket connection by forwarding it without termination

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js';
import type { ForwardConfig } from '../config/forwarding-types.js';
import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
/**
@ -14,7 +14,7 @@ export class HttpsTerminateToHttpHandler extends ForwardingHandler {
* Create a new HTTPS termination with HTTP backend handler
* @param config The forwarding configuration
*/
constructor(config: ForwardConfig) {
constructor(config: IForwardConfig) {
super(config);
// Validate that this is an HTTPS terminate to HTTP configuration

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js';
import { ForwardingHandler } from './base-handler.js';
import type { ForwardConfig } from '../config/forwarding-types.js';
import type { IForwardConfig } from '../config/forwarding-types.js';
import { ForwardingHandlerEvents } from '../config/forwarding-types.js';
/**
@ -13,7 +13,7 @@ export class HttpsTerminateToHttpsHandler extends ForwardingHandler {
* Create a new HTTPS termination with HTTPS backend handler
* @param config The forwarding configuration
*/
constructor(config: ForwardConfig) {
constructor(config: IForwardConfig) {
super(config);
// Validate that this is an HTTPS terminate to HTTPS configuration

View File

@ -1,8 +1,8 @@
import * as plugins from '../../plugins.js';
import type {
ForwardConfig,
DomainOptions,
AcmeOptions
import type {
IForwardConfig,
IDomainOptions,
IAcmeOptions
} from '../../certificate/models/certificate-types.js';
/**
@ -35,8 +35,8 @@ export enum HttpStatus {
/**
* Represents a domain configuration with certificate status information
*/
export interface DomainCertificate {
options: DomainOptions;
export interface IDomainCertificate {
options: IDomainOptions;
certObtained: boolean;
obtainingInProgress: boolean;
certificate?: string;
@ -82,7 +82,7 @@ export class ServerError extends HttpError {
/**
* Redirect configuration for HTTP requests
*/
export interface RedirectConfig {
export interface IRedirectConfig {
source: string; // Source path or pattern
destination: string; // Destination URL
type: HttpStatus; // Redirect status code
@ -92,7 +92,7 @@ export interface RedirectConfig {
/**
* HTTP router configuration
*/
export interface RouterConfig {
export interface IRouterConfig {
routes: Array<{
path: string;
handler: (req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse) => void;
@ -102,5 +102,4 @@ export interface RouterConfig {
// Backward compatibility interfaces
export { HttpError as Port80HandlerError };
export { CertificateError as CertError };
export type IDomainCertificate = DomainCertificate;
export { CertificateError as CertError };

View File

@ -7,7 +7,7 @@ import * as plugins from '../../plugins.js';
/**
* Structure for SmartAcme certificate result
*/
export interface SmartAcmeCert {
export interface ISmartAcmeCert {
id?: string;
domainName: string;
created?: number | Date | string;
@ -20,7 +20,7 @@ export interface SmartAcmeCert {
/**
* Structure for SmartAcme options
*/
export interface SmartAcmeOptions {
export interface ISmartAcmeOptions {
accountEmail: string;
certManager: ICertManager;
environment: 'production' | 'integration';
@ -39,8 +39,8 @@ export interface SmartAcmeOptions {
*/
export interface ICertManager {
init(): Promise<void>;
get(domainName: string): Promise<SmartAcmeCert | null>;
put(cert: SmartAcmeCert): Promise<SmartAcmeCert>;
get(domainName: string): Promise<ISmartAcmeCert | null>;
put(cert: ISmartAcmeCert): Promise<ISmartAcmeCert>;
delete(domainName: string): Promise<void>;
close?(): Promise<void>;
}
@ -59,7 +59,7 @@ export interface IChallengeHandler<T> {
/**
* HTTP-01 challenge type
*/
export interface Http01Challenge {
export interface IHttp01Challenge {
type: string; // 'http-01'
token: string;
keyAuthorization: string;
@ -69,17 +69,17 @@ export interface Http01Challenge {
/**
* HTTP-01 Memory Handler Interface
*/
export interface Http01MemoryHandler extends IChallengeHandler<Http01Challenge> {
export interface IHttp01MemoryHandler extends IChallengeHandler<IHttp01Challenge> {
handleRequest(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse, next?: () => void): void;
}
/**
* SmartAcme main class interface
*/
export interface SmartAcme {
export interface ISmartAcme {
start(): Promise<void>;
stop(): Promise<void>;
getCertificateForDomain(domain: string): Promise<SmartAcmeCert>;
getCertificateForDomain(domain: string): Promise<ISmartAcmeCert>;
on?(event: string, listener: (data: any) => void): void;
eventEmitter?: plugins.EventEmitter;
}

View File

@ -4,15 +4,15 @@ import {
CertificateEvents
} from '../../certificate/events/certificate-events.js';
import type {
CertificateData,
CertificateFailure,
CertificateExpiring
ICertificateData,
ICertificateFailure,
ICertificateExpiring
} from '../../certificate/models/certificate-types.js';
import type {
SmartAcme,
SmartAcmeCert,
SmartAcmeOptions,
Http01MemoryHandler
ISmartAcme,
ISmartAcmeCert,
ISmartAcmeOptions,
IHttp01MemoryHandler
} from './acme-interfaces.js';
/**
@ -20,8 +20,8 @@ import type {
* It acts as a bridge between the HTTP server and the ACME challenge verification process
*/
export class ChallengeResponder extends plugins.EventEmitter {
private smartAcme: SmartAcme | null = null;
private http01Handler: Http01MemoryHandler | null = null;
private smartAcme: ISmartAcme | null = null;
private http01Handler: IHttp01MemoryHandler | null = null;
/**
* Creates a new challenge responder
@ -95,7 +95,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
emitter.on('certificate', (data: any) => {
const isRenewal = !!data.isRenewal;
const certData: CertificateData = {
const certData: ICertificateData = {
domain: data.domainName || data.domain,
certificate: data.publicKey || data.cert,
privateKey: data.privateKey || data.key,
@ -114,7 +114,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
// Forward error events
emitter.on('error', (error: any) => {
const domain = error.domainName || error.domain || 'unknown';
const failureData: CertificateFailure = {
const failureData: ICertificateFailure = {
domain,
error: error.message || String(error),
isRenewal: !!error.isRenewal
@ -171,7 +171,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
* @param domain Domain name to request a certificate for
* @param isRenewal Whether this is a renewal request
*/
public async requestCertificate(domain: string, isRenewal: boolean = false): Promise<CertificateData> {
public async requestCertificate(domain: string, isRenewal: boolean = false): Promise<ICertificateData> {
if (!this.smartAcme) {
throw new Error('ACME client not initialized');
}
@ -181,7 +181,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
const certObj = await this.smartAcme.getCertificateForDomain(domain);
// Convert the certificate object to our CertificateData format
const certData: CertificateData = {
const certData: ICertificateData = {
domain,
certificate: certObj.publicKey,
privateKey: certObj.privateKey,
@ -193,7 +193,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
return certData;
} catch (error) {
// Create failure object
const failure: CertificateFailure = {
const failure: ICertificateFailure = {
domain,
error: error instanceof Error ? error.message : String(error),
isRenewal
@ -217,7 +217,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
*/
public checkCertificateExpiry(
domain: string,
certificate: CertificateData,
certificate: ICertificateData,
thresholdDays: number = 30
): void {
if (!certificate.expiryDate) return;
@ -227,7 +227,7 @@ export class ChallengeResponder extends plugins.EventEmitter {
const daysDifference = Math.floor((expiryDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
if (daysDifference <= thresholdDays) {
const expiryInfo: CertificateExpiring = {
const expiryInfo: ICertificateExpiring = {
domain,
expiryDate,
daysRemaining: daysDifference

View File

@ -2,12 +2,12 @@ import * as plugins from '../../plugins.js';
import { IncomingMessage, ServerResponse } from 'http';
import { CertificateEvents } from '../../certificate/events/certificate-events.js';
import type {
ForwardConfig,
DomainOptions,
CertificateData,
CertificateFailure,
CertificateExpiring,
AcmeOptions
IForwardConfig,
IDomainOptions,
ICertificateData,
ICertificateFailure,
ICertificateExpiring,
IAcmeOptions
} from '../../certificate/models/certificate-types.js';
import {
HttpEvents,
@ -16,7 +16,7 @@ import {
CertificateError,
ServerError,
} from '../models/http-types.js';
import type { DomainCertificate } from '../models/http-types.js';
import type { IDomainCertificate } from '../models/http-types.js';
import { ChallengeResponder } from './challenge-responder.js';
// Re-export for backward compatibility
@ -40,21 +40,21 @@ export const Port80HandlerEvents = CertificateEvents;
* Now with glob pattern support for domain matching
*/
export class Port80Handler extends plugins.EventEmitter {
private domainCertificates: Map<string, DomainCertificate>;
private domainCertificates: Map<string, IDomainCertificate>;
private challengeResponder: ChallengeResponder | null = null;
private server: plugins.http.Server | null = null;
// Renewal scheduling is handled externally by SmartProxy
private isShuttingDown: boolean = false;
private options: Required<AcmeOptions>;
private options: Required<IAcmeOptions>;
/**
* Creates a new Port80Handler
* @param options Configuration options
*/
constructor(options: AcmeOptions = {}) {
constructor(options: IAcmeOptions = {}) {
super();
this.domainCertificates = new Map<string, DomainCertificate>();
this.domainCertificates = new Map<string, IDomainCertificate>();
// Default options
this.options = {
@ -80,19 +80,19 @@ export class Port80Handler extends plugins.EventEmitter {
);
// Forward certificate events from the challenge responder
this.challengeResponder.on(CertificateEvents.CERTIFICATE_ISSUED, (data: CertificateData) => {
this.challengeResponder.on(CertificateEvents.CERTIFICATE_ISSUED, (data: ICertificateData) => {
this.emit(CertificateEvents.CERTIFICATE_ISSUED, data);
});
this.challengeResponder.on(CertificateEvents.CERTIFICATE_RENEWED, (data: CertificateData) => {
this.challengeResponder.on(CertificateEvents.CERTIFICATE_RENEWED, (data: ICertificateData) => {
this.emit(CertificateEvents.CERTIFICATE_RENEWED, data);
});
this.challengeResponder.on(CertificateEvents.CERTIFICATE_FAILED, (error: CertificateFailure) => {
this.challengeResponder.on(CertificateEvents.CERTIFICATE_FAILED, (error: ICertificateFailure) => {
this.emit(CertificateEvents.CERTIFICATE_FAILED, error);
});
this.challengeResponder.on(CertificateEvents.CERTIFICATE_EXPIRING, (expiry: CertificateExpiring) => {
this.challengeResponder.on(CertificateEvents.CERTIFICATE_EXPIRING, (expiry: ICertificateExpiring) => {
this.emit(CertificateEvents.CERTIFICATE_EXPIRING, expiry);
});
}
@ -198,7 +198,7 @@ export class Port80Handler extends plugins.EventEmitter {
* Adds a domain with configuration options
* @param options Domain configuration options
*/
public addDomain(options: DomainOptions): void {
public addDomain(options: IDomainOptions): void {
if (!options.domainName || typeof options.domainName !== 'string') {
throw new HttpError('Invalid domain name');
}
@ -247,7 +247,7 @@ export class Port80Handler extends plugins.EventEmitter {
* Gets the certificate for a domain if it exists
* @param domain The domain to get the certificate for
*/
public getCertificate(domain: string): CertificateData | null {
public getCertificate(domain: string): ICertificateData | null {
// Can't get certificates for glob patterns
if (this.isGlobPattern(domain)) {
return null;
@ -283,7 +283,7 @@ export class Port80Handler extends plugins.EventEmitter {
* @param requestDomain The actual domain from the request
* @returns The domain info or null if not found
*/
private getDomainInfoForRequest(requestDomain: string): { domainInfo: DomainCertificate, pattern: string } | null {
private getDomainInfoForRequest(requestDomain: string): { domainInfo: IDomainCertificate, pattern: string } | null {
// Try direct match first
if (this.domainCertificates.has(requestDomain)) {
return {
@ -459,7 +459,7 @@ export class Port80Handler extends plugins.EventEmitter {
private forwardRequest(
req: plugins.http.IncomingMessage,
res: plugins.http.ServerResponse,
target: ForwardConfig,
target: IForwardConfig,
requestType: string
): void {
const options = {
@ -612,7 +612,7 @@ export class Port80Handler extends plugins.EventEmitter {
* @param eventType The event type to emit
* @param data The certificate data
*/
private emitCertificateEvent(eventType: CertificateEvents, data: CertificateData): void {
private emitCertificateEvent(eventType: CertificateEvents, data: ICertificateData): void {
this.emit(eventType, data);
}

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js';
import type { ReverseProxyConfig } from '../../proxies/network-proxy/models/types.js';
import type { IReverseProxyConfig } from '../../proxies/network-proxy/models/types.js';
/**
* Optional path pattern configuration that can be added to proxy configs
@ -15,7 +15,7 @@ export type IPathPatternConfig = PathPatternConfig;
* Interface for router result with additional metadata
*/
export interface RouterResult {
config: ReverseProxyConfig;
config: IReverseProxyConfig;
pathMatch?: string;
pathParams?: Record<string, string>;
pathRemainder?: string;
@ -41,11 +41,11 @@ export type IRouterResult = RouterResult;
*/
export class ProxyRouter {
// Store original configs for reference
private reverseProxyConfigs: ReverseProxyConfig[] = [];
private reverseProxyConfigs: IReverseProxyConfig[] = [];
// Default config to use when no match is found (optional)
private defaultConfig?: ReverseProxyConfig;
private defaultConfig?: IReverseProxyConfig;
// Store path patterns separately since they're not in the original interface
private pathPatterns: Map<ReverseProxyConfig, string> = new Map();
private pathPatterns: Map<IReverseProxyConfig, string> = new Map();
// Logger interface
private logger: {
error: (message: string, data?: any) => void;
@ -55,7 +55,7 @@ export class ProxyRouter {
};
constructor(
configs?: ReverseProxyConfig[],
configs?: IReverseProxyConfig[],
logger?: {
error: (message: string, data?: any) => void;
warn: (message: string, data?: any) => void;
@ -73,7 +73,7 @@ export class ProxyRouter {
* Sets a new set of reverse configs to be routed to
* @param reverseCandidatesArg Array of reverse proxy configurations
*/
public setNewProxyConfigs(reverseCandidatesArg: ReverseProxyConfig[]): void {
public setNewProxyConfigs(reverseCandidatesArg: IReverseProxyConfig[]): void {
this.reverseProxyConfigs = [...reverseCandidatesArg];
// Find default config if any (config with "*" as hostname)
@ -87,7 +87,7 @@ export class ProxyRouter {
* @param req The incoming HTTP request
* @returns The matching proxy config or undefined if no match found
*/
public routeReq(req: plugins.http.IncomingMessage): ReverseProxyConfig {
public routeReq(req: plugins.http.IncomingMessage): IReverseProxyConfig {
const result = this.routeReqWithDetails(req);
return result ? result.config : undefined;
}
@ -356,7 +356,7 @@ export class ProxyRouter {
* Gets all currently active proxy configurations
* @returns Array of all active configurations
*/
public getProxyConfigs(): ReverseProxyConfig[] {
public getProxyConfigs(): IReverseProxyConfig[] {
return [...this.reverseProxyConfigs];
}
@ -380,7 +380,7 @@ export class ProxyRouter {
* @param pathPattern Optional path pattern for route matching
*/
public addProxyConfig(
config: ReverseProxyConfig,
config: IReverseProxyConfig,
pathPattern?: string
): void {
this.reverseProxyConfigs.push(config);
@ -398,7 +398,7 @@ export class ProxyRouter {
* @returns Boolean indicating if the config was found and updated
*/
public setPathPattern(
config: ReverseProxyConfig,
config: IReverseProxyConfig,
pathPattern: string
): boolean {
const exists = this.reverseProxyConfigs.includes(config);

View File

@ -2,26 +2,26 @@ import * as plugins from '../../plugins.js';
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
import { type NetworkProxyOptions, type CertificateEntry, type Logger, createLogger } from './models/types.js';
import { type INetworkProxyOptions, type ICertificateEntry, type ILogger, createLogger } from './models/types.js';
import { Port80Handler } from '../../http/port80/port80-handler.js';
import { CertificateEvents } from '../../certificate/events/certificate-events.js';
import { buildPort80Handler } from '../../certificate/acme/acme-factory.js';
import { subscribeToPort80Handler } from '../../core/utils/event-utils.js';
import type { DomainOptions } from '../../certificate/models/certificate-types.js';
import type { IDomainOptions } from '../../certificate/models/certificate-types.js';
/**
* Manages SSL certificates for NetworkProxy including ACME integration
*/
export class CertificateManager {
private defaultCertificates: { key: string; cert: string };
private certificateCache: Map<string, CertificateEntry> = new Map();
private certificateCache: Map<string, ICertificateEntry> = new Map();
private port80Handler: Port80Handler | null = null;
private externalPort80Handler: boolean = false;
private certificateStoreDir: string;
private logger: Logger;
private logger: ILogger;
private httpsServer: plugins.https.Server | null = null;
constructor(private options: NetworkProxyOptions) {
constructor(private options: INetworkProxyOptions) {
this.certificateStoreDir = path.resolve(options.acme?.certificateStore || './certs');
this.logger = createLogger(options.logLevel || 'info');
@ -219,9 +219,9 @@ export class CertificateManager {
if (!certData) {
this.logger.info(`No certificate found for ${domain}, registering for issuance`);
// Register with new domain options format
const domainOptions: DomainOptions = {
const domainOptions: IDomainOptions = {
domainName: domain,
sslRedirect: true,
acmeMaintenance: true
@ -274,7 +274,7 @@ export class CertificateManager {
/**
* Gets a certificate for a domain
*/
public getCertificate(domain: string): CertificateEntry | undefined {
public getCertificate(domain: string): ICertificateEntry | undefined {
return this.certificateCache.get(domain);
}
@ -300,7 +300,7 @@ export class CertificateManager {
try {
// Use the new domain options format
const domainOptions: DomainOptions = {
const domainOptions: IDomainOptions = {
domainName: domain,
sslRedirect: true,
acmeMaintenance: true
@ -341,7 +341,7 @@ export class CertificateManager {
}
// Register the domain for certificate issuance with new domain options format
const domainOptions: DomainOptions = {
const domainOptions: IDomainOptions = {
domainName: domain,
sslRedirect: true,
acmeMaintenance: true

View File

@ -1,15 +1,15 @@
import * as plugins from '../../plugins.js';
import { type NetworkProxyOptions, type ConnectionEntry, type Logger, createLogger } from './models/types.js';
import { type INetworkProxyOptions, type IConnectionEntry, type ILogger, createLogger } from './models/types.js';
/**
* Manages a pool of backend connections for efficient reuse
*/
export class ConnectionPool {
private connectionPool: Map<string, Array<ConnectionEntry>> = new Map();
private connectionPool: Map<string, Array<IConnectionEntry>> = new Map();
private roundRobinPositions: Map<string, number> = new Map();
private logger: Logger;
private logger: ILogger;
constructor(private options: NetworkProxyOptions) {
constructor(private options: INetworkProxyOptions) {
this.logger = createLogger(options.logLevel || 'info');
}

View File

@ -1,10 +1,10 @@
import * as plugins from '../../../plugins.js';
import type { AcmeOptions } from '../../../certificate/models/certificate-types.js';
import type { IAcmeOptions } from '../../../certificate/models/certificate-types.js';
/**
* Configuration options for NetworkProxy
*/
export interface NetworkProxyOptions {
export interface INetworkProxyOptions {
port: number;
maxConnections?: number;
keepAliveTimeout?: number;
@ -16,22 +16,22 @@ export interface NetworkProxyOptions {
allowHeaders?: string;
maxAge?: number;
};
// Settings for SmartProxy integration
connectionPoolSize?: number; // Maximum connections to maintain in the pool to each backend
portProxyIntegration?: boolean; // Flag to indicate this proxy is used by SmartProxy
useExternalPort80Handler?: boolean; // Flag to indicate using external Port80Handler
// Protocol to use when proxying to backends: HTTP/1.x or HTTP/2
backendProtocol?: 'http1' | 'http2';
// ACME certificate management options
acme?: AcmeOptions;
acme?: IAcmeOptions;
}
/**
* Interface for a certificate entry in the cache
*/
export interface CertificateEntry {
export interface ICertificateEntry {
key: string;
cert: string;
expires?: Date;
@ -40,7 +40,7 @@ export interface CertificateEntry {
/**
* Interface for reverse proxy configuration
*/
export interface ReverseProxyConfig {
export interface IReverseProxyConfig {
destinationIps: string[];
destinationPorts: number[];
hostName: string;
@ -62,7 +62,7 @@ export interface ReverseProxyConfig {
/**
* Interface for connection tracking in the pool
*/
export interface ConnectionEntry {
export interface IConnectionEntry {
socket: plugins.net.Socket;
lastUsed: number;
isIdle: boolean;
@ -71,7 +71,7 @@ export interface ConnectionEntry {
/**
* WebSocket with heartbeat interface
*/
export interface WebSocketWithHeartbeat extends plugins.wsDefault {
export interface IWebSocketWithHeartbeat extends plugins.wsDefault {
lastPong: number;
isAlive: boolean;
}
@ -79,7 +79,7 @@ export interface WebSocketWithHeartbeat extends plugins.wsDefault {
/**
* Logger interface for consistent logging across components
*/
export interface Logger {
export interface ILogger {
debug(message: string, data?: any): void;
info(message: string, data?: any): void;
warn(message: string, data?: any): void;
@ -89,14 +89,14 @@ export interface Logger {
/**
* Creates a logger based on the specified log level
*/
export function createLogger(logLevel: string = 'info'): Logger {
export function createLogger(logLevel: string = 'info'): ILogger {
const logLevels = {
error: 0,
warn: 1,
info: 2,
debug: 3
};
return {
debug: (message: string, data?: any) => {
if (logLevels[logLevel] >= logLevels.debug) {
@ -119,12 +119,4 @@ export function createLogger(logLevel: string = 'info'): Logger {
}
}
};
}
// Backward compatibility interfaces
export interface INetworkProxyOptions extends NetworkProxyOptions {}
export interface ICertificateEntry extends CertificateEntry {}
export interface IReverseProxyConfig extends ReverseProxyConfig {}
export interface IConnectionEntry extends ConnectionEntry {}
export interface IWebSocketWithHeartbeat extends WebSocketWithHeartbeat {}
export interface ILogger extends Logger {}
}

View File

@ -3,9 +3,9 @@ import {
createLogger
} from './models/types.js';
import type {
NetworkProxyOptions,
Logger,
ReverseProxyConfig
INetworkProxyOptions,
ILogger,
IReverseProxyConfig
} from './models/types.js';
import { CertificateManager } from './certificate-manager.js';
import { ConnectionPool } from './connection-pool.js';
@ -24,8 +24,8 @@ export class NetworkProxy implements IMetricsTracker {
return {};
}
// Configuration
public options: NetworkProxyOptions;
public proxyConfigs: ReverseProxyConfig[] = [];
public options: INetworkProxyOptions;
public proxyConfigs: IReverseProxyConfig[] = [];
// Server instances (HTTP/2 with HTTP/1 fallback)
public httpsServer: any;
@ -54,12 +54,12 @@ export class NetworkProxy implements IMetricsTracker {
private connectionPoolCleanupInterval: NodeJS.Timeout;
// Logger
private logger: Logger;
private logger: ILogger;
/**
* Creates a new NetworkProxy instance
*/
constructor(optionsArg: NetworkProxyOptions) {
constructor(optionsArg: INetworkProxyOptions) {
// Set default options
this.options = {
port: optionsArg.port,
@ -328,7 +328,7 @@ export class NetworkProxy implements IMetricsTracker {
* Updates proxy configurations
*/
public async updateProxyConfigs(
proxyConfigsArg: ReverseProxyConfig[]
proxyConfigsArg: IReverseProxyConfig[]
): Promise<void> {
this.logger.info(`Updating proxy configurations (${proxyConfigsArg.length} configs)`);
@ -385,8 +385,8 @@ export class NetworkProxy implements IMetricsTracker {
allowedIPs?: string[];
}>,
sslKeyPair?: { key: string; cert: string }
): ReverseProxyConfig[] {
const proxyConfigs: ReverseProxyConfig[] = [];
): IReverseProxyConfig[] {
const proxyConfigs: IReverseProxyConfig[] = [];
// Use default certificates if not provided
const defaultCerts = this.certificateManager.getDefaultCertificates();
@ -478,7 +478,7 @@ export class NetworkProxy implements IMetricsTracker {
/**
* Gets all proxy configurations currently in use
*/
public getProxyConfigs(): ReverseProxyConfig[] {
public getProxyConfigs(): IReverseProxyConfig[] {
return [...this.proxyConfigs];
}
}

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js';
import { type NetworkProxyOptions, type Logger, createLogger, type ReverseProxyConfig } from './models/types.js';
import { type INetworkProxyOptions, type ILogger, createLogger, type IReverseProxyConfig } from './models/types.js';
import { ConnectionPool } from './connection-pool.js';
import { ProxyRouter } from '../../http/router/index.js';
@ -19,13 +19,13 @@ export type MetricsTracker = IMetricsTracker;
*/
export class RequestHandler {
private defaultHeaders: { [key: string]: string } = {};
private logger: Logger;
private logger: ILogger;
private metricsTracker: IMetricsTracker | null = null;
// HTTP/2 client sessions for backend proxying
private h2Sessions: Map<string, plugins.http2.ClientHttp2Session> = new Map();
constructor(
private options: NetworkProxyOptions,
private options: INetworkProxyOptions,
private connectionPool: ConnectionPool,
private router: ProxyRouter
) {
@ -137,7 +137,7 @@ export class RequestHandler {
this.applyDefaultHeaders(res);
// Determine routing configuration
let proxyConfig: ReverseProxyConfig | undefined;
let proxyConfig: IReverseProxyConfig | undefined;
try {
proxyConfig = this.router.routeReq(req);
} catch (err) {
@ -235,7 +235,7 @@ export class RequestHandler {
// Remove host header to avoid issues with virtual hosts on target server
// The host header should match the target server's expected hostname
if (options.headers && options.headers.host) {
if ((proxyConfig as ReverseProxyConfig).rewriteHostHeader) {
if ((proxyConfig as IReverseProxyConfig).rewriteHostHeader) {
options.headers.host = `${destination.host}:${destination.port}`;
}
}
@ -426,7 +426,7 @@ export class RequestHandler {
outboundHeaders[key] = value;
}
}
if (outboundHeaders.host && (proxyConfig as any).rewriteHostHeader) {
if (outboundHeaders.host && (proxyConfig as IReverseProxyConfig).rewriteHostHeader) {
outboundHeaders.host = `${destination.host}:${destination.port}`;
}
// Create HTTP/1 proxy request

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js';
import { type NetworkProxyOptions, type WebSocketWithHeartbeat, type Logger, createLogger, type ReverseProxyConfig } from './models/types.js';
import { type INetworkProxyOptions, type IWebSocketWithHeartbeat, type ILogger, createLogger, type IReverseProxyConfig } from './models/types.js';
import { ConnectionPool } from './connection-pool.js';
import { ProxyRouter } from '../../http/router/index.js';
@ -9,10 +9,10 @@ import { ProxyRouter } from '../../http/router/index.js';
export class WebSocketHandler {
private heartbeatInterval: NodeJS.Timeout | null = null;
private wsServer: plugins.ws.WebSocketServer | null = null;
private logger: Logger;
private logger: ILogger;
constructor(
private options: NetworkProxyOptions,
private options: INetworkProxyOptions,
private connectionPool: ConnectionPool,
private router: ProxyRouter
) {
@ -30,7 +30,7 @@ export class WebSocketHandler {
});
// Handle WebSocket connections
this.wsServer.on('connection', (wsIncoming: WebSocketWithHeartbeat, req: plugins.http.IncomingMessage) => {
this.wsServer.on('connection', (wsIncoming: IWebSocketWithHeartbeat, req: plugins.http.IncomingMessage) => {
this.handleWebSocketConnection(wsIncoming, req);
});
@ -56,9 +56,9 @@ export class WebSocketHandler {
}
this.logger.debug(`WebSocket heartbeat check for ${this.wsServer.clients.size} clients`);
this.wsServer.clients.forEach((ws: plugins.wsDefault) => {
const wsWithHeartbeat = ws as WebSocketWithHeartbeat;
const wsWithHeartbeat = ws as IWebSocketWithHeartbeat;
if (wsWithHeartbeat.isAlive === false) {
this.logger.debug('Terminating inactive WebSocket connection');
@ -79,7 +79,7 @@ export class WebSocketHandler {
/**
* Handle a new WebSocket connection
*/
private handleWebSocketConnection(wsIncoming: WebSocketWithHeartbeat, req: plugins.http.IncomingMessage): void {
private handleWebSocketConnection(wsIncoming: IWebSocketWithHeartbeat, req: plugins.http.IncomingMessage): void {
try {
// Initialize heartbeat tracking
wsIncoming.isAlive = true;
@ -127,7 +127,7 @@ export class WebSocketHandler {
}
// Override host header if needed
if ((proxyConfig as ReverseProxyConfig).rewriteHostHeader) {
if ((proxyConfig as IReverseProxyConfig).rewriteHostHeader) {
headers['host'] = `${destination.host}:${destination.port}`;
}

View File

@ -1,8 +1,8 @@
import * as plugins from '../../plugins.js';
import type {
ConnectionRecord,
DomainConfig,
SmartProxyOptions,
IConnectionRecord,
IDomainConfig,
ISmartProxyOptions,
} from './models/interfaces.js';
import { ConnectionManager } from './connection-manager.js';
import { SecurityManager } from './security-manager.js';
@ -12,14 +12,14 @@ import { NetworkProxyBridge } from './network-proxy-bridge.js';
import { TimeoutManager } from './timeout-manager.js';
import { PortRangeManager } from './port-range-manager.js';
import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js';
import type { ForwardingType } from '../../forwarding/config/forwarding-types.js';
import type { TForwardingType } from '../../forwarding/config/forwarding-types.js';
/**
* Handles new connection processing and setup logic
*/
export class ConnectionHandler {
constructor(
private settings: SmartProxyOptions,
private settings: ISmartProxyOptions,
private connectionManager: ConnectionManager,
private securityManager: SecurityManager,
private domainConfigManager: DomainConfigManager,
@ -102,7 +102,7 @@ export class ConnectionHandler {
*/
private handleNetworkProxyConnection(
socket: plugins.net.Socket,
record: ConnectionRecord
record: IConnectionRecord
): void {
const connectionId = record.id;
let initialDataReceived = false;
@ -307,7 +307,7 @@ export class ConnectionHandler {
/**
* Handle a standard (non-NetworkProxy) connection
*/
private handleStandardConnection(socket: plugins.net.Socket, record: ConnectionRecord): void {
private handleStandardConnection(socket: plugins.net.Socket, record: IConnectionRecord): void {
const connectionId = record.id;
const localPort = record.localPort;
@ -382,7 +382,7 @@ export class ConnectionHandler {
const setupConnection = (
serverName: string,
initialChunk?: Buffer,
forcedDomain?: DomainConfig,
forcedDomain?: IDomainConfig,
overridePort?: number
) => {
// Clear the initial timeout since we've received data
@ -500,7 +500,7 @@ export class ConnectionHandler {
const globalDomainConfig = {
domains: ['global'],
forwarding: {
type: 'http-only' as ForwardingType,
type: 'http-only' as TForwardingType,
target: {
host: this.settings.targetIP!,
port: this.settings.toPort
@ -730,8 +730,8 @@ export class ConnectionHandler {
*/
private setupDirectConnection(
socket: plugins.net.Socket,
record: ConnectionRecord,
domainConfig?: DomainConfig,
record: IConnectionRecord,
domainConfig?: IDomainConfig,
serverName?: string,
initialChunk?: Buffer,
overridePort?: number

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js';
import type { ConnectionRecord, SmartProxyOptions } from './models/interfaces.js';
import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
import { SecurityManager } from './security-manager.js';
import { TimeoutManager } from './timeout-manager.js';
@ -7,14 +7,14 @@ import { TimeoutManager } from './timeout-manager.js';
* Manages connection lifecycle, tracking, and cleanup
*/
export class ConnectionManager {
private connectionRecords: Map<string, ConnectionRecord> = new Map();
private connectionRecords: Map<string, IConnectionRecord> = new Map();
private terminationStats: {
incoming: Record<string, number>;
outgoing: Record<string, number>;
} = { incoming: {}, outgoing: {} };
constructor(
private settings: SmartProxyOptions,
private settings: ISmartProxyOptions,
private securityManager: SecurityManager,
private timeoutManager: TimeoutManager
) {}
@ -30,12 +30,12 @@ export class ConnectionManager {
/**
* Create and track a new connection
*/
public createConnection(socket: plugins.net.Socket): ConnectionRecord {
public createConnection(socket: plugins.net.Socket): IConnectionRecord {
const connectionId = this.generateConnectionId();
const remoteIP = socket.remoteAddress || '';
const localPort = socket.localPort || 0;
const record: ConnectionRecord = {
const record: IConnectionRecord = {
id: connectionId,
incoming: socket,
outgoing: null,
@ -66,7 +66,7 @@ export class ConnectionManager {
/**
* Track an existing connection
*/
public trackConnection(connectionId: string, record: ConnectionRecord): void {
public trackConnection(connectionId: string, record: IConnectionRecord): void {
this.connectionRecords.set(connectionId, record);
this.securityManager.trackConnectionByIP(record.remoteIP, connectionId);
}
@ -74,14 +74,14 @@ export class ConnectionManager {
/**
* Get a connection by ID
*/
public getConnection(connectionId: string): ConnectionRecord | undefined {
public getConnection(connectionId: string): IConnectionRecord | undefined {
return this.connectionRecords.get(connectionId);
}
/**
* Get all active connections
*/
public getConnections(): Map<string, ConnectionRecord> {
public getConnections(): Map<string, IConnectionRecord> {
return this.connectionRecords;
}
@ -95,7 +95,7 @@ export class ConnectionManager {
/**
* Initiates cleanup once for a connection
*/
public initiateCleanupOnce(record: ConnectionRecord, reason: string = 'normal'): void {
public initiateCleanupOnce(record: IConnectionRecord, reason: string = 'normal'): void {
if (this.settings.enableDetailedLogging) {
console.log(`[${record.id}] Connection cleanup initiated for ${record.remoteIP} (${reason})`);
}
@ -110,11 +110,11 @@ export class ConnectionManager {
this.cleanupConnection(record, reason);
}
/**
* Clean up a connection record
*/
public cleanupConnection(record: ConnectionRecord, reason: string = 'normal'): void {
public cleanupConnection(record: IConnectionRecord, reason: string = 'normal'): void {
if (!record.connectionClosed) {
record.connectionClosed = true;
@ -178,7 +178,7 @@ export class ConnectionManager {
/**
* Helper method to clean up a socket
*/
private cleanupSocket(record: ConnectionRecord, side: 'incoming' | 'outgoing', socket: plugins.net.Socket): void {
private cleanupSocket(record: IConnectionRecord, side: 'incoming' | 'outgoing', socket: plugins.net.Socket): void {
try {
if (!socket.destroyed) {
// Try graceful shutdown first, then force destroy after a short timeout
@ -213,7 +213,7 @@ export class ConnectionManager {
/**
* Creates a generic error handler for incoming or outgoing sockets
*/
public handleError(side: 'incoming' | 'outgoing', record: ConnectionRecord) {
public handleError(side: 'incoming' | 'outgoing', record: IConnectionRecord) {
return (err: Error) => {
const code = (err as any).code;
let reason = 'error';
@ -256,7 +256,7 @@ export class ConnectionManager {
/**
* Creates a generic close handler for incoming or outgoing sockets
*/
public handleClose(side: 'incoming' | 'outgoing', record: ConnectionRecord) {
public handleClose(side: 'incoming' | 'outgoing', record: IConnectionRecord) {
return () => {
if (this.settings.enableDetailedLogging) {
console.log(`[${record.id}] Connection closed on ${side} side from ${record.remoteIP}`);

View File

@ -1,6 +1,6 @@
import * as plugins from '../../plugins.js';
import type { DomainConfig, SmartProxyOptions } from './models/interfaces.js';
import type { ForwardingType, ForwardConfig } from '../../forwarding/config/forwarding-types.js';
import type { IDomainConfig, ISmartProxyOptions } from './models/interfaces.js';
import type { TForwardingType, IForwardConfig } from '../../forwarding/config/forwarding-types.js';
import type { ForwardingHandler } from '../../forwarding/handlers/base-handler.js';
import { ForwardingHandlerFactory } from '../../forwarding/factory/forwarding-factory.js';
@ -9,17 +9,17 @@ import { ForwardingHandlerFactory } from '../../forwarding/factory/forwarding-fa
*/
export class DomainConfigManager {
// Track round-robin indices for domain configs
private domainTargetIndices: Map<DomainConfig, number> = new Map();
private domainTargetIndices: Map<IDomainConfig, number> = new Map();
// Cache forwarding handlers for each domain config
private forwardingHandlers: Map<DomainConfig, ForwardingHandler> = new Map();
private forwardingHandlers: Map<IDomainConfig, ForwardingHandler> = new Map();
constructor(private settings: SmartProxyOptions) {}
constructor(private settings: ISmartProxyOptions) {}
/**
* Updates the domain configurations
*/
public updateDomainConfigs(newDomainConfigs: DomainConfig[]): void {
public updateDomainConfigs(newDomainConfigs: IDomainConfig[]): void {
this.settings.domainConfigs = newDomainConfigs;
// Reset target indices for removed configs
@ -31,7 +31,7 @@ export class DomainConfigManager {
}
// Clear handlers for removed configs and create handlers for new configs
const handlersToRemove: DomainConfig[] = [];
const handlersToRemove: IDomainConfig[] = [];
for (const [config] of this.forwardingHandlers) {
if (!currentConfigSet.has(config)) {
handlersToRemove.push(config);
@ -55,29 +55,29 @@ export class DomainConfigManager {
}
}
}
/**
* Get all domain configurations
*/
public getDomainConfigs(): DomainConfig[] {
public getDomainConfigs(): IDomainConfig[] {
return this.settings.domainConfigs;
}
/**
* Find domain config matching a server name
*/
public findDomainConfig(serverName: string): DomainConfig | undefined {
public findDomainConfig(serverName: string): IDomainConfig | undefined {
if (!serverName) return undefined;
return this.settings.domainConfigs.find((config) =>
config.domains.some((d) => plugins.minimatch(serverName, d))
);
}
/**
* Find domain config for a specific port
*/
public findDomainConfigForPort(port: number): DomainConfig | undefined {
public findDomainConfigForPort(port: number): IDomainConfig | undefined {
return this.settings.domainConfigs.find(
(domain) => {
const portRanges = domain.forwarding?.advanced?.portRanges;
@ -98,7 +98,7 @@ export class DomainConfigManager {
/**
* Get target IP with round-robin support
*/
public getTargetIP(domainConfig: DomainConfig): string {
public getTargetIP(domainConfig: IDomainConfig): string {
const targetHosts = Array.isArray(domainConfig.forwarding.target.host)
? domainConfig.forwarding.target.host
: [domainConfig.forwarding.target.host];
@ -117,21 +117,21 @@ export class DomainConfigManager {
* Get target host with round-robin support (for tests)
* This is just an alias for getTargetIP for easier test compatibility
*/
public getTargetHost(domainConfig: DomainConfig): string {
public getTargetHost(domainConfig: IDomainConfig): string {
return this.getTargetIP(domainConfig);
}
/**
* Get target port from domain config
*/
public getTargetPort(domainConfig: DomainConfig, defaultPort: number): number {
public getTargetPort(domainConfig: IDomainConfig, defaultPort: number): number {
return domainConfig.forwarding.target.port || defaultPort;
}
/**
* Checks if a domain should use NetworkProxy
*/
public shouldUseNetworkProxy(domainConfig: DomainConfig): boolean {
public shouldUseNetworkProxy(domainConfig: IDomainConfig): boolean {
const forwardingType = this.getForwardingType(domainConfig);
return forwardingType === 'https-terminate-to-http' ||
forwardingType === 'https-terminate-to-https';
@ -140,7 +140,7 @@ export class DomainConfigManager {
/**
* Gets the NetworkProxy port for a domain
*/
public getNetworkProxyPort(domainConfig: DomainConfig): number | undefined {
public getNetworkProxyPort(domainConfig: IDomainConfig): number | undefined {
// First check if we should use NetworkProxy at all
if (!this.shouldUseNetworkProxy(domainConfig)) {
return undefined;
@ -148,14 +148,14 @@ export class DomainConfigManager {
return domainConfig.forwarding.advanced?.networkProxyPort || this.settings.networkProxyPort;
}
/**
* Get effective allowed and blocked IPs for a domain
*
* This method combines domain-specific security rules from the forwarding configuration
* with global security defaults when necessary.
*/
public getEffectiveIPRules(domainConfig: DomainConfig): {
public getEffectiveIPRules(domainConfig: IDomainConfig): {
allowedIPs: string[],
blockedIPs: string[]
} {
@ -201,7 +201,7 @@ export class DomainConfigManager {
/**
* Get connection timeout for a domain
*/
public getConnectionTimeout(domainConfig?: DomainConfig): number {
public getConnectionTimeout(domainConfig?: IDomainConfig): number {
if (domainConfig?.forwarding.advanced?.timeout) {
return domainConfig.forwarding.advanced.timeout;
}
@ -212,7 +212,7 @@ export class DomainConfigManager {
/**
* Creates a forwarding handler for a domain configuration
*/
private createForwardingHandler(domainConfig: DomainConfig): ForwardingHandler {
private createForwardingHandler(domainConfig: IDomainConfig): ForwardingHandler {
// Create a new handler using the factory
const handler = ForwardingHandlerFactory.createHandler(domainConfig.forwarding);
@ -228,7 +228,7 @@ export class DomainConfigManager {
* Gets a forwarding handler for a domain config
* If no handler exists, creates one
*/
public getForwardingHandler(domainConfig: DomainConfig): ForwardingHandler {
public getForwardingHandler(domainConfig: IDomainConfig): ForwardingHandler {
// If we already have a handler, return it
if (this.forwardingHandlers.has(domainConfig)) {
return this.forwardingHandlers.get(domainConfig)!;
@ -244,7 +244,7 @@ export class DomainConfigManager {
/**
* Gets the forwarding type for a domain config
*/
public getForwardingType(domainConfig?: DomainConfig): ForwardingType | undefined {
public getForwardingType(domainConfig?: IDomainConfig): TForwardingType | undefined {
if (!domainConfig?.forwarding) return undefined;
return domainConfig.forwarding.type;
}
@ -252,7 +252,7 @@ export class DomainConfigManager {
/**
* Checks if the forwarding type requires TLS termination
*/
public requiresTlsTermination(domainConfig?: DomainConfig): boolean {
public requiresTlsTermination(domainConfig?: IDomainConfig): boolean {
if (!domainConfig) return false;
const forwardingType = this.getForwardingType(domainConfig);
@ -263,7 +263,7 @@ export class DomainConfigManager {
/**
* Checks if the forwarding type supports HTTP
*/
public supportsHttp(domainConfig?: DomainConfig): boolean {
public supportsHttp(domainConfig?: IDomainConfig): boolean {
if (!domainConfig) return false;
const forwardingType = this.getForwardingType(domainConfig);
@ -285,7 +285,7 @@ export class DomainConfigManager {
/**
* Checks if HTTP requests should be redirected to HTTPS
*/
public shouldRedirectToHttps(domainConfig?: DomainConfig): boolean {
public shouldRedirectToHttps(domainConfig?: IDomainConfig): boolean {
if (!domainConfig?.forwarding) return false;
// Only check for redirect if HTTP is enabled

View File

@ -1,28 +1,28 @@
import * as plugins from '../../../plugins.js';
import type { ForwardConfig } from '../../../forwarding/config/forwarding-types.js';
import type { IForwardConfig } from '../../../forwarding/config/forwarding-types.js';
/**
* Provision object for static or HTTP-01 certificate
*/
export type SmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
export type TSmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
/**
* Domain configuration with forwarding configuration
*/
export interface DomainConfig {
export interface IDomainConfig {
domains: string[]; // Glob patterns for domain(s)
forwarding: ForwardConfig; // Unified forwarding configuration
forwarding: IForwardConfig; // Unified forwarding configuration
}
/**
* Configuration options for the SmartProxy
*/
import type { AcmeOptions } from '../../../certificate/models/certificate-types.js';
export interface SmartProxyOptions {
import type { IAcmeOptions } from '../../../certificate/models/certificate-types.js';
export interface ISmartProxyOptions {
fromPort: number;
toPort: number;
targetIP?: string; // Global target host to proxy to, defaults to 'localhost'
domainConfigs: DomainConfig[];
domainConfigs: IDomainConfig[];
sniEnabled?: boolean;
defaultAllowedIPs?: string[];
defaultBlockedIPs?: string[];
@ -81,19 +81,19 @@ export interface SmartProxyOptions {
networkProxyPort?: number; // Port where NetworkProxy is listening (default: 8443)
// ACME configuration options for SmartProxy
acme?: AcmeOptions;
acme?: IAcmeOptions;
/**
* Optional certificate provider callback. Return 'http01' to use HTTP-01 challenges,
* or a static certificate object for immediate provisioning.
*/
certProvisionFunction?: (domain: string) => Promise<SmartProxyCertProvisionObject>;
certProvisionFunction?: (domain: string) => Promise<TSmartProxyCertProvisionObject>;
}
/**
* Enhanced connection record
*/
export interface ConnectionRecord {
export interface IConnectionRecord {
id: string; // Unique connection identifier
incoming: plugins.net.Socket;
outgoing: plugins.net.Socket | null;
@ -116,7 +116,7 @@ export interface ConnectionRecord {
isTLS: boolean; // Whether this connection is a TLS connection
tlsHandshakeComplete: boolean; // Whether the TLS handshake is complete
hasReceivedInitialData: boolean; // Whether initial data has been received
domainConfig?: DomainConfig; // Associated domain config for this connection
domainConfig?: IDomainConfig; // Associated domain config for this connection
// Keep-alive tracking
hasKeepAlive: boolean; // Whether keep-alive is enabled for this connection
@ -133,10 +133,4 @@ export interface ConnectionRecord {
// Browser connection tracking
isBrowserConnection?: boolean; // Whether this connection appears to be from a browser
domainSwitches?: number; // Number of times the domain has been switched on this connection
}
// Backward compatibility types
export type ISmartProxyCertProvisionObject = SmartProxyCertProvisionObject;
export interface IDomainConfig extends DomainConfig {}
export interface ISmartProxyOptions extends SmartProxyOptions {}
export interface IConnectionRecord extends ConnectionRecord {}
}

View File

@ -3,8 +3,8 @@ import { NetworkProxy } from '../network-proxy/index.js';
import { Port80Handler } from '../../http/port80/port80-handler.js';
import { Port80HandlerEvents } from '../../core/models/common-types.js';
import { subscribeToPort80Handler } from '../../core/utils/event-utils.js';
import type { CertificateData } from '../../certificate/models/certificate-types.js';
import type { ConnectionRecord, SmartProxyOptions, DomainConfig } from './models/interfaces.js';
import type { ICertificateData } from '../../certificate/models/certificate-types.js';
import type { IConnectionRecord, ISmartProxyOptions, IDomainConfig } from './models/interfaces.js';
/**
* Manages NetworkProxy integration for TLS termination
@ -13,7 +13,7 @@ export class NetworkProxyBridge {
private networkProxy: NetworkProxy | null = null;
private port80Handler: Port80Handler | null = null;
constructor(private settings: SmartProxyOptions) {}
constructor(private settings: ISmartProxyOptions) {}
/**
* Set the Port80Handler to use for certificate management
@ -66,23 +66,23 @@ export class NetworkProxyBridge {
/**
* Handle certificate issuance or renewal events
*/
private handleCertificateEvent(data: CertificateData): void {
private handleCertificateEvent(data: ICertificateData): void {
if (!this.networkProxy) return;
console.log(`Received certificate for ${data.domain} from Port80Handler, updating NetworkProxy`);
try {
// Find existing config for this domain
const existingConfigs = this.networkProxy.getProxyConfigs()
.filter(config => config.hostName === data.domain);
if (existingConfigs.length > 0) {
// Update existing configs with new certificate
for (const config of existingConfigs) {
config.privateKey = data.privateKey;
config.publicKey = data.certificate;
}
// Apply updated configs
this.networkProxy.updateProxyConfigs(existingConfigs)
.then(() => console.log(`Updated certificate for ${data.domain} in NetworkProxy`))
@ -95,11 +95,11 @@ export class NetworkProxyBridge {
console.log(`Error handling certificate event: ${err}`);
}
}
/**
* Apply an external (static) certificate into NetworkProxy
*/
public applyExternalCertificate(data: CertificateData): void {
public applyExternalCertificate(data: ICertificateData): void {
if (!this.networkProxy) {
console.log(`NetworkProxy not initialized: cannot apply external certificate for ${data.domain}`);
return;
@ -183,7 +183,7 @@ export class NetworkProxyBridge {
public forwardToNetworkProxy(
connectionId: string,
socket: plugins.net.Socket,
record: ConnectionRecord,
record: IConnectionRecord,
initialData: Buffer,
customProxyPort?: number,
onError?: (reason: string) => void

View File

@ -1,10 +1,10 @@
import type { SmartProxyOptions } from './models/interfaces.js';
import type { ISmartProxyOptions } from './models/interfaces.js';
/**
* Manages port ranges and port-based configuration
*/
export class PortRangeManager {
constructor(private settings: SmartProxyOptions) {}
constructor(private settings: ISmartProxyOptions) {}
/**
* Get all ports that should be listened on

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js';
import type { SmartProxyOptions } from './models/interfaces.js';
import type { ISmartProxyOptions } from './models/interfaces.js';
/**
* Handles security aspects like IP tracking, rate limiting, and authorization
@ -8,7 +8,7 @@ export class SecurityManager {
private connectionsByIP: Map<string, Set<string>> = new Map();
private connectionRateByIP: Map<string, number[]> = new Map();
constructor(private settings: SmartProxyOptions) {}
constructor(private settings: ISmartProxyOptions) {}
/**
* Get connections count by IP

View File

@ -13,15 +13,15 @@ import { ConnectionHandler } from './connection-handler.js';
// External dependencies from migrated modules
import { Port80Handler } from '../../http/port80/port80-handler.js';
import { CertProvisioner } from '../../certificate/providers/cert-provisioner.js';
import type { CertificateData } from '../../certificate/models/certificate-types.js';
import type { ICertificateData } from '../../certificate/models/certificate-types.js';
import { buildPort80Handler } from '../../certificate/acme/acme-factory.js';
import type { ForwardingType } from '../../forwarding/config/forwarding-types.js';
import type { TForwardingType } from '../../forwarding/config/forwarding-types.js';
import { createPort80HandlerOptions } from '../../common/port80-adapter.js';
// Import types from models
import type { SmartProxyOptions, DomainConfig } from './models/interfaces.js';
import type { ISmartProxyOptions, IDomainConfig } from './models/interfaces.js';
// Provide backward compatibility types
export type { SmartProxyOptions as IPortProxySettings, DomainConfig as IDomainConfig };
export type { ISmartProxyOptions as IPortProxySettings, IDomainConfig };
/**
* SmartProxy - Main class that coordinates all components
@ -46,7 +46,7 @@ export class SmartProxy extends plugins.EventEmitter {
// CertProvisioner for unified certificate workflows
private certProvisioner?: CertProvisioner;
constructor(settingsArg: SmartProxyOptions) {
constructor(settingsArg: ISmartProxyOptions) {
super();
// Set reasonable defaults for all settings
this.settings = {
@ -63,12 +63,12 @@ export class SmartProxy extends plugins.EventEmitter {
keepAliveInitialDelay: settingsArg.keepAliveInitialDelay || 10000,
maxPendingDataSize: settingsArg.maxPendingDataSize || 10 * 1024 * 1024,
disableInactivityCheck: settingsArg.disableInactivityCheck || false,
enableKeepAliveProbes:
enableKeepAliveProbes:
settingsArg.enableKeepAliveProbes !== undefined ? settingsArg.enableKeepAliveProbes : true,
enableDetailedLogging: settingsArg.enableDetailedLogging || false,
enableTlsDebugLogging: settingsArg.enableTlsDebugLogging || false,
enableRandomizedTimeouts: settingsArg.enableRandomizedTimeouts || false,
allowSessionTicket:
allowSessionTicket:
settingsArg.allowSessionTicket !== undefined ? settingsArg.allowSessionTicket : true,
maxConnectionsPerIP: settingsArg.maxConnectionsPerIP || 100,
connectionRateLimitPerMinute: settingsArg.connectionRateLimitPerMinute || 300,
@ -126,7 +126,7 @@ export class SmartProxy extends plugins.EventEmitter {
/**
* The settings for the port proxy
*/
public settings: SmartProxyOptions;
public settings: ISmartProxyOptions;
/**
* Initialize the Port80Handler for ACME certificate management
@ -413,7 +413,7 @@ export class SmartProxy extends plugins.EventEmitter {
/**
* Updates the domain configurations for the proxy
*/
public async updateDomainConfigs(newDomainConfigs: DomainConfig[]): Promise<void> {
public async updateDomainConfigs(newDomainConfigs: IDomainConfig[]): Promise<void> {
console.log(`Updating domain configurations (${newDomainConfigs.length} configs)`);
// Update domain configs in DomainConfigManager
@ -475,7 +475,7 @@ export class SmartProxy extends plugins.EventEmitter {
} else {
// Static certificate (e.g., DNS-01 provisioned) supports wildcards
const certObj = provision as plugins.tsclass.network.ICert;
const certData: CertificateData = {
const certData: ICertificateData = {
domain: certObj.domainName,
certificate: certObj.publicKey,
privateKey: certObj.privateKey,

View File

@ -1,10 +1,10 @@
import type { ConnectionRecord, SmartProxyOptions } from './models/interfaces.js';
import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js';
/**
* Manages timeouts and inactivity tracking for connections
*/
export class TimeoutManager {
constructor(private settings: SmartProxyOptions) {}
constructor(private settings: ISmartProxyOptions) {}
/**
* Ensure timeout values don't exceed Node.js max safe integer
@ -28,7 +28,7 @@ export class TimeoutManager {
/**
* Update connection activity timestamp
*/
public updateActivity(record: ConnectionRecord): void {
public updateActivity(record: IConnectionRecord): void {
record.lastActivity = Date.now();
// Clear any inactivity warning
@ -36,11 +36,11 @@ export class TimeoutManager {
record.inactivityWarningIssued = false;
}
}
/**
* Calculate effective inactivity timeout based on connection type
*/
public getEffectiveInactivityTimeout(record: ConnectionRecord): number {
public getEffectiveInactivityTimeout(record: IConnectionRecord): number {
let effectiveTimeout = this.settings.inactivityTimeout || 14400000; // 4 hours default
// For immortal keep-alive connections, use an extremely long timeout
@ -60,7 +60,7 @@ export class TimeoutManager {
/**
* Calculate effective max lifetime based on connection type
*/
public getEffectiveMaxLifetime(record: ConnectionRecord): number {
public getEffectiveMaxLifetime(record: IConnectionRecord): number {
// Use domain-specific timeout from forwarding.advanced if available
const baseTimeout = record.domainConfig?.forwarding?.advanced?.timeout ||
this.settings.maxConnectionLifetime ||
@ -91,8 +91,8 @@ export class TimeoutManager {
* @returns The cleanup timer
*/
public setupConnectionTimeout(
record: ConnectionRecord,
onTimeout: (record: ConnectionRecord, reason: string) => void
record: IConnectionRecord,
onTimeout: (record: IConnectionRecord, reason: string) => void
): NodeJS.Timeout {
// Clear any existing timer
if (record.cleanupTimer) {
@ -120,7 +120,7 @@ export class TimeoutManager {
* Check for inactivity on a connection
* @returns Object with check results
*/
public checkInactivity(record: ConnectionRecord): {
public checkInactivity(record: IConnectionRecord): {
isInactive: boolean;
shouldWarn: boolean;
inactivityTime: number;
@ -169,7 +169,7 @@ export class TimeoutManager {
/**
* Apply socket timeout settings
*/
public applySocketTimeouts(record: ConnectionRecord): void {
public applySocketTimeouts(record: IConnectionRecord): void {
// Skip for immortal keep-alive connections
if (record.hasKeepAlive && this.settings.keepAliveTreatment === 'immortal') {
// Disable timeouts completely for immortal connections

View File

@ -1,5 +1,5 @@
import * as plugins from '../../plugins.js';
import type { SmartProxyOptions } from './models/interfaces.js';
import type { ISmartProxyOptions } from './models/interfaces.js';
import { SniHandler } from '../../tls/sni/sni-handler.js';
/**
@ -16,7 +16,7 @@ interface IConnectionInfo {
* Manages TLS-related operations including SNI extraction and validation
*/
export class TlsManager {
constructor(private settings: SmartProxyOptions) {}
constructor(private settings: ISmartProxyOptions) {}
/**
* Check if a data chunk appears to be a TLS handshake