This commit is contained in:
2025-05-27 14:06:22 +00:00
parent af408d38c9
commit 073c8378c7
10 changed files with 2927 additions and 746 deletions

View File

@ -6,11 +6,7 @@ import * as paths from './paths.js';
// Import the consolidated email config
import type { IEmailConfig, IDomainRule } from './mail/routing/classes.email.config.js';
import type { EmailProcessingMode } from './mail/delivery/interfaces.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';
// Import the email configuration helpers directly from mail/delivery
import { configureEmailStorage, configureEmailServer } from './mail/delivery/index.js';
@ -87,13 +83,7 @@ export class DcRouter {
// 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;
public emailServer?: UnifiedEmailServer;
// Environment access
@ -119,9 +109,9 @@ export class DcRouter {
await this.setupUnifiedEmailHandling();
// Apply custom email storage configuration if available
if (this.unifiedEmailServer && this.options.emailPortConfig?.receivedEmailsPath) {
if (this.emailServer && this.options.emailPortConfig?.receivedEmailsPath) {
logger.log('info', 'Applying custom email storage configuration');
configureEmailStorage(this.unifiedEmailServer, this.options);
configureEmailStorage(this.emailServer, this.options);
}
}
@ -402,8 +392,8 @@ export class DcRouter {
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 unified email server if running
this.emailServer ? this.emailServer.stop().catch(err => console.error('Error stopping email server:', err)) : Promise.resolve(),
// Stop HTTP SmartProxy if running
this.smartProxy ? this.smartProxy.stop().catch(err => console.error('Error stopping SmartProxy:', err)) : Promise.resolve(),
@ -485,78 +475,35 @@ export class DcRouter {
}
try {
// Create domain router for pattern matching
this.domainRouter = new DomainRouter({
// Create unified email server with all components
this.emailServer = new UnifiedEmailServer(this, {
ports: emailConfig.ports.map(port => portMapping[port] || port + 10000),
hostname: 'localhost', // Listen on localhost for SmartProxy forwarding
domains: emailConfig.domains || [],
domainRules: emailConfig.domainRules,
defaultMode: emailConfig.defaultMode,
defaultServer: emailConfig.defaultServer,
defaultPort: emailConfig.defaultPort,
defaultTls: 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: emailConfig.queue?.storageType || 'memory',
persistentPath: emailConfig.queue?.persistentPath,
maxRetries: emailConfig.queue?.maxRetries,
baseRetryDelay: emailConfig.queue?.baseRetryDelay,
maxRetryDelay: 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 with internal configuration
this.unifiedEmailServer = new UnifiedEmailServer({
ports: internalEmailConfig.ports,
hostname: internalEmailConfig.hostname,
defaultTls: emailConfig.defaultTls,
maxMessageSize: emailConfig.maxMessageSize,
auth: emailConfig.auth,
tls: emailConfig.tls,
domainRules: emailConfig.domainRules,
defaultMode: emailConfig.defaultMode,
defaultServer: emailConfig.defaultServer,
defaultPort: emailConfig.defaultPort,
defaultTls: emailConfig.defaultTls
dkim: emailConfig.dkim,
outbound: emailConfig.outbound,
rateLimits: emailConfig.rateLimits,
debug: emailConfig.debug
});
// Set up event listeners
this.unifiedEmailServer.on('error', (err) => {
this.emailServer.on('error', (err: Error) => {
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();
await this.emailServer.start();
logger.log('info', `Unified email handling configured with ${emailConfig.domainRules.length} domain rules on internal ports`);
logger.log('info', `Email server listening on ports: ${internalEmailConfig.ports.join(', ')}`);
logger.log('info', `Unified email handling configured with ${emailConfig.domainRules.length} domain rules`);
logger.log('info', `Email server listening on internal ports: ${this.emailServer['options'].ports.join(', ')}`);
} catch (error) {
logger.log('error', `Error setting up unified email handling: ${error.message}`);
throw error;
@ -585,39 +532,13 @@ export class DcRouter {
*/
private async stopUnifiedEmailComponents(): Promise<void> {
try {
// Stop all components in the correct order
// 1. Stop the unified email server first
if (this.unifiedEmailServer) {
await this.unifiedEmailServer.stop();
// Stop the unified email server which contains all components
if (this.emailServer) {
await this.emailServer.stop();
logger.log('info', 'Unified email server stopped');
this.unifiedEmailServer = undefined;
this.emailServer = 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}`);
@ -638,14 +559,9 @@ export class DcRouter {
// 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);
if (this.emailServer) {
this.emailServer.updateDomainRules(rules);
}
console.log(`Domain rules updated with ${rules.length} rules`);
@ -656,10 +572,7 @@ export class DcRouter {
*/
public getStats(): any {
const stats: any = {
unifiedEmailServer: this.unifiedEmailServer?.getStats(),
deliveryQueue: this.deliveryQueue?.getStats(),
deliverySystem: this.deliverySystem?.getStats(),
rateLimiter: this.rateLimiter?.getStats()
emailServer: this.emailServer?.getStats()
};
return stats;
@ -702,8 +615,8 @@ export class DcRouter {
// Use the dedicated helper to configure the email server
// Pass through the options specified by the implementation
if (this.unifiedEmailServer) {
configureEmailServer(this.unifiedEmailServer, {
if (this.emailServer) {
configureEmailServer(this.emailServer, {
ports: [config.internalPort], // Use whatever port the implementation specifies
hostname: config.host,
tls: config.secure ? {
@ -717,7 +630,7 @@ export class DcRouter {
}
// If email handling is already set up, restart it to apply changes
if (this.unifiedEmailServer) {
if (this.emailServer) {
logger.log('info', 'Restarting unified email handling to apply MTA configuration changes');
await this.stopUnifiedEmailComponents();
await this.setupUnifiedEmailHandling();