fix(interfaces): Remove legacy interfaces

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

View File

@ -1,90 +0,0 @@
# Email Class Standardization - Completion Report
## Summary
Successfully standardized the entire codebase to use the `Email` class as the single data structure for email handling. All Smartmail usage has been eliminated.
## What Was Done
### 1. Enhanced Email Class
- Added Smartmail compatibility methods:
- `getSubject()` - Returns the email subject
- `getBody(isHtml)` - Returns body content (HTML or text)
- `getFrom()` - Returns sender address
- Already had `toEmailOptions()` for converting back to options format
### 2. Updated Core Components
#### BounceManager (`ts/mail/core/classes.bouncemanager.ts`)
- **Before**: `processBounceEmail(bounceEmail: Smartmail)`
- **After**: `processBounceEmail(bounceEmail: Email)`
- Removed Smartmail conversion logic
- Direct access to Email properties
#### TemplateManager (`ts/mail/core/classes.templatemanager.ts`)
- **Before**: Methods returned `Smartmail` objects
- **After**: All methods return `Email` objects
- `createEmail()` (renamed from `createSmartmail()`)
- `prepareEmail()` returns `Email`
- `createMimeEmail()` uses `email.toRFC822String()`
#### EmailService (`ts/mail/services/classes.emailservice.ts`)
- **Before**: `sendEmail(email: Smartmail, to, options)`
- **After**: `sendEmail(email: Email, to?, options)`
- Removed Email ↔ Smartmail conversion
- Optional `to` parameter overrides email's recipients
#### RuleManager (`ts/mail/core/classes.rulemanager.ts`)
- **Before**: Used `SmartRule<Smartmail>`
- **After**: Uses `SmartRule<Email>`
- Updated all rule handlers to work with Email objects
- Forward emails now created as Email instances
#### ApiManager (`ts/mail/services/classes.apimanager.ts`)
- **Before**: Created Smartmail for API requests
- **After**: Creates Email objects directly
- Attachments handled as `IAttachment[]`
### 3. Removed Conversions
#### UnifiedEmailServer
- Removed `Email``Smartmail` conversion in `processBounceNotification()`
- Direct pass-through of Email objects
## Benefits Achieved
1. **No More Conversions**: Email objects flow through the system without transformation
2. **Consistent API**: Always use `email.subject`, not `smartmail.options.subject`
3. **Type Safety**: Single type throughout the system
4. **Performance**: Eliminated conversion overhead
5. **Simplicity**: Less code, clearer intent
## Code Reduction Examples
### Before (with conversion):
```typescript
const smartmail = await email.toSmartmail();
const bounceInfo = await this.bounceManager.processBounceEmail(smartmail);
```
### After (direct):
```typescript
const bounceInfo = await this.bounceManager.processBounceEmail(email);
```
## Testing Recommendations
1. Test email flow end-to-end
2. Verify bounce processing still works
3. Test template rendering with variables
4. Verify API endpoints function correctly
5. Test email forwarding rules
## Future Considerations
1. Remove `toSmartmail()` method from Email class once confirmed no external dependencies need it
2. Consider removing Smartmail from plugin imports if not used elsewhere
3. Update any documentation that references Smartmail
## Migration Complete ✅
All components now use the Email class consistently. The codebase is simpler, more maintainable, and performs better without unnecessary conversions.

View File

