fix(interfaces): Remove legacy interfaces

This commit is contained in:
2025-05-27 21:03:17 +00:00
parent 6aa54d974e
commit 8d59d617f1
19 changed files with 91 additions and 1863 deletions

View File

@ -1,4 +1,3 @@
import type { IBaseConfig, ITlsConfig, IQueueConfig, IRateLimitConfig, IMonitoringConfig } from './base.config.js';
/**
* MIGRATION GUIDE:
@ -157,476 +156,4 @@ export interface IDomainRule {
}>;
}
/**
* Email service configuration
* @deprecated Use IUnifiedEmailServerOptions from mail/routing/classes.unified.email.server.ts instead
* This interface is kept for backward compatibility only
*/
export interface IEmailConfig extends IBaseConfig {
/**
* Whether to enable email functionality
*/
useEmail?: boolean;
/**
* Whether to use MTA service (legacy compatibility)
*/
useMta?: boolean;
/**
* MTA configuration (legacy compatibility)
*/
mtaConfig?: IMtaConfig;
/**
* Whether the email server is behind SmartProxy (uses internal ports)
*/
behindSmartProxy?: boolean;
/**
* Email server configuration for both sending and receiving
*/
serverConfig?: IEmailServerConfig;
/**
* Email ports to listen on
*/
ports?: number[];
/**
* Email server hostname
*/
hostname?: string;
/**
* TLS configuration
*/
tls?: ITlsConfig;
/**
* Domain routing rules
*/
domainRules?: IDomainRule[];
/**
* Default processing mode for emails
*/
defaultMode?: EmailProcessingMode;
/**
* Default server for forwarding
*/
defaultServer?: string;
/**
* Default port for forwarding
*/
defaultPort?: number;
/**
* Default TLS setting for forwarding
*/
defaultTls?: boolean;
/**
* Maximum message size in bytes
*/
maxMessageSize?: number;
/**
* Authentication settings
*/
auth?: {
/**
* Whether authentication is required
*/
required?: boolean;
/**
* Supported authentication methods
*/
methods?: ('PLAIN' | 'LOGIN' | 'OAUTH2')[];
/**
* User credentials
*/
users?: Array<{username: string, password: string}>;
};
/**
* Queue configuration
*/
queue?: IQueueConfig;
/**
* Template configuration
*/
templateConfig?: {
/**
* Default sender email address
*/
from?: string;
/**
* Default reply-to email address
*/
replyTo?: string;
/**
* Default footer HTML
*/
footerHtml?: string;
/**
* Default footer text
*/
footerText?: string;
};
/**
* Whether to load templates from directory
*/
loadTemplatesFromDir?: boolean;
/**
* Directory path for email templates
*/
templatesDir?: string;
}
/**
* MTA configuration
* @deprecated Use IUnifiedEmailServerOptions from mail/routing/classes.unified.email.server.ts instead
* This interface is kept for backward compatibility only
*/
export interface IMtaConfig {
/**
* SMTP server configuration
*/
smtp?: {
/**
* Whether to enable the SMTP server
*/
enabled?: boolean;
/**
* Port to listen on
*/
port?: number;
/**
* SMTP server hostname
*/
hostname?: string;
/**
* Maximum allowed email size in bytes
*/
maxSize?: number;
};
/**
* TLS configuration
*/
tls?: ITlsConfig;
/**
* Outbound email configuration
*/
outbound?: {
/**
* Maximum concurrent sending jobs
*/
concurrency?: number;
/**
* Retry configuration
*/
retries?: {
/**
* Maximum number of retries per message
*/
max?: number;
/**
* Initial delay between retries (milliseconds)
*/
delay?: number;
/**
* Whether to use exponential backoff for retries
*/
useBackoff?: boolean;
};
/**
* Rate limiting configuration
*/
rateLimit?: IRateLimitConfig;
/**
* IP warmup configuration
*/
warmup?: {
/**
* Whether IP warmup is enabled
*/
enabled?: boolean;
/**
* IP addresses to warm up
*/
ipAddresses?: string[];
/**
* Target domains to warm up
*/
targetDomains?: string[];
/**
* Allocation policy to use
*/
allocationPolicy?: string;
/**
* Fallback percentage for ESP routing during warmup
*/
fallbackPercentage?: number;
};
/**
* Reputation monitoring configuration
*/
reputation?: IMonitoringConfig & {
/**
* Alert thresholds
*/
alertThresholds?: {
/**
* Minimum acceptable reputation score
*/
minReputationScore?: number;
/**
* Maximum acceptable complaint rate
*/
maxComplaintRate?: number;
};
};
};
/**
* Security settings
*/
security?: {
/**
* Whether to use DKIM signing
*/
useDkim?: boolean;
/**
* Whether to verify inbound DKIM signatures
*/
verifyDkim?: boolean;
/**
* Whether to verify SPF on inbound
*/
verifySpf?: boolean;
/**
* Whether to verify DMARC on inbound
*/
verifyDmarc?: boolean;
/**
* Whether to enforce DMARC policy
*/
enforceDmarc?: boolean;
/**
* Whether to use TLS for outbound when available
*/
useTls?: boolean;
/**
* Whether to require valid certificates
*/
requireValidCerts?: boolean;
/**
* Log level for email security events
*/
securityLogLevel?: 'info' | 'warn' | 'error';
/**
* Whether to check IP reputation for inbound emails
*/
checkIPReputation?: boolean;
/**
* Whether to scan content for malicious payloads
*/
scanContent?: boolean;
/**
* Action to take when malicious content is detected
*/
maliciousContentAction?: 'tag' | 'quarantine' | 'reject';
/**
* Minimum threat score to trigger action
*/
threatScoreThreshold?: number;
/**
* Whether to reject connections from high-risk IPs
*/
rejectHighRiskIPs?: boolean;
};
/**
* Domains configuration
*/
domains?: {
/**
* List of domains that this MTA will handle as local
*/
local?: string[];
/**
* Whether to auto-create DNS records
*/
autoCreateDnsRecords?: boolean;
/**
* DKIM selector to use
*/
dkimSelector?: string;
};
/**
* Queue configuration
*/
queue?: IQueueConfig;
}
/**
* Email server configuration
*/
export interface IEmailServerConfig {
/**
* Server ports
*/
ports?: number[];
/**
* Server hostname
*/
hostname?: string;
/**
* TLS configuration
*/
tls?: ITlsConfig;
/**
* Security settings
*/
security?: {
/**
* Whether to use DKIM signing
*/
useDkim?: boolean;
/**
* Whether to verify inbound DKIM signatures
*/
verifyDkim?: boolean;
/**
* Whether to verify SPF on inbound
*/
verifySpf?: boolean;
/**
* Whether to verify DMARC on inbound
*/
verifyDmarc?: boolean;
/**
* Whether to enforce DMARC policy
*/
enforceDmarc?: boolean;
/**
* Whether to use TLS for outbound when available
*/
useTls?: boolean;
/**
* Whether to require valid certificates
*/
requireValidCerts?: boolean;
/**
* Log level for email security events
*/
securityLogLevel?: 'info' | 'warn' | 'error';
/**
* Whether to check IP reputation for inbound emails
*/
checkIPReputation?: boolean;
/**
* Whether to scan content for malicious payloads
*/
scanContent?: boolean;
/**
* Action to take when malicious content is detected
*/
maliciousContentAction?: 'tag' | 'quarantine' | 'reject';
/**
* Minimum threat score to trigger action
*/
threatScoreThreshold?: number;
};
/**
* Delivery settings
*/
delivery?: {
/**
* Concurrency settings
*/
concurrency?: number;
/**
* Rate limiting configuration
*/
rateLimit?: IRateLimitConfig;
/**
* Retry configuration
*/
retries?: {
/**
* Maximum retry attempts
*/
max?: number;
/**
* Base delay between retries in milliseconds
*/
delay?: number;
/**
* Whether to use exponential backoff
*/
useBackoff?: boolean;
};
};
}

