@venizia/ignis-docs 0.2.0 → 0.2.1-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/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +24 -13
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +8 -4
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +247 -153
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +109 -323
- package/content/extensions/components/authentication/api.md +489 -602
- package/content/extensions/components/authentication/errors.md +135 -498
- package/content/extensions/components/authentication/index.md +89 -801
- package/content/extensions/components/authentication/usage.md +231 -955
- package/content/extensions/components/authorization/api.md +991 -644
- package/content/extensions/components/authorization/errors.md +204 -208
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +88 -795
- package/content/extensions/components/authorization/usage.md +219 -528
- package/content/extensions/components/health-check.md +77 -243
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +553 -285
- package/content/extensions/components/mail/errors.md +76 -62
- package/content/extensions/components/mail/index.md +111 -463
- package/content/extensions/components/mail/usage.md +139 -173
- package/content/extensions/components/request-tracker.md +70 -173
- package/content/extensions/components/socket-io/api.md +462 -785
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +69 -372
- package/content/extensions/components/socket-io/usage.md +212 -105
- package/content/extensions/components/static-asset/api.md +461 -141
- package/content/extensions/components/static-asset/errors.md +121 -53
- package/content/extensions/components/static-asset/index.md +84 -606
- package/content/extensions/components/static-asset/usage.md +184 -299
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +311 -404
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +75 -407
- package/content/extensions/components/websocket/usage.md +120 -338
- package/content/extensions/helpers/cron/index.md +52 -160
- package/content/extensions/helpers/crypto/index.md +67 -480
- package/content/extensions/helpers/crypto/reference.md +528 -0
- package/content/extensions/helpers/env/index.md +64 -178
- package/content/extensions/helpers/error/index.md +286 -196
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +76 -550
- package/content/extensions/helpers/inversion/reference.md +530 -0
- package/content/extensions/helpers/kafka/admin.md +23 -3
- package/content/extensions/helpers/kafka/compile-binary.md +83 -52
- package/content/extensions/helpers/kafka/consumer.md +65 -28
- package/content/extensions/helpers/kafka/examples.md +46 -253
- package/content/extensions/helpers/kafka/index.md +55 -617
- package/content/extensions/helpers/kafka/producer.md +156 -22
- package/content/extensions/helpers/kafka/schema-registry.md +59 -79
- package/content/extensions/helpers/logger/hf-logger.md +220 -0
- package/content/extensions/helpers/logger/index.md +78 -552
- package/content/extensions/helpers/logger/pino.md +105 -0
- package/content/extensions/helpers/logger/reference.md +937 -0
- package/content/extensions/helpers/network/api.md +276 -195
- package/content/extensions/helpers/network/index.md +87 -524
- package/content/extensions/helpers/queue/index.md +80 -897
- package/content/extensions/helpers/queue/reference.md +494 -0
- package/content/extensions/helpers/redis/index.md +86 -640
- package/content/extensions/helpers/redis/reference.md +784 -0
- package/content/extensions/helpers/secrets/index.md +136 -0
- package/content/extensions/helpers/socket-io/api.md +325 -204
- package/content/extensions/helpers/socket-io/index.md +79 -429
- package/content/extensions/helpers/storage/api.md +584 -464
- package/content/extensions/helpers/storage/index.md +80 -573
- package/content/extensions/helpers/types/index.md +75 -495
- package/content/extensions/helpers/types/reference.md +689 -0
- package/content/extensions/helpers/uid/index.md +176 -189
- package/content/extensions/helpers/websocket/api.md +366 -214
- package/content/extensions/helpers/websocket/index.md +68 -503
- package/content/extensions/helpers/worker-thread/index.md +62 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +5 -4
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +8 -7
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +64 -22
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +600 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +25 -46
- package/content/references/base/filter-system/application-usage.md +95 -123
- package/content/references/base/filter-system/array-operators.md +33 -43
- package/content/references/base/filter-system/comparison-operators.md +55 -63
- package/content/references/base/filter-system/default-filter.md +144 -350
- package/content/references/base/filter-system/fields-order-pagination.md +116 -148
- package/content/references/base/filter-system/index.md +114 -253
- package/content/references/base/filter-system/json-filtering.md +52 -181
- package/content/references/base/filter-system/list-operators.md +28 -44
- package/content/references/base/filter-system/logical-operators.md +69 -114
- package/content/references/base/filter-system/null-operators.md +41 -98
- package/content/references/base/filter-system/pattern-matching.md +48 -51
- package/content/references/base/filter-system/quick-reference.md +93 -196
- package/content/references/base/filter-system/range-operators.md +22 -38
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +173 -232
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +43 -28
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +81 -1452
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +281 -419
- package/content/references/base/repositories/index.md +84 -644
- package/content/references/base/repositories/mixins.md +25 -21
- package/content/references/base/repositories/relations.md +194 -375
- package/content/references/base/repositories/soft-deletable.md +67 -55
- package/content/references/base/secrets.md +267 -0
- package/content/references/base/services.md +8 -6
- package/content/references/configuration/environment-variables.md +73 -21
- package/content/references/configuration/index.md +51 -31
- package/content/references/index.md +1 -1
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +6 -2
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +81 -61
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +58 -218
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +24 -23
|
@@ -1,6 +1,107 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Mail Component - Full Reference
|
|
3
|
+
description: Binding keys, configuration options, interfaces, and internal implementation of the Mail component
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Mail Component Reference
|
|
8
|
+
|
|
9
|
+
Every binding key, configuration variant, interface, and internal mechanism of `MailComponent`. For the task-oriented walkthrough, see [Usage & Examples](./usage).
|
|
10
|
+
|
|
11
|
+
**Files:**
|
|
12
|
+
|
|
13
|
+
- [`packages/core-server/src/components/mail/component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/component.ts)
|
|
14
|
+
- [`packages/core-server/src/components/mail/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
|
|
15
|
+
- [`packages/core-server/src/components/mail/common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/constants.ts)
|
|
16
|
+
- [`packages/core-server/src/components/mail/common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/keys.ts)
|
|
17
|
+
- [`packages/core-server/src/components/mail/services/mail.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/mail.service.ts)
|
|
18
|
+
- [`packages/core-server/src/components/mail/services/template.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/template.service.ts)
|
|
19
|
+
- [`packages/core-server/src/components/mail/services/generator.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/generator.service.ts)
|
|
20
|
+
- [`packages/core-server/src/components/mail/providers/mail-transporter.provider.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/providers/mail-transporter.provider.ts)
|
|
21
|
+
- [`packages/core-server/src/components/mail/providers/mail-queue-executor.provider.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/providers/mail-queue-executor.provider.ts)
|
|
22
|
+
- [`packages/core-server/src/components/mail/helpers/transporters/nodemail-transporter.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/transporters/nodemail-transporter.helper.ts)
|
|
23
|
+
- [`packages/core-server/src/components/mail/helpers/transporters/mailgun-transporter.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/transporters/mailgun-transporter.helper.ts)
|
|
24
|
+
- [`packages/core-server/src/components/mail/helpers/executors/direct-executor.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/executors/direct-executor.helper.ts)
|
|
25
|
+
- [`packages/core-server/src/components/mail/helpers/executors/internal-queue-executor.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/executors/internal-queue-executor.helper.ts)
|
|
26
|
+
- [`packages/core-server/src/components/mail/helpers/executors/bull-mq-executor.helper.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/executors/bull-mq-executor.helper.ts)
|
|
27
|
+
- [`packages/core-server/src/components/mail/utilities/type.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/utilities/type.utility.ts)
|
|
28
|
+
- [`packages/core-server/src/components/mail/utilities/verification.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/utilities/verification.utility.ts)
|
|
29
|
+
|
|
30
|
+
## Quick reference
|
|
31
|
+
|
|
32
|
+
| Item | Value |
|
|
33
|
+
|------|-------|
|
|
34
|
+
| Package | `@venizia/ignis` |
|
|
35
|
+
| Subpath | `@venizia/ignis/mail` |
|
|
36
|
+
| Component class | `MailComponent` |
|
|
37
|
+
| Runtimes | Both (Bun, Node.js) |
|
|
38
|
+
|
|
39
|
+
| Component | Purpose |
|
|
40
|
+
|-----------|---------|
|
|
41
|
+
| `MailComponent` | Registers mail services, transporters, generators, and the queue executor |
|
|
42
|
+
| `MailService` | `send()`, `sendBatch()`, `sendTemplate()`, `verify()` |
|
|
43
|
+
| `TemplateEngineService` | <code v-pre>{{variable}}</code> substitution engine, in-memory template registry |
|
|
44
|
+
| `NodemailerTransportHelper` | SMTP transport via `nodemailer` |
|
|
45
|
+
| `MailgunTransportHelper` | Mailgun HTTP API transport via `mailgun.js` |
|
|
46
|
+
| `DirectMailExecutorHelper` | Runs the processor immediately, no queue |
|
|
47
|
+
| `InternalQueueMailExecutorHelper` | In-memory queue (`SequentialQueueHelper`) |
|
|
48
|
+
| `BullMQMailExecutorHelper` | Redis-backed queue, distributed workers |
|
|
49
|
+
| `MailTransportProvider` | Factory: `TMailOptions` -> `IMailTransport` |
|
|
50
|
+
| `MailQueueExecutorProvider` | Factory: `IMailQueueExecutorConfig` -> `IMailQueueExecutor` |
|
|
51
|
+
| `NumericCodeGenerator` | Cryptographically random numeric codes |
|
|
52
|
+
| `RandomTokenGenerator` | Cryptographically random base64url tokens |
|
|
53
|
+
| `DefaultVerificationDataGenerator` | Composes both generators into an `IVerificationData` |
|
|
54
|
+
|
|
55
|
+
## Import paths
|
|
2
56
|
|
|
3
|
-
|
|
57
|
+
```typescript
|
|
58
|
+
import {
|
|
59
|
+
MailComponent,
|
|
60
|
+
MailKeys,
|
|
61
|
+
MailProviders,
|
|
62
|
+
MailErrorCodes,
|
|
63
|
+
MailDefaults,
|
|
64
|
+
MailExecutorErrors,
|
|
65
|
+
MailQueueExecutorTypes,
|
|
66
|
+
BullMQExecutorModes,
|
|
67
|
+
MailService,
|
|
68
|
+
TemplateEngineService,
|
|
69
|
+
NumericCodeGenerator,
|
|
70
|
+
RandomTokenGenerator,
|
|
71
|
+
DefaultVerificationDataGenerator,
|
|
72
|
+
MailTransportProvider,
|
|
73
|
+
MailQueueExecutorProvider,
|
|
74
|
+
} from '@venizia/ignis/mail';
|
|
75
|
+
|
|
76
|
+
import type {
|
|
77
|
+
TMailOptions,
|
|
78
|
+
IBaseMailOptions,
|
|
79
|
+
INodemailerMailOptions,
|
|
80
|
+
IMailgunMailOptions,
|
|
81
|
+
ICustomMailOptions,
|
|
82
|
+
IGenericMailOptions,
|
|
83
|
+
IMailService,
|
|
84
|
+
IMailTemplateEngine,
|
|
85
|
+
IMailMessage,
|
|
86
|
+
IMailSendResult,
|
|
87
|
+
IMailTransport,
|
|
88
|
+
IMailAttachment,
|
|
89
|
+
IMailQueueExecutor,
|
|
90
|
+
IMailQueueExecutorConfig,
|
|
91
|
+
IMailQueueOptions,
|
|
92
|
+
IMailQueueResult,
|
|
93
|
+
IMailProcessorResult,
|
|
94
|
+
ITemplate,
|
|
95
|
+
IVerificationCodeGenerator,
|
|
96
|
+
IVerificationTokenGenerator,
|
|
97
|
+
IVerificationDataGenerator,
|
|
98
|
+
IVerificationData,
|
|
99
|
+
IVerificationGenerationOptions,
|
|
100
|
+
TMailProvider,
|
|
101
|
+
TNodemailerConfig,
|
|
102
|
+
TMailgunConfig,
|
|
103
|
+
} from '@venizia/ignis/mail';
|
|
104
|
+
```
|
|
4
105
|
|
|
5
106
|
## Architecture
|
|
6
107
|
|
|
@@ -8,126 +109,292 @@
|
|
|
8
109
|
┌─────────────────────────────────────────────────┐
|
|
9
110
|
│ Your Application │
|
|
10
111
|
│ │
|
|
11
|
-
│
|
|
12
|
-
│ ├── binds MailKeys.MAIL_OPTIONS
|
|
112
|
+
│ preConfigure() │
|
|
113
|
+
│ ├── binds MailKeys.MAIL_OPTIONS (required) │
|
|
13
114
|
│ ├── binds MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG │
|
|
115
|
+
│ │ (optional -- defaults to `direct`) │
|
|
14
116
|
│ └── registers MailComponent │
|
|
15
117
|
└───────────────────────┬─────────────────────────┘
|
|
16
118
|
│
|
|
17
119
|
▼
|
|
18
120
|
┌─────────────────────────────────────────────────┐
|
|
19
|
-
│ MailComponent
|
|
121
|
+
│ MailComponent.binding() │
|
|
20
122
|
│ │
|
|
21
|
-
│
|
|
22
|
-
│ ├── initGenerators() │
|
|
123
|
+
│ ├── initGenerators() (transient bindings) │
|
|
23
124
|
│ │ ├── NumericCodeGenerator │
|
|
24
125
|
│ │ ├── RandomTokenGenerator │
|
|
25
126
|
│ │ └── DefaultVerificationDataGenerator │
|
|
26
127
|
│ │ │
|
|
27
|
-
│ ├── initProviders()
|
|
28
|
-
│ │ ├── MailTransportProvider
|
|
29
|
-
│ │ └── MailQueueExecutorProvider
|
|
128
|
+
│ ├── initProviders() (singleton bindings) │
|
|
129
|
+
│ │ ├── MailTransportProvider │
|
|
130
|
+
│ │ └── MailQueueExecutorProvider │
|
|
30
131
|
│ │ │
|
|
31
|
-
│ ├── initServices()
|
|
32
|
-
│ │ ├── MailService
|
|
33
|
-
│ │ └── TemplateEngineService
|
|
132
|
+
│ ├── initServices() (singleton bindings) │
|
|
133
|
+
│ │ ├── MailService │
|
|
134
|
+
│ │ └── TemplateEngineService │
|
|
34
135
|
│ │ │
|
|
35
136
|
│ └── createAndBindInstances() │
|
|
36
|
-
│ ├── Transport Instance
|
|
37
|
-
│ └── Queue Executor
|
|
137
|
+
│ ├── Transport Instance ◄── MAIL_OPTIONS │
|
|
138
|
+
│ └── Queue Executor ◄── QUEUE_CONFIG │
|
|
139
|
+
│ (or the direct │
|
|
140
|
+
│ default) │
|
|
38
141
|
└─────────────────────────────────────────────────┘
|
|
39
142
|
```
|
|
40
143
|
|
|
41
|
-
**
|
|
144
|
+
`MailService` and the queue executor are **independent** consumers of the transport/config. `IMailQueueExecutor` never calls `MailService`. See [How it works](./#how-it-works) on the Overview for that distinction.
|
|
42
145
|
|
|
43
|
-
|
|
44
|
-
- **`MailService`** -- Extends `BaseService`. Provides `send()`, `sendBatch()`, `sendTemplate()`, and `verify()`. Injects transport instance and template engine. Validates messages before sending
|
|
45
|
-
- **`TemplateEngineService`** -- Extends `BaseService`. Manages email templates with simple <code v-pre>{{variable}}</code> substitution. Stores templates in an in-memory `Map`
|
|
46
|
-
- **`MailTransportProvider`** -- Extends `BaseProvider`. Factory that creates transport instances with type-guard methods (`isNodemailerOptions`, `isMailgunOptions`, `isCustomOptions`) and throws `MailErrorCodes` on invalid configs
|
|
47
|
-
- **`MailQueueExecutorProvider`** -- Extends `BaseProvider`. Factory that creates queue executor instances. Throws for missing sub-configs (e.g., `config.internalQueue` or `config.bullmq`)
|
|
48
|
-
- **Verification Generators** -- `NumericCodeGenerator`, `RandomTokenGenerator`, `DefaultVerificationDataGenerator`. Generate verification codes, tokens, and data for email verification flows
|
|
146
|
+
**Source:** [`component.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/component.ts)
|
|
49
147
|
|
|
50
|
-
|
|
148
|
+
## Binding keys
|
|
51
149
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
150
|
+
| Key | Constant | Type | Required | Default |
|
|
151
|
+
|-----|----------|------|----------|---------|
|
|
152
|
+
| `@app/components/mail/options` | `MailKeys.MAIL_OPTIONS` | `TMailOptions` | **Yes** | -- |
|
|
153
|
+
| `@app/components/mail/queue/executor-config` | `MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG` | `IMailQueueExecutorConfig` | No | `{ type: MailQueueExecutorTypes.DIRECT }` |
|
|
154
|
+
| `@app/components/mail/service` | `MailKeys.MAIL_SERVICE` | `IMailService` | No | `MailService` (singleton) |
|
|
155
|
+
| `@app/components/mail/services/template-engine` | `MailKeys.MAIL_TEMPLATE_ENGINE` | `IMailTemplateEngine` | No | `TemplateEngineService` (singleton) |
|
|
156
|
+
| `@app/components/mail/transport-provider` | `MailKeys.MAIL_TRANSPORT_PROVIDER` | `TGetMailTransportFn` | No | `MailTransportProvider` (singleton) |
|
|
157
|
+
| `@app/components/mail/transport-instance` | `MailKeys.MAIL_TRANSPORT_INSTANCE` | `IMailTransport` | No | Created by the component |
|
|
158
|
+
| `@app/components/mail/queue-executor-provider` | `MailKeys.MAIL_QUEUE_EXECUTOR_PROVIDER` | `TGetMailQueueExecutorFn` | No | `MailQueueExecutorProvider` (singleton) |
|
|
159
|
+
| `@app/components/mail/queue-executor-instance` | `MailKeys.MAIL_QUEUE_EXECUTOR_INSTANCE` | `IMailQueueExecutor` | No | Created by the component |
|
|
160
|
+
| `@app/components/mail/verification/code-generator` | `MailKeys.MAIL_VERIFICATION_CODE_GENERATOR` | `IVerificationCodeGenerator` | No | `NumericCodeGenerator` (transient) |
|
|
161
|
+
| `@app/components/mail/verification/token-generator` | `MailKeys.MAIL_VERIFICATION_TOKEN_GENERATOR` | `IVerificationTokenGenerator` | No | `RandomTokenGenerator` (transient) |
|
|
162
|
+
| `@app/components/mail/verification/data-generator` | `MailKeys.MAIL_VERIFICATION_DATA_GENERATOR` | `IVerificationDataGenerator` | No | `DefaultVerificationDataGenerator` (transient) |
|
|
56
163
|
|
|
57
|
-
|
|
164
|
+
> [!IMPORTANT]
|
|
165
|
+
> `MailKeys.MAIL_OPTIONS` is the only binding `MailComponent` requires. It throws `Mail options not configured` in `binding()` if the key is not bound. `MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG` is read with `isOptional: true` -- when it is not bound, `createAndBindInstances()` falls back to `{ type: MailQueueExecutorTypes.DIRECT }` rather than failing startup.
|
|
58
166
|
|
|
59
|
-
|
|
167
|
+
**Source:** [`common/keys.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/keys.ts)
|
|
60
168
|
|
|
61
|
-
|
|
169
|
+
## Configuration
|
|
62
170
|
|
|
63
|
-
|
|
64
|
-
- **`'mailgun'`** -- Creates a `MailgunTransportHelper` using the Mailgun HTTP API
|
|
65
|
-
- **`'custom'`** -- Expects the `config` value to implement `IMailTransport` directly (must have `send()` and `verify()`)
|
|
66
|
-
- **Any other string** -- Falls through to `default` and throws `Unsupported mail provider: <provider>` with `MailErrorCodes.INVALID_CONFIGURATION`
|
|
171
|
+
### Transport options (`TMailOptions`)
|
|
67
172
|
|
|
68
|
-
|
|
173
|
+
A discriminated union on `provider`, extending `IBaseMailOptions`:
|
|
69
174
|
|
|
70
175
|
```typescript
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
176
|
+
interface IBaseMailOptions {
|
|
177
|
+
from?: string;
|
|
178
|
+
fromName?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
interface INodemailerMailOptions extends IBaseMailOptions {
|
|
182
|
+
provider: 'nodemailer';
|
|
183
|
+
config: TNodemailerConfig; // SMTPTransport | SMTPTransport.Options | string
|
|
184
|
+
module?: TNodemailerModule; // the peer itself; see "Peer dependency loading"
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
interface IMailgunMailOptions extends IBaseMailOptions {
|
|
188
|
+
provider: 'mailgun';
|
|
189
|
+
config: TMailgunConfig; // AnyType & { domain: string } -- also requires username, key at runtime
|
|
190
|
+
module?: TMailgunModule; // the peer itself; see "Peer dependency loading"
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
interface ICustomMailOptions extends IBaseMailOptions {
|
|
194
|
+
provider: 'custom';
|
|
195
|
+
config: IMailTransport; // Must implement send() and verify()
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
interface IGenericMailOptions extends IBaseMailOptions {
|
|
199
|
+
provider: string;
|
|
200
|
+
config: Record<string, AnyType>;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
type TMailOptions =
|
|
204
|
+
| INodemailerMailOptions
|
|
205
|
+
| IMailgunMailOptions
|
|
206
|
+
| ICustomMailOptions
|
|
207
|
+
| IGenericMailOptions;
|
|
74
208
|
```
|
|
75
209
|
|
|
76
|
-
|
|
210
|
+
**Nodemailer (SMTP with basic auth):**
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
{
|
|
214
|
+
provider: MailProviders.NODEMAILER,
|
|
215
|
+
from: 'noreply@example.com',
|
|
216
|
+
fromName: 'Example App',
|
|
217
|
+
config: {
|
|
218
|
+
host: 'smtp.gmail.com',
|
|
219
|
+
port: 465,
|
|
220
|
+
secure: true,
|
|
221
|
+
auth: { user: 'your-email@gmail.com', pass: 'your-app-password' },
|
|
222
|
+
},
|
|
223
|
+
}
|
|
224
|
+
```
|
|
77
225
|
|
|
78
|
-
|
|
226
|
+
**Nodemailer (OAuth2):**
|
|
79
227
|
|
|
80
228
|
```typescript
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
229
|
+
{
|
|
230
|
+
provider: MailProviders.NODEMAILER,
|
|
231
|
+
from: applicationEnvironment.get<string>('APP_ENV_MAIL_FROM') ?? 'noreply@example.com',
|
|
232
|
+
fromName: applicationEnvironment.get<string>('APP_ENV_MAIL_FROM_NAME') ?? 'Example App',
|
|
233
|
+
config: {
|
|
234
|
+
host: applicationEnvironment.get<string>('APP_ENV_MAIL_HOST') ?? 'smtp.gmail.com',
|
|
235
|
+
port: +(applicationEnvironment.get<number>('APP_ENV_MAIL_PORT') ?? 465),
|
|
236
|
+
secure: toBoolean(applicationEnvironment.get<boolean>('APP_ENV_MAIL_SECURE') ?? true),
|
|
237
|
+
auth: {
|
|
238
|
+
type: 'oauth2',
|
|
239
|
+
user: applicationEnvironment.get<string>('APP_ENV_MAIL_USER'),
|
|
240
|
+
clientId: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_ID'),
|
|
241
|
+
clientSecret: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_SECRET'),
|
|
242
|
+
refreshToken: applicationEnvironment.get<string>('APP_ENV_MAIL_REFRESH_TOKEN'),
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
`APP_ENV_MAIL_*` is an app-level convention, not a framework-defined env var. `applicationEnvironment.get()` reads whatever variables your wrapper component chooses to look up. A matching `.env`:
|
|
249
|
+
|
|
250
|
+
```
|
|
251
|
+
APP_ENV_MAIL_HOST=smtp.gmail.com
|
|
252
|
+
APP_ENV_MAIL_PORT=465
|
|
253
|
+
APP_ENV_MAIL_SECURE=true
|
|
254
|
+
APP_ENV_MAIL_USER=your-email@gmail.com
|
|
255
|
+
APP_ENV_MAIL_CLIENT_ID=your-oauth2-client-id
|
|
256
|
+
APP_ENV_MAIL_CLIENT_SECRET=your-oauth2-client-secret
|
|
257
|
+
APP_ENV_MAIL_REFRESH_TOKEN=your-oauth2-refresh-token
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
> [!TIP]
|
|
261
|
+
> For Gmail OAuth2, follow [Google's OAuth2 setup guide](https://developers.google.com/gmail/api/auth/web-server) to obtain the client ID, secret, and refresh token.
|
|
262
|
+
|
|
263
|
+
**Mailgun:**
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
{
|
|
267
|
+
provider: MailProviders.MAILGUN,
|
|
268
|
+
from: 'noreply@example.com',
|
|
269
|
+
fromName: 'Example App',
|
|
270
|
+
config: {
|
|
271
|
+
username: 'api', // required -- mailgun.js client username
|
|
272
|
+
key: process.env.MAILGUN_API_KEY, // required -- Mailgun API key
|
|
273
|
+
domain: 'mg.example.com', // required
|
|
274
|
+
host: 'api.eu.mailgun.net', // optional -- EU region
|
|
275
|
+
},
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> [!IMPORTANT]
|
|
280
|
+
> `MailgunTransportHelper` validates `username`, `key`, and `domain` on construction. It throws `Invalid Mailgun configuration | Missing required keys: <keys>` if any is missing. This check runs even though `TMailgunConfig`'s only *typed* requirement is `domain` - `username` and `key` are checked at runtime, not by the type.
|
|
281
|
+
|
|
282
|
+
**Custom transport:**
|
|
283
|
+
|
|
284
|
+
```typescript
|
|
285
|
+
{
|
|
286
|
+
provider: MailProviders.CUSTOM,
|
|
287
|
+
from: 'noreply@example.com',
|
|
288
|
+
config: myTransportImplementingIMailTransport, // must have send() and verify()
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Generic provider (extensibility escape hatch):**
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
{
|
|
296
|
+
provider: 'sendgrid',
|
|
297
|
+
from: 'noreply@example.com',
|
|
298
|
+
config: { apiKey: process.env.SENDGRID_API_KEY },
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
> [!WARNING]
|
|
303
|
+
> `IGenericMailOptions` falls through to the `default` case in `MailTransportProvider` and throws `Unsupported mail provider: <provider>`. The framework's built-in provider does not handle it. `IGenericMailOptions` exists only so you can bind a **custom** `MailTransportProvider` that recognizes the provider string.
|
|
304
|
+
|
|
305
|
+
### Queue executor options (`IMailQueueExecutorConfig`)
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
interface IMailQueueExecutorConfig {
|
|
309
|
+
type: TConstValue<typeof MailQueueExecutorTypes>; // 'direct' | 'internal-queue' | 'bullmq'
|
|
310
|
+
internalQueue?: {
|
|
311
|
+
identifier: string;
|
|
312
|
+
};
|
|
313
|
+
bullmq?: {
|
|
314
|
+
redis: IRedisSingleHelperOptions;
|
|
315
|
+
queue: { identifier: string; name: string };
|
|
316
|
+
mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED -- no default
|
|
317
|
+
};
|
|
85
318
|
}
|
|
86
319
|
```
|
|
87
320
|
|
|
88
|
-
|
|
321
|
+
```typescript
|
|
322
|
+
// Direct (also the implicit default when MAIL_QUEUE_EXECUTOR_CONFIG is unbound)
|
|
323
|
+
{ type: 'direct' }
|
|
324
|
+
|
|
325
|
+
// Internal queue (in-memory)
|
|
326
|
+
{ type: 'internal-queue', internalQueue: { identifier: 'mail-internal-queue' } }
|
|
327
|
+
|
|
328
|
+
// BullMQ (Redis-backed)
|
|
329
|
+
{
|
|
330
|
+
type: 'bullmq',
|
|
331
|
+
bullmq: {
|
|
332
|
+
redis: { host: 'localhost', port: 6379, password: 'your-redis-password' },
|
|
333
|
+
queue: { identifier: 'mail-queue', name: 'mail-queue' },
|
|
334
|
+
mode: 'both', // 'queue-only' | 'worker-only' | 'both' -- required, no default
|
|
335
|
+
},
|
|
336
|
+
}
|
|
337
|
+
```
|
|
89
338
|
|
|
90
|
-
|
|
339
|
+
**Choosing an executor:**
|
|
91
340
|
|
|
92
|
-
|
|
93
|
-
|
|
341
|
+
| Executor | Use when | Why |
|
|
342
|
+
|---|---|---|
|
|
343
|
+
| `direct` | Development or low-volume apps | No queueing overhead |
|
|
344
|
+
| `internal-queue` | Single-instance apps, moderate volume | In-memory, with retry |
|
|
345
|
+
| `bullmq` | Distributed or high-volume systems | Redis-backed, configurable concurrency/priority/backoff |
|
|
94
346
|
|
|
95
|
-
|
|
347
|
+
**Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
|
|
96
348
|
|
|
97
|
-
|
|
349
|
+
### Constants
|
|
98
350
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
351
|
+
| Constant | Value | Description |
|
|
352
|
+
|----------|-------|-------------|
|
|
353
|
+
| `MailDefaults.BATCH_CONCURRENCY` | `5` | Default concurrent sends in `sendBatch()` |
|
|
354
|
+
| `MailDefaults.FALLBACK_FROM` | `'noreply@example.com'` | Used by `getDefaultFrom()` when `options.from` is unset |
|
|
355
|
+
| `MailExecutorErrors.PROCESSOR_NOT_SET` | `'Processor not set. Call setProcessor() first.'` | Thrown by all three queue executors |
|
|
356
|
+
| `MailQueueExecutorTypes.DIRECT` | `'direct'` | Immediate execution |
|
|
357
|
+
| `MailQueueExecutorTypes.INTERNAL_QUEUE` | `'internal-queue'` | In-memory queue |
|
|
358
|
+
| `MailQueueExecutorTypes.BULLMQ` | `'bullmq'` | Redis-backed queue |
|
|
359
|
+
| `BullMQExecutorModes.QUEUE_ONLY` | `'queue-only'` | Producer only (enqueue) |
|
|
360
|
+
| `BullMQExecutorModes.WORKER_ONLY` | `'worker-only'` | Consumer only (process) |
|
|
361
|
+
| `BullMQExecutorModes.BOTH` | `'both'` | Full duplex (produce + consume) |
|
|
104
362
|
|
|
105
|
-
|
|
363
|
+
`MailQueueExecutorTypes` and `BullMQExecutorModes` both carry a `SCHEME_SET`/`MODE_SET` and a static `isValid()`:
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
MailQueueExecutorTypes.isValid('bullmq'); // true
|
|
367
|
+
MailQueueExecutorTypes.isValid('unknown'); // false
|
|
368
|
+
BullMQExecutorModes.isValid('both'); // true
|
|
369
|
+
BullMQExecutorModes.isValid('invalid'); // false
|
|
370
|
+
```
|
|
106
371
|
|
|
107
|
-
|
|
108
|
-
- `send()` converts `IMailMessage` to Mailgun's format: `to` becomes an array, `replyTo` becomes `h:Reply-To`, all custom headers are prefixed with `h:`
|
|
109
|
-
- Attachments are mapped to `{ filename, data }` objects where `data` is `path ?? content ?? Buffer.from('')`
|
|
110
|
-
- `verify()` sends a test email to `verify@<domain>` with `o:testmode: 'yes'` flag to check API credentials without actually sending
|
|
111
|
-
- No `close()` method (HTTP API is stateless)
|
|
372
|
+
#### `MailErrorCodes`
|
|
112
373
|
|
|
113
|
-
|
|
374
|
+
Built through `MessageCode.build()`, so every value is lower-case (`ApplicationError` lower-cases whatever it is handed):
|
|
114
375
|
|
|
115
|
-
|
|
376
|
+
| Constant | Value | Description |
|
|
377
|
+
|----------|-------|-------------|
|
|
378
|
+
| `MailErrorCodes.INVALID_CONFIGURATION` | `'core.mail.invalid_configuration'` | Invalid or missing configuration (transport, template engine, subject, body) |
|
|
379
|
+
| `MailErrorCodes.SEND_FAILED` | `'core.mail.send_failed'` | Single email send failed |
|
|
380
|
+
| `MailErrorCodes.VERIFICATION_FAILED` | `'core.mail.verification_failed'` | Transport connection verification failed |
|
|
381
|
+
| `MailErrorCodes.INVALID_RECIPIENT` | `'core.mail.invalid_recipient'` | Missing or empty recipient address |
|
|
382
|
+
| `MailErrorCodes.BATCH_SEND_FAILED` | `'core.mail.batch_send_failed'` | Batch email operation failed |
|
|
383
|
+
| `MailErrorCodes.TEMPLATE_NOT_FOUND` | `'core.mail.template_not_found'` | Template name not found in registry |
|
|
116
384
|
|
|
117
|
-
|
|
385
|
+
**Source:** [`common/constants.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/constants.ts)
|
|
386
|
+
|
|
387
|
+
## `IMailService` interface
|
|
118
388
|
|
|
119
389
|
```typescript
|
|
120
390
|
interface IMailService {
|
|
121
|
-
// Send a single email
|
|
122
391
|
send(message: IMailMessage): Promise<IMailSendResult>;
|
|
123
392
|
|
|
124
|
-
// Send multiple emails with controlled concurrency
|
|
125
393
|
sendBatch(
|
|
126
394
|
messages: IMailMessage[],
|
|
127
395
|
options?: { concurrency?: number },
|
|
128
396
|
): Promise<IMailSendResult[]>;
|
|
129
397
|
|
|
130
|
-
// Send email using a registered template
|
|
131
398
|
sendTemplate(opts: {
|
|
132
399
|
templateName: string;
|
|
133
400
|
data: Record<string, any>;
|
|
@@ -135,100 +402,78 @@ interface IMailService {
|
|
|
135
402
|
options?: Partial<IMailMessage>;
|
|
136
403
|
}): Promise<IMailSendResult>;
|
|
137
404
|
|
|
138
|
-
// Verify transport connection
|
|
139
405
|
verify(): Promise<boolean>;
|
|
140
406
|
}
|
|
141
407
|
```
|
|
142
408
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
**`send(message: IMailMessage)`**
|
|
146
|
-
|
|
147
|
-
Sends a single email using the configured transport. Internally:
|
|
148
|
-
1. Calls `validateMessage()` which throws for missing `to`, `subject`, or both `text`/`html` (see error reference below)
|
|
149
|
-
2. Merges `message.from` with the default from address (via `getDefaultFrom()`)
|
|
150
|
-
3. Delegates to `transport.send()`
|
|
151
|
-
4. Returns a result object with `success`, `messageId`, and optional `error` fields
|
|
152
|
-
5. If the transport throws, catches the error and re-throws with `MailErrorCodes.SEND_FAILED`
|
|
409
|
+
**`send(message)`**
|
|
153
410
|
|
|
154
|
-
|
|
411
|
+
1. `validateMessage()` throws for missing `to`, missing `subject`, or missing both `text`/`html` (400, see below).
|
|
412
|
+
2. Merges `message.from` with `getDefaultFrom()` if unset.
|
|
413
|
+
3. Delegates to `transport.send()`.
|
|
414
|
+
4. Returns `{ success, messageId, error? }`.
|
|
415
|
+
5. If the transport throws, and the error is **not** already an `ApplicationError`, it is caught and re-thrown as `MailErrorCodes.SEND_FAILED` (500).
|
|
416
|
+
6. An `ApplicationError` thrown earlier in the same `try` block - for example, from `validateMessage()` - is re-thrown as-is, unchanged.
|
|
155
417
|
|
|
156
|
-
|
|
418
|
+
**`sendBatch(messages, options?)`**
|
|
157
419
|
|
|
158
|
-
|
|
420
|
+
Sends every message via `send()` with concurrency bounded by `executePromiseWithLimit()`, default `MailDefaults.BATCH_CONCURRENCY` (`5`). A `send()` that throws is caught per-message and downgraded to `{ success: false, error }`; a failure in the batch operation itself throws `MailErrorCodes.BATCH_SEND_FAILED`.
|
|
159
421
|
|
|
160
|
-
|
|
422
|
+
**`sendTemplate(opts)`**
|
|
161
423
|
|
|
162
|
-
|
|
163
|
-
2. Template subject rendered through the template engine (if `templateData.subject` is defined)
|
|
164
|
-
3. `'No Subject'` -- fallback if neither is provided
|
|
165
|
-
|
|
166
|
-
Throws `MailErrorCodes.INVALID_CONFIGURATION` if the template engine is not configured. Re-throws any other errors (including `TEMPLATE_NOT_FOUND` from the template engine).
|
|
424
|
+
Renders a registered template and sends it. Subject resolution order: `options.subject` -> the template's own `subject` (rendered through the same engine) -> `'No Subject'`. Throws `MailErrorCodes.INVALID_CONFIGURATION` ("Template engine not configured") if `templateEngine` was not injected. Re-throws any other error unchanged, including `TEMPLATE_NOT_FOUND` from the template engine.
|
|
167
425
|
|
|
168
426
|
**`verify()`**
|
|
169
427
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
### Protected Methods (MailService)
|
|
428
|
+
Delegates to `transport.verify()`. If the transport throws, wraps it as `MailErrorCodes.VERIFICATION_FAILED` (500).
|
|
173
429
|
|
|
174
|
-
|
|
430
|
+
### Protected methods (`MailService`)
|
|
175
431
|
|
|
176
|
-
|
|
432
|
+
**`validateMessage(message)`**
|
|
177
433
|
|
|
178
|
-
| Check | Error
|
|
434
|
+
| Check | Error code | Status | Message |
|
|
179
435
|
|-------|-----------|--------|---------|
|
|
180
|
-
| `to` is falsy or empty array | `INVALID_RECIPIENT` | 400 | `Recipient email address is required` |
|
|
436
|
+
| `to` is falsy or an empty array | `INVALID_RECIPIENT` | 400 | `Recipient email address is required` |
|
|
181
437
|
| `subject` is falsy | `INVALID_CONFIGURATION` | 400 | `Email subject is required` |
|
|
182
438
|
| Both `text` and `html` are falsy | `INVALID_CONFIGURATION` | 400 | `Email must have either text or html content` |
|
|
183
439
|
|
|
184
440
|
**`getDefaultFrom()`**
|
|
185
441
|
|
|
186
|
-
|
|
442
|
+
- If `options.from` is unset, returns `MailDefaults.FALLBACK_FROM` (`'noreply@example.com'`).
|
|
443
|
+
- Else if `options.fromName` is unset, returns `options.from` as-is.
|
|
444
|
+
- Else returns `"${fromName}" <${from}>`.
|
|
445
|
+
|
|
446
|
+
**Source:** [`services/mail.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/mail.service.ts)
|
|
187
447
|
|
|
188
|
-
## IMailMessage
|
|
448
|
+
## `IMailMessage` interface
|
|
189
449
|
|
|
190
450
|
```typescript
|
|
191
451
|
interface IMailMessage {
|
|
192
|
-
from?: string;
|
|
193
|
-
to: string | string[];
|
|
194
|
-
cc?: string | string[];
|
|
195
|
-
bcc?: string | string[];
|
|
196
|
-
replyTo?: string;
|
|
197
|
-
subject: string;
|
|
198
|
-
text?: string;
|
|
199
|
-
html?: string;
|
|
200
|
-
attachments?: IMailAttachment[];
|
|
201
|
-
headers?: Record<string, string>;
|
|
202
|
-
requireValidate?: boolean;
|
|
203
|
-
[key: string]: any;
|
|
452
|
+
from?: string; // Uses getDefaultFrom() if not provided
|
|
453
|
+
to: string | string[];
|
|
454
|
+
cc?: string | string[];
|
|
455
|
+
bcc?: string | string[];
|
|
456
|
+
replyTo?: string;
|
|
457
|
+
subject: string;
|
|
458
|
+
text?: string;
|
|
459
|
+
html?: string;
|
|
460
|
+
attachments?: IMailAttachment[];
|
|
461
|
+
headers?: Record<string, string>;
|
|
462
|
+
requireValidate?: boolean; // Passed through to sendTemplate()'s render() call
|
|
463
|
+
[key: string]: any; // Open-ended -- provider-specific fields pass through
|
|
204
464
|
}
|
|
205
465
|
```
|
|
206
466
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
**`replyTo`**
|
|
218
|
-
|
|
219
|
-
Reply-to address if different from the sender. Useful for no-reply addresses that route replies to a support inbox. Mailgun maps this to `h:Reply-To`.
|
|
220
|
-
|
|
221
|
-
**`subject`**
|
|
222
|
-
|
|
223
|
-
Email subject line. Supports template variables when used with `sendTemplate()` (the subject is rendered through the same template engine).
|
|
224
|
-
|
|
225
|
-
**`text`, `html`**
|
|
226
|
-
|
|
227
|
-
Plain text and HTML versions of the email body. At least one must be provided (validated by `validateMessage()`). Most email clients prefer HTML but fall back to text if HTML is not available. Best practice is to provide both.
|
|
228
|
-
|
|
229
|
-
**`attachments`**
|
|
230
|
-
|
|
231
|
-
Array of `IMailAttachment` objects:
|
|
467
|
+
| Field | Notes |
|
|
468
|
+
|-------|-------|
|
|
469
|
+
| `from` | Falls back to `getDefaultFrom()`. Can be overridden per message (multi-tenant scenarios). |
|
|
470
|
+
| `to`, `cc`, `bcc` | Single string or array. Nodemailer joins arrays with `', '`; Mailgun passes `to` as-is (always coerced to an array), `cc`/`bcc` pass through unmodified. |
|
|
471
|
+
| `replyTo` | Nodemailer passes it directly; Mailgun maps it to `h:Reply-To`. |
|
|
472
|
+
| `subject` | Rendered through the template engine when set via `sendTemplate()`. |
|
|
473
|
+
| `text`, `html` | At least one is required (`validateMessage()`). |
|
|
474
|
+
| `attachments` | See `IMailAttachment` below. |
|
|
475
|
+
| `headers` | Nodemailer passes them directly; Mailgun prefixes every key with `h:`. |
|
|
476
|
+
| `requireValidate` | `true` makes template rendering throw on missing placeholders instead of preserving them as literal text. Defaults to `false`. |
|
|
232
477
|
|
|
233
478
|
```typescript
|
|
234
479
|
interface IMailAttachment {
|
|
@@ -241,28 +486,17 @@ interface IMailAttachment {
|
|
|
241
486
|
}
|
|
242
487
|
```
|
|
243
488
|
|
|
244
|
-
|
|
245
|
-
-
|
|
246
|
-
-
|
|
247
|
-
-
|
|
489
|
+
- File path: `{ filename: 'doc.pdf', path: '/path/to/doc.pdf' }`
|
|
490
|
+
- Buffer: `{ filename: 'data.txt', content: Buffer.from('...') }`
|
|
491
|
+
- Inline image: `{ filename: 'logo.png', path: '...', cid: 'logo' }`
|
|
492
|
+
- Mailgun maps each attachment to `{ filename, data: att.path ?? att.content ?? Buffer.from('') }`.
|
|
248
493
|
|
|
249
|
-
|
|
494
|
+
**Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
|
|
250
495
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
**`requireValidate`**
|
|
254
|
-
|
|
255
|
-
When `true`, template rendering will throw an error if any <code v-pre>{{variable}}</code> placeholders are missing from the data object. Defaults to `false` (missing variables are preserved as their original placeholder text, not replaced with empty strings).
|
|
256
|
-
|
|
257
|
-
**`[key: string]: any`**
|
|
258
|
-
|
|
259
|
-
The interface is open-ended -- additional fields are accepted for provider-specific options.
|
|
260
|
-
|
|
261
|
-
## IMailTemplateEngine Interface
|
|
496
|
+
## `IMailTemplateEngine` interface
|
|
262
497
|
|
|
263
498
|
```typescript
|
|
264
499
|
interface IMailTemplateEngine {
|
|
265
|
-
// Render a template with data
|
|
266
500
|
render(opts: {
|
|
267
501
|
templateData?: string;
|
|
268
502
|
templateName?: string;
|
|
@@ -270,93 +504,118 @@ interface IMailTemplateEngine {
|
|
|
270
504
|
requireValidate?: boolean;
|
|
271
505
|
}): string;
|
|
272
506
|
|
|
273
|
-
// Register a new template
|
|
274
507
|
registerTemplate(opts: { name: string; content: string }): void;
|
|
275
508
|
|
|
276
|
-
// Validate template data
|
|
277
509
|
validateTemplateData(opts: { template: string; data: Record<string, any> }): {
|
|
278
510
|
isValid: boolean;
|
|
279
511
|
missingKeys: string[];
|
|
280
512
|
allKeys: string[];
|
|
281
513
|
};
|
|
282
514
|
|
|
283
|
-
// Get a registered template
|
|
284
515
|
getTemplate(name: string): ITemplate | undefined;
|
|
285
|
-
|
|
286
|
-
// List all registered templates
|
|
287
516
|
listTemplates(): ITemplate[];
|
|
288
|
-
|
|
289
|
-
// Check if template exists
|
|
290
517
|
hasTemplate(name: string): boolean;
|
|
291
|
-
|
|
292
|
-
// Remove a template
|
|
293
518
|
removeTemplate(name: string): boolean;
|
|
294
519
|
}
|
|
295
520
|
```
|
|
296
521
|
|
|
297
|
-
**
|
|
522
|
+
`TemplateEngineService` also exposes `clearTemplates(): void`. It resets the registry, but it is **not** part of the `IMailTemplateEngine` interface - you can only reach it when you hold the concrete class, not the interface type.
|
|
298
523
|
|
|
299
|
-
|
|
524
|
+
| Method | Behavior |
|
|
525
|
+
|--------|----------|
|
|
526
|
+
| `render(opts)` | Renders by `templateName` (registry lookup) or raw `templateData`. Throws if neither is given. Throws `TEMPLATE_NOT_FOUND` (404) if `templateName` is not registered. Delegates to `renderSimpleTemplate()`. |
|
|
527
|
+
| `registerTemplate(opts)` | `options` (on the class) accepts `subject`/`description` (`Partial<ITemplate>`). Overwrites an existing template with the same name. |
|
|
528
|
+
| `validateTemplateData(opts)` | Extracts every unique <code v-pre>{{key}}</code> via `/\{\{(\s*[\w.]+\s*)\}\}/g`, deduplicates, resolves nested dot-notation values. Returns `isValid`, `missingKeys`, `allKeys`. |
|
|
529
|
+
| `getTemplate(name)` | Returns `undefined` if not registered. |
|
|
530
|
+
| `listTemplates()` | All templates, as an array of `ITemplate`. |
|
|
531
|
+
| `hasTemplate(name)` | Registry membership check. |
|
|
532
|
+
| `removeTemplate(name)` | Logs the removal, returns `true`/`false`. |
|
|
300
533
|
|
|
301
|
-
|
|
534
|
+
```typescript
|
|
535
|
+
interface ITemplate {
|
|
536
|
+
name: string;
|
|
537
|
+
content?: string;
|
|
538
|
+
render?: (data: Record<string, AnyType>) => string;
|
|
539
|
+
subject?: string;
|
|
540
|
+
description?: string;
|
|
541
|
+
}
|
|
542
|
+
```
|
|
302
543
|
|
|
303
|
-
|
|
544
|
+
The `render` field supports a custom per-template render function, though the built-in `TemplateEngineService` always uses `content` + `renderSimpleTemplate()` instead.
|
|
304
545
|
|
|
305
|
-
|
|
546
|
+
**Source:** [`services/template.service.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/services/template.service.ts)
|
|
306
547
|
|
|
307
|
-
|
|
548
|
+
## Transport layer
|
|
308
549
|
|
|
309
|
-
|
|
310
|
-
- `isValid` -- `true` if all placeholders have non-null, non-undefined values
|
|
311
|
-
- `missingKeys` -- Array of placeholder names that are missing or null/undefined in the data
|
|
312
|
-
- `allKeys` -- Array of all unique placeholder names found in the template
|
|
550
|
+
`MailTransportProvider.value()` returns a factory function that switches on `TMailOptions.provider`:
|
|
313
551
|
|
|
314
|
-
|
|
552
|
+
| Provider | Result |
|
|
553
|
+
|----------|--------|
|
|
554
|
+
| `'nodemailer'` | `NodemailerTransportHelper`, backed by `nodemailer` |
|
|
555
|
+
| `'mailgun'` | `MailgunTransportHelper`, backed by the Mailgun HTTP API (`mailgun.js`) |
|
|
556
|
+
| `'custom'` | The `config` value itself, validated to implement `IMailTransport` |
|
|
557
|
+
| Any other string | Throws `Unsupported mail provider: <provider>` (`INVALID_CONFIGURATION`, 500) |
|
|
315
558
|
|
|
316
|
-
|
|
559
|
+
Type narrowing uses three private guards, each checking `provider === X && 'config' in options`:
|
|
317
560
|
|
|
318
|
-
|
|
561
|
+
```typescript
|
|
562
|
+
private isNodemailerOptions(options: TMailOptions): options is INodemailerMailOptions
|
|
563
|
+
private isMailgunOptions(options: TMailOptions): options is IMailgunMailOptions
|
|
564
|
+
private isCustomOptions(options: TMailOptions): options is ICustomMailOptions
|
|
565
|
+
```
|
|
319
566
|
|
|
320
|
-
|
|
567
|
+
For `custom`, an additional `isMailTransport()` utility check reports specifically which of `send`/`verify` is missing.
|
|
321
568
|
|
|
322
|
-
|
|
569
|
+
```typescript
|
|
570
|
+
interface IMailTransport {
|
|
571
|
+
send(message: IMailMessage): Promise<IMailSendResult>;
|
|
572
|
+
verify(): Promise<boolean>;
|
|
573
|
+
close?(): Promise<void>;
|
|
574
|
+
}
|
|
575
|
+
```
|
|
323
576
|
|
|
324
|
-
|
|
577
|
+
### Peer dependency loading
|
|
325
578
|
|
|
326
|
-
|
|
579
|
+
Pass the peer yourself through `module`, or let the transport find it. With `module` set, the transport uses it as-is; without it, the transport falls back to `ModuleUtility.loadSync({ module })` from the client-factory seam `configure()` calls. A missing package then throws the framework's install hint - `[ModuleUtility.loadSync] nodemailer is required. Please install 'nodemailer'` - not Node's raw `Cannot find module`.
|
|
327
580
|
|
|
328
|
-
|
|
581
|
+
That fallback keeps the specifier invisible to `Bun.build`. Importing `@venizia/ignis/mail` for the Nodemailer transport no longer drags `mailgun.js` into your bundle, and the reverse holds too.
|
|
329
582
|
|
|
330
|
-
|
|
583
|
+
**A compiled application must pass `module`.** A `bun build --compile` binary ships without `node_modules`, so the runtime lookup has nothing to resolve against and the component throws that install hint at boot - with the peer sitting in `package.json`, which is no help because nothing put it inside the binary. The static import is what embeds it:
|
|
331
584
|
|
|
332
585
|
```typescript
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
}
|
|
586
|
+
import * as nodemailer from 'nodemailer';
|
|
587
|
+
|
|
588
|
+
this.bind({ key: MailKeys.MAIL_OPTIONS }).toValue({
|
|
589
|
+
provider: MailProviders.NODEMAILER,
|
|
590
|
+
config: { host, port, secure, auth },
|
|
591
|
+
module: nodemailer,
|
|
592
|
+
});
|
|
340
593
|
```
|
|
341
594
|
|
|
342
|
-
|
|
595
|
+
`module` is typed as the shape the transport calls (`createTransport` for Nodemailer, a constructor for Mailgun), so handing over the wrong thing is a compile error rather than a boot crash. Prefer it over [`ModuleUtility.register`](/references/utilities/module#compiled-binaries): the dependency arrives where it is used and cannot be defeated by binding order. Every IGNIS component that reaches an optional peer now takes one of these options - the [table in the module reference](/references/utilities/module#compiled-binaries) lists them.
|
|
343
596
|
|
|
344
|
-
|
|
597
|
+
**Nodemailer (`NodemailerTransportHelper`, extends `BaseHelper`):**
|
|
345
598
|
|
|
346
|
-
|
|
599
|
+
- `configure()` builds the transporter via `nodemailer.createTransport(config)`.
|
|
600
|
+
- `send()` maps `IMailMessage` fields to Nodemailer's mail options, joining array recipients with `', '`. It catches transport errors and returns `{ success: false, error }` - it never throws.
|
|
601
|
+
- `verify()` delegates to `transporter.verify()` (SMTP handshake). It catches errors and returns `false` - it never throws.
|
|
602
|
+
- `close()` calls `transporter.close()`.
|
|
347
603
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
```
|
|
604
|
+
**Mailgun (`MailgunTransportHelper`, extends `BaseHelper`):**
|
|
605
|
+
|
|
606
|
+
- `configure()` calls `validateConfig()` **before** building the client. `username`, `key`, and `domain` must all be present in `config`, or it throws `Invalid Mailgun configuration | Missing required keys: <keys>` (`INVALID_CONFIGURATION`, 500). This check runs even though `TMailgunConfig`'s type only requires `domain`.
|
|
607
|
+
- `send()` converts `IMailMessage` to Mailgun's format: `to` is coerced to an array, `replyTo` becomes `h:Reply-To`, and every custom header is prefixed `h:`. Attachments map to `{ filename, data: path ?? content ?? Buffer.from('') }`.
|
|
608
|
+
- It catches send errors and returns `{ success: false, error }` - it never throws.
|
|
609
|
+
- `verify()` sends a test message to `verify@<domain>` with `o:testmode: 'yes'`, since Mailgun has no dedicated verify endpoint. It catches errors and returns `false` - it never throws.
|
|
610
|
+
- No `close()` - the HTTP API is stateless.
|
|
356
611
|
|
|
357
|
-
|
|
612
|
+
**Custom transport:** set `provider: MailProviders.CUSTOM` and pass an object implementing `IMailTransport` as `config`. Useful for SendGrid, AWS SES, or a custom SMTP relay that the framework does not ship a helper for.
|
|
358
613
|
|
|
359
|
-
|
|
614
|
+
**Source:** [`helpers/transporters/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/transporters)
|
|
615
|
+
|
|
616
|
+
## Queue executor implementations
|
|
617
|
+
|
|
618
|
+
All three implement:
|
|
360
619
|
|
|
361
620
|
```typescript
|
|
362
621
|
interface IMailQueueExecutor {
|
|
@@ -365,49 +624,96 @@ interface IMailQueueExecutor {
|
|
|
365
624
|
}
|
|
366
625
|
```
|
|
367
626
|
|
|
368
|
-
|
|
627
|
+
**`DirectMailExecutorHelper`** extends `BaseHelper`. It calls the processor immediately, with no queue, and returns `{ queued: false, message: 'Email sent immediately (no queue)', result }`. If `enqueueVerificationEmail()` runs before `setProcessor()`, it throws `MailExecutorErrors.PROCESSOR_NOT_SET`.
|
|
628
|
+
|
|
629
|
+
**`InternalQueueMailExecutorHelper`** extends `BaseHelper` (constructor takes `IInternalQueueMailExecutorOpts`). It wraps `SequentialQueueHelper<IQueueJobPayload>` from `@venizia/ignis-helpers`, with `autoDispatch: true`.
|
|
630
|
+
|
|
631
|
+
- Job IDs: `job_<counter>_<timestamp>`.
|
|
632
|
+
- `options.delay > 0` schedules the enqueue itself via `setTimeout`, tracked in an internal `delayedJobs: Map<string, NodeJS.Timeout>`.
|
|
633
|
+
- Retry: on a thrown error, or `{ success: false }` from the processor, it retries up to `options.attempts ?? 3` times using `calculateBackoff()`:
|
|
634
|
+
|
|
635
|
+
| `backoff` config | Delay |
|
|
636
|
+
|---|---|
|
|
637
|
+
| Not set | `1000ms` fixed |
|
|
638
|
+
| `type: 'fixed'` | the raw `delay` |
|
|
639
|
+
| `type: 'exponential'` | `delay * 2 ** (attempt - 1)` |
|
|
640
|
+
|
|
641
|
+
- `close()` clears every pending delayed/retry `setTimeout`. Without that cleanup, a live timer keeps the event loop open past shutdown.
|
|
642
|
+
- No persistence across restarts.
|
|
643
|
+
|
|
644
|
+
**`BullMQMailExecutorHelper`** extends `BaseHelper` (constructor takes `IBullMQMailExecutorOpts`). It wraps `BullMQHelper` (`@venizia/ignis-helpers/bullmq`) and a `RedisSingleHelper` connection.
|
|
645
|
+
|
|
646
|
+
| Mode | Queue created | Workers created | Can enqueue | Can process |
|
|
647
|
+
|------|----------------|------------------|-------------|-------------|
|
|
648
|
+
| `'queue-only'` | Yes | No (`setProcessor()` skips worker creation, logs a warning) | Yes - processor not required | No |
|
|
649
|
+
| `'worker-only'` | No | Yes | No (throws) | Yes |
|
|
650
|
+
| `'both'` | Yes | Yes | Yes - processor required | Yes |
|
|
651
|
+
|
|
652
|
+
`setProcessor(processor, opts?)` is `async`. It clears all existing workers, then creates `opts?.numberOfWorkers ?? 1` workers, each with `concurrencyPerWorker ?? 5` and `lockDuration ?? 30000`ms. In `queue-only` mode it stores the processor and returns without creating any worker.
|
|
653
|
+
|
|
654
|
+
`enqueueVerificationEmail()` throws in three cases:
|
|
655
|
+
|
|
656
|
+
| Condition | Throws |
|
|
657
|
+
|---|---|
|
|
658
|
+
| Mode is `worker-only` | `Cannot enqueue jobs in worker-only mode...` |
|
|
659
|
+
| Queue was never created (should not happen in a queue-enabled mode) | `Queue helper not initialized...` |
|
|
660
|
+
| No processor set, and `mode !== 'queue-only'` | `PROCESSOR_NOT_SET` |
|
|
661
|
+
|
|
662
|
+
Its enqueue options: `attempts: options?.attempts ?? 3`, `backoff: { type: options?.backoff?.type ?? 'exponential', delay: options?.backoff?.delay ?? 1000 }`, `removeOnComplete: true`, `removeOnFail: false`.
|
|
663
|
+
|
|
664
|
+
**Dynamic workers** - manage the worker pool without a restart:
|
|
665
|
+
|
|
666
|
+
| Method | Behavior |
|
|
667
|
+
|---|---|
|
|
668
|
+
| `addWorker(opts)` | Requires a processor set first. Takes a unique identifier, default concurrency `5`, default lock duration `30000`ms |
|
|
669
|
+
| `removeWorker(index)` | Closes the worker, then splices it out. Returns `false` if `index` is out of range |
|
|
670
|
+
| `clearWorkers()` | Closes and empties every worker. Called internally by `setProcessor()` |
|
|
671
|
+
| `getWorkerCount()` | Returns the current worker count |
|
|
672
|
+
| `getMode()` | Returns the executor's `BullMQExecutorModes` value |
|
|
673
|
+
|
|
674
|
+
`close()` tears down workers, then the queue, then the Redis connection. Every step runs even if an earlier one failed, and every failure is collected into one thrown error at the end.
|
|
369
675
|
|
|
370
676
|
```typescript
|
|
677
|
+
interface IBullMQMailExecutorOpts {
|
|
678
|
+
redis: IRedisSingleHelperOptions; // from @venizia/ignis-helpers
|
|
679
|
+
queue: { identifier: string; name: string };
|
|
680
|
+
mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED, no default
|
|
681
|
+
}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
**Source:** [`helpers/executors/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/helpers/executors)
|
|
685
|
+
|
|
686
|
+
## Additional interfaces
|
|
687
|
+
|
|
688
|
+
```typescript
|
|
689
|
+
interface IMailSendResult {
|
|
690
|
+
success: boolean;
|
|
691
|
+
messageId?: string;
|
|
692
|
+
response?: any;
|
|
693
|
+
error?: string;
|
|
694
|
+
}
|
|
695
|
+
|
|
371
696
|
interface IMailQueueOptions {
|
|
372
697
|
priority?: number;
|
|
373
698
|
delay?: number;
|
|
374
699
|
attempts?: number;
|
|
375
|
-
backoff?: {
|
|
376
|
-
type: 'fixed' | 'exponential';
|
|
377
|
-
delay: number;
|
|
378
|
-
};
|
|
700
|
+
backoff?: { type: 'fixed' | 'exponential'; delay: number };
|
|
379
701
|
}
|
|
380
|
-
```
|
|
381
|
-
|
|
382
|
-
### IMailQueueResult
|
|
383
702
|
|
|
384
|
-
```typescript
|
|
385
703
|
interface IMailQueueResult {
|
|
386
704
|
jobId?: string;
|
|
387
|
-
queued: boolean;
|
|
705
|
+
queued: boolean; // false for direct execution, true for internal-queue and bullmq
|
|
388
706
|
message: string;
|
|
389
|
-
result?: IMailProcessorResult;
|
|
707
|
+
result?: IMailProcessorResult; // populated only for direct execution (synchronous processor)
|
|
390
708
|
}
|
|
391
|
-
```
|
|
392
|
-
|
|
393
|
-
Returned by `enqueueVerificationEmail()`. The `queued` field is `false` for direct execution, `true` for internal queue and BullMQ. The `result` field is populated only for direct execution (since the processor runs synchronously).
|
|
394
709
|
|
|
395
|
-
### IMailProcessorResult
|
|
396
|
-
|
|
397
|
-
```typescript
|
|
398
710
|
interface IMailProcessorResult {
|
|
399
711
|
success: boolean;
|
|
400
712
|
message: string;
|
|
401
713
|
expiresInMinutes: number;
|
|
402
714
|
nextResendAt?: string;
|
|
403
715
|
}
|
|
404
|
-
```
|
|
405
716
|
|
|
406
|
-
The return type of the processor function registered via `setProcessor()`.
|
|
407
|
-
|
|
408
|
-
### IVerificationData
|
|
409
|
-
|
|
410
|
-
```typescript
|
|
411
717
|
interface IVerificationData {
|
|
412
718
|
verificationCode: string;
|
|
413
719
|
codeGeneratedAt: string;
|
|
@@ -418,88 +724,50 @@ interface IVerificationData {
|
|
|
418
724
|
tokenExpiresAt: string;
|
|
419
725
|
lastCodeSentAt: string;
|
|
420
726
|
}
|
|
421
|
-
```
|
|
422
|
-
|
|
423
|
-
### IVerificationGenerationOptions
|
|
424
727
|
|
|
425
|
-
```typescript
|
|
426
728
|
interface IVerificationGenerationOptions {
|
|
427
|
-
codeLength: number;
|
|
729
|
+
codeLength: number; // all four fields required, no defaults
|
|
428
730
|
tokenBytes: number;
|
|
429
731
|
codeExpiryMinutes: number;
|
|
430
732
|
tokenExpiryHours: number;
|
|
431
733
|
}
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
All fields are required. Passed to `DefaultVerificationDataGenerator.generateVerificationData()`.
|
|
435
|
-
|
|
436
|
-
### IBullMQMailExecutorOpts
|
|
437
734
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
735
|
+
interface IQueueJobPayload {
|
|
736
|
+
id: string;
|
|
737
|
+
email: string;
|
|
738
|
+
options?: IMailQueueOptions;
|
|
739
|
+
attempts: number;
|
|
740
|
+
scheduledAt: number;
|
|
443
741
|
}
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
The `mode` field is **required** -- there is no default value. This is the config object for `IMailQueueExecutorConfig.bullmq`.
|
|
447
|
-
|
|
448
|
-
### BullMQ Dynamic Worker Management Methods
|
|
449
|
-
|
|
450
|
-
Dynamic worker management methods (on `BullMQMailExecutorHelper`, not on the interface):
|
|
451
|
-
|
|
452
|
-
- **`addWorker(opts)`** -- Adds a new BullMQ worker with configurable concurrency (default 5) and lock duration (default 30000ms). Requires `setProcessor()` to have been called first. Each worker gets a unique identifier
|
|
453
|
-
- **`removeWorker(index)`** -- Removes a worker by its array index. Calls `worker.close()` before removal. Returns `false` if the index is out of range
|
|
454
|
-
- **`clearWorkers()`** -- Closes all workers and empties the worker array. Called internally by `setProcessor()` before creating new workers
|
|
455
|
-
- **`getWorkerCount()`** -- Returns the current number of active workers
|
|
456
|
-
- **`getMode()`** -- Returns the current executor mode
|
|
457
|
-
|
|
458
|
-
**Extended `setProcessor()` signature (BullMQ only):**
|
|
459
742
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
async setProcessor(
|
|
464
|
-
processor: (email: string) => Promise<IMailProcessorResult>,
|
|
465
|
-
opts?: {
|
|
466
|
-
numberOfWorkers?: number; // default: 1
|
|
467
|
-
concurrencyPerWorker?: number; // default: 5
|
|
468
|
-
lockDuration?: number; // default: 30000 (ms)
|
|
469
|
-
},
|
|
470
|
-
): Promise<void>
|
|
743
|
+
interface IInternalQueueMailExecutorOpts {
|
|
744
|
+
identifier: string;
|
|
745
|
+
}
|
|
471
746
|
```
|
|
472
747
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
## Utility Functions
|
|
748
|
+
**Source:** [`common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/common/types.ts)
|
|
476
749
|
|
|
477
|
-
|
|
750
|
+
## Utility functions
|
|
478
751
|
|
|
479
752
|
```typescript
|
|
480
|
-
//
|
|
753
|
+
// type.utility.ts
|
|
481
754
|
function isMailTransport(value: AnyType): value is IMailTransport;
|
|
482
|
-
|
|
483
|
-
// Check if a value is valid TMailOptions
|
|
484
755
|
function isValidMailOptions(options: AnyType): options is TMailOptions;
|
|
485
|
-
```
|
|
486
|
-
|
|
487
|
-
`isMailTransport()` checks for `send` and `verify` as functions, and `close` as either a function or undefined.
|
|
488
|
-
|
|
489
|
-
`isValidMailOptions()` checks for a string `provider` field and a truthy `config` field.
|
|
490
756
|
|
|
491
|
-
|
|
757
|
+
// verification.utility.ts
|
|
758
|
+
function getExpiryTime(minutes: number): Date; // Date `minutes` minutes from now
|
|
759
|
+
function getExpiryTimeInHours(hours: number): Date; // Date `hours` hours from now
|
|
760
|
+
```
|
|
492
761
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
function getExpiryTime(minutes: number): Date;
|
|
762
|
+
- `isMailTransport()` checks that `send` and `verify` are functions, and that `close` is either a function or `undefined`.
|
|
763
|
+
- `isValidMailOptions()` checks that `provider` is a string and `config` is truthy. It does not validate the shape of `config` against the specific provider.
|
|
496
764
|
|
|
497
|
-
|
|
498
|
-
function getExpiryTimeInHours(hours: number): Date;
|
|
499
|
-
```
|
|
765
|
+
**Source:** [`utilities/`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/components/mail/utilities)
|
|
500
766
|
|
|
501
|
-
## See
|
|
767
|
+
## See also
|
|
502
768
|
|
|
503
|
-
- [
|
|
504
|
-
- [Usage & Examples](./usage) --
|
|
505
|
-
- [Error Reference](./errors) --
|
|
769
|
+
- [Overview](./) -- quick start, imports, common configuration tasks
|
|
770
|
+
- [Usage & Examples](./usage) -- sending emails, templates, queue executors, verification generators
|
|
771
|
+
- [Error Reference](./errors) -- error codes and troubleshooting
|
|
772
|
+
- [Queue Helper](/extensions/helpers/queue/) -- `SequentialQueueHelper` and `BullMQHelper`
|
|
773
|
+
- [Redis Helper](/extensions/helpers/redis/) -- `RedisSingleHelper`, `IRedisSingleHelperOptions`
|