Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
a9963f3b8a | |||
05c9156458 | |||
47e3c86487 | |||
1387928938 | |||
19578b061e | |||
e8a539829a | |||
a646f4ad28 |
31
changelog.md
31
changelog.md
@ -1,5 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-05-05 - 10.0.12 - fix(port80handler)
|
||||
refactor ACME challenge handling to use dedicated Http01MemoryHandler, remove obsolete readme.plan.md, and update version to 10.0.12
|
||||
|
||||
- Removed readme.plan.md planning document
|
||||
- Eliminated internal acmeHttp01Storage from Port80Handler
|
||||
- Instantiated and integrated Http01MemoryHandler as a class property for managing HTTP-01 challenges
|
||||
- Delegated ACME HTTP-01 challenge responses to smartAcmeHttp01Handler
|
||||
- Updated ts/00_commitinfo_data.ts version from 10.0.11 to 10.0.12
|
||||
- Adjusted certificate provisioning logic to properly handle wildcard domains and on-demand requests
|
||||
|
||||
## 2025-05-05 - 10.0.12 - fix(port80handler)
|
||||
Remove obsolete readme.plan.md and refactor Port80Handler's ACME challenge handling to use a dedicated Http01MemoryHandler
|
||||
|
||||
- Deleted readme.plan.md planning document which was no longer needed
|
||||
- Removed internal acmeHttp01Storage map from Port80Handler
|
||||
- Instantiated Http01MemoryHandler as a class property and provided it to SmartAcme for challenge handling
|
||||
- Delegated ACME HTTP-01 challenge responses to the new smartAcmeHttp01Handler instead of in-memory storage
|
||||
|
||||
## 2025-05-05 - 10.0.11 - fix(dependencies)
|
||||
Bump @push.rocks/smartacme to ^7.2.5 and @tsclass/tsclass to ^9.2.0; update MemoryCertManager import to use plugins.smartacme.certmanagers.MemoryCertManager()
|
||||
|
||||
- Updated @push.rocks/smartacme from ^7.2.4 to ^7.2.5
|
||||
- Updated @tsclass/tsclass from ^9.1.0 to ^9.2.0
|
||||
- Refactored MemoryCertManager instantiation to use the new import path
|
||||
|
||||
## 2025-05-05 - 10.0.10 - fix(docs)
|
||||
Update README: rename certProviderFunction to certProvisionFunction in configuration options for consistency.
|
||||
|
||||
- Replaced 'certProviderFunction' with 'certProvisionFunction' in the docs to reflect the updated API.
|
||||
- Ensured all references in the readme are consistent with the new naming convention.
|
||||
|
||||
## 2025-05-05 - 10.0.9 - fix(documentation)
|
||||
Update documentation to use 'certProviderFunction' instead of 'certProvider' in SmartProxy settings.
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@push.rocks/smartproxy",
|
||||
"version": "10.0.9",
|
||||
"version": "10.0.12",
|
||||
"private": false,
|
||||
"description": "A powerful proxy package that effectively handles high traffic, with features such as SSL/TLS support, port proxying, WebSocket handling, dynamic routing with authentication options, and automatic ACME certificate management.",
|
||||
"main": "dist_ts/index.js",
|
||||
@ -24,14 +24,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@push.rocks/lik": "^6.2.2",
|
||||
"@push.rocks/smartacme": "^7.2.4",
|
||||
"@push.rocks/smartacme": "^7.3.2",
|
||||
"@push.rocks/smartdelay": "^3.0.5",
|
||||
"@push.rocks/smartnetwork": "^4.0.1",
|
||||
"@push.rocks/smartpromise": "^4.2.3",
|
||||
"@push.rocks/smartrequest": "^2.1.0",
|
||||
"@push.rocks/smartstring": "^4.0.15",
|
||||
"@push.rocks/taskbuffer": "^3.1.7",
|
||||
"@tsclass/tsclass": "^9.1.0",
|
||||
"@tsclass/tsclass": "^9.2.0",
|
||||
"@types/minimatch": "^5.1.2",
|
||||
"@types/ws": "^8.18.1",
|
||||
"minimatch": "^10.0.1",
|
||||
|
34
pnpm-lock.yaml
generated
34
pnpm-lock.yaml
generated
@ -12,8 +12,8 @@ importers:
|
||||
specifier: ^6.2.2
|
||||
version: 6.2.2
|
||||
'@push.rocks/smartacme':
|
||||
specifier: ^7.2.4
|
||||
version: 7.2.4(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)
|
||||
specifier: ^7.3.2
|
||||
version: 7.3.2(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)
|
||||
'@push.rocks/smartdelay':
|
||||
specifier: ^3.0.5
|
||||
version: 3.0.5
|
||||
@ -33,8 +33,8 @@ importers:
|
||||
specifier: ^3.1.7
|
||||
version: 3.1.7
|
||||
'@tsclass/tsclass':
|
||||
specifier: ^9.1.0
|
||||
version: 9.1.0
|
||||
specifier: ^9.2.0
|
||||
version: 9.2.0
|
||||
'@types/minimatch':
|
||||
specifier: ^5.1.2
|
||||
version: 5.1.2
|
||||
@ -355,8 +355,8 @@ packages:
|
||||
'@cloudflare/workers-types@4.20250303.0':
|
||||
resolution: {integrity: sha512-O7F7nRT4bbmwHf3gkRBLfJ7R6vHIJ/oZzWdby6obOiw2yavUfp/AIwS7aO2POu5Cv8+h3TXS3oHs3kKCZLraUA==}
|
||||
|
||||
'@cloudflare/workers-types@4.20250504.0':
|
||||
resolution: {integrity: sha512-/70Kb5vrqj+O0krOuS8LVLiCeDuCGzQy4X+wGGs4/rHv0gZJulv7Uj5YlUjIaRemK/Dyrzlk7WNJwTy8yv0cIw==}
|
||||
'@cloudflare/workers-types@4.20250505.0':
|
||||
resolution: {integrity: sha512-pLQ/UaCupEy3fTTfy7yCR7FuAbawvCohYAdadGHPUfzssksA9MhkqBLlzYWRwIoC34R8grVn4XOCknEg+NMr0Q==}
|
||||
|
||||
'@colors/colors@1.6.0':
|
||||
resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==}
|
||||
@ -872,8 +872,8 @@ packages:
|
||||
'@push.rocks/qenv@6.1.0':
|
||||
resolution: {integrity: sha512-1FUFMlSVwFSFg8LbqfkzJ2LLP4lMGApUtgOpsvrde6+AxBmB4gjoNgCUH7z3xXfDAtYqcrtSELXBNE0xVL1MqQ==}
|
||||
|
||||
'@push.rocks/smartacme@7.2.4':
|
||||
resolution: {integrity: sha512-0ciewRheDAwv0ER0ZyLQVLAn0ZoG1++ibSZ14HoXn8GOTOLyuRLWLNDQL2fI4LtLxeaNYmQUS+f7tt4KaZb/UA==}
|
||||
'@push.rocks/smartacme@7.3.2':
|
||||
resolution: {integrity: sha512-pfNd31wqvEn/2Bi9qZGCzvpV6/5V1jB9xOuWlsUTp4RihDVwQq2/se69pUeXDd1smWOM1yF4zq+45VO5DMDsCg==}
|
||||
|
||||
'@push.rocks/smartarchive@3.0.8':
|
||||
resolution: {integrity: sha512-1jPmR0b7hXmjYQoRiTlRXrIbZcdcFmSdGOfznufjcDpGPe86Km0d8TBnzqghTx4dTihzKC67IxAaz/DM3lvxpA==}
|
||||
@ -1567,8 +1567,8 @@ packages:
|
||||
'@tsclass/tsclass@8.2.1':
|
||||
resolution: {integrity: sha512-bRDCfJTipsTcK6eEokWdsOR1mGCQFeM7zTg6PRHzbxTWQcWQD9AhEr2q3CrPcmAbvIS7fvkO6/pU/mPm1MZxhQ==}
|
||||
|
||||
'@tsclass/tsclass@9.1.0':
|
||||
resolution: {integrity: sha512-PkG1bXK/bqVtxaRHje+iJHjtcdRHLHrNTOkzqh+jv2A7mgiyNo2YBJIl4eEJLkw1X3FwEFU4vCAtsegSmJgRug==}
|
||||
'@tsclass/tsclass@9.2.0':
|
||||
resolution: {integrity: sha512-A6ULEkQfYgOnCKQVQRt26O7PRzFo4PE2EoD25RAtnuFuVrNwGynYC20Vee2c8KAOyI7nQ/LaREki9KAX4AHOHQ==}
|
||||
|
||||
'@types/accepts@1.3.7':
|
||||
resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==}
|
||||
@ -4760,7 +4760,7 @@ snapshots:
|
||||
'@api.global/typedrequest': 3.1.10
|
||||
'@api.global/typedrequest-interfaces': 3.0.19
|
||||
'@api.global/typedsocket': 3.0.1
|
||||
'@cloudflare/workers-types': 4.20250504.0
|
||||
'@cloudflare/workers-types': 4.20250505.0
|
||||
'@design.estate/dees-comms': 1.0.27
|
||||
'@push.rocks/lik': 6.2.2
|
||||
'@push.rocks/smartchok': 1.0.34
|
||||
@ -4827,7 +4827,7 @@ snapshots:
|
||||
'@push.rocks/smartpromise': 4.2.3
|
||||
'@push.rocks/smartrequest': 2.1.0
|
||||
'@push.rocks/smartstring': 4.0.15
|
||||
'@tsclass/tsclass': 9.1.0
|
||||
'@tsclass/tsclass': 9.2.0
|
||||
cloudflare: 4.2.0
|
||||
transitivePeerDependencies:
|
||||
- encoding
|
||||
@ -5671,7 +5671,7 @@ snapshots:
|
||||
|
||||
'@cloudflare/workers-types@4.20250303.0': {}
|
||||
|
||||
'@cloudflare/workers-types@4.20250504.0': {}
|
||||
'@cloudflare/workers-types@4.20250505.0': {}
|
||||
|
||||
'@colors/colors@1.6.0': {}
|
||||
|
||||
@ -6284,7 +6284,7 @@ snapshots:
|
||||
'@push.rocks/smartlog': 3.0.7
|
||||
'@push.rocks/smartpath': 5.0.18
|
||||
|
||||
'@push.rocks/smartacme@7.2.4(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)':
|
||||
'@push.rocks/smartacme@7.3.2(@aws-sdk/credential-providers@3.798.0)(socks@2.8.4)':
|
||||
dependencies:
|
||||
'@api.global/typedserver': 3.0.74
|
||||
'@apiclient.xyz/cloudflare': 6.4.1
|
||||
@ -6292,13 +6292,15 @@ snapshots:
|
||||
'@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/smartfile': 11.2.0
|
||||
'@push.rocks/smartlog': 3.0.7
|
||||
'@push.rocks/smartnetwork': 4.0.1
|
||||
'@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
|
||||
'@tsclass/tsclass': 9.2.0
|
||||
acme-client: 5.4.0
|
||||
transitivePeerDependencies:
|
||||
- '@aws-sdk/credential-providers'
|
||||
@ -7736,7 +7738,7 @@ snapshots:
|
||||
dependencies:
|
||||
type-fest: 4.40.1
|
||||
|
||||
'@tsclass/tsclass@9.1.0':
|
||||
'@tsclass/tsclass@9.2.0':
|
||||
dependencies:
|
||||
type-fest: 4.40.1
|
||||
|
||||
|
@ -384,7 +384,7 @@ Listen for certificate events via EventEmitter:
|
||||
- **SmartProxy**:
|
||||
- `certificate` (domain, publicKey, privateKey, expiryDate, source, isRenewal)
|
||||
|
||||
Provide a `certProviderFunction(domain)` in SmartProxy settings to supply static certs or return `'http01'`.
|
||||
Provide a `certProvisionFunction(domain)` in SmartProxy settings to supply static certs or return `'http01'`.
|
||||
|
||||
## Configuration Options
|
||||
|
||||
@ -429,7 +429,7 @@ Provide a `certProviderFunction(domain)` in SmartProxy settings to supply static
|
||||
- `sniEnabled`, `defaultAllowedIPs`, `preserveSourceIP` (booleans)
|
||||
- Timeouts: `initialDataTimeout`, `socketTimeout`, `inactivityTimeout`, etc.
|
||||
- Socket opts: `noDelay`, `keepAlive`, `enableKeepAliveProbes`
|
||||
- `acme` (IAcmeOptions), `certProviderFunction` (callback)
|
||||
- `acme` (IAcmeOptions), `certProvisionFunction` (callback)
|
||||
- `useNetworkProxy` (number[]), `networkProxyPort` (number)
|
||||
|
||||
## Troubleshooting
|
||||
|
@ -1,29 +0,0 @@
|
||||
# Project Simplification Plan
|
||||
|
||||
This document outlines a roadmap to simplify and refactor the SmartProxy & NetworkProxy codebase for better maintainability, reduced duplication, and clearer configuration.
|
||||
|
||||
## Goals
|
||||
- Eliminate duplicate code and shared types
|
||||
- Unify certificate management flow across components
|
||||
- Simplify configuration schemas and option handling
|
||||
- Centralize plugin imports and module interfaces
|
||||
- Strengthen type safety and linting
|
||||
- Improve test coverage and CI integration
|
||||
|
||||
## Plan
|
||||
- [x] Extract all shared interfaces and types (e.g., certificate, proxy, domain configs) into a common `ts/common` module
|
||||
- [x] Consolidate ACME/Port80Handler logic:
|
||||
- [x] Merge standalone Port80Handler into a single certificate service
|
||||
- [x] Remove duplicate ACME setup in SmartProxy and NetworkProxy
|
||||
- [x] Unify configuration options:
|
||||
- [x] Merge `INetworkProxyOptions.acme`, `IPort80HandlerOptions`, and `port80HandlerConfig` into one schema
|
||||
- [x] Deprecate old option names and provide clear upgrade path
|
||||
- [x] Centralize plugin imports in `ts/plugins.ts` and update all modules to use it
|
||||
- [x] Remove legacy or unused code paths (e.g., old HTTP/2 fallback logic if obsolete)
|
||||
- [ ] Enhance and expand test coverage:
|
||||
- Add unit tests for certificate issuance, renewal, and error handling
|
||||
- Add integration tests for HTTP challenge routing and request forwarding
|
||||
- [ ] Update main README.md with architecture overview and configuration guide
|
||||
- [ ] Review and prune external dependencies no longer needed
|
||||
|
||||
Once these steps are complete, the project will be cleaner, easier to understand, and simpler to extend.
|
@ -3,6 +3,6 @@
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartproxy',
|
||||
version: '10.0.9',
|
||||
version: '10.0.12',
|
||||
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.'
|
||||
}
|
||||
|
@ -77,9 +77,9 @@ export interface IDomainForwardConfig {
|
||||
* Unified ACME configuration options used across proxies and handlers
|
||||
*/
|
||||
export interface IAcmeOptions {
|
||||
accountEmail?: string; // Email for Let's Encrypt account
|
||||
enabled?: boolean; // Whether ACME is enabled
|
||||
port?: number; // Port to listen on for ACME challenges (default: 80)
|
||||
contactEmail?: string; // Email for Let's Encrypt account
|
||||
useProduction?: boolean; // Use production environment (default: staging)
|
||||
httpsRedirectPort?: number; // Port to redirect HTTP requests to HTTPS (default: 443)
|
||||
renewThresholdDays?: number; // Days before expiry to renew certificates
|
||||
|
@ -357,7 +357,7 @@ export class CertificateManager {
|
||||
// Build and configure Port80Handler
|
||||
this.port80Handler = buildPort80Handler({
|
||||
port: this.options.acme.port,
|
||||
contactEmail: this.options.acme.contactEmail,
|
||||
accountEmail: this.options.acme.accountEmail,
|
||||
useProduction: this.options.acme.useProduction,
|
||||
httpsRedirectPort: this.options.port, // Redirect to our HTTPS port
|
||||
enabled: this.options.acme.enabled,
|
||||
|
@ -76,7 +76,7 @@ export class NetworkProxy implements IMetricsTracker {
|
||||
acme: {
|
||||
enabled: optionsArg.acme?.enabled || false,
|
||||
port: optionsArg.acme?.port || 80,
|
||||
contactEmail: optionsArg.acme?.contactEmail || 'admin@example.com',
|
||||
accountEmail: optionsArg.acme?.accountEmail || 'admin@example.com',
|
||||
useProduction: optionsArg.acme?.useProduction || false, // Default to staging for safety
|
||||
renewThresholdDays: optionsArg.acme?.renewThresholdDays || 30,
|
||||
autoRenew: optionsArg.acme?.autoRenew !== false, // Default to true
|
||||
|
@ -10,21 +10,6 @@ import type {
|
||||
IAcmeOptions
|
||||
} from '../common/types.js';
|
||||
// (fs and path I/O moved to CertProvisioner)
|
||||
// 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
|
||||
@ -80,11 +65,11 @@ interface IDomainCertificate {
|
||||
*/
|
||||
export class Port80Handler extends plugins.EventEmitter {
|
||||
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 smartAcmeHttp01Handler!: plugins.smartacme.handlers.Http01MemoryHandler;
|
||||
private server: plugins.http.Server | null = null;
|
||||
|
||||
// Renewal scheduling is handled externally by SmartProxy
|
||||
// (Removed internal renewal timer)
|
||||
private isShuttingDown: boolean = false;
|
||||
@ -101,7 +86,7 @@ export class Port80Handler extends plugins.EventEmitter {
|
||||
// Default options
|
||||
this.options = {
|
||||
port: options.port ?? 80,
|
||||
contactEmail: options.contactEmail ?? 'admin@example.com',
|
||||
accountEmail: options.accountEmail ?? 'admin@example.com',
|
||||
useProduction: options.useProduction ?? false, // Safer default: staging
|
||||
httpsRedirectPort: options.httpsRedirectPort ?? 443,
|
||||
enabled: options.enabled ?? true, // Enable by default
|
||||
@ -131,13 +116,14 @@ export class Port80Handler extends plugins.EventEmitter {
|
||||
console.log('Port80Handler is disabled, skipping start');
|
||||
return;
|
||||
}
|
||||
// Initialize SmartAcme for ACME challenge management (diskless HTTP handler)
|
||||
// Initialize SmartAcme with in-memory HTTP-01 challenge handler
|
||||
if (this.options.enabled) {
|
||||
this.smartAcmeHttp01Handler = new plugins.smartacme.handlers.Http01MemoryHandler();
|
||||
this.smartAcme = new plugins.smartacme.SmartAcme({
|
||||
accountEmail: this.options.contactEmail,
|
||||
certManager: new plugins.smartacme.MemoryCertManager(),
|
||||
accountEmail: this.options.accountEmail,
|
||||
certManager: new plugins.smartacme.certmanagers.MemoryCertManager(),
|
||||
environment: this.options.useProduction ? 'production' : 'integration',
|
||||
challengeHandlers: [ new DisklessHttp01Handler(this.acmeHttp01Storage) ],
|
||||
challengeHandlers: [ this.smartAcmeHttp01Handler ],
|
||||
challengePriority: ['http-01'],
|
||||
});
|
||||
await this.smartAcme.start();
|
||||
@ -448,17 +434,12 @@ export class Port80Handler extends plugins.EventEmitter {
|
||||
res.end('Not found');
|
||||
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}`);
|
||||
// Delegate to Http01MemoryHandler
|
||||
if (this.smartAcmeHttp01Handler) {
|
||||
this.smartAcmeHttp01Handler.handleRequest(req, res);
|
||||
} else {
|
||||
res.statusCode = 404;
|
||||
res.end('Challenge token not found');
|
||||
res.statusCode = 500;
|
||||
res.end('ACME HTTP-01 handler not initialized');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -81,8 +81,7 @@ export class CertProvisioner extends plugins.EventEmitter {
|
||||
// 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;
|
||||
const isWildcard = domain.includes('*');
|
||||
let provision: ISmartProxyCertProvisionObject | 'http01' = 'http01';
|
||||
if (this.certProvider) {
|
||||
try {
|
||||
@ -90,11 +89,20 @@ export class CertProvisioner extends plugins.EventEmitter {
|
||||
} catch (err) {
|
||||
console.error(`certProvider error for ${domain}:`, err);
|
||||
}
|
||||
} else if (isWildcard) {
|
||||
// No certProvider: cannot handle wildcard without DNS-01 support
|
||||
console.warn(`Skipping wildcard domain without certProvisionFunction: ${domain}`);
|
||||
continue;
|
||||
}
|
||||
if (provision === 'http01') {
|
||||
if (isWildcard) {
|
||||
console.warn(`Skipping HTTP-01 for wildcard domain: ${domain}`);
|
||||
continue;
|
||||
}
|
||||
this.provisionMap.set(domain, 'http01');
|
||||
this.port80Handler.addDomain({ domainName: domain, sslRedirect: true, acmeMaintenance: true });
|
||||
} else {
|
||||
// Static certificate (e.g., DNS-01 provisioned or user-provided) supports wildcard domains
|
||||
this.provisionMap.set(domain, 'static');
|
||||
const certObj = provision as plugins.tsclass.network.ICert;
|
||||
const certData: ICertificateData = {
|
||||
@ -162,18 +170,22 @@ export class CertProvisioner extends plugins.EventEmitter {
|
||||
* @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}`);
|
||||
}
|
||||
const isWildcard = domain.includes('*');
|
||||
// Determine provisioning method
|
||||
let provision: ISmartProxyCertProvisionObject | 'http01' = 'http01';
|
||||
if (this.certProvider) {
|
||||
provision = await this.certProvider(domain);
|
||||
} else if (isWildcard) {
|
||||
// Cannot perform HTTP-01 on wildcard without certProvider
|
||||
throw new Error(`Cannot request certificate for wildcard domain without certProvisionFunction: ${domain}`);
|
||||
}
|
||||
if (provision === 'http01') {
|
||||
if (isWildcard) {
|
||||
throw new Error(`Cannot request HTTP-01 certificate for wildcard domain: ${domain}`);
|
||||
}
|
||||
await this.port80Handler.renewCertificate(domain);
|
||||
} else {
|
||||
// Static certificate (e.g., DNS-01 provisioned) supports wildcards
|
||||
const certObj = provision as plugins.tsclass.network.ICert;
|
||||
const certData: ICertificateData = {
|
||||
domain: certObj.domainName,
|
||||
|
@ -78,7 +78,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
||||
this.settings.acme = {
|
||||
enabled: false,
|
||||
port: 80,
|
||||
contactEmail: 'admin@example.com',
|
||||
accountEmail: 'admin@example.com',
|
||||
useProduction: false,
|
||||
renewThresholdDays: 30,
|
||||
autoRenew: true,
|
||||
@ -391,16 +391,23 @@ export class SmartProxy extends plugins.EventEmitter {
|
||||
if (this.port80Handler && this.settings.acme?.enabled) {
|
||||
for (const domainConfig of newDomainConfigs) {
|
||||
for (const domain of domainConfig.domains) {
|
||||
if (domain.includes('*')) continue;
|
||||
let provision = 'http01' as string | plugins.tsclass.network.ICert;
|
||||
const isWildcard = domain.includes('*');
|
||||
let provision: string | plugins.tsclass.network.ICert = 'http01';
|
||||
if (this.settings.certProvisionFunction) {
|
||||
try {
|
||||
provision = await this.settings.certProvisionFunction(domain);
|
||||
} catch (err) {
|
||||
console.log(`certProvider error for ${domain}: ${err}`);
|
||||
}
|
||||
} else if (isWildcard) {
|
||||
console.warn(`Skipping wildcard domain without certProvisionFunction: ${domain}`);
|
||||
continue;
|
||||
}
|
||||
if (provision === 'http01') {
|
||||
if (isWildcard) {
|
||||
console.warn(`Skipping HTTP-01 for wildcard domain: ${domain}`);
|
||||
continue;
|
||||
}
|
||||
this.port80Handler.addDomain({
|
||||
domainName: domain,
|
||||
sslRedirect: true,
|
||||
@ -408,6 +415,7 @@ export class SmartProxy extends plugins.EventEmitter {
|
||||
});
|
||||
console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`);
|
||||
} else {
|
||||
// Static certificate (e.g., DNS-01 provisioned) supports wildcards
|
||||
const certObj = provision as plugins.tsclass.network.ICert;
|
||||
const certData: ICertificateData = {
|
||||
domain: certObj.domainName,
|
||||
|
Reference in New Issue
Block a user