diff --git a/backend/src/emails/Email.builder.ts b/backend/src/emails/Email.builder.ts index 66785f656..7e137cd48 100644 --- a/backend/src/emails/Email.builder.ts +++ b/backend/src/emails/Email.builder.ts @@ -4,6 +4,7 @@ import { User } from '@entity/User' import { CONFIG } from '@/config' import { LogError } from '@/server/LogError' +import { TimeDuration } from '@/util/time' import { decimalSeparatorByLanguage, resetInterface } from '@/util/utilities' import { sendEmailTranslated } from './sendEmailTranslated' @@ -21,12 +22,12 @@ export interface EmailLocals { contributionAmount?: string overviewURL?: string activationLink?: string - timeDurationObject?: Date + timeDurationObject?: TimeDuration resendLink?: string resetLink?: string transactionMemo?: string transactionAmount?: string - [key: string]: string | Date | undefined + [key: string]: string | TimeDuration | undefined } export enum EmailType { @@ -159,6 +160,7 @@ export class EmailBuilder { this.receiver.to = `${recipient.firstName} ${recipient.lastName} <${recipient.emailContact.email}>` this.locals.firstName = recipient.firstName this.locals.lastName = recipient.lastName + this.locals.locale = recipient.language return this } @@ -174,11 +176,6 @@ export class EmailBuilder { return this } - public setLanguage(locale: string): this { - this.locals.locale = locale - return this - } - public setResetLink(resetLink: string): this { this.locals.resentLink = resetLink return this @@ -187,7 +184,7 @@ export class EmailBuilder { public setContribution(contribution: Contribution): this { this.locals.contributionMemo = contribution.memo if (!this.locals.locale || this.locals.locale === '') { - throw new LogError('missing locale please call setLanguage before') + throw new LogError('missing locale please call setRecipient before') } this.locals.contributionAmount = decimalSeparatorByLanguage( contribution.amount, @@ -199,7 +196,7 @@ export class EmailBuilder { public setTransaction(transaction: Transaction): this { this.locals.transactionMemo = transaction.memo if (!this.locals.locale || this.locals.locale === '') { - throw new LogError('missing locale please call setLanguage before') + throw new LogError('missing locale please call setRecipient before') } this.locals.transactionAmount = decimalSeparatorByLanguage( transaction.amount, @@ -213,7 +210,7 @@ export class EmailBuilder { return this } - public setTimeDurationObject(timeDurationObject: Date): this { + public setTimeDurationObject(timeDurationObject: TimeDuration): this { this.locals.timeDurationObject = timeDurationObject return this } diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 5910befa1..a5af00f69 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -14,7 +14,7 @@ import { Order } from '@enum/Order' import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage' import { RIGHTS } from '@/auth/RIGHTS' -import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' +import { EmailBuilder, EmailType } from '@/emails/Email.builder' import { EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE, EVENT_CONTRIBUTION_MESSAGE_CREATE, @@ -170,15 +170,13 @@ export class ContributionMessageResolver { } // send email (never for moderator messages) - void sendAddedContributionMessageEmail({ - firstName: contribution.user.firstName, - lastName: contribution.user.lastName, - email: contribution.user.emailContact.email, - language: contribution.user.language, - senderFirstName: moderator.firstName, - senderLastName: moderator.lastName, - contributionMemo: contribution.memo, - }) + const emailBuilder = new EmailBuilder() + void emailBuilder + .setRecipient(contribution.user) + .setSender(moderator) + .setContribution(contribution) + .setType(EmailType.ADDED_CONTRIBUTION_MESSAGE) + .sendEmail() } await queryRunner.commitTransaction() await EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE( diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index cd5493230..9dabe1193 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -50,6 +50,7 @@ import { getUserCreation, validateContribution, getOpenCreations } from './util/ import { findContributions } from './util/findContributions' import { getLastTransaction } from './util/getLastTransaction' import { sendTransactionsToDltConnector } from './util/sendTransactionsToDltConnector' +import { EmailBuilder, EmailType } from '@/emails/Email.builder' @Resolver() export class ContributionResolver { @@ -336,16 +337,13 @@ export class ContributionResolver { contribution, contribution.amount, ) - - void sendContributionDeletedEmail({ - firstName: user.firstName, - lastName: user.lastName, - email: user.emailContact.email, - language: user.language, - senderFirstName: moderator.firstName, - senderLastName: moderator.lastName, - contributionMemo: contribution.memo, - }) + const emailBuilder = new EmailBuilder() + void emailBuilder + .setRecipient(user) + .setSender(moderator) + .setContribution(contribution) + .setType(EmailType.CONTRIBUTION_DELETED) + .sendEmail() return !!res } @@ -438,16 +436,13 @@ export class ContributionResolver { void sendTransactionsToDltConnector() logger.info('creation commited successfuly.') - void sendContributionConfirmedEmail({ - firstName: user.firstName, - lastName: user.lastName, - email: user.emailContact.email, - language: user.language, - senderFirstName: moderatorUser.firstName, - senderLastName: moderatorUser.lastName, - contributionMemo: contribution.memo, - contributionAmount: contribution.amount, - }) + const emailBuilder = new EmailBuilder() + void emailBuilder + .setRecipient(user) + .setSender(moderatorUser) + .setContribution(contribution) + .setType(EmailType.CONTRIBUTION_CONFIRMED) + .sendEmail() } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Creation was not successful', e) @@ -521,15 +516,13 @@ export class ContributionResolver { contributionToUpdate.amount, ) - void sendContributionDeniedEmail({ - firstName: user.firstName, - lastName: user.lastName, - email: user.emailContact.email, - language: user.language, - senderFirstName: moderator.firstName, - senderLastName: moderator.lastName, - contributionMemo: contributionToUpdate.memo, - }) + const emailBuilder = new EmailBuilder() + void emailBuilder + .setRecipient(user) + .setSender(moderator) + .setContribution(contributionToUpdate) + .setType(EmailType.CONTRIBUTION_DENIED) + .sendEmail() return !!res } diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 45ccd720e..c1561b523 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -31,11 +31,8 @@ import { subscribe } from '@/apis/KlicktippController' import { encode } from '@/auth/JWT' import { RIGHTS } from '@/auth/RIGHTS' import { CONFIG } from '@/config' -import { - sendAccountActivationEmail, - sendAccountMultiRegistrationEmail, - sendResetPasswordEmail, -} from '@/emails/sendEmailVariants' +import { EmailBuilder, EmailType } from '@/emails/Email.builder' +import { sendResetPasswordEmail } from '@/emails/sendEmailVariants' import { Event, EventType, @@ -248,12 +245,12 @@ export class UserResolver { } logger.debug('partly faked user', user) - 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, - language: foundUser.language, // use language of the emails owner for sending - }) + const emailBuilder = new EmailBuilder() + void emailBuilder + .setRecipient(foundUser) // this is the real name of the email owner, but just "firstName" and "lastName" would be the name of the new registrant which shall not be passed to the outside + .setType(EmailType.ACCOUNT_MULTI_REGISTRATION) + .sendEmail() + await EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION(foundUser) logger.info( @@ -328,14 +325,14 @@ export class UserResolver { emailContact.emailVerificationCode.toString(), ).replace(/{code}/g, redeemCode ? '/' + redeemCode : '') - void sendAccountActivationEmail({ - firstName, - lastName, - email, - language, - activationLink, - timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME), - }) + const emailBuilder = new EmailBuilder() + void emailBuilder + .setRecipient(dbUser) + .setActivationLink(activationLink) + .setTimeDurationObject(getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME)) + .setType(EmailType.ACCOUNT_ACTIVATION) + .sendEmail() + logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) await EVENT_EMAIL_CONFIRMATION(dbUser) @@ -794,15 +791,13 @@ export class UserResolver { user.emailContact.emailResendCount++ await user.emailContact.save() - // eslint-disable-next-line @typescript-eslint/no-unused-vars - void sendAccountActivationEmail({ - firstName: user.firstName, - lastName: user.lastName, - email, - language: user.language, - activationLink: activationLink(user.emailContact.emailVerificationCode), - timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME), - }) + const emailBuilder = new EmailBuilder() + void emailBuilder + .setRecipient(user) + .setActivationLink(activationLink(user.emailContact.emailVerificationCode)) + .setTimeDurationObject(getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME)) + .setType(EmailType.ACCOUNT_ACTIVATION) + .sendEmail() await EVENT_EMAIL_ADMIN_CONFIRMATION(user, getUser(context)) diff --git a/backend/src/util/time.ts b/backend/src/util/time.ts index d429c8d6b..7b8d09671 100644 --- a/backend/src/util/time.ts +++ b/backend/src/util/time.ts @@ -1,4 +1,9 @@ -export const getTimeDurationObject = (time: number): { hours?: number; minutes: number } => { +export interface TimeDuration { + hours?: number + minutes: number +} + +export const getTimeDurationObject = (time: number): TimeDuration => { if (time > 60) { return { hours: Math.floor(time / 60),