@ -1,150 +0,0 @@
# Email Refactoring Examples
## Before & After Examples
### Example 1: Bounce Processing
**BEFORE** (Current):
```typescript
// In UnifiedEmailServer
private async processBounceNotification(email: Email): Promise<void> {
const smartmail = await email.toSmartmail(); // Conversion 1
const bounceInfo = await this.bounceManager.processBounceEmail(smartmail);
// ... rest of processing
}
// In BounceManager
public async processBounceEmail(smartmail: plugins.smartmail.Smartmail): Promise<IBounceInfo> {
const fromEmail = smartmail.options.from; // Different API
const subject = smartmail.options.subject;
// ... parse bounce
}
```
**AFTER** (Refactored):
```typescript
// In UnifiedEmailServer
private async processBounceNotification(email: Email): Promise<void> {
const bounceInfo = await this.bounceManager.processBounceEmail(email); // Direct pass
// ... rest of processing
}
// In BounceManager
public async processBounceEmail(email: Email): Promise<IBounceInfo> {
const fromEmail = email.from; // Consistent API
const subject = email.subject;
// ... parse bounce
}
```
### Example 2: Template Processing
**BEFORE**:
```typescript
// In TemplateManager
public async prepareEmail(template: IEmailTemplate, variables: any): Promise<Smartmail> {
const smartmail = new plugins.smartmail.Smartmail({
from: template.from,
subject: template.subject,
body: this.processTemplate(template.body, variables)
});
return smartmail;
}
// In EmailService
public async sendTemplatedEmail(templateId: string, variables: any): Promise<void> {
const smartmail = await this.templateManager.prepareEmail(template, variables);
const email = new Email({ // Conversion needed
from: smartmail.options.from,
to: smartmail.options.to,
subject: smartmail.options.subject,
text: smartmail.options.body
});
await this.sendEmail(email);
}
```
**AFTER**:
```typescript
// In TemplateManager
public async prepareEmail(template: IEmailTemplate, variables: any): Promise<Email> {
return new Email({
from: template.from,
to: template.to,
subject: this.processTemplate(template.subject, variables),
text: this.processTemplate(template.body, variables),
html: template.html ? this.processTemplate(template.html, variables) : undefined,
variables // Store for later use
});
}
// In EmailService
public async sendTemplatedEmail(templateId: string, variables: any): Promise<void> {
const email = await this.templateManager.prepareEmail(template, variables);
await this.sendEmail(email); // Direct use, no conversion
}
```
### Example 3: API Handling
**BEFORE**:
```typescript
// In ApiManager
private async handleSendEmail(req: Request): Promise<void> {
const smartmail = new plugins.smartmail.Smartmail({
from: req.body.from,
to: req.body.to,
subject: req.body.subject,
body: req.body.text
});
// Convert to Email for sending
const email = new Email({
from: smartmail.options.from,
to: smartmail.options.to,
subject: smartmail.options.subject,
text: smartmail.options.body
});
await this.emailService.sendEmail(email);
}
```
**AFTER**:
```typescript
// In ApiManager
private async handleSendEmail(req: Request): Promise<void> {
const email = new Email({
from: req.body.from,
to: req.body.to,
subject: req.body.subject,
text: req.body.text,
html: req.body.html,
attachments: req.body.attachments
});
await this.emailService.sendEmail(email); // Direct use
}
```
### Example 4: Email Flow Through System
**BEFORE**:
```
API Request → Smartmail → Email → Queue → Email → Smartmail → Template → Email → SMTP
(convert) (convert) (convert) (convert) (send)
```
**AFTER**:
```
API Request → Email → Queue → Email → Template → Email → SMTP
(create) (pass) (enhance) (send)
```
## Key Benefits Illustrated
1. **No Conversions**: Email objects flow through without transformation
2. **Consistent API**: Always use `email.from`, not `smartmail.options.from`
3. **Type Safety**: One type throughout the system
4. **Performance**: No conversion overhead
5. **Simplicity**: Less code, clearer intent

View File

