Compare commits

...

18 Commits

Author SHA1 Message Date
2e4c6312cd 10.0.8
Some checks failed
Default (tags) / security (push) Successful in 32s
Default (tags) / test (push) Failing after 1m17s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-05 10:29:00 +00:00
9b773608c7 fix(smartproxy): rename certProvider to certProvisionFunction in certificate provisioning interfaces and SmartProxy 2025-05-05 10:29:00 +00:00
3502807023 10.0.7
Some checks failed
Default (tags) / security (push) Successful in 43s
Default (tags) / test (push) Failing after 1m26s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-04 13:49:22 +00:00
c6dff8b78d fix(core): refactor: Rename IPortProxySettings to ISmartProxyOptions in internal modules 2025-05-04 13:49:22 +00:00
12b18373db 10.0.6
Some checks failed
Default (tags) / security (push) Successful in 31s
Default (tags) / test (push) Failing after 1m15s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-04 13:05:48 +00:00
30c25ec70c fix(smartproxy): No changes detected in project files. This commit updates commit info without modifying any functionality. 2025-05-04 13:05:48 +00:00
434834fc06 10.0.5
Some checks failed
Default (tags) / security (push) Successful in 38s
Default (tags) / test (push) Failing after 1m15s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-04 13:04:35 +00:00
e7243243d0 fix(exports/types): Refactor exports and remove duplicate IReverseProxyConfig interface 2025-05-04 13:04:34 +00:00
cce2aed892 10.0.4
Some checks failed
Default (tags) / security (push) Successful in 42s
Default (tags) / test (push) Failing after 1m16s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-04 12:44:35 +00:00
8cd693c063 fix(core): Refactor module exports and update packageManager version in package.json 2025-05-04 12:44:35 +00:00
09ad7644f4 10.0.3
Some checks failed
Default (tags) / security (push) Successful in 46s
Default (tags) / test (push) Failing after 1m19s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-04 12:21:02 +00:00
f72f884eda fix(smartproxy): Update dependency versions (@push.rocks/smartacme to ^7.2.4, @push.rocks/smartnetwork to ^4.0.1, ws to ^8.18.2) and export common types via index.ts for easier imports. 2025-05-04 12:21:02 +00:00
73f3dfcad4 10.0.2
Some checks failed
Default (tags) / security (push) Successful in 42s
Default (tags) / test (push) Failing after 1m18s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-03 19:02:26 +00:00
8291f1f33a fix(tlsalert): Centralize plugin imports in TlsAlert and update plan checklist 2025-05-03 19:02:26 +00:00
f512fb4252 10.0.1
Some checks failed
Default (tags) / security (push) Successful in 37s
Default (tags) / test (push) Failing after 1m18s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-03 13:27:59 +00:00
1f3ee1eafc fix(docs): Improve mermaid diagram formatting in readme.md using HTML <br> tags for line breaks 2025-05-03 13:27:59 +00:00
910c8160f6 10.0.0
Some checks failed
Default (tags) / security (push) Successful in 50s
Default (tags) / test (push) Failing after 1m19s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-05-03 13:19:23 +00:00
0e634c46a6 BREAKING CHANGE(smartproxy): Update documentation and refactor core proxy components; remove legacy performRenewals method from SmartProxy; update router type imports and adjust test suites for improved coverage 2025-05-03 13:19:23 +00:00
22 changed files with 396 additions and 595 deletions

View File

@ -1,5 +1,65 @@
# Changelog # Changelog
## 2025-05-05 - 10.0.8 - fix(smartproxy)
rename certProvider to certProvisionFunction in certificate provisioning interfaces and SmartProxy
- In ts/smartproxy/classes.pp.interfaces.ts, renamed the optional property 'certProvider' to 'certProvisionFunction'.
- In ts/smartproxy/classes.smartproxy.ts, updated references from this.settings.certProvider to this.settings.certProvisionFunction.
## 2025-05-04 - 10.0.7 - fix(core)
refactor: Rename IPortProxySettings to ISmartProxyOptions in internal modules
- Replaced IPortProxySettings with ISmartProxyOptions in connection handler, connection manager, domain config manager, security manager, timeout manager, TLS manager, and network proxy bridge.
- Updated type imports and constructors accordingly while preserving backward compatibility via export alias.
## 2025-05-04 - 10.0.6 - fix(smartproxy)
No changes detected in project files. This commit updates commit info without modifying any functionality.
## 2025-05-04 - 10.0.5 - fix(exports/types)
Refactor exports and remove duplicate IReverseProxyConfig interface
- Removed redundant IReverseProxyConfig extension from ts/common/types.ts
- Updated ts/index.ts to export networkproxy via index.js instead of classes.np.networkproxy.js
- Simplified module exports to avoid duplicate interface definitions
## 2025-05-04 - 10.0.4 - fix(core)
Refactor module exports and update packageManager version in package.json
- In package.json, bumped pnpm version from 10.7.0 to 10.10.0 for dependency consistency.
- In ts/index.ts, removed redundant type export and now export common types directly.
- In ts/smartproxy/classes.smartproxy.ts, reorganized imports and explicitly export IPortProxySettings and IDomainConfig.
## 2025-05-04 - 10.0.3 - fix(smartproxy)
Update dependency versions (@push.rocks/smartacme to ^7.2.4, @push.rocks/smartnetwork to ^4.0.1, ws to ^8.18.2) and export common types via index.ts for easier imports.
- Upgrade @push.rocks/smartacme from ^7.2.3 to ^7.2.4
- Upgrade @push.rocks/smartnetwork from ^4.0.0 to ^4.0.1
- Upgrade ws from ^8.18.1 to ^8.18.2
- Export common types from ts/common/types.ts in index.ts
## 2025-05-03 - 10.0.2 - fix(tlsalert)
Centralize plugin imports in TlsAlert and update plan checklist
- Mark the 'Centralize plugin imports in ts/plugins.ts' item as complete in readme.plan.md
- Replace direct 'net' imports with a centralized 'plugins' import in ts/smartproxy/classes.pp.tlsalert.ts
- Update all socket type references from net.Socket to plugins.net.Socket for consistency
## 2025-05-03 - 10.0.1 - fix(docs)
Improve mermaid diagram formatting in readme.md using HTML <br> tags for line breaks
- Replaced newline characters with <br> in the SmartProxy Components diagram nodes for better HTML rendering
- Improved visual clarity of the architectural diagrams in the readme
## 2025-05-03 - 10.0.0 - BREAKING CHANGE(smartproxy)
Update documentation and refactor core proxy components; remove legacy performRenewals method from SmartProxy; update router type imports and adjust test suites for improved coverage
- Expanded README with detailed Quick Start examples for HTTP/HTTPS reverse proxy, ACME integration, HTTP→HTTPS redirect, nftables port forwarding, and SNI-based TCP proxying
- Updated readme.plan.md checkboxes to show completed tasks
- Refactored ProxyRouter to import types via plugins.tsclass, ensuring consistency in type imports
- Removed deprecated performRenewals method from SmartProxy, constituting a breaking change for users relying on it
- Updated multiple test suites (router, networkproxy, certprovisioner, etc.) to reflect new behaviors and improved diagnostics
## 2025-05-02 - 9.0.0 - BREAKING CHANGE(acme) ## 2025-05-02 - 9.0.0 - BREAKING CHANGE(acme)
Refactor ACME configuration and certificate provisioning by replacing legacy port80HandlerConfig with unified acme options and updating CertProvisioner event subscriptions Refactor ACME configuration and certificate provisioning by replacing legacy port80HandlerConfig with unified acme options and updating CertProvisioner event subscriptions

View File

@ -1,6 +1,6 @@
{ {
"name": "@push.rocks/smartproxy", "name": "@push.rocks/smartproxy",
"version": "9.0.0", "version": "10.0.8",
"private": false, "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.", "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", "main": "dist_ts/index.js",
@ -24,9 +24,9 @@
}, },
"dependencies": { "dependencies": {
"@push.rocks/lik": "^6.2.2", "@push.rocks/lik": "^6.2.2",
"@push.rocks/smartacme": "^7.2.3", "@push.rocks/smartacme": "^7.2.4",
"@push.rocks/smartdelay": "^3.0.5", "@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartnetwork": "^4.0.0", "@push.rocks/smartnetwork": "^4.0.1",
"@push.rocks/smartpromise": "^4.2.3", "@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartrequest": "^2.1.0", "@push.rocks/smartrequest": "^2.1.0",
"@push.rocks/smartstring": "^4.0.15", "@push.rocks/smartstring": "^4.0.15",
@ -36,7 +36,7 @@
"@types/ws": "^8.18.1", "@types/ws": "^8.18.1",
"minimatch": "^10.0.1", "minimatch": "^10.0.1",
"pretty-ms": "^9.2.0", "pretty-ms": "^9.2.0",
"ws": "^8.18.1" "ws": "^8.18.2"
}, },
"files": [ "files": [
"ts/**/*", "ts/**/*",
@ -86,5 +86,5 @@
"puppeteer" "puppeteer"
] ]
}, },
"packageManager": "pnpm@10.7.0+sha512.6b865ad4b62a1d9842b61d674a393903b871d9244954f652b8842c2b553c72176b278f64c463e52d40fff8aba385c235c8c9ecf5cc7de4fd78b8bb6d49633ab6" "packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
} }

