Merge branch 'master' into add-client-request-time

This commit is contained in:
Moriz Wahl 2022-09-20 10:57:24 +02:00 committed by GitHub
commit 220da200a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 453 additions and 21 deletions

View File

@ -40,6 +40,7 @@ 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'
// mock account activation email to avoid console spam // mock account activation email to avoid console spam
jest.mock('@/mailer/sendAccountActivationEmail', () => { 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 mutate: any, query: any, con: any
let testEnv: any let testEnv: any
@ -1450,6 +1459,20 @@ describe('AdminResolver', () => {
expect(transaction[0].linkedUserId).toEqual(null) expect(transaction[0].linkedUserId).toEqual(null)
expect(transaction[0].typeId).toEqual(1) 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', () => { describe('confirm two creations one after the other quickly', () => {

View File

@ -66,6 +66,8 @@ 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 { sendAddedContributionMessageEmail } from '@/mailer/sendAddedContributionMessageEmail'
// const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_IN_REGISTER = 1
// const EMAIL_OPT_UNKNOWN = 3 // elopage? // const EMAIL_OPT_UNKNOWN = 3 // elopage?
@ -470,6 +472,16 @@ export class AdminResolver {
await queryRunner.commitTransaction() await queryRunner.commitTransaction()
logger.info('creation commited successfuly.') 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) { } catch (e) {
await queryRunner.rollbackTransaction() await queryRunner.rollbackTransaction()
logger.error(`Creation was not successful: ${e}`) logger.error(`Creation was not successful: ${e}`)
@ -713,7 +725,10 @@ export class AdminResolver {
await queryRunner.startTransaction('READ UNCOMMITTED') await queryRunner.startTransaction('READ UNCOMMITTED')
const contributionMessage = DbContributionMessage.create() const contributionMessage = DbContributionMessage.create()
try { try {
const contribution = await Contribution.findOne({ id: contributionId }) const contribution = await Contribution.findOne({
where: { id: contributionId },
relations: ['user'],
})
if (!contribution) { if (!contribution) {
throw new Error('Contribution not found') throw new Error('Contribution not found')
} }
@ -737,6 +752,18 @@ export class AdminResolver {
await queryRunner.manager.update(Contribution, { id: contributionId }, contribution) await queryRunner.manager.update(Contribution, { id: contributionId }, contribution)
} }
await queryRunner.commitTransaction() 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) { } catch (e) {
await queryRunner.rollbackTransaction() await queryRunner.rollbackTransaction()
logger.error(`ContributionMessage was not successful: ${e}`) logger.error(`ContributionMessage was not successful: ${e}`)

View File

@ -12,6 +12,14 @@ import { listContributionMessages, login } 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'
jest.mock('@/mailer/sendAddedContributionMessageEmail', () => {
return {
__esModule: true,
sendAddedContributionMessageEmail: jest.fn(),
}
})
let mutate: any, query: any, con: any let mutate: any, query: any, con: any
let testEnv: 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',
})
})
}) })
}) })
}) })

View File

