Merge pull request #2918 from gradido/fix-emails

fix(backend): no await for emails
This commit is contained in:
Ulf Gebhardt 2023-04-12 12:25:46 +02:00 committed by GitHub
commit cabd2b8c4a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 49 additions and 72 deletions

View File

@ -76,7 +76,7 @@ const email = {
EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net',
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || '',
EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'mailserver',
EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '1025',
EMAIL_SMTP_PORT: Number(process.env.EMAIL_SMTP_PORT) || 1025,
// eslint-disable-next-line no-unneeded-ternary
EMAIL_TLS: process.env.EMAIL_TLS === 'false' ? false : true,
EMAIL_LINK_VERIFICATION:

View File

@ -10,7 +10,7 @@ import { sendEmailTranslated } from './sendEmailTranslated'
CONFIG.EMAIL = false
CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL'
CONFIG.EMAIL_SMTP_PORT = '1234'
CONFIG.EMAIL_SMTP_PORT = 1234
CONFIG.EMAIL_USERNAME = 'user'
CONFIG.EMAIL_PASSWORD = 'pwd'
CONFIG.EMAIL_TLS = true
@ -31,7 +31,7 @@ jest.mock('nodemailer', () => {
})
describe('sendEmailTranslated', () => {
let result: Record<string, unknown> | null
let result: Record<string, unknown> | boolean | null
describe('config email is false', () => {
beforeEach(async () => {

View File

@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-return */
import path from 'path'
import Email from 'email-templates'
@ -6,44 +7,46 @@ import i18n from 'i18n'
import { createTransport } from 'nodemailer'
import { CONFIG } from '@/config'
import { LogError } from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
export const sendEmailTranslated = async (params: {
export const sendEmailTranslated = async ({
receiver,
template,
locals,
}: {
receiver: {
to: string
cc?: string
}
template: string
locals: Record<string, unknown>
}): Promise<Record<string, unknown> | null> => {
let resultSend: Record<string, unknown> | null = null
}): Promise<Record<string, unknown> | boolean | null> => {
// TODO: test the calling order of 'i18n.setLocale' for example: language of logging 'en', language of email receiver 'es', reset language of current user 'de'
// because language of receiver can differ from language of current user who triggers the sending
const rememberLocaleToRestore = i18n.getLocale()
i18n.setLocale('en') // for logging
logger.info(
`send Email: language=${params.locals.locale} to=${params.receiver.to}` +
(params.receiver.cc ? `, cc=${params.receiver.cc}` : '') +
`, subject=${i18n.__('emails.' + params.template + '.subject')}`,
)
if (!CONFIG.EMAIL) {
logger.info(`Emails are disabled via config...`)
return null
}
// because language of receiver can differ from language of current user who triggers the sending
// const rememberLocaleToRestore = i18n.getLocale()
i18n.setLocale('en') // for logging
logger.info(
`send Email: language=${locals.locale as string} to=${receiver.to}` +
(receiver.cc ? `, cc=${receiver.cc}` : '') +
`, subject=${i18n.__('emails.' + template + '.subject')}`,
)
if (CONFIG.EMAIL_TEST_MODUS) {
logger.info(
`Testmodus=ON: change receiver from ${params.receiver.to} to ${CONFIG.EMAIL_TEST_RECEIVER}`,
`Testmodus=ON: change receiver from ${receiver.to} to ${CONFIG.EMAIL_TEST_RECEIVER}`,
)
params.receiver.to = CONFIG.EMAIL_TEST_RECEIVER
receiver.to = CONFIG.EMAIL_TEST_RECEIVER
}
const transport = createTransport({
host: CONFIG.EMAIL_SMTP_URL,
port: Number(CONFIG.EMAIL_SMTP_PORT),
port: CONFIG.EMAIL_SMTP_PORT,
secure: false, // true for 465, false for other ports
requireTLS: CONFIG.EMAIL_TLS,
auth: {
@ -52,7 +55,7 @@ export const sendEmailTranslated = async (params: {
},
})
i18n.setLocale(params.locals.locale as string) // for email
i18n.setLocale(locals.locale as string) // for email
// TESTING: see 'README.md'
const email = new Email({
@ -64,23 +67,16 @@ export const sendEmailTranslated = async (params: {
// i18n, // is only needed if you don't install i18n
})
// ATTENTION: await is needed, because otherwise on send the email gets send in the language of the current user, because below the language gets reset
await email
const resultSend = await email
.send({
template: path.join(__dirname, 'templates', params.template),
message: params.receiver,
locals: params.locals, // the 'locale' in here seems not to be used by 'email-template', because it doesn't work if the language isn't set before by 'i18n.setLocale'
})
.then((result: Record<string, unknown>) => {
resultSend = result
logger.info('Send email successfully !!!')
logger.info('Result: ', result)
template: path.join(__dirname, 'templates', template),
message: receiver,
locals, // the 'locale' in here seems not to be used by 'email-template', because it doesn't work if the language isn't set before by 'i18n.setLocale'
})
.catch((error: unknown) => {
throw new LogError('Error sending notification email', error)
logger.error('Error sending notification email', error)
return false
})
i18n.setLocale(rememberLocaleToRestore)
return resultSend
}

View File

@ -13,7 +13,7 @@ export const sendAddedContributionMessageEmail = (data: {
senderFirstName: string
senderLastName: string
contributionMemo: string
}): Promise<Record<string, unknown> | null> => {
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: {
to: `${data.firstName} ${data.lastName} <${data.email}>`,
@ -40,7 +40,7 @@ export const sendAccountActivationEmail = (data: {
language: string
activationLink: string
timeDurationObject: Record<string, unknown>
}): Promise<Record<string, unknown> | null> => {
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'accountActivation',
@ -62,7 +62,7 @@ export const sendAccountMultiRegistrationEmail = (data: {
lastName: string
email: string
language: string
}): Promise<Record<string, unknown> | null> => {
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'accountMultiRegistration',
@ -86,7 +86,7 @@ export const sendContributionConfirmedEmail = (data: {
senderLastName: string
contributionMemo: string
contributionAmount: Decimal
}): Promise<Record<string, unknown> | null> => {
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'contributionConfirmed',
@ -113,7 +113,7 @@ export const sendContributionDeletedEmail = (data: {
senderFirstName: string
senderLastName: string
contributionMemo: string
}): Promise<Record<string, unknown> | null> => {
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'contributionDeleted',
@ -139,7 +139,7 @@ export const sendContributionDeniedEmail = (data: {
senderFirstName: string
senderLastName: string
contributionMemo: string
}): Promise<Record<string, unknown> | null> => {
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'contributionDenied',
@ -164,7 +164,7 @@ export const sendResetPasswordEmail = (data: {
language: string
resetLink: string
timeDurationObject: Record<string, unknown>
}): Promise<Record<string, unknown> | null> => {
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'resetPassword',
@ -191,7 +191,7 @@ export const sendTransactionLinkRedeemedEmail = (data: {
senderEmail: string
transactionMemo: string
transactionAmount: Decimal
}): Promise<Record<string, unknown> | null> => {
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'transactionLinkRedeemed',
@ -220,7 +220,7 @@ export const sendTransactionReceivedEmail = (data: {
senderLastName: string
senderEmail: string
transactionAmount: Decimal
}): Promise<Record<string, unknown> | null> => {
}): Promise<Record<string, unknown> | boolean | null> => {
return sendEmailTranslated({
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
template: 'transactionReceived',

View File

@ -146,7 +146,7 @@ export class ContributionMessageResolver {
await queryRunner.manager.update(DbContribution, { id: contributionId }, contribution)
}
await sendAddedContributionMessageEmail({
void sendAddedContributionMessageEmail({
firstName: contribution.user.firstName,
lastName: contribution.user.lastName,
email: contribution.user.emailContact.email,

View File

@ -149,7 +149,7 @@ export const executeTransaction = async (
} finally {
await queryRunner.release()
}
await sendTransactionReceivedEmail({
void sendTransactionReceivedEmail({
firstName: recipient.firstName,
lastName: recipient.lastName,
email: recipient.emailContact.email,
@ -160,7 +160,7 @@ export const executeTransaction = async (
transactionAmount: amount,
})
if (transactionLink) {
await sendTransactionLinkRedeemedEmail({
void sendTransactionLinkRedeemedEmail({
firstName: sender.firstName,
lastName: sender.lastName,
email: sender.emailContact.email,

View File

@ -245,7 +245,7 @@ export class UserResolver {
user.publisherId = publisherId
logger.debug('partly faked user', user)
const emailSent = await sendAccountMultiRegistrationEmail({
void sendAccountMultiRegistrationEmail({
firstName: foundUser.firstName, // this is the real name of the email owner, but just "firstName" would be the name of the new registrant which shall not be passed to the outside
lastName: foundUser.lastName, // this is the real name of the email owner, but just "lastName" would be the name of the new registrant which shall not be passed to the outside
email,
@ -258,9 +258,6 @@ export class UserResolver {
)
/* uncomment this, when you need the activation link on the console */
// In case EMails are disabled log the activation link for the user
if (!emailSent) {
logger.debug(`Email not send!`)
}
logger.info('createUser() faked and send multi registration mail...')
return user
@ -325,8 +322,7 @@ export class UserResolver {
emailContact.emailVerificationCode.toString(),
).replace(/{code}/g, redeemCode ? '/' + redeemCode : '')
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const emailSent = await sendAccountActivationEmail({
void sendAccountActivationEmail({
firstName,
lastName,
email,
@ -338,10 +334,6 @@ export class UserResolver {
await EVENT_EMAIL_CONFIRMATION(dbUser)
if (!emailSent) {
logger.debug(`Account confirmation link: ${activationLink}`)
}
await queryRunner.commitTransaction()
logger.addContext('user', dbUser.id)
} catch (e) {
@ -392,8 +384,8 @@ export class UserResolver {
})
logger.info(`optInCode for ${email}=${user.emailContact}`)
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const emailSent = await sendResetPasswordEmail({
void sendResetPasswordEmail({
firstName: user.firstName,
lastName: user.lastName,
email,
@ -402,13 +394,6 @@ export class UserResolver {
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
})
/* uncomment this, when you need the activation link on the console */
// In case EMails are disabled log the activation link for the user
if (!emailSent) {
logger.debug(
`Reset password link: ${activationLink(user.emailContact.emailVerificationCode)}`,
)
}
logger.info(`forgotPassword(${email}) successful...`)
await EVENT_EMAIL_FORGOT_PASSWORD(user)
@ -804,7 +789,7 @@ export class UserResolver {
await user.emailContact.save()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const emailSent = await sendAccountActivationEmail({
void sendAccountActivationEmail({
firstName: user.firstName,
lastName: user.lastName,
email,
@ -813,10 +798,6 @@ export class UserResolver {
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
})
// In case EMails are disabled log the activation link for the user
if (!emailSent) {
logger.info(`Account confirmation link: ${activationLink}`)
}
await EVENT_EMAIL_ADMIN_CONFIRMATION(user, getUser(context))
return true