@venizia/ignis-docs 0.0.5 → 0.0.6-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 +1 -1
- package/wiki/best-practices/architecture-decisions.md +0 -8
- package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
- package/wiki/best-practices/performance-optimization.md +3 -3
- package/wiki/best-practices/security-guidelines.md +2 -2
- package/wiki/best-practices/troubleshooting-tips.md +1 -1
- package/wiki/guides/core-concepts/components-guide.md +1 -1
- package/wiki/guides/core-concepts/components.md +2 -2
- package/wiki/guides/core-concepts/dependency-injection.md +1 -1
- package/wiki/guides/core-concepts/services.md +1 -1
- package/wiki/guides/tutorials/building-a-crud-api.md +1 -1
- package/wiki/guides/tutorials/ecommerce-api.md +2 -2
- package/wiki/guides/tutorials/realtime-chat.md +6 -6
- package/wiki/guides/tutorials/testing.md +1 -1
- package/wiki/references/base/bootstrapping.md +0 -2
- package/wiki/references/base/components.md +2 -2
- package/wiki/references/base/controllers.md +0 -1
- package/wiki/references/base/datasources.md +1 -1
- package/wiki/references/base/dependency-injection.md +1 -1
- package/wiki/references/base/filter-system/quick-reference.md +0 -14
- package/wiki/references/base/middlewares.md +0 -8
- package/wiki/references/base/providers.md +0 -9
- package/wiki/references/base/services.md +0 -1
- package/wiki/references/components/authentication/api.md +444 -0
- package/wiki/references/components/authentication/errors.md +177 -0
- package/wiki/references/components/authentication/index.md +571 -0
- package/wiki/references/components/authentication/usage.md +781 -0
- package/wiki/references/components/health-check.md +292 -103
- package/wiki/references/components/index.md +14 -12
- package/wiki/references/components/mail/api.md +505 -0
- package/wiki/references/components/mail/errors.md +176 -0
- package/wiki/references/components/mail/index.md +535 -0
- package/wiki/references/components/mail/usage.md +404 -0
- package/wiki/references/components/request-tracker.md +229 -25
- package/wiki/references/components/socket-io/api.md +1051 -0
- package/wiki/references/components/socket-io/errors.md +119 -0
- package/wiki/references/components/socket-io/index.md +410 -0
- package/wiki/references/components/socket-io/usage.md +322 -0
- package/wiki/references/components/static-asset/api.md +261 -0
- package/wiki/references/components/static-asset/errors.md +89 -0
- package/wiki/references/components/static-asset/index.md +617 -0
- package/wiki/references/components/static-asset/usage.md +364 -0
- package/wiki/references/components/swagger.md +390 -110
- package/wiki/references/components/template/api-page.md +125 -0
- package/wiki/references/components/template/errors-page.md +100 -0
- package/wiki/references/components/template/index.md +104 -0
- package/wiki/references/components/template/setup-page.md +134 -0
- package/wiki/references/components/template/single-page.md +132 -0
- package/wiki/references/components/template/usage-page.md +127 -0
- package/wiki/references/components/websocket/api.md +508 -0
- package/wiki/references/components/websocket/errors.md +123 -0
- package/wiki/references/components/websocket/index.md +453 -0
- package/wiki/references/components/websocket/usage.md +475 -0
- package/wiki/references/helpers/cron/index.md +224 -0
- package/wiki/references/helpers/crypto/index.md +537 -0
- package/wiki/references/helpers/env/index.md +214 -0
- package/wiki/references/helpers/error/index.md +232 -0
- package/wiki/references/helpers/index.md +16 -15
- package/wiki/references/helpers/inversion/index.md +608 -0
- package/wiki/references/helpers/logger/index.md +600 -0
- package/wiki/references/helpers/network/api.md +986 -0
- package/wiki/references/helpers/network/index.md +620 -0
- package/wiki/references/helpers/queue/index.md +589 -0
- package/wiki/references/helpers/redis/index.md +495 -0
- package/wiki/references/helpers/socket-io/api.md +497 -0
- package/wiki/references/helpers/socket-io/index.md +513 -0
- package/wiki/references/helpers/storage/api.md +705 -0
- package/wiki/references/helpers/storage/index.md +583 -0
- package/wiki/references/helpers/template/index.md +66 -0
- package/wiki/references/helpers/template/single-page.md +126 -0
- package/wiki/references/helpers/testing/index.md +510 -0
- package/wiki/references/helpers/types/index.md +512 -0
- package/wiki/references/helpers/uid/index.md +272 -0
- package/wiki/references/helpers/websocket/api.md +736 -0
- package/wiki/references/helpers/websocket/index.md +574 -0
- package/wiki/references/helpers/worker-thread/index.md +470 -0
- package/wiki/references/quick-reference.md +3 -18
- package/wiki/references/utilities/jsx.md +1 -8
- package/wiki/references/utilities/statuses.md +0 -7
- package/wiki/references/components/authentication.md +0 -476
- package/wiki/references/components/mail.md +0 -687
- package/wiki/references/components/socket-io.md +0 -562
- package/wiki/references/components/static-asset.md +0 -1277
- package/wiki/references/helpers/cron.md +0 -108
- package/wiki/references/helpers/crypto.md +0 -132
- package/wiki/references/helpers/env.md +0 -83
- package/wiki/references/helpers/error.md +0 -97
- package/wiki/references/helpers/inversion.md +0 -176
- package/wiki/references/helpers/logger.md +0 -296
- package/wiki/references/helpers/network.md +0 -396
- package/wiki/references/helpers/queue.md +0 -150
- package/wiki/references/helpers/redis.md +0 -142
- package/wiki/references/helpers/socket-io.md +0 -932
- package/wiki/references/helpers/storage.md +0 -665
- package/wiki/references/helpers/testing.md +0 -133
- package/wiki/references/helpers/types.md +0 -167
- package/wiki/references/helpers/uid.md +0 -167
- package/wiki/references/helpers/worker-thread.md +0 -178
|
@@ -0,0 +1,535 @@
|
|
|
1
|
+
# Mail -- Setup & Configuration
|
|
2
|
+
|
|
3
|
+
> Flexible email sending system with support for multiple transports (Nodemailer, Mailgun, custom), template-based rendering with mustache-style variable syntax, and queue-based processing via Direct, Internal Queue, or BullMQ executors.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
| Item | Value |
|
|
8
|
+
|------|-------|
|
|
9
|
+
| **Package** | `@venizia/ignis` |
|
|
10
|
+
| **Class** | `MailComponent` |
|
|
11
|
+
| **Runtimes** | Both |
|
|
12
|
+
|
|
13
|
+
### Key Components
|
|
14
|
+
|
|
15
|
+
| Component | Purpose |
|
|
16
|
+
| --------- | ------- |
|
|
17
|
+
| **MailComponent** | Main component registering mail services, transporters, and executors |
|
|
18
|
+
| **MailService** | Core service for sending emails, batch emails, and template-based emails |
|
|
19
|
+
| **TemplateEngineService** | Simple template engine with <code v-pre>{{variable}}</code> syntax |
|
|
20
|
+
| **NodemailerTransportHelper** | Nodemailer-based email transport implementation |
|
|
21
|
+
| **MailgunTransportHelper** | Mailgun API-based email transport implementation |
|
|
22
|
+
| **DirectMailExecutorHelper** | Execute email sending immediately without queue |
|
|
23
|
+
| **InternalQueueMailExecutorHelper** | Queue emails using in-memory queue |
|
|
24
|
+
| **BullMQMailExecutorHelper** | Queue emails using BullMQ for distributed processing |
|
|
25
|
+
| **MailTransportProvider** | Factory provider that creates transport instances based on configuration |
|
|
26
|
+
| **MailQueueExecutorProvider** | Factory provider that creates queue executor instances based on configuration |
|
|
27
|
+
| **NumericCodeGenerator** | Generates cryptographically random numeric verification codes |
|
|
28
|
+
| **RandomTokenGenerator** | Generates cryptographically random base64url tokens |
|
|
29
|
+
| **DefaultVerificationDataGenerator** | Composes code + token generators into full verification data objects |
|
|
30
|
+
|
|
31
|
+
### Transport Providers
|
|
32
|
+
|
|
33
|
+
| Provider | Value | When to Use |
|
|
34
|
+
| -------- | ----- | ----------- |
|
|
35
|
+
| **Nodemailer** | `MailProviders.NODEMAILER` | SMTP-based email sending (Gmail, SendGrid, etc.) |
|
|
36
|
+
| **Mailgun** | `MailProviders.MAILGUN` | Mailgun API for transactional emails |
|
|
37
|
+
| **Custom** | `MailProviders.CUSTOM` | Custom transport implementation |
|
|
38
|
+
|
|
39
|
+
### Queue Executor Types
|
|
40
|
+
|
|
41
|
+
| Type | Value | When to Use |
|
|
42
|
+
| ---- | ----- | ----------- |
|
|
43
|
+
| **Direct** | `'direct'` | No queue, send immediately |
|
|
44
|
+
| **Internal Queue** | `'internal-queue'` | In-memory queue for simple use cases |
|
|
45
|
+
| **BullMQ** | `'bullmq'` | Redis-backed queue for distributed systems |
|
|
46
|
+
|
|
47
|
+
#### Import Paths
|
|
48
|
+
```typescript
|
|
49
|
+
import {
|
|
50
|
+
MailComponent,
|
|
51
|
+
MailKeys,
|
|
52
|
+
MailProviders,
|
|
53
|
+
MailErrorCodes,
|
|
54
|
+
MailDefaults,
|
|
55
|
+
MailQueueExecutorTypes,
|
|
56
|
+
BullMQExecutorModes,
|
|
57
|
+
MailService,
|
|
58
|
+
TemplateEngineService,
|
|
59
|
+
NumericCodeGenerator,
|
|
60
|
+
RandomTokenGenerator,
|
|
61
|
+
DefaultVerificationDataGenerator,
|
|
62
|
+
MailTransportProvider,
|
|
63
|
+
MailQueueExecutorProvider,
|
|
64
|
+
} from '@venizia/ignis/mail';
|
|
65
|
+
|
|
66
|
+
import type {
|
|
67
|
+
TMailOptions,
|
|
68
|
+
IBaseMailOptions,
|
|
69
|
+
INodemailerMailOptions,
|
|
70
|
+
IMailgunMailOptions,
|
|
71
|
+
ICustomMailOptions,
|
|
72
|
+
IGenericMailOptions,
|
|
73
|
+
IMailService,
|
|
74
|
+
IMailTemplateEngine,
|
|
75
|
+
IMailMessage,
|
|
76
|
+
IMailSendResult,
|
|
77
|
+
IMailTransport,
|
|
78
|
+
IMailAttachment,
|
|
79
|
+
IMailQueueExecutor,
|
|
80
|
+
IMailQueueExecutorConfig,
|
|
81
|
+
IMailQueueOptions,
|
|
82
|
+
IMailQueueResult,
|
|
83
|
+
IMailProcessorResult,
|
|
84
|
+
ITemplate,
|
|
85
|
+
IVerificationCodeGenerator,
|
|
86
|
+
IVerificationTokenGenerator,
|
|
87
|
+
IVerificationDataGenerator,
|
|
88
|
+
IVerificationData,
|
|
89
|
+
IVerificationGenerationOptions,
|
|
90
|
+
TMailProvider,
|
|
91
|
+
TNodemailerConfig,
|
|
92
|
+
TMailgunConfig,
|
|
93
|
+
} from '@venizia/ignis/mail';
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Setup
|
|
97
|
+
|
|
98
|
+
The recommended approach is to create a wrapper component that binds the mail options and queue executor config, then registers `MailComponent` internally.
|
|
99
|
+
|
|
100
|
+
### Step 1: Bind Configuration
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
// src/components/mail/component.ts
|
|
104
|
+
import {
|
|
105
|
+
BaseApplication,
|
|
106
|
+
BaseComponent,
|
|
107
|
+
Binding,
|
|
108
|
+
CoreBindings,
|
|
109
|
+
inject,
|
|
110
|
+
applicationEnvironment,
|
|
111
|
+
toBoolean,
|
|
112
|
+
} from '@venizia/ignis';
|
|
113
|
+
import { MailComponent, MailKeys, MailProviders } from '@venizia/ignis/mail';
|
|
114
|
+
|
|
115
|
+
export class NodemailerComponent extends BaseComponent {
|
|
116
|
+
constructor(
|
|
117
|
+
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
|
|
118
|
+
protected application: BaseApplication,
|
|
119
|
+
) {
|
|
120
|
+
super({
|
|
121
|
+
scope: NodemailerComponent.name,
|
|
122
|
+
initDefault: { enable: true, container: application },
|
|
123
|
+
bindings: {
|
|
124
|
+
// Configure mail transport options
|
|
125
|
+
[MailKeys.MAIL_OPTIONS]: Binding.bind({
|
|
126
|
+
key: MailKeys.MAIL_OPTIONS,
|
|
127
|
+
}).toValue({
|
|
128
|
+
provider: MailProviders.NODEMAILER,
|
|
129
|
+
from: 'noreply@example.com',
|
|
130
|
+
fromName: 'Example App',
|
|
131
|
+
config: {
|
|
132
|
+
host: applicationEnvironment.get<string>('APP_ENV_MAIL_HOST') ?? 'smtp.gmail.com',
|
|
133
|
+
port: +(applicationEnvironment.get<number>('APP_ENV_MAIL_PORT') ?? 465),
|
|
134
|
+
secure: toBoolean(applicationEnvironment.get<boolean>('APP_ENV_MAIL_SECURE') ?? true),
|
|
135
|
+
auth: {
|
|
136
|
+
type: 'oauth2',
|
|
137
|
+
user: applicationEnvironment.get<string>('APP_ENV_MAIL_USER'),
|
|
138
|
+
clientId: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_ID'),
|
|
139
|
+
clientSecret: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_SECRET'),
|
|
140
|
+
refreshToken: applicationEnvironment.get<string>('APP_ENV_MAIL_REFRESH_TOKEN'),
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
}),
|
|
144
|
+
// Configure queue executor
|
|
145
|
+
[MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG]: Binding.bind({
|
|
146
|
+
key: MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG,
|
|
147
|
+
}).toValue({
|
|
148
|
+
type: 'internal-queue',
|
|
149
|
+
internalQueue: {
|
|
150
|
+
identifier: 'mail-internal-queue',
|
|
151
|
+
},
|
|
152
|
+
}),
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
override async binding(): Promise<void> {
|
|
158
|
+
this.logger.info('[binding] Binding mail component...');
|
|
159
|
+
|
|
160
|
+
// Register the core MailComponent
|
|
161
|
+
this.application.component(MailComponent);
|
|
162
|
+
|
|
163
|
+
this.logger.info('[binding] Mail component initialized successfully');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Step 2: Register Component
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// src/application.ts
|
|
172
|
+
import { BaseApplication, ValueOrPromise } from '@venizia/ignis';
|
|
173
|
+
import { NodemailerComponent } from './components/mail/component';
|
|
174
|
+
|
|
175
|
+
export class Application extends BaseApplication {
|
|
176
|
+
preConfigure(): ValueOrPromise<void> {
|
|
177
|
+
// Register the mail component
|
|
178
|
+
this.component(NodemailerComponent);
|
|
179
|
+
|
|
180
|
+
// ... other components
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Configuration
|
|
186
|
+
|
|
187
|
+
### Transport Options
|
|
188
|
+
|
|
189
|
+
The `TMailOptions` configuration determines which email transport provider is used and how it's configured. It is a discriminated union of four variants.
|
|
190
|
+
|
|
191
|
+
**Nodemailer SMTP example:**
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
{
|
|
195
|
+
provider: MailProviders.NODEMAILER,
|
|
196
|
+
from: 'noreply@example.com',
|
|
197
|
+
fromName: 'Example App',
|
|
198
|
+
config: {
|
|
199
|
+
host: 'smtp.gmail.com',
|
|
200
|
+
port: 465,
|
|
201
|
+
secure: true,
|
|
202
|
+
auth: {
|
|
203
|
+
user: 'your-email@gmail.com',
|
|
204
|
+
pass: 'your-app-password',
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Simple SMTP Authentication (e.g., Gmail with app password):**
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
// Simple SMTP Authentication (e.g., Gmail with app password)
|
|
214
|
+
this.bind<TMailOptions>({ key: MailBindingKeys.MAIL_OPTIONS }).toValue({
|
|
215
|
+
provider: 'nodemailer',
|
|
216
|
+
from: 'noreply@example.com',
|
|
217
|
+
fromName: 'My App',
|
|
218
|
+
config: {
|
|
219
|
+
host: 'smtp.gmail.com',
|
|
220
|
+
port: 465,
|
|
221
|
+
secure: true,
|
|
222
|
+
auth: {
|
|
223
|
+
user: process.env.APP_ENV_MAIL_USER,
|
|
224
|
+
pass: process.env.APP_ENV_MAIL_PASS,
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Mailgun example:**
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
{
|
|
234
|
+
provider: MailProviders.MAILGUN,
|
|
235
|
+
from: 'noreply@example.com',
|
|
236
|
+
fromName: 'Example App',
|
|
237
|
+
config: {
|
|
238
|
+
apiKey: process.env.MAILGUN_API_KEY,
|
|
239
|
+
domain: 'mg.example.com',
|
|
240
|
+
host: 'api.eu.mailgun.net', // Optional: EU region
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Generic provider example:**
|
|
246
|
+
|
|
247
|
+
The `IGenericMailOptions` variant allows any arbitrary provider string with a `Record<string, AnyType>` config. This is the catch-all for providers not covered by the named variants:
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
{
|
|
251
|
+
provider: 'sendgrid',
|
|
252
|
+
from: 'noreply@example.com',
|
|
253
|
+
config: {
|
|
254
|
+
apiKey: process.env.SENDGRID_API_KEY,
|
|
255
|
+
// Any key-value pairs accepted
|
|
256
|
+
},
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
> [!WARNING]
|
|
261
|
+
> The `IGenericMailOptions` variant will fall through to the `default` case in `MailTransportProvider` and throw `Unsupported mail provider: <provider>` unless the transport provider is replaced with a custom one that handles the provider string. This variant exists for extensibility -- you must bind a custom `MailTransportProvider` that knows how to handle your provider string.
|
|
262
|
+
|
|
263
|
+
**OAuth2 with environment variables:**
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
{
|
|
267
|
+
provider: MailProviders.NODEMAILER,
|
|
268
|
+
from: applicationEnvironment.get<string>('APP_ENV_MAIL_FROM') ?? 'noreply@example.com',
|
|
269
|
+
fromName: applicationEnvironment.get<string>('APP_ENV_MAIL_FROM_NAME') ?? 'Example App',
|
|
270
|
+
config: {
|
|
271
|
+
host: applicationEnvironment.get<string>('APP_ENV_MAIL_HOST') ?? 'smtp.gmail.com',
|
|
272
|
+
port: +(applicationEnvironment.get<number>('APP_ENV_MAIL_PORT') ?? 465),
|
|
273
|
+
secure: toBoolean(applicationEnvironment.get<boolean>('APP_ENV_MAIL_SECURE') ?? true),
|
|
274
|
+
auth: {
|
|
275
|
+
type: 'oauth2',
|
|
276
|
+
user: applicationEnvironment.get<string>('APP_ENV_MAIL_USER'),
|
|
277
|
+
clientId: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_ID'),
|
|
278
|
+
clientSecret: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_SECRET'),
|
|
279
|
+
refreshToken: applicationEnvironment.get<string>('APP_ENV_MAIL_REFRESH_TOKEN'),
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Example `.env` file for Nodemailer with OAuth2:**
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
APP_ENV_MAIL_HOST=smtp.gmail.com
|
|
289
|
+
APP_ENV_MAIL_PORT=465
|
|
290
|
+
APP_ENV_MAIL_SECURE=true
|
|
291
|
+
APP_ENV_MAIL_USER=your-email@gmail.com
|
|
292
|
+
APP_ENV_MAIL_CLIENT_ID=your-oauth2-client-id
|
|
293
|
+
APP_ENV_MAIL_CLIENT_SECRET=your-oauth2-client-secret
|
|
294
|
+
APP_ENV_MAIL_REFRESH_TOKEN=your-oauth2-refresh-token
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
> [!TIP]
|
|
298
|
+
> For Gmail OAuth2, follow [Google's OAuth2 setup guide](https://developers.google.com/gmail/api/auth/web-server) to obtain client ID, secret, and refresh token.
|
|
299
|
+
|
|
300
|
+
### Queue Executor Options
|
|
301
|
+
|
|
302
|
+
The `IMailQueueExecutorConfig` configuration determines how emails are queued and processed.
|
|
303
|
+
|
|
304
|
+
**Direct execution (no queue):**
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
{
|
|
308
|
+
type: 'direct',
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Internal queue (in-memory):**
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
{
|
|
316
|
+
type: 'internal-queue',
|
|
317
|
+
internalQueue: {
|
|
318
|
+
identifier: 'mail-internal-queue',
|
|
319
|
+
},
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
**BullMQ (Redis-backed):**
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
{
|
|
327
|
+
type: 'bullmq',
|
|
328
|
+
bullmq: {
|
|
329
|
+
redis: {
|
|
330
|
+
host: 'localhost',
|
|
331
|
+
port: 6379,
|
|
332
|
+
password: 'your-redis-password',
|
|
333
|
+
},
|
|
334
|
+
queue: {
|
|
335
|
+
identifier: 'mail-queue',
|
|
336
|
+
name: 'mail-queue',
|
|
337
|
+
},
|
|
338
|
+
mode: 'both', // 'queue-only', 'worker-only', or 'both'
|
|
339
|
+
},
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
> [!NOTE]
|
|
344
|
+
> - **`'queue-only'`** -- Only enqueues jobs, does not process them (useful for web servers that offload to workers)
|
|
345
|
+
> - **`'worker-only'`** -- Only processes jobs, does not enqueue (useful for dedicated worker processes)
|
|
346
|
+
> - **`'both'`** -- Both enqueues and processes jobs (simplest setup for single-instance apps)
|
|
347
|
+
|
|
348
|
+
> [!NOTE]
|
|
349
|
+
> Choose the right queue executor for your environment:
|
|
350
|
+
> - **`direct`** -- Development or low-volume applications. No queueing overhead.
|
|
351
|
+
> - **`internal-queue`** -- Single-instance applications with moderate volume. In-memory queue with retry support.
|
|
352
|
+
> - **`bullmq`** -- Distributed systems or high-volume applications. Redis-backed with configurable concurrency, priority, and backoff.
|
|
353
|
+
|
|
354
|
+
#### BullMQ Dynamic Worker Management
|
|
355
|
+
|
|
356
|
+
The `BullMQMailExecutorHelper` supports dynamic worker scaling at runtime. Workers can be added and removed without restarting the application:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
const executor = this.application.get<BullMQMailExecutorHelper>({
|
|
360
|
+
key: MailKeys.MAIL_QUEUE_EXECUTOR_INSTANCE,
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Add a new worker with custom concurrency
|
|
364
|
+
executor.addWorker({
|
|
365
|
+
workerIdentifier: 'mail-queue-worker-extra',
|
|
366
|
+
concurrency: 10,
|
|
367
|
+
lockDuration: 60000, // 60 seconds
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Check current worker count
|
|
371
|
+
const count = executor.getWorkerCount(); // e.g. 2
|
|
372
|
+
|
|
373
|
+
// Check current mode
|
|
374
|
+
const mode = executor.getMode(); // e.g. 'both'
|
|
375
|
+
|
|
376
|
+
// Remove a specific worker by index
|
|
377
|
+
await executor.removeWorker(1);
|
|
378
|
+
|
|
379
|
+
// Remove all workers
|
|
380
|
+
await executor.clearWorkers();
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The `setProcessor()` method on BullMQ also accepts an optional second argument for worker configuration:
|
|
384
|
+
|
|
385
|
+
```typescript
|
|
386
|
+
await executor.setProcessor(
|
|
387
|
+
async (email: string) => {
|
|
388
|
+
// your processing logic
|
|
389
|
+
return { success: true, message: 'Sent', expiresInMinutes: 10 };
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
numberOfWorkers: 3, // Spawn 3 workers (default: 1)
|
|
393
|
+
concurrencyPerWorker: 10, // Each worker handles 10 concurrent jobs (default: 5)
|
|
394
|
+
lockDuration: 60000, // Job lock duration in ms (default: 30000)
|
|
395
|
+
},
|
|
396
|
+
);
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
#### Full Transport Options Interface
|
|
400
|
+
|
|
401
|
+
The `TMailOptions` union type has four variants. All extend `IBaseMailOptions`:
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
interface IBaseMailOptions {
|
|
405
|
+
from?: string;
|
|
406
|
+
fromName?: string;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
interface INodemailerMailOptions extends IBaseMailOptions {
|
|
410
|
+
provider: 'nodemailer';
|
|
411
|
+
config: TNodemailerConfig; // SMTPTransport | SMTPTransport.Options | string
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
interface IMailgunMailOptions extends IBaseMailOptions {
|
|
415
|
+
provider: 'mailgun';
|
|
416
|
+
config: TMailgunConfig; // { domain: string; [key: string]: any }
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
interface ICustomMailOptions extends IBaseMailOptions {
|
|
420
|
+
provider: 'custom';
|
|
421
|
+
config: IMailTransport; // Must implement send() and verify()
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
interface IGenericMailOptions extends IBaseMailOptions {
|
|
425
|
+
provider: string;
|
|
426
|
+
config: Record<string, AnyType>;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
type TMailOptions =
|
|
430
|
+
| INodemailerMailOptions
|
|
431
|
+
| IMailgunMailOptions
|
|
432
|
+
| ICustomMailOptions
|
|
433
|
+
| IGenericMailOptions;
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
#### Full Queue Executor Config Interface
|
|
437
|
+
```typescript
|
|
438
|
+
interface IMailQueueExecutorConfig {
|
|
439
|
+
type: TConstValue<typeof MailQueueExecutorTypes>; // 'direct' | 'internal-queue' | 'bullmq'
|
|
440
|
+
internalQueue?: {
|
|
441
|
+
identifier: string;
|
|
442
|
+
};
|
|
443
|
+
bullmq?: {
|
|
444
|
+
redis: IRedisHelperOptions;
|
|
445
|
+
queue: {
|
|
446
|
+
identifier: string;
|
|
447
|
+
name: string;
|
|
448
|
+
};
|
|
449
|
+
mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED: 'queue-only' | 'worker-only' | 'both'
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
> [!IMPORTANT]
|
|
455
|
+
> The `bullmq.mode` field is **required** when `type` is `'bullmq'`. There is no default value -- you must explicitly choose `'queue-only'`, `'worker-only'`, or `'both'`.
|
|
456
|
+
|
|
457
|
+
#### Nodemailer Transport Capabilities
|
|
458
|
+
- Basic SMTP authentication (`user`/`pass`)
|
|
459
|
+
- OAuth2 authentication (client ID, secret, refresh token)
|
|
460
|
+
- TLS/SSL connections
|
|
461
|
+
- Custom SMTP headers
|
|
462
|
+
- Connection pooling
|
|
463
|
+
- Attachment handling (file path, buffer, stream)
|
|
464
|
+
- HTML and plain text content
|
|
465
|
+
- SMTP connection verification via `verify()` method
|
|
466
|
+
- Peer dependency validation via `validateModule()` (requires `nodemailer` to be installed)
|
|
467
|
+
|
|
468
|
+
#### Mailgun Transport Capabilities
|
|
469
|
+
- US and EU regional endpoints
|
|
470
|
+
- API key authentication
|
|
471
|
+
- HTML and plain text emails
|
|
472
|
+
- Inline attachments with CID
|
|
473
|
+
- Custom headers (auto-prefixed with `h:`)
|
|
474
|
+
- Batch sending via Mailgun's API
|
|
475
|
+
- Test mode verification via `verify()` method (uses `o:testmode` flag)
|
|
476
|
+
- Peer dependency validation via `validateModule()` (requires `mailgun.js` to be installed)
|
|
477
|
+
|
|
478
|
+
### Constants
|
|
479
|
+
|
|
480
|
+
| Constant | Value | Description |
|
|
481
|
+
|----------|-------|-------------|
|
|
482
|
+
| `MailDefaults.BATCH_CONCURRENCY` | `5` | Default concurrent sends in batch |
|
|
483
|
+
| `MailQueueExecutorTypes.DIRECT` | `'direct'` | Immediate execution |
|
|
484
|
+
| `MailQueueExecutorTypes.INTERNAL_QUEUE` | `'internal-queue'` | In-memory queue |
|
|
485
|
+
| `MailQueueExecutorTypes.BULLMQ` | `'bullmq'` | Redis-backed queue |
|
|
486
|
+
| `BullMQExecutorModes.QUEUE_ONLY` | `'queue-only'` | Producer only (enqueue) |
|
|
487
|
+
| `BullMQExecutorModes.WORKER_ONLY` | `'worker-only'` | Consumer only (process) |
|
|
488
|
+
| `BullMQExecutorModes.BOTH` | `'both'` | Full duplex (produce + consume) |
|
|
489
|
+
|
|
490
|
+
#### MailErrorCodes
|
|
491
|
+
|
|
492
|
+
| Constant | Value | Description |
|
|
493
|
+
|----------|-------|-------------|
|
|
494
|
+
| `MailErrorCodes.INVALID_CONFIGURATION` | `'MAIL_INVALID_CONFIGURATION'` | Invalid or missing configuration (transport, template engine, subject, body) |
|
|
495
|
+
| `MailErrorCodes.SEND_FAILED` | `'MAIL_SEND_FAILED'` | Single email send failed |
|
|
496
|
+
| `MailErrorCodes.VERIFICATION_FAILED` | `'MAIL_VERIFICATION_FAILED'` | Transport connection verification failed |
|
|
497
|
+
| `MailErrorCodes.INVALID_RECIPIENT` | `'MAIL_INVALID_RECIPIENT'` | Missing or empty recipient address |
|
|
498
|
+
| `MailErrorCodes.BATCH_SEND_FAILED` | `'MAIL_BATCH_SEND_FAILED'` | Batch email operation failed |
|
|
499
|
+
| `MailErrorCodes.TEMPLATE_NOT_FOUND` | `'TEMPLATE_NOT_FOUND'` | Template name not found in registry |
|
|
500
|
+
|
|
501
|
+
#### MailQueueExecutorTypes Validation
|
|
502
|
+
|
|
503
|
+
Both `MailQueueExecutorTypes` and `BullMQExecutorModes` include a `SCHEME_SET` / `MODE_SET` and an `isValid()` static method for runtime validation:
|
|
504
|
+
|
|
505
|
+
```typescript
|
|
506
|
+
MailQueueExecutorTypes.isValid('bullmq'); // true
|
|
507
|
+
MailQueueExecutorTypes.isValid('unknown'); // false
|
|
508
|
+
BullMQExecutorModes.isValid('both'); // true
|
|
509
|
+
BullMQExecutorModes.isValid('invalid'); // false
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
## Binding Keys
|
|
513
|
+
|
|
514
|
+
| Key | Constant | Type | Required | Default |
|
|
515
|
+
|-----|----------|------|----------|---------|
|
|
516
|
+
| `@app/components/mail/options` | `MailKeys.MAIL_OPTIONS` | `TMailOptions` | Yes | -- |
|
|
517
|
+
| `@app/components/mail/queue/executor-config` | `MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG` | `IMailQueueExecutorConfig` | Yes | -- |
|
|
518
|
+
| `@app/components/mail/service` | `MailKeys.MAIL_SERVICE` | `IMailService` | No | `MailService` (singleton) |
|
|
519
|
+
| `@app/components/mail/services/template-engine` | `MailKeys.MAIL_TEMPLATE_ENGINE` | `IMailTemplateEngine` | No | `TemplateEngineService` (singleton) |
|
|
520
|
+
| `@app/components/mail/transport-provider` | `MailKeys.MAIL_TRANSPORT_PROVIDER` | `TGetMailTransportFn` | No | `MailTransportProvider` (singleton) |
|
|
521
|
+
| `@app/components/mail/transport-instance` | `MailKeys.MAIL_TRANSPORT_INSTANCE` | `IMailTransport` | No | Created by component |
|
|
522
|
+
| `@app/components/mail/queue-executor-provider` | `MailKeys.MAIL_QUEUE_EXECUTOR_PROVIDER` | `TGetMailQueueExecutorFn` | No | `MailQueueExecutorProvider` (singleton) |
|
|
523
|
+
| `@app/components/mail/queue-executor-instance` | `MailKeys.MAIL_QUEUE_EXECUTOR_INSTANCE` | `IMailQueueExecutor` | No | Created by component |
|
|
524
|
+
| `@app/components/mail/verification/code-generator` | `MailKeys.MAIL_VERIFICATION_CODE_GENERATOR` | `IVerificationCodeGenerator` | No | `NumericCodeGenerator` |
|
|
525
|
+
| `@app/components/mail/verification/token-generator` | `MailKeys.MAIL_VERIFICATION_TOKEN_GENERATOR` | `IVerificationTokenGenerator` | No | `RandomTokenGenerator` |
|
|
526
|
+
| `@app/components/mail/verification/data-generator` | `MailKeys.MAIL_VERIFICATION_DATA_GENERATOR` | `IVerificationDataGenerator` | No | `DefaultVerificationDataGenerator` |
|
|
527
|
+
|
|
528
|
+
> [!IMPORTANT]
|
|
529
|
+
> Both `MailKeys.MAIL_OPTIONS` and `MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG` must be bound before registering `MailComponent`. The component throws an error if `MAIL_OPTIONS` is not found.
|
|
530
|
+
|
|
531
|
+
## See Also
|
|
532
|
+
|
|
533
|
+
- [Usage & Examples](./usage) -- Sending emails, templates, queue executors, and verification
|
|
534
|
+
- [API Reference](./api) -- Architecture, interfaces, and internals
|
|
535
|
+
- [Error Reference](./errors) -- Error codes and troubleshooting
|