Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
076aac27ce | |||
7f84405279 | |||
13ef31c13f | |||
5cf4c0f150 | |||
04b7552b34 |
30
changelog.md
30
changelog.md
@ -1,5 +1,35 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-05-08 - 2.7.0 - feat(dcrouter)
|
||||||
|
Implement unified email configuration with pattern‐based routing and consolidated email processing. Migrate SMTP forwarding and store‐and‐forward into a single, configuration-driven system that supports glob pattern matching in domain rules.
|
||||||
|
|
||||||
|
- Introduced IEmailConfig interface to consolidate MTA, forwarding, and processing settings.
|
||||||
|
- Added pattern-based domain routing with glob patterns (e.g., '*@example.com', '*@*.example.net').
|
||||||
|
- Reworked DcRouter integration to expose unified email handling and updated readme.plan.md and changelog.md accordingly.
|
||||||
|
- Removed deprecated SMTP forwarding components in favor of the consolidated approach.
|
||||||
|
|
||||||
|
## 2025-05-08 - 2.7.0 - feat(dcrouter)
|
||||||
|
Implement consolidated email configuration with pattern-based routing
|
||||||
|
|
||||||
|
- Added new pattern-based email routing with glob patterns (e.g., `*@task.vc`, `*@*.example.net`)
|
||||||
|
- Consolidated all email functionality (MTA, forwarding, processing) under a unified `emailConfig` interface
|
||||||
|
- Implemented domain router with pattern specificity calculation for most accurate matching
|
||||||
|
- Removed deprecated components (SMTP forwarding, Store-and-Forward) in favor of the unified approach
|
||||||
|
- Updated DcRouter tests to use the new consolidated email configuration pattern
|
||||||
|
- Enhanced inline documentation with detailed interface definitions and configuration examples
|
||||||
|
- Updated implementation plan with comprehensive component designs for the unified email system
|
||||||
|
|
||||||
|
## 2025-05-07 - 2.6.0 - feat(dcrouter)
|
||||||
|
Implement integrated DcRouter with comprehensive SmartProxy configuration, enhanced SMTP processing, and robust store‐and‐forward email routing
|
||||||
|
|
||||||
|
- Marked completion of tasks in readme.plan.md with [x] flags for SMTP server setup, email processing pipeline, queue management, and delivery system.
|
||||||
|
- Reworked DcRouter to use direct SmartProxy configuration, separating smtpConfig and smtpForwarding approaches.
|
||||||
|
- Added new components for delivery queue and delivery system with persistent storage support.
|
||||||
|
- Improved SMTP server implementation with TLS support, event handlers for connection, authentication, sender/recipient validation, and data processing.
|
||||||
|
- Refined domain-based routing and transformation logic in EmailProcessor with metrics and logging.
|
||||||
|
- Updated exported modules in dcrouter index to include SMTP store‐and‐forward components.
|
||||||
|
- Enhanced inline documentation and code comments for configuration interfaces and integration details.
|
||||||
|
|
||||||
## 2025-05-07 - 2.5.0 - feat(dcrouter)
|
## 2025-05-07 - 2.5.0 - feat(dcrouter)
|
||||||
Enhance DcRouter configuration and update documentation
|
Enhance DcRouter configuration and update documentation
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@serve.zone/platformservice",
|
"name": "@serve.zone/platformservice",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "2.5.0",
|
"version": "2.7.0",
|
||||||
"description": "A multifaceted platform service handling mail, SMS, letter delivery, and AI services.",
|
"description": "A multifaceted platform service handling mail, SMS, letter delivery, and AI services.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
"typings": "dist_ts/index.d.ts",
|
"typings": "dist_ts/index.d.ts",
|
||||||
|
1388
readme.plan.md
1388
readme.plan.md
File diff suppressed because it is too large
Load Diff
@ -2,9 +2,10 @@ import { tap, expect } from '@push.rocks/tapbundle';
|
|||||||
import * as plugins from '../ts/plugins.js';
|
import * as plugins from '../ts/plugins.js';
|
||||||
import {
|
import {
|
||||||
DcRouter,
|
DcRouter,
|
||||||
type IDcRouterOptions,
|
type IDcRouterOptions,
|
||||||
type ISmtpForwardingConfig,
|
type IEmailConfig,
|
||||||
type IDomainRoutingConfig
|
type EmailProcessingMode,
|
||||||
|
type IDomainRule
|
||||||
} from '../ts/dcrouter/index.js';
|
} from '../ts/dcrouter/index.js';
|
||||||
|
|
||||||
tap.test('DcRouter class - basic functionality', async () => {
|
tap.test('DcRouter class - basic functionality', async () => {
|
||||||
@ -21,71 +22,97 @@ tap.test('DcRouter class - basic functionality', async () => {
|
|||||||
expect(router.options.tls.contactEmail).toEqual('test@example.com');
|
expect(router.options.tls.contactEmail).toEqual('test@example.com');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('DcRouter class - HTTP routing configuration', async () => {
|
tap.test('DcRouter class - SmartProxy configuration', async () => {
|
||||||
// Create HTTP routing configuration
|
// Create SmartProxy configuration
|
||||||
const httpRoutes: IDomainRoutingConfig[] = [
|
const smartProxyConfig: plugins.smartproxy.ISmartProxyOptions = {
|
||||||
{
|
fromPort: 443,
|
||||||
domain: 'example.com',
|
toPort: 8080,
|
||||||
targetServer: '192.168.1.10',
|
targetIP: '10.0.0.10',
|
||||||
targetPort: 8080,
|
sniEnabled: true,
|
||||||
useTls: true
|
acme: {
|
||||||
|
port: 80,
|
||||||
|
enabled: true,
|
||||||
|
autoRenew: true,
|
||||||
|
useProduction: false,
|
||||||
|
renewThresholdDays: 30,
|
||||||
|
accountEmail: 'admin@example.com'
|
||||||
},
|
},
|
||||||
{
|
globalPortRanges: [
|
||||||
domain: '*.example.org',
|
{ from: 80, to: 80 },
|
||||||
targetServer: '192.168.1.20',
|
{ from: 443, to: 443 }
|
||||||
targetPort: 9000,
|
],
|
||||||
useTls: false
|
domainConfigs: [
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const options: IDcRouterOptions = {
|
|
||||||
httpDomainRoutes: httpRoutes,
|
|
||||||
tls: {
|
|
||||||
contactEmail: 'test@example.com'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const router = new DcRouter(options);
|
|
||||||
expect(router.options.httpDomainRoutes.length).toEqual(2);
|
|
||||||
expect(router.options.httpDomainRoutes[0].domain).toEqual('example.com');
|
|
||||||
expect(router.options.httpDomainRoutes[1].domain).toEqual('*.example.org');
|
|
||||||
});
|
|
||||||
|
|
||||||
tap.test('DcRouter class - SMTP forwarding configuration', async () => {
|
|
||||||
// Create SMTP forwarding configuration
|
|
||||||
const smtpForwarding: ISmtpForwardingConfig = {
|
|
||||||
enabled: true,
|
|
||||||
ports: [25, 587, 465],
|
|
||||||
defaultServer: 'mail.example.com',
|
|
||||||
defaultPort: 25,
|
|
||||||
useTls: true,
|
|
||||||
preserveSourceIp: true,
|
|
||||||
domainRoutes: [
|
|
||||||
{
|
{
|
||||||
domain: 'example.com',
|
domains: ['example.com', 'www.example.com'],
|
||||||
server: 'mail1.example.com',
|
allowedIPs: ['0.0.0.0/0'],
|
||||||
port: 25
|
targetIPs: ['10.0.0.10'],
|
||||||
},
|
portRanges: [
|
||||||
{
|
{ from: 80, to: 80 },
|
||||||
domain: 'example.org',
|
{ from: 443, to: 443 }
|
||||||
server: 'mail2.example.org',
|
]
|
||||||
port: 587
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const options: IDcRouterOptions = {
|
const options: IDcRouterOptions = {
|
||||||
smtpForwarding,
|
smartProxyConfig,
|
||||||
tls: {
|
tls: {
|
||||||
contactEmail: 'test@example.com'
|
contactEmail: 'test@example.com'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const router = new DcRouter(options);
|
const router = new DcRouter(options);
|
||||||
expect(router.options.smtpForwarding.enabled).toEqual(true);
|
expect(router.options.smartProxyConfig).toBeTruthy();
|
||||||
expect(router.options.smtpForwarding.ports.length).toEqual(3);
|
expect(router.options.smartProxyConfig.domainConfigs.length).toEqual(1);
|
||||||
expect(router.options.smtpForwarding.domainRoutes.length).toEqual(2);
|
expect(router.options.smartProxyConfig.domainConfigs[0].domains[0]).toEqual('example.com');
|
||||||
expect(router.options.smtpForwarding.domainRoutes[0].domain).toEqual('example.com');
|
});
|
||||||
|
|
||||||
|
tap.test('DcRouter class - Email configuration', async () => {
|
||||||
|
// Create consolidated email configuration
|
||||||
|
const emailConfig: IEmailConfig = {
|
||||||
|
ports: [25, 587, 465],
|
||||||
|
hostname: 'mail.example.com',
|
||||||
|
maxMessageSize: 50 * 1024 * 1024, // 50MB
|
||||||
|
|
||||||
|
defaultMode: 'forward' as EmailProcessingMode,
|
||||||
|
defaultServer: 'fallback-mail.example.com',
|
||||||
|
defaultPort: 25,
|
||||||
|
defaultTls: true,
|
||||||
|
|
||||||
|
domainRules: [
|
||||||
|
{
|
||||||
|
pattern: '*@example.com',
|
||||||
|
mode: 'forward' as EmailProcessingMode,
|
||||||
|
target: {
|
||||||
|
server: 'mail1.example.com',
|
||||||
|
port: 25,
|
||||||
|
useTls: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: '*@example.org',
|
||||||
|
mode: 'mta' as EmailProcessingMode,
|
||||||
|
mtaOptions: {
|
||||||
|
domain: 'example.org',
|
||||||
|
allowLocalDelivery: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const options: IDcRouterOptions = {
|
||||||
|
emailConfig,
|
||||||
|
tls: {
|
||||||
|
contactEmail: 'test@example.com'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const router = new DcRouter(options);
|
||||||
|
expect(router.options.emailConfig).toBeTruthy();
|
||||||
|
expect(router.options.emailConfig.ports.length).toEqual(3);
|
||||||
|
expect(router.options.emailConfig.domainRules.length).toEqual(2);
|
||||||
|
expect(router.options.emailConfig.domainRules[0].pattern).toEqual('*@example.com');
|
||||||
|
expect(router.options.emailConfig.domainRules[1].pattern).toEqual('*@example.org');
|
||||||
});
|
});
|
||||||
|
|
||||||
tap.test('DcRouter class - Domain pattern matching', async () => {
|
tap.test('DcRouter class - Domain pattern matching', async () => {
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/platformservice',
|
name: '@serve.zone/platformservice',
|
||||||
version: '2.5.0',
|
version: '2.7.0',
|
||||||
description: 'A multifaceted platform service handling mail, SMS, letter delivery, and AI services.'
|
description: 'A multifaceted platform service handling mail, SMS, letter delivery, and AI services.'
|
||||||
}
|
}
|
||||||
|
@ -3,62 +3,24 @@ import * as paths from '../paths.js';
|
|||||||
import { SmtpPortConfig, type ISmtpPortSettings } from './classes.smtp.portconfig.js';
|
import { SmtpPortConfig, type ISmtpPortSettings } from './classes.smtp.portconfig.js';
|
||||||
import { EmailDomainRouter, type IEmailDomainRoutingConfig } from './classes.email.domainrouter.js';
|
import { EmailDomainRouter, type IEmailDomainRoutingConfig } from './classes.email.domainrouter.js';
|
||||||
|
|
||||||
import { type IMtaConfig, MtaService } from '../mta/classes.mta.js';
|
|
||||||
|
|
||||||
// Certificate types are available via plugins.tsclass
|
// Certificate types are available via plugins.tsclass
|
||||||
|
|
||||||
/**
|
// Import the consolidated email config
|
||||||
* Configuration for SMTP forwarding functionality
|
import type { IEmailConfig } from './classes.email.config.js';
|
||||||
*/
|
import { DomainRouter } from './classes.domain.router.js';
|
||||||
export interface ISmtpForwardingConfig {
|
|
||||||
/** Whether SMTP forwarding is enabled */
|
|
||||||
enabled?: boolean;
|
|
||||||
/** SMTP ports to listen on */
|
|
||||||
ports?: number[];
|
|
||||||
/** Default SMTP server hostname */
|
|
||||||
defaultServer: string;
|
|
||||||
/** Default SMTP server port */
|
|
||||||
defaultPort?: number;
|
|
||||||
/** Whether to use TLS when connecting to the default server */
|
|
||||||
useTls?: boolean;
|
|
||||||
/** Preserve source IP address when forwarding */
|
|
||||||
preserveSourceIp?: boolean;
|
|
||||||
/** Domain-specific routing rules */
|
|
||||||
domainRoutes?: Array<{
|
|
||||||
domain: string;
|
|
||||||
server: string;
|
|
||||||
port?: number;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple domain-based routing configuration
|
|
||||||
*/
|
|
||||||
export interface IDomainRoutingConfig {
|
|
||||||
/** The domain name or pattern (e.g., example.com or *.example.com) */
|
|
||||||
domain: string;
|
|
||||||
/** Target server hostname or IP */
|
|
||||||
targetServer: string;
|
|
||||||
/** Target port */
|
|
||||||
targetPort: number;
|
|
||||||
/** Enable HTTPS/TLS for this route */
|
|
||||||
useTls?: boolean;
|
|
||||||
/** Allow incoming connections from these IP ranges (default: all) */
|
|
||||||
allowedIps?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IDcRouterOptions {
|
export interface IDcRouterOptions {
|
||||||
/** HTTP/HTTPS domain-based routing */
|
/**
|
||||||
httpDomainRoutes?: IDomainRoutingConfig[];
|
* 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;
|
||||||
|
|
||||||
/** SMTP forwarding configuration */
|
/**
|
||||||
smtpForwarding?: ISmtpForwardingConfig;
|
* Consolidated email configuration
|
||||||
|
* This enables all email handling with pattern-based routing
|
||||||
/** MTA service configuration (if not using SMTP forwarding) */
|
*/
|
||||||
mtaConfig?: IMtaConfig;
|
emailConfig?: IEmailConfig;
|
||||||
|
|
||||||
/** Existing MTA service instance to use (if not using SMTP forwarding) */
|
|
||||||
mtaServiceInstance?: MtaService;
|
|
||||||
|
|
||||||
/** TLS/certificate configuration */
|
/** TLS/certificate configuration */
|
||||||
tls?: {
|
tls?: {
|
||||||
@ -89,15 +51,17 @@ export interface PortProxyRuleContext {
|
|||||||
proxy: plugins.smartproxy.SmartProxy;
|
proxy: plugins.smartproxy.SmartProxy;
|
||||||
configs: plugins.smartproxy.IPortProxySettings['domainConfigs'];
|
configs: plugins.smartproxy.IPortProxySettings['domainConfigs'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DcRouter {
|
export class DcRouter {
|
||||||
public options: IDcRouterOptions;
|
public options: IDcRouterOptions;
|
||||||
|
|
||||||
// Core services
|
// Core services
|
||||||
public smartProxy?: plugins.smartproxy.SmartProxy;
|
public smartProxy?: plugins.smartproxy.SmartProxy;
|
||||||
public smtpProxy?: plugins.smartproxy.SmartProxy;
|
|
||||||
public mta?: MtaService;
|
|
||||||
public dnsServer?: plugins.smartdns.DnsServer;
|
public dnsServer?: plugins.smartdns.DnsServer;
|
||||||
|
|
||||||
|
// Unified email components
|
||||||
|
public domainRouter?: DomainRouter;
|
||||||
|
|
||||||
// Environment access
|
// Environment access
|
||||||
private qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
private qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
||||||
|
|
||||||
@ -112,14 +76,14 @@ export class DcRouter {
|
|||||||
console.log('Starting DcRouter services...');
|
console.log('Starting DcRouter services...');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Set up HTTP/HTTPS traffic handling with SmartProxy
|
// Set up SmartProxy for HTTP/HTTPS and general TCP/SNI traffic
|
||||||
await this.setupHttpProxy();
|
if (this.options.smartProxyConfig) {
|
||||||
|
await this.setupSmartProxy();
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Set up MTA or SMTP forwarding
|
// Set up unified email handling if configured
|
||||||
if (this.options.smtpForwarding?.enabled) {
|
if (this.options.emailConfig) {
|
||||||
await this.setupSmtpForwarding();
|
await this.setupUnifiedEmailHandling();
|
||||||
} else {
|
|
||||||
await this.setupMtaService();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Set up DNS server if configured
|
// 3. Set up DNS server if configured
|
||||||
@ -139,119 +103,40 @@ export class DcRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set up SmartProxy for HTTP/HTTPS traffic
|
* Set up SmartProxy with direct configuration
|
||||||
*/
|
*/
|
||||||
private async setupHttpProxy() {
|
private async setupSmartProxy(): Promise<void> {
|
||||||
if (!this.options.httpDomainRoutes || this.options.httpDomainRoutes.length === 0) {
|
if (!this.options.smartProxyConfig) {
|
||||||
console.log('No HTTP domain routes configured, skipping HTTP proxy setup');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Setting up SmartProxy for HTTP/HTTPS traffic');
|
console.log('Setting up SmartProxy with direct configuration');
|
||||||
|
|
||||||
// Prepare SmartProxy configuration
|
// Create SmartProxy instance with full configuration
|
||||||
const smartProxyConfig: plugins.smartproxy.ISmartProxyOptions = {
|
this.smartProxy = new plugins.smartproxy.SmartProxy(this.options.smartProxyConfig);
|
||||||
fromPort: 443,
|
|
||||||
toPort: this.options.httpDomainRoutes[0].targetPort,
|
|
||||||
targetIP: this.options.httpDomainRoutes[0].targetServer,
|
|
||||||
sniEnabled: true,
|
|
||||||
acme: {
|
|
||||||
port: 80,
|
|
||||||
enabled: true,
|
|
||||||
autoRenew: true,
|
|
||||||
useProduction: true,
|
|
||||||
renewThresholdDays: 30,
|
|
||||||
accountEmail: this.options.tls?.contactEmail || 'admin@example.com' // ACME requires an email
|
|
||||||
},
|
|
||||||
globalPortRanges: [{ from: 443, to: 443 }],
|
|
||||||
domainConfigs: []
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create domain configs from the HTTP routes
|
// Set up event listeners
|
||||||
smartProxyConfig.domainConfigs = this.options.httpDomainRoutes.map(route => ({
|
this.smartProxy.on('error', (err) => {
|
||||||
domains: [route.domain],
|
console.error('SmartProxy error:', err);
|
||||||
targetIPs: [route.targetServer],
|
|
||||||
allowedIPs: route.allowedIps || ['0.0.0.0/0'],
|
|
||||||
// Skip certificate management for wildcard domains as it's not supported by HTTP-01 challenges
|
|
||||||
certificateManagement: !route.domain.includes('*')
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Create and start the SmartProxy instance
|
|
||||||
this.smartProxy = new plugins.smartproxy.SmartProxy(smartProxyConfig);
|
|
||||||
|
|
||||||
// Listen for certificate events
|
|
||||||
this.smartProxy.on('certificate-issued', event => {
|
|
||||||
console.log(`Certificate issued for ${event.domain}, expires ${event.expiryDate}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.smartProxy.on('certificate-renewed', event => {
|
if (this.options.smartProxyConfig.acme) {
|
||||||
console.log(`Certificate renewed for ${event.domain}, expires ${event.expiryDate}`);
|
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();
|
await this.smartProxy.start();
|
||||||
|
|
||||||
console.log(`HTTP/HTTPS proxy configured with ${smartProxyConfig.domainConfigs.length} domain routes`);
|
console.log('SmartProxy started successfully');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up the MTA service
|
|
||||||
*/
|
|
||||||
private async setupMtaService() {
|
|
||||||
// Use existing MTA service if provided
|
|
||||||
if (this.options.mtaServiceInstance) {
|
|
||||||
this.mta = this.options.mtaServiceInstance;
|
|
||||||
console.log('Using provided MTA service instance');
|
|
||||||
} else if (this.options.mtaConfig) {
|
|
||||||
// Create new MTA service with the provided configuration
|
|
||||||
this.mta = new MtaService(undefined, this.options.mtaConfig);
|
|
||||||
console.log('Created new MTA service instance');
|
|
||||||
|
|
||||||
// Start the MTA service
|
|
||||||
await this.mta.start();
|
|
||||||
console.log('MTA service started');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set up SMTP forwarding with SmartProxy
|
|
||||||
*/
|
|
||||||
private async setupSmtpForwarding() {
|
|
||||||
if (!this.options.smtpForwarding) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const forwarding = this.options.smtpForwarding;
|
|
||||||
console.log('Setting up SMTP forwarding');
|
|
||||||
|
|
||||||
// Determine which ports to listen on
|
|
||||||
const smtpPorts = forwarding.ports || [25, 587, 465];
|
|
||||||
|
|
||||||
// Create SmartProxy instance for SMTP forwarding
|
|
||||||
this.smtpProxy = new plugins.smartproxy.SmartProxy({
|
|
||||||
// Listen on the first SMTP port
|
|
||||||
fromPort: smtpPorts[0],
|
|
||||||
// Forward to the default server
|
|
||||||
toPort: forwarding.defaultPort || 25,
|
|
||||||
targetIP: forwarding.defaultServer,
|
|
||||||
// Enable SNI if port 465 is included (implicit TLS)
|
|
||||||
sniEnabled: smtpPorts.includes(465),
|
|
||||||
// Preserve source IP if requested
|
|
||||||
preserveSourceIP: forwarding.preserveSourceIp || false,
|
|
||||||
// Create domain configs for SMTP routing
|
|
||||||
domainConfigs: forwarding.domainRoutes?.map(route => ({
|
|
||||||
domains: [route.domain],
|
|
||||||
allowedIPs: ['0.0.0.0/0'], // Allow from anywhere by default
|
|
||||||
targetIPs: [route.server]
|
|
||||||
})) || [],
|
|
||||||
// Include all SMTP ports in the global port ranges
|
|
||||||
globalPortRanges: smtpPorts.map(port => ({ from: port, to: port }))
|
|
||||||
});
|
|
||||||
|
|
||||||
// Start the SMTP proxy
|
|
||||||
await this.smtpProxy.start();
|
|
||||||
|
|
||||||
console.log(`SMTP forwarding configured on ports ${smtpPorts.join(', ')}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a domain matches a pattern (including wildcard support)
|
* Check if a domain matches a pattern (including wildcard support)
|
||||||
@ -287,16 +172,11 @@ export class DcRouter {
|
|||||||
try {
|
try {
|
||||||
// Stop all services in parallel for faster shutdown
|
// Stop all services in parallel for faster shutdown
|
||||||
await Promise.all([
|
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
|
// Stop HTTP SmartProxy if running
|
||||||
this.smartProxy ? this.smartProxy.stop().catch(err => console.error('Error stopping HTTP SmartProxy:', err)) : Promise.resolve(),
|
this.smartProxy ? this.smartProxy.stop().catch(err => console.error('Error stopping SmartProxy:', err)) : Promise.resolve(),
|
||||||
|
|
||||||
// Stop SMTP SmartProxy if running
|
|
||||||
this.smtpProxy ? this.smtpProxy.stop().catch(err => console.error('Error stopping SMTP SmartProxy:', err)) : Promise.resolve(),
|
|
||||||
|
|
||||||
// Stop MTA service if it's our own (not an external instance)
|
|
||||||
(this.mta && !this.options.mtaServiceInstance) ?
|
|
||||||
this.mta.stop().catch(err => console.error('Error stopping MTA service:', err)) :
|
|
||||||
Promise.resolve(),
|
|
||||||
|
|
||||||
// Stop DNS server if running
|
// Stop DNS server if running
|
||||||
this.dnsServer ?
|
this.dnsServer ?
|
||||||
@ -312,46 +192,84 @@ export class DcRouter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update HTTP domain routes
|
* Update SmartProxy configuration
|
||||||
* @param routes New HTTP domain routes
|
* @param config New SmartProxy configuration
|
||||||
*/
|
*/
|
||||||
public async updateHttpRoutes(routes: IDomainRoutingConfig[]): Promise<void> {
|
public async updateSmartProxyConfig(config: plugins.smartproxy.ISmartProxyOptions): Promise<void> {
|
||||||
this.options.httpDomainRoutes = routes;
|
// Stop existing SmartProxy if running
|
||||||
|
|
||||||
// If SmartProxy is already running, we need to restart it with the new configuration
|
|
||||||
if (this.smartProxy) {
|
if (this.smartProxy) {
|
||||||
// Stop the existing SmartProxy
|
|
||||||
await this.smartProxy.stop();
|
await this.smartProxy.stop();
|
||||||
this.smartProxy = undefined;
|
this.smartProxy = undefined;
|
||||||
|
|
||||||
// Start a new SmartProxy with the updated configuration
|
|
||||||
await this.setupHttpProxy();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`Updated HTTP routes with ${routes.length} domains`);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update SMTP forwarding configuration
|
|
||||||
* @param config New SMTP forwarding configuration
|
|
||||||
*/
|
|
||||||
public async updateSmtpForwarding(config: ISmtpForwardingConfig): Promise<void> {
|
|
||||||
// Stop existing SMTP proxy if running
|
|
||||||
if (this.smtpProxy) {
|
|
||||||
await this.smtpProxy.stop();
|
|
||||||
this.smtpProxy = undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update configuration
|
// Update configuration
|
||||||
this.options.smtpForwarding = config;
|
this.options.smartProxyConfig = config;
|
||||||
|
|
||||||
// Restart SMTP forwarding if enabled
|
// Start new SmartProxy with updated configuration
|
||||||
if (config.enabled) {
|
await this.setupSmartProxy();
|
||||||
await this.setupSmtpForwarding();
|
|
||||||
|
console.log('SmartProxy configuration updated');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up unified email handling with pattern-based routing
|
||||||
|
* This implements the consolidated emailConfig approach
|
||||||
|
*/
|
||||||
|
private async setupUnifiedEmailHandling(): Promise<void> {
|
||||||
|
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');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('SMTP forwarding configuration updated');
|
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<void> {
|
||||||
|
// 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<void> {
|
||||||
|
// TODO: Implement stopping all unified email components
|
||||||
|
|
||||||
|
// Clear the domain router
|
||||||
|
this.domainRouter = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DcRouter;
|
export default DcRouter;
|
||||||
|
351
ts/dcrouter/classes.domain.router.ts
Normal file
351
ts/dcrouter/classes.domain.router.ts
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
import * as plugins from '../plugins.js';
|
||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
import { type IDomainRule, type EmailProcessingMode } from './classes.email.config.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the domain-based router
|
||||||
|
*/
|
||||||
|
export interface IDomainRouterOptions {
|
||||||
|
// Domain rules with glob pattern matching
|
||||||
|
domainRules: IDomainRule[];
|
||||||
|
|
||||||
|
// Default handling for unmatched domains
|
||||||
|
defaultMode: EmailProcessingMode;
|
||||||
|
defaultServer?: string;
|
||||||
|
defaultPort?: number;
|
||||||
|
defaultTls?: boolean;
|
||||||
|
|
||||||
|
// Pattern matching options
|
||||||
|
caseSensitive?: boolean;
|
||||||
|
priorityOrder?: 'most-specific' | 'first-match';
|
||||||
|
|
||||||
|
// Cache settings for pattern matching
|
||||||
|
enableCache?: boolean;
|
||||||
|
cacheSize?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of a pattern match operation
|
||||||
|
*/
|
||||||
|
export interface IPatternMatchResult {
|
||||||
|
rule: IDomainRule;
|
||||||
|
exactMatch: boolean;
|
||||||
|
wildcardMatch: boolean;
|
||||||
|
specificity: number; // Higher is more specific
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pattern matching and routing class for email domains
|
||||||
|
*/
|
||||||
|
export class DomainRouter extends EventEmitter {
|
||||||
|
private options: IDomainRouterOptions;
|
||||||
|
private patternCache: Map<string, IDomainRule | null> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new domain router
|
||||||
|
* @param options Router options
|
||||||
|
*/
|
||||||
|
constructor(options: IDomainRouterOptions) {
|
||||||
|
super();
|
||||||
|
this.options = {
|
||||||
|
// Default options
|
||||||
|
caseSensitive: false,
|
||||||
|
priorityOrder: 'most-specific',
|
||||||
|
enableCache: true,
|
||||||
|
cacheSize: 1000,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match an email address against defined rules
|
||||||
|
* @param email Email address to match
|
||||||
|
* @returns The matching rule or null if no match
|
||||||
|
*/
|
||||||
|
public matchRule(email: string): IDomainRule | null {
|
||||||
|
// Check cache first if enabled
|
||||||
|
if (this.options.enableCache && this.patternCache.has(email)) {
|
||||||
|
return this.patternCache.get(email) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize email if case-insensitive
|
||||||
|
const normalizedEmail = this.options.caseSensitive ? email : email.toLowerCase();
|
||||||
|
|
||||||
|
// Get all matching rules
|
||||||
|
const matches = this.getAllMatchingRules(normalizedEmail);
|
||||||
|
|
||||||
|
if (matches.length === 0) {
|
||||||
|
// Cache the result (null) if caching is enabled
|
||||||
|
if (this.options.enableCache) {
|
||||||
|
this.addToCache(email, null);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by specificity or order
|
||||||
|
let matchedRule: IDomainRule;
|
||||||
|
|
||||||
|
if (this.options.priorityOrder === 'most-specific') {
|
||||||
|
// Sort by specificity (most specific first)
|
||||||
|
const sortedMatches = matches.sort((a, b) => {
|
||||||
|
const aSpecificity = this.calculateSpecificity(a.pattern);
|
||||||
|
const bSpecificity = this.calculateSpecificity(b.pattern);
|
||||||
|
return bSpecificity - aSpecificity;
|
||||||
|
});
|
||||||
|
|
||||||
|
matchedRule = sortedMatches[0];
|
||||||
|
} else {
|
||||||
|
// First match in the list
|
||||||
|
matchedRule = matches[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the result if caching is enabled
|
||||||
|
if (this.options.enableCache) {
|
||||||
|
this.addToCache(email, matchedRule);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchedRule;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate pattern specificity
|
||||||
|
* Higher is more specific
|
||||||
|
* @param pattern Pattern to calculate specificity for
|
||||||
|
*/
|
||||||
|
private calculateSpecificity(pattern: string): number {
|
||||||
|
let specificity = 0;
|
||||||
|
|
||||||
|
// Exact match is most specific
|
||||||
|
if (!pattern.includes('*')) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count characters that aren't wildcards
|
||||||
|
specificity += pattern.replace(/\*/g, '').length;
|
||||||
|
|
||||||
|
// Position of wildcards affects specificity
|
||||||
|
if (pattern.startsWith('*@')) {
|
||||||
|
// Wildcard in local part
|
||||||
|
specificity += 10;
|
||||||
|
} else if (pattern.includes('@*')) {
|
||||||
|
// Wildcard in domain part
|
||||||
|
specificity += 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
return specificity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if email matches a specific pattern
|
||||||
|
* @param email Email address to check
|
||||||
|
* @param pattern Pattern to check against
|
||||||
|
* @returns True if matching, false otherwise
|
||||||
|
*/
|
||||||
|
public matchesPattern(email: string, pattern: string): boolean {
|
||||||
|
// Normalize if case-insensitive
|
||||||
|
const normalizedEmail = this.options.caseSensitive ? email : email.toLowerCase();
|
||||||
|
const normalizedPattern = this.options.caseSensitive ? pattern : pattern.toLowerCase();
|
||||||
|
|
||||||
|
// Exact match
|
||||||
|
if (normalizedEmail === normalizedPattern) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert glob pattern to regex
|
||||||
|
const regexPattern = this.globToRegExp(normalizedPattern);
|
||||||
|
return regexPattern.test(normalizedEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a glob pattern to a regular expression
|
||||||
|
* @param pattern Glob pattern
|
||||||
|
* @returns Regular expression
|
||||||
|
*/
|
||||||
|
private globToRegExp(pattern: string): RegExp {
|
||||||
|
// Escape special regex characters except * and ?
|
||||||
|
let regexString = pattern
|
||||||
|
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
||||||
|
.replace(/\*/g, '.*')
|
||||||
|
.replace(/\?/g, '.');
|
||||||
|
|
||||||
|
return new RegExp(`^${regexString}$`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all rules that match an email address
|
||||||
|
* @param email Email address to match
|
||||||
|
* @returns Array of matching rules
|
||||||
|
*/
|
||||||
|
public getAllMatchingRules(email: string): IDomainRule[] {
|
||||||
|
return this.options.domainRules.filter(rule => this.matchesPattern(email, rule.pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new routing rule
|
||||||
|
* @param rule Domain rule to add
|
||||||
|
*/
|
||||||
|
public addRule(rule: IDomainRule): void {
|
||||||
|
// Validate the rule
|
||||||
|
this.validateRule(rule);
|
||||||
|
|
||||||
|
// Add the rule
|
||||||
|
this.options.domainRules.push(rule);
|
||||||
|
|
||||||
|
// Clear cache since rules have changed
|
||||||
|
this.clearCache();
|
||||||
|
|
||||||
|
// Emit event
|
||||||
|
this.emit('ruleAdded', rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a domain rule
|
||||||
|
* @param rule Rule to validate
|
||||||
|
*/
|
||||||
|
private validateRule(rule: IDomainRule): void {
|
||||||
|
// Pattern is required
|
||||||
|
if (!rule.pattern) {
|
||||||
|
throw new Error('Domain rule pattern is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode is required
|
||||||
|
if (!rule.mode) {
|
||||||
|
throw new Error('Domain rule mode is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward mode requires target
|
||||||
|
if (rule.mode === 'forward' && !rule.target) {
|
||||||
|
throw new Error('Forward mode requires target configuration');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward mode target requires server
|
||||||
|
if (rule.mode === 'forward' && rule.target && !rule.target.server) {
|
||||||
|
throw new Error('Forward mode target requires server');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update an existing rule
|
||||||
|
* @param pattern Pattern to update
|
||||||
|
* @param updates Updates to apply
|
||||||
|
* @returns True if rule was found and updated, false otherwise
|
||||||
|
*/
|
||||||
|
public updateRule(pattern: string, updates: Partial<IDomainRule>): boolean {
|
||||||
|
const ruleIndex = this.options.domainRules.findIndex(r => r.pattern === pattern);
|
||||||
|
|
||||||
|
if (ruleIndex === -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current rule
|
||||||
|
const currentRule = this.options.domainRules[ruleIndex];
|
||||||
|
|
||||||
|
// Create updated rule
|
||||||
|
const updatedRule: IDomainRule = {
|
||||||
|
...currentRule,
|
||||||
|
...updates
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate the updated rule
|
||||||
|
this.validateRule(updatedRule);
|
||||||
|
|
||||||
|
// Update the rule
|
||||||
|
this.options.domainRules[ruleIndex] = updatedRule;
|
||||||
|
|
||||||
|
// Clear cache since rules have changed
|
||||||
|
this.clearCache();
|
||||||
|
|
||||||
|
// Emit event
|
||||||
|
this.emit('ruleUpdated', updatedRule);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a rule
|
||||||
|
* @param pattern Pattern to remove
|
||||||
|
* @returns True if rule was found and removed, false otherwise
|
||||||
|
*/
|
||||||
|
public removeRule(pattern: string): boolean {
|
||||||
|
const initialLength = this.options.domainRules.length;
|
||||||
|
this.options.domainRules = this.options.domainRules.filter(r => r.pattern !== pattern);
|
||||||
|
|
||||||
|
const removed = initialLength > this.options.domainRules.length;
|
||||||
|
|
||||||
|
if (removed) {
|
||||||
|
// Clear cache since rules have changed
|
||||||
|
this.clearCache();
|
||||||
|
|
||||||
|
// Emit event
|
||||||
|
this.emit('ruleRemoved', pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get rule by pattern
|
||||||
|
* @param pattern Pattern to find
|
||||||
|
* @returns Rule with matching pattern or null if not found
|
||||||
|
*/
|
||||||
|
public getRule(pattern: string): IDomainRule | null {
|
||||||
|
return this.options.domainRules.find(r => r.pattern === pattern) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all rules
|
||||||
|
* @returns Array of all domain rules
|
||||||
|
*/
|
||||||
|
public getRules(): IDomainRule[] {
|
||||||
|
return [...this.options.domainRules];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update options
|
||||||
|
* @param options New options
|
||||||
|
*/
|
||||||
|
public updateOptions(options: Partial<IDomainRouterOptions>): void {
|
||||||
|
this.options = {
|
||||||
|
...this.options,
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
|
||||||
|
// Clear cache if cache settings changed
|
||||||
|
if ('enableCache' in options || 'cacheSize' in options) {
|
||||||
|
this.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit event
|
||||||
|
this.emit('optionsUpdated', this.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an item to the pattern cache
|
||||||
|
* @param email Email address
|
||||||
|
* @param rule Matching rule or null
|
||||||
|
*/
|
||||||
|
private addToCache(email: string, rule: IDomainRule | null): void {
|
||||||
|
// If cache is disabled, do nothing
|
||||||
|
if (!this.options.enableCache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to cache
|
||||||
|
this.patternCache.set(email, rule);
|
||||||
|
|
||||||
|
// Check if cache size exceeds limit
|
||||||
|
if (this.patternCache.size > (this.options.cacheSize || 1000)) {
|
||||||
|
// Remove oldest entry (first in the Map)
|
||||||
|
const firstKey = this.patternCache.keys().next().value;
|
||||||
|
this.patternCache.delete(firstKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear pattern matching cache
|
||||||
|
*/
|
||||||
|
public clearCache(): void {
|
||||||
|
this.patternCache.clear();
|
||||||
|
this.emit('cacheCleared');
|
||||||
|
}
|
||||||
|
}
|
129
ts/dcrouter/classes.email.config.ts
Normal file
129
ts/dcrouter/classes.email.config.ts
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
import * as plugins from '../plugins.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email processing modes
|
||||||
|
*/
|
||||||
|
export type EmailProcessingMode = 'forward' | 'mta' | 'process';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consolidated email configuration interface
|
||||||
|
*/
|
||||||
|
export interface IEmailConfig {
|
||||||
|
// Email server settings
|
||||||
|
ports: number[];
|
||||||
|
hostname: string;
|
||||||
|
maxMessageSize?: number;
|
||||||
|
|
||||||
|
// TLS configuration for email server
|
||||||
|
tls?: {
|
||||||
|
certPath?: string;
|
||||||
|
keyPath?: string;
|
||||||
|
caPath?: string;
|
||||||
|
minVersion?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Authentication for inbound connections
|
||||||
|
auth?: {
|
||||||
|
required?: boolean;
|
||||||
|
methods?: ('PLAIN' | 'LOGIN' | 'OAUTH2')[];
|
||||||
|
users?: Array<{username: string, password: string}>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default routing for unmatched domains
|
||||||
|
defaultMode: EmailProcessingMode;
|
||||||
|
defaultServer?: string;
|
||||||
|
defaultPort?: number;
|
||||||
|
defaultTls?: boolean;
|
||||||
|
|
||||||
|
// Domain rules with glob pattern support
|
||||||
|
domainRules: IDomainRule[];
|
||||||
|
|
||||||
|
// Queue configuration for all email processing
|
||||||
|
queue?: {
|
||||||
|
storageType?: 'memory' | 'disk';
|
||||||
|
persistentPath?: string;
|
||||||
|
maxRetries?: number;
|
||||||
|
baseRetryDelay?: number;
|
||||||
|
maxRetryDelay?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Advanced MTA settings
|
||||||
|
mtaGlobalOptions?: IMtaOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Domain rule interface for pattern-based routing
|
||||||
|
*/
|
||||||
|
export interface IDomainRule {
|
||||||
|
// Domain pattern (e.g., "*@example.com", "*@*.example.net")
|
||||||
|
pattern: string;
|
||||||
|
|
||||||
|
// Handling mode for this pattern
|
||||||
|
mode: EmailProcessingMode;
|
||||||
|
|
||||||
|
// Forward mode configuration
|
||||||
|
target?: {
|
||||||
|
server: string;
|
||||||
|
port?: number;
|
||||||
|
useTls?: boolean;
|
||||||
|
authentication?: {
|
||||||
|
user?: string;
|
||||||
|
pass?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// MTA mode configuration
|
||||||
|
mtaOptions?: IMtaOptions;
|
||||||
|
|
||||||
|
// Process mode configuration
|
||||||
|
contentScanning?: boolean;
|
||||||
|
scanners?: IContentScanner[];
|
||||||
|
transformations?: ITransformation[];
|
||||||
|
|
||||||
|
// Rate limits for this domain
|
||||||
|
rateLimits?: {
|
||||||
|
maxMessagesPerMinute?: number;
|
||||||
|
maxRecipientsPerMessage?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MTA options interface
|
||||||
|
*/
|
||||||
|
export interface IMtaOptions {
|
||||||
|
domain?: string;
|
||||||
|
allowLocalDelivery?: boolean;
|
||||||
|
localDeliveryPath?: string;
|
||||||
|
dkimSign?: boolean;
|
||||||
|
dkimOptions?: {
|
||||||
|
domainName: string;
|
||||||
|
keySelector: string;
|
||||||
|
privateKey: string;
|
||||||
|
};
|
||||||
|
smtpBanner?: string;
|
||||||
|
maxConnections?: number;
|
||||||
|
connTimeout?: number;
|
||||||
|
spoolDir?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Content scanner interface
|
||||||
|
*/
|
||||||
|
export interface IContentScanner {
|
||||||
|
type: 'spam' | 'virus' | 'attachment';
|
||||||
|
threshold?: number;
|
||||||
|
action: 'tag' | 'reject';
|
||||||
|
blockedExtensions?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformation interface
|
||||||
|
*/
|
||||||
|
export interface ITransformation {
|
||||||
|
type: string;
|
||||||
|
header?: string;
|
||||||
|
value?: string;
|
||||||
|
domains?: string[];
|
||||||
|
append?: boolean;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
@ -1,3 +1,8 @@
|
|||||||
|
// Core DcRouter components
|
||||||
export * from './classes.dcrouter.js';
|
export * from './classes.dcrouter.js';
|
||||||
export * from './classes.smtp.portconfig.js';
|
export * from './classes.smtp.portconfig.js';
|
||||||
export * from './classes.email.domainrouter.js';
|
export * from './classes.email.domainrouter.js';
|
||||||
|
|
||||||
|
// Unified Email Configuration
|
||||||
|
export * from './classes.email.config.js';
|
||||||
|
export * from './classes.domain.router.js';
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/platformservice',
|
name: '@serve.zone/platformservice',
|
||||||
version: '2.5.0',
|
version: '2.7.0',
|
||||||
description: 'A multifaceted platform service handling mail, SMS, letter delivery, and AI services.'
|
description: 'A multifaceted platform service handling mail, SMS, letter delivery, and AI services.'
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Reference in New Issue
Block a user