@b2y/email-service 1.0.9 → 1.2.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/package.json CHANGED
@@ -1,53 +1,55 @@
1
- {
2
- "name": "@b2y/email-service",
3
- "version": "1.0.9",
4
- "main": "index.js",
5
- "exports": {
6
- ".": "./index.js",
7
- "./providers": "./providers/index.js",
8
- "./providers/base": "./providers/BaseProvider.js",
9
- "./providers/nodemailer": "./providers/NodeMailerProvider.js",
10
- "./providers/sendgrid": "./providers/SendGridProvider.js",
11
- "./utils/templateEngine": "./utils/TemplateEngine.js",
12
- "./EmailService": "./service/EmailService.js"
13
- },
14
- "files": [
15
- "providers/",
16
- "templates/",
17
- "utils/",
18
- "constants/",
19
- "enum/",
20
- "model/",
21
- "service/",
22
- "*.js"
23
- ],
24
- "scripts": {
25
- "test": "echo \"Error: no test specified\" && exit 1",
26
- "start": "node test.js"
27
- },
28
- "author": "",
29
- "license": "ISC",
30
- "description": "A flexible email service with support for multiple providers and custom templates",
31
- "keywords": [
32
- "email",
33
- "templates",
34
- "nodemailer",
35
- "sendgrid"
36
- ],
37
- "publishConfig": {
38
- "access": "public"
39
- },
40
- "dependencies": {
41
- "@aws-sdk/client-ses": "^3.957.0",
42
- "@sendgrid/mail": "^8.1.5",
43
- "b2y-logger": "^1.1.0",
44
- "dotenv": "^16.5.0",
45
- "handlebars": "^4.7.8",
46
- "log4js": "^6.9.1",
47
- "nodemailer": "^6.10.0",
48
- "pg": "^8.16.3",
49
- "pg-hstore": "^2.3.4",
50
- "postmark": "^4.0.5",
51
- "sequelize": "^6.37.7"
52
- }
53
- }
1
+ {
2
+ "name": "@b2y/email-service",
3
+ "version": "1.2.0",
4
+ "main": "index.js",
5
+ "exports": {
6
+ ".": "./index.js",
7
+ "./providers": "./providers/index.js",
8
+ "./providers/base": "./providers/BaseProvider.js",
9
+ "./providers/nodemailer": "./providers/NodeMailerProvider.js",
10
+ "./providers/sendgrid": "./providers/SendGridProvider.js",
11
+ "./utils/templateEngine": "./utils/TemplateEngine.js",
12
+ "./EmailService": "./service/EmailService.js"
13
+ },
14
+ "files": [
15
+ "providers/",
16
+ "templates/",
17
+ "utils/",
18
+ "constants/",
19
+ "enum/",
20
+ "model/",
21
+ "service/",
22
+ "*.js"
23
+ ],
24
+ "scripts": {
25
+ "test": "echo \"Error: no test specified\" && exit 1",
26
+ "start": "node test.js"
27
+ },
28
+ "author": "",
29
+ "license": "ISC",
30
+ "description": "A flexible email service with support for multiple providers and custom templates",
31
+ "keywords": [
32
+ "email",
33
+ "templates",
34
+ "nodemailer",
35
+ "sendgrid"
36
+ ],
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "dependencies": {
41
+ "@aws-sdk/client-ses": "^3.957.0",
42
+ "@sendgrid/mail": "^8.1.5",
43
+ "b2y-logger": "^1.1.0",
44
+ "dotenv": "^16.5.0",
45
+ "form-data": "^4.0.5",
46
+ "handlebars": "^4.7.8",
47
+ "log4js": "^6.9.1",
48
+ "mailgun.js": "^12.7.0",
49
+ "nodemailer": "^6.10.0",
50
+ "pg": "^8.16.3",
51
+ "pg-hstore": "^2.3.4",
52
+ "postmark": "^4.0.5",
53
+ "sequelize": "^6.37.7"
54
+ }
55
+ }
@@ -1,77 +1,84 @@
1
- // providers/AmazonSES.js
2
- const { SESClient, SendEmailCommand } = require('@aws-sdk/client-ses');
3
- const BaseProvider = require('./BaseProvider');
4
- const Config = require('../Config');
5
- const logger = require('../Logger');
6
- const StatusMessage = require('../constants/StatusMessageConstants');
7
- const { EmailProvider } = require('../enum/EmailProvider');
8
-
9
- class AmazonSESProvider extends BaseProvider {
10
- constructor() {
11
- super();
12
- this.client = new SESClient({
13
- region: Config.awsRegion,
14
- credentials: {
15
- accessKeyId: Config.awsAccessKeyId,
16
- secretAccessKey: Config.awsSecretAccessKey
17
- }
18
- });
19
- }
20
-
21
- async sendEmail(options) {
22
- try {
23
- this.validateEmailOptions(options);
24
-
25
- const toAddresses = this.formatRecipients(options.to);
26
- const ccAddresses = options.cc ? this.formatRecipients(options.cc) : [];
27
- const bccAddresses = options.bcc ? this.formatRecipients(options.bcc) : [];
28
-
29
- const params = {
30
- Source: options.from || Config.awsFromEmail,
31
- Destination: {
32
- ToAddresses: toAddresses,
33
- ...(ccAddresses.length > 0 && { CcAddresses: ccAddresses }),
34
- ...(bccAddresses.length > 0 && { BccAddresses: bccAddresses })
35
- },
36
- Message: {
37
- Subject: {
38
- Data: options.subject,
39
- Charset: 'UTF-8'
40
- },
41
- Body: {
42
- Html: {
43
- Data: options.html,
44
- Charset: 'UTF-8'
45
- }
46
- }
47
- }
48
- };
49
-
50
- // Add attachments if present (SES requires using SendRawEmail for attachments)
51
- if (options.attachments && options.attachments.length > 0) {
52
- logger.warn('SES provider: Attachments require SendRawEmail. Consider implementing raw email support.');
53
- }
54
-
55
- const command = new SendEmailCommand(params);
56
- const result = await this.client.send(command);
57
-
58
- logger.info('Email sent successfully via Amazon SES', {
59
- messageId: result.MessageId,
60
- recipients: { to: options.to, cc: options.cc, bcc: options.bcc },
61
- tenantId: options.tenantId
62
- });
63
-
64
- return {
65
- success: true,
66
- messageId: result.MessageId,
67
- provider: EmailProvider.SES,
68
- tenantId: options.tenantId
69
- };
70
- } catch (error) {
71
- logger.error(`Amazon SES failed to send email: ${error.message}`);
72
- throw new Error(`${StatusMessage.AMAZON_SES_SEND_ERROR}: ${error.message}`);
73
- }
74
- }
75
- }
76
-
1
+ // providers/AmazonSES.js
2
+ const { SESClient, SendEmailCommand } = require('@aws-sdk/client-ses');
3
+ const BaseProvider = require('./BaseProvider');
4
+ const Config = require('../Config');
5
+ const logger = require('../Logger');
6
+ const StatusMessage = require('../constants/StatusMessageConstants');
7
+ const { EmailProvider } = require('../enum/EmailProvider');
8
+
9
+ class AmazonSESProvider extends BaseProvider {
10
+ constructor() {
11
+ super();
12
+ this.client = new SESClient({
13
+ region: Config.awsRegion,
14
+ credentials: {
15
+ accessKeyId: Config.awsAccessKeyId,
16
+ secretAccessKey: Config.awsSecretAccessKey
17
+ }
18
+ });
19
+ }
20
+
21
+ async sendEmail(options) {
22
+ try {
23
+ this.validateEmailOptions(options);
24
+
25
+ const toAddresses = this.formatRecipients(options.to);
26
+ const ccAddresses = options.cc ? this.formatRecipients(options.cc) : [];
27
+ const bccAddresses = options.bcc ? this.formatRecipients(options.bcc) : [];
28
+
29
+
30
+ const fromAddress = this.formatFromAddress(
31
+ options.from || Config.awsFromEmail,
32
+ options.fromName || Config.fromName
33
+ );
34
+
35
+ const params = {
36
+ Source: fromAddress,
37
+ Destination: {
38
+ ToAddresses: toAddresses,
39
+ ...(ccAddresses.length > 0 && { CcAddresses: ccAddresses }),
40
+ ...(bccAddresses.length > 0 && { BccAddresses: bccAddresses })
41
+ },
42
+ Message: {
43
+ Subject: {
44
+ Data: options.subject,
45
+ Charset: 'UTF-8'
46
+ },
47
+ Body: {
48
+ Html: {
49
+ Data: options.html,
50
+ Charset: 'UTF-8'
51
+ }
52
+ }
53
+ }
54
+ };
55
+
56
+ // Add attachments if present (SES requires using SendRawEmail for attachments)
57
+ if (options.attachments && options.attachments.length > 0) {
58
+ logger.warn('SES provider: Attachments require SendRawEmail. Consider implementing raw email support.');
59
+ }
60
+
61
+ const command = new SendEmailCommand(params);
62
+ const result = await this.client.send(command);
63
+
64
+ logger.info('Email sent successfully via Amazon SES', {
65
+ messageId: result.MessageId,
66
+ recipients: { to: options.to, cc: options.cc, bcc: options.bcc },
67
+ tenantId: options.tenantId,
68
+ from: fromAddress
69
+ });
70
+
71
+ return {
72
+ success: true,
73
+ messageId: result.MessageId,
74
+ provider: EmailProvider.SES,
75
+ tenantId: options.tenantId
76
+ };
77
+ } catch (error) {
78
+ logger.error(`Amazon SES failed to send email: ${error.message}`);
79
+ throw new Error(`${StatusMessage.AMAZON_SES_SEND_ERROR}: ${error.message}`);
80
+ }
81
+ }
82
+ }
83
+
77
84
  module.exports = AmazonSESProvider;
