fix(logger): Replace raw console logging calls with structured logger usage across certificate management, connection handling, and route processing for improved observability.
This commit is contained in:
		| @@ -3,6 +3,6 @@ | ||||
|  */ | ||||
| export const commitinfo = { | ||||
|   name: '@push.rocks/smartproxy', | ||||
|   version: '19.3.10', | ||||
|   version: '19.3.11', | ||||
|   description: 'A powerful proxy package with unified route-based configuration for high traffic management. Features include SSL/TLS support, flexible routing patterns, WebSocket handling, advanced security options, and automatic ACME certificate management.' | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import type { IRouteConfig, IRouteTls } from './models/route-types.js'; | ||||
| import type { IAcmeOptions } from './models/interfaces.js'; | ||||
| import { CertStore } from './cert-store.js'; | ||||
| import type { AcmeStateManager } from './acme-state-manager.js'; | ||||
| import { logger } from '../../core/utils/logger.js'; | ||||
|  | ||||
| export interface ICertStatus { | ||||
|   domain: string; | ||||
| @@ -125,16 +126,16 @@ export class SmartCertManager { | ||||
|        | ||||
|       // Add challenge route once at initialization if not already active | ||||
|       if (!this.challengeRouteActive) { | ||||
|         console.log('Adding ACME challenge route during initialization'); | ||||
|         logger.log('info', 'Adding ACME challenge route during initialization', { component: 'certificate-manager' }); | ||||
|         await this.addChallengeRoute(); | ||||
|       } else { | ||||
|         console.log('Challenge route already active from previous instance'); | ||||
|         logger.log('info', 'Challenge route already active from previous instance', { component: 'certificate-manager' }); | ||||
|       } | ||||
|     } | ||||
|      | ||||
|     // Skip automatic certificate provisioning during initialization | ||||
|     // This will be called later after ports are listening | ||||
|     console.log('Certificate manager initialized. Deferring certificate provisioning until after ports are listening.'); | ||||
|     logger.log('info', 'Certificate manager initialized. Deferring certificate provisioning until after ports are listening.', { component: 'certificate-manager' }); | ||||
|      | ||||
|     // Start renewal timer | ||||
|     this.startRenewalTimer(); | ||||
| @@ -157,7 +158,7 @@ export class SmartCertManager { | ||||
|         try { | ||||
|           await this.provisionCertificate(route, true); // Allow concurrent since we're managing it here | ||||
|         } catch (error) { | ||||
|           console.error(`Failed to provision certificate for route ${route.name}: ${error}`); | ||||
|           logger.log('error', `Failed to provision certificate for route ${route.name}`, { routeName: route.name, error, component: 'certificate-manager' }); | ||||
|         } | ||||
|       } | ||||
|     } finally { | ||||
| @@ -176,13 +177,13 @@ export class SmartCertManager { | ||||
|      | ||||
|     // Check if provisioning is already in progress (prevent concurrent provisioning) | ||||
|     if (!allowConcurrent && this.isProvisioning) { | ||||
|       console.log(`Certificate provisioning already in progress, skipping ${route.name}`); | ||||
|       logger.log('info', `Certificate provisioning already in progress, skipping ${route.name}`, { routeName: route.name, component: 'certificate-manager' }); | ||||
|       return; | ||||
|     } | ||||
|      | ||||
|     const domains = this.extractDomainsFromRoute(route); | ||||
|     if (domains.length === 0) { | ||||
|       console.warn(`Route ${route.name} has TLS termination but no domains`); | ||||
|       logger.log('warn', `Route ${route.name} has TLS termination but no domains`, { routeName: route.name, component: 'certificate-manager' }); | ||||
|       return; | ||||
|     } | ||||
|      | ||||
| @@ -219,7 +220,7 @@ export class SmartCertManager { | ||||
|     // Check if we already have a valid certificate | ||||
|     const existingCert = await this.certStore.getCertificate(routeName); | ||||
|     if (existingCert && this.isCertificateValid(existingCert)) { | ||||
|       console.log(`Using existing valid certificate for ${primaryDomain}`); | ||||
|       logger.log('info', `Using existing valid certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' }); | ||||
|       await this.applyCertificate(primaryDomain, existingCert); | ||||
|       this.updateCertStatus(routeName, 'valid', 'acme', existingCert); | ||||
|       return; | ||||
| @@ -230,7 +231,7 @@ export class SmartCertManager { | ||||
|                          this.globalAcmeDefaults?.renewThresholdDays ||  | ||||
|                          30; | ||||
|      | ||||
|     console.log(`Requesting ACME certificate for ${domains.join(', ')} (renew ${renewThreshold} days before expiry)`); | ||||
|     logger.log('info', `Requesting ACME certificate for ${domains.join(', ')} (renew ${renewThreshold} days before expiry)`, { domains: domains.join(', '), renewThreshold, component: 'certificate-manager' }); | ||||
|     this.updateCertStatus(routeName, 'pending', 'acme'); | ||||
|      | ||||
|     try { | ||||
| @@ -252,7 +253,7 @@ export class SmartCertManager { | ||||
|                                     hasDnsChallenge; | ||||
|        | ||||
|       if (shouldIncludeWildcard) { | ||||
|         console.log(`Requesting wildcard certificate for ${primaryDomain} (DNS-01 available)`); | ||||
|         logger.log('info', `Requesting wildcard certificate for ${primaryDomain} (DNS-01 available)`, { domain: primaryDomain, challengeType: 'DNS-01', component: 'certificate-manager' }); | ||||
|       } | ||||
|        | ||||
|       // Use smartacme to get certificate with optional wildcard | ||||
| @@ -279,9 +280,9 @@ export class SmartCertManager { | ||||
|       await this.applyCertificate(primaryDomain, certData); | ||||
|       this.updateCertStatus(routeName, 'valid', 'acme', certData); | ||||
|        | ||||
|       console.log(`Successfully provisioned ACME certificate for ${primaryDomain}`); | ||||
|       logger.log('info', `Successfully provisioned ACME certificate for ${primaryDomain}`, { domain: primaryDomain, component: 'certificate-manager' }); | ||||
|     } catch (error) { | ||||
|       console.error(`Failed to provision ACME certificate for ${primaryDomain}: ${error}`); | ||||
|       logger.log('error', `Failed to provision ACME certificate for ${primaryDomain}: ${error.message}`, { domain: primaryDomain, error: error.message, component: 'certificate-manager' }); | ||||
|       this.updateCertStatus(routeName, 'error', 'acme', undefined, error.message); | ||||
|       throw error; | ||||
|     } | ||||
| @@ -328,9 +329,9 @@ export class SmartCertManager { | ||||
|       await this.applyCertificate(domain, certData); | ||||
|       this.updateCertStatus(routeName, 'valid', 'static', certData); | ||||
|        | ||||
|       console.log(`Successfully loaded static certificate for ${domain}`); | ||||
|       logger.log('info', `Successfully loaded static certificate for ${domain}`, { domain, component: 'certificate-manager' }); | ||||
|     } catch (error) { | ||||
|       console.error(`Failed to provision static certificate for ${domain}: ${error}`); | ||||
|       logger.log('error', `Failed to provision static certificate for ${domain}: ${error.message}`, { domain, error: error.message, component: 'certificate-manager' }); | ||||
|       this.updateCertStatus(routeName, 'error', 'static', undefined, error.message); | ||||
|       throw error; | ||||
|     } | ||||
| @@ -341,7 +342,7 @@ export class SmartCertManager { | ||||
|    */ | ||||
|   private async applyCertificate(domain: string, certData: ICertificateData): Promise<void> { | ||||
|     if (!this.httpProxy) { | ||||
|       console.warn('HttpProxy not set, cannot apply certificate'); | ||||
|       logger.log('warn', `HttpProxy not set, cannot apply certificate for domain ${domain}`, { domain, component: 'certificate-manager' }); | ||||
|       return; | ||||
|     } | ||||
|      | ||||
| @@ -398,13 +399,13 @@ export class SmartCertManager { | ||||
|   private async addChallengeRoute(): Promise<void> { | ||||
|     // Check with state manager first | ||||
|     if (this.acmeStateManager && this.acmeStateManager.isChallengeRouteActive()) { | ||||
|       console.log('Challenge route already active in global state, skipping'); | ||||
|       logger.log('info', 'Challenge route already active in global state, skipping', { component: 'certificate-manager' }); | ||||
|       this.challengeRouteActive = true; | ||||
|       return; | ||||
|     } | ||||
|      | ||||
|     if (this.challengeRouteActive) { | ||||
|       console.log('Challenge route already active locally, skipping'); | ||||
|       logger.log('info', 'Challenge route already active locally, skipping', { component: 'certificate-manager' }); | ||||
|       return; | ||||
|     } | ||||
|      | ||||
| @@ -427,9 +428,9 @@ export class SmartCertManager { | ||||
|         this.acmeStateManager.addChallengeRoute(challengeRoute); | ||||
|       } | ||||
|        | ||||
|       console.log('ACME challenge route successfully added'); | ||||
|       logger.log('info', 'ACME challenge route successfully added', { component: 'certificate-manager' }); | ||||
|     } catch (error) { | ||||
|       console.error('Failed to add challenge route:', error); | ||||
|       logger.log('error', `Failed to add challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' }); | ||||
|       if ((error as any).code === 'EADDRINUSE') { | ||||
|         throw new Error(`Port ${this.globalAcmeDefaults?.port || 80} is already in use for ACME challenges`); | ||||
|       } | ||||
| @@ -442,7 +443,7 @@ export class SmartCertManager { | ||||
|    */ | ||||
|   private async removeChallengeRoute(): Promise<void> { | ||||
|     if (!this.challengeRouteActive) { | ||||
|       console.log('Challenge route not active, skipping removal'); | ||||
|       logger.log('info', 'Challenge route not active, skipping removal', { component: 'certificate-manager' }); | ||||
|       return; | ||||
|     } | ||||
|      | ||||
| @@ -460,9 +461,9 @@ export class SmartCertManager { | ||||
|         this.acmeStateManager.removeChallengeRoute('acme-challenge'); | ||||
|       } | ||||
|        | ||||
|       console.log('ACME challenge route successfully removed'); | ||||
|       logger.log('info', 'ACME challenge route successfully removed', { component: 'certificate-manager' }); | ||||
|     } catch (error) { | ||||
|       console.error('Failed to remove challenge route:', error); | ||||
|       logger.log('error', `Failed to remove challenge route: ${error.message}`, { error: error.message, component: 'certificate-manager' }); | ||||
|       // Reset the flag even on error to avoid getting stuck | ||||
|       this.challengeRouteActive = false; | ||||
|       throw error; | ||||
| @@ -492,11 +493,11 @@ export class SmartCertManager { | ||||
|         const cert = await this.certStore.getCertificate(routeName); | ||||
|          | ||||
|         if (cert && !this.isCertificateValid(cert)) { | ||||
|           console.log(`Certificate for ${routeName} needs renewal`); | ||||
|           logger.log('info', `Certificate for ${routeName} needs renewal`, { routeName, component: 'certificate-manager' }); | ||||
|           try { | ||||
|             await this.provisionCertificate(route); | ||||
|           } catch (error) { | ||||
|             console.error(`Failed to renew certificate for ${routeName}: ${error}`); | ||||
|             logger.log('error', `Failed to renew certificate for ${routeName}: ${error.message}`, { routeName, error: error.message, component: 'certificate-manager' }); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| @@ -621,7 +622,7 @@ export class SmartCertManager { | ||||
|      | ||||
|     // Always remove challenge route on shutdown | ||||
|     if (this.challengeRoute) { | ||||
|       console.log('Removing ACME challenge route during shutdown'); | ||||
|       logger.log('info', 'Removing ACME challenge route during shutdown', { component: 'certificate-manager' }); | ||||
|       await this.removeChallengeRoute(); | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import * as plugins from '../../plugins.js'; | ||||
| import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js'; | ||||
| import { SecurityManager } from './security-manager.js'; | ||||
| import { TimeoutManager } from './timeout-manager.js'; | ||||
| import { logger } from '../../core/utils/logger.js'; | ||||
|  | ||||
| /** | ||||
|  * Manages connection lifecycle, tracking, and cleanup | ||||
| @@ -97,7 +98,7 @@ export class ConnectionManager { | ||||
|    */ | ||||
|   public initiateCleanupOnce(record: IConnectionRecord, reason: string = 'normal'): void { | ||||
|     if (this.settings.enableDetailedLogging) { | ||||
|       console.log(`[${record.id}] Connection cleanup initiated for ${record.remoteIP} (${reason})`); | ||||
|       logger.log('info', `Connection cleanup initiated`, { connectionId: record.id, remoteIP: record.remoteIP, reason, component: 'connection-manager' }); | ||||
|     } | ||||
|  | ||||
|     if ( | ||||
| @@ -139,7 +140,7 @@ export class ConnectionManager { | ||||
|           // Reset the handler references | ||||
|           record.renegotiationHandler = undefined; | ||||
|         } catch (err) { | ||||
|           console.log(`[${record.id}] Error removing data handlers: ${err}`); | ||||
|           logger.log('error', `Error removing data handlers for connection ${record.id}: ${err}`, { connectionId: record.id, error: err, component: 'connection-manager' }); | ||||
|         } | ||||
|       } | ||||
|  | ||||
| @@ -160,16 +161,36 @@ export class ConnectionManager { | ||||
|  | ||||
|       // Log connection details | ||||
|       if (this.settings.enableDetailedLogging) { | ||||
|         console.log( | ||||
|           `[${record.id}] Connection from ${record.remoteIP} on port ${record.localPort} terminated (${reason}).` + | ||||
|             ` Duration: ${plugins.prettyMs(duration)}, Bytes IN: ${bytesReceived}, OUT: ${bytesSent}, ` + | ||||
|             `TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}` + | ||||
|             `${record.usingNetworkProxy ? ', Using NetworkProxy' : ''}` + | ||||
|             `${record.domainSwitches ? `, Domain switches: ${record.domainSwitches}` : ''}` | ||||
|         logger.log('info',  | ||||
|           `Connection from ${record.remoteIP} on port ${record.localPort} terminated (${reason}). ` + | ||||
|           `Duration: ${plugins.prettyMs(duration)}, Bytes IN: ${bytesReceived}, OUT: ${bytesSent}, ` + | ||||
|           `TLS: ${record.isTLS ? 'Yes' : 'No'}, Keep-Alive: ${record.hasKeepAlive ? 'Yes' : 'No'}` + | ||||
|           `${record.usingNetworkProxy ? ', Using NetworkProxy' : ''}` + | ||||
|           `${record.domainSwitches ? `, Domain switches: ${record.domainSwitches}` : ''}`,  | ||||
|           { | ||||
|             connectionId: record.id, | ||||
|             remoteIP: record.remoteIP, | ||||
|             localPort: record.localPort, | ||||
|             reason, | ||||
|             duration: plugins.prettyMs(duration), | ||||
|             bytes: { in: bytesReceived, out: bytesSent }, | ||||
|             tls: record.isTLS, | ||||
|             keepAlive: record.hasKeepAlive, | ||||
|             usingNetworkProxy: record.usingNetworkProxy, | ||||
|             domainSwitches: record.domainSwitches || 0, | ||||
|             component: 'connection-manager' | ||||
|           } | ||||
|         ); | ||||
|       } else { | ||||
|         console.log( | ||||
|           `[${record.id}] Connection from ${record.remoteIP} terminated (${reason}). Active connections: ${this.connectionRecords.size}` | ||||
|         logger.log('info',  | ||||
|           `Connection from ${record.remoteIP} terminated (${reason}). Active connections: ${this.connectionRecords.size}`,  | ||||
|           { | ||||
|             connectionId: record.id, | ||||
|             remoteIP: record.remoteIP, | ||||
|             reason, | ||||
|             activeConnections: this.connectionRecords.size, | ||||
|             component: 'connection-manager' | ||||
|           } | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
| @@ -189,7 +210,7 @@ export class ConnectionManager { | ||||
|               socket.destroy(); | ||||
|             } | ||||
|           } catch (err) { | ||||
|             console.log(`[${record.id}] Error destroying ${side} socket: ${err}`); | ||||
|             logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' }); | ||||
|           } | ||||
|         }, 1000); | ||||
|  | ||||
| @@ -199,13 +220,13 @@ export class ConnectionManager { | ||||
|         } | ||||
|       } | ||||
|     } catch (err) { | ||||
|       console.log(`[${record.id}] Error closing ${side} socket: ${err}`); | ||||
|       logger.log('error', `Error closing ${side} socket for connection ${record.id}: ${err}`, { connectionId: record.id, side, error: err, component: 'connection-manager' }); | ||||
|       try { | ||||
|         if (!socket.destroyed) { | ||||
|           socket.destroy(); | ||||
|         } | ||||
|       } catch (destroyErr) { | ||||
|         console.log(`[${record.id}] Error destroying ${side} socket: ${destroyErr}`); | ||||
|         logger.log('error', `Error destroying ${side} socket for connection ${record.id}: ${destroyErr}`, { connectionId: record.id, side, error: destroyErr, component: 'connection-manager' }); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -224,21 +245,36 @@ export class ConnectionManager { | ||||
|  | ||||
|       if (code === 'ECONNRESET') { | ||||
|         reason = 'econnreset'; | ||||
|         console.log( | ||||
|           `[${record.id}] ECONNRESET on ${side} side from ${record.remoteIP}: ${err.message}. ` + | ||||
|           `Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)} ago` | ||||
|         ); | ||||
|         logger.log('warn', `ECONNRESET on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, { | ||||
|           connectionId: record.id, | ||||
|           side, | ||||
|           remoteIP: record.remoteIP, | ||||
|           error: err.message, | ||||
|           duration: plugins.prettyMs(connectionDuration), | ||||
|           lastActivity: plugins.prettyMs(lastActivityAge), | ||||
|           component: 'connection-manager' | ||||
|         }); | ||||
|       } else if (code === 'ETIMEDOUT') { | ||||
|         reason = 'etimedout'; | ||||
|         console.log( | ||||
|           `[${record.id}] ETIMEDOUT on ${side} side from ${record.remoteIP}: ${err.message}. ` + | ||||
|           `Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)} ago` | ||||
|         ); | ||||
|         logger.log('warn', `ETIMEDOUT on ${side} connection from ${record.remoteIP}. Error: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, { | ||||
|           connectionId: record.id, | ||||
|           side, | ||||
|           remoteIP: record.remoteIP, | ||||
|           error: err.message, | ||||
|           duration: plugins.prettyMs(connectionDuration), | ||||
|           lastActivity: plugins.prettyMs(lastActivityAge), | ||||
|           component: 'connection-manager' | ||||
|         }); | ||||
|       } else { | ||||
|         console.log( | ||||
|           `[${record.id}] Error on ${side} side from ${record.remoteIP}: ${err.message}. ` + | ||||
|           `Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)} ago` | ||||
|         ); | ||||
|         logger.log('error', `Error on ${side} connection from ${record.remoteIP}: ${err.message}. Duration: ${plugins.prettyMs(connectionDuration)}, Last activity: ${plugins.prettyMs(lastActivityAge)}`, { | ||||
|           connectionId: record.id, | ||||
|           side, | ||||
|           remoteIP: record.remoteIP, | ||||
|           error: err.message, | ||||
|           duration: plugins.prettyMs(connectionDuration), | ||||
|           lastActivity: plugins.prettyMs(lastActivityAge), | ||||
|           component: 'connection-manager' | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|       if (side === 'incoming' && record.incomingTerminationReason === null) { | ||||
| @@ -259,7 +295,12 @@ export class ConnectionManager { | ||||
|   public handleClose(side: 'incoming' | 'outgoing', record: IConnectionRecord) { | ||||
|     return () => { | ||||
|       if (this.settings.enableDetailedLogging) { | ||||
|         console.log(`[${record.id}] Connection closed on ${side} side from ${record.remoteIP}`); | ||||
|         logger.log('info', `Connection closed on ${side} side`, { | ||||
|           connectionId: record.id, | ||||
|           side, | ||||
|           remoteIP: record.remoteIP, | ||||
|           component: 'connection-manager' | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|       if (side === 'incoming' && record.incomingTerminationReason === null) { | ||||
| @@ -321,11 +362,13 @@ export class ConnectionManager { | ||||
|       if (inactivityTime > effectiveTimeout && !record.connectionClosed) { | ||||
|         // For keep-alive connections, issue a warning first | ||||
|         if (record.hasKeepAlive && !record.inactivityWarningIssued) { | ||||
|           console.log( | ||||
|             `[${id}] Warning: Keep-alive connection from ${record.remoteIP} inactive for ${ | ||||
|               plugins.prettyMs(inactivityTime) | ||||
|             }. Will close in 10 minutes if no activity.` | ||||
|           ); | ||||
|           logger.log('warn', `Keep-alive connection ${id} from ${record.remoteIP} inactive for ${plugins.prettyMs(inactivityTime)}. Will close in 10 minutes if no activity.`, { | ||||
|             connectionId: id, | ||||
|             remoteIP: record.remoteIP, | ||||
|             inactiveFor: plugins.prettyMs(inactivityTime), | ||||
|             closureWarning: '10 minutes', | ||||
|             component: 'connection-manager' | ||||
|           }); | ||||
|  | ||||
|           // Set warning flag and add grace period | ||||
|           record.inactivityWarningIssued = true; | ||||
| @@ -337,27 +380,30 @@ export class ConnectionManager { | ||||
|               record.outgoing.write(Buffer.alloc(0)); | ||||
|  | ||||
|               if (this.settings.enableDetailedLogging) { | ||||
|                 console.log(`[${id}] Sent probe packet to test keep-alive connection`); | ||||
|                 logger.log('info', `Sent probe packet to test keep-alive connection ${id}`, { connectionId: id, component: 'connection-manager' }); | ||||
|               } | ||||
|             } catch (err) { | ||||
|               console.log(`[${id}] Error sending probe packet: ${err}`); | ||||
|               logger.log('error', `Error sending probe packet to connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' }); | ||||
|             } | ||||
|           } | ||||
|         } else { | ||||
|           // For non-keep-alive or after warning, close the connection | ||||
|           console.log( | ||||
|             `[${id}] Inactivity check: No activity on connection from ${record.remoteIP} ` + | ||||
|             `for ${plugins.prettyMs(inactivityTime)}.` + | ||||
|             (record.hasKeepAlive ? ' Despite keep-alive being enabled.' : '') | ||||
|           ); | ||||
|           logger.log('warn', `Closing inactive connection ${id} from ${record.remoteIP} (inactive for ${plugins.prettyMs(inactivityTime)}, keep-alive: ${record.hasKeepAlive ? 'Yes' : 'No'})`, { | ||||
|             connectionId: id, | ||||
|             remoteIP: record.remoteIP, | ||||
|             inactiveFor: plugins.prettyMs(inactivityTime), | ||||
|             hasKeepAlive: record.hasKeepAlive, | ||||
|             component: 'connection-manager' | ||||
|           }); | ||||
|           this.cleanupConnection(record, 'inactivity'); | ||||
|         } | ||||
|       } else if (inactivityTime <= effectiveTimeout && record.inactivityWarningIssued) { | ||||
|         // If activity detected after warning, clear the warning | ||||
|         if (this.settings.enableDetailedLogging) { | ||||
|           console.log( | ||||
|             `[${id}] Connection activity detected after inactivity warning, resetting warning` | ||||
|           ); | ||||
|           logger.log('info', `Connection ${id} activity detected after inactivity warning`, { | ||||
|             connectionId: id, | ||||
|             component: 'connection-manager' | ||||
|           }); | ||||
|         } | ||||
|         record.inactivityWarningIssued = false; | ||||
|       } | ||||
| @@ -369,11 +415,12 @@ export class ConnectionManager { | ||||
|         !record.connectionClosed && | ||||
|         now - record.outgoingClosedTime > 120000 | ||||
|       ) { | ||||
|         console.log( | ||||
|           `[${id}] Parity check: Incoming socket for ${record.remoteIP} still active ${ | ||||
|             plugins.prettyMs(now - record.outgoingClosedTime) | ||||
|           } after outgoing closed.` | ||||
|         ); | ||||
|         logger.log('warn', `Parity check: Connection ${id} from ${record.remoteIP} has incoming socket still active ${plugins.prettyMs(now - record.outgoingClosedTime)} after outgoing socket closed`, { | ||||
|           connectionId: id, | ||||
|           remoteIP: record.remoteIP, | ||||
|           timeElapsed: plugins.prettyMs(now - record.outgoingClosedTime), | ||||
|           component: 'connection-manager' | ||||
|         }); | ||||
|         this.cleanupConnection(record, 'parity_check'); | ||||
|       } | ||||
|     } | ||||
| @@ -406,7 +453,7 @@ export class ConnectionManager { | ||||
|             record.outgoing.end(); | ||||
|           } | ||||
|         } catch (err) { | ||||
|           console.log(`Error during graceful connection end for ${id}: ${err}`); | ||||
|           logger.log('error', `Error during graceful end of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' }); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| @@ -433,7 +480,7 @@ export class ConnectionManager { | ||||
|               } | ||||
|             } | ||||
|           } catch (err) { | ||||
|             console.log(`Error during forced connection destruction for ${id}: ${err}`); | ||||
|             logger.log('error', `Error during forced destruction of connection ${id}: ${err}`, { connectionId: id, error: err, component: 'connection-manager' }); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import * as plugins from '../../plugins.js'; | ||||
| import type { IConnectionRecord, ISmartProxyOptions } from './models/interfaces.js'; | ||||
| import { logger } from '../../core/utils/logger.js'; | ||||
| // Route checking functions have been removed | ||||
| import type { IRouteConfig, IRouteAction, IRouteContext } from './models/route-types.js'; | ||||
| import { ConnectionManager } from './connection-manager.js'; | ||||
| @@ -83,7 +84,7 @@ export class RouteConnectionHandler { | ||||
|     // Validate IP against rate limits and connection limits | ||||
|     const ipValidation = this.securityManager.validateIP(remoteIP); | ||||
|     if (!ipValidation.allowed) { | ||||
|       console.log(`Connection rejected from ${remoteIP}: ${ipValidation.reason}`); | ||||
|       logger.log('warn', `Connection rejected`, { remoteIP, reason: ipValidation.reason, component: 'route-handler' }); | ||||
|       socket.end(); | ||||
|       socket.destroy(); | ||||
|       return; | ||||
| @@ -114,21 +115,35 @@ export class RouteConnectionHandler { | ||||
|         } catch (err) { | ||||
|           // Ignore errors - these are optional enhancements | ||||
|           if (this.settings.enableDetailedLogging) { | ||||
|             console.log(`[${connectionId}] Enhanced TCP keep-alive settings not supported: ${err}`); | ||||
|             logger.log('warn', `Enhanced TCP keep-alive settings not supported`, { connectionId, error: err, component: 'route-handler' }); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (this.settings.enableDetailedLogging) { | ||||
|       console.log( | ||||
|         `[${connectionId}] New connection from ${remoteIP} on port ${localPort}. ` + | ||||
|           `Keep-Alive: ${record.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` + | ||||
|           `Active connections: ${this.connectionManager.getConnectionCount()}` | ||||
|       logger.log('info',  | ||||
|         `New connection from ${remoteIP} on port ${localPort}. ` + | ||||
|         `Keep-Alive: ${record.hasKeepAlive ? 'Enabled' : 'Disabled'}. ` + | ||||
|         `Active connections: ${this.connectionManager.getConnectionCount()}`,  | ||||
|         { | ||||
|           connectionId, | ||||
|           remoteIP, | ||||
|           localPort, | ||||
|           keepAlive: record.hasKeepAlive ? 'Enabled' : 'Disabled', | ||||
|           activeConnections: this.connectionManager.getConnectionCount(), | ||||
|           component: 'route-handler' | ||||
|         } | ||||
|       ); | ||||
|     } else { | ||||
|       console.log( | ||||
|         `New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}` | ||||
|       logger.log('info',  | ||||
|         `New connection from ${remoteIP} on port ${localPort}. Active connections: ${this.connectionManager.getConnectionCount()}`,  | ||||
|         { | ||||
|           remoteIP, | ||||
|           localPort, | ||||
|           activeConnections: this.connectionManager.getConnectionCount(), | ||||
|           component: 'route-handler' | ||||
|         } | ||||
|       ); | ||||
|     } | ||||
|  | ||||
| @@ -147,14 +162,20 @@ export class RouteConnectionHandler { | ||||
|     // Set an initial timeout for handshake data | ||||
|     let initialTimeout: NodeJS.Timeout | null = setTimeout(() => { | ||||
|       if (!initialDataReceived) { | ||||
|         console.log( | ||||
|           `[${connectionId}] Initial data warning (${this.settings.initialDataTimeout}ms) for connection from ${record.remoteIP}` | ||||
|         ); | ||||
|         logger.log('warn', `No initial data received from ${record.remoteIP} after ${this.settings.initialDataTimeout}ms for connection ${connectionId}`, { | ||||
|           connectionId, | ||||
|           timeout: this.settings.initialDataTimeout, | ||||
|           remoteIP: record.remoteIP, | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|  | ||||
|         // Add a grace period | ||||
|         setTimeout(() => { | ||||
|           if (!initialDataReceived) { | ||||
|             console.log(`[${connectionId}] Final initial data timeout after grace period`); | ||||
|             logger.log('warn', `Final initial data timeout after grace period for connection ${connectionId}`, { | ||||
|               connectionId, | ||||
|               component: 'route-handler' | ||||
|             }); | ||||
|             if (record.incomingTerminationReason === null) { | ||||
|               record.incomingTerminationReason = 'initial_timeout'; | ||||
|               this.connectionManager.incrementTerminationStat('incoming', 'initial_timeout'); | ||||
| @@ -187,10 +208,11 @@ export class RouteConnectionHandler { | ||||
|  | ||||
|       // Block non-TLS connections on port 443 | ||||
|       if (!this.tlsManager.isTlsHandshake(chunk) && localPort === 443) { | ||||
|         console.log( | ||||
|           `[${connectionId}] Non-TLS connection detected on port 443. ` + | ||||
|             `Terminating connection - only TLS traffic is allowed on standard HTTPS port.` | ||||
|         ); | ||||
|         logger.log('warn', `Non-TLS connection ${connectionId} detected on port 443. Terminating connection - only TLS traffic is allowed on standard HTTPS port.`, { | ||||
|           connectionId, | ||||
|           message: 'Terminating connection - only TLS traffic is allowed on standard HTTPS port.', | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|         if (record.incomingTerminationReason === null) { | ||||
|           record.incomingTerminationReason = 'non_tls_blocked'; | ||||
|           this.connectionManager.incrementTerminationStat('incoming', 'non_tls_blocked'); | ||||
| @@ -223,7 +245,10 @@ export class RouteConnectionHandler { | ||||
|  | ||||
|           // Check if we should reject connections without SNI | ||||
|           if (!serverName && this.settings.allowSessionTicket === false) { | ||||
|             console.log(`[${connectionId}] No SNI detected in TLS ClientHello; sending TLS alert.`); | ||||
|             logger.log('warn', `No SNI detected in TLS ClientHello for connection ${connectionId}; sending TLS alert`, { | ||||
|               connectionId, | ||||
|               component: 'route-handler' | ||||
|             }); | ||||
|             if (record.incomingTerminationReason === null) { | ||||
|               record.incomingTerminationReason = 'session_ticket_blocked_no_sni'; | ||||
|               this.connectionManager.incrementTerminationStat( | ||||
| @@ -245,7 +270,11 @@ export class RouteConnectionHandler { | ||||
|           } | ||||
|  | ||||
|           if (this.settings.enableDetailedLogging) { | ||||
|             console.log(`[${connectionId}] TLS connection with SNI: ${serverName || '(empty)'}`); | ||||
|             logger.log('info', `TLS connection with SNI`, { | ||||
|               connectionId, | ||||
|               serverName: serverName || '(empty)', | ||||
|               component: 'route-handler' | ||||
|             }); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| @@ -278,12 +307,18 @@ export class RouteConnectionHandler { | ||||
|     }); | ||||
|  | ||||
|     if (!routeMatch) { | ||||
|       console.log( | ||||
|         `[${connectionId}] No route found for ${serverName || 'connection'} on port ${localPort}` | ||||
|       ); | ||||
|       logger.log('warn', `No route found for ${serverName || 'connection'} on port ${localPort} (connection: ${connectionId})`, { | ||||
|         connectionId, | ||||
|         serverName: serverName || 'connection', | ||||
|         localPort, | ||||
|         component: 'route-handler' | ||||
|       }); | ||||
|  | ||||
|       // No matching route, use default/fallback handling | ||||
|       console.log(`[${connectionId}] Using default route handling for connection`); | ||||
|       logger.log('info', `Using default route handling for connection ${connectionId}`, { | ||||
|         connectionId, | ||||
|         component: 'route-handler' | ||||
|       }); | ||||
|  | ||||
|       // Check default security settings | ||||
|       const defaultSecuritySettings = this.settings.defaults?.security; | ||||
| @@ -296,7 +331,11 @@ export class RouteConnectionHandler { | ||||
|           ); | ||||
|  | ||||
|           if (!isAllowed) { | ||||
|             console.log(`[${connectionId}] IP ${remoteIP} not in default allowed list`); | ||||
|             logger.log('warn', `IP ${remoteIP} not in default allowed list for connection ${connectionId}`, { | ||||
|               connectionId, | ||||
|               remoteIP, | ||||
|               component: 'route-handler' | ||||
|             }); | ||||
|             socket.end(); | ||||
|             this.connectionManager.cleanupConnection(record, 'ip_blocked'); | ||||
|             return; | ||||
| @@ -321,7 +360,10 @@ export class RouteConnectionHandler { | ||||
|         ); | ||||
|       } else { | ||||
|         // No default target available, terminate the connection | ||||
|         console.log(`[${connectionId}] No default target configured. Closing connection.`); | ||||
|         logger.log('warn', `No default target configured for connection ${connectionId}. Closing connection`, { | ||||
|           connectionId, | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|         socket.end(); | ||||
|         this.connectionManager.cleanupConnection(record, 'no_default_target'); | ||||
|         return; | ||||
| @@ -332,11 +374,13 @@ export class RouteConnectionHandler { | ||||
|     const route = routeMatch.route; | ||||
|  | ||||
|     if (this.settings.enableDetailedLogging) { | ||||
|       console.log( | ||||
|         `[${connectionId}] Route matched: "${route.name || 'unnamed'}" for ${ | ||||
|           serverName || 'connection' | ||||
|         } on port ${localPort}` | ||||
|       ); | ||||
|       logger.log('info', `Route matched`, { | ||||
|         connectionId, | ||||
|         routeName: route.name || 'unnamed', | ||||
|         serverName: serverName || 'connection', | ||||
|         localPort, | ||||
|         component: 'route-handler' | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -356,7 +400,11 @@ export class RouteConnectionHandler { | ||||
|         return; | ||||
|  | ||||
|       default: | ||||
|         console.log(`[${connectionId}] Unknown action type: ${(route.action as any).type}`); | ||||
|         logger.log('error', `Unknown action type '${(route.action as any).type}' for connection ${connectionId}`, { | ||||
|           connectionId, | ||||
|           actionType: (route.action as any).type, | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|         socket.end(); | ||||
|         this.connectionManager.cleanupConnection(record, 'unknown_action'); | ||||
|     } | ||||
| @@ -381,30 +429,36 @@ export class RouteConnectionHandler { | ||||
|        | ||||
|       // Log the connection for monitoring purposes | ||||
|       if (this.settings.enableDetailedLogging) { | ||||
|         console.log( | ||||
|           `[${record.id}] NFTables forwarding (kernel-level): ` + | ||||
|             `${record.remoteIP}:${socket.remotePort} -> ${socket.localAddress}:${record.localPort}` + | ||||
|             ` (Route: "${route.name || 'unnamed'}", Domain: ${record.lockedDomain || 'n/a'})` | ||||
|         ); | ||||
|         logger.log('info', `NFTables forwarding (kernel-level)`, { | ||||
|           connectionId: record.id, | ||||
|           source: `${record.remoteIP}:${socket.remotePort}`, | ||||
|           destination: `${socket.localAddress}:${record.localPort}`, | ||||
|           routeName: route.name || 'unnamed', | ||||
|           domain: record.lockedDomain || 'n/a', | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|       } else { | ||||
|         console.log( | ||||
|           `[${record.id}] NFTables forwarding: ${record.remoteIP} -> port ${ | ||||
|             record.localPort | ||||
|           } (Route: "${route.name || 'unnamed'}")` | ||||
|         ); | ||||
|         logger.log('info', `NFTables forwarding`, { | ||||
|           connectionId: record.id, | ||||
|           remoteIP: record.remoteIP, | ||||
|           localPort: record.localPort, | ||||
|           routeName: route.name || 'unnamed', | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|       // Additional NFTables-specific logging if configured | ||||
|       if (action.nftables) { | ||||
|         const nftConfig = action.nftables; | ||||
|         if (this.settings.enableDetailedLogging) { | ||||
|           console.log( | ||||
|             `[${record.id}] NFTables config: ` + | ||||
|               `protocol=${nftConfig.protocol || 'tcp'}, ` + | ||||
|               `preserveSourceIP=${nftConfig.preserveSourceIP || false}, ` + | ||||
|               `priority=${nftConfig.priority || 'default'}, ` + | ||||
|               `maxRate=${nftConfig.maxRate || 'unlimited'}` | ||||
|           ); | ||||
|           logger.log('info', `NFTables config`, { | ||||
|             connectionId: record.id, | ||||
|             protocol: nftConfig.protocol || 'tcp', | ||||
|             preserveSourceIP: nftConfig.preserveSourceIP || false, | ||||
|             priority: nftConfig.priority || 'default', | ||||
|             maxRate: nftConfig.maxRate || 'unlimited', | ||||
|             component: 'route-handler' | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|        | ||||
| @@ -419,7 +473,10 @@ export class RouteConnectionHandler { | ||||
|  | ||||
|     // We should have a target configuration for forwarding | ||||
|     if (!action.target) { | ||||
|       console.log(`[${connectionId}] Forward action missing target configuration`); | ||||
|       logger.log('error', `Forward action missing target configuration for connection ${connectionId}`, { | ||||
|         connectionId, | ||||
|         component: 'route-handler' | ||||
|       }); | ||||
|       socket.end(); | ||||
|       this.connectionManager.cleanupConnection(record, 'missing_target'); | ||||
|       return; | ||||
| @@ -447,14 +504,18 @@ export class RouteConnectionHandler { | ||||
|       try { | ||||
|         targetHost = action.target.host(routeContext); | ||||
|         if (this.settings.enableDetailedLogging) { | ||||
|           console.log( | ||||
|             `[${connectionId}] Dynamic host resolved to: ${ | ||||
|               Array.isArray(targetHost) ? targetHost.join(', ') : targetHost | ||||
|             }` | ||||
|           ); | ||||
|           logger.log('info', `Dynamic host resolved to ${Array.isArray(targetHost) ? targetHost.join(', ') : targetHost} for connection ${connectionId}`, { | ||||
|             connectionId, | ||||
|             targetHost: Array.isArray(targetHost) ? targetHost.join(', ') : targetHost, | ||||
|             component: 'route-handler' | ||||
|           }); | ||||
|         } | ||||
|       } catch (err) { | ||||
|         console.log(`[${connectionId}] Error in host mapping function: ${err}`); | ||||
|         logger.log('error', `Error in host mapping function for connection ${connectionId}: ${err}`, { | ||||
|           connectionId, | ||||
|           error: err, | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|         socket.end(); | ||||
|         this.connectionManager.cleanupConnection(record, 'host_mapping_error'); | ||||
|         return; | ||||
| @@ -474,14 +535,21 @@ export class RouteConnectionHandler { | ||||
|       try { | ||||
|         targetPort = action.target.port(routeContext); | ||||
|         if (this.settings.enableDetailedLogging) { | ||||
|           console.log( | ||||
|             `[${connectionId}] Dynamic port mapping: ${record.localPort} -> ${targetPort}` | ||||
|           ); | ||||
|           logger.log('info', `Dynamic port mapping from ${record.localPort} to ${targetPort} for connection ${connectionId}`, { | ||||
|             connectionId, | ||||
|             sourcePort: record.localPort, | ||||
|             targetPort, | ||||
|             component: 'route-handler' | ||||
|           }); | ||||
|         } | ||||
|         // Store the resolved target port in the context for potential future use | ||||
|         routeContext.targetPort = targetPort; | ||||
|       } catch (err) { | ||||
|         console.log(`[${connectionId}] Error in port mapping function: ${err}`); | ||||
|         logger.log('error', `Error in port mapping function for connection ${connectionId}: ${err}`, { | ||||
|           connectionId, | ||||
|           error: err, | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|         socket.end(); | ||||
|         this.connectionManager.cleanupConnection(record, 'port_mapping_error'); | ||||
|         return; | ||||
| @@ -503,7 +571,12 @@ export class RouteConnectionHandler { | ||||
|         case 'passthrough': | ||||
|           // For TLS passthrough, just forward directly | ||||
|           if (this.settings.enableDetailedLogging) { | ||||
|             console.log(`[${connectionId}] Using TLS passthrough to ${selectedHost}:${targetPort}`); | ||||
|             logger.log('info', `Using TLS passthrough to ${selectedHost}:${targetPort} for connection ${connectionId}`, { | ||||
|               connectionId, | ||||
|               targetHost: selectedHost, | ||||
|               targetPort, | ||||
|               component: 'route-handler' | ||||
|             }); | ||||
|           } | ||||
|  | ||||
|           return this.setupDirectConnection( | ||||
| @@ -521,9 +594,11 @@ export class RouteConnectionHandler { | ||||
|           // For TLS termination, use HttpProxy | ||||
|           if (this.httpProxyBridge.getHttpProxy()) { | ||||
|             if (this.settings.enableDetailedLogging) { | ||||
|               console.log( | ||||
|                 `[${connectionId}] Using HttpProxy for TLS termination to ${action.target.host}` | ||||
|               ); | ||||
|               logger.log('info', `Using HttpProxy for TLS termination to ${Array.isArray(action.target.host) ? action.target.host.join(', ') : action.target.host} for connection ${connectionId}`, { | ||||
|                 connectionId, | ||||
|                 targetHost: action.target.host, | ||||
|                 component: 'route-handler' | ||||
|               }); | ||||
|             } | ||||
|  | ||||
|             // If we have an initial chunk with TLS data, start processing it | ||||
| @@ -540,12 +615,18 @@ export class RouteConnectionHandler { | ||||
|             } | ||||
|  | ||||
|             // This shouldn't normally happen - we should have TLS data at this point | ||||
|             console.log(`[${connectionId}] TLS termination route without TLS data`); | ||||
|             logger.log('error', `TLS termination route without TLS data for connection ${connectionId}`, { | ||||
|               connectionId, | ||||
|               component: 'route-handler' | ||||
|             }); | ||||
|             socket.end(); | ||||
|             this.connectionManager.cleanupConnection(record, 'tls_error'); | ||||
|             return; | ||||
|           } else { | ||||
|             console.log(`[${connectionId}] HttpProxy not available for TLS termination`); | ||||
|             logger.log('error', `HttpProxy not available for TLS termination for connection ${connectionId}`, { | ||||
|               connectionId, | ||||
|               component: 'route-handler' | ||||
|             }); | ||||
|             socket.end(); | ||||
|             this.connectionManager.cleanupConnection(record, 'no_http_proxy'); | ||||
|             return; | ||||
| @@ -558,9 +639,11 @@ export class RouteConnectionHandler { | ||||
|       if (isHttpProxyPort && this.httpProxyBridge.getHttpProxy()) { | ||||
|         // Forward non-TLS connections to HttpProxy if configured | ||||
|         if (this.settings.enableDetailedLogging) { | ||||
|           console.log( | ||||
|             `[${connectionId}] Using HttpProxy for non-TLS connection on port ${record.localPort}` | ||||
|           ); | ||||
|           logger.log('info', `Using HttpProxy for non-TLS connection ${connectionId} on port ${record.localPort}`, { | ||||
|             connectionId, | ||||
|             port: record.localPort, | ||||
|             component: 'route-handler' | ||||
|           }); | ||||
|         } | ||||
|          | ||||
|         this.httpProxyBridge.forwardToHttpProxy( | ||||
| @@ -575,9 +658,12 @@ export class RouteConnectionHandler { | ||||
|       } else { | ||||
|         // Basic forwarding | ||||
|         if (this.settings.enableDetailedLogging) { | ||||
|           console.log( | ||||
|             `[${connectionId}] Using basic forwarding to ${action.target.host}:${action.target.port}` | ||||
|           ); | ||||
|           logger.log('info', `Using basic forwarding to ${Array.isArray(action.target.host) ? action.target.host.join(', ') : action.target.host}:${action.target.port} for connection ${connectionId}`, { | ||||
|             connectionId, | ||||
|             targetHost: action.target.host, | ||||
|             targetPort: action.target.port, | ||||
|             component: 'route-handler' | ||||
|           }); | ||||
|         } | ||||
|  | ||||
|         // Get the appropriate host value | ||||
| @@ -633,7 +719,10 @@ export class RouteConnectionHandler { | ||||
|   ): void { | ||||
|     // For TLS connections, we can't do redirects at the TCP level | ||||
|     if (record.isTLS) { | ||||
|       console.log(`[${record.id}] Cannot redirect TLS connection at TCP level`); | ||||
|       logger.log('warn', `Cannot redirect TLS connection ${record.id} at TCP level`, { | ||||
|         connectionId: record.id, | ||||
|         component: 'route-handler' | ||||
|       }); | ||||
|       socket.end(); | ||||
|       this.connectionManager.cleanupConnection(record, 'tls_redirect_error'); | ||||
|       return; | ||||
| @@ -658,9 +747,11 @@ export class RouteConnectionHandler { | ||||
|     const connectionId = record.id; | ||||
|  | ||||
|     if (this.settings.enableDetailedLogging) { | ||||
|       console.log( | ||||
|         `[${connectionId}] Blocking connection based on route "${route.name || 'unnamed'}"` | ||||
|       ); | ||||
|       logger.log('info', `Blocking connection ${connectionId} based on route '${route.name || 'unnamed'}'`, { | ||||
|         connectionId, | ||||
|         routeName: route.name || 'unnamed', | ||||
|         component: 'route-handler' | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     // Simply close the connection | ||||
| @@ -699,8 +790,16 @@ export class RouteConnectionHandler { | ||||
|     targetSocket.once('error', (err) => { | ||||
|       // This handler runs only once during the initial connection phase | ||||
|       const code = (err as any).code; | ||||
|       console.log( | ||||
|         `[${connectionId}] Connection setup error to ${finalTargetHost}:${finalTargetPort}: ${err.message} (${code})` | ||||
|       logger.log('error',  | ||||
|         `Connection setup error for ${connectionId} to ${finalTargetHost}:${finalTargetPort}: ${err.message} (${code})`,  | ||||
|         { | ||||
|           connectionId, | ||||
|           targetHost: finalTargetHost, | ||||
|           targetPort: finalTargetPort, | ||||
|           errorMessage: err.message, | ||||
|           errorCode: code, | ||||
|           component: 'route-handler' | ||||
|         } | ||||
|       ); | ||||
|  | ||||
|       // Resume the incoming socket to prevent it from hanging | ||||
| @@ -708,29 +807,57 @@ export class RouteConnectionHandler { | ||||
|  | ||||
|       // Log specific error types for easier debugging | ||||
|       if (code === 'ECONNREFUSED') { | ||||
|         console.log( | ||||
|           `[${connectionId}] Target ${finalTargetHost}:${finalTargetPort} refused connection. ` + | ||||
|           `Check if the target service is running and listening on that port.` | ||||
|         logger.log('error',  | ||||
|           `Connection ${connectionId}: Target ${finalTargetHost}:${finalTargetPort} refused connection. Check if the target service is running and listening on that port.`,  | ||||
|           { | ||||
|             connectionId, | ||||
|             targetHost: finalTargetHost, | ||||
|             targetPort: finalTargetPort, | ||||
|             recommendation: 'Check if the target service is running and listening on that port.', | ||||
|             component: 'route-handler' | ||||
|           } | ||||
|         ); | ||||
|       } else if (code === 'ETIMEDOUT') { | ||||
|         console.log( | ||||
|           `[${connectionId}] Connection to ${finalTargetHost}:${finalTargetPort} timed out. ` + | ||||
|           `Check network conditions, firewall rules, or if the target is too far away.` | ||||
|         logger.log('error',  | ||||
|           `Connection ${connectionId} to ${finalTargetHost}:${finalTargetPort} timed out. Check network conditions, firewall rules, or if the target is too far away.`,  | ||||
|           { | ||||
|             connectionId, | ||||
|             targetHost: finalTargetHost, | ||||
|             targetPort: finalTargetPort, | ||||
|             recommendation: 'Check network conditions, firewall rules, or if the target is too far away.', | ||||
|             component: 'route-handler' | ||||
|           } | ||||
|         ); | ||||
|       } else if (code === 'ECONNRESET') { | ||||
|         console.log( | ||||
|           `[${connectionId}] Connection to ${finalTargetHost}:${finalTargetPort} was reset. ` + | ||||
|           `The target might have closed the connection abruptly.` | ||||
|         logger.log('error',  | ||||
|           `Connection ${connectionId} to ${finalTargetHost}:${finalTargetPort} was reset. The target might have closed the connection abruptly.`,  | ||||
|           { | ||||
|             connectionId, | ||||
|             targetHost: finalTargetHost, | ||||
|             targetPort: finalTargetPort, | ||||
|             recommendation: 'The target might have closed the connection abruptly.', | ||||
|             component: 'route-handler' | ||||
|           } | ||||
|         ); | ||||
|       } else if (code === 'EHOSTUNREACH') { | ||||
|         console.log( | ||||
|           `[${connectionId}] Host ${finalTargetHost} is unreachable. ` + | ||||
|           `Check DNS settings, network routing, or firewall rules.` | ||||
|         logger.log('error',  | ||||
|           `Connection ${connectionId}: Host ${finalTargetHost} is unreachable. Check DNS settings, network routing, or firewall rules.`,  | ||||
|           { | ||||
|             connectionId, | ||||
|             targetHost: finalTargetHost, | ||||
|             recommendation: 'Check DNS settings, network routing, or firewall rules.', | ||||
|             component: 'route-handler' | ||||
|           } | ||||
|         ); | ||||
|       } else if (code === 'ENOTFOUND') { | ||||
|         console.log( | ||||
|           `[${connectionId}] DNS lookup failed for ${finalTargetHost}. ` + | ||||
|           `Check your DNS settings or if the hostname is correct.` | ||||
|         logger.log('error',  | ||||
|           `Connection ${connectionId}: DNS lookup failed for ${finalTargetHost}. Check your DNS settings or if the hostname is correct.`,  | ||||
|           { | ||||
|             connectionId, | ||||
|             targetHost: finalTargetHost, | ||||
|             recommendation: 'Check your DNS settings or if the hostname is correct.', | ||||
|             component: 'route-handler' | ||||
|           } | ||||
|         ); | ||||
|       } | ||||
|  | ||||
| @@ -779,9 +906,12 @@ export class RouteConnectionHandler { | ||||
|     record.targetPort = finalTargetPort; | ||||
|  | ||||
|     if (this.settings.enableDetailedLogging) { | ||||
|       console.log( | ||||
|         `[${connectionId}] Setting up direct connection to ${finalTargetHost}:${finalTargetPort}` | ||||
|       ); | ||||
|       logger.log('info', `Setting up direct connection ${connectionId} to ${finalTargetHost}:${finalTargetPort}`, { | ||||
|         connectionId, | ||||
|         targetHost: finalTargetHost, | ||||
|         targetPort: finalTargetPort, | ||||
|         component: 'route-handler' | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     // Setup connection options | ||||
| @@ -826,9 +956,11 @@ export class RouteConnectionHandler { | ||||
|         } catch (err) { | ||||
|           // Ignore errors - these are optional enhancements | ||||
|           if (this.settings.enableDetailedLogging) { | ||||
|             console.log( | ||||
|               `[${connectionId}] Enhanced TCP keep-alive not supported for outgoing socket: ${err}` | ||||
|             ); | ||||
|             logger.log('warn', `Enhanced TCP keep-alive not supported for outgoing socket on connection ${connectionId}: ${err}`, { | ||||
|               connectionId, | ||||
|               error: err, | ||||
|               component: 'route-handler' | ||||
|             }); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| @@ -848,22 +980,23 @@ export class RouteConnectionHandler { | ||||
|     socket.on('timeout', () => { | ||||
|       // For keep-alive connections, just log a warning instead of closing | ||||
|       if (record.hasKeepAlive) { | ||||
|         console.log( | ||||
|           `[${connectionId}] Timeout event on incoming keep-alive connection from ${ | ||||
|             record.remoteIP | ||||
|           } after ${plugins.prettyMs( | ||||
|             this.settings.socketTimeout || 3600000 | ||||
|           )}. Connection preserved.` | ||||
|         ); | ||||
|         logger.log('warn', `Timeout event on incoming keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`, { | ||||
|           connectionId, | ||||
|           remoteIP: record.remoteIP, | ||||
|           timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000), | ||||
|           status: 'Connection preserved', | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       // For non-keep-alive connections, proceed with normal cleanup | ||||
|       console.log( | ||||
|         `[${connectionId}] Timeout on incoming side from ${ | ||||
|           record.remoteIP | ||||
|         } after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}` | ||||
|       ); | ||||
|       logger.log('warn', `Timeout on incoming side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`, { | ||||
|         connectionId, | ||||
|         remoteIP: record.remoteIP, | ||||
|         timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000), | ||||
|         component: 'route-handler' | ||||
|       }); | ||||
|       if (record.incomingTerminationReason === null) { | ||||
|         record.incomingTerminationReason = 'timeout'; | ||||
|         this.connectionManager.incrementTerminationStat('incoming', 'timeout'); | ||||
| @@ -874,22 +1007,23 @@ export class RouteConnectionHandler { | ||||
|     targetSocket.on('timeout', () => { | ||||
|       // For keep-alive connections, just log a warning instead of closing | ||||
|       if (record.hasKeepAlive) { | ||||
|         console.log( | ||||
|           `[${connectionId}] Timeout event on outgoing keep-alive connection from ${ | ||||
|             record.remoteIP | ||||
|           } after ${plugins.prettyMs( | ||||
|             this.settings.socketTimeout || 3600000 | ||||
|           )}. Connection preserved.` | ||||
|         ); | ||||
|         logger.log('warn', `Timeout event on outgoing keep-alive connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}. Connection preserved.`, { | ||||
|           connectionId, | ||||
|           remoteIP: record.remoteIP, | ||||
|           timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000), | ||||
|           status: 'Connection preserved', | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       // For non-keep-alive connections, proceed with normal cleanup | ||||
|       console.log( | ||||
|         `[${connectionId}] Timeout on outgoing side from ${ | ||||
|           record.remoteIP | ||||
|         } after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}` | ||||
|       ); | ||||
|       logger.log('warn', `Timeout on outgoing side for connection ${connectionId} from ${record.remoteIP} after ${plugins.prettyMs(this.settings.socketTimeout || 3600000)}`, { | ||||
|         connectionId, | ||||
|         remoteIP: record.remoteIP, | ||||
|         timeout: plugins.prettyMs(this.settings.socketTimeout || 3600000), | ||||
|         component: 'route-handler' | ||||
|       }); | ||||
|       if (record.outgoingTerminationReason === null) { | ||||
|         record.outgoingTerminationReason = 'timeout'; | ||||
|         this.connectionManager.incrementTerminationStat('outgoing', 'timeout'); | ||||
| @@ -909,9 +1043,12 @@ export class RouteConnectionHandler { | ||||
|     // Wait for the outgoing connection to be ready before setting up piping | ||||
|     targetSocket.once('connect', () => { | ||||
|       if (this.settings.enableDetailedLogging) { | ||||
|         console.log( | ||||
|           `[${connectionId}] Connection established to target: ${finalTargetHost}:${finalTargetPort}` | ||||
|         ); | ||||
|         logger.log('info', `Connection ${connectionId} established to target ${finalTargetHost}:${finalTargetPort}`, { | ||||
|           connectionId, | ||||
|           targetHost: finalTargetHost, | ||||
|           targetPort: finalTargetPort, | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|       } | ||||
|  | ||||
|       // Clear the initial connection error handler | ||||
| @@ -933,7 +1070,11 @@ export class RouteConnectionHandler { | ||||
|         // Write pending data immediately | ||||
|         targetSocket.write(combinedData, (err) => { | ||||
|           if (err) { | ||||
|             console.log(`[${connectionId}] Error writing pending data to target: ${err.message}`); | ||||
|             logger.log('error', `Error writing pending data to target for connection ${connectionId}: ${err.message}`, { | ||||
|               connectionId, | ||||
|               error: err.message, | ||||
|               component: 'route-handler' | ||||
|             }); | ||||
|             return this.connectionManager.initiateCleanupOnce(record, 'write_error'); | ||||
|           } | ||||
|         }); | ||||
| @@ -954,15 +1095,17 @@ export class RouteConnectionHandler { | ||||
|       }); | ||||
|  | ||||
|       // Log successful connection | ||||
|       console.log( | ||||
|       logger.log('info',  | ||||
|         `Connection established: ${record.remoteIP} -> ${finalTargetHost}:${finalTargetPort}` + | ||||
|           `${ | ||||
|             serverName | ||||
|               ? ` (SNI: ${serverName})` | ||||
|               : record.lockedDomain | ||||
|               ? ` (Domain: ${record.lockedDomain})` | ||||
|               : '' | ||||
|           }` | ||||
|         `${serverName ? ` (SNI: ${serverName})` : record.lockedDomain ? ` (Domain: ${record.lockedDomain})` : ''}`,  | ||||
|         { | ||||
|           remoteIP: record.remoteIP, | ||||
|           targetHost: finalTargetHost, | ||||
|           targetPort: finalTargetPort, | ||||
|           sni: serverName || undefined, | ||||
|           domain: !serverName && record.lockedDomain ? record.lockedDomain : undefined, | ||||
|           component: 'route-handler' | ||||
|         } | ||||
|       ); | ||||
|  | ||||
|       // Add TLS renegotiation handler if needed | ||||
| @@ -990,17 +1133,21 @@ export class RouteConnectionHandler { | ||||
|         socket.on('data', renegotiationHandler); | ||||
|  | ||||
|         if (this.settings.enableDetailedLogging) { | ||||
|           console.log( | ||||
|             `[${connectionId}] TLS renegotiation handler installed for SNI domain: ${serverName}` | ||||
|           ); | ||||
|           logger.log('info', `TLS renegotiation handler installed for connection ${connectionId} with SNI ${serverName}`, { | ||||
|             connectionId, | ||||
|             serverName, | ||||
|             component: 'route-handler' | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       // Set connection timeout | ||||
|       record.cleanupTimer = this.timeoutManager.setupConnectionTimeout(record, (record, reason) => { | ||||
|         console.log( | ||||
|           `[${connectionId}] Connection from ${record.remoteIP} exceeded max lifetime, forcing cleanup.` | ||||
|         ); | ||||
|         logger.log('warn', `Connection ${connectionId} from ${record.remoteIP} exceeded max lifetime, forcing cleanup`, { | ||||
|           connectionId, | ||||
|           remoteIP: record.remoteIP, | ||||
|           component: 'route-handler' | ||||
|         }); | ||||
|         this.connectionManager.initiateCleanupOnce(record, reason); | ||||
|       }); | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import * as plugins from '../../plugins.js'; | ||||
| import { logger } from '../../core/utils/logger.js'; | ||||
|  | ||||
| // Importing required components | ||||
| import { ConnectionManager } from './connection-manager.js'; | ||||
| @@ -239,7 +240,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|     ); | ||||
|      | ||||
|     if (autoRoutes.length === 0 && !this.hasStaticCertRoutes()) { | ||||
|       console.log('No routes require certificate management'); | ||||
|       logger.log('info', 'No routes require certificate management', { component: 'certificate-manager' }); | ||||
|       return; | ||||
|     } | ||||
|      | ||||
| @@ -256,7 +257,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|         useProduction: this.settings.acme.useProduction || false, | ||||
|         port: this.settings.acme.port || 80 | ||||
|       }; | ||||
|       console.log(`Using top-level ACME configuration with email: ${acmeOptions.email}`); | ||||
|       logger.log('info', `Using top-level ACME configuration with email: ${acmeOptions.email}`, { component: 'certificate-manager' }); | ||||
|     } else if (autoRoutes.length > 0) { | ||||
|       // Check for route-level ACME config | ||||
|       const routeWithAcme = autoRoutes.find(r => r.action.tls?.acme?.email); | ||||
| @@ -267,7 +268,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|           useProduction: routeAcme.useProduction || false, | ||||
|           port: routeAcme.challengePort || 80 | ||||
|         }; | ||||
|         console.log(`Using route-level ACME configuration from route '${routeWithAcme.name}' with email: ${acmeOptions.email}`); | ||||
|         logger.log('info', `Using route-level ACME configuration from route '${routeWithAcme.name}' with email: ${acmeOptions.email}`, { component: 'certificate-manager' }); | ||||
|       } | ||||
|     } | ||||
|      | ||||
| @@ -305,7 +306,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|   public async start() { | ||||
|     // Don't start if already shutting down | ||||
|     if (this.isShuttingDown) { | ||||
|       console.log("Cannot start SmartProxy while it's shutting down"); | ||||
|       logger.log('warn', "Cannot start SmartProxy while it's in the shutdown process"); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
| @@ -332,9 +333,9 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|     const allWarnings = [...configWarnings, ...acmeWarnings]; | ||||
|      | ||||
|     if (allWarnings.length > 0) { | ||||
|       console.log("Configuration warnings:"); | ||||
|       logger.log('warn', `${allWarnings.length} configuration warnings found`, { count: allWarnings.length }); | ||||
|       for (const warning of allWarnings) { | ||||
|         console.log(` - ${warning}`); | ||||
|         logger.log('warn', `${warning}`); | ||||
|       } | ||||
|     } | ||||
|  | ||||
| @@ -353,7 +354,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|      | ||||
|     // Now that ports are listening, provision any required certificates | ||||
|     if (this.certManager) { | ||||
|       console.log('Starting certificate provisioning now that ports are ready'); | ||||
|       logger.log('info', 'Starting certificate provisioning now that ports are ready', { component: 'certificate-manager' }); | ||||
|       await this.certManager.provisionAllCertificates(); | ||||
|     } | ||||
|  | ||||
| @@ -411,16 +412,26 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|       const terminationStats = this.connectionManager.getTerminationStats(); | ||||
|  | ||||
|       // Log detailed stats | ||||
|       console.log( | ||||
|         `Active connections: ${connectionRecords.size}. ` + | ||||
|         `Types: TLS=${tlsConnections} (Completed=${completedTlsHandshakes}, Pending=${pendingTlsHandshakes}), ` + | ||||
|         `Non-TLS=${nonTlsConnections}, KeepAlive=${keepAliveConnections}, HttpProxy=${httpProxyConnections}. ` + | ||||
|         `Longest running: IN=${plugins.prettyMs(maxIncoming)}, OUT=${plugins.prettyMs(maxOutgoing)}. ` + | ||||
|         `Termination stats: ${JSON.stringify({ | ||||
|           IN: terminationStats.incoming, | ||||
|           OUT: terminationStats.outgoing, | ||||
|         })}` | ||||
|       ); | ||||
|       logger.log('info', 'Connection statistics', { | ||||
|         activeConnections: connectionRecords.size, | ||||
|         tls: { | ||||
|           total: tlsConnections, | ||||
|           completed: completedTlsHandshakes, | ||||
|           pending: pendingTlsHandshakes | ||||
|         }, | ||||
|         nonTls: nonTlsConnections, | ||||
|         keepAlive: keepAliveConnections, | ||||
|         httpProxy: httpProxyConnections, | ||||
|         longestRunning: { | ||||
|           incoming: plugins.prettyMs(maxIncoming), | ||||
|           outgoing: plugins.prettyMs(maxOutgoing) | ||||
|         }, | ||||
|         terminationStats: { | ||||
|           incoming: terminationStats.incoming, | ||||
|           outgoing: terminationStats.outgoing | ||||
|         }, | ||||
|         component: 'connection-manager' | ||||
|       }); | ||||
|     }, this.settings.inactivityCheckInterval || 60000); | ||||
|  | ||||
|     // Make sure the interval doesn't keep the process alive | ||||
| @@ -439,19 +450,19 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|    * Stop the proxy server | ||||
|    */ | ||||
|   public async stop() { | ||||
|     console.log('SmartProxy shutting down...'); | ||||
|     logger.log('info', 'SmartProxy shutting down...'); | ||||
|     this.isShuttingDown = true; | ||||
|     this.portManager.setShuttingDown(true); | ||||
|      | ||||
|     // Stop certificate manager | ||||
|     if (this.certManager) { | ||||
|       await this.certManager.stop(); | ||||
|       console.log('Certificate manager stopped'); | ||||
|       logger.log('info', 'Certificate manager stopped'); | ||||
|     } | ||||
|      | ||||
|     // Stop NFTablesManager | ||||
|     await this.nftablesManager.stop(); | ||||
|     console.log('NFTablesManager stopped'); | ||||
|     logger.log('info', 'NFTablesManager stopped'); | ||||
|  | ||||
|     // Stop the connection logger | ||||
|     if (this.connectionLogger) { | ||||
| @@ -461,7 +472,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|  | ||||
|     // Stop all port listeners | ||||
|     await this.portManager.closeAll(); | ||||
|     console.log('All servers closed. Cleaning up active connections...'); | ||||
|     logger.log('info', 'All servers closed. Cleaning up active connections...'); | ||||
|  | ||||
|     // Clean up all active connections | ||||
|     this.connectionManager.clearConnections(); | ||||
| @@ -472,7 +483,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|     // Clear ACME state manager | ||||
|     this.acmeStateManager.clear(); | ||||
|  | ||||
|     console.log('SmartProxy shutdown complete.'); | ||||
|     logger.log('info', 'SmartProxy shutdown complete.'); | ||||
|   } | ||||
|    | ||||
|   /** | ||||
| @@ -481,7 +492,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|    * Note: This legacy method has been removed. Use updateRoutes instead. | ||||
|    */ | ||||
|   public async updateDomainConfigs(): Promise<void> { | ||||
|     console.warn('Method updateDomainConfigs() is deprecated. Use updateRoutes() instead.'); | ||||
|     logger.log('warn', 'Method updateDomainConfigs() is deprecated. Use updateRoutes() instead.'); | ||||
|     throw new Error('updateDomainConfigs() is deprecated - use updateRoutes() instead'); | ||||
|   } | ||||
|    | ||||
| @@ -497,7 +508,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|       const challengeRouteExists = this.settings.routes.some(r => r.name === 'acme-challenge'); | ||||
|        | ||||
|       if (!challengeRouteExists) { | ||||
|         console.log('Challenge route successfully removed from routes'); | ||||
|         logger.log('info', 'Challenge route successfully removed from routes'); | ||||
|         return; | ||||
|       } | ||||
|        | ||||
| @@ -505,7 +516,9 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|       await plugins.smartdelay.delayFor(retryDelay); | ||||
|     } | ||||
|      | ||||
|     throw new Error('Failed to verify challenge route removal after ' + maxRetries + ' attempts'); | ||||
|     const error = `Failed to verify challenge route removal after ${maxRetries} attempts`; | ||||
|     logger.log('error', error); | ||||
|     throw new Error(error); | ||||
|   } | ||||
|    | ||||
|   /** | ||||
| @@ -533,7 +546,7 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|    */ | ||||
|   public async updateRoutes(newRoutes: IRouteConfig[]): Promise<void> { | ||||
|     return this.routeUpdateLock.runExclusive(async () => { | ||||
|       console.log(`Updating routes (${newRoutes.length} routes)`); | ||||
|       logger.log('info', `Updating routes (${newRoutes.length} routes)`, { routeCount: newRoutes.length, component: 'route-manager' }); | ||||
|  | ||||
|       // Get existing routes that use NFTables | ||||
|       const oldNfTablesRoutes = this.settings.routes.filter( | ||||
| @@ -658,14 +671,14 @@ export class SmartProxy extends plugins.EventEmitter { | ||||
|      | ||||
|     // Check for wildcard domains (they can't get ACME certs) | ||||
|     if (domain.includes('*')) { | ||||
|       console.log(`Wildcard domains like "${domain}" are not supported for ACME certificates`); | ||||
|       logger.log('warn', `Wildcard domains like "${domain}" are not supported for automatic ACME certificates`, { domain, component: 'certificate-manager' }); | ||||
|       return false; | ||||
|     } | ||||
|      | ||||
|     // Check if domain has at least one dot and no invalid characters | ||||
|     const validDomainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; | ||||
|     if (!validDomainRegex.test(domain)) { | ||||
|       console.log(`Domain "${domain}" has invalid format`); | ||||
|       logger.log('warn', `Domain "${domain}" has invalid format for certificate issuance`, { domain, component: 'certificate-manager' }); | ||||
|       return false; | ||||
|     } | ||||
|      | ||||
|   | ||||
		Reference in New Issue
	
	Block a user