27 KiB
Implementation Hints and Learnings
smartmta Migration (2026-02-11)
Overview
dcrouter's custom MTA code (~27,149 lines / 68 files in ts/mail/ + ts/deliverability/) has been replaced with @push.rocks/smartmta v5.2.1, a TypeScript+Rust hybrid MTA. dcrouter is now an orchestrator that wires together SmartProxy, smartmta, smartdns, smartradius, and OpsServer.
Architecture
- No socket-handler mode — smartmta's Rust SMTP server binds its own ports directly
- SmartProxy forward mode only — external email ports forwarded to internal ports where smartmta listens
- Email traffic flow: External Port → SmartProxy → Internal Port → smartmta UnifiedEmailServer
Key API Differences (smartmta vs old custom MTA)
updateEmailRoutes()instead ofupdateRoutes()dkimCreatoris public (no need for(this.emailServer as any).dkimCreator)bounceManageris private, but exposed via public methods:emailServer.getSuppressionList()emailServer.getHardBouncedAddresses()emailServer.getBounceHistory(email)emailServer.removeFromSuppressionList(email)
Emailclass imported from@push.rocks/smartmtaIAttachmenttype accessed viaCorenamespace:import { type Core } from '@push.rocks/smartmta'; type IAttachment = Core.IAttachment;
Deleted Directories
ts/mail/(60 files) — replaced by smartmtats/deliverability/(3 files) — IPWarmupManager/SenderReputationMonitor will move to smartmtats/errors/email.errors.ts,ts/errors/mta.errors.ts— smartmta has its own errorsts/cache/documents/classes.cached.bounce.ts,classes.cached.suppression.ts,classes.cached.dkim.ts— smartmta handles its own persistence
Remaining Cache Documents
CachedEmail— kept (dcrouter-level queue persistence)CachedIPReputation— kept (dcrouter-level IP reputation caching)
Dependencies Removed
mailauth, mailparser, @types/mailparser, ip, @push.rocks/smartmail, @push.rocks/smartrule, node-forge
Pre-existing Test Failures (not caused by migration)
test/test.jwt-auth.ts—response.text is not a function(webrequest compatibility issue)test/test.opsserver-api.ts— same webrequest issue, timeouts
smartmta Location
Source at ../../push.rocks/smartmta, release with gitzone commit -ypbrt
Dependency Upgrade (2026-02-11)
SmartProxy v23.1.2 Route Validation
- SmartProxy 23.1.2 enforces stricter route validation
- Forward actions MUST use
targets(array) instead oftarget(singular) - Test configurations that call
DcRouter.start()needcacheConfig: { enabled: false }to avoid starting a real MongoDB process in tests
// WRONG - will fail validation
action: { type: 'forward', target: { host: 'localhost', port: 10025 } }
// CORRECT
action: { type: 'forward', targets: [{ host: 'localhost', port: 10025 }] }
Files Fixed:
ts/classes.dcrouter.ts-generateEmailRoutes()methodtest/test.dcrouter.email.ts- Updated assertions and addedcacheConfig: { enabled: false }
Dependency Upgrade (2026-02-10)
SmartProxy v23.1.0 Upgrade
@push.rocks/smartproxy: 22.4.2 → 23.1.0
Key Changes:
- Rust-based proxy components for improved performance
- Rust binary runs as separate process via IPC
getStatistics()now returnsPromise<any>(was synchronous)- nftables-proxy removed (not used by dcrouter)
Code Changes Required:
// Old (synchronous)
const proxyStats = this.dcRouter.smartProxy.getStatistics();
// New (async)
const proxyStats = await this.dcRouter.smartProxy.getStatistics();
Files Modified:
ts/monitoring/classes.metricsmanager.ts- AddedawaittogetStatistics()call
Dependency Upgrade (2026-02-01)
Major Upgrades Completed
@api.global/typedserver: 3.0.80 → 8.3.0@api.global/typedsocket: 3.1.1 → 4.1.0@apiclient.xyz/cloudflare: 6.4.3 → 7.1.0@design.estate/dees-catalog: 1.12.4 → 3.41.4@push.rocks/smartpath: 5.1.0 → 6.0.0@push.rocks/smartproxy: 19.6.17 → 22.4.2@push.rocks/smartrequest: 2.1.0 → 5.0.1uuid: 11.1.0 → 13.0.0
Breaking Changes Fixed
-
SmartProxy v22:
target→targets(array)// Old action: { type: 'forward', target: { host: 'x', port: 25 } } // New action: { type: 'forward', targets: [{ host: 'x', port: 25 }] } -
SmartRequest v5:
SmartRequestClient→SmartRequest,.body→.json()// Old const resp = await plugins.smartrequest.SmartRequestClient.create()...post(); const json = resp.body; // New const resp = await plugins.smartrequest.SmartRequest.create()...post(); const json = await resp.json(); -
dees-catalog v3: Icon naming changed to library-prefixed format
// Old (deprecated but supported) <dees-icon iconFA="check"></dees-icon> // New <dees-icon icon="fa:check"></dees-icon> <dees-icon icon="lucide:menu"></dees-icon>
TC39 Decorators
- ts_web components updated to use
accessorkeyword for@state()decorators - Required for TC39 standard decorator support
tswatch Configuration
The project now uses tswatch for development:
pnpm run watch
Configuration in npmextra.json:
{
"@git.zone/tswatch": {
"watchers": [{
"name": "dcrouter-dev",
"watch": ["ts/**/*.ts", "ts_*/**/*.ts", "test_watch/devserver.ts"],
"command": "pnpm run build && tsrun test_watch/devserver.ts",
"restart": true,
"debounce": 500,
"runOnStart": true
}]
}
}
RADIUS Server Integration (2026-02-01)
Overview
DcRouter now supports RADIUS server functionality for network authentication via @push.rocks/smartradius.
Key Features
- MAC Authentication Bypass (MAB) - Authenticate network devices based on MAC address
- VLAN Assignment - Assign VLANs based on MAC address or OUI patterns
- RADIUS Accounting - Track sessions, data usage, and billing
Configuration Example
const dcRouter = new DcRouter({
radiusConfig: {
authPort: 1812, // Authentication port (default)
acctPort: 1813, // Accounting port (default)
clients: [
{
name: 'switch-1',
ipRange: '192.168.1.0/24',
secret: 'shared-secret',
enabled: true
}
],
vlanAssignment: {
defaultVlan: 100, // VLAN for unknown MACs
allowUnknownMacs: true,
mappings: [
{ mac: '00:11:22:33:44:55', vlan: 10, enabled: true },
{ mac: '00:11:22', vlan: 20, enabled: true } // OUI pattern
]
},
accounting: {
enabled: true,
retentionDays: 30
}
}
});
Components
RadiusServer- Main server wrapping smartradiusVlanManager- MAC-to-VLAN mapping with OUI pattern supportAccountingManager- Session tracking and billing data
OpsServer API Endpoints
getRadiusClients/setRadiusClient/removeRadiusClient- Client managementgetVlanMappings/setVlanMapping/removeVlanMapping- VLAN mappingstestVlanAssignment- Test what VLAN a MAC would getgetRadiusSessions/disconnectRadiusSession- Session managementgetRadiusStatistics/getRadiusAccountingSummary- Statistics
Files
ts/radius/- RADIUS modulets/opsserver/handlers/radius.handler.ts- OpsServer handlerts_interfaces/requests/radius.ts- TypedRequest interfaces
Test Fix: test.dcrouter.email.ts (2026-02-01)
Issue
The test DcRouter class - Custom email storage path was failing with "domainConfigs is not iterable".
Root Cause
The test was using outdated email config properties:
- Used
domainRules: [](non-existent property) - Used
defaultMode(non-existent property) - Missing required
domains: []property - Missing required
routes: []property - Referenced
router.unifiedEmailServerinstead ofrouter.emailServer
Fix
Updated the test to use the correct IUnifiedEmailServerOptions interface properties:
const emailConfig: IEmailConfig = {
ports: [2525],
hostname: 'mail.example.com',
domains: [], // Required: domain configurations
routes: [] // Required: email routing rules
};
And fixed the property name:
expect(router.emailServer).toBeTruthy(); // Not unifiedEmailServer
Key Learning
When using IUnifiedEmailServerOptions (aliased as IEmailConfig in some tests):
domains: IEmailDomainConfig[]is required (array of domain configs)routes: IEmailRoute[]is required (email routing rules)- Access the email server via
dcRouter.emailServernotdcRouter.unifiedEmailServer
Network Metrics Implementation (2025-06-23)
SmartProxy Metrics API Integration
- Updated to use new SmartProxy metrics API (v19.6.7)
- Use
getMetrics()for detailed metrics with grouped methods:const metrics = smartProxy.getMetrics(); metrics.connections.active() // Current active connections metrics.throughput.instant() // Real-time throughput {in, out} metrics.connections.topIPs(10) // Top 10 IPs by connection count - Use
getStatistics()for basic stats
Network Traffic Display
- All throughput values shown in bits per second (kbit/s, Mbit/s, Gbit/s)
- Conversion:
bytesPerSecond * 8 / 1000000for Mbps - Network graph shows separate lines for inbound (green) and outbound (purple)
- Throughput tiles and graph use same data source for consistency
Requests/sec vs Connections
- Requests/sec shows HTTP request counts (derived from connections)
- Single connection can handle multiple requests
- Current implementation tracks connections, not individual requests
- Trend line shows historical request counts, not throughput
DKIM Implementation Status (2025-05-30)
Note: DKIM is now handled by @push.rocks/smartmta. The dkimCreator is a public property on UnifiedEmailServer.
SmartProxy Usage
New Route-Based Architecture (v18+)
- SmartProxy now uses a route-based configuration system
- Routes define match criteria and actions instead of simple port-to-port forwarding
- All traffic types (HTTP, HTTPS, TCP, WebSocket) are configured through routes
// NEW: Route-based SmartProxy configuration
const smartProxy = new plugins.smartproxy.SmartProxy({
routes: [
{
name: 'https-traffic',
match: {
ports: 443,
domains: ['example.com', '*.example.com']
},
action: {
type: 'forward',
target: {
host: 'backend.server.com',
port: 8080
}
},
tls: {
mode: 'terminate',
certificate: 'auto'
}
}
],
defaults: {
target: {
host: 'fallback.server.com',
port: 8080
}
},
acme: {
accountEmail: 'admin@example.com',
enabled: true,
useProduction: true
}
});
Migration from Old to New
// OLD configuration style (deprecated)
{
fromPort: 443,
toPort: 8080,
targetIP: 'backend.server.com',
domainConfigs: [...]
}
// NEW route-based style
{
routes: [{
name: 'main-route',
match: { ports: 443 },
action: {
type: 'forward',
target: { host: 'backend.server.com', port: 8080 }
}
}]
}
Direct Component Usage
- Use SmartProxy components directly instead of creating your own wrappers
- SmartProxy already includes Port80Handler and NetworkProxy functionality
- When using SmartProxy, configure it directly rather than instantiating Port80Handler or NetworkProxy separately
Certificate Management
- SmartProxy has built-in ACME certificate management
- Configure it in the
acmeproperty of SmartProxy options - Use
accountEmail(notemail) for the ACME contact email - SmartProxy handles both HTTP-01 challenges and certificate application automatically
qenv Usage
Direct Usage
- Use qenv directly instead of creating environment variable wrappers
- Instantiate qenv with appropriate basePath and nogitPath:
const qenv = new plugins.qenv.Qenv('./', '.nogit/');
const value = await qenv.getEnvVarOnDemand('ENV_VAR_NAME');
TypeScript Interfaces
SmartProxy Interfaces
- Always check the interfaces from the node_modules to ensure correct property names
- Important interfaces for the new architecture:
ISmartProxyOptions: Main configuration withroutesarrayIRouteConfig: Individual route configurationIRouteMatch: Match criteria for routesIRouteTarget: Target configuration for forwardingIAcmeOptions: ACME certificate configurationTTlsMode: TLS handling modes ('passthrough' | 'terminate' | 'terminate-and-reencrypt')
New Route Configuration
interface IRouteConfig {
name: string;
match: {
ports: number | number[];
domains?: string | string[];
path?: string;
headers?: Record<string, string | RegExp>;
};
action: {
type: 'forward' | 'redirect' | 'block' | 'static';
target?: {
host: string | string[] | ((context) => string);
port: number | 'preserve' | ((context) => number);
};
};
tls?: {
mode: TTlsMode;
certificate?: 'auto' | { key: string; cert: string; };
};
security?: {
authentication?: IRouteAuthentication;
rateLimit?: IRouteRateLimit;
ipAllowList?: string[];
ipBlockList?: string[];
};
}
Required Properties
- For
ISmartProxyOptions,routesarray is the main configuration - For
IAcmeOptions, useaccountEmailfor the contact email - Routes must have
name,match, andactionproperties
Testing
Test Structure
- Follow the project's test structure, using
@push.rocks/tapbundle - Use
expect(value).toEqual(expected)for equality checks - Use
expect(value).toBeTruthy()for boolean assertions
tap.test('test description', async () => {
const result = someFunction();
expect(result.property).toEqual('expected value');
expect(result.valid).toBeTruthy();
});
Cleanup
- Include a cleanup test to ensure proper test resource handling
- Add a
stoptest to forcefully end the test when needed:
tap.test('stop', async () => {
await tap.stopForcefully();
});
Architecture Principles
Simplicity
- Prefer direct usage of libraries instead of creating wrappers
- Don't reinvent functionality that already exists in dependencies
- Keep interfaces clean and focused, avoiding unnecessary abstraction layers
Component Integration
- Leverage built-in integrations between components (like SmartProxy's ACME handling)
- Use parallel operations for performance (like in the
stop()method) - Separate concerns clearly (HTTP handling vs. SMTP handling)
Email Integration with SmartProxy
Architecture (Post-Migration)
- Email traffic is routed through SmartProxy using automatic route generation
- smartmta's UnifiedEmailServer runs on internal ports and receives forwarded traffic from SmartProxy
- SmartProxy handles external ports (25, 587, 465) and forwards to internal ports
- smartmta's Rust SMTP bridge handles SMTP protocol processing
Port Mapping
- External port 25 → Internal port 10025 (SMTP)
- External port 587 → Internal port 10587 (Submission)
- External port 465 → Internal port 10465 (SMTPS)
TLS Handling
- Ports 25 and 587: Use 'passthrough' mode (STARTTLS handled by smartmta)
- Port 465: Use 'terminate' mode (SmartProxy handles TLS termination)
SmartMetrics Integration (2025-06-12) - COMPLETED
Overview
Fixed the UI metrics display to show accurate CPU and memory data from SmartMetrics.
Key Findings
-
CPU Metrics:
- SmartMetrics provides
cpuUsageTextas a string percentage - MetricsManager parses it as
cpuUsage.user(system is always 0) - UI was incorrectly dividing by 2, showing half the actual CPU usage
- SmartMetrics provides
-
Memory Metrics:
- SmartMetrics calculates
maxMemoryMBas minimum of:- V8 heap size limit
- System total memory
- Docker memory limit (if available)
- Provides
memoryUsageBytes(total process memory including children) - Provides
memoryPercentage(pre-calculated percentage) - UI was only showing heap usage, missing actual memory constraints
- SmartMetrics calculates
Changes Made
-
MetricsManager Enhanced:
- Added
maxMemoryMBfrom SmartMetrics instance - Added
actualUsageBytesfrom SmartMetrics data - Added
actualUsagePercentagefrom SmartMetrics data - Kept existing memory fields for compatibility
- Added
-
Interface Updated:
- Added optional fields to
IServerStats.memoryUsage - Fields are optional to maintain backward compatibility
- Added optional fields to
-
UI Fixed:
- Removed incorrect CPU division by 2
- Uses
actualUsagePercentagewhen available (falls back to heap percentage) - Shows actual memory usage vs max memory limit (not just heap)
Result
- CPU now shows accurate usage percentage
- Memory shows percentage of actual constraints (Docker/system/V8 limits)
- Better monitoring for containerized environments
Network UI Implementation (2025-06-20) - COMPLETED
Overview
Revamped the Network UI to display real network data from SmartProxy instead of mock data.
Architecture
-
MetricsManager Integration:
- Already integrates with SmartProxy via
dcRouter.smartProxy.getStats() - Extended with
getNetworkStats()method to expose unused metrics:getConnectionsByIP()- Connection counts by IP addressgetThroughputRate()- Real-time bandwidth rates (bytes/second)getTopIPs()- Top connecting IPs sorted by connection count
- Note: SmartProxy base interface doesn't include all methods, manual implementation required
- Already integrates with SmartProxy via
-
Existing Infrastructure Leveraged:
getActiveConnectionsendpoint already exists in security.handler.ts- Enhanced to include real SmartProxy data via MetricsManager
- IConnectionInfo interface already supports network data structures
-
State Management:
- Added
INetworkStateinterface following existing patterns - Created
networkStatePartwith connections, throughput, and IP data - Integrated with existing auto-refresh mechanism
- Added
-
UI Changes (Minimal):
- Removed
generateMockData()method and all mock generation - Connected to real
networkStatePartstate - Added
renderTopIPs()section to display top connected IPs - Updated traffic chart to show real request data
- Kept all existing UI components (DeesTable, DeesChartArea)
- Removed
Implementation Details
-
Data Transformation:
- Converts IConnectionInfo[] to INetworkRequest[] for table display
- Calculates traffic buckets based on selected time range
- Maps connection data to chart-compatible format
-
Real Metrics Displayed:
- Active connections count (from server stats)
- Requests per second (calculated from recent connections)
- Throughput rates (currently showing 0 until SmartProxy exposes rates)
- Top IPs with connection counts and percentages
-
TypeScript Fixes:
- SmartProxy methods like
getThroughputRate()not in base interface - Implemented manual fallbacks for missing methods
- Fixed
publicIpv4→publicIpproperty name
- SmartProxy methods like
Result
- Network view now shows real connection activity
- Auto-refreshes with other stats every second
- Displays actual IPs and connection counts
- No more mock/demo data
- Minimal code changes (streamlined approach)
Throughput Data Fix (2025-06-20)
The throughput was showing 0 because:
- MetricsManager was hardcoding throughputRate to 0, assuming the method didn't exist
- SmartProxy's
getStats()returnsIProxyStatsinterface, but the actual object (MetricsCollector) implementsIProxyStatsExtended getThroughputRate()only exists in the extended interface
Solution implemented:
- Updated MetricsManager to check if methods exist at runtime and call them
- Added property name mapping (
bytesInPerSec→bytesInPerSecond) - Created new
getNetworkStatsendpoint in security.handler.ts - Updated frontend to call the new endpoint for complete network metrics
The throughput data now flows correctly from SmartProxy → MetricsManager → API → UI.
Email Operations Dashboard (2026-02-01)
Overview
Replaced mock data in the email UI with real backend data from the delivery queue and security logger.
New Files Created
ts_interfaces/requests/email-ops.ts- TypedRequest interfaces for email operationsts/opsserver/handlers/email-ops.handler.ts- Backend handler for email operations
Key Interfaces
IReq_GetQueuedEmails- Fetch emails from delivery queue by statusIReq_GetSentEmails- Fetch delivered emailsIReq_GetFailedEmails- Fetch failed emailsIReq_ResendEmail- Re-queue a failed email for retryIReq_GetSecurityIncidents- Fetch security events from SecurityLoggerIReq_GetBounceRecords- Fetch bounce records and suppression listIReq_RemoveFromSuppressionList- Remove email from suppression list
UI Changes (ops-view-emails.ts)
- Replaced mock folders (inbox/sent/draft/trash) with operations views:
- Queued: Emails pending delivery
- Sent: Successfully delivered emails
- Failed: Failed emails with resend capability
- Security: Security incidents from SecurityLogger
- Removed
generateMockEmails()method - Added state management via
emailOpsStatePartin appstate.ts - Added resend button for failed emails
- Added security incident detail view
Data Flow
UnifiedDeliveryQueue → EmailOpsHandler → TypedRequest → Frontend State → UI
SecurityLogger → EmailOpsHandler → TypedRequest → Frontend State → UI
BounceManager → EmailOpsHandler → TypedRequest → Frontend State → UI
Backend Data Access
The handler accesses data from:
dcRouter.emailServer.deliveryQueue- Email queue items (IQueueItem)SecurityLogger.getInstance()- Security events (ISecurityEvent)emailServer.bounceManager- Bounce records and suppression list
OpsServer UI Fixes (2026-02-02)
Configuration Page Fix
The configuration page had field name mismatches between frontend and backend:
- Frontend expected
serverandstoragesections - Backend returns
proxysection (notserver) - Backend has no
storagesection
Fix: Updated ops-view-config.ts to use correct section names:
proxyinstead ofserver- Removed non-existent
storagesection - Added optional chaining (
?.) for safety
Auth Persistence Fix
Login state was using 'soft' mode in Smartstate which is memory-only:
- User login was lost on page refresh
- State reset to logged out after browser restart
Changes:
ts_web/appstate.ts: Changed loginStatePart from'soft'to'persistent'- Now uses IndexedDB to persist across browser sessions
ts/opsserver/handlers/admin.handler.ts: JWT expiry changed from 7 days to 24 hoursts_web/elements/ops-dashboard.ts: Added JWT expiry check on session restore- Validates stored JWT hasn't expired before auto-logging in
- Clears expired sessions and shows login form
Config UI Read-Only Conversion (2026-02-03)
Overview
The configuration UI has been converted from an editable interface to a read-only display. DcRouter is configured through code or remotely, not through the UI.
Changes Made
-
Backend (
ts/opsserver/handlers/config.handler.ts):- Removed
updateConfigurationhandler - Removed
updateConfiguration()private method - Kept
getConfigurationhandler (read-only)
- Removed
-
Interfaces (
ts_interfaces/requests/config.ts):- Removed
IReq_UpdateConfigurationinterface - Kept
IReq_GetConfigurationinterface
- Removed
-
Frontend (
ts_web/elements/ops-view-config.ts):- Removed
editingSectionandeditedConfigstate properties - Removed
startEdit(),cancelEdit(),saveConfig()methods - Removed Edit/Save/Cancel buttons
- Removed warning banner about immediate changes
- Enhanced read-only display with:
- Status badges for boolean values (enabled/disabled)
- Array display as pills/tags with counts
- Section icons (mail, globe, network, shield)
- Better formatting for numbers and byte sizes
- Empty state handling ("Not configured", "None configured")
- Info note explaining configuration is read-only
- Removed
-
State Management (
ts_web/appstate.ts):- Removed
updateConfigurationAction - Kept
fetchConfigurationAction(read-only)
- Removed
-
Tests (
test/test.protected-endpoint.ts):- Replaced
updateConfigurationtests withverifyIdentitytests - Added test for read-only config access
- Kept auth flow testing with different protected endpoint
- Replaced
-
Documentation:
readme.md: Updated API endpoints to show config as read-onlyts_web/readme.md: RemovedupdateConfigurationActionfrom actions listts_interfaces/readme.md: RemovedIReq_UpdateConfigurationfrom table
Visual Display Features
- Boolean values shown as colored badges (green=enabled, red=disabled)
- Arrays displayed as pills with count summaries
- Section headers with relevant Lucide icons
- Numbers formatted with locale separators
- Byte sizes auto-formatted (B, KB, MB, GB)
- Time values shown with "seconds" suffix
- Nested objects with visual indentation
Smartdata Cache System (2026-02-03)
Overview
DcRouter now uses smartdata + LocalTsmDb for persistent caching. Data is stored at ~/.serve.zone/dcrouter/tsmdb.
Technology Stack
| Layer | Package | Purpose |
|---|---|---|
| ORM | @push.rocks/smartdata |
Document classes, decorators, queries |
| Database | @push.rocks/smartmongo (LocalTsmDb) |
Embedded TsmDB via Unix socket |
TC39 Decorators
The project uses TC39 Stage 3 decorators (not experimental decorators). The tsconfig was updated:
- Removed
experimentalDecorators: true - Removed
emitDecoratorMetadata: true
This is required for smartdata v7+ compatibility.
Cache Document Classes
Located in ts/cache/documents/:
| Class | Purpose | Default TTL |
|---|---|---|
CachedEmail |
Email queue items | 30 days |
CachedIPReputation |
IP reputation lookups | 24 hours |
Note: CachedBounce, CachedSuppression, and CachedDKIMKey were removed in the smartmta migration (smartmta handles its own persistence for those).
Usage Pattern
// Document classes use smartdata decorators
@plugins.smartdata.Collection(() => getDb())
export class CachedEmail extends CachedDocument<CachedEmail> {
@plugins.smartdata.svDb()
public createdAt: Date = new Date();
@plugins.smartdata.svDb()
public expiresAt: Date = new Date(Date.now() + TTL.DAYS_30);
@plugins.smartdata.unI()
@plugins.smartdata.svDb()
public id: string;
// ...
}
// Query examples
const email = await CachedEmail.getInstance({ id: 'abc123' });
const pending = await CachedEmail.getInstances({ status: 'pending' });
await email.save();
await email.delete();
Configuration
const dcRouter = new DcRouter({
cacheConfig: {
enabled: true,
storagePath: '~/.serve.zone/dcrouter/tsmdb',
dbName: 'dcrouter',
cleanupIntervalHours: 1,
ttlConfig: {
emails: 30, // days
ipReputation: 1, // days
bounces: 30, // days
dkimKeys: 90, // days
suppression: 30 // days
}
}
});
Cache Cleaner
- Runs hourly by default (configurable via
cleanupIntervalHours) - Finds and deletes documents where
expiresAt < now() - Uses smartdata's
getInstances()+delete()pattern
Key Files
ts/cache/classes.cachedb.ts- CacheDb singleton wrapperts/cache/classes.cached.document.ts- Base class with TTL supportts/cache/classes.cache.cleaner.ts- Periodic cleanup servicets/cache/documents/*.ts- Document class definitions