@ -8,7 +8,10 @@ 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 } from '@dbTools/typeorm' import { getCustomRepository, getConnection } from '@dbTools/typeorm'
import { sendTransactionReceivedEmail } from '@/mailer/sendTransactionReceivedEmail' import {
sendTransactionLinkRedeemedEmail,
sendTransactionReceivedEmail,
} from '@/mailer/sendTransactionReceivedEmail'
import { Transaction } from '@model/Transaction' import { Transaction } from '@model/Transaction'
import { TransactionList } from '@model/TransactionList' import { TransactionList } from '@model/TransactionList'
@ -154,6 +157,19 @@ export const executeTransaction = async (
memo, memo,
overviewURL: CONFIG.EMAIL_LINK_OVERVIEW, 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`) logger.info(`finished executeTransaction successfully`)
return true return true
} }

View File

@ -19,6 +19,8 @@ import { contributionLinkFactory } from '@/seeds/factory/contributionLink'
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 { EventProtocol } from '@entity/EventProtocol'
import { logger } from '@test/testSetup' import { logger } 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'
@ -169,6 +171,15 @@ describe('UserResolver', () => {
duration: expect.any(String), duration: expect.any(String),
}) })
}) })
it('stores the send confirmation event in the database', () => {
expect(EventProtocol.find()).resolves.toContainEqual(
expect.objectContaining({
type: EventProtocolType.SEND_CONFIRMATION_EMAIL,
userId: user[0].id,
}),
)
})
}) })
describe('email already exists', () => { describe('email already exists', () => {
@ -245,18 +256,26 @@ describe('UserResolver', () => {
mutation: setPassword, mutation: setPassword,
variables: { code: emailOptIn, password: 'Aa12345_' }, variables: { code: emailOptIn, password: 'Aa12345_' },
}) })
// make Peter Lustig Admin // make Peter Lustig Admin
const peter = await User.findOneOrFail({ id: user[0].id }) const peter = await User.findOneOrFail({ id: user[0].id })
peter.isAdmin = new Date() peter.isAdmin = new Date()
await peter.save() await peter.save()
// date statement
const actualDate = new Date()
const futureDate = new Date() // Create a future day from the executed day
futureDate.setDate(futureDate.getDate() + 1)
// factory logs in as Peter Lustig // factory logs in as Peter Lustig
link = await contributionLinkFactory(testEnv, { link = await contributionLinkFactory(testEnv, {
name: 'Dokumenta 2022', name: 'Dokumenta 2022',
memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2022', memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2022',
amount: 200, amount: 200,
validFrom: new Date(2022, 5, 18), validFrom: actualDate,
validTo: new Date(2022, 8, 25), validTo: futureDate,
}) })
resetToken() resetToken()
await mutate({ await mutate({
mutation: createUser, mutation: createUser,
@ -271,6 +290,15 @@ describe('UserResolver', () => {
}), }),
) )
}) })
it('stores the account activated event in the database', () => {
expect(EventProtocol.find()).resolves.toContainEqual(
expect.objectContaining({
type: EventProtocolType.ACTIVATE_ACCOUNT,
userId: user[0].id,
}),
)
})
}) })
/* A transaction link requires GDD on account /* A transaction link requires GDD on account
@ -383,6 +411,10 @@ bei Gradidio sei dabei!`,
}), }),
) )
}) })
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith('Password entered is lexically invalid')
})
}) })
describe('no valid optin code', () => { describe('no valid optin code', () => {
@ -405,6 +437,10 @@ bei Gradidio sei dabei!`,
}), }),
) )
}) })
it('logs the error found', () => {
expect(logger.error).toBeCalledWith('Could not login with emailVerificationCode')
})
}) })
}) })
@ -433,6 +469,10 @@ bei Gradidio sei dabei!`,
}), }),
) )
}) })
it('logs the error found', () => {
expect(logger.error).toBeCalledWith('User with email=bibi@bloxberg.de does not exist')
})
}) })
describe('user is in database and correct login data', () => { describe('user is in database and correct login data', () => {
@ -475,6 +515,7 @@ bei Gradidio sei dabei!`,
describe('user is in database and wrong password', () => { describe('user is in database and wrong password', () => {
beforeAll(async () => { beforeAll(async () => {
await userFactory(testEnv, bibiBloxberg) await userFactory(testEnv, bibiBloxberg)
result = await query({ query: login, variables: { ...variables, password: 'wrong' } })
}) })
afterAll(async () => { afterAll(async () => {
@ -482,14 +523,16 @@ bei Gradidio sei dabei!`,
}) })
it('returns an error', () => { it('returns an error', () => {
expect( expect(result).toEqual(
query({ query: login, variables: { ...variables, password: 'wrong' } }),
).resolves.toEqual(
expect.objectContaining({ expect.objectContaining({
errors: [new GraphQLError('No user with this credentials')], errors: [new GraphQLError('No user with this credentials')],
}), }),
) )
}) })
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith('The User has no valid credentials.')
})
}) })
}) })
@ -562,6 +605,8 @@ bei Gradidio sei dabei!`,
}) })
describe('authenticated', () => { describe('authenticated', () => {
let user: User[]
const variables = { const variables = {
email: 'bibi@bloxberg.de', email: 'bibi@bloxberg.de',
password: 'Aa12345_', password: 'Aa12345_',
@ -569,6 +614,7 @@ bei Gradidio sei dabei!`,
beforeAll(async () => { beforeAll(async () => {
await query({ query: login, variables }) await query({ query: login, variables })
user = await User.find()
}) })
afterAll(() => { afterAll(() => {
@ -595,6 +641,15 @@ bei Gradidio sei dabei!`,
}), }),
) )
}) })
it('stores the login event in the database', () => {
expect(EventProtocol.find()).resolves.toContainEqual(
expect.objectContaining({
type: EventProtocolType.LOGIN,
userId: user[0].id,
}),
)
})
}) })
}) })
}) })
@ -649,13 +704,17 @@ bei Gradidio sei dabei!`,
}) })
describe('request reset password again', () => { describe('request reset password again', () => {
it('thows an error', async () => { it('throws an error', async () => {
await expect(mutate({ mutation: forgotPassword, variables })).resolves.toEqual( await expect(mutate({ mutation: forgotPassword, variables })).resolves.toEqual(
expect.objectContaining({ expect.objectContaining({
errors: [new GraphQLError('email already sent less than 10 minutes minutes ago')], errors: [new GraphQLError('email already sent less than 10 minutes minutes ago')],
}), }),
) )
}) })
it('logs the error found', () => {
expect(logger.error).toBeCalledWith(`email already sent less than 10 minutes minutes ago`)
})
}) })
}) })
}) })
@ -766,7 +825,7 @@ bei Gradidio sei dabei!`,
}) })
describe('language is not valid', () => { describe('language is not valid', () => {
it('thows an error', async () => { it('throws an error', async () => {
await expect( await expect(
mutate({ mutate({
mutation: updateUserInfos, mutation: updateUserInfos,
@ -780,6 +839,10 @@ bei Gradidio sei dabei!`,
}), }),
) )
}) })
it('logs the error found', () => {
expect(logger.error).toBeCalledWith(`"not-valid" isn't a valid language`)
})
}) })
describe('password', () => { describe('password', () => {
@ -799,6 +862,10 @@ bei Gradidio sei dabei!`,
}), }),
) )
}) })
it('logs the error found', () => {
expect(logger.error).toBeCalledWith(`Old password is invalid`)
})
}) })
describe('invalid new password', () => { describe('invalid new password', () => {
@ -821,6 +888,10 @@ bei Gradidio sei dabei!`,
}), }),
) )
}) })
it('logs the error found', () => {
expect(logger.error).toBeCalledWith('newPassword does not fullfil the rules')
})
}) })
describe('correct old and new password', () => { describe('correct old and new password', () => {
@ -840,7 +911,7 @@ bei Gradidio sei dabei!`,
) )
}) })
it('can login wtih new password', async () => { it('can login with new password', async () => {
await expect( await expect(
query({ query({
query: login, query: login,
@ -860,7 +931,7 @@ bei Gradidio sei dabei!`,
) )
}) })
it('cannot login wtih old password', async () => { it('cannot login with old password', async () => {
await expect( await expect(
query({ query({
query: login, query: login,
@ -875,6 +946,10 @@ bei Gradidio sei dabei!`,
}), }),
) )
}) })
it('logs the error thrown', () => {
expect(logger.error).toBeCalledWith('The User has no valid credentials.')
})
}) })
}) })
}) })

View File

@ -30,6 +30,7 @@ import {
EventRedeemRegister, EventRedeemRegister,
EventRegister, EventRegister,
EventSendConfirmationEmail, EventSendConfirmationEmail,
EventActivateAccount,
} from '@/event/Event' } from '@/event/Event'
import { getUserCreation } from './util/creations' import { getUserCreation } from './util/creations'
import { UserRepository } from '@/typeorm/repository/User' import { UserRepository } from '@/typeorm/repository/User'
@ -273,7 +274,7 @@ export class UserResolver {
logger.info(`login with ${email}, ***, ${publisherId} ...`) logger.info(`login with ${email}, ***, ${publisherId} ...`)
email = email.trim().toLowerCase() email = email.trim().toLowerCase()
const dbUser = await DbUser.findOneOrFail({ email }, { withDeleted: true }).catch(() => { const dbUser = await DbUser.findOneOrFail({ email }, { withDeleted: true }).catch(() => {
logger.error(`User with email=${email} does not exists`) logger.error(`User with email=${email} does not exist`)
throw new Error('No user with this credentials') throw new Error('No user with this credentials')
}) })
if (dbUser.deletedAt) { if (dbUser.deletedAt) {
@ -389,7 +390,7 @@ export class UserResolver {
/* uncomment this, when you need the activation link on the console */ /* uncomment this, when you need the activation link on the console */
// In case EMails are disabled log the activation link for the user // In case EMails are disabled log the activation link for the user
if (!emailSent) { if (!emailSent) {
logger.debug(`Email not send!`) logger.debug(`Email not sent!`)
} }
logger.info('createUser() faked and send multi registration mail...') logger.info('createUser() faked and send multi registration mail...')
@ -548,6 +549,7 @@ export class UserResolver {
logger.info(`setPassword(${code}, ***)...`) logger.info(`setPassword(${code}, ***)...`)
// Validate Password // Validate Password
if (!isPassword(password)) { if (!isPassword(password)) {
logger.error('Password entered is lexically invalid')
throw new Error( throw new Error(
'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!', 'Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!',
) )
@ -610,6 +612,8 @@ export class UserResolver {
await queryRunner.connect() await queryRunner.connect()
await queryRunner.startTransaction('READ UNCOMMITTED') await queryRunner.startTransaction('READ UNCOMMITTED')
const event = new Event()
try { try {
// Save user // Save user
await queryRunner.manager.save(user).catch((error) => { await queryRunner.manager.save(user).catch((error) => {
@ -618,6 +622,11 @@ export class UserResolver {
}) })
await queryRunner.commitTransaction() await queryRunner.commitTransaction()
const eventActivateAccount = new EventActivateAccount()
eventActivateAccount.userId = user.id
eventProtocol.writeEvent(event.setEventActivateAccount(eventActivateAccount))
logger.info('User data written successfully...') logger.info('User data written successfully...')
} catch (e) { } catch (e) {
await queryRunner.rollbackTransaction() await queryRunner.rollbackTransaction()
@ -727,6 +736,7 @@ export class UserResolver {
try { try {
await queryRunner.manager.save(userEntity).catch((error) => { await queryRunner.manager.save(userEntity).catch((error) => {
logger.error('error saving user: ' + error)
throw new Error('error saving user: ' + error) throw new Error('error saving user: ' + error)
}) })

View File

@ -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 <bibi@bloxberg.de>`,
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'),
})
})
})

View File

@ -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<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),
})
}