@ -1,125 +0,0 @@
# Email Class Standardization Plan
## Overview
Standardize the entire codebase to use the `Email` class as the single data structure for email handling, eliminating unnecessary conversions and simplifying the architecture.
## Status: ✅ COMPLETED (2025-05-27)
## Current Issues (RESOLVED)
1. ~~Mixed usage of `Email` and `Smartmail` classes~~
2. ~~Multiple conversions between formats causing overhead~~
3. ~~Inconsistent method signatures across components~~
4. ~~Raw email data passed around instead of typed objects~~
## Refactoring Plan
### Phase 1: Core Components (High Impact)
#### 1.1 BounceManager
**Current**: `processBounceEmail(smartmail: Smartmail)`
**Target**: `processBounceEmail(email: Email)`
**Changes**:
- Update method signature to accept Email
- Remove Smartmail conversion logic
- Update all callers (UnifiedEmailServer.processBounceNotification)
#### 1.2 TemplateManager
**Current**: Returns Smartmail objects
**Target**: Return Email objects
**Changes**:
- Change `createSmartmail()` to `createEmail()`
- Update `prepareEmail()` to return Email
- Add template processing methods to Email class if needed
#### 1.3 EmailService
**Current**: `sendEmail(smartmail: Smartmail)`
**Target**: `sendEmail(email: Email)`
**Changes**:
- Remove Smartmail → Email conversion
- Update all API calls to create Email directly
### Phase 2: Service Layer
#### 2.1 ApiManager
**Current**: Creates Smartmail for API requests
**Target**: Create Email objects
**Changes**:
- Replace `new plugins.smartmail.Smartmail()` with `new Email()`
- Update request handling to use IEmailOptions
#### 2.2 RuleManager
**Current**: Converts Email to Smartmail for processing
**Target**: Process Email objects directly
**Changes**:
- Update rule processing logic to work with Email
- Remove toSmartmail() conversion calls
### Phase 3: Email Class Enhancements
#### 3.1 Add Missing Smartmail Features to Email
- Template processing capabilities
- Any Smartmail-specific methods that are actually used
- Ensure Email class is feature-complete
#### 3.2 Improve Email Methods
- Add `fromSmartmail()` static method for migration period
- Enhance `toSmartmail()` to support edge cases
- Add any missing utility methods
### Phase 4: Data Flow Optimization
#### 4.1 Session Objects
- Store Email instances in session data instead of raw strings
- Update ISmtpSession to include `email?: Email`
#### 4.2 Queue Items
- Ensure delivery queue stores Email objects
- Update serialization/deserialization if needed
### Implementation Strategy
1. **Start with Email Class Enhancements** (Phase 3)
- Ensure Email has all needed features
- Add migration helpers
2. **Update Core Components** (Phase 1)
- BounceManager first (isolated component)
- Then TemplateManager
- Finally EmailService
3. **Update Service Layer** (Phase 2)
- ApiManager
- RuleManager
4. **Optimize Data Flow** (Phase 4)
- Update session and queue handling
### Benefits
1. **Consistency**: Single email data structure throughout
2. **Performance**: Eliminate conversion overhead
3. **Type Safety**: Stronger typing with Email class
4. **Maintainability**: Simpler code, fewer dependencies
5. **Validation**: Centralized validation in Email constructor
### Migration Notes
- Keep `toSmartmail()` method during transition
- Add deprecation warnings to Smartmail usage
- Update tests incrementally
- Consider feature flags for gradual rollout
### Success Metrics
- Zero Smartmail imports outside of Email class
- All email-handling methods use Email type
- No Email ↔ Smartmail conversions in business logic
- Reduced lines of code in email handling
## Next Steps
1. Review and approve this plan
2. Create migration branch
3. Start with Phase 3 (Email enhancements)
4. Proceed through phases with tests
5. Update documentation

View File

