@flusys/nestjs-email 4.1.1 → 5.0.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 +93 -384
- package/cjs/config/message-keys.js +1 -38
- package/cjs/controllers/email-template.controller.js +9 -3
- package/cjs/docs/email-swagger.config.js +5 -3
- package/cjs/dtos/email-template.dto.js +1 -21
- package/cjs/entities/email-template.entity.js +1 -7
- package/cjs/providers/email-factory.service.js +5 -5
- package/cjs/providers/mailgun-provider.js +5 -2
- package/cjs/providers/sendgrid-provider.js +5 -2
- package/cjs/services/email-provider-config.service.js +0 -10
- package/cjs/services/email-template.service.js +0 -13
- package/config/message-keys.d.ts +0 -68
- package/dtos/email-template.dto.d.ts +0 -2
- package/entities/email-template.entity.d.ts +0 -1
- package/fesm/config/message-keys.js +1 -33
- package/fesm/controllers/email-template.controller.js +10 -4
- package/fesm/docs/email-swagger.config.js +5 -3
- package/fesm/dtos/email-template.dto.js +1 -21
- package/fesm/entities/email-template.entity.js +1 -7
- package/fesm/providers/email-factory.service.js +5 -5
- package/fesm/providers/mailgun-provider.js +5 -2
- package/fesm/providers/sendgrid-provider.js +5 -2
- package/fesm/services/email-provider-config.service.js +0 -10
- package/fesm/services/email-template.service.js +0 -13
- package/interfaces/email-template.interface.d.ts +0 -1
- package/package.json +3 -3
- package/services/email-provider-config.service.d.ts +0 -2
- package/services/email-template.service.d.ts +0 -1
package/README.md
CHANGED
|
@@ -1,72 +1,9 @@
|
|
|
1
1
|
# @flusys/nestjs-email
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Database-driven email system for NestJS — multi-provider (SMTP, SendGrid, Mailgun), template engine with `{{variable}}` interpolation, and company scoping.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@flusys/nestjs-email)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
|
-
[](https://nestjs.com/)
|
|
8
|
-
[](https://www.typescriptlang.org/)
|
|
9
|
-
[](https://nodejs.org/)
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## Table of Contents
|
|
14
|
-
|
|
15
|
-
- [Overview](#overview)
|
|
16
|
-
- [Features](#features)
|
|
17
|
-
- [Compatibility](#compatibility)
|
|
18
|
-
- [Installation](#installation)
|
|
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)
|
|
27
|
-
- [Entities](#entities)
|
|
28
|
-
- [Email Providers](#email-providers)
|
|
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)
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## Overview
|
|
42
|
-
|
|
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.
|
|
44
|
-
|
|
45
|
-
---
|
|
46
|
-
|
|
47
|
-
## Features
|
|
48
|
-
|
|
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
|
|
57
|
-
|
|
58
|
-
---
|
|
59
|
-
|
|
60
|
-
## Compatibility
|
|
61
|
-
|
|
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` |
|
|
70
7
|
|
|
71
8
|
---
|
|
72
9
|
|
|
@@ -75,20 +12,19 @@
|
|
|
75
12
|
```bash
|
|
76
13
|
npm install @flusys/nestjs-email @flusys/nestjs-shared @flusys/nestjs-core
|
|
77
14
|
|
|
78
|
-
# Provider
|
|
79
|
-
npm install nodemailer
|
|
80
|
-
npm install @sendgrid/mail
|
|
81
|
-
npm install mailgun.js form-data
|
|
15
|
+
# Provider SDKs — install only what you use
|
|
16
|
+
npm install nodemailer # SMTP (default, always safe to install)
|
|
17
|
+
npm install @sendgrid/mail # SendGrid
|
|
18
|
+
npm install mailgun.js form-data # Mailgun
|
|
82
19
|
```
|
|
83
20
|
|
|
84
|
-
|
|
21
|
+
## 1. Module Registration
|
|
85
22
|
|
|
86
|
-
|
|
23
|
+
### forRoot (sync)
|
|
87
24
|
|
|
88
|
-
|
|
25
|
+
#### Mode 1: Single Database
|
|
89
26
|
|
|
90
27
|
```typescript
|
|
91
|
-
import { Module } from '@nestjs/common';
|
|
92
28
|
import { EmailModule } from '@flusys/nestjs-email';
|
|
93
29
|
|
|
94
30
|
@Module({
|
|
@@ -102,9 +38,9 @@ import { EmailModule } from '@flusys/nestjs-email';
|
|
|
102
38
|
},
|
|
103
39
|
config: {
|
|
104
40
|
defaultDatabaseConfig: {
|
|
105
|
-
type: '
|
|
41
|
+
type: 'mysql',
|
|
106
42
|
host: process.env.DB_HOST,
|
|
107
|
-
port: Number(process.env.DB_PORT ??
|
|
43
|
+
port: Number(process.env.DB_PORT ?? 3306),
|
|
108
44
|
username: process.env.DB_USER,
|
|
109
45
|
password: process.env.DB_PASSWORD,
|
|
110
46
|
database: process.env.DB_NAME,
|
|
@@ -116,36 +52,40 @@ import { EmailModule } from '@flusys/nestjs-email';
|
|
|
116
52
|
export class AppModule {}
|
|
117
53
|
```
|
|
118
54
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
## Module Registration
|
|
124
|
-
|
|
125
|
-
### forRoot (Sync)
|
|
55
|
+
#### Mode 2: Multi-Tenant
|
|
126
56
|
|
|
127
57
|
```typescript
|
|
128
58
|
EmailModule.forRoot({
|
|
129
59
|
global: true,
|
|
130
60
|
includeController: true,
|
|
131
61
|
bootstrapAppConfig: {
|
|
132
|
-
databaseMode: '
|
|
133
|
-
enableCompanyFeature:
|
|
62
|
+
databaseMode: 'multi-tenant',
|
|
63
|
+
enableCompanyFeature: true,
|
|
134
64
|
},
|
|
135
65
|
config: {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
66
|
+
tenantDefaultDatabaseConfig: {
|
|
67
|
+
type: 'mysql',
|
|
68
|
+
host: process.env.TENANT_DB_HOST,
|
|
69
|
+
port: Number(process.env.TENANT_DB_PORT ?? 3306),
|
|
70
|
+
username: process.env.TENANT_DB_USER,
|
|
71
|
+
password: process.env.TENANT_DB_PASSWORD,
|
|
72
|
+
database: process.env.TENANT_DB_NAME,
|
|
73
|
+
},
|
|
74
|
+
tenants: [
|
|
75
|
+
{ id: 'tenant-a', database: 'tenant_a_db' },
|
|
76
|
+
{ id: 'tenant-b', database: 'tenant_b_db' },
|
|
77
|
+
],
|
|
140
78
|
},
|
|
141
|
-
})
|
|
79
|
+
});
|
|
142
80
|
```
|
|
143
81
|
|
|
144
|
-
### forRootAsync (
|
|
82
|
+
### forRootAsync (factory)
|
|
145
83
|
|
|
146
84
|
```typescript
|
|
147
|
-
import { ConfigService } from '@nestjs/config';
|
|
85
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
86
|
+
import { EmailModule, ITenantDatabaseConfig } from '@flusys/nestjs-email';
|
|
148
87
|
|
|
88
|
+
// Single database
|
|
149
89
|
EmailModule.forRootAsync({
|
|
150
90
|
global: true,
|
|
151
91
|
includeController: true,
|
|
@@ -154,346 +94,115 @@ EmailModule.forRootAsync({
|
|
|
154
94
|
enableCompanyFeature: true,
|
|
155
95
|
},
|
|
156
96
|
imports: [ConfigModule],
|
|
157
|
-
useFactory: (
|
|
97
|
+
useFactory: (config: ConfigService) => ({
|
|
158
98
|
defaultDatabaseConfig: {
|
|
159
|
-
type: '
|
|
160
|
-
host:
|
|
161
|
-
port:
|
|
162
|
-
username:
|
|
163
|
-
password:
|
|
164
|
-
database:
|
|
99
|
+
type: 'mysql',
|
|
100
|
+
host: config.get('DB_HOST'),
|
|
101
|
+
port: config.get<number>('DB_PORT'),
|
|
102
|
+
username: config.get('DB_USER'),
|
|
103
|
+
password: config.get('DB_PASSWORD'),
|
|
104
|
+
database: config.get('DB_NAME'),
|
|
165
105
|
},
|
|
166
106
|
}),
|
|
167
107
|
inject: [ConfigService],
|
|
168
|
-
})
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### forRootAsync (Class)
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
import { EmailOptionsFactory, IEmailModuleConfig } from '@flusys/nestjs-email';
|
|
175
|
-
|
|
176
|
-
@Injectable()
|
|
177
|
-
export class MyEmailConfigFactory implements EmailOptionsFactory {
|
|
178
|
-
createEmailOptions(): IEmailModuleConfig {
|
|
179
|
-
return { defaultDatabaseConfig: { /* ... */ } };
|
|
180
|
-
}
|
|
181
|
-
createOptions() { return this.createEmailOptions(); }
|
|
182
|
-
}
|
|
108
|
+
});
|
|
183
109
|
|
|
110
|
+
// Multi-tenant
|
|
184
111
|
EmailModule.forRootAsync({
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
112
|
+
global: true,
|
|
113
|
+
includeController: true,
|
|
114
|
+
bootstrapAppConfig: {
|
|
115
|
+
databaseMode: 'multi-tenant',
|
|
116
|
+
enableCompanyFeature: true,
|
|
117
|
+
},
|
|
118
|
+
imports: [ConfigModule],
|
|
119
|
+
useFactory: (config: ConfigService) => ({
|
|
120
|
+
tenantDefaultDatabaseConfig: {
|
|
121
|
+
type: 'mysql',
|
|
122
|
+
host: config.get('TENANT_DB_HOST'),
|
|
123
|
+
port: config.get<number>('TENANT_DB_PORT'),
|
|
124
|
+
username: config.get('TENANT_DB_USER'),
|
|
125
|
+
password: config.get('TENANT_DB_PASSWORD'),
|
|
126
|
+
database: config.get('TENANT_DB_NAME'),
|
|
127
|
+
},
|
|
128
|
+
tenants: config.get<ITenantDatabaseConfig[]>('TENANTS'),
|
|
129
|
+
}),
|
|
130
|
+
inject: [ConfigService],
|
|
131
|
+
});
|
|
205
132
|
```
|
|
206
133
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
## Feature Toggles
|
|
210
|
-
|
|
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 |
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## API Endpoints
|
|
219
|
-
|
|
220
|
-
All endpoints use **POST**. All require JWT authentication unless noted.
|
|
221
|
-
|
|
222
|
-
### Email Config — `POST /email/email-config/*`
|
|
223
|
-
|
|
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 |
|
|
233
|
-
|
|
234
|
-
### Email Templates — `POST /email/email-template/*`
|
|
235
|
-
|
|
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 |
|
|
243
|
-
|
|
244
|
-
### Email Send — `POST /email/send/*`
|
|
245
|
-
|
|
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 |
|
|
250
|
-
|
|
251
|
-
---
|
|
252
|
-
|
|
253
|
-
## Entities
|
|
254
|
-
|
|
255
|
-
### Core Entities (always registered)
|
|
256
|
-
|
|
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 |
|
|
261
|
-
|
|
262
|
-
### Company Feature Entities (`enableCompanyFeature: true`)
|
|
263
|
-
|
|
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 |
|
|
268
|
-
|
|
269
|
-
#### Register Entities in TypeORM
|
|
134
|
+
## 2. Register Entities
|
|
270
135
|
|
|
271
136
|
```typescript
|
|
272
|
-
import {
|
|
137
|
+
import { getEmailEntitiesByConfig } from '@flusys/nestjs-email/entities';
|
|
273
138
|
|
|
274
139
|
TypeOrmModule.forRoot({
|
|
275
140
|
entities: [
|
|
276
|
-
...
|
|
277
|
-
// other entities
|
|
141
|
+
...getEmailEntitiesByConfig(false), // match enableCompanyFeature in bootstrapAppConfig
|
|
278
142
|
],
|
|
279
|
-
})
|
|
143
|
+
});
|
|
280
144
|
```
|
|
281
145
|
|
|
282
|
-
|
|
146
|
+
| `enableCompanyFeature` | Entities registered |
|
|
147
|
+
| ---------------------- | ---------------------------------------------------- |
|
|
148
|
+
| `false` | `EmailConfig`, `EmailTemplate` |
|
|
149
|
+
| `true` | `EmailConfigWithCompany`, `EmailTemplateWithCompany` |
|
|
283
150
|
|
|
284
|
-
##
|
|
151
|
+
## 3. Send Emails
|
|
285
152
|
|
|
286
|
-
###
|
|
153
|
+
### Via template
|
|
287
154
|
|
|
288
|
-
Create
|
|
155
|
+
Create a template first:
|
|
289
156
|
|
|
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"
|
|
301
|
-
},
|
|
302
|
-
"fromEmail": "noreply@example.com",
|
|
303
|
-
"fromName": "My App",
|
|
304
|
-
"isDefault": true
|
|
305
|
-
}
|
|
306
|
-
```
|
|
307
|
-
|
|
308
|
-
### SendGrid
|
|
309
|
-
|
|
310
|
-
Install `@sendgrid/mail` first, then create a config:
|
|
311
|
-
|
|
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
|
-
}
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
### Mailgun
|
|
325
|
-
|
|
326
|
-
Install `mailgun.js form-data` first, then create a config:
|
|
327
|
-
|
|
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"
|
|
337
|
-
},
|
|
338
|
-
"fromEmail": "noreply@example.com",
|
|
339
|
-
"fromName": "My App",
|
|
340
|
-
"isDefault": true
|
|
341
|
-
}
|
|
342
|
-
```
|
|
343
|
-
|
|
344
|
-
### Custom Provider
|
|
345
|
-
|
|
346
|
-
Implement `IEmailProvider` and register it with `StorageProviderRegistry`:
|
|
347
|
-
|
|
348
|
-
```typescript
|
|
349
|
-
import { IEmailProvider, EmailProviderRegistry } from '@flusys/nestjs-email';
|
|
350
|
-
|
|
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
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Register before module initialization
|
|
361
|
-
EmailProviderRegistry.register('custom', MyCustomProvider);
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
---
|
|
365
|
-
|
|
366
|
-
## Template Engine
|
|
367
|
-
|
|
368
|
-
Templates use `{{variableName}}` syntax. All values are HTML-escaped automatically.
|
|
369
|
-
|
|
370
|
-
**Create a template:**
|
|
371
157
|
```json
|
|
372
158
|
POST /email/email-template/insert
|
|
373
159
|
{
|
|
374
|
-
"name": "Welcome
|
|
160
|
+
"name": "Welcome",
|
|
375
161
|
"slug": "welcome",
|
|
376
|
-
"subject": "Welcome
|
|
377
|
-
"
|
|
378
|
-
"
|
|
162
|
+
"subject": "Welcome, {{userName}}!",
|
|
163
|
+
"htmlContent": "<h1>Hello {{userName}}</h1><p>Welcome to {{appName}}.</p>",
|
|
164
|
+
"isHtml": true
|
|
379
165
|
}
|
|
380
166
|
```
|
|
381
167
|
|
|
382
|
-
|
|
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
|
-
}
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
---
|
|
397
|
-
|
|
398
|
-
## Exported Services
|
|
399
|
-
|
|
400
|
-
These services are exported by `EmailModule` and injectable in your application:
|
|
401
|
-
|
|
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 |
|
|
409
|
-
|
|
410
|
-
> **Note:** Always use `@Inject(ServiceClass)` explicitly — esbuild bundling loses TypeScript metadata.
|
|
411
|
-
|
|
412
|
-
---
|
|
413
|
-
|
|
414
|
-
## Sending Emails Programmatically
|
|
415
|
-
|
|
416
|
-
Inject `EmailSendService` to send emails from other services:
|
|
168
|
+
Then send it from your service:
|
|
417
169
|
|
|
418
170
|
```typescript
|
|
419
171
|
import { EmailSendService } from '@flusys/nestjs-email';
|
|
420
172
|
|
|
421
173
|
@Injectable()
|
|
422
174
|
export class UserService {
|
|
423
|
-
constructor(
|
|
424
|
-
@Inject(EmailSendService) private readonly emailSendService: EmailSendService,
|
|
425
|
-
) {}
|
|
175
|
+
constructor(@Inject(EmailSendService) private readonly emailSend: EmailSendService) {}
|
|
426
176
|
|
|
427
|
-
async
|
|
428
|
-
await this.
|
|
429
|
-
templateSlug: 'welcome',
|
|
177
|
+
async sendWelcome(user: { email: string; name: string }): Promise<void> {
|
|
178
|
+
await this.emailSend.sendTemplateEmail({
|
|
179
|
+
templateSlug: 'welcome', // or templateId: 'uuid'
|
|
430
180
|
to: user.email,
|
|
431
181
|
variables: { userName: user.name, appName: 'My App' },
|
|
432
182
|
});
|
|
433
183
|
}
|
|
434
|
-
|
|
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
|
-
}
|
|
449
184
|
}
|
|
450
185
|
```
|
|
451
186
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
## Troubleshooting
|
|
455
|
-
|
|
456
|
-
**`No default email config found`**
|
|
457
|
-
|
|
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" }
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
---
|
|
465
|
-
|
|
466
|
-
**`Template not found`**
|
|
467
|
-
|
|
468
|
-
Check the `templateSlug` matches exactly (case-sensitive). Use `POST /email/email-template/get-all` to list available templates.
|
|
469
|
-
|
|
470
|
-
---
|
|
187
|
+
All `{{variable}}` values in HTML are HTML-escaped automatically.
|
|
471
188
|
|
|
472
|
-
|
|
189
|
+
### Raw email with attachments
|
|
473
190
|
|
|
474
|
-
Use the test endpoint first:
|
|
475
|
-
```json
|
|
476
|
-
POST /email/email-config/test
|
|
477
|
-
{ "id": "your-config-id", "to": "test@example.com" }
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
For Gmail, enable "App Passwords" and use the app password, not your account password.
|
|
481
|
-
|
|
482
|
-
---
|
|
483
|
-
|
|
484
|
-
**`No metadata for entity`**
|
|
485
|
-
|
|
486
|
-
Call `EmailModule.getEntities()` with the correct flags when registering `TypeOrmModule`:
|
|
487
191
|
```typescript
|
|
488
|
-
|
|
192
|
+
await this.emailSend.sendEmail({
|
|
193
|
+
to: 'recipient@example.com',
|
|
194
|
+
subject: 'Your Report',
|
|
195
|
+
html: '<p>See attached.</p>',
|
|
196
|
+
attachments: [
|
|
197
|
+
{
|
|
198
|
+
filename: 'report.pdf',
|
|
199
|
+
content: base64String, // base64-encoded file content
|
|
200
|
+
contentType: 'application/pdf',
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
});
|
|
489
204
|
```
|
|
490
205
|
|
|
491
|
-
---
|
|
492
|
-
|
|
493
206
|
## License
|
|
494
207
|
|
|
495
208
|
MIT © FLUSYS
|
|
496
|
-
|
|
497
|
-
---
|
|
498
|
-
|
|
499
|
-
> Part of the **FLUSYS** framework — a full-stack monorepo powering Angular 21 + NestJS 11 applications.
|
|
@@ -10,12 +10,6 @@ function _export(target, all) {
|
|
|
10
10
|
});
|
|
11
11
|
}
|
|
12
12
|
_export(exports, {
|
|
13
|
-
get EMAIL_CONFIG_MESSAGES () {
|
|
14
|
-
return EMAIL_CONFIG_MESSAGES;
|
|
15
|
-
},
|
|
16
|
-
get EMAIL_MODULE_MESSAGES () {
|
|
17
|
-
return EMAIL_MODULE_MESSAGES;
|
|
18
|
-
},
|
|
19
13
|
get EMAIL_SEND_MESSAGES () {
|
|
20
14
|
return EMAIL_SEND_MESSAGES;
|
|
21
15
|
},
|
|
@@ -23,39 +17,13 @@ _export(exports, {
|
|
|
23
17
|
return EMAIL_TEMPLATE_MESSAGES;
|
|
24
18
|
}
|
|
25
19
|
});
|
|
26
|
-
const EMAIL_CONFIG_MESSAGES = {
|
|
27
|
-
CREATE_SUCCESS: 'email.config.create.success',
|
|
28
|
-
CREATE_MANY_SUCCESS: 'email.config.create.many.success',
|
|
29
|
-
GET_SUCCESS: 'email.config.get.success',
|
|
30
|
-
GET_ALL_SUCCESS: 'email.config.get.all.success',
|
|
31
|
-
UPDATE_SUCCESS: 'email.config.update.success',
|
|
32
|
-
UPDATE_MANY_SUCCESS: 'email.config.update.many.success',
|
|
33
|
-
DELETE_SUCCESS: 'email.config.delete.success',
|
|
34
|
-
RESTORE_SUCCESS: 'email.config.restore.success',
|
|
35
|
-
NOT_FOUND: 'email.config.not.found',
|
|
36
|
-
ACTIVE_SUCCESS: 'email.config.active.success',
|
|
37
|
-
TEST_SUCCESS: 'email.config.test.success',
|
|
38
|
-
TEST_FAILED: 'email.config.test.failed'
|
|
39
|
-
};
|
|
40
20
|
const EMAIL_TEMPLATE_MESSAGES = {
|
|
41
|
-
CREATE_SUCCESS: 'email.template.create.success',
|
|
42
|
-
CREATE_MANY_SUCCESS: 'email.template.create.many.success',
|
|
43
21
|
GET_SUCCESS: 'email.template.get.success',
|
|
44
|
-
|
|
45
|
-
UPDATE_SUCCESS: 'email.template.update.success',
|
|
46
|
-
UPDATE_MANY_SUCCESS: 'email.template.update.many.success',
|
|
47
|
-
DELETE_SUCCESS: 'email.template.delete.success',
|
|
48
|
-
RESTORE_SUCCESS: 'email.template.restore.success',
|
|
49
|
-
NOT_FOUND: 'email.template.not.found',
|
|
50
|
-
PREVIEW_SUCCESS: 'email.template.preview.success'
|
|
22
|
+
NOT_FOUND: 'email.template.not.found'
|
|
51
23
|
};
|
|
52
24
|
const EMAIL_SEND_MESSAGES = {
|
|
53
25
|
SUCCESS: 'email.send.success',
|
|
54
26
|
FAILED: 'email.send.failed',
|
|
55
|
-
QUEUED: 'email.send.queued',
|
|
56
|
-
TEMPLATE_SUCCESS: 'email.send.template.success',
|
|
57
|
-
BULK_SUCCESS: 'email.send.bulk.success',
|
|
58
|
-
TEST_SUCCESS: 'email.send.test.success',
|
|
59
27
|
CONFIG_NOT_FOUND: 'email.send.config.not.found',
|
|
60
28
|
CONFIG_INACTIVE: 'email.send.config.inactive',
|
|
61
29
|
CONFIG_DEFAULT_NOT_FOUND: 'email.send.config.default.not.found',
|
|
@@ -63,8 +31,3 @@ const EMAIL_SEND_MESSAGES = {
|
|
|
63
31
|
TEMPLATE_INACTIVE: 'email.send.template.inactive',
|
|
64
32
|
TEMPLATE_ID_OR_SLUG_REQUIRED: 'email.send.template.id.or.slug.required'
|
|
65
33
|
};
|
|
66
|
-
const EMAIL_MODULE_MESSAGES = {
|
|
67
|
-
EMAIL_CONFIG: EMAIL_CONFIG_MESSAGES,
|
|
68
|
-
EMAIL_TEMPLATE: EMAIL_TEMPLATE_MESSAGES,
|
|
69
|
-
EMAIL_SEND: EMAIL_SEND_MESSAGES
|
|
70
|
-
};
|
|
@@ -9,12 +9,12 @@ Object.defineProperty(exports, "EmailTemplateController", {
|
|
|
9
9
|
}
|
|
10
10
|
});
|
|
11
11
|
const _classes = require("@flusys/nestjs-shared/classes");
|
|
12
|
-
const _config = require("../config");
|
|
13
12
|
const _decorators = require("@flusys/nestjs-shared/decorators");
|
|
14
13
|
const _guards = require("@flusys/nestjs-shared/guards");
|
|
15
14
|
const _interfaces = require("@flusys/nestjs-shared/interfaces");
|
|
16
15
|
const _common = require("@nestjs/common");
|
|
17
16
|
const _swagger = require("@nestjs/swagger");
|
|
17
|
+
const _config = require("../config");
|
|
18
18
|
const _dtos = require("../dtos");
|
|
19
19
|
const _services = require("../services");
|
|
20
20
|
function _define_property(obj, key, value) {
|
|
@@ -45,7 +45,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
45
45
|
};
|
|
46
46
|
}
|
|
47
47
|
let EmailTemplateController = class EmailTemplateController extends (0, _classes.createApiController)(_dtos.CreateEmailTemplateDto, _dtos.UpdateEmailTemplateDto, _dtos.EmailTemplateResponseDto, {
|
|
48
|
-
entityName: '
|
|
48
|
+
entityName: 'email.template',
|
|
49
49
|
security: {
|
|
50
50
|
insert: {
|
|
51
51
|
level: 'permission',
|
|
@@ -93,11 +93,17 @@ let EmailTemplateController = class EmailTemplateController extends (0, _classes
|
|
|
93
93
|
}) {
|
|
94
94
|
async getBySlug(body, user) {
|
|
95
95
|
const data = await this.emailTemplateService.findBySlug(body.slug, user);
|
|
96
|
+
if (!data) {
|
|
97
|
+
throw new _common.NotFoundException({
|
|
98
|
+
message: 'Email template not found',
|
|
99
|
+
messageKey: _config.EMAIL_TEMPLATE_MESSAGES.NOT_FOUND
|
|
100
|
+
});
|
|
101
|
+
}
|
|
96
102
|
return {
|
|
97
103
|
success: true,
|
|
98
104
|
message: 'Template retrieved',
|
|
99
105
|
messageKey: _config.EMAIL_TEMPLATE_MESSAGES.GET_SUCCESS,
|
|
100
|
-
data
|
|
106
|
+
data
|
|
101
107
|
};
|
|
102
108
|
}
|
|
103
109
|
constructor(emailTemplateService){
|