update
This commit is contained in:
@@ -662,6 +662,22 @@ The email architecture consolidation has been fully completed. Contrary to the i
|
|||||||
- Pooled SMTP clients managed centrally in UnifiedEmailServer
|
- Pooled SMTP clients managed centrally in UnifiedEmailServer
|
||||||
- Clean separation of concerns between routing (DcRouter) and email handling (UnifiedEmailServer)
|
- Clean separation of concerns between routing (DcRouter) and email handling (UnifiedEmailServer)
|
||||||
|
|
||||||
|
## Email Router Architecture Decision (2025-05-28)
|
||||||
|
|
||||||
|
### Single Router Class
|
||||||
|
- **Important**: We will have only ONE router class, not two
|
||||||
|
- The existing `DomainRouter` will be evolved into `EmailRouter`
|
||||||
|
- This avoids confusion and redundancy
|
||||||
|
- Use `git mv` to rename and preserve git history
|
||||||
|
- Extend it to support the new match/action pattern inspired by SmartProxy
|
||||||
|
- Maintain backward compatibility for legacy domain-based rules
|
||||||
|
|
||||||
|
### Benefits of Single Router
|
||||||
|
- Clear, single source of truth for routing logic
|
||||||
|
- No confusion about which router to use
|
||||||
|
- Preserved git history and gradual migration path
|
||||||
|
- Supports all match criteria (not just domains)
|
||||||
|
|
||||||
## Email Routing Architecture (2025-05-27)
|
## Email Routing Architecture (2025-05-27)
|
||||||
|
|
||||||
### Current Routing Capabilities
|
### Current Routing Capabilities
|
||||||
@@ -697,3 +713,32 @@ The email architecture consolidation has been fully completed. Contrary to the i
|
|||||||
- `ts/mail/routing/classes.unified.email.server.ts` - processEmailByMode()
|
- `ts/mail/routing/classes.unified.email.server.ts` - processEmailByMode()
|
||||||
- `ts/mail/routing/classes.email.config.ts` - Rule interfaces
|
- `ts/mail/routing/classes.email.config.ts` - Rule interfaces
|
||||||
- `ts/mail/delivery/classes.delivery.system.ts` - Delivery execution
|
- `ts/mail/delivery/classes.delivery.system.ts` - Delivery execution
|
||||||
|
|
||||||
|
## Configuration System Cleanup (2025-05-27) - COMPLETED
|
||||||
|
|
||||||
|
### Overview
|
||||||
|
The `ts/config/` directory cleanup has been completed. Removed ~500+ lines of unused legacy configuration code.
|
||||||
|
|
||||||
|
### Changes Made
|
||||||
|
✅ **Removed Files:**
|
||||||
|
- `base.config.ts` - All unused base interfaces
|
||||||
|
- `platform.config.ts` - Completely unused platform config
|
||||||
|
- `email.config.ts` - Deprecated email configuration
|
||||||
|
- `email.port.mapping.ts` - Unused port mapping utilities
|
||||||
|
- `schemas.ts` - Removed all schemas except SMS
|
||||||
|
- `sms.config.ts` - Moved to SMS module
|
||||||
|
|
||||||
|
✅ **SMS Configuration Moved:**
|
||||||
|
- Created `ts/sms/config/sms.config.ts` - ISmsConfig interface
|
||||||
|
- Created `ts/sms/config/sms.schema.ts` - Validation schema
|
||||||
|
- Updated SmsService to import from new location
|
||||||
|
|
||||||
|
✅ **Kept:**
|
||||||
|
- `validator.ts` - Generic validation utility (might move to utils later)
|
||||||
|
- `index.ts` - Now only exports ConfigValidator
|
||||||
|
|
||||||
|
### Result
|
||||||
|
- Config directory now contains only 2 files (validator.ts, index.ts)
|
||||||
|
- SMS configuration is self-contained in SMS module
|
||||||
|
- All deprecated email configuration removed
|
||||||
|
- Build passes successfully
|
454
readme.plan.md
454
readme.plan.md
@@ -1,233 +1,247 @@
|
|||||||
# Email Routing Development Plan
|
# Simplified Email Routing Plan
|
||||||
|
|
||||||
## Overview
|
## Core Principle
|
||||||
This plan outlines the development of a comprehensive email routing system for DcRouter. The system will handle incoming emails, apply routing rules, and deliver them to appropriate destinations.
|
Following SmartProxy's elegant pattern: **One array of routes with match/action pairs**. No complex nested configurations, no scattered features - just simple, powerful routing.
|
||||||
|
|
||||||
## Current State
|
## Architecture Overview
|
||||||
- UnifiedEmailServer consolidates all email components
|
|
||||||
- DomainRouter handles pattern-based routing decisions
|
|
||||||
- Email class standardized throughout the system
|
|
||||||
- Connection pooling implemented for SMTP clients
|
|
||||||
- L Missing: Advanced routing rules and transformations
|
|
||||||
- L Missing: Email forwarding implementation
|
|
||||||
- L Missing: Multi-recipient routing strategies
|
|
||||||
|
|
||||||
## Phase 1: Core Routing Enhancement (Priority: High)
|
### Single Router Class
|
||||||
|
- **EmailRouter** - The ONLY routing class
|
||||||
|
- Handles ALL routing logic: domains, IPs, content, relay, etc.
|
||||||
|
- No separate RelayManager, no RecipientExpander - all built-in
|
||||||
|
|
||||||
### 1.1 Enhance Domain Routing Rules
|
### Core Interfaces (Just 3!)
|
||||||
- [ ] Add support for regex patterns in email matching
|
|
||||||
- [ ] Implement priority-based rule evaluation
|
|
||||||
- [ ] Add conditional routing based on email properties:
|
|
||||||
- [ ] Subject line patterns
|
|
||||||
- [ ] Attachment presence/types
|
|
||||||
- [ ] Message size
|
|
||||||
- [ ] Time-based routing (business hours vs off-hours)
|
|
||||||
- [ ] Add routing based on sender reputation/score
|
|
||||||
|
|
||||||
### 1.2 Implement Email Forwarding Mode
|
```typescript
|
||||||
- [ ] Complete the 'forward' mode in `processEmailByMode()`
|
interface IEmailRoute {
|
||||||
- [ ] Add forwarding authentication support
|
name: string; // Route identifier
|
||||||
- [ ] Implement forwarding with modifications:
|
priority?: number; // Order of evaluation (default: 0)
|
||||||
|
match: IEmailMatch; // What to match
|
||||||
|
action: IEmailAction; // What to do
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IEmailMatch {
|
||||||
|
// Simple pattern matching
|
||||||
|
recipients?: string | string[]; // "*@example.com", "admin@*"
|
||||||
|
senders?: string | string[];
|
||||||
|
clientIp?: string | string[]; // IPs or CIDR ranges
|
||||||
|
authenticated?: boolean;
|
||||||
|
|
||||||
|
// Optional advanced matching
|
||||||
|
headers?: Record<string, string | RegExp>;
|
||||||
|
sizeRange?: { min?: number; max?: number };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IEmailAction {
|
||||||
|
type: 'forward' | 'deliver' | 'reject' | 'process';
|
||||||
|
|
||||||
|
// Action-specific options
|
||||||
|
forward?: { host: string; port?: number; };
|
||||||
|
reject?: { code: number; message: string; };
|
||||||
|
process?: {
|
||||||
|
scan?: boolean;
|
||||||
|
dkim?: boolean;
|
||||||
|
queue?: 'normal' | 'priority';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
### Phase 1: Core Functionality (1-2 weeks)
|
||||||
|
|
||||||
|
1. **Create new EmailRouter**
|
||||||
|
- Fresh implementation with route-based design
|
||||||
|
- Implement `evaluateRoutes(context)` method
|
||||||
|
- Clean, modern API only
|
||||||
|
|
||||||
|
2. **Update UnifiedEmailServer**
|
||||||
|
- Replace domainRules with routes array
|
||||||
|
- Simplify `processEmailByMode()` to use actions
|
||||||
|
- Implement forward mode (currently missing)
|
||||||
|
|
||||||
|
3. **Essential Features Only**
|
||||||
|
- Forward emails (using existing SmtpClient)
|
||||||
|
- Process emails (existing flow)
|
||||||
|
- Reject emails (simple SMTP response)
|
||||||
|
- IP-based relay (just another match condition)
|
||||||
|
|
||||||
|
### Phase 2: Progressive Enhancement (2-3 weeks)
|
||||||
|
|
||||||
|
Only add features that are actually needed:
|
||||||
|
- Multi-target forwarding (array of hosts)
|
||||||
|
- Basic transformations (headers only)
|
||||||
|
- Rate limiting per route
|
||||||
|
|
||||||
|
## Configuration Example
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const emailServer = new UnifiedEmailServer({
|
||||||
|
ports: [25, 587, 465],
|
||||||
|
hostname: 'mail.example.com',
|
||||||
|
|
||||||
|
// ALL routing in one simple array
|
||||||
|
routes: [
|
||||||
|
// Relay from office
|
||||||
|
{
|
||||||
|
name: 'office-relay',
|
||||||
|
priority: 100,
|
||||||
|
match: { clientIp: '192.168.0.0/16' },
|
||||||
|
action: { type: 'forward', forward: { host: 'internal.mail' } }
|
||||||
|
},
|
||||||
|
|
||||||
|
// Process local mail
|
||||||
|
{
|
||||||
|
name: 'local-mail',
|
||||||
|
priority: 50,
|
||||||
|
match: { recipients: '*@mycompany.com' },
|
||||||
|
action: {
|
||||||
|
type: 'process',
|
||||||
|
process: { scan: true, dkim: true, queue: 'normal' }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Reject everything else
|
||||||
|
{
|
||||||
|
name: 'default',
|
||||||
|
match: { recipients: '*' },
|
||||||
|
action: {
|
||||||
|
type: 'reject',
|
||||||
|
reject: { code: 550, message: 'Relay denied' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Simplifications
|
||||||
|
|
||||||
|
### What We're NOT Building
|
||||||
|
- ❌ Separate RelayManager class - relay is just a route match
|
||||||
|
- ❌ RecipientExpander class - alias expansion is a delivery concern
|
||||||
|
- ❌ ContentAnalyzer with ML - overkill for email routing
|
||||||
|
- ❌ 20+ new classes - keep it simple
|
||||||
|
- ❌ Complex analytics - use existing logging
|
||||||
|
- ❌ API-driven routing - config file is enough
|
||||||
|
- ❌ Compliance routing - not a router concern
|
||||||
|
|
||||||
|
### What We ARE Building
|
||||||
|
- ✅ One router class that does routing well
|
||||||
|
- ✅ Simple match/action pattern
|
||||||
|
- ✅ IP-based relay as a native feature
|
||||||
|
- ✅ Email forwarding (currently missing)
|
||||||
|
- ✅ Clean, modern implementation
|
||||||
|
|
||||||
|
## Clean Implementation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Simple, single way to configure:
|
||||||
|
const router = new EmailRouter(routes);
|
||||||
|
|
||||||
|
// No legacy formats, no auto-detection
|
||||||
|
// Just clean route-based configuration
|
||||||
|
```
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
1. **Simplicity** - One router, one config array, clear patterns
|
||||||
|
2. **Flexibility** - Any match criteria, any action
|
||||||
|
3. **Performance** - Simple priority-based evaluation
|
||||||
|
4. **Maintainability** - Less code, fewer classes, clearer intent
|
||||||
|
5. **Modern** - Clean slate implementation
|
||||||
|
|
||||||
|
## Detailed Implementation Steps
|
||||||
|
|
||||||
|
### Step 1: Create Core Interfaces (30 minutes)
|
||||||
|
- [x] Create `ts/mail/routing/interfaces.ts`
|
||||||
|
- [x] Add `IEmailRoute` interface
|
||||||
|
- [x] Add `IEmailMatch` interface
|
||||||
|
- [x] Add `IEmailAction` interface
|
||||||
|
- [x] Add `IEmailContext` interface: `{ email: Email; session: IExtendedSmtpSession }`
|
||||||
|
- [x] Export all interfaces
|
||||||
|
|
||||||
|
### Step 2: Create EmailRouter Class (2 hours)
|
||||||
|
- [x] Create `ts/mail/routing/classes.email.router.ts`
|
||||||
|
- [x] Import necessary dependencies
|
||||||
|
- [x] Define class with routes property
|
||||||
|
- [x] Implement constructor: `constructor(routes: IEmailRoute[])`
|
||||||
|
- [x] Add `sortRoutesByPriority()` method
|
||||||
|
- [x] Call sort in constructor
|
||||||
|
|
||||||
|
### Step 3: Implement Route Matching (3 hours)
|
||||||
|
- [ ] Add `evaluateRoutes(context: IEmailContext): Promise<IEmailRoute | null>` method
|
||||||
|
- [ ] Add `matchesRoute(route: IEmailRoute, context: IEmailContext): boolean` method
|
||||||
|
- [ ] Implement `matchesRecipients()` with glob support
|
||||||
|
- [ ] Implement `matchesSenders()` with glob support
|
||||||
|
- [ ] Implement `matchesClientIp()` with CIDR support
|
||||||
|
- [ ] Implement `matchesAuthenticated()` check
|
||||||
|
- [ ] Add pattern cache for performance
|
||||||
|
|
||||||
|
### Step 4: Update UnifiedEmailServer Configuration (2 hours)
|
||||||
|
- [ ] Update `IUnifiedEmailServerOptions` interface
|
||||||
|
- [ ] Replace `domainRules` with `routes: IEmailRoute[]`
|
||||||
|
- [ ] Remove `defaultMode`, `defaultServer`, etc.
|
||||||
|
- [ ] Update constructor to create EmailRouter with routes
|
||||||
|
- [ ] Replace DomainRouter usage with EmailRouter
|
||||||
|
|
||||||
|
### Step 5: Implement Action Execution (4 hours)
|
||||||
|
- [ ] Add `executeAction(action: IEmailAction, email: Email, context: IEmailContext): Promise<void>` to UnifiedEmailServer
|
||||||
|
- [ ] Implement 'forward' action:
|
||||||
|
- [ ] Get SMTP client via `getSmtpClient()`
|
||||||
- [ ] Add forwarding headers (X-Forwarded-For, etc.)
|
- [ ] Add forwarding headers (X-Forwarded-For, etc.)
|
||||||
- [ ] Preserve original DKIM signatures
|
- [ ] Send email using pooled client
|
||||||
- [ ] Add new DKIM signature for forwarding domain
|
- [ ] Handle errors with bounce manager
|
||||||
- [ ] Handle forwarding failures gracefully
|
- [ ] Implement 'process' action:
|
||||||
|
- [ ] Use existing `handleProcessMode()` logic
|
||||||
|
- [ ] Apply scan/dkim options from action
|
||||||
|
- [ ] Implement 'deliver' action:
|
||||||
|
- [ ] Queue for local delivery
|
||||||
|
- [ ] Implement 'reject' action:
|
||||||
|
- [ ] Return SMTP rejection with code/message
|
||||||
|
|
||||||
### 1.3 Multi-Recipient Strategies
|
### Step 6: Refactor processEmailByMode (2 hours)
|
||||||
- [ ] Implement recipient expansion for aliases
|
- [ ] Update `processEmailByMode()` to use EmailRouter
|
||||||
- [ ] Add distribution list support
|
- [ ] Call `evaluateRoutes()` for each recipient
|
||||||
- [ ] Handle BCC recipients properly in routing
|
- [ ] Call `executeAction()` based on matched route
|
||||||
- [ ] Implement recipient-specific routing rules
|
- [ ] Handle no-match case (default reject)
|
||||||
|
- [ ] Remove old mode-based logic
|
||||||
|
|
||||||
## Phase 2: Advanced Routing Features (Priority: Medium)
|
### Step 7: Testing (4 hours)
|
||||||
|
- [ ] Create `test/test.email.router.ts`
|
||||||
|
- [ ] Test route priority sorting
|
||||||
|
- [ ] Test recipient matching (exact, glob, multiple)
|
||||||
|
- [ ] Test IP matching (single, CIDR, arrays)
|
||||||
|
- [ ] Test authentication matching
|
||||||
|
- [ ] Test action execution
|
||||||
|
- [ ] Test no-match scenarios
|
||||||
|
|
||||||
### 2.1 Content-Based Routing
|
### Step 8: Integration Testing (2 hours)
|
||||||
- [ ] Implement deep content inspection for routing
|
- [ ] Update existing email routing test
|
||||||
- [ ] Add ML-based classification for routing decisions
|
- [ ] Test relay scenario (IP-based forward)
|
||||||
- [ ] Support routing based on:
|
- [ ] Test local delivery scenario
|
||||||
- [ ] Language detection
|
- [ ] Test rejection scenario
|
||||||
- [ ] Sentiment analysis
|
- [ ] Test forwarding with headers
|
||||||
- [ ] Spam probability
|
- [ ] Verify connection pooling works
|
||||||
- [ ] Phishing detection
|
|
||||||
|
|
||||||
### 2.2 Load Balancing & Failover
|
### Step 9: Update Examples and Docs (1 hour)
|
||||||
- [ ] Add round-robin routing for multiple destinations
|
- [ ] Update configuration examples in readme
|
||||||
- [ ] Implement weighted routing algorithms
|
- [ ] Add route examples for common scenarios
|
||||||
- [ ] Add health checks for destination servers
|
- [ ] Document glob pattern syntax
|
||||||
- [ ] Automatic failover to backup routes
|
- [ ] Document CIDR notation for IPs
|
||||||
- [ ] Circuit breaker pattern for failing routes
|
- [ ] Add troubleshooting section
|
||||||
|
|
||||||
### 2.3 Routing Transformations
|
### Step 10: Cleanup (1 hour)
|
||||||
- [ ] Implement email transformations during routing:
|
- [ ] Remove DomainRouter imports
|
||||||
- [ ] Header manipulation (add/remove/modify)
|
- [ ] Remove old interfaces (IDomainRule, etc.)
|
||||||
- [ ] Body modifications (append disclaimers, etc.)
|
- [ ] Remove legacy configuration code
|
||||||
- [ ] Attachment processing (compress, convert, scan)
|
- [ ] Update any remaining references
|
||||||
- [ ] Format conversion (HTML to text, etc.)
|
- [ ] Run full test suite
|
||||||
- [ ] Add template-based transformations
|
- [ ] Commit changes
|
||||||
|
|
||||||
## Phase 3: Enterprise Features (Priority: Low)
|
## Total Time Estimate
|
||||||
|
- **Day 1**: Steps 1-3 (5.5 hours)
|
||||||
|
- **Day 2**: Steps 4-6 (8 hours)
|
||||||
|
- **Day 3**: Steps 7-8 (6 hours)
|
||||||
|
- **Day 4**: Steps 9-10 (2 hours)
|
||||||
|
|
||||||
### 3.1 Routing Analytics
|
**Total**: ~21 hours of focused work (2-3 days)
|
||||||
- [ ] Track routing decisions and outcomes
|
|
||||||
- [ ] Generate routing performance metrics
|
|
||||||
- [ ] Identify routing bottlenecks
|
|
||||||
- [ ] Provide routing recommendations
|
|
||||||
|
|
||||||
### 3.2 API-Driven Routing
|
|
||||||
- [ ] REST API for dynamic route management
|
|
||||||
- [ ] Webhook support for routing decisions
|
|
||||||
- [ ] External routing policy engines integration
|
|
||||||
- [ ] Real-time route updates without restart
|
|
||||||
|
|
||||||
### 3.3 Advanced Security Routing
|
|
||||||
- [ ] Quarantine routing for suspicious emails
|
|
||||||
- [ ] Encrypted routing channels
|
|
||||||
- [ ] Zero-trust routing verification
|
|
||||||
- [ ] Compliance-based routing (GDPR, HIPAA, etc.)
|
|
||||||
|
|
||||||
## Implementation Details
|
|
||||||
|
|
||||||
### Core Routing Flow
|
|
||||||
```
|
|
||||||
Incoming Email <20> DomainRouter <20> Rule Evaluation <20> Processing Mode <20> Destination
|
|
||||||
<20> <20> <20> <20>
|
|
||||||
Pattern Match Apply Priority Transform Deliver
|
|
||||||
<20> <20> <20> <20>
|
|
||||||
Check Cache Check Conditions Apply Rules Track Result
|
|
||||||
```
|
|
||||||
|
|
||||||
### Rule Structure Enhancement
|
|
||||||
```typescript
|
|
||||||
interface IEnhancedDomainRule {
|
|
||||||
// Existing
|
|
||||||
pattern: string;
|
|
||||||
mode: EmailProcessingMode;
|
|
||||||
|
|
||||||
// New additions
|
|
||||||
priority: number;
|
|
||||||
conditions?: {
|
|
||||||
timeRange?: { start: string; end: string };
|
|
||||||
senderPattern?: string | RegExp;
|
|
||||||
subjectPattern?: string | RegExp;
|
|
||||||
hasAttachments?: boolean;
|
|
||||||
minSize?: number;
|
|
||||||
maxSize?: number;
|
|
||||||
headers?: Record<string, string | RegExp>;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Routing targets
|
|
||||||
targets?: {
|
|
||||||
primary: IRouteTarget;
|
|
||||||
fallback?: IRouteTarget[];
|
|
||||||
loadBalance?: {
|
|
||||||
algorithm: 'round-robin' | 'weighted' | 'least-connections';
|
|
||||||
targets: IWeightedTarget[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Transformations
|
|
||||||
transformations?: ITransformation[];
|
|
||||||
|
|
||||||
// Monitoring
|
|
||||||
tracking?: {
|
|
||||||
enabled: boolean;
|
|
||||||
metrics: string[];
|
|
||||||
alerts?: IAlertConfig[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Route Target Definition
|
|
||||||
```typescript
|
|
||||||
interface IRouteTarget {
|
|
||||||
type: 'smtp' | 'api' | 'storage' | 'queue';
|
|
||||||
|
|
||||||
// SMTP forwarding
|
|
||||||
smtp?: {
|
|
||||||
host: string;
|
|
||||||
port: number;
|
|
||||||
secure?: boolean;
|
|
||||||
auth?: ISmtpAuth;
|
|
||||||
envelope?: {
|
|
||||||
from?: string;
|
|
||||||
to?: string[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// API forwarding
|
|
||||||
api?: {
|
|
||||||
url: string;
|
|
||||||
method: 'POST' | 'PUT';
|
|
||||||
headers?: Record<string, string>;
|
|
||||||
auth?: IApiAuth;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Storage
|
|
||||||
storage?: {
|
|
||||||
type: 'filesystem' | 's3' | 'database';
|
|
||||||
path: string;
|
|
||||||
format?: 'eml' | 'json' | 'mbox';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing Strategy
|
|
||||||
|
|
||||||
### Unit Tests
|
|
||||||
- [ ] Test rule matching logic
|
|
||||||
- [ ] Test priority evaluation
|
|
||||||
- [ ] Test condition evaluation
|
|
||||||
- [ ] Test transformation application
|
|
||||||
|
|
||||||
### Integration Tests
|
|
||||||
- [ ] Test end-to-end routing scenarios
|
|
||||||
- [ ] Test failover mechanisms
|
|
||||||
- [ ] Test load balancing algorithms
|
|
||||||
- [ ] Test concurrent routing
|
|
||||||
|
|
||||||
### Performance Tests
|
|
||||||
- [ ] Measure routing decision time
|
|
||||||
- [ ] Test high-volume routing
|
|
||||||
- [ ] Benchmark transformation performance
|
|
||||||
- [ ] Stress test failover scenarios
|
|
||||||
|
|
||||||
## Migration Path
|
|
||||||
|
|
||||||
1. **Phase 1**: Implement backward-compatible enhancements
|
|
||||||
- Extend existing interfaces
|
|
||||||
- Add new optional fields
|
|
||||||
- Maintain current behavior as default
|
|
||||||
|
|
||||||
2. **Phase 2**: Gradual migration
|
|
||||||
- Provide migration tools
|
|
||||||
- Support both old and new formats
|
|
||||||
- Log deprecation warnings
|
|
||||||
|
|
||||||
3. **Phase 3**: Complete migration
|
|
||||||
- Remove deprecated code
|
|
||||||
- Update all documentation
|
|
||||||
- Provide final migration guide
|
|
||||||
|
|
||||||
## Success Metrics
|
|
||||||
|
|
||||||
- **Routing Accuracy**: 99.9% emails routed correctly
|
|
||||||
- **Performance**: <100ms routing decision time
|
|
||||||
- **Reliability**: 99.99% uptime for routing service
|
|
||||||
- **Flexibility**: Support 50+ routing conditions
|
|
||||||
- **Scalability**: Handle 10k+ routes without degradation
|
|
||||||
|
|
||||||
## Timeline
|
|
||||||
|
|
||||||
- **Phase 1**: 2-3 weeks
|
|
||||||
- **Phase 2**: 3-4 weeks
|
|
||||||
- **Phase 3**: 4-6 weeks
|
|
||||||
|
|
||||||
Total estimated time: 9-13 weeks
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
1. Review and approve this plan
|
|
||||||
2. Create detailed technical specifications
|
|
||||||
3. Set up test infrastructure
|
|
||||||
4. Begin Phase 1 implementation
|
|
||||||
5. Regular progress reviews
|
|
@@ -1,59 +1,2 @@
|
|||||||
// Export configuration interfaces
|
// Export validation tools only
|
||||||
export * from './base.config.js';
|
|
||||||
export * from './email.config.js';
|
|
||||||
export * from './sms.config.js';
|
|
||||||
export * from './platform.config.js';
|
|
||||||
|
|
||||||
// Export validation tools
|
|
||||||
export * from './validator.js';
|
export * from './validator.js';
|
||||||
export * from './schemas.js';
|
|
||||||
|
|
||||||
// Re-export commonly used types
|
|
||||||
import type { IPlatformConfig } from './platform.config.js';
|
|
||||||
import type { ISmsConfig } from './sms.config.js';
|
|
||||||
import type {
|
|
||||||
IBaseConfig,
|
|
||||||
ITlsConfig,
|
|
||||||
IHttpServerConfig,
|
|
||||||
IRateLimitConfig,
|
|
||||||
IQueueConfig
|
|
||||||
} from './base.config.js';
|
|
||||||
|
|
||||||
// Default platform configuration
|
|
||||||
export const defaultConfig: IPlatformConfig = {
|
|
||||||
id: 'platform-service-config',
|
|
||||||
version: '1.0.0',
|
|
||||||
environment: 'production',
|
|
||||||
name: 'PlatformService',
|
|
||||||
enabled: true,
|
|
||||||
logging: {
|
|
||||||
level: 'info',
|
|
||||||
structured: true,
|
|
||||||
correlationTracking: true
|
|
||||||
},
|
|
||||||
server: {
|
|
||||||
enabled: true,
|
|
||||||
host: '0.0.0.0',
|
|
||||||
port: 3000,
|
|
||||||
cors: true
|
|
||||||
},
|
|
||||||
// Email configuration removed - use IUnifiedEmailServerOptions in DcRouter instead
|
|
||||||
email: undefined,
|
|
||||||
paths: {
|
|
||||||
dataDir: 'data',
|
|
||||||
logsDir: 'logs',
|
|
||||||
tempDir: 'temp',
|
|
||||||
emailTemplatesDir: 'templates/email'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Export main types for convenience
|
|
||||||
export type {
|
|
||||||
IPlatformConfig,
|
|
||||||
ISmsConfig,
|
|
||||||
IBaseConfig,
|
|
||||||
ITlsConfig,
|
|
||||||
IHttpServerConfig,
|
|
||||||
IRateLimitConfig,
|
|
||||||
IQueueConfig
|
|
||||||
};
|
|
@@ -1,6 +1,5 @@
|
|||||||
import * as plugins from '../plugins.js';
|
import * as plugins from '../plugins.js';
|
||||||
import { ValidationError } from '../errors/base.errors.js';
|
import { ValidationError } from '../errors/base.errors.js';
|
||||||
import type { IBaseConfig } from './base.config.js';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validation result
|
* Validation result
|
||||||
@@ -95,56 +94,6 @@ export type ValidationSchema = Record<string, {
|
|||||||
* Validates configuration objects against schemas and provides default values
|
* Validates configuration objects against schemas and provides default values
|
||||||
*/
|
*/
|
||||||
export class ConfigValidator {
|
export class ConfigValidator {
|
||||||
/**
|
|
||||||
* Basic schema for IBaseConfig
|
|
||||||
*/
|
|
||||||
private static baseConfigSchema: ValidationSchema = {
|
|
||||||
id: {
|
|
||||||
type: 'string',
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
version: {
|
|
||||||
type: 'string',
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
environment: {
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
enum: ['development', 'test', 'staging', 'production'],
|
|
||||||
default: 'production'
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
type: 'string',
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
enabled: {
|
|
||||||
type: 'boolean',
|
|
||||||
required: false,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
logging: {
|
|
||||||
type: 'object',
|
|
||||||
required: false,
|
|
||||||
schema: {
|
|
||||||
level: {
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
enum: ['error', 'warn', 'info', 'debug'],
|
|
||||||
default: 'info'
|
|
||||||
},
|
|
||||||
structured: {
|
|
||||||
type: 'boolean',
|
|
||||||
required: false,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
correlationTracking: {
|
|
||||||
type: 'boolean',
|
|
||||||
required: false,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate a configuration object against a schema
|
* Validate a configuration object against a schema
|
||||||
@@ -261,15 +210,6 @@ export class ConfigValidator {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate base configuration
|
|
||||||
*
|
|
||||||
* @param config Base configuration
|
|
||||||
* @returns Validation result for base configuration
|
|
||||||
*/
|
|
||||||
public static validateBaseConfig(config: IBaseConfig): IValidationResult {
|
|
||||||
return this.validate(config, this.baseConfigSchema);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply defaults to a configuration object based on a schema
|
* Apply defaults to a configuration object based on a schema
|
||||||
|
60
ts/mail/routing/classes.email.router.ts
Normal file
60
ts/mail/routing/classes.email.router.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import * as plugins from '../../plugins.js';
|
||||||
|
import { EventEmitter } from 'node:events';
|
||||||
|
import type { IEmailRoute, IEmailMatch, IEmailAction, IEmailContext } from './interfaces.js';
|
||||||
|
import type { Email } from '../core/classes.email.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email router that evaluates routes and determines actions
|
||||||
|
*/
|
||||||
|
export class EmailRouter extends EventEmitter {
|
||||||
|
private routes: IEmailRoute[];
|
||||||
|
private patternCache: Map<string, boolean> = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new email router
|
||||||
|
* @param routes Array of email routes
|
||||||
|
*/
|
||||||
|
constructor(routes: IEmailRoute[]) {
|
||||||
|
super();
|
||||||
|
this.routes = this.sortRoutesByPriority(routes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort routes by priority (higher priority first)
|
||||||
|
* @param routes Routes to sort
|
||||||
|
* @returns Sorted routes
|
||||||
|
*/
|
||||||
|
private sortRoutesByPriority(routes: IEmailRoute[]): IEmailRoute[] {
|
||||||
|
return [...routes].sort((a, b) => {
|
||||||
|
const priorityA = a.priority ?? 0;
|
||||||
|
const priorityB = b.priority ?? 0;
|
||||||
|
return priorityB - priorityA; // Higher priority first
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all configured routes
|
||||||
|
* @returns Array of routes
|
||||||
|
*/
|
||||||
|
public getRoutes(): IEmailRoute[] {
|
||||||
|
return [...this.routes];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update routes
|
||||||
|
* @param routes New routes
|
||||||
|
*/
|
||||||
|
public updateRoutes(routes: IEmailRoute[]): void {
|
||||||
|
this.routes = this.sortRoutesByPriority(routes);
|
||||||
|
this.clearCache();
|
||||||
|
this.emit('routesUpdated', this.routes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the pattern cache
|
||||||
|
*/
|
||||||
|
public clearCache(): void {
|
||||||
|
this.patternCache.clear();
|
||||||
|
this.emit('cacheCleared');
|
||||||
|
}
|
||||||
|
}
|
101
ts/mail/routing/interfaces.ts
Normal file
101
ts/mail/routing/interfaces.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import type { Email } from '../core/classes.email.js';
|
||||||
|
import type { IExtendedSmtpSession } from '../delivery/smtpserver/interfaces.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route configuration for email routing
|
||||||
|
*/
|
||||||
|
export interface IEmailRoute {
|
||||||
|
/** Route identifier */
|
||||||
|
name: string;
|
||||||
|
/** Order of evaluation (higher priority evaluated first, default: 0) */
|
||||||
|
priority?: number;
|
||||||
|
/** Conditions to match */
|
||||||
|
match: IEmailMatch;
|
||||||
|
/** Action to take when matched */
|
||||||
|
action: IEmailAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match criteria for email routing
|
||||||
|
*/
|
||||||
|
export interface IEmailMatch {
|
||||||
|
/** Email patterns to match recipients: "*@example.com", "admin@*" */
|
||||||
|
recipients?: string | string[];
|
||||||
|
/** Email patterns to match senders */
|
||||||
|
senders?: string | string[];
|
||||||
|
/** IP addresses or CIDR ranges to match */
|
||||||
|
clientIp?: string | string[];
|
||||||
|
/** Require authentication status */
|
||||||
|
authenticated?: boolean;
|
||||||
|
|
||||||
|
// Optional advanced matching
|
||||||
|
/** Headers to match */
|
||||||
|
headers?: Record<string, string | RegExp>;
|
||||||
|
/** Message size range */
|
||||||
|
sizeRange?: { min?: number; max?: number };
|
||||||
|
/** Subject line patterns */
|
||||||
|
subject?: string | RegExp;
|
||||||
|
/** Has attachments */
|
||||||
|
hasAttachments?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Action to take when route matches
|
||||||
|
*/
|
||||||
|
export interface IEmailAction {
|
||||||
|
/** Type of action to perform */
|
||||||
|
type: 'forward' | 'deliver' | 'reject' | 'process';
|
||||||
|
|
||||||
|
/** Forward action configuration */
|
||||||
|
forward?: {
|
||||||
|
/** Target host to forward to */
|
||||||
|
host: string;
|
||||||
|
/** Target port (default: 25) */
|
||||||
|
port?: number;
|
||||||
|
/** Authentication credentials */
|
||||||
|
auth?: {
|
||||||
|
user: string;
|
||||||
|
pass: string;
|
||||||
|
};
|
||||||
|
/** Preserve original headers */
|
||||||
|
preserveHeaders?: boolean;
|
||||||
|
/** Additional headers to add */
|
||||||
|
addHeaders?: Record<string, string>;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Reject action configuration */
|
||||||
|
reject?: {
|
||||||
|
/** SMTP response code */
|
||||||
|
code: number;
|
||||||
|
/** SMTP response message */
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Process action configuration */
|
||||||
|
process?: {
|
||||||
|
/** Enable content scanning */
|
||||||
|
scan?: boolean;
|
||||||
|
/** Enable DKIM signing */
|
||||||
|
dkim?: boolean;
|
||||||
|
/** Delivery queue priority */
|
||||||
|
queue?: 'normal' | 'priority' | 'bulk';
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Delivery options (applies to forward/process/deliver) */
|
||||||
|
delivery?: {
|
||||||
|
/** Rate limit (messages per minute) */
|
||||||
|
rateLimit?: number;
|
||||||
|
/** Number of retry attempts */
|
||||||
|
retries?: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for route evaluation
|
||||||
|
*/
|
||||||
|
export interface IEmailContext {
|
||||||
|
/** The email being routed */
|
||||||
|
email: Email;
|
||||||
|
/** The SMTP session */
|
||||||
|
session: IExtendedSmtpSession;
|
||||||
|
}
|
@@ -1,2 +0,0 @@
|
|||||||
// Email services
|
|
||||||
// Note: EmailService and ApiManager have been removed. Use UnifiedEmailServer directly.
|
|
@@ -2,8 +2,9 @@ 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 type { ISmsConfig } from '../config/sms.config.js';
|
import type { ISmsConfig } from './config/sms.config.js';
|
||||||
import { ConfigValidator, smsConfigSchema } from '../config/index.js';
|
import { smsConfigSchema } from './config/sms.schema.js';
|
||||||
|
import { ConfigValidator } from '../config/validator.js';
|
||||||
|
|
||||||
export class SmsService {
|
export class SmsService {
|
||||||
public projectinfo: plugins.projectinfo.ProjectInfo;
|
public projectinfo: plugins.projectinfo.ProjectInfo;
|
||||||
|
109
ts/sms/config/sms.config.ts
Normal file
109
ts/sms/config/sms.config.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
/**
|
||||||
|
* SMS service configuration interface
|
||||||
|
*/
|
||||||
|
export interface ISmsConfig {
|
||||||
|
/**
|
||||||
|
* API token for the gateway service
|
||||||
|
*/
|
||||||
|
apiGatewayApiToken: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default sender ID or phone number
|
||||||
|
*/
|
||||||
|
defaultSender?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMS rate limiting
|
||||||
|
*/
|
||||||
|
rateLimit?: {
|
||||||
|
/**
|
||||||
|
* Whether rate limiting is enabled
|
||||||
|
*/
|
||||||
|
enabled?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum requests per period
|
||||||
|
*/
|
||||||
|
maxPerPeriod?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Period duration in milliseconds
|
||||||
|
*/
|
||||||
|
periodMs?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to apply rate limit per key
|
||||||
|
*/
|
||||||
|
perKey?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of burst tokens
|
||||||
|
*/
|
||||||
|
burstTokens?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum messages per recipient per day
|
||||||
|
*/
|
||||||
|
maxPerRecipientPerDay?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMS provider configuration
|
||||||
|
*/
|
||||||
|
provider?: {
|
||||||
|
/**
|
||||||
|
* Provider type
|
||||||
|
*/
|
||||||
|
type?: 'gateway' | 'twilio' | 'other';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider-specific configuration
|
||||||
|
*/
|
||||||
|
config?: Record<string, any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fallback provider configuration
|
||||||
|
*/
|
||||||
|
fallback?: {
|
||||||
|
/**
|
||||||
|
* Whether to use fallback provider
|
||||||
|
*/
|
||||||
|
enabled?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider type
|
||||||
|
*/
|
||||||
|
type?: 'gateway' | 'twilio' | 'other';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider-specific configuration
|
||||||
|
*/
|
||||||
|
config?: Record<string, any>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verification code settings
|
||||||
|
*/
|
||||||
|
verification?: {
|
||||||
|
/**
|
||||||
|
* Code length
|
||||||
|
*/
|
||||||
|
codeLength?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Code expiration time in seconds
|
||||||
|
*/
|
||||||
|
expirationSeconds?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum number of attempts
|
||||||
|
*/
|
||||||
|
maxAttempts?: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cooldown period in seconds
|
||||||
|
*/
|
||||||
|
cooldownSeconds?: number;
|
||||||
|
};
|
||||||
|
}
|
122
ts/sms/config/sms.schema.ts
Normal file
122
ts/sms/config/sms.schema.ts
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import type { ValidationSchema } from '../../config/validator.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SMS service configuration schema
|
||||||
|
*/
|
||||||
|
export const smsConfigSchema: ValidationSchema = {
|
||||||
|
apiGatewayApiToken: {
|
||||||
|
type: 'string',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
defaultSender: {
|
||||||
|
type: 'string',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
rateLimit: {
|
||||||
|
type: 'object',
|
||||||
|
required: false,
|
||||||
|
schema: {
|
||||||
|
enabled: {
|
||||||
|
type: 'boolean',
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
maxPerPeriod: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
default: 100,
|
||||||
|
min: 1
|
||||||
|
},
|
||||||
|
periodMs: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
default: 60000, // 1 minute
|
||||||
|
min: 1000
|
||||||
|
},
|
||||||
|
perKey: {
|
||||||
|
type: 'boolean',
|
||||||
|
required: false,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
burstTokens: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
default: 5,
|
||||||
|
min: 0
|
||||||
|
},
|
||||||
|
maxPerRecipientPerDay: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
default: 10,
|
||||||
|
min: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provider: {
|
||||||
|
type: 'object',
|
||||||
|
required: false,
|
||||||
|
schema: {
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
enum: ['gateway', 'twilio', 'other'],
|
||||||
|
default: 'gateway'
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
type: 'object',
|
||||||
|
required: false
|
||||||
|
},
|
||||||
|
fallback: {
|
||||||
|
type: 'object',
|
||||||
|
required: false,
|
||||||
|
schema: {
|
||||||
|
enabled: {
|
||||||
|
type: 'boolean',
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
enum: ['gateway', 'twilio', 'other']
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
type: 'object',
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
verification: {
|
||||||
|
type: 'object',
|
||||||
|
required: false,
|
||||||
|
schema: {
|
||||||
|
codeLength: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
default: 6,
|
||||||
|
min: 4,
|
||||||
|
max: 10
|
||||||
|
},
|
||||||
|
expirationSeconds: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
default: 300, // 5 minutes
|
||||||
|
min: 60
|
||||||
|
},
|
||||||
|
maxAttempts: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
default: 3,
|
||||||
|
min: 1
|
||||||
|
},
|
||||||
|
cooldownSeconds: {
|
||||||
|
type: 'number',
|
||||||
|
required: false,
|
||||||
|
default: 60, // 1 minute
|
||||||
|
min: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
Reference in New Issue
Block a user