fix(structure): Unify structure even further
This commit is contained in:
@ -438,76 +438,34 @@ export class DcRouter {
|
||||
* This implements the consolidated emailConfig approach
|
||||
*/
|
||||
private async setupUnifiedEmailHandling(): Promise<void> {
|
||||
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');
|
||||
}
|
||||
|
||||
const emailConfig = this.options.emailConfig;
|
||||
|
||||
// Map external ports to internal ports with support for custom port mapping
|
||||
const defaultPortMapping = {
|
||||
const portMapping = this.options.emailPortConfig?.portMapping || {
|
||||
25: 10025, // SMTP
|
||||
587: 10587, // Submission
|
||||
465: 10465 // SMTPS
|
||||
};
|
||||
|
||||
// Use custom port mapping if provided, otherwise fall back to defaults
|
||||
const portMapping = this.options.emailPortConfig?.portMapping || defaultPortMapping;
|
||||
|
||||
// Create internal email server configuration
|
||||
const internalEmailConfig: IEmailConfig = {
|
||||
// Create unified email server with mapped internal ports
|
||||
this.emailServer = new UnifiedEmailServer(this, {
|
||||
...emailConfig,
|
||||
domains: emailConfig.domains || [], // Provide default empty array
|
||||
ports: emailConfig.ports.map(port => portMapping[port] || port + 10000),
|
||||
hostname: 'localhost' // Listen on localhost for SmartProxy forwarding
|
||||
};
|
||||
});
|
||||
|
||||
// If custom MTA options are provided, merge them
|
||||
if (this.options.emailPortConfig?.portSettings) {
|
||||
// Will be used in MTA configuration
|
||||
logger.log('info', 'Custom port settings detected for email configuration');
|
||||
}
|
||||
// Set up error handling
|
||||
this.emailServer.on('error', (err: Error) => {
|
||||
logger.log('error', `UnifiedEmailServer error: ${err.message}`);
|
||||
});
|
||||
|
||||
// Configure custom email storage path if specified
|
||||
if (this.options.emailPortConfig?.receivedEmailsPath) {
|
||||
logger.log('info', `Custom email storage path configured: ${this.options.emailPortConfig.receivedEmailsPath}`);
|
||||
}
|
||||
// Start the server
|
||||
await this.emailServer.start();
|
||||
|
||||
try {
|
||||
// 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,
|
||||
maxMessageSize: emailConfig.maxMessageSize,
|
||||
auth: emailConfig.auth,
|
||||
tls: emailConfig.tls,
|
||||
dkim: emailConfig.dkim,
|
||||
outbound: emailConfig.outbound,
|
||||
rateLimits: emailConfig.rateLimits,
|
||||
debug: emailConfig.debug
|
||||
});
|
||||
|
||||
// Set up event listeners
|
||||
this.emailServer.on('error', (err: Error) => {
|
||||
logger.log('error', `UnifiedEmailServer error: ${err.message}`);
|
||||
});
|
||||
|
||||
// Start the unified email server
|
||||
await this.emailServer.start();
|
||||
|
||||
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;
|
||||
}
|
||||
logger.log('info', `Email server started on ports: ${this.emailServer['options'].ports.join(', ')}`);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,7 @@ export interface IAttachment {
|
||||
|
||||
export interface IEmailOptions {
|
||||
from: string;
|
||||
to: string | string[]; // Support multiple recipients
|
||||
to?: string | string[]; // Optional for templates
|
||||
cc?: string | string[]; // Optional CC recipients
|
||||
bcc?: string | string[]; // Optional BCC recipients
|
||||
subject: string;
|
||||
@ -68,16 +68,14 @@ export class Email {
|
||||
this.from = options.from;
|
||||
|
||||
// Handle to addresses (single or multiple)
|
||||
this.to = this.parseRecipients(options.to);
|
||||
this.to = options.to ? this.parseRecipients(options.to) : [];
|
||||
|
||||
// Handle optional cc and bcc
|
||||
this.cc = options.cc ? this.parseRecipients(options.cc) : [];
|
||||
this.bcc = options.bcc ? this.parseRecipients(options.bcc) : [];
|
||||
|
||||
// Validate that we have at least one recipient
|
||||
if (this.to.length === 0 && this.cc.length === 0 && this.bcc.length === 0) {
|
||||
throw new Error('Email must have at least one recipient');
|
||||
}
|
||||
// Note: Templates may be created without recipients
|
||||
// Recipients will be added when the email is actually sent
|
||||
|
||||
// Set subject with sanitization
|
||||
this.subject = this.sanitizeString(options.subject || '');
|
||||
|
@ -237,7 +237,7 @@ export class TemplateManager {
|
||||
subject: template.subject,
|
||||
text: template.bodyText || '',
|
||||
html: template.bodyHtml,
|
||||
to: '', // Will be set when sending
|
||||
// Note: 'to' is intentionally omitted for templates
|
||||
attachments,
|
||||
variables: context || {}
|
||||
};
|
||||
|
@ -22,7 +22,6 @@ import type {
|
||||
} from './classes.email.config.js';
|
||||
import { Email } from '../core/classes.email.js';
|
||||
import { BounceManager, BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
|
||||
import * as tls from 'node:tls';
|
||||
import { createSmtpServer } from '../delivery/smtpserver/index.js';
|
||||
import { createPooledSmtpClient } from '../delivery/smtpclient/create-client.js';
|
||||
import type { SmtpClient } from '../delivery/smtpclient/smtp-client.js';
|
||||
@ -167,17 +166,16 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
private domainRouter: DomainRouter;
|
||||
private servers: any[] = [];
|
||||
private stats: IServerStats;
|
||||
private processingTimes: number[] = [];
|
||||
|
||||
// Add components needed for sending and securing emails
|
||||
public dkimCreator: DKIMCreator;
|
||||
private ipReputationChecker: IPReputationChecker;
|
||||
private ipReputationChecker: IPReputationChecker; // TODO: Implement IP reputation checks in processEmailByMode
|
||||
private bounceManager: BounceManager;
|
||||
private ipWarmupManager: IPWarmupManager;
|
||||
private senderReputationMonitor: SenderReputationMonitor;
|
||||
public deliveryQueue: UnifiedDeliveryQueue;
|
||||
public deliverySystem: MultiModeDeliverySystem;
|
||||
private rateLimiter: UnifiedRateLimiter;
|
||||
private rateLimiter: UnifiedRateLimiter; // TODO: Implement rate limiting in SMTP server handlers
|
||||
private dkimKeys: Map<string, string> = new Map(); // domain -> private key
|
||||
private smtpClients: Map<string, SmtpClient> = new Map(); // host:port -> client
|
||||
|
||||
@ -265,7 +263,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
bounceHandler: {
|
||||
processSmtpFailure: this.processSmtpFailure.bind(this)
|
||||
},
|
||||
onDeliverySuccess: async (item, result) => {
|
||||
onDeliverySuccess: async (item, _result) => {
|
||||
// Record delivery success event for reputation monitoring
|
||||
const email = item.processingResult as Email;
|
||||
const senderDomain = email.from.split('@')[1];
|
||||
@ -479,13 +477,7 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
logger.log('info', 'Stopping UnifiedEmailServer');
|
||||
|
||||
try {
|
||||
// Stop all SMTP servers
|
||||
for (const server of this.servers) {
|
||||
// Nothing to do, servers will be garbage collected
|
||||
// The server.stop() method is not needed during this transition
|
||||
}
|
||||
|
||||
// Clear the servers array
|
||||
// Clear the servers array - servers will be garbage collected
|
||||
this.servers = [];
|
||||
|
||||
// Stop the delivery system
|
||||
@ -523,26 +515,6 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Update processing time statistics
|
||||
*/
|
||||
private updateProcessingTimeStats(): void {
|
||||
if (this.processingTimes.length === 0) return;
|
||||
|
||||
// Keep only the last 1000 processing times
|
||||
if (this.processingTimes.length > 1000) {
|
||||
this.processingTimes = this.processingTimes.slice(-1000);
|
||||
}
|
||||
|
||||
// Calculate stats
|
||||
const sum = this.processingTimes.reduce((acc, time) => acc + time, 0);
|
||||
const avg = sum / this.processingTimes.length;
|
||||
const max = Math.max(...this.processingTimes);
|
||||
const min = Math.min(...this.processingTimes);
|
||||
|
||||
this.stats.processingTime = { avg, max, min };
|
||||
}
|
||||
|
||||
/**
|
||||
* Process email based on the determined mode
|
||||
*/
|
||||
@ -825,7 +797,6 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
}
|
||||
|
||||
const selector = this.options.dkim?.selector || 'default';
|
||||
const keySize = this.options.dkim?.keySize || 2048;
|
||||
|
||||
for (const domain of this.options.domains) {
|
||||
try {
|
||||
@ -973,15 +944,6 @@ export class UnifiedEmailServer extends EventEmitter {
|
||||
return { ...this.stats };
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate email address format
|
||||
*/
|
||||
private isValidEmail(email: string): boolean {
|
||||
// Basic validation - a more comprehensive validation could be used
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return emailRegex.test(email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send an email through the delivery system
|
||||
* @param email The email to send
|
||||
|
Reference in New Issue
Block a user