@push.rocks/smartmta 5.1.3 → 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +15 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -0
- package/dist_ts/logger.d.ts +17 -0
- package/dist_ts/logger.js +76 -0
- package/dist_ts/mail/core/classes.bouncemanager.d.ts +185 -0
- package/dist_ts/mail/core/classes.bouncemanager.js +569 -0
- package/dist_ts/mail/core/classes.email.d.ts +291 -0
- package/dist_ts/mail/core/classes.email.js +802 -0
- package/dist_ts/mail/core/classes.emailvalidator.d.ts +61 -0
- package/dist_ts/mail/core/classes.emailvalidator.js +184 -0
- package/dist_ts/mail/core/classes.templatemanager.d.ts +95 -0
- package/dist_ts/mail/core/classes.templatemanager.js +240 -0
- package/dist_ts/mail/core/index.d.ts +4 -0
- package/dist_ts/mail/core/index.js +6 -0
- package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
- package/dist_ts/mail/delivery/classes.delivery.queue.js +488 -0
- package/dist_ts/mail/delivery/classes.delivery.system.d.ts +160 -0
- package/dist_ts/mail/delivery/classes.delivery.system.js +630 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
- package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +820 -0
- package/dist_ts/mail/delivery/index.d.ts +4 -0
- package/dist_ts/mail/delivery/index.js +6 -0
- package/dist_ts/mail/delivery/interfaces.d.ts +140 -0
- package/dist_ts/mail/delivery/interfaces.js +17 -0
- package/dist_ts/mail/index.d.ts +7 -0
- package/dist_ts/mail/index.js +12 -0
- package/dist_ts/mail/routing/classes.dkim.manager.d.ts +25 -0
- package/dist_ts/mail/routing/classes.dkim.manager.js +127 -0
- package/dist_ts/mail/routing/classes.dns.manager.d.ts +79 -0
- package/dist_ts/mail/routing/classes.dns.manager.js +415 -0
- package/dist_ts/mail/routing/classes.domain.registry.d.ts +54 -0
- package/dist_ts/mail/routing/classes.domain.registry.js +119 -0
- package/dist_ts/mail/routing/classes.email.action.executor.d.ts +33 -0
- package/dist_ts/mail/routing/classes.email.action.executor.js +137 -0
- package/dist_ts/mail/routing/classes.email.router.d.ts +171 -0
- package/dist_ts/mail/routing/classes.email.router.js +494 -0
- package/dist_ts/mail/routing/classes.unified.email.server.d.ts +241 -0
- package/dist_ts/mail/routing/classes.unified.email.server.js +935 -0
- package/dist_ts/mail/routing/index.d.ts +7 -0
- package/dist_ts/mail/routing/index.js +9 -0
- package/dist_ts/mail/routing/interfaces.d.ts +187 -0
- package/dist_ts/mail/routing/interfaces.js +2 -0
- package/dist_ts/mail/security/classes.dkimcreator.d.ts +72 -0
- package/dist_ts/mail/security/classes.dkimcreator.js +360 -0
- package/dist_ts/mail/security/classes.spfverifier.d.ts +62 -0
- package/dist_ts/mail/security/classes.spfverifier.js +87 -0
- package/dist_ts/mail/security/index.d.ts +2 -0
- package/dist_ts/mail/security/index.js +4 -0
- package/dist_ts/paths.d.ts +14 -0
- package/dist_ts/paths.js +39 -0
- package/dist_ts/plugins.d.ts +24 -0
- package/dist_ts/plugins.js +28 -0
- package/dist_ts/security/classes.contentscanner.d.ts +130 -0
- package/dist_ts/security/classes.contentscanner.js +338 -0
- package/dist_ts/security/classes.ipreputationchecker.d.ts +73 -0
- package/dist_ts/security/classes.ipreputationchecker.js +263 -0
- package/dist_ts/security/classes.rustsecuritybridge.d.ts +403 -0
- package/dist_ts/security/classes.rustsecuritybridge.js +502 -0
- package/dist_ts/security/classes.securitylogger.d.ts +140 -0
- package/dist_ts/security/classes.securitylogger.js +235 -0
- package/dist_ts/security/index.d.ts +4 -0
- package/dist_ts/security/index.js +5 -0
- package/package.json +6 -1
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/index.ts +3 -0
- package/ts/logger.ts +91 -0
- package/ts/mail/core/classes.bouncemanager.ts +731 -0
- package/ts/mail/core/classes.email.ts +942 -0
- package/ts/mail/core/classes.emailvalidator.ts +239 -0
- package/ts/mail/core/classes.templatemanager.ts +320 -0
- package/ts/mail/core/index.ts +5 -0
- package/ts/mail/delivery/classes.delivery.queue.ts +645 -0
- package/ts/mail/delivery/classes.delivery.system.ts +816 -0
- package/ts/mail/delivery/classes.unified.rate.limiter.ts +1053 -0
- package/ts/mail/delivery/index.ts +5 -0
- package/ts/mail/delivery/interfaces.ts +167 -0
- package/ts/mail/index.ts +17 -0
- package/ts/mail/routing/classes.dkim.manager.ts +157 -0
- package/ts/mail/routing/classes.dns.manager.ts +573 -0
- package/ts/mail/routing/classes.domain.registry.ts +139 -0
- package/ts/mail/routing/classes.email.action.executor.ts +175 -0
- package/ts/mail/routing/classes.email.router.ts +575 -0
- package/ts/mail/routing/classes.unified.email.server.ts +1207 -0
- package/ts/mail/routing/index.ts +9 -0
- package/ts/mail/routing/interfaces.ts +202 -0
- package/ts/mail/security/classes.dkimcreator.ts +447 -0
- package/ts/mail/security/classes.spfverifier.ts +126 -0
- package/ts/mail/security/index.ts +3 -0
- package/ts/paths.ts +48 -0
- package/ts/plugins.ts +53 -0
- package/ts/security/classes.contentscanner.ts +400 -0
- package/ts/security/classes.ipreputationchecker.ts +315 -0
- package/ts/security/classes.rustsecuritybridge.ts +964 -0
- package/ts/security/classes.securitylogger.ts +299 -0
- package/ts/security/index.ts +40 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export interface IEmailValidationResult {
|
|
2
|
+
isValid: boolean;
|
|
3
|
+
hasMx: boolean;
|
|
4
|
+
hasSpamMarkings: boolean;
|
|
5
|
+
score: number;
|
|
6
|
+
details?: {
|
|
7
|
+
formatValid?: boolean;
|
|
8
|
+
mxRecords?: string[];
|
|
9
|
+
disposable?: boolean;
|
|
10
|
+
role?: boolean;
|
|
11
|
+
spamIndicators?: string[];
|
|
12
|
+
errorMessage?: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Advanced email validator class using smartmail's capabilities
|
|
17
|
+
*/
|
|
18
|
+
export declare class EmailValidator {
|
|
19
|
+
private validator;
|
|
20
|
+
private dnsCache;
|
|
21
|
+
constructor(options?: {
|
|
22
|
+
maxCacheSize?: number;
|
|
23
|
+
cacheTTL?: number;
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Validates an email address using comprehensive checks
|
|
27
|
+
* @param email The email to validate
|
|
28
|
+
* @param options Validation options
|
|
29
|
+
* @returns Validation result with details
|
|
30
|
+
*/
|
|
31
|
+
validate(email: string, options?: {
|
|
32
|
+
checkMx?: boolean;
|
|
33
|
+
checkDisposable?: boolean;
|
|
34
|
+
checkRole?: boolean;
|
|
35
|
+
checkSyntaxOnly?: boolean;
|
|
36
|
+
}): Promise<IEmailValidationResult>;
|
|
37
|
+
/**
|
|
38
|
+
* Gets MX records for a domain with caching
|
|
39
|
+
* @param domain Domain to check
|
|
40
|
+
* @returns Array of MX records
|
|
41
|
+
*/
|
|
42
|
+
private getMxRecords;
|
|
43
|
+
/**
|
|
44
|
+
* Validates multiple email addresses in batch
|
|
45
|
+
* @param emails Array of emails to validate
|
|
46
|
+
* @param options Validation options
|
|
47
|
+
* @returns Object with email addresses as keys and validation results as values
|
|
48
|
+
*/
|
|
49
|
+
validateBatch(emails: string[], options?: {
|
|
50
|
+
checkMx?: boolean;
|
|
51
|
+
checkDisposable?: boolean;
|
|
52
|
+
checkRole?: boolean;
|
|
53
|
+
checkSyntaxOnly?: boolean;
|
|
54
|
+
}): Promise<Record<string, IEmailValidationResult>>;
|
|
55
|
+
/**
|
|
56
|
+
* Quick check if an email format is valid (synchronous, no DNS checks)
|
|
57
|
+
* @param email Email to check
|
|
58
|
+
* @returns Boolean indicating if format is valid
|
|
59
|
+
*/
|
|
60
|
+
isValidFormat(email: string): boolean;
|
|
61
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { logger } from '../../logger.js';
|
|
3
|
+
import { LRUCache } from 'lru-cache';
|
|
4
|
+
/**
|
|
5
|
+
* Advanced email validator class using smartmail's capabilities
|
|
6
|
+
*/
|
|
7
|
+
export class EmailValidator {
|
|
8
|
+
validator;
|
|
9
|
+
dnsCache;
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.validator = new plugins.smartmail.EmailAddressValidator();
|
|
12
|
+
// Initialize LRU cache for DNS records
|
|
13
|
+
this.dnsCache = new LRUCache({
|
|
14
|
+
// Default to 1000 entries (reasonable for most applications)
|
|
15
|
+
max: options?.maxCacheSize || 1000,
|
|
16
|
+
// Default TTL of 1 hour (DNS records don't change frequently)
|
|
17
|
+
ttl: options?.cacheTTL || 60 * 60 * 1000,
|
|
18
|
+
// Optional cache monitoring
|
|
19
|
+
allowStale: false,
|
|
20
|
+
updateAgeOnGet: true,
|
|
21
|
+
// Add logging for cache events in production environments
|
|
22
|
+
disposeAfter: (value, key) => {
|
|
23
|
+
logger.log('debug', `DNS cache entry expired for domain: ${key}`);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Validates an email address using comprehensive checks
|
|
29
|
+
* @param email The email to validate
|
|
30
|
+
* @param options Validation options
|
|
31
|
+
* @returns Validation result with details
|
|
32
|
+
*/
|
|
33
|
+
async validate(email, options = {}) {
|
|
34
|
+
try {
|
|
35
|
+
const result = {
|
|
36
|
+
isValid: false,
|
|
37
|
+
hasMx: false,
|
|
38
|
+
hasSpamMarkings: false,
|
|
39
|
+
score: 0,
|
|
40
|
+
details: {
|
|
41
|
+
formatValid: false,
|
|
42
|
+
spamIndicators: []
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
// Always check basic format
|
|
46
|
+
result.details.formatValid = this.validator.isValidEmailFormat(email);
|
|
47
|
+
if (!result.details.formatValid) {
|
|
48
|
+
result.details.errorMessage = 'Invalid email format';
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
// If syntax-only check is requested, return early
|
|
52
|
+
if (options.checkSyntaxOnly) {
|
|
53
|
+
result.isValid = true;
|
|
54
|
+
result.score = 0.5;
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
// Get domain for additional checks
|
|
58
|
+
const domain = email.split('@')[1];
|
|
59
|
+
// Check MX records
|
|
60
|
+
if (options.checkMx !== false) {
|
|
61
|
+
try {
|
|
62
|
+
const mxRecords = await this.getMxRecords(domain);
|
|
63
|
+
result.details.mxRecords = mxRecords;
|
|
64
|
+
result.hasMx = mxRecords && mxRecords.length > 0;
|
|
65
|
+
if (!result.hasMx) {
|
|
66
|
+
result.details.spamIndicators.push('No MX records');
|
|
67
|
+
result.details.errorMessage = 'Domain has no MX records';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
logger.log('error', `Error checking MX records: ${error.message}`);
|
|
72
|
+
result.details.errorMessage = 'Unable to check MX records';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Check if domain is disposable
|
|
76
|
+
if (options.checkDisposable !== false) {
|
|
77
|
+
result.details.disposable = await this.validator.isDisposableEmail(email);
|
|
78
|
+
if (result.details.disposable) {
|
|
79
|
+
result.details.spamIndicators.push('Disposable email');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Check if email is a role account
|
|
83
|
+
if (options.checkRole !== false) {
|
|
84
|
+
result.details.role = this.validator.isRoleAccount(email);
|
|
85
|
+
if (result.details.role) {
|
|
86
|
+
result.details.spamIndicators.push('Role account');
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// Calculate spam score and final validity
|
|
90
|
+
result.hasSpamMarkings = result.details.spamIndicators.length > 0;
|
|
91
|
+
// Calculate a score between 0-1 based on checks
|
|
92
|
+
let scoreFactors = 0;
|
|
93
|
+
let scoreTotal = 0;
|
|
94
|
+
// Format check (highest weight)
|
|
95
|
+
scoreFactors += 0.4;
|
|
96
|
+
if (result.details.formatValid)
|
|
97
|
+
scoreTotal += 0.4;
|
|
98
|
+
// MX check (high weight)
|
|
99
|
+
if (options.checkMx !== false) {
|
|
100
|
+
scoreFactors += 0.3;
|
|
101
|
+
if (result.hasMx)
|
|
102
|
+
scoreTotal += 0.3;
|
|
103
|
+
}
|
|
104
|
+
// Disposable check (medium weight)
|
|
105
|
+
if (options.checkDisposable !== false) {
|
|
106
|
+
scoreFactors += 0.2;
|
|
107
|
+
if (!result.details.disposable)
|
|
108
|
+
scoreTotal += 0.2;
|
|
109
|
+
}
|
|
110
|
+
// Role account check (low weight)
|
|
111
|
+
if (options.checkRole !== false) {
|
|
112
|
+
scoreFactors += 0.1;
|
|
113
|
+
if (!result.details.role)
|
|
114
|
+
scoreTotal += 0.1;
|
|
115
|
+
}
|
|
116
|
+
// Normalize score based on factors actually checked
|
|
117
|
+
result.score = scoreFactors > 0 ? scoreTotal / scoreFactors : 0;
|
|
118
|
+
// Email is valid if score is above 0.7 (configurable threshold)
|
|
119
|
+
result.isValid = result.score >= 0.7;
|
|
120
|
+
return result;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
logger.log('error', `Email validation error: ${error.message}`);
|
|
124
|
+
return {
|
|
125
|
+
isValid: false,
|
|
126
|
+
hasMx: false,
|
|
127
|
+
hasSpamMarkings: true,
|
|
128
|
+
score: 0,
|
|
129
|
+
details: {
|
|
130
|
+
formatValid: false,
|
|
131
|
+
errorMessage: `Validation error: ${error.message}`,
|
|
132
|
+
spamIndicators: ['Validation error']
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Gets MX records for a domain with caching
|
|
139
|
+
* @param domain Domain to check
|
|
140
|
+
* @returns Array of MX records
|
|
141
|
+
*/
|
|
142
|
+
async getMxRecords(domain) {
|
|
143
|
+
// Check cache first
|
|
144
|
+
const cachedRecords = this.dnsCache.get(domain);
|
|
145
|
+
if (cachedRecords) {
|
|
146
|
+
logger.log('debug', `Using cached MX records for domain: ${domain}`);
|
|
147
|
+
return cachedRecords;
|
|
148
|
+
}
|
|
149
|
+
try {
|
|
150
|
+
// Use smartmail's getMxRecords method
|
|
151
|
+
const records = await this.validator.getMxRecords(domain);
|
|
152
|
+
// Store in cache (TTL is handled by the LRU cache configuration)
|
|
153
|
+
this.dnsCache.set(domain, records);
|
|
154
|
+
logger.log('debug', `Cached MX records for domain: ${domain}`);
|
|
155
|
+
return records;
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
logger.log('error', `Error fetching MX records for ${domain}: ${error.message}`);
|
|
159
|
+
return [];
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Validates multiple email addresses in batch
|
|
164
|
+
* @param emails Array of emails to validate
|
|
165
|
+
* @param options Validation options
|
|
166
|
+
* @returns Object with email addresses as keys and validation results as values
|
|
167
|
+
*/
|
|
168
|
+
async validateBatch(emails, options = {}) {
|
|
169
|
+
const results = {};
|
|
170
|
+
for (const email of emails) {
|
|
171
|
+
results[email] = await this.validate(email, options);
|
|
172
|
+
}
|
|
173
|
+
return results;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Quick check if an email format is valid (synchronous, no DNS checks)
|
|
177
|
+
* @param email Email to check
|
|
178
|
+
* @returns Boolean indicating if format is valid
|
|
179
|
+
*/
|
|
180
|
+
isValidFormat(email) {
|
|
181
|
+
return this.validator.isValidEmailFormat(email);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5lbWFpbHZhbGlkYXRvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvY29yZS9jbGFzc2VzLmVtYWlsdmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3pDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFpQnJDOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGNBQWM7SUFDakIsU0FBUyxDQUEwQztJQUNuRCxRQUFRLENBQTZCO0lBRTdDLFlBQVksT0FHWDtRQUNDLElBQUksQ0FBQyxTQUFTLEdBQUcsSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFFL0QsdUNBQXVDO1FBQ3ZDLElBQUksQ0FBQyxRQUFRLEdBQUcsSUFBSSxRQUFRLENBQW1CO1lBQzdDLDZEQUE2RDtZQUM3RCxHQUFHLEVBQUUsT0FBTyxFQUFFLFlBQVksSUFBSSxJQUFJO1lBQ2xDLDhEQUE4RDtZQUM5RCxHQUFHLEVBQUUsT0FBTyxFQUFFLFFBQVEsSUFBSSxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUk7WUFDeEMsNEJBQTRCO1lBQzVCLFVBQVUsRUFBRSxLQUFLO1lBQ2pCLGNBQWMsRUFBRSxJQUFJO1lBQ3BCLDBEQUEwRDtZQUMxRCxZQUFZLEVBQUUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLEVBQUU7Z0JBQzNCLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLHVDQUF1QyxHQUFHLEVBQUUsQ0FBQyxDQUFDO1lBQ3BFLENBQUM7U0FDRixDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7Ozs7O09BS0c7SUFDSSxLQUFLLENBQUMsUUFBUSxDQUNuQixLQUFhLEVBQ2IsVUFLSSxFQUFFO1FBRU4sSUFBSSxDQUFDO1lBQ0gsTUFBTSxNQUFNLEdBQTJCO2dCQUNyQyxPQUFPLEVBQUUsS0FBSztnQkFDZCxLQUFLLEVBQUUsS0FBSztnQkFDWixlQUFlLEVBQUUsS0FBSztnQkFDdEIsS0FBSyxFQUFFLENBQUM7Z0JBQ1IsT0FBTyxFQUFFO29CQUNQLFdBQVcsRUFBRSxLQUFLO29CQUNsQixjQUFjLEVBQUUsRUFBRTtpQkFDbkI7YUFDRixDQUFDO1lBRUYsNEJBQTRCO1lBQzVCLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsa0JBQWtCLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDdEUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVyxFQUFFLENBQUM7Z0JBQ2hDLE1BQU0sQ0FBQyxPQUFPLENBQUMsWUFBWSxHQUFHLHNCQUFzQixDQUFDO2dCQUNyRCxPQUFPLE1BQU0sQ0FBQztZQUNoQixDQUFDO1lBRUQsa0RBQWtEO1lBQ2xELElBQUksT0FBTyxDQUFDLGVBQWUsRUFBRSxDQUFDO2dCQUM1QixNQUFNLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQztnQkFDdEIsTUFBTSxDQUFDLEtBQUssR0FBRyxHQUFHLENBQUM7Z0JBQ25CLE9BQU8sTUFBTSxDQUFDO1lBQ2hCLENBQUM7WUFFRCxtQ0FBbUM7WUFDbkMsTUFBTSxNQUFNLEdBQUcsS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUVuQyxtQkFBbUI7WUFDbkIsSUFBSSxPQUFPLENBQUMsT0FBTyxLQUFLLEtBQUssRUFBRSxDQUFDO2dCQUM5QixJQUFJLENBQUM7b0JBQ0gsTUFBTSxTQUFTLEdBQUcsTUFBTSxJQUFJLENBQUMsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDO29CQUNsRCxNQUFNLENBQUMsT0FBTyxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUM7b0JBQ3JDLE1BQU0sQ0FBQyxLQUFLLEdBQUcsU0FBUyxJQUFJLFNBQVMsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDO29CQUVqRCxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO3dCQUNsQixNQUFNLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsZUFBZSxDQUFDLENBQUM7d0JBQ3BELE1BQU0sQ0FBQyxPQUFPLENBQUMsWUFBWSxHQUFHLDBCQUEwQixDQUFDO29CQUMzRCxDQUFDO2dCQUNILENBQUM7Z0JBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztvQkFDZixNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSw4QkFBOEIsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7b0JBQ25FLE1BQU0sQ0FBQyxPQUFPLENBQUMsWUFBWSxHQUFHLDRCQUE0QixDQUFDO2dCQUM3RCxDQUFDO1lBQ0gsQ0FBQztZQUVELGdDQUFnQztZQUNoQyxJQUFJLE9BQU8sQ0FBQyxlQUFlLEtBQUssS0FBSyxFQUFFLENBQUM7Z0JBQ3RDLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVSxHQUFHLE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDMUUsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLFVBQVUsRUFBRSxDQUFDO29CQUM5QixNQUFNLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsQ0FBQztnQkFDekQsQ0FBQztZQUNILENBQUM7WUFFRCxtQ0FBbUM7WUFDbkMsSUFBSSxPQUFPLENBQUMsU0FBUyxLQUFLLEtBQUssRUFBRSxDQUFDO2dCQUNoQyxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDMUQsSUFBSSxNQUFNLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO29CQUN4QixNQUFNLENBQUMsT0FBTyxDQUFDLGNBQWMsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7Z0JBQ3JELENBQUM7WUFDSCxDQUFDO1lBRUQsMENBQTBDO1lBQzFDLE1BQU0sQ0FBQyxlQUFlLEdBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQyxjQUFjLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztZQUVsRSxnREFBZ0Q7WUFDaEQsSUFBSSxZQUFZLEdBQUcsQ0FBQyxDQUFDO1lBQ3JCLElBQUksVUFBVSxHQUFHLENBQUMsQ0FBQztZQUVuQixnQ0FBZ0M7WUFDaEMsWUFBWSxJQUFJLEdBQUcsQ0FBQztZQUNwQixJQUFJLE1BQU0sQ0FBQyxPQUFPLENBQUMsV0FBVztnQkFBRSxVQUFVLElBQUksR0FBRyxDQUFDO1lBRWxELHlCQUF5QjtZQUN6QixJQUFJLE9BQU8sQ0FBQyxPQUFPLEtBQUssS0FBSyxFQUFFLENBQUM7Z0JBQzlCLFlBQVksSUFBSSxHQUFHLENBQUM7Z0JBQ3BCLElBQUksTUFBTSxDQUFDLEtBQUs7b0JBQUUsVUFBVSxJQUFJLEdBQUcsQ0FBQztZQUN0QyxDQUFDO1lBRUQsbUNBQW1DO1lBQ25DLElBQUksT0FBTyxDQUFDLGVBQWUsS0FBSyxLQUFLLEVBQUUsQ0FBQztnQkFDdEMsWUFBWSxJQUFJLEdBQUcsQ0FBQztnQkFDcEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsVUFBVTtvQkFBRSxVQUFVLElBQUksR0FBRyxDQUFDO1lBQ3BELENBQUM7WUFFRCxrQ0FBa0M7WUFDbEMsSUFBSSxPQUFPLENBQUMsU0FBUyxLQUFLLEtBQUssRUFBRSxDQUFDO2dCQUNoQyxZQUFZLElBQUksR0FBRyxDQUFDO2dCQUNwQixJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJO29CQUFFLFVBQVUsSUFBSSxHQUFHLENBQUM7WUFDOUMsQ0FBQztZQUVELG9EQUFvRDtZQUNwRCxNQUFNLENBQUMsS0FBSyxHQUFHLFlBQVksR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFVBQVUsR0FBRyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUVoRSxnRUFBZ0U7WUFDaEUsTUFBTSxDQUFDLE9BQU8sR0FBRyxNQUFNLENBQUMsS0FBSyxJQUFJLEdBQUcsQ0FBQztZQUVyQyxPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLDJCQUEyQixLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztZQUNoRSxPQUFPO2dCQUNMLE9BQU8sRUFBRSxLQUFLO2dCQUNkLEtBQUssRUFBRSxLQUFLO2dCQUNaLGVBQWUsRUFBRSxJQUFJO2dCQUNyQixLQUFLLEVBQUUsQ0FBQztnQkFDUixPQUFPLEVBQUU7b0JBQ1AsV0FBVyxFQUFFLEtBQUs7b0JBQ2xCLFlBQVksRUFBRSxxQkFBcUIsS0FBSyxDQUFDLE9BQU8sRUFBRTtvQkFDbEQsY0FBYyxFQUFFLENBQUMsa0JBQWtCLENBQUM7aUJBQ3JDO2FBQ0YsQ0FBQztRQUNKLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLEtBQUssQ0FBQyxZQUFZLENBQUMsTUFBYztRQUN2QyxvQkFBb0I7UUFDcEIsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDaEQsSUFBSSxhQUFhLEVBQUUsQ0FBQztZQUNsQixNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSx1Q0FBdUMsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUNyRSxPQUFPLGFBQWEsQ0FBQztRQUN2QixDQUFDO1FBRUQsSUFBSSxDQUFDO1lBQ0gsc0NBQXNDO1lBQ3RDLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsTUFBTSxDQUFDLENBQUM7WUFFMUQsaUVBQWlFO1lBQ2pFLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztZQUNuQyxNQUFNLENBQUMsR0FBRyxDQUFDLE9BQU8sRUFBRSxpQ0FBaUMsTUFBTSxFQUFFLENBQUMsQ0FBQztZQUUvRCxPQUFPLE9BQU8sQ0FBQztRQUNqQixDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE1BQU0sQ0FBQyxHQUFHLENBQUMsT0FBTyxFQUFFLGlDQUFpQyxNQUFNLEtBQUssS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUM7WUFDakYsT0FBTyxFQUFFLENBQUM7UUFDWixDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7OztPQUtHO0lBQ0ksS0FBSyxDQUFDLGFBQWEsQ0FDeEIsTUFBZ0IsRUFDaEIsVUFLSSxFQUFFO1FBRU4sTUFBTSxPQUFPLEdBQTJDLEVBQUUsQ0FBQztRQUUzRCxLQUFLLE1BQU0sS0FBSyxJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQzNCLE9BQU8sQ0FBQyxLQUFLLENBQUMsR0FBRyxNQUFNLElBQUksQ0FBQyxRQUFRLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDO1FBQ3ZELENBQUM7UUFFRCxPQUFPLE9BQU8sQ0FBQztJQUNqQixDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLGFBQWEsQ0FBQyxLQUFhO1FBQ2hDLE9BQU8sSUFBSSxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNsRCxDQUFDO0NBRUYifQ==
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Email } from './classes.email.js';
|
|
2
|
+
/**
|
|
3
|
+
* Email template type definition
|
|
4
|
+
*/
|
|
5
|
+
export interface IEmailTemplate<T = any> {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
description: string;
|
|
9
|
+
from: string;
|
|
10
|
+
subject: string;
|
|
11
|
+
bodyHtml: string;
|
|
12
|
+
bodyText?: string;
|
|
13
|
+
category?: string;
|
|
14
|
+
sampleData?: T;
|
|
15
|
+
attachments?: Array<{
|
|
16
|
+
name: string;
|
|
17
|
+
path: string;
|
|
18
|
+
contentType?: string;
|
|
19
|
+
}>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Email template context - data used to render the template
|
|
23
|
+
*/
|
|
24
|
+
export interface ITemplateContext {
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Template category definitions
|
|
29
|
+
*/
|
|
30
|
+
export declare enum TemplateCategory {
|
|
31
|
+
NOTIFICATION = "notification",
|
|
32
|
+
TRANSACTIONAL = "transactional",
|
|
33
|
+
MARKETING = "marketing",
|
|
34
|
+
SYSTEM = "system"
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Enhanced template manager using Email class for template rendering
|
|
38
|
+
*/
|
|
39
|
+
export declare class TemplateManager {
|
|
40
|
+
private templates;
|
|
41
|
+
private defaultConfig;
|
|
42
|
+
constructor(defaultConfig?: {
|
|
43
|
+
from?: string;
|
|
44
|
+
replyTo?: string;
|
|
45
|
+
footerHtml?: string;
|
|
46
|
+
footerText?: string;
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* Register built-in email templates
|
|
50
|
+
*/
|
|
51
|
+
private registerBuiltinTemplates;
|
|
52
|
+
/**
|
|
53
|
+
* Register a new email template
|
|
54
|
+
* @param template The email template to register
|
|
55
|
+
*/
|
|
56
|
+
registerTemplate<T = any>(template: IEmailTemplate<T>): void;
|
|
57
|
+
/**
|
|
58
|
+
* Get an email template by ID
|
|
59
|
+
* @param templateId The template ID
|
|
60
|
+
* @returns The template or undefined if not found
|
|
61
|
+
*/
|
|
62
|
+
getTemplate<T = any>(templateId: string): IEmailTemplate<T> | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* List all available templates
|
|
65
|
+
* @param category Optional category filter
|
|
66
|
+
* @returns Array of email templates
|
|
67
|
+
*/
|
|
68
|
+
listTemplates(category?: TemplateCategory): IEmailTemplate[];
|
|
69
|
+
/**
|
|
70
|
+
* Create an Email instance from a template
|
|
71
|
+
* @param templateId The template ID
|
|
72
|
+
* @param context The template context data
|
|
73
|
+
* @returns A configured Email instance
|
|
74
|
+
*/
|
|
75
|
+
createEmail<T = any>(templateId: string, context?: ITemplateContext): Promise<Email>;
|
|
76
|
+
/**
|
|
77
|
+
* Create and completely process an Email instance from a template
|
|
78
|
+
* @param templateId The template ID
|
|
79
|
+
* @param context The template context data
|
|
80
|
+
* @returns A complete, processed Email instance ready to send
|
|
81
|
+
*/
|
|
82
|
+
prepareEmail<T = any>(templateId: string, context?: ITemplateContext): Promise<Email>;
|
|
83
|
+
/**
|
|
84
|
+
* Create a MIME-formatted email from a template
|
|
85
|
+
* @param templateId The template ID
|
|
86
|
+
* @param context The template context data
|
|
87
|
+
* @returns A MIME-formatted email string
|
|
88
|
+
*/
|
|
89
|
+
createMimeEmail(templateId: string, context?: ITemplateContext): Promise<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Load templates from a directory
|
|
92
|
+
* @param directory The directory containing template JSON files
|
|
93
|
+
*/
|
|
94
|
+
loadTemplatesFromDirectory(directory: string): Promise<void>;
|
|
95
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import * as paths from '../../paths.js';
|
|
3
|
+
import { logger } from '../../logger.js';
|
|
4
|
+
import { Email } from './classes.email.js';
|
|
5
|
+
/**
|
|
6
|
+
* Template category definitions
|
|
7
|
+
*/
|
|
8
|
+
export var TemplateCategory;
|
|
9
|
+
(function (TemplateCategory) {
|
|
10
|
+
TemplateCategory["NOTIFICATION"] = "notification";
|
|
11
|
+
TemplateCategory["TRANSACTIONAL"] = "transactional";
|
|
12
|
+
TemplateCategory["MARKETING"] = "marketing";
|
|
13
|
+
TemplateCategory["SYSTEM"] = "system";
|
|
14
|
+
})(TemplateCategory || (TemplateCategory = {}));
|
|
15
|
+
/**
|
|
16
|
+
* Enhanced template manager using Email class for template rendering
|
|
17
|
+
*/
|
|
18
|
+
export class TemplateManager {
|
|
19
|
+
templates = new Map();
|
|
20
|
+
defaultConfig;
|
|
21
|
+
constructor(defaultConfig) {
|
|
22
|
+
// Set default configuration
|
|
23
|
+
this.defaultConfig = {
|
|
24
|
+
from: defaultConfig?.from || 'noreply@mail.lossless.com',
|
|
25
|
+
replyTo: defaultConfig?.replyTo,
|
|
26
|
+
footerHtml: defaultConfig?.footerHtml || '',
|
|
27
|
+
footerText: defaultConfig?.footerText || ''
|
|
28
|
+
};
|
|
29
|
+
// Initialize with built-in templates
|
|
30
|
+
this.registerBuiltinTemplates();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Register built-in email templates
|
|
34
|
+
*/
|
|
35
|
+
registerBuiltinTemplates() {
|
|
36
|
+
// Welcome email
|
|
37
|
+
this.registerTemplate({
|
|
38
|
+
id: 'welcome',
|
|
39
|
+
name: 'Welcome Email',
|
|
40
|
+
description: 'Sent to users when they first sign up',
|
|
41
|
+
from: this.defaultConfig.from,
|
|
42
|
+
subject: 'Welcome to {{serviceName}}!',
|
|
43
|
+
category: TemplateCategory.TRANSACTIONAL,
|
|
44
|
+
bodyHtml: `
|
|
45
|
+
<h1>Welcome, {{firstName}}!</h1>
|
|
46
|
+
<p>Thank you for joining {{serviceName}}. We're excited to have you on board.</p>
|
|
47
|
+
<p>To get started, <a href="{{accountUrl}}">visit your account</a>.</p>
|
|
48
|
+
`,
|
|
49
|
+
bodyText: `Welcome, {{firstName}}!
|
|
50
|
+
|
|
51
|
+
Thank you for joining {{serviceName}}. We're excited to have you on board.
|
|
52
|
+
|
|
53
|
+
To get started, visit your account: {{accountUrl}}
|
|
54
|
+
`,
|
|
55
|
+
sampleData: {
|
|
56
|
+
firstName: 'John',
|
|
57
|
+
accountUrl: 'https://example.com/account'
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// Password reset
|
|
61
|
+
this.registerTemplate({
|
|
62
|
+
id: 'password-reset',
|
|
63
|
+
name: 'Password Reset',
|
|
64
|
+
description: 'Sent when a user requests a password reset',
|
|
65
|
+
from: this.defaultConfig.from,
|
|
66
|
+
subject: 'Password Reset Request',
|
|
67
|
+
category: TemplateCategory.TRANSACTIONAL,
|
|
68
|
+
bodyHtml: `
|
|
69
|
+
<h2>Password Reset Request</h2>
|
|
70
|
+
<p>You recently requested to reset your password. Click the link below to reset it:</p>
|
|
71
|
+
<p><a href="{{resetUrl}}">Reset Password</a></p>
|
|
72
|
+
<p>This link will expire in {{expiryHours}} hours.</p>
|
|
73
|
+
<p>If you didn't request a password reset, please ignore this email.</p>
|
|
74
|
+
`,
|
|
75
|
+
sampleData: {
|
|
76
|
+
resetUrl: 'https://example.com/reset-password?token=abc123',
|
|
77
|
+
expiryHours: 24
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
// System notification
|
|
81
|
+
this.registerTemplate({
|
|
82
|
+
id: 'system-notification',
|
|
83
|
+
name: 'System Notification',
|
|
84
|
+
description: 'General system notification template',
|
|
85
|
+
from: this.defaultConfig.from,
|
|
86
|
+
subject: '{{subject}}',
|
|
87
|
+
category: TemplateCategory.SYSTEM,
|
|
88
|
+
bodyHtml: `
|
|
89
|
+
<h2>{{title}}</h2>
|
|
90
|
+
<div>{{message}}</div>
|
|
91
|
+
`,
|
|
92
|
+
sampleData: {
|
|
93
|
+
subject: 'Important System Notification',
|
|
94
|
+
title: 'System Maintenance',
|
|
95
|
+
message: 'The system will be undergoing maintenance on Saturday from 2-4am UTC.'
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Register a new email template
|
|
101
|
+
* @param template The email template to register
|
|
102
|
+
*/
|
|
103
|
+
registerTemplate(template) {
|
|
104
|
+
if (this.templates.has(template.id)) {
|
|
105
|
+
logger.log('warn', `Template with ID '${template.id}' already exists and will be overwritten`);
|
|
106
|
+
}
|
|
107
|
+
// Add footer to templates if configured
|
|
108
|
+
if (this.defaultConfig.footerHtml && template.bodyHtml) {
|
|
109
|
+
template.bodyHtml += this.defaultConfig.footerHtml;
|
|
110
|
+
}
|
|
111
|
+
if (this.defaultConfig.footerText && template.bodyText) {
|
|
112
|
+
template.bodyText += this.defaultConfig.footerText;
|
|
113
|
+
}
|
|
114
|
+
this.templates.set(template.id, template);
|
|
115
|
+
logger.log('info', `Registered email template: ${template.id}`);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get an email template by ID
|
|
119
|
+
* @param templateId The template ID
|
|
120
|
+
* @returns The template or undefined if not found
|
|
121
|
+
*/
|
|
122
|
+
getTemplate(templateId) {
|
|
123
|
+
return this.templates.get(templateId);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* List all available templates
|
|
127
|
+
* @param category Optional category filter
|
|
128
|
+
* @returns Array of email templates
|
|
129
|
+
*/
|
|
130
|
+
listTemplates(category) {
|
|
131
|
+
const templates = Array.from(this.templates.values());
|
|
132
|
+
if (category) {
|
|
133
|
+
return templates.filter(template => template.category === category);
|
|
134
|
+
}
|
|
135
|
+
return templates;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Create an Email instance from a template
|
|
139
|
+
* @param templateId The template ID
|
|
140
|
+
* @param context The template context data
|
|
141
|
+
* @returns A configured Email instance
|
|
142
|
+
*/
|
|
143
|
+
async createEmail(templateId, context) {
|
|
144
|
+
const template = this.getTemplate(templateId);
|
|
145
|
+
if (!template) {
|
|
146
|
+
throw new Error(`Template with ID '${templateId}' not found`);
|
|
147
|
+
}
|
|
148
|
+
// Build attachments array for Email
|
|
149
|
+
const attachments = [];
|
|
150
|
+
if (template.attachments && template.attachments.length > 0) {
|
|
151
|
+
for (const attachment of template.attachments) {
|
|
152
|
+
try {
|
|
153
|
+
const attachmentPath = plugins.path.isAbsolute(attachment.path)
|
|
154
|
+
? attachment.path
|
|
155
|
+
: plugins.path.join(paths.MtaAttachmentsDir, attachment.path);
|
|
156
|
+
// Read the file
|
|
157
|
+
const fileBuffer = await plugins.fs.promises.readFile(attachmentPath);
|
|
158
|
+
attachments.push({
|
|
159
|
+
filename: attachment.name,
|
|
160
|
+
content: fileBuffer,
|
|
161
|
+
contentType: attachment.contentType || 'application/octet-stream'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
logger.log('error', `Failed to add attachment '${attachment.name}': ${error.message}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Create Email instance with template content
|
|
170
|
+
const emailOptions = {
|
|
171
|
+
from: template.from || this.defaultConfig.from,
|
|
172
|
+
subject: template.subject,
|
|
173
|
+
text: template.bodyText || '',
|
|
174
|
+
html: template.bodyHtml,
|
|
175
|
+
// Note: 'to' is intentionally omitted for templates
|
|
176
|
+
attachments,
|
|
177
|
+
variables: context || {}
|
|
178
|
+
};
|
|
179
|
+
return new Email(emailOptions);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Create and completely process an Email instance from a template
|
|
183
|
+
* @param templateId The template ID
|
|
184
|
+
* @param context The template context data
|
|
185
|
+
* @returns A complete, processed Email instance ready to send
|
|
186
|
+
*/
|
|
187
|
+
async prepareEmail(templateId, context = {}) {
|
|
188
|
+
const email = await this.createEmail(templateId, context);
|
|
189
|
+
// Email class processes variables when needed, no pre-compilation required
|
|
190
|
+
return email;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Create a MIME-formatted email from a template
|
|
194
|
+
* @param templateId The template ID
|
|
195
|
+
* @param context The template context data
|
|
196
|
+
* @returns A MIME-formatted email string
|
|
197
|
+
*/
|
|
198
|
+
async createMimeEmail(templateId, context = {}) {
|
|
199
|
+
const email = await this.prepareEmail(templateId, context);
|
|
200
|
+
return email.toRFC822String(context);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Load templates from a directory
|
|
204
|
+
* @param directory The directory containing template JSON files
|
|
205
|
+
*/
|
|
206
|
+
async loadTemplatesFromDirectory(directory) {
|
|
207
|
+
try {
|
|
208
|
+
// Ensure directory exists
|
|
209
|
+
if (!plugins.fs.existsSync(directory)) {
|
|
210
|
+
logger.log('error', `Template directory does not exist: ${directory}`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// Get all JSON files
|
|
214
|
+
const files = plugins.fs.readdirSync(directory)
|
|
215
|
+
.filter(file => file.endsWith('.json'));
|
|
216
|
+
for (const file of files) {
|
|
217
|
+
try {
|
|
218
|
+
const filePath = plugins.path.join(directory, file);
|
|
219
|
+
const content = plugins.fs.readFileSync(filePath, 'utf8');
|
|
220
|
+
const template = JSON.parse(content);
|
|
221
|
+
// Validate template
|
|
222
|
+
if (!template.id || !template.subject || (!template.bodyHtml && !template.bodyText)) {
|
|
223
|
+
logger.log('warn', `Invalid template in ${file}: missing required fields`);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
this.registerTemplate(template);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
logger.log('error', `Error loading template from ${file}: ${error.message}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
logger.log('info', `Loaded ${this.templates.size} email templates`);
|
|
233
|
+
}
|
|
234
|
+
catch (error) {
|
|
235
|
+
logger.log('error', `Failed to load templates from directory: ${error.message}`);
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy50ZW1wbGF0ZW1hbmFnZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2NvcmUvY2xhc3Nlcy50ZW1wbGF0ZW1hbmFnZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUM1QyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUN6QyxPQUFPLEVBQUUsS0FBSyxFQUF3QyxNQUFNLG9CQUFvQixDQUFDO0FBNkJqRjs7R0FFRztBQUNILE1BQU0sQ0FBTixJQUFZLGdCQUtYO0FBTEQsV0FBWSxnQkFBZ0I7SUFDMUIsaURBQTZCLENBQUE7SUFDN0IsbURBQStCLENBQUE7SUFDL0IsMkNBQXVCLENBQUE7SUFDdkIscUNBQWlCLENBQUE7QUFDbkIsQ0FBQyxFQUxXLGdCQUFnQixLQUFoQixnQkFBZ0IsUUFLM0I7QUFFRDs7R0FFRztBQUNILE1BQU0sT0FBTyxlQUFlO0lBQ2xCLFNBQVMsR0FBZ0MsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUNuRCxhQUFhLENBS25CO0lBRUYsWUFBWSxhQUtYO1FBQ0MsNEJBQTRCO1FBQzVCLElBQUksQ0FBQyxhQUFhLEdBQUc7WUFDbkIsSUFBSSxFQUFFLGFBQWEsRUFBRSxJQUFJLElBQUksMkJBQTJCO1lBQ3hELE9BQU8sRUFBRSxhQUFhLEVBQUUsT0FBTztZQUMvQixVQUFVLEVBQUUsYUFBYSxFQUFFLFVBQVUsSUFBSSxFQUFFO1lBQzNDLFVBQVUsRUFBRSxhQUFhLEVBQUUsVUFBVSxJQUFJLEVBQUU7U0FDNUMsQ0FBQztRQUVGLHFDQUFxQztRQUNyQyxJQUFJLENBQUMsd0JBQXdCLEVBQUUsQ0FBQztJQUNsQyxDQUFDO0lBRUQ7O09BRUc7SUFDSyx3QkFBd0I7UUFDOUIsZ0JBQWdCO1FBQ2hCLElBQUksQ0FBQyxnQkFB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
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Core email components
|
|
2
|
+
export * from './classes.email.js';
|
|
3
|
+
export * from './classes.emailvalidator.js';
|
|
4
|
+
export * from './classes.templatemanager.js';
|
|
5
|
+
export * from './classes.bouncemanager.js';
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9tYWlsL2NvcmUvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsd0JBQXdCO0FBQ3hCLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYyw2QkFBNkIsQ0FBQztBQUM1QyxjQUFjLDhCQUE4QixDQUFDO0FBQzdDLGNBQWMsNEJBQTRCLENBQUMifQ==
|