View File

@ -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 <bibi@bloxberg.de>',
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'),
})
})
})

View File

@ -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<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),
})
}

View File

@ -1,7 +1,7 @@
import { backendLogger as logger } from '@/server/logger' import { backendLogger as logger } from '@/server/logger'
import Decimal from 'decimal.js-light' import Decimal from 'decimal.js-light'
import { sendEMail } from './sendEMail' import { sendEMail } from './sendEMail'
import { transactionReceived } from './text/transactionReceived' import { transactionLinkRedeemed, transactionReceived } from './text/transactionReceived'
export const sendTransactionReceivedEmail = (data: { export const sendTransactionReceivedEmail = (data: {
senderFirstName: string senderFirstName: string
@ -26,3 +26,27 @@ export const sendTransactionReceivedEmail = (data: {
text: transactionReceived.de.text(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<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),
})
}

View File

@ -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}`,
},
}

View File

@ -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}`,
},
}

View File

@ -29,6 +29,37 @@ Mit freundlichen Grüßen,
dein Gradido-Team 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}`, Link zu deinem Konto: ${data.overviewURL}`,
}, },
} }

View File

@ -61,7 +61,7 @@ EVENT_PROTOCOL_DISABLED=false
DATABASE_CONFIG_VERSION=v1.2022-03-18 DATABASE_CONFIG_VERSION=v1.2022-03-18
# frontend # frontend
FRONTEND_CONFIG_VERSION=v2.2022-04-07 FRONTEND_CONFIG_VERSION=v3.2022-09-16
GRAPHQL_URI=https://stage1.gradido.net/graphql GRAPHQL_URI=https://stage1.gradido.net/graphql
ADMIN_AUTH_URL=https://stage1.gradido.net/admin/authenticate?token={token} 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_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" META_AUTHOR="Bernd Hückstädt - Gradido-Akademie"
SUPPORT_MAIL=support@supportmail.com
# admin # admin
ADMIN_CONFIG_VERSION=v1.2022-03-18 ADMIN_CONFIG_VERSION=v1.2022-03-18