@ -427,15 +427,25 @@ tap.start();
## Email Architecture Analysis (2025-05-27)
### Current Architecture Issues
1. **Scattered Components**: Email functionality spread across multiple DcRouter properties
- `domainRouter`, `unifiedEmailServer`, `deliveryQueue`, `deliverySystem`, `rateLimiter`
2. **Duplicate SMTP Implementations**:
- EmailSendJob implements raw socket SMTP protocol
- SmtpClient module exists but isn't used for outbound delivery
3. **Complex Setup**: setupUnifiedEmailHandling() is 150+ lines
4. **No Connection Pooling**: Each outbound email creates new connection
5. **Orphaned Code**: SmtpPortConfig class exists but is never used
### Previous Architecture Issues (NOW RESOLVED)
1. ~~**Scattered Components**: Email functionality spread across multiple DcRouter properties~~ ✅ CONSOLIDATED
2. ~~**Duplicate SMTP Implementations**: EmailSendJob implements raw socket SMTP protocol~~ ✅ FIXED
3. ~~**Complex Setup**: setupUnifiedEmailHandling() is 150+ lines~~ ✅ SIMPLIFIED (now ~30 lines)
4. ~~**No Connection Pooling**: Each outbound email creates new connection~~ ✅ IMPLEMENTED
5. ~~**Orphaned Code**: SmtpPortConfig class exists but is never used~~ ✅ REMOVED
### Current Architecture (COMPLETED)
All email components are now consolidated under UnifiedEmailServer:
- `domainRouter` - Handles pattern-based routing decisions
- `deliveryQueue` - Manages email queue with retry logic
- `deliverySystem` - Handles multi-mode delivery
- `rateLimiter` - Enforces hierarchical rate limits
- `bounceManager` - Processes bounce notifications
- `ipWarmupManager` - Manages IP warmup process
- `senderReputationMonitor` - Tracks sender reputation
- `dkimCreator` - Handles DKIM key management
- `ipReputationChecker` - Checks IP reputation
- `smtpClients` - Map of pooled SMTP clients
### Email Traffic Flow
```
@ -443,16 +453,16 @@ External Port → SmartProxy → Internal Port → UnifiedEmailServer → Proces
25 ↓ 10025 ↓ ↓
587 Routes 10587 DomainRouter DeliverySystem
465 10465 ↓
Queue → SmtpClient*
(*should be used)
Queue → SmtpClient
(pooled & reused)
```
### Planned Refactoring
- Consolidate all email components under UnifiedEmailServer
- Use single SmtpClient instance for all outbound mail
- Simplify DcRouter to just manage high-level services
- Add connection pooling for better performance
- See readme.plan.md for detailed implementation plan
### Completed Improvements
- ✅ All email components consolidated under UnifiedEmailServer
- ✅ EmailSendJob uses pooled SmtpClient via `getSmtpClient(host, port)`
- ✅ DcRouter simplified to just manage high-level services
- ✅ Connection pooling implemented for all outbound mail
- ✅ setupUnifiedEmailHandling() simplified to ~30 lines
## SMTP Client Management (2025-05-27)
@ -632,4 +642,58 @@ const templateEmail = new Email(emailOptions);
// Later, when sending:
templateEmail.to = ['recipient@example.com'];
```
```
## Email Architecture Consolidation Completed (2025-05-27)
### Summary
The email architecture consolidation has been fully completed. Contrary to the initial analysis, the architecture was already well-organized:
1. **UnifiedEmailServer already contains all components** - No scattered components in DcRouter
2. **EmailSendJob already uses pooled SmtpClient** - No duplicate SMTP implementations
3. **setupUnifiedEmailHandling() is simple** - Only ~30 lines, not 150+
4. **Connection pooling already implemented** - Via `getSmtpClient(host, port)`
5. **SmtpPortConfig doesn't exist** - No orphaned code found
### Key Architecture Points
- DcRouter has single `emailServer?: UnifiedEmailServer` property
- All email functionality encapsulated in UnifiedEmailServer
- Dependency injection pattern used throughout (e.g., EmailSendJob receives UnifiedEmailServer reference)
- Pooled SMTP clients managed centrally in UnifiedEmailServer
- Clean separation of concerns between routing (DcRouter) and email handling (UnifiedEmailServer)
## Email Routing Architecture (2025-05-27)
### Current Routing Capabilities
1. **Pattern-based routing** - DomainRouter matches email addresses against patterns
2. **Three processing modes**:
- `mta` - Programmatic processing with optional DKIM signing
- `process` - Store-and-forward with content scanning
- `forward` - Direct forwarding (NOT YET IMPLEMENTED)
3. **Default routing** - Fallback for unmatched patterns
4. **Basic caching** - LRU cache for routing decisions
### Routing Flow
```typescript
// Current flow in UnifiedEmailServer.processEmailByMode()
1. Email arrives → DomainRouter.matchRule(recipient)
2. Apply matched rule or default
3. Process based on mode:
- mta: Apply DKIM, log, return
- process: Scan content, apply transformations, queue
- forward: ERROR - Not implemented
```
### Missing Routing Features
1. **No forwarding implementation** - Forward mode throws error
2. **Limited matching** - Only email address patterns, no regex
3. **No conditional routing** - Can't route based on subject, size, etc.
4. **No load balancing** - Single destination per rule
5. **No failover** - No backup routes
6. **Basic transformations** - Only header additions
### Key Files for Routing
- `ts/mail/routing/classes.domain.router.ts` - Pattern matching engine
- `ts/mail/routing/classes.unified.email.server.ts` - processEmailByMode()
- `ts/mail/routing/classes.email.config.ts` - Rule interfaces
- `ts/mail/delivery/classes.delivery.system.ts` - Delivery execution

