import * as plugins from '../../plugins.js'; import * as paths from '../../paths.js'; import { MtaConnector } from '../delivery/classes.connector.mta.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 type { SzPlatformService } from '../../platformservice.js'; // Import MTA service import { MtaService } from '../delivery/classes.mta.js'; // Import configuration interfaces import type { IEmailConfig } from '../../config/email.config.js'; import { ConfigValidator, emailConfigSchema } from '../../config/index.js'; /** * Options for sending an email * @see ISendEmailOptions in MtaConnector */ export type ISendEmailOptions = import('../delivery/classes.connector.mta.js').ISendEmailOptions; /** * 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 { public platformServiceRef: SzPlatformService; // typedrouter public typedrouter = new plugins.typedrequest.TypedRouter(); // connectors public mtaConnector: MtaConnector; public qenv = new plugins.qenv.Qenv('./', '.nogit/'); // MTA service public mtaService: MtaService; // services public apiManager: ApiManager; public ruleManager: RuleManager; public templateManager: TemplateManager; public emailValidator: EmailValidator; public bounceManager: BounceManager; // configuration private config: IEmailConfig; constructor(platformServiceRefArg: SzPlatformService, options: IEmailConfig = {}) { this.platformServiceRef = platformServiceRefArg; this.platformServiceRef.typedrouter.addTypedRouter(this.typedrouter); // 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.useMta) { // Initialize MTA service this.mtaService = new MtaService(platformServiceRefArg, this.config.mtaConfig); // Initialize MTA connector this.mtaConnector = new MtaConnector(this); } // Initialize API manager and rule manager this.apiManager = new ApiManager(this); this.ruleManager = new RuleManager(this); // Set up MTA SMTP server webhook if using MTA if (this.config.useMta) { // The MTA SMTP server will handle incoming emails directly // through its SMTP protocol. No additional webhook needed. } } /** * 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 MTA service if enabled if (this.config.useMta && this.mtaService) { await this.mtaService.start(); logger.log('success', 'Started MTA service'); } logger.log('success', `Started email service`); } /** * Stop the email service */ public async stop() { // Stop MTA service if it's running if (this.config.useMta && this.mtaService) { await this.mtaService.stop(); logger.log('info', 'Stopped MTA service'); } logger.log('info', 'Stopped email service'); } /** * Send an email using the MTA * @param email The email to send * @param to Recipient(s) * @param options Additional options */ public async sendEmail( email: plugins.smartmail.Smartmail, to: string | string[], options: ISendEmailOptions = {} ): Promise { // Determine which connector to use if (this.config.useMta && this.mtaConnector) { return this.mtaConnector.sendEmail(email, to, options); } else { throw new Error('MTA 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 { try { // Get email from template const smartmail = await this.templateManager.prepareEmail(templateId, context); // Send the email return this.sendEmail(smartmail, 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 { return this.emailValidator.validate(email, options); } /** * Get email service statistics * @returns Service statistics in the format expected by the API */ public getStats(): plugins.servezoneInterfaces.platformservice.mta.IReq_GetEMailStats['response'] { // First generate detailed internal stats const detailedStats: IEmailServiceStats = { activeProviders: [] }; if (this.config.useMta) { detailedStats.activeProviders.push('mta'); detailedStats.mta = this.mtaService.getStats(); } // Convert detailed stats to the format expected by the API const apiStats: plugins.servezoneInterfaces.platformservice.mta.IReq_GetEMailStats['response'] = { 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; } }