View File

@ -75,7 +75,7 @@ pm2 startup
sudo apt-get install -y certbot sudo apt-get install -y certbot
sudo apt-get install -y python3-certbot-nginx sudo apt-get install -y python3-certbot-nginx
sudo certbot 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 > Please read the Terms of Service at > Y
> Would you be willing, once your first certificate is successfully issued, to > N > 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 > No names were found in your configuration files. Please enter in your domain > stage1.gradido.net

View File

@ -1,4 +1,4 @@
CONFIG_VERSION=v2.2022-04-07 CONFIG_VERSION=v3.2022-09-16
# Environment # Environment
DEFAULT_PUBLISHER_ID=2896 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_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_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_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" META_AUTHOR="Bernd Hückstädt - Gradido-Akademie"
# Support Mail
SUPPORT_MAIL=support@supportmail.com

View File

@ -21,4 +21,7 @@ META_DESCRIPTION_DE=$META_DESCRIPTION_DE
META_DESCRIPTION_EN=$META_DESCRIPTION_EN META_DESCRIPTION_EN=$META_DESCRIPTION_EN
META_KEYWORDS_DE=$META_KEYWORDS_DE META_KEYWORDS_DE=$META_KEYWORDS_DE
META_KEYWORDS_EN=$META_KEYWORDS_EN META_KEYWORDS_EN=$META_KEYWORDS_EN
META_AUTHOR=$META_AUTHOR META_AUTHOR=$META_AUTHOR
# Support Mail
SUPPORT_MAIL=$SUPPORT_MAIL