43
pnpm-lock.yaml generated
View File

@ -12,14 +12,14 @@ importers:
specifier: ^6.2.2 specifier: ^6.2.2
version: 6.2.2 version: 6.2.2
'@push.rocks/smartacme': '@push.rocks/smartacme':
specifier: ^7.2.3 specifier: ^7.2.4
version: 7.2.3(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4) version: 7.2.4(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)
'@push.rocks/smartdelay': '@push.rocks/smartdelay':
specifier: ^3.0.5 specifier: ^3.0.5
version: 3.0.5 version: 3.0.5
'@push.rocks/smartnetwork': '@push.rocks/smartnetwork':
specifier: ^4.0.0 specifier: ^4.0.1
version: 4.0.0 version: 4.0.1
'@push.rocks/smartpromise': '@push.rocks/smartpromise':
specifier: ^4.2.3 specifier: ^4.2.3
version: 4.2.3 version: 4.2.3
@ -48,8 +48,8 @@ importers:
specifier: ^9.2.0 specifier: ^9.2.0
version: 9.2.0 version: 9.2.0
ws: ws:
specifier: ^8.18.1 specifier: ^8.18.2
version: 8.18.1 version: 8.18.2
devDependencies: devDependencies:
'@git.zone/tsbuild': '@git.zone/tsbuild':
specifier: ^2.3.2 specifier: ^2.3.2
@ -355,8 +355,8 @@ packages:
'@cloudflare/workers-types@4.20250303.0': '@cloudflare/workers-types@4.20250303.0':
resolution: {integrity: sha512-O7F7nRT4bbmwHf3gkRBLfJ7R6vHIJ/oZzWdby6obOiw2yavUfp/AIwS7aO2POu5Cv8+h3TXS3oHs3kKCZLraUA==} resolution: {integrity: sha512-O7F7nRT4bbmwHf3gkRBLfJ7R6vHIJ/oZzWdby6obOiw2yavUfp/AIwS7aO2POu5Cv8+h3TXS3oHs3kKCZLraUA==}
'@cloudflare/workers-types@4.20250430.0': '@cloudflare/workers-types@4.20250504.0':
resolution: {integrity: sha512-JWAX7ZhQ7KjkdJwASgG58MZ/pQ15brlnZ9/0YBwDQ0hrJ/LaK392aTRFlj2r/PRKDZ5dOuujRywNYaNpfeFiEA==} resolution: {integrity: sha512-/70Kb5vrqj+O0krOuS8LVLiCeDuCGzQy4X+wGGs4/rHv0gZJulv7Uj5YlUjIaRemK/Dyrzlk7WNJwTy8yv0cIw==}
'@colors/colors@1.6.0': '@colors/colors@1.6.0':
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
@ -872,8 +872,8 @@ packages:
'@push.rocks/qenv@6.1.0': '@push.rocks/qenv@6.1.0':
resolution: {integrity: sha512-1FUFMlSVwFSFg8LbqfkzJ2LLP4lMGApUtgOpsvrde6+AxBmB4gjoNgCUH7z3xXfDAtYqcrtSELXBNE0xVL1MqQ==} resolution: {integrity: sha512-1FUFMlSVwFSFg8LbqfkzJ2LLP4lMGApUtgOpsvrde6+AxBmB4gjoNgCUH7z3xXfDAtYqcrtSELXBNE0xVL1MqQ==}
'@push.rocks/smartacme@7.2.3': '@push.rocks/smartacme@7.2.4':
resolution: {integrity: sha512-PTwn/Zf7l+IMWqeiQ8mTxi7fdrtObQH13YzF65si/VxXTqHeZ7zvisLLKZcMEgSaOj1aQ/Ku83gaO8YqO4gDig==} resolution: {integrity: sha512-0ciewRheDAwv0ER0ZyLQVLAn0ZoG1++ibSZ14HoXn8GOTOLyuRLWLNDQL2fI4LtLxeaNYmQUS+f7tt4KaZb/UA==}
'@push.rocks/smartarchive@3.0.8': '@push.rocks/smartarchive@3.0.8':
resolution: {integrity: sha512-1jPmR0b7hXmjYQoRiTlRXrIbZcdcFmSdGOfznufjcDpGPe86Km0d8TBnzqghTx4dTihzKC67IxAaz/DM3lvxpA==} resolution: {integrity: sha512-1jPmR0b7hXmjYQoRiTlRXrIbZcdcFmSdGOfznufjcDpGPe86Km0d8TBnzqghTx4dTihzKC67IxAaz/DM3lvxpA==}
@ -974,8 +974,8 @@ packages:
'@push.rocks/smartnetwork@3.0.2': '@push.rocks/smartnetwork@3.0.2':
resolution: {integrity: sha512-s6CNGzQ1n/d/6cOKXbxeW6/tO//dr1woLqI01g7XhqTriw0nsm2G2kWaZh2J0VOguGNWBgQVCIpR0LjdRNWb3g==} resolution: {integrity: sha512-s6CNGzQ1n/d/6cOKXbxeW6/tO//dr1woLqI01g7XhqTriw0nsm2G2kWaZh2J0VOguGNWBgQVCIpR0LjdRNWb3g==}
'@push.rocks/smartnetwork@4.0.0': '@push.rocks/smartnetwork@4.0.1':
resolution: {integrity: sha512-hLE1JNrBjlWtibgFz7t2aMfP15VOfPFyKMpo6FI0JdhmJfD3V5w/nFpSdD6WdXeXUBjCVTJ3C6SrRl8izoG55g==} resolution: {integrity: sha512-zLH88bKY6/cK6vVnCW4Fsugu4T+l6OerWWappit+BecdnQ6vrgShXSAa13JIkkWkWcs4dxEirlEfycQEEQw8BQ==}
'@push.rocks/smartnpm@2.0.4': '@push.rocks/smartnpm@2.0.4':
resolution: {integrity: sha512-ljRPqnUsXzL5qnuAEt5POy0NnfKs7eYPuuJPJjYiK9VUdP/CyF4h14qTB4H816vNEuF7VU/ASRtz0qDlXmrztg==} resolution: {integrity: sha512-ljRPqnUsXzL5qnuAEt5POy0NnfKs7eYPuuJPJjYiK9VUdP/CyF4h14qTB4H816vNEuF7VU/ASRtz0qDlXmrztg==}
@ -4622,8 +4622,8 @@ packages:
utf-8-validate: utf-8-validate:
optional: true optional: true
ws@8.18.1: ws@8.18.2:
resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==}
engines: {node: '>=10.0.0'} engines: {node: '>=10.0.0'}
peerDependencies: peerDependencies:
bufferutil: ^4.0.1 bufferutil: ^4.0.1
@ -4760,7 +4760,7 @@ snapshots:
'@api.global/typedrequest': 3.1.10 '@api.global/typedrequest': 3.1.10
'@api.global/typedrequest-interfaces': 3.0.19 '@api.global/typedrequest-interfaces': 3.0.19
'@api.global/typedsocket': 3.0.1 '@api.global/typedsocket': 3.0.1
'@cloudflare/workers-types': 4.20250430.0 '@cloudflare/workers-types': 4.20250504.0
'@design.estate/dees-comms': 1.0.27 '@design.estate/dees-comms': 1.0.27
'@push.rocks/lik': 6.2.2 '@push.rocks/lik': 6.2.2
'@push.rocks/smartchok': 1.0.34 '@push.rocks/smartchok': 1.0.34
@ -5671,7 +5671,7 @@ snapshots:
'@cloudflare/workers-types@4.20250303.0': {} '@cloudflare/workers-types@4.20250303.0': {}
'@cloudflare/workers-types@4.20250430.0': {} '@cloudflare/workers-types@4.20250504.0': {}
'@colors/colors@1.6.0': {} '@colors/colors@1.6.0': {}
@ -5950,7 +5950,7 @@ snapshots:
'@push.rocks/tapbundle': 5.6.3(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4) '@push.rocks/tapbundle': 5.6.3(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)
'@types/ws': 8.18.1 '@types/ws': 8.18.1
figures: 6.1.0 figures: 6.1.0
ws: 8.18.1 ws: 8.18.2
transitivePeerDependencies: transitivePeerDependencies:
- '@aws-sdk/credential-providers' - '@aws-sdk/credential-providers'
- '@mongodb-js/zstd' - '@mongodb-js/zstd'
@ -6284,7 +6284,7 @@ snapshots:
'@push.rocks/smartlog': 3.0.7 '@push.rocks/smartlog': 3.0.7
'@push.rocks/smartpath': 5.0.18 '@push.rocks/smartpath': 5.0.18
'@push.rocks/smartacme@7.2.3(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)': '@push.rocks/smartacme@7.2.4(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)':
dependencies: dependencies:
'@api.global/typedserver': 3.0.74 '@api.global/typedserver': 3.0.74
'@apiclient.xyz/cloudflare': 6.4.1 '@apiclient.xyz/cloudflare': 6.4.1
@ -6304,6 +6304,7 @@ snapshots:
- '@aws-sdk/credential-providers' - '@aws-sdk/credential-providers'
- '@mongodb-js/zstd' - '@mongodb-js/zstd'
- '@nuxt/kit' - '@nuxt/kit'
- aws-crt
- bufferutil - bufferutil
- encoding - encoding
- gcp-metadata - gcp-metadata
@ -6605,7 +6606,7 @@ snapshots:
public-ip: 6.0.2 public-ip: 6.0.2
systeminformation: 5.25.11 systeminformation: 5.25.11
'@push.rocks/smartnetwork@4.0.0': '@push.rocks/smartnetwork@4.0.1':
dependencies: dependencies:
'@push.rocks/smartping': 1.0.8 '@push.rocks/smartping': 1.0.8
'@push.rocks/smartpromise': 4.2.3 '@push.rocks/smartpromise': 4.2.3
@ -10544,7 +10545,7 @@ snapshots:
debug: 4.4.0 debug: 4.4.0
devtools-protocol: 0.0.1413902 devtools-protocol: 0.0.1413902
typed-query-selector: 2.12.0 typed-query-selector: 2.12.0
ws: 8.18.1 ws: 8.18.2
transitivePeerDependencies: transitivePeerDependencies:
- bare-buffer - bare-buffer
- bufferutil - bufferutil
@ -11303,7 +11304,7 @@ snapshots:
ws@8.17.1: {} ws@8.17.1: {}
ws@8.18.1: {} ws@8.18.2: {}
xml-js@1.6.11: xml-js@1.6.11:
dependencies: dependencies:

