Replace email-templates by nodemailer

This commit is contained in:
Jonas Lochmann
2022-01-24 18:15:31 +01:00
parent e1bbff7e07
commit 387d4cd53c
4 changed files with 162 additions and 2586 deletions
+1 -1
View File
@@ -13,7 +13,7 @@
- default value: 8080
- NODE_ENV
- should be set to ``production`` in production
- when using ``development``, then mails are not sent; instead they are written to a html file which is opened
- when using ``development``, then mails are not sent; instead they are print to the console
- GOOGLE_PLAY_PUBLIC_KEY
- key for validating purchases
- purchases using google play don't work without it/ when it is not set
+68 -2533
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -32,11 +32,12 @@
"@adobe/jsonschema2md": "^6.0.3",
"@types/basic-auth": "^1.1.3",
"@types/body-parser": "^1.19.0",
"@types/email-templates": "^8.0.0",
"@types/ejs": "^3.1.0",
"@types/express": "^4.17.9",
"@types/http-errors": "^1.8.0",
"@types/lodash": "^4.14.166",
"@types/node": "^14.14.16",
"@types/nodemailer": "^6.4.4",
"@types/socket.io": "^2.1.12",
"@types/tokgen": "^1.0.0",
"@types/umzug": "^2.3.0",
@@ -52,12 +53,12 @@
"body-parser": "^1.19.0",
"ejs": "^3.1.5",
"email-addresses": "^3.1.0",
"email-templates": "^8.0.2",
"express": "^4.17.1",
"http-errors": "^1.8.0",
"iab_verifier": "^0.1.2",
"lodash": "^4.17.21",
"mariadb": "^2.5.2",
"nodemailer": "^6.7.2",
"pg": "^8.5.1",
"pg-hstore": "^2.3.3",
"rate-limiter-flexible": "^2.1.15",
+90 -50
View File
@@ -1,6 +1,6 @@
/*
* server component for the TimeLimit App
* Copyright (C) 2019 - 2021 Jonas Lochmann
* Copyright (C) 2019 - 2022 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -15,39 +15,84 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { compile } from 'ejs'
import { parseOneAddress } from 'email-addresses'
import * as Email from 'email-templates'
import { join } from 'path'
import { readFileSync } from 'fs'
import { createTransport } from 'nodemailer'
import { resolve } from 'path'
import { config } from '../config'
import { IllegalStateException } from '../exception'
const mailimprint = process.env.MAIL_IMPRINT || 'not defined'
const mailServerBlacklist = (process.env.MAIL_SERVER_BLACKLIST || '').split(',').filter((item) => !!item)
const email = new Email({
message: {
from: process.env.MAIL_SENDER || ''
},
transport: JSON.parse(process.env.MAIL_TRANSPORT || 'null') || undefined,
views: {
options: {
extension: 'ejs'
const mailSender = process.env.MAIL_SENDER || ''
const mailTransportConfig = JSON.parse(process.env.MAIL_TRANSPORT || 'null') || undefined
const isDevMode = process.env.NODE_ENV === 'development'
const mailTransport = isDevMode || mailTransportConfig !== undefined ?
createTransport(isDevMode ? {
jsonTransport: true
} : mailTransportConfig) :
null
function createMailTemplateSender (templateName: string) {
const compileTemplate = (filename: string) => compile(
readFileSync(resolve(__dirname, '../../other/mail', templateName, filename)).toString('utf8')
)
const subjectTemplate = compileTemplate('subject.ejs')
const textTemplate = compileTemplate('text.ejs')
const htmlTemplate = compileTemplate('html.ejs')
const sendMail = async ({ receiver, params }: {
receiver: string
params: object
}) => {
if (!mailTransport) {
throw new Error('can not send mails without mail config and without NODE_ENV=development')
}
},
juice: false
})
const subject = subjectTemplate(params).replace(/\n/g, ' ')
const text = textTemplate(params)
const html = htmlTemplate(params)
await new Promise<void>((resolve, reject) => {
mailTransport.sendMail({
from: mailSender,
to: receiver,
subject,
text,
html
}, (err, info) => {
if (err) {
reject(err)
} else {
if (isDevMode) {
const data = (info as any).message
console.log(JSON.stringify(JSON.parse(data), null, 2))
}
resolve()
}
})
})
}
return { sendMail }
}
const loginMailSender = createMailTemplateSender('login')
export const sendAuthenticationMail = async ({
receiver, code, locale, deviceName
}: {
receiver: string, code: string, locale: string, deviceName: string | null
}) => {
await email.send({
template: join(__dirname, '../../other/mail/login'),
message: {
to: receiver
},
locals: {
await loginMailSender.sendMail({
receiver,
params: {
subject: locale === 'de' ? 'Anmeldung bei TimeLimit' : 'Sign in at TimeLimit',
introtext: locale === 'de' ? 'Geben Sie zum Authentifizieren folgenden Code in TimeLimit ein' : 'To authenticate, enter the following code in TimeLimit',
code,
@@ -60,16 +105,15 @@ export const sendAuthenticationMail = async ({
})
}
const manipulationMailSender = createMailTemplateSender('manipulation')
export const sendManipulationWarningMail = async ({ receiver, deviceName }: {
receiver: string
deviceName: string
}) => {
await email.send({
template: join(__dirname, '../../other/mail/manipulation'),
message: {
to: receiver
},
locals: {
await manipulationMailSender.sendMail({
receiver,
params: {
subject: 'TimeLimit@' + deviceName + ' - Manipulation',
deviceName,
mailimprint
@@ -77,16 +121,15 @@ export const sendManipulationWarningMail = async ({ receiver, deviceName }: {
})
}
const uninstallMailSender = createMailTemplateSender('uninstall')
export const sendUninstallWarningMail = async ({ receiver, deviceName }: {
receiver: string
deviceName: string
}) => {
await email.send({
template: join(__dirname, '../../other/mail/uninstall'),
message: {
to: receiver
},
locals: {
await uninstallMailSender.sendMail({
receiver,
params: {
subject: 'TimeLimit removed from ' + deviceName,
deviceName,
mailimprint
@@ -94,31 +137,29 @@ export const sendUninstallWarningMail = async ({ receiver, deviceName }: {
})
}
const taskDoneSender = createMailTemplateSender('taskdone')
export const sendTaskDoneMail = async ({ receiver, child, task }: {
receiver: string
child: string
task: string
}) => {
await email.send({
template: join(__dirname, '../../other/mail/taskdone'),
message: {
to: receiver
},
locals: { child, task, mailimprint }
await taskDoneSender.sendMail({
receiver,
params: { child, task, mailimprint }
})
}
const deviceLinkedSender = createMailTemplateSender('device-linked-by-mail')
export const sendDeviceLinkedMail = async ({ receiver, deviceName, locale }: {
receiver: string
deviceName: string
locale: string
}) => {
await email.send({
template: join(__dirname, '../../other/mail/device-linked-by-mail'),
message: {
to: receiver
},
locals: {
await deviceLinkedSender.sendMail({
receiver,
params: {
subject: locale === 'de' ? 'Gerät hinzugefügt' : 'Device added',
preText: locale === 'de' ? 'Soeben wurde das Gerät' : 'The device',
deviceName,
@@ -129,16 +170,15 @@ export const sendDeviceLinkedMail = async ({ receiver, deviceName, locale }: {
})
}
const passwordRecoveryUsedMailSender = createMailTemplateSender('password-recovery-used')
export const sendPasswordRecoveryUsedMail = async ({ receiver, locale }: {
receiver: string
locale: string
}) => {
await email.send({
template: join(__dirname, '../../other/mail/password-recovery-used'),
message: {
to: receiver
},
locals: {
await passwordRecoveryUsedMailSender.sendMail({
receiver,
params: {
subject: locale === 'de' ? 'Passwort-Vergessen-Funktion verwendet' : 'Password reset',
text: locale === 'de' ?
'Soeben wurde Ihr TimeLimit-Passwort mit der Passwort-Vergessen-Funktion geändert.' :