@breadstone/archipel-mcp 0.0.10 → 0.0.11
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/data/guides/ai-text-generation.md +361 -0
- package/data/guides/analytics-and-error-tracking.md +189 -0
- package/data/guides/authentication-and-authorization.md +657 -0
- package/data/guides/blob-storage.md +242 -0
- package/data/guides/caching.md +255 -0
- package/data/guides/cryptography-and-otp.md +240 -0
- package/data/guides/document-generation.md +174 -0
- package/data/guides/email-delivery.md +196 -0
- package/data/guides/esigning-integration.md +231 -0
- package/data/guides/getting-started.md +351 -0
- package/data/guides/implementing-ports.md +317 -0
- package/data/guides/index.md +61 -0
- package/data/guides/mcp-server.md +222 -0
- package/data/guides/openapi-and-feature-discovery.md +266 -0
- package/data/guides/payments-and-feature-gating.md +244 -0
- package/data/guides/resource-management.md +352 -0
- package/data/guides/telemetry-and-observability.md +190 -0
- package/data/guides/testing.md +319 -0
- package/data/guides/tsdoc-guidelines.md +45 -0
- package/data/packages/platform-openapi/api/Class.SwaggerFeatureDiscovery.md +10 -6
- package/package.json +1 -1
- package/src/GuidesLoader.d.ts +13 -0
- package/src/GuidesLoader.js +81 -0
- package/src/main.js +36 -198
- package/src/models/IGuideDoc.d.ts +15 -0
- package/src/models/IGuideDoc.js +3 -0
- package/src/tools/registerGetConfigPatternTool.d.ts +5 -0
- package/src/tools/registerGetConfigPatternTool.js +15 -0
- package/src/tools/registerGetDtoPatternTool.d.ts +5 -0
- package/src/tools/registerGetDtoPatternTool.js +15 -0
- package/src/tools/registerGetErrorHandlingPatternTool.d.ts +5 -0
- package/src/tools/registerGetErrorHandlingPatternTool.js +15 -0
- package/src/tools/registerGetGuardPatternTool.d.ts +5 -0
- package/src/tools/registerGetGuardPatternTool.js +15 -0
- package/src/tools/registerGetGuideTool.d.ts +6 -0
- package/src/tools/registerGetGuideTool.js +32 -0
- package/src/tools/registerGetMappingPatternTool.d.ts +5 -0
- package/src/tools/registerGetMappingPatternTool.js +34 -0
- package/src/tools/registerGetModulePatternTool.d.ts +5 -0
- package/src/tools/registerGetModulePatternTool.js +29 -0
- package/src/tools/registerGetPackageDocTool.d.ts +6 -0
- package/src/tools/registerGetPackageDocTool.js +43 -0
- package/src/tools/registerGetQueryPatternTool.d.ts +5 -0
- package/src/tools/registerGetQueryPatternTool.js +29 -0
- package/src/tools/registerGetRepositoryPatternTool.d.ts +5 -0
- package/src/tools/registerGetRepositoryPatternTool.js +29 -0
- package/src/tools/registerGetTestingPatternTool.d.ts +5 -0
- package/src/tools/registerGetTestingPatternTool.js +15 -0
- package/src/tools/registerListGuidesTool.d.ts +6 -0
- package/src/tools/registerListGuidesTool.js +22 -0
- package/src/tools/registerListPackagesTool.d.ts +6 -0
- package/src/tools/registerListPackagesTool.js +20 -0
- package/src/tools/registerSearchDocsTool.d.ts +6 -0
- package/src/tools/registerSearchDocsTool.js +35 -0
- package/src/tools/registerSearchGuidesTool.d.ts +6 -0
- package/src/tools/registerSearchGuidesTool.js +28 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Cryptography & OTP
|
|
3
|
+
description: Hash passwords with bcrypt, generate prefixed UUIDs, and implement TOTP-based multi-factor authentication with QR code enrollment.
|
|
4
|
+
order: 17
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Cryptography & OTP
|
|
8
|
+
|
|
9
|
+
This guide covers cryptographic utilities provided by `platform-cryptography`: password hashing with bcrypt, prefixed UUID generation, and TOTP-based one-time password flows for multi-factor authentication.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
yarn add @breadstone/archipel-platform-cryptography
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Password Hashing with Bcrypt
|
|
22
|
+
|
|
23
|
+
`BcryptService` wraps the `bcrypt` library to hash and verify passwords securely. It generates a unique salt per hash automatically.
|
|
24
|
+
|
|
25
|
+
### Hashing a Password
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
import { Injectable } from '@nestjs/common';
|
|
29
|
+
import { BcryptService } from '@breadstone/archipel-platform-cryptography';
|
|
30
|
+
|
|
31
|
+
@Injectable()
|
|
32
|
+
export class UserService {
|
|
33
|
+
private readonly _bcrypt: BcryptService;
|
|
34
|
+
|
|
35
|
+
constructor(bcrypt: BcryptService) {
|
|
36
|
+
this._bcrypt = bcrypt;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async createUser(email: string, password: string): Promise<void> {
|
|
40
|
+
const hashedPassword = await this._bcrypt.hash(password);
|
|
41
|
+
// Store hashedPassword in the database — never the plain password
|
|
42
|
+
await this._userRepository.create({ email, password: hashedPassword });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Verifying a Password
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
public async authenticate(email: string, password: string): Promise<boolean> {
|
|
51
|
+
const user = await this._userRepository.findByEmail(email);
|
|
52
|
+
if (!user) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return this._bcrypt.compare(password, user.password);
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`compare()` returns `true` if the plain-text password matches the stored hash.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Generating Prefixed UUIDs
|
|
65
|
+
|
|
66
|
+
`CryptoService` generates UUIDs with a configurable prefix. This is useful for creating human-readable identifiers that indicate their resource type.
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { Injectable } from '@nestjs/common';
|
|
70
|
+
import { CryptoService } from '@breadstone/archipel-platform-cryptography';
|
|
71
|
+
|
|
72
|
+
@Injectable()
|
|
73
|
+
export class OrderService {
|
|
74
|
+
private readonly _crypto: CryptoService;
|
|
75
|
+
|
|
76
|
+
constructor(crypto: CryptoService) {
|
|
77
|
+
this._crypto = crypto;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public createOrderId(): string {
|
|
81
|
+
return this._crypto.getRandomGuid('ord');
|
|
82
|
+
// Example: "ord-a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The prefix is required and must be a non-empty string. Use short, descriptive prefixes that represent the resource type:
|
|
88
|
+
|
|
89
|
+
| Resource | Prefix | Example |
|
|
90
|
+
| ------------ | ------ | ------------------------------------------ |
|
|
91
|
+
| Order | `ord` | `ord-550e8400-e29b-41d4-a716-446655440000` |
|
|
92
|
+
| Invoice | `inv` | `inv-6ba7b810-9dad-11d1-80b4-00c04fd430c8` |
|
|
93
|
+
| Subscription | `sub` | `sub-f47ac10b-58cc-4372-a567-0e02b2c3d479` |
|
|
94
|
+
| User | `usr` | `usr-7c9e6679-7425-40de-944b-e07fc1f90ae7` |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## TOTP Multi-Factor Authentication
|
|
99
|
+
|
|
100
|
+
`OtpService` implements TOTP (Time-Based One-Time Password) as defined in [RFC 6238](https://datatracker.ietf.org/doc/html/rfc6238). It supports QR code enrollment and token verification.
|
|
101
|
+
|
|
102
|
+
### Injecting OtpService
|
|
103
|
+
|
|
104
|
+
`OtpService` is provided behind the `OTP_SERVICE_TOKEN` injection token so consumers depend on the `IOtpService` interface:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { Module } from '@nestjs/common';
|
|
108
|
+
import { OtpService, OTP_SERVICE_TOKEN } from '@breadstone/archipel-platform-cryptography';
|
|
109
|
+
|
|
110
|
+
@Module({
|
|
111
|
+
providers: [
|
|
112
|
+
{
|
|
113
|
+
provide: OTP_SERVICE_TOKEN,
|
|
114
|
+
useClass: OtpService,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
exports: [OTP_SERVICE_TOKEN],
|
|
118
|
+
})
|
|
119
|
+
export class SecurityModule {}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Enrolling a User
|
|
123
|
+
|
|
124
|
+
Generate a secret and a QR code URI for the user's authenticator app:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
128
|
+
import { OTP_SERVICE_TOKEN, type IOtpService } from '@breadstone/archipel-platform-cryptography';
|
|
129
|
+
|
|
130
|
+
@Injectable()
|
|
131
|
+
export class MfaService {
|
|
132
|
+
private readonly _otp: IOtpService;
|
|
133
|
+
|
|
134
|
+
constructor(@Inject(OTP_SERVICE_TOKEN) otp: IOtpService) {
|
|
135
|
+
this._otp = otp;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public async enroll(userId: string, email: string): Promise<{ secret: string; uri: string }> {
|
|
139
|
+
const secret = this._otp.generateSecret();
|
|
140
|
+
const uri = this._otp.generateUri({
|
|
141
|
+
issuer: 'MyApp',
|
|
142
|
+
label: email,
|
|
143
|
+
secret: secret,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Store the secret securely alongside the user record
|
|
147
|
+
await this._userRepository.updateMfaSecret(userId, secret);
|
|
148
|
+
|
|
149
|
+
return { secret, uri };
|
|
150
|
+
// The URI can be encoded as a QR code for scanning with Google Authenticator, Authy, etc.
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Verifying a Token
|
|
156
|
+
|
|
157
|
+
Verify a 6-digit TOTP code entered by the user:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
public verifyToken(token: string, secret: string): boolean {
|
|
161
|
+
return this._otp.verify(token, secret);
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Verification uses a configurable tolerance window (`TOTP_EPOCH_TOLERANCE`). The default tolerance is **30 seconds** (±1 time step), meaning the current code and the immediately preceding/following codes are accepted. This compensates for minor clock drift between the server and the user's device.
|
|
166
|
+
|
|
167
|
+
### Tolerance Configuration
|
|
168
|
+
|
|
169
|
+
The `TOTP_EPOCH_TOLERANCE` constant controls the verification window:
|
|
170
|
+
|
|
171
|
+
| `TOTP_EPOCH_TOLERANCE` | Accepted range |
|
|
172
|
+
| ---------------------- | ----------------- |
|
|
173
|
+
| `0` | Current step only |
|
|
174
|
+
| `30` | ±1 step (±30 s) |
|
|
175
|
+
| `60` | ±2 steps (±60 s) |
|
|
176
|
+
| `90` | ±3 steps (±90 s) |
|
|
177
|
+
|
|
178
|
+
The default value is `30` (±1 step).
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Combining Services
|
|
183
|
+
|
|
184
|
+
A typical MFA login flow uses all three services:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
@Injectable()
|
|
188
|
+
export class AuthService {
|
|
189
|
+
private readonly _bcrypt: BcryptService;
|
|
190
|
+
private readonly _otp: IOtpService;
|
|
191
|
+
private readonly _crypto: CryptoService;
|
|
192
|
+
|
|
193
|
+
constructor(bcrypt: BcryptService, @Inject(OTP_SERVICE_TOKEN) otp: IOtpService, crypto: CryptoService) {
|
|
194
|
+
this._bcrypt = bcrypt;
|
|
195
|
+
this._otp = otp;
|
|
196
|
+
this._crypto = crypto;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public async login(email: string, password: string, totpCode?: string): Promise<IAuthResult> {
|
|
200
|
+
const user = await this._userRepository.findByEmail(email);
|
|
201
|
+
if (!user) {
|
|
202
|
+
throw new UnauthorizedException();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Step 1: Verify password
|
|
206
|
+
const validPassword = await this._bcrypt.compare(password, user.password);
|
|
207
|
+
if (!validPassword) {
|
|
208
|
+
throw new UnauthorizedException();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Step 2: Verify TOTP (if MFA is enabled)
|
|
212
|
+
if (user.mfaEnabled) {
|
|
213
|
+
if (!totpCode) {
|
|
214
|
+
return { status: 'mfa-required' };
|
|
215
|
+
}
|
|
216
|
+
const validTotp = this._otp.verify(totpCode, user.mfaSecret);
|
|
217
|
+
if (!validTotp) {
|
|
218
|
+
throw new UnauthorizedException('Invalid MFA code');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Step 3: Issue session
|
|
223
|
+
const sessionId = this._crypto.getRandomGuid('ses');
|
|
224
|
+
return { status: 'authenticated', sessionId };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Security Best Practices
|
|
232
|
+
|
|
233
|
+
| Practice | Why |
|
|
234
|
+
| ----------------------------------- | --------------------------------------------------------- |
|
|
235
|
+
| Never store plain-text passwords | Always use `BcryptService.hash()` before persisting |
|
|
236
|
+
| Store MFA secrets encrypted at rest | The TOTP secret is equivalent to a password |
|
|
237
|
+
| Use rate limiting on verification | Prevents brute-force attacks on 6-digit TOTP codes |
|
|
238
|
+
| Generate backup codes on enrollment | Users may lose access to their authenticator device |
|
|
239
|
+
| Log MFA events, not secrets | Never log secrets, tokens, or plain-text passwords |
|
|
240
|
+
| Use `OTP_SERVICE_TOKEN` for DI | Depend on `IOtpService` interface, not the concrete class |
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Document Generation
|
|
3
|
+
description: Generate PDF and DOCX documents from templates with variable substitution and image processing.
|
|
4
|
+
order: 11
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Document Generation
|
|
8
|
+
|
|
9
|
+
This guide covers generating documents with `platform-documents`: template setup, variable substitution, image processing, and rendering to PDF or DOCX.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
yarn add @breadstone/archipel-platform-documents
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Module Registration
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { Module } from '@nestjs/common';
|
|
25
|
+
import { DocumentModule } from '@breadstone/archipel-platform-documents';
|
|
26
|
+
|
|
27
|
+
@Module({
|
|
28
|
+
imports: [
|
|
29
|
+
DocumentModule.forRoot({
|
|
30
|
+
maxImageWidth: 1920,
|
|
31
|
+
maxImageHeight: 1080,
|
|
32
|
+
delimiters: { start: '[[', end: ']]' },
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
35
|
+
})
|
|
36
|
+
export class AppModule {}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Options
|
|
40
|
+
|
|
41
|
+
| Option | Default | Description |
|
|
42
|
+
| ---------------- | ------- | ------------------------------------------------- |
|
|
43
|
+
| `maxImageWidth` | `1920` | Maximum width for embedded images (in pixels) |
|
|
44
|
+
| `maxImageHeight` | `1080` | Maximum height for embedded images (in pixels) |
|
|
45
|
+
| `delimiters` | `[[ ]]` | Start and end delimiters for template variables |
|
|
46
|
+
| `debug` | `false` | Enable verbose logging for the rendering pipeline |
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## Creating Templates
|
|
51
|
+
|
|
52
|
+
Templates are document files (DOCX) with placeholder variables. By default, placeholders use double brackets:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
Dear [[customerName]],
|
|
56
|
+
|
|
57
|
+
Your order [[orderId]] has been confirmed.
|
|
58
|
+
Total: [[orderTotal]]
|
|
59
|
+
|
|
60
|
+
Thank you for your business.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
You can change the delimiter format:
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
DocumentModule.forRoot({
|
|
67
|
+
delimiters: { start: '{{', end: '}}' },
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Rendering Documents
|
|
74
|
+
|
|
75
|
+
Use `DocumentEngine` to render templates with data:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { Injectable } from '@nestjs/common';
|
|
79
|
+
import { DocumentEngine } from '@breadstone/archipel-platform-documents';
|
|
80
|
+
|
|
81
|
+
@Injectable()
|
|
82
|
+
export class InvoiceService {
|
|
83
|
+
private readonly _documentEngine: DocumentEngine;
|
|
84
|
+
|
|
85
|
+
constructor(documentEngine: DocumentEngine) {
|
|
86
|
+
this._documentEngine = documentEngine;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public async generateInvoice(orderId: string, data: Record<string, unknown>): Promise<Buffer> {
|
|
90
|
+
return this._documentEngine.render({
|
|
91
|
+
template: 'invoice-template.docx',
|
|
92
|
+
data: {
|
|
93
|
+
customerName: data['customerName'],
|
|
94
|
+
orderId,
|
|
95
|
+
orderTotal: data['total'],
|
|
96
|
+
date: new Date().toLocaleDateString(),
|
|
97
|
+
},
|
|
98
|
+
format: 'pdf', // or 'docx'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Supported Output Formats
|
|
105
|
+
|
|
106
|
+
| Format | Renderer | Description |
|
|
107
|
+
| ------ | ------------- | ------------------------------------- |
|
|
108
|
+
| `pdf` | PDF Renderer | Convert template to PDF |
|
|
109
|
+
| `docx` | DOCX Renderer | Keep as DOCX with filled placeholders |
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Image Processing
|
|
114
|
+
|
|
115
|
+
The `SharpImageProcessor` automatically optimizes embedded images:
|
|
116
|
+
|
|
117
|
+
- Resizes images that exceed `maxImageWidth` or `maxImageHeight`
|
|
118
|
+
- Preserves aspect ratio
|
|
119
|
+
- Compresses images for smaller file sizes
|
|
120
|
+
|
|
121
|
+
This is useful for templates that include dynamic images (logos, product photos, signatures).
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Serving Documents via HTTP
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { Controller, Get, Res, Param } from '@nestjs/common';
|
|
129
|
+
import { Response } from 'express';
|
|
130
|
+
|
|
131
|
+
@Controller('invoices')
|
|
132
|
+
export class InvoiceController {
|
|
133
|
+
private readonly _invoiceService: InvoiceService;
|
|
134
|
+
|
|
135
|
+
constructor(invoiceService: InvoiceService) {
|
|
136
|
+
this._invoiceService = invoiceService;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@Get(':orderId/pdf')
|
|
140
|
+
public async downloadInvoice(@Param('orderId') orderId: string, @Res() res: Response): Promise<void> {
|
|
141
|
+
const pdf = await this._invoiceService.generateInvoice(orderId, {
|
|
142
|
+
/* ... */
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
res.set({
|
|
146
|
+
'Content-Type': 'application/pdf',
|
|
147
|
+
'Content-Disposition': `attachment; filename="invoice-${orderId}.pdf"`,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
res.send(pdf);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Debug Mode
|
|
158
|
+
|
|
159
|
+
Enable debug logging to trace the rendering pipeline:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
DocumentModule.forRoot({
|
|
163
|
+
debug: true,
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
This logs each step of the rendering process: template loading, placeholder parsing, variable substitution, image processing, and final output generation.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Next Steps
|
|
172
|
+
|
|
173
|
+
- See the [Blob Storage](/guides/blob-storage) guide for storing generated documents
|
|
174
|
+
- Browse the [platform-documents API reference](/packages/platform-documents/api/) for complete method signatures
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Email Delivery
|
|
3
|
+
description: Send transactional emails with multiple providers, template engines, and email verification flows.
|
|
4
|
+
order: 8
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Email Delivery
|
|
8
|
+
|
|
9
|
+
This guide covers sending emails with `platform-mailing`: choosing a delivery provider, configuring template rendering, sending transactional emails, and integrating email verification flows.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
yarn add @breadstone/archipel-platform-mailing
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Choose a Delivery Strategy
|
|
22
|
+
|
|
23
|
+
`platform-mailing` supports multiple email providers. You select the strategy via environment variables — no code changes required to switch providers.
|
|
24
|
+
|
|
25
|
+
| Strategy | Value for `MAIL_DELIVERY_STRATEGY` | When to use |
|
|
26
|
+
| ------------ | ---------------------------------- | ------------------------------------ |
|
|
27
|
+
| **SMTP** | `smtp` | Any SMTP server (self-hosted, relay) |
|
|
28
|
+
| **Postmark** | `postmark` | Postmark transactional email |
|
|
29
|
+
| **Resend** | `resend` | Resend API |
|
|
30
|
+
| **SendGrid** | `sendgrid` | Twilio SendGrid |
|
|
31
|
+
| **Mailgun** | `mailgun` | Mailgun API |
|
|
32
|
+
| **Log** | `log` | Development only — logs to console |
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Module Registration
|
|
37
|
+
|
|
38
|
+
`MailModule` is a global module. Register it once in your root module:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { Module } from '@nestjs/common';
|
|
42
|
+
import { MailModule } from '@breadstone/archipel-platform-mailing';
|
|
43
|
+
|
|
44
|
+
@Module({
|
|
45
|
+
imports: [MailModule],
|
|
46
|
+
})
|
|
47
|
+
export class AppModule {}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The module reads all configuration from environment variables. No `register()` call is needed.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Configuration
|
|
55
|
+
|
|
56
|
+
### Core Variables
|
|
57
|
+
|
|
58
|
+
```env
|
|
59
|
+
MAIL_DELIVERY_STRATEGY=resend
|
|
60
|
+
MAIL_TEMPLATE_STRATEGY=file
|
|
61
|
+
MAIL_TEMPLATE_ENGINE_FORMAT=html
|
|
62
|
+
MAIL_FROM_ADDRESS=noreply@yourapp.com
|
|
63
|
+
MAIL_FROM_NAME=YourApp
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Provider-Specific Variables
|
|
67
|
+
|
|
68
|
+
#### SMTP
|
|
69
|
+
|
|
70
|
+
```env
|
|
71
|
+
MAIL_SMTP_HOST=smtp.example.com
|
|
72
|
+
MAIL_SMTP_PORT=587
|
|
73
|
+
MAIL_SMTP_USER=user@example.com
|
|
74
|
+
MAIL_SMTP_PASS=password
|
|
75
|
+
MAIL_SMTP_SECURE=true
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Resend
|
|
79
|
+
|
|
80
|
+
```env
|
|
81
|
+
MAIL_RESEND_API_KEY=re_...
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Postmark
|
|
85
|
+
|
|
86
|
+
```env
|
|
87
|
+
MAIL_POSTMARK_API_KEY=...
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### SendGrid
|
|
91
|
+
|
|
92
|
+
```env
|
|
93
|
+
MAIL_SENDGRID_API_KEY=SG...
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Mailgun
|
|
97
|
+
|
|
98
|
+
```env
|
|
99
|
+
MAIL_MAILGUN_API_KEY=key-...
|
|
100
|
+
MAIL_MAILGUN_DOMAIN=mg.yourapp.com
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Template Strategies
|
|
104
|
+
|
|
105
|
+
Templates can be loaded from the filesystem or from blob storage:
|
|
106
|
+
|
|
107
|
+
| Strategy | Value for `MAIL_TEMPLATE_STRATEGY` | Description |
|
|
108
|
+
| -------- | ---------------------------------- | ------------------------------------------- |
|
|
109
|
+
| **File** | `file` | Load templates from a local directory |
|
|
110
|
+
| **Blob** | `blob` | Load templates from `platform-blob-storage` |
|
|
111
|
+
|
|
112
|
+
For file-based templates, place your `.html` or `.txt` files in the configured template directory.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Sending Emails
|
|
117
|
+
|
|
118
|
+
Inject `MailService` to send emails with templates:
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
import { Injectable } from '@nestjs/common';
|
|
122
|
+
import { MailService } from '@breadstone/archipel-platform-mailing';
|
|
123
|
+
|
|
124
|
+
@Injectable()
|
|
125
|
+
export class NotificationService {
|
|
126
|
+
private readonly _mailService: MailService;
|
|
127
|
+
|
|
128
|
+
constructor(mailService: MailService) {
|
|
129
|
+
this._mailService = mailService;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
public async sendWelcomeEmail(email: string, userName: string): Promise<void> {
|
|
133
|
+
await this._mailService.send({
|
|
134
|
+
to: email,
|
|
135
|
+
subject: 'Welcome to YourApp',
|
|
136
|
+
template: 'welcome',
|
|
137
|
+
context: {
|
|
138
|
+
userName,
|
|
139
|
+
dashboardUrl: 'https://yourapp.com/dashboard',
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The `template` field is the template name (without extension). The `context` object is passed to the template engine for variable substitution.
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Email Verification
|
|
151
|
+
|
|
152
|
+
`MailVerificationService` provides a complete email verification flow:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
import { Injectable } from '@nestjs/common';
|
|
156
|
+
import { MailVerificationService } from '@breadstone/archipel-platform-mailing';
|
|
157
|
+
import { VerificationService } from '@breadstone/archipel-platform-authentication';
|
|
158
|
+
|
|
159
|
+
@Injectable()
|
|
160
|
+
export class RegistrationService {
|
|
161
|
+
private readonly _mailVerification: MailVerificationService;
|
|
162
|
+
private readonly _verification: VerificationService;
|
|
163
|
+
|
|
164
|
+
constructor(mailVerification: MailVerificationService, verification: VerificationService) {
|
|
165
|
+
this._mailVerification = mailVerification;
|
|
166
|
+
this._verification = verification;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
public async sendVerificationEmail(userId: string, email: string): Promise<void> {
|
|
170
|
+
const token = await this._verification.exhibitToken({ userId, email });
|
|
171
|
+
|
|
172
|
+
await this._mailVerification.sendVerification(email, token);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Health Checks
|
|
180
|
+
|
|
181
|
+
`MailHealthIndicator` is registered automatically and verifies connectivity to the configured mail provider. It appears in your `/health` endpoint alongside other health indicators.
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Development Tips
|
|
186
|
+
|
|
187
|
+
- Use `MAIL_DELIVERY_STRATEGY=log` during local development to see email content in the console without sending real emails.
|
|
188
|
+
- Use the **blob** template strategy when templates are shared across multiple services or managed by a CMS.
|
|
189
|
+
- Template variables use the format defined by the template engine. Place templates in a well-organized directory structure (e.g., `templates/mail/welcome.html`).
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Next Steps
|
|
194
|
+
|
|
195
|
+
- See the [Authentication & Authorization](/guides/authentication-and-authorization) guide for integrating email verification into the login flow
|
|
196
|
+
- Browse the [platform-mailing API reference](/packages/platform-mailing/api/) for complete method signatures
|