View File

@ -10,7 +10,6 @@ export * from './schemas.js';
// Re-export commonly used types
import type { IPlatformConfig } from './platform.config.js';
import type { IEmailConfig, IMtaConfig } from './email.config.js';
import type { ISmsConfig } from './sms.config.js';
import type {
IBaseConfig,
@ -38,46 +37,8 @@ export const defaultConfig: IPlatformConfig = {
port: 3000,
cors: true
},
email: {
useMta: true,
mtaConfig: {
smtp: {
enabled: true,
port: 25,
hostname: 'mta.lossless.one',
maxSize: 10 * 1024 * 1024 // 10MB
},
tls: {
domain: 'mta.lossless.one',
autoRenew: true
},
security: {
useDkim: true,
verifyDkim: true,
verifySpf: true,
verifyDmarc: true,
enforceDmarc: true,
useTls: true,
requireValidCerts: false,
securityLogLevel: 'warn',
checkIPReputation: true,
scanContent: true,
maliciousContentAction: 'tag',
threatScoreThreshold: 50,
rejectHighRiskIPs: false
},
domains: {
local: ['lossless.one'],
autoCreateDnsRecords: true,
dkimSelector: 'mta'
}
},
templateConfig: {
from: 'no-reply@lossless.one',
replyTo: 'support@lossless.one'
},
loadTemplatesFromDir: true
},
// Email configuration removed - use IUnifiedEmailServerOptions in DcRouter instead
email: undefined,
paths: {
dataDir: 'data',
logsDir: 'logs',
@ -89,8 +50,6 @@ export const defaultConfig: IPlatformConfig = {
// Export main types for convenience
export type {
IPlatformConfig,
IEmailConfig,
IMtaConfig,
ISmsConfig,
IBaseConfig,
ITlsConfig,

View File

@ -1,5 +1,4 @@
import type { IBaseConfig, IHttpServerConfig, IDatabaseConfig } from './base.config.js';
import type { IEmailConfig } from './email.config.js';
import type { ISmsConfig } from './sms.config.js';
/**
@ -19,8 +18,9 @@ export interface IPlatformConfig extends IBaseConfig {
/**
* Email service configuration
* @deprecated - Use IUnifiedEmailServerOptions in DcRouter instead
*/
email?: IEmailConfig;
email?: any;
/**
* SMS service configuration

View File

@ -1,195 +0,0 @@
import * as plugins from '../../plugins.js';
import { EmailService } from '../services/classes.emailservice.js';
import { logger } from '../../logger.js';
import { Email, type IEmailOptions } from './classes.email.js';
export class RuleManager {
public emailRef: EmailService;
public smartruleInstance = new plugins.smartrule.SmartRule<Email>();
constructor(emailRefArg: EmailService) {
this.emailRef = emailRefArg;
// Register handler for incoming emails if email server is enabled
if (this.emailRef.unifiedEmailServer) {
this.setupIncomingHandler();
}
}
/**
* Set up handler for incoming emails via the UnifiedEmailServer
*/
private setupIncomingHandler() {
// Use UnifiedEmailServer events for incoming emails
const incomingDir = './received';
// The UnifiedEmailServer raises events for incoming emails
// For backward compatibility, also watch the directory
this.watchIncomingEmails(incomingDir);
}
/**
* Watch directory for incoming emails (conceptual implementation)
*/
private watchIncomingEmails(directory: string) {
console.log(`Watching for incoming emails in: ${directory}`);
// Conceptual - in a real implementation, set up proper file watching
// or use UnifiedEmailServer events for incoming emails
/*
// Example using a file watcher:
const watcher = plugins.fs.watch(directory, async (eventType, filename) => {
if (eventType === 'rename' && filename.endsWith('.eml')) {
const filePath = plugins.path.join(directory, filename);
await this.handleIncomingEmail(filePath);
}
});
*/
// Set up event listener on UnifiedEmailServer if available
if (this.emailRef.unifiedEmailServer) {
this.emailRef.unifiedEmailServer.on('emailProcessed', (email: Email, mode, rule) => {
// Process email through rule system
this.smartruleInstance.makeDecision(email);
});
}
}
/**
* Handle incoming email received via email server
*/
public async handleIncomingEmail(emailPath: string) {
try {
// Process the email file using direct file access or access through UnifiedEmailServer
// For compatibility with existing code, we'll make a basic assumption about structure
const emailContent = await plugins.fs.promises.readFile(emailPath, 'utf8');
// Parse the email content into proper format
const parsedContent = await plugins.mailparser.simpleParser(emailContent);
// Create an Email object with the parsed content
const fromAddress = Array.isArray(parsedContent.from)
? parsedContent.from[0]?.text || 'unknown@example.com'
: parsedContent.from?.text || 'unknown@example.com';
const toAddress = Array.isArray(parsedContent.to)
? parsedContent.to[0]?.text || 'unknown@example.com'
: parsedContent.to?.text || 'unknown@example.com';
const fetchedEmail = new Email({
from: fromAddress,
to: toAddress,
subject: parsedContent.subject || '',
text: parsedContent.text || '',
html: parsedContent.html || undefined
});
console.log('=======================');
console.log('Received a mail:');
console.log(`From: ${fetchedEmail.from}`);
console.log(`Subject: ${fetchedEmail.subject}`);
console.log('^^^^^^^^^^^^^^^^^^^^^^^');
logger.log(
'info',
`email from ${fetchedEmail.from} with subject '${fetchedEmail.subject}'`,
{
eventType: 'receivedEmail',
provider: 'unified',
email: {
from: fetchedEmail.from,
subject: fetchedEmail.subject,
},
}
);
// Process with rules
this.smartruleInstance.makeDecision(fetchedEmail);
} catch (error) {
logger.log('error', `Failed to process incoming email: ${error.message}`, {
eventType: 'emailError',
provider: 'unified',
error: error.message
});
}
}
public async init() {
// Setup email rules
await this.createForwards();
}
/**
* creates the default forwards
*/
public async createForwards() {
const forwards: { originalToAddress: string[]; forwardedToAddress: string[] }[] = [];
console.log(`${forwards.length} forward rules configured:`);
for (const forward of forwards) {
console.log(forward);
}
for (const forward of forwards) {
this.smartruleInstance.createRule(
10,
async (emailArg: Email) => {
const matched = forward.originalToAddress.reduce<boolean>((prevValue, currentValue) => {
return emailArg.to.some(to => to.includes(currentValue)) || prevValue;
}, false);
if (matched) {
console.log('Forward rule matched');
console.log(forward);
return 'apply-continue';
} else {
return 'continue';
}
},
async (emailArg: Email) => {
forward.forwardedToAddress.map(async (toArg) => {
const forwardedEmail = new Email({
from: 'forwarder@mail.lossless.one',
to: toArg,
subject: `Forwarded mail for '${emailArg.to.join(', ')}'`,
html:
`
<div style="background: #CCC; padding: 10px; border-radius: 3px;">
<div><b>Original Sender:</b></div>
<div>${emailArg.from}</div>
<div><b>Original Recipient:</b></div>
<div>${emailArg.to.join(', ')}</div>
<div><b>Forwarded to:</b></div>
<div>${forward.forwardedToAddress.reduce<string>((pVal, cVal) => {
return `${pVal ? pVal + ', ' : ''}${cVal}`;
}, null)}</div>
<div><b>Subject:</b></div>
<div>${emailArg.getSubject()}</div>
<div><b>The original body can be found below.</b></div>
</div>
` + emailArg.getBody(true),
text: `Forwarded mail from ${emailArg.from} to ${emailArg.to.join(', ')}\n\n${emailArg.getBody()}`,
attachments: emailArg.attachments
});
// Use the EmailService's sendEmail method to send with the appropriate provider
await this.emailRef.sendEmail(forwardedEmail);
console.log(`forwarded mail to ${toArg}`);
logger.log(
'info',
`email from ${emailArg.from} to ${toArg} with subject '${emailArg.getSubject()}'`,
{
eventType: 'forwardedEmail',
email: {
from: emailArg.from,
to: emailArg.to.join(', '),
forwardedTo: toArg,
subject: emailArg.subject,
},
}
);
});
}
);
}
}
}

View File

@ -2,5 +2,4 @@
export * from './classes.email.js';
export * from './classes.emailvalidator.js';
export * from './classes.templatemanager.js';
export * from './classes.bouncemanager.js';
export * from './classes.rulemanager.js';
export * from './classes.bouncemanager.js';

View File

@ -9,21 +9,12 @@ import * as Delivery from './delivery/index.js';
export { Core, Delivery };
// For backward compatibility
// For direct imports
import { Email } from './core/classes.email.js';
import { EmailService } from './services/classes.emailservice.js';
import { BounceManager, BounceType, BounceCategory } from './core/classes.bouncemanager.js';
import { EmailValidator } from './core/classes.emailvalidator.js';
import { TemplateManager } from './core/classes.templatemanager.js';
import { RuleManager } from './core/classes.rulemanager.js';
import { ApiManager } from './services/classes.apimanager.js';
import { UnifiedEmailServer } from './routing/classes.unified.email.server.js';
import { DcRouter } from '../classes.dcrouter.js';
// Re-export with compatibility names
// Re-export commonly used classes
export {
EmailService as Email, // For backward compatibility with email/index.ts
ApiManager,
Email as EmailClass, // Provide the actual Email class under a different name
Email,
DcRouter
};

View File

@ -1,76 +1,8 @@
import * as plugins from '../../plugins.js';
import type { EmailProcessingMode } from '../delivery/interfaces.js';
// Re-export EmailProcessingMode type
export type { EmailProcessingMode };
/**
* Consolidated email configuration interface
*/
export interface IEmailConfig {
// Email server settings
ports: number[];
hostname: string;
domains?: string[]; // Domains to handle email for
maxMessageSize?: number;
debug?: boolean;
// 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;
};
// Outbound email settings
outbound?: {
maxConnections?: number;
connectionTimeout?: number;
socketTimeout?: number;
retryAttempts?: number;
defaultFrom?: string;
};
// DKIM settings
dkim?: {
enabled: boolean;
selector?: string;
keySize?: number;
};
// Rate limiting configuration
rateLimits?: any; // Using any to avoid circular dependency
// Advanced MTA settings
mtaGlobalOptions?: IMtaOptions;
}
/**
* Domain rule interface for pattern-based routing

View File

@ -17,7 +17,6 @@ import {
} from '../../deliverability/index.js';
import { DomainRouter } from './classes.domain.router.js';
import type {
IEmailConfig,
IDomainRule
} from './classes.email.config.js';
import { Email } from '../core/classes.email.js';

View File

@ -1,100 +0,0 @@
import * as plugins from '../../plugins.js';
import { EmailService } from './classes.emailservice.js';
import { logger } from '../../logger.js';
import { Email, type IEmailOptions, type IAttachment } from '../core/classes.email.js';
export class ApiManager {
public emailRef: EmailService;
public typedRouter = new plugins.typedrequest.TypedRouter();
constructor(emailRefArg: EmailService) {
this.emailRef = emailRefArg;
this.emailRef.typedrouter.addTypedRouter(this.typedRouter);
// Register API endpoints
this.registerApiEndpoints();
}
/**
* Register API endpoints for email functionality
*/
private registerApiEndpoints() {
// Register the SendEmail endpoint
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.platformservice.mta.IReq_SendEmail>(
new plugins.typedrequest.TypedHandler('sendEmail', async (requestData) => {
// Build attachments array
const attachments: IAttachment[] = [];
if (requestData.attachments) {
for (const attachment of requestData.attachments) {
attachments.push({
filename: attachment.name,
content: Buffer.from(attachment.binaryAttachmentString, 'binary'),
contentType: 'application/octet-stream'
});
}
}
// Create Email instance
const emailOptions: IEmailOptions = {
from: requestData.from,
to: requestData.to,
subject: requestData.title,
text: requestData.body,
attachments
};
const mailToSend = new Email(emailOptions);
// Send email through the service which will route to the appropriate connector
const emailId = await this.emailRef.sendEmail(mailToSend, undefined, {});
logger.log(
'info',
`sent an email to ${requestData.to} with subject '${mailToSend.subject}'`,
{
eventType: 'sentEmail',
email: {
to: requestData.to,
subject: mailToSend.subject,
},
}
);
return {
responseId: emailId,
};
})
);
// Add endpoint to check email status
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.platformservice.mta.IReq_CheckEmailStatus>(
new plugins.typedrequest.TypedHandler('checkEmailStatus', async (requestData) => {
// Check if we can get status - temporarily disabled during transition
// Simplified response during migration
const detailedStatus = {
status: 'UNKNOWN',
details: {
message: 'Email status checking is not available during system migration'
}
};
// Convert to the expected API response format
const apiResponse: plugins.servezoneInterfaces.platformservice.mta.IReq_CheckEmailStatus['response'] = {
status: detailedStatus.status.toString(), // Convert enum to string
details: {
message: detailedStatus.details?.message ||
`Status: ${detailedStatus.status}`
}
};
return apiResponse;
})
);
// Add statistics endpoint
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.platformservice.mta.IReq_GetEMailStats>(
new plugins.typedrequest.TypedHandler('getEmailStats', async () => {
return this.emailRef.getStats();
})
);
}
}

View File

@ -1,382 +0,0 @@
import * as plugins from '../../plugins.js';
import * as paths from '../../paths.js';
import { RuleManager } from '../core/classes.rulemanager.js';
import { ApiManager } from './classes.apimanager.js';
import { TemplateManager } from '../core/classes.templatemanager.js';
import { EmailValidator } from '../core/classes.emailvalidator.js';
import { BounceManager } from '../core/classes.bouncemanager.js';
import { logger } from '../../logger.js';
// Import types from router interfaces
import { UnifiedEmailServer } from '../routing/classes.unified.email.server.js';
import { DomainRouter } from '../routing/classes.domain.router.js';
import { Email } from '../core/classes.email.js';
// Import configuration interfaces
import type { IEmailConfig } from '../../config/email.config.js';
import { ConfigValidator, emailConfigSchema } from '../../config/index.js';
/**
* Options for sending an email
*/
export interface ISendEmailOptions {
/** Email sender override */
from?: string;
/** Optional reply-to address */
replyTo?: string;
/** CC recipients */
cc?: string | string[];
/** BCC recipients */
bcc?: string | string[];
/** Priority level */
priority?: 'high' | 'normal' | 'low';
/** Custom email headers */
headers?: Record<string, string>;
/** Whether to track opens */
trackOpens?: boolean;
/** Whether to track clicks */
trackClicks?: boolean;
/** Whether to skip suppression list check */
skipSuppressionCheck?: boolean;
/** Specific IP to use for sending */
ipAddress?: string;
/** Whether this is a transactional email */
isTransactional?: boolean;
}
/**
* Template context data for email templates
* @see ITemplateContext in TemplateManager
*/
export type ITemplateContext = import('../core/classes.templatemanager.js').ITemplateContext;
/**
* Validation options for email addresses
* Compatible with EmailValidator.validate options
*/
export interface IValidateEmailOptions {
/** Check MX records for the domain */
checkMx?: boolean;
/** Check if the domain is disposable (temporary email) */
checkDisposable?: boolean;
/** Check if the email is a role account (e.g., info@, support@) */
checkRole?: boolean;
/** Only check syntax without DNS lookups */
checkSyntaxOnly?: boolean;
}
/**
* Result of email validation
* @see IEmailValidationResult from EmailValidator
*/
export type IValidationResult = import('../core/classes.emailvalidator.js').IEmailValidationResult;
/**
* Email service statistics
*/
export interface IEmailServiceStats {
/** Active email providers */
activeProviders: string[];
/** MTA statistics, if MTA is active */
mta?: {
/** Service start time */
startTime: Date;
/** Total emails received */
emailsReceived: number;
/** Total emails sent */
emailsSent: number;
/** Total emails that failed to send */
emailsFailed: number;
/** Active SMTP connections */
activeConnections: number;
/** Current email queue size */
queueSize: number;
/** Certificate information */
certificateInfo?: {
/** Domain for the certificate */
domain: string;
/** Certificate expiration date */
expiresAt: Date;
/** Days until certificate expires */
daysUntilExpiry: number;
};
/** IP warmup information */
warmupInfo?: {
/** Whether IP warmup is enabled */
enabled: boolean;
/** Number of active IPs */
activeIPs: number;
/** Number of IPs in warmup phase */
inWarmupPhase: number;
/** Number of IPs that completed warmup */
completedWarmup: number;
};
/** Reputation monitoring information */
reputationInfo?: {
/** Whether reputation monitoring is enabled */
enabled: boolean;
/** Number of domains being monitored */
monitoredDomains: number;
/** Average reputation score across domains */
averageScore: number;
/** Number of domains with reputation issues */
domainsWithIssues: number;
};
/** Rate limiting information */
rateLimiting?: {
/** Global rate limit statistics */
global: {
/** Current available tokens */
availableTokens: number;
/** Maximum tokens per period */
maxTokens: number;
/** Current consumption rate */
consumptionRate: number;
/** Number of rate limiting events */
rateLimitEvents: number;
};
};
};
}
/**
* Email service with MTA support
*/
export class EmailService {
// typedrouter
public typedrouter = new plugins.typedrequest.TypedRouter();
// environment
public qenv = new plugins.qenv.Qenv('./', '.nogit/');
// unified email server
public unifiedEmailServer: UnifiedEmailServer;
public domainRouter: DomainRouter;
// services
public apiManager: ApiManager;
public ruleManager: RuleManager;
public templateManager: TemplateManager;
public emailValidator: EmailValidator;
public bounceManager: BounceManager;
// configuration
private config: IEmailConfig;
constructor(options: IEmailConfig = {}) {
// Validate and apply defaults to configuration
const validationResult = ConfigValidator.validate(options, emailConfigSchema);
if (!validationResult.valid) {
logger.warn(`Email service configuration has validation errors: ${validationResult.errors.join(', ')}`);
}
// Set configuration with defaults
this.config = validationResult.config;
// Initialize validator
this.emailValidator = new EmailValidator();
// Initialize bounce manager
this.bounceManager = new BounceManager();
// Initialize template manager
this.templateManager = new TemplateManager(this.config.templateConfig);
if (this.config.useEmail) {
// Initialize domain router for pattern matching
this.domainRouter = new DomainRouter({
domainRules: this.config.domainRules || [],
defaultMode: this.config.defaultMode || 'mta',
defaultServer: this.config.defaultServer,
defaultPort: this.config.defaultPort,
defaultTls: this.config.defaultTls
});
// Initialize UnifiedEmailServer
const useInternalPorts = this.config.behindSmartProxy || false;
const emailPorts = useInternalPorts ?
this.config.ports.map(p => p + 10000) : // Use internal ports (10025, etc.)
this.config.ports; // Use standard ports (25, etc.)
// Pass null as dcRouter since this is a standalone service
this.unifiedEmailServer = new UnifiedEmailServer(null as any, {
ports: emailPorts,
hostname: this.config.hostname || 'localhost',
domains: [this.config.hostname || 'localhost'], // Default to hostname
auth: this.config.auth,
tls: this.config.tls,
maxMessageSize: this.config.maxMessageSize,
domainRules: this.config.domainRules || [],
defaultMode: this.config.defaultMode || 'mta',
defaultServer: this.config.defaultServer,
defaultPort: this.config.defaultPort,
defaultTls: this.config.defaultTls
});
// Handle processed emails
this.unifiedEmailServer.on('emailProcessed', (email, mode, rule) => {
// Process email as needed (e.g., save to database, trigger notifications)
logger.log('info', `Email processed: ${email.subject}`);
});
}
// Initialize API manager and rule manager
this.apiManager = new ApiManager(this);
this.ruleManager = new RuleManager(this);
}
/**
* Start the email service
*/
public async start() {
// Initialize rule manager
await this.ruleManager.init();
// Load email templates if configured
if (this.config.loadTemplatesFromDir) {
try {
await this.templateManager.loadTemplatesFromDirectory(paths.emailTemplatesDir);
} catch (error) {
logger.log('error', `Failed to load email templates: ${error.message}`);
}
}
// Start UnifiedEmailServer if enabled
if (this.config.useEmail && this.unifiedEmailServer) {
await this.unifiedEmailServer.start();
logger.log('success', 'Started UnifiedEmailServer');
}
logger.log('success', `Started email service`);
}
/**
* Stop the email service
*/
public async stop() {
// Stop UnifiedEmailServer if it's running
if (this.config.useEmail && this.unifiedEmailServer) {
await this.unifiedEmailServer.stop();
logger.log('info', 'Stopped UnifiedEmailServer');
}
logger.log('info', 'Stopped email service');
}
/**
* Send an email using the UnifiedEmailServer
* @param email The email to send
* @param to Recipient(s) - if provided, overrides the email's 'to' field
* @param options Additional options
*/
public async sendEmail(
email: Email,
to?: string | string[],
options: ISendEmailOptions = {}
): Promise<string> {
if (this.config.useEmail && this.unifiedEmailServer) {
// If 'to' is provided, update the email's recipients
if (to) {
const recipients = Array.isArray(to) ? to : [to];
email.to = recipients;
}
// Determine the domain for routing
let matchedRule;
const recipientDomain = email.to[0].split('@')[1];
if (recipientDomain && this.domainRouter) {
matchedRule = this.domainRouter.matchRule(email.to[0]);
}
// Send through UnifiedEmailServer
return this.unifiedEmailServer.sendEmail(
email,
matchedRule?.mode || 'mta',
matchedRule
);
} else {
throw new Error('Email server not configured');
}
}
/**
* Send an email using a template
* @param templateId The template ID
* @param to Recipient email(s)
* @param context The template context data
* @param options Additional options
*/
public async sendTemplateEmail(
templateId: string,
to: string | string[],
context: ITemplateContext = {},
options: ISendEmailOptions = {}
): Promise<string> {
try {
// Get email from template
const email = await this.templateManager.prepareEmail(templateId, context);
// Send the email through UnifiedEmailServer
return this.sendEmail(email, to, options);
} catch (error) {
logger.log('error', `Failed to send template email: ${error.message}`, {
templateId,
to,
error: error.message
});
throw error;
}
}
/**
* Validate an email address
* @param email The email address to validate
* @param options Validation options
* @returns Validation result
*/
public async validateEmail(
email: string,
options: IValidateEmailOptions = {}
): Promise<IValidationResult> {
return this.emailValidator.validate(email, options);
}
/**
* Get email service statistics
* @returns Service statistics in the format expected by the API
*/
public getStats(): any {
// First generate detailed internal stats
const detailedStats: IEmailServiceStats = {
activeProviders: []
};
if (this.config.useEmail && this.unifiedEmailServer) {
detailedStats.activeProviders.push('unifiedEmail');
const serverStats = this.unifiedEmailServer.getStats();
detailedStats.mta = {
startTime: serverStats.startTime,
emailsReceived: serverStats.messages.processed,
emailsSent: serverStats.messages.delivered,
emailsFailed: serverStats.messages.failed,
activeConnections: serverStats.connections.current,
queueSize: 0 // Would need to be updated from deliveryQueue
};
}
// Convert detailed stats to the format expected by the API
const apiStats: any = {
totalEmailsSent: detailedStats.mta?.emailsSent || 0,
totalEmailsDelivered: detailedStats.mta?.emailsSent || 0, // Default to emails sent if we don't track delivery separately
totalEmailsBounced: detailedStats.mta?.emailsFailed || 0,
averageDeliveryTimeMs: 0, // We don't track this yet
lastUpdated: new Date().toISOString()
};
return apiStats;
}
}

View File

@ -1,3 +1,2 @@
// Email services
export * from './classes.emailservice.js';
export * from './classes.apimanager.js';
// Note: EmailService and ApiManager have been removed. Use UnifiedEmailServer directly.