start the path to rust

This commit is contained in:
2026-02-10 15:54:09 +00:00
parent 237dba3bab
commit 8bd8c295b0
318 changed files with 28352 additions and 428 deletions

View File

@@ -0,0 +1,79 @@
import type { IEmailDomainConfig } from './interfaces.js';
/** External DcRouter interface shape used by DnsManager */
interface IDcRouterLike {
storageManager: IStorageManagerLike;
dnsServer?: any;
options?: {
dnsNsDomains?: string[];
dnsScopes?: string[];
};
}
/** External StorageManager interface shape used by DnsManager */
interface IStorageManagerLike {
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<void>;
}
/**
* DNS validation result
*/
export interface IDnsValidationResult {
valid: boolean;
errors: string[];
warnings: string[];
requiredChanges: string[];
}
/**
* Manages DNS configuration for email domains
* Handles both validation and creation of DNS records
*/
export declare class DnsManager {
private dcRouter;
private storageManager;
constructor(dcRouter: IDcRouterLike);
/**
* Validate all domain configurations
*/
validateAllDomains(domainConfigs: IEmailDomainConfig[]): Promise<Map<string, IDnsValidationResult>>;
/**
* Validate a single domain configuration
*/
validateDomain(config: IEmailDomainConfig): Promise<IDnsValidationResult>;
/**
* Validate forward mode configuration
*/
private validateForwardMode;
/**
* Validate internal DNS mode configuration
*/
private validateInternalDnsMode;
/**
* Validate external DNS mode configuration
*/
private validateExternalDnsMode;
/**
* Check DNS records for a domain
*/
private checkDnsRecords;
/**
* Resolve NS records for a domain
*/
private resolveNs;
/**
* Get base domain from email domain (e.g., mail.example.com -> example.com)
*/
private getBaseDomain;
/**
* Ensure all DNS records are created for configured domains
* This is the main entry point for DNS record management
*/
ensureDnsRecords(domainConfigs: IEmailDomainConfig[], dkimCreator?: any): Promise<void>;
/**
* Create DNS records for internal-dns mode domains
*/
private createInternalDnsRecords;
/**
* Create DKIM DNS records for all domains
*/
private createDkimRecords;
}
export {};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,165 @@
import * as plugins from '../../plugins.js';
import { DKIMCreator } from '../security/classes.dkimcreator.js';
/**
* Interface for DNS record information
*/
export interface IDnsRecord {
name: string;
type: string;
value: string;
ttl?: number;
dnsSecEnabled?: boolean;
}
/**
* Interface for DNS lookup options
*/
export interface IDnsLookupOptions {
/** Cache time to live in milliseconds, 0 to disable caching */
cacheTtl?: number;
/** Timeout for DNS queries in milliseconds */
timeout?: number;
}
/**
* Interface for DNS verification result
*/
export interface IDnsVerificationResult {
record: string;
found: boolean;
valid: boolean;
value?: string;
expectedValue?: string;
error?: string;
}
/**
* Manager for DNS-related operations, including record lookups, verification, and generation
*/
export declare class DNSManager {
dkimCreator: DKIMCreator;
private cache;
private defaultOptions;
constructor(dkimCreatorArg: DKIMCreator, options?: IDnsLookupOptions);
/**
* Lookup MX records for a domain
* @param domain Domain to look up
* @param options Lookup options
* @returns Array of MX records sorted by priority
*/
lookupMx(domain: string, options?: IDnsLookupOptions): Promise<plugins.dns.MxRecord[]>;
/**
* Lookup TXT records for a domain
* @param domain Domain to look up
* @param options Lookup options
* @returns Array of TXT records
*/
lookupTxt(domain: string, options?: IDnsLookupOptions): Promise<string[][]>;
/**
* Find specific TXT record by subdomain and prefix
* @param domain Base domain
* @param subdomain Subdomain prefix (e.g., "dkim._domainkey")
* @param prefix Record prefix to match (e.g., "v=DKIM1")
* @param options Lookup options
* @returns Matching TXT record or null if not found
*/
findTxtRecord(domain: string, subdomain?: string, prefix?: string, options?: IDnsLookupOptions): Promise<string | null>;
/**
* Verify if a domain has a valid SPF record
* @param domain Domain to verify
* @returns Verification result
*/
verifySpfRecord(domain: string): Promise<IDnsVerificationResult>;
/**
* Verify if a domain has a valid DKIM record
* @param domain Domain to verify
* @param selector DKIM selector (usually "mta" in our case)
* @returns Verification result
*/
verifyDkimRecord(domain: string, selector?: string): Promise<IDnsVerificationResult>;
/**
* Verify if a domain has a valid DMARC record
* @param domain Domain to verify
* @returns Verification result
*/
verifyDmarcRecord(domain: string): Promise<IDnsVerificationResult>;
/**
* Check all email authentication records (SPF, DKIM, DMARC) for a domain
* @param domain Domain to check
* @param dkimSelector DKIM selector
* @returns Object with verification results for each record type
*/
verifyEmailAuthRecords(domain: string, dkimSelector?: string): Promise<{
spf: IDnsVerificationResult;
dkim: IDnsVerificationResult;
dmarc: IDnsVerificationResult;
}>;
/**
* Generate a recommended SPF record for a domain
* @param domain Domain name
* @param options Configuration options for the SPF record
* @returns Generated SPF record
*/
generateSpfRecord(domain: string, options?: {
includeMx?: boolean;
includeA?: boolean;
includeIps?: string[];
includeSpf?: string[];
policy?: 'none' | 'neutral' | 'softfail' | 'fail' | 'reject';
}): IDnsRecord;
/**
* Generate a recommended DMARC record for a domain
* @param domain Domain name
* @param options Configuration options for the DMARC record
* @returns Generated DMARC record
*/
generateDmarcRecord(domain: string, options?: {
policy?: 'none' | 'quarantine' | 'reject';
subdomainPolicy?: 'none' | 'quarantine' | 'reject';
pct?: number;
rua?: string;
ruf?: string;
daysInterval?: number;
}): IDnsRecord;
/**
* Save DNS record recommendations to a file
* @param domain Domain name
* @param records DNS records to save
*/
saveDnsRecommendations(domain: string, records: IDnsRecord[]): Promise<void>;
/**
* Get cache key value
* @param key Cache key
* @returns Cached value or undefined if not found or expired
*/
private getFromCache;
/**
* Set cache key value
* @param key Cache key
* @param data Data to cache
* @param ttl TTL in milliseconds
*/
private setInCache;
/**
* Clear the DNS cache
* @param key Optional specific key to clear, or all cache if not provided
*/
clearCache(key?: string): void;
/**
* Promise-based wrapper for dns.resolveMx
* @param domain Domain to resolve
* @param timeout Timeout in milliseconds
* @returns Promise resolving to MX records
*/
private dnsResolveMx;
/**
* Promise-based wrapper for dns.resolveTxt
* @param domain Domain to resolve
* @param timeout Timeout in milliseconds
* @returns Promise resolving to TXT records
*/
private dnsResolveTxt;
/**
* Generate all recommended DNS records for proper email authentication
* @param domain Domain to generate records for
* @returns Array of recommended DNS records
*/
generateAllRecommendedRecords(domain: string): Promise<IDnsRecord[]>;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,54 @@
import type { IEmailDomainConfig } from './interfaces.js';
/**
* Registry for email domain configurations
* Provides fast lookups and validation for domains
*/
export declare class DomainRegistry {
private domains;
private defaults;
constructor(domainConfigs: IEmailDomainConfig[], defaults?: {
dnsMode?: 'forward' | 'internal-dns' | 'external-dns';
dkim?: IEmailDomainConfig['dkim'];
rateLimits?: IEmailDomainConfig['rateLimits'];
});
/**
* Get default DKIM configuration
*/
private getDefaultDkimConfig;
/**
* Apply defaults to a domain configuration
*/
private applyDefaults;
/**
* Check if a domain is registered
*/
isDomainRegistered(domain: string): boolean;
/**
* Check if an email address belongs to a registered domain
*/
isEmailRegistered(email: string): boolean;
/**
* Get domain configuration
*/
getDomainConfig(domain: string): IEmailDomainConfig | undefined;
/**
* Get domain configuration for an email address
*/
getEmailDomainConfig(email: string): IEmailDomainConfig | undefined;
/**
* Extract domain from email address
*/
private extractDomain;
/**
* Get all registered domains
*/
getAllDomains(): string[];
/**
* Get all domain configurations
*/
getAllConfigs(): IEmailDomainConfig[];
/**
* Get domains by DNS mode
*/
getDomainsByMode(mode: 'forward' | 'internal-dns' | 'external-dns'): IEmailDomainConfig[];
}

View File

@@ -0,0 +1,119 @@
import { logger } from '../../logger.js';
/**
* Registry for email domain configurations
* Provides fast lookups and validation for domains
*/
export class DomainRegistry {
domains = new Map();
defaults;
constructor(domainConfigs, defaults) {
// Set defaults
this.defaults = {
dnsMode: defaults?.dnsMode || 'external-dns',
...this.getDefaultDkimConfig(),
...defaults?.dkim,
rateLimits: defaults?.rateLimits
};
// Process and store domain configurations
for (const config of domainConfigs) {
const processedConfig = this.applyDefaults(config);
this.domains.set(config.domain.toLowerCase(), processedConfig);
logger.log('info', `Registered domain: ${config.domain} with DNS mode: ${processedConfig.dnsMode}`);
}
}
/**
* Get default DKIM configuration
*/
getDefaultDkimConfig() {
return {
selector: 'default',
keySize: 2048,
rotateKeys: false,
rotationInterval: 90
};
}
/**
* Apply defaults to a domain configuration
*/
applyDefaults(config) {
return {
...config,
dnsMode: config.dnsMode || this.defaults.dnsMode,
dkim: {
...this.getDefaultDkimConfig(),
...this.defaults,
...config.dkim
},
rateLimits: {
...this.defaults.rateLimits,
...config.rateLimits,
outbound: {
...this.defaults.rateLimits?.outbound,
...config.rateLimits?.outbound
},
inbound: {
...this.defaults.rateLimits?.inbound,
...config.rateLimits?.inbound
}
}
};
}
/**
* Check if a domain is registered
*/
isDomainRegistered(domain) {
return this.domains.has(domain.toLowerCase());
}
/**
* Check if an email address belongs to a registered domain
*/
isEmailRegistered(email) {
const domain = this.extractDomain(email);
if (!domain)
return false;
return this.isDomainRegistered(domain);
}
/**
* Get domain configuration
*/
getDomainConfig(domain) {
return this.domains.get(domain.toLowerCase());
}
/**
* Get domain configuration for an email address
*/
getEmailDomainConfig(email) {
const domain = this.extractDomain(email);
if (!domain)
return undefined;
return this.getDomainConfig(domain);
}
/**
* Extract domain from email address
*/
extractDomain(email) {
const parts = email.toLowerCase().split('@');
if (parts.length !== 2)
return null;
return parts[1];
}
/**
* Get all registered domains
*/
getAllDomains() {
return Array.from(this.domains.keys());
}
/**
* Get all domain configurations
*/
getAllConfigs() {
return Array.from(this.domains.values());
}
/**
* Get domains by DNS mode
*/
getDomainsByMode(mode) {
return Array.from(this.domains.values()).filter(config => config.dnsMode === mode);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kb21haW4ucmVnaXN0cnkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3JvdXRpbmcvY2xhc3Nlcy5kb21haW4ucmVnaXN0cnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRXpDOzs7R0FHRztBQUNILE1BQU0sT0FBTyxjQUFjO0lBQ2pCLE9BQU8sR0FBb0MsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNyRCxRQUFRLENBR2Q7SUFFRixZQUNFLGFBQW1DLEVBQ25DLFFBSUM7UUFFRCxlQUFlO1FBQ2YsSUFBSSxDQUFDLFFBQVEsR0FBRztZQUNkLE9BQU8sRUFBRSxRQUFRLEVBQUUsT0FBTyxJQUFJLGNBQWM7WUFDNUMsR0FBRyxJQUFJLENBQUMsb0JBQW9CLEVBQUU7WUFDOUIsR0FBRyxRQUFRLEVBQUUsSUFBSTtZQUNqQixVQUFVLEVBQUUsUUFBUSxFQUFFLFVBQVU7U0FDakMsQ0FBQztRQUVGLDBDQUEwQztRQUMxQyxLQUFLLE1BQU0sTUFBTSxJQUFJLGFBQWEsRUFBRSxDQUFDO1lBQ25DLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDbkQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUUsRUFBRSxlQUFlLENBQUMsQ0FBQztZQUMvRCxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxzQkFBc0IsTUFBTSxDQUFDLE1BQU0sbUJBQW1CLGVBQWUsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO1FBQ3RHLENBQUM7SUFDSCxDQUFDO0lBRUQ7O09BRUc7SUFDSyxvQkFBb0I7UUFDMUIsT0FBTztZQUNMLFFBQVEsRUFBRSxTQUFTO1lBQ25CLE9BQU8sRUFBRSxJQUFJO1lBQ2IsVUFBVSxFQUFFLEtBQUs7WUFDakIsZ0JB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

View File

@@ -0,0 +1,64 @@
import type { EmailProcessingMode } from '../delivery/interfaces.js';
export type { EmailProcessingMode };
/**
* Domain rule interface for pattern-based routing
*/
export interface IDomainRule {
pattern: string;
mode: EmailProcessingMode;
target?: {
server: string;
port?: number;
useTls?: boolean;
authentication?: {
user?: string;
pass?: string;
};
};
mtaOptions?: IMtaOptions;
contentScanning?: boolean;
scanners?: IContentScanner[];
transformations?: ITransformation[];
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;
}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3JvdXRpbmcvY2xhc3Nlcy5lbWFpbC5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9

View File

@@ -0,0 +1,171 @@
import { EventEmitter } from 'node:events';
import type { IEmailRoute, IEmailContext } from './interfaces.js';
/**
* Email router that evaluates routes and determines actions
*/
export declare class EmailRouter extends EventEmitter {
private routes;
private patternCache;
private storageManager?;
private persistChanges;
/**
* Create a new email router
* @param routes Array of email routes
* @param options Router options
*/
constructor(routes: IEmailRoute[], options?: {
storageManager?: any;
persistChanges?: boolean;
});
/**
* Sort routes by priority (higher priority first)
* @param routes Routes to sort
* @returns Sorted routes
*/
private sortRoutesByPriority;
/**
* Get all configured routes
* @returns Array of routes
*/
getRoutes(): IEmailRoute[];
/**
* Update routes
* @param routes New routes
* @param persist Whether to persist changes (defaults to persistChanges setting)
*/
updateRoutes(routes: IEmailRoute[], persist?: boolean): Promise<void>;
/**
* Set routes (alias for updateRoutes)
* @param routes New routes
* @param persist Whether to persist changes
*/
setRoutes(routes: IEmailRoute[], persist?: boolean): Promise<void>;
/**
* Clear the pattern cache
*/
clearCache(): void;
/**
* Evaluate routes and find the first match
* @param context Email context
* @returns Matched route or null
*/
evaluateRoutes(context: IEmailContext): Promise<IEmailRoute | null>;
/**
* Check if a route matches the context
* @param route Route to check
* @param context Email context
* @returns True if route matches
*/
private matchesRoute;
/**
* Check if email recipients match patterns
* @param email Email to check
* @param patterns Patterns to match
* @returns True if any recipient matches
*/
private matchesRecipients;
/**
* Check if email sender matches patterns
* @param email Email to check
* @param patterns Patterns to match
* @returns True if sender matches
*/
private matchesSenders;
/**
* Check if client IP matches patterns
* @param context Email context
* @param patterns IP patterns to match
* @returns True if IP matches
*/
private matchesClientIp;
/**
* Check if email headers match patterns
* @param email Email to check
* @param headerPatterns Header patterns to match
* @returns True if headers match
*/
private matchesHeaders;
/**
* Check if email size matches range
* @param email Email to check
* @param sizeRange Size range to match
* @returns True if size is in range
*/
private matchesSize;
/**
* Check if email subject matches pattern
* @param email Email to check
* @param pattern Pattern to match
* @returns True if subject matches
*/
private matchesSubject;
/**
* Check if a string matches a glob pattern
* @param str String to check
* @param pattern Glob pattern
* @returns True if matches
*/
private matchesPattern;
/**
* Convert glob pattern to RegExp
* @param pattern Glob pattern
* @returns Regular expression
*/
private globToRegExp;
/**
* Check if IP is in CIDR range
* @param ip IP address to check
* @param cidr CIDR notation (e.g., '192.168.0.0/16')
* @returns True if IP is in range
*/
private ipInCidr;
/**
* Convert IP address to number
* @param ip IP address
* @returns Number representation
*/
private ipToNumber;
/**
* Calculate approximate email size in bytes
* @param email Email to measure
* @returns Size in bytes
*/
private calculateEmailSize;
/**
* Save current routes to storage
*/
saveRoutes(): Promise<void>;
/**
* Load routes from storage
* @param options Load options
*/
loadRoutes(options?: {
merge?: boolean;
replace?: boolean;
}): Promise<IEmailRoute[]>;
/**
* Add a route
* @param route Route to add
* @param persist Whether to persist changes
*/
addRoute(route: IEmailRoute, persist?: boolean): Promise<void>;
/**
* Remove a route by name
* @param name Route name
* @param persist Whether to persist changes
*/
removeRoute(name: string, persist?: boolean): Promise<void>;
/**
* Update a route
* @param name Route name
* @param route Updated route data
* @param persist Whether to persist changes
*/
updateRoute(name: string, route: IEmailRoute, persist?: boolean): Promise<void>;
/**
* Get a route by name
* @param name Route name
* @returns Route or undefined
*/
getRoute(name: string): IEmailRoute | undefined;
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,441 @@
import * as plugins from '../../plugins.js';
import { EventEmitter } from 'events';
import { DKIMCreator } from '../security/classes.dkimcreator.js';
interface IIPWarmupConfig {
enabled?: boolean;
ips?: string[];
[key: string]: any;
}
interface IReputationMonitorConfig {
enabled?: boolean;
domains?: string[];
[key: string]: any;
}
import type { IEmailRoute, IEmailDomainConfig } from './interfaces.js';
import { Email } from '../core/classes.email.js';
import { DomainRegistry } from './classes.domain.registry.js';
import { BounceType, BounceCategory } from '../core/classes.bouncemanager.js';
import type { SmtpClient } from '../delivery/smtpclient/smtp-client.js';
import { MultiModeDeliverySystem } from '../delivery/classes.delivery.system.js';
import { UnifiedDeliveryQueue } from '../delivery/classes.delivery.queue.js';
import { UnifiedRateLimiter, type IHierarchicalRateLimits } from '../delivery/classes.unified.rate.limiter.js';
import type { EmailProcessingMode, ISmtpSession as IBaseSmtpSession } from '../delivery/interfaces.js';
/** External DcRouter interface shape used by UnifiedEmailServer */
interface DcRouter {
storageManager: any;
dnsServer?: any;
options?: any;
}
/**
* Extended SMTP session interface with route information
*/
export interface IExtendedSmtpSession extends ISmtpSession {
/**
* Matched route for this session
*/
matchedRoute?: IEmailRoute;
}
/**
* Options for the unified email server
*/
export interface IUnifiedEmailServerOptions {
ports: number[];
hostname: string;
domains: IEmailDomainConfig[];
banner?: string;
debug?: boolean;
useSocketHandler?: boolean;
auth?: {
required?: boolean;
methods?: ('PLAIN' | 'LOGIN' | 'OAUTH2')[];
users?: Array<{
username: string;
password: string;
}>;
};
tls?: {
certPath?: string;
keyPath?: string;
caPath?: string;
minVersion?: string;
ciphers?: string;
};
maxMessageSize?: number;
maxClients?: number;
maxConnections?: number;
connectionTimeout?: number;
socketTimeout?: number;
routes: IEmailRoute[];
defaults?: {
dnsMode?: 'forward' | 'internal-dns' | 'external-dns';
dkim?: IEmailDomainConfig['dkim'];
rateLimits?: IEmailDomainConfig['rateLimits'];
};
outbound?: {
maxConnections?: number;
connectionTimeout?: number;
socketTimeout?: number;
retryAttempts?: number;
defaultFrom?: string;
};
rateLimits?: IHierarchicalRateLimits;
ipWarmupConfig?: IIPWarmupConfig;
reputationMonitorConfig?: IReputationMonitorConfig;
}
/**
* Extended SMTP session interface for UnifiedEmailServer
*/
export interface ISmtpSession extends IBaseSmtpSession {
/**
* User information if authenticated
*/
user?: {
username: string;
[key: string]: any;
};
/**
* Matched route for this session
*/
matchedRoute?: IEmailRoute;
}
/**
* Authentication data for SMTP
*/
import type { ISmtpAuth } from '../delivery/interfaces.js';
export type IAuthData = ISmtpAuth;
/**
* Server statistics
*/
export interface IServerStats {
startTime: Date;
connections: {
current: number;
total: number;
};
messages: {
processed: number;
delivered: number;
failed: number;
};
processingTime: {
avg: number;
max: number;
min: number;
};
}
/**
* Unified email server that handles all email traffic with pattern-based routing
*/
export declare class UnifiedEmailServer extends EventEmitter {
private dcRouter;
private options;
private emailRouter;
domainRegistry: DomainRegistry;
private servers;
private stats;
dkimCreator: DKIMCreator;
private ipReputationChecker;
private bounceManager;
private ipWarmupManager;
private senderReputationMonitor;
deliveryQueue: UnifiedDeliveryQueue;
deliverySystem: MultiModeDeliverySystem;
private rateLimiter;
private dkimKeys;
private smtpClients;
constructor(dcRouter: DcRouter, options: IUnifiedEmailServerOptions);
/**
* Get or create an SMTP client for the given host and port
* Uses connection pooling for efficiency
*/
getSmtpClient(host: string, port?: number): SmtpClient;
/**
* Start the unified email server
*/
start(): Promise<void>;
/**
* Handle a socket from smartproxy in socket-handler mode
* @param socket The socket to handle
* @param port The port this connection is for (25, 587, 465)
*/
handleSocket(socket: plugins.net.Socket | plugins.tls.TLSSocket, port: number): Promise<void>;
/**
* Stop the unified email server
*/
stop(): Promise<void>;
/**
* Process email based on routing rules
*/
processEmailByMode(emailData: Email | Buffer, session: IExtendedSmtpSession): Promise<Email>;
/**
* Execute action based on route configuration
*/
private executeAction;
/**
* Handle forward action
*/
private handleForwardAction;
/**
* Handle process action
*/
private handleProcessAction;
/**
* Handle deliver action
*/
private handleDeliverAction;
/**
* Handle reject action
*/
private handleRejectAction;
/**
* Handle email in MTA mode (programmatic processing)
*/
private _handleMtaMode;
/**
* Handle email in process mode (store-and-forward with scanning)
*/
private _handleProcessMode;
/**
* Get file extension from filename
*/
private getFileExtension;
/**
* Set up DKIM configuration for all domains
*/
private setupDkimForDomains;
/**
* Apply per-domain rate limits from domain configurations
*/
private applyDomainRateLimits;
/**
* Check and rotate DKIM keys if needed
*/
private checkAndRotateDkimKeys;
/**
* Generate SmartProxy routes for email ports
*/
generateProxyRoutes(portMapping?: Record<number, number>): any[];
/**
* Update server configuration
*/
updateOptions(options: Partial<IUnifiedEmailServerOptions>): void;
/**
* Update email routes
*/
updateEmailRoutes(routes: IEmailRoute[]): void;
/**
* Get server statistics
*/
getStats(): IServerStats;
/**
* Get domain registry
*/
getDomainRegistry(): DomainRegistry;
/**
* Update email routes dynamically
*/
updateRoutes(routes: IEmailRoute[]): void;
/**
* Send an email through the delivery system
* @param email The email to send
* @param mode The processing mode to use
* @param rule Optional rule to apply
* @param options Optional sending options
* @returns The ID of the queued email
*/
sendEmail(email: Email, mode?: EmailProcessingMode, route?: IEmailRoute, options?: {
skipSuppressionCheck?: boolean;
ipAddress?: string;
isTransactional?: boolean;
}): Promise<string>;
/**
* Handle DKIM signing for an email
* @param email The email to sign
* @param domain The domain to sign with
* @param selector The DKIM selector
*/
private handleDkimSigning;
/**
* Process a bounce notification email
* @param bounceEmail The email containing bounce notification information
* @returns Processed bounce record or null if not a bounce
*/
processBounceNotification(bounceEmail: Email): Promise<boolean>;
/**
* Process an SMTP failure as a bounce
* @param recipient Recipient email that failed
* @param smtpResponse SMTP error response
* @param options Additional options for bounce processing
* @returns Processed bounce record
*/
processSmtpFailure(recipient: string, smtpResponse: string, options?: {
sender?: string;
originalEmailId?: string;
statusCode?: string;
headers?: Record<string, string>;
}): Promise<boolean>;
/**
* Check if an email address is suppressed (has bounced previously)
* @param email Email address to check
* @returns Whether the email is suppressed
*/
isEmailSuppressed(email: string): boolean;
/**
* Get suppression information for an email
* @param email Email address to check
* @returns Suppression information or null if not suppressed
*/
getSuppressionInfo(email: string): {
reason: string;
timestamp: number;
expiresAt?: number;
} | null;
/**
* Get bounce history information for an email
* @param email Email address to check
* @returns Bounce history or null if no bounces
*/
getBounceHistory(email: string): {
lastBounce: number;
count: number;
type: BounceType;
category: BounceCategory;
} | null;
/**
* Get all suppressed email addresses
* @returns Array of suppressed email addresses
*/
getSuppressionList(): string[];
/**
* Get all hard bounced email addresses
* @returns Array of hard bounced email addresses
*/
getHardBouncedAddresses(): string[];
/**
* Add an email to the suppression list
* @param email Email address to suppress
* @param reason Reason for suppression
* @param expiresAt Optional expiration time (undefined for permanent)
*/
addToSuppressionList(email: string, reason: string, expiresAt?: number): void;
/**
* Remove an email from the suppression list
* @param email Email address to remove from suppression
*/
removeFromSuppressionList(email: string): void;
/**
* Get the status of IP warmup process
* @param ipAddress Optional specific IP to check
* @returns Status of IP warmup
*/
getIPWarmupStatus(ipAddress?: string): any;
/**
* Add a new IP address to the warmup process
* @param ipAddress IP address to add
*/
addIPToWarmup(ipAddress: string): void;
/**
* Remove an IP address from the warmup process
* @param ipAddress IP address to remove
*/
removeIPFromWarmup(ipAddress: string): void;
/**
* Update metrics for an IP in the warmup process
* @param ipAddress IP address
* @param metrics Metrics to update
*/
updateIPWarmupMetrics(ipAddress: string, metrics: {
openRate?: number;
bounceRate?: number;
complaintRate?: number;
}): void;
/**
* Check if an IP can send more emails today
* @param ipAddress IP address to check
* @returns Whether the IP can send more today
*/
canIPSendMoreToday(ipAddress: string): boolean;
/**
* Check if an IP can send more emails in the current hour
* @param ipAddress IP address to check
* @returns Whether the IP can send more this hour
*/
canIPSendMoreThisHour(ipAddress: string): boolean;
/**
* Get the best IP to use for sending an email based on warmup status
* @param emailInfo Information about the email being sent
* @returns Best IP to use or null
*/
getBestIPForSending(emailInfo: {
from: string;
to: string[];
domain: string;
isTransactional?: boolean;
}): string | null;
/**
* Set the active IP allocation policy for warmup
* @param policyName Name of the policy to set
*/
setIPAllocationPolicy(policyName: string): void;
/**
* Record that an email was sent using a specific IP
* @param ipAddress IP address used for sending
*/
recordIPSend(ipAddress: string): void;
/**
* Get reputation data for a domain
* @param domain Domain to get reputation for
* @returns Domain reputation metrics
*/
getDomainReputationData(domain: string): any;
/**
* Get summary reputation data for all monitored domains
* @returns Summary data for all domains
*/
getReputationSummary(): any;
/**
* Add a domain to the reputation monitoring system
* @param domain Domain to add
*/
addDomainToMonitoring(domain: string): void;
/**
* Remove a domain from the reputation monitoring system
* @param domain Domain to remove
*/
removeDomainFromMonitoring(domain: string): void;
/**
* Record an email event for domain reputation tracking
* @param domain Domain sending the email
* @param event Event details
*/
recordReputationEvent(domain: string, event: {
type: 'sent' | 'delivered' | 'bounce' | 'complaint' | 'open' | 'click';
count?: number;
hardBounce?: boolean;
receivingDomain?: string;
}): void;
/**
* Check if DKIM key exists for a domain
* @param domain Domain to check
*/
hasDkimKey(domain: string): boolean;
/**
* Record successful email delivery
* @param domain Sending domain
*/
recordDelivery(domain: string): void;
/**
* Record email bounce
* @param domain Sending domain
* @param receivingDomain Receiving domain that bounced
* @param bounceType Type of bounce (hard/soft)
* @param reason Bounce reason
*/
recordBounce(domain: string, receivingDomain: string, bounceType: 'hard' | 'soft', reason: string): void;
/**
* Get the rate limiter instance
* @returns The unified rate limiter
*/
getRateLimiter(): UnifiedRateLimiter;
}
export {};

File diff suppressed because one or more lines are too long

5
dist_ts/mail/routing/index.d.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
export * from './classes.email.router.js';
export * from './classes.unified.email.server.js';
export * from './classes.dns.manager.js';
export * from './interfaces.js';
export * from './classes.domain.registry.js';

View File

@@ -0,0 +1,7 @@
// Email routing components
export * from './classes.email.router.js';
export * from './classes.unified.email.server.js';
export * from './classes.dns.manager.js';
export * from './interfaces.js';
export * from './classes.domain.registry.js';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL3JvdXRpbmcvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsMkJBQTJCO0FBQzNCLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyxtQ0FBbUMsQ0FBQztBQUNsRCxjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsaUJBQWlCLENBQUM7QUFDaEMsY0FBYyw4QkFBOEIsQ0FBQyJ9

187
dist_ts/mail/routing/interfaces.d.ts vendored Normal file
View File

@@ -0,0 +1,187 @@
import type { Email } from '../core/classes.email.js';
import type { IExtendedSmtpSession } from './classes.unified.email.server.js';
/**
* Route configuration for email routing
*/
export interface IEmailRoute {
/** Route identifier */
name: string;
/** Order of evaluation (higher priority evaluated first, default: 0) */
priority?: number;
/** Conditions to match */
match: IEmailMatch;
/** Action to take when matched */
action: IEmailAction;
}
/**
* Match criteria for email routing
*/
export interface IEmailMatch {
/** Email patterns to match recipients: "*@example.com", "admin@*" */
recipients?: string | string[];
/** Email patterns to match senders */
senders?: string | string[];
/** IP addresses or CIDR ranges to match */
clientIp?: string | string[];
/** Require authentication status */
authenticated?: boolean;
/** Headers to match */
headers?: Record<string, string | RegExp>;
/** Message size range */
sizeRange?: {
min?: number;
max?: number;
};
/** Subject line patterns */
subject?: string | RegExp;
/** Has attachments */
hasAttachments?: boolean;
}
/**
* Action to take when route matches
*/
export interface IEmailAction {
/** Type of action to perform */
type: 'forward' | 'deliver' | 'reject' | 'process';
/** Forward action configuration */
forward?: {
/** Target host to forward to */
host: string;
/** Target port (default: 25) */
port?: number;
/** Authentication credentials */
auth?: {
user: string;
pass: string;
};
/** Preserve original headers */
preserveHeaders?: boolean;
/** Additional headers to add */
addHeaders?: Record<string, string>;
};
/** Reject action configuration */
reject?: {
/** SMTP response code */
code: number;
/** SMTP response message */
message: string;
};
/** Process action configuration */
process?: {
/** Enable content scanning */
scan?: boolean;
/** Enable DKIM signing */
dkim?: boolean;
/** Delivery queue priority */
queue?: 'normal' | 'priority' | 'bulk';
};
/** Options for various action types */
options?: {
/** MTA specific options */
mtaOptions?: {
domain?: string;
allowLocalDelivery?: boolean;
localDeliveryPath?: string;
dkimSign?: boolean;
dkimOptions?: {
domainName: string;
keySelector: string;
privateKey?: string;
};
smtpBanner?: string;
maxConnections?: number;
connTimeout?: number;
spoolDir?: string;
};
/** Content scanning configuration */
contentScanning?: boolean;
scanners?: Array<{
type: 'spam' | 'virus' | 'attachment';
threshold?: number;
action: 'tag' | 'reject';
blockedExtensions?: string[];
}>;
/** Email transformations */
transformations?: Array<{
type: string;
header?: string;
value?: string;
domains?: string[];
append?: boolean;
[key: string]: any;
}>;
};
/** Delivery options (applies to forward/process/deliver) */
delivery?: {
/** Rate limit (messages per minute) */
rateLimit?: number;
/** Number of retry attempts */
retries?: number;
};
}
/**
* Context for route evaluation
*/
export interface IEmailContext {
/** The email being routed */
email: Email;
/** The SMTP session */
session: IExtendedSmtpSession;
}
/**
* Email domain configuration
*/
export interface IEmailDomainConfig {
/** Domain name */
domain: string;
/** DNS handling mode */
dnsMode: 'forward' | 'internal-dns' | 'external-dns';
/** DNS configuration based on mode */
dns?: {
/** For 'forward' mode */
forward?: {
/** Skip DNS validation (default: false) */
skipDnsValidation?: boolean;
/** Target server's expected domain */
targetDomain?: string;
};
/** For 'internal-dns' mode */
internal?: {
/** TTL for DNS records in seconds (default: 3600) */
ttl?: number;
/** MX record priority (default: 10) */
mxPriority?: number;
};
/** For 'external-dns' mode */
external?: {
/** Custom DNS servers (default: system DNS) */
servers?: string[];
/** Which records to validate (default: ['MX', 'SPF', 'DKIM', 'DMARC']) */
requiredRecords?: ('MX' | 'SPF' | 'DKIM' | 'DMARC')[];
};
};
/** Per-domain DKIM settings (DKIM always enabled) */
dkim?: {
/** DKIM selector (default: 'default') */
selector?: string;
/** Key size in bits (default: 2048) */
keySize?: number;
/** Automatically rotate keys (default: false) */
rotateKeys?: boolean;
/** Days between key rotations (default: 90) */
rotationInterval?: number;
};
/** Per-domain rate limits */
rateLimits?: {
outbound?: {
messagesPerMinute?: number;
messagesPerHour?: number;
messagesPerDay?: number;
};
inbound?: {
messagesPerMinute?: number;
connectionsPerIp?: number;
recipientsPerMessage?: number;
};
};
}

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvcm91dGluZy9pbnRlcmZhY2VzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIifQ==