import * as plugins from '../plugins.js'; import * as paths from '../paths.js'; import { SmtpPortConfig, type ISmtpPortSettings } from './classes.smtp.portconfig.js'; import { EmailDomainRouter, type IEmailDomainRoutingConfig } from './classes.email.domainrouter.js'; // Certificate types are available via plugins.tsclass // Import the consolidated email config import type { IEmailConfig } from './classes.email.config.js'; import { DomainRouter } from './classes.domain.router.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; // 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 { console.log('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 }); // TODO: Initialize the full unified email processing pipeline console.log(`Unified email handling configured with ${this.options.emailConfig.domainRules.length} domain rules`); } catch (error) { console.error('Error setting up unified email handling:', error); 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 { // TODO: Implement stopping all unified email components // Clear the domain router this.domainRouter = undefined; } } export default DcRouter;