@push.rocks/smartmta 6.5.1 → 7.0.0

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.
Files changed (56) hide show
  1. package/changelog.md +20 -0
  2. package/dist_rust/mailer-bin_linux_amd64 +0 -0
  3. package/dist_rust/mailer-bin_linux_arm64 +0 -0
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/index.d.ts +3 -0
  6. package/dist_ts/index.js +4 -1
  7. package/dist_ts/mail/core/classes.bouncemanager.d.ts +5 -12
  8. package/dist_ts/mail/core/classes.bouncemanager.js +27 -92
  9. package/dist_ts/mail/core/classes.email.js +2 -2
  10. package/dist_ts/mail/core/classes.templatemanager.d.ts +10 -6
  11. package/dist_ts/mail/core/classes.templatemanager.js +35 -51
  12. package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +15 -20
  13. package/dist_ts/mail/delivery/classes.delivery.queue.js +196 -114
  14. package/dist_ts/mail/delivery/classes.delivery.system.js +5 -5
  15. package/dist_ts/mail/interfaces.storage.d.ts +37 -6
  16. package/dist_ts/mail/interfaces.storage.js +33 -3
  17. package/dist_ts/mail/routing/classes.dkim.manager.d.ts +2 -2
  18. package/dist_ts/mail/routing/classes.dkim.manager.js +2 -3
  19. package/dist_ts/mail/routing/classes.dns.manager.d.ts +2 -2
  20. package/dist_ts/mail/routing/classes.dns.manager.js +1 -1
  21. package/dist_ts/mail/routing/classes.email.router.d.ts +2 -2
  22. package/dist_ts/mail/routing/classes.email.router.js +4 -5
  23. package/dist_ts/mail/routing/classes.unified.email.server.d.ts +8 -6
  24. package/dist_ts/mail/routing/classes.unified.email.server.js +12 -41
  25. package/dist_ts/mail/routing/interfaces.d.ts +0 -2
  26. package/dist_ts/mail/security/classes.dkimcreator.d.ts +15 -45
  27. package/dist_ts/mail/security/classes.dkimcreator.js +66 -294
  28. package/dist_ts/paths.d.ts +0 -12
  29. package/dist_ts/paths.js +3 -36
  30. package/dist_ts/plugins.d.ts +2 -5
  31. package/dist_ts/plugins.js +3 -6
  32. package/dist_ts/security/classes.contentscanner.js +1 -2
  33. package/dist_ts/security/classes.ipreputationchecker.d.ts +6 -6
  34. package/dist_ts/security/classes.ipreputationchecker.js +24 -84
  35. package/dist_ts/security/classes.rustsecuritybridge.d.ts +3 -3
  36. package/package.json +2 -3
  37. package/readme.md +12 -6
  38. package/ts/00_commitinfo_data.ts +1 -1
  39. package/ts/index.ts +4 -0
  40. package/ts/mail/core/classes.bouncemanager.ts +31 -110
  41. package/ts/mail/core/classes.email.ts +1 -1
  42. package/ts/mail/core/classes.templatemanager.ts +42 -57
  43. package/ts/mail/delivery/classes.delivery.queue.ts +264 -135
  44. package/ts/mail/delivery/classes.delivery.system.ts +7 -5
  45. package/ts/mail/interfaces.storage.ts +64 -10
  46. package/ts/mail/routing/classes.dkim.manager.ts +3 -3
  47. package/ts/mail/routing/classes.dns.manager.ts +3 -3
  48. package/ts/mail/routing/classes.email.router.ts +6 -6
  49. package/ts/mail/routing/classes.unified.email.server.ts +23 -44
  50. package/ts/mail/routing/interfaces.ts +0 -2
  51. package/ts/mail/security/classes.dkimcreator.ts +104 -352
  52. package/ts/paths.ts +2 -41
  53. package/ts/plugins.ts +1 -6
  54. package/ts/security/classes.contentscanner.ts +0 -1
  55. package/ts/security/classes.ipreputationchecker.ts +26 -90
  56. package/ts/security/classes.rustsecuritybridge.ts +3 -3
@@ -1,7 +1,5 @@
1
- import * as plugins from '../plugins.js';
2
- import * as paths from '../paths.js';
3
1
  import { logger } from '../logger.js';
4
- import { hasStorageManagerMethods, type IStorageManagerLike } from '../mail/interfaces.storage.js';
2
+ import type { IStorageManager } from '../mail/interfaces.storage.js';
5
3
  import { SecurityLogger, SecurityLogLevel, SecurityEventType } from './classes.securitylogger.js';
6
4
  import { RustSecurityBridge } from './classes.rustsecuritybridge.js';
7
5
  import { LRUCache } from 'lru-cache';
