@venturialstd/email 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +315 -0
- package/data/README.md +102 -0
- package/data/default-templates.json +34 -0
- package/dist/constants/email.constant.d.ts +9 -0
- package/dist/constants/email.constant.d.ts.map +1 -0
- package/dist/constants/email.constant.js +13 -0
- package/dist/constants/email.constant.js.map +1 -0
- package/dist/constants/email.settings.constant.d.ts +10 -0
- package/dist/constants/email.settings.constant.d.ts.map +1 -0
- package/dist/constants/email.settings.constant.js +13 -0
- package/dist/constants/email.settings.constant.js.map +1 -0
- package/dist/constants/index.d.ts +3 -0
- package/dist/constants/index.d.ts.map +1 -0
- package/dist/constants/index.js +19 -0
- package/dist/constants/index.js.map +1 -0
- package/dist/email.module.d.ts +3 -0
- package/dist/email.module.d.ts.map +1 -0
- package/dist/email.module.js +75 -0
- package/dist/email.module.js.map +1 -0
- package/dist/entities/email-log.entity.d.ts +24 -0
- package/dist/entities/email-log.entity.d.ts.map +1 -0
- package/dist/entities/email-log.entity.js +110 -0
- package/dist/entities/email-log.entity.js.map +1 -0
- package/dist/entities/email-template.entity.d.ts +20 -0
- package/dist/entities/email-template.entity.d.ts.map +1 -0
- package/dist/entities/email-template.entity.js +90 -0
- package/dist/entities/email-template.entity.js.map +1 -0
- package/dist/entities/index.d.ts +3 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/index.js +19 -0
- package/dist/entities/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/services/email-log.service.d.ts +8 -0
- package/dist/services/email-log.service.d.ts.map +1 -0
- package/dist/services/email-log.service.js +34 -0
- package/dist/services/email-log.service.js.map +1 -0
- package/dist/services/email-template.service.d.ts +46 -0
- package/dist/services/email-template.service.d.ts.map +1 -0
- package/dist/services/email-template.service.js +198 -0
- package/dist/services/email-template.service.js.map +1 -0
- package/dist/services/email.service.d.ts +75 -0
- package/dist/services/email.service.d.ts.map +1 -0
- package/dist/services/email.service.js +406 -0
- package/dist/services/email.service.js.map +1 -0
- package/dist/services/index.d.ts +4 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +20 -0
- package/dist/services/index.js.map +1 -0
- package/dist/settings/email.settings.d.ts +3 -0
- package/dist/settings/email.settings.d.ts.map +1 -0
- package/dist/settings/email.settings.js +92 -0
- package/dist/settings/email.settings.js.map +1 -0
- package/dist/types/email.config.type.d.ts +33 -0
- package/dist/types/email.config.type.d.ts.map +1 -0
- package/dist/types/email.config.type.js +3 -0
- package/dist/types/email.config.type.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +18 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# @venturialstd/email
|
|
2
|
+
|
|
3
|
+
Email Module with Multiple Transporter Support for Venturial Standard Backend.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📧 Multiple transporter support (SMTP, and extensible for others)
|
|
8
|
+
- 🔧 Configuration via code or settings service
|
|
9
|
+
- 🎨 **Handlebars template support** - Beautiful, dynamic email templates
|
|
10
|
+
- 📊 **Database template management** - Store and manage templates in database
|
|
11
|
+
- 📝 HTML and plain text emails
|
|
12
|
+
- 🔄 Attachment support
|
|
13
|
+
- 📊 **Email logging and tracking** - All sent emails are logged with status
|
|
14
|
+
- 🚀 Built on top of @nestjs-modules/mailer
|
|
15
|
+
|
|
16
|
+
## Quick Links
|
|
17
|
+
|
|
18
|
+
- [Database Template Management](./DATABASE_TEMPLATES.md) - Store templates in database
|
|
19
|
+
- [Handlebars Templates Guide](./HANDLEBARS_TEMPLATES.md) - Complete guide to using templates
|
|
20
|
+
- [Email Logging Documentation](./EMAIL_LOGGING.md) - Email tracking and monitoring
|
|
21
|
+
- [Usage Examples](./USAGE.md) - Comprehensive usage examples
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @venturialstd/email
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Import the Module
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { EmailModule } from '@venturialstd/email';
|
|
35
|
+
|
|
36
|
+
@Module({
|
|
37
|
+
imports: [
|
|
38
|
+
EmailModule,
|
|
39
|
+
// ... other modules
|
|
40
|
+
],
|
|
41
|
+
})
|
|
42
|
+
export class AppModule {}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Send Emails
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
import { EmailService, EmailConfig } from '@venturialstd/email';
|
|
49
|
+
|
|
50
|
+
@Injectable()
|
|
51
|
+
export class MyService {
|
|
52
|
+
constructor(private readonly emailService: EmailService) {}
|
|
53
|
+
|
|
54
|
+
async sendWelcomeEmail() {
|
|
55
|
+
const config: EmailConfig = {
|
|
56
|
+
transporter: 'SMTP',
|
|
57
|
+
smtp: {
|
|
58
|
+
host: 'smtp.example.com',
|
|
59
|
+
port: 587,
|
|
60
|
+
secure: false,
|
|
61
|
+
auth: {
|
|
62
|
+
user: 'user@example.com',
|
|
63
|
+
pass: 'password',
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
await this.emailService.sendEmail(config, {
|
|
69
|
+
to: 'recipient@example.com',
|
|
70
|
+
subject: 'Welcome!',
|
|
71
|
+
html: '<h1>Welcome to our platform!</h1>',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Send Emails with Handlebars Templates
|
|
78
|
+
|
|
79
|
+
The module includes pre-built Handlebars templates and helper methods:
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
// Welcome email
|
|
83
|
+
await emailService.sendWelcomeEmail(null, {
|
|
84
|
+
to: 'user@example.com',
|
|
85
|
+
name: 'John Doe',
|
|
86
|
+
message: 'Thank you for joining our platform!',
|
|
87
|
+
actionUrl: 'https://app.example.com/onboarding',
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Password reset email
|
|
91
|
+
await emailService.sendPasswordResetEmail(null, {
|
|
92
|
+
to: 'user@example.com',
|
|
93
|
+
name: 'John Doe',
|
|
94
|
+
resetUrl: 'https://app.example.com/reset?token=abc123',
|
|
95
|
+
expirationTime: 30,
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Notification email
|
|
99
|
+
await emailService.sendNotificationEmail(null, {
|
|
100
|
+
to: 'user@example.com',
|
|
101
|
+
name: 'John Doe',
|
|
102
|
+
title: 'Order Confirmation',
|
|
103
|
+
notificationTitle: 'Your order has been confirmed!',
|
|
104
|
+
message: 'Order #12345 is being processed.',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Custom template
|
|
108
|
+
await emailService.sendEmailWithTemplate(null, {
|
|
109
|
+
to: 'user@example.com',
|
|
110
|
+
subject: 'Custom Email',
|
|
111
|
+
template: 'my-custom-template',
|
|
112
|
+
context: {
|
|
113
|
+
customVariable: 'value',
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
See [HANDLEBARS_TEMPLATES.md](./HANDLEBARS_TEMPLATES.md) for complete template documentation.
|
|
119
|
+
|
|
120
|
+
### Configuration Options
|
|
121
|
+
|
|
122
|
+
#### SMTP Configuration
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
const config: EmailConfig = {
|
|
126
|
+
transporter: 'SMTP',
|
|
127
|
+
smtp: {
|
|
128
|
+
host: 'smtp.gmail.com',
|
|
129
|
+
port: 587,
|
|
130
|
+
secure: false, // true for 465, false for other ports
|
|
131
|
+
auth: {
|
|
132
|
+
user: 'your-email@gmail.com',
|
|
133
|
+
pass: 'your-password',
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
defaults: {
|
|
137
|
+
from: '"Your Name" <your-email@gmail.com>',
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Using Settings Service
|
|
143
|
+
|
|
144
|
+
If no config is provided, the module will attempt to load configuration from the Settings Service:
|
|
145
|
+
|
|
146
|
+
- `EMAIL_TRANSPORTER` - Transport type (default: 'SMTP')
|
|
147
|
+
- `EMAIL_SMTP_HOST` - SMTP server host
|
|
148
|
+
- `EMAIL_SMTP_PORT` - SMTP server port
|
|
149
|
+
- `EMAIL_SMTP_SECURE` - Use SSL/TLS (true/false)
|
|
150
|
+
- `EMAIL_SMTP_USER` - SMTP authentication user
|
|
151
|
+
- `EMAIL_SMTP_PASSWORD` - SMTP authentication password
|
|
152
|
+
- `EMAIL_DEFAULT_FROM` - Default sender email address
|
|
153
|
+
|
|
154
|
+
## API
|
|
155
|
+
|
|
156
|
+
### EmailService
|
|
157
|
+
|
|
158
|
+
#### `sendEmail(config, options)`
|
|
159
|
+
|
|
160
|
+
Sends an email with the specified options. **All emails are automatically logged to the database.**
|
|
161
|
+
|
|
162
|
+
**Parameters:**
|
|
163
|
+
- `config: EmailConfig | null` - Email configuration (transporter settings)
|
|
164
|
+
- `options: SendEmailOptions` - Email options (to, subject, html, etc.)
|
|
165
|
+
|
|
166
|
+
**Returns:** `Promise<void>`
|
|
167
|
+
|
|
168
|
+
#### `getEmailLogs(options?)`
|
|
169
|
+
|
|
170
|
+
Retrieves email logs with optional filtering.
|
|
171
|
+
|
|
172
|
+
**Parameters:**
|
|
173
|
+
- `options?`: Optional filtering options
|
|
174
|
+
- `status?: EmailStatus` - Filter by status (PENDING, SENT, FAILED)
|
|
175
|
+
- `to?: string` - Filter by recipient email
|
|
176
|
+
- `from?: string` - Filter by sender email
|
|
177
|
+
- `startDate?: Date` - Filter from this date
|
|
178
|
+
- `endDate?: Date` - Filter until this date
|
|
179
|
+
- `limit?: number` - Limit number of results
|
|
180
|
+
|
|
181
|
+
**Returns:** `Promise<EmailLog[]>`
|
|
182
|
+
|
|
183
|
+
**Example:**
|
|
184
|
+
```typescript
|
|
185
|
+
// Get all failed emails
|
|
186
|
+
const failedEmails = await emailService.getEmailLogs({
|
|
187
|
+
status: EmailStatus.FAILED,
|
|
188
|
+
limit: 10,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Get emails sent to specific recipient
|
|
192
|
+
const userEmails = await emailService.getEmailLogs({
|
|
193
|
+
to: 'user@example.com',
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
#### `getEmailLogById(id)`
|
|
198
|
+
|
|
199
|
+
Gets a single email log by ID.
|
|
200
|
+
|
|
201
|
+
**Parameters:**
|
|
202
|
+
- `id: string` - Email log ID
|
|
203
|
+
|
|
204
|
+
**Returns:** `Promise<EmailLog | null>`
|
|
205
|
+
|
|
206
|
+
#### `getEmailLogStats(startDate?, endDate?)`
|
|
207
|
+
|
|
208
|
+
Gets email statistics for a date range.
|
|
209
|
+
|
|
210
|
+
**Parameters:**
|
|
211
|
+
- `startDate?: Date` - Start date for statistics
|
|
212
|
+
- `endDate?: Date` - End date for statistics
|
|
213
|
+
|
|
214
|
+
**Returns:** `Promise<{ total: number; sent: number; failed: number; pending: number }>`
|
|
215
|
+
|
|
216
|
+
**Example:**
|
|
217
|
+
```typescript
|
|
218
|
+
const stats = await emailService.getEmailLogStats(
|
|
219
|
+
new Date('2024-01-01'),
|
|
220
|
+
new Date('2024-01-31')
|
|
221
|
+
);
|
|
222
|
+
console.log(`Total emails: ${stats.total}, Sent: ${stats.sent}, Failed: ${stats.failed}`);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Email Logging
|
|
226
|
+
|
|
227
|
+
All emails sent through the service are automatically logged to the `email_log` table with the following information:
|
|
228
|
+
|
|
229
|
+
- **Recipient(s)**: to, cc, bcc
|
|
230
|
+
- **Sender**: from address
|
|
231
|
+
- **Content**: subject, text, html
|
|
232
|
+
- **Status**: PENDING, SENT, or FAILED
|
|
233
|
+
- **Metadata**: Additional information like template name, config source
|
|
234
|
+
- **Timestamps**: Created, updated, and sent timestamps
|
|
235
|
+
- **Error tracking**: Error messages for failed emails
|
|
236
|
+
|
|
237
|
+
### Email Log Entity
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
export enum EmailStatus {
|
|
241
|
+
PENDING = 'PENDING',
|
|
242
|
+
SENT = 'SENT',
|
|
243
|
+
FAILED = 'FAILED',
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export interface EmailLog {
|
|
247
|
+
id: string;
|
|
248
|
+
to: string;
|
|
249
|
+
from?: string;
|
|
250
|
+
cc?: string;
|
|
251
|
+
bcc?: string;
|
|
252
|
+
subject: string;
|
|
253
|
+
textContent?: string;
|
|
254
|
+
htmlContent?: string;
|
|
255
|
+
transporter: string;
|
|
256
|
+
status: EmailStatus;
|
|
257
|
+
errorMessage?: string;
|
|
258
|
+
metadata?: Record<string, any>;
|
|
259
|
+
hasAttachments: boolean;
|
|
260
|
+
sentAt?: Date;
|
|
261
|
+
createdAt: Date;
|
|
262
|
+
updatedAt: Date;
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## Types
|
|
267
|
+
|
|
268
|
+
### EmailConfig
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
interface EmailConfig {
|
|
272
|
+
transporter: 'SMTP';
|
|
273
|
+
smtp?: SMTPConfig;
|
|
274
|
+
defaults?: {
|
|
275
|
+
from?: string;
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### SMTPConfig
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
interface SMTPConfig {
|
|
284
|
+
host: string;
|
|
285
|
+
port: number;
|
|
286
|
+
secure: boolean;
|
|
287
|
+
auth: {
|
|
288
|
+
user: string;
|
|
289
|
+
pass: string;
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### SendEmailOptions
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
interface SendEmailOptions {
|
|
298
|
+
to: string | string[];
|
|
299
|
+
subject: string;
|
|
300
|
+
text?: string;
|
|
301
|
+
html?: string;
|
|
302
|
+
from?: string;
|
|
303
|
+
cc?: string | string[];
|
|
304
|
+
bcc?: string | string[];
|
|
305
|
+
attachments?: Array<{
|
|
306
|
+
filename: string;
|
|
307
|
+
content?: string | Buffer;
|
|
308
|
+
path?: string;
|
|
309
|
+
}>;
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
## License
|
|
314
|
+
|
|
315
|
+
Proprietary - Venturial Standard
|
package/data/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Email Templates Data
|
|
2
|
+
|
|
3
|
+
This folder contains the default email templates in JSON format that are seeded into the database during setup.
|
|
4
|
+
|
|
5
|
+
## File Structure
|
|
6
|
+
|
|
7
|
+
### `default-templates.json`
|
|
8
|
+
|
|
9
|
+
Contains an array of email template objects with the following structure:
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"name": "template-name", // Unique identifier (lowercase, hyphenated)
|
|
14
|
+
"type": "HANDLEBARS", // Template type: HANDLEBARS, HTML, or TEXT
|
|
15
|
+
"subject": "Email Subject {{var}}", // Subject line (supports Handlebars variables)
|
|
16
|
+
"description": "Template description", // Human-readable description
|
|
17
|
+
"variables": ["var1", "var2"], // Array of variable names used in template
|
|
18
|
+
"defaultValues": { // Default values for variables
|
|
19
|
+
"var1": "default value"
|
|
20
|
+
},
|
|
21
|
+
"htmlContent": "HTML content...", // Full HTML email content
|
|
22
|
+
"textContent": "Text content..." // Optional plain text version (optional)
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Template Types
|
|
27
|
+
|
|
28
|
+
- **HANDLEBARS**: Template uses Handlebars syntax for variable interpolation and logic
|
|
29
|
+
- **HTML**: Plain HTML template without variable processing
|
|
30
|
+
- **TEXT**: Plain text template without HTML
|
|
31
|
+
|
|
32
|
+
## Default Templates
|
|
33
|
+
|
|
34
|
+
The following templates are included by default:
|
|
35
|
+
|
|
36
|
+
1. **welcome** - Welcome email for new users
|
|
37
|
+
2. **password-reset** - Password reset request with URL or code
|
|
38
|
+
3. **notification** - General notification email
|
|
39
|
+
|
|
40
|
+
## Adding New Templates
|
|
41
|
+
|
|
42
|
+
To add new default templates:
|
|
43
|
+
|
|
44
|
+
1. Add a new template object to the `default-templates.json` array
|
|
45
|
+
2. Ensure all required fields are included
|
|
46
|
+
3. Test the template by running `npm run db:setup` in the test folder
|
|
47
|
+
4. The template will be automatically seeded into the database
|
|
48
|
+
|
|
49
|
+
## Variable Interpolation
|
|
50
|
+
|
|
51
|
+
Templates support Handlebars syntax:
|
|
52
|
+
|
|
53
|
+
- Simple variables: `{{variableName}}`
|
|
54
|
+
- Conditional blocks: `{{#if variable}}...{{/if}}`
|
|
55
|
+
- Loops: `{{#each items}}{{name}}{{/each}}`
|
|
56
|
+
- Nested properties: `{{user.name}}`
|
|
57
|
+
|
|
58
|
+
## Best Practices
|
|
59
|
+
|
|
60
|
+
1. **Keep HTML inline-styled** - Email clients don't support external CSS well
|
|
61
|
+
2. **Provide default values** - Include sensible defaults for optional variables
|
|
62
|
+
3. **List all variables** - Document all variables used in the `variables` array
|
|
63
|
+
4. **Test across clients** - Email rendering varies significantly across clients
|
|
64
|
+
5. **Include text version** - Always provide a `textContent` fallback for accessibility
|
|
65
|
+
|
|
66
|
+
## Usage in Code
|
|
67
|
+
|
|
68
|
+
Templates are automatically loaded from this file during database setup:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
// test/db-setup.ts
|
|
72
|
+
const defaultTemplates = JSON.parse(
|
|
73
|
+
readFileSync(join(__dirname, '..', 'data', 'default-templates.json'), 'utf-8')
|
|
74
|
+
);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
For production use, templates are managed through the `EmailTemplateService`:
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// Create a new template
|
|
81
|
+
await emailTemplateService.createTemplate({
|
|
82
|
+
name: 'my-template',
|
|
83
|
+
subject: 'Hello {{name}}',
|
|
84
|
+
htmlContent: '<h1>Hello {{name}}!</h1>',
|
|
85
|
+
type: TemplateType.HANDLEBARS,
|
|
86
|
+
variables: ['name'],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Send email using template
|
|
90
|
+
await emailService.sendEmailWithTemplate(null, {
|
|
91
|
+
to: 'user@example.com',
|
|
92
|
+
template: 'my-template',
|
|
93
|
+
context: { name: 'John' },
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Notes
|
|
98
|
+
|
|
99
|
+
- Templates are seeded only during `npm run db:setup`
|
|
100
|
+
- Existing templates in the database are not overwritten
|
|
101
|
+
- Template modifications should be done through the API or database directly
|
|
102
|
+
- This file is for initial seeding only
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"name": "welcome",
|
|
4
|
+
"type": "HANDLEBARS",
|
|
5
|
+
"subject": "Welcome to {{companyName}}!",
|
|
6
|
+
"description": "Welcome email for new users",
|
|
7
|
+
"variables": ["name", "message", "companyName", "actionUrl", "actionText"],
|
|
8
|
+
"defaultValues": {
|
|
9
|
+
"companyName": "Your Company",
|
|
10
|
+
"actionText": "Get Started"
|
|
11
|
+
},
|
|
12
|
+
"htmlContent": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>{{subject}}</title>\n <style>\n body {\n font-family: Arial, sans-serif;\n line-height: 1.6;\n color: #333;\n max-width: 600px;\n margin: 0 auto;\n padding: 20px;\n }\n .header {\n background-color: #4CAF50;\n color: white;\n padding: 20px;\n text-align: center;\n border-radius: 5px 5px 0 0;\n }\n .content {\n background-color: #f9f9f9;\n padding: 30px;\n border-radius: 0 0 5px 5px;\n }\n .button {\n display: inline-block;\n padding: 12px 24px;\n background-color: #4CAF50;\n color: white;\n text-decoration: none;\n border-radius: 4px;\n margin: 20px 0;\n }\n .footer {\n text-align: center;\n margin-top: 30px;\n padding-top: 20px;\n border-top: 1px solid #ddd;\n color: #666;\n font-size: 12px;\n }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <h1>Welcome!</h1>\n </div>\n <div class=\"content\">\n <h2>Hello {{name}}!</h2>\n <p>Welcome to our platform. We're excited to have you on board.</p>\n <p>{{message}}</p>\n {{#if actionUrl}}\n <p style=\"text-align: center;\">\n <a href=\"{{actionUrl}}\" class=\"button\">{{actionText}}</a>\n </p>\n {{/if}}\n </div>\n <div class=\"footer\">\n <p>© {{year}} {{companyName}}. All rights reserved.</p>\n </div>\n</body>\n</html>"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"name": "password-reset",
|
|
16
|
+
"type": "HANDLEBARS",
|
|
17
|
+
"subject": "Password Reset Request",
|
|
18
|
+
"description": "Password reset email with URL or code",
|
|
19
|
+
"variables": ["name", "resetUrl", "resetCode", "expirationTime"],
|
|
20
|
+
"defaultValues": {
|
|
21
|
+
"expirationTime": 60
|
|
22
|
+
},
|
|
23
|
+
"htmlContent": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>{{subject}}</title>\n <style>\n body {\n font-family: Arial, sans-serif;\n line-height: 1.6;\n color: #333;\n max-width: 600px;\n margin: 0 auto;\n padding: 20px;\n }\n .header {\n background-color: #ff9800;\n color: white;\n padding: 20px;\n text-align: center;\n border-radius: 5px 5px 0 0;\n }\n .content {\n background-color: #f9f9f9;\n padding: 30px;\n border-radius: 0 0 5px 5px;\n }\n .button {\n display: inline-block;\n padding: 12px 24px;\n background-color: #ff9800;\n color: white;\n text-decoration: none;\n border-radius: 4px;\n margin: 20px 0;\n }\n .code {\n background-color: #f0f0f0;\n padding: 15px;\n text-align: center;\n font-size: 24px;\n font-weight: bold;\n letter-spacing: 5px;\n border-radius: 4px;\n margin: 20px 0;\n }\n .footer {\n text-align: center;\n margin-top: 30px;\n padding-top: 20px;\n border-top: 1px solid #ddd;\n color: #666;\n font-size: 12px;\n }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <h1>Password Reset Request</h1>\n </div>\n <div class=\"content\">\n <h2>Hello {{name}}!</h2>\n <p>We received a request to reset your password. If you didn't make this request, you can safely ignore this email.</p>\n {{#if resetCode}}\n <p>Your password reset code is:</p>\n <div class=\"code\">{{resetCode}}</div>\n <p>This code will expire in {{expirationTime}} minutes.</p>\n {{else}}\n <p>Click the button below to reset your password:</p>\n <p style=\"text-align: center;\">\n <a href=\"{{resetUrl}}\" class=\"button\">Reset Password</a>\n </p>\n <p>This link will expire in {{expirationTime}} minutes.</p>\n <p style=\"font-size: 12px; color: #666;\">If the button doesn't work, copy and paste this link into your browser:<br>{{resetUrl}}</p>\n {{/if}}\n </div>\n <div class=\"footer\">\n <p>© {{year}} Your Company. All rights reserved.</p>\n <p>If you didn't request this password reset, please contact support immediately.</p>\n </div>\n</body>\n</html>"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"name": "notification",
|
|
27
|
+
"type": "HANDLEBARS",
|
|
28
|
+
"subject": "{{title}}",
|
|
29
|
+
"description": "General notification email",
|
|
30
|
+
"variables": ["name", "title", "notificationTitle", "message", "details"],
|
|
31
|
+
"defaultValues": {},
|
|
32
|
+
"htmlContent": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>{{subject}}</title>\n <style>\n body {\n font-family: Arial, sans-serif;\n line-height: 1.6;\n color: #333;\n max-width: 600px;\n margin: 0 auto;\n padding: 20px;\n }\n .header {\n background-color: #2196F3;\n color: white;\n padding: 20px;\n text-align: center;\n border-radius: 5px 5px 0 0;\n }\n .content {\n background-color: #f9f9f9;\n padding: 30px;\n border-radius: 0 0 5px 5px;\n }\n .notification-box {\n background-color: white;\n border-left: 4px solid #2196F3;\n padding: 15px;\n margin: 20px 0;\n }\n .footer {\n text-align: center;\n margin-top: 30px;\n padding-top: 20px;\n border-top: 1px solid #ddd;\n color: #666;\n font-size: 12px;\n }\n </style>\n</head>\n<body>\n <div class=\"header\">\n <h1>{{title}}</h1>\n </div>\n <div class=\"content\">\n <h2>Hello {{name}}!</h2>\n <div class=\"notification-box\">\n <p><strong>{{notificationTitle}}</strong></p>\n <p>{{message}}</p>\n </div>\n {{#if details}}\n <h3>Details:</h3>\n <ul>\n {{#each details}}\n <li><strong>{{@key}}:</strong> {{this}}</li>\n {{/each}}\n </ul>\n {{/if}}\n </div>\n <div class=\"footer\">\n <p>© {{year}} Your Company. All rights reserved.</p>\n </div>\n</body>\n</html>"
|
|
33
|
+
}
|
|
34
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.constant.d.ts","sourceRoot":"","sources":["../../src/constants/email.constant.ts"],"names":[],"mappings":"AAGA,oBAAY,iBAAiB;IAC3B,IAAI,SAAS;CACd;AAKD,eAAO,MAAM,oBAAoB;;;;CAIhC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EMAIL_DEFAULT_CONFIG = exports.EMAIL_TRANSPORTER = void 0;
|
|
4
|
+
var EMAIL_TRANSPORTER;
|
|
5
|
+
(function (EMAIL_TRANSPORTER) {
|
|
6
|
+
EMAIL_TRANSPORTER["SMTP"] = "SMTP";
|
|
7
|
+
})(EMAIL_TRANSPORTER || (exports.EMAIL_TRANSPORTER = EMAIL_TRANSPORTER = {}));
|
|
8
|
+
exports.EMAIL_DEFAULT_CONFIG = {
|
|
9
|
+
TRANSPORTER: EMAIL_TRANSPORTER.SMTP,
|
|
10
|
+
SMTP_PORT: 587,
|
|
11
|
+
SMTP_SECURE: false,
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=email.constant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.constant.js","sourceRoot":"","sources":["../../src/constants/email.constant.ts"],"names":[],"mappings":";;;AAGA,IAAY,iBAEX;AAFD,WAAY,iBAAiB;IAC3B,kCAAa,CAAA;AACf,CAAC,EAFW,iBAAiB,iCAAjB,iBAAiB,QAE5B;AAKY,QAAA,oBAAoB,GAAG;IAClC,WAAW,EAAE,iBAAiB,CAAC,IAAI;IACnC,SAAS,EAAE,GAAG;IACd,WAAW,EAAE,KAAK;CACnB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const EMAIL_SETTING_KEYS: {
|
|
2
|
+
readonly GENERAL_TRANSPORTER: "GLOBAL:EMAIL:GENERAL:TRANSPORTER";
|
|
3
|
+
readonly GENERAL_DEFAULT_FROM: "GLOBAL:EMAIL:GENERAL:DEFAULT_FROM";
|
|
4
|
+
readonly SMTP_HOST: "GLOBAL:EMAIL:SMTP:HOST";
|
|
5
|
+
readonly SMTP_PORT: "GLOBAL:EMAIL:SMTP:PORT";
|
|
6
|
+
readonly SMTP_SECURE: "GLOBAL:EMAIL:SMTP:SECURE";
|
|
7
|
+
readonly SMTP_USER: "GLOBAL:EMAIL:SMTP:USER";
|
|
8
|
+
readonly SMTP_PASSWORD: "GLOBAL:EMAIL:SMTP:PASSWORD";
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=email.settings.constant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.settings.constant.d.ts","sourceRoot":"","sources":["../../src/constants/email.settings.constant.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB;;;;;;;;CAUrB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EMAIL_SETTING_KEYS = void 0;
|
|
4
|
+
exports.EMAIL_SETTING_KEYS = {
|
|
5
|
+
GENERAL_TRANSPORTER: 'GLOBAL:EMAIL:GENERAL:TRANSPORTER',
|
|
6
|
+
GENERAL_DEFAULT_FROM: 'GLOBAL:EMAIL:GENERAL:DEFAULT_FROM',
|
|
7
|
+
SMTP_HOST: 'GLOBAL:EMAIL:SMTP:HOST',
|
|
8
|
+
SMTP_PORT: 'GLOBAL:EMAIL:SMTP:PORT',
|
|
9
|
+
SMTP_SECURE: 'GLOBAL:EMAIL:SMTP:SECURE',
|
|
10
|
+
SMTP_USER: 'GLOBAL:EMAIL:SMTP:USER',
|
|
11
|
+
SMTP_PASSWORD: 'GLOBAL:EMAIL:SMTP:PASSWORD',
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=email.settings.constant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.settings.constant.js","sourceRoot":"","sources":["../../src/constants/email.settings.constant.ts"],"names":[],"mappings":";;;AAGa,QAAA,kBAAkB,GAAG;IAChC,mBAAmB,EAAE,kCAAkC;IACvD,oBAAoB,EAAE,mCAAmC;IAGzD,SAAS,EAAE,wBAAwB;IACnC,SAAS,EAAE,wBAAwB;IACnC,WAAW,EAAE,0BAA0B;IACvC,SAAS,EAAE,wBAAwB;IACnC,aAAa,EAAE,4BAA4B;CACnC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./email.constant"), exports);
|
|
18
|
+
__exportStar(require("./email.settings.constant"), exports);
|
|
19
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/constants/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,mDAAiC;AACjC,4DAA0C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.module.d.ts","sourceRoot":"","sources":["../src/email.module.ts"],"names":[],"mappings":"AASA,qBA+Da,WAAW;CAAG"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.EmailModule = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const config_1 = require("@nestjs/config");
|
|
12
|
+
const typeorm_1 = require("@nestjs/typeorm");
|
|
13
|
+
const mailer_1 = require("@nestjs-modules/mailer");
|
|
14
|
+
const core_1 = require("@venturialstd/core");
|
|
15
|
+
const entities_1 = require("./entities");
|
|
16
|
+
const services_1 = require("./services");
|
|
17
|
+
let EmailModule = class EmailModule {
|
|
18
|
+
};
|
|
19
|
+
exports.EmailModule = EmailModule;
|
|
20
|
+
exports.EmailModule = EmailModule = __decorate([
|
|
21
|
+
(0, common_1.Module)({
|
|
22
|
+
imports: [
|
|
23
|
+
core_1.SharedModule.forRoot({}),
|
|
24
|
+
core_1.SettingsModule,
|
|
25
|
+
typeorm_1.TypeOrmModule.forFeature([entities_1.EmailLog, entities_1.EmailTemplate]),
|
|
26
|
+
mailer_1.MailerModule.forRootAsync({
|
|
27
|
+
imports: [config_1.ConfigModule],
|
|
28
|
+
inject: [config_1.ConfigService],
|
|
29
|
+
useFactory: (configService) => {
|
|
30
|
+
const port = Number(configService.get('SMTP_PORT', 587));
|
|
31
|
+
const secure = configService.get('SMTP_SECURE', 'false') === 'true';
|
|
32
|
+
const transportConfig = {
|
|
33
|
+
host: configService.get('SMTP_HOST', 'localhost'),
|
|
34
|
+
port,
|
|
35
|
+
secure,
|
|
36
|
+
auth: {
|
|
37
|
+
user: configService.get('SMTP_USER'),
|
|
38
|
+
pass: configService.get('SMTP_PASSWORD'),
|
|
39
|
+
},
|
|
40
|
+
requireTLS: undefined,
|
|
41
|
+
tls: undefined,
|
|
42
|
+
};
|
|
43
|
+
if (port === 587 && !secure) {
|
|
44
|
+
transportConfig.requireTLS = true;
|
|
45
|
+
transportConfig.tls = {
|
|
46
|
+
rejectUnauthorized: false,
|
|
47
|
+
minVersion: 'TLSv1.2',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
else if (port === 465 && secure) {
|
|
51
|
+
transportConfig.tls = {
|
|
52
|
+
rejectUnauthorized: false,
|
|
53
|
+
minVersion: 'TLSv1.2',
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
transportConfig.tls = {
|
|
58
|
+
rejectUnauthorized: false,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
transport: transportConfig,
|
|
63
|
+
defaults: {
|
|
64
|
+
from: configService.get('EMAIL_DEFAULT_FROM', '"No Reply" <noreply@example.com>'),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
],
|
|
70
|
+
controllers: [],
|
|
71
|
+
providers: [services_1.EmailService, services_1.EmailLogService, services_1.EmailTemplateService],
|
|
72
|
+
exports: [services_1.EmailService, services_1.EmailLogService, services_1.EmailTemplateService],
|
|
73
|
+
})
|
|
74
|
+
], EmailModule);
|
|
75
|
+
//# sourceMappingURL=email.module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"email.module.js","sourceRoot":"","sources":["../src/email.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA6D;AAC7D,6CAAgD;AAChD,mDAAsD;AACtD,6CAAkE;AAElE,yCAAqD;AACrD,yCAAiF;AAiE1E,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IA/DvB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,mBAAY,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,qBAAc;YACd,uBAAa,CAAC,UAAU,CAAC,CAAC,mBAAQ,EAAE,wBAAa,CAAC,CAAC;YACnD,qBAAY,CAAC,YAAY,CAAC;gBACxB,OAAO,EAAE,CAAC,qBAAY,CAAC;gBACvB,MAAM,EAAE,CAAC,sBAAa,CAAC;gBACvB,UAAU,EAAE,CAAC,aAA4B,EAAE,EAAE;oBAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC;oBACzD,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;oBAEpE,MAAM,eAAe,GAAG;wBACtB,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC;wBACjD,IAAI;wBACJ,MAAM;wBACN,IAAI,EAAE;4BACJ,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC;4BACpC,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,eAAe,CAAC;yBACzC;wBACD,UAAU,EAAE,SAAgC;wBAC5C,GAAG,EAAE,SAKQ;qBACd,CAAC;oBAGF,IAAI,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;wBAE5B,eAAe,CAAC,UAAU,GAAG,IAAI,CAAC;wBAClC,eAAe,CAAC,GAAG,GAAG;4BACpB,kBAAkB,EAAE,KAAK;4BACzB,UAAU,EAAE,SAAkB;yBAC/B,CAAC;oBACJ,CAAC;yBAAM,IAAI,IAAI,KAAK,GAAG,IAAI,MAAM,EAAE,CAAC;wBAElC,eAAe,CAAC,GAAG,GAAG;4BACpB,kBAAkB,EAAE,KAAK;4BACzB,UAAU,EAAE,SAAkB;yBAC/B,CAAC;oBACJ,CAAC;yBAAM,CAAC;wBAEN,eAAe,CAAC,GAAG,GAAG;4BACpB,kBAAkB,EAAE,KAAK;yBAC1B,CAAC;oBACJ,CAAC;oBAED,OAAO;wBACL,SAAS,EAAE,eAAe;wBAC1B,QAAQ,EAAE;4BACR,IAAI,EAAE,aAAa,CAAC,GAAG,CAAC,oBAAoB,EAAE,kCAAkC,CAAC;yBAClF;qBACF,CAAC;gBACJ,CAAC;aACF,CAAC;SACH;QACD,WAAW,EAAE,EAAE;QACf,SAAS,EAAE,CAAC,uBAAY,EAAE,0BAAe,EAAE,+BAAoB,CAAC;QAChE,OAAO,EAAE,CAAC,uBAAY,EAAE,0BAAe,EAAE,+BAAoB,CAAC;KAC/D,CAAC;GACW,WAAW,CAAG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare enum EmailStatus {
|
|
2
|
+
PENDING = "PENDING",
|
|
3
|
+
SENT = "SENT",
|
|
4
|
+
FAILED = "FAILED"
|
|
5
|
+
}
|
|
6
|
+
export declare class EmailLog {
|
|
7
|
+
id: string;
|
|
8
|
+
to: string;
|
|
9
|
+
from: string | null;
|
|
10
|
+
cc: string | null;
|
|
11
|
+
bcc: string | null;
|
|
12
|
+
subject: string;
|
|
13
|
+
textContent: string | null;
|
|
14
|
+
htmlContent: string | null;
|
|
15
|
+
transporter: string;
|
|
16
|
+
status: EmailStatus;
|
|
17
|
+
errorMessage: string | null;
|
|
18
|
+
metadata: Record<string, unknown> | null;
|
|
19
|
+
hasAttachments: boolean;
|
|
20
|
+
sentAt: Date | null;
|
|
21
|
+
createdAt: Date;
|
|
22
|
+
updatedAt: Date;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=email-log.entity.d.ts.map
|