Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
edd8ca8d70 | |||
8a396a04fa | |||
09aadc702e | |||
a59ebd6202 | |||
0d8740d812 | |||
e6a138279d |
18
changelog.md
18
changelog.md
@ -1,5 +1,23 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-05-02 - 8.0.0 - BREAKING CHANGE(certProvisioner)
|
||||||
|
Refactor: Introduce unified CertProvisioner to centralize certificate provisioning and renewal; remove legacy ACME config from Port80Handler and update SmartProxy to delegate certificate lifecycle management.
|
||||||
|
|
||||||
|
- Removed deprecated acme properties and renewal scheduler from IPort80HandlerOptions and Port80Handler.
|
||||||
|
- Created new CertProvisioner component in ts/smartproxy/classes.pp.certprovisioner.ts to handle static and HTTP-01 certificate workflows.
|
||||||
|
- Updated SmartProxy to initialize CertProvisioner and re-emit certificate events.
|
||||||
|
- Eliminated legacy renewal logic and associated autoRenew settings from Port80Handler.
|
||||||
|
- Adjusted tests to reflect changes in certificate provisioning and renewal behavior.
|
||||||
|
|
||||||
|
## 2025-05-01 - 7.2.0 - feat(ACME/Certificate)
|
||||||
|
Introduce certificate provider hook and observable certificate events; remove legacy ACME flow
|
||||||
|
|
||||||
|
- Extended IPortProxySettings with a new certProvider callback that allows returning a static certificate or 'http01' for ACME challenges.
|
||||||
|
- Updated Port80Handler to leverage SmartAcme's getCertificateForDomain and removed outdated methods such as getAcmeClient and processAuthorizations.
|
||||||
|
- Enhanced SmartProxy to extend EventEmitter, invoking certProvider on non-wildcard domains and re-emitting certificate events (with domain, publicKey, privateKey, expiryDate, source, and isRenewal flag).
|
||||||
|
- Updated NetworkProxyBridge to support applying external certificates via a new applyExternalCertificate method.
|
||||||
|
- Revised documentation (readme.md and readme.plan.md) to include usage examples for the new certificate provider hook.
|
||||||
|
|
||||||
## 2025-04-30 - 7.1.4 - fix(dependencies)
|
## 2025-04-30 - 7.1.4 - fix(dependencies)
|
||||||
Update dependency versions in package.json
|
Update dependency versions in package.json
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@push.rocks/smartproxy",
|
"name": "@push.rocks/smartproxy",
|
||||||
"version": "7.1.4",
|
"version": "8.0.0",
|
||||||
"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",
|
||||||
@ -18,21 +18,22 @@
|
|||||||
"@git.zone/tsbuild": "^2.3.2",
|
"@git.zone/tsbuild": "^2.3.2",
|
||||||
"@git.zone/tsrun": "^1.2.44",
|
"@git.zone/tsrun": "^1.2.44",
|
||||||
"@git.zone/tstest": "^1.0.77",
|
"@git.zone/tstest": "^1.0.77",
|
||||||
"@push.rocks/tapbundle": "^6.0.0",
|
"@push.rocks/tapbundle": "^6.0.3",
|
||||||
"@types/node": "^22.15.3",
|
"@types/node": "^22.15.3",
|
||||||
"typescript": "^5.8.3"
|
"typescript": "^5.8.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@push.rocks/lik": "^6.2.2",
|
"@push.rocks/lik": "^6.2.2",
|
||||||
|
"@push.rocks/smartacme": "^7.2.3",
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
"@push.rocks/smartnetwork": "^4.0.0",
|
"@push.rocks/smartnetwork": "^4.0.0",
|
||||||
"@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",
|
||||||
|
"@push.rocks/taskbuffer": "^3.1.7",
|
||||||
"@tsclass/tsclass": "^9.1.0",
|
"@tsclass/tsclass": "^9.1.0",
|
||||||
"@types/minimatch": "^5.1.2",
|
"@types/minimatch": "^5.1.2",
|
||||||
"@types/ws": "^8.18.1",
|
"@types/ws": "^8.18.1",
|
||||||
"acme-client": "^5.4.0",
|
|
||||||
"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.1"
|
||||||
|
422
pnpm-lock.yaml
generated
422
pnpm-lock.yaml
generated
@ -11,6 +11,9 @@ importers:
|
|||||||
'@push.rocks/lik':
|
'@push.rocks/lik':
|
||||||
specifier: ^6.2.2
|
specifier: ^6.2.2
|
||||||
version: 6.2.2
|
version: 6.2.2
|
||||||
|
'@push.rocks/smartacme':
|
||||||
|
specifier: ^7.2.3
|
||||||
|
version: 7.2.3(@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
|
||||||
@ -26,6 +29,9 @@ importers:
|
|||||||
'@push.rocks/smartstring':
|
'@push.rocks/smartstring':
|
||||||
specifier: ^4.0.15
|
specifier: ^4.0.15
|
||||||
version: 4.0.15
|
version: 4.0.15
|
||||||
|
'@push.rocks/taskbuffer':
|
||||||
|
specifier: ^3.1.7
|
||||||
|
version: 3.1.7
|
||||||
'@tsclass/tsclass':
|
'@tsclass/tsclass':
|
||||||
specifier: ^9.1.0
|
specifier: ^9.1.0
|
||||||
version: 9.1.0
|
version: 9.1.0
|
||||||
@ -35,9 +41,6 @@ importers:
|
|||||||
'@types/ws':
|
'@types/ws':
|
||||||
specifier: ^8.18.1
|
specifier: ^8.18.1
|
||||||
version: 8.18.1
|
version: 8.18.1
|
||||||
acme-client:
|
|
||||||
specifier: ^5.4.0
|
|
||||||
version: 5.4.0
|
|
||||||
minimatch:
|
minimatch:
|
||||||
specifier: ^10.0.1
|
specifier: ^10.0.1
|
||||||
version: 10.0.1
|
version: 10.0.1
|
||||||
@ -58,8 +61,8 @@ importers:
|
|||||||
specifier: ^1.0.77
|
specifier: ^1.0.77
|
||||||
version: 1.0.96(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)(typescript@5.8.3)
|
version: 1.0.96(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)(typescript@5.8.3)
|
||||||
'@push.rocks/tapbundle':
|
'@push.rocks/tapbundle':
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.3
|
||||||
version: 6.0.0(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)
|
version: 6.0.3(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.15.3
|
specifier: ^22.15.3
|
||||||
version: 22.15.3
|
version: 22.15.3
|
||||||
@ -81,9 +84,15 @@ packages:
|
|||||||
'@api.global/typedserver@3.0.68':
|
'@api.global/typedserver@3.0.68':
|
||||||
resolution: {integrity: sha512-7o6fkz60ed8q2lmEe44hsu/6kNqG4j5WVgWwmY+a1MmSOUtuu5+VTYYNyc8KrSgtbRBzx4+2A2N31l4wDjcy3w==}
|
resolution: {integrity: sha512-7o6fkz60ed8q2lmEe44hsu/6kNqG4j5WVgWwmY+a1MmSOUtuu5+VTYYNyc8KrSgtbRBzx4+2A2N31l4wDjcy3w==}
|
||||||
|
|
||||||
|
'@api.global/typedserver@3.0.74':
|
||||||
|
resolution: {integrity: sha512-lrXaCPaVZLihlF9w39pEqTw2kiHFCheRKTZuK07S7gTGyfdXKPmccVR/EK4ox58E1gjh9A2K8yY8ZWGcjuSJkw==}
|
||||||
|
|
||||||
'@api.global/typedsocket@3.0.1':
|
'@api.global/typedsocket@3.0.1':
|
||||||
resolution: {integrity: sha512-xojiAVNXtHoxkpBo8U2HHJG8FrVXXuLvDNndSHXwx4C9VslUwDn5zSCI+PdBl8iAg+ZuBmKjqkpZZ9sL6DC5yQ==}
|
resolution: {integrity: sha512-xojiAVNXtHoxkpBo8U2HHJG8FrVXXuLvDNndSHXwx4C9VslUwDn5zSCI+PdBl8iAg+ZuBmKjqkpZZ9sL6DC5yQ==}
|
||||||
|
|
||||||
|
'@apiclient.xyz/cloudflare@6.4.1':
|
||||||
|
resolution: {integrity: sha512-RYFphnbunjK+Imq/3ynIQpAvIGBJ38kqSZ2nrpTm26zsBIxW7S6xEe3zhXfVMtUIgC99OL3Xr/SGXl3CNBwCug==}
|
||||||
|
|
||||||
'@aws-crypto/crc32@5.2.0':
|
'@aws-crypto/crc32@5.2.0':
|
||||||
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
|
resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==}
|
||||||
engines: {node: '>=16.0.0'}
|
engines: {node: '>=16.0.0'}
|
||||||
@ -346,6 +355,9 @@ 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':
|
||||||
|
resolution: {integrity: sha512-JWAX7ZhQ7KjkdJwASgG58MZ/pQ15brlnZ9/0YBwDQ0hrJ/LaK392aTRFlj2r/PRKDZ5dOuujRywNYaNpfeFiEA==}
|
||||||
|
|
||||||
'@colors/colors@1.6.0':
|
'@colors/colors@1.6.0':
|
||||||
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
|
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
|
||||||
engines: {node: '>=0.1.90'}
|
engines: {node: '>=0.1.90'}
|
||||||
@ -724,6 +736,9 @@ packages:
|
|||||||
'@lit/reactive-element@2.0.4':
|
'@lit/reactive-element@2.0.4':
|
||||||
resolution: {integrity: sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==}
|
resolution: {integrity: sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==}
|
||||||
|
|
||||||
|
'@lit/reactive-element@2.1.0':
|
||||||
|
resolution: {integrity: sha512-L2qyoZSQClcBmq0qajBVbhYEcG6iK0XfLn66ifLe/RfC0/ihpc+pl0Wdn8bJ8o+hj38cG0fGXRgSS20MuXn7qA==}
|
||||||
|
|
||||||
'@mixmark-io/domino@2.2.0':
|
'@mixmark-io/domino@2.2.0':
|
||||||
resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
|
resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
|
||||||
|
|
||||||
@ -857,6 +872,9 @@ 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':
|
||||||
|
resolution: {integrity: sha512-PTwn/Zf7l+IMWqeiQ8mTxi7fdrtObQH13YzF65si/VxXTqHeZ7zvisLLKZcMEgSaOj1aQ/Ku83gaO8YqO4gDig==}
|
||||||
|
|
||||||
'@push.rocks/smartarchive@3.0.8':
|
'@push.rocks/smartarchive@3.0.8':
|
||||||
resolution: {integrity: sha512-1jPmR0b7hXmjYQoRiTlRXrIbZcdcFmSdGOfznufjcDpGPe86Km0d8TBnzqghTx4dTihzKC67IxAaz/DM3lvxpA==}
|
resolution: {integrity: sha512-1jPmR0b7hXmjYQoRiTlRXrIbZcdcFmSdGOfznufjcDpGPe86Km0d8TBnzqghTx4dTihzKC67IxAaz/DM3lvxpA==}
|
||||||
|
|
||||||
@ -887,6 +905,9 @@ packages:
|
|||||||
'@push.rocks/smartdelay@3.0.5':
|
'@push.rocks/smartdelay@3.0.5':
|
||||||
resolution: {integrity: sha512-mUuI7kj2f7ztjpic96FvRIlf2RsKBa5arw81AHNsndbxO6asRcxuWL8dTVxouEIK8YsBUlj0AsrCkHhMbLQdHw==}
|
resolution: {integrity: sha512-mUuI7kj2f7ztjpic96FvRIlf2RsKBa5arw81AHNsndbxO6asRcxuWL8dTVxouEIK8YsBUlj0AsrCkHhMbLQdHw==}
|
||||||
|
|
||||||
|
'@push.rocks/smartdns@6.2.2':
|
||||||
|
resolution: {integrity: sha512-MhJcHujbyIuwIIFdnXb2OScGtRjNsliLUS8GoAurFsKtcCOaA0ytfP+PNzkukyBufjb1nMiJF3rjhswXdHakAQ==}
|
||||||
|
|
||||||
'@push.rocks/smartenv@5.0.12':
|
'@push.rocks/smartenv@5.0.12':
|
||||||
resolution: {integrity: sha512-tDEFwywzq0FNzRYc9qY2dRl2pgQuZG0G2/yml2RLWZWSW+Fn1EHshnKOGHz8o77W7zvu4hTgQQX42r/JY5XHTg==}
|
resolution: {integrity: sha512-tDEFwywzq0FNzRYc9qY2dRl2pgQuZG0G2/yml2RLWZWSW+Fn1EHshnKOGHz8o77W7zvu4hTgQQX42r/JY5XHTg==}
|
||||||
|
|
||||||
@ -896,8 +917,8 @@ packages:
|
|||||||
'@push.rocks/smartexpect@1.6.1':
|
'@push.rocks/smartexpect@1.6.1':
|
||||||
resolution: {integrity: sha512-NFQXEPkGiMNxyvFwKyzDWe3ADYdf8KNvIcV7TGNZZT3uPQtk65te4Q+a1cWErjP/61yE9XdYiQA66QQp+TV9IQ==}
|
resolution: {integrity: sha512-NFQXEPkGiMNxyvFwKyzDWe3ADYdf8KNvIcV7TGNZZT3uPQtk65te4Q+a1cWErjP/61yE9XdYiQA66QQp+TV9IQ==}
|
||||||
|
|
||||||
'@push.rocks/smartexpect@2.2.2':
|
'@push.rocks/smartexpect@2.4.2':
|
||||||
resolution: {integrity: sha512-s2zJlLc6Wub7P/jgKSM51kW2UjslxQwx2BXoyJVO95OgiOwarde0AuxPR0lfRA/FvHdBfTmJf4upiWtcjYMB/Q==}
|
resolution: {integrity: sha512-L+aS1n5rWhf/yOh5R3zPgwycYtDr5FfrDWgasy6ShhN6Zbn/z/AOPbWcF/OpeTmx0XabWB2h5d4xBcCKLl47cQ==}
|
||||||
|
|
||||||
'@push.rocks/smartfeed@1.0.11':
|
'@push.rocks/smartfeed@1.0.11':
|
||||||
resolution: {integrity: sha512-02uhXxQamgfBo3T12FsAdfyElnpoWuDUb08B2AE60DbIaukVx/7Mi17xwobApY1flNSr5StZDt8N8vxPhBhIXw==}
|
resolution: {integrity: sha512-02uhXxQamgfBo3T12FsAdfyElnpoWuDUb08B2AE60DbIaukVx/7Mi17xwobApY1flNSr5StZDt8N8vxPhBhIXw==}
|
||||||
@ -1043,8 +1064,8 @@ packages:
|
|||||||
'@push.rocks/tapbundle@5.6.3':
|
'@push.rocks/tapbundle@5.6.3':
|
||||||
resolution: {integrity: sha512-hFzsf59rg1K70i45llj7PCyyCZp7JW19XRR+Q1gge1T0pBN8Wi53aYqP/2qtxdMiNVe2s3ESp6VJZv3sLOMYPQ==}
|
resolution: {integrity: sha512-hFzsf59rg1K70i45llj7PCyyCZp7JW19XRR+Q1gge1T0pBN8Wi53aYqP/2qtxdMiNVe2s3ESp6VJZv3sLOMYPQ==}
|
||||||
|
|
||||||
'@push.rocks/tapbundle@6.0.0':
|
'@push.rocks/tapbundle@6.0.3':
|
||||||
resolution: {integrity: sha512-ARIs189TysvI8EsPAC7LH6O0WbBYI9E7XxdihwmM6LRgLvzAbp1agfO6lOjpKrAYWKjT3KdlUEihilxOBrgTYQ==}
|
resolution: {integrity: sha512-SuP14V6TPdtd1y1CYTvwTKJdpHa7EzY55NfaaEMxW4oRKvHgJiOiPEiR/IrtL9tSiDMSfrx12waTMgZheYaBug==}
|
||||||
|
|
||||||
'@push.rocks/taskbuffer@3.1.7':
|
'@push.rocks/taskbuffer@3.1.7':
|
||||||
resolution: {integrity: sha512-QktGVJPucqQmW/QNGnscf4FAigT1H7JWKFGFdRuDEaOHKFh9qN+PXG3QY7DtZ4jfXdGLxPN4yAufDuPSAJYFnw==}
|
resolution: {integrity: sha512-QktGVJPucqQmW/QNGnscf4FAigT1H7JWKFGFdRuDEaOHKFh9qN+PXG3QY7DtZ4jfXdGLxPN4yAufDuPSAJYFnw==}
|
||||||
@ -1540,6 +1561,9 @@ packages:
|
|||||||
'@tsclass/tsclass@4.4.4':
|
'@tsclass/tsclass@4.4.4':
|
||||||
resolution: {integrity: sha512-YZOAF+u+r4u5rCev2uUd1KBTBdfyFdtDmcv4wuN+864lMccbdfRICR3SlJwCfYS1lbeV3QNLYGD30wjRXgvCJA==}
|
resolution: {integrity: sha512-YZOAF+u+r4u5rCev2uUd1KBTBdfyFdtDmcv4wuN+864lMccbdfRICR3SlJwCfYS1lbeV3QNLYGD30wjRXgvCJA==}
|
||||||
|
|
||||||
|
'@tsclass/tsclass@5.0.0':
|
||||||
|
resolution: {integrity: sha512-2X66VCk0Oe1L01j6GQHC6F9Gj7lpZPPSUTDNax7e29lm4OqBTyAzTR3ePR8coSbWBwsmRV8awLRSrSI+swlqWA==}
|
||||||
|
|
||||||
'@tsclass/tsclass@8.2.1':
|
'@tsclass/tsclass@8.2.1':
|
||||||
resolution: {integrity: sha512-bRDCfJTipsTcK6eEokWdsOR1mGCQFeM7zTg6PRHzbxTWQcWQD9AhEr2q3CrPcmAbvIS7fvkO6/pU/mPm1MZxhQ==}
|
resolution: {integrity: sha512-bRDCfJTipsTcK6eEokWdsOR1mGCQFeM7zTg6PRHzbxTWQcWQD9AhEr2q3CrPcmAbvIS7fvkO6/pU/mPm1MZxhQ==}
|
||||||
|
|
||||||
@ -1552,6 +1576,9 @@ packages:
|
|||||||
'@types/babel__code-frame@7.0.6':
|
'@types/babel__code-frame@7.0.6':
|
||||||
resolution: {integrity: sha512-Anitqkl3+KrzcW2k77lRlg/GfLZLWXBuNgbEcIOU6M92yw42vsd3xV/Z/yAHEj8m+KUjL6bWOVOFqX8PFPJ4LA==}
|
resolution: {integrity: sha512-Anitqkl3+KrzcW2k77lRlg/GfLZLWXBuNgbEcIOU6M92yw42vsd3xV/Z/yAHEj8m+KUjL6bWOVOFqX8PFPJ4LA==}
|
||||||
|
|
||||||
|
'@types/bn.js@5.1.6':
|
||||||
|
resolution: {integrity: sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==}
|
||||||
|
|
||||||
'@types/body-parser@1.19.5':
|
'@types/body-parser@1.19.5':
|
||||||
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
|
resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==}
|
||||||
|
|
||||||
@ -1606,6 +1633,12 @@ packages:
|
|||||||
'@types/default-gateway@7.2.2':
|
'@types/default-gateway@7.2.2':
|
||||||
resolution: {integrity: sha512-35C93fYQlnLKLASkMPoxRvok4fENwB3By9clRLd2I/08n/XRl0pCdf7EB17K5oMMwZu8NBYA8i66jH5r/LYBKA==}
|
resolution: {integrity: sha512-35C93fYQlnLKLASkMPoxRvok4fENwB3By9clRLd2I/08n/XRl0pCdf7EB17K5oMMwZu8NBYA8i66jH5r/LYBKA==}
|
||||||
|
|
||||||
|
'@types/dns-packet@5.6.5':
|
||||||
|
resolution: {integrity: sha512-qXOC7XLOEe43ehtWJCMnQXvgcIpv6rPmQ1jXT98Ad8A3TB1Ue50jsCbSSSyuazScEuZ/Q026vHbrOTVkmwA+7Q==}
|
||||||
|
|
||||||
|
'@types/elliptic@6.4.18':
|
||||||
|
resolution: {integrity: sha512-UseG6H5vjRiNpQvrhy4VF/JXdA3V/Fp5amvveaL+fs28BZ6xIKJBPnUPRlEaZpysD9MbpfaLi8lbl7PGUAkpWw==}
|
||||||
|
|
||||||
'@types/express-serve-static-core@4.19.6':
|
'@types/express-serve-static-core@4.19.6':
|
||||||
resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==}
|
resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==}
|
||||||
|
|
||||||
@ -1618,6 +1651,9 @@ packages:
|
|||||||
'@types/express@5.0.0':
|
'@types/express@5.0.0':
|
||||||
resolution: {integrity: sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==}
|
resolution: {integrity: sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==}
|
||||||
|
|
||||||
|
'@types/express@5.0.1':
|
||||||
|
resolution: {integrity: sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==}
|
||||||
|
|
||||||
'@types/fast-json-stable-stringify@2.1.2':
|
'@types/fast-json-stable-stringify@2.1.2':
|
||||||
resolution: {integrity: sha512-vsxcbfLDdjytnCnHXtinE40Xl46Wr7l/VGRGt7ewJwCPMKEHOdEsTxXX8xwgoR7cbc+6dE8SB4jlMrOV2zAg7g==}
|
resolution: {integrity: sha512-vsxcbfLDdjytnCnHXtinE40Xl46Wr7l/VGRGt7ewJwCPMKEHOdEsTxXX8xwgoR7cbc+6dE8SB4jlMrOV2zAg7g==}
|
||||||
deprecated: This is a stub types definition. fast-json-stable-stringify provides its own type definitions, so you do not need this installed.
|
deprecated: This is a stub types definition. fast-json-stable-stringify provides its own type definitions, so you do not need this installed.
|
||||||
@ -1700,9 +1736,15 @@ packages:
|
|||||||
'@types/ms@2.1.0':
|
'@types/ms@2.1.0':
|
||||||
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==}
|
||||||
|
|
||||||
|
'@types/node-fetch@2.6.12':
|
||||||
|
resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==}
|
||||||
|
|
||||||
'@types/node-forge@1.3.11':
|
'@types/node-forge@1.3.11':
|
||||||
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==}
|
||||||
|
|
||||||
|
'@types/node@18.19.87':
|
||||||
|
resolution: {integrity: sha512-OIAAu6ypnVZHmsHCeJ+7CCSub38QNBS9uceMQeg7K5Ur0Jr+wG9wEOEvvMbhp09pxD5czIUy/jND7s7Tb6Nw7A==}
|
||||||
|
|
||||||
'@types/node@22.15.3':
|
'@types/node@22.15.3':
|
||||||
resolution: {integrity: sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==}
|
resolution: {integrity: sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==}
|
||||||
|
|
||||||
@ -1825,6 +1867,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-84E1025aUSjvZU1j17eCTwV7m5Zg3cZHErV3+CaJM9JPCesZwLraIa0ONIQ9w4KLgcDgJFw9UnJ0LbFf42h6tg==}
|
resolution: {integrity: sha512-84E1025aUSjvZU1j17eCTwV7m5Zg3cZHErV3+CaJM9JPCesZwLraIa0ONIQ9w4KLgcDgJFw9UnJ0LbFf42h6tg==}
|
||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
abort-controller@3.0.0:
|
||||||
|
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||||
|
engines: {node: '>=6.5'}
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@ -1890,8 +1936,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
|
resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
asn1js@3.0.5:
|
asn1js@3.0.6:
|
||||||
resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==}
|
resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
ast-types@0.13.4:
|
ast-types@0.13.4:
|
||||||
@ -1915,8 +1961,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
|
resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
axios@1.8.2:
|
axios@1.9.0:
|
||||||
resolution: {integrity: sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==}
|
resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==}
|
||||||
|
|
||||||
b4a@1.6.7:
|
b4a@1.6.7:
|
||||||
resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==}
|
resolution: {integrity: sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==}
|
||||||
@ -1963,6 +2009,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
|
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
|
|
||||||
|
bn.js@4.12.2:
|
||||||
|
resolution: {integrity: sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==}
|
||||||
|
|
||||||
body-parser@1.20.3:
|
body-parser@1.20.3:
|
||||||
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
|
resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==}
|
||||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||||
@ -1983,6 +2032,9 @@ packages:
|
|||||||
broadcast-channel@7.0.0:
|
broadcast-channel@7.0.0:
|
||||||
resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==}
|
resolution: {integrity: sha512-a2tW0Ia1pajcPBOGUF2jXlDnvE9d5/dg6BG9h60OmRUcZVr/veUrU8vEQFwwQIhwG3KVzYwSk3v2nRRGFgQDXQ==}
|
||||||
|
|
||||||
|
brorand@1.1.0:
|
||||||
|
resolution: {integrity: sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=}
|
||||||
|
|
||||||
browserify-zlib@0.1.4:
|
browserify-zlib@0.1.4:
|
||||||
resolution: {integrity: sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=}
|
resolution: {integrity: sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=}
|
||||||
|
|
||||||
@ -2120,6 +2172,9 @@ packages:
|
|||||||
resolution: {integrity: sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=}
|
resolution: {integrity: sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=}
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
|
cloudflare@4.2.0:
|
||||||
|
resolution: {integrity: sha512-L9IainZq+7PN3NG/Mg7T9oRm7SB7AskNe6QLD8j7FrJY+2Dn1qpeLqXF8Im04ANnssezf0KAOtSuxNAAyNEgkg==}
|
||||||
|
|
||||||
co-body@6.2.0:
|
co-body@6.2.0:
|
||||||
resolution: {integrity: sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==}
|
resolution: {integrity: sha512-Kbpv2Yd1NdL1V/V4cwLVxraHDV6K8ayohr2rmH0J87Er8+zJjcTa6dAn9QMPC9CRgU8+aNajKbSf1TzDB1yKPA==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
@ -2389,6 +2444,9 @@ packages:
|
|||||||
ee-first@1.1.1:
|
ee-first@1.1.1:
|
||||||
resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=}
|
resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=}
|
||||||
|
|
||||||
|
elliptic@6.6.1:
|
||||||
|
resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==}
|
||||||
|
|
||||||
emoji-regex@8.0.0:
|
emoji-regex@8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
@ -2515,6 +2573,10 @@ packages:
|
|||||||
resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=}
|
resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
event-target-shim@5.0.1:
|
||||||
|
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
eventemitter3@4.0.7:
|
eventemitter3@4.0.7:
|
||||||
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||||
|
|
||||||
@ -2635,6 +2697,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
form-data-encoder@1.7.2:
|
||||||
|
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
|
||||||
|
|
||||||
form-data-encoder@2.1.4:
|
form-data-encoder@2.1.4:
|
||||||
resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
|
resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==}
|
||||||
engines: {node: '>= 14.17'}
|
engines: {node: '>= 14.17'}
|
||||||
@ -2647,6 +2712,10 @@ packages:
|
|||||||
resolution: {integrity: sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=}
|
resolution: {integrity: sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=}
|
||||||
engines: {node: '>=0.4.x'}
|
engines: {node: '>=0.4.x'}
|
||||||
|
|
||||||
|
formdata-node@4.4.1:
|
||||||
|
resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==}
|
||||||
|
engines: {node: '>= 12.20'}
|
||||||
|
|
||||||
forwarded@0.2.0:
|
forwarded@0.2.0:
|
||||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
@ -2785,6 +2854,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
hash.js@1.1.7:
|
||||||
|
resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==}
|
||||||
|
|
||||||
hasown@2.0.2:
|
hasown@2.0.2:
|
||||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@ -2802,6 +2874,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
hmac-drbg@1.0.1:
|
||||||
|
resolution: {integrity: sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=}
|
||||||
|
|
||||||
html-escaper@2.0.2:
|
html-escaper@2.0.2:
|
||||||
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
|
||||||
|
|
||||||
@ -3148,12 +3223,21 @@ packages:
|
|||||||
lit-element@4.1.1:
|
lit-element@4.1.1:
|
||||||
resolution: {integrity: sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==}
|
resolution: {integrity: sha512-HO9Tkkh34QkTeUmEdNYhMT8hzLid7YlMlATSi1q4q17HE5d9mrrEHJ/o8O2D0cMi182zK1F3v7x0PWFjrhXFew==}
|
||||||
|
|
||||||
|
lit-element@4.2.0:
|
||||||
|
resolution: {integrity: sha512-MGrXJVAI5x+Bfth/pU9Kst1iWID6GHDLEzFEnyULB/sFiRLgkd8NPK/PeeXxktA3T6EIIaq8U3KcbTU5XFcP2Q==}
|
||||||
|
|
||||||
lit-html@3.2.1:
|
lit-html@3.2.1:
|
||||||
resolution: {integrity: sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==}
|
resolution: {integrity: sha512-qI/3lziaPMSKsrwlxH/xMgikhQ0EGOX2ICU73Bi/YHFvz2j/yMCIrw4+puF2IpQ4+upd3EWbvnHM9+PnJn48YA==}
|
||||||
|
|
||||||
|
lit-html@3.3.0:
|
||||||
|
resolution: {integrity: sha512-RHoswrFAxY2d8Cf2mm4OZ1DgzCoBKUKSPvA1fhtSELxUERq2aQQ2h05pO9j81gS1o7RIRJ+CePLogfyahwmynw==}
|
||||||
|
|
||||||
lit@3.2.1:
|
lit@3.2.1:
|
||||||
resolution: {integrity: sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==}
|
resolution: {integrity: sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==}
|
||||||
|
|
||||||
|
lit@3.3.0:
|
||||||
|
resolution: {integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==}
|
||||||
|
|
||||||
locate-path@5.0.0:
|
locate-path@5.0.0:
|
||||||
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -3447,6 +3531,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
minimalistic-assert@1.0.1:
|
||||||
|
resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
|
||||||
|
|
||||||
|
minimalistic-crypto-utils@1.0.1:
|
||||||
|
resolution: {integrity: sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=}
|
||||||
|
|
||||||
minimatch@10.0.1:
|
minimatch@10.0.1:
|
||||||
resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==}
|
resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==}
|
||||||
engines: {node: 20 || >=22}
|
engines: {node: 20 || >=22}
|
||||||
@ -3564,6 +3654,19 @@ packages:
|
|||||||
no-case@2.3.2:
|
no-case@2.3.2:
|
||||||
resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==}
|
resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==}
|
||||||
|
|
||||||
|
node-domexception@1.0.0:
|
||||||
|
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||||
|
engines: {node: '>=10.5.0'}
|
||||||
|
|
||||||
|
node-fetch@2.7.0:
|
||||||
|
resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==}
|
||||||
|
engines: {node: 4.x || >=6.0.0}
|
||||||
|
peerDependencies:
|
||||||
|
encoding: ^0.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
encoding:
|
||||||
|
optional: true
|
||||||
|
|
||||||
node-forge@1.3.1:
|
node-forge@1.3.1:
|
||||||
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
||||||
engines: {node: '>= 6.13.0'}
|
engines: {node: '>= 6.13.0'}
|
||||||
@ -4265,6 +4368,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==}
|
resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
|
|
||||||
|
tr46@0.0.3:
|
||||||
|
resolution: {integrity: sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=}
|
||||||
|
|
||||||
tr46@3.0.0:
|
tr46@3.0.0:
|
||||||
resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==}
|
resolution: {integrity: sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -4302,8 +4408,8 @@ packages:
|
|||||||
engines: {node: '>=18.0.0'}
|
engines: {node: '>=18.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
tsyringe@4.8.0:
|
tsyringe@4.10.0:
|
||||||
resolution: {integrity: sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==}
|
resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==}
|
||||||
engines: {node: '>= 6.0.0'}
|
engines: {node: '>= 6.0.0'}
|
||||||
|
|
||||||
turndown-plugin-gfm@1.0.2:
|
turndown-plugin-gfm@1.0.2:
|
||||||
@ -4354,6 +4460,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==}
|
resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
undici-types@5.26.5:
|
||||||
|
resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
|
||||||
|
|
||||||
undici-types@6.21.0:
|
undici-types@6.21.0:
|
||||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||||
|
|
||||||
@ -4418,6 +4527,13 @@ packages:
|
|||||||
vfile@6.0.3:
|
vfile@6.0.3:
|
||||||
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
|
resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==}
|
||||||
|
|
||||||
|
web-streams-polyfill@4.0.0-beta.3:
|
||||||
|
resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==}
|
||||||
|
engines: {node: '>= 14'}
|
||||||
|
|
||||||
|
webidl-conversions@3.0.1:
|
||||||
|
resolution: {integrity: sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=}
|
||||||
|
|
||||||
webidl-conversions@7.0.0:
|
webidl-conversions@7.0.0:
|
||||||
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@ -4434,6 +4550,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
|
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
whatwg-url@5.0.0:
|
||||||
|
resolution: {integrity: sha1-lmRU6HZUYuN2RNNib2dCzotwll0=}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@ -4636,6 +4755,53 @@ snapshots:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
|
'@api.global/typedserver@3.0.74':
|
||||||
|
dependencies:
|
||||||
|
'@api.global/typedrequest': 3.1.10
|
||||||
|
'@api.global/typedrequest-interfaces': 3.0.19
|
||||||
|
'@api.global/typedsocket': 3.0.1
|
||||||
|
'@cloudflare/workers-types': 4.20250430.0
|
||||||
|
'@design.estate/dees-comms': 1.0.27
|
||||||
|
'@push.rocks/lik': 6.2.2
|
||||||
|
'@push.rocks/smartchok': 1.0.34
|
||||||
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
|
'@push.rocks/smartenv': 5.0.12
|
||||||
|
'@push.rocks/smartfeed': 1.0.11
|
||||||
|
'@push.rocks/smartfile': 11.2.0
|
||||||
|
'@push.rocks/smartjson': 5.0.20
|
||||||
|
'@push.rocks/smartlog': 3.0.7
|
||||||
|
'@push.rocks/smartlog-destination-devtools': 1.0.12
|
||||||
|
'@push.rocks/smartlog-interfaces': 3.0.2
|
||||||
|
'@push.rocks/smartmanifest': 2.0.2
|
||||||
|
'@push.rocks/smartmatch': 2.0.0
|
||||||
|
'@push.rocks/smartmime': 2.0.4
|
||||||
|
'@push.rocks/smartntml': 2.0.8
|
||||||
|
'@push.rocks/smartopen': 2.0.0
|
||||||
|
'@push.rocks/smartpath': 5.0.18
|
||||||
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
|
'@push.rocks/smartrequest': 2.1.0
|
||||||
|
'@push.rocks/smartrx': 3.0.10
|
||||||
|
'@push.rocks/smartsitemap': 2.0.3
|
||||||
|
'@push.rocks/smartstream': 3.2.5
|
||||||
|
'@push.rocks/smarttime': 4.1.1
|
||||||
|
'@push.rocks/taskbuffer': 3.1.7
|
||||||
|
'@push.rocks/webrequest': 3.0.37
|
||||||
|
'@push.rocks/webstore': 2.0.20
|
||||||
|
'@tsclass/tsclass': 8.2.1
|
||||||
|
'@types/express': 5.0.1
|
||||||
|
body-parser: 1.20.3
|
||||||
|
cors: 2.8.5
|
||||||
|
express: 4.21.2
|
||||||
|
express-force-ssl: 0.3.2
|
||||||
|
lit: 3.3.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@nuxt/kit'
|
||||||
|
- bufferutil
|
||||||
|
- react
|
||||||
|
- supports-color
|
||||||
|
- utf-8-validate
|
||||||
|
- vue
|
||||||
|
|
||||||
'@api.global/typedsocket@3.0.1':
|
'@api.global/typedsocket@3.0.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@api.global/typedrequest': 3.1.10
|
'@api.global/typedrequest': 3.1.10
|
||||||
@ -4654,6 +4820,18 @@ snapshots:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
- vue
|
- vue
|
||||||
|
|
||||||
|
'@apiclient.xyz/cloudflare@6.4.1':
|
||||||
|
dependencies:
|
||||||
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
|
'@push.rocks/smartlog': 3.0.7
|
||||||
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
|
'@push.rocks/smartrequest': 2.1.0
|
||||||
|
'@push.rocks/smartstring': 4.0.15
|
||||||
|
'@tsclass/tsclass': 9.1.0
|
||||||
|
cloudflare: 4.2.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
|
||||||
'@aws-crypto/crc32@5.2.0':
|
'@aws-crypto/crc32@5.2.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@aws-crypto/util': 5.2.0
|
'@aws-crypto/util': 5.2.0
|
||||||
@ -5493,6 +5671,8 @@ snapshots:
|
|||||||
|
|
||||||
'@cloudflare/workers-types@4.20250303.0': {}
|
'@cloudflare/workers-types@4.20250303.0': {}
|
||||||
|
|
||||||
|
'@cloudflare/workers-types@4.20250430.0': {}
|
||||||
|
|
||||||
'@colors/colors@1.6.0': {}
|
'@colors/colors@1.6.0': {}
|
||||||
|
|
||||||
'@configvault.io/interfaces@1.0.17':
|
'@configvault.io/interfaces@1.0.17':
|
||||||
@ -5839,6 +6019,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@lit-labs/ssr-dom-shim': 1.3.0
|
'@lit-labs/ssr-dom-shim': 1.3.0
|
||||||
|
|
||||||
|
'@lit/reactive-element@2.1.0':
|
||||||
|
dependencies:
|
||||||
|
'@lit-labs/ssr-dom-shim': 1.3.0
|
||||||
|
|
||||||
'@mixmark-io/domino@2.2.0': {}
|
'@mixmark-io/domino@2.2.0': {}
|
||||||
|
|
||||||
'@mongodb-js/saslprep@1.2.2':
|
'@mongodb-js/saslprep@1.2.2':
|
||||||
@ -5922,21 +6106,21 @@ snapshots:
|
|||||||
'@peculiar/asn1-schema': 2.3.15
|
'@peculiar/asn1-schema': 2.3.15
|
||||||
'@peculiar/asn1-x509': 2.3.15
|
'@peculiar/asn1-x509': 2.3.15
|
||||||
'@peculiar/asn1-x509-attr': 2.3.15
|
'@peculiar/asn1-x509-attr': 2.3.15
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@peculiar/asn1-csr@2.3.15':
|
'@peculiar/asn1-csr@2.3.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@peculiar/asn1-schema': 2.3.15
|
'@peculiar/asn1-schema': 2.3.15
|
||||||
'@peculiar/asn1-x509': 2.3.15
|
'@peculiar/asn1-x509': 2.3.15
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@peculiar/asn1-ecc@2.3.15':
|
'@peculiar/asn1-ecc@2.3.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@peculiar/asn1-schema': 2.3.15
|
'@peculiar/asn1-schema': 2.3.15
|
||||||
'@peculiar/asn1-x509': 2.3.15
|
'@peculiar/asn1-x509': 2.3.15
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@peculiar/asn1-pfx@2.3.15':
|
'@peculiar/asn1-pfx@2.3.15':
|
||||||
@ -5945,14 +6129,14 @@ snapshots:
|
|||||||
'@peculiar/asn1-pkcs8': 2.3.15
|
'@peculiar/asn1-pkcs8': 2.3.15
|
||||||
'@peculiar/asn1-rsa': 2.3.15
|
'@peculiar/asn1-rsa': 2.3.15
|
||||||
'@peculiar/asn1-schema': 2.3.15
|
'@peculiar/asn1-schema': 2.3.15
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@peculiar/asn1-pkcs8@2.3.15':
|
'@peculiar/asn1-pkcs8@2.3.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@peculiar/asn1-schema': 2.3.15
|
'@peculiar/asn1-schema': 2.3.15
|
||||||
'@peculiar/asn1-x509': 2.3.15
|
'@peculiar/asn1-x509': 2.3.15
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@peculiar/asn1-pkcs9@2.3.15':
|
'@peculiar/asn1-pkcs9@2.3.15':
|
||||||
@ -5963,19 +6147,19 @@ snapshots:
|
|||||||
'@peculiar/asn1-schema': 2.3.15
|
'@peculiar/asn1-schema': 2.3.15
|
||||||
'@peculiar/asn1-x509': 2.3.15
|
'@peculiar/asn1-x509': 2.3.15
|
||||||
'@peculiar/asn1-x509-attr': 2.3.15
|
'@peculiar/asn1-x509-attr': 2.3.15
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@peculiar/asn1-rsa@2.3.15':
|
'@peculiar/asn1-rsa@2.3.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@peculiar/asn1-schema': 2.3.15
|
'@peculiar/asn1-schema': 2.3.15
|
||||||
'@peculiar/asn1-x509': 2.3.15
|
'@peculiar/asn1-x509': 2.3.15
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@peculiar/asn1-schema@2.3.15':
|
'@peculiar/asn1-schema@2.3.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
pvtsutils: 1.3.6
|
pvtsutils: 1.3.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
@ -5983,13 +6167,13 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@peculiar/asn1-schema': 2.3.15
|
'@peculiar/asn1-schema': 2.3.15
|
||||||
'@peculiar/asn1-x509': 2.3.15
|
'@peculiar/asn1-x509': 2.3.15
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
'@peculiar/asn1-x509@2.3.15':
|
'@peculiar/asn1-x509@2.3.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@peculiar/asn1-schema': 2.3.15
|
'@peculiar/asn1-schema': 2.3.15
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
pvtsutils: 1.3.6
|
pvtsutils: 1.3.6
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
|
|
||||||
@ -6005,7 +6189,7 @@ snapshots:
|
|||||||
pvtsutils: 1.3.6
|
pvtsutils: 1.3.6
|
||||||
reflect-metadata: 0.2.2
|
reflect-metadata: 0.2.2
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
tsyringe: 4.8.0
|
tsyringe: 4.10.0
|
||||||
|
|
||||||
'@pkgjs/parseargs@0.11.0':
|
'@pkgjs/parseargs@0.11.0':
|
||||||
optional: true
|
optional: true
|
||||||
@ -6100,6 +6284,38 @@ 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)':
|
||||||
|
dependencies:
|
||||||
|
'@api.global/typedserver': 3.0.74
|
||||||
|
'@apiclient.xyz/cloudflare': 6.4.1
|
||||||
|
'@push.rocks/lik': 6.2.2
|
||||||
|
'@push.rocks/smartdata': 5.15.1(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)
|
||||||
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
|
'@push.rocks/smartdns': 6.2.2
|
||||||
|
'@push.rocks/smartlog': 3.0.7
|
||||||
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
|
'@push.rocks/smartrequest': 2.1.0
|
||||||
|
'@push.rocks/smartstring': 4.0.15
|
||||||
|
'@push.rocks/smarttime': 4.1.1
|
||||||
|
'@push.rocks/smartunique': 3.0.9
|
||||||
|
'@tsclass/tsclass': 9.1.0
|
||||||
|
acme-client: 5.4.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@aws-sdk/credential-providers'
|
||||||
|
- '@mongodb-js/zstd'
|
||||||
|
- '@nuxt/kit'
|
||||||
|
- bufferutil
|
||||||
|
- encoding
|
||||||
|
- gcp-metadata
|
||||||
|
- kerberos
|
||||||
|
- mongodb-client-encryption
|
||||||
|
- react
|
||||||
|
- snappy
|
||||||
|
- socks
|
||||||
|
- supports-color
|
||||||
|
- utf-8-validate
|
||||||
|
- vue
|
||||||
|
|
||||||
'@push.rocks/smartarchive@3.0.8':
|
'@push.rocks/smartarchive@3.0.8':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartfile': 10.0.41
|
'@push.rocks/smartfile': 10.0.41
|
||||||
@ -6205,6 +6421,22 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
|
|
||||||
|
'@push.rocks/smartdns@6.2.2':
|
||||||
|
dependencies:
|
||||||
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
|
'@push.rocks/smartenv': 5.0.12
|
||||||
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
|
'@push.rocks/smartrequest': 2.1.0
|
||||||
|
'@tsclass/tsclass': 5.0.0
|
||||||
|
'@types/dns-packet': 5.6.5
|
||||||
|
'@types/elliptic': 6.4.18
|
||||||
|
acme-client: 5.4.0
|
||||||
|
dns-packet: 5.6.1
|
||||||
|
elliptic: 6.6.1
|
||||||
|
minimatch: 10.0.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- supports-color
|
||||||
|
|
||||||
'@push.rocks/smartenv@5.0.12':
|
'@push.rocks/smartenv@5.0.12':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
@ -6222,7 +6454,7 @@ snapshots:
|
|||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
fast-deep-equal: 3.1.3
|
fast-deep-equal: 3.1.3
|
||||||
|
|
||||||
'@push.rocks/smartexpect@2.2.2':
|
'@push.rocks/smartexpect@2.4.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
@ -6652,7 +6884,7 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
'@push.rocks/tapbundle@6.0.0(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)':
|
'@push.rocks/tapbundle@6.0.3(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@open-wc/testing': 4.0.0
|
'@open-wc/testing': 4.0.0
|
||||||
'@push.rocks/consolecolor': 2.0.2
|
'@push.rocks/consolecolor': 2.0.2
|
||||||
@ -6660,7 +6892,7 @@ snapshots:
|
|||||||
'@push.rocks/smartcrypto': 2.0.4
|
'@push.rocks/smartcrypto': 2.0.4
|
||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
'@push.rocks/smartenv': 5.0.12
|
'@push.rocks/smartenv': 5.0.12
|
||||||
'@push.rocks/smartexpect': 2.2.2
|
'@push.rocks/smartexpect': 2.4.2
|
||||||
'@push.rocks/smartfile': 11.2.0
|
'@push.rocks/smartfile': 11.2.0
|
||||||
'@push.rocks/smartjson': 5.0.20
|
'@push.rocks/smartjson': 5.0.20
|
||||||
'@push.rocks/smartmongo': 2.0.12(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)
|
'@push.rocks/smartmongo': 2.0.12(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)
|
||||||
@ -6690,7 +6922,7 @@ snapshots:
|
|||||||
'@push.rocks/smartdelay': 3.0.5
|
'@push.rocks/smartdelay': 3.0.5
|
||||||
'@push.rocks/smartlog': 3.0.7
|
'@push.rocks/smartlog': 3.0.7
|
||||||
'@push.rocks/smartpromise': 4.2.3
|
'@push.rocks/smartpromise': 4.2.3
|
||||||
'@push.rocks/smartrx': 3.0.7
|
'@push.rocks/smartrx': 3.0.10
|
||||||
'@push.rocks/smarttime': 4.1.1
|
'@push.rocks/smarttime': 4.1.1
|
||||||
'@push.rocks/smartunique': 3.0.9
|
'@push.rocks/smartunique': 3.0.9
|
||||||
|
|
||||||
@ -7495,6 +7727,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
type-fest: 4.37.0
|
type-fest: 4.37.0
|
||||||
|
|
||||||
|
'@tsclass/tsclass@5.0.0':
|
||||||
|
dependencies:
|
||||||
|
type-fest: 4.40.1
|
||||||
|
|
||||||
'@tsclass/tsclass@8.2.1':
|
'@tsclass/tsclass@8.2.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
type-fest: 4.40.1
|
type-fest: 4.40.1
|
||||||
@ -7509,6 +7745,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/babel__code-frame@7.0.6': {}
|
'@types/babel__code-frame@7.0.6': {}
|
||||||
|
|
||||||
|
'@types/bn.js@5.1.6':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
'@types/body-parser@1.19.5':
|
'@types/body-parser@1.19.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/connect': 3.4.38
|
'@types/connect': 3.4.38
|
||||||
@ -7569,6 +7809,14 @@ snapshots:
|
|||||||
|
|
||||||
'@types/default-gateway@7.2.2': {}
|
'@types/default-gateway@7.2.2': {}
|
||||||
|
|
||||||
|
'@types/dns-packet@5.6.5':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
|
'@types/elliptic@6.4.18':
|
||||||
|
dependencies:
|
||||||
|
'@types/bn.js': 5.1.6
|
||||||
|
|
||||||
'@types/express-serve-static-core@4.19.6':
|
'@types/express-serve-static-core@4.19.6':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.15.3
|
'@types/node': 22.15.3
|
||||||
@ -7597,6 +7845,12 @@ snapshots:
|
|||||||
'@types/qs': 6.9.18
|
'@types/qs': 6.9.18
|
||||||
'@types/serve-static': 1.15.7
|
'@types/serve-static': 1.15.7
|
||||||
|
|
||||||
|
'@types/express@5.0.1':
|
||||||
|
dependencies:
|
||||||
|
'@types/body-parser': 1.19.5
|
||||||
|
'@types/express-serve-static-core': 5.0.6
|
||||||
|
'@types/serve-static': 1.15.7
|
||||||
|
|
||||||
'@types/fast-json-stable-stringify@2.1.2':
|
'@types/fast-json-stable-stringify@2.1.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-json-stable-stringify: 2.1.0
|
fast-json-stable-stringify: 2.1.0
|
||||||
@ -7693,10 +7947,19 @@ snapshots:
|
|||||||
|
|
||||||
'@types/ms@2.1.0': {}
|
'@types/ms@2.1.0': {}
|
||||||
|
|
||||||
|
'@types/node-fetch@2.6.12':
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 22.15.3
|
||||||
|
form-data: 4.0.2
|
||||||
|
|
||||||
'@types/node-forge@1.3.11':
|
'@types/node-forge@1.3.11':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.15.3
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
|
'@types/node@18.19.87':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 5.26.5
|
||||||
|
|
||||||
'@types/node@22.15.3':
|
'@types/node@22.15.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.21.0
|
undici-types: 6.21.0
|
||||||
@ -7877,6 +8140,10 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
|
|
||||||
|
abort-controller@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
event-target-shim: 5.0.1
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
@ -7885,8 +8152,8 @@ snapshots:
|
|||||||
acme-client@5.4.0:
|
acme-client@5.4.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@peculiar/x509': 1.12.3
|
'@peculiar/x509': 1.12.3
|
||||||
asn1js: 3.0.5
|
asn1js: 3.0.6
|
||||||
axios: 1.8.2(debug@4.4.0)
|
axios: 1.9.0(debug@4.4.0)
|
||||||
debug: 4.4.0
|
debug: 4.4.0
|
||||||
node-forge: 1.3.1
|
node-forge: 1.3.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@ -7935,7 +8202,7 @@ snapshots:
|
|||||||
|
|
||||||
array-union@2.1.0: {}
|
array-union@2.1.0: {}
|
||||||
|
|
||||||
asn1js@3.0.5:
|
asn1js@3.0.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
pvtsutils: 1.3.6
|
pvtsutils: 1.3.6
|
||||||
pvutils: 1.1.3
|
pvutils: 1.1.3
|
||||||
@ -7957,7 +8224,7 @@ snapshots:
|
|||||||
|
|
||||||
axe-core@4.10.3: {}
|
axe-core@4.10.3: {}
|
||||||
|
|
||||||
axios@1.8.2(debug@4.4.0):
|
axios@1.9.0(debug@4.4.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects: 1.15.9(debug@4.4.0)
|
follow-redirects: 1.15.9(debug@4.4.0)
|
||||||
form-data: 4.0.2
|
form-data: 4.0.2
|
||||||
@ -8004,6 +8271,8 @@ snapshots:
|
|||||||
|
|
||||||
basic-ftp@5.0.5: {}
|
basic-ftp@5.0.5: {}
|
||||||
|
|
||||||
|
bn.js@4.12.2: {}
|
||||||
|
|
||||||
body-parser@1.20.3:
|
body-parser@1.20.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
bytes: 3.1.2
|
bytes: 3.1.2
|
||||||
@ -8043,6 +8312,8 @@ snapshots:
|
|||||||
p-queue: 6.6.2
|
p-queue: 6.6.2
|
||||||
unload: 2.4.1
|
unload: 2.4.1
|
||||||
|
|
||||||
|
brorand@1.1.0: {}
|
||||||
|
|
||||||
browserify-zlib@0.1.4:
|
browserify-zlib@0.1.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
pako: 0.2.9
|
pako: 0.2.9
|
||||||
@ -8181,6 +8452,18 @@ snapshots:
|
|||||||
|
|
||||||
clone@2.1.2: {}
|
clone@2.1.2: {}
|
||||||
|
|
||||||
|
cloudflare@4.2.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 18.19.87
|
||||||
|
'@types/node-fetch': 2.6.12
|
||||||
|
abort-controller: 3.0.0
|
||||||
|
agentkeepalive: 4.6.0
|
||||||
|
form-data-encoder: 1.7.2
|
||||||
|
formdata-node: 4.4.1
|
||||||
|
node-fetch: 2.7.0
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- encoding
|
||||||
|
|
||||||
co-body@6.2.0:
|
co-body@6.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@hapi/bourne': 3.0.0
|
'@hapi/bourne': 3.0.0
|
||||||
@ -8407,6 +8690,16 @@ snapshots:
|
|||||||
|
|
||||||
ee-first@1.1.1: {}
|
ee-first@1.1.1: {}
|
||||||
|
|
||||||
|
elliptic@6.6.1:
|
||||||
|
dependencies:
|
||||||
|
bn.js: 4.12.2
|
||||||
|
brorand: 1.1.0
|
||||||
|
hash.js: 1.1.7
|
||||||
|
hmac-drbg: 1.0.1
|
||||||
|
inherits: 2.0.4
|
||||||
|
minimalistic-assert: 1.0.1
|
||||||
|
minimalistic-crypto-utils: 1.0.1
|
||||||
|
|
||||||
emoji-regex@8.0.0: {}
|
emoji-regex@8.0.0: {}
|
||||||
|
|
||||||
emoji-regex@9.2.2: {}
|
emoji-regex@9.2.2: {}
|
||||||
@ -8567,6 +8860,8 @@ snapshots:
|
|||||||
|
|
||||||
etag@1.8.1: {}
|
etag@1.8.1: {}
|
||||||
|
|
||||||
|
event-target-shim@5.0.1: {}
|
||||||
|
|
||||||
eventemitter3@4.0.7: {}
|
eventemitter3@4.0.7: {}
|
||||||
|
|
||||||
execa@5.1.1:
|
execa@5.1.1:
|
||||||
@ -8743,6 +9038,8 @@ snapshots:
|
|||||||
cross-spawn: 7.0.6
|
cross-spawn: 7.0.6
|
||||||
signal-exit: 4.1.0
|
signal-exit: 4.1.0
|
||||||
|
|
||||||
|
form-data-encoder@1.7.2: {}
|
||||||
|
|
||||||
form-data-encoder@2.1.4: {}
|
form-data-encoder@2.1.4: {}
|
||||||
|
|
||||||
form-data@4.0.2:
|
form-data@4.0.2:
|
||||||
@ -8754,6 +9051,11 @@ snapshots:
|
|||||||
|
|
||||||
format@0.2.2: {}
|
format@0.2.2: {}
|
||||||
|
|
||||||
|
formdata-node@4.4.1:
|
||||||
|
dependencies:
|
||||||
|
node-domexception: 1.0.0
|
||||||
|
web-streams-polyfill: 4.0.0-beta.3
|
||||||
|
|
||||||
forwarded@0.2.0: {}
|
forwarded@0.2.0: {}
|
||||||
|
|
||||||
fresh@0.5.2: {}
|
fresh@0.5.2: {}
|
||||||
@ -8940,6 +9242,11 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-symbols: 1.1.0
|
has-symbols: 1.1.0
|
||||||
|
|
||||||
|
hash.js@1.1.7:
|
||||||
|
dependencies:
|
||||||
|
inherits: 2.0.4
|
||||||
|
minimalistic-assert: 1.0.1
|
||||||
|
|
||||||
hasown@2.0.2:
|
hasown@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
@ -8970,6 +9277,12 @@ snapshots:
|
|||||||
|
|
||||||
he@1.2.0: {}
|
he@1.2.0: {}
|
||||||
|
|
||||||
|
hmac-drbg@1.0.1:
|
||||||
|
dependencies:
|
||||||
|
hash.js: 1.1.7
|
||||||
|
minimalistic-assert: 1.0.1
|
||||||
|
minimalistic-crypto-utils: 1.0.1
|
||||||
|
|
||||||
html-escaper@2.0.2: {}
|
html-escaper@2.0.2: {}
|
||||||
|
|
||||||
html-minifier@4.0.0:
|
html-minifier@4.0.0:
|
||||||
@ -9341,16 +9654,32 @@ snapshots:
|
|||||||
'@lit/reactive-element': 2.0.4
|
'@lit/reactive-element': 2.0.4
|
||||||
lit-html: 3.2.1
|
lit-html: 3.2.1
|
||||||
|
|
||||||
|
lit-element@4.2.0:
|
||||||
|
dependencies:
|
||||||
|
'@lit-labs/ssr-dom-shim': 1.3.0
|
||||||
|
'@lit/reactive-element': 2.1.0
|
||||||
|
lit-html: 3.3.0
|
||||||
|
|
||||||
lit-html@3.2.1:
|
lit-html@3.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/trusted-types': 2.0.7
|
'@types/trusted-types': 2.0.7
|
||||||
|
|
||||||
|
lit-html@3.3.0:
|
||||||
|
dependencies:
|
||||||
|
'@types/trusted-types': 2.0.7
|
||||||
|
|
||||||
lit@3.2.1:
|
lit@3.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@lit/reactive-element': 2.0.4
|
'@lit/reactive-element': 2.0.4
|
||||||
lit-element: 4.1.1
|
lit-element: 4.1.1
|
||||||
lit-html: 3.2.1
|
lit-html: 3.2.1
|
||||||
|
|
||||||
|
lit@3.3.0:
|
||||||
|
dependencies:
|
||||||
|
'@lit/reactive-element': 2.1.0
|
||||||
|
lit-element: 4.2.0
|
||||||
|
lit-html: 3.3.0
|
||||||
|
|
||||||
locate-path@5.0.0:
|
locate-path@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 4.1.0
|
p-locate: 4.1.0
|
||||||
@ -9812,6 +10141,10 @@ snapshots:
|
|||||||
|
|
||||||
min-indent@1.0.1: {}
|
min-indent@1.0.1: {}
|
||||||
|
|
||||||
|
minimalistic-assert@1.0.1: {}
|
||||||
|
|
||||||
|
minimalistic-crypto-utils@1.0.1: {}
|
||||||
|
|
||||||
minimatch@10.0.1:
|
minimatch@10.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion: 2.0.1
|
brace-expansion: 2.0.1
|
||||||
@ -9935,6 +10268,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lower-case: 1.1.4
|
lower-case: 1.1.4
|
||||||
|
|
||||||
|
node-domexception@1.0.0: {}
|
||||||
|
|
||||||
|
node-fetch@2.7.0:
|
||||||
|
dependencies:
|
||||||
|
whatwg-url: 5.0.0
|
||||||
|
|
||||||
node-forge@1.3.1: {}
|
node-forge@1.3.1: {}
|
||||||
|
|
||||||
normalize-newline@4.1.0:
|
normalize-newline@4.1.0:
|
||||||
@ -10753,6 +11092,8 @@ snapshots:
|
|||||||
'@tokenizer/token': 0.3.0
|
'@tokenizer/token': 0.3.0
|
||||||
ieee754: 1.2.1
|
ieee754: 1.2.1
|
||||||
|
|
||||||
|
tr46@0.0.3: {}
|
||||||
|
|
||||||
tr46@3.0.0:
|
tr46@3.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
punycode: 2.3.1
|
punycode: 2.3.1
|
||||||
@ -10782,7 +11123,7 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
|
||||||
tsyringe@4.8.0:
|
tsyringe@4.10.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 1.14.1
|
tslib: 1.14.1
|
||||||
|
|
||||||
@ -10815,6 +11156,8 @@ snapshots:
|
|||||||
|
|
||||||
uint8array-extras@1.4.0: {}
|
uint8array-extras@1.4.0: {}
|
||||||
|
|
||||||
|
undici-types@5.26.5: {}
|
||||||
|
|
||||||
undici-types@6.21.0: {}
|
undici-types@6.21.0: {}
|
||||||
|
|
||||||
unified@11.0.5:
|
unified@11.0.5:
|
||||||
@ -10883,6 +11226,10 @@ snapshots:
|
|||||||
'@types/unist': 3.0.3
|
'@types/unist': 3.0.3
|
||||||
vfile-message: 4.0.2
|
vfile-message: 4.0.2
|
||||||
|
|
||||||
|
web-streams-polyfill@4.0.0-beta.3: {}
|
||||||
|
|
||||||
|
webidl-conversions@3.0.1: {}
|
||||||
|
|
||||||
webidl-conversions@7.0.0: {}
|
webidl-conversions@7.0.0: {}
|
||||||
|
|
||||||
whatwg-mimetype@3.0.0: {}
|
whatwg-mimetype@3.0.0: {}
|
||||||
@ -10897,6 +11244,11 @@ snapshots:
|
|||||||
tr46: 5.1.1
|
tr46: 5.1.1
|
||||||
webidl-conversions: 7.0.0
|
webidl-conversions: 7.0.0
|
||||||
|
|
||||||
|
whatwg-url@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
tr46: 0.0.3
|
||||||
|
webidl-conversions: 3.0.1
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
44
readme.md
44
readme.md
@ -199,6 +199,50 @@ sequenceDiagram
|
|||||||
- **IP Filtering** - Control access with IP allow/block lists using glob patterns
|
- **IP Filtering** - Control access with IP allow/block lists using glob patterns
|
||||||
- **NfTables Integration** - Direct manipulation of nftables for advanced low-level port forwarding
|
- **NfTables Integration** - Direct manipulation of nftables for advanced low-level port forwarding
|
||||||
|
|
||||||
|
## Certificate Provider Hook & Events
|
||||||
|
|
||||||
|
You can customize how certificates are provisioned per domain by using the `certProvider` callback and listen for certificate events emitted by `SmartProxy`.
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { SmartProxy } from '@push.rocks/smartproxy';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
|
||||||
|
// Example certProvider: static for a specific domain, HTTP-01 otherwise
|
||||||
|
const certProvider = async (domain: string) => {
|
||||||
|
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({
|
||||||
|
fromPort: 80,
|
||||||
|
toPort: 8080,
|
||||||
|
domainConfigs: [{
|
||||||
|
domains: ['static.example.com', 'dynamic.example.com'],
|
||||||
|
allowedIPs: ['*']
|
||||||
|
}],
|
||||||
|
certProvider
|
||||||
|
});
|
||||||
|
|
||||||
|
// Listen for certificate issuance or renewal
|
||||||
|
proxy.on('certificate', (evt) => {
|
||||||
|
console.log(`Certificate for ${evt.domain} ready, expires on ${evt.expiryDate}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
await proxy.start();
|
||||||
|
```
|
||||||
|
|
||||||
## Configuration Options
|
## Configuration Options
|
||||||
|
|
||||||
### backendProtocol
|
### backendProtocol
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
## Refactor: Introduce a Unified CertProvisioner for Certificate Lifecycle
|
||||||
|
|
||||||
|
- [x] Ensure Port80Handler is challenge-only:
|
||||||
|
- Remove any internal scheduling and deprecated ACME flows (`getAcmeClient`, `processAuthorizations`, `handleAcmeChallenge`) from Port80Handler.
|
||||||
|
- Remove legacy ACME options (`renewThresholdDays`, `renewCheckIntervalHours`, `mongoDescriptor`, etc.) from `IPort80HandlerOptions`.
|
||||||
|
- Retain only methods for HTTP-01 challenge and direct renewals (`obtainCertificate`, `renewCertificate`, `getDomainCertificateStatus`).
|
||||||
|
- [x] Clean up deprecated `acme` configuration:
|
||||||
|
- Remove the `acme` property from `IPortProxySettings` and all legacy references in code.
|
||||||
|
|
||||||
|
- [x] Implement `CertProvisioner` component:
|
||||||
|
- [x] Create class `ts/smartproxy/classes.pp.certprovisioner.ts`.
|
||||||
|
- [x] Constructor accepts:
|
||||||
|
* `domainConfigs: IDomainConfig[]`
|
||||||
|
* `port80Handler: Port80Handler`
|
||||||
|
* `networkProxyBridge: NetworkProxyBridge`
|
||||||
|
* optional `certProvider: (domain) => Promise<ICert | 'http01'>`
|
||||||
|
* `renewThresholdDays`, `renewCheckIntervalHours`, `autoRenew` settings.
|
||||||
|
- Responsibilities:
|
||||||
|
* Initial provisioning: static vs HTTP-01.
|
||||||
|
* Subscribe to Port80Handler events (CERTIFICATE_ISSUED/RENEWED) and to static cert updates.
|
||||||
|
* Re-emit unified `'certificate'` events to SmartProxy.
|
||||||
|
* Central scheduling of renewals via `@push.rocks/taskbuffer`.
|
||||||
|
|
||||||
|
- [x] Refactor SmartProxy:
|
||||||
|
- [x] Remove existing scheduling / renewal logic.
|
||||||
|
- [x] Instantiate `CertProvisioner` in `start()`, delegate cert workflows entirely.
|
||||||
|
- [x] Forward CertProvisioner events to SmartProxy’s `'certificate'` listener.
|
||||||
|
|
||||||
|
- [x] CertProvisioner lifecycle methods:
|
||||||
|
- [x] `start()`: provision all domains, start scheduler.
|
||||||
|
- [x] `stop()`: stop scheduler.
|
||||||
|
- [x] `requestCertificate(domain)`: on-demand provisioning.
|
||||||
|
|
||||||
|
- [x] Handle static certificate auto-refresh:
|
||||||
|
- [x] In the renewal scheduler, for domains with static certs, re-call `certProvider(domain)` near expiry.
|
||||||
|
- [x] Apply returned cert via `networkProxyBridge.applyExternalCertificate()`.
|
||||||
|
|
||||||
|
- [ ] Tests:
|
||||||
|
- Unit tests for `CertProvisioner`, mocking Port80Handler and `certProvider`:
|
||||||
|
* Validate initial provisioning and dynamic/static flows.
|
||||||
|
* Validate scheduling triggers correct renewals.
|
||||||
|
- Integration tests:
|
||||||
|
* Use actual in-memory Port80Handler with short intervals to verify renewals and event emission.
|
||||||
|
|
||||||
|
- [ ] Documentation:
|
||||||
|
- Add code-level TS doc for `CertProvisioner` API (options, methods, events).
|
||||||
|
- Update root `README.md` and architecture diagrams to show `CertProvisioner` role.
|
||||||
|
140
test/test.certprovisioner.unit.ts
Normal file
140
test/test.certprovisioner.unit.ts
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import { tap, expect } from '@push.rocks/tapbundle';
|
||||||
|
import * as plugins from '../ts/plugins.js';
|
||||||
|
import { CertProvisioner } from '../ts/smartproxy/classes.pp.certprovisioner.js';
|
||||||
|
import type { IDomainConfig, ISmartProxyCertProvisionObject } from '../ts/smartproxy/classes.pp.interfaces.js';
|
||||||
|
import type { ICertificateData } from '../ts/port80handler/classes.port80handler.js';
|
||||||
|
|
||||||
|
// Fake Port80Handler stub
|
||||||
|
class FakePort80Handler extends plugins.EventEmitter {
|
||||||
|
public domainsAdded: string[] = [];
|
||||||
|
public renewCalled: string[] = [];
|
||||||
|
addDomain(opts: { domainName: string; sslRedirect: boolean; acmeMaintenance: boolean }) {
|
||||||
|
this.domainsAdded.push(opts.domainName);
|
||||||
|
}
|
||||||
|
async renewCertificate(domain: string): Promise<void> {
|
||||||
|
this.renewCalled.push(domain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fake NetworkProxyBridge stub
|
||||||
|
class FakeNetworkProxyBridge {
|
||||||
|
public appliedCerts: ICertificateData[] = [];
|
||||||
|
applyExternalCertificate(cert: ICertificateData) {
|
||||||
|
this.appliedCerts.push(cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tap.test('CertProvisioner handles static provisioning', async () => {
|
||||||
|
const domain = 'static.com';
|
||||||
|
const domainConfigs: IDomainConfig[] = [{ domains: [domain], allowedIPs: [] }];
|
||||||
|
const fakePort80 = new FakePort80Handler();
|
||||||
|
const fakeBridge = new FakeNetworkProxyBridge();
|
||||||
|
// certProvider returns static certificate
|
||||||
|
const certProvider = async (d: string): Promise<ISmartProxyCertProvisionObject> => {
|
||||||
|
expect(d).toEqual(domain);
|
||||||
|
return {
|
||||||
|
domainName: domain,
|
||||||
|
publicKey: 'CERT',
|
||||||
|
privateKey: 'KEY',
|
||||||
|
validUntil: Date.now() + 3600 * 1000
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const prov = new CertProvisioner(
|
||||||
|
domainConfigs,
|
||||||
|
fakePort80 as any,
|
||||||
|
fakeBridge as any,
|
||||||
|
certProvider,
|
||||||
|
1, // low renew threshold
|
||||||
|
1, // short interval
|
||||||
|
false // disable auto renew for unit test
|
||||||
|
);
|
||||||
|
const events: any[] = [];
|
||||||
|
prov.on('certificate', (data) => events.push(data));
|
||||||
|
await prov.start();
|
||||||
|
// Static flow: no addDomain, certificate applied via bridge
|
||||||
|
expect(fakePort80.domainsAdded.length).toEqual(0);
|
||||||
|
expect(fakeBridge.appliedCerts.length).toEqual(1);
|
||||||
|
expect(events.length).toEqual(1);
|
||||||
|
const evt = events[0];
|
||||||
|
expect(evt.domain).toEqual(domain);
|
||||||
|
expect(evt.certificate).toEqual('CERT');
|
||||||
|
expect(evt.privateKey).toEqual('KEY');
|
||||||
|
expect(evt.isRenewal).toEqual(false);
|
||||||
|
expect(evt.source).toEqual('static');
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('CertProvisioner handles http01 provisioning', async () => {
|
||||||
|
const domain = 'http01.com';
|
||||||
|
const domainConfigs: IDomainConfig[] = [{ domains: [domain], allowedIPs: [] }];
|
||||||
|
const fakePort80 = new FakePort80Handler();
|
||||||
|
const fakeBridge = new FakeNetworkProxyBridge();
|
||||||
|
// certProvider returns http01 directive
|
||||||
|
const certProvider = async (): Promise<ISmartProxyCertProvisionObject> => 'http01';
|
||||||
|
const prov = new CertProvisioner(
|
||||||
|
domainConfigs,
|
||||||
|
fakePort80 as any,
|
||||||
|
fakeBridge as any,
|
||||||
|
certProvider,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
const events: any[] = [];
|
||||||
|
prov.on('certificate', (data) => events.push(data));
|
||||||
|
await prov.start();
|
||||||
|
// HTTP-01 flow: addDomain called, no static cert applied
|
||||||
|
expect(fakePort80.domainsAdded).toEqual([domain]);
|
||||||
|
expect(fakeBridge.appliedCerts.length).toEqual(0);
|
||||||
|
expect(events.length).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('CertProvisioner on-demand http01 renewal', async () => {
|
||||||
|
const domain = 'renew.com';
|
||||||
|
const domainConfigs: IDomainConfig[] = [{ domains: [domain], allowedIPs: [] }];
|
||||||
|
const fakePort80 = new FakePort80Handler();
|
||||||
|
const fakeBridge = new FakeNetworkProxyBridge();
|
||||||
|
const certProvider = async (): Promise<ISmartProxyCertProvisionObject> => 'http01';
|
||||||
|
const prov = new CertProvisioner(
|
||||||
|
domainConfigs,
|
||||||
|
fakePort80 as any,
|
||||||
|
fakeBridge as any,
|
||||||
|
certProvider,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// requestCertificate should call renewCertificate
|
||||||
|
await prov.requestCertificate(domain);
|
||||||
|
expect(fakePort80.renewCalled).toEqual([domain]);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('CertProvisioner on-demand static provisioning', async () => {
|
||||||
|
const domain = 'ondemand.com';
|
||||||
|
const domainConfigs: IDomainConfig[] = [{ domains: [domain], allowedIPs: [] }];
|
||||||
|
const fakePort80 = new FakePort80Handler();
|
||||||
|
const fakeBridge = new FakeNetworkProxyBridge();
|
||||||
|
const certProvider = async (): Promise<ISmartProxyCertProvisionObject> => ({
|
||||||
|
domainName: domain,
|
||||||
|
publicKey: 'PKEY',
|
||||||
|
privateKey: 'PRIV',
|
||||||
|
validUntil: Date.now() + 1000
|
||||||
|
});
|
||||||
|
const prov = new CertProvisioner(
|
||||||
|
domainConfigs,
|
||||||
|
fakePort80 as any,
|
||||||
|
fakeBridge as any,
|
||||||
|
certProvider,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
const events: any[] = [];
|
||||||
|
prov.on('certificate', (data) => events.push(data));
|
||||||
|
await prov.requestCertificate(domain);
|
||||||
|
expect(fakeBridge.appliedCerts.length).toEqual(1);
|
||||||
|
expect(events.length).toEqual(1);
|
||||||
|
expect(events[0].domain).toEqual(domain);
|
||||||
|
expect(events[0].source).toEqual('static');
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
45
test/test.smartproxy.renewals.node.ts
Normal file
45
test/test.smartproxy.renewals.node.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
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();
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartproxy',
|
name: '@push.rocks/smartproxy',
|
||||||
version: '7.1.4',
|
version: '8.0.0',
|
||||||
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.'
|
||||||
}
|
}
|
||||||
|
@ -353,11 +353,8 @@ export class CertificateManager {
|
|||||||
port: this.options.acme.port,
|
port: this.options.acme.port,
|
||||||
contactEmail: this.options.acme.contactEmail,
|
contactEmail: this.options.acme.contactEmail,
|
||||||
useProduction: this.options.acme.useProduction,
|
useProduction: this.options.acme.useProduction,
|
||||||
renewThresholdDays: this.options.acme.renewThresholdDays,
|
|
||||||
httpsRedirectPort: this.options.port, // Redirect to our HTTPS port
|
httpsRedirectPort: this.options.port, // Redirect to our HTTPS port
|
||||||
renewCheckIntervalHours: 24, // Check daily for renewals
|
|
||||||
enabled: this.options.acme.enabled,
|
enabled: this.options.acme.enabled,
|
||||||
autoRenew: this.options.acme.autoRenew,
|
|
||||||
certificateStore: this.options.acme.certificateStore,
|
certificateStore: this.options.acme.certificateStore,
|
||||||
skipConfiguredCerts: this.options.acme.skipConfiguredCerts
|
skipConfiguredCerts: this.options.acme.skipConfiguredCerts
|
||||||
});
|
});
|
||||||
|
@ -12,6 +12,10 @@ import { Port80Handler } from '../port80handler/classes.port80handler.js';
|
|||||||
* automatic certificate management, and high-performance connection pooling.
|
* automatic certificate management, and high-performance connection pooling.
|
||||||
*/
|
*/
|
||||||
export class NetworkProxy implements IMetricsTracker {
|
export class NetworkProxy implements IMetricsTracker {
|
||||||
|
// Provide a minimal JSON representation to avoid circular references during deep equality checks
|
||||||
|
public toJSON(): any {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
// Configuration
|
// Configuration
|
||||||
public options: INetworkProxyOptions;
|
public options: INetworkProxyOptions;
|
||||||
public proxyConfigs: IReverseProxyConfig[] = [];
|
public proxyConfigs: IReverseProxyConfig[] = [];
|
||||||
|
@ -7,7 +7,6 @@ import * as tls from 'tls';
|
|||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import * as http2 from 'http2';
|
import * as http2 from 'http2';
|
||||||
|
|
||||||
|
|
||||||
export { EventEmitter, http, https, net, tls, url, http2 };
|
export { EventEmitter, http, https, net, tls, url, http2 };
|
||||||
|
|
||||||
// tsclass scope
|
// tsclass scope
|
||||||
@ -22,13 +21,27 @@ import * as smartpromise from '@push.rocks/smartpromise';
|
|||||||
import * as smartrequest from '@push.rocks/smartrequest';
|
import * as smartrequest from '@push.rocks/smartrequest';
|
||||||
import * as smartstring from '@push.rocks/smartstring';
|
import * as smartstring from '@push.rocks/smartstring';
|
||||||
|
|
||||||
export { lik, smartdelay, smartrequest, smartpromise, smartstring };
|
import * as smartacme from '@push.rocks/smartacme';
|
||||||
|
import * as smartacmePlugins from '@push.rocks/smartacme/dist_ts/smartacme.plugins.js';
|
||||||
|
import * as smartacmeHandlers from '@push.rocks/smartacme/dist_ts/handlers/index.js';
|
||||||
|
import * as taskbuffer from '@push.rocks/taskbuffer';
|
||||||
|
|
||||||
|
export {
|
||||||
|
lik,
|
||||||
|
smartdelay,
|
||||||
|
smartrequest,
|
||||||
|
smartpromise,
|
||||||
|
smartstring,
|
||||||
|
smartacme,
|
||||||
|
smartacmePlugins,
|
||||||
|
smartacmeHandlers,
|
||||||
|
taskbuffer,
|
||||||
|
};
|
||||||
|
|
||||||
// third party scope
|
// third party scope
|
||||||
import * as acme from 'acme-client';
|
|
||||||
import prettyMs from 'pretty-ms';
|
import prettyMs from 'pretty-ms';
|
||||||
import * as ws from 'ws';
|
import * as ws from 'ws';
|
||||||
import wsDefault from 'ws';
|
import wsDefault from 'ws';
|
||||||
import { minimatch } from 'minimatch';
|
import { minimatch } from 'minimatch';
|
||||||
|
|
||||||
export { acme, prettyMs, ws, wsDefault, minimatch };
|
export { prettyMs, ws, wsDefault, minimatch };
|
||||||
|
@ -1,7 +1,21 @@
|
|||||||
import * as plugins from '../plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
import { IncomingMessage, ServerResponse } from 'http';
|
import { IncomingMessage, ServerResponse } from 'http';
|
||||||
import * as fs from 'fs';
|
// (fs and path I/O moved to CertProvisioner)
|
||||||
import * as path from 'path';
|
// ACME HTTP-01 challenge handler storing tokens in memory (diskless)
|
||||||
|
class DisklessHttp01Handler {
|
||||||
|
private storage: Map<string, string>;
|
||||||
|
constructor(storage: Map<string, string>) { this.storage = storage; }
|
||||||
|
public getSupportedTypes(): string[] { return ['http-01']; }
|
||||||
|
public async prepare(ch: any): Promise<void> {
|
||||||
|
this.storage.set(ch.token, ch.keyAuthorization);
|
||||||
|
}
|
||||||
|
public async verify(ch: any): Promise<void> {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
public async cleanup(ch: any): Promise<void> {
|
||||||
|
this.storage.delete(ch.token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom error classes for better error handling
|
* Custom error classes for better error handling
|
||||||
@ -59,8 +73,6 @@ interface IDomainCertificate {
|
|||||||
obtainingInProgress: boolean;
|
obtainingInProgress: boolean;
|
||||||
certificate?: string;
|
certificate?: string;
|
||||||
privateKey?: string;
|
privateKey?: string;
|
||||||
challengeToken?: string;
|
|
||||||
challengeKeyAuthorization?: string;
|
|
||||||
expiryDate?: Date;
|
expiryDate?: Date;
|
||||||
lastRenewalAttempt?: Date;
|
lastRenewalAttempt?: Date;
|
||||||
}
|
}
|
||||||
@ -72,13 +84,9 @@ interface IPort80HandlerOptions {
|
|||||||
port?: number;
|
port?: number;
|
||||||
contactEmail?: string;
|
contactEmail?: string;
|
||||||
useProduction?: boolean;
|
useProduction?: boolean;
|
||||||
renewThresholdDays?: number;
|
|
||||||
httpsRedirectPort?: number;
|
httpsRedirectPort?: number;
|
||||||
renewCheckIntervalHours?: number;
|
|
||||||
enabled?: boolean; // Whether ACME is enabled at all
|
enabled?: boolean; // Whether ACME is enabled at all
|
||||||
autoRenew?: boolean; // Whether to automatically renew certificates
|
// (Persistence moved to CertProvisioner)
|
||||||
certificateStore?: string; // Directory to store certificates
|
|
||||||
skipConfiguredCerts?: boolean; // Skip domains that already have certificates
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,10 +136,13 @@ export interface ICertificateExpiring {
|
|||||||
*/
|
*/
|
||||||
export class Port80Handler extends plugins.EventEmitter {
|
export class Port80Handler extends plugins.EventEmitter {
|
||||||
private domainCertificates: Map<string, IDomainCertificate>;
|
private domainCertificates: Map<string, IDomainCertificate>;
|
||||||
|
// In-memory storage for ACME HTTP-01 challenge tokens
|
||||||
|
private acmeHttp01Storage: Map<string, string> = new Map();
|
||||||
|
// SmartAcme instance for certificate management
|
||||||
|
private smartAcme: plugins.smartacme.SmartAcme | null = null;
|
||||||
private server: plugins.http.Server | null = null;
|
private server: plugins.http.Server | null = null;
|
||||||
private acmeClient: plugins.acme.Client | null = null;
|
// Renewal scheduling is handled externally by SmartProxy
|
||||||
private accountKey: string | null = null;
|
// (Removed internal renewal timer)
|
||||||
private renewalTimer: NodeJS.Timeout | null = null;
|
|
||||||
private isShuttingDown: boolean = false;
|
private isShuttingDown: boolean = false;
|
||||||
private options: Required<IPort80HandlerOptions>;
|
private options: Required<IPort80HandlerOptions>;
|
||||||
|
|
||||||
@ -148,13 +159,8 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
port: options.port ?? 80,
|
port: options.port ?? 80,
|
||||||
contactEmail: options.contactEmail ?? 'admin@example.com',
|
contactEmail: options.contactEmail ?? 'admin@example.com',
|
||||||
useProduction: options.useProduction ?? false, // Safer default: staging
|
useProduction: options.useProduction ?? false, // Safer default: staging
|
||||||
renewThresholdDays: options.renewThresholdDays ?? 10, // Changed to 10 days as per requirements
|
|
||||||
httpsRedirectPort: options.httpsRedirectPort ?? 443,
|
httpsRedirectPort: options.httpsRedirectPort ?? 443,
|
||||||
renewCheckIntervalHours: options.renewCheckIntervalHours ?? 24,
|
enabled: options.enabled ?? true // Enable by default
|
||||||
enabled: options.enabled ?? true, // Enable by default
|
|
||||||
autoRenew: options.autoRenew ?? true, // Auto-renew by default
|
|
||||||
certificateStore: options.certificateStore ?? './certs', // Default store location
|
|
||||||
skipConfiguredCerts: options.skipConfiguredCerts ?? false
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,13 +181,20 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
console.log('Port80Handler is disabled, skipping start');
|
console.log('Port80Handler is disabled, skipping start');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Initialize SmartAcme for ACME challenge management (diskless HTTP handler)
|
||||||
|
if (this.options.enabled) {
|
||||||
|
this.smartAcme = new plugins.smartacme.SmartAcme({
|
||||||
|
accountEmail: this.options.contactEmail,
|
||||||
|
certManager: new plugins.smartacme.MemoryCertManager(),
|
||||||
|
environment: this.options.useProduction ? 'production' : 'integration',
|
||||||
|
challengeHandlers: [ new DisklessHttp01Handler(this.acmeHttp01Storage) ],
|
||||||
|
challengePriority: ['http-01'],
|
||||||
|
});
|
||||||
|
await this.smartAcme.start();
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
// Load certificates from store if enabled
|
|
||||||
if (this.options.certificateStore) {
|
|
||||||
this.loadCertificatesFromStore();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.server = plugins.http.createServer((req, res) => this.handleRequest(req, res));
|
this.server = plugins.http.createServer((req, res) => this.handleRequest(req, res));
|
||||||
|
|
||||||
@ -197,7 +210,6 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
|
|
||||||
this.server.listen(this.options.port, () => {
|
this.server.listen(this.options.port, () => {
|
||||||
console.log(`Port80Handler is listening on port ${this.options.port}`);
|
console.log(`Port80Handler is listening on port ${this.options.port}`);
|
||||||
this.startRenewalTimer();
|
|
||||||
this.emit(Port80HandlerEvents.MANAGER_STARTED, this.options.port);
|
this.emit(Port80HandlerEvents.MANAGER_STARTED, this.options.port);
|
||||||
|
|
||||||
// Start certificate process for domains with acmeMaintenance enabled
|
// Start certificate process for domains with acmeMaintenance enabled
|
||||||
@ -234,11 +246,6 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
|
|
||||||
this.isShuttingDown = true;
|
this.isShuttingDown = true;
|
||||||
|
|
||||||
// Stop the renewal timer
|
|
||||||
if (this.renewalTimer) {
|
|
||||||
clearInterval(this.renewalTimer);
|
|
||||||
this.renewalTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise<void>((resolve) => {
|
return new Promise<void>((resolve) => {
|
||||||
if (this.server) {
|
if (this.server) {
|
||||||
@ -353,10 +360,7 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
|
|
||||||
console.log(`Certificate set for ${domain}`);
|
console.log(`Certificate set for ${domain}`);
|
||||||
|
|
||||||
// Save certificate to store if enabled
|
// (Persistence of certificates moved to CertProvisioner)
|
||||||
if (this.options.certificateStore) {
|
|
||||||
this.saveCertificateToStore(domain, certificate, privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit certificate event
|
// Emit certificate event
|
||||||
this.emitCertificateEvent(Port80HandlerEvents.CERTIFICATE_ISSUED, {
|
this.emitCertificateEvent(Port80HandlerEvents.CERTIFICATE_ISSUED, {
|
||||||
@ -391,134 +395,7 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves a certificate to the filesystem store
|
|
||||||
* @param domain The domain for the certificate
|
|
||||||
* @param certificate The certificate (PEM format)
|
|
||||||
* @param privateKey The private key (PEM format)
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private saveCertificateToStore(domain: string, certificate: string, privateKey: string): void {
|
|
||||||
// Skip if certificate store is not enabled
|
|
||||||
if (!this.options.certificateStore) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const storePath = this.options.certificateStore;
|
|
||||||
|
|
||||||
// Ensure the directory exists
|
|
||||||
if (!fs.existsSync(storePath)) {
|
|
||||||
fs.mkdirSync(storePath, { recursive: true });
|
|
||||||
console.log(`Created certificate store directory: ${storePath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const certPath = path.join(storePath, `${domain}.cert.pem`);
|
|
||||||
const keyPath = path.join(storePath, `${domain}.key.pem`);
|
|
||||||
|
|
||||||
// Write certificate and private key files
|
|
||||||
fs.writeFileSync(certPath, certificate);
|
|
||||||
fs.writeFileSync(keyPath, privateKey);
|
|
||||||
|
|
||||||
// Set secure permissions for private key
|
|
||||||
try {
|
|
||||||
fs.chmodSync(keyPath, 0o600);
|
|
||||||
} catch (err) {
|
|
||||||
console.log(`Warning: Could not set secure permissions on ${keyPath}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Saved certificate for ${domain} to ${certPath}`);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Error saving certificate for ${domain}:`, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads certificates from the certificate store
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private loadCertificatesFromStore(): void {
|
|
||||||
if (!this.options.certificateStore) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const storePath = this.options.certificateStore;
|
|
||||||
|
|
||||||
// Ensure the directory exists
|
|
||||||
if (!fs.existsSync(storePath)) {
|
|
||||||
fs.mkdirSync(storePath, { recursive: true });
|
|
||||||
console.log(`Created certificate store directory: ${storePath}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get list of certificate files
|
|
||||||
const files = fs.readdirSync(storePath);
|
|
||||||
const certFiles = files.filter(file => file.endsWith('.cert.pem'));
|
|
||||||
|
|
||||||
// Load each certificate
|
|
||||||
for (const certFile of certFiles) {
|
|
||||||
const domain = certFile.replace('.cert.pem', '');
|
|
||||||
const keyFile = `${domain}.key.pem`;
|
|
||||||
|
|
||||||
// Skip if key file doesn't exist
|
|
||||||
if (!files.includes(keyFile)) {
|
|
||||||
console.log(`Warning: Found certificate for ${domain} but no key file`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if we should skip configured certs
|
|
||||||
if (this.options.skipConfiguredCerts) {
|
|
||||||
const domainInfo = this.domainCertificates.get(domain);
|
|
||||||
if (domainInfo && domainInfo.certObtained) {
|
|
||||||
console.log(`Skipping already configured certificate for ${domain}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load certificate and key
|
|
||||||
try {
|
|
||||||
const certificate = fs.readFileSync(path.join(storePath, certFile), 'utf8');
|
|
||||||
const privateKey = fs.readFileSync(path.join(storePath, keyFile), 'utf8');
|
|
||||||
|
|
||||||
// Extract expiry date
|
|
||||||
let expiryDate: Date | undefined;
|
|
||||||
try {
|
|
||||||
const matches = certificate.match(/Not After\s*:\s*(.*?)(?:\n|$)/i);
|
|
||||||
if (matches && matches[1]) {
|
|
||||||
expiryDate = new Date(matches[1]);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.log(`Warning: Could not extract expiry date from certificate for ${domain}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if domain is already registered
|
|
||||||
let domainInfo = this.domainCertificates.get(domain);
|
|
||||||
if (!domainInfo) {
|
|
||||||
// Register domain if not already registered
|
|
||||||
domainInfo = {
|
|
||||||
options: {
|
|
||||||
domainName: domain,
|
|
||||||
sslRedirect: true,
|
|
||||||
acmeMaintenance: true
|
|
||||||
},
|
|
||||||
certObtained: false,
|
|
||||||
obtainingInProgress: false
|
|
||||||
};
|
|
||||||
this.domainCertificates.set(domain, domainInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set certificate
|
|
||||||
domainInfo.certificate = certificate;
|
|
||||||
domainInfo.privateKey = privateKey;
|
|
||||||
domainInfo.certObtained = true;
|
|
||||||
domainInfo.expiryDate = expiryDate;
|
|
||||||
|
|
||||||
console.log(`Loaded certificate for ${domain} from store, valid until ${expiryDate?.toISOString() || 'unknown'}`);
|
|
||||||
} catch (err) {
|
|
||||||
console.error(`Error loading certificate for ${domain}:`, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error loading certificates from store:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a domain is a glob pattern
|
* Check if a domain is a glob pattern
|
||||||
@ -579,38 +456,6 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Lazy initialization of the ACME client
|
|
||||||
* @returns An ACME client instance
|
|
||||||
*/
|
|
||||||
private async getAcmeClient(): Promise<plugins.acme.Client> {
|
|
||||||
if (this.acmeClient) {
|
|
||||||
return this.acmeClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Generate a new account key
|
|
||||||
this.accountKey = (await plugins.acme.forge.createPrivateKey()).toString();
|
|
||||||
|
|
||||||
this.acmeClient = new plugins.acme.Client({
|
|
||||||
directoryUrl: this.options.useProduction
|
|
||||||
? plugins.acme.directory.letsencrypt.production
|
|
||||||
: plugins.acme.directory.letsencrypt.staging,
|
|
||||||
accountKey: this.accountKey,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a new account
|
|
||||||
await this.acmeClient.createAccount({
|
|
||||||
termsOfServiceAgreed: true,
|
|
||||||
contact: [`mailto:${this.options.contactEmail}`],
|
|
||||||
});
|
|
||||||
|
|
||||||
return this.acmeClient;
|
|
||||||
} catch (error) {
|
|
||||||
const message = error instanceof Error ? error.message : 'Unknown error initializing ACME client';
|
|
||||||
throw new Port80HandlerError(`Failed to initialize ACME client: ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles incoming HTTP requests
|
* Handles incoming HTTP requests
|
||||||
@ -640,19 +485,32 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
const { domainInfo, pattern } = domainMatch;
|
const { domainInfo, pattern } = domainMatch;
|
||||||
const options = domainInfo.options;
|
const options = domainInfo.options;
|
||||||
|
|
||||||
// If the request is for an ACME HTTP-01 challenge, handle it
|
// Handle ACME HTTP-01 challenge requests or forwarding
|
||||||
if (req.url && req.url.startsWith('/.well-known/acme-challenge/') && (options.acmeMaintenance || options.acmeForward)) {
|
if (req.url && req.url.startsWith('/.well-known/acme-challenge/')) {
|
||||||
// Check if we should forward ACME requests
|
// Forward ACME requests if configured
|
||||||
if (options.acmeForward) {
|
if (options.acmeForward) {
|
||||||
this.forwardRequest(req, res, options.acmeForward, 'ACME challenge');
|
this.forwardRequest(req, res, options.acmeForward, 'ACME challenge');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// If not managing ACME for this domain, return 404
|
||||||
// Only handle ACME challenges for non-glob patterns
|
if (!options.acmeMaintenance) {
|
||||||
if (!this.isGlobPattern(pattern)) {
|
res.statusCode = 404;
|
||||||
this.handleAcmeChallenge(req, res, domain);
|
res.end('Not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Serve challenge response from in-memory storage
|
||||||
|
const token = req.url.split('/').pop() || '';
|
||||||
|
const keyAuth = this.acmeHttp01Storage.get(token);
|
||||||
|
if (keyAuth) {
|
||||||
|
res.statusCode = 200;
|
||||||
|
res.setHeader('Content-Type', 'text/plain');
|
||||||
|
res.end(keyAuth);
|
||||||
|
console.log(`Served ACME challenge response for ${domain}`);
|
||||||
|
} else {
|
||||||
|
res.statusCode = 404;
|
||||||
|
res.end('Challenge token not found');
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we should forward non-ACME requests
|
// Check if we should forward non-ACME requests
|
||||||
@ -762,292 +620,71 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Serves the ACME HTTP-01 challenge response
|
|
||||||
* @param req The HTTP request
|
|
||||||
* @param res The HTTP response
|
|
||||||
* @param domain The domain for the challenge
|
|
||||||
*/
|
|
||||||
private handleAcmeChallenge(req: plugins.http.IncomingMessage, res: plugins.http.ServerResponse, domain: string): void {
|
|
||||||
const domainInfo = this.domainCertificates.get(domain);
|
|
||||||
if (!domainInfo) {
|
|
||||||
res.statusCode = 404;
|
|
||||||
res.end('Domain not configured');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The token is the last part of the URL
|
|
||||||
const urlParts = req.url?.split('/');
|
|
||||||
const token = urlParts ? urlParts[urlParts.length - 1] : '';
|
|
||||||
|
|
||||||
if (domainInfo.challengeToken === token && domainInfo.challengeKeyAuthorization) {
|
|
||||||
res.statusCode = 200;
|
|
||||||
res.setHeader('Content-Type', 'text/plain');
|
|
||||||
res.end(domainInfo.challengeKeyAuthorization);
|
|
||||||
console.log(`Served ACME challenge response for ${domain}`);
|
|
||||||
} else {
|
|
||||||
res.statusCode = 404;
|
|
||||||
res.end('Challenge token not found');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtains a certificate for a domain using ACME HTTP-01 challenge
|
* Obtains a certificate for a domain using ACME HTTP-01 challenge
|
||||||
* @param domain The domain to obtain a certificate for
|
* @param domain The domain to obtain a certificate for
|
||||||
* @param isRenewal Whether this is a renewal attempt
|
* @param isRenewal Whether this is a renewal attempt
|
||||||
*/
|
*/
|
||||||
|
/**
|
||||||
|
* Obtains a certificate for a domain using SmartAcme HTTP-01 challenges
|
||||||
|
* @param domain The domain to obtain a certificate for
|
||||||
|
* @param isRenewal Whether this is a renewal attempt
|
||||||
|
*/
|
||||||
private async obtainCertificate(domain: string, isRenewal: boolean = false): Promise<void> {
|
private async obtainCertificate(domain: string, isRenewal: boolean = false): Promise<void> {
|
||||||
// Don't allow certificate issuance for glob patterns
|
|
||||||
if (this.isGlobPattern(domain)) {
|
if (this.isGlobPattern(domain)) {
|
||||||
throw new CertificateError('Cannot obtain certificates for glob pattern domains', domain, isRenewal);
|
throw new CertificateError('Cannot obtain certificates for glob pattern domains', domain, isRenewal);
|
||||||
}
|
}
|
||||||
|
const domainInfo = this.domainCertificates.get(domain)!;
|
||||||
// Get the domain info
|
|
||||||
const domainInfo = this.domainCertificates.get(domain);
|
|
||||||
if (!domainInfo) {
|
|
||||||
throw new CertificateError('Domain not found', domain, isRenewal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify that acmeMaintenance is enabled
|
|
||||||
if (!domainInfo.options.acmeMaintenance) {
|
if (!domainInfo.options.acmeMaintenance) {
|
||||||
console.log(`Skipping certificate issuance for ${domain} - acmeMaintenance is disabled`);
|
console.log(`Skipping certificate issuance for ${domain} - acmeMaintenance is disabled`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent concurrent certificate issuance
|
|
||||||
if (domainInfo.obtainingInProgress) {
|
if (domainInfo.obtainingInProgress) {
|
||||||
console.log(`Certificate issuance already in progress for ${domain}`);
|
console.log(`Certificate issuance already in progress for ${domain}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!this.smartAcme) {
|
||||||
|
throw new Port80HandlerError('SmartAcme is not initialized');
|
||||||
|
}
|
||||||
domainInfo.obtainingInProgress = true;
|
domainInfo.obtainingInProgress = true;
|
||||||
domainInfo.lastRenewalAttempt = new Date();
|
domainInfo.lastRenewalAttempt = new Date();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const client = await this.getAcmeClient();
|
// Request certificate via SmartAcme
|
||||||
|
const certObj = await this.smartAcme.getCertificateForDomain(domain);
|
||||||
// Create a new order for the domain
|
const certificate = certObj.publicKey;
|
||||||
const order = await client.createOrder({
|
const privateKey = certObj.privateKey;
|
||||||
identifiers: [{ type: 'dns', value: domain }],
|
const expiryDate = new Date(certObj.validUntil);
|
||||||
});
|
|
||||||
|
|
||||||
// Get the authorizations for the order
|
|
||||||
const authorizations = await client.getAuthorizations(order);
|
|
||||||
|
|
||||||
// Process each authorization
|
|
||||||
await this.processAuthorizations(client, domain, authorizations);
|
|
||||||
|
|
||||||
// Generate a CSR and private key
|
|
||||||
const [csrBuffer, privateKeyBuffer] = await plugins.acme.forge.createCsr({
|
|
||||||
commonName: domain,
|
|
||||||
});
|
|
||||||
|
|
||||||
const csr = csrBuffer.toString();
|
|
||||||
const privateKey = privateKeyBuffer.toString();
|
|
||||||
|
|
||||||
// Finalize the order with our CSR
|
|
||||||
await client.finalizeOrder(order, csr);
|
|
||||||
|
|
||||||
// Get the certificate with the full chain
|
|
||||||
const certificate = await client.getCertificate(order);
|
|
||||||
|
|
||||||
// Store the certificate and key
|
|
||||||
domainInfo.certificate = certificate;
|
domainInfo.certificate = certificate;
|
||||||
domainInfo.privateKey = privateKey;
|
domainInfo.privateKey = privateKey;
|
||||||
domainInfo.certObtained = true;
|
domainInfo.certObtained = true;
|
||||||
|
domainInfo.expiryDate = expiryDate;
|
||||||
// Clear challenge data
|
|
||||||
delete domainInfo.challengeToken;
|
|
||||||
delete domainInfo.challengeKeyAuthorization;
|
|
||||||
|
|
||||||
// Extract expiry date from certificate
|
|
||||||
domainInfo.expiryDate = this.extractExpiryDateFromCertificate(certificate, domain);
|
|
||||||
|
|
||||||
console.log(`Certificate ${isRenewal ? 'renewed' : 'obtained'} for ${domain}`);
|
console.log(`Certificate ${isRenewal ? 'renewed' : 'obtained'} for ${domain}`);
|
||||||
|
// Persistence moved to CertProvisioner
|
||||||
// Save the certificate to the store if enabled
|
|
||||||
if (this.options.certificateStore) {
|
|
||||||
this.saveCertificateToStore(domain, certificate, privateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit the appropriate event
|
|
||||||
const eventType = isRenewal
|
const eventType = isRenewal
|
||||||
? Port80HandlerEvents.CERTIFICATE_RENEWED
|
? Port80HandlerEvents.CERTIFICATE_RENEWED
|
||||||
: Port80HandlerEvents.CERTIFICATE_ISSUED;
|
: Port80HandlerEvents.CERTIFICATE_ISSUED;
|
||||||
|
|
||||||
this.emitCertificateEvent(eventType, {
|
this.emitCertificateEvent(eventType, {
|
||||||
domain,
|
domain,
|
||||||
certificate,
|
certificate,
|
||||||
privateKey,
|
privateKey,
|
||||||
expiryDate: domainInfo.expiryDate || this.getDefaultExpiryDate()
|
expiryDate: expiryDate || this.getDefaultExpiryDate()
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// Check for rate limit errors
|
const errorMsg = error?.message || 'Unknown error';
|
||||||
if (error.message && (
|
|
||||||
error.message.includes('rateLimited') ||
|
|
||||||
error.message.includes('too many certificates') ||
|
|
||||||
error.message.includes('rate limit')
|
|
||||||
)) {
|
|
||||||
console.error(`Rate limit reached for ${domain}. Waiting before retry.`);
|
|
||||||
} else {
|
|
||||||
console.error(`Error during certificate issuance for ${domain}:`, error);
|
console.error(`Error during certificate issuance for ${domain}:`, error);
|
||||||
}
|
|
||||||
|
|
||||||
// Emit failure event
|
|
||||||
this.emit(Port80HandlerEvents.CERTIFICATE_FAILED, {
|
this.emit(Port80HandlerEvents.CERTIFICATE_FAILED, {
|
||||||
domain,
|
domain,
|
||||||
error: error.message || 'Unknown error',
|
error: errorMsg,
|
||||||
isRenewal
|
isRenewal
|
||||||
} as ICertificateFailure);
|
} as ICertificateFailure);
|
||||||
|
throw new CertificateError(errorMsg, domain, isRenewal);
|
||||||
throw new CertificateError(
|
|
||||||
error.message || 'Certificate issuance failed',
|
|
||||||
domain,
|
|
||||||
isRenewal
|
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
// Reset flag whether successful or not
|
|
||||||
domainInfo.obtainingInProgress = false;
|
domainInfo.obtainingInProgress = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Process ACME authorizations by verifying and completing challenges
|
|
||||||
* @param client ACME client
|
|
||||||
* @param domain Domain name
|
|
||||||
* @param authorizations Authorizations to process
|
|
||||||
*/
|
|
||||||
private async processAuthorizations(
|
|
||||||
client: plugins.acme.Client,
|
|
||||||
domain: string,
|
|
||||||
authorizations: plugins.acme.Authorization[]
|
|
||||||
): Promise<void> {
|
|
||||||
const domainInfo = this.domainCertificates.get(domain);
|
|
||||||
if (!domainInfo) {
|
|
||||||
throw new CertificateError('Domain not found during authorization', domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const authz of authorizations) {
|
|
||||||
const challenge = authz.challenges.find(ch => ch.type === 'http-01');
|
|
||||||
if (!challenge) {
|
|
||||||
throw new CertificateError('HTTP-01 challenge not found', domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the key authorization for the challenge
|
|
||||||
const keyAuthorization = await client.getChallengeKeyAuthorization(challenge);
|
|
||||||
|
|
||||||
// Store the challenge data
|
|
||||||
domainInfo.challengeToken = challenge.token;
|
|
||||||
domainInfo.challengeKeyAuthorization = keyAuthorization;
|
|
||||||
|
|
||||||
// ACME client type definition workaround - use compatible approach
|
|
||||||
// First check if challenge verification is needed
|
|
||||||
const authzUrl = authz.url;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check if authzUrl exists and perform verification
|
|
||||||
if (authzUrl) {
|
|
||||||
await client.verifyChallenge(authz, challenge);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Complete the challenge
|
|
||||||
await client.completeChallenge(challenge);
|
|
||||||
|
|
||||||
// Wait for validation
|
|
||||||
await client.waitForValidStatus(challenge);
|
|
||||||
console.log(`HTTP-01 challenge completed for ${domain}`);
|
|
||||||
} catch (error) {
|
|
||||||
const errorMessage = error instanceof Error ? error.message : 'Unknown challenge error';
|
|
||||||
console.error(`Challenge error for ${domain}:`, error);
|
|
||||||
throw new CertificateError(`Challenge verification failed: ${errorMessage}`, domain);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the certificate renewal timer
|
|
||||||
*/
|
|
||||||
private startRenewalTimer(): void {
|
|
||||||
if (this.renewalTimer) {
|
|
||||||
clearInterval(this.renewalTimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert hours to milliseconds
|
|
||||||
const checkInterval = this.options.renewCheckIntervalHours * 60 * 60 * 1000;
|
|
||||||
|
|
||||||
this.renewalTimer = setInterval(() => this.checkForRenewals(), checkInterval);
|
|
||||||
|
|
||||||
// Prevent the timer from keeping the process alive
|
|
||||||
if (this.renewalTimer.unref) {
|
|
||||||
this.renewalTimer.unref();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Certificate renewal check scheduled every ${this.options.renewCheckIntervalHours} hours`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks for certificates that need renewal
|
|
||||||
*/
|
|
||||||
private checkForRenewals(): void {
|
|
||||||
if (this.isShuttingDown) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip renewal if auto-renewal is disabled
|
|
||||||
if (this.options.autoRenew === false) {
|
|
||||||
console.log('Auto-renewal is disabled, skipping certificate renewal check');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Checking for certificates that need renewal...');
|
|
||||||
|
|
||||||
const now = new Date();
|
|
||||||
const renewThresholdMs = this.options.renewThresholdDays * 24 * 60 * 60 * 1000;
|
|
||||||
|
|
||||||
for (const [domain, domainInfo] of this.domainCertificates.entries()) {
|
|
||||||
// Skip glob patterns
|
|
||||||
if (this.isGlobPattern(domain)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip domains with acmeMaintenance disabled
|
|
||||||
if (!domainInfo.options.acmeMaintenance) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip domains without certificates or already in renewal
|
|
||||||
if (!domainInfo.certObtained || domainInfo.obtainingInProgress) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip domains without expiry dates
|
|
||||||
if (!domainInfo.expiryDate) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeUntilExpiry = domainInfo.expiryDate.getTime() - now.getTime();
|
|
||||||
|
|
||||||
// Check if certificate is near expiry
|
|
||||||
if (timeUntilExpiry <= renewThresholdMs) {
|
|
||||||
console.log(`Certificate for ${domain} expires soon, renewing...`);
|
|
||||||
|
|
||||||
const daysRemaining = Math.ceil(timeUntilExpiry / (24 * 60 * 60 * 1000));
|
|
||||||
|
|
||||||
this.emit(Port80HandlerEvents.CERTIFICATE_EXPIRING, {
|
|
||||||
domain,
|
|
||||||
expiryDate: domainInfo.expiryDate,
|
|
||||||
daysRemaining
|
|
||||||
} as ICertificateExpiring);
|
|
||||||
|
|
||||||
// Start renewal process
|
|
||||||
this.obtainCertificate(domain, true).catch(err => {
|
|
||||||
const errorMessage = err instanceof Error ? err.message : 'Unknown error';
|
|
||||||
console.error(`Error renewing certificate for ${domain}:`, errorMessage);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract expiry date from certificate using a more robust approach
|
* Extract expiry date from certificate using a more robust approach
|
||||||
@ -1176,4 +813,16 @@ export class Port80Handler extends plugins.EventEmitter {
|
|||||||
public getConfig(): Required<IPort80HandlerOptions> {
|
public getConfig(): Required<IPort80HandlerOptions> {
|
||||||
return { ...this.options };
|
return { ...this.options };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a certificate renewal for a specific domain.
|
||||||
|
* @param domain The domain to renew.
|
||||||
|
*/
|
||||||
|
public async renewCertificate(domain: string): Promise<void> {
|
||||||
|
if (!this.domainCertificates.has(domain)) {
|
||||||
|
throw new Port80HandlerError(`Domain not managed: ${domain}`);
|
||||||
|
}
|
||||||
|
// Trigger renewal via ACME
|
||||||
|
await this.obtainCertificate(domain, true);
|
||||||
|
}
|
||||||
}
|
}
|
183
ts/smartproxy/classes.pp.certprovisioner.ts
Normal file
183
ts/smartproxy/classes.pp.certprovisioner.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
import * as plugins from '../plugins.js';
|
||||||
|
import type { IDomainConfig, ISmartProxyCertProvisionObject } from './classes.pp.interfaces.js';
|
||||||
|
import { Port80Handler, Port80HandlerEvents, type ICertificateData } from '../port80handler/classes.port80handler.js';
|
||||||
|
import type { NetworkProxyBridge } from './classes.pp.networkproxybridge.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CertProvisioner manages certificate provisioning and renewal workflows,
|
||||||
|
* unifying static certificates and HTTP-01 challenges via Port80Handler.
|
||||||
|
*/
|
||||||
|
export class CertProvisioner extends plugins.EventEmitter {
|
||||||
|
private domainConfigs: IDomainConfig[];
|
||||||
|
private port80Handler: Port80Handler;
|
||||||
|
private networkProxyBridge: NetworkProxyBridge;
|
||||||
|
private certProvider?: (domain: string) => Promise<ISmartProxyCertProvisionObject>;
|
||||||
|
private forwardConfigs: Array<{ domain: string; forwardConfig?: { ip: string; port: number }; acmeForwardConfig?: { ip: string; port: number }; sslRedirect: boolean }>;
|
||||||
|
private renewThresholdDays: number;
|
||||||
|
private renewCheckIntervalHours: number;
|
||||||
|
private autoRenew: boolean;
|
||||||
|
private renewManager?: plugins.taskbuffer.TaskManager;
|
||||||
|
// Track provisioning type per domain: 'http01' or 'static'
|
||||||
|
private provisionMap: Map<string, 'http01' | 'static'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param domainConfigs Array of domain configuration objects
|
||||||
|
* @param port80Handler HTTP-01 challenge handler instance
|
||||||
|
* @param networkProxyBridge Bridge for applying external certificates
|
||||||
|
* @param certProvider Optional callback returning a static cert or 'http01'
|
||||||
|
* @param renewThresholdDays Days before expiry to trigger renewals
|
||||||
|
* @param renewCheckIntervalHours Interval in hours to check for renewals
|
||||||
|
* @param autoRenew Whether to automatically schedule renewals
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
domainConfigs: IDomainConfig[],
|
||||||
|
port80Handler: Port80Handler,
|
||||||
|
networkProxyBridge: NetworkProxyBridge,
|
||||||
|
certProvider?: (domain: string) => Promise<ISmartProxyCertProvisionObject>,
|
||||||
|
renewThresholdDays: number = 30,
|
||||||
|
renewCheckIntervalHours: number = 24,
|
||||||
|
autoRenew: boolean = true,
|
||||||
|
forwardConfigs: Array<{ domain: string; forwardConfig?: { ip: string; port: number }; acmeForwardConfig?: { ip: string; port: number }; sslRedirect: boolean }> = []
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this.domainConfigs = domainConfigs;
|
||||||
|
this.port80Handler = port80Handler;
|
||||||
|
this.networkProxyBridge = networkProxyBridge;
|
||||||
|
this.certProvider = certProvider;
|
||||||
|
this.renewThresholdDays = renewThresholdDays;
|
||||||
|
this.renewCheckIntervalHours = renewCheckIntervalHours;
|
||||||
|
this.autoRenew = autoRenew;
|
||||||
|
this.provisionMap = new Map();
|
||||||
|
this.forwardConfigs = forwardConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start initial provisioning and schedule renewals.
|
||||||
|
*/
|
||||||
|
public async start(): Promise<void> {
|
||||||
|
// Subscribe to Port80Handler certificate events
|
||||||
|
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (data: ICertificateData) => {
|
||||||
|
this.emit('certificate', { ...data, source: 'http01', isRenewal: false });
|
||||||
|
});
|
||||||
|
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (data: ICertificateData) => {
|
||||||
|
this.emit('certificate', { ...data, source: 'http01', isRenewal: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Apply external forwarding for ACME challenges (e.g. Synology)
|
||||||
|
for (const f of this.forwardConfigs) {
|
||||||
|
this.port80Handler.addDomain({
|
||||||
|
domainName: f.domain,
|
||||||
|
sslRedirect: f.sslRedirect,
|
||||||
|
acmeMaintenance: false,
|
||||||
|
forward: f.forwardConfig,
|
||||||
|
acmeForward: f.acmeForwardConfig
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Initial provisioning for all domains
|
||||||
|
const domains = this.domainConfigs.flatMap(cfg => cfg.domains);
|
||||||
|
for (const domain of domains) {
|
||||||
|
// Skip wildcard domains
|
||||||
|
if (domain.includes('*')) continue;
|
||||||
|
let provision: ISmartProxyCertProvisionObject | 'http01' = 'http01';
|
||||||
|
if (this.certProvider) {
|
||||||
|
try {
|
||||||
|
provision = await this.certProvider(domain);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`certProvider error for ${domain}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (provision === 'http01') {
|
||||||
|
this.provisionMap.set(domain, 'http01');
|
||||||
|
this.port80Handler.addDomain({ domainName: domain, sslRedirect: true, acmeMaintenance: true });
|
||||||
|
} else {
|
||||||
|
this.provisionMap.set(domain, 'static');
|
||||||
|
const certObj = provision as plugins.tsclass.network.ICert;
|
||||||
|
const certData: ICertificateData = {
|
||||||
|
domain: certObj.domainName,
|
||||||
|
certificate: certObj.publicKey,
|
||||||
|
privateKey: certObj.privateKey,
|
||||||
|
expiryDate: new Date(certObj.validUntil)
|
||||||
|
};
|
||||||
|
this.networkProxyBridge.applyExternalCertificate(certData);
|
||||||
|
this.emit('certificate', { ...certData, source: 'static', isRenewal: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedule renewals if enabled
|
||||||
|
if (this.autoRenew) {
|
||||||
|
this.renewManager = new plugins.taskbuffer.TaskManager();
|
||||||
|
const renewTask = new plugins.taskbuffer.Task({
|
||||||
|
name: 'CertificateRenewals',
|
||||||
|
taskFunction: async () => {
|
||||||
|
for (const [domain, type] of this.provisionMap.entries()) {
|
||||||
|
// Skip wildcard domains
|
||||||
|
if (domain.includes('*')) continue;
|
||||||
|
try {
|
||||||
|
if (type === 'http01') {
|
||||||
|
await this.port80Handler.renewCertificate(domain);
|
||||||
|
} else if (type === 'static' && this.certProvider) {
|
||||||
|
const provision2 = await this.certProvider(domain);
|
||||||
|
if (provision2 !== 'http01') {
|
||||||
|
const certObj = provision2 as plugins.tsclass.network.ICert;
|
||||||
|
const certData: ICertificateData = {
|
||||||
|
domain: certObj.domainName,
|
||||||
|
certificate: certObj.publicKey,
|
||||||
|
privateKey: certObj.privateKey,
|
||||||
|
expiryDate: new Date(certObj.validUntil)
|
||||||
|
};
|
||||||
|
this.networkProxyBridge.applyExternalCertificate(certData);
|
||||||
|
this.emit('certificate', { ...certData, source: 'static', isRenewal: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Renewal error for ${domain}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const hours = this.renewCheckIntervalHours;
|
||||||
|
const cronExpr = `0 0 */${hours} * * *`;
|
||||||
|
this.renewManager.addAndScheduleTask(renewTask, cronExpr);
|
||||||
|
this.renewManager.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop all scheduled renewal tasks.
|
||||||
|
*/
|
||||||
|
public async stop(): Promise<void> {
|
||||||
|
// Stop scheduled renewals
|
||||||
|
if (this.renewManager) {
|
||||||
|
this.renewManager.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a certificate on-demand for the given domain.
|
||||||
|
* @param domain Domain name to provision
|
||||||
|
*/
|
||||||
|
public async requestCertificate(domain: string): Promise<void> {
|
||||||
|
// Skip wildcard domains
|
||||||
|
if (domain.includes('*')) {
|
||||||
|
throw new Error(`Cannot request certificate for wildcard domain: ${domain}`);
|
||||||
|
}
|
||||||
|
// Determine provisioning method
|
||||||
|
let provision: ISmartProxyCertProvisionObject | 'http01' = 'http01';
|
||||||
|
if (this.certProvider) {
|
||||||
|
provision = await this.certProvider(domain);
|
||||||
|
}
|
||||||
|
if (provision === 'http01') {
|
||||||
|
await this.port80Handler.renewCertificate(domain);
|
||||||
|
} else {
|
||||||
|
const certObj = provision as plugins.tsclass.network.ICert;
|
||||||
|
const certData: ICertificateData = {
|
||||||
|
domain: certObj.domainName,
|
||||||
|
certificate: certObj.publicKey,
|
||||||
|
privateKey: certObj.privateKey,
|
||||||
|
expiryDate: new Date(certObj.validUntil)
|
||||||
|
};
|
||||||
|
this.networkProxyBridge.applyExternalCertificate(certData);
|
||||||
|
this.emit('certificate', { ...certData, source: 'static', isRenewal: false });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,10 @@
|
|||||||
import * as plugins from '../plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provision object for static or HTTP-01 certificate
|
||||||
|
*/
|
||||||
|
export type ISmartProxyCertProvisionObject = plugins.tsclass.network.ICert | 'http01';
|
||||||
|
|
||||||
/** Domain configuration with per-domain allowed port ranges */
|
/** Domain configuration with per-domain allowed port ranges */
|
||||||
export interface IDomainConfig {
|
export interface IDomainConfig {
|
||||||
domains: string[]; // Glob patterns for domain(s)
|
domains: string[]; // Glob patterns for domain(s)
|
||||||
@ -104,17 +109,11 @@ export interface IPortProxySettings {
|
|||||||
}>;
|
}>;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Legacy ACME configuration (deprecated, use port80HandlerConfig instead)
|
/**
|
||||||
acme?: {
|
* Optional certificate provider callback. Return 'http01' to use HTTP-01 challenges,
|
||||||
enabled?: boolean;
|
* or a static certificate object for immediate provisioning.
|
||||||
port?: number;
|
*/
|
||||||
contactEmail?: string;
|
certProvider?: (domain: string) => Promise<ISmartProxyCertProvisionObject>;
|
||||||
useProduction?: boolean;
|
|
||||||
renewThresholdDays?: number;
|
|
||||||
autoRenew?: boolean;
|
|
||||||
certificateStore?: string;
|
|
||||||
skipConfiguredCerts?: boolean;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,10 +43,6 @@ export class NetworkProxyBridge {
|
|||||||
useExternalPort80Handler: !!this.port80Handler // Use Port80Handler if available
|
useExternalPort80Handler: !!this.port80Handler // Use Port80Handler if available
|
||||||
};
|
};
|
||||||
|
|
||||||
// Copy ACME settings for backward compatibility (if port80HandlerConfig not set)
|
|
||||||
if (!this.settings.port80HandlerConfig && this.settings.acme) {
|
|
||||||
networkProxyOptions.acme = { ...this.settings.acme };
|
|
||||||
}
|
|
||||||
|
|
||||||
this.networkProxy = new NetworkProxy(networkProxyOptions);
|
this.networkProxy = new NetworkProxy(networkProxyOptions);
|
||||||
|
|
||||||
@ -95,6 +91,17 @@ export class NetworkProxyBridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply an external (static) certificate into NetworkProxy
|
||||||
|
*/
|
||||||
|
public applyExternalCertificate(data: ICertificateData): void {
|
||||||
|
if (!this.networkProxy) {
|
||||||
|
console.log(`NetworkProxy not initialized: cannot apply external certificate for ${data.domain}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.handleCertificateEvent(data);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the NetworkProxy instance
|
* Get the NetworkProxy instance
|
||||||
*/
|
*/
|
||||||
@ -277,7 +284,7 @@ export class NetworkProxyBridge {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Log ACME-eligible domains
|
// Log ACME-eligible domains
|
||||||
const acmeEnabled = this.settings.port80HandlerConfig?.enabled || this.settings.acme?.enabled;
|
const acmeEnabled = !!this.settings.port80HandlerConfig?.enabled;
|
||||||
if (acmeEnabled) {
|
if (acmeEnabled) {
|
||||||
const acmeEligibleDomains = proxyConfigs
|
const acmeEligibleDomains = proxyConfigs
|
||||||
.filter((config) => !config.hostName.includes('*')) // Exclude wildcards
|
.filter((config) => !config.hostName.includes('*')) // Exclude wildcards
|
||||||
@ -338,7 +345,7 @@ export class NetworkProxyBridge {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.settings.port80HandlerConfig?.enabled && !this.settings.acme?.enabled) {
|
if (!this.settings.port80HandlerConfig?.enabled) {
|
||||||
console.log('Cannot request certificate - ACME is not enabled');
|
console.log('Cannot request certificate - ACME is not enabled');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -117,10 +117,6 @@ export class PortRangeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add ACME HTTP challenge port if enabled
|
|
||||||
if (this.settings.acme?.enabled && this.settings.acme.port) {
|
|
||||||
ports.add(this.settings.acme.port);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add global port ranges
|
// Add global port ranges
|
||||||
if (this.settings.globalPortRanges) {
|
if (this.settings.globalPortRanges) {
|
||||||
@ -202,12 +198,6 @@ export class PortRangeManager {
|
|||||||
warnings.push(`NetworkProxy port ${this.settings.networkProxyPort} is also used in port ranges`);
|
warnings.push(`NetworkProxy port ${this.settings.networkProxyPort} is also used in port ranges`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check ACME port
|
|
||||||
if (this.settings.acme?.enabled && this.settings.acme.port) {
|
|
||||||
if (portMappings.has(this.settings.acme.port)) {
|
|
||||||
warnings.push(`ACME HTTP challenge port ${this.settings.acme.port} is also used in port ranges`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return warnings;
|
return warnings;
|
||||||
}
|
}
|
||||||
|
@ -8,14 +8,16 @@ import { NetworkProxyBridge } from './classes.pp.networkproxybridge.js';
|
|||||||
import { TimeoutManager } from './classes.pp.timeoutmanager.js';
|
import { TimeoutManager } from './classes.pp.timeoutmanager.js';
|
||||||
import { PortRangeManager } from './classes.pp.portrangemanager.js';
|
import { PortRangeManager } from './classes.pp.portrangemanager.js';
|
||||||
import { ConnectionHandler } from './classes.pp.connectionhandler.js';
|
import { ConnectionHandler } from './classes.pp.connectionhandler.js';
|
||||||
import { Port80Handler, Port80HandlerEvents } from '../port80handler/classes.port80handler.js';
|
import { Port80Handler } from '../port80handler/classes.port80handler.js';
|
||||||
|
import { CertProvisioner } from './classes.pp.certprovisioner.js';
|
||||||
|
import type { ICertificateData } from '../port80handler/classes.port80handler.js';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SmartProxy - Main class that coordinates all components
|
* SmartProxy - Main class that coordinates all components
|
||||||
*/
|
*/
|
||||||
export class SmartProxy {
|
export class SmartProxy extends plugins.EventEmitter {
|
||||||
private netServers: plugins.net.Server[] = [];
|
private netServers: plugins.net.Server[] = [];
|
||||||
private connectionLogger: NodeJS.Timeout | null = null;
|
private connectionLogger: NodeJS.Timeout | null = null;
|
||||||
private isShuttingDown: boolean = false;
|
private isShuttingDown: boolean = false;
|
||||||
@ -32,8 +34,11 @@ export class SmartProxy {
|
|||||||
|
|
||||||
// Port80Handler for ACME certificate management
|
// Port80Handler for ACME certificate management
|
||||||
private port80Handler: Port80Handler | null = null;
|
private port80Handler: Port80Handler | null = null;
|
||||||
|
// CertProvisioner for unified certificate workflows
|
||||||
|
private certProvisioner?: CertProvisioner;
|
||||||
|
|
||||||
constructor(settingsArg: IPortProxySettings) {
|
constructor(settingsArg: IPortProxySettings) {
|
||||||
|
super();
|
||||||
// Set reasonable defaults for all settings
|
// Set reasonable defaults for all settings
|
||||||
this.settings = {
|
this.settings = {
|
||||||
...settingsArg,
|
...settingsArg,
|
||||||
@ -66,24 +71,8 @@ export class SmartProxy {
|
|||||||
globalPortRanges: settingsArg.globalPortRanges || [],
|
globalPortRanges: settingsArg.globalPortRanges || [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set port80HandlerConfig defaults, using legacy acme config if available
|
// Set default port80HandlerConfig if not provided
|
||||||
if (!this.settings.port80HandlerConfig || Object.keys(this.settings.port80HandlerConfig).length === 0) {
|
if (!this.settings.port80HandlerConfig || Object.keys(this.settings.port80HandlerConfig).length === 0) {
|
||||||
if (this.settings.acme) {
|
|
||||||
// Migrate from legacy acme config
|
|
||||||
this.settings.port80HandlerConfig = {
|
|
||||||
enabled: this.settings.acme.enabled,
|
|
||||||
port: this.settings.acme.port || 80,
|
|
||||||
contactEmail: this.settings.acme.contactEmail || 'admin@example.com',
|
|
||||||
useProduction: this.settings.acme.useProduction || false,
|
|
||||||
renewThresholdDays: this.settings.acme.renewThresholdDays || 30,
|
|
||||||
autoRenew: this.settings.acme.autoRenew !== false, // Default to true
|
|
||||||
certificateStore: this.settings.acme.certificateStore || './certs',
|
|
||||||
skipConfiguredCerts: this.settings.acme.skipConfiguredCerts || false,
|
|
||||||
httpsRedirectPort: this.settings.fromPort,
|
|
||||||
renewCheckIntervalHours: 24
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// Set defaults if no config provided
|
|
||||||
this.settings.port80HandlerConfig = {
|
this.settings.port80HandlerConfig = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
port: 80,
|
port: 80,
|
||||||
@ -97,7 +86,6 @@ export class SmartProxy {
|
|||||||
renewCheckIntervalHours: 24
|
renewCheckIntervalHours: 24
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize component managers
|
// Initialize component managers
|
||||||
this.timeoutManager = new TimeoutManager(this.settings);
|
this.timeoutManager = new TimeoutManager(this.settings);
|
||||||
@ -156,62 +144,13 @@ export class SmartProxy {
|
|||||||
port: config.port,
|
port: config.port,
|
||||||
contactEmail: config.contactEmail,
|
contactEmail: config.contactEmail,
|
||||||
useProduction: config.useProduction,
|
useProduction: config.useProduction,
|
||||||
renewThresholdDays: config.renewThresholdDays,
|
|
||||||
httpsRedirectPort: config.httpsRedirectPort || this.settings.fromPort,
|
httpsRedirectPort: config.httpsRedirectPort || this.settings.fromPort,
|
||||||
renewCheckIntervalHours: config.renewCheckIntervalHours,
|
|
||||||
enabled: config.enabled,
|
enabled: config.enabled,
|
||||||
autoRenew: config.autoRenew,
|
|
||||||
certificateStore: config.certificateStore,
|
certificateStore: config.certificateStore,
|
||||||
skipConfiguredCerts: config.skipConfiguredCerts
|
skipConfiguredCerts: config.skipConfiguredCerts
|
||||||
});
|
});
|
||||||
|
|
||||||
// Register domain forwarding configurations
|
|
||||||
if (config.domainForwards) {
|
|
||||||
for (const forward of config.domainForwards) {
|
|
||||||
this.port80Handler.addDomain({
|
|
||||||
domainName: forward.domain,
|
|
||||||
sslRedirect: true,
|
|
||||||
acmeMaintenance: true,
|
|
||||||
forward: forward.forwardConfig,
|
|
||||||
acmeForward: forward.acmeForwardConfig
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Registered domain forwarding for ${forward.domain}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register all non-wildcard domains from domain configs
|
|
||||||
for (const domainConfig of this.settings.domainConfigs) {
|
|
||||||
for (const domain of domainConfig.domains) {
|
|
||||||
// Skip wildcards
|
|
||||||
if (domain.includes('*')) continue;
|
|
||||||
|
|
||||||
this.port80Handler.addDomain({
|
|
||||||
domainName: domain,
|
|
||||||
sslRedirect: true,
|
|
||||||
acmeMaintenance: true
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`Registered domain ${domain} with Port80Handler`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up event listeners
|
|
||||||
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_ISSUED, (certData) => {
|
|
||||||
console.log(`Certificate issued for ${certData.domain}, valid until ${certData.expiryDate.toISOString()}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_RENEWED, (certData) => {
|
|
||||||
console.log(`Certificate renewed for ${certData.domain}, valid until ${certData.expiryDate.toISOString()}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_FAILED, (failureData) => {
|
|
||||||
console.log(`Certificate ${failureData.isRenewal ? 'renewal' : 'issuance'} failed for ${failureData.domain}: ${failureData.error}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.port80Handler.on(Port80HandlerEvents.CERTIFICATE_EXPIRING, (expiryData) => {
|
|
||||||
console.log(`Certificate for ${expiryData.domain} is expiring in ${expiryData.daysRemaining} days`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Share Port80Handler with NetworkProxyBridge
|
// Share Port80Handler with NetworkProxyBridge
|
||||||
this.networkProxyBridge.setPort80Handler(this.port80Handler);
|
this.networkProxyBridge.setPort80Handler(this.port80Handler);
|
||||||
@ -236,6 +175,37 @@ export class SmartProxy {
|
|||||||
|
|
||||||
// Initialize Port80Handler if enabled
|
// Initialize Port80Handler if enabled
|
||||||
await this.initializePort80Handler();
|
await this.initializePort80Handler();
|
||||||
|
// Initialize CertProvisioner for unified certificate workflows
|
||||||
|
if (this.port80Handler) {
|
||||||
|
this.certProvisioner = new CertProvisioner(
|
||||||
|
this.settings.domainConfigs,
|
||||||
|
this.port80Handler,
|
||||||
|
this.networkProxyBridge,
|
||||||
|
this.settings.certProvider,
|
||||||
|
this.settings.port80HandlerConfig?.renewThresholdDays || 30,
|
||||||
|
this.settings.port80HandlerConfig?.renewCheckIntervalHours || 24,
|
||||||
|
this.settings.port80HandlerConfig?.autoRenew !== false,
|
||||||
|
// External ACME forwarding for specific domains
|
||||||
|
this.settings.port80HandlerConfig?.domainForwards?.map(f => ({
|
||||||
|
domain: f.domain,
|
||||||
|
forwardConfig: f.forwardConfig,
|
||||||
|
acmeForwardConfig: f.acmeForwardConfig,
|
||||||
|
sslRedirect: false
|
||||||
|
})) || []
|
||||||
|
);
|
||||||
|
this.certProvisioner.on('certificate', (certData) => {
|
||||||
|
this.emit('certificate', {
|
||||||
|
domain: certData.domain,
|
||||||
|
publicKey: certData.certificate,
|
||||||
|
privateKey: certData.privateKey,
|
||||||
|
expiryDate: certData.expiryDate,
|
||||||
|
source: certData.source,
|
||||||
|
isRenewal: certData.isRenewal
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await this.certProvisioner.start();
|
||||||
|
console.log('CertProvisioner started');
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize and start NetworkProxy if needed
|
// Initialize and start NetworkProxy if needed
|
||||||
if (
|
if (
|
||||||
@ -364,6 +334,11 @@ export class SmartProxy {
|
|||||||
public async stop() {
|
public async stop() {
|
||||||
console.log('PortProxy shutting down...');
|
console.log('PortProxy shutting down...');
|
||||||
this.isShuttingDown = true;
|
this.isShuttingDown = true;
|
||||||
|
// Stop CertProvisioner if active
|
||||||
|
if (this.certProvisioner) {
|
||||||
|
await this.certProvisioner.stop();
|
||||||
|
console.log('CertProvisioner stopped');
|
||||||
|
}
|
||||||
|
|
||||||
// Stop the Port80Handler if running
|
// Stop the Port80Handler if running
|
||||||
if (this.port80Handler) {
|
if (this.port80Handler) {
|
||||||
@ -429,22 +404,40 @@ export class SmartProxy {
|
|||||||
await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
|
await this.networkProxyBridge.syncDomainConfigsToNetworkProxy();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If Port80Handler is running, register non-wildcard domains
|
// If Port80Handler is running, provision certificates per new domain
|
||||||
if (this.port80Handler && this.settings.port80HandlerConfig?.enabled) {
|
if (this.port80Handler && this.settings.port80HandlerConfig?.enabled) {
|
||||||
for (const domainConfig of newDomainConfigs) {
|
for (const domainConfig of newDomainConfigs) {
|
||||||
for (const domain of domainConfig.domains) {
|
for (const domain of domainConfig.domains) {
|
||||||
// Skip wildcards
|
|
||||||
if (domain.includes('*')) continue;
|
if (domain.includes('*')) continue;
|
||||||
|
let provision = 'http01' as string | plugins.tsclass.network.ICert;
|
||||||
|
if (this.settings.certProvider) {
|
||||||
|
try {
|
||||||
|
provision = await this.settings.certProvider(domain);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`certProvider error for ${domain}: ${err}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (provision === 'http01') {
|
||||||
this.port80Handler.addDomain({
|
this.port80Handler.addDomain({
|
||||||
domainName: domain,
|
domainName: domain,
|
||||||
sslRedirect: true,
|
sslRedirect: true,
|
||||||
acmeMaintenance: true
|
acmeMaintenance: true
|
||||||
});
|
});
|
||||||
|
console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`);
|
||||||
|
} else {
|
||||||
|
const certObj = provision as plugins.tsclass.network.ICert;
|
||||||
|
const certData: ICertificateData = {
|
||||||
|
domain: certObj.domainName,
|
||||||
|
certificate: certObj.publicKey,
|
||||||
|
privateKey: certObj.privateKey,
|
||||||
|
expiryDate: new Date(certObj.validUntil)
|
||||||
|
};
|
||||||
|
this.networkProxyBridge.applyExternalCertificate(certData);
|
||||||
|
console.log(`Applied static certificate for ${domain} from certProvider`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
console.log('Registered non-wildcard domains with Port80Handler');
|
console.log('Provisioned certificates for new domains');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,6 +508,27 @@ export class SmartProxy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform scheduled renewals for managed domains
|
||||||
|
*/
|
||||||
|
private async performRenewals(): Promise<void> {
|
||||||
|
if (!this.port80Handler) return;
|
||||||
|
const statuses = this.port80Handler.getDomainCertificateStatus();
|
||||||
|
const threshold = this.settings.port80HandlerConfig.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
|
||||||
*/
|
*/
|
||||||
|
Reference in New Issue
Block a user