fix(interfaces): Remove legacy interfaces
This commit is contained in:
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -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';
|
@ -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
|
||||
};
|
@ -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
|
||||
|
@ -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';
|
||||
|
@ -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();
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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.
|
Reference in New Issue
Block a user