fix(port80handler): refactor ACME challenge handling to use dedicated Http01MemoryHandler, remove obsolete readme.plan.md, and update version to 10.0.12
This commit is contained in:
		
							
								
								
									
										18
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,5 +1,23 @@ | |||||||
| # Changelog | # 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) | ## 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() | 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,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 = { | export const commitinfo = { | ||||||
|   name: '@push.rocks/smartproxy', |   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.' |   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.' | ||||||
| } | } | ||||||
|   | |||||||
| @@ -65,11 +65,11 @@ interface IDomainCertificate { | |||||||
|  */ |  */ | ||||||
| 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 |   // SmartAcme instance for certificate management | ||||||
|   private smartAcme: plugins.smartacme.SmartAcme | null = null; |   private smartAcme: plugins.smartacme.SmartAcme | null = null; | ||||||
|  |   private smartAcmeHttp01Handler!: plugins.smartacme.handlers.Http01MemoryHandler; | ||||||
|   private server: plugins.http.Server | null = null; |   private server: plugins.http.Server | null = null; | ||||||
|  |    | ||||||
|   // Renewal scheduling is handled externally by SmartProxy |   // Renewal scheduling is handled externally by SmartProxy | ||||||
|   // (Removed internal renewal timer) |   // (Removed internal renewal timer) | ||||||
|   private isShuttingDown: boolean = false; |   private isShuttingDown: boolean = false; | ||||||
| @@ -116,13 +116,14 @@ 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) |     // Initialize SmartAcme with in-memory HTTP-01 challenge handler | ||||||
|     if (this.options.enabled) { |     if (this.options.enabled) { | ||||||
|  |       this.smartAcmeHttp01Handler = new plugins.smartacme.handlers.Http01MemoryHandler(); | ||||||
|       this.smartAcme = new plugins.smartacme.SmartAcme({ |       this.smartAcme = new plugins.smartacme.SmartAcme({ | ||||||
|         accountEmail: this.options.accountEmail, |         accountEmail: this.options.accountEmail, | ||||||
|         certManager: new plugins.smartacme.certmanagers.MemoryCertManager(), |         certManager: new plugins.smartacme.certmanagers.MemoryCertManager(), | ||||||
|         environment: this.options.useProduction ? 'production' : 'integration', |         environment: this.options.useProduction ? 'production' : 'integration', | ||||||
|         challengeHandlers: [ new plugins.smartacme.handlers.Http01MemoryHandler() ], |         challengeHandlers: [ this.smartAcmeHttp01Handler ], | ||||||
|         challengePriority: ['http-01'], |         challengePriority: ['http-01'], | ||||||
|       }); |       }); | ||||||
|       await this.smartAcme.start(); |       await this.smartAcme.start(); | ||||||
| @@ -433,17 +434,12 @@ export class Port80Handler extends plugins.EventEmitter { | |||||||
|         res.end('Not found'); |         res.end('Not found'); | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|       // Serve challenge response from in-memory storage |       // Delegate to Http01MemoryHandler | ||||||
|       const token = req.url.split('/').pop() || ''; |       if (this.smartAcmeHttp01Handler) { | ||||||
|       const keyAuth = this.acmeHttp01Storage.get(token); |         this.smartAcmeHttp01Handler.handleRequest(req, res); | ||||||
|       if (keyAuth) { |  | ||||||
|         res.statusCode = 200; |  | ||||||
|         res.setHeader('Content-Type', 'text/plain'); |  | ||||||
|         res.end(keyAuth); |  | ||||||
|         console.log(`Served ACME challenge response for ${domain}`); |  | ||||||
|       } else { |       } else { | ||||||
|         res.statusCode = 404; |         res.statusCode = 500; | ||||||
|         res.end('Challenge token not found'); |         res.end('ACME HTTP-01 handler not initialized'); | ||||||
|       } |       } | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -81,8 +81,7 @@ export class CertProvisioner extends plugins.EventEmitter { | |||||||
|     // Initial provisioning for all domains |     // Initial provisioning for all domains | ||||||
|     const domains = this.domainConfigs.flatMap(cfg => cfg.domains); |     const domains = this.domainConfigs.flatMap(cfg => cfg.domains); | ||||||
|     for (const domain of domains) { |     for (const domain of domains) { | ||||||
|       // Skip wildcard domains |       const isWildcard = domain.includes('*'); | ||||||
|       if (domain.includes('*')) continue; |  | ||||||
|       let provision: ISmartProxyCertProvisionObject | 'http01' = 'http01'; |       let provision: ISmartProxyCertProvisionObject | 'http01' = 'http01'; | ||||||
|       if (this.certProvider) { |       if (this.certProvider) { | ||||||
|         try { |         try { | ||||||
| @@ -90,11 +89,20 @@ export class CertProvisioner extends plugins.EventEmitter { | |||||||
|         } catch (err) { |         } catch (err) { | ||||||
|           console.error(`certProvider error for ${domain}:`, 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 (provision === 'http01') { | ||||||
|  |         if (isWildcard) { | ||||||
|  |           console.warn(`Skipping HTTP-01 for wildcard domain: ${domain}`); | ||||||
|  |           continue; | ||||||
|  |         } | ||||||
|         this.provisionMap.set(domain, 'http01'); |         this.provisionMap.set(domain, 'http01'); | ||||||
|         this.port80Handler.addDomain({ domainName: domain, sslRedirect: true, acmeMaintenance: true }); |         this.port80Handler.addDomain({ domainName: domain, sslRedirect: true, acmeMaintenance: true }); | ||||||
|       } else { |       } else { | ||||||
|  |         // Static certificate (e.g., DNS-01 provisioned or user-provided) supports wildcard domains | ||||||
|         this.provisionMap.set(domain, 'static'); |         this.provisionMap.set(domain, 'static'); | ||||||
|         const certObj = provision as plugins.tsclass.network.ICert; |         const certObj = provision as plugins.tsclass.network.ICert; | ||||||
|         const certData: ICertificateData = { |         const certData: ICertificateData = { | ||||||
| @@ -162,18 +170,22 @@ export class CertProvisioner extends plugins.EventEmitter { | |||||||
|    * @param domain Domain name to provision |    * @param domain Domain name to provision | ||||||
|    */ |    */ | ||||||
|   public async requestCertificate(domain: string): Promise<void> { |   public async requestCertificate(domain: string): Promise<void> { | ||||||
|     // Skip wildcard domains |     const isWildcard = domain.includes('*'); | ||||||
|     if (domain.includes('*')) { |  | ||||||
|       throw new Error(`Cannot request certificate for wildcard domain: ${domain}`); |  | ||||||
|     } |  | ||||||
|     // Determine provisioning method |     // Determine provisioning method | ||||||
|     let provision: ISmartProxyCertProvisionObject | 'http01' = 'http01'; |     let provision: ISmartProxyCertProvisionObject | 'http01' = 'http01'; | ||||||
|     if (this.certProvider) { |     if (this.certProvider) { | ||||||
|       provision = await this.certProvider(domain); |       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 (provision === 'http01') { | ||||||
|  |       if (isWildcard) { | ||||||
|  |         throw new Error(`Cannot request HTTP-01 certificate for wildcard domain: ${domain}`); | ||||||
|  |       } | ||||||
|       await this.port80Handler.renewCertificate(domain); |       await this.port80Handler.renewCertificate(domain); | ||||||
|     } else { |     } else { | ||||||
|  |       // Static certificate (e.g., DNS-01 provisioned) supports wildcards | ||||||
|       const certObj = provision as plugins.tsclass.network.ICert; |       const certObj = provision as plugins.tsclass.network.ICert; | ||||||
|       const certData: ICertificateData = { |       const certData: ICertificateData = { | ||||||
|         domain: certObj.domainName, |         domain: certObj.domainName, | ||||||
|   | |||||||
| @@ -391,16 +391,23 @@ export class SmartProxy extends plugins.EventEmitter { | |||||||
|     if (this.port80Handler && this.settings.acme?.enabled) { |     if (this.port80Handler && this.settings.acme?.enabled) { | ||||||
|       for (const domainConfig of newDomainConfigs) { |       for (const domainConfig of newDomainConfigs) { | ||||||
|         for (const domain of domainConfig.domains) { |         for (const domain of domainConfig.domains) { | ||||||
|           if (domain.includes('*')) continue; |           const isWildcard = domain.includes('*'); | ||||||
|           let provision = 'http01' as string | plugins.tsclass.network.ICert; |           let provision: string | plugins.tsclass.network.ICert = 'http01'; | ||||||
|           if (this.settings.certProvisionFunction) { |           if (this.settings.certProvisionFunction) { | ||||||
|             try { |             try { | ||||||
|               provision = await this.settings.certProvisionFunction(domain); |               provision = await this.settings.certProvisionFunction(domain); | ||||||
|             } catch (err) { |             } catch (err) { | ||||||
|               console.log(`certProvider error for ${domain}: ${err}`); |               console.log(`certProvider error for ${domain}: ${err}`); | ||||||
|             } |             } | ||||||
|  |           } else if (isWildcard) { | ||||||
|  |             console.warn(`Skipping wildcard domain without certProvisionFunction: ${domain}`); | ||||||
|  |             continue; | ||||||
|           } |           } | ||||||
|           if (provision === 'http01') { |           if (provision === 'http01') { | ||||||
|  |             if (isWildcard) { | ||||||
|  |               console.warn(`Skipping HTTP-01 for wildcard domain: ${domain}`); | ||||||
|  |               continue; | ||||||
|  |             } | ||||||
|             this.port80Handler.addDomain({ |             this.port80Handler.addDomain({ | ||||||
|               domainName: domain, |               domainName: domain, | ||||||
|               sslRedirect: true, |               sslRedirect: true, | ||||||
| @@ -408,6 +415,7 @@ export class SmartProxy extends plugins.EventEmitter { | |||||||
|             }); |             }); | ||||||
|             console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`); |             console.log(`Registered domain ${domain} with Port80Handler for HTTP-01`); | ||||||
|           } else { |           } else { | ||||||
|  |             // Static certificate (e.g., DNS-01 provisioned) supports wildcards | ||||||
|             const certObj = provision as plugins.tsclass.network.ICert; |             const certObj = provision as plugins.tsclass.network.ICert; | ||||||
|             const certData: ICertificateData = { |             const certData: ICertificateData = { | ||||||
|               domain: certObj.domainName, |               domain: certObj.domainName, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user