@venizia/ignis-docs 0.0.5 → 0.0.6-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/package.json +1 -1
- package/wiki/best-practices/architectural-patterns.md +0 -2
- 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/code-style-standards/index.md +0 -1
- package/wiki/best-practices/code-style-standards/tooling.md +0 -3
- package/wiki/best-practices/contribution-workflow.md +12 -12
- package/wiki/best-practices/index.md +4 -14
- 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/application/bootstrapping.md +6 -7
- 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 +4 -5
- package/wiki/guides/core-concepts/persistent/datasources.md +4 -5
- package/wiki/guides/core-concepts/services.md +1 -1
- package/wiki/guides/get-started/5-minute-quickstart.md +4 -5
- package/wiki/guides/get-started/philosophy.md +12 -24
- package/wiki/guides/index.md +2 -9
- package/wiki/guides/reference/mcp-docs-server.md +13 -13
- package/wiki/guides/tutorials/building-a-crud-api.md +10 -10
- package/wiki/guides/tutorials/complete-installation.md +11 -12
- package/wiki/guides/tutorials/ecommerce-api.md +3 -3
- package/wiki/guides/tutorials/realtime-chat.md +6 -6
- package/wiki/guides/tutorials/testing.md +4 -5
- package/wiki/index.md +8 -14
- package/wiki/references/base/bootstrapping.md +0 -3
- 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 +2 -2
- package/wiki/references/base/filter-system/default-filter.md +2 -3
- package/wiki/references/base/filter-system/index.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/repositories/advanced.md +1 -1
- package/wiki/references/base/repositories/mixins.md +2 -3
- 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/index.md +2 -9
- 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
- package/wiki/references/src-details/boot.md +0 -379
- package/wiki/references/src-details/core.md +0 -263
- package/wiki/references/src-details/dev-configs.md +0 -298
- package/wiki/references/src-details/docs.md +0 -71
- package/wiki/references/src-details/helpers.md +0 -211
- package/wiki/references/src-details/index.md +0 -86
- package/wiki/references/src-details/inversion.md +0 -340
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
# Mail -- API Reference
|
|
2
|
+
|
|
3
|
+
> Architecture, interfaces, and internal implementation details of the Mail component.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
┌─────────────────────────────────────────────────┐
|
|
9
|
+
│ Your Application │
|
|
10
|
+
│ │
|
|
11
|
+
│ NodemailerComponent (wrapper) │
|
|
12
|
+
│ ├── binds MailKeys.MAIL_OPTIONS │
|
|
13
|
+
│ ├── binds MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG │
|
|
14
|
+
│ └── registers MailComponent │
|
|
15
|
+
└───────────────────────┬─────────────────────────┘
|
|
16
|
+
│
|
|
17
|
+
▼
|
|
18
|
+
┌─────────────────────────────────────────────────┐
|
|
19
|
+
│ MailComponent │
|
|
20
|
+
│ │
|
|
21
|
+
│ binding() │
|
|
22
|
+
│ ├── initGenerators() │
|
|
23
|
+
│ │ ├── NumericCodeGenerator │
|
|
24
|
+
│ │ ├── RandomTokenGenerator │
|
|
25
|
+
│ │ └── DefaultVerificationDataGenerator │
|
|
26
|
+
│ │ │
|
|
27
|
+
│ ├── initProviders() │
|
|
28
|
+
│ │ ├── MailTransportProvider (singleton) │
|
|
29
|
+
│ │ └── MailQueueExecutorProvider (singleton) │
|
|
30
|
+
│ │ │
|
|
31
|
+
│ ├── initServices() │
|
|
32
|
+
│ │ ├── MailService (singleton) │
|
|
33
|
+
│ │ └── TemplateEngineService (singleton) │
|
|
34
|
+
│ │ │
|
|
35
|
+
│ └── createAndBindInstances() │
|
|
36
|
+
│ ├── Transport Instance ◄── MAIL_OPTIONS │
|
|
37
|
+
│ └── Queue Executor ◄── QUEUE_CONFIG │
|
|
38
|
+
└─────────────────────────────────────────────────┘
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Architecture Components:**
|
|
42
|
+
|
|
43
|
+
- **`MailComponent`** -- Initializes and registers all mail services, transporters, and queue executors. Extends `BaseComponent`. Validates that `MailKeys.MAIL_OPTIONS` is bound before proceeding
|
|
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
|
|
49
|
+
|
|
50
|
+
**Tech Stack:**
|
|
51
|
+
|
|
52
|
+
- **Nodemailer** -- SMTP-based email sending (peer dependency: `nodemailer`)
|
|
53
|
+
- **Mailgun** -- Mailgun API client (peer dependency: `mailgun.js`)
|
|
54
|
+
- **BullMQ** (optional) -- Redis-backed queue for distributed processing (peer dependency: `bullmq`)
|
|
55
|
+
- **Handlebars-style Templates** -- Simple <code v-pre>{{variable}}</code> syntax for email templates (no external dependency)
|
|
56
|
+
|
|
57
|
+
## Transport Layer
|
|
58
|
+
|
|
59
|
+
### Transport Layer Implementation
|
|
60
|
+
|
|
61
|
+
The `MailTransportProvider` extends `BaseProvider` and returns a factory function from its `value()` method. It creates the appropriate transport based on the `provider` field in `MailKeys.MAIL_OPTIONS`:
|
|
62
|
+
|
|
63
|
+
- **`'nodemailer'`** -- Creates a `NodemailerTransportHelper` backed by `nodemailer` with SMTP or OAuth2 auth
|
|
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`
|
|
67
|
+
|
|
68
|
+
The provider uses three private type-guard methods to narrow the union type before creating transports:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
private isNodemailerOptions(options: TMailOptions): options is INodemailerMailOptions
|
|
72
|
+
private isMailgunOptions(options: TMailOptions): options is IMailgunMailOptions
|
|
73
|
+
private isCustomOptions(options: TMailOptions): options is ICustomMailOptions
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For custom transports, an additional `isMailTransport()` utility validates that the config object has `send()` and `verify()` methods, reporting specific missing methods in the error message.
|
|
77
|
+
|
|
78
|
+
Both built-in transports implement `IMailTransport`:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
interface IMailTransport {
|
|
82
|
+
send(message: IMailMessage): Promise<IMailSendResult>;
|
|
83
|
+
verify(): Promise<boolean>;
|
|
84
|
+
close?(): Promise<void>;
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Module Validation
|
|
89
|
+
|
|
90
|
+
Both `NodemailerTransportHelper` and `MailgunTransportHelper` call `validateModule()` in their `configure()` method before importing the peer dependency. This checks that the required npm module is installed:
|
|
91
|
+
|
|
92
|
+
- `NodemailerTransportHelper` requires `nodemailer`
|
|
93
|
+
- `MailgunTransportHelper` requires `mailgun.js`
|
|
94
|
+
|
|
95
|
+
If the module is not installed, `validateModule()` throws an error identifying the missing peer dependency.
|
|
96
|
+
|
|
97
|
+
**Nodemailer Transport:**
|
|
98
|
+
|
|
99
|
+
The `NodemailerTransportHelper` extends `BaseHelper` and wraps Nodemailer's SMTP transport. In `configure()`, it calls `require('nodemailer')` and creates a transporter. Key behaviors:
|
|
100
|
+
- `send()` maps `IMailMessage` fields to Nodemailer's mail options, joining array recipients with `, `
|
|
101
|
+
- `send()` catches transport errors and returns `{ success: false, error: ... }` instead of throwing
|
|
102
|
+
- `verify()` delegates to Nodemailer's built-in `transporter.verify()` SMTP handshake
|
|
103
|
+
- `close()` calls `transporter.close()` to release the connection
|
|
104
|
+
|
|
105
|
+
**Mailgun Transport:**
|
|
106
|
+
|
|
107
|
+
The `MailgunTransportHelper` extends `BaseHelper` and uses the Mailgun REST API via `mailgun.js`. In `configure()`, it creates a `Mailgun` client using `FormData`. Key behaviors:
|
|
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)
|
|
112
|
+
|
|
113
|
+
**Custom Transport:**
|
|
114
|
+
|
|
115
|
+
You can provide your own transport implementation by setting `provider: MailProviders.CUSTOM` and passing an object that implements `IMailTransport` as the `config` value. The provider validates that `send()` and `verify()` are functions, reporting specific missing methods. This is useful for integrating with services like SendGrid, AWS SES, or custom SMTP relays.
|
|
116
|
+
|
|
117
|
+
## IMailService Interface
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
interface IMailService {
|
|
121
|
+
// Send a single email
|
|
122
|
+
send(message: IMailMessage): Promise<IMailSendResult>;
|
|
123
|
+
|
|
124
|
+
// Send multiple emails with controlled concurrency
|
|
125
|
+
sendBatch(
|
|
126
|
+
messages: IMailMessage[],
|
|
127
|
+
options?: { concurrency?: number },
|
|
128
|
+
): Promise<IMailSendResult[]>;
|
|
129
|
+
|
|
130
|
+
// Send email using a registered template
|
|
131
|
+
sendTemplate(opts: {
|
|
132
|
+
templateName: string;
|
|
133
|
+
data: Record<string, any>;
|
|
134
|
+
recipients: string | string[];
|
|
135
|
+
options?: Partial<IMailMessage>;
|
|
136
|
+
}): Promise<IMailSendResult>;
|
|
137
|
+
|
|
138
|
+
// Verify transport connection
|
|
139
|
+
verify(): Promise<boolean>;
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Method Details:**
|
|
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`
|
|
153
|
+
|
|
154
|
+
**`sendBatch(messages: IMailMessage[], options?: { concurrency?: number })`**
|
|
155
|
+
|
|
156
|
+
Sends multiple emails with controlled concurrency using `executePromiseWithLimit()`. Default concurrency is `MailDefaults.BATCH_CONCURRENCY` (5). Each message is sent via `send()` individually. If an individual `send()` throws, it is caught and converted to `{ success: false, error: '...' }` so the batch continues. If the entire batch operation fails, throws with `MailErrorCodes.BATCH_SEND_FAILED`.
|
|
157
|
+
|
|
158
|
+
**`sendTemplate(opts: { templateName, data, recipients, options? })`**
|
|
159
|
+
|
|
160
|
+
Renders a registered template with the provided data and sends the email. The subject is resolved through a priority chain:
|
|
161
|
+
|
|
162
|
+
1. `options.subject` -- explicit override from the caller
|
|
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).
|
|
167
|
+
|
|
168
|
+
**`verify()`**
|
|
169
|
+
|
|
170
|
+
Verifies the transport connection without sending an email. Delegates to `transport.verify()`. If the transport throws, catches and re-throws with `MailErrorCodes.VERIFICATION_FAILED`.
|
|
171
|
+
|
|
172
|
+
### Protected Methods (MailService)
|
|
173
|
+
|
|
174
|
+
**`validateMessage(message: IMailMessage)`**
|
|
175
|
+
|
|
176
|
+
Pre-transport validation that throws immediately for invalid messages:
|
|
177
|
+
|
|
178
|
+
| Check | Error Code | Status | Message |
|
|
179
|
+
|-------|-----------|--------|---------|
|
|
180
|
+
| `to` is falsy or empty array | `INVALID_RECIPIENT` | 400 | `Recipient email address is required` |
|
|
181
|
+
| `subject` is falsy | `INVALID_CONFIGURATION` | 400 | `Email subject is required` |
|
|
182
|
+
| Both `text` and `html` are falsy | `INVALID_CONFIGURATION` | 400 | `Email must have either text or html content` |
|
|
183
|
+
|
|
184
|
+
**`getDefaultFrom()`**
|
|
185
|
+
|
|
186
|
+
Constructs the default "from" address. If `options.fromName` is set, returns `"fromName" <from>`. Otherwise returns `options.from ?? 'noreply@example.com'`.
|
|
187
|
+
|
|
188
|
+
## IMailMessage Interface
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
interface IMailMessage {
|
|
192
|
+
from?: string; // Sender email (uses default if not provided)
|
|
193
|
+
to: string | string[]; // Recipient(s)
|
|
194
|
+
cc?: string | string[]; // CC recipient(s)
|
|
195
|
+
bcc?: string | string[]; // BCC recipient(s)
|
|
196
|
+
replyTo?: string; // Reply-to address
|
|
197
|
+
subject: string; // Email subject
|
|
198
|
+
text?: string; // Plain text content
|
|
199
|
+
html?: string; // HTML content
|
|
200
|
+
attachments?: IMailAttachment[]; // File attachments
|
|
201
|
+
headers?: Record<string, string>; // Custom headers
|
|
202
|
+
requireValidate?: boolean; // Validate template data
|
|
203
|
+
[key: string]: any; // Additional arbitrary fields
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Field Details:**
|
|
208
|
+
|
|
209
|
+
**`from`**
|
|
210
|
+
|
|
211
|
+
Sender email address. If not provided, uses the default `from` value from `MailKeys.MAIL_OPTIONS`. When `fromName` is configured, the default from is formatted as `"Display Name" <email@example.com>`. Can be overridden per-message for multi-tenant scenarios.
|
|
212
|
+
|
|
213
|
+
**`to`, `cc`, `bcc`**
|
|
214
|
+
|
|
215
|
+
Recipient addresses. Can be a single string or an array of strings. Format can be either plain email (`user@example.com`) or display name + email (`John Doe <john@example.com>`). For Nodemailer, arrays are joined with `, `. For Mailgun, arrays are passed as-is.
|
|
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:
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
interface IMailAttachment {
|
|
235
|
+
filename?: string;
|
|
236
|
+
contentType?: string;
|
|
237
|
+
path?: string;
|
|
238
|
+
content?: string | Buffer | Readable;
|
|
239
|
+
cid?: string;
|
|
240
|
+
[key: string]: any;
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Examples:
|
|
245
|
+
- A file path: `{ filename: 'doc.pdf', path: '/path/to/doc.pdf' }`
|
|
246
|
+
- A buffer: `{ filename: 'data.txt', content: Buffer.from('...') }`
|
|
247
|
+
- An inline image: `{ filename: 'logo.png', path: '...', cid: 'logo' }`
|
|
248
|
+
|
|
249
|
+
**`headers`**
|
|
250
|
+
|
|
251
|
+
Custom SMTP headers. For Nodemailer, passed directly. For Mailgun, each key is auto-prefixed with `h:`.
|
|
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
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
interface IMailTemplateEngine {
|
|
265
|
+
// Render a template with data
|
|
266
|
+
render(opts: {
|
|
267
|
+
templateData?: string;
|
|
268
|
+
templateName?: string;
|
|
269
|
+
data: Record<string, any>;
|
|
270
|
+
requireValidate?: boolean;
|
|
271
|
+
}): string;
|
|
272
|
+
|
|
273
|
+
// Register a new template
|
|
274
|
+
registerTemplate(opts: { name: string; content: string }): void;
|
|
275
|
+
|
|
276
|
+
// Validate template data
|
|
277
|
+
validateTemplateData(opts: { template: string; data: Record<string, any> }): {
|
|
278
|
+
isValid: boolean;
|
|
279
|
+
missingKeys: string[];
|
|
280
|
+
allKeys: string[];
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
// Get a registered template
|
|
284
|
+
getTemplate(name: string): ITemplate | undefined;
|
|
285
|
+
|
|
286
|
+
// List all registered templates
|
|
287
|
+
listTemplates(): ITemplate[];
|
|
288
|
+
|
|
289
|
+
// Check if template exists
|
|
290
|
+
hasTemplate(name: string): boolean;
|
|
291
|
+
|
|
292
|
+
// Remove a template
|
|
293
|
+
removeTemplate(name: string): boolean;
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Method Details:**
|
|
298
|
+
|
|
299
|
+
**`render(opts: { templateData?, templateName?, data, requireValidate? })`**
|
|
300
|
+
|
|
301
|
+
Renders a template by name (from registry) or by raw template string (`templateData`). At least one of `templateData` or `templateName` must be provided -- throws if neither is given. If `templateName` is used, looks up the template and throws `TEMPLATE_NOT_FOUND` if not registered. Delegates to `renderSimpleTemplate()` which replaces all <code v-pre>{{variable}}</code> placeholders with values from the `data` object. If `requireValidate` is `true`, throws `INVALID_CONFIGURATION` if any placeholders are missing from the data.
|
|
302
|
+
|
|
303
|
+
**`registerTemplate(opts: { name, content, options? })`**
|
|
304
|
+
|
|
305
|
+
Registers a new template in the in-memory registry. The `options` parameter (on the class implementation) can include `subject` and `description` via `Partial<ITemplate>`. Overwrites any existing template with the same name.
|
|
306
|
+
|
|
307
|
+
**`validateTemplateData(opts: { template, data })`**
|
|
308
|
+
|
|
309
|
+
Extracts all <code v-pre>{{variable}}</code> placeholders from the template string using the regex `/\{\{(\s*[\w.]+\s*)\}\}/g`. Deduplicates keys. For each unique key, resolves nested values via dot notation. Returns:
|
|
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
|
|
313
|
+
|
|
314
|
+
**`getTemplate(name: string)`**
|
|
315
|
+
|
|
316
|
+
Retrieves a registered template by name. Returns `undefined` if the template does not exist.
|
|
317
|
+
|
|
318
|
+
**`listTemplates()`**
|
|
319
|
+
|
|
320
|
+
Returns an array of all registered templates (values from the internal `Map`).
|
|
321
|
+
|
|
322
|
+
**`hasTemplate(name: string)`**
|
|
323
|
+
|
|
324
|
+
Checks if a template with the given name exists in the registry.
|
|
325
|
+
|
|
326
|
+
**`removeTemplate(name: string)`**
|
|
327
|
+
|
|
328
|
+
Removes a template from the registry. Logs the removal. Returns `true` if the template was found and removed, `false` otherwise.
|
|
329
|
+
|
|
330
|
+
### ITemplate Interface
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
interface ITemplate {
|
|
334
|
+
name: string;
|
|
335
|
+
content?: string;
|
|
336
|
+
render?: (data: Record<string, AnyType>) => string;
|
|
337
|
+
subject?: string;
|
|
338
|
+
description?: string;
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
The `render` function on the interface supports custom render implementations, though the built-in `TemplateEngineService` uses `content` + `renderSimpleTemplate()` instead.
|
|
343
|
+
|
|
344
|
+
## Additional Interfaces
|
|
345
|
+
|
|
346
|
+
### IMailSendResult
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
interface IMailSendResult {
|
|
350
|
+
success: boolean;
|
|
351
|
+
messageId?: string;
|
|
352
|
+
response?: any;
|
|
353
|
+
error?: string;
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Returned by `send()` and individual entries in the `sendBatch()` result array. Transport-level errors populate `error` with the message string.
|
|
358
|
+
|
|
359
|
+
### IMailQueueExecutor
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
interface IMailQueueExecutor {
|
|
363
|
+
enqueueVerificationEmail(email: string, options?: IMailQueueOptions): Promise<IMailQueueResult>;
|
|
364
|
+
setProcessor(processor: (email: string) => Promise<IMailProcessorResult>): void;
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### IMailQueueOptions
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
interface IMailQueueOptions {
|
|
372
|
+
priority?: number;
|
|
373
|
+
delay?: number;
|
|
374
|
+
attempts?: number;
|
|
375
|
+
backoff?: {
|
|
376
|
+
type: 'fixed' | 'exponential';
|
|
377
|
+
delay: number;
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### IMailQueueResult
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
interface IMailQueueResult {
|
|
386
|
+
jobId?: string;
|
|
387
|
+
queued: boolean;
|
|
388
|
+
message: string;
|
|
389
|
+
result?: IMailProcessorResult;
|
|
390
|
+
}
|
|
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
|
+
|
|
395
|
+
### IMailProcessorResult
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
interface IMailProcessorResult {
|
|
399
|
+
success: boolean;
|
|
400
|
+
message: string;
|
|
401
|
+
expiresInMinutes: number;
|
|
402
|
+
nextResendAt?: string;
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
The return type of the processor function registered via `setProcessor()`.
|
|
407
|
+
|
|
408
|
+
### IVerificationData
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
interface IVerificationData {
|
|
412
|
+
verificationCode: string;
|
|
413
|
+
codeGeneratedAt: string;
|
|
414
|
+
codeExpiresAt: string;
|
|
415
|
+
codeAttempts: number;
|
|
416
|
+
verificationToken: string;
|
|
417
|
+
tokenGeneratedAt: string;
|
|
418
|
+
tokenExpiresAt: string;
|
|
419
|
+
lastCodeSentAt: string;
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### IVerificationGenerationOptions
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
interface IVerificationGenerationOptions {
|
|
427
|
+
codeLength: number;
|
|
428
|
+
tokenBytes: number;
|
|
429
|
+
codeExpiryMinutes: number;
|
|
430
|
+
tokenExpiryHours: number;
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
All fields are required. Passed to `DefaultVerificationDataGenerator.generateVerificationData()`.
|
|
435
|
+
|
|
436
|
+
### IBullMQMailExecutorOpts
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
interface IBullMQMailExecutorOpts {
|
|
440
|
+
redis: IRedisHelperOptions;
|
|
441
|
+
queue: { identifier: string; name: string };
|
|
442
|
+
mode: TConstValue<typeof BullMQExecutorModes>; // REQUIRED
|
|
443
|
+
}
|
|
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
|
+
|
|
460
|
+
Unlike the interface's synchronous `setProcessor()`, the BullMQ executor's version is `async` and accepts an optional second argument:
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
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>
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
It clears all existing workers before creating new ones. In `queue-only` mode, it stores the processor but skips worker creation entirely (logs a warning).
|
|
474
|
+
|
|
475
|
+
## Utility Functions
|
|
476
|
+
|
|
477
|
+
### Type Utilities
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
// Check if a value implements IMailTransport
|
|
481
|
+
function isMailTransport(value: AnyType): value is IMailTransport;
|
|
482
|
+
|
|
483
|
+
// Check if a value is valid TMailOptions
|
|
484
|
+
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
|
+
|
|
491
|
+
### Verification Utilities
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
// Get a Date object `minutes` minutes in the future
|
|
495
|
+
function getExpiryTime(minutes: number): Date;
|
|
496
|
+
|
|
497
|
+
// Get a Date object `hours` hours in the future
|
|
498
|
+
function getExpiryTimeInHours(hours: number): Date;
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
## See Also
|
|
502
|
+
|
|
503
|
+
- [Setup & Configuration](./) -- Quick reference, setup steps, configuration options, and binding keys
|
|
504
|
+
- [Usage & Examples](./usage) -- Sending emails, templates, queue executors, and verification
|
|
505
|
+
- [Error Reference](./errors) -- Error codes and troubleshooting
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Mail -- Error Reference & Troubleshooting
|
|
2
|
+
|
|
3
|
+
> Complete error code reference and troubleshooting guide for the Mail component.
|
|
4
|
+
|
|
5
|
+
## Error Reference
|
|
6
|
+
|
|
7
|
+
All errors are created via `getError()` and include `statusCode`, `messageCode`, and `message` fields.
|
|
8
|
+
|
|
9
|
+
### MailService Errors
|
|
10
|
+
|
|
11
|
+
| Condition | Status | Error Code | Message |
|
|
12
|
+
|-----------|--------|-----------|---------|
|
|
13
|
+
| `to` is missing or empty array | 400 | `MAIL_INVALID_RECIPIENT` | `Recipient email address is required` |
|
|
14
|
+
| `subject` is falsy | 400 | `MAIL_INVALID_CONFIGURATION` | `Email subject is required` |
|
|
15
|
+
| Both `text` and `html` are falsy | 400 | `MAIL_INVALID_CONFIGURATION` | `Email must have either text or html content` |
|
|
16
|
+
| Transport throws during `send()` | 500 | `MAIL_SEND_FAILED` | `Failed to send email: <error>` |
|
|
17
|
+
| Batch operation fails | 500 | `MAIL_BATCH_SEND_FAILED` | `Failed to send batch emails: <error>` |
|
|
18
|
+
| Template engine not configured for `sendTemplate()` | 500 | `MAIL_INVALID_CONFIGURATION` | `Template engine not configured` |
|
|
19
|
+
| Transport throws during `verify()` | 500 | `MAIL_VERIFICATION_FAILED` | `Mail transport verification failed: <error>` |
|
|
20
|
+
|
|
21
|
+
### MailComponent Errors
|
|
22
|
+
|
|
23
|
+
| Condition | Status | Error Code | Message |
|
|
24
|
+
|-----------|--------|-----------|---------|
|
|
25
|
+
| `MAIL_OPTIONS` not bound | -- | -- | `Mail options not configured` |
|
|
26
|
+
|
|
27
|
+
### MailTransportProvider Errors
|
|
28
|
+
|
|
29
|
+
| Condition | Status | Error Code | Message |
|
|
30
|
+
|-----------|--------|-----------|---------|
|
|
31
|
+
| Unsupported provider string | 500 | `MAIL_INVALID_CONFIGURATION` | `Unsupported mail provider: <provider>` |
|
|
32
|
+
| Nodemailer options fail type guard | 500 | `MAIL_INVALID_CONFIGURATION` | `Invalid Nodemailer configuration` |
|
|
33
|
+
| Mailgun options fail type guard | 500 | `MAIL_INVALID_CONFIGURATION` | `Invalid Mailgun configuration` |
|
|
34
|
+
| Custom options fail type guard | 500 | `MAIL_INVALID_CONFIGURATION` | `Invalid custom mail provider configuration` |
|
|
35
|
+
| Custom config missing `send`/`verify` | 500 | `MAIL_INVALID_CONFIGURATION` | `Custom mail provider must implement IMailTransport interface. Missing methods: <methods>` |
|
|
36
|
+
|
|
37
|
+
### MailQueueExecutorProvider Errors
|
|
38
|
+
|
|
39
|
+
| Condition | Status | Error Code | Message |
|
|
40
|
+
|-----------|--------|-----------|---------|
|
|
41
|
+
| `config.internalQueue` missing for `internal-queue` type | -- | -- | `Internal queue configuration is missing` |
|
|
42
|
+
| `config.bullmq` missing for `bullmq` type | -- | -- | `BullMQ configuration is missing` |
|
|
43
|
+
| Unknown executor type | -- | -- | `Unknown mail queue executor type: <type>` |
|
|
44
|
+
|
|
45
|
+
### TemplateEngineService Errors
|
|
46
|
+
|
|
47
|
+
| Condition | Status | Error Code | Message |
|
|
48
|
+
|-----------|--------|-----------|---------|
|
|
49
|
+
| Neither `templateName` nor `templateData` provided | -- | -- | `Either templateName or templateData must be provided` |
|
|
50
|
+
| Template name not found in registry | 404 | `TEMPLATE_NOT_FOUND` | `Template not found: <name>` |
|
|
51
|
+
| Missing template data keys (with `requireValidate: true`) | 400 | `MAIL_INVALID_CONFIGURATION` | `Missing template data for keys: <keys>` |
|
|
52
|
+
|
|
53
|
+
### Queue Executor Errors
|
|
54
|
+
|
|
55
|
+
| Condition | Executor | Message |
|
|
56
|
+
|-----------|----------|---------|
|
|
57
|
+
| Processor not set before enqueue | Direct, Internal Queue, BullMQ | `Processor not set. Call setProcessor() first.` |
|
|
58
|
+
| Processor not set before adding worker | BullMQ | `Processor not set. Call setProcessor() first.` |
|
|
59
|
+
| Enqueue in worker-only mode | BullMQ | `Cannot enqueue jobs in worker-only mode. Set mode to "queue-only" or "both".` |
|
|
60
|
+
| Queue helper unexpectedly null | BullMQ | `Queue helper not initialized. This should not happen in queue-enabled mode.` |
|
|
61
|
+
|
|
62
|
+
## Troubleshooting
|
|
63
|
+
|
|
64
|
+
### "Mail options not configured"
|
|
65
|
+
|
|
66
|
+
**Cause:** `MailKeys.MAIL_OPTIONS` was not bound in the DI container before `MailComponent` was registered. The component checks `isBound()` in its `binding()` phase and throws immediately.
|
|
67
|
+
|
|
68
|
+
**Fix:** Ensure the options binding exists before calling `this.application.component(MailComponent)`:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
this.application.bind({ key: MailKeys.MAIL_OPTIONS }).toValue({
|
|
72
|
+
provider: MailProviders.NODEMAILER,
|
|
73
|
+
from: 'noreply@example.com',
|
|
74
|
+
config: { host: 'smtp.example.com', port: 587, secure: false, auth: { user: '...', pass: '...' } },
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Then register the component
|
|
78
|
+
this.application.component(MailComponent);
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### "TEMPLATE_NOT_FOUND" when calling `sendTemplate()`
|
|
82
|
+
|
|
83
|
+
**Cause:** The template name passed to `sendTemplate()` was never registered via `templateEngine.registerTemplate()`.
|
|
84
|
+
|
|
85
|
+
**Fix:** Register the template before sending. If templates are loaded from a database, ensure the sync runs before the first `sendTemplate()` call:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
this.templateEngine.registerTemplate({
|
|
89
|
+
name: 'welcome-email',
|
|
90
|
+
content: '<h1>Welcome {{userName}}</h1>',
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Emails silently fail with `success: false`
|
|
95
|
+
|
|
96
|
+
**Cause:** The transport connection is misconfigured (wrong credentials, blocked port, expired OAuth2 token). The `MailService.send()` method catches transport errors and returns `{ success: false, error: '...' }` rather than throwing.
|
|
97
|
+
|
|
98
|
+
**Fix:** Check `result.error` for the specific transport error. Verify transport on startup:
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
const isConnected = await this.mailService.verify();
|
|
102
|
+
if (!isConnected) {
|
|
103
|
+
this.logger.error('Mail transport verification failed');
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### BullMQ executor not processing jobs
|
|
108
|
+
|
|
109
|
+
**Cause:** The `mode` is set to `'queue-only'` which only enqueues jobs without starting a worker, or the Redis connection is unreachable.
|
|
110
|
+
|
|
111
|
+
**Fix:** Ensure `mode` is `'both'` or `'worker-only'` on the instance that should process jobs. Verify Redis connectivity:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
{
|
|
115
|
+
type: 'bullmq',
|
|
116
|
+
bullmq: {
|
|
117
|
+
redis: { host: 'localhost', port: 6379, /* ... */ },
|
|
118
|
+
queue: { identifier: 'mail-queue', name: 'mail-queue' },
|
|
119
|
+
mode: 'both', // Must be 'both' or 'worker-only' to process
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Template variables not replaced (placeholders preserved)
|
|
125
|
+
|
|
126
|
+
**Cause:** The `data` object passed to `render()` or `sendTemplate()` does not contain all <code v-pre>{{key}}</code> placeholders found in the template. When `requireValidate` is **not** set (or set to `false`), the template engine preserves the original placeholder text as-is (e.g., <code v-pre>{{missingKey}}</code> remains literally in the output). It does **not** replace missing variables with empty strings.
|
|
127
|
+
|
|
128
|
+
**Fix:** Use `validateTemplateData()` to check which keys are missing before rendering:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
const validation = this.templateEngine.validateTemplateData({ template, data });
|
|
132
|
+
if (!validation.isValid) {
|
|
133
|
+
console.error('Missing keys:', validation.missingKeys);
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Or set `requireValidate: true` to throw an error when keys are missing:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
const html = this.templateEngine.render({
|
|
141
|
+
templateName: 'welcome-email',
|
|
142
|
+
data,
|
|
143
|
+
requireValidate: true, // Throws if any placeholders are missing
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### "Processor not set. Call setProcessor() first."
|
|
148
|
+
|
|
149
|
+
**Cause:** The queue executor's `enqueueVerificationEmail()` was called before `setProcessor()`. All three executor types (Direct, Internal Queue, BullMQ) require a processor function to be registered first.
|
|
150
|
+
|
|
151
|
+
**Fix:** Call `setProcessor()` before enqueuing any jobs:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
executor.setProcessor(async (email: string) => {
|
|
155
|
+
// Your email processing logic here
|
|
156
|
+
return { success: true, message: 'Verification email sent', expiresInMinutes: 10 };
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### "Cannot enqueue jobs in worker-only mode"
|
|
161
|
+
|
|
162
|
+
**Cause:** The BullMQ executor is configured with `mode: 'worker-only'`, which does not initialize a queue and therefore cannot accept new jobs.
|
|
163
|
+
|
|
164
|
+
**Fix:** Use `mode: 'both'` or `mode: 'queue-only'` on instances that need to enqueue jobs. Use `mode: 'worker-only'` only on dedicated worker processes.
|
|
165
|
+
|
|
166
|
+
### Credential logging at startup
|
|
167
|
+
|
|
168
|
+
**Cause:** The `MailComponent.createAndBindInstances()` method logs the full `mailOptions` object (including credentials) at `info` level during initialization. This is by design for debugging, but it means sensitive values like API keys, passwords, and OAuth tokens will appear in logs.
|
|
169
|
+
|
|
170
|
+
**Fix:** In production, either use a log level higher than `info` for the mail component scope, or ensure your log pipeline redacts sensitive fields.
|
|
171
|
+
|
|
172
|
+
## See Also
|
|
173
|
+
|
|
174
|
+
- [Setup & Configuration](./) -- Quick reference, setup steps, configuration options, and binding keys
|
|
175
|
+
- [Usage & Examples](./usage) -- Sending emails, templates, queue executors, and verification
|
|
176
|
+
- [API Reference](./api) -- Architecture, interfaces, and internals
|