mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge pull request #2918 from gradido/fix-emails
fix(backend): no await for emails
This commit is contained in:
commit
cabd2b8c4a
@ -76,7 +76,7 @@ const email = {
|
|||||||
EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net',
|
EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net',
|
||||||
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || '',
|
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || '',
|
||||||
EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'mailserver',
|
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
|
// eslint-disable-next-line no-unneeded-ternary
|
||||||
EMAIL_TLS: process.env.EMAIL_TLS === 'false' ? false : true,
|
EMAIL_TLS: process.env.EMAIL_TLS === 'false' ? false : true,
|
||||||
EMAIL_LINK_VERIFICATION:
|
EMAIL_LINK_VERIFICATION:
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import { sendEmailTranslated } from './sendEmailTranslated'
|
|||||||
|
|
||||||
CONFIG.EMAIL = false
|
CONFIG.EMAIL = false
|
||||||
CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL'
|
CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL'
|
||||||
CONFIG.EMAIL_SMTP_PORT = '1234'
|
CONFIG.EMAIL_SMTP_PORT = 1234
|
||||||
CONFIG.EMAIL_USERNAME = 'user'
|
CONFIG.EMAIL_USERNAME = 'user'
|
||||||
CONFIG.EMAIL_PASSWORD = 'pwd'
|
CONFIG.EMAIL_PASSWORD = 'pwd'
|
||||||
CONFIG.EMAIL_TLS = true
|
CONFIG.EMAIL_TLS = true
|
||||||
@ -31,7 +31,7 @@ jest.mock('nodemailer', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('sendEmailTranslated', () => {
|
describe('sendEmailTranslated', () => {
|
||||||
let result: Record<string, unknown> | null
|
let result: Record<string, unknown> | boolean | null
|
||||||
|
|
||||||
describe('config email is false', () => {
|
describe('config email is false', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|||||||
@ -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 path from 'path'
|
||||||
|
|
||||||
import Email from 'email-templates'
|
import Email from 'email-templates'
|
||||||
@ -6,44 +7,46 @@ import i18n from 'i18n'
|
|||||||
import { createTransport } from 'nodemailer'
|
import { createTransport } from 'nodemailer'
|
||||||
|
|
||||||
import { CONFIG } from '@/config'
|
import { CONFIG } from '@/config'
|
||||||
import { LogError } from '@/server/LogError'
|
|
||||||
import { backendLogger as logger } from '@/server/logger'
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
|
|
||||||
export const sendEmailTranslated = async (params: {
|
export const sendEmailTranslated = async ({
|
||||||
|
receiver,
|
||||||
|
template,
|
||||||
|
locals,
|
||||||
|
}: {
|
||||||
receiver: {
|
receiver: {
|
||||||
to: string
|
to: string
|
||||||
cc?: string
|
cc?: string
|
||||||
}
|
}
|
||||||
template: string
|
template: string
|
||||||
locals: Record<string, unknown>
|
locals: Record<string, unknown>
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
let resultSend: Record<string, unknown> | null = 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'
|
// 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) {
|
if (!CONFIG.EMAIL) {
|
||||||
logger.info(`Emails are disabled via config...`)
|
logger.info(`Emails are disabled via config...`)
|
||||||
return null
|
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) {
|
if (CONFIG.EMAIL_TEST_MODUS) {
|
||||||
logger.info(
|
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({
|
const transport = createTransport({
|
||||||
host: CONFIG.EMAIL_SMTP_URL,
|
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
|
secure: false, // true for 465, false for other ports
|
||||||
requireTLS: CONFIG.EMAIL_TLS,
|
requireTLS: CONFIG.EMAIL_TLS,
|
||||||
auth: {
|
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'
|
// TESTING: see 'README.md'
|
||||||
const email = new Email({
|
const email = new Email({
|
||||||
@ -64,23 +67,16 @@ export const sendEmailTranslated = async (params: {
|
|||||||
// i18n, // is only needed if you don't install i18n
|
// 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
|
const resultSend = await email
|
||||||
await email
|
|
||||||
.send({
|
.send({
|
||||||
template: path.join(__dirname, 'templates', params.template),
|
template: path.join(__dirname, 'templates', template),
|
||||||
message: params.receiver,
|
message: 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'
|
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)
|
|
||||||
})
|
})
|
||||||
.catch((error: unknown) => {
|
.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
|
return resultSend
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export const sendAddedContributionMessageEmail = (data: {
|
|||||||
senderFirstName: string
|
senderFirstName: string
|
||||||
senderLastName: string
|
senderLastName: string
|
||||||
contributionMemo: string
|
contributionMemo: string
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
return sendEmailTranslated({
|
return sendEmailTranslated({
|
||||||
receiver: {
|
receiver: {
|
||||||
to: `${data.firstName} ${data.lastName} <${data.email}>`,
|
to: `${data.firstName} ${data.lastName} <${data.email}>`,
|
||||||
@ -40,7 +40,7 @@ export const sendAccountActivationEmail = (data: {
|
|||||||
language: string
|
language: string
|
||||||
activationLink: string
|
activationLink: string
|
||||||
timeDurationObject: Record<string, unknown>
|
timeDurationObject: Record<string, unknown>
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
return sendEmailTranslated({
|
return sendEmailTranslated({
|
||||||
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
template: 'accountActivation',
|
template: 'accountActivation',
|
||||||
@ -62,7 +62,7 @@ export const sendAccountMultiRegistrationEmail = (data: {
|
|||||||
lastName: string
|
lastName: string
|
||||||
email: string
|
email: string
|
||||||
language: string
|
language: string
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
return sendEmailTranslated({
|
return sendEmailTranslated({
|
||||||
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
template: 'accountMultiRegistration',
|
template: 'accountMultiRegistration',
|
||||||
@ -86,7 +86,7 @@ export const sendContributionConfirmedEmail = (data: {
|
|||||||
senderLastName: string
|
senderLastName: string
|
||||||
contributionMemo: string
|
contributionMemo: string
|
||||||
contributionAmount: Decimal
|
contributionAmount: Decimal
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
return sendEmailTranslated({
|
return sendEmailTranslated({
|
||||||
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
template: 'contributionConfirmed',
|
template: 'contributionConfirmed',
|
||||||
@ -113,7 +113,7 @@ export const sendContributionDeletedEmail = (data: {
|
|||||||
senderFirstName: string
|
senderFirstName: string
|
||||||
senderLastName: string
|
senderLastName: string
|
||||||
contributionMemo: string
|
contributionMemo: string
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
return sendEmailTranslated({
|
return sendEmailTranslated({
|
||||||
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
template: 'contributionDeleted',
|
template: 'contributionDeleted',
|
||||||
@ -139,7 +139,7 @@ export const sendContributionDeniedEmail = (data: {
|
|||||||
senderFirstName: string
|
senderFirstName: string
|
||||||
senderLastName: string
|
senderLastName: string
|
||||||
contributionMemo: string
|
contributionMemo: string
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
return sendEmailTranslated({
|
return sendEmailTranslated({
|
||||||
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
template: 'contributionDenied',
|
template: 'contributionDenied',
|
||||||
@ -164,7 +164,7 @@ export const sendResetPasswordEmail = (data: {
|
|||||||
language: string
|
language: string
|
||||||
resetLink: string
|
resetLink: string
|
||||||
timeDurationObject: Record<string, unknown>
|
timeDurationObject: Record<string, unknown>
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
return sendEmailTranslated({
|
return sendEmailTranslated({
|
||||||
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
template: 'resetPassword',
|
template: 'resetPassword',
|
||||||
@ -191,7 +191,7 @@ export const sendTransactionLinkRedeemedEmail = (data: {
|
|||||||
senderEmail: string
|
senderEmail: string
|
||||||
transactionMemo: string
|
transactionMemo: string
|
||||||
transactionAmount: Decimal
|
transactionAmount: Decimal
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
return sendEmailTranslated({
|
return sendEmailTranslated({
|
||||||
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
template: 'transactionLinkRedeemed',
|
template: 'transactionLinkRedeemed',
|
||||||
@ -220,7 +220,7 @@ export const sendTransactionReceivedEmail = (data: {
|
|||||||
senderLastName: string
|
senderLastName: string
|
||||||
senderEmail: string
|
senderEmail: string
|
||||||
transactionAmount: Decimal
|
transactionAmount: Decimal
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | boolean | null> => {
|
||||||
return sendEmailTranslated({
|
return sendEmailTranslated({
|
||||||
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
template: 'transactionReceived',
|
template: 'transactionReceived',
|
||||||
|
|||||||
@ -146,7 +146,7 @@ export class ContributionMessageResolver {
|
|||||||
await queryRunner.manager.update(DbContribution, { id: contributionId }, contribution)
|
await queryRunner.manager.update(DbContribution, { id: contributionId }, contribution)
|
||||||
}
|
}
|
||||||
|
|
||||||
await sendAddedContributionMessageEmail({
|
void sendAddedContributionMessageEmail({
|
||||||
firstName: contribution.user.firstName,
|
firstName: contribution.user.firstName,
|
||||||
lastName: contribution.user.lastName,
|
lastName: contribution.user.lastName,
|
||||||
email: contribution.user.emailContact.email,
|
email: contribution.user.emailContact.email,
|
||||||
|
|||||||
@ -149,7 +149,7 @@ export const executeTransaction = async (
|
|||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
await sendTransactionReceivedEmail({
|
void sendTransactionReceivedEmail({
|
||||||
firstName: recipient.firstName,
|
firstName: recipient.firstName,
|
||||||
lastName: recipient.lastName,
|
lastName: recipient.lastName,
|
||||||
email: recipient.emailContact.email,
|
email: recipient.emailContact.email,
|
||||||
@ -160,7 +160,7 @@ export const executeTransaction = async (
|
|||||||
transactionAmount: amount,
|
transactionAmount: amount,
|
||||||
})
|
})
|
||||||
if (transactionLink) {
|
if (transactionLink) {
|
||||||
await sendTransactionLinkRedeemedEmail({
|
void sendTransactionLinkRedeemedEmail({
|
||||||
firstName: sender.firstName,
|
firstName: sender.firstName,
|
||||||
lastName: sender.lastName,
|
lastName: sender.lastName,
|
||||||
email: sender.emailContact.email,
|
email: sender.emailContact.email,
|
||||||
|
|||||||
@ -245,7 +245,7 @@ export class UserResolver {
|
|||||||
user.publisherId = publisherId
|
user.publisherId = publisherId
|
||||||
logger.debug('partly faked user', user)
|
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
|
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
|
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,
|
email,
|
||||||
@ -258,9 +258,6 @@ export class UserResolver {
|
|||||||
)
|
)
|
||||||
/* uncomment this, when you need the activation link on the console */
|
/* uncomment this, when you need the activation link on the console */
|
||||||
// In case EMails are disabled log the activation link for the user
|
// 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...')
|
logger.info('createUser() faked and send multi registration mail...')
|
||||||
|
|
||||||
return user
|
return user
|
||||||
@ -325,8 +322,7 @@ export class UserResolver {
|
|||||||
emailContact.emailVerificationCode.toString(),
|
emailContact.emailVerificationCode.toString(),
|
||||||
).replace(/{code}/g, redeemCode ? '/' + redeemCode : '')
|
).replace(/{code}/g, redeemCode ? '/' + redeemCode : '')
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
void sendAccountActivationEmail({
|
||||||
const emailSent = await sendAccountActivationEmail({
|
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
email,
|
email,
|
||||||
@ -338,10 +334,6 @@ export class UserResolver {
|
|||||||
|
|
||||||
await EVENT_EMAIL_CONFIRMATION(dbUser)
|
await EVENT_EMAIL_CONFIRMATION(dbUser)
|
||||||
|
|
||||||
if (!emailSent) {
|
|
||||||
logger.debug(`Account confirmation link: ${activationLink}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
logger.addContext('user', dbUser.id)
|
logger.addContext('user', dbUser.id)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -392,8 +384,8 @@ export class UserResolver {
|
|||||||
})
|
})
|
||||||
|
|
||||||
logger.info(`optInCode for ${email}=${user.emailContact}`)
|
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,
|
firstName: user.firstName,
|
||||||
lastName: user.lastName,
|
lastName: user.lastName,
|
||||||
email,
|
email,
|
||||||
@ -402,13 +394,6 @@ export class UserResolver {
|
|||||||
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
|
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...`)
|
logger.info(`forgotPassword(${email}) successful...`)
|
||||||
await EVENT_EMAIL_FORGOT_PASSWORD(user)
|
await EVENT_EMAIL_FORGOT_PASSWORD(user)
|
||||||
|
|
||||||
@ -804,7 +789,7 @@ export class UserResolver {
|
|||||||
await user.emailContact.save()
|
await user.emailContact.save()
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const emailSent = await sendAccountActivationEmail({
|
void sendAccountActivationEmail({
|
||||||
firstName: user.firstName,
|
firstName: user.firstName,
|
||||||
lastName: user.lastName,
|
lastName: user.lastName,
|
||||||
email,
|
email,
|
||||||
@ -813,10 +798,6 @@ export class UserResolver {
|
|||||||
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
|
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))
|
await EVENT_EMAIL_ADMIN_CONFIRMATION(user, getUser(context))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user