import * as plugins from './plugins.js'; import * as paths from './paths.js'; import { SmtpPortConfig, type ISmtpPortSettings } from './classes.smtp.portconfig.js'; // Certificate types are available via plugins.tsclass // Import the consolidated email config import type { IEmailConfig, IDomainRule } from './mail/routing/classes.email.config.js'; import { DomainRouter } from './mail/routing/classes.domain.router.js'; import { UnifiedEmailServer } from './mail/routing/classes.unified.email.server.js'; import { UnifiedDeliveryQueue, type IQueueOptions } from './mail/delivery/classes.delivery.queue.js'; import { MultiModeDeliverySystem, type IMultiModeDeliveryOptions } from './mail/delivery/classes.delivery.system.js'; import { UnifiedRateLimiter, type IHierarchicalRateLimits } from './mail/delivery/classes.unified.rate.limiter.js'; import { logger } from './logger.js'; export interface IDcRouterOptions { /** * Direct SmartProxy configuration - gives full control over HTTP/HTTPS and TCP/SNI traffic * This is the preferred way to configure HTTP/HTTPS and general TCP/SNI traffic */ smartProxyConfig?: plugins.smartproxy.ISmartProxyOptions; /** * Consolidated email configuration * This enables all email handling with pattern-based routing */ emailConfig?: IEmailConfig; /** TLS/certificate configuration */ tls?: { /** Contact email for ACME certificates */ contactEmail: string; /** Domain for main certificate */ domain?: string; /** Path to certificate file (if not using auto-provisioning) */ certPath?: string; /** Path to key file (if not using auto-provisioning) */ keyPath?: string; }; /** DNS server configuration */ dnsServerConfig?: plugins.smartdns.IDnsServerOptions; } /** * DcRouter can be run on ingress and egress to and from a datacenter site. */ /** * Context passed to HTTP routing rules */ /** * Context passed to port proxy (SmartProxy) routing rules */ export interface PortProxyRuleContext { proxy: plugins.smartproxy.SmartProxy; configs: plugins.smartproxy.IPortProxySettings['domainConfigs']; } export class DcRouter { public options: IDcRouterOptions; // Core services public smartProxy?: plugins.smartproxy.SmartProxy; public dnsServer?: plugins.smartdns.DnsServer; // Unified email components public domainRouter?: DomainRouter; public unifiedEmailServer?: UnifiedEmailServer; public deliveryQueue?: UnifiedDeliveryQueue; public deliverySystem?: MultiModeDeliverySystem; public rateLimiter?: UnifiedRateLimiter; // Environment access private qenv = new plugins.qenv.Qenv('./', '.nogit/'); constructor(optionsArg: IDcRouterOptions) { // Set defaults in options this.options = { ...optionsArg }; } public async start() { console.log('Starting DcRouter services...'); try { // Set up SmartProxy for HTTP/HTTPS and general TCP/SNI traffic if (this.options.smartProxyConfig) { await this.setupSmartProxy(); } // Set up unified email handling if configured if (this.options.emailConfig) { await this.setupUnifiedEmailHandling(); } // 3. Set up DNS server if configured if (this.options.dnsServerConfig) { this.dnsServer = new plugins.smartdns.DnsServer(this.options.dnsServerConfig); await this.dnsServer.start(); console.log('DNS server started'); } console.log('DcRouter started successfully'); } catch (error) { console.error('Error starting DcRouter:', error); // Try to clean up any services that may have started await this.stop(); throw error; } } /** * Set up SmartProxy with direct configuration */ private async setupSmartProxy(): Promise { if (!this.options.smartProxyConfig) { return; } console.log('Setting up SmartProxy with direct configuration'); // Create SmartProxy instance with full configuration this.smartProxy = new plugins.smartproxy.SmartProxy(this.options.smartProxyConfig); // Set up event listeners this.smartProxy.on('error', (err) => { console.error('SmartProxy error:', err); }); if (this.options.smartProxyConfig.acme) { this.smartProxy.on('certificate-issued', (event) => { console.log(`Certificate issued for ${event.domain}, expires ${event.expiryDate}`); }); this.smartProxy.on('certificate-renewed', (event) => { console.log(`Certificate renewed for ${event.domain}, expires ${event.expiryDate}`); }); } // Start SmartProxy await this.smartProxy.start(); console.log('SmartProxy started successfully'); } /** * Check if a domain matches a pattern (including wildcard support) * @param domain The domain to check * @param pattern The pattern to match against (e.g., "*.example.com") * @returns Whether the domain matches the pattern */ private isDomainMatch(domain: string, pattern: string): boolean { // Normalize inputs domain = domain.toLowerCase(); pattern = pattern.toLowerCase(); // Check for exact match if (domain === pattern) { return true; } // Check for wildcard match (*.example.com) if (pattern.startsWith('*.')) { const patternSuffix = pattern.slice(2); // Remove the "*." prefix // Check if domain ends with the pattern suffix and has at least one character before it return domain.endsWith(patternSuffix) && domain.length > patternSuffix.length; } // No match return false; } public async stop() { console.log('Stopping DcRouter services...'); try { // Stop all services in parallel for faster shutdown await Promise.all([ // Stop unified email components if running this.domainRouter ? this.stopUnifiedEmailComponents().catch(err => console.error('Error stopping unified email components:', err)) : Promise.resolve(), // Stop HTTP SmartProxy if running this.smartProxy ? this.smartProxy.stop().catch(err => console.error('Error stopping SmartProxy:', err)) : Promise.resolve(), // Stop DNS server if running this.dnsServer ? this.dnsServer.stop().catch(err => console.error('Error stopping DNS server:', err)) : Promise.resolve() ]); console.log('All DcRouter services stopped'); } catch (error) { console.error('Error during DcRouter shutdown:', error); throw error; } } /** * Update SmartProxy configuration * @param config New SmartProxy configuration */ public async updateSmartProxyConfig(config: plugins.smartproxy.ISmartProxyOptions): Promise { // Stop existing SmartProxy if running if (this.smartProxy) { await this.smartProxy.stop(); this.smartProxy = undefined; } // Update configuration this.options.smartProxyConfig = config; // Start new SmartProxy with updated configuration await this.setupSmartProxy(); console.log('SmartProxy configuration updated'); } /** * Set up unified email handling with pattern-based routing * This implements the consolidated emailConfig approach */ private async setupUnifiedEmailHandling(): Promise { logger.log('info', 'Setting up unified email handling with pattern-based routing'); if (!this.options.emailConfig) { throw new Error('Email configuration is required for unified email handling'); } try { // Create domain router for pattern matching this.domainRouter = new DomainRouter({ domainRules: this.options.emailConfig.domainRules, defaultMode: this.options.emailConfig.defaultMode, defaultServer: this.options.emailConfig.defaultServer, defaultPort: this.options.emailConfig.defaultPort, defaultTls: this.options.emailConfig.defaultTls }); // Initialize the rate limiter this.rateLimiter = new UnifiedRateLimiter({ global: { maxMessagesPerMinute: 100, maxRecipientsPerMessage: 100, maxConnectionsPerIP: 20, maxErrorsPerIP: 10, maxAuthFailuresPerIP: 5 } }); // Initialize the unified delivery queue const queueOptions: IQueueOptions = { storageType: this.options.emailConfig.queue?.storageType || 'memory', persistentPath: this.options.emailConfig.queue?.persistentPath, maxRetries: this.options.emailConfig.queue?.maxRetries, baseRetryDelay: this.options.emailConfig.queue?.baseRetryDelay, maxRetryDelay: this.options.emailConfig.queue?.maxRetryDelay }; this.deliveryQueue = new UnifiedDeliveryQueue(queueOptions); await this.deliveryQueue.initialize(); // Initialize the delivery system const deliveryOptions: IMultiModeDeliveryOptions = { globalRateLimit: 100, // Default to 100 emails per minute concurrentDeliveries: 10 }; this.deliverySystem = new MultiModeDeliverySystem(this.deliveryQueue, deliveryOptions); await this.deliverySystem.start(); // Initialize the unified email server this.unifiedEmailServer = new UnifiedEmailServer({ ports: this.options.emailConfig.ports, hostname: this.options.emailConfig.hostname, maxMessageSize: this.options.emailConfig.maxMessageSize, auth: this.options.emailConfig.auth, tls: this.options.emailConfig.tls, domainRules: this.options.emailConfig.domainRules, defaultMode: this.options.emailConfig.defaultMode, defaultServer: this.options.emailConfig.defaultServer, defaultPort: this.options.emailConfig.defaultPort, defaultTls: this.options.emailConfig.defaultTls }); // Set up event listeners this.unifiedEmailServer.on('error', (err) => { logger.log('error', `UnifiedEmailServer error: ${err.message}`); }); // Connect the unified email server with the delivery queue this.unifiedEmailServer.on('emailProcessed', (email, mode, rule) => { this.deliveryQueue!.enqueue(email, mode, rule).catch(err => { logger.log('error', `Failed to enqueue email: ${err.message}`); }); }); // Start the unified email server await this.unifiedEmailServer.start(); logger.log('info', `Unified email handling configured with ${this.options.emailConfig.domainRules.length} domain rules`); } catch (error) { logger.log('error', `Error setting up unified email handling: ${error.message}`); throw error; } } /** * Update the unified email configuration * @param config New email configuration */ public async updateEmailConfig(config: IEmailConfig): Promise { // Stop existing email components await this.stopUnifiedEmailComponents(); // Update configuration this.options.emailConfig = config; // Start email handling with new configuration await this.setupUnifiedEmailHandling(); console.log('Unified email configuration updated'); } /** * Stop all unified email components */ private async stopUnifiedEmailComponents(): Promise { try { // Stop all components in the correct order // 1. Stop the unified email server first if (this.unifiedEmailServer) { await this.unifiedEmailServer.stop(); logger.log('info', 'Unified email server stopped'); this.unifiedEmailServer = undefined; } // 2. Stop the delivery system if (this.deliverySystem) { await this.deliverySystem.stop(); logger.log('info', 'Delivery system stopped'); this.deliverySystem = undefined; } // 3. Stop the delivery queue if (this.deliveryQueue) { await this.deliveryQueue.shutdown(); logger.log('info', 'Delivery queue shut down'); this.deliveryQueue = undefined; } // 4. Stop the rate limiter if (this.rateLimiter) { this.rateLimiter.stop(); logger.log('info', 'Rate limiter stopped'); this.rateLimiter = undefined; } // 5. Clear the domain router this.domainRouter = undefined; logger.log('info', 'All unified email components stopped'); } catch (error) { logger.log('error', `Error stopping unified email components: ${error.message}`); throw error; } } /** * Update domain rules for email routing * @param rules New domain rules to apply */ public async updateDomainRules(rules: IDomainRule[]): Promise { // Validate that email config exists if (!this.options.emailConfig) { throw new Error('Email configuration is required before updating domain rules'); } // Update the configuration this.options.emailConfig.domainRules = rules; // Update the domain router if it exists if (this.domainRouter) { this.domainRouter.updateRules(rules); } // Update the unified email server if it exists if (this.unifiedEmailServer) { this.unifiedEmailServer.updateDomainRules(rules); } console.log(`Domain rules updated with ${rules.length} rules`); } /** * Get statistics from all components */ public getStats(): any { const stats: any = { unifiedEmailServer: this.unifiedEmailServer?.getStats(), deliveryQueue: this.deliveryQueue?.getStats(), deliverySystem: this.deliverySystem?.getStats(), rateLimiter: this.rateLimiter?.getStats() }; return stats; } } export default DcRouter;