@flusys/nestjs-email 1.1.0-beta
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/cjs/config/email-config.service.js +94 -0
- package/cjs/config/email.constants.js +40 -0
- package/cjs/config/index.js +19 -0
- package/cjs/controllers/email-config.controller.js +59 -0
- package/cjs/controllers/email-send.controller.js +142 -0
- package/cjs/controllers/email-template.controller.js +84 -0
- package/cjs/controllers/index.js +20 -0
- package/cjs/docs/email-swagger.config.js +176 -0
- package/cjs/docs/index.js +11 -0
- package/cjs/dtos/email-config.dto.js +238 -0
- package/cjs/dtos/email-send.dto.js +376 -0
- package/cjs/dtos/email-template.dto.js +283 -0
- package/cjs/dtos/index.js +20 -0
- package/cjs/entities/email-config-base.entity.js +111 -0
- package/cjs/entities/email-config-with-company.entity.js +63 -0
- package/cjs/entities/email-config.entity.js +25 -0
- package/cjs/entities/email-template-base.entity.js +134 -0
- package/cjs/entities/email-template-with-company.entity.js +63 -0
- package/cjs/entities/email-template.entity.js +25 -0
- package/cjs/entities/index.js +41 -0
- package/cjs/enums/email-provider-type.enum.js +18 -0
- package/cjs/enums/index.js +18 -0
- package/cjs/index.js +27 -0
- package/cjs/interfaces/email-config.interface.js +4 -0
- package/cjs/interfaces/email-module-options.interface.js +4 -0
- package/cjs/interfaces/email-provider.interface.js +6 -0
- package/cjs/interfaces/email-template.interface.js +4 -0
- package/cjs/interfaces/index.js +21 -0
- package/cjs/modules/email.module.js +177 -0
- package/cjs/modules/index.js +18 -0
- package/cjs/providers/email-factory.service.js +160 -0
- package/cjs/providers/email-provider.registry.js +51 -0
- package/cjs/providers/index.js +22 -0
- package/cjs/providers/mailgun-provider.js +125 -0
- package/cjs/providers/sendgrid-provider.js +156 -0
- package/cjs/providers/smtp-provider.js +185 -0
- package/cjs/services/email-datasource.provider.js +215 -0
- package/cjs/services/email-provider-config.service.js +180 -0
- package/cjs/services/email-send.service.js +228 -0
- package/cjs/services/email-template.service.js +186 -0
- package/cjs/services/index.js +21 -0
- package/config/email-config.service.d.ts +13 -0
- package/config/email.constants.d.ts +11 -0
- package/config/index.d.ts +2 -0
- package/controllers/email-config.controller.d.ts +17 -0
- package/controllers/email-send.controller.d.ts +19 -0
- package/controllers/email-template.controller.d.ts +25 -0
- package/controllers/index.d.ts +3 -0
- package/docs/email-swagger.config.d.ts +3 -0
- package/docs/index.d.ts +1 -0
- package/dtos/email-config.dto.d.ts +33 -0
- package/dtos/email-send.dto.d.ts +45 -0
- package/dtos/email-template.dto.d.ts +42 -0
- package/dtos/index.d.ts +3 -0
- package/entities/email-config-base.entity.d.ts +11 -0
- package/entities/email-config-with-company.entity.d.ts +4 -0
- package/entities/email-config.entity.d.ts +3 -0
- package/entities/email-template-base.entity.d.ts +14 -0
- package/entities/email-template-with-company.entity.d.ts +4 -0
- package/entities/email-template.entity.d.ts +3 -0
- package/entities/index.d.ts +7 -0
- package/enums/email-provider-type.enum.d.ts +5 -0
- package/enums/index.d.ts +1 -0
- package/fesm/config/email-config.service.js +84 -0
- package/fesm/config/email.constants.js +13 -0
- package/fesm/config/index.js +2 -0
- package/fesm/controllers/email-config.controller.js +49 -0
- package/fesm/controllers/email-send.controller.js +132 -0
- package/fesm/controllers/email-template.controller.js +74 -0
- package/fesm/controllers/index.js +3 -0
- package/fesm/docs/email-swagger.config.js +172 -0
- package/fesm/docs/index.js +1 -0
- package/fesm/dtos/email-config.dto.js +223 -0
- package/fesm/dtos/email-send.dto.js +360 -0
- package/fesm/dtos/email-template.dto.js +268 -0
- package/fesm/dtos/index.js +3 -0
- package/fesm/entities/email-config-base.entity.js +101 -0
- package/fesm/entities/email-config-with-company.entity.js +53 -0
- package/fesm/entities/email-config.entity.js +15 -0
- package/fesm/entities/email-template-base.entity.js +124 -0
- package/fesm/entities/email-template-with-company.entity.js +53 -0
- package/fesm/entities/email-template.entity.js +15 -0
- package/fesm/entities/index.js +20 -0
- package/fesm/enums/email-provider-type.enum.js +8 -0
- package/fesm/enums/index.js +1 -0
- package/fesm/index.js +10 -0
- package/fesm/interfaces/email-config.interface.js +3 -0
- package/fesm/interfaces/email-module-options.interface.js +3 -0
- package/fesm/interfaces/email-provider.interface.js +5 -0
- package/fesm/interfaces/email-template.interface.js +3 -0
- package/fesm/interfaces/index.js +4 -0
- package/fesm/modules/email.module.js +167 -0
- package/fesm/modules/index.js +1 -0
- package/fesm/providers/email-factory.service.js +109 -0
- package/fesm/providers/email-provider.registry.js +44 -0
- package/fesm/providers/index.js +5 -0
- package/fesm/providers/mailgun-provider.js +119 -0
- package/fesm/providers/sendgrid-provider.js +150 -0
- package/fesm/providers/smtp-provider.js +137 -0
- package/fesm/services/email-datasource.provider.js +164 -0
- package/fesm/services/email-provider-config.service.js +170 -0
- package/fesm/services/email-send.service.js +218 -0
- package/fesm/services/email-template.service.js +176 -0
- package/fesm/services/index.js +4 -0
- package/index.d.ts +9 -0
- package/interfaces/email-config.interface.d.ts +28 -0
- package/interfaces/email-module-options.interface.d.ts +26 -0
- package/interfaces/email-provider.interface.d.ts +34 -0
- package/interfaces/email-template.interface.d.ts +64 -0
- package/interfaces/index.d.ts +4 -0
- package/modules/email.module.d.ts +9 -0
- package/modules/index.d.ts +1 -0
- package/package.json +105 -0
- package/providers/email-factory.service.d.ts +14 -0
- package/providers/email-provider.registry.d.ts +10 -0
- package/providers/index.d.ts +5 -0
- package/providers/mailgun-provider.d.ts +11 -0
- package/providers/sendgrid-provider.d.ts +11 -0
- package/providers/smtp-provider.d.ts +11 -0
- package/services/email-datasource.provider.d.ts +25 -0
- package/services/email-provider-config.service.d.ts +32 -0
- package/services/email-send.service.d.ts +20 -0
- package/services/email-template.service.d.ts +31 -0
- package/services/index.d.ts +4 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "MailgunProvider", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return MailgunProvider;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _common = require("@nestjs/common");
|
|
12
|
+
function _define_property(obj, key, value) {
|
|
13
|
+
if (key in obj) {
|
|
14
|
+
Object.defineProperty(obj, key, {
|
|
15
|
+
value: value,
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true
|
|
19
|
+
});
|
|
20
|
+
} else {
|
|
21
|
+
obj[key] = value;
|
|
22
|
+
}
|
|
23
|
+
return obj;
|
|
24
|
+
}
|
|
25
|
+
let MailgunProvider = class MailgunProvider {
|
|
26
|
+
/**
|
|
27
|
+
* Initialize the Mailgun provider with configuration
|
|
28
|
+
*/ async initialize(config) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
try {
|
|
31
|
+
// Dynamic import mailgun.js and form-data - use eval to bypass TypeScript module resolution
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
33
|
+
const Mailgun = (await new Function('return import("mailgun.js")')()).default;
|
|
34
|
+
const FormData = (await new Function('return import("form-data")')()).default;
|
|
35
|
+
const mailgun = new Mailgun(FormData);
|
|
36
|
+
// Determine API URL based on region
|
|
37
|
+
const url = config.region === 'eu' ? 'https://api.eu.mailgun.net' : 'https://api.mailgun.net';
|
|
38
|
+
this.client = mailgun.client({
|
|
39
|
+
username: 'api',
|
|
40
|
+
key: config.apiKey,
|
|
41
|
+
url
|
|
42
|
+
});
|
|
43
|
+
this.logger.log(`Mailgun Provider initialized for domain: ${config.domain}`);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
this.logger.error('Failed to initialize Mailgun:', error.message);
|
|
46
|
+
throw new Error('Mailgun initialization failed. Make sure mailgun.js and form-data are installed: npm install mailgun.js form-data');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Send a single email
|
|
51
|
+
*/ async sendEmail(options) {
|
|
52
|
+
if (!this.client || !this.config) {
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
error: 'Mailgun provider not initialized'
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const to = Array.isArray(options.to) ? options.to.join(', ') : options.to;
|
|
60
|
+
const cc = options.cc ? Array.isArray(options.cc) ? options.cc.join(', ') : options.cc : undefined;
|
|
61
|
+
const bcc = options.bcc ? Array.isArray(options.bcc) ? options.bcc.join(', ') : options.bcc : undefined;
|
|
62
|
+
const messageData = {
|
|
63
|
+
from: options.fromName ? `${options.fromName} <${options.from}>` : options.from,
|
|
64
|
+
to,
|
|
65
|
+
subject: options.subject
|
|
66
|
+
};
|
|
67
|
+
// Only include html/text if they have values (required for plain text templates)
|
|
68
|
+
if (options.html) messageData.html = options.html;
|
|
69
|
+
if (options.text) messageData.text = options.text;
|
|
70
|
+
// Optional fields
|
|
71
|
+
if (cc) messageData.cc = cc;
|
|
72
|
+
if (bcc) messageData.bcc = bcc;
|
|
73
|
+
if (options.replyTo) messageData['h:Reply-To'] = options.replyTo;
|
|
74
|
+
// Handle attachments
|
|
75
|
+
if (options.attachments && options.attachments.length > 0) {
|
|
76
|
+
messageData.attachment = options.attachments.map((a)=>({
|
|
77
|
+
filename: a.filename,
|
|
78
|
+
data: typeof a.content === 'string' ? Buffer.from(a.content, 'base64') : a.content,
|
|
79
|
+
contentType: a.contentType
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
const result = await this.client.messages.create(this.config.domain, messageData);
|
|
83
|
+
this.logger.log(`Email sent via Mailgun: ${result.id}`);
|
|
84
|
+
return {
|
|
85
|
+
success: true,
|
|
86
|
+
messageId: result.id
|
|
87
|
+
};
|
|
88
|
+
} catch (error) {
|
|
89
|
+
this.logger.error('Mailgun send failed:', error);
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
error: error.message
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Send multiple emails (batch)
|
|
98
|
+
*/ async sendBulkEmails(options) {
|
|
99
|
+
// Mailgun supports batch sending with recipient variables
|
|
100
|
+
// For simplicity, we send individually here
|
|
101
|
+
return Promise.all(options.map((opt)=>this.sendEmail(opt)));
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Health check for the provider
|
|
105
|
+
*/ async healthCheck() {
|
|
106
|
+
if (!this.client || !this.config) return false;
|
|
107
|
+
try {
|
|
108
|
+
// Verify domain exists
|
|
109
|
+
await this.client.domains.get(this.config.domain);
|
|
110
|
+
return true;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Cleanup
|
|
117
|
+
*/ async close() {
|
|
118
|
+
this.client = null;
|
|
119
|
+
}
|
|
120
|
+
constructor(){
|
|
121
|
+
_define_property(this, "logger", new _common.Logger(MailgunProvider.name));
|
|
122
|
+
_define_property(this, "client", null);
|
|
123
|
+
_define_property(this, "config", null);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "SendGridProvider", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return SendGridProvider;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _common = require("@nestjs/common");
|
|
12
|
+
function _define_property(obj, key, value) {
|
|
13
|
+
if (key in obj) {
|
|
14
|
+
Object.defineProperty(obj, key, {
|
|
15
|
+
value: value,
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true
|
|
19
|
+
});
|
|
20
|
+
} else {
|
|
21
|
+
obj[key] = value;
|
|
22
|
+
}
|
|
23
|
+
return obj;
|
|
24
|
+
}
|
|
25
|
+
let SendGridProvider = class SendGridProvider {
|
|
26
|
+
/**
|
|
27
|
+
* Initialize the SendGrid provider with configuration
|
|
28
|
+
*/ async initialize(config) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
try {
|
|
31
|
+
// Dynamic import @sendgrid/mail - use eval to bypass TypeScript module resolution
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
33
|
+
const sgMailModule = await new Function('return import("@sendgrid/mail")')();
|
|
34
|
+
this.sgMail = sgMailModule.default || sgMailModule;
|
|
35
|
+
this.sgMail.setApiKey(config.apiKey);
|
|
36
|
+
this.logger.log('SendGrid Provider initialized');
|
|
37
|
+
} catch (error) {
|
|
38
|
+
this.logger.error('Failed to initialize SendGrid:', error.message);
|
|
39
|
+
throw new Error('SendGrid initialization failed. Make sure @sendgrid/mail is installed: npm install @sendgrid/mail');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Send a single email
|
|
44
|
+
*/ async sendEmail(options) {
|
|
45
|
+
if (!this.sgMail) {
|
|
46
|
+
return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: 'SendGrid provider not initialized'
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const msg = {
|
|
53
|
+
to: Array.isArray(options.to) ? options.to : [
|
|
54
|
+
options.to
|
|
55
|
+
],
|
|
56
|
+
from: options.fromName ? {
|
|
57
|
+
email: options.from,
|
|
58
|
+
name: options.fromName
|
|
59
|
+
} : options.from,
|
|
60
|
+
subject: options.subject
|
|
61
|
+
};
|
|
62
|
+
// Only include html/text if they have values (required for plain text templates)
|
|
63
|
+
if (options.html) msg.html = options.html;
|
|
64
|
+
if (options.text) msg.text = options.text;
|
|
65
|
+
// Optional fields
|
|
66
|
+
if (options.cc) {
|
|
67
|
+
msg.cc = Array.isArray(options.cc) ? options.cc : [
|
|
68
|
+
options.cc
|
|
69
|
+
];
|
|
70
|
+
}
|
|
71
|
+
if (options.bcc) {
|
|
72
|
+
msg.bcc = Array.isArray(options.bcc) ? options.bcc : [
|
|
73
|
+
options.bcc
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
if (options.replyTo) msg.replyTo = options.replyTo;
|
|
77
|
+
if (options.attachments?.length) {
|
|
78
|
+
msg.attachments = options.attachments.map((a)=>({
|
|
79
|
+
filename: a.filename,
|
|
80
|
+
content: typeof a.content === 'string' ? a.content : a.content.toString('base64'),
|
|
81
|
+
type: a.contentType,
|
|
82
|
+
disposition: 'attachment'
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
const [response] = await this.sgMail.send(msg);
|
|
86
|
+
const messageId = response.headers['x-message-id'] || response.headers['X-Message-Id'];
|
|
87
|
+
this.logger.log(`Email sent via SendGrid: ${messageId}`);
|
|
88
|
+
return {
|
|
89
|
+
success: true,
|
|
90
|
+
messageId
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
this.logger.error('SendGrid send failed:', error);
|
|
94
|
+
const errorMessage = error.response?.body?.errors?.[0]?.message || error.message;
|
|
95
|
+
return {
|
|
96
|
+
success: false,
|
|
97
|
+
error: errorMessage
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Send multiple emails (batch)
|
|
103
|
+
*/ async sendBulkEmails(options) {
|
|
104
|
+
if (!this.sgMail) {
|
|
105
|
+
return options.map(()=>({
|
|
106
|
+
success: false,
|
|
107
|
+
error: 'SendGrid provider not initialized'
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
// SendGrid supports batch sending with sendMultiple
|
|
111
|
+
const messages = options.map((opt)=>{
|
|
112
|
+
const msg = {
|
|
113
|
+
to: Array.isArray(opt.to) ? opt.to : [
|
|
114
|
+
opt.to
|
|
115
|
+
],
|
|
116
|
+
from: opt.fromName ? {
|
|
117
|
+
email: opt.from,
|
|
118
|
+
name: opt.fromName
|
|
119
|
+
} : opt.from,
|
|
120
|
+
subject: opt.subject
|
|
121
|
+
};
|
|
122
|
+
if (opt.html) msg.html = opt.html;
|
|
123
|
+
if (opt.text) msg.text = opt.text;
|
|
124
|
+
return msg;
|
|
125
|
+
});
|
|
126
|
+
try {
|
|
127
|
+
await this.sgMail.send(messages);
|
|
128
|
+
return options.map(()=>({
|
|
129
|
+
success: true
|
|
130
|
+
}));
|
|
131
|
+
} catch (error) {
|
|
132
|
+
this.logger.error('SendGrid bulk send failed:', error);
|
|
133
|
+
return options.map(()=>({
|
|
134
|
+
success: false,
|
|
135
|
+
error: error.message
|
|
136
|
+
}));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Health check for the provider
|
|
141
|
+
*/ async healthCheck() {
|
|
142
|
+
// SendGrid doesn't have a direct health check API
|
|
143
|
+
// We verify the API key format is valid
|
|
144
|
+
return !!(this.sgMail && this.config?.apiKey?.startsWith('SG.'));
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Cleanup (no-op for SendGrid)
|
|
148
|
+
*/ async close() {
|
|
149
|
+
this.sgMail = null;
|
|
150
|
+
}
|
|
151
|
+
constructor(){
|
|
152
|
+
_define_property(this, "logger", new _common.Logger(SendGridProvider.name));
|
|
153
|
+
_define_property(this, "sgMail", null);
|
|
154
|
+
_define_property(this, "config", null);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "SmtpProvider", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return SmtpProvider;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _common = require("@nestjs/common");
|
|
12
|
+
function _define_property(obj, key, value) {
|
|
13
|
+
if (key in obj) {
|
|
14
|
+
Object.defineProperty(obj, key, {
|
|
15
|
+
value: value,
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true
|
|
19
|
+
});
|
|
20
|
+
} else {
|
|
21
|
+
obj[key] = value;
|
|
22
|
+
}
|
|
23
|
+
return obj;
|
|
24
|
+
}
|
|
25
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
26
|
+
if (typeof WeakMap !== "function") return null;
|
|
27
|
+
var cacheBabelInterop = new WeakMap();
|
|
28
|
+
var cacheNodeInterop = new WeakMap();
|
|
29
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
30
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
31
|
+
})(nodeInterop);
|
|
32
|
+
}
|
|
33
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
34
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
35
|
+
return obj;
|
|
36
|
+
}
|
|
37
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
38
|
+
return {
|
|
39
|
+
default: obj
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
43
|
+
if (cache && cache.has(obj)) {
|
|
44
|
+
return cache.get(obj);
|
|
45
|
+
}
|
|
46
|
+
var newObj = {
|
|
47
|
+
__proto__: null
|
|
48
|
+
};
|
|
49
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
50
|
+
for(var key in obj){
|
|
51
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
52
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
53
|
+
if (desc && (desc.get || desc.set)) {
|
|
54
|
+
Object.defineProperty(newObj, key, desc);
|
|
55
|
+
} else {
|
|
56
|
+
newObj[key] = obj[key];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
newObj.default = obj;
|
|
61
|
+
if (cache) {
|
|
62
|
+
cache.set(obj, newObj);
|
|
63
|
+
}
|
|
64
|
+
return newObj;
|
|
65
|
+
}
|
|
66
|
+
let SmtpProvider = class SmtpProvider {
|
|
67
|
+
/**
|
|
68
|
+
* Initialize the SMTP provider with configuration
|
|
69
|
+
*/ async initialize(config) {
|
|
70
|
+
this.config = config;
|
|
71
|
+
this.logger.log(`Initializing SMTP: host=${config.host}, port=${config.port}, secure=${config.secure}, user=${config.auth?.user}`);
|
|
72
|
+
// Dynamic import nodemailer
|
|
73
|
+
const nodemailer = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("nodemailer")));
|
|
74
|
+
// Determine secure mode: port 465 = implicit TLS, port 587 = STARTTLS
|
|
75
|
+
const isSecure = config.secure ?? config.port === 465;
|
|
76
|
+
this.transporter = nodemailer.createTransport({
|
|
77
|
+
host: config.host,
|
|
78
|
+
port: config.port,
|
|
79
|
+
secure: isSecure,
|
|
80
|
+
auth: config.auth,
|
|
81
|
+
// Timeout settings to prevent hanging
|
|
82
|
+
connectionTimeout: 10000,
|
|
83
|
+
greetingTimeout: 10000,
|
|
84
|
+
socketTimeout: 30000,
|
|
85
|
+
// TLS options for better compatibility
|
|
86
|
+
tls: {
|
|
87
|
+
rejectUnauthorized: false,
|
|
88
|
+
minVersion: 'TLSv1.2'
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
this.logger.log(`SMTP transporter created with secure=${isSecure}`);
|
|
92
|
+
// Verify connection with timeout
|
|
93
|
+
try {
|
|
94
|
+
const verifyPromise = this.transporter.verify();
|
|
95
|
+
const timeoutPromise = new Promise((_, reject)=>setTimeout(()=>reject(new Error('SMTP verification timeout')), 10000));
|
|
96
|
+
await Promise.race([
|
|
97
|
+
verifyPromise,
|
|
98
|
+
timeoutPromise
|
|
99
|
+
]);
|
|
100
|
+
this.logger.log(`SMTP Provider verified successfully: ${config.host}:${config.port}`);
|
|
101
|
+
} catch (error) {
|
|
102
|
+
this.logger.warn(`SMTP verification failed: ${error.message}`);
|
|
103
|
+
// Don't throw - allow initialization even if verification fails
|
|
104
|
+
// Connection will be retried on send
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Send a single email
|
|
109
|
+
*/ async sendEmail(options) {
|
|
110
|
+
if (!this.transporter) {
|
|
111
|
+
return {
|
|
112
|
+
success: false,
|
|
113
|
+
error: 'SMTP provider not initialized'
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const toAddress = Array.isArray(options.to) ? options.to.join(', ') : options.to;
|
|
117
|
+
this.logger.log(`Sending email to: ${toAddress}, subject: ${options.subject}`);
|
|
118
|
+
try {
|
|
119
|
+
const mailOptions = {
|
|
120
|
+
from: options.fromName ? `"${options.fromName}" <${options.from}>` : options.from,
|
|
121
|
+
to: toAddress,
|
|
122
|
+
cc: options.cc ? Array.isArray(options.cc) ? options.cc.join(', ') : options.cc : undefined,
|
|
123
|
+
bcc: options.bcc ? Array.isArray(options.bcc) ? options.bcc.join(', ') : options.bcc : undefined,
|
|
124
|
+
subject: options.subject,
|
|
125
|
+
text: options.text,
|
|
126
|
+
html: options.html,
|
|
127
|
+
replyTo: options.replyTo,
|
|
128
|
+
attachments: options.attachments?.map((a)=>({
|
|
129
|
+
filename: a.filename,
|
|
130
|
+
content: a.content,
|
|
131
|
+
contentType: a.contentType,
|
|
132
|
+
encoding: a.encoding
|
|
133
|
+
}))
|
|
134
|
+
};
|
|
135
|
+
this.logger.log(`Mail options: from=${mailOptions.from}, to=${mailOptions.to}`);
|
|
136
|
+
// Send with timeout
|
|
137
|
+
const sendPromise = this.transporter.sendMail(mailOptions);
|
|
138
|
+
const timeoutPromise = new Promise((_, reject)=>setTimeout(()=>reject(new Error('SMTP send timeout after 30 seconds')), 30000));
|
|
139
|
+
const info = await Promise.race([
|
|
140
|
+
sendPromise,
|
|
141
|
+
timeoutPromise
|
|
142
|
+
]);
|
|
143
|
+
this.logger.log(`Email sent via SMTP: ${info.messageId}`);
|
|
144
|
+
return {
|
|
145
|
+
success: true,
|
|
146
|
+
messageId: info.messageId
|
|
147
|
+
};
|
|
148
|
+
} catch (error) {
|
|
149
|
+
this.logger.error(`SMTP send failed: ${error.message}`, error.stack);
|
|
150
|
+
return {
|
|
151
|
+
success: false,
|
|
152
|
+
error: error.message
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Send multiple emails (batch)
|
|
158
|
+
*/ async sendBulkEmails(options) {
|
|
159
|
+
return Promise.all(options.map((opt)=>this.sendEmail(opt)));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Health check for the provider
|
|
163
|
+
*/ async healthCheck() {
|
|
164
|
+
if (!this.transporter) return false;
|
|
165
|
+
try {
|
|
166
|
+
await this.transporter.verify();
|
|
167
|
+
return true;
|
|
168
|
+
} catch {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Close the transporter
|
|
174
|
+
*/ async close() {
|
|
175
|
+
if (this.transporter) {
|
|
176
|
+
this.transporter.close();
|
|
177
|
+
this.transporter = null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
constructor(){
|
|
181
|
+
_define_property(this, "logger", new _common.Logger(SmtpProvider.name));
|
|
182
|
+
_define_property(this, "transporter", null);
|
|
183
|
+
_define_property(this, "config", null);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", {
|
|
3
|
+
value: true
|
|
4
|
+
});
|
|
5
|
+
Object.defineProperty(exports, "EmailDataSourceProvider", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
get: function() {
|
|
8
|
+
return EmailDataSourceProvider;
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
const _modules = require("@flusys/nestjs-shared/modules");
|
|
12
|
+
const _common = require("@nestjs/common");
|
|
13
|
+
const _core = require("@nestjs/core");
|
|
14
|
+
const _express = require("express");
|
|
15
|
+
const _interfaces = require("../interfaces");
|
|
16
|
+
const _emailconstants = require("../config/email.constants");
|
|
17
|
+
function _define_property(obj, key, value) {
|
|
18
|
+
if (key in obj) {
|
|
19
|
+
Object.defineProperty(obj, key, {
|
|
20
|
+
value: value,
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true
|
|
24
|
+
});
|
|
25
|
+
} else {
|
|
26
|
+
obj[key] = value;
|
|
27
|
+
}
|
|
28
|
+
return obj;
|
|
29
|
+
}
|
|
30
|
+
function _getRequireWildcardCache(nodeInterop) {
|
|
31
|
+
if (typeof WeakMap !== "function") return null;
|
|
32
|
+
var cacheBabelInterop = new WeakMap();
|
|
33
|
+
var cacheNodeInterop = new WeakMap();
|
|
34
|
+
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
35
|
+
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
36
|
+
})(nodeInterop);
|
|
37
|
+
}
|
|
38
|
+
function _interop_require_wildcard(obj, nodeInterop) {
|
|
39
|
+
if (!nodeInterop && obj && obj.__esModule) {
|
|
40
|
+
return obj;
|
|
41
|
+
}
|
|
42
|
+
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
43
|
+
return {
|
|
44
|
+
default: obj
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
var cache = _getRequireWildcardCache(nodeInterop);
|
|
48
|
+
if (cache && cache.has(obj)) {
|
|
49
|
+
return cache.get(obj);
|
|
50
|
+
}
|
|
51
|
+
var newObj = {
|
|
52
|
+
__proto__: null
|
|
53
|
+
};
|
|
54
|
+
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
55
|
+
for(var key in obj){
|
|
56
|
+
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
57
|
+
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
58
|
+
if (desc && (desc.get || desc.set)) {
|
|
59
|
+
Object.defineProperty(newObj, key, desc);
|
|
60
|
+
} else {
|
|
61
|
+
newObj[key] = obj[key];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
newObj.default = obj;
|
|
66
|
+
if (cache) {
|
|
67
|
+
cache.set(obj, newObj);
|
|
68
|
+
}
|
|
69
|
+
return newObj;
|
|
70
|
+
}
|
|
71
|
+
function _ts_decorate(decorators, target, key, desc) {
|
|
72
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
73
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
74
|
+
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;
|
|
75
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
76
|
+
}
|
|
77
|
+
function _ts_metadata(k, v) {
|
|
78
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
79
|
+
}
|
|
80
|
+
function _ts_param(paramIndex, decorator) {
|
|
81
|
+
return function(target, key) {
|
|
82
|
+
decorator(target, key, paramIndex);
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
let EmailDataSourceProvider = class EmailDataSourceProvider extends _modules.MultiTenantDataSourceService {
|
|
86
|
+
// ==================== Factory Methods ====================
|
|
87
|
+
/**
|
|
88
|
+
* Build parent options from EmailModuleOptions
|
|
89
|
+
*/ static buildParentOptions(options) {
|
|
90
|
+
return {
|
|
91
|
+
bootstrapAppConfig: options.bootstrapAppConfig,
|
|
92
|
+
defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
|
|
93
|
+
tenantDefaultDatabaseConfig: options.config?.tenantDefaultDatabaseConfig,
|
|
94
|
+
tenants: options.config?.tenants
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
// ==================== Feature Flags ====================
|
|
98
|
+
/**
|
|
99
|
+
* Get global enable company feature flag
|
|
100
|
+
*/ getEnableCompanyFeature() {
|
|
101
|
+
return this.emailOptions.bootstrapAppConfig?.enableCompanyFeature ?? false;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Get enable company feature for specific tenant
|
|
105
|
+
* Falls back to global setting if not specified per-tenant
|
|
106
|
+
*/ getEnableCompanyFeatureForTenant(tenant) {
|
|
107
|
+
return tenant?.enableCompanyFeature ?? this.getEnableCompanyFeature();
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get enable company feature for current request context
|
|
111
|
+
*/ getEnableCompanyFeatureForCurrentTenant() {
|
|
112
|
+
return this.getEnableCompanyFeatureForTenant(this.getCurrentTenant() ?? undefined);
|
|
113
|
+
}
|
|
114
|
+
// ==================== Entity Management ====================
|
|
115
|
+
/**
|
|
116
|
+
* Get email entities for migrations based on company feature flag
|
|
117
|
+
*/ async getEmailEntities(enableCompanyFeature) {
|
|
118
|
+
const enable = enableCompanyFeature ?? this.getEnableCompanyFeature();
|
|
119
|
+
const { EmailConfig, EmailTemplate } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")));
|
|
120
|
+
if (enable) {
|
|
121
|
+
const { EmailConfigWithCompany, EmailTemplateWithCompany } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../entities")));
|
|
122
|
+
return [
|
|
123
|
+
EmailConfigWithCompany,
|
|
124
|
+
EmailTemplateWithCompany
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
return [
|
|
128
|
+
EmailConfig,
|
|
129
|
+
EmailTemplate
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
// ==================== Overrides ====================
|
|
133
|
+
/**
|
|
134
|
+
* Override to dynamically set entities based on tenant config
|
|
135
|
+
*/ async createDataSourceFromConfig(config) {
|
|
136
|
+
const currentTenant = this.getCurrentTenant();
|
|
137
|
+
const enableCompanyFeature = this.getEnableCompanyFeatureForTenant(currentTenant ?? undefined);
|
|
138
|
+
const entities = await this.getEmailEntities(enableCompanyFeature);
|
|
139
|
+
return super.createDataSourceFromConfig(config, entities);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Override to use Email-specific static cache
|
|
143
|
+
*/ async getSingleDataSource() {
|
|
144
|
+
// Return existing initialized connection from Email-specific cache
|
|
145
|
+
if (EmailDataSourceProvider.singleDataSource?.isInitialized) {
|
|
146
|
+
return EmailDataSourceProvider.singleDataSource;
|
|
147
|
+
}
|
|
148
|
+
// If another request is creating the connection, wait for it
|
|
149
|
+
if (EmailDataSourceProvider.singleConnectionLock) {
|
|
150
|
+
return EmailDataSourceProvider.singleConnectionLock;
|
|
151
|
+
}
|
|
152
|
+
const config = this.getDefaultDatabaseConfig();
|
|
153
|
+
if (!config) {
|
|
154
|
+
throw new Error('No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.');
|
|
155
|
+
}
|
|
156
|
+
// Create connection with lock to prevent race conditions
|
|
157
|
+
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
158
|
+
EmailDataSourceProvider.singleConnectionLock = connectionPromise;
|
|
159
|
+
try {
|
|
160
|
+
const dataSource = await connectionPromise;
|
|
161
|
+
EmailDataSourceProvider.singleDataSource = dataSource;
|
|
162
|
+
return dataSource;
|
|
163
|
+
} finally{
|
|
164
|
+
EmailDataSourceProvider.singleConnectionLock = null;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Override to use Email-specific static cache for tenant connections
|
|
169
|
+
*/ async getOrCreateTenantConnection(tenant) {
|
|
170
|
+
// Return existing initialized connection from Email-specific cache
|
|
171
|
+
const existing = EmailDataSourceProvider.tenantConnections.get(tenant.id);
|
|
172
|
+
if (existing?.isInitialized) {
|
|
173
|
+
return existing;
|
|
174
|
+
}
|
|
175
|
+
// If another request is creating this tenant's connection, wait for it
|
|
176
|
+
const pendingConnection = EmailDataSourceProvider.connectionLocks.get(tenant.id);
|
|
177
|
+
if (pendingConnection) {
|
|
178
|
+
return pendingConnection;
|
|
179
|
+
}
|
|
180
|
+
// Create connection with lock to prevent race conditions
|
|
181
|
+
const config = this.buildTenantDatabaseConfig(tenant);
|
|
182
|
+
const connectionPromise = this.createDataSourceFromConfig(config);
|
|
183
|
+
EmailDataSourceProvider.connectionLocks.set(tenant.id, connectionPromise);
|
|
184
|
+
try {
|
|
185
|
+
const dataSource = await connectionPromise;
|
|
186
|
+
EmailDataSourceProvider.tenantConnections.set(tenant.id, dataSource);
|
|
187
|
+
return dataSource;
|
|
188
|
+
} finally{
|
|
189
|
+
EmailDataSourceProvider.connectionLocks.delete(tenant.id);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
constructor(emailOptions, request){
|
|
193
|
+
super(EmailDataSourceProvider.buildParentOptions(emailOptions), request), _define_property(this, "emailOptions", void 0), _define_property(this, "logger", void 0), this.emailOptions = emailOptions, this.logger = new _common.Logger(EmailDataSourceProvider.name);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
// Override parent's static properties to have Email-specific cache
|
|
197
|
+
_define_property(EmailDataSourceProvider, "tenantConnections", new Map());
|
|
198
|
+
_define_property(EmailDataSourceProvider, "singleDataSource", null);
|
|
199
|
+
_define_property(EmailDataSourceProvider, "tenantsRegistry", new Map());
|
|
200
|
+
_define_property(EmailDataSourceProvider, "initialized", false);
|
|
201
|
+
_define_property(EmailDataSourceProvider, "connectionLocks", new Map());
|
|
202
|
+
_define_property(EmailDataSourceProvider, "singleConnectionLock", null);
|
|
203
|
+
EmailDataSourceProvider = _ts_decorate([
|
|
204
|
+
(0, _common.Injectable)({
|
|
205
|
+
scope: _common.Scope.REQUEST
|
|
206
|
+
}),
|
|
207
|
+
_ts_param(0, (0, _common.Inject)(_emailconstants.EMAIL_MODULE_OPTIONS)),
|
|
208
|
+
_ts_param(1, (0, _common.Optional)()),
|
|
209
|
+
_ts_param(1, (0, _common.Inject)(_core.REQUEST)),
|
|
210
|
+
_ts_metadata("design:type", Function),
|
|
211
|
+
_ts_metadata("design:paramtypes", [
|
|
212
|
+
typeof _interfaces.EmailModuleOptions === "undefined" ? Object : _interfaces.EmailModuleOptions,
|
|
213
|
+
typeof _express.Request === "undefined" ? Object : _express.Request
|
|
214
|
+
])
|
|
215
|
+
], EmailDataSourceProvider);
|