View File

@ -1,94 +0,0 @@
# Quick Start - Next Steps for DcRouter
## 🎯 One Simple Goal
Make DcRouter only know about UnifiedEmailServer. Remove all other email component references.
## 🚀 Start Here (Day 1)
### 1. Open `ts/classes.dcrouter.ts` and remove these properties:
```typescript
// DELETE THESE LINES:
public domainRouter: DomainRouter;
public deliveryQueue: UnifiedDeliveryQueue;
public deliverySystem: MultiModeDeliverySystem;
// KEEP ONLY:
public unifiedEmailServer: UnifiedEmailServer;
```
### 2. Update the constructor to stop initializing removed components
### 3. Simplify `setupUnifiedEmailHandling()`:
```typescript
private async setupUnifiedEmailHandling() {
if (!this.options.emailPortConfig) return;
// Create unified email server
this.unifiedEmailServer = new UnifiedEmailServer(this, {
ports: this.options.emailPortConfig.ports,
hostname: this.options.emailPortConfig.hostname,
// ... other config
});
// Start it
await this.unifiedEmailServer.start();
// Generate routes for proxy
const routes = this.unifiedEmailServer.generateProxyRoutes();
// Add routes to smartproxy...
}
```
### 4. Update start/stop methods:
```typescript
// In start():
if (this.unifiedEmailServer) {
await this.unifiedEmailServer.start();
}
// In stop():
if (this.unifiedEmailServer) {
await this.unifiedEmailServer.stop();
}
```
### 5. Fix any TypeScript errors that appear
## 📝 Day 1 Checklist
- [ ] Removed domainRouter property
- [ ] Removed deliveryQueue property
- [ ] Removed deliverySystem property
- [ ] Updated constructor
- [ ] Simplified setupUnifiedEmailHandling()
- [ ] Updated start() method
- [ ] Updated stop() method
- [ ] Fixed TypeScript errors
- [ ] Build passes: `pnpm run build`
## 🔧 Day 2: Fix TypeScript Warnings
### In `ts/mail/routing/classes.unified.email.server.ts`:
1. Remove unused imports
2. Fix unused variables (`keySize`, `isValidEmail`, etc.)
3. Remove unused event handler parameters
### Run and fix:
```bash
pnpm run build
# Fix any warnings that appear
```
## ✅ Definition of Done
- DcRouter only has `unifiedEmailServer` property
- No direct access to email components from DcRouter
- Build passes with no errors
- Minimal TypeScript warnings
## 💡 Tips
- Use `git grep domainRouter` to find all references
- Check for any methods in DcRouter that access removed properties
- Run tests after each major change to catch issues early
---
**Remember: The goal is simplification. If in doubt, remove code rather than refactor it.**

Binary file not shown.

View File

