diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index f0ce064b4..75c672bd5 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -40,6 +40,7 @@ import Decimal from 'decimal.js-light' import { Contribution } from '@entity/Contribution' import { Transaction as DbTransaction } from '@entity/Transaction' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' +import { sendContributionConfirmedEmail } from '@/mailer/sendContributionConfirmedEmail' // mock account activation email to avoid console spam jest.mock('@/mailer/sendAccountActivationEmail', () => { @@ -49,6 +50,14 @@ jest.mock('@/mailer/sendAccountActivationEmail', () => { } }) +// mock account activation email to avoid console spam +jest.mock('@/mailer/sendContributionConfirmedEmail', () => { + return { + __esModule: true, + sendContributionConfirmedEmail: jest.fn(), + } +}) + let mutate: any, query: any, con: any let testEnv: any @@ -1450,6 +1459,20 @@ describe('AdminResolver', () => { expect(transaction[0].linkedUserId).toEqual(null) expect(transaction[0].typeId).toEqual(1) }) + + it('calls sendContributionConfirmedEmail', async () => { + expect(sendContributionConfirmedEmail).toBeCalledWith( + expect.objectContaining({ + contributionMemo: 'Herzlich Willkommen bei Gradido liebe Bibi!', + overviewURL: 'http://localhost/overview', + recipientEmail: 'bibi@bloxberg.de', + recipientFirstName: 'Bibi', + recipientLastName: 'Bloxberg', + senderFirstName: 'Peter', + senderLastName: 'Lustig', + }), + ) + }) }) describe('confirm two creations one after the other quickly', () => { diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index ac3936e4f..d71ffc72c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -66,6 +66,8 @@ import { ContributionMessage as DbContributionMessage } from '@entity/Contributi import ContributionMessageArgs from '@arg/ContributionMessageArgs' import { ContributionMessageType } from '@enum/MessageType' import { ContributionMessage } from '@model/ContributionMessage' +import { sendContributionConfirmedEmail } from '@/mailer/sendContributionConfirmedEmail' +import { sendAddedContributionMessageEmail } from '@/mailer/sendAddedContributionMessageEmail' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -470,6 +472,16 @@ export class AdminResolver { await queryRunner.commitTransaction() logger.info('creation commited successfuly.') + sendContributionConfirmedEmail({ + senderFirstName: moderatorUser.firstName, + senderLastName: moderatorUser.lastName, + recipientFirstName: user.firstName, + recipientLastName: user.lastName, + recipientEmail: user.email, + contributionMemo: contribution.memo, + contributionAmount: contribution.amount, + overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, + }) } catch (e) { await queryRunner.rollbackTransaction() logger.error(`Creation was not successful: ${e}`) @@ -713,7 +725,10 @@ export class AdminResolver { await queryRunner.startTransaction('READ UNCOMMITTED') const contributionMessage = DbContributionMessage.create() try { - const contribution = await Contribution.findOne({ id: contributionId }) + const contribution = await Contribution.findOne({ + where: { id: contributionId }, + relations: ['user'], + }) if (!contribution) { throw new Error('Contribution not found') } @@ -737,6 +752,18 @@ export class AdminResolver { await queryRunner.manager.update(Contribution, { id: contributionId }, contribution) } await queryRunner.commitTransaction() + + await sendAddedContributionMessageEmail({ + senderFirstName: user.firstName, + senderLastName: user.lastName, + recipientFirstName: contribution.user.firstName, + recipientLastName: contribution.user.lastName, + recipientEmail: contribution.user.email, + senderEmail: user.email, + contributionMemo: contribution.memo, + message, + overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, + }) } catch (e) { await queryRunner.rollbackTransaction() logger.error(`ContributionMessage was not successful: ${e}`) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index c0e330750..40e9e2ace 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -12,6 +12,14 @@ import { listContributionMessages, login } from '@/seeds/graphql/queries' import { userFactory } from '@/seeds/factory/user' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' +import { sendAddedContributionMessageEmail } from '@/mailer/sendAddedContributionMessageEmail' + +jest.mock('@/mailer/sendAddedContributionMessageEmail', () => { + return { + __esModule: true, + sendAddedContributionMessageEmail: jest.fn(), + } +}) let mutate: any, query: any, con: any let testEnv: any @@ -151,6 +159,20 @@ describe('ContributionMessageResolver', () => { }), ) }) + + it('calls sendAddedContributionMessageEmail', async () => { + expect(sendAddedContributionMessageEmail).toBeCalledWith({ + senderFirstName: 'Peter', + senderLastName: 'Lustig', + recipientFirstName: 'Bibi', + recipientLastName: 'Bloxberg', + recipientEmail: 'bibi@bloxberg.de', + senderEmail: 'peter@lustig.de', + contributionMemo: 'Test env contribution', + message: 'Admin Test', + overviewURL: 'http://localhost/overview', + }) + }) }) }) }) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index bc062a1f4..1d8748f58 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -8,7 +8,10 @@ import { Context, getUser } from '@/server/context' import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' import { getCustomRepository, getConnection } from '@dbTools/typeorm' -import { sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail' +import { + sendTransactionLinkRedeemedEmail, + sendTransactionReceivedEmail, +} from '@/mailer/sendTransactionReceivedEmail' import { Transaction } from '@model/Transaction' import { TransactionList } from '@model/TransactionList' @@ -154,6 +157,19 @@ export const executeTransaction = async ( memo, overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, }) + if (transactionLink) { + await sendTransactionLinkRedeemedEmail({ + senderFirstName: recipient.firstName, + senderLastName: recipient.lastName, + recipientFirstName: sender.firstName, + recipientLastName: sender.lastName, + email: sender.email, + senderEmail: recipient.email, + amount, + memo, + overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, + }) + } logger.info(`finished executeTransaction successfully`) return true } diff --git a/backend/src/mailer/sendAddedContributionMessageEmail.test.ts b/backend/src/mailer/sendAddedContributionMessageEmail.test.ts new file mode 100644 index 000000000..1151a0abc --- /dev/null +++ b/backend/src/mailer/sendAddedContributionMessageEmail.test.ts @@ -0,0 +1,40 @@ +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 `, + subject: 'Gradido Frage zur Schöpfung', + text: + expect.stringContaining('Hallo Bibi Bloxberg') && + expect.stringContaining('Peter Lustig') && + expect.stringContaining( + 'Du hast soeben zu deinem eingereichten Gradido Schöpfungsantrag "Vielen herzlichen Dank für den neuen Hexenbesen!" eine Rückfrage von Peter Lustig erhalten.', + ) && + expect.stringContaining('Was für ein Besen ist es geworden?') && + expect.stringContaining('http://localhost/overview'), + }) + }) +}) diff --git a/backend/src/mailer/sendAddedContributionMessageEmail.ts b/backend/src/mailer/sendAddedContributionMessageEmail.ts new file mode 100644 index 000000000..14d5f6d31 --- /dev/null +++ b/backend/src/mailer/sendAddedContributionMessageEmail.ts @@ -0,0 +1,26 @@ +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 => { + 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), + }) +} diff --git a/backend/src/mailer/sendContributionConfirmedEmail.test.ts b/backend/src/mailer/sendContributionConfirmedEmail.test.ts new file mode 100644 index 000000000..1935144fd --- /dev/null +++ b/backend/src/mailer/sendContributionConfirmedEmail.test.ts @@ -0,0 +1,39 @@ +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 ', + subject: 'Schöpfung wurde bestätigt', + text: + expect.stringContaining('Hallo Bibi Bloxberg') && + expect.stringContaining( + 'Dein Gradido Schöpfungsantrag "Vielen herzlichen Dank für den neuen Hexenbesen!" wurde soeben bestätigt.', + ) && + expect.stringContaining('Betrag: 200,00 GDD') && + expect.stringContaining('Link zu deinem Konto: http://localhost/overview'), + }) + }) +}) diff --git a/backend/src/mailer/sendContributionConfirmedEmail.ts b/backend/src/mailer/sendContributionConfirmedEmail.ts new file mode 100644 index 000000000..439d240eb --- /dev/null +++ b/backend/src/mailer/sendContributionConfirmedEmail.ts @@ -0,0 +1,26 @@ +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 => { + 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), + }) +} diff --git a/backend/src/mailer/sendTransactionReceivedEmail.ts b/backend/src/mailer/sendTransactionReceivedEmail.ts index 692f92f9a..55f63e37e 100644 --- a/backend/src/mailer/sendTransactionReceivedEmail.ts +++ b/backend/src/mailer/sendTransactionReceivedEmail.ts @@ -1,7 +1,7 @@ import { backendLogger as logger } from '@/server/logger' import Decimal from 'decimal.js-light' import { sendEMail } from './sendEMail' -import { transactionReceived } from './text/transactionReceived' +import { transactionLinkRedeemed, transactionReceived } from './text/transactionReceived' export const sendTransactionReceivedEmail = (data: { senderFirstName: string @@ -26,3 +26,27 @@ export const sendTransactionReceivedEmail = (data: { text: transactionReceived.de.text(data), }) } + +export const sendTransactionLinkRedeemedEmail = (data: { + email: string + senderFirstName: string + senderLastName: string + recipientFirstName: string + recipientLastName: string + senderEmail: string + amount: Decimal + memo: string + overviewURL: string +}): Promise => { + 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), + }) +} diff --git a/backend/src/mailer/text/contributionConfirmed.ts b/backend/src/mailer/text/contributionConfirmed.ts new file mode 100644 index 000000000..2d9fce6a8 --- /dev/null +++ b/backend/src/mailer/text/contributionConfirmed.ts @@ -0,0 +1,30 @@ +import Decimal from 'decimal.js-light' + +export const contributionConfirmed = { + de: { + subject: 'Schöpfung 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 Gradido Schöpfungsantrag "${data.contributionMemo}" wurde soeben von ${data.senderFirstName} ${ + data.senderLastName + } bestätigt. +Betrag: ${data.contributionAmount.toFixed(2).replace('.', ',')} GDD + +Bitte antworte nicht auf diese E-Mail! + +Mit freundlichen Grüßen, +dein Gradido-Team + + +Link zu deinem Konto: ${data.overviewURL}`, + }, +} diff --git a/backend/src/mailer/text/contributionMessageReceived.ts b/backend/src/mailer/text/contributionMessageReceived.ts new file mode 100644 index 000000000..4affb71a8 --- /dev/null +++ b/backend/src/mailer/text/contributionMessageReceived.ts @@ -0,0 +1,30 @@ +export const contributionMessageReceived = { + de: { + subject: 'Gradido Frage zur Schöpfung', + 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 soeben zu deinem eingereichten Gradido Schöpfungsantrag "${data.contributionMemo}" eine Rückfrage von ${data.senderFirstName} ${data.senderLastName} erhalten. +Die Rückfrage lautet: + +${data.message} + +Bitte antworte nicht auf diese E-Mail! + +Mit freundlichen Grüßen, +dein Gradido-Team + + +Link zu deinem Konto: ${data.overviewURL}`, + }, +} diff --git a/backend/src/mailer/text/transactionReceived.ts b/backend/src/mailer/text/transactionReceived.ts index 07d03ad45..2764d1b60 100644 --- a/backend/src/mailer/text/transactionReceived.ts +++ b/backend/src/mailer/text/transactionReceived.ts @@ -29,6 +29,37 @@ Mit freundlichen Grüßen, dein Gradido-Team +Link zu deinem Konto: ${data.overviewURL}`, + }, +} + +export const transactionLinkRedeemed = { + de: { + subject: 'Gradido link eingelösst', + 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össt. +Betrag: ${data.amount.toFixed(2).replace('.', ',')} GDD, +Memo: ${data.memo} + +Bitte antworte nicht auf diese E-Mail! + +Mit freundlichen Grüßen, +dein Gradido-Team + Link zu deinem Konto: ${data.overviewURL}`, }, } diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index 1255104c9..ebb6717e7 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -61,7 +61,7 @@ EVENT_PROTOCOL_DISABLED=false DATABASE_CONFIG_VERSION=v1.2022-03-18 # frontend -FRONTEND_CONFIG_VERSION=v2.2022-04-07 +FRONTEND_CONFIG_VERSION=v3.2022-09-16 GRAPHQL_URI=https://stage1.gradido.net/graphql ADMIN_AUTH_URL=https://stage1.gradido.net/admin/authenticate?token={token} @@ -77,6 +77,8 @@ META_KEYWORDS_DE="Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natü META_KEYWORDS_EN="Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System" META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" +SUPPORT_MAIL=support@supportmail.com + # admin ADMIN_CONFIG_VERSION=v1.2022-03-18 diff --git a/deployment/bare_metal/install.sh b/deployment/bare_metal/install.sh index 0fcf6d847..9366cbc75 100755 --- a/deployment/bare_metal/install.sh +++ b/deployment/bare_metal/install.sh @@ -75,7 +75,7 @@ pm2 startup sudo apt-get install -y certbot sudo apt-get install -y python3-certbot-nginx sudo certbot -> Enter email address (used for urgent renewal and security notices) > support@gradido.net +> Enter email address (used for urgent renewal and security notices) > e.g. support@supportmail.com > Please read the Terms of Service at > Y > Would you be willing, once your first certificate is successfully issued, to > N > No names were found in your configuration files. Please enter in your domain > stage1.gradido.net diff --git a/frontend/.env.dist b/frontend/.env.dist index 65d9ae305..4127f339a 100644 --- a/frontend/.env.dist +++ b/frontend/.env.dist @@ -1,4 +1,4 @@ -CONFIG_VERSION=v2.2022-04-07 +CONFIG_VERSION=v3.2022-09-16 # Environment DEFAULT_PUBLISHER_ID=2896 @@ -21,4 +21,7 @@ META_DESCRIPTION_DE="Dankbarkeit ist die Währung der neuen Zeit. Immer mehr Men META_DESCRIPTION_EN="Gratitude is the currency of the new age. More and more people are unleashing their potential and shaping a good future for all." META_KEYWORDS_DE="Grundeinkommen, Währung, Dankbarkeit, Schenk-Ökonomie, Natürliche Ökonomie des Lebens, Ökonomie, Ökologie, Potenzialentfaltung, Schenken und Danken, Kreislauf des Lebens, Geldsystem" META_KEYWORDS_EN="Basic Income, Currency, Gratitude, Gift Economy, Natural Economy of Life, Economy, Ecology, Potential Development, Giving and Thanking, Cycle of Life, Monetary System" -META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" \ No newline at end of file +META_AUTHOR="Bernd Hückstädt - Gradido-Akademie" + +# Support Mail +SUPPORT_MAIL=support@supportmail.com \ No newline at end of file diff --git a/frontend/.env.template b/frontend/.env.template index 6d6b83fc7..0b9d34b38 100644 --- a/frontend/.env.template +++ b/frontend/.env.template @@ -21,4 +21,7 @@ META_DESCRIPTION_DE=$META_DESCRIPTION_DE META_DESCRIPTION_EN=$META_DESCRIPTION_EN META_KEYWORDS_DE=$META_KEYWORDS_DE META_KEYWORDS_EN=$META_KEYWORDS_EN -META_AUTHOR=$META_AUTHOR \ No newline at end of file +META_AUTHOR=$META_AUTHOR + +# Support Mail +SUPPORT_MAIL=$SUPPORT_MAIL \ No newline at end of file diff --git a/frontend/src/config/index.js b/frontend/src/config/index.js index 917a25d70..5ab5f2392 100644 --- a/frontend/src/config/index.js +++ b/frontend/src/config/index.js @@ -8,7 +8,7 @@ const constants = { DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 CONFIG_VERSION: { DEFAULT: 'DEFAULT', - EXPECTED: 'v2.2022-04-07', + EXPECTED: 'v3.2022-09-16', CURRENT: '', }, } @@ -60,6 +60,10 @@ const meta = { META_AUTHOR: process.env.META_AUTHOR || 'Bernd Hückstädt - Gradido-Akademie', } +const supportmail = { + SUPPORT_MAIL: process.env.SUPPORT_MAIL || 'support@supportmail.com', +} + // Check config version constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT if ( @@ -79,6 +83,7 @@ const CONFIG = { ...endpoints, ...community, ...meta, + ...supportmail, } module.exports = CONFIG diff --git a/frontend/src/pages/InfoStatistic.vue b/frontend/src/pages/InfoStatistic.vue index 309404f48..1e09f83ed 100644 --- a/frontend/src/pages/InfoStatistic.vue +++ b/frontend/src/pages/InfoStatistic.vue @@ -83,7 +83,7 @@ export default { countAdminUser: null, itemsContributionLinks: [], itemsAdminUser: [], - supportMail: 'support@supportemail.de', + supportMail: CONFIG.SUPPORT_MAIL, membersCount: '1203', totalUsers: null, totalGradidoCreated: null,