BREAKING CHANGE(config): convert configuration management to read-only; remove updateConfiguration endpoint and client-side editing
This commit is contained in:
230
ts/cache/documents/classes.cached.email.ts
vendored
Normal file
230
ts/cache/documents/classes.cached.email.ts
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
import * as plugins from '../../plugins.js';
|
||||
import { CachedDocument, TTL } from '../classes.cached.document.js';
|
||||
import { CacheDb } from '../classes.cachedb.js';
|
||||
|
||||
/**
|
||||
* Email status in the cache
|
||||
*/
|
||||
export type TCachedEmailStatus = 'pending' | 'processing' | 'delivered' | 'failed' | 'deferred';
|
||||
|
||||
/**
|
||||
* Helper to get the smartdata database instance
|
||||
*/
|
||||
const getDb = () => CacheDb.getInstance().getDb();
|
||||
|
||||
/**
|
||||
* CachedEmail - Stores email queue items in the cache
|
||||
*
|
||||
* Used for persistent email queue storage, tracking delivery status,
|
||||
* and maintaining email history for the configured TTL period.
|
||||
*/
|
||||
@plugins.smartdata.Collection(() => getDb())
|
||||
export class CachedEmail extends CachedDocument<CachedEmail> {
|
||||
/**
|
||||
* Unique identifier for this email
|
||||
*/
|
||||
@plugins.smartdata.unI()
|
||||
@plugins.smartdata.svDb()
|
||||
public id: string;
|
||||
|
||||
/**
|
||||
* Email message ID (RFC 822 Message-ID header)
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public messageId: string;
|
||||
|
||||
/**
|
||||
* Sender email address (envelope from)
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public from: string;
|
||||
|
||||
/**
|
||||
* Recipient email addresses
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public to: string[];
|
||||
|
||||
/**
|
||||
* CC recipients
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public cc: string[];
|
||||
|
||||
/**
|
||||
* BCC recipients
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public bcc: string[];
|
||||
|
||||
/**
|
||||
* Email subject
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public subject: string;
|
||||
|
||||
/**
|
||||
* Raw RFC822 email content
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public rawContent: string;
|
||||
|
||||
/**
|
||||
* Current status of the email
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public status: TCachedEmailStatus;
|
||||
|
||||
/**
|
||||
* Number of delivery attempts
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public attempts: number = 0;
|
||||
|
||||
/**
|
||||
* Maximum number of delivery attempts
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public maxAttempts: number = 3;
|
||||
|
||||
/**
|
||||
* Timestamp for next delivery attempt
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public nextAttempt: Date;
|
||||
|
||||
/**
|
||||
* Last error message if delivery failed
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public lastError: string;
|
||||
|
||||
/**
|
||||
* Timestamp when the email was successfully delivered
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public deliveredAt: Date;
|
||||
|
||||
/**
|
||||
* Sender domain (for querying/filtering)
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public senderDomain: string;
|
||||
|
||||
/**
|
||||
* Priority level (higher = more important)
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public priority: number = 0;
|
||||
|
||||
/**
|
||||
* JSON-serialized route data
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public routeData: string;
|
||||
|
||||
/**
|
||||
* DKIM signature status
|
||||
*/
|
||||
@plugins.smartdata.svDb()
|
||||
public dkimSigned: boolean = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.setTTL(TTL.DAYS_30); // Default 30-day TTL
|
||||
this.status = 'pending';
|
||||
this.to = [];
|
||||
this.cc = [];
|
||||
this.bcc = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CachedEmail with a unique ID
|
||||
*/
|
||||
public static createNew(): CachedEmail {
|
||||
const email = new CachedEmail();
|
||||
email.id = plugins.uuid.v4();
|
||||
return email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find an email by ID
|
||||
*/
|
||||
public static async findById(id: string): Promise<CachedEmail | null> {
|
||||
return await CachedEmail.getInstance({
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all emails with a specific status
|
||||
*/
|
||||
public static async findByStatus(status: TCachedEmailStatus): Promise<CachedEmail[]> {
|
||||
return await CachedEmail.getInstances({
|
||||
status,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all emails pending delivery (status = pending and nextAttempt <= now)
|
||||
*/
|
||||
public static async findPendingForDelivery(): Promise<CachedEmail[]> {
|
||||
const now = new Date();
|
||||
return await CachedEmail.getInstances({
|
||||
status: 'pending',
|
||||
nextAttempt: { $lte: now },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Find emails by sender domain
|
||||
*/
|
||||
public static async findBySenderDomain(domain: string): Promise<CachedEmail[]> {
|
||||
return await CachedEmail.getInstances({
|
||||
senderDomain: domain,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark as delivered
|
||||
*/
|
||||
public markDelivered(): void {
|
||||
this.status = 'delivered';
|
||||
this.deliveredAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark as failed with error
|
||||
*/
|
||||
public markFailed(error: string): void {
|
||||
this.status = 'failed';
|
||||
this.lastError = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increment attempt counter and schedule next attempt
|
||||
*/
|
||||
public scheduleRetry(delayMs: number = 5 * 60 * 1000): void {
|
||||
this.attempts++;
|
||||
this.status = 'deferred';
|
||||
this.nextAttempt = new Date(Date.now() + delayMs);
|
||||
|
||||
// If max attempts reached, mark as failed
|
||||
if (this.attempts >= this.maxAttempts) {
|
||||
this.status = 'failed';
|
||||
this.lastError = `Max attempts (${this.maxAttempts}) reached`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract sender domain from email address
|
||||
*/
|
||||
public updateSenderDomain(): void {
|
||||
if (this.from) {
|
||||
const match = this.from.match(/@([^>]+)>?$/);
|
||||
if (match) {
|
||||
this.senderDomain = match[1].toLowerCase();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user