@@ -54,20 +52,20 @@ export interface IIPReputationOptions {
54
52
  highRiskThreshold?: number; // Score below this is high risk
55
53
  mediumRiskThreshold?: number; // Score below this is medium risk
56
54
  lowRiskThreshold?: number; // Score below this is low risk
57
- enableLocalCache?: boolean; // Whether to persist cache to disk (default: true)
55
+ persistCache?: boolean; // Whether to persist cache through IStorageManager (default: true)
58
56
  enableDNSBL?: boolean; // Whether to use DNSBL checks (default: true)
59
57
  enableIPInfo?: boolean; // Whether to use IP info service (default: true)
60
58
  }
61
59
 
62
60
  /**
63
61
  * IP reputation checker — delegates DNSBL lookups to the Rust security bridge.
64
- * Retains LRU caching and disk persistence in TypeScript.
62
+ * Retains an in-memory LRU cache and optionally persists it through managed storage.
65
63
  */
66
64
  export class IPReputationChecker {
67
65
  private static instance: IPReputationChecker;
68
66
  private reputationCache: LRUCache<string, IReputationResult>;
69
67
  private options: Required<IIPReputationOptions>;
70
- private storageManager?: IStorageManagerLike;
68
+ private storageManager?: IStorageManager;
71
69
 
72
70
  private static readonly DEFAULT_OPTIONS: Required<IIPReputationOptions> = {
73
71
  maxCacheSize: 10000,
@@ -76,12 +74,12 @@ export class IPReputationChecker {
76
74
  highRiskThreshold: ReputationThreshold.HIGH_RISK,
77
75
  mediumRiskThreshold: ReputationThreshold.MEDIUM_RISK,
78
76
  lowRiskThreshold: ReputationThreshold.LOW_RISK,
79
- enableLocalCache: true,
77
+ persistCache: true,
80
78
  enableDNSBL: true,
81
79
  enableIPInfo: true
82
80
  };
83
81
 
84
- constructor(options: IIPReputationOptions = {}, storageManager?: IStorageManagerLike) {
82
+ constructor(options: IIPReputationOptions = {}, storageManager?: IStorageManager) {
85
83
  this.options = {
86
84
  ...IPReputationChecker.DEFAULT_OPTIONS,
87
85
  ...options
@@ -94,14 +92,14 @@ export class IPReputationChecker {
94
92
  ttl: this.options.cacheTTL,
95
93
  });
96
94
 
97
- if (this.options.enableLocalCache) {
95
+ if (this.options.persistCache) {
98
96
  this.loadCache().catch(error => {
99
97
  logger.log('error', `Failed to load IP reputation cache during initialization: ${error.message}`);
100
98
  });
101
99
  }
102
100
  }
103
101
 
104
- public static getInstance(options: IIPReputationOptions = {}, storageManager?: IStorageManagerLike): IPReputationChecker {
102
+ public static getInstance(options: IIPReputationOptions = {}, storageManager?: IStorageManager): IPReputationChecker {
105
103
  if (!IPReputationChecker.instance) {
106
104
  IPReputationChecker.instance = new IPReputationChecker(options, storageManager);
107
105
  }
@@ -146,7 +144,7 @@ export class IPReputationChecker {
146
144
 
147
145
  this.reputationCache.set(ip, result);
148
146
 
149
- if (this.options.enableLocalCache) {
147
+ if (this.options.persistCache) {
150
148
  this.saveCache().catch(error => {
151
149
  logger.log('error', `Failed to save IP reputation cache: ${error.message}`);
152
150
  });
@@ -208,87 +206,25 @@ export class IPReputationChecker {
208
206
  }
209
207
 
210
208
  private async saveCache(): Promise<void> {
211
- try {
212
- const entries = Array.from(this.reputationCache.entries()).map(([ip, data]) => ({
213
- ip,
214
- data
215
- }));
216
-
217
- if (entries.length === 0) {
218
- return;
219
- }
220
-
221
- const cacheData = JSON.stringify(entries);
222
-
223
- if (hasStorageManagerMethods(this.storageManager, ['set'])) {
224
- await this.storageManager.set('/security/ip-reputation-cache.json', cacheData);
225
- logger.log('info', `Saved ${entries.length} IP reputation cache entries to StorageManager`);
226
- } else {
227
- const cacheDir = plugins.path.join(paths.dataDir, 'security');
228
- await plugins.smartfs.directory(cacheDir).recursive().create();
229
- const cacheFile = plugins.path.join(cacheDir, 'ip_reputation_cache.json');
230
- await plugins.smartfs.file(cacheFile).write(cacheData);
231
- logger.log('info', `Saved ${entries.length} IP reputation cache entries to disk`);
232
- }
233
- } catch (error) {
234
- logger.log('error', `Failed to save IP reputation cache: ${error.message}`);
235
- }
209
+ if (!this.storageManager) return;
210
+ const entries = Array.from(this.reputationCache.entries()).map(([ip, data]) => ({ ip, data }));
211
+ if (entries.length === 0) return;
212
+ await this.storageManager.set('/security/ip-reputation-cache.json', JSON.stringify(entries));
213
+ logger.log('info', `Saved ${entries.length} IP reputation cache entries`);
236
214
  }
237
215
 
238
216
  private async loadCache(): Promise<void> {
239
- try {
240
- let cacheData: string | null = null;
241
- let fromFilesystem = false;
242
-
243
- if (hasStorageManagerMethods(this.storageManager, ['get', 'set'])) {
244
- try {
245
- cacheData = await this.storageManager.get('/security/ip-reputation-cache.json');
246
-
247
- if (!cacheData) {
248
- const cacheFile = plugins.path.join(paths.dataDir, 'security', 'ip_reputation_cache.json');
249
- if (plugins.fs.existsSync(cacheFile)) {
250
- logger.log('info', 'Migrating IP reputation cache from filesystem to StorageManager');
251
- cacheData = plugins.fs.readFileSync(cacheFile, 'utf8');
252
- fromFilesystem = true;
253
- await this.storageManager.set('/security/ip-reputation-cache.json', cacheData);
254
- logger.log('info', 'IP reputation cache migrated to StorageManager successfully');
255
- try {
256
- plugins.fs.unlinkSync(cacheFile);
257
- logger.log('info', 'Old cache file removed after migration');
258
- } catch (deleteError) {
259
- logger.log('warn', `Could not delete old cache file: ${deleteError.message}`);
260
- }
261
- }
262
- }
263
- } catch (error) {
264
- logger.log('error', `Error loading from StorageManager: ${error.message}`);
265
- }
266
- } else {
267
- const cacheFile = plugins.path.join(paths.dataDir, 'security', 'ip_reputation_cache.json');
268
- if (plugins.fs.existsSync(cacheFile)) {
269
- cacheData = plugins.fs.readFileSync(cacheFile, 'utf8');
270
- fromFilesystem = true;
271
- }
272
- }
273
-
274
- if (cacheData) {
275
- const entries = JSON.parse(cacheData);
276
- const now = Date.now();
277
- const validEntries = entries.filter(entry => {
278
- const age = now - entry.data.timestamp;
279
- return age < this.options.cacheTTL;
280
- });
281
-
282
- for (const entry of validEntries) {
283
- this.reputationCache.set(entry.ip, entry.data);
284
- }
285
-
286
- const source = fromFilesystem ? 'disk' : 'StorageManager';
287
- logger.log('info', `Loaded ${validEntries.length} IP reputation cache entries from ${source}`);
288
- }
289
- } catch (error) {
290
- logger.log('error', `Failed to load IP reputation cache: ${error.message}`);
217
+ if (!this.storageManager) return;
218
+ const cacheData = await this.storageManager.get('/security/ip-reputation-cache.json');
219
+ if (!cacheData) return;
220
+
221
+ const entries = JSON.parse(cacheData);
222
+ const now = Date.now();
223
+ const validEntries = entries.filter((entry) => now - entry.data.timestamp < this.options.cacheTTL);
224
+ for (const entry of validEntries) {
225
+ this.reputationCache.set(entry.ip, entry.data);
291
226
  }
227
+ logger.log('info', `Loaded ${validEntries.length} IP reputation cache entries`);
292
228
  }
293
229
 
294
230
  public static getRiskLevel(score: number): 'high' | 'medium' | 'low' | 'trusted' {
@@ -303,11 +239,11 @@ export class IPReputationChecker {
303
239
  }
304
240
  }
305
241
 
306
- public updateStorageManager(storageManager: IStorageManagerLike): void {
242
+ public updateStorageManager(storageManager: IStorageManager): void {
307
243
  this.storageManager = storageManager;
308
244
  logger.log('info', 'IPReputationChecker storage manager updated');
309
245
 
310
- if (this.options.enableLocalCache && this.reputationCache.size > 0) {
246
+ if (this.options.persistCache && this.reputationCache.size > 0) {
311
247
  this.saveCache().catch(error => {
312
248
  logger.log('error', `Failed to save cache to new storage manager: ${error.message}`);
313
249
  });
@@ -159,6 +159,7 @@ interface ISmtpServerConfig {
159
159
  additionalTlsCerts?: Array<{ domains: string[]; certPem: string; keyPem: string }>;
160
160
  maxMessageSize?: number;
161
161
  maxConnections?: number;
162
+ maxConcurrentMessages?: number;
162
163
  maxRecipients?: number;
163
164
  connectionTimeoutSecs?: number;
164
165
  dataTimeoutSecs?: number;
@@ -183,9 +184,8 @@ interface IRateLimitConfig {
183
184
  }
184
185
 
185
186
  interface IEmailData {
186
- type: 'inline' | 'file';
187
- base64?: string;
188
- path?: string;
187
+ type: 'inline';
188
+ base64: string;
189
189
  }
190
190
 
191
191
  interface IEmailReceivedEvent {