@@ -1,74 +1,86 @@
1
- // providers/BaseProvider.js
2
- const logger = require('../Logger');
3
- const StatusMessage = require('../constants/StatusMessageConstants');
4
- class BaseProvider {
5
- constructor() {
6
- if (new.target === BaseProvider) {
7
- throw new Error(StatusMessage.CANNOT_INSTANTIATE_BASE);
8
- }
9
- }
10
-
11
- async sendEmail(options) {
12
- logger.error('sendEmail method not implemented in BaseProvider');
13
- throw new Error(StatusMessage.PROVIDER_NOT_IMPLEMENTED);
14
- }
15
-
16
- validateEmailOptions(options) {
17
- const { to, subject, html, from } = options;
18
-
19
- if (!to || (Array.isArray(to) && to.length === 0)) {
20
- throw new Error(StatusMessage.RECIPIENTS_REQUIRED);
21
- }
22
-
23
- if (!subject) {
24
- throw new Error(StatusMessage.SUBJECT_REQUIRED);
25
- }
26
-
27
- if (!html) {
28
- throw new Error(StatusMessage.HTML_CONTENT_REQUIRED);
29
- }
30
-
31
- return true;
32
- }
33
-
34
- formatRecipients(recipients) {
35
- if (!recipients) return [];
36
-
37
- if (typeof recipients === 'string') {
38
- return recipients.split(',').map(r => r.trim());
39
- }
40
-
41
- if (Array.isArray(recipients)) {
42
- return recipients;
43
- }
44
-
45
- return [];
46
- }
47
-
48
- buildMailOptions(options) {
49
- const { to, cc, bcc, subject, html, from, attachments } = options;
50
-
51
- const mailOptions = {
52
- from,
53
- to: this.formatRecipients(to),
54
- subject,
55
- html,
56
- };
57
-
58
- if (cc && cc.length > 0) {
59
- mailOptions.cc = this.formatRecipients(cc);
60
- }
61
-
62
- if (bcc && bcc.length > 0) {
63
- mailOptions.bcc = this.formatRecipients(bcc);
64
- }
65
-
66
- if (attachments && attachments.length > 0) {
67
- mailOptions.attachments = attachments;
68
- }
69
-
70
- return mailOptions;
71
- }
72
- }
73
-
1
+ // providers/BaseProvider.js
2
+ const logger = require('../Logger');
3
+ const StatusMessage = require('../constants/StatusMessageConstants');
4
+ const Config = require('../Config');
5
+
6
+ class BaseProvider {
7
+ constructor() {
8
+ if (new.target === BaseProvider) {
9
+ throw new Error(StatusMessage.CANNOT_INSTANTIATE_BASE);
10
+ }
11
+ }
12
+
13
+ async sendEmail(options) {
14
+ logger.error('sendEmail method not implemented in BaseProvider');
15
+ throw new Error(StatusMessage.PROVIDER_NOT_IMPLEMENTED);
16
+ }
17
+
18
+ validateEmailOptions(options) {
19
+ const { to, subject, html, from } = options;
20
+
21
+ if (!to || (Array.isArray(to) && to.length === 0)) {
22
+ throw new Error(StatusMessage.RECIPIENTS_REQUIRED);
23
+ }
24
+
25
+ if (!subject) {
26
+ throw new Error(StatusMessage.SUBJECT_REQUIRED);
27
+ }
28
+
29
+ if (!html) {
30
+ throw new Error(StatusMessage.HTML_CONTENT_REQUIRED);
31
+ }
32
+
33
+ return true;
34
+ }
35
+
36
+ formatRecipients(recipients) {
37
+ if (!recipients) return [];
38
+
39
+ if (typeof recipients === 'string') {
40
+ return recipients.split(',').map(r => r.trim());
41
+ }
42
+
43
+ if (Array.isArray(recipients)) {
44
+ return recipients;
45
+ }
46
+
47
+ return [];
48
+ }
49
+
50
+ formatFromAddress(email, name = null) {
51
+ const fromName = name || Config.fromName;
52
+ const fromEmail = email || Config.fromEmail;
53
+
54
+ if (!fromEmail) {
55
+ throw new Error('FROM_EMAIL is required');
56
+ }
57
+ return fromName ? `"${fromName}" <${fromEmail}>` : fromEmail;
58
+ }
59
+
60
+ buildMailOptions(options) {
61
+ const { to, cc, bcc, subject, html, from, fromName, attachments } = options;
62
+
63
+ const mailOptions = {
64
+ from: this.formatFromAddress(from, fromName),
65
+ to: this.formatRecipients(to),
66
+ subject,
67
+ html,
68
+ };
69
+
70
+ if (cc && cc.length > 0) {
71
+ mailOptions.cc = this.formatRecipients(cc);
72
+ }
73
+
74
+ if (bcc && bcc.length > 0) {
75
+ mailOptions.bcc = this.formatRecipients(bcc);
76
+ }
77
+
78
+ if (attachments && attachments.length > 0) {
79
+ mailOptions.attachments = attachments;
80
+ }
81
+
82
+ return mailOptions;
83
+ }
84
+ }
85
+
74
86
  module.exports = BaseProvider;
