@flusys/nestjs-email 4.0.1 → 4.1.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
CHANGED
|
@@ -1,97 +1,94 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @flusys/nestjs-email
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
> Production-grade email management for NestJS — multi-provider (SMTP, SendGrid, Mailgun), database-driven template engine with `{{variable}}` interpolation, company scoping, and multi-tenant support.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@flusys/nestjs-email)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nestjs.com/)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://nodejs.org/)
|
|
10
|
+
|
|
11
|
+
---
|
|
6
12
|
|
|
7
13
|
## Table of Contents
|
|
8
14
|
|
|
9
15
|
- [Overview](#overview)
|
|
16
|
+
- [Features](#features)
|
|
17
|
+
- [Compatibility](#compatibility)
|
|
10
18
|
- [Installation](#installation)
|
|
11
|
-
- [
|
|
12
|
-
- [Module
|
|
19
|
+
- [Quick Start](#quick-start)
|
|
20
|
+
- [Module Registration](#module-registration)
|
|
21
|
+
- [forRoot (Sync)](#forroot-sync)
|
|
22
|
+
- [forRootAsync (Factory)](#forrootasync-factory)
|
|
23
|
+
- [forRootAsync (Class)](#forrootasync-class)
|
|
24
|
+
- [Configuration Reference](#configuration-reference)
|
|
25
|
+
- [Feature Toggles](#feature-toggles)
|
|
26
|
+
- [API Endpoints](#api-endpoints)
|
|
13
27
|
- [Entities](#entities)
|
|
14
|
-
- [Interfaces](#interfaces)
|
|
15
28
|
- [Email Providers](#email-providers)
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
- [
|
|
24
|
-
- [
|
|
29
|
+
- [SMTP (Default)](#smtp-default)
|
|
30
|
+
- [SendGrid](#sendgrid)
|
|
31
|
+
- [Mailgun](#mailgun)
|
|
32
|
+
- [Custom Provider](#custom-provider)
|
|
33
|
+
- [Template Engine](#template-engine)
|
|
34
|
+
- [Exported Services](#exported-services)
|
|
35
|
+
- [Sending Emails Programmatically](#sending-emails-programmatically)
|
|
36
|
+
- [Troubleshooting](#troubleshooting)
|
|
37
|
+
- [License](#license)
|
|
25
38
|
|
|
26
39
|
---
|
|
27
40
|
|
|
28
41
|
## Overview
|
|
29
42
|
|
|
30
|
-
`@flusys/nestjs-email` provides
|
|
43
|
+
`@flusys/nestjs-email` provides a complete email management system. Email provider configurations and templates are stored in the database — no code changes are needed to add new email templates or switch providers. Providers are loaded dynamically, so you only install the SDK for the providers you use.
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
- **Email Templates** - Variable interpolation with `{{variableName}}` syntax
|
|
34
|
-
- **HTML/Plain Text** - Support via `isHtml` toggle
|
|
35
|
-
- **Multi-Tenant Support** - Company-scoped configurations and templates
|
|
36
|
-
- **Attachments** - File attachment support
|
|
45
|
+
---
|
|
37
46
|
|
|
38
|
-
|
|
47
|
+
## Features
|
|
39
48
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
- **Multi-provider** — SMTP (nodemailer), SendGrid, Mailgun with a pluggable custom provider interface
|
|
50
|
+
- **Database-driven templates** — Templates stored in PostgreSQL with `{{variable}}` interpolation
|
|
51
|
+
- **HTML XSS protection** — All variable values are HTML-escaped before interpolation
|
|
52
|
+
- **Provider caching** — SHA-256 config hash prevents duplicate provider instances
|
|
53
|
+
- **Test email** — Verify a provider configuration before using it in production
|
|
54
|
+
- **Company scoping** — Optional `companyId` on configs and templates for multi-company setups
|
|
55
|
+
- **Multi-tenant** — Per-tenant DataSource isolation via the DataSource Provider pattern
|
|
56
|
+
- **Attachments** — Base64-encoded file attachments supported
|
|
47
57
|
|
|
48
58
|
---
|
|
49
59
|
|
|
50
|
-
##
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
npm install @flusys/nestjs-email
|
|
60
|
+
## Compatibility
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
62
|
+
| Package | Version |
|
|
63
|
+
|---------|---------|
|
|
64
|
+
| `@flusys/nestjs-core` | `^4.0.0` |
|
|
65
|
+
| `@flusys/nestjs-shared` | `^4.0.0` |
|
|
66
|
+
| `nodemailer` | `^6.0.0` |
|
|
67
|
+
| `@sendgrid/mail` | `^8.0.0` *(optional)* |
|
|
68
|
+
| `mailgun.js` | `^10.0.0` *(optional)* |
|
|
69
|
+
| Node.js | `>= 18.x` |
|
|
60
70
|
|
|
61
71
|
---
|
|
62
72
|
|
|
63
|
-
##
|
|
73
|
+
## Installation
|
|
64
74
|
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
export const EMAIL_MODULE_OPTIONS = 'EMAIL_MODULE_OPTIONS';
|
|
75
|
+
```bash
|
|
76
|
+
npm install @flusys/nestjs-email @flusys/nestjs-shared @flusys/nestjs-core
|
|
68
77
|
|
|
69
|
-
|
|
70
|
-
|
|
78
|
+
# Provider-specific SDKs (install only what you use)
|
|
79
|
+
npm install nodemailer # SMTP (recommended default)
|
|
80
|
+
npm install @sendgrid/mail # SendGrid
|
|
81
|
+
npm install mailgun.js form-data # Mailgun
|
|
71
82
|
```
|
|
72
83
|
|
|
73
84
|
---
|
|
74
85
|
|
|
75
|
-
##
|
|
76
|
-
|
|
77
|
-
### Module Options Interface
|
|
86
|
+
## Quick Start
|
|
78
87
|
|
|
79
|
-
|
|
80
|
-
interface IEmailModuleConfig extends IDataSourceServiceOptions {
|
|
81
|
-
defaultProvider?: string; // Default provider type
|
|
82
|
-
rateLimitPerMinute?: number; // Rate limiting
|
|
83
|
-
enableLogging?: boolean; // Enable debug logging
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
interface EmailModuleOptions extends IDynamicModuleConfig {
|
|
87
|
-
bootstrapAppConfig?: IBootstrapAppConfig;
|
|
88
|
-
config?: IEmailModuleConfig;
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Basic Setup
|
|
88
|
+
### Minimal Setup (SMTP, Single Database)
|
|
93
89
|
|
|
94
90
|
```typescript
|
|
91
|
+
import { Module } from '@nestjs/common';
|
|
95
92
|
import { EmailModule } from '@flusys/nestjs-email';
|
|
96
93
|
|
|
97
94
|
@Module({
|
|
@@ -106,11 +103,11 @@ import { EmailModule } from '@flusys/nestjs-email';
|
|
|
106
103
|
config: {
|
|
107
104
|
defaultDatabaseConfig: {
|
|
108
105
|
type: 'postgres',
|
|
109
|
-
host:
|
|
110
|
-
port: 5432,
|
|
111
|
-
username:
|
|
112
|
-
password:
|
|
113
|
-
database:
|
|
106
|
+
host: process.env.DB_HOST,
|
|
107
|
+
port: Number(process.env.DB_PORT ?? 5432),
|
|
108
|
+
username: process.env.DB_USER,
|
|
109
|
+
password: process.env.DB_PASSWORD,
|
|
110
|
+
database: process.env.DB_NAME,
|
|
114
111
|
},
|
|
115
112
|
},
|
|
116
113
|
}),
|
|
@@ -119,25 +116,36 @@ import { EmailModule } from '@flusys/nestjs-email';
|
|
|
119
116
|
export class AppModule {}
|
|
120
117
|
```
|
|
121
118
|
|
|
122
|
-
|
|
119
|
+
After startup, create an email config via the API and then send emails using `EmailSendService`.
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Module Registration
|
|
124
|
+
|
|
125
|
+
### forRoot (Sync)
|
|
123
126
|
|
|
124
127
|
```typescript
|
|
125
128
|
EmailModule.forRoot({
|
|
126
129
|
global: true,
|
|
127
130
|
includeController: true,
|
|
128
131
|
bootstrapAppConfig: {
|
|
129
|
-
databaseMode: 'single',
|
|
130
|
-
enableCompanyFeature: true
|
|
132
|
+
databaseMode: 'single', // 'single' | 'multi-tenant'
|
|
133
|
+
enableCompanyFeature: false, // true = company-scoped templates & configs
|
|
131
134
|
},
|
|
132
135
|
config: {
|
|
133
|
-
defaultDatabaseConfig: {
|
|
136
|
+
defaultDatabaseConfig: { /* TypeORM DataSourceOptions */ },
|
|
137
|
+
defaultProvider: 'smtp', // Optional: default provider type
|
|
138
|
+
rateLimitPerMinute: 100, // Optional: rate limit on send endpoint
|
|
139
|
+
enableLogging: false, // Optional: debug logging
|
|
134
140
|
},
|
|
135
|
-
})
|
|
141
|
+
})
|
|
136
142
|
```
|
|
137
143
|
|
|
138
|
-
###
|
|
144
|
+
### forRootAsync (Factory)
|
|
139
145
|
|
|
140
146
|
```typescript
|
|
147
|
+
import { ConfigService } from '@nestjs/config';
|
|
148
|
+
|
|
141
149
|
EmailModule.forRootAsync({
|
|
142
150
|
global: true,
|
|
143
151
|
includeController: true,
|
|
@@ -146,882 +154,346 @@ EmailModule.forRootAsync({
|
|
|
146
154
|
enableCompanyFeature: true,
|
|
147
155
|
},
|
|
148
156
|
imports: [ConfigModule],
|
|
149
|
-
useFactory:
|
|
150
|
-
defaultDatabaseConfig:
|
|
157
|
+
useFactory: (configService: ConfigService) => ({
|
|
158
|
+
defaultDatabaseConfig: {
|
|
159
|
+
type: 'postgres',
|
|
160
|
+
host: configService.get('DB_HOST'),
|
|
161
|
+
port: configService.get<number>('DB_PORT'),
|
|
162
|
+
username: configService.get('DB_USER'),
|
|
163
|
+
password: configService.get('DB_PASSWORD'),
|
|
164
|
+
database: configService.get('DB_NAME'),
|
|
165
|
+
},
|
|
151
166
|
}),
|
|
152
167
|
inject: [ConfigService],
|
|
153
|
-
})
|
|
168
|
+
})
|
|
154
169
|
```
|
|
155
170
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
## Entities
|
|
159
|
-
|
|
160
|
-
### Entity Groups
|
|
171
|
+
### forRootAsync (Class)
|
|
161
172
|
|
|
162
173
|
```typescript
|
|
163
|
-
|
|
164
|
-
export const EmailCoreEntities = [EmailConfig, EmailTemplate];
|
|
165
|
-
|
|
166
|
-
// Company-specific entities
|
|
167
|
-
export const EmailCompanyEntities = [EmailConfigWithCompany, EmailTemplateWithCompany];
|
|
174
|
+
import { EmailOptionsFactory, IEmailModuleConfig } from '@flusys/nestjs-email';
|
|
168
175
|
|
|
169
|
-
|
|
170
|
-
export
|
|
171
|
-
|
|
176
|
+
@Injectable()
|
|
177
|
+
export class MyEmailConfigFactory implements EmailOptionsFactory {
|
|
178
|
+
createEmailOptions(): IEmailModuleConfig {
|
|
179
|
+
return { defaultDatabaseConfig: { /* ... */ } };
|
|
180
|
+
}
|
|
181
|
+
createOptions() { return this.createEmailOptions(); }
|
|
172
182
|
}
|
|
173
183
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
184
|
+
EmailModule.forRootAsync({
|
|
185
|
+
bootstrapAppConfig: { databaseMode: 'single', enableCompanyFeature: false },
|
|
186
|
+
useClass: MyEmailConfigFactory,
|
|
187
|
+
})
|
|
177
188
|
```
|
|
178
189
|
|
|
179
190
|
---
|
|
180
191
|
|
|
181
|
-
##
|
|
182
|
-
|
|
183
|
-
### Provider Configuration Interfaces
|
|
184
|
-
|
|
185
|
-
```typescript
|
|
186
|
-
interface ISmtpTlsConfig {
|
|
187
|
-
rejectUnauthorized?: boolean; // Reject unauthorized certs (default: true)
|
|
188
|
-
minVersion?: 'TLSv1.2' | 'TLSv1.3'; // Min TLS version (default: 'TLSv1.2')
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
interface ISmtpConfig {
|
|
192
|
-
host: string;
|
|
193
|
-
port: number;
|
|
194
|
-
secure?: boolean;
|
|
195
|
-
auth?: { user: string; pass: string };
|
|
196
|
-
tls?: ISmtpTlsConfig; // TLS configuration
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
interface ISendGridConfig {
|
|
200
|
-
apiKey: string;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
interface IMailgunConfig {
|
|
204
|
-
apiKey: string;
|
|
205
|
-
domain: string;
|
|
206
|
-
region?: 'us' | 'eu';
|
|
207
|
-
}
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Email Template Schema Interfaces
|
|
192
|
+
## Configuration Reference
|
|
211
193
|
|
|
212
194
|
```typescript
|
|
213
|
-
interface
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
required?: boolean;
|
|
217
|
-
defaultValue?: string;
|
|
218
|
-
description?: string;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
interface IEmailSection {
|
|
222
|
-
id: string;
|
|
223
|
-
type: 'header' | 'body' | 'footer';
|
|
224
|
-
name: string;
|
|
225
|
-
blocks: IEmailBlock[];
|
|
226
|
-
order: number;
|
|
227
|
-
style?: IEmailSectionStyle;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
interface IEmailSectionStyle {
|
|
231
|
-
backgroundColor?: string;
|
|
232
|
-
padding?: string;
|
|
233
|
-
maxWidth?: string;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
interface IEmailBlock {
|
|
237
|
-
id: string;
|
|
238
|
-
type: 'text' | 'image' | 'button' | 'divider' | 'html';
|
|
239
|
-
order: number;
|
|
240
|
-
content: Record<string, any>;
|
|
241
|
-
style?: IEmailBlockStyle;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
interface IEmailBlockStyle {
|
|
245
|
-
textAlign?: 'left' | 'center' | 'right';
|
|
246
|
-
padding?: string;
|
|
247
|
-
margin?: string;
|
|
248
|
-
backgroundColor?: string;
|
|
249
|
-
}
|
|
195
|
+
interface IEmailModuleConfig extends IDataSourceServiceOptions {
|
|
196
|
+
/** Optional: default provider type when no config is specified */
|
|
197
|
+
defaultProvider?: 'smtp' | 'sendgrid' | 'mailgun';
|
|
250
198
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
backgroundColor?: string;
|
|
254
|
-
fontFamily?: string;
|
|
255
|
-
baseTextColor?: string;
|
|
256
|
-
}
|
|
199
|
+
/** Optional: max emails per minute (default: unlimited) */
|
|
200
|
+
rateLimitPerMinute?: number;
|
|
257
201
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
version: string;
|
|
261
|
-
name: string;
|
|
262
|
-
subject: string;
|
|
263
|
-
preheader?: string;
|
|
264
|
-
sections: IEmailSection[];
|
|
265
|
-
variables?: IEmailTemplateVariable[];
|
|
266
|
-
settings?: IEmailSettings;
|
|
202
|
+
/** Optional: enable debug logging for send operations */
|
|
203
|
+
enableLogging?: boolean;
|
|
267
204
|
}
|
|
268
205
|
```
|
|
269
206
|
|
|
270
207
|
---
|
|
271
208
|
|
|
272
|
-
##
|
|
273
|
-
|
|
274
|
-
### Provider Types
|
|
209
|
+
## Feature Toggles
|
|
275
210
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
MAILGUN = 'mailgun',
|
|
281
|
-
}
|
|
282
|
-
```
|
|
211
|
+
| Feature | Config Key | Default | Effect |
|
|
212
|
+
|---------|-----------|---------|--------|
|
|
213
|
+
| Company scoping | `enableCompanyFeature: true` | `false` | Uses `EmailConfigWithCompany` and `EmailTemplateWithCompany` entities; filters all queries by `companyId` |
|
|
214
|
+
| Multi-tenant | `databaseMode: 'multi-tenant'` | `'single'` | Creates per-tenant DataSource connections |
|
|
283
215
|
|
|
284
|
-
|
|
216
|
+
---
|
|
285
217
|
|
|
286
|
-
|
|
218
|
+
## API Endpoints
|
|
287
219
|
|
|
288
|
-
|
|
289
|
-
{
|
|
290
|
-
provider: 'smtp',
|
|
291
|
-
config: {
|
|
292
|
-
host: 'smtp.gmail.com',
|
|
293
|
-
port: 587,
|
|
294
|
-
secure: false, // true for port 465
|
|
295
|
-
auth: { user: 'your@gmail.com', pass: 'app-password' },
|
|
296
|
-
tls: {
|
|
297
|
-
rejectUnauthorized: true, // Certificate validation (default: true)
|
|
298
|
-
minVersion: 'TLSv1.2', // Minimum TLS version
|
|
299
|
-
},
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
```
|
|
220
|
+
All endpoints use **POST**. All require JWT authentication unless noted.
|
|
303
221
|
|
|
304
|
-
###
|
|
222
|
+
### Email Config — `POST /email/email-config/*`
|
|
305
223
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
224
|
+
| Endpoint | Permission | Description |
|
|
225
|
+
|----------|-----------|-------------|
|
|
226
|
+
| `POST /email/email-config/insert` | `email-config.create` | Create a provider configuration |
|
|
227
|
+
| `POST /email/email-config/get-all` | `email-config.read` | List all configs |
|
|
228
|
+
| `POST /email/email-config/get/:id` | `email-config.read` | Get config by ID |
|
|
229
|
+
| `POST /email/email-config/update` | `email-config.update` | Update config |
|
|
230
|
+
| `POST /email/email-config/delete` | `email-config.delete` | Delete config |
|
|
231
|
+
| `POST /email/email-config/test` | `email-config.create` | Send a test email to verify config |
|
|
232
|
+
| `POST /email/email-config/set-default` | `email-config.update` | Set as default provider |
|
|
313
233
|
|
|
314
|
-
###
|
|
234
|
+
### Email Templates — `POST /email/email-template/*`
|
|
315
235
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
region: 'us', // 'us' or 'eu'
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
```
|
|
236
|
+
| Endpoint | Permission | Description |
|
|
237
|
+
|----------|-----------|-------------|
|
|
238
|
+
| `POST /email/email-template/insert` | `email-template.create` | Create a template |
|
|
239
|
+
| `POST /email/email-template/get-all` | `email-template.read` | List all templates |
|
|
240
|
+
| `POST /email/email-template/get/:id` | `email-template.read` | Get template by ID |
|
|
241
|
+
| `POST /email/email-template/update` | `email-template.update` | Update template |
|
|
242
|
+
| `POST /email/email-template/delete` | `email-template.delete` | Delete template |
|
|
327
243
|
|
|
328
|
-
###
|
|
244
|
+
### Email Send — `POST /email/send/*`
|
|
329
245
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
healthCheck(): Promise<boolean>;
|
|
335
|
-
initialize?(config: any): Promise<void>;
|
|
336
|
-
close?(): Promise<void>;
|
|
337
|
-
}
|
|
246
|
+
| Endpoint | Permission | Description |
|
|
247
|
+
|----------|-----------|-------------|
|
|
248
|
+
| `POST /email/send/template` | `email-config.create` | Send using a stored template |
|
|
249
|
+
| `POST /email/send/raw` | `email-config.create` | Send raw HTML email |
|
|
338
250
|
|
|
339
|
-
|
|
340
|
-
to: string | string[];
|
|
341
|
-
cc?: string | string[];
|
|
342
|
-
bcc?: string | string[];
|
|
343
|
-
subject: string;
|
|
344
|
-
html?: string;
|
|
345
|
-
text?: string;
|
|
346
|
-
from?: string;
|
|
347
|
-
fromName?: string;
|
|
348
|
-
replyTo?: string;
|
|
349
|
-
attachments?: IEmailAttachment[];
|
|
350
|
-
}
|
|
251
|
+
---
|
|
351
252
|
|
|
352
|
-
|
|
353
|
-
filename: string;
|
|
354
|
-
content: Buffer | string;
|
|
355
|
-
contentType?: string;
|
|
356
|
-
encoding?: string;
|
|
357
|
-
}
|
|
253
|
+
## Entities
|
|
358
254
|
|
|
359
|
-
|
|
360
|
-
success: boolean;
|
|
361
|
-
messageId?: string;
|
|
362
|
-
error?: string;
|
|
363
|
-
}
|
|
364
|
-
```
|
|
255
|
+
### Core Entities (always registered)
|
|
365
256
|
|
|
366
|
-
|
|
257
|
+
| Entity | Table | Description |
|
|
258
|
+
|--------|-------|-------------|
|
|
259
|
+
| `EmailConfig` | `email_config` | Provider configuration (SMTP credentials, SendGrid API key, etc.) |
|
|
260
|
+
| `EmailTemplate` | `email_template` | Email templates with `{{variable}}` placeholders |
|
|
367
261
|
|
|
368
|
-
|
|
262
|
+
### Company Feature Entities (`enableCompanyFeature: true`)
|
|
369
263
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
EmailProviderRegistry.register(EmailProviderTypeEnum.MAILGUN, MailgunProvider);
|
|
375
|
-
|
|
376
|
-
// Registry methods
|
|
377
|
-
EmailProviderRegistry.register(name, ProviderClass); // Register provider
|
|
378
|
-
EmailProviderRegistry.get(name); // Get provider class
|
|
379
|
-
EmailProviderRegistry.has(name); // Check if registered
|
|
380
|
-
EmailProviderRegistry.getAll(); // List all providers
|
|
381
|
-
EmailProviderRegistry.clear(); // Clear registry
|
|
382
|
-
```
|
|
264
|
+
| Entity | Table | Description |
|
|
265
|
+
|--------|-------|-------------|
|
|
266
|
+
| `EmailConfigWithCompany` | `email_config` | Same as EmailConfig + `companyId` and `branchId` columns |
|
|
267
|
+
| `EmailTemplateWithCompany` | `email_template` | Same as EmailTemplate + `companyId` column |
|
|
383
268
|
|
|
384
|
-
|
|
269
|
+
#### Register Entities in TypeORM
|
|
385
270
|
|
|
386
271
|
```typescript
|
|
387
|
-
|
|
388
|
-
async initialize(config: any): Promise<void> {
|
|
389
|
-
// Initialize connection
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
async sendEmail(options: IEmailSendOptions): Promise<IEmailSendResult> {
|
|
393
|
-
// Send email
|
|
394
|
-
return { success: true, messageId: 'xxx' };
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
async sendBulkEmails(options: IEmailSendOptions[]): Promise<IEmailSendResult[]> {
|
|
398
|
-
return Promise.all(options.map(opt => this.sendEmail(opt)));
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
async healthCheck(): Promise<boolean> {
|
|
402
|
-
return true;
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
async close(): Promise<void> {
|
|
406
|
-
// Cleanup connections
|
|
407
|
-
}
|
|
408
|
-
}
|
|
272
|
+
import { EmailModule } from '@flusys/nestjs-email';
|
|
409
273
|
|
|
410
|
-
|
|
411
|
-
|
|
274
|
+
TypeOrmModule.forRoot({
|
|
275
|
+
entities: [
|
|
276
|
+
...EmailModule.getEntities({ enableCompanyFeature: true }),
|
|
277
|
+
// other entities
|
|
278
|
+
],
|
|
279
|
+
})
|
|
412
280
|
```
|
|
413
281
|
|
|
414
282
|
---
|
|
415
283
|
|
|
416
|
-
## Email
|
|
417
|
-
|
|
418
|
-
### EmailConfig Entity
|
|
419
|
-
|
|
420
|
-
```typescript
|
|
421
|
-
interface IEmailConfig {
|
|
422
|
-
id: string;
|
|
423
|
-
name: string; // e.g., 'default', 'marketing'
|
|
424
|
-
provider: EmailProviderTypeEnum;
|
|
425
|
-
config: Record<string, any>; // Provider-specific config
|
|
426
|
-
fromEmail: string | null;
|
|
427
|
-
fromName: string | null;
|
|
428
|
-
isActive: boolean;
|
|
429
|
-
isDefault: boolean; // Auto-selected when emailConfigId not provided
|
|
430
|
-
companyId?: string | null; // When company feature enabled
|
|
431
|
-
}
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### Default Configuration Resolution
|
|
435
|
-
|
|
436
|
-
When `emailConfigId` is not provided:
|
|
437
|
-
1. Find config with `isDefault: true` and `isActive: true`
|
|
438
|
-
2. Fall back to oldest active config
|
|
284
|
+
## Email Providers
|
|
439
285
|
|
|
440
|
-
###
|
|
286
|
+
### SMTP (Default)
|
|
441
287
|
|
|
442
|
-
|
|
443
|
-
import { EmailProviderConfigService } from '@flusys/nestjs-email';
|
|
288
|
+
Create an `EmailConfig` record with provider `smtp`:
|
|
444
289
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
config: {
|
|
451
|
-
host:
|
|
452
|
-
port: 587,
|
|
453
|
-
|
|
290
|
+
```json
|
|
291
|
+
POST /email/email-config/insert
|
|
292
|
+
{
|
|
293
|
+
"name": "Company SMTP",
|
|
294
|
+
"provider": "smtp",
|
|
295
|
+
"config": {
|
|
296
|
+
"host": "smtp.gmail.com",
|
|
297
|
+
"port": 587,
|
|
298
|
+
"secure": false,
|
|
299
|
+
"user": "noreply@example.com",
|
|
300
|
+
"password": "app-password"
|
|
454
301
|
},
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
---
|
|
461
|
-
|
|
462
|
-
## Email Templates
|
|
463
|
-
|
|
464
|
-
### EmailTemplate Entity
|
|
465
|
-
|
|
466
|
-
```typescript
|
|
467
|
-
interface IEmailTemplate {
|
|
468
|
-
id: string;
|
|
469
|
-
name: string;
|
|
470
|
-
slug: string; // URL-friendly identifier
|
|
471
|
-
description: string | null;
|
|
472
|
-
subject: string; // Supports {{variables}}
|
|
473
|
-
schema: Record<string, unknown>;
|
|
474
|
-
htmlContent: string;
|
|
475
|
-
textContent: string | null;
|
|
476
|
-
schemaVersion: number; // Auto-incremented on schema change
|
|
477
|
-
isActive: boolean;
|
|
478
|
-
isHtml: boolean; // true=HTML, false=plain text
|
|
479
|
-
metadata: Record<string, unknown> | null;
|
|
480
|
-
companyId?: string | null;
|
|
302
|
+
"fromEmail": "noreply@example.com",
|
|
303
|
+
"fromName": "My App",
|
|
304
|
+
"isDefault": true
|
|
481
305
|
}
|
|
482
306
|
```
|
|
483
307
|
|
|
484
|
-
###
|
|
308
|
+
### SendGrid
|
|
485
309
|
|
|
486
|
-
|
|
487
|
-
import { EmailTemplateService } from '@flusys/nestjs-email';
|
|
488
|
-
|
|
489
|
-
await templateService.insert({
|
|
490
|
-
name: 'Welcome Email',
|
|
491
|
-
slug: 'welcome-email',
|
|
492
|
-
subject: 'Welcome to {{appName}}, {{userName}}!',
|
|
493
|
-
isHtml: true,
|
|
494
|
-
htmlContent: `
|
|
495
|
-
<h1>Welcome, {{userName}}!</h1>
|
|
496
|
-
<p>Thank you for joining {{appName}}.</p>
|
|
497
|
-
<a href="{{verificationLink}}">Verify Email</a>
|
|
498
|
-
`,
|
|
499
|
-
textContent: 'Welcome! Verify: {{verificationLink}}',
|
|
500
|
-
schema: {},
|
|
501
|
-
isActive: true,
|
|
502
|
-
}, user);
|
|
503
|
-
```
|
|
310
|
+
Install `@sendgrid/mail` first, then create a config:
|
|
504
311
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
312
|
+
```json
|
|
313
|
+
POST /email/email-config/insert
|
|
314
|
+
{
|
|
315
|
+
"name": "SendGrid Production",
|
|
316
|
+
"provider": "sendgrid",
|
|
317
|
+
"config": { "apiKey": "SG.xxxxxxxxxxxx" },
|
|
318
|
+
"fromEmail": "noreply@example.com",
|
|
319
|
+
"fromName": "My App",
|
|
320
|
+
"isDefault": true
|
|
321
|
+
}
|
|
511
322
|
```
|
|
512
323
|
|
|
513
|
-
###
|
|
514
|
-
|
|
515
|
-
- **`isHtml: true`** - Sends `htmlContent` as HTML, `textContent` as fallback
|
|
516
|
-
- **`isHtml: false`** - Sends `textContent` only (no HTML)
|
|
517
|
-
|
|
518
|
-
---
|
|
519
|
-
|
|
520
|
-
## Email Sending
|
|
324
|
+
### Mailgun
|
|
521
325
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
```typescript
|
|
525
|
-
import { EmailSendService } from '@flusys/nestjs-email';
|
|
326
|
+
Install `mailgun.js form-data` first, then create a config:
|
|
526
327
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
// Send using template
|
|
537
|
-
const result = await emailSendService.sendTemplateEmail({
|
|
538
|
-
templateSlug: 'welcome-email',
|
|
539
|
-
to: 'user@example.com',
|
|
540
|
-
variables: {
|
|
541
|
-
userName: 'John',
|
|
542
|
-
appName: 'My App',
|
|
328
|
+
```json
|
|
329
|
+
POST /email/email-config/insert
|
|
330
|
+
{
|
|
331
|
+
"name": "Mailgun",
|
|
332
|
+
"provider": "mailgun",
|
|
333
|
+
"config": {
|
|
334
|
+
"apiKey": "key-xxxxxxxxxxxx",
|
|
335
|
+
"domain": "mg.example.com",
|
|
336
|
+
"region": "us"
|
|
543
337
|
},
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
to: 'user@example.com',
|
|
549
|
-
subject: 'Your Invoice',
|
|
550
|
-
html: '<p>Invoice attached.</p>',
|
|
551
|
-
attachments: [{
|
|
552
|
-
filename: 'invoice.pdf',
|
|
553
|
-
content: base64Content,
|
|
554
|
-
contentType: 'application/pdf',
|
|
555
|
-
}],
|
|
556
|
-
}, user);
|
|
338
|
+
"fromEmail": "noreply@example.com",
|
|
339
|
+
"fromName": "My App",
|
|
340
|
+
"isDefault": true
|
|
341
|
+
}
|
|
557
342
|
```
|
|
558
343
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
## DTOs
|
|
344
|
+
### Custom Provider
|
|
562
345
|
|
|
563
|
-
|
|
346
|
+
Implement `IEmailProvider` and register it with `StorageProviderRegistry`:
|
|
564
347
|
|
|
565
348
|
```typescript
|
|
566
|
-
|
|
567
|
-
class BaseEmailDto {
|
|
568
|
-
to: string | string[]; // Recipient(s)
|
|
569
|
-
cc?: string | string[]; // CC recipients
|
|
570
|
-
bcc?: string | string[]; // BCC recipients
|
|
571
|
-
from?: string; // Sender email
|
|
572
|
-
fromName?: string; // Sender name
|
|
573
|
-
replyTo?: string; // Reply-to address
|
|
574
|
-
emailConfigId?: string; // Email config to use
|
|
575
|
-
attachments?: EmailAttachmentDto[];
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
class SendEmailDto extends BaseEmailDto {
|
|
579
|
-
subject: string;
|
|
580
|
-
html: string;
|
|
581
|
-
text?: string;
|
|
582
|
-
}
|
|
349
|
+
import { IEmailProvider, EmailProviderRegistry } from '@flusys/nestjs-email';
|
|
583
350
|
|
|
584
|
-
class
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
emailConfigId: string;
|
|
592
|
-
recipient: string;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
class EmailAttachmentDto {
|
|
596
|
-
filename: string;
|
|
597
|
-
content: string; // Base64 encoded
|
|
598
|
-
contentType?: string;
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
class EmailSendResultDto {
|
|
602
|
-
success: boolean;
|
|
603
|
-
messageId?: string;
|
|
604
|
-
error?: string;
|
|
351
|
+
class MyCustomProvider implements IEmailProvider {
|
|
352
|
+
async send(options: IEmailSendOptions): Promise<void> {
|
|
353
|
+
// custom sending logic
|
|
354
|
+
}
|
|
355
|
+
async testConnection(): Promise<boolean> {
|
|
356
|
+
return true;
|
|
357
|
+
}
|
|
605
358
|
}
|
|
606
|
-
```
|
|
607
|
-
|
|
608
|
-
### Custom Validators
|
|
609
359
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
```typescript
|
|
613
|
-
// Validates single email or array of emails
|
|
614
|
-
@IsEmailOrEmailArray()
|
|
615
|
-
to: string | string[];
|
|
616
|
-
|
|
617
|
-
// Requires either templateId or templateSlug
|
|
618
|
-
@RequireTemplateIdOrSlug()
|
|
619
|
-
templateId?: string;
|
|
360
|
+
// Register before module initialization
|
|
361
|
+
EmailProviderRegistry.register('custom', MyCustomProvider);
|
|
620
362
|
```
|
|
621
363
|
|
|
622
364
|
---
|
|
623
365
|
|
|
624
|
-
##
|
|
366
|
+
## Template Engine
|
|
625
367
|
|
|
626
|
-
|
|
368
|
+
Templates use `{{variableName}}` syntax. All values are HTML-escaped automatically.
|
|
627
369
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
### getOtpEmailFormat(otp, userName?)
|
|
639
|
-
|
|
640
|
-
Generates styled HTML for OTP verification emails.
|
|
641
|
-
|
|
642
|
-
```typescript
|
|
643
|
-
const html = getOtpEmailFormat(123456, 'John');
|
|
644
|
-
// Returns formatted HTML with OTP code prominently displayed
|
|
370
|
+
**Create a template:**
|
|
371
|
+
```json
|
|
372
|
+
POST /email/email-template/insert
|
|
373
|
+
{
|
|
374
|
+
"name": "Welcome Email",
|
|
375
|
+
"slug": "welcome",
|
|
376
|
+
"subject": "Welcome to {{appName}}, {{userName}}!",
|
|
377
|
+
"html": "<h1>Hello {{userName}}</h1><p>Welcome to <strong>{{appName}}</strong>.</p><p><a href=\"{{loginUrl}}\">Login here</a></p>",
|
|
378
|
+
"variables": ["appName", "userName", "loginUrl"]
|
|
379
|
+
}
|
|
645
380
|
```
|
|
646
381
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
382
|
+
**Send using the template:**
|
|
383
|
+
```json
|
|
384
|
+
POST /email/send/template
|
|
385
|
+
{
|
|
386
|
+
"templateSlug": "welcome",
|
|
387
|
+
"to": "user@example.com",
|
|
388
|
+
"variables": {
|
|
389
|
+
"appName": "My App",
|
|
390
|
+
"userName": "John Doe",
|
|
391
|
+
"loginUrl": "https://app.example.com/login"
|
|
392
|
+
}
|
|
393
|
+
}
|
|
654
394
|
```
|
|
655
395
|
|
|
656
396
|
---
|
|
657
397
|
|
|
658
|
-
##
|
|
659
|
-
|
|
660
|
-
All endpoints use POST (RPC pattern) and require JWT authentication.
|
|
661
|
-
|
|
662
|
-
### Email Configuration
|
|
663
|
-
|
|
664
|
-
| Endpoint | Description |
|
|
665
|
-
|----------|-------------|
|
|
666
|
-
| `POST /email/email-config/insert` | Create configuration |
|
|
667
|
-
| `POST /email/email-config/get/:id` | Get by ID |
|
|
668
|
-
| `POST /email/email-config/get-all` | Get all (paginated) |
|
|
669
|
-
| `POST /email/email-config/update` | Update |
|
|
670
|
-
| `POST /email/email-config/delete` | Delete |
|
|
398
|
+
## Exported Services
|
|
671
399
|
|
|
672
|
-
|
|
400
|
+
These services are exported by `EmailModule` and injectable in your application:
|
|
673
401
|
|
|
674
|
-
|
|
|
675
|
-
|
|
676
|
-
| `
|
|
677
|
-
| `
|
|
678
|
-
| `
|
|
679
|
-
| `
|
|
680
|
-
| `
|
|
681
|
-
| `POST /email/email-template/delete` | Delete |
|
|
402
|
+
| Service | Description |
|
|
403
|
+
|---------|-------------|
|
|
404
|
+
| `EmailSendService` | Send emails via template slug or raw HTML |
|
|
405
|
+
| `EmailTemplateService` | CRUD for email templates |
|
|
406
|
+
| `EmailProviderConfigService` | CRUD for provider configurations |
|
|
407
|
+
| `EmailConfigService` | Exposes runtime config (provider defaults, rate limits) |
|
|
408
|
+
| `EmailDataSourceProvider` | Dynamic TypeORM DataSource resolution per request |
|
|
682
409
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
| Endpoint | Description |
|
|
686
|
-
|----------|-------------|
|
|
687
|
-
| `POST /email/send/direct` | Send direct email |
|
|
688
|
-
| `POST /email/send/template` | Send using template |
|
|
689
|
-
| `POST /email/send/test` | Test configuration |
|
|
690
|
-
|
|
691
|
-
### Example Requests
|
|
692
|
-
|
|
693
|
-
```bash
|
|
694
|
-
# Send template email
|
|
695
|
-
curl -X POST http://localhost:3000/email/send/template \
|
|
696
|
-
-H "Authorization: Bearer <token>" \
|
|
697
|
-
-H "Content-Type: application/json" \
|
|
698
|
-
-d '{
|
|
699
|
-
"templateSlug": "welcome-email",
|
|
700
|
-
"to": "user@example.com",
|
|
701
|
-
"variables": { "userName": "John" }
|
|
702
|
-
}'
|
|
703
|
-
|
|
704
|
-
# Test configuration
|
|
705
|
-
curl -X POST http://localhost:3000/email/send/test \
|
|
706
|
-
-H "Authorization: Bearer <token>" \
|
|
707
|
-
-d '{ "emailConfigId": "uuid", "recipient": "test@example.com" }'
|
|
708
|
-
```
|
|
709
|
-
|
|
710
|
-
### Controller Permissions
|
|
711
|
-
|
|
712
|
-
Controllers use permission-based security via `createApiController`:
|
|
713
|
-
|
|
714
|
-
```typescript
|
|
715
|
-
// Email Config permissions
|
|
716
|
-
EMAIL_CONFIG_PERMISSIONS.CREATE // 'email-config.create'
|
|
717
|
-
EMAIL_CONFIG_PERMISSIONS.READ // 'email-config.read'
|
|
718
|
-
EMAIL_CONFIG_PERMISSIONS.UPDATE // 'email-config.update'
|
|
719
|
-
EMAIL_CONFIG_PERMISSIONS.DELETE // 'email-config.delete'
|
|
720
|
-
|
|
721
|
-
// Email Template permissions
|
|
722
|
-
EMAIL_TEMPLATE_PERMISSIONS.CREATE // 'email-template.create'
|
|
723
|
-
EMAIL_TEMPLATE_PERMISSIONS.READ // 'email-template.read'
|
|
724
|
-
EMAIL_TEMPLATE_PERMISSIONS.UPDATE // 'email-template.update'
|
|
725
|
-
EMAIL_TEMPLATE_PERMISSIONS.DELETE // 'email-template.delete'
|
|
726
|
-
|
|
727
|
-
// Email Send permission
|
|
728
|
-
'email.send'
|
|
729
|
-
```
|
|
410
|
+
> **Note:** Always use `@Inject(ServiceClass)` explicitly — esbuild bundling loses TypeScript metadata.
|
|
730
411
|
|
|
731
412
|
---
|
|
732
413
|
|
|
733
|
-
##
|
|
414
|
+
## Sending Emails Programmatically
|
|
734
415
|
|
|
735
|
-
|
|
416
|
+
Inject `EmailSendService` to send emails from other services:
|
|
736
417
|
|
|
737
418
|
```typescript
|
|
738
|
-
import {
|
|
739
|
-
import { setupSwaggerDocs } from '@flusys/nestjs-core/docs';
|
|
740
|
-
|
|
741
|
-
// In your bootstrap function
|
|
742
|
-
setupSwaggerDocs(app, emailSwaggerConfig(bootstrapAppConfig));
|
|
743
|
-
```
|
|
419
|
+
import { EmailSendService } from '@flusys/nestjs-email';
|
|
744
420
|
|
|
745
|
-
|
|
421
|
+
@Injectable()
|
|
422
|
+
export class UserService {
|
|
423
|
+
constructor(
|
|
424
|
+
@Inject(EmailSendService) private readonly emailSendService: EmailSendService,
|
|
425
|
+
) {}
|
|
426
|
+
|
|
427
|
+
async sendWelcomeEmail(user: { email: string; name: string }): Promise<void> {
|
|
428
|
+
await this.emailSendService.sendTemplateEmail({
|
|
429
|
+
templateSlug: 'welcome',
|
|
430
|
+
to: user.email,
|
|
431
|
+
variables: { userName: user.name, appName: 'My App' },
|
|
432
|
+
});
|
|
433
|
+
}
|
|
746
434
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
435
|
+
async sendRawEmail(): Promise<void> {
|
|
436
|
+
await this.emailSendService.sendRawEmail({
|
|
437
|
+
to: 'recipient@example.com',
|
|
438
|
+
subject: 'Hello',
|
|
439
|
+
html: '<p>Hello world</p>',
|
|
440
|
+
attachments: [
|
|
441
|
+
{
|
|
442
|
+
filename: 'report.pdf',
|
|
443
|
+
content: base64EncodedPdfString,
|
|
444
|
+
contentType: 'application/pdf',
|
|
445
|
+
},
|
|
446
|
+
],
|
|
447
|
+
});
|
|
448
|
+
}
|
|
758
449
|
}
|
|
759
450
|
```
|
|
760
451
|
|
|
761
|
-
The Swagger documentation automatically adapts based on `enableCompanyFeature`:
|
|
762
|
-
- When enabled: Shows company isolation and multi-tenant features
|
|
763
|
-
- When disabled: Hides companyId fields from schemas
|
|
764
|
-
|
|
765
452
|
---
|
|
766
453
|
|
|
767
|
-
##
|
|
768
|
-
|
|
769
|
-
### Company Isolation
|
|
770
|
-
|
|
771
|
-
When `enableCompanyFeature: true`:
|
|
772
|
-
- Email configs are company-scoped
|
|
773
|
-
- Templates are company-scoped
|
|
774
|
-
- Users can only access their company's resources
|
|
454
|
+
## Troubleshooting
|
|
775
455
|
|
|
776
|
-
|
|
456
|
+
**`No default email config found`**
|
|
777
457
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
if (this.emailConfig.isCompanyFeatureEnabled() && user?.companyId) {
|
|
783
|
-
query.andWhere('emailTemplate.companyId = :companyId', {
|
|
784
|
-
companyId: user.companyId,
|
|
785
|
-
});
|
|
786
|
-
}
|
|
787
|
-
```
|
|
788
|
-
|
|
789
|
-
### Dynamic Entity Selection
|
|
790
|
-
|
|
791
|
-
```typescript
|
|
792
|
-
protected resolveEntity(): EntityTarget<EmailTemplateBase> {
|
|
793
|
-
return this.emailConfig.isCompanyFeatureEnabled()
|
|
794
|
-
? EmailTemplateWithCompany
|
|
795
|
-
: EmailTemplate;
|
|
796
|
-
}
|
|
458
|
+
Create at least one `EmailConfig` record and mark it as default:
|
|
459
|
+
```json
|
|
460
|
+
POST /email/email-config/set-default
|
|
461
|
+
{ "id": "your-config-id" }
|
|
797
462
|
```
|
|
798
463
|
|
|
799
464
|
---
|
|
800
465
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
### 1. Use Template Slugs
|
|
804
|
-
|
|
805
|
-
```typescript
|
|
806
|
-
// Use slug for stable references
|
|
807
|
-
await emailSendService.sendTemplateEmail({
|
|
808
|
-
templateSlug: 'welcome-email', // Stable
|
|
809
|
-
...
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
// Avoid hardcoded IDs
|
|
813
|
-
templateId: 'f47ac10b-58cc-4372-a567-...' // May change
|
|
814
|
-
```
|
|
815
|
-
|
|
816
|
-
### 2. Provide Both HTML and Text
|
|
817
|
-
|
|
818
|
-
```typescript
|
|
819
|
-
await templateService.insert({
|
|
820
|
-
isHtml: true,
|
|
821
|
-
htmlContent: '<h1>Thank you!</h1>',
|
|
822
|
-
textContent: 'Thank you!', // For clients that don't render HTML
|
|
823
|
-
});
|
|
824
|
-
```
|
|
466
|
+
**`Template not found`**
|
|
825
467
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
```typescript
|
|
829
|
-
const result = await emailSendService.sendTestEmail(
|
|
830
|
-
configId,
|
|
831
|
-
'admin@example.com',
|
|
832
|
-
user,
|
|
833
|
-
);
|
|
834
|
-
if (!result.success) throw new Error(result.error);
|
|
835
|
-
```
|
|
836
|
-
|
|
837
|
-
### 4. Use Variables for Personalization
|
|
838
|
-
|
|
839
|
-
```typescript
|
|
840
|
-
// Template
|
|
841
|
-
subject: 'Hello {{userName}}!'
|
|
842
|
-
|
|
843
|
-
// Sending
|
|
844
|
-
variables: { userName: 'John' }
|
|
845
|
-
```
|
|
468
|
+
Check the `templateSlug` matches exactly (case-sensitive). Use `POST /email/email-template/get-all` to list available templates.
|
|
846
469
|
|
|
847
470
|
---
|
|
848
471
|
|
|
849
|
-
|
|
472
|
+
**SMTP connection refused**
|
|
850
473
|
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
import { EmailModule } from '@flusys/nestjs-email';
|
|
856
|
-
|
|
857
|
-
// Services
|
|
858
|
-
import {
|
|
859
|
-
EmailConfigService, // Module configuration service
|
|
860
|
-
EmailProviderConfigService, // CRUD for email provider configs
|
|
861
|
-
EmailTemplateService,
|
|
862
|
-
EmailSendService,
|
|
863
|
-
EmailDataSourceProvider,
|
|
864
|
-
} from '@flusys/nestjs-email/services';
|
|
865
|
-
|
|
866
|
-
// Entities
|
|
867
|
-
import {
|
|
868
|
-
EmailConfig,
|
|
869
|
-
EmailConfigBase,
|
|
870
|
-
EmailConfigWithCompany,
|
|
871
|
-
EmailTemplate,
|
|
872
|
-
EmailTemplateBase,
|
|
873
|
-
EmailTemplateWithCompany,
|
|
874
|
-
EmailCoreEntities,
|
|
875
|
-
EmailCompanyEntities,
|
|
876
|
-
getEmailEntitiesByConfig,
|
|
877
|
-
} from '@flusys/nestjs-email/entities';
|
|
878
|
-
|
|
879
|
-
// DTOs
|
|
880
|
-
import {
|
|
881
|
-
CreateEmailConfigDto,
|
|
882
|
-
UpdateEmailConfigDto,
|
|
883
|
-
EmailConfigResponseDto,
|
|
884
|
-
CreateEmailTemplateDto,
|
|
885
|
-
UpdateEmailTemplateDto,
|
|
886
|
-
EmailTemplateResponseDto,
|
|
887
|
-
EmailAttachmentDto,
|
|
888
|
-
SendEmailDto,
|
|
889
|
-
SendTemplateEmailDto,
|
|
890
|
-
TestEmailDto,
|
|
891
|
-
EmailSendResultDto,
|
|
892
|
-
} from '@flusys/nestjs-email/dtos';
|
|
893
|
-
|
|
894
|
-
// Interfaces
|
|
895
|
-
import {
|
|
896
|
-
IEmailProvider,
|
|
897
|
-
IEmailSendOptions,
|
|
898
|
-
IEmailSendResult,
|
|
899
|
-
IEmailAttachment,
|
|
900
|
-
IEmailProviderConfig,
|
|
901
|
-
IEmailConfig,
|
|
902
|
-
IEmailTemplate,
|
|
903
|
-
IEmailTemplateVariable,
|
|
904
|
-
IEmailSection,
|
|
905
|
-
IEmailSectionStyle,
|
|
906
|
-
IEmailBlock,
|
|
907
|
-
IEmailBlockStyle,
|
|
908
|
-
IEmailSettings,
|
|
909
|
-
IEmailSchema,
|
|
910
|
-
ISmtpConfig,
|
|
911
|
-
ISmtpTlsConfig,
|
|
912
|
-
ISendGridConfig,
|
|
913
|
-
IMailgunConfig,
|
|
914
|
-
IEmailModuleConfig,
|
|
915
|
-
EmailModuleOptions,
|
|
916
|
-
EmailModuleAsyncOptions,
|
|
917
|
-
EmailOptionsFactory,
|
|
918
|
-
} from '@flusys/nestjs-email/interfaces';
|
|
919
|
-
|
|
920
|
-
// Providers
|
|
921
|
-
import {
|
|
922
|
-
EmailProviderRegistry,
|
|
923
|
-
EmailFactoryService,
|
|
924
|
-
SmtpProvider,
|
|
925
|
-
SendGridProvider,
|
|
926
|
-
MailgunProvider,
|
|
927
|
-
} from '@flusys/nestjs-email/providers';
|
|
928
|
-
|
|
929
|
-
// Controllers
|
|
930
|
-
import {
|
|
931
|
-
EmailConfigController,
|
|
932
|
-
EmailTemplateController,
|
|
933
|
-
EmailSendController,
|
|
934
|
-
} from '@flusys/nestjs-email/controllers';
|
|
935
|
-
|
|
936
|
-
// Enums
|
|
937
|
-
import { EmailProviderTypeEnum } from '@flusys/nestjs-email/enums';
|
|
938
|
-
|
|
939
|
-
// Constants
|
|
940
|
-
import {
|
|
941
|
-
EMAIL_MODULE_OPTIONS,
|
|
942
|
-
DEFAULT_FROM_NAME,
|
|
943
|
-
} from '@flusys/nestjs-email/config';
|
|
944
|
-
|
|
945
|
-
// Utilities
|
|
946
|
-
import {
|
|
947
|
-
getOtpEmailFormat,
|
|
948
|
-
getResetPasswordEmailFormat,
|
|
949
|
-
} from '@flusys/nestjs-email/utils';
|
|
950
|
-
|
|
951
|
-
// Swagger Config
|
|
952
|
-
import { emailSwaggerConfig } from '@flusys/nestjs-email/docs';
|
|
474
|
+
Use the test endpoint first:
|
|
475
|
+
```json
|
|
476
|
+
POST /email/email-config/test
|
|
477
|
+
{ "id": "your-config-id", "to": "test@example.com" }
|
|
953
478
|
```
|
|
954
479
|
|
|
955
|
-
|
|
480
|
+
For Gmail, enable "App Passwords" and use the app password, not your account password.
|
|
956
481
|
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
```typescript
|
|
960
|
-
class EmailConfigService {
|
|
961
|
-
isCompanyFeatureEnabled(): boolean;
|
|
962
|
-
getDatabaseMode(): DatabaseMode;
|
|
963
|
-
isMultiTenant(): boolean;
|
|
964
|
-
getDefaultFromName(): string;
|
|
965
|
-
getOptions(): EmailModuleOptions;
|
|
966
|
-
}
|
|
967
|
-
```
|
|
968
|
-
|
|
969
|
-
#### EmailProviderConfigService
|
|
970
|
-
|
|
971
|
-
```typescript
|
|
972
|
-
class EmailProviderConfigService {
|
|
973
|
-
// Standard CRUD (inherited from RequestScopedApiService)
|
|
974
|
-
insert(dto, user): Promise<EmailConfigBase>;
|
|
975
|
-
getById(id, user): Promise<EmailConfigBase | null>;
|
|
976
|
-
getAll(filterDto, user): Promise<{ list: EmailConfigBase[]; total: number }>;
|
|
977
|
-
update(dto, user): Promise<EmailConfigBase>;
|
|
978
|
-
delete(id, user): Promise<boolean>;
|
|
979
|
-
|
|
980
|
-
// Custom methods
|
|
981
|
-
findByIdDirect(id): Promise<EmailConfigBase | null>;
|
|
982
|
-
getDefaultConfig(user?): Promise<EmailConfigBase | null>;
|
|
983
|
-
getConfigByProvider(provider, user?): Promise<EmailConfigBase[]>;
|
|
984
|
-
}
|
|
985
|
-
```
|
|
482
|
+
---
|
|
986
483
|
|
|
987
|
-
|
|
484
|
+
**`No metadata for entity`**
|
|
988
485
|
|
|
486
|
+
Call `EmailModule.getEntities()` with the correct flags when registering `TypeOrmModule`:
|
|
989
487
|
```typescript
|
|
990
|
-
|
|
991
|
-
// Standard CRUD (inherited from RequestScopedApiService)
|
|
992
|
-
insert(dto, user): Promise<EmailTemplateBase>;
|
|
993
|
-
getById(id, user): Promise<EmailTemplateBase | null>;
|
|
994
|
-
getAll(filterDto, user): Promise<{ list: EmailTemplateBase[]; total: number }>;
|
|
995
|
-
update(dto, user): Promise<EmailTemplateBase>;
|
|
996
|
-
delete(id, user): Promise<boolean>;
|
|
997
|
-
|
|
998
|
-
// Custom methods
|
|
999
|
-
findByIdDirect(id): Promise<EmailTemplateBase | null>;
|
|
1000
|
-
findBySlug(slug, user?): Promise<EmailTemplateBase | null>;
|
|
1001
|
-
getActiveTemplates(user?): Promise<EmailTemplateBase[]>;
|
|
1002
|
-
}
|
|
488
|
+
entities: [...EmailModule.getEntities({ enableCompanyFeature: true })]
|
|
1003
489
|
```
|
|
1004
490
|
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
```typescript
|
|
1008
|
-
class EmailSendService {
|
|
1009
|
-
sendEmail(dto: SendEmailDto, user?): Promise<IEmailSendResult>;
|
|
1010
|
-
sendTemplateEmail(dto: SendTemplateEmailDto, user?): Promise<IEmailSendResult>;
|
|
1011
|
-
sendTestEmail(emailConfigId, recipient, user?): Promise<IEmailSendResult>;
|
|
1012
|
-
}
|
|
1013
|
-
```
|
|
491
|
+
---
|
|
1014
492
|
|
|
1015
|
-
|
|
493
|
+
## License
|
|
1016
494
|
|
|
1017
|
-
|
|
1018
|
-
class EmailFactoryService {
|
|
1019
|
-
createProvider(config: IEmailProviderConfig): Promise<IEmailProvider>;
|
|
1020
|
-
// Providers are cached by config hash for reuse
|
|
1021
|
-
// Connections cleaned up on module destroy
|
|
1022
|
-
}
|
|
1023
|
-
```
|
|
495
|
+
MIT © FLUSYS
|
|
1024
496
|
|
|
1025
497
|
---
|
|
1026
498
|
|
|
1027
|
-
**
|
|
499
|
+
> Part of the **FLUSYS** framework — a full-stack monorepo powering Angular 21 + NestJS 11 applications.
|