709
readme.md
View File

@ -1,11 +1,178 @@
# @push.rocks/smartproxy # @push.rocks/smartproxy
A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, and dynamic routing with authentication options. 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
## Exports
The following classes and interfaces are provided:
- **NetworkProxy** (ts/networkproxy/classes.np.networkproxy.ts)
HTTP/HTTPS reverse proxy with TLS termination, WebSocket support,
connection pooling, and optional ACME integration.
- **Port80Handler** (ts/port80handler/classes.port80handler.ts)
ACME HTTP-01 challenge handler and certificate manager.
- **NfTablesProxy** (ts/nfttablesproxy/classes.nftablesproxy.ts)
Low-level port forwarding using nftables NAT rules.
- **Redirect**, **SslRedirect** (ts/redirect/classes.redirect.ts)
HTTP/HTTPS redirect server and shortcut for HTTP→HTTPS.
- **SmartProxy** (ts/smartproxy/classes.smartproxy.ts)
TCP/SNI-based proxy with dynamic routing, IP filtering, and unified certificates.
- **SniHandler** (ts/smartproxy/classes.pp.snihandler.ts)
Static utilities to extract SNI hostnames from TLS handshakes.
- **Interfaces**
- IPortProxySettings, IDomainConfig (ts/smartproxy/classes.pp.interfaces.ts)
- INetworkProxyOptions (ts/networkproxy/classes.np.types.ts)
- IAcmeOptions, IDomainOptions, IForwardConfig (ts/common/types.ts)
- INfTableProxySettings (ts/nfttablesproxy/classes.nftablesproxy.ts)
## Installation
Install via npm:
```bash
npm install @push.rocks/smartproxy
```
## Quick Start
### 1. HTTP(S) Reverse Proxy (NetworkProxy)
```typescript
import { NetworkProxy } from '@push.rocks/smartproxy';
const proxy = new NetworkProxy({ port: 443 });
await proxy.start();
await proxy.updateProxyConfigs([
{
hostName: 'example.com',
destinationIps: ['127.0.0.1'],
destinationPorts: [3000],
publicKey: fs.readFileSync('cert.pem', 'utf8'),
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';
// 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}`);
});
await acme.start();
acme.addDomain({
domainName: 'example.com',
sslRedirect: true,
acmeMaintenance: true
});
```
### 4. Low-Level Port Forwarding (NfTablesProxy)
```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
});
await nft.start();
// ...
await nft.stop();
```
### 5. TCP/SNI Proxy (SmartProxy)
```typescript
import { SmartProxy } from '@push.rocks/smartproxy';
const smart = new SmartProxy({
fromPort: 443,
toPort: 8443,
domainConfigs: [
{
domains: ['example.com', '*.example.com'],
allowedIPs: ['*'],
targetIPs: ['127.0.0.1'],
}
],
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);
```
## API Reference
For full configuration options and type definitions, see the TypeScript interfaces in the `ts/` directory:
- `INetworkProxyOptions` (ts/networkproxy/classes.np.types.ts)
- `IAcmeOptions`, `IDomainOptions`, `IForwardConfig` (ts/common/types.ts)
- `INfTableProxySettings` (ts/nfttablesproxy/classes.nftablesproxy.ts)
- `IPortProxySettings`, `IDomainConfig` (ts/smartproxy/classes.pp.interfaces.ts)
## Architecture & Flow Diagrams ## Architecture & Flow Diagrams
### Component Architecture
The diagram below illustrates the main components of SmartProxy and how they interact:
```mermaid ```mermaid
flowchart TB flowchart TB
@ -13,12 +180,12 @@ flowchart TB
subgraph "SmartProxy Components" subgraph "SmartProxy Components"
direction TB direction TB
HTTP80[HTTP Port 80\nSslRedirect] HTTP80["HTTP Port 80<br>Redirect / SslRedirect"]
HTTPS443[HTTPS Port 443\nNetworkProxy] HTTPS443["HTTPS Port 443<br>NetworkProxy"]
SmartProxy[SmartProxy\nwith SNI routing] SmartProxy["SmartProxy<br>(TCP/SNI Proxy)"]
NfTables[NfTablesProxy] NfTables[NfTablesProxy]
Router[ProxyRouter] Router[ProxyRouter]
ACME[Port80Handler\nACME/Let's Encrypt] ACME["Port80Handler<br>(ACME HTTP-01)"]
Certs[(SSL Certificates)] Certs[(SSL Certificates)]
end end
@ -190,470 +357,104 @@ sequenceDiagram
## Features ## Features
- **HTTPS Reverse Proxy** - Route traffic to backend services based on hostname with TLS termination - HTTP/HTTPS Reverse Proxy (NetworkProxy)
- **WebSocket Support** - Full WebSocket proxying with heartbeat monitoring • TLS termination, virtual-host routing, HTTP/2 & WebSocket support, pooling & metrics
- **TCP Connection Handling** - Advanced connection handling with SNI inspection and domain-based routing
- **Enhanced TLS Handling** - Robust TLS handshake processing with improved certificate error handling
- **HTTP to HTTPS Redirection** - Automatically redirect HTTP requests to HTTPS
- **Let's Encrypt Integration** - Automatic certificate management using ACME protocol
- **IP Filtering** - Control access with IP allow/block lists using glob patterns
- **NfTables Integration** - Direct manipulation of nftables for advanced low-level port forwarding
## Certificate Provider Hook & Events - Automatic ACME Certificates (Port80Handler)
• HTTP-01 challenge handling, certificate issuance/renewal, pluggable storage
You can customize how certificates are provisioned per domain by using the `certProvider` callback and listen for certificate events emitted by `SmartProxy`. - Low-Level Port Forwarding (NfTablesProxy)
• nftables NAT rules for ports/ranges, IPv4/IPv6, IP filtering, QoS & ipset support
```typescript - Custom Redirects (Redirect / SslRedirect)
import { SmartProxy } from '@push.rocks/smartproxy'; • URL redirects with wildcard host/path, template variables & status codes
import * as fs from 'fs';
// Example certProvider: static for a specific domain, HTTP-01 otherwise - TCP/SNI Proxy (SmartProxy)
const certProvider = async (domain: string) => { • SNI-based routing, IP allow/block lists, port ranges, timeouts & graceful shutdown
if (domain === 'static.example.com') {
// Load from disk or vault
return {
id: 'static-cert',
domainName: domain,
created: Date.now(),
validUntil: Date.now() + 90 * 24 * 60 * 60 * 1000,
privateKey: fs.readFileSync('/etc/ssl/private/static.key', 'utf8'),
publicKey: fs.readFileSync('/etc/ssl/certs/static.crt', 'utf8'),
csr: ''
};
}
// Fallback to ACME HTTP-01 challenge
return 'http01';
};
const proxy = new SmartProxy({ - SNI Utilities (SniHandler)
fromPort: 80, • Robust ClientHello parsing, fragmentation & session resumption support
toPort: 8080,
domainConfigs: [{
domains: ['static.example.com', 'dynamic.example.com'],
allowedIPs: ['*']
}],
certProvider
});
// Listen for certificate issuance or renewal ## Certificate Hooks & Events
proxy.on('certificate', (evt) => {
console.log(`Certificate for ${evt.domain} ready, expires on ${evt.expiryDate}`);
});
await proxy.start(); Listen for certificate events via EventEmitter:
``` - **Port80Handler**:
- `certificate-issued`, `certificate-renewed`, `certificate-failed`
- `manager-started`, `manager-stopped`, `request-forwarded`
- **SmartProxy**:
- `certificate` (domain, publicKey, privateKey, expiryDate, source, isRenewal)
Provide a `certProvider(domain)` in SmartProxy settings to supply static certs or return `'http01'`.
## Configuration Options ## Configuration Options
### backendProtocol ### NetworkProxy (INetworkProxyOptions)
- `port` (number, required)
Type: 'http1' | 'http2' (default: 'http1') - `backendProtocol` ('http1'|'http2', default 'http1')
- `maxConnections` (number, default 10000)
Controls the protocol used when proxying requests to backend services. By default, the proxy uses HTTP/1.x (`http.request`). Setting `backendProtocol: 'http2'` establishes HTTP/2 client sessions (`http2.connect`) to your backends for full end-to-end HTTP/2 support (assuming your backend servers support HTTP/2). - `keepAliveTimeout` (ms, default 120000)
- `headersTimeout` (ms, default 60000)
Example: - `cors` (object)
```js - `connectionPoolSize` (number, default 50)
import { NetworkProxy } from '@push.rocks/smartproxy'; - `logLevel` ('error'|'warn'|'info'|'debug')
- `acme` (IAcmeOptions)
const proxy = new NetworkProxy({ - `useExternalPort80Handler` (boolean)
port: 8443, - `portProxyIntegration` (boolean)
backendProtocol: 'http2',
// other options... ### Port80Handler (IAcmeOptions)
}); - `enabled` (boolean, default true)
proxy.start(); - `port` (number, default 80)
``` - `contactEmail` (string)
- **Basic Authentication** - Support for basic auth on proxied routes - `useProduction` (boolean, default false)
- **Connection Management** - Intelligent connection tracking and cleanup with configurable timeouts - `renewThresholdDays` (number, default 30)
- **Browser Compatibility** - Optimized for modern browsers with fixes for common TLS handshake issues - `autoRenew` (boolean, default true)
- `certificateStore` (string)
## Installation - `skipConfiguredCerts` (boolean)
- `domainForwards` (IDomainForwardConfig[])
```bash
npm install @push.rocks/smartproxy ### NfTablesProxy (INfTableProxySettings)
``` - `fromPort` / `toPort` (number|range|array)
- `toHost` (string, default 'localhost')
## Usage - `preserveSourceIP`, `deleteOnExit`, `protocol`, `enableLogging`, `ipv6Support` (booleans)
- `allowedSourceIPs`, `bannedSourceIPs` (string[])
### Basic Reverse Proxy Setup - `useIPSets` (boolean, default true)
- `qos`, `netProxyIntegration` (objects)
```typescript
import { NetworkProxy } from '@push.rocks/smartproxy'; ### Redirect / SslRedirect
- Constructor options: `httpPort`, `httpsPort`, `sslOptions`, `rules` (RedirectRule[])
// Create a reverse proxy listening on port 443
const proxy = new NetworkProxy({ ### SmartProxy (IPortProxySettings)
port: 443 - `fromPort`, `toPort` (number)
}); - `domainConfigs` (IDomainConfig[])
- `sniEnabled`, `defaultAllowedIPs`, `preserveSourceIP` (booleans)
// Define reverse proxy configurations - Timeouts: `initialDataTimeout`, `socketTimeout`, `inactivityTimeout`, etc.
const proxyConfigs = [ - Socket opts: `noDelay`, `keepAlive`, `enableKeepAliveProbes`
{ - `acme` (IAcmeOptions), `certProvider` (callback)
hostName: 'example.com', - `useNetworkProxy` (number[]), `networkProxyPort` (number)
destinationIps: ['127.0.0.1'],
destinationPorts: [3000],
publicKey: 'your-cert-content',
privateKey: 'your-key-content',
rewriteHostHeader: true
},
{
hostName: 'api.example.com',
destinationIps: ['127.0.0.1'],
destinationPorts: [4000],
publicKey: 'your-cert-content',
privateKey: 'your-key-content',
// Optional basic auth
authentication: {
type: 'Basic',
user: 'admin',
pass: 'secret'
}
}
];
// Start the proxy and update configurations
(async () => {
await proxy.start();
await proxy.updateProxyConfigs(proxyConfigs);
// Add default headers to all responses
await proxy.addDefaultHeaders({
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains; preload'
});
})();
```
### HTTP to HTTPS Redirection
```typescript
import { SslRedirect } from '@push.rocks/smartproxy';
// Create and start HTTP to HTTPS redirect service on port 80
const redirector = new SslRedirect(80);
redirector.start();
```
### TCP Connection Handling with Domain-based Routing
```typescript
import { SmartProxy } from '@push.rocks/smartproxy';
// Configure SmartProxy with domain-based routing
const smartProxy = new SmartProxy({
fromPort: 443,
toPort: 8443,
targetIP: 'localhost', // Default target host
sniEnabled: true, // Enable SNI inspection
// Enhanced reliability settings
initialDataTimeout: 60000, // 60 seconds for initial TLS handshake
socketTimeout: 3600000, // 1 hour socket timeout
maxConnectionLifetime: 3600000, // 1 hour connection lifetime
inactivityTimeout: 3600000, // 1 hour inactivity timeout
maxPendingDataSize: 10 * 1024 * 1024, // 10MB buffer for large TLS handshakes
// Browser compatibility enhancement
enableTlsDebugLogging: false, // Enable for troubleshooting TLS issues
// Port and IP configuration
globalPortRanges: [{ from: 443, to: 443 }],
defaultAllowedIPs: ['*'], // Allow all IPs by default
// Socket optimizations for better connection stability
noDelay: true, // Disable Nagle's algorithm
keepAlive: true, // Enable TCP keepalive
enableKeepAliveProbes: true, // Enhanced keepalive for stability
// Domain-specific routing configuration
domainConfigs: [
{
domains: ['example.com', '*.example.com'], // Glob patterns for matching domains
allowedIPs: ['192.168.1.*'], // Restrict access by IP
blockedIPs: ['192.168.1.100'], // Block specific IPs
targetIPs: ['10.0.0.1', '10.0.0.2'], // Round-robin between multiple targets
portRanges: [{ from: 443, to: 443 }],
connectionTimeout: 7200000 // Domain-specific timeout (2 hours)
}
],
preserveSourceIP: true
});
smartProxy.start();
```
### NfTables Port Forwarding
```typescript
import { NfTablesProxy } from '@push.rocks/smartproxy';
// Basic usage - forward single port
const basicProxy = new NfTablesProxy({
fromPort: 80,
toPort: 8080,
toHost: 'localhost',
preserveSourceIP: true,
deleteOnExit: true // Automatically clean up rules on process exit
});
// Forward port ranges
const rangeProxy = new NfTablesProxy({
fromPort: { from: 3000, to: 3010 }, // Forward ports 3000-3010
toPort: { from: 8000, to: 8010 }, // To ports 8000-8010
protocol: 'tcp', // TCP protocol (default)
ipv6Support: true, // Enable IPv6 support
enableLogging: true // Enable detailed logging
});
// Multiple port specifications with IP filtering
const advancedProxy = new NfTablesProxy({
fromPort: [80, 443, { from: 8000, to: 8010 }], // Multiple ports/ranges
toPort: [8080, 8443, { from: 18000, to: 18010 }],
allowedSourceIPs: ['10.0.0.0/8', '192.168.1.0/24'], // Only allow these IPs
bannedSourceIPs: ['192.168.1.100'], // Explicitly block these IPs
useIPSets: true, // Use IP sets for efficient IP management
forceCleanSlate: false // Clean all NfTablesProxy rules before starting
});
// Advanced features: QoS, connection tracking, and NetworkProxy integration
const advancedProxy = new NfTablesProxy({
fromPort: 443,
toPort: 8443,
toHost: 'localhost',
useAdvancedNAT: true, // Use connection tracking for stateful NAT
qos: {
enabled: true,
maxRate: '10mbps', // Limit bandwidth
priority: 1 // Set traffic priority (1-10)
},
netProxyIntegration: {
enabled: true,
redirectLocalhost: true, // Redirect localhost traffic to NetworkProxy
sslTerminationPort: 8443 // Port where NetworkProxy handles SSL
}
});
// Start any of the proxies
await basicProxy.start();
```
### Automatic HTTPS Certificate Management
```typescript
import { Port80Handler } from '@push.rocks/smartproxy';
// Create an ACME handler for Let's Encrypt
const acmeHandler = new Port80Handler({
port: 80,
contactEmail: 'admin@example.com',
useProduction: true, // Use Let's Encrypt production servers (default is staging)
renewThresholdDays: 30, // Renew certificates 30 days before expiry
httpsRedirectPort: 443 // Redirect HTTP to HTTPS on this port
});
// Add domains to manage certificates for
acmeHandler.addDomain({
domainName: 'example.com',
sslRedirect: true,
acmeMaintenance: true
});
acmeHandler.addDomain({
domainName: 'api.example.com',
sslRedirect: true,
acmeMaintenance: true
});
// Support for glob pattern domains for routing (certificates not issued for glob patterns)
acmeHandler.addDomain({
domainName: '*.example.com',
sslRedirect: true,
acmeMaintenance: false, // Can't issue certificates for wildcard domains via HTTP-01
forward: { ip: '192.168.1.10', port: 8080 } // Forward requests to this target
});
```
## Configuration Options
### NetworkProxy Options
| Option | Description | Default |
|----------------|---------------------------------------------------|---------|
| `port` | Port to listen on for HTTPS connections | - |
| `maxConnections` | Maximum concurrent connections | 10000 |
| `keepAliveTimeout` | Keep-alive timeout in milliseconds | 60000 |
| `headersTimeout` | Headers timeout in milliseconds | 60000 |
| `logLevel` | Logging level ('error', 'warn', 'info', 'debug') | 'info' |
| `cors` | CORS configuration object | - |
| `rewriteHostHeader` | Whether to rewrite the Host header | false |
### SmartProxy Settings
| Option | Description | Default |
|---------------------------|--------------------------------------------------------|-------------|
| `fromPort` | Port to listen on | - |
| `toPort` | Destination port to forward to | - |
| `targetIP` | Default destination IP if not specified in domainConfig | 'localhost' |
| `sniEnabled` | Enable SNI inspection for TLS connections | false |
| `defaultAllowedIPs` | IP patterns allowed by default | - |
| `defaultBlockedIPs` | IP patterns blocked by default | - |
| `preserveSourceIP` | Preserve the original client IP | false |
| `maxConnectionLifetime` | Maximum time in ms to keep a connection open | 3600000 |
| `initialDataTimeout` | Timeout for initial data/handshake in ms | 60000 |
| `socketTimeout` | Socket inactivity timeout in ms | 3600000 |
| `inactivityTimeout` | Connection inactivity check timeout in ms | 3600000 |
| `inactivityCheckInterval` | How often to check for inactive connections in ms | 60000 |
| `maxPendingDataSize` | Maximum bytes to buffer during connection setup | 10485760 |
| `globalPortRanges` | Array of port ranges to listen on | - |
| `forwardAllGlobalRanges` | Forward all global range connections to targetIP | false |
| `gracefulShutdownTimeout` | Time in ms to wait during shutdown | 30000 |
| `noDelay` | Disable Nagle's algorithm | true |
| `keepAlive` | Enable TCP keepalive | true |
| `keepAliveInitialDelay` | Initial delay before sending keepalive probes in ms | 30000 |
| `enableKeepAliveProbes` | Enable enhanced TCP keep-alive probes | false |
| `enableTlsDebugLogging` | Enable detailed TLS handshake debugging | false |
| `enableDetailedLogging` | Enable detailed connection logging | false |
| `enableRandomizedTimeouts`| Randomize timeouts slightly to prevent thundering herd | true |
### NfTablesProxy Settings
| Option | Description | Default |
|-----------------------|---------------------------------------------------|-------------|
| `fromPort` | Source port(s) or range(s) to forward from | - |
| `toPort` | Destination port(s) or range(s) to forward to | - |
| `toHost` | Destination host to forward to | 'localhost' |
| `preserveSourceIP` | Preserve the original client IP | false |
| `deleteOnExit` | Remove nftables rules when process exits | false |
| `protocol` | Protocol to forward ('tcp', 'udp', or 'all') | 'tcp' |
| `enableLogging` | Enable detailed logging | false |
| `logFormat` | Format for logs ('plain' or 'json') | 'plain' |
| `ipv6Support` | Enable IPv6 support | false |
| `allowedSourceIPs` | Array of IP addresses/CIDR allowed to connect | - |
| `bannedSourceIPs` | Array of IP addresses/CIDR blocked from connecting | - |
| `useIPSets` | Use nftables sets for efficient IP management | true |
| `forceCleanSlate` | Clear all NfTablesProxy rules before starting | false |
| `tableName` | Custom table name | 'portproxy' |
| `maxRetries` | Maximum number of retries for failed commands | 3 |
| `retryDelayMs` | Delay between retries in milliseconds | 1000 |
| `useAdvancedNAT` | Use connection tracking for stateful NAT | false |
| `qos` | Quality of Service options (object) | - |
| `netProxyIntegration` | NetworkProxy integration options (object) | - |
## Advanced Features
### TLS Handshake Optimization
The enhanced `SmartProxy` implementation includes significant improvements for TLS handshake handling:
- Robust SNI extraction with improved error handling
- Increased buffer size for complex TLS handshakes (10MB)
- Longer initial handshake timeout (60 seconds)
- Detection and tracking of TLS connection states
- Optional detailed TLS debug logging for troubleshooting
- Browser compatibility fixes for Chrome certificate errors
```typescript
// Example configuration to solve Chrome certificate errors
const portProxy = new SmartProxy({
// ... other settings
initialDataTimeout: 60000, // Give browser more time for handshake
maxPendingDataSize: 10 * 1024 * 1024, // Larger buffer for complex handshakes
enableTlsDebugLogging: true, // Enable when troubleshooting
});
```
### Connection Management and Monitoring
The `SmartProxy` class includes built-in connection tracking and monitoring:
- Automatic cleanup of idle connections with configurable timeouts
- Timeouts for connections that exceed maximum lifetime
- Detailed logging of connection states
- Termination statistics
- Randomized timeouts to prevent "thundering herd" problems
- Per-domain timeout configuration
### WebSocket Support
The `NetworkProxy` class provides WebSocket support with:
- WebSocket connection proxying
- Automatic heartbeat monitoring
- Connection cleanup for inactive WebSockets
### SNI-based Routing
The `SmartProxy` class can inspect the SNI (Server Name Indication) field in TLS handshakes to route connections based on the requested domain:
- Multiple backend targets per domain
- Round-robin load balancing
- Domain-specific allowed IP ranges
- Protection against SNI renegotiation attacks
### Enhanced NfTables Management
The `NfTablesProxy` class offers advanced capabilities:
- Support for multiple port ranges and individual ports
- More efficient IP filtering using nftables sets
- IPv6 support with full feature parity
- Quality of Service (QoS) features including bandwidth limiting and traffic prioritization
- Advanced connection tracking for stateful NAT
- Robust error handling with retry mechanisms
- Structured logging with JSON support
- NetworkProxy integration for SSL termination
- Comprehensive cleanup on shutdown
### Port80Handler with Glob Pattern Support
The `Port80Handler` class includes support for glob pattern domain matching:
- Supports wildcard domains like `*.example.com` for HTTP request routing
- Detects glob patterns and skips certificate issuance for them
- Smart routing that first attempts exact matches, then tries pattern matching
- Supports forwarding HTTP requests to backend services
- Separate forwarding configuration for ACME challenges
## Troubleshooting ## Troubleshooting
### Browser Certificate Errors ### NetworkProxy
- Verify ports, certificates and `rejectUnauthorized` for TLS errors
- Configure CORS or use `addDefaultHeaders` for preflight issues
- Increase `maxConnections` or `connectionPoolSize` under load
If you experience certificate errors in browsers, especially in Chrome, try these solutions: ### Port80Handler
- Run as root or grant CAP_NET_BIND_SERVICE for port 80
- Inspect `certificate-failed` events and switch staging/production
1. **Increase Initial Data Timeout**: Set `initialDataTimeout` to 60 seconds or higher ### NfTablesProxy
2. **Increase Buffer Size**: Set `maxPendingDataSize` to 10MB or higher - Ensure `nft` is installed and run with sufficient privileges
3. **Enable TLS Debug Logging**: Set `enableTlsDebugLogging: true` to troubleshoot handshake issues - Use `forceCleanSlate:true` to clear conflicting rules
4. **Enable Keep-Alive Probes**: Set `enableKeepAliveProbes: true` for better connection stability
5. **Check Certificate Chain**: Ensure your certificate chain is complete and in the correct order
```typescript ### Redirect / SslRedirect
// Configuration to fix Chrome certificate errors - Check `fromHost`/`fromPath` patterns and Host headers
const smartProxy = new SmartProxy({ - Validate `sslOptions` key/cert correctness
// ... other settings
initialDataTimeout: 60000,
maxPendingDataSize: 10 * 1024 * 1024,
enableTlsDebugLogging: true,
enableKeepAliveProbes: true
});
```
### Connection Stability ### SmartProxy & SniHandler
- Increase `initialDataTimeout`/`maxPendingDataSize` for large ClientHello
For improved connection stability in high-traffic environments: - Enable `enableTlsDebugLogging` to trace handshake
- Ensure `allowSessionTicket` and fragmentation support for resumption
1. **Set Appropriate Timeouts**: Use longer timeouts for long-lived connections
2. **Use Domain-Specific Timeouts**: Configure per-domain timeouts for different types of services
3. **Enable TCP Keep-Alive**: Ensure `keepAlive` is set to `true`
4. **Monitor Connection Statistics**: Enable detailed logging to track termination reasons
5. **Fine-tune Inactivity Checks**: Adjust `inactivityCheckInterval` based on your traffic patterns
### NfTables Troubleshooting
If you're experiencing issues with NfTablesProxy:
1. **Enable Detailed Logging**: Set `enableLogging: true` to see all rule operations
2. **Force Clean Slate**: Use `forceCleanSlate: true` to remove any lingering rules
3. **Use IP Sets**: Enable `useIPSets: true` for cleaner rule management
4. **Check Permissions**: Ensure your process has sufficient permissions to modify nftables
5. **Verify IPv6 Support**: If using `ipv6Support: true`, ensure ip6tables is available
## License and Legal Information ## License and Legal Information

View File

@ -15,11 +15,11 @@ This document outlines a roadmap to simplify and refactor the SmartProxy & Netwo
- [x] Consolidate ACME/Port80Handler logic: - [x] Consolidate ACME/Port80Handler logic:
- [x] Merge standalone Port80Handler into a single certificate service - [x] Merge standalone Port80Handler into a single certificate service
- [x] Remove duplicate ACME setup in SmartProxy and NetworkProxy - [x] Remove duplicate ACME setup in SmartProxy and NetworkProxy
- [ ] Unify configuration options: - [x] Unify configuration options:
- [x] Merge `INetworkProxyOptions.acme`, `IPort80HandlerOptions`, and `port80HandlerConfig` into one schema - [x] Merge `INetworkProxyOptions.acme`, `IPort80HandlerOptions`, and `port80HandlerConfig` into one schema
- [ ] Deprecate old option names and provide clear upgrade path - [x] Deprecate old option names and provide clear upgrade path
- [ ] Centralize plugin imports in `ts/plugins.ts` and update all modules to use it - [x] Centralize plugin imports in `ts/plugins.ts` and update all modules to use it
- [ ] Remove legacy or unused code paths (e.g., old HTTP/2 fallback logic if obsolete) - [x] Remove legacy or unused code paths (e.g., old HTTP/2 fallback logic if obsolete)
- [ ] Enhance and expand test coverage: - [ ] Enhance and expand test coverage:
- Add unit tests for certificate issuance, renewal, and error handling - Add unit tests for certificate issuance, renewal, and error handling
- Add integration tests for HTTP challenge routing and request forwarding - Add integration tests for HTTP challenge routing and request forwarding

View File

@ -1,45 +0,0 @@
import { tap, expect } from '@push.rocks/tapbundle';
import { SmartProxy } from '../ts/smartproxy/classes.smartproxy.js';
tap.test('performRenewals only renews domains below threshold', async () => {
// Set up SmartProxy instance without real servers
const proxy = new SmartProxy({
fromPort: 0,
toPort: 0,
domainConfigs: [],
sniEnabled: false,
defaultAllowedIPs: [],
globalPortRanges: []
});
// Stub port80Handler status and renewal
const statuses = new Map<string, any>();
const now = new Date();
statuses.set('expiring.com', {
certObtained: true,
expiryDate: new Date(now.getTime() + 2 * 24 * 60 * 60 * 1000),
obtainingInProgress: false
});
statuses.set('ok.com', {
certObtained: true,
expiryDate: new Date(now.getTime() + 100 * 24 * 60 * 60 * 1000),
obtainingInProgress: false
});
const renewed: string[] = [];
// Inject fake handler
(proxy as any).port80Handler = {
getDomainCertificateStatus: () => statuses,
renewCertificate: async (domain: string) => { renewed.push(domain); }
};
// Configure threshold
proxy.settings.port80HandlerConfig.enabled = true;
proxy.settings.port80HandlerConfig.autoRenew = true;
proxy.settings.port80HandlerConfig.renewThresholdDays = 10;
// Execute renewals
await (proxy as any).performRenewals();
// Only the expiring.com domain should be renewed
expect(renewed).toEqual(['expiring.com']);
});
export default tap.start();

View File

@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartproxy', name: '@push.rocks/smartproxy',
version: '9.0.0', version: '10.0.8',
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.' 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,4 @@
import * as http from 'http'; import * as plugins from './plugins.js';
import * as url from 'url';
import * as tsclass from '@tsclass/tsclass';
/** /**
* Optional path pattern configuration that can be added to proxy configs * Optional path pattern configuration that can be added to proxy configs
@ -13,7 +11,7 @@ export interface IPathPatternConfig {
* Interface for router result with additional metadata * Interface for router result with additional metadata
*/ */
export interface IRouterResult { export interface IRouterResult {
config: tsclass.network.IReverseProxyConfig; config: plugins.tsclass.network.IReverseProxyConfig;
pathMatch?: string; pathMatch?: string;
pathParams?: Record<string, string>; pathParams?: Record<string, string>;
pathRemainder?: string; pathRemainder?: string;
@ -36,11 +34,11 @@ export interface IRouterResult {
*/ */
export class ProxyRouter { export class ProxyRouter {
// Store original configs for reference // Store original configs for reference
private reverseProxyConfigs: tsclass.network.IReverseProxyConfig[] = []; private reverseProxyConfigs: plugins.tsclass.network.IReverseProxyConfig[] = [];
// Default config to use when no match is found (optional) // Default config to use when no match is found (optional)
private defaultConfig?: tsclass.network.IReverseProxyConfig; private defaultConfig?: plugins.tsclass.network.IReverseProxyConfig;
// Store path patterns separately since they're not in the original interface // Store path patterns separately since they're not in the original interface
private pathPatterns: Map<tsclass.network.IReverseProxyConfig, string> = new Map(); private pathPatterns: Map<plugins.tsclass.network.IReverseProxyConfig, string> = new Map();
// Logger interface // Logger interface
private logger: { private logger: {
error: (message: string, data?: any) => void; error: (message: string, data?: any) => void;
@ -50,7 +48,7 @@ export class ProxyRouter {
}; };
constructor( constructor(
configs?: tsclass.network.IReverseProxyConfig[], configs?: plugins.tsclass.network.IReverseProxyConfig[],
logger?: { logger?: {
error: (message: string, data?: any) => void; error: (message: string, data?: any) => void;
warn: (message: string, data?: any) => void; warn: (message: string, data?: any) => void;
@ -68,7 +66,7 @@ export class ProxyRouter {
* Sets a new set of reverse configs to be routed to * Sets a new set of reverse configs to be routed to
* @param reverseCandidatesArg Array of reverse proxy configurations * @param reverseCandidatesArg Array of reverse proxy configurations
*/ */
public setNewProxyConfigs(reverseCandidatesArg: tsclass.network.IReverseProxyConfig[]): void { public setNewProxyConfigs(reverseCandidatesArg: plugins.tsclass.network.IReverseProxyConfig[]): void {
this.reverseProxyConfigs = [...reverseCandidatesArg]; this.reverseProxyConfigs = [...reverseCandidatesArg];
// Find default config if any (config with "*" as hostname) // Find default config if any (config with "*" as hostname)
@ -82,7 +80,7 @@ export class ProxyRouter {
* @param req The incoming HTTP request * @param req The incoming HTTP request
* @returns The matching proxy config or undefined if no match found * @returns The matching proxy config or undefined if no match found
*/ */
public routeReq(req: http.IncomingMessage): tsclass.network.IReverseProxyConfig { public routeReq(req: plugins.http.IncomingMessage): plugins.tsclass.network.IReverseProxyConfig {
const result = this.routeReqWithDetails(req); const result = this.routeReqWithDetails(req);
return result ? result.config : undefined; return result ? result.config : undefined;
} }
@ -92,7 +90,7 @@ export class ProxyRouter {
* @param req The incoming HTTP request * @param req The incoming HTTP request
* @returns Detailed routing result including matched config and path information * @returns Detailed routing result including matched config and path information
*/ */
public routeReqWithDetails(req: http.IncomingMessage): IRouterResult | undefined { public routeReqWithDetails(req: plugins.http.IncomingMessage): IRouterResult | undefined {
// Extract and validate host header // Extract and validate host header
const originalHost = req.headers.host; const originalHost = req.headers.host;
if (!originalHost) { if (!originalHost) {
@ -101,7 +99,7 @@ export class ProxyRouter {
} }
// Parse URL for path matching // Parse URL for path matching
const parsedUrl = url.parse(req.url || '/'); const parsedUrl = plugins.url.parse(req.url || '/');
const urlPath = parsedUrl.pathname || '/'; const urlPath = parsedUrl.pathname || '/';
// Extract hostname without port // Extract hostname without port
@ -351,7 +349,7 @@ export class ProxyRouter {
* Gets all currently active proxy configurations * Gets all currently active proxy configurations
* @returns Array of all active configurations * @returns Array of all active configurations
*/ */
public getProxyConfigs(): tsclass.network.IReverseProxyConfig[] { public getProxyConfigs(): plugins.tsclass.network.IReverseProxyConfig[] {
return [...this.reverseProxyConfigs]; return [...this.reverseProxyConfigs];
} }
@ -375,7 +373,7 @@ export class ProxyRouter {
* @param pathPattern Optional path pattern for route matching * @param pathPattern Optional path pattern for route matching
*/ */
public addProxyConfig( public addProxyConfig(
config: tsclass.network.IReverseProxyConfig, config: plugins.tsclass.network.IReverseProxyConfig,
pathPattern?: string pathPattern?: string
): void { ): void {
this.reverseProxyConfigs.push(config); this.reverseProxyConfigs.push(config);
@ -393,7 +391,7 @@ export class ProxyRouter {
* @returns Boolean indicating if the config was found and updated * @returns Boolean indicating if the config was found and updated
*/ */
public setPathPattern( public setPathPattern(
config: tsclass.network.IReverseProxyConfig, config: plugins.tsclass.network.IReverseProxyConfig,
pathPattern: string pathPattern: string
): boolean { ): boolean {
const exists = this.reverseProxyConfigs.includes(config); const exists = this.reverseProxyConfigs.includes(config);

View File

@ -1,3 +1,5 @@
import * as plugins from '../plugins.js';
/** /**
* Shared types for certificate management and domain options * Shared types for certificate management and domain options
*/ */

View File

@ -1,7 +1,9 @@
export * from './nfttablesproxy/classes.nftablesproxy.js'; export * from './nfttablesproxy/classes.nftablesproxy.js';
export * from './networkproxy/classes.np.networkproxy.js'; export * from './networkproxy/index.js';
export * from './port80handler/classes.port80handler.js'; export * from './port80handler/classes.port80handler.js';
export * from './redirect/classes.redirect.js'; export * from './redirect/classes.redirect.js';
export * from './smartproxy/classes.smartproxy.js'; export * from './smartproxy/classes.smartproxy.js';
export * from './smartproxy/classes.pp.snihandler.js'; export * from './smartproxy/classes.pp.snihandler.js';
export * from './smartproxy/classes.pp.interfaces.js'; export * from './smartproxy/classes.pp.interfaces.js';
export * from './common/types.js';

View File

@ -2,7 +2,7 @@ import * as plugins from '../plugins.js';
import type { import type {
IConnectionRecord, IConnectionRecord,
IDomainConfig, IDomainConfig,
IPortProxySettings, ISmartProxyOptions,
} from './classes.pp.interfaces.js'; } from './classes.pp.interfaces.js';
import { ConnectionManager } from './classes.pp.connectionmanager.js'; import { ConnectionManager } from './classes.pp.connectionmanager.js';
import { SecurityManager } from './classes.pp.securitymanager.js'; import { SecurityManager } from './classes.pp.securitymanager.js';
@ -17,7 +17,7 @@ import { PortRangeManager } from './classes.pp.portrangemanager.js';
*/ */
export class ConnectionHandler { export class ConnectionHandler {
constructor( constructor(
private settings: IPortProxySettings, private settings: ISmartProxyOptions,
private connectionManager: ConnectionManager, private connectionManager: ConnectionManager,
private securityManager: SecurityManager, private securityManager: SecurityManager,
private domainConfigManager: DomainConfigManager, private domainConfigManager: DomainConfigManager,

View File

@ -1,5 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import type { IConnectionRecord, IPortProxySettings } from './classes.pp.interfaces.js'; import type { IConnectionRecord, ISmartProxyOptions } from './classes.pp.interfaces.js';
import { SecurityManager } from './classes.pp.securitymanager.js'; import { SecurityManager } from './classes.pp.securitymanager.js';
import { TimeoutManager } from './classes.pp.timeoutmanager.js'; import { TimeoutManager } from './classes.pp.timeoutmanager.js';
@ -14,7 +14,7 @@ export class ConnectionManager {
} = { incoming: {}, outgoing: {} }; } = { incoming: {}, outgoing: {} };
constructor( constructor(
private settings: IPortProxySettings, private settings: ISmartProxyOptions,
private securityManager: SecurityManager, private securityManager: SecurityManager,
private timeoutManager: TimeoutManager private timeoutManager: TimeoutManager
) {} ) {}

View File

@ -1,5 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import type { IDomainConfig, IPortProxySettings } from './classes.pp.interfaces.js'; import type { IDomainConfig, ISmartProxyOptions } from './classes.pp.interfaces.js';
/** /**
* Manages domain configurations and target selection * Manages domain configurations and target selection
@ -8,7 +8,7 @@ export class DomainConfigManager {
// Track round-robin indices for domain configs // Track round-robin indices for domain configs
private domainTargetIndices: Map<IDomainConfig, number> = new Map(); private domainTargetIndices: Map<IDomainConfig, number> = new Map();
constructor(private settings: IPortProxySettings) {} constructor(private settings: ISmartProxyOptions) {}
/** /**
* Updates the domain configurations * Updates the domain configurations

View File

@ -22,7 +22,7 @@ export interface IDomainConfig {
/** Port proxy settings including global allowed port ranges */ /** Port proxy settings including global allowed port ranges */
import type { IAcmeOptions } from '../common/types.js'; import type { IAcmeOptions } from '../common/types.js';
export interface IPortProxySettings { export interface ISmartProxyOptions {
fromPort: number; fromPort: number;
toPort: number; toPort: number;
targetIP?: string; // Global target host to proxy to, defaults to 'localhost' targetIP?: string; // Global target host to proxy to, defaults to 'localhost'
@ -91,7 +91,7 @@ export interface IPortProxySettings {
* Optional certificate provider callback. Return 'http01' to use HTTP-01 challenges, * Optional certificate provider callback. Return 'http01' to use HTTP-01 challenges,
* or a static certificate object for immediate provisioning. * or a static certificate object for immediate provisioning.
*/ */
certProvider?: (domain: string) => Promise<ISmartProxyCertProvisionObject>; certProvisionFunction?: (domain: string) => Promise<ISmartProxyCertProvisionObject>;
} }
/** /**

View File

@ -4,7 +4,7 @@ import { Port80Handler } from '../port80handler/classes.port80handler.js';
import { Port80HandlerEvents } from '../common/types.js'; import { Port80HandlerEvents } from '../common/types.js';
import { subscribeToPort80Handler } from '../common/eventUtils.js'; import { subscribeToPort80Handler } from '../common/eventUtils.js';
import type { ICertificateData } from '../common/types.js'; import type { ICertificateData } from '../common/types.js';
import type { IConnectionRecord, IPortProxySettings, IDomainConfig } from './classes.pp.interfaces.js'; import type { IConnectionRecord, ISmartProxyOptions, IDomainConfig } from './classes.pp.interfaces.js';
/** /**
* Manages NetworkProxy integration for TLS termination * Manages NetworkProxy integration for TLS termination
@ -13,7 +13,7 @@ export class NetworkProxyBridge {
private networkProxy: NetworkProxy | null = null; private networkProxy: NetworkProxy | null = null;
private port80Handler: Port80Handler | null = null; private port80Handler: Port80Handler | null = null;
constructor(private settings: IPortProxySettings) {} constructor(private settings: ISmartProxyOptions) {}
/** /**
* Set the Port80Handler to use for certificate management * Set the Port80Handler to use for certificate management

View File

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

View File

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

View File

@ -1,10 +1,10 @@
import type { IConnectionRecord, IPortProxySettings } from './classes.pp.interfaces.js'; import type { IConnectionRecord, ISmartProxyOptions } from './classes.pp.interfaces.js';
/** /**
* Manages timeouts and inactivity tracking for connections * Manages timeouts and inactivity tracking for connections
*/ */
export class TimeoutManager { export class TimeoutManager {
constructor(private settings: IPortProxySettings) {} constructor(private settings: ISmartProxyOptions) {}
/** /**
* Ensure timeout values don't exceed Node.js max safe integer * Ensure timeout values don't exceed Node.js max safe integer

View File

@ -1,4 +1,4 @@
import * as net from 'net'; import * as plugins from '../plugins.js';
/** /**
* TlsAlert class for managing TLS alert messages * TlsAlert class for managing TLS alert messages
@ -99,7 +99,7 @@ export class TlsAlert {
* @returns Promise that resolves when the alert has been sent * @returns Promise that resolves when the alert has been sent
*/ */
static async send( static async send(
socket: net.Socket, socket: plugins.net.Socket,
level: number, level: number,
description: number, description: number,
closeAfterSend: boolean = false, closeAfterSend: boolean = false,
@ -183,7 +183,7 @@ export class TlsAlert {
* @param socket The socket to send the alert to * @param socket The socket to send the alert to
* @returns Promise that resolves when the alert has been sent * @returns Promise that resolves when the alert has been sent
*/ */
static async sendSniRequired(socket: net.Socket): Promise<void> { static async sendSniRequired(socket: plugins.net.Socket): Promise<void> {
return this.send(socket, this.LEVEL_WARNING, this.UNRECOGNIZED_NAME); return this.send(socket, this.LEVEL_WARNING, this.UNRECOGNIZED_NAME);
} }
@ -194,7 +194,7 @@ export class TlsAlert {
* @param closeDelay Milliseconds to wait before closing the connection (default: 200ms) * @param closeDelay Milliseconds to wait before closing the connection (default: 200ms)
* @returns Promise that resolves when the alert has been sent and the connection closed * @returns Promise that resolves when the alert has been sent and the connection closed
*/ */
static async sendCloseNotify(socket: net.Socket, closeDelay: number = 200): Promise<void> { static async sendCloseNotify(socket: plugins.net.Socket, closeDelay: number = 200): Promise<void> {
return this.send(socket, this.LEVEL_WARNING, this.CLOSE_NOTIFY, true, closeDelay); return this.send(socket, this.LEVEL_WARNING, this.CLOSE_NOTIFY, true, closeDelay);
} }
@ -208,7 +208,7 @@ export class TlsAlert {
* @returns Promise that resolves when the alert has been sent * @returns Promise that resolves when the alert has been sent
*/ */
static async sendCertificateExpired( static async sendCertificateExpired(
socket: net.Socket, socket: plugins.net.Socket,
fatal: boolean = false, fatal: boolean = false,
closeAfterSend: boolean = true, closeAfterSend: boolean = true,
closeDelay: number = 200 closeDelay: number = 200
@ -224,7 +224,7 @@ export class TlsAlert {
* @param socket The socket to send the alerts to * @param socket The socket to send the alerts to
* @returns Promise that resolves when all alerts have been sent * @returns Promise that resolves when all alerts have been sent
*/ */
static async sendForceSniSequence(socket: net.Socket): Promise<void> { static async sendForceSniSequence(socket: plugins.net.Socket): Promise<void> {
try { try {
// Send unrecognized_name (warning) // Send unrecognized_name (warning)
socket.cork(); socket.cork();
@ -249,7 +249,7 @@ export class TlsAlert {
* @returns Promise that resolves when the alert has been sent and the connection closed * @returns Promise that resolves when the alert has been sent and the connection closed
*/ */
static async sendFatalAndClose( static async sendFatalAndClose(
socket: net.Socket, socket: plugins.net.Socket,
description: number, description: number,
closeDelay: number = 100 closeDelay: number = 100
): Promise<void> { ): Promise<void> {

View File

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

View File

@ -1,5 +1,5 @@
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import type { IPortProxySettings, IDomainConfig } from './classes.pp.interfaces.js';
import { ConnectionManager } from './classes.pp.connectionmanager.js'; import { ConnectionManager } from './classes.pp.connectionmanager.js';
import { SecurityManager } from './classes.pp.securitymanager.js'; import { SecurityManager } from './classes.pp.securitymanager.js';
import { DomainConfigManager } from './classes.pp.domainconfigmanager.js'; import { DomainConfigManager } from './classes.pp.domainconfigmanager.js';
@ -13,6 +13,9 @@ import { CertProvisioner } from './classes.pp.certprovisioner.js';
import type { ICertificateData } from '../common/types.js'; import type { ICertificateData } from '../common/types.js';
import { buildPort80Handler } from '../common/acmeFactory.js'; import { buildPort80Handler } from '../common/acmeFactory.js';
import type { ISmartProxyOptions, IDomainConfig } from './classes.pp.interfaces.js';
export type { ISmartProxyOptions as IPortProxySettings, IDomainConfig };
/** /**
* SmartProxy - Main class that coordinates all components * SmartProxy - Main class that coordinates all components
*/ */
@ -36,7 +39,7 @@ export class SmartProxy extends plugins.EventEmitter {
// CertProvisioner for unified certificate workflows // CertProvisioner for unified certificate workflows
private certProvisioner?: CertProvisioner; private certProvisioner?: CertProvisioner;
constructor(settingsArg: IPortProxySettings) { constructor(settingsArg: ISmartProxyOptions) {
super(); super();
// Set reasonable defaults for all settings // Set reasonable defaults for all settings
this.settings = { this.settings = {
@ -116,7 +119,7 @@ export class SmartProxy extends plugins.EventEmitter {
/** /**
* The settings for the port proxy * The settings for the port proxy
*/ */
public settings: IPortProxySettings; public settings: ISmartProxyOptions;
/** /**
* Initialize the Port80Handler for ACME certificate management * Initialize the Port80Handler for ACME certificate management
@ -162,7 +165,7 @@ export class SmartProxy extends plugins.EventEmitter {
this.settings.domainConfigs, this.settings.domainConfigs,
this.port80Handler, this.port80Handler,
this.networkProxyBridge, this.networkProxyBridge,
this.settings.certProvider, this.settings.certProvisionFunction,
acme.renewThresholdDays!, acme.renewThresholdDays!,
acme.renewCheckIntervalHours!, acme.renewCheckIntervalHours!,
acme.autoRenew!, acme.autoRenew!,
@ -390,9 +393,9 @@ export class SmartProxy extends plugins.EventEmitter {
for (const domain of domainConfig.domains) { for (const domain of domainConfig.domains) {
if (domain.includes('*')) continue; if (domain.includes('*')) continue;
let provision = 'http01' as string | plugins.tsclass.network.ICert; let provision = 'http01' as string | plugins.tsclass.network.ICert;
if (this.settings.certProvider) { if (this.settings.certProvisionFunction) {
try { try {
provision = await this.settings.certProvider(domain); provision = await this.settings.certProvisionFunction(domain);
} catch (err) { } catch (err) {
console.log(`certProvider error for ${domain}: ${err}`); console.log(`certProvider error for ${domain}: ${err}`);
} }
@ -422,27 +425,6 @@ export class SmartProxy extends plugins.EventEmitter {
} }
/**
* Perform scheduled renewals for managed domains
*/
private async performRenewals(): Promise<void> {
if (!this.port80Handler) return;
const statuses = this.port80Handler.getDomainCertificateStatus();
const threshold = this.settings.acme?.renewThresholdDays ?? 30;
const now = new Date();
for (const [domain, status] of statuses.entries()) {
if (!status.certObtained || status.obtainingInProgress || !status.expiryDate) continue;
const msRemaining = status.expiryDate.getTime() - now.getTime();
const daysRemaining = Math.ceil(msRemaining / (24 * 60 * 60 * 1000));
if (daysRemaining <= threshold) {
try {
await this.port80Handler.renewCertificate(domain);
} catch (err) {
console.error(`Error renewing certificate for ${domain}:`, err);
}
}
}
}
/** /**
* Request a certificate for a specific domain * Request a certificate for a specific domain
*/ */