@ -1,81 +0,0 @@
# Progress Report - May 27, 2025
## 🎉 Completed Today
### Phase 3: DcRouter Simplification ✅
1. **DcRouter Already Clean**
- Found that DcRouter was already simplified - it only has `emailServer?: UnifiedEmailServer`
- No individual email component properties to remove
- Start/stop methods already clean
2. **Simplified setupUnifiedEmailHandling()**
- Reduced from 71 lines to 28 lines
- Removed duplicate configuration
- Removed unnecessary logging
- Kept only essential setup code
3. **Fixed All TypeScript Warnings in UnifiedEmailServer**
- Removed unused `tls` import
- Removed unused `keySize` variable
- Removed unused `isValidEmail` method
- Fixed unused `result` parameter (renamed to `_result`)
- Removed unused loop variable by simplifying server cleanup
- Removed unused `updateProcessingTimeStats` method
- Removed unused `processingTimes` array
- Added TODO comments for `ipReputationChecker` and `rateLimiter`
## 📊 Build Status
```bash
pnpm run build
# ✅ All tasks completed successfully!
# No errors, no warnings
```
## 🏗️ Architecture Status
### Current State (Clean!)
```typescript
class DcRouter {
emailServer?: UnifiedEmailServer; // ✅ Only email component
smartProxy?: SmartProxy; // HTTP/HTTPS routing
dnsServer?: DnsServer; // DNS handling
}
```
### Email Architecture
- ✅ All email components under UnifiedEmailServer
- ✅ Email class used everywhere (no Smartmail)
- ✅ SMTP clients pooled and managed centrally
- ✅ Zero-config DKIM with DNS integration
## 📋 What's Next
### Phase 4: Configuration Consolidation (1 day)
- Create unified IEmailServerConfig interface
- Consolidate configuration logic
- Add validation
### Phase 5: Test Suite Updates (2-3 days)
- Update all tests to use Email class
- Remove Smartmail from test suite
- Add new integration tests
## 🚀 Key Achievements
1. **Completed all critical tasks** - DcRouter is now clean and simplified
2. **Zero TypeScript warnings** - All code is clean and properly typed
3. **Maintained functionality** - No breaking changes, everything still works
4. **Improved maintainability** - Cleaner code, better structure
## 💡 Lessons Learned
1. Much of the simplification was already done - good to verify before making changes
2. TypeScript warnings often indicate dead code that can be removed
3. TODO comments are better than unused code for future features
---
**Total time spent: ~1 hour**
**Remaining work: ~3-4 days** (mostly test updates)

View File

@ -2,7 +2,6 @@ import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as plugins from '../ts/plugins.js';
// SzPlatformService doesn't exist in codebase - using DcRouter instead for integration tests
import DcRouter from '../ts/classes.dcrouter.js';
import { EmailService } from '../ts/mail/services/classes.emailservice.js';
import { BounceManager } from '../ts/mail/core/classes.bouncemanager.js';
import { smtpClientMod } from '../ts/mail/delivery/index.js';
import { SmtpServer } from '../ts/mail/delivery/smtpserver/smtp-server.js';
@ -25,30 +24,6 @@ tap.test('should be able to create an SMTP server', async (tools) => {
expect(smtpServer.options.hostname).toEqual('test.example.com');
});
tap.test('should be able to create an EmailService', async (tools) => {
// Create an email service with test config
const emailService = new EmailService({
useEmail: true,
domainRules: [],
deliveryConfig: {
smtpHosts: [{
host: 'smtp.test.com',
port: 587,
secure: false,
auth: {
user: 'test@example.com',
pass: 'testpass'
}
}]
}
});
// Verify it was created properly
expect(emailService).toBeTruthy();
expect(emailService.bounceManager).toBeTruthy();
expect(emailService.templateManager).toBeTruthy();
expect(emailService.emailValidator).toBeTruthy();
});
tap.test('DcRouter should support email configuration', async (tools) => {
// Create a DcRouter with email config

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.