@brightchain/brightchain-api-lib 0.25.0 → 0.27.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.
- package/package.json +4 -3
- package/src/lib/appConstants.d.ts +1 -1
- package/src/lib/appConstants.js +1 -1
- package/src/lib/application.d.ts +3 -14
- package/src/lib/application.d.ts.map +1 -1
- package/src/lib/application.js +91 -34
- package/src/lib/application.js.map +1 -1
- package/src/lib/constants.d.ts.map +1 -1
- package/src/lib/constants.js +1 -1
- package/src/lib/constants.js.map +1 -1
- package/src/lib/databaseInit.d.ts +7 -11
- package/src/lib/databaseInit.d.ts.map +1 -1
- package/src/lib/databaseInit.js +41 -107
- package/src/lib/databaseInit.js.map +1 -1
- package/src/lib/datastore/block-document-store-factory.d.ts +3 -0
- package/src/lib/datastore/block-document-store-factory.d.ts.map +1 -1
- package/src/lib/datastore/block-document-store-factory.js +15 -18
- package/src/lib/datastore/block-document-store-factory.js.map +1 -1
- package/src/lib/datastore/block-document-store.d.ts +2 -191
- package/src/lib/datastore/block-document-store.d.ts.map +1 -1
- package/src/lib/datastore/block-document-store.js +4 -628
- package/src/lib/datastore/block-document-store.js.map +1 -1
- package/src/lib/datastore/document-store.d.ts +1 -62
- package/src/lib/datastore/document-store.d.ts.map +1 -1
- package/src/lib/datastore/memory-document-store.d.ts +1 -8
- package/src/lib/datastore/memory-document-store.d.ts.map +1 -1
- package/src/lib/datastore/memory-document-store.js +3 -214
- package/src/lib/datastore/memory-document-store.js.map +1 -1
- package/src/lib/environment.d.ts +4 -47
- package/src/lib/environment.d.ts.map +1 -1
- package/src/lib/environment.js +2 -136
- package/src/lib/environment.js.map +1 -1
- package/src/lib/interfaces/environment.d.ts +3 -25
- package/src/lib/interfaces/environment.d.ts.map +1 -1
- package/src/lib/interfaces/responses/emailGatewayResponses.d.ts +30 -0
- package/src/lib/interfaces/responses/emailGatewayResponses.d.ts.map +1 -0
- package/src/lib/interfaces/responses/emailGatewayResponses.js +3 -0
- package/src/lib/interfaces/responses/emailGatewayResponses.js.map +1 -0
- package/src/lib/interfaces/responses/index.d.ts +1 -0
- package/src/lib/interfaces/responses/index.d.ts.map +1 -1
- package/src/lib/middleware/index.d.ts +1 -1
- package/src/lib/middleware/index.d.ts.map +1 -1
- package/src/lib/middleware/index.js +3 -2
- package/src/lib/middleware/index.js.map +1 -1
- package/src/lib/middleware/validateBody.d.ts +1 -12
- package/src/lib/middleware/validateBody.d.ts.map +1 -1
- package/src/lib/middleware/validateBody.js +4 -32
- package/src/lib/middleware/validateBody.js.map +1 -1
- package/src/lib/middlewares.d.ts.map +1 -1
- package/src/lib/middlewares.js +7 -1
- package/src/lib/middlewares.js.map +1 -1
- package/src/lib/plugins/brightchain-database-plugin.d.ts +27 -79
- package/src/lib/plugins/brightchain-database-plugin.d.ts.map +1 -1
- package/src/lib/plugins/brightchain-database-plugin.js +32 -103
- package/src/lib/plugins/brightchain-database-plugin.js.map +1 -1
- package/src/lib/routers/app.d.ts.map +1 -1
- package/src/lib/routers/app.js +1 -0
- package/src/lib/routers/app.js.map +1 -1
- package/src/lib/services/emailGateway/antiSpamFilter.d.ts +229 -0
- package/src/lib/services/emailGateway/antiSpamFilter.d.ts.map +1 -0
- package/src/lib/services/emailGateway/antiSpamFilter.js +325 -0
- package/src/lib/services/emailGateway/antiSpamFilter.js.map +1 -0
- package/src/lib/services/emailGateway/bounceProcessor.d.ts +182 -0
- package/src/lib/services/emailGateway/bounceProcessor.d.ts.map +1 -0
- package/src/lib/services/emailGateway/bounceProcessor.js +391 -0
- package/src/lib/services/emailGateway/bounceProcessor.js.map +1 -0
- package/src/lib/services/emailGateway/emailAuthVerifier.d.ts +99 -0
- package/src/lib/services/emailGateway/emailAuthVerifier.d.ts.map +1 -0
- package/src/lib/services/emailGateway/emailAuthVerifier.js +202 -0
- package/src/lib/services/emailGateway/emailAuthVerifier.js.map +1 -0
- package/src/lib/services/emailGateway/emailGatewayConfig.d.ts +74 -0
- package/src/lib/services/emailGateway/emailGatewayConfig.d.ts.map +1 -0
- package/src/lib/services/emailGateway/emailGatewayConfig.js +107 -0
- package/src/lib/services/emailGateway/emailGatewayConfig.js.map +1 -0
- package/src/lib/services/emailGateway/emailGatewayService.d.ts +209 -0
- package/src/lib/services/emailGateway/emailGatewayService.d.ts.map +1 -0
- package/src/lib/services/emailGateway/emailGatewayService.js +254 -0
- package/src/lib/services/emailGateway/emailGatewayService.js.map +1 -0
- package/src/lib/services/emailGateway/gatewayObservability.d.ts +123 -0
- package/src/lib/services/emailGateway/gatewayObservability.d.ts.map +1 -0
- package/src/lib/services/emailGateway/gatewayObservability.js +186 -0
- package/src/lib/services/emailGateway/gatewayObservability.js.map +1 -0
- package/src/lib/services/emailGateway/inboundProcessor.d.ts +113 -0
- package/src/lib/services/emailGateway/inboundProcessor.d.ts.map +1 -0
- package/src/lib/services/emailGateway/inboundProcessor.js +298 -0
- package/src/lib/services/emailGateway/inboundProcessor.js.map +1 -0
- package/src/lib/services/emailGateway/index.d.ts +23 -0
- package/src/lib/services/emailGateway/index.d.ts.map +1 -0
- package/src/lib/services/emailGateway/index.js +26 -0
- package/src/lib/services/emailGateway/index.js.map +1 -0
- package/src/lib/services/emailGateway/outboundDeliveryWorker.d.ts +122 -0
- package/src/lib/services/emailGateway/outboundDeliveryWorker.d.ts.map +1 -0
- package/src/lib/services/emailGateway/outboundDeliveryWorker.js +110 -0
- package/src/lib/services/emailGateway/outboundDeliveryWorker.js.map +1 -0
- package/src/lib/services/emailGateway/outboundQueue.d.ts +135 -0
- package/src/lib/services/emailGateway/outboundQueue.d.ts.map +1 -0
- package/src/lib/services/emailGateway/outboundQueue.js +227 -0
- package/src/lib/services/emailGateway/outboundQueue.js.map +1 -0
- package/src/lib/services/emailGateway/outboundQueueStore.d.ts +110 -0
- package/src/lib/services/emailGateway/outboundQueueStore.d.ts.map +1 -0
- package/src/lib/services/emailGateway/outboundQueueStore.js +131 -0
- package/src/lib/services/emailGateway/outboundQueueStore.js.map +1 -0
- package/src/lib/services/emailGateway/recipientLookupService.d.ts +146 -0
- package/src/lib/services/emailGateway/recipientLookupService.d.ts.map +1 -0
- package/src/lib/services/emailGateway/recipientLookupService.js +307 -0
- package/src/lib/services/emailGateway/recipientLookupService.js.map +1 -0
- package/src/lib/services/emailGateway/retryBackoff.d.ts +79 -0
- package/src/lib/services/emailGateway/retryBackoff.d.ts.map +1 -0
- package/src/lib/services/emailGateway/retryBackoff.js +77 -0
- package/src/lib/services/emailGateway/retryBackoff.js.map +1 -0
- package/src/lib/services/index.d.ts +1 -0
- package/src/lib/services/index.d.ts.map +1 -1
- package/src/lib/services/index.js +1 -0
- package/src/lib/services/index.js.map +1 -1
- package/src/lib/services/quorumDatabaseAdapter.d.ts +7 -1
- package/src/lib/services/quorumDatabaseAdapter.d.ts.map +1 -1
- package/src/lib/services/quorumDatabaseAdapter.js +83 -0
- package/src/lib/services/quorumDatabaseAdapter.js.map +1 -1
- package/src/lib/services/sessionAdapter.d.ts +2 -61
- package/src/lib/services/sessionAdapter.d.ts.map +1 -1
- package/src/lib/services/sessionAdapter.js +2 -102
- package/src/lib/services/sessionAdapter.js.map +1 -1
- package/src/lib/shared-types.d.ts +7 -15
- package/src/lib/shared-types.d.ts.map +1 -1
- package/src/lib/types/backend-id.d.ts +1 -2
- package/src/lib/types/backend-id.d.ts.map +1 -1
- package/src/lib/validation/userValidation.d.ts +2 -43
- package/src/lib/validation/userValidation.d.ts.map +1 -1
- package/src/lib/validation/userValidation.js +6 -144
- package/src/lib/validation/userValidation.js.map +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Email Gateway module barrel export.
|
|
4
|
+
*
|
|
5
|
+
* Re-exports all public gateway components so consumers can import from
|
|
6
|
+
* `@brightchain/brightchain-api-lib` (via the services barrel) with a
|
|
7
|
+
* single import path.
|
|
8
|
+
*
|
|
9
|
+
* @see Requirements 11.2, 11.3
|
|
10
|
+
* @module emailGateway
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
const tslib_1 = require("tslib");
|
|
14
|
+
tslib_1.__exportStar(require("./antiSpamFilter"), exports);
|
|
15
|
+
tslib_1.__exportStar(require("./bounceProcessor"), exports);
|
|
16
|
+
tslib_1.__exportStar(require("./emailAuthVerifier"), exports);
|
|
17
|
+
tslib_1.__exportStar(require("./emailGatewayConfig"), exports);
|
|
18
|
+
tslib_1.__exportStar(require("./emailGatewayService"), exports);
|
|
19
|
+
tslib_1.__exportStar(require("./gatewayObservability"), exports);
|
|
20
|
+
tslib_1.__exportStar(require("./inboundProcessor"), exports);
|
|
21
|
+
tslib_1.__exportStar(require("./outboundDeliveryWorker"), exports);
|
|
22
|
+
tslib_1.__exportStar(require("./outboundQueue"), exports);
|
|
23
|
+
tslib_1.__exportStar(require("./outboundQueueStore"), exports);
|
|
24
|
+
tslib_1.__exportStar(require("./recipientLookupService"), exports);
|
|
25
|
+
tslib_1.__exportStar(require("./retryBackoff"), exports);
|
|
26
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../brightchain-api-lib/src/lib/services/emailGateway/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,2DAAiC;AACjC,4DAAkC;AAClC,8DAAoC;AACpC,+DAAqC;AACrC,gEAAsC;AACtC,iEAAuC;AACvC,6DAAmC;AACnC,mEAAyC;AACzC,0DAAgC;AAChC,+DAAqC;AACrC,mEAAyC;AACzC,yDAA+B"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OutboundDeliveryWorker — dequeues messages from the OutboundQueue,
|
|
3
|
+
* serializes them to RFC 5322 format, applies DKIM signing, and
|
|
4
|
+
* delegates delivery to Postfix via sendmail or LMTP.
|
|
5
|
+
*
|
|
6
|
+
* The worker registers itself as the OutboundQueue's handler so that
|
|
7
|
+
* each dequeued item flows through the delivery pipeline:
|
|
8
|
+
* 1. Enforce max message size
|
|
9
|
+
* 2. Serialize IEmailMetadata → RFC 5322 via EmailSerializer
|
|
10
|
+
* 3. Apply DKIM signature
|
|
11
|
+
* 4. Send via Postfix transport
|
|
12
|
+
* 5. Update queue status on success/failure
|
|
13
|
+
*
|
|
14
|
+
* Transport and DKIM signing are injected as interfaces so the worker
|
|
15
|
+
* can be tested without real Postfix or key material.
|
|
16
|
+
*
|
|
17
|
+
* @see Requirements 2.1, 2.2, 2.3, 2.4, 2.5, 6.1
|
|
18
|
+
* @module outboundDeliveryWorker
|
|
19
|
+
*/
|
|
20
|
+
import type { IEmailGatewayConfig } from './emailGatewayConfig';
|
|
21
|
+
import type { IDomainAwareComponent } from './emailGatewayService';
|
|
22
|
+
import type { IOutboundQueueItem } from './emailGatewayService';
|
|
23
|
+
import type { OutboundQueue } from './outboundQueue';
|
|
24
|
+
import type { IOutboundQueueStore } from './outboundQueueStore';
|
|
25
|
+
/**
|
|
26
|
+
* Result returned by the Postfix transport after attempting delivery.
|
|
27
|
+
*/
|
|
28
|
+
export interface IPostfixTransportResult {
|
|
29
|
+
/** Whether the delivery succeeded. */
|
|
30
|
+
success: boolean;
|
|
31
|
+
/** SMTP response code (e.g. 250 for success, 4xx for temp failure, 5xx for permanent). */
|
|
32
|
+
responseCode: number;
|
|
33
|
+
/** Human-readable response message from the MTA. */
|
|
34
|
+
responseMessage: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Interface for delegating email delivery to Postfix via sendmail or LMTP.
|
|
38
|
+
*
|
|
39
|
+
* Implementations wrap the actual transport mechanism (child_process sendmail,
|
|
40
|
+
* LMTP socket, etc.) behind a simple async interface.
|
|
41
|
+
*
|
|
42
|
+
* @see Requirement 2.4
|
|
43
|
+
*/
|
|
44
|
+
export interface IPostfixTransport {
|
|
45
|
+
/**
|
|
46
|
+
* Send a raw RFC 5322 message to the specified recipients.
|
|
47
|
+
*
|
|
48
|
+
* @param from - Envelope sender address
|
|
49
|
+
* @param to - Envelope recipient addresses
|
|
50
|
+
* @param rawMessage - Complete RFC 5322 message bytes (with DKIM signature)
|
|
51
|
+
* @returns Delivery result with SMTP response code
|
|
52
|
+
*/
|
|
53
|
+
send(from: string, to: string[], rawMessage: Uint8Array): Promise<IPostfixTransportResult>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Interface for applying DKIM signatures to outbound messages.
|
|
57
|
+
*
|
|
58
|
+
* Implementations read the private key from the configured path and
|
|
59
|
+
* produce a signed message with the DKIM-Signature header prepended.
|
|
60
|
+
*
|
|
61
|
+
* @see Requirement 6.1
|
|
62
|
+
*/
|
|
63
|
+
export interface IDkimSigner {
|
|
64
|
+
/**
|
|
65
|
+
* Sign a raw RFC 5322 message with DKIM.
|
|
66
|
+
*
|
|
67
|
+
* @param rawMessage - The unsigned RFC 5322 message bytes
|
|
68
|
+
* @param domain - The signing domain (canonical domain)
|
|
69
|
+
* @param selector - The DKIM selector for DNS lookup
|
|
70
|
+
* @returns The signed message bytes with DKIM-Signature header prepended
|
|
71
|
+
*/
|
|
72
|
+
sign(rawMessage: Uint8Array, domain: string, selector: string): Promise<Uint8Array>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Worker that processes outbound email delivery from the queue.
|
|
76
|
+
*
|
|
77
|
+
* Registers as the OutboundQueue's handler and processes each dequeued
|
|
78
|
+
* item through the serialization → DKIM → transport pipeline.
|
|
79
|
+
*
|
|
80
|
+
* @see Requirements 2.1, 2.2, 2.3, 2.4, 2.5, 6.1
|
|
81
|
+
*/
|
|
82
|
+
export declare class OutboundDeliveryWorker implements IDomainAwareComponent {
|
|
83
|
+
private readonly queue;
|
|
84
|
+
private readonly store;
|
|
85
|
+
private readonly config;
|
|
86
|
+
private readonly transport;
|
|
87
|
+
private readonly dkimSigner;
|
|
88
|
+
private readonly serializer;
|
|
89
|
+
/** Current DKIM signing domain — updated via `updateCanonicalDomain()`. */
|
|
90
|
+
private signingDomain;
|
|
91
|
+
/**
|
|
92
|
+
* @param queue - The outbound queue to register as handler on
|
|
93
|
+
* @param store - Queue store for updating item status on success/failure
|
|
94
|
+
* @param config - Gateway configuration (max message size, DKIM settings)
|
|
95
|
+
* @param transport - Postfix transport for actual SMTP delivery
|
|
96
|
+
* @param dkimSigner - DKIM signer for message authentication
|
|
97
|
+
*/
|
|
98
|
+
constructor(queue: OutboundQueue, store: IOutboundQueueStore, config: IEmailGatewayConfig, transport: IPostfixTransport, dkimSigner: IDkimSigner);
|
|
99
|
+
/**
|
|
100
|
+
* Update the DKIM signing domain for outbound messages.
|
|
101
|
+
*
|
|
102
|
+
* @param newDomain - The new canonical domain for DKIM signing
|
|
103
|
+
*
|
|
104
|
+
* @see Requirement 8.5 — hot-reload canonical domain without restart
|
|
105
|
+
*/
|
|
106
|
+
updateCanonicalDomain(newDomain: string): void;
|
|
107
|
+
/**
|
|
108
|
+
* Handle a single dequeued outbound queue item.
|
|
109
|
+
*
|
|
110
|
+
* Pipeline:
|
|
111
|
+
* 1. Serialize IEmailMetadata → RFC 5322 via EmailSerializer
|
|
112
|
+
* 2. Enforce max message size (Req 2.5)
|
|
113
|
+
* 3. Apply DKIM signature (Req 6.1)
|
|
114
|
+
* 4. Delegate to Postfix transport (Req 2.4)
|
|
115
|
+
* 5. Mark completed or failed in the store
|
|
116
|
+
*
|
|
117
|
+
* @param item - The dequeued outbound queue item
|
|
118
|
+
* @throws Re-throws transport errors so the OutboundQueue can requeue
|
|
119
|
+
*/
|
|
120
|
+
handleItem(item: IOutboundQueueItem): Promise<void>;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=outboundDeliveryWorker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outboundDeliveryWorker.d.ts","sourceRoot":"","sources":["../../../../../../brightchain-api-lib/src/lib/services/emailGateway/outboundDeliveryWorker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAIhE;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,sCAAsC;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,0FAA0F;IAC1F,YAAY,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;;;OAOG;IACH,IAAI,CACF,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,EAAE,EACZ,UAAU,EAAE,UAAU,GACrB,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;OAOG;IACH,IAAI,CACF,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,CAAC,CAAC;CACxB;AAID;;;;;;;GAOG;AACH,qBAAa,sBAAuB,YAAW,qBAAqB;IAchE,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAjB7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkB;IAE7C,2EAA2E;IAC3E,OAAO,CAAC,aAAa,CAAS;IAE9B;;;;;;OAMG;gBAEgB,KAAK,EAAE,aAAa,EACpB,KAAK,EAAE,mBAAmB,EAC1B,MAAM,EAAE,mBAAmB,EAC3B,SAAS,EAAE,iBAAiB,EAC5B,UAAU,EAAE,WAAW;IAQ1C;;;;;;OAMG;IACH,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9C;;;;;;;;;;;;OAYG;IACG,UAAU,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;CAuC1D"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OutboundDeliveryWorker — dequeues messages from the OutboundQueue,
|
|
4
|
+
* serializes them to RFC 5322 format, applies DKIM signing, and
|
|
5
|
+
* delegates delivery to Postfix via sendmail or LMTP.
|
|
6
|
+
*
|
|
7
|
+
* The worker registers itself as the OutboundQueue's handler so that
|
|
8
|
+
* each dequeued item flows through the delivery pipeline:
|
|
9
|
+
* 1. Enforce max message size
|
|
10
|
+
* 2. Serialize IEmailMetadata → RFC 5322 via EmailSerializer
|
|
11
|
+
* 3. Apply DKIM signature
|
|
12
|
+
* 4. Send via Postfix transport
|
|
13
|
+
* 5. Update queue status on success/failure
|
|
14
|
+
*
|
|
15
|
+
* Transport and DKIM signing are injected as interfaces so the worker
|
|
16
|
+
* can be tested without real Postfix or key material.
|
|
17
|
+
*
|
|
18
|
+
* @see Requirements 2.1, 2.2, 2.3, 2.4, 2.5, 6.1
|
|
19
|
+
* @module outboundDeliveryWorker
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.OutboundDeliveryWorker = void 0;
|
|
23
|
+
const brightchain_lib_1 = require("@brightchain/brightchain-lib");
|
|
24
|
+
// ─── OutboundDeliveryWorker ─────────────────────────────────────────────────
|
|
25
|
+
/**
|
|
26
|
+
* Worker that processes outbound email delivery from the queue.
|
|
27
|
+
*
|
|
28
|
+
* Registers as the OutboundQueue's handler and processes each dequeued
|
|
29
|
+
* item through the serialization → DKIM → transport pipeline.
|
|
30
|
+
*
|
|
31
|
+
* @see Requirements 2.1, 2.2, 2.3, 2.4, 2.5, 6.1
|
|
32
|
+
*/
|
|
33
|
+
class OutboundDeliveryWorker {
|
|
34
|
+
queue;
|
|
35
|
+
store;
|
|
36
|
+
config;
|
|
37
|
+
transport;
|
|
38
|
+
dkimSigner;
|
|
39
|
+
serializer;
|
|
40
|
+
/** Current DKIM signing domain — updated via `updateCanonicalDomain()`. */
|
|
41
|
+
signingDomain;
|
|
42
|
+
/**
|
|
43
|
+
* @param queue - The outbound queue to register as handler on
|
|
44
|
+
* @param store - Queue store for updating item status on success/failure
|
|
45
|
+
* @param config - Gateway configuration (max message size, DKIM settings)
|
|
46
|
+
* @param transport - Postfix transport for actual SMTP delivery
|
|
47
|
+
* @param dkimSigner - DKIM signer for message authentication
|
|
48
|
+
*/
|
|
49
|
+
constructor(queue, store, config, transport, dkimSigner) {
|
|
50
|
+
this.queue = queue;
|
|
51
|
+
this.store = store;
|
|
52
|
+
this.config = config;
|
|
53
|
+
this.transport = transport;
|
|
54
|
+
this.dkimSigner = dkimSigner;
|
|
55
|
+
this.serializer = new brightchain_lib_1.EmailSerializer();
|
|
56
|
+
this.signingDomain = config.canonicalDomain;
|
|
57
|
+
// Register this worker as the queue's handler.
|
|
58
|
+
this.queue.setHandler(this.handleItem.bind(this));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Update the DKIM signing domain for outbound messages.
|
|
62
|
+
*
|
|
63
|
+
* @param newDomain - The new canonical domain for DKIM signing
|
|
64
|
+
*
|
|
65
|
+
* @see Requirement 8.5 — hot-reload canonical domain without restart
|
|
66
|
+
*/
|
|
67
|
+
updateCanonicalDomain(newDomain) {
|
|
68
|
+
this.signingDomain = newDomain;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Handle a single dequeued outbound queue item.
|
|
72
|
+
*
|
|
73
|
+
* Pipeline:
|
|
74
|
+
* 1. Serialize IEmailMetadata → RFC 5322 via EmailSerializer
|
|
75
|
+
* 2. Enforce max message size (Req 2.5)
|
|
76
|
+
* 3. Apply DKIM signature (Req 6.1)
|
|
77
|
+
* 4. Delegate to Postfix transport (Req 2.4)
|
|
78
|
+
* 5. Mark completed or failed in the store
|
|
79
|
+
*
|
|
80
|
+
* @param item - The dequeued outbound queue item
|
|
81
|
+
* @throws Re-throws transport errors so the OutboundQueue can requeue
|
|
82
|
+
*/
|
|
83
|
+
async handleItem(item) {
|
|
84
|
+
// 1. Serialize metadata to RFC 5322
|
|
85
|
+
const rawMessage = this.serializer.serialize(item.metadata);
|
|
86
|
+
// 2. Enforce max message size (Req 2.5)
|
|
87
|
+
if (rawMessage.byteLength > this.config.maxMessageSizeBytes) {
|
|
88
|
+
await this.store.markFailed(item.messageId, `Message size ${rawMessage.byteLength} bytes exceeds maximum ${this.config.maxMessageSizeBytes} bytes`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// 3. Apply DKIM signature (Req 6.1)
|
|
92
|
+
const signedMessage = await this.dkimSigner.sign(rawMessage, this.signingDomain, this.config.dkimSelector);
|
|
93
|
+
// 4. Delegate to Postfix transport (Req 2.4)
|
|
94
|
+
const result = await this.transport.send(item.from, item.to, signedMessage);
|
|
95
|
+
// 5. Update status based on transport result
|
|
96
|
+
if (result.success) {
|
|
97
|
+
await this.store.markCompleted(item.messageId);
|
|
98
|
+
}
|
|
99
|
+
else if (result.responseCode >= 500) {
|
|
100
|
+
// Permanent failure (5xx) — mark failed, do not retry
|
|
101
|
+
await this.store.markFailed(item.messageId, `Permanent failure (${result.responseCode}): ${result.responseMessage}`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Temporary failure (4xx) — throw so OutboundQueue requeues with retry logic
|
|
105
|
+
throw new Error(`Temporary failure (${result.responseCode}): ${result.responseMessage}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.OutboundDeliveryWorker = OutboundDeliveryWorker;
|
|
110
|
+
//# sourceMappingURL=outboundDeliveryWorker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outboundDeliveryWorker.js","sourceRoot":"","sources":["../../../../../../brightchain-api-lib/src/lib/services/emailGateway/outboundDeliveryWorker.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;GAkBG;;;AAEH,kEAA+D;AAsE/D,+EAA+E;AAE/E;;;;;;;GAOG;AACH,MAAa,sBAAsB;IAcd;IACA;IACA;IACA;IACA;IAjBF,UAAU,CAAkB;IAE7C,2EAA2E;IACnE,aAAa,CAAS;IAE9B;;;;;;OAMG;IACH,YACmB,KAAoB,EACpB,KAA0B,EAC1B,MAA2B,EAC3B,SAA4B,EAC5B,UAAuB;QAJvB,UAAK,GAAL,KAAK,CAAe;QACpB,UAAK,GAAL,KAAK,CAAqB;QAC1B,WAAM,GAAN,MAAM,CAAqB;QAC3B,cAAS,GAAT,SAAS,CAAmB;QAC5B,eAAU,GAAV,UAAU,CAAa;QAExC,IAAI,CAAC,UAAU,GAAG,IAAI,iCAAe,EAAE,CAAC;QACxC,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,eAAe,CAAC;QAC5C,+CAA+C;QAC/C,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACpD,CAAC;IAED;;;;;;OAMG;IACH,qBAAqB,CAAC,SAAiB;QACrC,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;IACjC,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,KAAK,CAAC,UAAU,CAAC,IAAwB;QACvC,oCAAoC;QACpC,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAE5D,wCAAwC;QACxC,IAAI,UAAU,CAAC,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,mBAAmB,EAAE,CAAC;YAC5D,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CACzB,IAAI,CAAC,SAAS,EACd,gBAAgB,UAAU,CAAC,UAAU,0BAA0B,IAAI,CAAC,MAAM,CAAC,mBAAmB,QAAQ,CACvG,CAAC;YACF,OAAO;QACT,CAAC;QAED,oCAAoC;QACpC,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,CAC9C,UAAU,EACV,IAAI,CAAC,aAAa,EAClB,IAAI,CAAC,MAAM,CAAC,YAAY,CACzB,CAAC;QAEF,6CAA6C;QAC7C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE,aAAa,CAAC,CAAC;QAE5E,6CAA6C;QAC7C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACjD,CAAC;aAAM,IAAI,MAAM,CAAC,YAAY,IAAI,GAAG,EAAE,CAAC;YACtC,sDAAsD;YACtD,MAAM,IAAI,CAAC,KAAK,CAAC,UAAU,CACzB,IAAI,CAAC,SAAS,EACd,sBAAsB,MAAM,CAAC,YAAY,MAAM,MAAM,CAAC,eAAe,EAAE,CACxE,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,6EAA6E;YAC7E,MAAM,IAAI,KAAK,CACb,sBAAsB,MAAM,CAAC,YAAY,MAAM,MAAM,CAAC,eAAe,EAAE,CACxE,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAzFD,wDAyFC"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OutboundQueue — persistent FIFO queue with concurrency control for
|
|
3
|
+
* outbound email delivery.
|
|
4
|
+
*
|
|
5
|
+
* Wraps an `IOutboundQueueStore` for persistence and enforces a configurable
|
|
6
|
+
* concurrency limit on simultaneous deliveries. The processing loop dequeues
|
|
7
|
+
* items and passes them to a handler callback.
|
|
8
|
+
*
|
|
9
|
+
* On `start()`, the queue resumes processing any previously queued messages
|
|
10
|
+
* that have not exceeded the maximum retry duration (Req 9.2).
|
|
11
|
+
*
|
|
12
|
+
* @see Requirements 9.1, 9.2, 9.3, 9.4, 9.5
|
|
13
|
+
* @module outboundQueue
|
|
14
|
+
*/
|
|
15
|
+
import type { IEmailGatewayConfig } from './emailGatewayConfig';
|
|
16
|
+
import type { IOutboundQueue, IOutboundQueueItem } from './emailGatewayService';
|
|
17
|
+
import type { IOutboundQueueStore } from './outboundQueueStore';
|
|
18
|
+
/**
|
|
19
|
+
* Callback invoked for each dequeued item. The handler is responsible for
|
|
20
|
+
* performing the actual SMTP delivery (or delegating to a worker).
|
|
21
|
+
*
|
|
22
|
+
* @param item - The dequeued outbound queue item
|
|
23
|
+
* @returns Resolves when the handler has finished processing the item
|
|
24
|
+
*/
|
|
25
|
+
export type OutboundQueueHandler = (item: IOutboundQueueItem) => Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Persistent FIFO outbound email queue with concurrency control.
|
|
28
|
+
*
|
|
29
|
+
* Implements `IOutboundQueue` so the `EmailGatewayService` orchestrator
|
|
30
|
+
* can enqueue messages without knowing about concurrency or persistence
|
|
31
|
+
* internals.
|
|
32
|
+
*
|
|
33
|
+
* @see Requirements 9.1, 9.2, 9.3, 9.4, 9.5
|
|
34
|
+
*/
|
|
35
|
+
export declare class OutboundQueue implements IOutboundQueue {
|
|
36
|
+
private readonly store;
|
|
37
|
+
private readonly config;
|
|
38
|
+
private handler?;
|
|
39
|
+
/** Whether the processing loop is active. */
|
|
40
|
+
private running;
|
|
41
|
+
/** Number of items currently being processed by the handler. */
|
|
42
|
+
private activeCount;
|
|
43
|
+
/** Handle for the polling interval so we can clear it on stop(). */
|
|
44
|
+
private pollTimer;
|
|
45
|
+
/** Polling interval in milliseconds for the processing loop. */
|
|
46
|
+
private readonly pollIntervalMs;
|
|
47
|
+
/**
|
|
48
|
+
* @param store - Persistence backend for queue items
|
|
49
|
+
* @param config - Gateway configuration (concurrency, retry settings)
|
|
50
|
+
* @param handler - Callback invoked for each dequeued item
|
|
51
|
+
* @param pollIntervalMs - How often to poll the store for ready items (default: 1000ms)
|
|
52
|
+
*/
|
|
53
|
+
constructor(store: IOutboundQueueStore, config: IEmailGatewayConfig, handler?: OutboundQueueHandler | undefined, pollIntervalMs?: number);
|
|
54
|
+
/**
|
|
55
|
+
* Set or replace the delivery handler callback.
|
|
56
|
+
*/
|
|
57
|
+
setHandler(handler: OutboundQueueHandler): void;
|
|
58
|
+
/**
|
|
59
|
+
* Enqueue a message for outbound delivery.
|
|
60
|
+
*
|
|
61
|
+
* Persists the item to the store. Rejects with an error if the store
|
|
62
|
+
* is unavailable (Req 9.5).
|
|
63
|
+
*
|
|
64
|
+
* @param item - The outbound queue item to enqueue
|
|
65
|
+
* @throws Error if the store is unavailable
|
|
66
|
+
*
|
|
67
|
+
* @see Requirement 9.5
|
|
68
|
+
*/
|
|
69
|
+
enqueue(item: IOutboundQueueItem): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Start the processing loop.
|
|
72
|
+
*
|
|
73
|
+
* On startup, resumes processing any previously queued messages that
|
|
74
|
+
* have not exceeded the maximum retry duration (Req 9.2). Then begins
|
|
75
|
+
* polling the store for new items.
|
|
76
|
+
*
|
|
77
|
+
* @see Requirements 9.2, 9.4
|
|
78
|
+
*/
|
|
79
|
+
start(): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Stop the processing loop.
|
|
82
|
+
*
|
|
83
|
+
* Clears the polling timer. In-flight handler invocations are allowed
|
|
84
|
+
* to complete but no new items will be dequeued.
|
|
85
|
+
*/
|
|
86
|
+
stop(): void;
|
|
87
|
+
/**
|
|
88
|
+
* Whether the queue processing loop is currently running.
|
|
89
|
+
*/
|
|
90
|
+
isRunning(): boolean;
|
|
91
|
+
/**
|
|
92
|
+
* The number of items currently being processed (in-flight).
|
|
93
|
+
*/
|
|
94
|
+
getActiveCount(): number;
|
|
95
|
+
/**
|
|
96
|
+
* The current queue depth (delegated to the store).
|
|
97
|
+
*/
|
|
98
|
+
getQueueDepth(): Promise<number>;
|
|
99
|
+
/**
|
|
100
|
+
* Resume processing items that were queued before the last shutdown.
|
|
101
|
+
*
|
|
102
|
+
* Walks the store and expires any items that have exceeded the maximum
|
|
103
|
+
* retry duration, then triggers a processing pass for the rest.
|
|
104
|
+
*
|
|
105
|
+
* @see Requirement 9.2
|
|
106
|
+
*/
|
|
107
|
+
private resumeExistingItems;
|
|
108
|
+
/**
|
|
109
|
+
* Dequeue and process items up to the concurrency limit.
|
|
110
|
+
*
|
|
111
|
+
* Each dequeued item is handed to the handler callback. The concurrency
|
|
112
|
+
* limit (Req 9.4) is enforced by tracking `activeCount`.
|
|
113
|
+
*/
|
|
114
|
+
private processAvailable;
|
|
115
|
+
/**
|
|
116
|
+
* Process a single dequeued item by invoking the handler.
|
|
117
|
+
*
|
|
118
|
+
* If the handler throws, the item is checked against retry limits.
|
|
119
|
+
* If eligible for retry, it is requeued with an exponential back-off
|
|
120
|
+
* delay. If limits are exceeded, it is marked as permanently failed.
|
|
121
|
+
*
|
|
122
|
+
* @see Requirements 3.1, 3.3, 3.4
|
|
123
|
+
*/
|
|
124
|
+
private processItem;
|
|
125
|
+
/**
|
|
126
|
+
* Determine whether a queue item has exceeded the maximum retry duration.
|
|
127
|
+
*
|
|
128
|
+
* @param item - The queue item to check
|
|
129
|
+
* @returns `true` if the item's age exceeds `config.retryMaxDurationMs`
|
|
130
|
+
*
|
|
131
|
+
* @see Requirement 9.2
|
|
132
|
+
*/
|
|
133
|
+
private isExpired;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=outboundQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outboundQueue.d.ts","sourceRoot":"","sources":["../../../../../../brightchain-api-lib/src/lib/services/emailGateway/outboundQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAGhE;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,kBAAkB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAE/E;;;;;;;;GAQG;AACH,qBAAa,aAAc,YAAW,cAAc;IAoBhD,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,OAAO,CAAC;IArBlB,6CAA6C;IAC7C,OAAO,CAAC,OAAO,CAAS;IAExB,gEAAgE;IAChE,OAAO,CAAC,WAAW,CAAK;IAExB,oEAAoE;IACpE,OAAO,CAAC,SAAS,CAA+C;IAEhE,gEAAgE;IAChE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC;;;;;OAKG;gBAEgB,KAAK,EAAE,mBAAmB,EAC1B,MAAM,EAAE,mBAAmB,EACpC,OAAO,CAAC,EAAE,oBAAoB,YAAA,EACtC,cAAc,CAAC,EAAE,MAAM;IAOzB;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAI/C;;;;;;;;;;OAUG;IACG,OAAO,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAUtD;;;;;;;;OAQG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAe5B;;;;;OAKG;IACH,IAAI,IAAI,IAAI;IAWZ;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC;IAMtC;;;;;;;OAOG;YACW,mBAAmB;IAMjC;;;;;OAKG;YACW,gBAAgB;IA6B9B;;;;;;;;OAQG;YACW,WAAW;IAmCzB;;;;;;;OAOG;IACH,OAAO,CAAC,SAAS;CAIlB"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* OutboundQueue — persistent FIFO queue with concurrency control for
|
|
4
|
+
* outbound email delivery.
|
|
5
|
+
*
|
|
6
|
+
* Wraps an `IOutboundQueueStore` for persistence and enforces a configurable
|
|
7
|
+
* concurrency limit on simultaneous deliveries. The processing loop dequeues
|
|
8
|
+
* items and passes them to a handler callback.
|
|
9
|
+
*
|
|
10
|
+
* On `start()`, the queue resumes processing any previously queued messages
|
|
11
|
+
* that have not exceeded the maximum retry duration (Req 9.2).
|
|
12
|
+
*
|
|
13
|
+
* @see Requirements 9.1, 9.2, 9.3, 9.4, 9.5
|
|
14
|
+
* @module outboundQueue
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.OutboundQueue = void 0;
|
|
18
|
+
const brightchain_lib_1 = require("@brightchain/brightchain-lib");
|
|
19
|
+
const retryBackoff_1 = require("./retryBackoff");
|
|
20
|
+
/**
|
|
21
|
+
* Persistent FIFO outbound email queue with concurrency control.
|
|
22
|
+
*
|
|
23
|
+
* Implements `IOutboundQueue` so the `EmailGatewayService` orchestrator
|
|
24
|
+
* can enqueue messages without knowing about concurrency or persistence
|
|
25
|
+
* internals.
|
|
26
|
+
*
|
|
27
|
+
* @see Requirements 9.1, 9.2, 9.3, 9.4, 9.5
|
|
28
|
+
*/
|
|
29
|
+
class OutboundQueue {
|
|
30
|
+
store;
|
|
31
|
+
config;
|
|
32
|
+
handler;
|
|
33
|
+
/** Whether the processing loop is active. */
|
|
34
|
+
running = false;
|
|
35
|
+
/** Number of items currently being processed by the handler. */
|
|
36
|
+
activeCount = 0;
|
|
37
|
+
/** Handle for the polling interval so we can clear it on stop(). */
|
|
38
|
+
pollTimer = null;
|
|
39
|
+
/** Polling interval in milliseconds for the processing loop. */
|
|
40
|
+
pollIntervalMs;
|
|
41
|
+
/**
|
|
42
|
+
* @param store - Persistence backend for queue items
|
|
43
|
+
* @param config - Gateway configuration (concurrency, retry settings)
|
|
44
|
+
* @param handler - Callback invoked for each dequeued item
|
|
45
|
+
* @param pollIntervalMs - How often to poll the store for ready items (default: 1000ms)
|
|
46
|
+
*/
|
|
47
|
+
constructor(store, config, handler, pollIntervalMs) {
|
|
48
|
+
this.store = store;
|
|
49
|
+
this.config = config;
|
|
50
|
+
this.handler = handler;
|
|
51
|
+
this.pollIntervalMs = pollIntervalMs ?? 1000;
|
|
52
|
+
}
|
|
53
|
+
// ─── Public API ─────────────────────────────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* Set or replace the delivery handler callback.
|
|
56
|
+
*/
|
|
57
|
+
setHandler(handler) {
|
|
58
|
+
this.handler = handler;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Enqueue a message for outbound delivery.
|
|
62
|
+
*
|
|
63
|
+
* Persists the item to the store. Rejects with an error if the store
|
|
64
|
+
* is unavailable (Req 9.5).
|
|
65
|
+
*
|
|
66
|
+
* @param item - The outbound queue item to enqueue
|
|
67
|
+
* @throws Error if the store is unavailable
|
|
68
|
+
*
|
|
69
|
+
* @see Requirement 9.5
|
|
70
|
+
*/
|
|
71
|
+
async enqueue(item) {
|
|
72
|
+
try {
|
|
73
|
+
await this.store.enqueue(item);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw new Error(`Outbound queue store unavailable: ${err instanceof Error ? err.message : String(err)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Start the processing loop.
|
|
81
|
+
*
|
|
82
|
+
* On startup, resumes processing any previously queued messages that
|
|
83
|
+
* have not exceeded the maximum retry duration (Req 9.2). Then begins
|
|
84
|
+
* polling the store for new items.
|
|
85
|
+
*
|
|
86
|
+
* @see Requirements 9.2, 9.4
|
|
87
|
+
*/
|
|
88
|
+
async start() {
|
|
89
|
+
if (this.running) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
this.running = true;
|
|
93
|
+
// Resume: process any items already in the store that are still valid.
|
|
94
|
+
await this.resumeExistingItems();
|
|
95
|
+
// Start the polling loop.
|
|
96
|
+
this.pollTimer = setInterval(() => {
|
|
97
|
+
void this.processAvailable();
|
|
98
|
+
}, this.pollIntervalMs);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Stop the processing loop.
|
|
102
|
+
*
|
|
103
|
+
* Clears the polling timer. In-flight handler invocations are allowed
|
|
104
|
+
* to complete but no new items will be dequeued.
|
|
105
|
+
*/
|
|
106
|
+
stop() {
|
|
107
|
+
if (!this.running) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.running = false;
|
|
111
|
+
if (this.pollTimer !== null) {
|
|
112
|
+
clearInterval(this.pollTimer);
|
|
113
|
+
this.pollTimer = null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Whether the queue processing loop is currently running.
|
|
118
|
+
*/
|
|
119
|
+
isRunning() {
|
|
120
|
+
return this.running;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* The number of items currently being processed (in-flight).
|
|
124
|
+
*/
|
|
125
|
+
getActiveCount() {
|
|
126
|
+
return this.activeCount;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* The current queue depth (delegated to the store).
|
|
130
|
+
*/
|
|
131
|
+
async getQueueDepth() {
|
|
132
|
+
return this.store.getQueueDepth();
|
|
133
|
+
}
|
|
134
|
+
// ─── Internal Processing ────────────────────────────────────────────
|
|
135
|
+
/**
|
|
136
|
+
* Resume processing items that were queued before the last shutdown.
|
|
137
|
+
*
|
|
138
|
+
* Walks the store and expires any items that have exceeded the maximum
|
|
139
|
+
* retry duration, then triggers a processing pass for the rest.
|
|
140
|
+
*
|
|
141
|
+
* @see Requirement 9.2
|
|
142
|
+
*/
|
|
143
|
+
async resumeExistingItems() {
|
|
144
|
+
// We process available items which will naturally skip expired ones
|
|
145
|
+
// via the dequeue + expiry check in processItem.
|
|
146
|
+
await this.processAvailable();
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Dequeue and process items up to the concurrency limit.
|
|
150
|
+
*
|
|
151
|
+
* Each dequeued item is handed to the handler callback. The concurrency
|
|
152
|
+
* limit (Req 9.4) is enforced by tracking `activeCount`.
|
|
153
|
+
*/
|
|
154
|
+
async processAvailable() {
|
|
155
|
+
if (!this.running || !this.handler) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
while (this.activeCount < this.config.queueConcurrency && this.running) {
|
|
159
|
+
const item = await this.store.dequeue();
|
|
160
|
+
if (!item) {
|
|
161
|
+
// No more items ready for processing.
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
// Check if the item has exceeded the max retry duration (Req 9.2).
|
|
165
|
+
if (this.isExpired(item)) {
|
|
166
|
+
await this.store.markFailed(item.messageId, 'Maximum retry duration exceeded');
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
// Process the item with concurrency tracking.
|
|
170
|
+
this.activeCount++;
|
|
171
|
+
void this.processItem(item).finally(() => {
|
|
172
|
+
this.activeCount--;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Process a single dequeued item by invoking the handler.
|
|
178
|
+
*
|
|
179
|
+
* If the handler throws, the item is checked against retry limits.
|
|
180
|
+
* If eligible for retry, it is requeued with an exponential back-off
|
|
181
|
+
* delay. If limits are exceeded, it is marked as permanently failed.
|
|
182
|
+
*
|
|
183
|
+
* @see Requirements 3.1, 3.3, 3.4
|
|
184
|
+
*/
|
|
185
|
+
async processItem(item) {
|
|
186
|
+
if (!this.handler) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
try {
|
|
190
|
+
await this.handler(item);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Handler failed — check if the item is eligible for retry.
|
|
194
|
+
const nextRetryCount = item.retryCount + 1;
|
|
195
|
+
const retryCandidate = { retryCount: nextRetryCount, enqueuedAt: item.enqueuedAt };
|
|
196
|
+
if ((0, retryBackoff_1.shouldRetry)(retryCandidate, this.config)) {
|
|
197
|
+
// Eligible for retry — requeue with back-off delay.
|
|
198
|
+
const nextAttemptAt = (0, retryBackoff_1.computeNextAttemptAt)(this.config.retryBaseIntervalMs, item.retryCount);
|
|
199
|
+
const requeued = {
|
|
200
|
+
...item,
|
|
201
|
+
status: brightchain_lib_1.OutboundDeliveryStatus.Queued,
|
|
202
|
+
retryCount: nextRetryCount,
|
|
203
|
+
nextAttemptAt,
|
|
204
|
+
};
|
|
205
|
+
await this.store.requeue(requeued);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
// Retry limits exceeded — mark permanently failed (Req 3.4).
|
|
209
|
+
await this.store.markFailed(item.messageId, `Retry limits exceeded (retryCount=${nextRetryCount}, maxCount=${this.config.retryMaxCount}, maxDuration=${this.config.retryMaxDurationMs}ms)`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Determine whether a queue item has exceeded the maximum retry duration.
|
|
215
|
+
*
|
|
216
|
+
* @param item - The queue item to check
|
|
217
|
+
* @returns `true` if the item's age exceeds `config.retryMaxDurationMs`
|
|
218
|
+
*
|
|
219
|
+
* @see Requirement 9.2
|
|
220
|
+
*/
|
|
221
|
+
isExpired(item) {
|
|
222
|
+
const age = Date.now() - item.enqueuedAt.getTime();
|
|
223
|
+
return age > this.config.retryMaxDurationMs;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
exports.OutboundQueue = OutboundQueue;
|
|
227
|
+
//# sourceMappingURL=outboundQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"outboundQueue.js","sourceRoot":"","sources":["../../../../../../brightchain-api-lib/src/lib/services/emailGateway/outboundQueue.ts"],"names":[],"mappings":"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"}
|