View File

@ -8,7 +8,7 @@ const constants = {
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
CONFIG_VERSION: { CONFIG_VERSION: {
DEFAULT: 'DEFAULT', DEFAULT: 'DEFAULT',
EXPECTED: 'v2.2022-04-07', EXPECTED: 'v3.2022-09-16',
CURRENT: '', CURRENT: '',
}, },
} }
@ -60,6 +60,10 @@ const meta = {
META_AUTHOR: process.env.META_AUTHOR || 'Bernd Hückstädt - Gradido-Akademie', 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 // Check config version
constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT constants.CONFIG_VERSION.CURRENT = process.env.CONFIG_VERSION || constants.CONFIG_VERSION.DEFAULT
if ( if (
@ -79,6 +83,7 @@ const CONFIG = {
...endpoints, ...endpoints,
...community, ...community,
...meta, ...meta,
...supportmail,
} }
module.exports = CONFIG module.exports = CONFIG

View File

@ -83,7 +83,7 @@ export default {
countAdminUser: null, countAdminUser: null,
itemsContributionLinks: [], itemsContributionLinks: [],
itemsAdminUser: [], itemsAdminUser: [],
supportMail: 'support@supportemail.de', supportMail: CONFIG.SUPPORT_MAIL,
membersCount: '1203', membersCount: '1203',
totalUsers: null, totalUsers: null,
totalGradidoCreated: null, totalGradidoCreated: null,