@stamhoofd/email
Advanced tools
Comparing version 2.55.2 to 2.56.0
import Mail from 'nodemailer/lib/mailer'; | ||
import { I18n } from "@stamhoofd/backend-i18n"; | ||
import { I18n } from '@stamhoofd/backend-i18n'; | ||
export type EmailInterfaceRecipient = { | ||
@@ -22,3 +22,3 @@ name?: string | null; | ||
retryCount?: number; | ||
type?: "transactional" | "broadcast"; | ||
type?: 'transactional' | 'broadcast'; | ||
headers?: Record<string, string> | null; | ||
@@ -63,3 +63,3 @@ callback?: (error: Error | null) => void; | ||
*/ | ||
sendWebmaster(data: Omit<EmailInterfaceBase, "to">): void; | ||
sendWebmaster(data: Omit<EmailInterfaceBase, 'to'>): void; | ||
send(data: EmailInterface): void; | ||
@@ -66,0 +66,0 @@ schedule(builder: EmailBuilder): void; |
@@ -13,4 +13,3 @@ "use strict"; | ||
// Importing postmark returns undefined (this is a bug, so we need to use require) | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const postmark = require("postmark"); | ||
const postmark = require('postmark'); | ||
class EmailStatic { | ||
@@ -27,3 +26,3 @@ transporter; | ||
if (!STAMHOOFD.SMTP_HOST || !STAMHOOFD.SMTP_PORT) { | ||
throw new Error("Missing environment variables to send emails"); | ||
throw new Error('Missing environment variables to send emails'); | ||
return; | ||
@@ -38,4 +37,4 @@ } | ||
user: STAMHOOFD.SMTP_USERNAME, // generated ethereal user | ||
pass: STAMHOOFD.SMTP_PASSWORD // generated ethereal password | ||
} | ||
pass: STAMHOOFD.SMTP_PASSWORD, // generated ethereal password | ||
}, | ||
}); | ||
@@ -49,4 +48,4 @@ // create reusable transporter object using the default SMTP transport | ||
user: STAMHOOFD.TRANSACTIONAL_SMTP_USERNAME, // generated ethereal user | ||
pass: STAMHOOFD.TRANSACTIONAL_SMTP_PASSWORD // generated ethereal password | ||
} | ||
pass: STAMHOOFD.TRANSACTIONAL_SMTP_PASSWORD, // generated ethereal password | ||
}, | ||
}); | ||
@@ -56,6 +55,6 @@ // verify connection configuration | ||
if (error) { | ||
console.error("SMTP server not working", error); | ||
console.error('SMTP server not working', error); | ||
} | ||
else { | ||
console.log("SMTP server is ready to take our messages"); | ||
console.log('SMTP server is ready to take our messages'); | ||
} | ||
@@ -66,6 +65,6 @@ }); | ||
if (error) { | ||
console.error("Transactinoal SMTP server not working", error); | ||
console.error('Transactinoal SMTP server not working', error); | ||
} | ||
else { | ||
console.log("Transactinoal SMTP server is ready to take our messages"); | ||
console.log('Transactinoal SMTP server is ready to take our messages'); | ||
} | ||
@@ -77,3 +76,3 @@ }); | ||
if (this.currentQueue.length == 0) { | ||
console.log("mail queue is empty"); | ||
console.log('mail queue is empty'); | ||
return; | ||
@@ -85,3 +84,3 @@ } | ||
if (this.currentQueue.length == 0) { | ||
console.log("mail queue is empty"); | ||
console.log('mail queue is empty'); | ||
return; | ||
@@ -92,3 +91,3 @@ } | ||
this.sending = true; | ||
this.doSend(next).catch(e => { | ||
this.doSend(next).catch((e) => { | ||
console.error(e); | ||
@@ -102,3 +101,3 @@ }).finally(() => { | ||
parseTo(to) { | ||
if (typeof to === "string") { | ||
if (typeof to === 'string') { | ||
return this.parseEmailStr(to).map(email => ({ email })); | ||
@@ -116,5 +115,5 @@ } | ||
let inAddr = false; | ||
let email = ""; | ||
let email = ''; | ||
let didFindAddr = false; | ||
let cleanedStr = ""; | ||
let cleanedStr = ''; | ||
const addresses = []; | ||
@@ -133,7 +132,7 @@ function endAddress() { | ||
didFindAddr = false; | ||
email = ""; | ||
email = ''; | ||
inAddr = false; | ||
insideQuote = false; | ||
escaped = false; | ||
cleanedStr = ""; | ||
cleanedStr = ''; | ||
} | ||
@@ -148,3 +147,3 @@ // eslint-disable-next-line @typescript-eslint/prefer-for-of | ||
if (insideQuote) { | ||
if (character === "\\") { | ||
if (character === '\\') { | ||
escaped = true; | ||
@@ -155,3 +154,3 @@ continue; | ||
if (!shouldEscape) { | ||
if (character === "\"") { | ||
if (character === '"') { | ||
if (insideQuote) { | ||
@@ -165,7 +164,7 @@ insideQuote = false; | ||
if (!insideQuote) { | ||
if (character === "<") { | ||
if (character === '<') { | ||
inAddr = true; | ||
continue; | ||
} | ||
if (character === ">") { | ||
if (character === '>') { | ||
inAddr = false; | ||
@@ -175,3 +174,3 @@ didFindAddr = true; | ||
} | ||
if (character === ",") { | ||
if (character === ',') { | ||
// End previous address | ||
@@ -202,3 +201,3 @@ endAddress(); | ||
} | ||
console.warn("Filtered email to " + l + ": not whitelisted"); | ||
console.warn('Filtered email to ' + l + ': not whitelisted'); | ||
return false; | ||
@@ -210,3 +209,3 @@ } | ||
if (!whitelist.includes('*') && !whitelist.includes('*@*')) { | ||
return recipients.filter(mail => { | ||
return recipients.filter((mail) => { | ||
return this.matchWhitelist(mail.email, whitelist); | ||
@@ -238,8 +237,8 @@ }); | ||
code: 'all_filtered', | ||
message: "All recipients are filtered due to hard bounce or spam", | ||
human: 'Alle ontvangers zijn gefilterd wegens een hard bounce of spam' | ||
message: 'All recipients are filtered due to hard bounce or spam', | ||
human: 'Alle ontvangers zijn gefilterd wegens een hard bounce of spam', | ||
})); | ||
} | ||
catch (e) { | ||
console.error("Error in email callback", e); | ||
console.error('Error in email callback', e); | ||
} | ||
@@ -258,8 +257,8 @@ return; | ||
code: 'all_filtered', | ||
message: "All recipients are filtered due to environment", | ||
human: 'Alle ontvangers zijn gefilterd omwille van de demo-omgeving die het versturen van bepaalde e-mails limiteert' | ||
message: 'All recipients are filtered due to environment', | ||
human: 'Alle ontvangers zijn gefilterd omwille van de demo-omgeving die het versturen van bepaalde e-mails limiteert', | ||
})); | ||
} | ||
catch (e) { | ||
console.error("Error in email callback", e); | ||
console.error('Error in email callback', e); | ||
} | ||
@@ -278,3 +277,3 @@ return; | ||
return '"' + cleanedName + '" <' + recipient.email + '>'; | ||
}).join(", "); | ||
}).join(', '); | ||
this.setupIfNeeded(); | ||
@@ -284,6 +283,6 @@ // send mail with defined transport object | ||
from: data.from, // sender address | ||
bcc: (STAMHOOFD.environment === "production" || !data.bcc) ? data.bcc : "simon@stamhoofd.be", | ||
bcc: (STAMHOOFD.environment === 'production' || !data.bcc) ? data.bcc : 'simon@stamhoofd.be', | ||
replyTo: data.replyTo, | ||
to, | ||
subject: data.subject, // Subject line | ||
subject: data.subject.substring(0, 1000), // Subject line | ||
text: data.text, // plain text body | ||
@@ -302,3 +301,3 @@ }; | ||
wordwrap: null, | ||
unorderedListItemPrefix: " - " | ||
unorderedListItemPrefix: ' - ', | ||
}); | ||
@@ -309,3 +308,3 @@ } | ||
if (parsedFrom.length !== 1) { | ||
throw new Error("Invalid from email " + data.from); | ||
throw new Error('Invalid from email ' + data.from); | ||
} | ||
@@ -320,7 +319,7 @@ try { | ||
} | ||
const transporter = (data.type === "transactional") ? this.transactionalTransporter : this.transporter; | ||
if (data.type === "transactional") { | ||
const transporter = (data.type === 'transactional') ? this.transactionalTransporter : this.transporter; | ||
if (data.type === 'transactional') { | ||
mail.headers = { | ||
...data.headers, | ||
...STAMHOOFD.TRANSACTIONAL_SMTP_HEADERS | ||
...STAMHOOFD.TRANSACTIONAL_SMTP_HEADERS, | ||
}; | ||
@@ -331,3 +330,3 @@ } | ||
...data.headers, | ||
...STAMHOOFD.SMTP_HEADERS | ||
...STAMHOOFD.SMTP_HEADERS, | ||
}; | ||
@@ -337,7 +336,7 @@ } | ||
await this.sendApi(mail); | ||
console.log("Message sent via api:", to, data.subject, data.type); | ||
console.log('Message sent via api:', to, data.subject, data.type); | ||
} | ||
else { | ||
const info = await transporter.sendMail(mail); | ||
console.log("Message sent:", to, data.subject, info.messageId, data.type); | ||
console.log('Message sent:', to, data.subject, info.messageId, data.type); | ||
} | ||
@@ -348,7 +347,7 @@ try { | ||
catch (e) { | ||
console.error("Error in email callback", e); | ||
console.error('Error in email callback', e); | ||
} | ||
} | ||
catch (e) { | ||
console.error("Failed to send e-mail:"); | ||
console.error('Failed to send e-mail:'); | ||
console.error(e); | ||
@@ -371,3 +370,3 @@ console.error(mail); | ||
catch (e2) { | ||
console.error("Error in email failure callback", e2, 'for original error', e); | ||
console.error('Error in email failure callback', e2, 'for original error', e); | ||
} | ||
@@ -378,5 +377,5 @@ // Email address is not verified. | ||
this.sendWebmaster({ | ||
subject: "E-mail kon niet worden verzonden", | ||
text: "Een e-mail vanaf " + data.from + " kon niet worden verstuurd aan " + mail.to + ": \n\n" + e + "\n\n" + (mail.text ?? ""), | ||
type: (data.type === "transactional") ? "broadcast" : "transactional" | ||
subject: 'E-mail kon niet worden verzonden', | ||
text: 'Een e-mail vanaf ' + data.from + ' kon niet worden verstuurd aan ' + mail.to + ': \n\n' + e + '\n\n' + (mail.text ?? ''), | ||
type: (data.type === 'transactional') ? 'broadcast' : 'transactional', | ||
}); | ||
@@ -390,3 +389,3 @@ } | ||
if (!STAMHOOFD.POSTMARK_SERVER_TOKEN) { | ||
throw new Error("Missing POSTMARK_SERVER_TOKEN"); | ||
throw new Error('Missing POSTMARK_SERVER_TOKEN'); | ||
} | ||
@@ -399,12 +398,12 @@ const client = new postmark.ServerClient(STAMHOOFD.POSTMARK_SERVER_TOKEN); | ||
const message = { | ||
"From": data.from, | ||
"To": data.to, | ||
"Bcc": data.bcc, | ||
"Subject": data.subject, | ||
"TextBody": data.text, | ||
"HtmlBody": data.html, | ||
"Headers": headers, | ||
"ReplyTo": data.replyTo, | ||
"Tag": "api", | ||
"MessageStream": (data.headers?.["X-PM-Message-Stream"] ?? "outbound") | ||
From: data.from, | ||
To: data.to, | ||
Bcc: data.bcc, | ||
Subject: data.subject, | ||
TextBody: data.text, | ||
HtmlBody: data.html, | ||
Headers: headers, | ||
ReplyTo: data.replyTo, | ||
Tag: 'api', | ||
MessageStream: (data.headers?.['X-PM-Message-Stream'] ?? 'outbound'), | ||
}; | ||
@@ -429,3 +428,3 @@ console.log('Falling back to Postmark API', message); | ||
getWebmasterFromEmail() { | ||
return '"' + (STAMHOOFD.platformName ?? 'Stamhoofd') + ' " <webmaster@' + (new backend_i18n_1.I18n("nl", "BE").localizedDomains.defaultTransactionalEmail()) + '>'; | ||
return '"' + (STAMHOOFD.platformName ?? 'Stamhoofd') + ' " <webmaster@' + (new backend_i18n_1.I18n('nl', 'BE').localizedDomains.defaultTransactionalEmail()) + '>'; | ||
} | ||
@@ -440,3 +439,3 @@ getWebmasterToEmail() { | ||
getPersonalEmailFor(i18n) { | ||
return '"Simon Backx" <' + (i18n.$t("5670bc42-cf94-46b6-9ce0-7cdc4ffbb4d9")) + '>'; | ||
return '"Simon Backx" <' + (i18n.$t('5670bc42-cf94-46b6-9ce0-7cdc4ffbb4d9')) + '>'; | ||
} | ||
@@ -449,3 +448,3 @@ /** | ||
from: this.getWebmasterFromEmail(), | ||
to: this.getWebmasterToEmail() | ||
to: this.getWebmasterToEmail(), | ||
}); | ||
@@ -452,0 +451,0 @@ this.send(mail); |
{ | ||
"name": "@stamhoofd/email", | ||
"version": "2.55.2", | ||
"version": "2.56.0", | ||
"main": "./dist/index.js", | ||
@@ -28,3 +28,3 @@ "types": "./dist/index.d.ts", | ||
}, | ||
"gitHead": "05688607d81f5f9d7d01b425d3ffe87e583e47c9" | ||
"gitHead": "39e791f29d992c918b83871d9dc6cf72c418b2c4" | ||
} |
import { DataValidator, Formatter } from '@stamhoofd/utility'; | ||
import nodemailer from "nodemailer" | ||
import nodemailer from 'nodemailer'; | ||
import Mail from 'nodemailer/lib/mailer'; | ||
@@ -7,17 +7,17 @@ import { EmailAddress } from '../models/EmailAddress'; | ||
import { sleep } from '@stamhoofd/utility'; | ||
import { I18n } from "@stamhoofd/backend-i18n" | ||
import { I18n } from '@stamhoofd/backend-i18n'; | ||
import { SimpleError } from '@simonbackx/simple-errors'; | ||
import {type default as Postmark} from "postmark"; | ||
import { type default as Postmark } from 'postmark'; | ||
// Importing postmark returns undefined (this is a bug, so we need to use require) | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const postmark = require("postmark") | ||
const postmark = require('postmark'); | ||
export type EmailInterfaceRecipient = { | ||
name?: string|null; | ||
name?: string | null; | ||
email: string; | ||
} | ||
}; | ||
export type EmailInterfaceBase = { | ||
to: string|EmailInterfaceRecipient[]; | ||
to: string | EmailInterfaceRecipient[]; | ||
bcc?: string; | ||
@@ -28,20 +28,20 @@ replyTo?: string; | ||
html?: string; | ||
attachments?: { filename: string; path?: string; href?: string; content?: string|Buffer; contentType?: string }[]; | ||
attachments?: { filename: string; path?: string; href?: string; content?: string | Buffer; contentType?: string }[]; | ||
retryCount?: number; | ||
type?: "transactional" | "broadcast", | ||
headers?: Record<string, string>|null, | ||
callback?: (error: Error|null) => void; | ||
} | ||
type?: 'transactional' | 'broadcast'; | ||
headers?: Record<string, string> | null; | ||
callback?: (error: Error | null) => void; | ||
}; | ||
export type EmailInterface = EmailInterfaceBase & { | ||
from: string; | ||
} | ||
}; | ||
/// An email builder is called until it returns undefined. This allows to reduce memory usage for an e-mail with multiple recipients | ||
export type EmailBuilder = () => EmailInterface | undefined | ||
export type EmailBuilder = () => EmailInterface | undefined; | ||
type InternalEmailData = { | ||
from: string; | ||
bcc: string|undefined; | ||
replyTo: string|undefined; | ||
bcc: string | undefined; | ||
replyTo: string | undefined; | ||
to: string; | ||
@@ -51,5 +51,5 @@ subject: string; | ||
html?: string; | ||
attachments?: { filename: string; path?: string; href?: string; content?: string|Buffer; contentType?: string }[]; | ||
attachments?: { filename: string; path?: string; href?: string; content?: string | Buffer; contentType?: string }[]; | ||
headers?: Record<string, string>; | ||
} | ||
}; | ||
@@ -59,6 +59,6 @@ class EmailStatic { | ||
transactionalTransporter: Mail; | ||
rps = 14 | ||
rps = 14; | ||
currentQueue: EmailBuilder[] = [] | ||
sending = false | ||
currentQueue: EmailBuilder[] = []; | ||
sending = false; | ||
@@ -70,3 +70,3 @@ setupIfNeeded() { | ||
if (!STAMHOOFD.SMTP_HOST || !STAMHOOFD.SMTP_PORT) { | ||
throw new Error("Missing environment variables to send emails"); | ||
throw new Error('Missing environment variables to send emails'); | ||
return; | ||
@@ -82,6 +82,6 @@ } | ||
user: STAMHOOFD.SMTP_USERNAME, // generated ethereal user | ||
pass: STAMHOOFD.SMTP_PASSWORD // generated ethereal password | ||
} | ||
pass: STAMHOOFD.SMTP_PASSWORD, // generated ethereal password | ||
}, | ||
}); | ||
// create reusable transporter object using the default SMTP transport | ||
@@ -94,4 +94,4 @@ this.transactionalTransporter = nodemailer.createTransport({ | ||
user: STAMHOOFD.TRANSACTIONAL_SMTP_USERNAME, // generated ethereal user | ||
pass: STAMHOOFD.TRANSACTIONAL_SMTP_PASSWORD // generated ethereal password | ||
} | ||
pass: STAMHOOFD.TRANSACTIONAL_SMTP_PASSWORD, // generated ethereal password | ||
}, | ||
}); | ||
@@ -102,6 +102,7 @@ | ||
if (error) { | ||
console.error("SMTP server not working", error); | ||
} else { | ||
console.log("SMTP server is ready to take our messages"); | ||
console.error('SMTP server not working', error); | ||
} | ||
else { | ||
console.log('SMTP server is ready to take our messages'); | ||
} | ||
}); | ||
@@ -112,6 +113,7 @@ | ||
if (error) { | ||
console.error("Transactinoal SMTP server not working", error); | ||
} else { | ||
console.log("Transactinoal SMTP server is ready to take our messages"); | ||
console.error('Transactinoal SMTP server not working', error); | ||
} | ||
else { | ||
console.log('Transactinoal SMTP server is ready to take our messages'); | ||
} | ||
}); | ||
@@ -123,33 +125,33 @@ } | ||
if (this.currentQueue.length == 0) { | ||
console.log("mail queue is empty") | ||
return | ||
console.log('mail queue is empty'); | ||
return; | ||
} | ||
let next = this.currentQueue[0]() | ||
let next = this.currentQueue[0](); | ||
while (next === undefined) { | ||
this.currentQueue.shift() | ||
this.currentQueue.shift(); | ||
if (this.currentQueue.length == 0) { | ||
console.log("mail queue is empty") | ||
return | ||
console.log('mail queue is empty'); | ||
return; | ||
} | ||
next = this.currentQueue[0]() | ||
next = this.currentQueue[0](); | ||
} | ||
this.sending = true; | ||
this.doSend(next).catch(e => { | ||
console.error(e) | ||
this.doSend(next).catch((e) => { | ||
console.error(e); | ||
}).finally(() => { | ||
this.sending = false | ||
this.sendNextIfNeeded() | ||
}) | ||
this.sending = false; | ||
this.sendNextIfNeeded(); | ||
}); | ||
} | ||
} | ||
parseTo(to: string|EmailInterfaceRecipient[]): EmailInterfaceRecipient[] { | ||
if (typeof to === "string") { | ||
return this.parseEmailStr(to).map(email => ({ email })) | ||
parseTo(to: string | EmailInterfaceRecipient[]): EmailInterfaceRecipient[] { | ||
if (typeof to === 'string') { | ||
return this.parseEmailStr(to).map(email => ({ email })); | ||
} | ||
// Filter invalid email addresses | ||
return to.filter(r => DataValidator.isEmailValid(r.email)) | ||
return to.filter(r => DataValidator.isEmailValid(r.email)); | ||
} | ||
@@ -161,27 +163,28 @@ | ||
parseEmailStr(emailStr: string): string[] { | ||
let insideQuote = false | ||
let escaped = false | ||
let inAddr = false | ||
let email = "" | ||
let didFindAddr = false | ||
let cleanedStr = "" | ||
let insideQuote = false; | ||
let escaped = false; | ||
let inAddr = false; | ||
let email = ''; | ||
let didFindAddr = false; | ||
let cleanedStr = ''; | ||
const addresses: string[] = [] | ||
const addresses: string[] = []; | ||
function endAddress() { | ||
let m: string | ||
let m: string; | ||
if (didFindAddr) { | ||
m = email.trim() | ||
} else { | ||
m = cleanedStr.trim() | ||
m = email.trim(); | ||
} | ||
else { | ||
m = cleanedStr.trim(); | ||
} | ||
if (DataValidator.isEmailValid(m)) { | ||
addresses.push(m) | ||
addresses.push(m); | ||
} | ||
didFindAddr = false | ||
email = "" | ||
inAddr = false | ||
insideQuote = false | ||
escaped = false | ||
cleanedStr = "" | ||
didFindAddr = false; | ||
email = ''; | ||
inAddr = false; | ||
insideQuote = false; | ||
escaped = false; | ||
cleanedStr = ''; | ||
} | ||
@@ -191,11 +194,11 @@ | ||
for (let index = 0; index < emailStr.length; index++) { | ||
const shouldEscape = escaped | ||
const shouldEscape = escaped; | ||
if (escaped) { | ||
escaped = false | ||
escaped = false; | ||
} | ||
const character = emailStr[index]; | ||
if (insideQuote) { | ||
if (character === "\\") { | ||
escaped = true | ||
continue | ||
if (character === '\\') { | ||
escaped = true; | ||
continue; | ||
} | ||
@@ -205,27 +208,27 @@ } | ||
if (!shouldEscape) { | ||
if (character === "\"") { | ||
if (character === '"') { | ||
if (insideQuote) { | ||
insideQuote = false | ||
continue | ||
insideQuote = false; | ||
continue; | ||
} | ||
insideQuote = true | ||
continue | ||
insideQuote = true; | ||
continue; | ||
} | ||
if (!insideQuote) { | ||
if (character === "<") { | ||
inAddr = true | ||
continue | ||
if (character === '<') { | ||
inAddr = true; | ||
continue; | ||
} | ||
if (character === ">") { | ||
inAddr = false | ||
didFindAddr = true | ||
continue | ||
if (character === '>') { | ||
inAddr = false; | ||
didFindAddr = true; | ||
continue; | ||
} | ||
if (character === ",") { | ||
if (character === ',') { | ||
// End previous address | ||
endAddress() | ||
continue | ||
endAddress(); | ||
continue; | ||
} | ||
@@ -236,9 +239,9 @@ } | ||
if (inAddr) { | ||
email += character | ||
email += character; | ||
} | ||
cleanedStr += character | ||
cleanedStr += character; | ||
} | ||
endAddress() | ||
return addresses | ||
endAddress(); | ||
return addresses; | ||
} | ||
@@ -254,3 +257,3 @@ | ||
const domainIndex = l.indexOf('@'); | ||
const domain = l.substring(domainIndex) | ||
const domain = l.substring(domainIndex); | ||
@@ -261,7 +264,7 @@ if (whitelist.includes('*' + domain)) { | ||
console.warn("Filtered email to " + l + ": not whitelisted") | ||
console.warn('Filtered email to ' + l + ': not whitelisted'); | ||
return false; | ||
} | ||
return true | ||
return true; | ||
} | ||
@@ -271,8 +274,8 @@ | ||
if (!whitelist.includes('*') && !whitelist.includes('*@*')) { | ||
return recipients.filter(mail => { | ||
return this.matchWhitelist(mail.email, whitelist) | ||
}) | ||
return recipients.filter((mail) => { | ||
return this.matchWhitelist(mail.email, whitelist); | ||
}); | ||
} | ||
return recipients | ||
return recipients; | ||
} | ||
@@ -288,15 +291,15 @@ | ||
// Filter recipients if bounced or spam | ||
let recipients = this.parseTo(data.to) | ||
let recipients = this.parseTo(data.to); | ||
if (recipients.length === 0) { | ||
// Invalid string | ||
console.warn("Invalid e-mail string: '"+data.to+"'. E-mail skipped") | ||
return | ||
console.warn("Invalid e-mail string: '" + data.to + "'. E-mail skipped"); | ||
return; | ||
} | ||
// Check spam and bounces | ||
recipients = await EmailAddress.filterSendTo(recipients) | ||
recipients = await EmailAddress.filterSendTo(recipients); | ||
if (recipients.length === 0) { | ||
// Invalid string | ||
console.warn("Filtered all emails due hard bounce or spam '"+data.to+"'. E-mail skipped") | ||
console.warn("Filtered all emails due hard bounce or spam '" + data.to + "'. E-mail skipped"); | ||
@@ -307,10 +310,11 @@ try { | ||
code: 'all_filtered', | ||
message: "All recipients are filtered due to hard bounce or spam", | ||
human: 'Alle ontvangers zijn gefilterd wegens een hard bounce of spam' | ||
}) | ||
) | ||
} catch (e) { | ||
console.error("Error in email callback", e) | ||
message: 'All recipients are filtered due to hard bounce or spam', | ||
human: 'Alle ontvangers zijn gefilterd wegens een hard bounce of spam', | ||
}), | ||
); | ||
} | ||
return | ||
catch (e) { | ||
console.error('Error in email callback', e); | ||
} | ||
return; | ||
} | ||
@@ -320,4 +324,4 @@ | ||
if (STAMHOOFD.environment !== 'production' || (STAMHOOFD.WHITELISTED_EMAIL_DESTINATIONS && STAMHOOFD.WHITELISTED_EMAIL_DESTINATIONS.length > 0)) { | ||
const whitelist = STAMHOOFD.WHITELISTED_EMAIL_DESTINATIONS ?? [] | ||
recipients = this.filterWhitelist(recipients, whitelist) | ||
const whitelist = STAMHOOFD.WHITELISTED_EMAIL_DESTINATIONS ?? []; | ||
recipients = this.filterWhitelist(recipients, whitelist); | ||
} | ||
@@ -331,23 +335,24 @@ | ||
code: 'all_filtered', | ||
message: "All recipients are filtered due to environment", | ||
human: 'Alle ontvangers zijn gefilterd omwille van de demo-omgeving die het versturen van bepaalde e-mails limiteert' | ||
}) | ||
) | ||
} catch (e) { | ||
console.error("Error in email callback", e) | ||
message: 'All recipients are filtered due to environment', | ||
human: 'Alle ontvangers zijn gefilterd omwille van de demo-omgeving die het versturen van bepaalde e-mails limiteert', | ||
}), | ||
); | ||
} | ||
return | ||
catch (e) { | ||
console.error('Error in email callback', e); | ||
} | ||
return; | ||
} | ||
// Rebuild to | ||
const to = recipients.map((recipient) => { | ||
if (!recipient.name) { | ||
return recipient.email | ||
return recipient.email; | ||
} | ||
const cleanedName = Formatter.emailSenderName(recipient.name) | ||
const cleanedName = Formatter.emailSenderName(recipient.name); | ||
if (cleanedName.length < 2) { | ||
return recipient.email | ||
return recipient.email; | ||
} | ||
return '"'+cleanedName+'" <'+recipient.email+'>' | ||
}).join(", ") | ||
return '"' + cleanedName + '" <' + recipient.email + '>'; | ||
}).join(', '); | ||
@@ -359,6 +364,6 @@ this.setupIfNeeded(); | ||
from: data.from, // sender address | ||
bcc: (STAMHOOFD.environment === "production" || !data.bcc) ? data.bcc : "simon@stamhoofd.be", | ||
bcc: (STAMHOOFD.environment === 'production' || !data.bcc) ? data.bcc : 'simon@stamhoofd.be', | ||
replyTo: data.replyTo, | ||
to, | ||
subject: data.subject, // Subject line | ||
subject: data.subject.substring(0, 1000), // Subject line | ||
text: data.text, // plain text body | ||
@@ -381,3 +386,3 @@ }; | ||
wordwrap: null, | ||
unorderedListItemPrefix: " - " | ||
unorderedListItemPrefix: ' - ', | ||
}); | ||
@@ -387,5 +392,5 @@ } | ||
const parsedFrom = this.parseEmailStr(data.from) | ||
const parsedFrom = this.parseEmailStr(data.from); | ||
if (parsedFrom.length !== 1) { | ||
throw new Error("Invalid from email " + data.from) | ||
throw new Error('Invalid from email ' + data.from); | ||
} | ||
@@ -398,35 +403,39 @@ | ||
// Not supported | ||
data.type = 'broadcast' | ||
data.type = 'broadcast'; | ||
} | ||
} | ||
const transporter = (data.type === "transactional") ? this.transactionalTransporter : this.transporter | ||
const transporter = (data.type === 'transactional') ? this.transactionalTransporter : this.transporter; | ||
if (data.type === "transactional") { | ||
if (data.type === 'transactional') { | ||
mail.headers = { | ||
...data.headers, | ||
...STAMHOOFD.TRANSACTIONAL_SMTP_HEADERS | ||
} | ||
} else { | ||
...STAMHOOFD.TRANSACTIONAL_SMTP_HEADERS, | ||
}; | ||
} | ||
else { | ||
mail.headers = { | ||
...data.headers, | ||
...STAMHOOFD.SMTP_HEADERS | ||
} | ||
...STAMHOOFD.SMTP_HEADERS, | ||
}; | ||
} | ||
if (STAMHOOFD.POSTMARK_SERVER_TOKEN && (data.retryCount === 1)) { | ||
await this.sendApi(mail) | ||
console.log("Message sent via api:", to, data.subject, data.type); | ||
} else { | ||
await this.sendApi(mail); | ||
console.log('Message sent via api:', to, data.subject, data.type); | ||
} | ||
else { | ||
const info = await transporter.sendMail(mail); | ||
console.log("Message sent:", to, data.subject, info.messageId, data.type); | ||
console.log('Message sent:', to, data.subject, info.messageId, data.type); | ||
} | ||
try { | ||
data.callback?.(null) | ||
} catch (e) { | ||
console.error("Error in email callback", e) | ||
data.callback?.(null); | ||
} | ||
} catch (e) { | ||
console.error("Failed to send e-mail:") | ||
catch (e) { | ||
console.error('Error in email callback', e); | ||
} | ||
} | ||
catch (e) { | ||
console.error('Failed to send e-mail:'); | ||
console.error(e); | ||
@@ -442,3 +451,2 @@ console.error(mail); | ||
if (data.retryCount <= 2) { | ||
if (data.type === 'transactional' && data.retryCount === 2) { | ||
@@ -448,8 +456,10 @@ data.type = 'broadcast'; | ||
this.send(data); | ||
} else { | ||
} | ||
else { | ||
try { | ||
data.callback?.(e) | ||
} catch (e2) { | ||
console.error("Error in email failure callback", e2, 'for original error', e) | ||
data.callback?.(e); | ||
} | ||
catch (e2) { | ||
console.error('Error in email failure callback', e2, 'for original error', e); | ||
} | ||
@@ -460,6 +470,6 @@ // Email address is not verified. | ||
this.sendWebmaster({ | ||
subject: "E-mail kon niet worden verzonden", | ||
text: "Een e-mail vanaf "+data.from+" kon niet worden verstuurd aan "+mail.to+": \n\n"+e+"\n\n"+(mail.text ?? ""), | ||
type: (data.type === "transactional") ? "broadcast" : "transactional" | ||
}) | ||
subject: 'E-mail kon niet worden verzonden', | ||
text: 'Een e-mail vanaf ' + data.from + ' kon niet worden verstuurd aan ' + mail.to + ': \n\n' + e + '\n\n' + (mail.text ?? ''), | ||
type: (data.type === 'transactional') ? 'broadcast' : 'transactional', | ||
}); | ||
} | ||
@@ -473,6 +483,6 @@ } | ||
if (!STAMHOOFD.POSTMARK_SERVER_TOKEN) { | ||
throw new Error("Missing POSTMARK_SERVER_TOKEN") | ||
throw new Error('Missing POSTMARK_SERVER_TOKEN'); | ||
} | ||
const client = new postmark.ServerClient(STAMHOOFD.POSTMARK_SERVER_TOKEN); | ||
const headers: {Name: string, Value: string}[] = []; | ||
const headers: { Name: string; Value: string }[] = []; | ||
for (const key in data.headers) { | ||
@@ -483,12 +493,12 @@ headers.push({ Name: key, Value: data.headers[key] }); | ||
const message: Postmark.Models.Message = { | ||
"From": data.from, | ||
"To": data.to, | ||
"Bcc": data.bcc, | ||
"Subject": data.subject, | ||
"TextBody": data.text, | ||
"HtmlBody": data.html, | ||
"Headers": headers, | ||
"ReplyTo": data.replyTo, | ||
"Tag": "api", | ||
"MessageStream": (data.headers?.["X-PM-Message-Stream"] ?? "outbound") | ||
From: data.from, | ||
To: data.to, | ||
Bcc: data.bcc, | ||
Subject: data.subject, | ||
TextBody: data.text, | ||
HtmlBody: data.html, | ||
Headers: headers, | ||
ReplyTo: data.replyTo, | ||
Tag: 'api', | ||
MessageStream: (data.headers?.['X-PM-Message-Stream'] ?? 'outbound'), | ||
}; | ||
@@ -500,7 +510,7 @@ | ||
await client.sendEmail(message); | ||
} catch (e) { | ||
} | ||
catch (e) { | ||
console.error('Failed to send email with Postmark API', e); | ||
throw e; | ||
} | ||
} | ||
@@ -514,11 +524,11 @@ | ||
// todo: use default email in platform settings | ||
return '"' + (STAMHOOFD.platformName ?? 'Stamhoofd') + ' " <hallo@'+ (i18n.localizedDomains.defaultTransactionalEmail()) +'>' | ||
return '"' + (STAMHOOFD.platformName ?? 'Stamhoofd') + ' " <hallo@' + (i18n.localizedDomains.defaultTransactionalEmail()) + '>'; | ||
} | ||
getWebmasterFromEmail() { | ||
return '"' + (STAMHOOFD.platformName ?? 'Stamhoofd') + ' " <webmaster@'+ (new I18n("nl", "BE").localizedDomains.defaultTransactionalEmail()) +'>' | ||
return '"' + (STAMHOOFD.platformName ?? 'Stamhoofd') + ' " <webmaster@' + (new I18n('nl', 'BE').localizedDomains.defaultTransactionalEmail()) + '>'; | ||
} | ||
getWebmasterToEmail() { | ||
return 'hallo@stamhoofd.be' | ||
return 'hallo@stamhoofd.be'; | ||
} | ||
@@ -531,3 +541,3 @@ | ||
getPersonalEmailFor(i18n: I18n) { | ||
return '"Simon Backx" <'+ (i18n.$t("5670bc42-cf94-46b6-9ce0-7cdc4ffbb4d9")) +'>' | ||
return '"Simon Backx" <' + (i18n.$t('5670bc42-cf94-46b6-9ce0-7cdc4ffbb4d9')) + '>'; | ||
} | ||
@@ -538,8 +548,8 @@ | ||
*/ | ||
sendWebmaster(data: Omit<EmailInterfaceBase, "to">) { | ||
const mail = Object.assign(data, { | ||
sendWebmaster(data: Omit<EmailInterfaceBase, 'to'>) { | ||
const mail = Object.assign(data, { | ||
from: this.getWebmasterFromEmail(), | ||
to: this.getWebmasterToEmail() | ||
}) | ||
this.send(mail) | ||
to: this.getWebmasterToEmail(), | ||
}); | ||
this.send(mail); | ||
} | ||
@@ -552,21 +562,21 @@ | ||
} | ||
let didSend = false | ||
let didSend = false; | ||
this.schedule(() => { | ||
if (didSend) { | ||
return undefined | ||
return undefined; | ||
} | ||
didSend = true; | ||
return data | ||
}) | ||
return data; | ||
}); | ||
} | ||
schedule(builder: EmailBuilder) { | ||
this.currentQueue.push(builder) | ||
this.sendNextIfNeeded() | ||
this.currentQueue.push(builder); | ||
this.sendNextIfNeeded(); | ||
} | ||
scheduleAndWait(builder: EmailBuilder) { | ||
this.currentQueue.push(builder) | ||
this.sendNextIfNeeded() | ||
this.currentQueue.push(builder); | ||
this.sendNextIfNeeded(); | ||
} | ||
@@ -573,0 +583,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
111979
1320