@xcelsior/email 1.0.1 → 1.0.3
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/.turbo/turbo-build.log +14 -14
- package/CHANGELOG.md +8 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +13 -7
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/email.ts +17 -6
- package/.turbo/turbo-lint.log +0 -5
- package/.turbo/turbo-test.log +0 -8
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
|
|
2
|
-
> @xcelsior/email@1.0.
|
|
3
|
-
> tsup
|
|
2
|
+
> @xcelsior/email@1.0.3 build /home/circleci/repo/packages/services/email
|
|
3
|
+
> tsup && tsc --noEmit
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
6
6
|
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
-
[34mCLI[39m tsup v8.5.
|
|
8
|
-
[34mCLI[39m Using tsup config: /
|
|
7
|
+
[34mCLI[39m tsup v8.5.1
|
|
8
|
+
[34mCLI[39m Using tsup config: /home/circleci/repo/packages/services/email/tsup.config.ts
|
|
9
9
|
[34mCLI[39m Target: es2020
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mCJS[39m Build start
|
|
12
12
|
[34mESM[39m Build start
|
|
13
|
-
[
|
|
14
|
-
[
|
|
15
|
-
[
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m7.95 KB[39m
|
|
14
|
+
[32mCJS[39m [1mdist/index.js.map [22m[32m12.80 KB[39m
|
|
15
|
+
[32mCJS[39m ⚡️ Build success in 16ms
|
|
16
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m5.72 KB[39m
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs.map [22m[32m12.54 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 15ms
|
|
19
|
+
[34mDTS[39m Build start
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 5052ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m818.00 B[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.mts [22m[32m818.00 B[39m
|
package/CHANGELOG.md
ADDED
package/dist/index.d.mts
CHANGED
|
@@ -11,7 +11,7 @@ interface Email {
|
|
|
11
11
|
attachments?: any[];
|
|
12
12
|
subject: string;
|
|
13
13
|
}
|
|
14
|
-
declare const emailTransporter: (
|
|
14
|
+
declare const emailTransporter: (logger?: any) => {
|
|
15
15
|
sendMail({ to, reference, from, replyTo, template, text, attachments, idempotencyKey, subject, }: Email, idempotentCheck?: boolean): Promise<nodemailer_lib_smtp_pool.SentMessageInfo | undefined>;
|
|
16
16
|
};
|
|
17
17
|
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ interface Email {
|
|
|
11
11
|
attachments?: any[];
|
|
12
12
|
subject: string;
|
|
13
13
|
}
|
|
14
|
-
declare const emailTransporter: (
|
|
14
|
+
declare const emailTransporter: (logger?: any) => {
|
|
15
15
|
sendMail({ to, reference, from, replyTo, template, text, attachments, idempotencyKey, subject, }: Email, idempotentCheck?: boolean): Promise<nodemailer_lib_smtp_pool.SentMessageInfo | undefined>;
|
|
16
16
|
};
|
|
17
17
|
|
package/dist/index.js
CHANGED
|
@@ -108,22 +108,28 @@ var EmailLogService = class {
|
|
|
108
108
|
var sesClient = new import_client_sesv2.SESv2Client({
|
|
109
109
|
region: process.env.AWS_REGION
|
|
110
110
|
});
|
|
111
|
-
var emailOptions = process.env.
|
|
112
|
-
port:
|
|
113
|
-
host: process.env.SMTP_HOST
|
|
111
|
+
var emailOptions = process.env.EMAIL_TRANSPORT === "smtp" ? {
|
|
112
|
+
port: process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT, 10) : 587,
|
|
113
|
+
host: process.env.SMTP_HOST,
|
|
114
|
+
auth: process.env.SMTP_USER && process.env.SMTP_PASSWORD ? {
|
|
115
|
+
user: process.env.SMTP_USER,
|
|
116
|
+
pass: process.env.SMTP_PASSWORD
|
|
117
|
+
} : void 0
|
|
114
118
|
} : {
|
|
115
119
|
SES: { sesClient, SendEmailCommand: import_client_sesv2.SendEmailCommand }
|
|
116
120
|
};
|
|
117
|
-
var emailTransporter = (
|
|
121
|
+
var emailTransporter = (logger = console) => {
|
|
118
122
|
const transporter = import_nodemailer.default.createTransport(
|
|
119
123
|
{
|
|
120
124
|
...emailOptions,
|
|
121
125
|
debug: true
|
|
122
126
|
},
|
|
123
127
|
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
128
|
+
...process.env.EMAIL_SOURCE_ARN ? {
|
|
129
|
+
ses: {
|
|
130
|
+
FromEmailAddressIdentityArn: process.env.EMAIL_SOURCE_ARN
|
|
131
|
+
}
|
|
132
|
+
} : void 0
|
|
127
133
|
}
|
|
128
134
|
);
|
|
129
135
|
return {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/email.ts","../src/email-log.ts","../src/template.ts"],"sourcesContent":["export * from './email';\nexport * from './template';\n","import { SendEmailCommand, SESv2Client } from '@aws-sdk/client-sesv2';\nimport nodemailer from 'nodemailer';\nimport { EmailLogService } from './email-log';\n\n/**\n * Template to use\n * await emailTransporter().sendMail({\n from: 'AusVie noreply<noreply@ausvie.com.au>',\n to: 'admin@ausvie.com.au',\n subject: 'Test', // Subject line\n text: 'Test text', // plaintext version\n html: `<div>${'Test text'}</div>`, // html version\n // attachments: [\n // {\n // filename,\n // content: fileData,\n // },\n // ],\n });\n */\n// Load the AWS SDK for Node.js\n// https://stackoverflow.com/questions/23042835/sending-mail-via-aws-ses-with-attachment-in-node-js\n\nconst sesClient = new SESv2Client({\n region: process.env.AWS_REGION,\n});\n\nconst emailOptions =\n process.env.NODE_ENV === 'development'\n ? {\n port: 1025,\n host: process.env.SMTP_HOST,\n }\n : {\n SES: { sesClient, SendEmailCommand },\n };\n\nexport interface Email {\n to?: string | null;\n from?: string;\n reference?: string;\n template?: string;\n idempotencyKey?: string;\n replyTo?: string | null;\n text?: string;\n attachments?: any[];\n subject: string;\n}\n\nexport const emailTransporter = (sourceArn: string | undefined, logger: any = console) => {\n const transporter = nodemailer.createTransport(\n {\n ...emailOptions,\n debug: true,\n } as any,\n {\n ses: {\n FromEmailAddressIdentityArn: sourceArn,\n },\n } as any\n );\n\n return {\n async sendMail(\n {\n to,\n reference,\n from,\n replyTo,\n template,\n text,\n attachments = [],\n idempotencyKey,\n subject,\n }: Email,\n idempotentCheck?: boolean\n ) {\n if (!to) {\n return;\n }\n const sender = from;\n const emailLogService = new EmailLogService();\n logger.info(\n `Sending email to ${to} with subject: ${subject}, reference: ${reference}, idempotencyKey: ${idempotencyKey}`\n );\n\n try {\n if (idempotentCheck) {\n const existingEmail =\n await emailLogService.getEmailByIdempotencyKey(idempotencyKey);\n if (existingEmail) {\n logger.info(`Email with idempotency key ${idempotencyKey} already sent`);\n return;\n }\n }\n const result = await transporter.sendMail({\n to,\n from: sender,\n replyTo: replyTo ?? undefined,\n html: template,\n text,\n subject,\n attachments,\n });\n\n // Log successful email\n await emailLogService.logEmail({\n subject: subject || 'No Subject',\n reference,\n sender: sender || 'Unknown Sender',\n idempotencyKey,\n receiver: to,\n status: 'success',\n });\n\n return result;\n } catch (error) {\n // Log failed email\n await emailLogService.logEmail({\n subject: subject || 'No Subject',\n sender: sender || 'Unknown Sender',\n receiver: to,\n status: 'failed',\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n }\n },\n };\n};\n","import { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport { DynamoDBDocumentClient, PutCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';\nimport { v4 as uuidv4 } from 'uuid';\n\ninterface EmailLog {\n reference?: string;\n timestamp: number;\n subject: string;\n sender: string;\n receiver: string;\n idempotencyKey?: string;\n status: 'success' | 'failed';\n error?: string;\n}\n\nexport class EmailLogService {\n private readonly tableName: string;\n private readonly dynamoClient: DynamoDBClient | undefined;\n private readonly docClient: DynamoDBDocumentClient | undefined;\n\n constructor() {\n this.tableName = process.env.EMAIL_TABLE_NAME!;\n if (this.tableName) {\n this.dynamoClient = new DynamoDBClient({\n region: process.env.AWS_REGION,\n });\n this.docClient = DynamoDBDocumentClient.from(this.dynamoClient);\n }\n }\n\n async logEmail({\n subject,\n sender,\n receiver,\n status,\n error,\n idempotencyKey,\n reference = uuidv4(),\n }: Omit<EmailLog, 'timestamp'>): Promise<string> {\n if (!this.docClient) {\n return reference;\n }\n const timestamp = Date.now();\n\n const log: EmailLog = {\n reference,\n timestamp,\n subject,\n sender,\n receiver,\n status,\n idempotencyKey,\n ...(error && { error }),\n };\n\n await this.docClient.send(\n new PutCommand({\n TableName: this.tableName,\n Item: log,\n })\n );\n return reference;\n }\n\n async getEmailByIdempotencyKey(idempotencyKey: string | undefined) {\n if (!idempotencyKey || !this.docClient) {\n return null;\n }\n\n const result = await this.docClient.send(\n new QueryCommand({\n TableName: this.tableName,\n IndexName: 'idempotency-key-index',\n KeyConditionExpression: 'idempotencyKey = :idempotencyKey',\n ExpressionAttributeValues: {\n ':idempotencyKey': idempotencyKey,\n },\n Limit: 1,\n })\n );\n return result.Items?.[0] as EmailLog | null;\n }\n}\n","import fs from 'node:fs';\nimport { formatters, dates } from '@xcelsior/utils';\nimport handlebars from 'handlebars';\nimport _ from 'lodash';\nimport moment from 'moment';\n\nconst { getDateTime } = dates;\nconst { convertMoneyFormat, moneyFormat } = formatters;\n\nhandlebars.registerHelper('multiply', (a, b) => a * b);\n\nhandlebars.registerHelper('datetime', (a) => moment(getDateTime(a)).format('DD/MM/YYYY HH:mm'));\n\nhandlebars.registerHelper('moneyFormat', (a, ...others) => {\n let currency = 'AUD' as const;\n if (others.length > 1) {\n currency = others[0] as any;\n }\n return moneyFormat(a, currency);\n});\n\nhandlebars.registerHelper('convertMoneyFormat', (amount, currency, rate = 1) =>\n convertMoneyFormat(amount, currency, rate)\n);\n\nhandlebars.registerHelper('titleCase', (string) => {\n if (!string) return '';\n return _.startCase(_.camelCase(string)).replace(/\\s/g, ' ');\n});\n\nhandlebars.registerHelper('choose', (a, b) => (a ? a : b));\n\nhandlebars.registerHelper(\n 'phoneFormat',\n (phone) =>\n `${phone.substring(0, phone.length / 2 - 2)}****${phone.substring(phone.length / 2 + 2)}`\n);\n\nhandlebars.registerHelper('title', (title) => _.startCase(title));\n\nhandlebars.registerHelper('eq', (a, b) => a === b);\nhandlebars.registerHelper('lowercase', (title: string) => title.toLowerCase());\n\nhandlebars.registerHelper('JSONparse', (string, key) => JSON.parse(string)[key]);\n\nhandlebars.registerHelper('incr', (a) => a + 1);\n\nhandlebars.registerHelper('gt', (a, b) => a > b);\n\nexport function renderTemplate(content: string, data: Record<string, any>): string {\n return handlebars.compile(content)(data);\n}\n\nexport async function renderTemplateFile(file: string, data: Record<string, any>) {\n const content = await fs.promises.readFile(file, {\n encoding: 'utf-8',\n });\n return renderTemplate(content, data);\n}\n"],"mappings":"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","names":["uuidv4","nodemailer","handlebars","moment","_","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/email.ts","../src/email-log.ts","../src/template.ts"],"sourcesContent":["export * from './email';\nexport * from './template';\n","import { SendEmailCommand, SESv2Client } from '@aws-sdk/client-sesv2';\nimport nodemailer from 'nodemailer';\nimport { EmailLogService } from './email-log';\n\n/**\n * Template to use\n * await emailTransporter().sendMail({\n from: 'AusVie noreply<noreply@ausvie.com.au>',\n to: 'admin@ausvie.com.au',\n subject: 'Test', // Subject line\n text: 'Test text', // plaintext version\n html: `<div>${'Test text'}</div>`, // html version\n // attachments: [\n // {\n // filename,\n // content: fileData,\n // },\n // ],\n });\n */\n// Load the AWS SDK for Node.js\n// https://stackoverflow.com/questions/23042835/sending-mail-via-aws-ses-with-attachment-in-node-js\n\nconst sesClient = new SESv2Client({\n region: process.env.AWS_REGION,\n});\n\nconst emailOptions =\n process.env.EMAIL_TRANSPORT === 'smtp'\n ? {\n port: process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT, 10) : 587,\n host: process.env.SMTP_HOST,\n auth:\n process.env.SMTP_USER && process.env.SMTP_PASSWORD\n ? {\n user: process.env.SMTP_USER,\n pass: process.env.SMTP_PASSWORD,\n }\n : undefined,\n }\n : {\n SES: { sesClient, SendEmailCommand },\n };\n\nexport interface Email {\n to?: string | null;\n from?: string;\n reference?: string;\n template?: string;\n idempotencyKey?: string;\n replyTo?: string | null;\n text?: string;\n attachments?: any[];\n subject: string;\n}\n\nexport const emailTransporter = (logger: any = console) => {\n const transporter = nodemailer.createTransport(\n {\n ...emailOptions,\n debug: true,\n } as any,\n {\n ...(process.env.EMAIL_SOURCE_ARN\n ? {\n ses: {\n FromEmailAddressIdentityArn: process.env.EMAIL_SOURCE_ARN!,\n },\n }\n : undefined),\n } as any\n );\n\n return {\n async sendMail(\n {\n to,\n reference,\n from,\n replyTo,\n template,\n text,\n attachments = [],\n idempotencyKey,\n subject,\n }: Email,\n idempotentCheck?: boolean\n ) {\n if (!to) {\n return;\n }\n const sender = from;\n const emailLogService = new EmailLogService();\n logger.info(\n `Sending email to ${to} with subject: ${subject}, reference: ${reference}, idempotencyKey: ${idempotencyKey}`\n );\n\n try {\n if (idempotentCheck) {\n const existingEmail =\n await emailLogService.getEmailByIdempotencyKey(idempotencyKey);\n if (existingEmail) {\n logger.info(`Email with idempotency key ${idempotencyKey} already sent`);\n return;\n }\n }\n const result = await transporter.sendMail({\n to,\n from: sender,\n replyTo: replyTo ?? undefined,\n html: template,\n text,\n subject,\n attachments,\n });\n\n // Log successful email\n await emailLogService.logEmail({\n subject: subject || 'No Subject',\n reference,\n sender: sender || 'Unknown Sender',\n idempotencyKey,\n receiver: to,\n status: 'success',\n });\n\n return result;\n } catch (error) {\n // Log failed email\n await emailLogService.logEmail({\n subject: subject || 'No Subject',\n sender: sender || 'Unknown Sender',\n receiver: to,\n status: 'failed',\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n }\n },\n };\n};\n","import { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport { DynamoDBDocumentClient, PutCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';\nimport { v4 as uuidv4 } from 'uuid';\n\ninterface EmailLog {\n reference?: string;\n timestamp: number;\n subject: string;\n sender: string;\n receiver: string;\n idempotencyKey?: string;\n status: 'success' | 'failed';\n error?: string;\n}\n\nexport class EmailLogService {\n private readonly tableName: string;\n private readonly dynamoClient: DynamoDBClient | undefined;\n private readonly docClient: DynamoDBDocumentClient | undefined;\n\n constructor() {\n this.tableName = process.env.EMAIL_TABLE_NAME!;\n if (this.tableName) {\n this.dynamoClient = new DynamoDBClient({\n region: process.env.AWS_REGION,\n });\n this.docClient = DynamoDBDocumentClient.from(this.dynamoClient);\n }\n }\n\n async logEmail({\n subject,\n sender,\n receiver,\n status,\n error,\n idempotencyKey,\n reference = uuidv4(),\n }: Omit<EmailLog, 'timestamp'>): Promise<string> {\n if (!this.docClient) {\n return reference;\n }\n const timestamp = Date.now();\n\n const log: EmailLog = {\n reference,\n timestamp,\n subject,\n sender,\n receiver,\n status,\n idempotencyKey,\n ...(error && { error }),\n };\n\n await this.docClient.send(\n new PutCommand({\n TableName: this.tableName,\n Item: log,\n })\n );\n return reference;\n }\n\n async getEmailByIdempotencyKey(idempotencyKey: string | undefined) {\n if (!idempotencyKey || !this.docClient) {\n return null;\n }\n\n const result = await this.docClient.send(\n new QueryCommand({\n TableName: this.tableName,\n IndexName: 'idempotency-key-index',\n KeyConditionExpression: 'idempotencyKey = :idempotencyKey',\n ExpressionAttributeValues: {\n ':idempotencyKey': idempotencyKey,\n },\n Limit: 1,\n })\n );\n return result.Items?.[0] as EmailLog | null;\n }\n}\n","import fs from 'node:fs';\nimport { formatters, dates } from '@xcelsior/utils';\nimport handlebars from 'handlebars';\nimport _ from 'lodash';\nimport moment from 'moment';\n\nconst { getDateTime } = dates;\nconst { convertMoneyFormat, moneyFormat } = formatters;\n\nhandlebars.registerHelper('multiply', (a, b) => a * b);\n\nhandlebars.registerHelper('datetime', (a) => moment(getDateTime(a)).format('DD/MM/YYYY HH:mm'));\n\nhandlebars.registerHelper('moneyFormat', (a, ...others) => {\n let currency = 'AUD' as const;\n if (others.length > 1) {\n currency = others[0] as any;\n }\n return moneyFormat(a, currency);\n});\n\nhandlebars.registerHelper('convertMoneyFormat', (amount, currency, rate = 1) =>\n convertMoneyFormat(amount, currency, rate)\n);\n\nhandlebars.registerHelper('titleCase', (string) => {\n if (!string) return '';\n return _.startCase(_.camelCase(string)).replace(/\\s/g, ' ');\n});\n\nhandlebars.registerHelper('choose', (a, b) => (a ? a : b));\n\nhandlebars.registerHelper(\n 'phoneFormat',\n (phone) =>\n `${phone.substring(0, phone.length / 2 - 2)}****${phone.substring(phone.length / 2 + 2)}`\n);\n\nhandlebars.registerHelper('title', (title) => _.startCase(title));\n\nhandlebars.registerHelper('eq', (a, b) => a === b);\nhandlebars.registerHelper('lowercase', (title: string) => title.toLowerCase());\n\nhandlebars.registerHelper('JSONparse', (string, key) => JSON.parse(string)[key]);\n\nhandlebars.registerHelper('incr', (a) => a + 1);\n\nhandlebars.registerHelper('gt', (a, b) => a > b);\n\nexport function renderTemplate(content: string, data: Record<string, any>): string {\n return handlebars.compile(content)(data);\n}\n\nexport async function renderTemplateFile(file: string, data: Record<string, any>) {\n const content = await fs.promises.readFile(file, {\n encoding: 'utf-8',\n });\n return renderTemplate(content, data);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,0BAA8C;AAC9C,wBAAuB;;;ACDvB,6BAA+B;AAC/B,0BAAiE;AACjE,kBAA6B;AAatB,IAAM,kBAAN,MAAsB;AAAA,EAKzB,cAAc;AACV,SAAK,YAAY,QAAQ,IAAI;AAC7B,QAAI,KAAK,WAAW;AAChB,WAAK,eAAe,IAAI,sCAAe;AAAA,QACnC,QAAQ,QAAQ,IAAI;AAAA,MACxB,CAAC;AACD,WAAK,YAAY,2CAAuB,KAAK,KAAK,YAAY;AAAA,IAClE;AAAA,EACJ;AAAA,EAEA,MAAM,SAAS;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAY,YAAAA,IAAO;AAAA,EACvB,GAAiD;AAC7C,QAAI,CAAC,KAAK,WAAW;AACjB,aAAO;AAAA,IACX;AACA,UAAM,YAAY,KAAK,IAAI;AAE3B,UAAM,MAAgB;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,GAAI,SAAS,EAAE,MAAM;AAAA,IACzB;AAEA,UAAM,KAAK,UAAU;AAAA,MACjB,IAAI,+BAAW;AAAA,QACX,WAAW,KAAK;AAAA,QAChB,MAAM;AAAA,MACV,CAAC;AAAA,IACL;AACA,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,yBAAyB,gBAAoC;AAC/D,QAAI,CAAC,kBAAkB,CAAC,KAAK,WAAW;AACpC,aAAO;AAAA,IACX;AAEA,UAAM,SAAS,MAAM,KAAK,UAAU;AAAA,MAChC,IAAI,iCAAa;AAAA,QACb,WAAW,KAAK;AAAA,QAChB,WAAW;AAAA,QACX,wBAAwB;AAAA,QACxB,2BAA2B;AAAA,UACvB,mBAAmB;AAAA,QACvB;AAAA,QACA,OAAO;AAAA,MACX,CAAC;AAAA,IACL;AACA,WAAO,OAAO,QAAQ,CAAC;AAAA,EAC3B;AACJ;;;AD3DA,IAAM,YAAY,IAAI,gCAAY;AAAA,EAC9B,QAAQ,QAAQ,IAAI;AACxB,CAAC;AAED,IAAM,eACF,QAAQ,IAAI,oBAAoB,SAC1B;AAAA,EACI,MAAM,QAAQ,IAAI,YAAY,SAAS,QAAQ,IAAI,WAAW,EAAE,IAAI;AAAA,EACpE,MAAM,QAAQ,IAAI;AAAA,EAClB,MACI,QAAQ,IAAI,aAAa,QAAQ,IAAI,gBAC/B;AAAA,IACI,MAAM,QAAQ,IAAI;AAAA,IAClB,MAAM,QAAQ,IAAI;AAAA,EACtB,IACA;AACd,IACA;AAAA,EACI,KAAK,EAAE,WAAW,uDAAiB;AACvC;AAcH,IAAM,mBAAmB,CAAC,SAAc,YAAY;AACvD,QAAM,cAAc,kBAAAC,QAAW;AAAA,IAC3B;AAAA,MACI,GAAG;AAAA,MACH,OAAO;AAAA,IACX;AAAA,IACA;AAAA,MACI,GAAI,QAAQ,IAAI,mBACV;AAAA,QACI,KAAK;AAAA,UACD,6BAA6B,QAAQ,IAAI;AAAA,QAC7C;AAAA,MACJ,IACA;AAAA,IACV;AAAA,EACJ;AAEA,SAAO;AAAA,IACH,MAAM,SACF;AAAA,MACI;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,CAAC;AAAA,MACf;AAAA,MACA;AAAA,IACJ,GACA,iBACF;AACE,UAAI,CAAC,IAAI;AACL;AAAA,MACJ;AACA,YAAM,SAAS;AACf,YAAM,kBAAkB,IAAI,gBAAgB;AAC5C,aAAO;AAAA,QACH,oBAAoB,EAAE,kBAAkB,OAAO,gBAAgB,SAAS,qBAAqB,cAAc;AAAA,MAC/G;AAEA,UAAI;AACA,YAAI,iBAAiB;AACjB,gBAAM,gBACF,MAAM,gBAAgB,yBAAyB,cAAc;AACjE,cAAI,eAAe;AACf,mBAAO,KAAK,8BAA8B,cAAc,eAAe;AACvE;AAAA,UACJ;AAAA,QACJ;AACA,cAAM,SAAS,MAAM,YAAY,SAAS;AAAA,UACtC;AAAA,UACA,MAAM;AAAA,UACN,SAAS,WAAW;AAAA,UACpB,MAAM;AAAA,UACN;AAAA,UACA;AAAA,UACA;AAAA,QACJ,CAAC;AAGD,cAAM,gBAAgB,SAAS;AAAA,UAC3B,SAAS,WAAW;AAAA,UACpB;AAAA,UACA,QAAQ,UAAU;AAAA,UAClB;AAAA,UACA,UAAU;AAAA,UACV,QAAQ;AAAA,QACZ,CAAC;AAED,eAAO;AAAA,MACX,SAAS,OAAO;AAEZ,cAAM,gBAAgB,SAAS;AAAA,UAC3B,SAAS,WAAW;AAAA,UACpB,QAAQ,UAAU;AAAA,UAClB,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAChE,CAAC;AAED,cAAM;AAAA,MACV;AAAA,IACJ;AAAA,EACJ;AACJ;;;AE7IA,qBAAe;AACf,mBAAkC;AAClC,wBAAuB;AACvB,oBAAc;AACd,oBAAmB;AAEnB,IAAM,EAAE,YAAY,IAAI;AACxB,IAAM,EAAE,oBAAoB,YAAY,IAAI;AAE5C,kBAAAC,QAAW,eAAe,YAAY,CAAC,GAAG,MAAM,IAAI,CAAC;AAErD,kBAAAA,QAAW,eAAe,YAAY,CAAC,UAAM,cAAAC,SAAO,YAAY,CAAC,CAAC,EAAE,OAAO,kBAAkB,CAAC;AAE9F,kBAAAD,QAAW,eAAe,eAAe,CAAC,MAAM,WAAW;AACvD,MAAI,WAAW;AACf,MAAI,OAAO,SAAS,GAAG;AACnB,eAAW,OAAO,CAAC;AAAA,EACvB;AACA,SAAO,YAAY,GAAG,QAAQ;AAClC,CAAC;AAED,kBAAAA,QAAW;AAAA,EAAe;AAAA,EAAsB,CAAC,QAAQ,UAAU,OAAO,MACtE,mBAAmB,QAAQ,UAAU,IAAI;AAC7C;AAEA,kBAAAA,QAAW,eAAe,aAAa,CAAC,WAAW;AAC/C,MAAI,CAAC,OAAQ,QAAO;AACpB,SAAO,cAAAE,QAAE,UAAU,cAAAA,QAAE,UAAU,MAAM,CAAC,EAAE,QAAQ,OAAO,GAAG;AAC9D,CAAC;AAED,kBAAAF,QAAW,eAAe,UAAU,CAAC,GAAG,MAAO,IAAI,IAAI,CAAE;AAEzD,kBAAAA,QAAW;AAAA,EACP;AAAA,EACA,CAAC,UACG,GAAG,MAAM,UAAU,GAAG,MAAM,SAAS,IAAI,CAAC,CAAC,OAAO,MAAM,UAAU,MAAM,SAAS,IAAI,CAAC,CAAC;AAC/F;AAEA,kBAAAA,QAAW,eAAe,SAAS,CAAC,UAAU,cAAAE,QAAE,UAAU,KAAK,CAAC;AAEhE,kBAAAF,QAAW,eAAe,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC;AACjD,kBAAAA,QAAW,eAAe,aAAa,CAAC,UAAkB,MAAM,YAAY,CAAC;AAE7E,kBAAAA,QAAW,eAAe,aAAa,CAAC,QAAQ,QAAQ,KAAK,MAAM,MAAM,EAAE,GAAG,CAAC;AAE/E,kBAAAA,QAAW,eAAe,QAAQ,CAAC,MAAM,IAAI,CAAC;AAE9C,kBAAAA,QAAW,eAAe,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC;AAExC,SAAS,eAAe,SAAiB,MAAmC;AAC/E,SAAO,kBAAAA,QAAW,QAAQ,OAAO,EAAE,IAAI;AAC3C;AAEA,eAAsB,mBAAmB,MAAc,MAA2B;AAC9E,QAAM,UAAU,MAAM,eAAAG,QAAG,SAAS,SAAS,MAAM;AAAA,IAC7C,UAAU;AAAA,EACd,CAAC;AACD,SAAO,eAAe,SAAS,IAAI;AACvC;","names":["uuidv4","nodemailer","handlebars","moment","_","fs"]}
|
package/dist/index.mjs
CHANGED
|
@@ -70,22 +70,28 @@ var EmailLogService = class {
|
|
|
70
70
|
var sesClient = new SESv2Client({
|
|
71
71
|
region: process.env.AWS_REGION
|
|
72
72
|
});
|
|
73
|
-
var emailOptions = process.env.
|
|
74
|
-
port:
|
|
75
|
-
host: process.env.SMTP_HOST
|
|
73
|
+
var emailOptions = process.env.EMAIL_TRANSPORT === "smtp" ? {
|
|
74
|
+
port: process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT, 10) : 587,
|
|
75
|
+
host: process.env.SMTP_HOST,
|
|
76
|
+
auth: process.env.SMTP_USER && process.env.SMTP_PASSWORD ? {
|
|
77
|
+
user: process.env.SMTP_USER,
|
|
78
|
+
pass: process.env.SMTP_PASSWORD
|
|
79
|
+
} : void 0
|
|
76
80
|
} : {
|
|
77
81
|
SES: { sesClient, SendEmailCommand }
|
|
78
82
|
};
|
|
79
|
-
var emailTransporter = (
|
|
83
|
+
var emailTransporter = (logger = console) => {
|
|
80
84
|
const transporter = nodemailer.createTransport(
|
|
81
85
|
{
|
|
82
86
|
...emailOptions,
|
|
83
87
|
debug: true
|
|
84
88
|
},
|
|
85
89
|
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
90
|
+
...process.env.EMAIL_SOURCE_ARN ? {
|
|
91
|
+
ses: {
|
|
92
|
+
FromEmailAddressIdentityArn: process.env.EMAIL_SOURCE_ARN
|
|
93
|
+
}
|
|
94
|
+
} : void 0
|
|
89
95
|
}
|
|
90
96
|
);
|
|
91
97
|
return {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/email.ts","../src/email-log.ts","../src/template.ts"],"sourcesContent":["import { SendEmailCommand, SESv2Client } from '@aws-sdk/client-sesv2';\nimport nodemailer from 'nodemailer';\nimport { EmailLogService } from './email-log';\n\n/**\n * Template to use\n * await emailTransporter().sendMail({\n from: 'AusVie noreply<noreply@ausvie.com.au>',\n to: 'admin@ausvie.com.au',\n subject: 'Test', // Subject line\n text: 'Test text', // plaintext version\n html: `<div>${'Test text'}</div>`, // html version\n // attachments: [\n // {\n // filename,\n // content: fileData,\n // },\n // ],\n });\n */\n// Load the AWS SDK for Node.js\n// https://stackoverflow.com/questions/23042835/sending-mail-via-aws-ses-with-attachment-in-node-js\n\nconst sesClient = new SESv2Client({\n region: process.env.AWS_REGION,\n});\n\nconst emailOptions =\n process.env.NODE_ENV === 'development'\n ? {\n port: 1025,\n host: process.env.SMTP_HOST,\n }\n : {\n SES: { sesClient, SendEmailCommand },\n };\n\nexport interface Email {\n to?: string | null;\n from?: string;\n reference?: string;\n template?: string;\n idempotencyKey?: string;\n replyTo?: string | null;\n text?: string;\n attachments?: any[];\n subject: string;\n}\n\nexport const emailTransporter = (sourceArn: string | undefined, logger: any = console) => {\n const transporter = nodemailer.createTransport(\n {\n ...emailOptions,\n debug: true,\n } as any,\n {\n ses: {\n FromEmailAddressIdentityArn: sourceArn,\n },\n } as any\n );\n\n return {\n async sendMail(\n {\n to,\n reference,\n from,\n replyTo,\n template,\n text,\n attachments = [],\n idempotencyKey,\n subject,\n }: Email,\n idempotentCheck?: boolean\n ) {\n if (!to) {\n return;\n }\n const sender = from;\n const emailLogService = new EmailLogService();\n logger.info(\n `Sending email to ${to} with subject: ${subject}, reference: ${reference}, idempotencyKey: ${idempotencyKey}`\n );\n\n try {\n if (idempotentCheck) {\n const existingEmail =\n await emailLogService.getEmailByIdempotencyKey(idempotencyKey);\n if (existingEmail) {\n logger.info(`Email with idempotency key ${idempotencyKey} already sent`);\n return;\n }\n }\n const result = await transporter.sendMail({\n to,\n from: sender,\n replyTo: replyTo ?? undefined,\n html: template,\n text,\n subject,\n attachments,\n });\n\n // Log successful email\n await emailLogService.logEmail({\n subject: subject || 'No Subject',\n reference,\n sender: sender || 'Unknown Sender',\n idempotencyKey,\n receiver: to,\n status: 'success',\n });\n\n return result;\n } catch (error) {\n // Log failed email\n await emailLogService.logEmail({\n subject: subject || 'No Subject',\n sender: sender || 'Unknown Sender',\n receiver: to,\n status: 'failed',\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n }\n },\n };\n};\n","import { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport { DynamoDBDocumentClient, PutCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';\nimport { v4 as uuidv4 } from 'uuid';\n\ninterface EmailLog {\n reference?: string;\n timestamp: number;\n subject: string;\n sender: string;\n receiver: string;\n idempotencyKey?: string;\n status: 'success' | 'failed';\n error?: string;\n}\n\nexport class EmailLogService {\n private readonly tableName: string;\n private readonly dynamoClient: DynamoDBClient | undefined;\n private readonly docClient: DynamoDBDocumentClient | undefined;\n\n constructor() {\n this.tableName = process.env.EMAIL_TABLE_NAME!;\n if (this.tableName) {\n this.dynamoClient = new DynamoDBClient({\n region: process.env.AWS_REGION,\n });\n this.docClient = DynamoDBDocumentClient.from(this.dynamoClient);\n }\n }\n\n async logEmail({\n subject,\n sender,\n receiver,\n status,\n error,\n idempotencyKey,\n reference = uuidv4(),\n }: Omit<EmailLog, 'timestamp'>): Promise<string> {\n if (!this.docClient) {\n return reference;\n }\n const timestamp = Date.now();\n\n const log: EmailLog = {\n reference,\n timestamp,\n subject,\n sender,\n receiver,\n status,\n idempotencyKey,\n ...(error && { error }),\n };\n\n await this.docClient.send(\n new PutCommand({\n TableName: this.tableName,\n Item: log,\n })\n );\n return reference;\n }\n\n async getEmailByIdempotencyKey(idempotencyKey: string | undefined) {\n if (!idempotencyKey || !this.docClient) {\n return null;\n }\n\n const result = await this.docClient.send(\n new QueryCommand({\n TableName: this.tableName,\n IndexName: 'idempotency-key-index',\n KeyConditionExpression: 'idempotencyKey = :idempotencyKey',\n ExpressionAttributeValues: {\n ':idempotencyKey': idempotencyKey,\n },\n Limit: 1,\n })\n );\n return result.Items?.[0] as EmailLog | null;\n }\n}\n","import fs from 'node:fs';\nimport { formatters, dates } from '@xcelsior/utils';\nimport handlebars from 'handlebars';\nimport _ from 'lodash';\nimport moment from 'moment';\n\nconst { getDateTime } = dates;\nconst { convertMoneyFormat, moneyFormat } = formatters;\n\nhandlebars.registerHelper('multiply', (a, b) => a * b);\n\nhandlebars.registerHelper('datetime', (a) => moment(getDateTime(a)).format('DD/MM/YYYY HH:mm'));\n\nhandlebars.registerHelper('moneyFormat', (a, ...others) => {\n let currency = 'AUD' as const;\n if (others.length > 1) {\n currency = others[0] as any;\n }\n return moneyFormat(a, currency);\n});\n\nhandlebars.registerHelper('convertMoneyFormat', (amount, currency, rate = 1) =>\n convertMoneyFormat(amount, currency, rate)\n);\n\nhandlebars.registerHelper('titleCase', (string) => {\n if (!string) return '';\n return _.startCase(_.camelCase(string)).replace(/\\s/g, ' ');\n});\n\nhandlebars.registerHelper('choose', (a, b) => (a ? a : b));\n\nhandlebars.registerHelper(\n 'phoneFormat',\n (phone) =>\n `${phone.substring(0, phone.length / 2 - 2)}****${phone.substring(phone.length / 2 + 2)}`\n);\n\nhandlebars.registerHelper('title', (title) => _.startCase(title));\n\nhandlebars.registerHelper('eq', (a, b) => a === b);\nhandlebars.registerHelper('lowercase', (title: string) => title.toLowerCase());\n\nhandlebars.registerHelper('JSONparse', (string, key) => JSON.parse(string)[key]);\n\nhandlebars.registerHelper('incr', (a) => a + 1);\n\nhandlebars.registerHelper('gt', (a, b) => a > b);\n\nexport function renderTemplate(content: string, data: Record<string, any>): string {\n return handlebars.compile(content)(data);\n}\n\nexport async function renderTemplateFile(file: string, data: Record<string, any>) {\n const content = await fs.promises.readFile(file, {\n encoding: 'utf-8',\n });\n return renderTemplate(content, data);\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/email.ts","../src/email-log.ts","../src/template.ts"],"sourcesContent":["import { SendEmailCommand, SESv2Client } from '@aws-sdk/client-sesv2';\nimport nodemailer from 'nodemailer';\nimport { EmailLogService } from './email-log';\n\n/**\n * Template to use\n * await emailTransporter().sendMail({\n from: 'AusVie noreply<noreply@ausvie.com.au>',\n to: 'admin@ausvie.com.au',\n subject: 'Test', // Subject line\n text: 'Test text', // plaintext version\n html: `<div>${'Test text'}</div>`, // html version\n // attachments: [\n // {\n // filename,\n // content: fileData,\n // },\n // ],\n });\n */\n// Load the AWS SDK for Node.js\n// https://stackoverflow.com/questions/23042835/sending-mail-via-aws-ses-with-attachment-in-node-js\n\nconst sesClient = new SESv2Client({\n region: process.env.AWS_REGION,\n});\n\nconst emailOptions =\n process.env.EMAIL_TRANSPORT === 'smtp'\n ? {\n port: process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT, 10) : 587,\n host: process.env.SMTP_HOST,\n auth:\n process.env.SMTP_USER && process.env.SMTP_PASSWORD\n ? {\n user: process.env.SMTP_USER,\n pass: process.env.SMTP_PASSWORD,\n }\n : undefined,\n }\n : {\n SES: { sesClient, SendEmailCommand },\n };\n\nexport interface Email {\n to?: string | null;\n from?: string;\n reference?: string;\n template?: string;\n idempotencyKey?: string;\n replyTo?: string | null;\n text?: string;\n attachments?: any[];\n subject: string;\n}\n\nexport const emailTransporter = (logger: any = console) => {\n const transporter = nodemailer.createTransport(\n {\n ...emailOptions,\n debug: true,\n } as any,\n {\n ...(process.env.EMAIL_SOURCE_ARN\n ? {\n ses: {\n FromEmailAddressIdentityArn: process.env.EMAIL_SOURCE_ARN!,\n },\n }\n : undefined),\n } as any\n );\n\n return {\n async sendMail(\n {\n to,\n reference,\n from,\n replyTo,\n template,\n text,\n attachments = [],\n idempotencyKey,\n subject,\n }: Email,\n idempotentCheck?: boolean\n ) {\n if (!to) {\n return;\n }\n const sender = from;\n const emailLogService = new EmailLogService();\n logger.info(\n `Sending email to ${to} with subject: ${subject}, reference: ${reference}, idempotencyKey: ${idempotencyKey}`\n );\n\n try {\n if (idempotentCheck) {\n const existingEmail =\n await emailLogService.getEmailByIdempotencyKey(idempotencyKey);\n if (existingEmail) {\n logger.info(`Email with idempotency key ${idempotencyKey} already sent`);\n return;\n }\n }\n const result = await transporter.sendMail({\n to,\n from: sender,\n replyTo: replyTo ?? undefined,\n html: template,\n text,\n subject,\n attachments,\n });\n\n // Log successful email\n await emailLogService.logEmail({\n subject: subject || 'No Subject',\n reference,\n sender: sender || 'Unknown Sender',\n idempotencyKey,\n receiver: to,\n status: 'success',\n });\n\n return result;\n } catch (error) {\n // Log failed email\n await emailLogService.logEmail({\n subject: subject || 'No Subject',\n sender: sender || 'Unknown Sender',\n receiver: to,\n status: 'failed',\n error: error instanceof Error ? error.message : String(error),\n });\n\n throw error;\n }\n },\n };\n};\n","import { DynamoDBClient } from '@aws-sdk/client-dynamodb';\nimport { DynamoDBDocumentClient, PutCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';\nimport { v4 as uuidv4 } from 'uuid';\n\ninterface EmailLog {\n reference?: string;\n timestamp: number;\n subject: string;\n sender: string;\n receiver: string;\n idempotencyKey?: string;\n status: 'success' | 'failed';\n error?: string;\n}\n\nexport class EmailLogService {\n private readonly tableName: string;\n private readonly dynamoClient: DynamoDBClient | undefined;\n private readonly docClient: DynamoDBDocumentClient | undefined;\n\n constructor() {\n this.tableName = process.env.EMAIL_TABLE_NAME!;\n if (this.tableName) {\n this.dynamoClient = new DynamoDBClient({\n region: process.env.AWS_REGION,\n });\n this.docClient = DynamoDBDocumentClient.from(this.dynamoClient);\n }\n }\n\n async logEmail({\n subject,\n sender,\n receiver,\n status,\n error,\n idempotencyKey,\n reference = uuidv4(),\n }: Omit<EmailLog, 'timestamp'>): Promise<string> {\n if (!this.docClient) {\n return reference;\n }\n const timestamp = Date.now();\n\n const log: EmailLog = {\n reference,\n timestamp,\n subject,\n sender,\n receiver,\n status,\n idempotencyKey,\n ...(error && { error }),\n };\n\n await this.docClient.send(\n new PutCommand({\n TableName: this.tableName,\n Item: log,\n })\n );\n return reference;\n }\n\n async getEmailByIdempotencyKey(idempotencyKey: string | undefined) {\n if (!idempotencyKey || !this.docClient) {\n return null;\n }\n\n const result = await this.docClient.send(\n new QueryCommand({\n TableName: this.tableName,\n IndexName: 'idempotency-key-index',\n KeyConditionExpression: 'idempotencyKey = :idempotencyKey',\n ExpressionAttributeValues: {\n ':idempotencyKey': idempotencyKey,\n },\n Limit: 1,\n })\n );\n return result.Items?.[0] as EmailLog | null;\n }\n}\n","import fs from 'node:fs';\nimport { formatters, dates } from '@xcelsior/utils';\nimport handlebars from 'handlebars';\nimport _ from 'lodash';\nimport moment from 'moment';\n\nconst { getDateTime } = dates;\nconst { convertMoneyFormat, moneyFormat } = formatters;\n\nhandlebars.registerHelper('multiply', (a, b) => a * b);\n\nhandlebars.registerHelper('datetime', (a) => moment(getDateTime(a)).format('DD/MM/YYYY HH:mm'));\n\nhandlebars.registerHelper('moneyFormat', (a, ...others) => {\n let currency = 'AUD' as const;\n if (others.length > 1) {\n currency = others[0] as any;\n }\n return moneyFormat(a, currency);\n});\n\nhandlebars.registerHelper('convertMoneyFormat', (amount, currency, rate = 1) =>\n convertMoneyFormat(amount, currency, rate)\n);\n\nhandlebars.registerHelper('titleCase', (string) => {\n if (!string) return '';\n return _.startCase(_.camelCase(string)).replace(/\\s/g, ' ');\n});\n\nhandlebars.registerHelper('choose', (a, b) => (a ? a : b));\n\nhandlebars.registerHelper(\n 'phoneFormat',\n (phone) =>\n `${phone.substring(0, phone.length / 2 - 2)}****${phone.substring(phone.length / 2 + 2)}`\n);\n\nhandlebars.registerHelper('title', (title) => _.startCase(title));\n\nhandlebars.registerHelper('eq', (a, b) => a === b);\nhandlebars.registerHelper('lowercase', (title: string) => title.toLowerCase());\n\nhandlebars.registerHelper('JSONparse', (string, key) => JSON.parse(string)[key]);\n\nhandlebars.registerHelper('incr', (a) => a + 1);\n\nhandlebars.registerHelper('gt', (a, b) => a > b);\n\nexport function renderTemplate(content: string, data: Record<string, any>): string {\n return handlebars.compile(content)(data);\n}\n\nexport async function renderTemplateFile(file: string, data: Record<string, any>) {\n const content = await fs.promises.readFile(file, {\n encoding: 'utf-8',\n });\n return renderTemplate(content, data);\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xcelsior/email",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"import": "./src/index.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"nodemailer": "^7.0.4",
|
|
24
24
|
"@aws-sdk/client-sesv2": "^3.888.0",
|
|
25
25
|
"uuid": "^11.1.0",
|
|
26
|
-
"@xcelsior/utils": "1.0.
|
|
26
|
+
"@xcelsior/utils": "1.0.1"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/lodash": "^4.17.20",
|
package/src/email.ts
CHANGED
|
@@ -26,10 +26,17 @@ const sesClient = new SESv2Client({
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
const emailOptions =
|
|
29
|
-
process.env.
|
|
29
|
+
process.env.EMAIL_TRANSPORT === 'smtp'
|
|
30
30
|
? {
|
|
31
|
-
port:
|
|
31
|
+
port: process.env.SMTP_PORT ? parseInt(process.env.SMTP_PORT, 10) : 587,
|
|
32
32
|
host: process.env.SMTP_HOST,
|
|
33
|
+
auth:
|
|
34
|
+
process.env.SMTP_USER && process.env.SMTP_PASSWORD
|
|
35
|
+
? {
|
|
36
|
+
user: process.env.SMTP_USER,
|
|
37
|
+
pass: process.env.SMTP_PASSWORD,
|
|
38
|
+
}
|
|
39
|
+
: undefined,
|
|
33
40
|
}
|
|
34
41
|
: {
|
|
35
42
|
SES: { sesClient, SendEmailCommand },
|
|
@@ -47,16 +54,20 @@ export interface Email {
|
|
|
47
54
|
subject: string;
|
|
48
55
|
}
|
|
49
56
|
|
|
50
|
-
export const emailTransporter = (
|
|
57
|
+
export const emailTransporter = (logger: any = console) => {
|
|
51
58
|
const transporter = nodemailer.createTransport(
|
|
52
59
|
{
|
|
53
60
|
...emailOptions,
|
|
54
61
|
debug: true,
|
|
55
62
|
} as any,
|
|
56
63
|
{
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
64
|
+
...(process.env.EMAIL_SOURCE_ARN
|
|
65
|
+
? {
|
|
66
|
+
ses: {
|
|
67
|
+
FromEmailAddressIdentityArn: process.env.EMAIL_SOURCE_ARN!,
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
: undefined),
|
|
60
71
|
} as any
|
|
61
72
|
);
|
|
62
73
|
|
package/.turbo/turbo-lint.log
DELETED
package/.turbo/turbo-test.log
DELETED