@@ -252,9 +252,7 @@ export class DcRouter {
}
public async start() {
console . log ( '╔═══════════════════════════════════════════════════════════════════╗ ' ) ;
console . log ( '║ Starting DcRouter Services ║' ) ;
console . log ( '╚═══════════════════════════════════════════════════════════════════╝' ) ;
logger . log ( 'info' , 'Starting DcRouter Services ' ) ;
this . opsServer = new OpsServer ( this ) ;
@@ -296,7 +294,7 @@ export class DcRouter {
this . logStartupSummary ( ) ;
} catch ( error ) {
console . error( '❌ Error starting DcRouter: ', error ) ;
logger . log ( ' error' , ' Error starting DcRouter', { error : String ( error ) } ) ;
// Try to clean up any services that may have started
await this . stop ( ) ;
throw error ;
@@ -307,104 +305,60 @@ export class DcRouter {
* Log comprehensive startup summary
*/
private logStartupSummary ( ) : void {
console . log ( '\n╔═══════════════════════════════════════════════════════════════════╗ ' ) ;
console . log ( '║ DcRouter Started Successfully ║' ) ;
console . log ( '╚═══════════════════════════════════════════════════════════════════╝\n' ) ;
logger . log ( 'info' , 'DcRouter Started Successfully ' ) ;
// Metrics summary
if ( this . metricsManager ) {
console . log ( '📊 Metrics Service:' ) ;
console . log ( ' ├─ SmartMetrics: Active' ) ;
console . log ( ' ├─ SmartProxy Stats: Active' ) ;
console . log ( ' └─ Real-time tracking: Enabled' ) ;
logger . log ( 'info' , ' Metrics Service: SmartMetrics active, SmartProxy stats active, real-time tracking enabled ' ) ;
}
// SmartProxy summary
if ( this . smartProxy ) {
console . log ( '🌐 SmartProxy Service:' ) ;
const routeCount = this . options . smartProxyConfig ? . routes ? . length || 0 ;
console . log ( ` ├─ Routes configured: ${ routeCount } ` ) ;
console . log ( ` ├─ ACME enabled: ${ this . options . smartProxyConfig ? . acme ? . enabled || false } ` ) ;
if ( this . options . smartProxyConfig ? . acme ? . enabled ) {
console . log ( ` ├─ ACME email: ${ this . options . smartProxyConfig . acme . email || 'not set' } ` ) ;
console . log ( ` └─ ACME mode: ${ this . options . smartProxyConfig . acme . useProduction ? 'production' : 'staging' } ` ) ;
} else {
console . log ( ' └─ ACME: disabled' ) ;
}
const acmeEnabled = this . options . smartProxyConfig ? . acme ? . enabled || false ;
const acmeMode = acmeEnabled
? ` email= ${ this . options . smartProxyConfig ! . acme ! . email || 'not set' } , mode= ${ this . options . smartProxyConfig ! . acme ! . useProduction ? 'production' : 'staging' } `
: 'disabled' ;
logger . log ( 'info' , ` SmartProxy Service: ${ routeCount } routes, ACME: ${ acmeMode } ` ) ;
}
// Email service summary
if ( this . emailServer && this . options . emailConfig ) {
console . log ( '\n📧 Email Service:' ) ;
const ports = this . options . emailConfig . ports || [ ] ;
console . log ( ` ├─ Ports: ${ ports . join ( ', ' ) } ` ) ;
console . log ( ` ├─ Hostname: ${ this . options . emailConfig . hostnam e || 'localhos t' } ` ) ;
console . log ( ` ├─ Domains configured: ${ this . options . emailConfig . domains ? . length || 0 } ` ) ;
if ( this . options . emailConfig . domains && this . options . emailConfig . domains . length > 0 ) {
this . options . emailConfig . domains . forEach ( ( domain , index ) = > {
const isLast = index === this . options . emailConfig ! . domains ! . length - 1 ;
console . log ( ` ${ isLast ? '└─' : '├─' } ${ domain . domain } ( ${ domain . dnsMode || 'default' } ) ` ) ;
} ) ;
}
console . log ( ` └─ DKIM: Initialized for all domains ` ) ;
const domainCount = this . options . emailConfig . domains ? . length || 0 ;
const domainNames = this . options . emailConfig . domains ? . map ( d = > ` ${ d . domain } ( ${ d . dnsMod e || 'defaul t' } ) `) . join ( ', ' ) || 'none' ;
logger . log ( 'info' , ` Email Service: ports=[ ${ ports . join ( ', ' ) } ], hostname= ${ this . options . emailConfig . hostname || 'localhost' } , domains= ${ domainCount } [ ${ domainNames } ], DKIM initialized ` ) ;
}
// DNS service summary
if ( this . dnsServer && this . options . dnsNsDomains && this . options . dnsScopes ) {
console . log ( '\n🌍 DNS Service:' ) ;
console . log ( ` ├─ Nameservers: ${ this . options . dnsNsDomains . join ( ', ' ) } ` ) ;
console . log ( ` ├─ Primary NS: ${ this . options . dnsNsDomains [ 0 ] } ` ) ;
console . log ( ` ├─ Authoritative for: ${ this . options . dnsScopes . length } domains ` ) ;
console . log ( ` ├─ UDP Port: 53 ` ) ;
console . log ( ` ├─ DNS-over-HTTPS: Enabled via socket handler ` ) ;
console . log ( ` └─ DNSSEC: ${ this . options . dnsNsDomains [ 0 ] ? 'Enabled' : 'Disabled' } ` ) ;
// Show authoritative domains
if ( this . options . dnsScopes . length > 0 ) {
console . log ( '\n Authoritative Domains:' ) ;
this . options . dnsScopes . forEach ( ( domain , index ) = > {
const isLast = index === this . options . dnsScopes ! . length - 1 ;
console . log ( ` ${ isLast ? '└─' : '├─' } ${ domain } ` ) ;
} ) ;
}
logger . log ( 'info' , ` DNS Service: nameservers=[ ${ this . options . dnsNsDomains . join ( ', ' ) } ], authoritative for ${ this . options . dnsScopes . length } domains [ ${ this . options . dnsScopes . join ( ', ' ) } ], UDP:53, DoH enabled ` ) ;
}
// RADIUS service summary
if ( this . radiusServer && this . options . radiusConfig ) {
console . log ( '\n🔐 RADIUS Service:' ) ;
console . log ( ` ├─ Auth Port: ${ this . options . radiusConfig . authPort || 1812 } ` ) ;
console . log ( ` ├─ Acct Port: ${ this . options . radiusConfig . acctPort || 1813 } ` ) ;
console . log ( ` ├─ Clients configured: ${ this . options . radiusConfig . clients ? . length || 0 } ` ) ;
const vlanStats = this . radiusServer . getVlanManager ( ) . getStats ( ) ;
console . log ( ` ├─ VLAN mappings: ${ vlanStats . totalMappings } ` ) ;
console . log ( ` └─ Accounting: ${ this . options . radiusConfig . accounting ? . enabled ? 'Enabled' : 'Disabled' } ` ) ;
logger . log ( 'info' , ` RADIUS Service: auth= ${ this . options . radiusConfig . authPort || 1812 } , acct= ${ this . options . radiusConfig . acctPort || 1813 } , clients= ${ this . options . radiusConfig . clients ? . length || 0 } , VLANs= ${ vlanStats . totalMappings } , accounting= ${ this . options . radiusConfig . accounting ? . enabled ? 'enabled' : 'disabled' } ` ) ;
}
// Remote Ingress summary
if ( this . tunnelManager && this . options . remoteIngressConfig ? . enabled ) {
console . log ( '\n🌐 Remote Ingress:' ) ;
console . log ( ` ├─ Tunnel Port: ${ this . options . remoteIngressConfig . tunnelPort || 8443 } ` ) ;
const edgeCount = this . remoteIngressManager ? . getAllEdges ( ) . length || 0 ;
const connectedCount = this . tunnelManager . getConnectedCount ( ) ;
console . log ( ` ├─ Registered E dges: ${ edgeCount } ` ) ;
console . log ( ` └─ Connected Edges: ${ connectedCount } ` ) ;
logger . log ( 'info' , ` Remote Ingress: tunnel port= ${ this . options . remoteIngressConfig . tunnelPort || 8443 } , e dges= ${ edgeCount } registered/ ${ connectedCount } connected `) ;
}
// Storage summary
if ( this . storageManager && this . options . storage ) {
console . log ( '\n💾 Storage:' ) ;
console . log ( ` └─ Path: ${ this . options . storage . fsPath || 'default' } ` ) ;
logger . log ( 'info' , ` Storage: path= ${ this . options . storage . fsPath || 'default' } ` ) ;
}
// Cache database summary
if ( this . cacheDb ) {
console . log ( '\n🗄️ Cache Database (smartdata + LocalTsmDb):' ) ;
console . log ( ` ├─ Storage: ${ this . cacheDb . getStoragePath ( ) } ` ) ;
console . log ( ` ├─ Database: ${ this . cacheDb . getDbName ( ) } ` ) ;
console . log ( ` └─ Cleaner: ${ this . cacheCleaner ? . isActive ( ) ? 'Active' : 'Inactive' } ( ${ ( this . options . cacheConfig ? . cleanupIntervalHours || 1 ) } h interval) ` ) ;
logger . log ( 'info' , ` Cache Database: storage= ${ this . cacheDb . getStoragePath ( ) } , db= ${ this . cacheDb . getDbName ( ) } , cleaner= ${ this . cacheCleaner ? . isActive ( ) ? 'active' : 'inactive' } ( ${ ( this . options . cacheConfig ? . cleanupIntervalHours || 1 ) } h interval) ` ) ;
}
console . log ( '\n✅ All services are running\n ' ) ;
logger . log ( 'info' , ' All services are running' ) ;
}
/**
@@ -439,7 +393,7 @@ export class DcRouter {
* Set up SmartProxy with direct configuration and automatic email routes
*/
private async setupSmartProxy ( ) : Promise < void > {
console . log ( '[DcRouter] Setting up SmartProxy...' ) ;
logger . log ( 'info' , ' Setting up SmartProxy...' ) ;
let routes : plugins.smartproxy.IRouteConfig [ ] = [ ] ;
let acmeConfig : plugins.smartproxy.IAcmeOptions | undefined ;
@@ -447,22 +401,20 @@ export class DcRouter {
if ( this . options . smartProxyConfig ) {
routes = this . options . smartProxyConfig . routes || [ ] ;
acmeConfig = this . options . smartProxyConfig . acme ;
console . log ( ` [DcRouter] Found ${ routes . length } routes in config ` ) ;
console . log ( ` [DcRouter] ACME config present: ${ ! ! acmeConfig } ` ) ;
logger . log ( 'info' , ` Found ${ routes . length } routes in config, ACME config present: ${ ! ! acmeConfig } ` ) ;
}
// If email config exists, automatically add email routes
if ( this . options . emailConfig ) {
const emailRoutes = this . generateEmailRoutes ( this . options . emailConfig ) ;
console . log ( ` Email R outes are: ` )
console . log ( emailRoutes )
logger . log ( 'debug' , ' Email r outes generated' , { routes : JSON.stringify ( emailRoutes ) } ) ;
routes = [ . . . routes , . . . emailRoutes ] ; // Enable email routing through SmartProxy
}
// If DNS is configured, add DNS routes
if ( this . options . dnsNsDomains && this . options . dnsNsDomains . length > 0 ) {
const dnsRoutes = this . generateDnsRoutes ( ) ;
console . log ( ` DNS R outes for nameservers ${ this . options . dnsNsDomains . join ( ', ' ) } : `, dnsRoutes ) ;
logger . log ( 'debug' , ` DNS r outes for nameservers ${ this . options . dnsNsDomains . join ( ', ' ) } ` , { routes : JSON.stringify ( dnsRoutes ) } ) ;
routes = [ . . . routes , . . . dnsRoutes ] ;
}
@@ -480,7 +432,7 @@ export class DcRouter {
// Configure DNS challenge if available
let challengeHandlers : any [ ] = [ ] ;
if ( this . options . dnsChallenge ? . cloudflareApiKey ) {
console . log ( 'Configuring Cloudflare DNS challenge for ACME' ) ;
logger . log ( 'info' , 'Configuring Cloudflare DNS challenge for ACME' ) ;
const cloudflareAccount = new plugins . cloudflare . CloudflareAccount ( this . options . dnsChallenge . cloudflareApiKey ) ;
const dns01Handler = new plugins . smartacme . handlers . Dns01Handler ( cloudflareAccount ) ;
challengeHandlers . push ( dns01Handler ) ;
@@ -488,7 +440,7 @@ export class DcRouter {
// If we have routes or need a basic SmartProxy instance, create it
if ( routes . length > 0 || this . options . smartProxyConfig ) {
console . log ( 'Setting up SmartProxy with combined configuration' ) ;
logger . log ( 'info' , 'Setting up SmartProxy with combined configuration' ) ;
// Track cert entries loaded from cert store so we can populate certificateStatusMap after start
const loadedCertEntries : Array < { domain : string ; publicKey : string ; validUntil? : number ; validFrom? : number } > = [ ] ;
@@ -537,7 +489,7 @@ export class DcRouter {
// Stop old SmartAcme if it exists (e.g., during updateSmartProxyConfig)
if ( this . smartAcme ) {
await this . smartAcme . stop ( ) . catch ( err = >
console . error( '[DcRouter] Error stopping old SmartAcme: ', err )
logger . log ( ' error' , ' Error stopping old SmartAcme', { error : String ( err ) } )
) ;
}
this . smartAcme = new plugins . smartacme . SmartAcme ( {
@@ -600,25 +552,19 @@ export class DcRouter {
}
// Create SmartProxy instance
console . log ( '[DcRouter] Creating SmartProxy instance with config:' , JSON . stringify ( {
routeCount : smartProxyConfig.routes?.length ,
acmeEnabled : smartProxyConfig.acme?.enabled ,
acmeEmail : smartProxyConfig.acme?.email ,
certProvisionFunction : ! ! smartProxyConfig . certProvisionFunction
} , null , 2 ) ) ;
logger . log ( 'info' , ` Creating SmartProxy instance: routes= ${ smartProxyConfig . routes ? . length } , acme= ${ smartProxyConfig . acme ? . enabled } , certProvisionFunction= ${ ! ! smartProxyConfig . certProvisionFunction } ` ) ;
this . smartProxy = new plugins . smartproxy . SmartProxy ( smartProxyConfig ) ;
// Set up event listeners
this . smartProxy . on ( 'error' , ( err ) = > {
console . error ( '[DcRouter] SmartProxy error:' , err ) ;
console . error ( '[DcRouter] Error stack:' , err . stack ) ;
logger . log ( 'error' , ` SmartProxy error: ${ err . message } ` , { stack : err.stack } ) ;
} ) ;
// Always listen for certificate events — emitted by both ACME and certProvisionFunction paths
// Events are keyed by domain for domain-centric certificate tracking
this . smartProxy . on ( 'certificate-issued' , ( event : plugins.smartproxy.ICertificateIssuedEvent ) = > {
console . log ( ` [DcRouter] Certificate issued for ${ event . domain } via ${ event . source } , expires ${ event . expiryDate } ` ) ;
logger . log ( 'info' , ` Certificate issued for ${ event . domain } via ${ event . source } , expires ${ event . expiryDate } ` ) ;
const routeNames = this . findRouteNamesForDomain ( event . domain ) ;
this . certificateStatusMap . set ( event . domain , {
status : 'valid' , routeNames ,
@@ -628,7 +574,7 @@ export class DcRouter {
} ) ;
this . smartProxy . on ( 'certificate-renewed' , ( event : plugins.smartproxy.ICertificateIssuedEvent ) = > {
console . log ( ` [DcRouter] Certificate renewed for ${ event . domain } via ${ event . source } , expires ${ event . expiryDate } ` ) ;
logger . log ( 'info' , ` Certificate renewed for ${ event . domain } via ${ event . source } , expires ${ event . expiryDate } ` ) ;
const routeNames = this . findRouteNamesForDomain ( event . domain ) ;
this . certificateStatusMap . set ( event . domain , {
status : 'valid' , routeNames ,
@@ -638,7 +584,7 @@ export class DcRouter {
} ) ;
this . smartProxy . on ( 'certificate-failed' , ( event : plugins.smartproxy.ICertificateFailedEvent ) = > {
console . error( ` [DcRouter] Certificate failed for ${ event . domain } ( ${ event . source } ): ` , event . error ) ;
logger . log ( ' error' , ` Certificate failed for ${ event . domain } ( ${ event . source } ): ${ event . error } ` ) ;
const routeNames = this . findRouteNamesForDomain ( event . domain ) ;
this . certificateStatusMap . set ( event . domain , {
status : 'failed' , routeNames , error : event.error ,
@@ -647,9 +593,9 @@ export class DcRouter {
} ) ;
// Start SmartProxy
console . log ( '[DcRouter] Starting SmartProxy...' ) ;
logger . log ( 'info' , ' Starting SmartProxy...' ) ;
await this . smartProxy . start ( ) ;
console . log ( '[DcRouter] SmartProxy started successfully' ) ;
logger . log ( 'info' , ' SmartProxy started successfully' ) ;
// Populate certificateStatusMap for certs loaded from store at startup
for ( const entry of loadedCertEntries ) {
@@ -701,10 +647,10 @@ export class DcRouter {
}
}
if ( loadedCertEntries . length > 0 ) {
console . log ( ` [DcRouter] Populated certificate status for ${ loadedCertEntries . length } store-loaded domain(s) ` ) ;
logger . log ( 'info' , ` Populated certificate status for ${ loadedCertEntries . length } store-loaded domain(s) ` ) ;
}
console . log ( ` SmartProxy started with ${ routes . length } routes ` ) ;
logger . log ( 'info' , ` SmartProxy started with ${ routes . length } routes ` ) ;
}
}
@@ -907,7 +853,7 @@ export class DcRouter {
}
public async stop() {
console . log ( 'Stopping DcRouter services...' ) ;
logger . log ( 'info' , 'Stopping DcRouter services...' ) ;
await this . opsServer . stop ( ) ;
@@ -918,36 +864,36 @@ export class DcRouter {
this . cacheCleaner ? Promise . resolve ( this . cacheCleaner . stop ( ) ) : Promise . resolve ( ) ,
// Stop metrics manager if running
this . metricsManager ? this . metricsManager . stop ( ) . catch ( err = > console . error( 'Error stopping MetricsManager: ' , err ) ) : Promise . resolve ( ) ,
this . metricsManager ? this . metricsManager . stop ( ) . catch ( err = > logger . log ( ' error' , 'Error stopping MetricsManager' , { error : String ( err ) } ) ) : Promise . resolve ( ) ,
// Stop unified email server if running
this . emailServer ? this . emailServer . stop ( ) . catch ( err = > console . error( 'Error stopping email server: ' , err ) ) : Promise . resolve ( ) ,
this . emailServer ? this . emailServer . stop ( ) . catch ( err = > logger . log ( ' error' , 'Error stopping email server' , { error : String ( err ) } ) ) : Promise . resolve ( ) ,
// Stop SmartAcme if running
this . smartAcme ? this . smartAcme . stop ( ) . catch ( err = > console . error( 'Error stopping SmartAcme: ' , err ) ) : Promise . resolve ( ) ,
this . smartAcme ? this . smartAcme . stop ( ) . catch ( err = > logger . log ( ' error' , 'Error stopping SmartAcme' , { error : String ( err ) } ) ) : Promise . resolve ( ) ,
// Stop HTTP SmartProxy if running
this . smartProxy ? this . smartProxy . stop ( ) . catch ( err = > console . error( 'Error stopping SmartProxy: ' , err ) ) : Promise . resolve ( ) ,
this . smartProxy ? this . smartProxy . stop ( ) . catch ( err = > logger . log ( ' error' , 'Error stopping SmartProxy' , { error : String ( err ) } ) ) : Promise . resolve ( ) ,
// Stop DNS server if running
this . dnsServer ?
this . dnsServer . stop ( ) . catch ( err = > console . error( 'Error stopping DNS server: ' , err ) ) :
this . dnsServer . stop ( ) . catch ( err = > logger . log ( ' error' , 'Error stopping DNS server' , { error : String ( err ) } ) ) :
Promise . resolve ( ) ,
// Stop RADIUS server if running
this . radiusServer ?
this . radiusServer . stop ( ) . catch ( err = > console . error( 'Error stopping RADIUS server: ' , err ) ) :
this . radiusServer . stop ( ) . catch ( err = > logger . log ( ' error' , 'Error stopping RADIUS server' , { error : String ( err ) } ) ) :
Promise . resolve ( ) ,
// Stop Remote Ingress tunnel manager if running
this . tunnelManager ?
this . tunnelManager . stop ( ) . catch ( err = > console . error( 'Error stopping TunnelManager: ' , err ) ) :
this . tunnelManager . stop ( ) . catch ( err = > logger . log ( ' error' , 'Error stopping TunnelManager' , { error : String ( err ) } ) ) :
Promise . resolve ( )
] ) ;
// Stop cache database after other services (they may need it during shutdown)
if ( this . cacheDb ) {
await this . cacheDb . stop ( ) . catch ( err = > console . error( 'Error stopping CacheDb: ' , err ) ) ;
await this . cacheDb . stop ( ) . catch ( err = > logger . log ( ' error' , 'Error stopping CacheDb' , { error : String ( err ) } ) ) ;
}
// Clear backoff cache in cert scheduler
@@ -969,9 +915,9 @@ export class DcRouter {
this . remoteIngressManager = undefined ;
this . certificateStatusMap . clear ( ) ;
console . log ( 'All DcRouter services stopped' ) ;
logger . log ( 'info' , 'All DcRouter services stopped' ) ;
} catch ( error ) {
console . error( 'Error during DcRouter shutdown: ' , error ) ;
logger . log ( ' error' , 'Error during DcRouter shutdown' , { error : String ( error ) } ) ;
throw error ;
}
}
@@ -998,7 +944,7 @@ export class DcRouter {
// Start new SmartProxy with updated configuration (will include email routes if configured)
await this . setupSmartProxy ( ) ;
console . log ( 'SmartProxy configuration updated' ) ;
logger . log ( 'info' , 'SmartProxy configuration updated' ) ;
}
@@ -1091,7 +1037,7 @@ export class DcRouter {
// Start email handling with new configuration
await this . setupUnifiedEmailHandling ( ) ;
console . log ( 'Unified email configuration updated' ) ;
logger . log ( 'info' , 'Unified email configuration updated' ) ;
}
/**
@@ -1131,7 +1077,7 @@ export class DcRouter {
this . emailServer . updateEmailRoutes ( routes ) ;
}
console . log ( ` Email routes updated with ${ routes . length } routes ` ) ;
logger . log ( 'info' , ` Email routes updated with ${ routes . length } routes ` ) ;
}
/**