@@ -0,0 +1,85 @@
1
+ // providers/MailgunProvider.js
2
+ const formData = require('form-data');
3
+ const Mailgun = require('mailgun.js');
4
+ const BaseProvider = require('./BaseProvider');
5
+ const Config = require('../Config');
6
+ const logger = require('../Logger');
7
+ const StatusMessage = require('../constants/StatusMessageConstants');
8
+ const { EmailProvider } = require('../enum/EmailProvider');
9
+ const AppConstants = require('../constants/AppConstants');
10
+ class MailgunProvider extends BaseProvider {
11
+ constructor() {
12
+ super();
13
+
14
+ if (!Config.mailgunApiKey) {
15
+ throw new Error('Mailgun API key is required');
16
+ }
17
+
18
+ if (!Config.mailgunDomain) {
19
+ throw new Error('Mailgun domain is required');
20
+ }
21
+
22
+ const mailgun = new Mailgun(formData);
23
+ this.client = mailgun.client({
24
+ username: AppConstants.MAILGUN_AUTH_USERNAME,
25
+ key: Config.mailgunApiKey
26
+ });
27
+ this.domain = Config.mailgunDomain;
28
+ }
29
+
30
+ async sendEmail(options) {
31
+ try {
32
+ this.validateEmailOptions(options);
33
+
34
+ const messageData = {
35
+ from: this.formatFromAddress(
36
+ options.from || Config.fromEmail,
37
+ options.fromName || Config.fromName
38
+ ),
39
+ to: this.formatRecipients(options.to),
40
+ subject: options.subject,
41
+ html: options.html
42
+ };
43
+
44
+ // Add CC if present
45
+ if (options.cc && options.cc.length > 0) {
46
+ messageData.cc = this.formatRecipients(options.cc);
47
+ }
48
+
49
+ // Add BCC if present
50
+ if (options.bcc && options.bcc.length > 0) {
51
+ messageData.bcc = this.formatRecipients(options.bcc);
52
+ }
53
+
54
+ // Add attachments if present
55
+ if (options.attachments && options.attachments.length > 0) {
56
+ messageData.attachment = options.attachments.map(att => ({
57
+ filename: att.filename,
58
+ data: att.content,
59
+ contentType: att.contentType
60
+ }));
61
+ }
62
+
63
+ const result = await this.client.messages.create(this.domain, messageData);
64
+
65
+ logger.info('Email sent successfully via Mailgun', {
66
+ messageId: result.id,
67
+ recipients: { to: options.to, cc: options.cc, bcc: options.bcc },
68
+ tenantId: options.tenantId,
69
+ from: messageData.from
70
+ });
71
+
72
+ return {
73
+ success: true,
74
+ messageId: result.id,
75
+ provider: EmailProvider.MAILGUN,
76
+ tenantId: options.tenantId
77
+ };
78
+ } catch (error) {
79
+ logger.error('Mailgun failed to send email:', error);
80
+ throw new Error(StatusMessage.MAILGUN_SEND_ERROR);
81
+ }
82
+ }
83
+ }
84
+
85
+ module.exports = MailgunProvider;
@@ -1,52 +1,53 @@
1
- // providers/NodeMailerProvider.js
2
- const nodemailer = require('nodemailer');
3
- const BaseProvider = require('./BaseProvider');
4
- const Config = require('../Config');
5
- const logger = require('../Logger');
6
- const StatusMessage = require('../constants/StatusMessageConstants');
7
- const { EmailProvider } = require('../enum/EmailProvider');
8
-
9
- class NodeMailerProvider extends BaseProvider {
10
- constructor() {
11
- super();
12
- this.transporter = nodemailer.createTransport({
13
- service: Config.emailService,
14
- host: Config.emailHost,
15
- port: Config.smtpPort,
16
- secure: Config.smtpPort,
17
- auth: {
18
- user: Config.emailUser,
19
- pass: Config.emailPass,
20
- },
21
- });
22
- }
23
-
24
- async sendEmail(options) {
25
- try {
26
- this.validateEmailOptions(options);
27
- const mailOptions = this.buildMailOptions({
28
- ...options,
29
- from: options.from || Config.emailUser
30
- });
31
- const result = await this.transporter.sendMail(mailOptions);
32
-
33
- logger.info('Email sent successfully via Nodemailer', {
34
- messageId: result.messageId,
35
- recipients: { to: options.to, cc: options.cc, bcc: options.bcc },
36
- tenantId: options.tenantId
37
- });
38
-
39
- return {
40
- success: true,
41
- messageId: result.messageId,
42
- provider: EmailProvider.NODE_MAILER,
43
- tenantId: options.tenantId
44
- };
45
- } catch (error) {
46
- logger.error(`Nodemailer failed to send email: ${error.message}`);
47
- throw new Error(`${StatusMessage.NODEMAILER_SEND_ERROR}: ${error.message}`);
48
- }
49
- }
50
- }
51
-
1
+ // providers/NodeMailerProvider.js
2
+ const nodemailer = require('nodemailer');
3
+ const BaseProvider = require('./BaseProvider');
4
+ const Config = require('../Config');
5
+ const logger = require('../Logger');
6
+ const StatusMessage = require('../constants/StatusMessageConstants');
7
+ const { EmailProvider } = require('../enum/EmailProvider');
8
+
9
+ class NodeMailerProvider extends BaseProvider {
10
+ constructor() {
11
+ super();
12
+ this.transporter = nodemailer.createTransport({
13
+ host: Config.emailHost,
14
+ port: Config.smtpPort,
15
+ secure: Config.smtpPort,
16
+ auth: {
17
+ user: Config.emailUser,
18
+ pass: Config.emailPass,
19
+ },
20
+ });
21
+ }
22
+
23
+ async sendEmail(options) {
24
+ try {
25
+ this.validateEmailOptions(options);
26
+ const mailOptions = this.buildMailOptions({
27
+ ...options,
28
+ from: options.from || Config.fromEmail,
29
+ fromName: options.fromName || Config.fromName
30
+ });
31
+ const result = await this.transporter.sendMail(mailOptions);
32
+
33
+ logger.info('Email sent successfully via Nodemailer', {
34
+ messageId: result.messageId,
35
+ recipients: { to: options.to, cc: options.cc, bcc: options.bcc },
36
+ tenantId: options.tenantId,
37
+ from: mailOptions.from
38
+ });
39
+
40
+ return {
41
+ success: true,
42
+ messageId: result.messageId,
43
+ provider: EmailProvider.NODE_MAILER,
44
+ tenantId: options.tenantId
45
+ };
46
+ } catch (error) {
47
+ logger.error(`Nodemailer failed to send email: ${error.message}`);
48
+ throw new Error(`${StatusMessage.NODEMAILER_SEND_ERROR}: ${error.message}`);
49
+ }
50
+ }
51
+ }
52
+
52
53
  module.exports = NodeMailerProvider;