fix(mail options): simplify mail options
This commit is contained in:
BIN
readme.plan.md
BIN
readme.plan.md
Binary file not shown.
@@ -3,10 +3,9 @@ import * as paths from './paths.js';
|
|||||||
|
|
||||||
// Certificate types are available via plugins.tsclass
|
// Certificate types are available via plugins.tsclass
|
||||||
|
|
||||||
// Import the consolidated email config
|
// Import the email server and its configuration
|
||||||
import type { IEmailConfig, IDomainRule } from './mail/routing/classes.email.config.js';
|
import { UnifiedEmailServer, type IUnifiedEmailServerOptions } from './mail/routing/classes.unified.email.server.js';
|
||||||
import type { EmailProcessingMode } from './mail/delivery/interfaces.js';
|
import type { IDomainRule, EmailProcessingMode } from './mail/routing/classes.email.config.js';
|
||||||
import { UnifiedEmailServer } from './mail/routing/classes.unified.email.server.js';
|
|
||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
// Import the email configuration helpers directly from mail/delivery
|
// Import the email configuration helpers directly from mail/delivery
|
||||||
import { configureEmailStorage, configureEmailServer } from './mail/delivery/index.js';
|
import { configureEmailStorage, configureEmailServer } from './mail/delivery/index.js';
|
||||||
@@ -19,10 +18,10 @@ export interface IDcRouterOptions {
|
|||||||
smartProxyConfig?: plugins.smartproxy.ISmartProxyOptions;
|
smartProxyConfig?: plugins.smartproxy.ISmartProxyOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consolidated email configuration
|
* Email server configuration
|
||||||
* This enables all email handling with pattern-based routing
|
* This enables all email handling with pattern-based routing
|
||||||
*/
|
*/
|
||||||
emailConfig?: IEmailConfig;
|
emailConfig?: IUnifiedEmailServerOptions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom email port configuration
|
* Custom email port configuration
|
||||||
@@ -238,7 +237,7 @@ export class DcRouter {
|
|||||||
/**
|
/**
|
||||||
* Generate SmartProxy routes for email configuration
|
* Generate SmartProxy routes for email configuration
|
||||||
*/
|
*/
|
||||||
private generateEmailRoutes(emailConfig: IEmailConfig): plugins.smartproxy.IRouteConfig[] {
|
private generateEmailRoutes(emailConfig: IUnifiedEmailServerOptions): plugins.smartproxy.IRouteConfig[] {
|
||||||
const emailRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
const emailRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
||||||
|
|
||||||
// Get the custom port mapping if available, otherwise use defaults
|
// Get the custom port mapping if available, otherwise use defaults
|
||||||
@@ -442,20 +441,22 @@ export class DcRouter {
|
|||||||
throw new Error('Email configuration is required for unified email handling');
|
throw new Error('Email configuration is required for unified email handling');
|
||||||
}
|
}
|
||||||
|
|
||||||
const emailConfig = this.options.emailConfig;
|
// Apply port mapping if behind SmartProxy
|
||||||
const portMapping = this.options.emailPortConfig?.portMapping || {
|
const portMapping = this.options.emailPortConfig?.portMapping || {
|
||||||
25: 10025, // SMTP
|
25: 10025, // SMTP
|
||||||
587: 10587, // Submission
|
587: 10587, // Submission
|
||||||
465: 10465 // SMTPS
|
465: 10465 // SMTPS
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create unified email server with mapped internal ports
|
// Create config with mapped ports
|
||||||
this.emailServer = new UnifiedEmailServer(this, {
|
const emailConfig: IUnifiedEmailServerOptions = {
|
||||||
...emailConfig,
|
...this.options.emailConfig,
|
||||||
domains: emailConfig.domains || [], // Provide default empty array
|
ports: this.options.emailConfig.ports.map(port => portMapping[port] || port + 10000),
|
||||||
ports: emailConfig.ports.map(port => portMapping[port] || port + 10000),
|
|
||||||
hostname: 'localhost' // Listen on localhost for SmartProxy forwarding
|
hostname: 'localhost' // Listen on localhost for SmartProxy forwarding
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// Create unified email server
|
||||||
|
this.emailServer = new UnifiedEmailServer(this, emailConfig);
|
||||||
|
|
||||||
// Set up error handling
|
// Set up error handling
|
||||||
this.emailServer.on('error', (err: Error) => {
|
this.emailServer.on('error', (err: Error) => {
|
||||||
@@ -465,14 +466,14 @@ export class DcRouter {
|
|||||||
// Start the server
|
// Start the server
|
||||||
await this.emailServer.start();
|
await this.emailServer.start();
|
||||||
|
|
||||||
logger.log('info', `Email server started on ports: ${this.emailServer['options'].ports.join(', ')}`);
|
logger.log('info', `Email server started on ports: ${emailConfig.ports.join(', ')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the unified email configuration
|
* Update the unified email configuration
|
||||||
* @param config New email configuration
|
* @param config New email configuration
|
||||||
*/
|
*/
|
||||||
public async updateEmailConfig(config: IEmailConfig): Promise<void> {
|
public async updateEmailConfig(config: IUnifiedEmailServerOptions): Promise<void> {
|
||||||
// Stop existing email components
|
// Stop existing email components
|
||||||
await this.stopUnifiedEmailComponents();
|
await this.stopUnifiedEmailComponents();
|
||||||
|
|
||||||
@@ -598,7 +599,7 @@ export class DcRouter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-export types for convenience
|
// Re-export email server types for convenience
|
||||||
export type { IEmailConfig, IDomainRule, EmailProcessingMode };
|
export type { IUnifiedEmailServerOptions, IDomainRule, EmailProcessingMode };
|
||||||
|
|
||||||
export default DcRouter;
|
export default DcRouter;
|
||||||
|
@@ -1,5 +1,37 @@
|
|||||||
import type { IBaseConfig, ITlsConfig, IQueueConfig, IRateLimitConfig, IMonitoringConfig } from './base.config.js';
|
import type { IBaseConfig, ITlsConfig, IQueueConfig, IRateLimitConfig, IMonitoringConfig } from './base.config.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MIGRATION GUIDE:
|
||||||
|
* ================
|
||||||
|
* The IEmailConfig and IMtaConfig interfaces are deprecated.
|
||||||
|
* Please use IUnifiedEmailServerOptions from '../mail/routing/classes.unified.email.server.js' instead.
|
||||||
|
*
|
||||||
|
* Example migration:
|
||||||
|
*
|
||||||
|
* OLD:
|
||||||
|
* ```typescript
|
||||||
|
* const config: IEmailConfig = {
|
||||||
|
* ports: [25, 587],
|
||||||
|
* hostname: 'mail.example.com',
|
||||||
|
* domainRules: [...],
|
||||||
|
* defaultMode: 'forward'
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* NEW:
|
||||||
|
* ```typescript
|
||||||
|
* const config: IUnifiedEmailServerOptions = {
|
||||||
|
* ports: [25, 587],
|
||||||
|
* hostname: 'mail.example.com',
|
||||||
|
* domains: ['example.com'],
|
||||||
|
* domainRules: [...],
|
||||||
|
* defaultMode: 'forward'
|
||||||
|
* };
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The new interface consolidates all email configuration into a single, cleaner structure.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Email processing modes
|
* Email processing modes
|
||||||
*/
|
*/
|
||||||
@@ -127,6 +159,8 @@ export interface IDomainRule {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Email service configuration
|
* Email service configuration
|
||||||
|
* @deprecated Use IUnifiedEmailServerOptions from mail/routing/classes.unified.email.server.ts instead
|
||||||
|
* This interface is kept for backward compatibility only
|
||||||
*/
|
*/
|
||||||
export interface IEmailConfig extends IBaseConfig {
|
export interface IEmailConfig extends IBaseConfig {
|
||||||
/**
|
/**
|
||||||
@@ -262,6 +296,8 @@ export interface IEmailConfig extends IBaseConfig {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* MTA configuration
|
* MTA configuration
|
||||||
|
* @deprecated Use IUnifiedEmailServerOptions from mail/routing/classes.unified.email.server.ts instead
|
||||||
|
* This interface is kept for backward compatibility only
|
||||||
*/
|
*/
|
||||||
export interface IMtaConfig {
|
export interface IMtaConfig {
|
||||||
/**
|
/**
|
||||||
|
194
ts/config/email.port.mapping.ts
Normal file
194
ts/config/email.port.mapping.ts
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
/**
|
||||||
|
* Email Port Mapping Configuration
|
||||||
|
*
|
||||||
|
* Centralizes the logic for mapping external email ports to internal ports
|
||||||
|
* when running behind SmartProxy
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default email port mapping
|
||||||
|
* Maps external ports to internal ports for SmartProxy forwarding
|
||||||
|
*/
|
||||||
|
export const DEFAULT_EMAIL_PORT_MAPPING: Record<number, number> = {
|
||||||
|
25: 10025, // SMTP - Standard email delivery port
|
||||||
|
587: 10587, // Submission - Authenticated submission port
|
||||||
|
465: 10465, // SMTPS - Implicit TLS submission port
|
||||||
|
2525: 12525 // Alternative SMTP port (often used for testing)
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email port configuration
|
||||||
|
*/
|
||||||
|
export interface IEmailPortConfig {
|
||||||
|
/**
|
||||||
|
* External port number
|
||||||
|
*/
|
||||||
|
external: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal port number (where the email server actually listens)
|
||||||
|
*/
|
||||||
|
internal: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Port description
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TLS mode for this port
|
||||||
|
*/
|
||||||
|
tlsMode: 'none' | 'starttls' | 'implicit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether authentication is required on this port
|
||||||
|
*/
|
||||||
|
requireAuth?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard email port configurations
|
||||||
|
*/
|
||||||
|
export const EMAIL_PORT_CONFIGS: Record<number, Omit<IEmailPortConfig, 'internal'>> = {
|
||||||
|
25: {
|
||||||
|
external: 25,
|
||||||
|
description: 'SMTP - Standard email delivery',
|
||||||
|
tlsMode: 'starttls',
|
||||||
|
requireAuth: false
|
||||||
|
},
|
||||||
|
587: {
|
||||||
|
external: 587,
|
||||||
|
description: 'Submission - Authenticated email submission',
|
||||||
|
tlsMode: 'starttls',
|
||||||
|
requireAuth: true
|
||||||
|
},
|
||||||
|
465: {
|
||||||
|
external: 465,
|
||||||
|
description: 'SMTPS - Implicit TLS submission',
|
||||||
|
tlsMode: 'implicit',
|
||||||
|
requireAuth: true
|
||||||
|
},
|
||||||
|
2525: {
|
||||||
|
external: 2525,
|
||||||
|
description: 'Alternative SMTP - Often used for testing',
|
||||||
|
tlsMode: 'starttls',
|
||||||
|
requireAuth: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map external ports to internal ports
|
||||||
|
* @param externalPorts Array of external ports
|
||||||
|
* @param customMapping Optional custom port mapping
|
||||||
|
* @returns Array of internal ports
|
||||||
|
*/
|
||||||
|
export function mapEmailPorts(
|
||||||
|
externalPorts: number[],
|
||||||
|
customMapping?: Record<number, number>
|
||||||
|
): number[] {
|
||||||
|
const mapping = { ...DEFAULT_EMAIL_PORT_MAPPING, ...customMapping };
|
||||||
|
|
||||||
|
return externalPorts.map(port => {
|
||||||
|
// Use custom mapping if available, otherwise add 10000 to the port
|
||||||
|
return mapping[port] || port + 10000;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get full port configuration including internal port mapping
|
||||||
|
* @param externalPort External port number
|
||||||
|
* @param customMapping Optional custom port mapping
|
||||||
|
* @returns Full port configuration
|
||||||
|
*/
|
||||||
|
export function getEmailPortConfig(
|
||||||
|
externalPort: number,
|
||||||
|
customMapping?: Record<number, number>
|
||||||
|
): IEmailPortConfig | undefined {
|
||||||
|
const config = EMAIL_PORT_CONFIGS[externalPort];
|
||||||
|
if (!config) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapping = { ...DEFAULT_EMAIL_PORT_MAPPING, ...customMapping };
|
||||||
|
const internalPort = mapping[externalPort] || externalPort + 10000;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
internal: internalPort
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate SmartProxy routes for email ports
|
||||||
|
* @param externalPorts Array of external ports to handle
|
||||||
|
* @param hostname Hostname for the email server
|
||||||
|
* @param customMapping Optional custom port mapping
|
||||||
|
* @returns Array of SmartProxy route configurations
|
||||||
|
*/
|
||||||
|
export function generateEmailProxyRoutes(
|
||||||
|
externalPorts: number[],
|
||||||
|
_hostname: string,
|
||||||
|
customMapping?: Record<number, number>
|
||||||
|
): plugins.smartproxy.IRouteConfig[] {
|
||||||
|
const routes: plugins.smartproxy.IRouteConfig[] = [];
|
||||||
|
|
||||||
|
for (const externalPort of externalPorts) {
|
||||||
|
const config = getEmailPortConfig(externalPort, customMapping);
|
||||||
|
if (!config) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create route for this email port
|
||||||
|
const route: plugins.smartproxy.IRouteConfig = {
|
||||||
|
name: `email-${config.description.toLowerCase().replace(/\s+/g, '-')}`,
|
||||||
|
match: {
|
||||||
|
ports: [externalPort]
|
||||||
|
},
|
||||||
|
action: {
|
||||||
|
type: 'forward',
|
||||||
|
target: {
|
||||||
|
host: 'localhost',
|
||||||
|
port: config.internal
|
||||||
|
},
|
||||||
|
tls: {
|
||||||
|
// For SMTP/Submission ports, use passthrough to let email server handle STARTTLS
|
||||||
|
// For SMTPS (465), SmartProxy handles TLS termination
|
||||||
|
mode: config.tlsMode === 'implicit' ? 'terminate' : 'passthrough',
|
||||||
|
certificate: config.tlsMode === 'implicit' ? 'auto' : undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
routes.push(route);
|
||||||
|
}
|
||||||
|
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate email port configuration
|
||||||
|
* @param ports Array of ports to validate
|
||||||
|
* @throws Error if invalid ports are specified
|
||||||
|
*/
|
||||||
|
export function validateEmailPorts(ports: number[]): void {
|
||||||
|
const invalidPorts = ports.filter(port => {
|
||||||
|
// Check if port is a valid number
|
||||||
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn about non-standard ports
|
||||||
|
if (!EMAIL_PORT_CONFIGS[port]) {
|
||||||
|
console.warn(`Port ${port} is not a standard email port. Supported standard ports are: ${Object.keys(EMAIL_PORT_CONFIGS).join(', ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (invalidPorts.length > 0) {
|
||||||
|
throw new Error(`Invalid email ports specified: ${invalidPorts.join(', ')}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type imports to avoid circular dependencies
|
||||||
|
import type * as plugins from '../plugins.js';
|
Reference in New Issue
Block a user