feat(structure): Use unified Email class
This commit is contained in:
90
readme.email-refactoring-completion.md
Normal file
90
readme.email-refactoring-completion.md
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# 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.
|
150
readme.email-refactoring-examples.md
Normal file
150
readme.email-refactoring-examples.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# 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
|
125
readme.email-refactoring-plan.md
Normal file
125
readme.email-refactoring-plan.md
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
# 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
|
@@ -499,6 +499,41 @@ public getSmtpClient(host: string, port: number = 25): SmtpClient {
|
|||||||
- This provides access to SMTP clients, DKIM signing, and other shared functionality
|
- This provides access to SMTP clients, DKIM signing, and other shared functionality
|
||||||
- Example: `new EmailSendJob(emailServerRef, email, options)`
|
- Example: `new EmailSendJob(emailServerRef, email, options)`
|
||||||
|
|
||||||
|
## Email Class Standardization (2025-05-27) - COMPLETED
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
The entire codebase has been standardized to use the `Email` class as the single data structure for email handling. All Smartmail usage has been eliminated.
|
||||||
|
|
||||||
|
### Key Changes
|
||||||
|
1. **Email Class Enhanced** - Added compatibility methods: `getSubject()`, `getBody(isHtml)`, `getFrom()`
|
||||||
|
2. **BounceManager** - Now accepts `Email` objects directly
|
||||||
|
3. **TemplateManager** - Returns `Email` objects instead of Smartmail
|
||||||
|
4. **EmailService** - `sendEmail()` accepts `Email` objects
|
||||||
|
5. **RuleManager** - Uses `SmartRule<Email>` instead of `SmartRule<Smartmail>`
|
||||||
|
6. **ApiManager** - Creates `Email` objects for API requests
|
||||||
|
|
||||||
|
### Benefits
|
||||||
|
- No more Email ↔ Smartmail conversions
|
||||||
|
- Consistent API throughout (`email.subject` not `smartmail.options.subject`)
|
||||||
|
- Better performance (no conversion overhead)
|
||||||
|
- Simpler, more maintainable code
|
||||||
|
|
||||||
|
### Usage Pattern
|
||||||
|
```typescript
|
||||||
|
// Create email
|
||||||
|
const email = new Email({
|
||||||
|
from: 'sender@example.com',
|
||||||
|
to: 'recipient@example.com',
|
||||||
|
subject: 'Hello',
|
||||||
|
text: 'World'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pass directly through system
|
||||||
|
await bounceManager.processBounceEmail(email);
|
||||||
|
await templateManager.prepareEmail(templateId, context);
|
||||||
|
await emailService.sendEmail(email);
|
||||||
|
```
|
||||||
|
|
||||||
## Email Class Design Pattern (2025-05-27)
|
## Email Class Design Pattern (2025-05-27)
|
||||||
|
|
||||||
### Three-Interface Pattern for Email
|
### Three-Interface Pattern for Email
|
||||||
|
@@ -3,6 +3,7 @@ import * as paths from '../../paths.js';
|
|||||||
import { logger } from '../../logger.js';
|
import { logger } from '../../logger.js';
|
||||||
import { SecurityLogger, SecurityLogLevel, SecurityEventType } from '../../security/index.js';
|
import { SecurityLogger, SecurityLogLevel, SecurityEventType } from '../../security/index.js';
|
||||||
import { LRUCache } from 'lru-cache';
|
import { LRUCache } from 'lru-cache';
|
||||||
|
import type { Email } from './classes.email.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bounce types for categorizing the reasons for bounces
|
* Bounce types for categorizing the reasons for bounces
|
||||||
@@ -380,7 +381,7 @@ export class BounceManager {
|
|||||||
* @param bounceEmail The email containing bounce information
|
* @param bounceEmail The email containing bounce information
|
||||||
* @returns Processed bounce record or null if not a bounce
|
* @returns Processed bounce record or null if not a bounce
|
||||||
*/
|
*/
|
||||||
public async processBounceEmail(bounceEmail: plugins.smartmail.Smartmail<any>): Promise<BounceRecord | null> {
|
public async processBounceEmail(bounceEmail: Email): Promise<BounceRecord | null> {
|
||||||
try {
|
try {
|
||||||
// Check if this is a bounce notification
|
// Check if this is a bounce notification
|
||||||
const subject = bounceEmail.getSubject();
|
const subject = bounceEmail.getSubject();
|
||||||
@@ -435,7 +436,7 @@ export class BounceManager {
|
|||||||
if (!recipient) {
|
if (!recipient) {
|
||||||
logger.log('warn', 'Could not extract recipient from bounce notification', {
|
logger.log('warn', 'Could not extract recipient from bounce notification', {
|
||||||
subject,
|
subject,
|
||||||
sender: bounceEmail.options.from
|
sender: bounceEmail.from
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -449,7 +450,7 @@ export class BounceManager {
|
|||||||
// Create bounce data
|
// Create bounce data
|
||||||
const bounceData: Partial<BounceRecord> = {
|
const bounceData: Partial<BounceRecord> = {
|
||||||
recipient,
|
recipient,
|
||||||
sender: bounceEmail.options.from,
|
sender: bounceEmail.from,
|
||||||
domain: recipient.split('@')[1],
|
domain: recipient.split('@')[1],
|
||||||
subject: bounceEmail.getSubject(),
|
subject: bounceEmail.getSubject(),
|
||||||
diagnosticCode,
|
diagnosticCode,
|
||||||
|
@@ -640,6 +640,34 @@ export class Email {
|
|||||||
return this.from;
|
return this.from;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the subject (Smartmail compatibility method)
|
||||||
|
* @returns The email subject
|
||||||
|
*/
|
||||||
|
public getSubject(): string {
|
||||||
|
return this.subject;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the body content (Smartmail compatibility method)
|
||||||
|
* @param isHtml Whether to return HTML content if available
|
||||||
|
* @returns The email body (HTML if requested and available, otherwise plain text)
|
||||||
|
*/
|
||||||
|
public getBody(isHtml: boolean = false): string {
|
||||||
|
if (isHtml && this.html) {
|
||||||
|
return this.html;
|
||||||
|
}
|
||||||
|
return this.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the from address (Smartmail compatibility method)
|
||||||
|
* @returns The sender email address
|
||||||
|
*/
|
||||||
|
public getFrom(): string {
|
||||||
|
return this.from;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the message ID
|
* Get the message ID
|
||||||
* @returns The message ID
|
* @returns The message ID
|
||||||
|
@@ -1,12 +1,11 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import { EmailService } from '../services/classes.emailservice.js';
|
import { EmailService } from '../services/classes.emailservice.js';
|
||||||
import { logger } from '../../logger.js';
|
import { logger } from '../../logger.js';
|
||||||
|
import { Email, type IEmailOptions } from './classes.email.js';
|
||||||
|
|
||||||
export class RuleManager {
|
export class RuleManager {
|
||||||
public emailRef: EmailService;
|
public emailRef: EmailService;
|
||||||
public smartruleInstance = new plugins.smartrule.SmartRule<
|
public smartruleInstance = new plugins.smartrule.SmartRule<Email>();
|
||||||
plugins.smartmail.Smartmail<any>
|
|
||||||
>();
|
|
||||||
|
|
||||||
constructor(emailRefArg: EmailService) {
|
constructor(emailRefArg: EmailService) {
|
||||||
this.emailRef = emailRefArg;
|
this.emailRef = emailRefArg;
|
||||||
@@ -50,19 +49,9 @@ export class RuleManager {
|
|||||||
|
|
||||||
// Set up event listener on UnifiedEmailServer if available
|
// Set up event listener on UnifiedEmailServer if available
|
||||||
if (this.emailRef.unifiedEmailServer) {
|
if (this.emailRef.unifiedEmailServer) {
|
||||||
this.emailRef.unifiedEmailServer.on('emailProcessed', (email, mode, rule) => {
|
this.emailRef.unifiedEmailServer.on('emailProcessed', (email: Email, mode, rule) => {
|
||||||
// Process email through rule system
|
// Process email through rule system
|
||||||
// Convert Email to Smartmail format
|
this.smartruleInstance.makeDecision(email);
|
||||||
// Convert Email object to Smartmail format
|
|
||||||
const smartmail = new plugins.smartmail.Smartmail({
|
|
||||||
// Use standard fields
|
|
||||||
from: email.from,
|
|
||||||
subject: email.subject || '',
|
|
||||||
body: email.text || email.html || ''
|
|
||||||
});
|
|
||||||
|
|
||||||
// Process with rules
|
|
||||||
this.smartruleInstance.makeDecision(smartmail);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -78,36 +67,44 @@ export class RuleManager {
|
|||||||
// Parse the email content into proper format
|
// Parse the email content into proper format
|
||||||
const parsedContent = await plugins.mailparser.simpleParser(emailContent);
|
const parsedContent = await plugins.mailparser.simpleParser(emailContent);
|
||||||
|
|
||||||
// Create a Smartmail object with the parsed content
|
// Create an Email object with the parsed content
|
||||||
const fetchedSmartmail = new plugins.smartmail.Smartmail({
|
const fromAddress = Array.isArray(parsedContent.from)
|
||||||
// Use standardized fields that are always available
|
? parsedContent.from[0]?.text || 'unknown@example.com'
|
||||||
body: parsedContent.text || parsedContent.html || '',
|
: 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 || '',
|
subject: parsedContent.subject || '',
|
||||||
// Use a default from address if not present
|
text: parsedContent.text || '',
|
||||||
from: parsedContent.from?.text || 'unknown@example.com'
|
html: parsedContent.html || undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('=======================');
|
console.log('=======================');
|
||||||
console.log('Received a mail:');
|
console.log('Received a mail:');
|
||||||
console.log(`From: ${fetchedSmartmail.options?.from || 'unknown'}`);
|
console.log(`From: ${fetchedEmail.from}`);
|
||||||
console.log(`Subject: ${fetchedSmartmail.options?.subject || 'no subject'}`);
|
console.log(`Subject: ${fetchedEmail.subject}`);
|
||||||
console.log('^^^^^^^^^^^^^^^^^^^^^^^');
|
console.log('^^^^^^^^^^^^^^^^^^^^^^^');
|
||||||
|
|
||||||
logger.log(
|
logger.log(
|
||||||
'info',
|
'info',
|
||||||
`email from ${fetchedSmartmail.options?.from || 'unknown'} with subject '${fetchedSmartmail.options?.subject || 'no subject'}'`,
|
`email from ${fetchedEmail.from} with subject '${fetchedEmail.subject}'`,
|
||||||
{
|
{
|
||||||
eventType: 'receivedEmail',
|
eventType: 'receivedEmail',
|
||||||
provider: 'unified',
|
provider: 'unified',
|
||||||
email: {
|
email: {
|
||||||
from: fetchedSmartmail.options?.from || 'unknown',
|
from: fetchedEmail.from,
|
||||||
subject: fetchedSmartmail.options?.subject || 'no subject',
|
subject: fetchedEmail.subject,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Process with rules
|
// Process with rules
|
||||||
this.smartruleInstance.makeDecision(fetchedSmartmail);
|
this.smartruleInstance.makeDecision(fetchedEmail);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to process incoming email: ${error.message}`, {
|
logger.log('error', `Failed to process incoming email: ${error.message}`, {
|
||||||
eventType: 'emailError',
|
eventType: 'emailError',
|
||||||
@@ -135,9 +132,9 @@ export class RuleManager {
|
|||||||
for (const forward of forwards) {
|
for (const forward of forwards) {
|
||||||
this.smartruleInstance.createRule(
|
this.smartruleInstance.createRule(
|
||||||
10,
|
10,
|
||||||
async (smartmailArg) => {
|
async (emailArg: Email) => {
|
||||||
const matched = forward.originalToAddress.reduce<boolean>((prevValue, currentValue) => {
|
const matched = forward.originalToAddress.reduce<boolean>((prevValue, currentValue) => {
|
||||||
return smartmailArg.options.creationObjectRef.To.includes(currentValue) || prevValue;
|
return emailArg.to.some(to => to.includes(currentValue)) || prevValue;
|
||||||
}, false);
|
}, false);
|
||||||
if (matched) {
|
if (matched) {
|
||||||
console.log('Forward rule matched');
|
console.log('Forward rule matched');
|
||||||
@@ -147,48 +144,46 @@ export class RuleManager {
|
|||||||
return 'continue';
|
return 'continue';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async (smartmailArg: plugins.smartmail.Smartmail<any>) => {
|
async (emailArg: Email) => {
|
||||||
forward.forwardedToAddress.map(async (toArg) => {
|
forward.forwardedToAddress.map(async (toArg) => {
|
||||||
const forwardedSmartMail = new plugins.smartmail.Smartmail({
|
const forwardedEmail = new Email({
|
||||||
body:
|
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 style="background: #CCC; padding: 10px; border-radius: 3px;">
|
||||||
<div><b>Original Sender:</b></div>
|
<div><b>Original Sender:</b></div>
|
||||||
<div>${smartmailArg.options.creationObjectRef.From}</div>
|
<div>${emailArg.from}</div>
|
||||||
<div><b>Original Recipient:</b></div>
|
<div><b>Original Recipient:</b></div>
|
||||||
<div>${smartmailArg.options.creationObjectRef.To}</div>
|
<div>${emailArg.to.join(', ')}</div>
|
||||||
<div><b>Forwarded to:</b></div>
|
<div><b>Forwarded to:</b></div>
|
||||||
<div>${forward.forwardedToAddress.reduce<string>((pVal, cVal) => {
|
<div>${forward.forwardedToAddress.reduce<string>((pVal, cVal) => {
|
||||||
return `${pVal ? pVal + ', ' : ''}${cVal}`;
|
return `${pVal ? pVal + ', ' : ''}${cVal}`;
|
||||||
}, null)}</div>
|
}, null)}</div>
|
||||||
<div><b>Subject:</b></div>
|
<div><b>Subject:</b></div>
|
||||||
<div>${smartmailArg.getSubject()}</div>
|
<div>${emailArg.getSubject()}</div>
|
||||||
<div><b>The original body can be found below.</b></div>
|
<div><b>The original body can be found below.</b></div>
|
||||||
</div>
|
</div>
|
||||||
` + smartmailArg.getBody(),
|
` + emailArg.getBody(true),
|
||||||
from: 'forwarder@mail.lossless.one',
|
text: `Forwarded mail from ${emailArg.from} to ${emailArg.to.join(', ')}\n\n${emailArg.getBody()}`,
|
||||||
subject: `Forwarded mail for '${smartmailArg.options.creationObjectRef.To}'`,
|
attachments: emailArg.attachments
|
||||||
});
|
});
|
||||||
for (const attachment of smartmailArg.attachments) {
|
|
||||||
forwardedSmartMail.addAttachment(attachment);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the EmailService's sendEmail method to send with the appropriate provider
|
// Use the EmailService's sendEmail method to send with the appropriate provider
|
||||||
await this.emailRef.sendEmail(forwardedSmartMail, toArg);
|
await this.emailRef.sendEmail(forwardedEmail);
|
||||||
|
|
||||||
console.log(`forwarded mail to ${toArg}`);
|
console.log(`forwarded mail to ${toArg}`);
|
||||||
logger.log(
|
logger.log(
|
||||||
'info',
|
'info',
|
||||||
`email from ${
|
`email from ${emailArg.from} to ${toArg} with subject '${emailArg.getSubject()}'`,
|
||||||
smartmailArg.options.creationObjectRef.From
|
|
||||||
} to ${toArg} with subject '${smartmailArg.getSubject()}'`,
|
|
||||||
{
|
{
|
||||||
eventType: 'forwardedEmail',
|
eventType: 'forwardedEmail',
|
||||||
email: {
|
email: {
|
||||||
from: smartmailArg.options.creationObjectRef.From,
|
from: emailArg.from,
|
||||||
to: smartmailArg.options.creationObjectRef.To,
|
to: emailArg.to.join(', '),
|
||||||
forwardedTo: toArg,
|
forwardedTo: toArg,
|
||||||
subject: smartmailArg.options.creationObjectRef.Subject,
|
subject: emailArg.subject,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import * as paths from '../../paths.js';
|
import * as paths from '../../paths.js';
|
||||||
import { logger } from '../../logger.js';
|
import { logger } from '../../logger.js';
|
||||||
|
import { Email, type IEmailOptions, type IAttachment } from './classes.email.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Email template type definition
|
* Email template type definition
|
||||||
@@ -40,7 +41,7 @@ export enum TemplateCategory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enhanced template manager using smartmail's capabilities
|
* Enhanced template manager using Email class for template rendering
|
||||||
*/
|
*/
|
||||||
export class TemplateManager {
|
export class TemplateManager {
|
||||||
private templates: Map<string, IEmailTemplate> = new Map();
|
private templates: Map<string, IEmailTemplate> = new Map();
|
||||||
@@ -191,80 +192,74 @@ export class TemplateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Smartmail instance from a template
|
* Create an Email instance from a template
|
||||||
* @param templateId The template ID
|
* @param templateId The template ID
|
||||||
* @param context The template context data
|
* @param context The template context data
|
||||||
* @returns A configured Smartmail instance
|
* @returns A configured Email instance
|
||||||
*/
|
*/
|
||||||
public async createSmartmail<T = any>(
|
public async createEmail<T = any>(
|
||||||
templateId: string,
|
templateId: string,
|
||||||
context?: ITemplateContext
|
context?: ITemplateContext
|
||||||
): Promise<plugins.smartmail.Smartmail<T>> {
|
): Promise<Email> {
|
||||||
const template = this.getTemplate(templateId);
|
const template = this.getTemplate(templateId);
|
||||||
|
|
||||||
if (!template) {
|
if (!template) {
|
||||||
throw new Error(`Template with ID '${templateId}' not found`);
|
throw new Error(`Template with ID '${templateId}' not found`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Smartmail instance with template content
|
// Build attachments array for Email
|
||||||
const smartmail = new plugins.smartmail.Smartmail<T>({
|
const attachments: IAttachment[] = [];
|
||||||
from: template.from || this.defaultConfig.from,
|
|
||||||
subject: template.subject,
|
|
||||||
body: template.bodyHtml || template.bodyText || '',
|
|
||||||
creationObjectRef: context as T
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add any template attachments
|
|
||||||
if (template.attachments && template.attachments.length > 0) {
|
if (template.attachments && template.attachments.length > 0) {
|
||||||
for (const attachment of template.attachments) {
|
for (const attachment of template.attachments) {
|
||||||
// Load attachment file
|
|
||||||
try {
|
try {
|
||||||
const attachmentPath = plugins.path.isAbsolute(attachment.path)
|
const attachmentPath = plugins.path.isAbsolute(attachment.path)
|
||||||
? attachment.path
|
? attachment.path
|
||||||
: plugins.path.join(paths.MtaAttachmentsDir, attachment.path);
|
: plugins.path.join(paths.MtaAttachmentsDir, attachment.path);
|
||||||
|
|
||||||
// Use appropriate SmartFile method - either read from file or create with empty buffer
|
// Read the file
|
||||||
// For a file path, use the fromFilePath static method
|
const fileBuffer = await plugins.fs.promises.readFile(attachmentPath);
|
||||||
const file = await plugins.smartfile.SmartFile.fromFilePath(attachmentPath);
|
|
||||||
|
|
||||||
// Set content type if specified
|
attachments.push({
|
||||||
if (attachment.contentType) {
|
filename: attachment.name,
|
||||||
(file as any).contentType = attachment.contentType;
|
content: fileBuffer,
|
||||||
}
|
contentType: attachment.contentType || 'application/octet-stream'
|
||||||
|
});
|
||||||
smartmail.addAttachment(file);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to add attachment '${attachment.name}': ${error.message}`);
|
logger.log('error', `Failed to add attachment '${attachment.name}': ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply template variables if context provided
|
// Create Email instance with template content
|
||||||
if (context) {
|
const emailOptions: IEmailOptions = {
|
||||||
// Use applyVariables from smartmail v2.1.0+
|
from: template.from || this.defaultConfig.from,
|
||||||
smartmail.applyVariables(context);
|
subject: template.subject,
|
||||||
}
|
text: template.bodyText || '',
|
||||||
|
html: template.bodyHtml,
|
||||||
|
to: '', // Will be set when sending
|
||||||
|
attachments,
|
||||||
|
variables: context || {}
|
||||||
|
};
|
||||||
|
|
||||||
return smartmail;
|
return new Email(emailOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and completely process a Smartmail instance from a template
|
* Create and completely process an Email instance from a template
|
||||||
* @param templateId The template ID
|
* @param templateId The template ID
|
||||||
* @param context The template context data
|
* @param context The template context data
|
||||||
* @returns A complete, processed Smartmail instance ready to send
|
* @returns A complete, processed Email instance ready to send
|
||||||
*/
|
*/
|
||||||
public async prepareEmail<T = any>(
|
public async prepareEmail<T = any>(
|
||||||
templateId: string,
|
templateId: string,
|
||||||
context: ITemplateContext = {}
|
context: ITemplateContext = {}
|
||||||
): Promise<plugins.smartmail.Smartmail<T>> {
|
): Promise<Email> {
|
||||||
const smartmail = await this.createSmartmail<T>(templateId, context);
|
const email = await this.createEmail<T>(templateId, context);
|
||||||
|
|
||||||
// Pre-compile all mustache templates (subject, body)
|
// Email class processes variables when needed, no pre-compilation required
|
||||||
smartmail.getSubject();
|
|
||||||
smartmail.getBody();
|
|
||||||
|
|
||||||
return smartmail;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -277,8 +272,8 @@ export class TemplateManager {
|
|||||||
templateId: string,
|
templateId: string,
|
||||||
context: ITemplateContext = {}
|
context: ITemplateContext = {}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const smartmail = await this.prepareEmail(templateId, context);
|
const email = await this.prepareEmail(templateId, context);
|
||||||
return smartmail.toMimeFormat();
|
return email.toRFC822String(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1160,17 +1160,8 @@ export class UnifiedEmailServer extends EventEmitter {
|
|||||||
logger.log('info', 'Processing potential bounce notification email');
|
logger.log('info', 'Processing potential bounce notification email');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Convert Email to Smartmail format for bounce processing
|
// Process as a bounce notification (no conversion needed anymore)
|
||||||
const smartmailEmail = new plugins.smartmail.Smartmail({
|
const bounceRecord = await this.bounceManager.processBounceEmail(bounceEmail);
|
||||||
from: bounceEmail.from,
|
|
||||||
to: [bounceEmail.to[0]], // Ensure to is an array with at least one recipient
|
|
||||||
subject: bounceEmail.subject,
|
|
||||||
body: bounceEmail.text, // Smartmail uses 'body' instead of 'text'
|
|
||||||
htmlBody: bounceEmail.html // Smartmail uses 'htmlBody' instead of 'html'
|
|
||||||
});
|
|
||||||
|
|
||||||
// Process as a bounce notification
|
|
||||||
const bounceRecord = await this.bounceManager.processBounceEmail(smartmailEmail);
|
|
||||||
|
|
||||||
if (bounceRecord) {
|
if (bounceRecord) {
|
||||||
logger.log('info', `Successfully processed bounce notification for ${bounceRecord.recipient}`, {
|
logger.log('info', `Successfully processed bounce notification for ${bounceRecord.recipient}`, {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import * as plugins from '../../plugins.js';
|
import * as plugins from '../../plugins.js';
|
||||||
import { EmailService } from './classes.emailservice.js';
|
import { EmailService } from './classes.emailservice.js';
|
||||||
import { logger } from '../../logger.js';
|
import { logger } from '../../logger.js';
|
||||||
|
import { Email, type IEmailOptions, type IAttachment } from '../core/classes.email.js';
|
||||||
|
|
||||||
export class ApiManager {
|
export class ApiManager {
|
||||||
public emailRef: EmailService;
|
public emailRef: EmailService;
|
||||||
@@ -21,35 +22,40 @@ export class ApiManager {
|
|||||||
// Register the SendEmail endpoint
|
// Register the SendEmail endpoint
|
||||||
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.platformservice.mta.IReq_SendEmail>(
|
this.typedRouter.addTypedHandler<plugins.servezoneInterfaces.platformservice.mta.IReq_SendEmail>(
|
||||||
new plugins.typedrequest.TypedHandler('sendEmail', async (requestData) => {
|
new plugins.typedrequest.TypedHandler('sendEmail', async (requestData) => {
|
||||||
const mailToSend = new plugins.smartmail.Smartmail({
|
// Build attachments array
|
||||||
body: requestData.body,
|
const attachments: IAttachment[] = [];
|
||||||
from: requestData.from,
|
|
||||||
subject: requestData.title,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (requestData.attachments) {
|
if (requestData.attachments) {
|
||||||
for (const attachment of requestData.attachments) {
|
for (const attachment of requestData.attachments) {
|
||||||
mailToSend.addAttachment(
|
attachments.push({
|
||||||
await plugins.smartfile.SmartFile.fromString(
|
filename: attachment.name,
|
||||||
attachment.name,
|
content: Buffer.from(attachment.binaryAttachmentString, 'binary'),
|
||||||
attachment.binaryAttachmentString,
|
contentType: 'application/octet-stream'
|
||||||
'binary'
|
});
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
// Send email through the service which will route to the appropriate connector
|
||||||
const emailId = await this.emailRef.sendEmail(mailToSend, requestData.to, {});
|
const emailId = await this.emailRef.sendEmail(mailToSend, undefined, {});
|
||||||
|
|
||||||
logger.log(
|
logger.log(
|
||||||
'info',
|
'info',
|
||||||
`sent an email to ${requestData.to} with subject '${mailToSend.getSubject()}'`,
|
`sent an email to ${requestData.to} with subject '${mailToSend.subject}'`,
|
||||||
{
|
{
|
||||||
eventType: 'sentEmail',
|
eventType: 'sentEmail',
|
||||||
email: {
|
email: {
|
||||||
to: requestData.to,
|
to: requestData.to,
|
||||||
subject: mailToSend.getSubject(),
|
subject: mailToSend.subject,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@@ -269,44 +269,31 @@ export class EmailService {
|
|||||||
/**
|
/**
|
||||||
* Send an email using the UnifiedEmailServer
|
* Send an email using the UnifiedEmailServer
|
||||||
* @param email The email to send
|
* @param email The email to send
|
||||||
* @param to Recipient(s)
|
* @param to Recipient(s) - if provided, overrides the email's 'to' field
|
||||||
* @param options Additional options
|
* @param options Additional options
|
||||||
*/
|
*/
|
||||||
public async sendEmail(
|
public async sendEmail(
|
||||||
email: plugins.smartmail.Smartmail<any>,
|
email: Email,
|
||||||
to: string | string[],
|
to?: string | string[],
|
||||||
options: ISendEmailOptions = {}
|
options: ISendEmailOptions = {}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
if (this.config.useEmail && this.unifiedEmailServer) {
|
if (this.config.useEmail && this.unifiedEmailServer) {
|
||||||
// Convert Smartmail to Email format
|
// If 'to' is provided, update the email's recipients
|
||||||
|
if (to) {
|
||||||
const recipients = Array.isArray(to) ? to : [to];
|
const recipients = Array.isArray(to) ? to : [to];
|
||||||
|
email.to = recipients;
|
||||||
// Access Smartmail properties using any type to bypass TypeScript checking
|
}
|
||||||
const emailAny = email as any;
|
|
||||||
|
|
||||||
const emailObj = new Email({
|
|
||||||
from: emailAny.from,
|
|
||||||
to: recipients,
|
|
||||||
subject: emailAny.subject,
|
|
||||||
text: emailAny.body || emailAny.text,
|
|
||||||
html: emailAny.htmlBody || emailAny.html,
|
|
||||||
attachments: emailAny.attachments ? emailAny.attachments.map((att: any) => ({
|
|
||||||
filename: att.filename,
|
|
||||||
content: att.contents || att.content,
|
|
||||||
contentType: att.contentType
|
|
||||||
})) : []
|
|
||||||
});
|
|
||||||
|
|
||||||
// Determine the domain for routing
|
// Determine the domain for routing
|
||||||
let matchedRule;
|
let matchedRule;
|
||||||
const recipientDomain = recipients[0].split('@')[1];
|
const recipientDomain = email.to[0].split('@')[1];
|
||||||
if (recipientDomain && this.domainRouter) {
|
if (recipientDomain && this.domainRouter) {
|
||||||
matchedRule = this.domainRouter.matchRule(recipients[0]);
|
matchedRule = this.domainRouter.matchRule(email.to[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send through UnifiedEmailServer
|
// Send through UnifiedEmailServer
|
||||||
return this.unifiedEmailServer.sendEmail(
|
return this.unifiedEmailServer.sendEmail(
|
||||||
emailObj,
|
email,
|
||||||
matchedRule?.mode || 'mta',
|
matchedRule?.mode || 'mta',
|
||||||
matchedRule
|
matchedRule
|
||||||
);
|
);
|
||||||
@@ -330,10 +317,10 @@ export class EmailService {
|
|||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
try {
|
try {
|
||||||
// Get email from template
|
// Get email from template
|
||||||
const smartmail = await this.templateManager.prepareEmail(templateId, context);
|
const email = await this.templateManager.prepareEmail(templateId, context);
|
||||||
|
|
||||||
// Send the email through UnifiedEmailServer
|
// Send the email through UnifiedEmailServer
|
||||||
return this.sendEmail(smartmail, to, options);
|
return this.sendEmail(email, to, options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.log('error', `Failed to send template email: ${error.message}`, {
|
logger.log('error', `Failed to send template email: ${error.message}`, {
|
||||||
templateId,
|
templateId,
|
||||||
|
Reference in New Issue
Block a user