feat(smartproxy): Update documentation and configuration guides to adopt new route-based SmartProxy architecture
This commit is contained in:
@ -1,6 +1,5 @@
|
||||
import * as plugins from './plugins.js';
|
||||
import * as paths from './paths.js';
|
||||
import { SmtpPortConfig, type ISmtpPortSettings } from './classes.smtp.portconfig.js';
|
||||
|
||||
// Certificate types are available via plugins.tsclass
|
||||
|
||||
@ -53,7 +52,7 @@ export interface IDcRouterOptions {
|
||||
*/
|
||||
export interface PortProxyRuleContext {
|
||||
proxy: plugins.smartproxy.SmartProxy;
|
||||
configs: plugins.smartproxy.IPortProxySettings['domainConfigs'];
|
||||
routes: plugins.smartproxy.IRouteConfig[];
|
||||
}
|
||||
|
||||
export class DcRouter {
|
||||
@ -84,17 +83,15 @@ export class DcRouter {
|
||||
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 SmartProxy for HTTP/HTTPS and all traffic including email routes
|
||||
await this.setupSmartProxy();
|
||||
|
||||
// Set up unified email handling if configured
|
||||
if (this.options.emailConfig) {
|
||||
await this.setupUnifiedEmailHandling();
|
||||
}
|
||||
|
||||
// 3. Set up DNS server if configured
|
||||
// Set up DNS server if configured
|
||||
if (this.options.dnsServerConfig) {
|
||||
this.dnsServer = new plugins.smartdns.DnsServer(this.options.dnsServerConfig);
|
||||
await this.dnsServer.start();
|
||||
@ -111,41 +108,175 @@ export class DcRouter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up SmartProxy with direct configuration
|
||||
* Set up SmartProxy with direct configuration and automatic email routes
|
||||
*/
|
||||
private async setupSmartProxy(): Promise<void> {
|
||||
if (!this.options.smartProxyConfig) {
|
||||
return;
|
||||
let routes: plugins.smartproxy.IRouteConfig[] = [];
|
||||
let acmeConfig: plugins.smartproxy.IAcmeOptions | undefined;
|
||||
|
||||
// If user provides full SmartProxy config, use it directly
|
||||
if (this.options.smartProxyConfig) {
|
||||
routes = this.options.smartProxyConfig.routes || [];
|
||||
acmeConfig = this.options.smartProxyConfig.acme;
|
||||
}
|
||||
|
||||
console.log('Setting up SmartProxy with direct configuration');
|
||||
// If email config exists, automatically add email routes
|
||||
if (this.options.emailConfig) {
|
||||
const emailRoutes = this.generateEmailRoutes(this.options.emailConfig);
|
||||
routes = [...routes, ...emailRoutes];
|
||||
}
|
||||
|
||||
// Create SmartProxy instance with full configuration
|
||||
this.smartProxy = new plugins.smartproxy.SmartProxy(this.options.smartProxyConfig);
|
||||
// Merge TLS/ACME configuration if provided at root level
|
||||
if (this.options.tls && !acmeConfig) {
|
||||
acmeConfig = {
|
||||
accountEmail: this.options.tls.contactEmail,
|
||||
enabled: true,
|
||||
useProduction: true,
|
||||
autoRenew: true,
|
||||
renewThresholdDays: 30
|
||||
};
|
||||
}
|
||||
|
||||
// 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}`);
|
||||
// If we have routes or need a basic SmartProxy instance, create it
|
||||
if (routes.length > 0 || this.options.smartProxyConfig) {
|
||||
console.log('Setting up SmartProxy with combined configuration');
|
||||
|
||||
// Create SmartProxy configuration
|
||||
const smartProxyConfig: plugins.smartproxy.ISmartProxyOptions = {
|
||||
...this.options.smartProxyConfig,
|
||||
routes,
|
||||
acme: acmeConfig
|
||||
};
|
||||
|
||||
// Create SmartProxy instance
|
||||
this.smartProxy = new plugins.smartproxy.SmartProxy(smartProxyConfig);
|
||||
|
||||
// Set up event listeners
|
||||
this.smartProxy.on('error', (err) => {
|
||||
console.error('SmartProxy error:', err);
|
||||
});
|
||||
|
||||
this.smartProxy.on('certificate-renewed', (event) => {
|
||||
console.log(`Certificate renewed for ${event.domain}, expires ${event.expiryDate}`);
|
||||
});
|
||||
if (acmeConfig) {
|
||||
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 with ${routes.length} routes`);
|
||||
}
|
||||
|
||||
// Start SmartProxy
|
||||
await this.smartProxy.start();
|
||||
|
||||
console.log('SmartProxy started successfully');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Generate SmartProxy routes for email configuration
|
||||
*/
|
||||
private generateEmailRoutes(emailConfig: IEmailConfig): plugins.smartproxy.IRouteConfig[] {
|
||||
const emailRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
||||
|
||||
// Create routes for each email port
|
||||
for (const port of emailConfig.ports) {
|
||||
// Handle different email ports differently
|
||||
switch (port) {
|
||||
case 25: // SMTP
|
||||
emailRoutes.push({
|
||||
name: 'smtp-route',
|
||||
match: {
|
||||
ports: [25]
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
target: {
|
||||
host: 'localhost', // Forward to internal email server
|
||||
port: 10025 // Internal email server port
|
||||
},
|
||||
// No TLS termination for port 25 (STARTTLS handled by email server)
|
||||
tls: {
|
||||
mode: 'passthrough'
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 587: // Submission
|
||||
emailRoutes.push({
|
||||
name: 'submission-route',
|
||||
match: {
|
||||
ports: [587]
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
target: {
|
||||
host: 'localhost',
|
||||
port: 10587
|
||||
},
|
||||
tls: {
|
||||
mode: 'passthrough' // STARTTLS handled by email server
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 465: // SMTPS
|
||||
emailRoutes.push({
|
||||
name: 'smtps-route',
|
||||
match: {
|
||||
ports: [465]
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
target: {
|
||||
host: 'localhost',
|
||||
port: 10465
|
||||
},
|
||||
tls: {
|
||||
mode: 'terminate', // Terminate TLS and re-encrypt to email server
|
||||
certificate: 'auto'
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add domain-specific email routes if configured
|
||||
if (emailConfig.domainRules) {
|
||||
for (const rule of emailConfig.domainRules) {
|
||||
// Extract domain from pattern (e.g., "*@example.com" -> "example.com")
|
||||
const domain = rule.pattern.split('@')[1];
|
||||
|
||||
if (domain && rule.mode === 'forward' && rule.target) {
|
||||
emailRoutes.push({
|
||||
name: `email-forward-${domain}`,
|
||||
match: {
|
||||
ports: emailConfig.ports,
|
||||
domains: [domain]
|
||||
},
|
||||
action: {
|
||||
type: 'forward',
|
||||
target: {
|
||||
host: rule.target.server,
|
||||
port: rule.target.port || 25
|
||||
},
|
||||
tls: {
|
||||
mode: rule.target.useTls ? 'terminate-and-reencrypt' : 'passthrough'
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return emailRoutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a domain matches a pattern (including wildcard support)
|
||||
* @param domain The domain to check
|
||||
@ -213,7 +344,7 @@ export class DcRouter {
|
||||
// Update configuration
|
||||
this.options.smartProxyConfig = config;
|
||||
|
||||
// Start new SmartProxy with updated configuration
|
||||
// Start new SmartProxy with updated configuration (will include email routes if configured)
|
||||
await this.setupSmartProxy();
|
||||
|
||||
console.log('SmartProxy configuration updated');
|
||||
@ -231,15 +362,31 @@ export class DcRouter {
|
||||
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
|
||||
const portMapping = {
|
||||
25: 10025, // SMTP
|
||||
587: 10587, // Submission
|
||||
465: 10465 // SMTPS
|
||||
};
|
||||
|
||||
// Create internal email server configuration
|
||||
const internalEmailConfig: IEmailConfig = {
|
||||
...emailConfig,
|
||||
ports: emailConfig.ports.map(port => portMapping[port] || port + 10000),
|
||||
hostname: 'localhost' // Listen on localhost for SmartProxy forwarding
|
||||
};
|
||||
|
||||
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
|
||||
domainRules: emailConfig.domainRules,
|
||||
defaultMode: emailConfig.defaultMode,
|
||||
defaultServer: emailConfig.defaultServer,
|
||||
defaultPort: emailConfig.defaultPort,
|
||||
defaultTls: emailConfig.defaultTls
|
||||
});
|
||||
|
||||
// Initialize the rate limiter
|
||||
@ -255,11 +402,11 @@ export class DcRouter {
|
||||
|
||||
// Initialize the unified delivery queue
|
||||
const queueOptions: IQueueOptions = {
|
||||
storageType: this.options.emailConfig.queue?.storageType || 'memory',
|
||||
persistentPath: this.options.emailConfig.queue?.persistentPath,
|
||||
maxRetries: this.options.emailConfig.queue?.maxRetries,
|
||||
baseRetryDelay: this.options.emailConfig.queue?.baseRetryDelay,
|
||||
maxRetryDelay: this.options.emailConfig.queue?.maxRetryDelay
|
||||
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);
|
||||
@ -274,18 +421,18 @@ export class DcRouter {
|
||||
this.deliverySystem = new MultiModeDeliverySystem(this.deliveryQueue, deliveryOptions);
|
||||
await this.deliverySystem.start();
|
||||
|
||||
// Initialize the unified email server
|
||||
// Initialize the unified email server with internal configuration
|
||||
this.unifiedEmailServer = new UnifiedEmailServer({
|
||||
ports: this.options.emailConfig.ports,
|
||||
hostname: this.options.emailConfig.hostname,
|
||||
maxMessageSize: this.options.emailConfig.maxMessageSize,
|
||||
auth: this.options.emailConfig.auth,
|
||||
tls: this.options.emailConfig.tls,
|
||||
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
|
||||
ports: internalEmailConfig.ports,
|
||||
hostname: internalEmailConfig.hostname,
|
||||
maxMessageSize: emailConfig.maxMessageSize,
|
||||
auth: emailConfig.auth,
|
||||
tls: emailConfig.tls,
|
||||
domainRules: emailConfig.domainRules,
|
||||
defaultMode: emailConfig.defaultMode,
|
||||
defaultServer: emailConfig.defaultServer,
|
||||
defaultPort: emailConfig.defaultPort,
|
||||
defaultTls: emailConfig.defaultTls
|
||||
});
|
||||
|
||||
// Set up event listeners
|
||||
@ -303,7 +450,8 @@ export class DcRouter {
|
||||
// Start the unified email server
|
||||
await this.unifiedEmailServer.start();
|
||||
|
||||
logger.log('info', `Unified email handling configured with ${this.options.emailConfig.domainRules.length} domain rules`);
|
||||
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(', ')}`);
|
||||
} catch (error) {
|
||||
logger.log('error', `Error setting up unified email handling: ${error.message}`);
|
||||
throw error;
|
||||
|
Reference in New Issue
Block a user