@venizia/ignis-docs 0.0.3 → 0.0.4-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/README.md +1 -1
- package/package.json +4 -2
- package/wiki/best-practices/api-usage-examples.md +591 -0
- package/wiki/best-practices/architectural-patterns.md +415 -0
- package/wiki/best-practices/architecture-decisions.md +488 -0
- package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +406 -17
- package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
- package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
- package/wiki/best-practices/data-modeling.md +376 -0
- package/wiki/best-practices/deployment-strategies.md +698 -0
- package/wiki/best-practices/index.md +27 -0
- package/wiki/best-practices/performance-optimization.md +196 -0
- package/wiki/best-practices/security-guidelines.md +218 -0
- package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
- package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
- package/wiki/changelogs/2025-12-17-refactor.md +1 -1
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
- package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +2 -2
- package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
- package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
- package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
- package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
- package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
- package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
- package/wiki/changelogs/index.md +6 -0
- package/wiki/changelogs/planned-schema-migrator.md +0 -8
- package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
- package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
- package/wiki/guides/core-concepts/components-guide.md +509 -0
- package/wiki/{get-started → guides}/core-concepts/components.md +24 -17
- package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
- package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
- package/wiki/guides/core-concepts/persistent/index.md +119 -0
- package/wiki/guides/core-concepts/persistent/models.md +241 -0
- package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
- package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
- package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
- package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
- package/wiki/guides/get-started/philosophy.md +682 -0
- package/wiki/guides/get-started/setup.md +157 -0
- package/wiki/guides/index.md +89 -0
- package/wiki/guides/reference/glossary.md +243 -0
- package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
- package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
- package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
- package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
- package/wiki/guides/tutorials/realtime-chat.md +1261 -0
- package/wiki/guides/tutorials/testing.md +723 -0
- package/wiki/index.md +176 -37
- package/wiki/references/base/application.md +27 -0
- package/wiki/references/base/bootstrapping.md +30 -26
- package/wiki/references/base/components.md +24 -7
- package/wiki/references/base/controllers.md +51 -20
- package/wiki/references/base/datasources.md +30 -0
- package/wiki/references/base/dependency-injection.md +39 -3
- package/wiki/references/base/filter-system/application-usage.md +224 -0
- package/wiki/references/base/filter-system/array-operators.md +132 -0
- package/wiki/references/base/filter-system/comparison-operators.md +109 -0
- package/wiki/references/base/filter-system/default-filter.md +428 -0
- package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
- package/wiki/references/base/filter-system/index.md +127 -0
- package/wiki/references/base/filter-system/json-filtering.md +197 -0
- package/wiki/references/base/filter-system/list-operators.md +71 -0
- package/wiki/references/base/filter-system/logical-operators.md +156 -0
- package/wiki/references/base/filter-system/null-operators.md +58 -0
- package/wiki/references/base/filter-system/pattern-matching.md +108 -0
- package/wiki/references/base/filter-system/quick-reference.md +431 -0
- package/wiki/references/base/filter-system/range-operators.md +63 -0
- package/wiki/references/base/filter-system/tips.md +190 -0
- package/wiki/references/base/filter-system/use-cases.md +452 -0
- package/wiki/references/base/index.md +90 -0
- package/wiki/references/base/middlewares.md +602 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +732 -0
- package/wiki/references/base/repositories/advanced.md +555 -0
- package/wiki/references/base/repositories/index.md +228 -0
- package/wiki/references/base/repositories/mixins.md +331 -0
- package/wiki/references/base/repositories/relations.md +486 -0
- package/wiki/references/base/repositories.md +40 -635
- package/wiki/references/base/services.md +28 -4
- package/wiki/references/components/authentication.md +22 -2
- package/wiki/references/components/health-check.md +12 -0
- package/wiki/references/components/index.md +23 -0
- package/wiki/references/components/mail.md +687 -0
- package/wiki/references/components/request-tracker.md +16 -0
- package/wiki/references/components/socket-io.md +18 -0
- package/wiki/references/components/static-asset.md +14 -26
- package/wiki/references/components/swagger.md +17 -0
- package/wiki/references/configuration/environment-variables.md +427 -0
- package/wiki/references/configuration/index.md +73 -0
- package/wiki/references/helpers/cron.md +14 -0
- package/wiki/references/helpers/crypto.md +15 -0
- package/wiki/references/helpers/env.md +16 -0
- package/wiki/references/helpers/error.md +17 -0
- package/wiki/references/helpers/index.md +14 -0
- package/wiki/references/helpers/inversion.md +24 -4
- package/wiki/references/helpers/logger.md +19 -0
- package/wiki/references/helpers/network.md +11 -0
- package/wiki/references/helpers/queue.md +19 -0
- package/wiki/references/helpers/redis.md +21 -0
- package/wiki/references/helpers/socket-io.md +24 -5
- package/wiki/references/helpers/storage.md +18 -10
- package/wiki/references/helpers/testing.md +18 -0
- package/wiki/references/helpers/types.md +16 -0
- package/wiki/references/helpers/uid.md +167 -0
- package/wiki/references/helpers/worker-thread.md +16 -0
- package/wiki/references/index.md +177 -0
- package/wiki/references/quick-reference.md +634 -0
- package/wiki/references/src-details/boot.md +3 -3
- package/wiki/references/src-details/dev-configs.md +0 -4
- package/wiki/references/src-details/docs.md +2 -2
- package/wiki/references/src-details/index.md +86 -0
- package/wiki/references/src-details/inversion.md +1 -6
- package/wiki/references/src-details/mcp-server.md +3 -15
- package/wiki/references/utilities/index.md +86 -10
- package/wiki/references/utilities/jsx.md +577 -0
- package/wiki/references/utilities/request.md +0 -2
- package/wiki/references/utilities/statuses.md +740 -0
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
- package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
- package/wiki/get-started/best-practices/data-modeling.md +0 -177
- package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
- package/wiki/get-started/best-practices/performance-optimization.md +0 -97
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/persistent.md +0 -539
- package/wiki/get-started/index.md +0 -65
- package/wiki/get-started/philosophy.md +0 -296
- package/wiki/get-started/prerequisites.md +0 -113
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
# Mail Component
|
|
2
|
+
|
|
3
|
+
Flexible email sending system with support for multiple transports, templating, and queue-based processing.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
| Component | Purpose |
|
|
8
|
+
| ----------------------------------- | ------------------------------------------------------------------------ |
|
|
9
|
+
| **MailComponent** | Main component registering mail services, transporters, and executors |
|
|
10
|
+
| **MailService** | Core service for sending emails, batch emails, and template-based emails |
|
|
11
|
+
| **TemplateEngineService** | Simple template engine with `{{variable}}` syntax |
|
|
12
|
+
| **NodemailerTransportHelper** | Nodemailer-based email transport implementation |
|
|
13
|
+
| **MailgunTransportHelper** | Mailgun API-based email transport implementation |
|
|
14
|
+
| **DirectMailExecutorHelper** | Execute email sending immediately without queue |
|
|
15
|
+
| **InternalQueueMailExecutorHelper** | Queue emails using in-memory queue |
|
|
16
|
+
| **BullMQMailExecutorHelper** | Queue emails using BullMQ for distributed processing |
|
|
17
|
+
|
|
18
|
+
### Key Binding Keys
|
|
19
|
+
|
|
20
|
+
| Key | Purpose |
|
|
21
|
+
| --------------------------------------- | ---------------------------------------------- |
|
|
22
|
+
| `MailKeys.MAIL_OPTIONS` | Mail transport configuration (required) |
|
|
23
|
+
| `MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG` | Queue executor configuration (required) |
|
|
24
|
+
| `MailKeys.MAIL_SERVICE` | Main mail service instance |
|
|
25
|
+
| `MailKeys.MAIL_TEMPLATE_ENGINE` | Template engine service |
|
|
26
|
+
| `MailKeys.MAIL_TRANSPORT_INSTANCE` | Transport instance (created by component) |
|
|
27
|
+
| `MailKeys.MAIL_QUEUE_EXECUTOR_INSTANCE` | Queue executor instance (created by component) |
|
|
28
|
+
|
|
29
|
+
### Transport Providers
|
|
30
|
+
|
|
31
|
+
| Provider | Value | When to Use |
|
|
32
|
+
| -------------- | -------------------------- | ------------------------------------------------ |
|
|
33
|
+
| **Nodemailer** | `MailProviders.NODEMAILER` | SMTP-based email sending (Gmail, SendGrid, etc.) |
|
|
34
|
+
| **Mailgun** | `MailProviders.MAILGUN` | Mailgun API for transactional emails |
|
|
35
|
+
| **Custom** | `MailProviders.CUSTOM` | Custom transport implementation |
|
|
36
|
+
|
|
37
|
+
### Queue Executor Types
|
|
38
|
+
|
|
39
|
+
| Type | Value | When to Use |
|
|
40
|
+
| ------------------ | ------------------ | ------------------------------------------ |
|
|
41
|
+
| **Direct** | `'direct'` | No queue, send immediately |
|
|
42
|
+
| **Internal Queue** | `'internal-queue'` | In-memory queue for simple use cases |
|
|
43
|
+
| **BullMQ** | `'bullmq'` | Redis-backed queue for distributed systems |
|
|
44
|
+
|
|
45
|
+
## Architecture Components
|
|
46
|
+
|
|
47
|
+
- **`MailComponent`**: Initializes and registers all mail services, transporters, and queue executors
|
|
48
|
+
- **`MailService`**: Provides methods to send single emails, batch emails, and template-based emails
|
|
49
|
+
- **`TemplateEngineService`**: Manages email templates with simple `{{variable}}` substitution
|
|
50
|
+
- **Verification Generators**: Generate verification codes, tokens, and data for email verification flows
|
|
51
|
+
- **Transport Providers**: Factory functions that create transport instances based on configuration
|
|
52
|
+
- **Queue Executor Providers**: Factory functions that create queue executor instances based on configuration
|
|
53
|
+
|
|
54
|
+
## Implementation Details
|
|
55
|
+
|
|
56
|
+
### Tech Stack
|
|
57
|
+
|
|
58
|
+
- **Nodemailer**: SMTP-based email sending
|
|
59
|
+
- **Mailgun**: Mailgun API client
|
|
60
|
+
- **BullMQ** (optional): Redis-backed queue for distributed processing
|
|
61
|
+
- **Handlebars-style Templates**: Simple `{{variable}}` syntax for email templates
|
|
62
|
+
|
|
63
|
+
### Configuration
|
|
64
|
+
|
|
65
|
+
The Mail component requires two main configurations to be bound before registering the component:
|
|
66
|
+
|
|
67
|
+
1. **`MailKeys.MAIL_OPTIONS`**: Transport configuration
|
|
68
|
+
2. **`MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG`**: Queue executor configuration
|
|
69
|
+
|
|
70
|
+
**Environment Variables (for Nodemailer with OAuth2):**
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
APP_ENV_MAIL_HOST=smtp.gmail.com
|
|
74
|
+
APP_ENV_MAIL_PORT=465
|
|
75
|
+
APP_ENV_MAIL_SECURE=true
|
|
76
|
+
APP_ENV_MAIL_USER=your-email@gmail.com
|
|
77
|
+
APP_ENV_MAIL_CLIENT_ID=your-oauth2-client-id
|
|
78
|
+
APP_ENV_MAIL_CLIENT_SECRET=your-oauth2-client-secret
|
|
79
|
+
APP_ENV_MAIL_REFRESH_TOKEN=your-oauth2-refresh-token
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
::: tip
|
|
83
|
+
For Gmail OAuth2 setup, you need to create credentials in Google Cloud Console and generate a refresh token. See [Nodemailer OAuth2 documentation](https://nodemailer.com/smtp/oauth2/) for details.
|
|
84
|
+
:::
|
|
85
|
+
|
|
86
|
+
### Code Samples
|
|
87
|
+
|
|
88
|
+
#### 1. Basic Setup with Nodemailer
|
|
89
|
+
|
|
90
|
+
The simplest way to set up the Mail component is by creating a custom component that wraps `MailComponent` with the necessary bindings.
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// src/components/mail/component.ts
|
|
94
|
+
import {
|
|
95
|
+
BaseApplication,
|
|
96
|
+
BaseComponent,
|
|
97
|
+
Binding,
|
|
98
|
+
CoreBindings,
|
|
99
|
+
inject,
|
|
100
|
+
MailComponent,
|
|
101
|
+
MailKeys,
|
|
102
|
+
MailProviders,
|
|
103
|
+
applicationEnvironment,
|
|
104
|
+
toBoolean,
|
|
105
|
+
} from '@venizia/ignis';
|
|
106
|
+
|
|
107
|
+
export class NodemailerComponent extends BaseComponent {
|
|
108
|
+
constructor(
|
|
109
|
+
@inject({ key: CoreBindings.APPLICATION_INSTANCE })
|
|
110
|
+
protected application: BaseApplication,
|
|
111
|
+
) {
|
|
112
|
+
super({
|
|
113
|
+
scope: NodemailerComponent.name,
|
|
114
|
+
initDefault: { enable: true, container: application },
|
|
115
|
+
bindings: {
|
|
116
|
+
// Configure mail transport options
|
|
117
|
+
[MailKeys.MAIL_OPTIONS]: Binding.bind({
|
|
118
|
+
key: MailKeys.MAIL_OPTIONS,
|
|
119
|
+
}).toValue({
|
|
120
|
+
provider: MailProviders.NODEMAILER,
|
|
121
|
+
from: 'noreply@example.com',
|
|
122
|
+
fromName: 'Example App',
|
|
123
|
+
config: {
|
|
124
|
+
host: applicationEnvironment.get<string>('APP_ENV_MAIL_HOST') ?? 'smtp.gmail.com',
|
|
125
|
+
port: +(applicationEnvironment.get<number>('APP_ENV_MAIL_PORT') ?? 465),
|
|
126
|
+
secure: toBoolean(applicationEnvironment.get<boolean>('APP_ENV_MAIL_SECURE') ?? true),
|
|
127
|
+
auth: {
|
|
128
|
+
type: 'oauth2',
|
|
129
|
+
user: applicationEnvironment.get<string>('APP_ENV_MAIL_USER'),
|
|
130
|
+
clientId: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_ID'),
|
|
131
|
+
clientSecret: applicationEnvironment.get<string>('APP_ENV_MAIL_CLIENT_SECRET'),
|
|
132
|
+
refreshToken: applicationEnvironment.get<string>('APP_ENV_MAIL_REFRESH_TOKEN'),
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
}),
|
|
136
|
+
// Configure queue executor
|
|
137
|
+
[MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG]: Binding.bind({
|
|
138
|
+
key: MailKeys.MAIL_QUEUE_EXECUTOR_CONFIG,
|
|
139
|
+
}).toValue({
|
|
140
|
+
type: 'internal-queue',
|
|
141
|
+
internalQueue: {
|
|
142
|
+
identifier: 'mail-internal-queue',
|
|
143
|
+
},
|
|
144
|
+
}),
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
override async binding(): Promise<void> {
|
|
150
|
+
this.logger.info('[binding] Binding mail component...');
|
|
151
|
+
|
|
152
|
+
// Register the core MailComponent
|
|
153
|
+
this.application.component(MailComponent);
|
|
154
|
+
|
|
155
|
+
this.logger.info('[binding] Mail component initialized successfully');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Then register it in your application:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// src/application.ts
|
|
164
|
+
import { BaseApplication, ValueOrPromise } from '@venizia/ignis';
|
|
165
|
+
import { NodemailerComponent } from './components/mail/component';
|
|
166
|
+
|
|
167
|
+
export class Application extends BaseApplication {
|
|
168
|
+
preConfigure(): ValueOrPromise<void> {
|
|
169
|
+
// Register the mail component
|
|
170
|
+
this.component(NodemailerComponent);
|
|
171
|
+
|
|
172
|
+
// ... other components
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
#### 2. Nodemailer with Simple SMTP Auth
|
|
178
|
+
|
|
179
|
+
For basic SMTP authentication (username/password):
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
{
|
|
183
|
+
provider: MailProviders.NODEMAILER,
|
|
184
|
+
from: 'noreply@example.com',
|
|
185
|
+
fromName: 'My App',
|
|
186
|
+
config: {
|
|
187
|
+
host: 'smtp.example.com',
|
|
188
|
+
port: 587,
|
|
189
|
+
secure: false, // true for 465, false for other ports
|
|
190
|
+
auth: {
|
|
191
|
+
user: 'smtp-username',
|
|
192
|
+
pass: 'smtp-password',
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### 3. Mailgun Configuration
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
{
|
|
202
|
+
provider: MailProviders.MAILGUN,
|
|
203
|
+
from: 'noreply@example.com',
|
|
204
|
+
fromName: 'My App',
|
|
205
|
+
config: {
|
|
206
|
+
domain: 'mg.example.com',
|
|
207
|
+
apiKey: process.env.MAILGUN_API_KEY,
|
|
208
|
+
url: 'https://api.mailgun.net', // Optional, defaults to US region
|
|
209
|
+
},
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
#### 4. Queue Executor Configurations
|
|
214
|
+
|
|
215
|
+
**Direct Executor (No Queue):**
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
{
|
|
219
|
+
type: 'direct',
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**Internal Queue Executor:**
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
{
|
|
227
|
+
type: 'internal-queue',
|
|
228
|
+
internalQueue: {
|
|
229
|
+
identifier: 'mail-verification-queue',
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**BullMQ Executor:**
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
{
|
|
238
|
+
type: 'bullmq',
|
|
239
|
+
bullmq: {
|
|
240
|
+
redis: {
|
|
241
|
+
name: 'mail-redis',
|
|
242
|
+
host: process.env.REDIS_HOST ?? 'localhost',
|
|
243
|
+
port: process.env.REDIS_PORT ?? 6379,
|
|
244
|
+
password: process.env.REDIS_PASSWORD ?? '',
|
|
245
|
+
database: 0,
|
|
246
|
+
autoConnect: true,
|
|
247
|
+
},
|
|
248
|
+
queue: {
|
|
249
|
+
identifier: 'mail-queue-executor',
|
|
250
|
+
name: 'mail-verification-queue',
|
|
251
|
+
},
|
|
252
|
+
mode: 'both', // 'queue-only' | 'worker-only' | 'both'
|
|
253
|
+
},
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**BullMQ Executor Mode Options:**
|
|
258
|
+
|
|
259
|
+
- **`queue-only`**: Only enqueue jobs, no workers (useful for API servers that just add jobs)
|
|
260
|
+
- **`worker-only`**: Only process jobs, cannot enqueue (useful for dedicated worker servers)
|
|
261
|
+
- **`both`**: Can both enqueue and process jobs (useful for single-server deployments)
|
|
262
|
+
|
|
263
|
+
#### 5. Sending Emails
|
|
264
|
+
|
|
265
|
+
Inject the `MailService` to send emails:
|
|
266
|
+
|
|
267
|
+
```typescript
|
|
268
|
+
import { BaseService, inject, MailKeys, IMailService } from '@venizia/ignis';
|
|
269
|
+
|
|
270
|
+
export class UserService extends BaseService {
|
|
271
|
+
constructor(
|
|
272
|
+
@inject({ key: MailKeys.MAIL_SERVICE })
|
|
273
|
+
private _mailService: IMailService,
|
|
274
|
+
) {
|
|
275
|
+
super({ scope: UserService.name });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
async sendWelcomeEmail(opts: { userEmail: string; userName: string }) {
|
|
279
|
+
const result = await this._mailService.send({
|
|
280
|
+
to: opts.userEmail,
|
|
281
|
+
subject: 'Welcome to Our App!',
|
|
282
|
+
html: `<h1>Welcome ${opts.userName}!</h1><p>Thanks for joining us.</p>`,
|
|
283
|
+
text: `Welcome ${opts.userName}! Thanks for joining us.`,
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
if (result.success) {
|
|
287
|
+
this.logger.info('[sendWelcomeEmail] Email sent: %s', result.messageId);
|
|
288
|
+
} else {
|
|
289
|
+
this.logger.error('[sendWelcomeEmail] Failed to send email: %s', result.error);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
#### 6. Using Templates
|
|
298
|
+
|
|
299
|
+
Register and use email templates:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
import { BaseService, inject, MailKeys, IMailTemplateEngine, IMailService } from '@venizia/ignis';
|
|
303
|
+
|
|
304
|
+
export class NotificationService extends BaseService {
|
|
305
|
+
constructor(
|
|
306
|
+
@inject({ key: MailKeys.MAIL_TEMPLATE_ENGINE })
|
|
307
|
+
private templateEngine: IMailTemplateEngine,
|
|
308
|
+
@inject({ key: MailKeys.MAIL_SERVICE })
|
|
309
|
+
private mailService: IMailService,
|
|
310
|
+
) {
|
|
311
|
+
super({ scope: NotificationService.name });
|
|
312
|
+
this.registerTemplates();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
registerTemplates() {
|
|
316
|
+
// Register a welcome email template
|
|
317
|
+
this.templateEngine.registerTemplate({
|
|
318
|
+
name: 'welcome-email',
|
|
319
|
+
content: `
|
|
320
|
+
<html>
|
|
321
|
+
<body>
|
|
322
|
+
<h1>Welcome {{userName}}!</h1>
|
|
323
|
+
<p>Your account has been created successfully.</p>
|
|
324
|
+
<p>Your verification code is: <strong>{{verificationCode}}</strong></p>
|
|
325
|
+
</body>
|
|
326
|
+
</html>
|
|
327
|
+
`,
|
|
328
|
+
options: {
|
|
329
|
+
subject: 'Welcome to {{appName}}',
|
|
330
|
+
description: 'Welcome email for new users',
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
async sendWelcomeEmail(userEmail: string, userName: string, verificationCode: string) {
|
|
336
|
+
const result = await this.mailService.sendTemplate({
|
|
337
|
+
templateName: 'welcome-email',
|
|
338
|
+
data: {
|
|
339
|
+
userName,
|
|
340
|
+
verificationCode,
|
|
341
|
+
appName: 'My Application',
|
|
342
|
+
},
|
|
343
|
+
recipients: userEmail,
|
|
344
|
+
options: {
|
|
345
|
+
// Optional: override template subject or add attachments
|
|
346
|
+
attachments: [
|
|
347
|
+
{
|
|
348
|
+
filename: 'logo.png',
|
|
349
|
+
path: '/path/to/logo.png',
|
|
350
|
+
cid: 'logo',
|
|
351
|
+
},
|
|
352
|
+
],
|
|
353
|
+
},
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### 7. Batch Email Sending
|
|
362
|
+
|
|
363
|
+
Send multiple emails with controlled concurrency:
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
async sendBulkNotifications(users: Array<{ email: string; name: string }>) {
|
|
367
|
+
const messages = users.map(user => ({
|
|
368
|
+
to: user.email,
|
|
369
|
+
subject: 'Important Update',
|
|
370
|
+
html: `<p>Hello ${user.name}, we have an important update for you.</p>`,
|
|
371
|
+
}));
|
|
372
|
+
|
|
373
|
+
const results = await this.mailService.sendBatch(messages, {
|
|
374
|
+
concurrency: 5, // Send 5 emails at a time
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const successCount = results.filter(r => r.success).length;
|
|
378
|
+
this.logger.info(
|
|
379
|
+
'[sendBulkNotifications] Sent %d/%d emails successfully',
|
|
380
|
+
successCount,
|
|
381
|
+
results.length,
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
return results;
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### 8. Email Verification Flow
|
|
389
|
+
|
|
390
|
+
Using the verification data generators:
|
|
391
|
+
|
|
392
|
+
```typescript
|
|
393
|
+
import {
|
|
394
|
+
BaseService,
|
|
395
|
+
inject,
|
|
396
|
+
MailKeys,
|
|
397
|
+
IMailService,
|
|
398
|
+
IVerificationDataGenerator,
|
|
399
|
+
} from '@venizia/ignis';
|
|
400
|
+
|
|
401
|
+
export class AuthService extends BaseService {
|
|
402
|
+
constructor(
|
|
403
|
+
@inject({ key: MailKeys.MAIL_SERVICE })
|
|
404
|
+
private mailService: IMailService,
|
|
405
|
+
@inject({ key: MailKeys.MAIL_VERIFICATION_DATA_GENERATOR })
|
|
406
|
+
private verificationGenerator: IVerificationDataGenerator,
|
|
407
|
+
) {
|
|
408
|
+
super({ scope: AuthService.name });
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async sendVerificationEmail(userEmail: string) {
|
|
412
|
+
// Generate verification code and token
|
|
413
|
+
const verificationData = this.verificationGenerator.generateVerificationData({
|
|
414
|
+
codeLength: 6, // 6-digit code
|
|
415
|
+
tokenBytes: 32, // 32-byte token
|
|
416
|
+
codeExpiryMinutes: 10, // Code expires in 10 minutes
|
|
417
|
+
tokenExpiryHours: 24, // Token expires in 24 hours
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Save verification data to database
|
|
421
|
+
// await this.saveVerificationData(userEmail, verificationData);
|
|
422
|
+
|
|
423
|
+
// Send verification email
|
|
424
|
+
const result = await this.mailService.send({
|
|
425
|
+
to: userEmail,
|
|
426
|
+
subject: 'Email Verification',
|
|
427
|
+
html: `
|
|
428
|
+
<h2>Verify Your Email</h2>
|
|
429
|
+
<p>Your verification code is: <strong>${verificationData.verificationCode}</strong></p>
|
|
430
|
+
<p>This code expires at: ${verificationData.codeExpiresAt}</p>
|
|
431
|
+
<p>Or click this link: https://example.com/verify?token=${verificationData.verificationToken}</p>
|
|
432
|
+
`,
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
return { result, verificationData };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
#### 9. Template Validation
|
|
441
|
+
|
|
442
|
+
Ensure all required template variables are provided:
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
const template = '<h1>Hello {{userName}}, your code is {{code}}</h1>';
|
|
446
|
+
const data = { userName: 'John' }; // Missing 'code'
|
|
447
|
+
|
|
448
|
+
const validation = this.templateEngine.validateTemplateData({ template, data });
|
|
449
|
+
|
|
450
|
+
if (!validation.isValid) {
|
|
451
|
+
console.error('Missing template variables:', validation.missingKeys);
|
|
452
|
+
// Output: ['code']
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Render with validation
|
|
456
|
+
try {
|
|
457
|
+
const html = this.templateEngine.render({
|
|
458
|
+
templateData: template,
|
|
459
|
+
data,
|
|
460
|
+
requireValidate: true, // Throws error if validation fails
|
|
461
|
+
});
|
|
462
|
+
} catch (error) {
|
|
463
|
+
console.error('Template rendering failed:', error.message);
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
#### 10. Syncing Templates from Database
|
|
468
|
+
|
|
469
|
+
For applications that store templates in a database:
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
async syncTemplatesFromDatabase() {
|
|
473
|
+
const templateEngine = this.application.get<IMailTemplateEngine>({
|
|
474
|
+
key: MailKeys.MAIL_TEMPLATE_ENGINE,
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
const configRepository = this.application.get<ConfigurationRepository>({
|
|
478
|
+
key: 'repositories.ConfigurationRepository',
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const templateConfigs = await configRepository.find({
|
|
482
|
+
filter: {
|
|
483
|
+
where: {
|
|
484
|
+
code: { inq: ['MAIL_TEMPLATE_WELCOME', 'MAIL_TEMPLATE_VERIFICATION'] },
|
|
485
|
+
},
|
|
486
|
+
},
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
templateConfigs.forEach(config => {
|
|
490
|
+
templateEngine.registerTemplate({
|
|
491
|
+
name: config.code,
|
|
492
|
+
content: config.jValue.content,
|
|
493
|
+
options: {
|
|
494
|
+
subject: config.jValue.subject,
|
|
495
|
+
description: config.jValue.description,
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
this.logger.info('[syncTemplates] Registered template: %s', config.code);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
## API Specifications
|
|
504
|
+
|
|
505
|
+
### Mail Options Configuration
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
// Nodemailer Options
|
|
509
|
+
interface INodemailerMailOptions {
|
|
510
|
+
provider: 'nodemailer';
|
|
511
|
+
from?: string;
|
|
512
|
+
fromName?: string;
|
|
513
|
+
config: {
|
|
514
|
+
host: string;
|
|
515
|
+
port: number;
|
|
516
|
+
secure: boolean;
|
|
517
|
+
auth: {
|
|
518
|
+
type?: 'oauth2' | 'login';
|
|
519
|
+
user: string;
|
|
520
|
+
pass?: string; // For simple auth
|
|
521
|
+
// OAuth2 specific
|
|
522
|
+
clientId?: string;
|
|
523
|
+
clientSecret?: string;
|
|
524
|
+
refreshToken?: string;
|
|
525
|
+
};
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Mailgun Options
|
|
530
|
+
interface IMailgunMailOptions {
|
|
531
|
+
provider: 'mailgun';
|
|
532
|
+
from?: string;
|
|
533
|
+
fromName?: string;
|
|
534
|
+
config: {
|
|
535
|
+
domain: string;
|
|
536
|
+
apiKey: string;
|
|
537
|
+
url?: string; // Optional, defaults to US region
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Queue Executor Configuration
|
|
542
|
+
interface IMailQueueExecutorConfig {
|
|
543
|
+
type: 'direct' | 'internal-queue' | 'bullmq';
|
|
544
|
+
|
|
545
|
+
// For internal-queue type
|
|
546
|
+
internalQueue?: {
|
|
547
|
+
identifier: string;
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
// For bullmq type
|
|
551
|
+
bullmq?: {
|
|
552
|
+
redis: {
|
|
553
|
+
name: string;
|
|
554
|
+
host: string;
|
|
555
|
+
port: string | number;
|
|
556
|
+
password: string;
|
|
557
|
+
user?: string;
|
|
558
|
+
database?: number;
|
|
559
|
+
autoConnect?: boolean;
|
|
560
|
+
maxRetry?: number;
|
|
561
|
+
};
|
|
562
|
+
queue: {
|
|
563
|
+
identifier: string;
|
|
564
|
+
name: string;
|
|
565
|
+
};
|
|
566
|
+
mode: 'queue-only' | 'worker-only' | 'both';
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### IMailService Interface
|
|
572
|
+
|
|
573
|
+
```typescript
|
|
574
|
+
interface IMailService {
|
|
575
|
+
// Send a single email
|
|
576
|
+
send(message: IMailMessage): Promise<IMailSendResult>;
|
|
577
|
+
|
|
578
|
+
// Send multiple emails with controlled concurrency
|
|
579
|
+
sendBatch(
|
|
580
|
+
messages: IMailMessage[],
|
|
581
|
+
options?: { concurrency?: number },
|
|
582
|
+
): Promise<IMailSendResult[]>;
|
|
583
|
+
|
|
584
|
+
// Send email using a registered template
|
|
585
|
+
sendTemplate(opts: {
|
|
586
|
+
templateName: string;
|
|
587
|
+
data: Record<string, any>;
|
|
588
|
+
recipients: string | string[];
|
|
589
|
+
options?: Partial<IMailMessage>;
|
|
590
|
+
}): Promise<IMailSendResult>;
|
|
591
|
+
|
|
592
|
+
// Verify transport connection
|
|
593
|
+
verify(): Promise<boolean>;
|
|
594
|
+
}
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### IMailMessage Interface
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
interface IMailMessage {
|
|
601
|
+
from?: string; // Sender email (uses default if not provided)
|
|
602
|
+
to: string | string[]; // Recipient(s)
|
|
603
|
+
cc?: string | string[]; // CC recipient(s)
|
|
604
|
+
bcc?: string | string[]; // BCC recipient(s)
|
|
605
|
+
replyTo?: string; // Reply-to address
|
|
606
|
+
subject: string; // Email subject
|
|
607
|
+
text?: string; // Plain text content
|
|
608
|
+
html?: string; // HTML content
|
|
609
|
+
attachments?: IMailAttachment[]; // File attachments
|
|
610
|
+
headers?: Record<string, string>; // Custom headers
|
|
611
|
+
requireValidate?: boolean; // Validate template data
|
|
612
|
+
}
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
### IMailTemplateEngine Interface
|
|
616
|
+
|
|
617
|
+
```typescript
|
|
618
|
+
interface IMailTemplateEngine {
|
|
619
|
+
// Render a template with data
|
|
620
|
+
render(opts: {
|
|
621
|
+
templateData?: string;
|
|
622
|
+
templateName?: string;
|
|
623
|
+
data: Record<string, any>;
|
|
624
|
+
requireValidate?: boolean;
|
|
625
|
+
}): string;
|
|
626
|
+
|
|
627
|
+
// Register a new template
|
|
628
|
+
registerTemplate(opts: { name: string; content: string; options?: Partial<ITemplate> }): void;
|
|
629
|
+
|
|
630
|
+
// Validate template data
|
|
631
|
+
validateTemplateData(opts: { template: string; data: Record<string, any> }): {
|
|
632
|
+
isValid: boolean;
|
|
633
|
+
missingKeys: string[];
|
|
634
|
+
allKeys: string[];
|
|
635
|
+
};
|
|
636
|
+
|
|
637
|
+
// Get a registered template
|
|
638
|
+
getTemplate(name: string): ITemplate | undefined;
|
|
639
|
+
|
|
640
|
+
// List all registered templates
|
|
641
|
+
listTemplates(): ITemplate[];
|
|
642
|
+
|
|
643
|
+
// Check if template exists
|
|
644
|
+
hasTemplate(name: string): boolean;
|
|
645
|
+
|
|
646
|
+
// Remove a template
|
|
647
|
+
removeTemplate(name: string): boolean;
|
|
648
|
+
}
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
## Best Practices
|
|
652
|
+
|
|
653
|
+
1. **Always bind configuration before registering MailComponent**: The component will throw an error if `MailKeys.MAIL_OPTIONS` is not bound.
|
|
654
|
+
|
|
655
|
+
2. **Use environment variables for sensitive data**: Never hardcode SMTP credentials or API keys in your code.
|
|
656
|
+
|
|
657
|
+
3. **Choose the right queue executor**:
|
|
658
|
+
- Use `direct` for development or low-volume applications
|
|
659
|
+
- Use `internal-queue` for single-instance applications with moderate volume
|
|
660
|
+
- Use `bullmq` for distributed systems or high-volume applications
|
|
661
|
+
|
|
662
|
+
4. **Validate templates in development**: Use `requireValidate: true` during development to catch missing template variables early.
|
|
663
|
+
|
|
664
|
+
5. **Handle send failures gracefully**: Always check the `success` field in `IMailSendResult` and implement proper error handling.
|
|
665
|
+
|
|
666
|
+
6. **Use batch sending for bulk operations**: The `sendBatch` method with controlled concurrency prevents overwhelming your mail server.
|
|
667
|
+
|
|
668
|
+
7. **Verify transport on startup**: Call `mailService.verify()` during application startup to ensure mail configuration is correct.
|
|
669
|
+
|
|
670
|
+
## See Also
|
|
671
|
+
|
|
672
|
+
- **Related Concepts:**
|
|
673
|
+
- [Components Overview](/guides/core-concepts/components) - Component system basics
|
|
674
|
+
- [Services](/guides/core-concepts/services) - Using mail service in business logic
|
|
675
|
+
|
|
676
|
+
- **Other Components:**
|
|
677
|
+
- [Components Index](./index) - All built-in components
|
|
678
|
+
|
|
679
|
+
- **References:**
|
|
680
|
+
- [Queue Helper](/references/helpers/queue) - Background job processing
|
|
681
|
+
|
|
682
|
+
- **External Resources:**
|
|
683
|
+
- [Nodemailer Documentation](https://nodemailer.com/) - Email library
|
|
684
|
+
- [Handlebars Documentation](https://handlebarsjs.com/) - Template engine
|
|
685
|
+
|
|
686
|
+
- **Best Practices:**
|
|
687
|
+
- [Security Guidelines](/best-practices/security-guidelines) - Email security
|
|
@@ -33,3 +33,19 @@ When the component is active, it adds the `requestId` and `RequestSpyMiddleware`
|
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
This feature is essential for building production-ready applications with proper logging and traceability.
|
|
36
|
+
|
|
37
|
+
## See Also
|
|
38
|
+
|
|
39
|
+
- **Related Concepts:**
|
|
40
|
+
- [Components Overview](/guides/core-concepts/components) - Component system basics
|
|
41
|
+
- [Middlewares](/references/base/middlewares) - Request middleware system
|
|
42
|
+
|
|
43
|
+
- **Other Components:**
|
|
44
|
+
- [Components Index](./index) - All built-in components
|
|
45
|
+
|
|
46
|
+
- **References:**
|
|
47
|
+
- [Logger Helper](/references/helpers/logger) - Logging utilities
|
|
48
|
+
|
|
49
|
+
- **Best Practices:**
|
|
50
|
+
- [Troubleshooting Tips](/best-practices/troubleshooting-tips) - Debugging with request IDs
|
|
51
|
+
- [Deployment Strategies](/best-practices/deployment-strategies) - Production logging
|