mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge pull request #2398 from gradido/2392-refactor-more-emails-to-translatables
refactor(backend): refactor more emails to translatables
This commit is contained in:
commit
70a3b1bc7e
@ -1,10 +1,28 @@
|
|||||||
# Docker More Closely
|
# Contributing
|
||||||
|
|
||||||
## Apple M1 Platform
|
If you contribute to our project, please consider the following points.
|
||||||
|
|
||||||
|
## Localization
|
||||||
|
|
||||||
|
### Quotation Marks
|
||||||
|
|
||||||
|
The following characters are different from the programming quotation mark:
|
||||||
|
|
||||||
|
`"` or `\"`
|
||||||
|
|
||||||
|
Please copy and paste the following quotes for the languages:
|
||||||
|
|
||||||
|
- de: „Dies ist ein Beispielsatz.“
|
||||||
|
- en: “This is a sample sentence.”
|
||||||
|
- See <https://grammar.collinsdictionary.com/easy-learning/when-do-you-use-quotation-marks-or-in-english>
|
||||||
|
|
||||||
|
## Docker – More Closely
|
||||||
|
|
||||||
|
### Apple M1 Platform
|
||||||
|
|
||||||
***Attention:** For using Docker commands in Apple M1 environments!*
|
***Attention:** For using Docker commands in Apple M1 environments!*
|
||||||
|
|
||||||
### Enviroment Variable For Apple M1 Platform
|
#### Environment Variable For Apple M1 Platform
|
||||||
|
|
||||||
To set the Docker platform environment variable in your terminal tab, run:
|
To set the Docker platform environment variable in your terminal tab, run:
|
||||||
|
|
||||||
@ -13,7 +31,7 @@ To set the Docker platform environment variable in your terminal tab, run:
|
|||||||
$ export DOCKER_DEFAULT_PLATFORM=linux/amd64
|
$ export DOCKER_DEFAULT_PLATFORM=linux/amd64
|
||||||
```
|
```
|
||||||
|
|
||||||
### Docker Compose Override File For Apple M1 Platform
|
#### Docker Compose Override File For Apple M1 Platform
|
||||||
|
|
||||||
For Docker compose `up` or `build` commands, you can use our Apple M1 override file that specifies the M1 platform:
|
For Docker compose `up` or `build` commands, you can use our Apple M1 override file that specifies the M1 platform:
|
||||||
|
|
||||||
@ -27,7 +45,7 @@ $ docker compose -f docker-compose.yml -f docker-compose.override.yml -f docker-
|
|||||||
$ docker compose -f docker-compose.yml -f docker-compose.apple-m1.override.yml up
|
$ docker compose -f docker-compose.yml -f docker-compose.apple-m1.override.yml up
|
||||||
```
|
```
|
||||||
|
|
||||||
## Analysing Docker Builds
|
### Analyzing Docker Builds
|
||||||
|
|
||||||
To analyze a Docker build, there is a wonderful tool called [dive](https://github.com/wagoodman/dive). Please sponsor if you're using it!
|
To analyze a Docker build, there is a wonderful tool called [dive](https://github.com/wagoodman/dive). Please sponsor if you're using it!
|
||||||
|
|
||||||
@ -8,7 +8,7 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc --build",
|
"build": "tsc --build && mkdir -p build/src/emails/templates/ && cp -r src/emails/templates/* build/src/emails/templates/ && mkdir -p build/src/locales/ && cp -r src/locales/*.json build/src/locales/",
|
||||||
"clean": "tsc --build --clean",
|
"clean": "tsc --build --clean",
|
||||||
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
|
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
|
||||||
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r tsconfig-paths/register src/index.ts",
|
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r tsconfig-paths/register src/index.ts",
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
= t('emails.accountMultiRegistration.subject')
|
|
||||||
@ -89,7 +89,7 @@ describe('sendEmailTranslated', () => {
|
|||||||
originalMessage: expect.objectContaining({
|
originalMessage: expect.objectContaining({
|
||||||
to: 'receiver@mail.org',
|
to: 'receiver@mail.org',
|
||||||
cc: 'support@gradido.net',
|
cc: 'support@gradido.net',
|
||||||
from: 'Gradido (nicht antworten) <info@gradido.net>',
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
attachments: [],
|
attachments: [],
|
||||||
subject: 'Gradido: Try To Register Again With Your Email',
|
subject: 'Gradido: Try To Register Again With Your Email',
|
||||||
html: expect.stringContaining('Gradido: Try To Register Again With Your Email'),
|
html: expect.stringContaining('Gradido: Try To Register Again With Your Email'),
|
||||||
@ -107,4 +107,41 @@ describe('sendEmailTranslated', () => {
|
|||||||
expect(i18n.__).toBeCalled()
|
expect(i18n.__).toBeCalled()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('with email EMAIL_TEST_MODUS true', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
CONFIG.EMAIL = true
|
||||||
|
CONFIG.EMAIL_TEST_MODUS = true
|
||||||
|
result = await sendEmailTranslated({
|
||||||
|
receiver: {
|
||||||
|
to: 'receiver@mail.org',
|
||||||
|
cc: 'support@gradido.net',
|
||||||
|
},
|
||||||
|
template: 'accountMultiRegistration',
|
||||||
|
locals: {
|
||||||
|
locale: 'en',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('call of "sendEmailTranslated" with faked "to"', () => {
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
envelope: {
|
||||||
|
from: CONFIG.EMAIL_SENDER,
|
||||||
|
to: [CONFIG.EMAIL_TEST_RECEIVER, 'support@gradido.net'],
|
||||||
|
},
|
||||||
|
message: expect.any(String),
|
||||||
|
originalMessage: expect.objectContaining({
|
||||||
|
to: CONFIG.EMAIL_TEST_RECEIVER,
|
||||||
|
cc: 'support@gradido.net',
|
||||||
|
from: `Gradido (do not answer) <${CONFIG.EMAIL_SENDER}>`,
|
||||||
|
attachments: [],
|
||||||
|
subject: 'Gradido: Try To Register Again With Your Email',
|
||||||
|
html: expect.stringContaining('Gradido: Try To Register Again With Your Email'),
|
||||||
|
text: expect.stringContaining('GRADIDO: TRY TO REGISTER AGAIN WITH YOUR EMAIL'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
|
import CONFIG from '@/config'
|
||||||
import { backendLogger as logger } from '@/server/logger'
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { createTransport } from 'nodemailer'
|
import { createTransport } from 'nodemailer'
|
||||||
import Email from 'email-templates'
|
import Email from 'email-templates'
|
||||||
import i18n from 'i18n'
|
import i18n from 'i18n'
|
||||||
|
|
||||||
import CONFIG from '@/config'
|
|
||||||
|
|
||||||
export const sendEmailTranslated = async (params: {
|
export const sendEmailTranslated = async (params: {
|
||||||
receiver: {
|
receiver: {
|
||||||
to: string
|
to: string
|
||||||
cc?: string
|
cc?: string
|
||||||
}
|
}
|
||||||
template: string
|
template: string
|
||||||
locals: Record<string, string>
|
locals: Record<string, unknown>
|
||||||
}): Promise<Record<string, unknown> | null> => {
|
}): Promise<Record<string, unknown> | null> => {
|
||||||
let resultSend: Record<string, unknown> | null = null
|
let resultSend: Record<string, unknown> | null = null
|
||||||
|
|
||||||
@ -32,8 +31,7 @@ export const sendEmailTranslated = async (params: {
|
|||||||
logger.info(`Emails are disabled via config...`)
|
logger.info(`Emails are disabled via config...`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// because 'CONFIG.EMAIL_TEST_MODUS' can be boolean 'true' or string '`false`'
|
if (CONFIG.EMAIL_TEST_MODUS) {
|
||||||
if (CONFIG.EMAIL_TEST_MODUS === true) {
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`Testmodus=ON: change receiver from ${params.receiver.to} to ${CONFIG.EMAIL_TEST_RECEIVER}`,
|
`Testmodus=ON: change receiver from ${params.receiver.to} to ${CONFIG.EMAIL_TEST_RECEIVER}`,
|
||||||
)
|
)
|
||||||
@ -50,12 +48,12 @@ export const sendEmailTranslated = async (params: {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
i18n.setLocale(params.locals.locale) // for email
|
i18n.setLocale(params.locals.locale as string) // for email
|
||||||
|
|
||||||
// TESTING: see 'README.md'
|
// TESTING: see 'README.md'
|
||||||
const email = new Email({
|
const email = new Email({
|
||||||
message: {
|
message: {
|
||||||
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
|
from: `Gradido (${i18n.__('emails.general.doNotAnswer')}) <${CONFIG.EMAIL_SENDER}>`,
|
||||||
},
|
},
|
||||||
transport,
|
transport,
|
||||||
preview: false,
|
preview: false,
|
||||||
@ -65,7 +63,7 @@ export const sendEmailTranslated = async (params: {
|
|||||||
// 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
|
// 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
|
await email
|
||||||
.send({
|
.send({
|
||||||
template: path.join(__dirname, params.template),
|
template: path.join(__dirname, 'templates', params.template),
|
||||||
message: params.receiver,
|
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'
|
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'
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,12 +1,34 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { testEnvironment } from '@test/helpers'
|
||||||
|
import { logger, i18n as localization } from '@test/testSetup'
|
||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
import { sendAccountMultiRegistrationEmail } from './sendEmailVariants'
|
import {
|
||||||
|
sendAddedContributionMessageEmail,
|
||||||
|
sendAccountActivationEmail,
|
||||||
|
sendAccountMultiRegistrationEmail,
|
||||||
|
sendContributionConfirmedEmail,
|
||||||
|
sendContributionRejectedEmail,
|
||||||
|
sendResetPasswordEmail,
|
||||||
|
sendTransactionLinkRedeemedEmail,
|
||||||
|
sendTransactionReceivedEmail,
|
||||||
|
} from './sendEmailVariants'
|
||||||
import { sendEmailTranslated } from './sendEmailTranslated'
|
import { sendEmailTranslated } from './sendEmailTranslated'
|
||||||
|
|
||||||
CONFIG.EMAIL = true
|
let con: any
|
||||||
CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL'
|
let testEnv: any
|
||||||
CONFIG.EMAIL_SMTP_PORT = '1234'
|
|
||||||
CONFIG.EMAIL_USERNAME = 'user'
|
beforeAll(async () => {
|
||||||
CONFIG.EMAIL_PASSWORD = 'pwd'
|
testEnv = await testEnvironment(logger, localization)
|
||||||
|
con = testEnv.con
|
||||||
|
// await cleanDB()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
// await cleanDB()
|
||||||
|
await con.close()
|
||||||
|
})
|
||||||
|
|
||||||
jest.mock('./sendEmailTranslated', () => {
|
jest.mock('./sendEmailTranslated', () => {
|
||||||
const originalModule = jest.requireActual('./sendEmailTranslated')
|
const originalModule = jest.requireActual('./sendEmailTranslated')
|
||||||
@ -17,7 +39,154 @@ jest.mock('./sendEmailTranslated', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('sendEmailVariants', () => {
|
describe('sendEmailVariants', () => {
|
||||||
let result: Record<string, unknown> | null
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
let result: any
|
||||||
|
|
||||||
|
describe('sendAddedContributionMessageEmail', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
result = await sendAddedContributionMessageEmail({
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
language: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
contributionMemo: 'My contribution.',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('calls "sendEmailTranslated"', () => {
|
||||||
|
it('with expected parameters', () => {
|
||||||
|
expect(sendEmailTranslated).toBeCalledWith({
|
||||||
|
receiver: {
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
},
|
||||||
|
template: 'addedContributionMessage',
|
||||||
|
locals: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
locale: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
contributionMemo: 'My contribution.',
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has expected result', () => {
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
envelope: {
|
||||||
|
from: 'info@gradido.net',
|
||||||
|
to: ['peter@lustig.de'],
|
||||||
|
},
|
||||||
|
message: expect.any(String),
|
||||||
|
originalMessage: expect.objectContaining({
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
|
attachments: [],
|
||||||
|
subject: 'Gradido: Message about your common good contribution',
|
||||||
|
html: expect.any(String),
|
||||||
|
text: expect.stringContaining('GRADIDO: MESSAGE ABOUT YOUR COMMON GOOD CONTRIBUTION'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
expect(result.originalMessage.html).toContain('<!DOCTYPE html>')
|
||||||
|
expect(result.originalMessage.html).toContain('<html lang="en">')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<title>Gradido: Message about your common good contribution</title>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'>Gradido: Message about your common good contribution</h1>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'you have received a message from Bibi Bloxberg regarding your common good contribution “My contribution.”.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
`Link to your account:<span> </span><a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
|
||||||
|
expect(result.originalMessage.html).toContain('Kind regards,<br><span>your Gradido team')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sendAccountActivationEmail', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
result = await sendAccountActivationEmail({
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
language: 'en',
|
||||||
|
activationLink: 'http://localhost/checkEmail/6627633878930542284',
|
||||||
|
timeDurationObject: { hours: 23, minutes: 30 },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('calls "sendEmailTranslated"', () => {
|
||||||
|
it('with expected parameters', () => {
|
||||||
|
expect(sendEmailTranslated).toBeCalledWith({
|
||||||
|
receiver: {
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
},
|
||||||
|
template: 'accountActivation',
|
||||||
|
locals: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
locale: 'en',
|
||||||
|
activationLink: 'http://localhost/checkEmail/6627633878930542284',
|
||||||
|
timeDurationObject: { hours: 23, minutes: 30 },
|
||||||
|
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has expected result', () => {
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
envelope: {
|
||||||
|
from: 'info@gradido.net',
|
||||||
|
to: ['peter@lustig.de'],
|
||||||
|
},
|
||||||
|
message: expect.any(String),
|
||||||
|
originalMessage: expect.objectContaining({
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
|
attachments: [],
|
||||||
|
subject: 'Gradido: Email Verification',
|
||||||
|
html: expect.any(String),
|
||||||
|
text: expect.stringContaining('GRADIDO: EMAIL VERIFICATION'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
expect(result.originalMessage.html).toContain('<!DOCTYPE html>')
|
||||||
|
expect(result.originalMessage.html).toContain('<html lang="en">')
|
||||||
|
expect(result.originalMessage.html).toContain('<title>Gradido: Email Verification</title>')
|
||||||
|
expect(result.originalMessage.html).toContain('>Gradido: Email Verification</h1>')
|
||||||
|
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'Your email address has just been registered with Gradido.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'Please click on this link to complete the registration and activate your Gradido account:',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<a href="http://localhost/checkEmail/6627633878930542284">http://localhost/checkEmail/6627633878930542284</a>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'or copy the link above into your browser window.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'The link has a validity of 23 hours and 30 minutes. If the validity of the link has already expired, you can have a new link sent to you here by entering your email address:',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
`<a href="${CONFIG.EMAIL_LINK_FORGOTPASSWORD}">${CONFIG.EMAIL_LINK_FORGOTPASSWORD}</a>`,
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Kind regards,<br><span>your Gradido team')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('sendAccountMultiRegistrationEmail', () => {
|
describe('sendAccountMultiRegistrationEmail', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -54,34 +223,401 @@ describe('sendEmailVariants', () => {
|
|||||||
message: expect.any(String),
|
message: expect.any(String),
|
||||||
originalMessage: expect.objectContaining({
|
originalMessage: expect.objectContaining({
|
||||||
to: 'Peter Lustig <peter@lustig.de>',
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
from: 'Gradido (nicht antworten) <info@gradido.net>',
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
attachments: [],
|
attachments: [],
|
||||||
subject: 'Gradido: Try To Register Again With Your Email',
|
subject: 'Gradido: Try To Register Again With Your Email',
|
||||||
html:
|
html: expect.any(String),
|
||||||
expect.stringContaining(
|
|
||||||
'<title>Gradido: Try To Register Again With Your Email</title>',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining('>Gradido: Try To Register Again With Your Email</h1>') &&
|
|
||||||
expect.stringContaining(
|
|
||||||
'Your email address has just been used again to register an account with Gradido.',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining(
|
|
||||||
'However, an account already exists for your email address.',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining(
|
|
||||||
'Please click on the following link if you have forgotten your password:',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining(
|
|
||||||
`<a href="${CONFIG.EMAIL_LINK_FORGOTPASSWORD}">${CONFIG.EMAIL_LINK_FORGOTPASSWORD}</a>`,
|
|
||||||
) &&
|
|
||||||
expect.stringContaining('or copy the link above into your browser window.') &&
|
|
||||||
expect.stringContaining(
|
|
||||||
'If you are not the one who tried to register again, please contact our support:',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining('Sincerely yours,<br><span>your Gradido team'),
|
|
||||||
text: expect.stringContaining('GRADIDO: TRY TO REGISTER AGAIN WITH YOUR EMAIL'),
|
text: expect.stringContaining('GRADIDO: TRY TO REGISTER AGAIN WITH YOUR EMAIL'),
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
expect(result.originalMessage.html).toContain('<!DOCTYPE html>')
|
||||||
|
expect(result.originalMessage.html).toContain('<html lang="en">')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<title>Gradido: Try To Register Again With Your Email</title>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'>Gradido: Try To Register Again With Your Email</h1>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'Your email address has just been used again to register an account with Gradido.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'However, an account already exists for your email address.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'Please click on the following link if you have forgotten your password:',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
`<a href="${CONFIG.EMAIL_LINK_FORGOTPASSWORD}">${CONFIG.EMAIL_LINK_FORGOTPASSWORD}</a>`,
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'or copy the link above into your browser window.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'If you are not the one who tried to register again, please contact our support:',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Kind regards,<br><span>your Gradido team')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sendContributionConfirmedEmail', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
result = await sendContributionConfirmedEmail({
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
language: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
contributionMemo: 'My contribution.',
|
||||||
|
contributionAmount: new Decimal(23.54),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('calls "sendEmailTranslated"', () => {
|
||||||
|
it('with expected parameters', () => {
|
||||||
|
expect(sendEmailTranslated).toBeCalledWith({
|
||||||
|
receiver: {
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
},
|
||||||
|
template: 'contributionConfirmed',
|
||||||
|
locals: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
locale: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
contributionMemo: 'My contribution.',
|
||||||
|
contributionAmount: '23.54',
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has expected result', () => {
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
envelope: {
|
||||||
|
from: 'info@gradido.net',
|
||||||
|
to: ['peter@lustig.de'],
|
||||||
|
},
|
||||||
|
message: expect.any(String),
|
||||||
|
originalMessage: expect.objectContaining({
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
|
attachments: [],
|
||||||
|
subject: 'Gradido: Your common good contribution was confirmed',
|
||||||
|
html: expect.any(String),
|
||||||
|
text: expect.stringContaining('GRADIDO: YOUR COMMON GOOD CONTRIBUTION WAS CONFIRMED'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
expect(result.originalMessage.html).toContain('<!DOCTYPE html>')
|
||||||
|
expect(result.originalMessage.html).toContain('<html lang="en">')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<title>Gradido: Your common good contribution was confirmed</title>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'>Gradido: Your common good contribution was confirmed</h1>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'Your public good contribution “My contribution.” has just been confirmed by Bibi Bloxberg and credited to your Gradido account.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Amount: 23.54 GDD')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
`Link to your account:<span> </span><a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
|
||||||
|
expect(result.originalMessage.html).toContain('Kind regards,<br><span>your Gradido team')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sendContributionRejectedEmail', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
result = await sendContributionRejectedEmail({
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
language: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
contributionMemo: 'My contribution.',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('calls "sendEmailTranslated"', () => {
|
||||||
|
it('with expected parameters', () => {
|
||||||
|
expect(sendEmailTranslated).toBeCalledWith({
|
||||||
|
receiver: {
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
},
|
||||||
|
template: 'contributionRejected',
|
||||||
|
locals: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
locale: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
contributionMemo: 'My contribution.',
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has expected result', () => {
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
envelope: {
|
||||||
|
from: 'info@gradido.net',
|
||||||
|
to: ['peter@lustig.de'],
|
||||||
|
},
|
||||||
|
message: expect.any(String),
|
||||||
|
originalMessage: expect.objectContaining({
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
|
attachments: [],
|
||||||
|
subject: 'Gradido: Your common good contribution was rejected',
|
||||||
|
html: expect.any(String),
|
||||||
|
text: expect.stringContaining('GRADIDO: YOUR COMMON GOOD CONTRIBUTION WAS REJECTED'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
expect(result.originalMessage.html).toContain('<!DOCTYPE html>')
|
||||||
|
expect(result.originalMessage.html).toContain('<html lang="en">')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<title>Gradido: Your common good contribution was rejected</title>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'>Gradido: Your common good contribution was rejected</h1>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'Your public good contribution “My contribution.” was rejected by Bibi Bloxberg.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'To see your common good contributions and related messages, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
`Link to your account:<span> </span><a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
|
||||||
|
expect(result.originalMessage.html).toContain('Kind regards,<br><span>your Gradido team')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sendResetPasswordEmail', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
result = await sendResetPasswordEmail({
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
language: 'en',
|
||||||
|
resetLink: 'http://localhost/reset-password/3762660021544901417',
|
||||||
|
timeDurationObject: { hours: 23, minutes: 30 },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('calls "sendEmailTranslated"', () => {
|
||||||
|
it('with expected parameters', () => {
|
||||||
|
expect(sendEmailTranslated).toBeCalledWith({
|
||||||
|
receiver: {
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
},
|
||||||
|
template: 'resetPassword',
|
||||||
|
locals: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
locale: 'en',
|
||||||
|
resetLink: 'http://localhost/reset-password/3762660021544901417',
|
||||||
|
timeDurationObject: { hours: 23, minutes: 30 },
|
||||||
|
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has expected result', () => {
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
envelope: {
|
||||||
|
from: 'info@gradido.net',
|
||||||
|
to: ['peter@lustig.de'],
|
||||||
|
},
|
||||||
|
message: expect.any(String),
|
||||||
|
originalMessage: expect.objectContaining({
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
|
attachments: [],
|
||||||
|
subject: 'Gradido: Reset password',
|
||||||
|
html: expect.any(String),
|
||||||
|
text: expect.stringContaining('GRADIDO: RESET PASSWORD'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
expect(result.originalMessage.html).toContain('<!DOCTYPE html>')
|
||||||
|
expect(result.originalMessage.html).toContain('<html lang="en">')
|
||||||
|
expect(result.originalMessage.html).toContain('<title>Gradido: Reset password</title>')
|
||||||
|
expect(result.originalMessage.html).toContain('>Gradido: Reset password</h1>')
|
||||||
|
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'You, or someone else, requested a password reset for this account.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('If it was you, please click on the link:')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<a href="http://localhost/reset-password/3762660021544901417">http://localhost/reset-password/3762660021544901417</a>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'or copy the link above into your browser window.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'The link has a validity of 23 hours and 30 minutes. If the validity of the link has already expired, you can have a new link sent to you here by entering your email address:',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
`<a href="${CONFIG.EMAIL_LINK_FORGOTPASSWORD}">${CONFIG.EMAIL_LINK_FORGOTPASSWORD}</a>`,
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Kind regards,<br><span>your Gradido team')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sendTransactionLinkRedeemedEmail', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
result = await sendTransactionLinkRedeemedEmail({
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
language: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
senderEmail: 'bibi@bloxberg.de',
|
||||||
|
transactionMemo: 'You deserve it! 🙏🏼',
|
||||||
|
transactionAmount: new Decimal(17.65),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('calls "sendEmailTranslated"', () => {
|
||||||
|
it('with expected parameters', () => {
|
||||||
|
expect(sendEmailTranslated).toBeCalledWith({
|
||||||
|
receiver: {
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
},
|
||||||
|
template: 'transactionLinkRedeemed',
|
||||||
|
locals: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
locale: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
senderEmail: 'bibi@bloxberg.de',
|
||||||
|
transactionMemo: 'You deserve it! 🙏🏼',
|
||||||
|
transactionAmount: '17.65',
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has expected result', () => {
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
envelope: {
|
||||||
|
from: 'info@gradido.net',
|
||||||
|
to: ['peter@lustig.de'],
|
||||||
|
},
|
||||||
|
message: expect.any(String),
|
||||||
|
originalMessage: expect.objectContaining({
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
|
attachments: [],
|
||||||
|
subject: 'Gradido: Your Gradido link has been redeemed',
|
||||||
|
html: expect.any(String),
|
||||||
|
text: expect.stringContaining('GRADIDO: YOUR GRADIDO LINK HAS BEEN REDEEMED'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
expect(result.originalMessage.html).toContain('<!DOCTYPE html>')
|
||||||
|
expect(result.originalMessage.html).toContain('<html lang="en">')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<title>Gradido: Your Gradido link has been redeemed</title>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'>Gradido: Your Gradido link has been redeemed</h1>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'Bibi Bloxberg (bibi@bloxberg.de) has just redeemed your link.',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Amount: 17.65 GDD')
|
||||||
|
expect(result.originalMessage.html).toContain('Memo: You deserve it! 🙏🏼')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
`You can find transaction details in your Gradido account:<span> </span><a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
|
||||||
|
expect(result.originalMessage.html).toContain('Kind regards,<br><span>your Gradido team')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sendTransactionReceivedEmail', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
result = await sendTransactionReceivedEmail({
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
email: 'peter@lustig.de',
|
||||||
|
language: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
senderEmail: 'bibi@bloxberg.de',
|
||||||
|
transactionAmount: new Decimal(37.4),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('calls "sendEmailTranslated"', () => {
|
||||||
|
it('with expected parameters', () => {
|
||||||
|
expect(sendEmailTranslated).toBeCalledWith({
|
||||||
|
receiver: {
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
},
|
||||||
|
template: 'transactionReceived',
|
||||||
|
locals: {
|
||||||
|
firstName: 'Peter',
|
||||||
|
lastName: 'Lustig',
|
||||||
|
locale: 'en',
|
||||||
|
senderFirstName: 'Bibi',
|
||||||
|
senderLastName: 'Bloxberg',
|
||||||
|
senderEmail: 'bibi@bloxberg.de',
|
||||||
|
transactionAmount: '37.40',
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('has expected result', () => {
|
||||||
|
expect(result).toMatchObject({
|
||||||
|
envelope: {
|
||||||
|
from: 'info@gradido.net',
|
||||||
|
to: ['peter@lustig.de'],
|
||||||
|
},
|
||||||
|
message: expect.any(String),
|
||||||
|
originalMessage: expect.objectContaining({
|
||||||
|
to: 'Peter Lustig <peter@lustig.de>',
|
||||||
|
from: 'Gradido (do not answer) <info@gradido.net>',
|
||||||
|
attachments: [],
|
||||||
|
subject: 'Gradido: You have received Gradidos',
|
||||||
|
html: expect.any(String),
|
||||||
|
text: expect.stringContaining('GRADIDO: YOU HAVE RECEIVED GRADIDOS'),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
expect(result.originalMessage.html).toContain('<!DOCTYPE html>')
|
||||||
|
expect(result.originalMessage.html).toContain('<html lang="en">')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'<title>Gradido: You have received Gradidos</title>',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('>Gradido: You have received Gradidos</h1>')
|
||||||
|
expect(result.originalMessage.html).toContain('Hello Peter Lustig')
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
'You have just received 37.40 GDD from Bibi Bloxberg (bibi@bloxberg.de).',
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain(
|
||||||
|
`You can find transaction details in your Gradido account:<span> </span><a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
|
||||||
|
)
|
||||||
|
expect(result.originalMessage.html).toContain('Please do not reply to this email!')
|
||||||
|
expect(result.originalMessage.html).toContain('Kind regards,<br><span>your Gradido team')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,6 +1,56 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
|
import { decimalSeparatorByLanguage } from '@/util/utilities'
|
||||||
import { sendEmailTranslated } from './sendEmailTranslated'
|
import { sendEmailTranslated } from './sendEmailTranslated'
|
||||||
|
|
||||||
|
export const sendAddedContributionMessageEmail = (data: {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
language: string
|
||||||
|
senderFirstName: string
|
||||||
|
senderLastName: string
|
||||||
|
contributionMemo: string
|
||||||
|
}): Promise<Record<string, unknown> | null> => {
|
||||||
|
return sendEmailTranslated({
|
||||||
|
receiver: {
|
||||||
|
to: `${data.firstName} ${data.lastName} <${data.email}>`,
|
||||||
|
},
|
||||||
|
template: 'addedContributionMessage',
|
||||||
|
locals: {
|
||||||
|
firstName: data.firstName,
|
||||||
|
lastName: data.lastName,
|
||||||
|
locale: data.language,
|
||||||
|
senderFirstName: data.senderFirstName,
|
||||||
|
senderLastName: data.senderLastName,
|
||||||
|
contributionMemo: data.contributionMemo,
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendAccountActivationEmail = (data: {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
language: string
|
||||||
|
activationLink: string
|
||||||
|
timeDurationObject: Record<string, unknown>
|
||||||
|
}): Promise<Record<string, unknown> | null> => {
|
||||||
|
return sendEmailTranslated({
|
||||||
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
|
template: 'accountActivation',
|
||||||
|
locals: {
|
||||||
|
firstName: data.firstName,
|
||||||
|
lastName: data.lastName,
|
||||||
|
locale: data.language,
|
||||||
|
activationLink: data.activationLink,
|
||||||
|
timeDurationObject: data.timeDurationObject,
|
||||||
|
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const sendAccountMultiRegistrationEmail = (data: {
|
export const sendAccountMultiRegistrationEmail = (data: {
|
||||||
firstName: string
|
firstName: string
|
||||||
lastName: string
|
lastName: string
|
||||||
@ -11,10 +61,136 @@ export const sendAccountMultiRegistrationEmail = (data: {
|
|||||||
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
template: 'accountMultiRegistration',
|
template: 'accountMultiRegistration',
|
||||||
locals: {
|
locals: {
|
||||||
locale: data.language,
|
|
||||||
firstName: data.firstName,
|
firstName: data.firstName,
|
||||||
lastName: data.lastName,
|
lastName: data.lastName,
|
||||||
|
locale: data.language,
|
||||||
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
|
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const sendContributionConfirmedEmail = (data: {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
language: string
|
||||||
|
senderFirstName: string
|
||||||
|
senderLastName: string
|
||||||
|
contributionMemo: string
|
||||||
|
contributionAmount: Decimal
|
||||||
|
}): Promise<Record<string, unknown> | null> => {
|
||||||
|
return sendEmailTranslated({
|
||||||
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
|
template: 'contributionConfirmed',
|
||||||
|
locals: {
|
||||||
|
firstName: data.firstName,
|
||||||
|
lastName: data.lastName,
|
||||||
|
locale: data.language,
|
||||||
|
senderFirstName: data.senderFirstName,
|
||||||
|
senderLastName: data.senderLastName,
|
||||||
|
contributionMemo: data.contributionMemo,
|
||||||
|
contributionAmount: decimalSeparatorByLanguage(data.contributionAmount, data.language),
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendContributionRejectedEmail = (data: {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
language: string
|
||||||
|
senderFirstName: string
|
||||||
|
senderLastName: string
|
||||||
|
contributionMemo: string
|
||||||
|
}): Promise<Record<string, unknown> | null> => {
|
||||||
|
return sendEmailTranslated({
|
||||||
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
|
template: 'contributionRejected',
|
||||||
|
locals: {
|
||||||
|
firstName: data.firstName,
|
||||||
|
lastName: data.lastName,
|
||||||
|
locale: data.language,
|
||||||
|
senderFirstName: data.senderFirstName,
|
||||||
|
senderLastName: data.senderLastName,
|
||||||
|
contributionMemo: data.contributionMemo,
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendResetPasswordEmail = (data: {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
language: string
|
||||||
|
resetLink: string
|
||||||
|
timeDurationObject: Record<string, unknown>
|
||||||
|
}): Promise<Record<string, unknown> | null> => {
|
||||||
|
return sendEmailTranslated({
|
||||||
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
|
template: 'resetPassword',
|
||||||
|
locals: {
|
||||||
|
firstName: data.firstName,
|
||||||
|
lastName: data.lastName,
|
||||||
|
locale: data.language,
|
||||||
|
resetLink: data.resetLink,
|
||||||
|
timeDurationObject: data.timeDurationObject,
|
||||||
|
resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendTransactionLinkRedeemedEmail = (data: {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
language: string
|
||||||
|
senderFirstName: string
|
||||||
|
senderLastName: string
|
||||||
|
senderEmail: string
|
||||||
|
transactionMemo: string
|
||||||
|
transactionAmount: Decimal
|
||||||
|
}): Promise<Record<string, unknown> | null> => {
|
||||||
|
return sendEmailTranslated({
|
||||||
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
|
template: 'transactionLinkRedeemed',
|
||||||
|
locals: {
|
||||||
|
firstName: data.firstName,
|
||||||
|
lastName: data.lastName,
|
||||||
|
locale: data.language,
|
||||||
|
senderFirstName: data.senderFirstName,
|
||||||
|
senderLastName: data.senderLastName,
|
||||||
|
senderEmail: data.senderEmail,
|
||||||
|
transactionMemo: data.transactionMemo,
|
||||||
|
transactionAmount: decimalSeparatorByLanguage(data.transactionAmount, data.language),
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sendTransactionReceivedEmail = (data: {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
language: string
|
||||||
|
senderFirstName: string
|
||||||
|
senderLastName: string
|
||||||
|
senderEmail: string
|
||||||
|
transactionAmount: Decimal
|
||||||
|
}): Promise<Record<string, unknown> | null> => {
|
||||||
|
return sendEmailTranslated({
|
||||||
|
receiver: { to: `${data.firstName} ${data.lastName} <${data.email}>` },
|
||||||
|
template: 'transactionReceived',
|
||||||
|
locals: {
|
||||||
|
firstName: data.firstName,
|
||||||
|
lastName: data.lastName,
|
||||||
|
locale: data.language,
|
||||||
|
senderFirstName: data.senderFirstName,
|
||||||
|
senderLastName: data.senderLastName,
|
||||||
|
senderEmail: data.senderEmail,
|
||||||
|
transactionAmount: decimalSeparatorByLanguage(data.transactionAmount, data.language),
|
||||||
|
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
20
backend/src/emails/templates/accountActivation/html.pug
Normal file
20
backend/src/emails/templates/accountActivation/html.pug
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang=locale)
|
||||||
|
head
|
||||||
|
title= t('emails.accountActivation.subject')
|
||||||
|
body
|
||||||
|
h1(style='margin-bottom: 24px;')= t('emails.accountActivation.subject')
|
||||||
|
#container.col
|
||||||
|
p(style='margin-bottom: 24px;')= t('emails.general.helloName', { firstName, lastName })
|
||||||
|
p= t('emails.accountActivation.emailRegistered')
|
||||||
|
p= t('emails.accountActivation.pleaseClickLink')
|
||||||
|
br
|
||||||
|
a(href=activationLink) #{activationLink}
|
||||||
|
br
|
||||||
|
span= t('emails.general.orCopyLink')
|
||||||
|
p= t('emails.accountActivation.duration', { hours: timeDurationObject.hours, minutes: timeDurationObject.minutes })
|
||||||
|
br
|
||||||
|
a(href=resendLink) #{resendLink}
|
||||||
|
p(style='margin-top: 24px;')= t('emails.general.sincerelyYours')
|
||||||
|
br
|
||||||
|
span= t('emails.general.yourGradidoTeam')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= t('emails.accountActivation.subject')
|
||||||
@ -5,7 +5,7 @@ html(lang=locale)
|
|||||||
body
|
body
|
||||||
h1(style='margin-bottom: 24px;')= t('emails.accountMultiRegistration.subject')
|
h1(style='margin-bottom: 24px;')= t('emails.accountMultiRegistration.subject')
|
||||||
#container.col
|
#container.col
|
||||||
p(style='margin-bottom: 24px;')= t('emails.accountMultiRegistration.helloName', { firstName, lastName })
|
p(style='margin-bottom: 24px;')= t('emails.general.helloName', { firstName, lastName })
|
||||||
p= t('emails.accountMultiRegistration.emailReused')
|
p= t('emails.accountMultiRegistration.emailReused')
|
||||||
br
|
br
|
||||||
span= t('emails.accountMultiRegistration.emailExists')
|
span= t('emails.accountMultiRegistration.emailExists')
|
||||||
@ -17,6 +17,6 @@ html(lang=locale)
|
|||||||
p= t('emails.accountMultiRegistration.ifYouAreNotTheOne')
|
p= t('emails.accountMultiRegistration.ifYouAreNotTheOne')
|
||||||
br
|
br
|
||||||
a(href='https://gradido.net/de/contact/') https://gradido.net/de/contact/
|
a(href='https://gradido.net/de/contact/') https://gradido.net/de/contact/
|
||||||
p(style='margin-top: 24px;')= t('emails.accountMultiRegistration.sincerelyYours')
|
p(style='margin-top: 24px;')= t('emails.general.sincerelyYours')
|
||||||
br
|
br
|
||||||
span= t('emails.accountMultiRegistration.yourGradidoTeam')
|
span= t('emails.general.yourGradidoTeam')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= t('emails.accountMultiRegistration.subject')
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang=locale)
|
||||||
|
head
|
||||||
|
title= t('emails.addedContributionMessage.subject')
|
||||||
|
body
|
||||||
|
h1(style='margin-bottom: 24px;')= t('emails.addedContributionMessage.subject')
|
||||||
|
#container.col
|
||||||
|
p(style='margin-bottom: 24px;')= t('emails.general.helloName', { firstName, lastName })
|
||||||
|
p= t('emails.addedContributionMessage.commonGoodContributionMessage', { senderFirstName, senderLastName, contributionMemo })
|
||||||
|
p= t('emails.addedContributionMessage.toSeeAndAnswerMessage')
|
||||||
|
p= t('emails.general.linkToYourAccount')
|
||||||
|
span= " "
|
||||||
|
a(href=overviewURL) #{overviewURL}
|
||||||
|
p= t('emails.general.pleaseDoNotReply')
|
||||||
|
p(style='margin-top: 24px;')= t('emails.general.sincerelyYours')
|
||||||
|
br
|
||||||
|
span= t('emails.general.yourGradidoTeam')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= t('emails.addedContributionMessage.subject')
|
||||||
17
backend/src/emails/templates/contributionConfirmed/html.pug
Normal file
17
backend/src/emails/templates/contributionConfirmed/html.pug
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang=locale)
|
||||||
|
head
|
||||||
|
title= t('emails.contributionConfirmed.subject')
|
||||||
|
body
|
||||||
|
h1(style='margin-bottom: 24px;')= t('emails.contributionConfirmed.subject')
|
||||||
|
#container.col
|
||||||
|
p(style='margin-bottom: 24px;')= t('emails.general.helloName', { firstName, lastName })
|
||||||
|
p= t('emails.contributionConfirmed.commonGoodContributionConfirmed', { senderFirstName, senderLastName, contributionMemo })
|
||||||
|
p= t('emails.general.amountGDD', { amountGDD: contributionAmount })
|
||||||
|
p= t('emails.general.linkToYourAccount')
|
||||||
|
span= " "
|
||||||
|
a(href=overviewURL) #{overviewURL}
|
||||||
|
p= t('emails.general.pleaseDoNotReply')
|
||||||
|
p(style='margin-top: 24px;')= t('emails.general.sincerelyYours')
|
||||||
|
br
|
||||||
|
span= t('emails.general.yourGradidoTeam')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= t('emails.contributionConfirmed.subject')
|
||||||
17
backend/src/emails/templates/contributionRejected/html.pug
Normal file
17
backend/src/emails/templates/contributionRejected/html.pug
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang=locale)
|
||||||
|
head
|
||||||
|
title= t('emails.contributionRejected.subject')
|
||||||
|
body
|
||||||
|
h1(style='margin-bottom: 24px;')= t('emails.contributionRejected.subject')
|
||||||
|
#container.col
|
||||||
|
p(style='margin-bottom: 24px;')= t('emails.general.helloName', { firstName, lastName })
|
||||||
|
p= t('emails.contributionRejected.commonGoodContributionRejected', { senderFirstName, senderLastName, contributionMemo })
|
||||||
|
p= t('emails.contributionRejected.toSeeContributionsAndMessages')
|
||||||
|
p= t('emails.general.linkToYourAccount')
|
||||||
|
span= " "
|
||||||
|
a(href=overviewURL) #{overviewURL}
|
||||||
|
p= t('emails.general.pleaseDoNotReply')
|
||||||
|
p(style='margin-top: 24px;')= t('emails.general.sincerelyYours')
|
||||||
|
br
|
||||||
|
span= t('emails.general.yourGradidoTeam')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= t('emails.contributionRejected.subject')
|
||||||
20
backend/src/emails/templates/resetPassword/html.pug
Normal file
20
backend/src/emails/templates/resetPassword/html.pug
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang=locale)
|
||||||
|
head
|
||||||
|
title= t('emails.resetPassword.subject')
|
||||||
|
body
|
||||||
|
h1(style='margin-bottom: 24px;')= t('emails.resetPassword.subject')
|
||||||
|
#container.col
|
||||||
|
p(style='margin-bottom: 24px;')= t('emails.general.helloName', { firstName, lastName })
|
||||||
|
p= t('emails.resetPassword.youOrSomeoneResetPassword')
|
||||||
|
p= t('emails.resetPassword.pleaseClickLink')
|
||||||
|
br
|
||||||
|
a(href=resetLink) #{resetLink}
|
||||||
|
br
|
||||||
|
span= t('emails.general.orCopyLink')
|
||||||
|
p= t('emails.resetPassword.duration', { hours: timeDurationObject.hours, minutes: timeDurationObject.minutes })
|
||||||
|
br
|
||||||
|
a(href=resendLink) #{resendLink}
|
||||||
|
p(style='margin-top: 24px;')= t('emails.general.sincerelyYours')
|
||||||
|
br
|
||||||
|
span= t('emails.general.yourGradidoTeam')
|
||||||
1
backend/src/emails/templates/resetPassword/subject.pug
Normal file
1
backend/src/emails/templates/resetPassword/subject.pug
Normal file
@ -0,0 +1 @@
|
|||||||
|
= t('emails.resetPassword.subject')
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang=locale)
|
||||||
|
head
|
||||||
|
title= t('emails.transactionLinkRedeemed.subject')
|
||||||
|
body
|
||||||
|
h1(style='margin-bottom: 24px;')= t('emails.transactionLinkRedeemed.subject')
|
||||||
|
#container.col
|
||||||
|
p(style='margin-bottom: 24px;')= t('emails.general.helloName', { firstName, lastName })
|
||||||
|
p= t('emails.transactionLinkRedeemed.hasRedeemedYourLink', { senderFirstName, senderLastName, senderEmail })
|
||||||
|
p= t('emails.general.amountGDD', { amountGDD: transactionAmount })
|
||||||
|
br
|
||||||
|
span= t('emails.transactionLinkRedeemed.memo', { transactionMemo })
|
||||||
|
p= t('emails.general.detailsYouFindOnLinkToYourAccount')
|
||||||
|
span= " "
|
||||||
|
a(href=overviewURL) #{overviewURL}
|
||||||
|
p= t('emails.general.pleaseDoNotReply')
|
||||||
|
p(style='margin-top: 24px;')= t('emails.general.sincerelyYours')
|
||||||
|
br
|
||||||
|
span= t('emails.general.yourGradidoTeam')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= t('emails.transactionLinkRedeemed.subject')
|
||||||
16
backend/src/emails/templates/transactionReceived/html.pug
Normal file
16
backend/src/emails/templates/transactionReceived/html.pug
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
doctype html
|
||||||
|
html(lang=locale)
|
||||||
|
head
|
||||||
|
title= t('emails.transactionReceived.subject')
|
||||||
|
body
|
||||||
|
h1(style='margin-bottom: 24px;')= t('emails.transactionReceived.subject')
|
||||||
|
#container.col
|
||||||
|
p(style='margin-bottom: 24px;')= t('emails.general.helloName', { firstName, lastName })
|
||||||
|
p= t('emails.transactionReceived.haveReceivedAmountGDDFrom', { transactionAmount, senderFirstName, senderLastName, senderEmail })
|
||||||
|
p= t('emails.general.detailsYouFindOnLinkToYourAccount')
|
||||||
|
span= " "
|
||||||
|
a(href=overviewURL) #{overviewURL}
|
||||||
|
p= t('emails.general.pleaseDoNotReply')
|
||||||
|
p(style='margin-top: 24px;')= t('emails.general.sincerelyYours')
|
||||||
|
br
|
||||||
|
span= t('emails.general.yourGradidoTeam')
|
||||||
@ -0,0 +1 @@
|
|||||||
|
= t('emails.transactionReceived.subject')
|
||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
import { objectValuesToArray } from '@/util/utilities'
|
import { objectValuesToArray } from '@/util/utilities'
|
||||||
import { testEnvironment, resetToken, cleanDB, contributionDateFormatter } from '@test/helpers'
|
import { testEnvironment, resetToken, cleanDB, contributionDateFormatter } from '@test/helpers'
|
||||||
|
import { logger, i18n as localization } from '@test/testSetup'
|
||||||
import { userFactory } from '@/seeds/factory/user'
|
import { userFactory } from '@/seeds/factory/user'
|
||||||
import { creationFactory } from '@/seeds/factory/creation'
|
import { creationFactory } from '@/seeds/factory/creation'
|
||||||
import { creations } from '@/seeds/creation/index'
|
import { creations } from '@/seeds/creation/index'
|
||||||
@ -35,30 +36,31 @@ import {
|
|||||||
} from '@/seeds/graphql/queries'
|
} from '@/seeds/graphql/queries'
|
||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
import {
|
||||||
import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
|
// sendAccountActivationEmail,
|
||||||
|
sendContributionConfirmedEmail,
|
||||||
|
// sendContributionRejectedEmail,
|
||||||
|
} from '@/emails/sendEmailVariants'
|
||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { Contribution } from '@entity/Contribution'
|
import { Contribution } from '@entity/Contribution'
|
||||||
import { Transaction as DbTransaction } from '@entity/Transaction'
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||||
import { sendContributionConfirmedEmail } from '@/mailer/sendContributionConfirmedEmail'
|
|
||||||
import { EventProtocol } from '@entity/EventProtocol'
|
import { EventProtocol } from '@entity/EventProtocol'
|
||||||
import { EventProtocolType } from '@/event/EventProtocolType'
|
import { EventProtocolType } from '@/event/EventProtocolType'
|
||||||
import { logger } from '@test/testSetup'
|
|
||||||
|
|
||||||
// mock account activation email to avoid console spam
|
// mock account activation email to avoid console spam
|
||||||
jest.mock('@/mailer/sendAccountActivationEmail', () => {
|
jest.mock('@/emails/sendEmailVariants', () => {
|
||||||
|
const originalModule = jest.requireActual('@/emails/sendEmailVariants')
|
||||||
return {
|
return {
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
sendAccountActivationEmail: jest.fn(),
|
...originalModule,
|
||||||
}
|
// TODO: test the call of …
|
||||||
})
|
// sendAccountActivationEmail: jest.fn((a) => originalModule.sendAccountActivationEmail(a)),
|
||||||
|
sendContributionConfirmedEmail: jest.fn((a) =>
|
||||||
// mock account activation email to avoid console spam
|
originalModule.sendContributionConfirmedEmail(a),
|
||||||
jest.mock('@/mailer/sendContributionConfirmedEmail', () => {
|
),
|
||||||
return {
|
// TODO: test the call of …
|
||||||
__esModule: true,
|
// sendContributionRejectedEmail: jest.fn((a) => originalModule.sendContributionRejectedEmail(a)),
|
||||||
sendContributionConfirmedEmail: jest.fn(),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -66,7 +68,7 @@ let mutate: any, query: any, con: any
|
|||||||
let testEnv: any
|
let testEnv: any
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
testEnv = await testEnvironment()
|
testEnv = await testEnvironment(logger, localization)
|
||||||
mutate = testEnv.mutate
|
mutate = testEnv.mutate
|
||||||
query = testEnv.query
|
query = testEnv.query
|
||||||
con = testEnv.con
|
con = testEnv.con
|
||||||
@ -1737,17 +1739,16 @@ describe('AdminResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('calls sendContributionConfirmedEmail', async () => {
|
it('calls sendContributionConfirmedEmail', async () => {
|
||||||
expect(sendContributionConfirmedEmail).toBeCalledWith(
|
expect(sendContributionConfirmedEmail).toBeCalledWith({
|
||||||
expect.objectContaining({
|
firstName: 'Bibi',
|
||||||
contributionMemo: 'Herzlich Willkommen bei Gradido liebe Bibi!',
|
lastName: 'Bloxberg',
|
||||||
overviewURL: 'http://localhost/overview',
|
email: 'bibi@bloxberg.de',
|
||||||
recipientEmail: 'bibi@bloxberg.de',
|
language: 'de',
|
||||||
recipientFirstName: 'Bibi',
|
senderFirstName: 'Peter',
|
||||||
recipientLastName: 'Bloxberg',
|
senderLastName: 'Lustig',
|
||||||
senderFirstName: 'Peter',
|
contributionMemo: 'Herzlich Willkommen bei Gradido liebe Bibi!',
|
||||||
senderLastName: 'Lustig',
|
contributionAmount: expect.decimalEqual(450),
|
||||||
}),
|
})
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('stores the send confirmation email event in the database', async () => {
|
it('stores the send confirmation email event in the database', async () => {
|
||||||
|
|||||||
@ -39,8 +39,14 @@ import { Decay } from '@model/Decay'
|
|||||||
import Paginated from '@arg/Paginated'
|
import Paginated from '@arg/Paginated'
|
||||||
import TransactionLinkFilters from '@arg/TransactionLinkFilters'
|
import TransactionLinkFilters from '@arg/TransactionLinkFilters'
|
||||||
import { Order } from '@enum/Order'
|
import { Order } from '@enum/Order'
|
||||||
import { findUserByEmail, activationLink, printTimeDuration } from './UserResolver'
|
import { getTimeDurationObject } from '@/util/time'
|
||||||
import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
|
import { findUserByEmail, activationLink } from './UserResolver'
|
||||||
|
import {
|
||||||
|
sendAddedContributionMessageEmail,
|
||||||
|
sendAccountActivationEmail,
|
||||||
|
sendContributionConfirmedEmail,
|
||||||
|
sendContributionRejectedEmail,
|
||||||
|
} from '@/emails/sendEmailVariants'
|
||||||
import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver'
|
import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver'
|
||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
import {
|
import {
|
||||||
@ -63,9 +69,6 @@ import { ContributionMessage as DbContributionMessage } from '@entity/Contributi
|
|||||||
import ContributionMessageArgs from '@arg/ContributionMessageArgs'
|
import ContributionMessageArgs from '@arg/ContributionMessageArgs'
|
||||||
import { ContributionMessageType } from '@enum/MessageType'
|
import { ContributionMessageType } from '@enum/MessageType'
|
||||||
import { ContributionMessage } from '@model/ContributionMessage'
|
import { ContributionMessage } from '@model/ContributionMessage'
|
||||||
import { sendContributionConfirmedEmail } from '@/mailer/sendContributionConfirmedEmail'
|
|
||||||
import { sendContributionRejectedEmail } from '@/mailer/sendContributionRejectedEmail'
|
|
||||||
import { sendAddedContributionMessageEmail } from '@/mailer/sendAddedContributionMessageEmail'
|
|
||||||
import { eventProtocol } from '@/event/EventProtocolEmitter'
|
import { eventProtocol } from '@/event/EventProtocolEmitter'
|
||||||
import {
|
import {
|
||||||
Event,
|
Event,
|
||||||
@ -492,14 +495,13 @@ export class AdminResolver {
|
|||||||
event.setEventAdminContributionDelete(eventAdminContributionDelete),
|
event.setEventAdminContributionDelete(eventAdminContributionDelete),
|
||||||
)
|
)
|
||||||
sendContributionRejectedEmail({
|
sendContributionRejectedEmail({
|
||||||
|
firstName: user.firstName,
|
||||||
|
lastName: user.lastName,
|
||||||
|
email: user.emailContact.email,
|
||||||
|
language: user.language,
|
||||||
senderFirstName: moderator.firstName,
|
senderFirstName: moderator.firstName,
|
||||||
senderLastName: moderator.lastName,
|
senderLastName: moderator.lastName,
|
||||||
recipientEmail: user.emailContact.email,
|
|
||||||
recipientFirstName: user.firstName,
|
|
||||||
recipientLastName: user.lastName,
|
|
||||||
contributionMemo: contribution.memo,
|
contributionMemo: contribution.memo,
|
||||||
contributionAmount: contribution.amount,
|
|
||||||
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return !!res
|
return !!res
|
||||||
@ -587,14 +589,14 @@ export class AdminResolver {
|
|||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
logger.info('creation commited successfuly.')
|
logger.info('creation commited successfuly.')
|
||||||
sendContributionConfirmedEmail({
|
sendContributionConfirmedEmail({
|
||||||
|
firstName: user.firstName,
|
||||||
|
lastName: user.lastName,
|
||||||
|
email: user.emailContact.email,
|
||||||
|
language: user.language,
|
||||||
senderFirstName: moderatorUser.firstName,
|
senderFirstName: moderatorUser.firstName,
|
||||||
senderLastName: moderatorUser.lastName,
|
senderLastName: moderatorUser.lastName,
|
||||||
recipientFirstName: user.firstName,
|
|
||||||
recipientLastName: user.lastName,
|
|
||||||
recipientEmail: user.emailContact.email,
|
|
||||||
contributionMemo: contribution.memo,
|
contributionMemo: contribution.memo,
|
||||||
contributionAmount: contribution.amount,
|
contributionAmount: contribution.amount,
|
||||||
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
@ -655,17 +657,18 @@ export class AdminResolver {
|
|||||||
}
|
}
|
||||||
const emailContact = user.emailContact
|
const emailContact = user.emailContact
|
||||||
if (emailContact.deletedAt) {
|
if (emailContact.deletedAt) {
|
||||||
logger.error(`The emailContact: ${email} of htis User is deleted.`)
|
logger.error(`The emailContact: ${email} of this User is deleted.`)
|
||||||
throw new Error(`The emailContact: ${email} of htis User is deleted.`)
|
throw new Error(`The emailContact: ${email} of this User is deleted.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const emailSent = await sendAccountActivationEmail({
|
const emailSent = await sendAccountActivationEmail({
|
||||||
link: activationLink(emailContact.emailVerificationCode),
|
|
||||||
firstName: user.firstName,
|
firstName: user.firstName,
|
||||||
lastName: user.lastName,
|
lastName: user.lastName,
|
||||||
email,
|
email,
|
||||||
duration: printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME),
|
language: user.language,
|
||||||
|
activationLink: activationLink(emailContact.emailVerificationCode),
|
||||||
|
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
|
||||||
})
|
})
|
||||||
|
|
||||||
// In case EMails are disabled log the activation link for the user
|
// In case EMails are disabled log the activation link for the user
|
||||||
@ -900,15 +903,13 @@ export class AdminResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await sendAddedContributionMessageEmail({
|
await sendAddedContributionMessageEmail({
|
||||||
|
firstName: contribution.user.firstName,
|
||||||
|
lastName: contribution.user.lastName,
|
||||||
|
email: contribution.user.emailContact.email,
|
||||||
|
language: contribution.user.language,
|
||||||
senderFirstName: user.firstName,
|
senderFirstName: user.firstName,
|
||||||
senderLastName: user.lastName,
|
senderLastName: user.lastName,
|
||||||
recipientFirstName: contribution.user.firstName,
|
|
||||||
recipientLastName: contribution.user.lastName,
|
|
||||||
recipientEmail: contribution.user.emailContact.email,
|
|
||||||
senderEmail: user.emailContact.email,
|
|
||||||
contributionMemo: contribution.memo,
|
contributionMemo: contribution.memo,
|
||||||
message,
|
|
||||||
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
|
||||||
})
|
})
|
||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
|
||||||
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
|
||||||
|
import { logger, i18n as localization } from '@test/testSetup'
|
||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import {
|
import {
|
||||||
adminCreateContributionMessage,
|
adminCreateContributionMessage,
|
||||||
@ -13,12 +14,16 @@ import { listContributionMessages } from '@/seeds/graphql/queries'
|
|||||||
import { userFactory } from '@/seeds/factory/user'
|
import { userFactory } from '@/seeds/factory/user'
|
||||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||||
import { sendAddedContributionMessageEmail } from '@/mailer/sendAddedContributionMessageEmail'
|
import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants'
|
||||||
|
|
||||||
jest.mock('@/mailer/sendAddedContributionMessageEmail', () => {
|
jest.mock('@/emails/sendEmailVariants', () => {
|
||||||
|
const originalModule = jest.requireActual('@/emails/sendEmailVariants')
|
||||||
return {
|
return {
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
sendAddedContributionMessageEmail: jest.fn(),
|
...originalModule,
|
||||||
|
sendAddedContributionMessageEmail: jest.fn((a) =>
|
||||||
|
originalModule.sendAddedContributionMessageEmail(a),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -27,7 +32,7 @@ let testEnv: any
|
|||||||
let result: any
|
let result: any
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
testEnv = await testEnvironment()
|
testEnv = await testEnvironment(logger, localization)
|
||||||
mutate = testEnv.mutate
|
mutate = testEnv.mutate
|
||||||
con = testEnv.con
|
con = testEnv.con
|
||||||
await cleanDB()
|
await cleanDB()
|
||||||
@ -162,15 +167,13 @@ describe('ContributionMessageResolver', () => {
|
|||||||
|
|
||||||
it('calls sendAddedContributionMessageEmail', async () => {
|
it('calls sendAddedContributionMessageEmail', async () => {
|
||||||
expect(sendAddedContributionMessageEmail).toBeCalledWith({
|
expect(sendAddedContributionMessageEmail).toBeCalledWith({
|
||||||
|
firstName: 'Bibi',
|
||||||
|
lastName: 'Bloxberg',
|
||||||
|
email: 'bibi@bloxberg.de',
|
||||||
|
language: 'de',
|
||||||
senderFirstName: 'Peter',
|
senderFirstName: 'Peter',
|
||||||
senderLastName: 'Lustig',
|
senderLastName: 'Lustig',
|
||||||
recipientFirstName: 'Bibi',
|
|
||||||
recipientLastName: 'Bloxberg',
|
|
||||||
recipientEmail: 'bibi@bloxberg.de',
|
|
||||||
senderEmail: 'peter@lustig.de',
|
|
||||||
contributionMemo: 'Test env contribution',
|
contributionMemo: 'Test env contribution',
|
||||||
message: 'Admin Test',
|
|
||||||
overviewURL: 'http://localhost/overview',
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -2,14 +2,11 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
|
|
||||||
import { backendLogger as logger } from '@/server/logger'
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
import CONFIG from '@/config'
|
|
||||||
|
|
||||||
import { Context, getUser } from '@/server/context'
|
import { Context, getUser } from '@/server/context'
|
||||||
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
||||||
import { getCustomRepository, getConnection, In } from '@dbTools/typeorm'
|
import { getCustomRepository, getConnection, In } from '@dbTools/typeorm'
|
||||||
|
|
||||||
import { sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail'
|
|
||||||
|
|
||||||
import { Transaction } from '@model/Transaction'
|
import { Transaction } from '@model/Transaction'
|
||||||
import { TransactionList } from '@model/TransactionList'
|
import { TransactionList } from '@model/TransactionList'
|
||||||
|
|
||||||
@ -36,7 +33,10 @@ import Decimal from 'decimal.js-light'
|
|||||||
import { BalanceResolver } from './BalanceResolver'
|
import { BalanceResolver } from './BalanceResolver'
|
||||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
|
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
|
||||||
import { findUserByEmail } from './UserResolver'
|
import { findUserByEmail } from './UserResolver'
|
||||||
import { sendTransactionLinkRedeemedEmail } from '@/mailer/sendTransactionLinkRedeemed'
|
import {
|
||||||
|
sendTransactionLinkRedeemedEmail,
|
||||||
|
sendTransactionReceivedEmail,
|
||||||
|
} from '@/emails/sendEmailVariants'
|
||||||
import { Event, EventTransactionReceive, EventTransactionSend } from '@/event/Event'
|
import { Event, EventTransactionReceive, EventTransactionSend } from '@/event/Event'
|
||||||
import { eventProtocol } from '@/event/EventProtocolEmitter'
|
import { eventProtocol } from '@/event/EventProtocolEmitter'
|
||||||
|
|
||||||
@ -159,29 +159,27 @@ export const executeTransaction = async (
|
|||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
logger.debug(`prepare Email for transaction received...`)
|
logger.debug(`prepare Email for transaction received...`)
|
||||||
// send notification email
|
|
||||||
// TODO: translate
|
|
||||||
await sendTransactionReceivedEmail({
|
await sendTransactionReceivedEmail({
|
||||||
|
firstName: recipient.firstName,
|
||||||
|
lastName: recipient.lastName,
|
||||||
|
email: recipient.emailContact.email,
|
||||||
|
language: recipient.language,
|
||||||
senderFirstName: sender.firstName,
|
senderFirstName: sender.firstName,
|
||||||
senderLastName: sender.lastName,
|
senderLastName: sender.lastName,
|
||||||
recipientFirstName: recipient.firstName,
|
|
||||||
recipientLastName: recipient.lastName,
|
|
||||||
email: recipient.emailContact.email,
|
|
||||||
senderEmail: sender.emailContact.email,
|
senderEmail: sender.emailContact.email,
|
||||||
amount,
|
transactionAmount: amount,
|
||||||
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
|
||||||
})
|
})
|
||||||
if (transactionLink) {
|
if (transactionLink) {
|
||||||
await sendTransactionLinkRedeemedEmail({
|
await sendTransactionLinkRedeemedEmail({
|
||||||
|
firstName: sender.firstName,
|
||||||
|
lastName: sender.lastName,
|
||||||
|
email: sender.emailContact.email,
|
||||||
|
language: sender.language,
|
||||||
senderFirstName: recipient.firstName,
|
senderFirstName: recipient.firstName,
|
||||||
senderLastName: recipient.lastName,
|
senderLastName: recipient.lastName,
|
||||||
recipientFirstName: sender.firstName,
|
|
||||||
recipientLastName: sender.lastName,
|
|
||||||
email: sender.emailContact.email,
|
|
||||||
senderEmail: recipient.emailContact.email,
|
senderEmail: recipient.emailContact.email,
|
||||||
amount,
|
transactionAmount: amount,
|
||||||
memo,
|
transactionMemo: memo,
|
||||||
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
logger.info(`finished executeTransaction successfully`)
|
logger.info(`finished executeTransaction successfully`)
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
|
||||||
import { testEnvironment, headerPushMock, resetToken, cleanDB } from '@test/helpers'
|
import { testEnvironment, headerPushMock, resetToken, cleanDB } from '@test/helpers'
|
||||||
|
import { logger, i18n as localization } from '@test/testSetup'
|
||||||
|
import { printTimeDuration } from '@/util/time'
|
||||||
import { userFactory } from '@/seeds/factory/user'
|
import { userFactory } from '@/seeds/factory/user'
|
||||||
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
|
||||||
import {
|
import {
|
||||||
@ -18,18 +20,18 @@ import { verifyLogin, queryOptIn, searchAdminUsers } from '@/seeds/graphql/queri
|
|||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
|
import {
|
||||||
import { sendAccountMultiRegistrationEmail } from '@/emails/sendEmailVariants'
|
sendAccountActivationEmail,
|
||||||
import { sendResetPasswordEmail } from '@/mailer/sendResetPasswordEmail'
|
sendAccountMultiRegistrationEmail,
|
||||||
import { printTimeDuration, activationLink } from './UserResolver'
|
sendResetPasswordEmail,
|
||||||
|
} from '@/emails/sendEmailVariants'
|
||||||
|
import { activationLink } from './UserResolver'
|
||||||
import { contributionLinkFactory } from '@/seeds/factory/contributionLink'
|
import { contributionLinkFactory } from '@/seeds/factory/contributionLink'
|
||||||
import { transactionLinkFactory } from '@/seeds/factory/transactionLink'
|
import { transactionLinkFactory } from '@/seeds/factory/transactionLink'
|
||||||
import { ContributionLink } from '@model/ContributionLink'
|
import { ContributionLink } from '@model/ContributionLink'
|
||||||
import { TransactionLink } from '@entity/TransactionLink'
|
import { TransactionLink } from '@entity/TransactionLink'
|
||||||
|
|
||||||
import { EventProtocolType } from '@/event/EventProtocolType'
|
import { EventProtocolType } from '@/event/EventProtocolType'
|
||||||
import { EventProtocol } from '@entity/EventProtocol'
|
import { EventProtocol } from '@entity/EventProtocol'
|
||||||
import { logger, i18n as localization } from '@test/testSetup'
|
|
||||||
import { validate as validateUUID, version as versionUUID } from 'uuid'
|
import { validate as validateUUID, version as versionUUID } from 'uuid'
|
||||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||||
import { UserContact } from '@entity/UserContact'
|
import { UserContact } from '@entity/UserContact'
|
||||||
@ -42,24 +44,16 @@ import { SecretKeyCryptographyCreateKey } from '@/password/EncryptorUtils'
|
|||||||
|
|
||||||
// import { klicktippSignIn } from '@/apis/KlicktippController'
|
// import { klicktippSignIn } from '@/apis/KlicktippController'
|
||||||
|
|
||||||
jest.mock('@/mailer/sendAccountActivationEmail', () => {
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
sendAccountActivationEmail: jest.fn(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
jest.mock('@/emails/sendEmailVariants', () => {
|
jest.mock('@/emails/sendEmailVariants', () => {
|
||||||
|
const originalModule = jest.requireActual('@/emails/sendEmailVariants')
|
||||||
return {
|
return {
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
sendAccountMultiRegistrationEmail: jest.fn(),
|
...originalModule,
|
||||||
}
|
sendAccountActivationEmail: jest.fn((a) => originalModule.sendAccountActivationEmail(a)),
|
||||||
})
|
sendAccountMultiRegistrationEmail: jest.fn((a) =>
|
||||||
|
originalModule.sendAccountMultiRegistrationEmail(a),
|
||||||
jest.mock('@/mailer/sendResetPasswordEmail', () => {
|
),
|
||||||
return {
|
sendResetPasswordEmail: jest.fn((a) => originalModule.sendResetPasswordEmail(a)),
|
||||||
__esModule: true,
|
|
||||||
sendResetPasswordEmail: jest.fn(),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -184,11 +178,15 @@ describe('UserResolver', () => {
|
|||||||
emailVerificationCode,
|
emailVerificationCode,
|
||||||
).replace(/{code}/g, '')
|
).replace(/{code}/g, '')
|
||||||
expect(sendAccountActivationEmail).toBeCalledWith({
|
expect(sendAccountActivationEmail).toBeCalledWith({
|
||||||
link: activationLink,
|
|
||||||
firstName: 'Peter',
|
firstName: 'Peter',
|
||||||
lastName: 'Lustig',
|
lastName: 'Lustig',
|
||||||
email: 'peter@lustig.de',
|
email: 'peter@lustig.de',
|
||||||
duration: expect.any(String),
|
language: 'de',
|
||||||
|
activationLink,
|
||||||
|
timeDurationObject: expect.objectContaining({
|
||||||
|
hours: expect.any(Number),
|
||||||
|
minutes: expect.any(Number),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -853,11 +851,15 @@ describe('UserResolver', () => {
|
|||||||
|
|
||||||
it('sends reset password email', () => {
|
it('sends reset password email', () => {
|
||||||
expect(sendResetPasswordEmail).toBeCalledWith({
|
expect(sendResetPasswordEmail).toBeCalledWith({
|
||||||
link: activationLink(emailContact.emailVerificationCode),
|
|
||||||
firstName: 'Bibi',
|
firstName: 'Bibi',
|
||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
email: 'bibi@bloxberg.de',
|
email: 'bibi@bloxberg.de',
|
||||||
duration: expect.any(String),
|
language: 'de',
|
||||||
|
resetLink: activationLink(emailContact.emailVerificationCode),
|
||||||
|
timeDurationObject: expect.objectContaining({
|
||||||
|
hours: expect.any(Number),
|
||||||
|
minutes: expect.any(Number),
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { User } from '@model/User'
|
|||||||
import { User as DbUser } from '@entity/User'
|
import { User as DbUser } from '@entity/User'
|
||||||
import { UserContact as DbUserContact } from '@entity/UserContact'
|
import { UserContact as DbUserContact } from '@entity/UserContact'
|
||||||
import { communityDbUser } from '@/util/communityUser'
|
import { communityDbUser } from '@/util/communityUser'
|
||||||
|
import { getTimeDurationObject, printTimeDuration } from '@/util/time'
|
||||||
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
|
||||||
import { ContributionLink as dbContributionLink } from '@entity/ContributionLink'
|
import { ContributionLink as dbContributionLink } from '@entity/ContributionLink'
|
||||||
import { encode } from '@/auth/JWT'
|
import { encode } from '@/auth/JWT'
|
||||||
@ -17,9 +18,11 @@ import UnsecureLoginArgs from '@arg/UnsecureLoginArgs'
|
|||||||
import UpdateUserInfosArgs from '@arg/UpdateUserInfosArgs'
|
import UpdateUserInfosArgs from '@arg/UpdateUserInfosArgs'
|
||||||
import { klicktippNewsletterStateMiddleware } from '@/middleware/klicktippMiddleware'
|
import { klicktippNewsletterStateMiddleware } from '@/middleware/klicktippMiddleware'
|
||||||
import { OptInType } from '@enum/OptInType'
|
import { OptInType } from '@enum/OptInType'
|
||||||
import { sendResetPasswordEmail as sendResetPasswordEmailMailer } from '@/mailer/sendResetPasswordEmail'
|
import {
|
||||||
import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail'
|
sendAccountActivationEmail,
|
||||||
import { sendAccountMultiRegistrationEmail } from '@/emails/sendEmailVariants'
|
sendAccountMultiRegistrationEmail,
|
||||||
|
sendResetPasswordEmail,
|
||||||
|
} from '@/emails/sendEmailVariants'
|
||||||
import { klicktippSignIn } from '@/apis/KlicktippController'
|
import { klicktippSignIn } from '@/apis/KlicktippController'
|
||||||
import { RIGHTS } from '@/auth/RIGHTS'
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
import { hasElopageBuys } from '@/util/hasElopageBuys'
|
import { hasElopageBuys } from '@/util/hasElopageBuys'
|
||||||
@ -507,11 +510,12 @@ export class UserResolver {
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const emailSent = await sendAccountActivationEmail({
|
const emailSent = await sendAccountActivationEmail({
|
||||||
link: activationLink,
|
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
email,
|
email,
|
||||||
duration: printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME),
|
language,
|
||||||
|
activationLink,
|
||||||
|
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
|
||||||
})
|
})
|
||||||
logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`)
|
logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`)
|
||||||
eventSendConfirmEmail.userId = dbUser.id
|
eventSendConfirmEmail.userId = dbUser.id
|
||||||
@ -570,12 +574,13 @@ export class UserResolver {
|
|||||||
// optInCode = await checkOptInCode(optInCode, user, OptInType.EMAIL_OPT_IN_RESET_PASSWORD)
|
// optInCode = await checkOptInCode(optInCode, user, OptInType.EMAIL_OPT_IN_RESET_PASSWORD)
|
||||||
logger.info(`optInCode for ${email}=${dbUserContact}`)
|
logger.info(`optInCode for ${email}=${dbUserContact}`)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
const emailSent = await sendResetPasswordEmailMailer({
|
const emailSent = await sendResetPasswordEmail({
|
||||||
link: activationLink(dbUserContact.emailVerificationCode),
|
|
||||||
firstName: user.firstName,
|
firstName: user.firstName,
|
||||||
lastName: user.lastName,
|
lastName: user.lastName,
|
||||||
email,
|
email,
|
||||||
duration: printTimeDuration(CONFIG.EMAIL_CODE_VALID_TIME),
|
language: user.language,
|
||||||
|
resetLink: activationLink(dbUserContact.emailVerificationCode),
|
||||||
|
timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME),
|
||||||
})
|
})
|
||||||
|
|
||||||
/* uncomment this, when you need the activation link on the console */
|
/* uncomment this, when you need the activation link on the console */
|
||||||
@ -913,20 +918,3 @@ const canResendOptIn = (optIn: LoginEmailOptIn): boolean => {
|
|||||||
const canEmailResend = (updatedAt: Date): boolean => {
|
const canEmailResend = (updatedAt: Date): boolean => {
|
||||||
return !isTimeExpired(updatedAt, CONFIG.EMAIL_CODE_REQUEST_TIME)
|
return !isTimeExpired(updatedAt, CONFIG.EMAIL_CODE_REQUEST_TIME)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTimeDurationObject = (time: number): { hours?: number; minutes: number } => {
|
|
||||||
if (time > 60) {
|
|
||||||
return {
|
|
||||||
hours: Math.floor(time / 60),
|
|
||||||
minutes: time % 60,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { minutes: time }
|
|
||||||
}
|
|
||||||
|
|
||||||
export const printTimeDuration = (duration: number): string => {
|
|
||||||
const time = getTimeDurationObject(duration)
|
|
||||||
const result = time.minutes > 0 ? `${time.minutes} minutes` : ''
|
|
||||||
if (time.hours) return `${time.hours} hours` + (result !== '' ? ` and ${result}` : '')
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,15 +1,61 @@
|
|||||||
{
|
{
|
||||||
"emails": {
|
"emails": {
|
||||||
|
"addedContributionMessage": {
|
||||||
|
"commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.",
|
||||||
|
"subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag",
|
||||||
|
"toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
||||||
|
},
|
||||||
|
"accountActivation": {
|
||||||
|
"duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen, in dem du deine E-Mail-Adresse eingibst:",
|
||||||
|
"emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.",
|
||||||
|
"pleaseClickLink": "Klicke bitte auf diesen Link, um die Registrierung abzuschließen und dein Gradido-Konto zu aktivieren:",
|
||||||
|
"subject": "Gradido: E-Mail Überprüfung"
|
||||||
|
},
|
||||||
"accountMultiRegistration": {
|
"accountMultiRegistration": {
|
||||||
"emailExists": "Es existiert jedoch zu deiner E-Mail-Adresse schon ein Konto.",
|
"emailExists": "Es existiert jedoch zu deiner E-Mail-Adresse schon ein Konto.",
|
||||||
"emailReused": "Deine E-Mail-Adresse wurde soeben erneut benutzt, um bei Gradido ein Konto zu registrieren.",
|
"emailReused": "deine E-Mail-Adresse wurde soeben erneut benutzt, um bei Gradido ein Konto zu registrieren.",
|
||||||
"helloName": "Hallo {firstName} {lastName}",
|
|
||||||
"ifYouAreNotTheOne": "Wenn du nicht derjenige bist, der sich versucht hat erneut zu registrieren, wende dich bitte an unseren support:",
|
"ifYouAreNotTheOne": "Wenn du nicht derjenige bist, der sich versucht hat erneut zu registrieren, wende dich bitte an unseren support:",
|
||||||
"onForgottenPasswordClickLink": "Klicke bitte auf den folgenden Link, falls du dein Passwort vergessen haben solltest:",
|
"onForgottenPasswordClickLink": "Klicke bitte auf den folgenden Link, falls du dein Passwort vergessen haben solltest:",
|
||||||
"onForgottenPasswordCopyLink": "oder kopiere den obigen Link in dein Browserfenster.",
|
"onForgottenPasswordCopyLink": "oder kopiere den obigen Link in dein Browserfenster.",
|
||||||
"sincerelyYours": "Mit freundlichen Grüßen,",
|
"subject": "Gradido: Erneuter Registrierungsversuch mit deiner E-Mail"
|
||||||
"subject": "Gradido: Erneuter Registrierungsversuch mit deiner E-Mail",
|
},
|
||||||
|
"contributionConfirmed": {
|
||||||
|
"commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt und in deinem Gradido-Konto gutgeschrieben.",
|
||||||
|
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde bestätigt"
|
||||||
|
},
|
||||||
|
"contributionRejected": {
|
||||||
|
"commonGoodContributionRejected": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde von {senderFirstName} {senderLastName} abgelehnt.",
|
||||||
|
"subject": "Gradido: Dein Gemeinwohl-Beitrag wurde abgelehnt",
|
||||||
|
"toSeeContributionsAndMessages": "Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"amountGDD": "Betrag: {amountGDD} GDD",
|
||||||
|
"detailsYouFindOnLinkToYourAccount": "Details zur Transaktion findest du in deinem Gradido-Konto:",
|
||||||
|
"doNotAnswer": "nicht antworten",
|
||||||
|
"helloName": "Hallo {firstName} {lastName},",
|
||||||
|
"linkToYourAccount": "Link zu deinem Konto:",
|
||||||
|
"orCopyLink": "oder kopiere den obigen Link in dein Browserfenster.",
|
||||||
|
"pleaseDoNotReply": "Bitte antworte nicht auf diese E-Mail!",
|
||||||
|
"sincerelyYours": "Liebe Grüße",
|
||||||
"yourGradidoTeam": "dein Gradido-Team"
|
"yourGradidoTeam": "dein Gradido-Team"
|
||||||
|
},
|
||||||
|
"resetPassword": {
|
||||||
|
"duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen, in dem du deine E-Mail-Adresse eingibst:",
|
||||||
|
"pleaseClickLink": "Wenn du es warst, klicke bitte auf den Link:",
|
||||||
|
"subject": "Gradido: Passwort zurücksetzen",
|
||||||
|
"youOrSomeoneResetPassword": "du, oder jemand anderes, hast für dieses Konto ein Zurücksetzen des Passworts angefordert."
|
||||||
|
},
|
||||||
|
"transactionLinkRedeemed": {
|
||||||
|
"hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) hat soeben deinen Link eingelöst.",
|
||||||
|
"memo": "Memo: {transactionMemo}",
|
||||||
|
"subject": "Gradido: Dein Gradido-Link wurde eingelöst"
|
||||||
|
},
|
||||||
|
"transactionReceived": {
|
||||||
|
"haveReceivedAmountGDDFrom": "du hast soeben {transactionAmount} GDD von {senderFirstName} {senderLastName} ({senderEmail}) erhalten.",
|
||||||
|
"subject": "Gradido: Du hast Gradidos erhalten"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"decimalSeparator": ","
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,61 @@
|
|||||||
{
|
{
|
||||||
"emails": {
|
"emails": {
|
||||||
"accountMultiRegistration": {
|
"addedContributionMessage": {
|
||||||
|
"commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.",
|
||||||
|
"subject": "Gradido: Message about your common good contribution",
|
||||||
|
"toSeeAndAnswerMessage": "To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!"
|
||||||
|
},
|
||||||
|
"accountActivation": {
|
||||||
|
"duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here by entering your email address:",
|
||||||
|
"emailRegistered": "Your email address has just been registered with Gradido.",
|
||||||
|
"pleaseClickLink": "Please click on this link to complete the registration and activate your Gradido account:",
|
||||||
|
"subject": "Gradido: Email Verification"
|
||||||
|
},
|
||||||
|
"accountMultiRegistration": {
|
||||||
"emailExists": "However, an account already exists for your email address.",
|
"emailExists": "However, an account already exists for your email address.",
|
||||||
"emailReused": "Your email address has just been used again to register an account with Gradido.",
|
"emailReused": "Your email address has just been used again to register an account with Gradido.",
|
||||||
"helloName": "Hello {firstName} {lastName}",
|
|
||||||
"ifYouAreNotTheOne": "If you are not the one who tried to register again, please contact our support:",
|
"ifYouAreNotTheOne": "If you are not the one who tried to register again, please contact our support:",
|
||||||
"onForgottenPasswordClickLink": "Please click on the following link if you have forgotten your password:",
|
"onForgottenPasswordClickLink": "Please click on the following link if you have forgotten your password:",
|
||||||
"onForgottenPasswordCopyLink": "or copy the link above into your browser window.",
|
"onForgottenPasswordCopyLink": "or copy the link above into your browser window.",
|
||||||
"sincerelyYours": "Sincerely yours,",
|
"subject": "Gradido: Try To Register Again With Your Email"
|
||||||
"subject": "Gradido: Try To Register Again With Your Email",
|
},
|
||||||
|
"contributionConfirmed": {
|
||||||
|
"commonGoodContributionConfirmed": "Your public good contribution “{contributionMemo}” has just been confirmed by {senderFirstName} {senderLastName} and credited to your Gradido account.",
|
||||||
|
"subject": "Gradido: Your common good contribution was confirmed"
|
||||||
|
},
|
||||||
|
"contributionRejected": {
|
||||||
|
"commonGoodContributionRejected": "Your public good contribution “{contributionMemo}” was rejected by {senderFirstName} {senderLastName}.",
|
||||||
|
"subject": "Gradido: Your common good contribution was rejected",
|
||||||
|
"toSeeContributionsAndMessages": "To see your common good contributions and related messages, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"amountGDD": "Amount: {amountGDD} GDD",
|
||||||
|
"detailsYouFindOnLinkToYourAccount": "You can find transaction details in your Gradido account:",
|
||||||
|
"doNotAnswer": "do not answer",
|
||||||
|
"helloName": "Hello {firstName} {lastName}",
|
||||||
|
"linkToYourAccount": "Link to your account:",
|
||||||
|
"orCopyLink": "or copy the link above into your browser window.",
|
||||||
|
"pleaseDoNotReply": "Please do not reply to this email!",
|
||||||
|
"sincerelyYours": "Kind regards,",
|
||||||
"yourGradidoTeam": "your Gradido team"
|
"yourGradidoTeam": "your Gradido team"
|
||||||
}
|
},
|
||||||
}
|
"resetPassword": {
|
||||||
}
|
"duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here by entering your email address:",
|
||||||
|
"pleaseClickLink": "If it was you, please click on the link:",
|
||||||
|
"subject": "Gradido: Reset password",
|
||||||
|
"youOrSomeoneResetPassword": "You, or someone else, requested a password reset for this account."
|
||||||
|
},
|
||||||
|
"transactionLinkRedeemed": {
|
||||||
|
"hasRedeemedYourLink": "{senderFirstName} {senderLastName} ({senderEmail}) has just redeemed your link.",
|
||||||
|
"memo": "Memo: {transactionMemo}",
|
||||||
|
"subject": "Gradido: Your Gradido link has been redeemed"
|
||||||
|
},
|
||||||
|
"transactionReceived": {
|
||||||
|
"haveReceivedAmountGDDFrom": "You have just received {transactionAmount} GDD from {senderFirstName} {senderLastName} ({senderEmail}).",
|
||||||
|
"subject": "Gradido: You have received Gradidos"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"decimalSeparator": "."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
import { sendAccountActivationEmail } from './sendAccountActivationEmail'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
|
|
||||||
jest.mock('./sendEMail', () => {
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
sendEMail: jest.fn(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sendAccountActivationEmail', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await sendAccountActivationEmail({
|
|
||||||
link: 'activationLink',
|
|
||||||
firstName: 'Peter',
|
|
||||||
lastName: 'Lustig',
|
|
||||||
email: 'peter@lustig.de',
|
|
||||||
duration: '23 hours and 30 minutes',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls sendEMail', () => {
|
|
||||||
expect(sendEMail).toBeCalledWith({
|
|
||||||
to: `Peter Lustig <peter@lustig.de>`,
|
|
||||||
subject: 'Gradido: E-Mail Überprüfung',
|
|
||||||
text:
|
|
||||||
expect.stringContaining('Hallo Peter Lustig') &&
|
|
||||||
expect.stringContaining('activationLink') &&
|
|
||||||
expect.stringContaining('23 Stunden und 30 Minuten'),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import { accountActivation } from './text/accountActivation'
|
|
||||||
import CONFIG from '@/config'
|
|
||||||
|
|
||||||
export const sendAccountActivationEmail = (data: {
|
|
||||||
link: string
|
|
||||||
firstName: string
|
|
||||||
lastName: string
|
|
||||||
email: string
|
|
||||||
duration: string
|
|
||||||
}): Promise<boolean> => {
|
|
||||||
return sendEMail({
|
|
||||||
to: `${data.firstName} ${data.lastName} <${data.email}>`,
|
|
||||||
subject: accountActivation.de.subject,
|
|
||||||
text: accountActivation.de.text({ ...data, resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD }),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
import { sendAddedContributionMessageEmail } from './sendAddedContributionMessageEmail'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
|
|
||||||
jest.mock('./sendEMail', () => {
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
sendEMail: jest.fn(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sendAddedContributionMessageEmail', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await sendAddedContributionMessageEmail({
|
|
||||||
senderFirstName: 'Peter',
|
|
||||||
senderLastName: 'Lustig',
|
|
||||||
recipientFirstName: 'Bibi',
|
|
||||||
recipientLastName: 'Bloxberg',
|
|
||||||
recipientEmail: 'bibi@bloxberg.de',
|
|
||||||
senderEmail: 'peter@lustig.de',
|
|
||||||
contributionMemo: 'Vielen herzlichen Dank für den neuen Hexenbesen!',
|
|
||||||
message: 'Was für ein Besen ist es geworden?',
|
|
||||||
overviewURL: 'http://localhost/overview',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls sendEMail', () => {
|
|
||||||
expect(sendEMail).toBeCalledWith({
|
|
||||||
to: `Bibi Bloxberg <bibi@bloxberg.de>`,
|
|
||||||
subject: 'Nachricht zu deinem Gemeinwohl-Beitrag',
|
|
||||||
text:
|
|
||||||
expect.stringContaining('Hallo Bibi Bloxberg') &&
|
|
||||||
expect.stringContaining('Peter Lustig') &&
|
|
||||||
expect.stringContaining(
|
|
||||||
'du hast zu deinem Gemeinwohl-Beitrag "Vielen herzlichen Dank für den neuen Hexenbesen!" eine Nachricht von Peter Lustig erhalten.',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining('Was für ein Besen ist es geworden?') &&
|
|
||||||
expect.stringContaining('http://localhost/overview'),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import { backendLogger as logger } from '@/server/logger'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import { contributionMessageReceived } from './text/contributionMessageReceived'
|
|
||||||
|
|
||||||
export const sendAddedContributionMessageEmail = (data: {
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
recipientEmail: string
|
|
||||||
senderEmail: string
|
|
||||||
contributionMemo: string
|
|
||||||
message: string
|
|
||||||
overviewURL: string
|
|
||||||
}): Promise<boolean> => {
|
|
||||||
logger.info(
|
|
||||||
`sendEmail(): to=${data.recipientFirstName} ${data.recipientLastName} <${data.recipientEmail}>,
|
|
||||||
subject=${contributionMessageReceived.de.subject},
|
|
||||||
text=${contributionMessageReceived.de.text(data)}`,
|
|
||||||
)
|
|
||||||
return sendEMail({
|
|
||||||
to: `${data.recipientFirstName} ${data.recipientLastName} <${data.recipientEmail}>`,
|
|
||||||
subject: contributionMessageReceived.de.subject,
|
|
||||||
text: contributionMessageReceived.de.text(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
|
||||||
import { sendContributionConfirmedEmail } from './sendContributionConfirmedEmail'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
|
|
||||||
jest.mock('./sendEMail', () => {
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
sendEMail: jest.fn(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sendContributionConfirmedEmail', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await sendContributionConfirmedEmail({
|
|
||||||
senderFirstName: 'Peter',
|
|
||||||
senderLastName: 'Lustig',
|
|
||||||
recipientFirstName: 'Bibi',
|
|
||||||
recipientLastName: 'Bloxberg',
|
|
||||||
recipientEmail: 'bibi@bloxberg.de',
|
|
||||||
contributionMemo: 'Vielen herzlichen Dank für den neuen Hexenbesen!',
|
|
||||||
contributionAmount: new Decimal(200.0),
|
|
||||||
overviewURL: 'http://localhost/overview',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls sendEMail', () => {
|
|
||||||
expect(sendEMail).toBeCalledWith({
|
|
||||||
to: 'Bibi Bloxberg <bibi@bloxberg.de>',
|
|
||||||
subject: 'Dein Gemeinwohl-Beitrag wurde bestätigt',
|
|
||||||
text:
|
|
||||||
expect.stringContaining('Hallo Bibi Bloxberg') &&
|
|
||||||
expect.stringContaining(
|
|
||||||
'dein Gemeinwohl-Beitrag "Vielen herzlichen Dank für den neuen Hexenbesen!" wurde soeben von Peter Lustig bestätigt und in deinem Gradido-Konto gutgeschrieben.',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining('Betrag: 200,00 GDD') &&
|
|
||||||
expect.stringContaining('Link zu deinem Konto: http://localhost/overview'),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import { backendLogger as logger } from '@/server/logger'
|
|
||||||
import Decimal from 'decimal.js-light'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import { contributionConfirmed } from './text/contributionConfirmed'
|
|
||||||
|
|
||||||
export const sendContributionConfirmedEmail = (data: {
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
recipientEmail: string
|
|
||||||
contributionMemo: string
|
|
||||||
contributionAmount: Decimal
|
|
||||||
overviewURL: string
|
|
||||||
}): Promise<boolean> => {
|
|
||||||
logger.info(
|
|
||||||
`sendEmail(): to=${data.recipientFirstName} ${data.recipientLastName} <${data.recipientEmail}>,
|
|
||||||
subject=${contributionConfirmed.de.subject},
|
|
||||||
text=${contributionConfirmed.de.text(data)}`,
|
|
||||||
)
|
|
||||||
return sendEMail({
|
|
||||||
to: `${data.recipientFirstName} ${data.recipientLastName} <${data.recipientEmail}>`,
|
|
||||||
subject: contributionConfirmed.de.subject,
|
|
||||||
text: contributionConfirmed.de.text(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
|
||||||
import { sendContributionRejectedEmail } from './sendContributionRejectedEmail'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
|
|
||||||
jest.mock('./sendEMail', () => {
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
sendEMail: jest.fn(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sendContributionConfirmedEmail', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await sendContributionRejectedEmail({
|
|
||||||
senderFirstName: 'Peter',
|
|
||||||
senderLastName: 'Lustig',
|
|
||||||
recipientFirstName: 'Bibi',
|
|
||||||
recipientLastName: 'Bloxberg',
|
|
||||||
recipientEmail: 'bibi@bloxberg.de',
|
|
||||||
contributionMemo: 'Vielen herzlichen Dank für den neuen Hexenbesen!',
|
|
||||||
contributionAmount: new Decimal(200.0),
|
|
||||||
overviewURL: 'http://localhost/overview',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls sendEMail', () => {
|
|
||||||
expect(sendEMail).toBeCalledWith({
|
|
||||||
to: 'Bibi Bloxberg <bibi@bloxberg.de>',
|
|
||||||
subject: 'Dein Gemeinwohl-Beitrag wurde abgelehnt',
|
|
||||||
text:
|
|
||||||
expect.stringContaining('Hallo Bibi Bloxberg') &&
|
|
||||||
expect.stringContaining(
|
|
||||||
'dein Gemeinwohl-Beitrag "Vielen herzlichen Dank für den neuen Hexenbesen!" wurde von Peter Lustig abgelehnt.',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining('Link zu deinem Konto: http://localhost/overview'),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
import { backendLogger as logger } from '@/server/logger'
|
|
||||||
import Decimal from 'decimal.js-light'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import { contributionRejected } from './text/contributionRejected'
|
|
||||||
|
|
||||||
export const sendContributionRejectedEmail = (data: {
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
recipientEmail: string
|
|
||||||
contributionMemo: string
|
|
||||||
contributionAmount: Decimal
|
|
||||||
overviewURL: string
|
|
||||||
}): Promise<boolean> => {
|
|
||||||
logger.info(
|
|
||||||
`sendEmail(): to=${data.recipientFirstName} ${data.recipientLastName} <${data.recipientEmail}>,
|
|
||||||
subject=${contributionRejected.de.subject},
|
|
||||||
text=${contributionRejected.de.text(data)}`,
|
|
||||||
)
|
|
||||||
return sendEMail({
|
|
||||||
to: `${data.recipientFirstName} ${data.recipientLastName} <${data.recipientEmail}>`,
|
|
||||||
subject: contributionRejected.de.subject,
|
|
||||||
text: contributionRejected.de.text(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import { createTransport } from 'nodemailer'
|
|
||||||
import CONFIG from '@/config'
|
|
||||||
|
|
||||||
import { logger } from '@test/testSetup'
|
|
||||||
|
|
||||||
CONFIG.EMAIL = false
|
|
||||||
CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL'
|
|
||||||
CONFIG.EMAIL_SMTP_PORT = '1234'
|
|
||||||
CONFIG.EMAIL_USERNAME = 'user'
|
|
||||||
CONFIG.EMAIL_PASSWORD = 'pwd'
|
|
||||||
|
|
||||||
jest.mock('nodemailer', () => {
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
createTransport: jest.fn(() => {
|
|
||||||
return {
|
|
||||||
sendMail: jest.fn(() => {
|
|
||||||
return {
|
|
||||||
messageId: 'message',
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sendEMail', () => {
|
|
||||||
let result: boolean
|
|
||||||
describe('config email is false', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
result = await sendEMail({
|
|
||||||
to: 'receiver@mail.org',
|
|
||||||
cc: 'support@gradido.net',
|
|
||||||
subject: 'Subject',
|
|
||||||
text: 'Text text text',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('logs warning', () => {
|
|
||||||
expect(logger.info).toBeCalledWith('Emails are disabled via config...')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns false', () => {
|
|
||||||
expect(result).toBeFalsy()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('config email is true', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
CONFIG.EMAIL = true
|
|
||||||
result = await sendEMail({
|
|
||||||
to: 'receiver@mail.org',
|
|
||||||
cc: 'support@gradido.net',
|
|
||||||
subject: 'Subject',
|
|
||||||
text: 'Text text text',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls the transporter', () => {
|
|
||||||
expect(createTransport).toBeCalledWith({
|
|
||||||
host: 'EMAIL_SMTP_URL',
|
|
||||||
port: 1234,
|
|
||||||
secure: false,
|
|
||||||
requireTLS: true,
|
|
||||||
auth: {
|
|
||||||
user: 'user',
|
|
||||||
pass: 'pwd',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls sendMail of transporter', () => {
|
|
||||||
expect((createTransport as jest.Mock).mock.results[0].value.sendMail).toBeCalledWith({
|
|
||||||
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
|
|
||||||
to: 'receiver@mail.org',
|
|
||||||
cc: 'support@gradido.net',
|
|
||||||
subject: 'Subject',
|
|
||||||
text: 'Text text text',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns true', () => {
|
|
||||||
expect(result).toBeTruthy()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('with email EMAIL_TEST_MODUS true', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
jest.clearAllMocks()
|
|
||||||
CONFIG.EMAIL = true
|
|
||||||
CONFIG.EMAIL_TEST_MODUS = true
|
|
||||||
result = await sendEMail({
|
|
||||||
to: 'receiver@mail.org',
|
|
||||||
cc: 'support@gradido.net',
|
|
||||||
subject: 'Subject',
|
|
||||||
text: 'Text text text',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls sendMail of transporter with faked to', () => {
|
|
||||||
expect((createTransport as jest.Mock).mock.results[0].value.sendMail).toBeCalledWith({
|
|
||||||
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
|
|
||||||
to: CONFIG.EMAIL_TEST_RECEIVER,
|
|
||||||
cc: 'support@gradido.net',
|
|
||||||
subject: 'Subject',
|
|
||||||
text: 'Text text text',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
import { backendLogger as logger } from '@/server/logger'
|
|
||||||
import { createTransport } from 'nodemailer'
|
|
||||||
|
|
||||||
import CONFIG from '@/config'
|
|
||||||
|
|
||||||
export const sendEMail = async (emailDef: {
|
|
||||||
to: string
|
|
||||||
cc?: string
|
|
||||||
subject: string
|
|
||||||
text: string
|
|
||||||
}): Promise<boolean> => {
|
|
||||||
logger.info(
|
|
||||||
`send Email: to=${emailDef.to}` +
|
|
||||||
(emailDef.cc ? `, cc=${emailDef.cc}` : '') +
|
|
||||||
`, subject=${emailDef.subject}, text=${emailDef.text}`,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!CONFIG.EMAIL) {
|
|
||||||
logger.info(`Emails are disabled via config...`)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (CONFIG.EMAIL_TEST_MODUS) {
|
|
||||||
logger.info(
|
|
||||||
`Testmodus=ON: change receiver from ${emailDef.to} to ${CONFIG.EMAIL_TEST_RECEIVER}`,
|
|
||||||
)
|
|
||||||
emailDef.to = CONFIG.EMAIL_TEST_RECEIVER
|
|
||||||
}
|
|
||||||
const transporter = createTransport({
|
|
||||||
host: CONFIG.EMAIL_SMTP_URL,
|
|
||||||
port: Number(CONFIG.EMAIL_SMTP_PORT),
|
|
||||||
secure: false, // true for 465, false for other ports
|
|
||||||
requireTLS: true,
|
|
||||||
auth: {
|
|
||||||
user: CONFIG.EMAIL_USERNAME,
|
|
||||||
pass: CONFIG.EMAIL_PASSWORD,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const info = await transporter.sendMail({
|
|
||||||
...emailDef,
|
|
||||||
from: `Gradido (nicht antworten) <${CONFIG.EMAIL_SENDER}>`,
|
|
||||||
})
|
|
||||||
if (!info.messageId) {
|
|
||||||
logger.error('error sending notification email, but transaction succeed')
|
|
||||||
throw new Error('error sending notification email, but transaction succeed')
|
|
||||||
}
|
|
||||||
logger.info('send Email successfully.')
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
import { sendResetPasswordEmail } from './sendResetPasswordEmail'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
|
|
||||||
jest.mock('./sendEMail', () => {
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
sendEMail: jest.fn(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sendResetPasswordEmail', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await sendResetPasswordEmail({
|
|
||||||
link: 'resetLink',
|
|
||||||
firstName: 'Peter',
|
|
||||||
lastName: 'Lustig',
|
|
||||||
email: 'peter@lustig.de',
|
|
||||||
duration: '23 hours and 30 minutes',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls sendEMail', () => {
|
|
||||||
expect(sendEMail).toBeCalledWith({
|
|
||||||
to: `Peter Lustig <peter@lustig.de>`,
|
|
||||||
subject: 'Gradido: Passwort zurücksetzen',
|
|
||||||
text:
|
|
||||||
expect.stringContaining('Hallo Peter Lustig') &&
|
|
||||||
expect.stringContaining('resetLink') &&
|
|
||||||
expect.stringContaining('23 Stunden und 30 Minuten'),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import { resetPassword } from './text/resetPassword'
|
|
||||||
import CONFIG from '@/config'
|
|
||||||
|
|
||||||
export const sendResetPasswordEmail = (data: {
|
|
||||||
link: string
|
|
||||||
firstName: string
|
|
||||||
lastName: string
|
|
||||||
email: string
|
|
||||||
duration: string
|
|
||||||
}): Promise<boolean> => {
|
|
||||||
return sendEMail({
|
|
||||||
to: `${data.firstName} ${data.lastName} <${data.email}>`,
|
|
||||||
subject: resetPassword.de.subject,
|
|
||||||
text: resetPassword.de.text({ ...data, resendLink: CONFIG.EMAIL_LINK_FORGOTPASSWORD }),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import Decimal from 'decimal.js-light'
|
|
||||||
import { sendTransactionLinkRedeemedEmail } from './sendTransactionLinkRedeemed'
|
|
||||||
|
|
||||||
jest.mock('./sendEMail', () => {
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
sendEMail: jest.fn(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sendTransactionLinkRedeemedEmail', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await sendTransactionLinkRedeemedEmail({
|
|
||||||
email: 'bibi@bloxberg.de',
|
|
||||||
senderFirstName: 'Peter',
|
|
||||||
senderLastName: 'Lustig',
|
|
||||||
recipientFirstName: 'Bibi',
|
|
||||||
recipientLastName: 'Bloxberg',
|
|
||||||
senderEmail: 'peter@lustig.de',
|
|
||||||
amount: new Decimal(42.0),
|
|
||||||
memo: 'Vielen Dank dass Du dabei bist',
|
|
||||||
overviewURL: 'http://localhost/overview',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls sendEMail', () => {
|
|
||||||
expect(sendEMail).toBeCalledWith({
|
|
||||||
to: `Bibi Bloxberg <bibi@bloxberg.de>`,
|
|
||||||
subject: 'Gradido-Link wurde eingelöst',
|
|
||||||
text:
|
|
||||||
expect.stringContaining('Hallo Bibi Bloxberg') &&
|
|
||||||
expect.stringContaining(
|
|
||||||
'Peter Lustig (peter@lustig.de) hat soeben deinen Link eingelöst.',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining('Betrag: 42,00 GDD,') &&
|
|
||||||
expect.stringContaining('Memo: Vielen Dank dass Du dabei bist') &&
|
|
||||||
expect.stringContaining(
|
|
||||||
'Details zur Transaktion findest du in deinem Gradido-Konto: http://localhost/overview',
|
|
||||||
) &&
|
|
||||||
expect.stringContaining('Bitte antworte nicht auf diese E-Mail!'),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
import { backendLogger as logger } from '@/server/logger'
|
|
||||||
import Decimal from 'decimal.js-light'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import { transactionLinkRedeemed } from './text/transactionLinkRedeemed'
|
|
||||||
|
|
||||||
export const sendTransactionLinkRedeemedEmail = (data: {
|
|
||||||
email: string
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
senderEmail: string
|
|
||||||
amount: Decimal
|
|
||||||
memo: string
|
|
||||||
overviewURL: string
|
|
||||||
}): Promise<boolean> => {
|
|
||||||
logger.info(
|
|
||||||
`sendEmail(): to=${data.recipientFirstName} ${data.recipientLastName},
|
|
||||||
<${data.email}>,
|
|
||||||
subject=${transactionLinkRedeemed.de.subject},
|
|
||||||
text=${transactionLinkRedeemed.de.text(data)}`,
|
|
||||||
)
|
|
||||||
return sendEMail({
|
|
||||||
to: `${data.recipientFirstName} ${data.recipientLastName} <${data.email}>`,
|
|
||||||
subject: transactionLinkRedeemed.de.subject,
|
|
||||||
text: transactionLinkRedeemed.de.text(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@ -1,38 +0,0 @@
|
|||||||
import { sendTransactionReceivedEmail } from './sendTransactionReceivedEmail'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import Decimal from 'decimal.js-light'
|
|
||||||
|
|
||||||
jest.mock('./sendEMail', () => {
|
|
||||||
return {
|
|
||||||
__esModule: true,
|
|
||||||
sendEMail: jest.fn(),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sendTransactionReceivedEmail', () => {
|
|
||||||
beforeEach(async () => {
|
|
||||||
await sendTransactionReceivedEmail({
|
|
||||||
senderFirstName: 'Bibi',
|
|
||||||
senderLastName: 'Bloxberg',
|
|
||||||
recipientFirstName: 'Peter',
|
|
||||||
recipientLastName: 'Lustig',
|
|
||||||
email: 'peter@lustig.de',
|
|
||||||
senderEmail: 'bibi@bloxberg.de',
|
|
||||||
amount: new Decimal(42.0),
|
|
||||||
overviewURL: 'http://localhost/overview',
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls sendEMail', () => {
|
|
||||||
expect(sendEMail).toBeCalledWith({
|
|
||||||
to: `Peter Lustig <peter@lustig.de>`,
|
|
||||||
subject: 'Du hast Gradidos erhalten',
|
|
||||||
text:
|
|
||||||
expect.stringContaining('Hallo Peter Lustig') &&
|
|
||||||
expect.stringContaining('42,00 GDD') &&
|
|
||||||
expect.stringContaining('Bibi Bloxberg') &&
|
|
||||||
expect.stringContaining('(bibi@bloxberg.de)') &&
|
|
||||||
expect.stringContaining('http://localhost/overview'),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
import { backendLogger as logger } from '@/server/logger'
|
|
||||||
import Decimal from 'decimal.js-light'
|
|
||||||
import { sendEMail } from './sendEMail'
|
|
||||||
import { transactionReceived } from './text/transactionReceived'
|
|
||||||
|
|
||||||
export const sendTransactionReceivedEmail = (data: {
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
email: string
|
|
||||||
senderEmail: string
|
|
||||||
amount: Decimal
|
|
||||||
overviewURL: string
|
|
||||||
}): Promise<boolean> => {
|
|
||||||
logger.info(
|
|
||||||
`sendEmail(): to=${data.recipientFirstName} ${data.recipientLastName},
|
|
||||||
<${data.email}>,
|
|
||||||
subject=${transactionReceived.de.subject},
|
|
||||||
text=${transactionReceived.de.text(data)}`,
|
|
||||||
)
|
|
||||||
return sendEMail({
|
|
||||||
to: `${data.recipientFirstName} ${data.recipientLastName} <${data.email}>`,
|
|
||||||
subject: transactionReceived.de.subject,
|
|
||||||
text: transactionReceived.de.text(data),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@ -1,32 +0,0 @@
|
|||||||
export const accountActivation = {
|
|
||||||
de: {
|
|
||||||
subject: 'Gradido: E-Mail Überprüfung',
|
|
||||||
text: (data: {
|
|
||||||
link: string
|
|
||||||
firstName: string
|
|
||||||
lastName: string
|
|
||||||
email: string
|
|
||||||
duration: string
|
|
||||||
resendLink: string
|
|
||||||
}): string =>
|
|
||||||
`Hallo ${data.firstName} ${data.lastName},
|
|
||||||
|
|
||||||
Deine E-Mail-Adresse wurde soeben bei Gradido registriert.
|
|
||||||
|
|
||||||
Klicke bitte auf diesen Link, um die Registrierung abzuschließen und dein Gradido-Konto zu aktivieren:
|
|
||||||
${data.link}
|
|
||||||
oder kopiere den obigen Link in dein Browserfenster.
|
|
||||||
|
|
||||||
Der Link hat eine Gültigkeit von ${data.duration
|
|
||||||
.replace('hours', 'Stunden')
|
|
||||||
.replace('minutes', 'Minuten')
|
|
||||||
.replace(
|
|
||||||
' and ',
|
|
||||||
' und ',
|
|
||||||
)}. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen, in dem du deine E-Mail-Adresse eingibst:
|
|
||||||
${data.resendLink}
|
|
||||||
|
|
||||||
Mit freundlichen Grüßen,
|
|
||||||
dein Gradido-Team`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
export const accountMultiRegistration = {
|
|
||||||
de: {
|
|
||||||
subject: 'Gradido: Erneuter Registrierungsversuch mit deiner E-Mail',
|
|
||||||
text: (data: {
|
|
||||||
firstName: string
|
|
||||||
lastName: string
|
|
||||||
email: string
|
|
||||||
resendLink: string
|
|
||||||
}): string =>
|
|
||||||
`Hallo ${data.firstName} ${data.lastName},
|
|
||||||
|
|
||||||
Deine E-Mail-Adresse wurde soeben erneut benutzt, um bei Gradido ein Konto zu registrieren.
|
|
||||||
Es existiert jedoch zu deiner E-Mail-Adresse schon ein Konto.
|
|
||||||
|
|
||||||
Klicke bitte auf den folgenden Link, falls du dein Passwort vergessen haben solltest:
|
|
||||||
${data.resendLink}
|
|
||||||
oder kopiere den obigen Link in dein Browserfenster.
|
|
||||||
|
|
||||||
Wenn du nicht derjenige bist, der sich versucht hat erneut zu registrieren, wende dich bitte an unseren support:
|
|
||||||
https://gradido.net/de/contact/
|
|
||||||
|
|
||||||
Mit freundlichen Grüßen,
|
|
||||||
dein Gradido-Team`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
|
||||||
|
|
||||||
export const contributionConfirmed = {
|
|
||||||
de: {
|
|
||||||
subject: 'Dein Gemeinwohl-Beitrag wurde bestätigt',
|
|
||||||
text: (data: {
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
contributionMemo: string
|
|
||||||
contributionAmount: Decimal
|
|
||||||
overviewURL: string
|
|
||||||
}): string =>
|
|
||||||
`Hallo ${data.recipientFirstName} ${data.recipientLastName},
|
|
||||||
|
|
||||||
dein Gemeinwohl-Beitrag "${data.contributionMemo}" wurde soeben von ${data.senderFirstName} ${
|
|
||||||
data.senderLastName
|
|
||||||
} bestätigt und in deinem Gradido-Konto gutgeschrieben.
|
|
||||||
|
|
||||||
Betrag: ${data.contributionAmount.toFixed(2).replace('.', ',')} GDD
|
|
||||||
|
|
||||||
Link zu deinem Konto: ${data.overviewURL}
|
|
||||||
|
|
||||||
Bitte antworte nicht auf diese E-Mail!
|
|
||||||
|
|
||||||
Liebe Grüße
|
|
||||||
dein Gradido-Team`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
export const contributionMessageReceived = {
|
|
||||||
de: {
|
|
||||||
subject: 'Nachricht zu deinem Gemeinwohl-Beitrag',
|
|
||||||
text: (data: {
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
recipientEmail: string
|
|
||||||
senderEmail: string
|
|
||||||
contributionMemo: string
|
|
||||||
message: string
|
|
||||||
overviewURL: string
|
|
||||||
}): string =>
|
|
||||||
`Hallo ${data.recipientFirstName} ${data.recipientLastName},
|
|
||||||
|
|
||||||
du hast zu deinem Gemeinwohl-Beitrag "${data.contributionMemo}" eine Nachricht von ${data.senderFirstName} ${data.senderLastName} erhalten.
|
|
||||||
|
|
||||||
Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü "Gemeinschaft" auf den Tab "Meine Beiträge zum Gemeinwohl"!
|
|
||||||
|
|
||||||
Link zu deinem Konto: ${data.overviewURL}
|
|
||||||
|
|
||||||
Bitte antworte nicht auf diese E-Mail!
|
|
||||||
|
|
||||||
Liebe Grüße
|
|
||||||
dein Gradido-Team`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
|
||||||
|
|
||||||
export const contributionRejected = {
|
|
||||||
de: {
|
|
||||||
subject: 'Dein Gemeinwohl-Beitrag wurde abgelehnt',
|
|
||||||
text: (data: {
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
contributionMemo: string
|
|
||||||
contributionAmount: Decimal
|
|
||||||
overviewURL: string
|
|
||||||
}): string =>
|
|
||||||
`Hallo ${data.recipientFirstName} ${data.recipientLastName},
|
|
||||||
|
|
||||||
dein Gemeinwohl-Beitrag "${data.contributionMemo}" wurde von ${data.senderFirstName} ${data.senderLastName} abgelehnt.
|
|
||||||
|
|
||||||
Um deine Gemeinwohl-Beiträge und dazugehörige Nachrichten zu sehen, gehe in deinem Gradido-Konto ins Menü "Gemeinschaft" auf den Tab "Meine Beiträge zum Gemeinwohl"!
|
|
||||||
|
|
||||||
Link zu deinem Konto: ${data.overviewURL}
|
|
||||||
|
|
||||||
Bitte antworte nicht auf diese E-Mail!
|
|
||||||
|
|
||||||
Liebe Grüße
|
|
||||||
dein Gradido-Team`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
export const resetPassword = {
|
|
||||||
de: {
|
|
||||||
subject: 'Gradido: Passwort zurücksetzen',
|
|
||||||
text: (data: {
|
|
||||||
link: string
|
|
||||||
firstName: string
|
|
||||||
lastName: string
|
|
||||||
email: string
|
|
||||||
duration: string
|
|
||||||
resendLink: string
|
|
||||||
}): string =>
|
|
||||||
`Hallo ${data.firstName} ${data.lastName},
|
|
||||||
|
|
||||||
Du oder jemand anderes hat für dieses Konto ein Zurücksetzen des Passworts angefordert.
|
|
||||||
Wenn du es warst, klicke bitte auf den Link: ${data.link}
|
|
||||||
oder kopiere den obigen Link in Dein Browserfenster.
|
|
||||||
|
|
||||||
Der Link hat eine Gültigkeit von ${data.duration
|
|
||||||
.replace('hours', 'Stunden')
|
|
||||||
.replace('minutes', 'Minuten')
|
|
||||||
.replace(
|
|
||||||
' and ',
|
|
||||||
' und ',
|
|
||||||
)}. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen, in dem du deine E-Mail-Adresse eingibst:
|
|
||||||
${data.resendLink}
|
|
||||||
|
|
||||||
Mit freundlichen Grüßen,
|
|
||||||
dein Gradido-Team`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
|
||||||
|
|
||||||
export const transactionLinkRedeemed = {
|
|
||||||
de: {
|
|
||||||
subject: 'Gradido-Link wurde eingelöst',
|
|
||||||
text: (data: {
|
|
||||||
email: string
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
senderEmail: string
|
|
||||||
amount: Decimal
|
|
||||||
memo: string
|
|
||||||
overviewURL: string
|
|
||||||
}): string =>
|
|
||||||
`Hallo ${data.recipientFirstName} ${data.recipientLastName},
|
|
||||||
|
|
||||||
${data.senderFirstName} ${data.senderLastName} (${
|
|
||||||
data.senderEmail
|
|
||||||
}) hat soeben deinen Link eingelöst.
|
|
||||||
|
|
||||||
Betrag: ${data.amount.toFixed(2).replace('.', ',')} GDD,
|
|
||||||
Memo: ${data.memo}
|
|
||||||
|
|
||||||
Details zur Transaktion findest du in deinem Gradido-Konto: ${data.overviewURL}
|
|
||||||
|
|
||||||
Bitte antworte nicht auf diese E-Mail!
|
|
||||||
|
|
||||||
Liebe Grüße
|
|
||||||
dein Gradido-Team`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
|
||||||
|
|
||||||
export const transactionReceived = {
|
|
||||||
de: {
|
|
||||||
subject: 'Du hast Gradidos erhalten',
|
|
||||||
text: (data: {
|
|
||||||
senderFirstName: string
|
|
||||||
senderLastName: string
|
|
||||||
recipientFirstName: string
|
|
||||||
recipientLastName: string
|
|
||||||
email: string
|
|
||||||
senderEmail: string
|
|
||||||
amount: Decimal
|
|
||||||
overviewURL: string
|
|
||||||
}): string =>
|
|
||||||
`Hallo ${data.recipientFirstName} ${data.recipientLastName},
|
|
||||||
|
|
||||||
du hast soeben ${data.amount.toFixed(2).replace('.', ',')} GDD von ${data.senderFirstName} ${
|
|
||||||
data.senderLastName
|
|
||||||
} (${data.senderEmail}) erhalten.
|
|
||||||
|
|
||||||
Details zur Transaktion findest du in deinem Gradido-Konto: ${data.overviewURL}
|
|
||||||
|
|
||||||
Bitte antworte nicht auf diese E-Mail!
|
|
||||||
|
|
||||||
Liebe Grüße
|
|
||||||
dein Gradido-Team`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
16
backend/src/util/time.ts
Normal file
16
backend/src/util/time.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const getTimeDurationObject = (time: number): { hours?: number; minutes: number } => {
|
||||||
|
if (time > 60) {
|
||||||
|
return {
|
||||||
|
hours: Math.floor(time / 60),
|
||||||
|
minutes: time % 60,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { minutes: time }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const printTimeDuration = (duration: number): string => {
|
||||||
|
const time = getTimeDurationObject(duration)
|
||||||
|
const result = time.minutes > 0 ? `${time.minutes} minutes` : ''
|
||||||
|
if (time.hours) return `${time.hours} hours` + (result !== '' ? ` and ${result}` : '')
|
||||||
|
return result
|
||||||
|
}
|
||||||
@ -1,5 +1,16 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import i18n from 'i18n'
|
||||||
|
|
||||||
export const objectValuesToArray = (obj: { [x: string]: string }): Array<string> => {
|
export const objectValuesToArray = (obj: { [x: string]: string }): Array<string> => {
|
||||||
return Object.keys(obj).map(function (key) {
|
return Object.keys(obj).map(function (key) {
|
||||||
return obj[key]
|
return obj[key]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const decimalSeparatorByLanguage = (a: Decimal, language: string): string => {
|
||||||
|
const rememberLocaleToRestore = i18n.getLocale()
|
||||||
|
i18n.setLocale(language)
|
||||||
|
const result = a.toFixed(2).replace('.', i18n.__('general.decimalSeparator'))
|
||||||
|
i18n.setLocale(rememberLocaleToRestore)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
|
import CONFIG from '@/config'
|
||||||
import { backendLogger as logger } from '@/server/logger'
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
import { i18n } from '@/server/localization'
|
import { i18n } from '@/server/localization'
|
||||||
|
|
||||||
|
CONFIG.EMAIL = true
|
||||||
|
CONFIG.EMAIL_TEST_MODUS = false
|
||||||
|
|
||||||
jest.setTimeout(1000000)
|
jest.setTimeout(1000000)
|
||||||
|
|
||||||
jest.mock('@/server/logger', () => {
|
jest.mock('@/server/logger', () => {
|
||||||
|
|||||||
@ -108,7 +108,7 @@ services:
|
|||||||
#env_file:
|
#env_file:
|
||||||
# - ./frontend/.env
|
# - ./frontend/.env
|
||||||
volumes:
|
volumes:
|
||||||
# <host_machine_directy>:<container_directory> – mirror bidirectional path in local context with path in Docker container
|
# <host_machine_directory>:<container_directory> – mirror bidirectional path in local context with path in Docker container
|
||||||
- ./logs/backend:/logs/backend
|
- ./logs/backend:/logs/backend
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user