Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
a9963f3b8a | |||
05c9156458 | |||
47e3c86487 |
18
changelog.md
18
changelog.md
@ -1,5 +1,23 @@
|
||||
# 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()
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@push.rocks/smartproxy",
|
||||
"version": "10.0.11",
|
||||
"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,7 +24,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@push.rocks/lik": "^6.2.2",
|
||||
"@push.rocks/smartacme": "^7.2.5",
|
||||
"@push.rocks/smartacme": "^7.3.2",
|
||||
"@push.rocks/smartdelay": "^3.0.5",
|
||||
"@push.rocks/smartnetwork": "^4.0.1",
|
||||
"@push.rocks/smartpromise": "^4.2.3",
|
||||
|
12
pnpm-lock.yaml
generated
12
pnpm-lock.yaml
generated
@ -12,8 +12,8 @@ importers:
|
||||
specifier: ^6.2.2
|
||||
version: 6.2.2
|
||||
'@push.rocks/smartacme':
|
||||
specifier: ^7.2.5
|
||||
version: 7.2.5(@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
|
||||
@ -872,8 +872,8 @@ packages:
|
||||
'@push.rocks/qenv@6.1.0':
|
||||
resolution: {integrity: sha512-1FUFMlSVwFSFg8LbqfkzJ2LLP4lMGApUtgOpsvrde6+AxBmB4gjoNgCUH7z3xXfDAtYqcrtSELXBNE0xVL1MqQ==}
|
||||
|
||||
'@push.rocks/smartacme@7.2.5':
|
||||
resolution: {integrity: sha512-Mn7qutAHd+mIbyq0Br/zyBBki7ATaZ/ofZQvnrR4xDvZiL8sBKeNx3iotb7jyHNYax5JUBh7slDKUxVjQBFC6Q==}
|
||||
'@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==}
|
||||
@ -6284,7 +6284,7 @@ snapshots:
|
||||
'@push.rocks/smartlog': 3.0.7
|
||||
'@push.rocks/smartpath': 5.0.18
|
||||
|
||||
'@push.rocks/smartacme@7.2.5(@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,7 +6292,9 @@ 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
|
||||
|
@ -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.11',
|
||||
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.'
|
||||
}
|
||||
|
@ -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;
|
||||
@ -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.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,
|
||||
|
@ -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