Merge branch 'master' into 1489-feature-backup-cron

This commit is contained in:
clauspeterhuebner 2023-01-12 14:07:27 +01:00 committed by GitHub
commit 3adbbe9377
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 169 additions and 119 deletions

View File

@ -527,7 +527,7 @@ jobs:
report_name: Coverage Backend
type: lcov
result_path: ./backend/coverage/lcov.info
min_coverage: 76
min_coverage: 78
token: ${{ github.token }}
##########################################################################

View File

@ -4141,9 +4141,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001271:
version "1.0.30001354"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001354.tgz"
integrity sha512-mImKeCkyGDAHNywYFA4bqnLAzTUvVkqPvhY4DV47X+Gl2c5Z8c3KNETnXp14GQt11LvxE8AwjzGxJ+rsikiOzg==
version "1.0.30001442"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz"
integrity sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==
capture-exit@^2.0.0:
version "2.0.0"

View File

@ -70,11 +70,13 @@ const email = {
EMAIL: process.env.EMAIL === 'true' || false,
EMAIL_TEST_MODUS: process.env.EMAIL_TEST_MODUS === 'true' || false,
EMAIL_TEST_RECEIVER: process.env.EMAIL_TEST_RECEIVER || 'stage1@gradido.net',
EMAIL_USERNAME: process.env.EMAIL_USERNAME || 'gradido_email',
EMAIL_USERNAME: process.env.EMAIL_USERNAME || '',
EMAIL_SENDER: process.env.EMAIL_SENDER || 'info@gradido.net',
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || 'xxx',
EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'gmail.com',
EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '587',
EMAIL_PASSWORD: process.env.EMAIL_PASSWORD || '',
EMAIL_SMTP_URL: process.env.EMAIL_SMTP_URL || 'mailserver',
EMAIL_SMTP_PORT: process.env.EMAIL_SMTP_PORT || '1025',
// eslint-disable-next-line no-unneeded-ternary
EMAIL_TLS: process.env.EMAIL_TLS === 'false' ? false : true,
EMAIL_LINK_VERIFICATION:
process.env.EMAIL_LINK_VERIFICATION || 'http://localhost/checkEmail/{optin}{code}',
EMAIL_LINK_SETPASSWORD:

View File

@ -41,7 +41,7 @@ export const sendEmailTranslated = async (params: {
host: CONFIG.EMAIL_SMTP_URL,
port: Number(CONFIG.EMAIL_SMTP_PORT),
secure: false, // true for 465, false for other ports
requireTLS: true,
requireTLS: CONFIG.EMAIL_TLS,
auth: {
user: CONFIG.EMAIL_USERNAME,
pass: CONFIG.EMAIL_PASSWORD,

View File

@ -32,7 +32,7 @@ export class BalanceResolver {
const lastTransaction = context.lastTransaction
? context.lastTransaction
: await dbTransaction.findOne({ userId: user.id }, { order: { balanceDate: 'DESC' } })
: await dbTransaction.findOne({ userId: user.id }, { order: { id: 'DESC' } })
logger.debug(`lastTransaction=${lastTransaction}`)

View File

@ -1947,6 +1947,23 @@ describe('ContributionResolver', () => {
}),
)
})
describe('confirm same contribution again', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: confirmContribution,
variables: {
id: creation ? creation.id : -1,
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('Contribution already confirmd.')],
}),
)
})
})
})
describe('confirm two creations one after the other quickly', () => {

View File

@ -553,108 +553,116 @@ export class ContributionResolver {
@Arg('id', () => Int) id: number,
@Ctx() context: Context,
): Promise<boolean> {
const clientTimezoneOffset = getClientTimezoneOffset(context)
const contribution = await DbContribution.findOne(id)
if (!contribution) {
logger.error(`Contribution not found for given id: ${id}`)
throw new Error('Contribution not found to given id.')
}
const moderatorUser = getUser(context)
if (moderatorUser.id === contribution.userId) {
logger.error('Moderator can not confirm own contribution')
throw new Error('Moderator can not confirm own contribution')
}
const user = await DbUser.findOneOrFail(
{ id: contribution.userId },
{ withDeleted: true, relations: ['emailContact'] },
)
if (user.deletedAt) {
logger.error('This user was deleted. Cannot confirm a contribution.')
throw new Error('This user was deleted. Cannot confirm a contribution.')
}
const creations = await getUserCreation(contribution.userId, clientTimezoneOffset, false)
validateContribution(
creations,
contribution.amount,
contribution.contributionDate,
clientTimezoneOffset,
)
// acquire lock
const releaseLock = await TRANSACTIONS_LOCK.acquire()
const receivedCallDate = new Date()
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED')
try {
const lastTransaction = await queryRunner.manager
.createQueryBuilder()
.select('transaction')
.from(DbTransaction, 'transaction')
.where('transaction.userId = :id', { id: contribution.userId })
.orderBy('transaction.id', 'DESC')
.getOne()
logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined')
let newBalance = new Decimal(0)
let decay: Decay | null = null
if (lastTransaction) {
decay = calculateDecay(
lastTransaction.balance,
lastTransaction.balanceDate,
receivedCallDate,
)
newBalance = decay.balance
const clientTimezoneOffset = getClientTimezoneOffset(context)
const contribution = await DbContribution.findOne(id)
if (!contribution) {
logger.error(`Contribution not found for given id: ${id}`)
throw new Error('Contribution not found to given id.')
}
newBalance = newBalance.add(contribution.amount.toString())
if (contribution.confirmedAt) {
logger.error(`Contribution already confirmd: ${id}`)
throw new Error('Contribution already confirmd.')
}
const moderatorUser = getUser(context)
if (moderatorUser.id === contribution.userId) {
logger.error('Moderator can not confirm own contribution')
throw new Error('Moderator can not confirm own contribution')
}
const user = await DbUser.findOneOrFail(
{ id: contribution.userId },
{ withDeleted: true, relations: ['emailContact'] },
)
if (user.deletedAt) {
logger.error('This user was deleted. Cannot confirm a contribution.')
throw new Error('This user was deleted. Cannot confirm a contribution.')
}
const creations = await getUserCreation(contribution.userId, clientTimezoneOffset, false)
validateContribution(
creations,
contribution.amount,
contribution.contributionDate,
clientTimezoneOffset,
)
const transaction = new DbTransaction()
transaction.typeId = TransactionTypeId.CREATION
transaction.memo = contribution.memo
transaction.userId = contribution.userId
transaction.previous = lastTransaction ? lastTransaction.id : null
transaction.amount = contribution.amount
transaction.creationDate = contribution.contributionDate
transaction.balance = newBalance
transaction.balanceDate = receivedCallDate
transaction.decay = decay ? decay.decay : new Decimal(0)
transaction.decayStart = decay ? decay.start : null
await queryRunner.manager.insert(DbTransaction, transaction)
const receivedCallDate = new Date()
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction('REPEATABLE READ') // 'READ COMMITTED')
try {
const lastTransaction = await queryRunner.manager
.createQueryBuilder()
.select('transaction')
.from(DbTransaction, 'transaction')
.where('transaction.userId = :id', { id: contribution.userId })
.orderBy('transaction.id', 'DESC')
.getOne()
logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined')
contribution.confirmedAt = receivedCallDate
contribution.confirmedBy = moderatorUser.id
contribution.transactionId = transaction.id
contribution.contributionStatus = ContributionStatus.CONFIRMED
await queryRunner.manager.update(DbContribution, { id: contribution.id }, contribution)
let newBalance = new Decimal(0)
let decay: Decay | null = null
if (lastTransaction) {
decay = calculateDecay(
lastTransaction.balance,
lastTransaction.balanceDate,
receivedCallDate,
)
newBalance = decay.balance
}
newBalance = newBalance.add(contribution.amount.toString())
await queryRunner.commitTransaction()
logger.info('creation commited successfuly.')
sendContributionConfirmedEmail({
firstName: user.firstName,
lastName: user.lastName,
email: user.emailContact.email,
language: user.language,
senderFirstName: moderatorUser.firstName,
senderLastName: moderatorUser.lastName,
contributionMemo: contribution.memo,
contributionAmount: contribution.amount,
})
} catch (e) {
await queryRunner.rollbackTransaction()
logger.error('Creation was not successful', e)
throw new Error('Creation was not successful.')
const transaction = new DbTransaction()
transaction.typeId = TransactionTypeId.CREATION
transaction.memo = contribution.memo
transaction.userId = contribution.userId
transaction.previous = lastTransaction ? lastTransaction.id : null
transaction.amount = contribution.amount
transaction.creationDate = contribution.contributionDate
transaction.balance = newBalance
transaction.balanceDate = receivedCallDate
transaction.decay = decay ? decay.decay : new Decimal(0)
transaction.decayStart = decay ? decay.start : null
await queryRunner.manager.insert(DbTransaction, transaction)
contribution.confirmedAt = receivedCallDate
contribution.confirmedBy = moderatorUser.id
contribution.transactionId = transaction.id
contribution.contributionStatus = ContributionStatus.CONFIRMED
await queryRunner.manager.update(DbContribution, { id: contribution.id }, contribution)
await queryRunner.commitTransaction()
logger.info('creation commited successfuly.')
sendContributionConfirmedEmail({
firstName: user.firstName,
lastName: user.lastName,
email: user.emailContact.email,
language: user.language,
senderFirstName: moderatorUser.firstName,
senderLastName: moderatorUser.lastName,
contributionMemo: contribution.memo,
contributionAmount: contribution.amount,
})
} catch (e) {
await queryRunner.rollbackTransaction()
logger.error('Creation was not successful', e)
throw new Error('Creation was not successful.')
} finally {
await queryRunner.release()
}
const event = new Event()
const eventContributionConfirm = new EventContributionConfirm()
eventContributionConfirm.userId = user.id
eventContributionConfirm.amount = contribution.amount
eventContributionConfirm.contributionId = contribution.id
await eventProtocol.writeEvent(event.setEventContributionConfirm(eventContributionConfirm))
} finally {
await queryRunner.release()
releaseLock()
}
const event = new Event()
const eventContributionConfirm = new EventContributionConfirm()
eventContributionConfirm.userId = user.id
eventContributionConfirm.amount = contribution.amount
eventContributionConfirm.contributionId = contribution.id
await eventProtocol.writeEvent(event.setEventContributionConfirm(eventContributionConfirm))
return true
}

View File

@ -211,7 +211,7 @@ export class TransactionResolver {
// find current balance
const lastTransaction = await dbTransaction.findOne(
{ userId: user.id },
{ order: { balanceDate: 'DESC' }, relations: ['contribution'] },
{ order: { id: 'DESC' }, relations: ['contribution'] },
)
logger.debug(`lastTransaction=${lastTransaction}`)

View File

@ -1913,9 +1913,9 @@ camelcase@^6.2.0:
integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==
caniuse-lite@^1.0.30001264:
version "1.0.30001418"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz"
integrity sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==
version "1.0.30001442"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz"
integrity sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==
chacha20-universal@^1.0.4:
version "1.0.4"

View File

@ -112,6 +112,17 @@ services:
volumes:
- /sessions
########################################################
# MAILSERVER TO FAKE SMTP ##############################
########################################################
mailserver:
image: maildev/maildev
ports:
- 1080:1080
- 1025:1025
networks:
- external-net
volumes:
frontend_node_modules:
admin_node_modules:

View File

@ -81,6 +81,17 @@ services:
nginx:
image: gradido/nginx:test
########################################################
# MAILSERVER TO FAKE SMTP ##############################
########################################################
mailserver:
image: maildev/maildev
ports:
- 1080:1080
- 1025:1025
networks:
- external-net
networks:
external-net:
internal-net:

View File

@ -37,7 +37,7 @@
</b-row>
<b-row class="mt-5 p-5">
<b-col>
<b-button @click="$emit('on-reset')">{{ $t('back') }}</b-button>
<b-button @click="$emit('on-back')">{{ $t('back') }}</b-button>
</b-col>
<b-col class="text-right">
<b-button

View File

@ -41,7 +41,7 @@
</b-row>
<b-row class="mt-5 p-5">
<b-col>
<b-button @click="$emit('on-reset')">{{ $t('back') }}</b-button>
<b-button @click="$emit('on-back')">{{ $t('back') }}</b-button>
</b-col>
<b-col class="text-right">
<b-button

View File

@ -322,7 +322,7 @@ Die ganze Welt bezwingen.“`)
[
{
email: 'someone@watches.tv',
amount: '87.23',
amount: 87.23,
memo: 'Long enough',
selected: 'send',
},

View File

@ -144,7 +144,7 @@ export default {
this.$emit('set-transaction', {
selected: this.radioSelected,
email: this.form.email,
amount: this.form.amount,
amount: Number(this.form.amount.replace(',', '.')),
memo: this.form.memo,
})
},

View File

@ -10,7 +10,7 @@
<div class="text-center">
<div><figure-qr-code :link="link" /></div>
<div>
<b-button variant="secondary" @click="$emit('on-reset')" class="mt-4" data-test="close-btn">
<b-button variant="secondary" @click="$emit('on-back')" class="mt-4" data-test="close-btn">
{{ $t('form.close') }}
</b-button>
</div>

View File

@ -6,7 +6,7 @@
{{ $t('form.send_transaction_success') }}
</div>
<div class="text-center mt-5">
<b-button variant="primary" @click="$emit('on-reset')">{{ $t('form.close') }}</b-button>
<b-button variant="primary" @click="$emit('on-back')">{{ $t('form.close') }}</b-button>
</div>
</div>
</template>

View File

@ -15,7 +15,7 @@
:amount="transactionData.amount"
:memo="transactionData.memo"
@send-transaction="sendTransaction"
@on-reset="onReset"
@on-back="onBack"
></transaction-confirmation-send>
</template>
<template #transactionConfirmationLink>
@ -26,17 +26,17 @@
:memo="transactionData.memo"
:loading="loading"
@send-transaction="sendTransaction"
@on-reset="onReset"
@on-back="onBack"
></transaction-confirmation-link>
</template>
<template #transactionResultSendSuccess>
<transaction-result-send-success @on-reset="onReset"></transaction-result-send-success>
<transaction-result-send-success @on-back="onBack"></transaction-result-send-success>
</template>
<template #transactionResultSendError>
<transaction-result-send-error
:error="error"
:errorResult="errorResult"
@on-reset="onReset"
@on-back="onBack"
></transaction-result-send-error>
</template>
<template #transactionResultLink>
@ -45,7 +45,7 @@
:amount="amount"
:memo="memo"
:validUntil="validUntil"
@on-reset="onReset"
@on-back="onBack"
></transaction-result-link>
</template>
</gdd-send>
@ -169,8 +169,9 @@ export default {
}
this.loading = false
},
onReset() {
onBack() {
this.currentTransactionStep = TRANSACTION_STEPS.transactionForm
this.$mount()
},
updateTransactions(pagination) {
this.$emit('update-transactions', pagination)

View File

@ -4578,9 +4578,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001280, caniuse-lite@^1.0.30001286:
version "1.0.30001439"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001439.tgz"
integrity sha512-1MgUzEkoMO6gKfXflStpYgZDlFM7M/ck/bgfVCACO5vnAf0fXoNVHdWtqGU+MYca+4bL9Z5bpOVmR33cWW9G2A==
version "1.0.30001442"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001442.tgz"
integrity sha512-239m03Pqy0hwxYPYR5JwOIxRJfLTWtle9FV8zosfV5pHg+/51uD4nxcUlM8+mWWGfwKtt8lJNHnD3cWw9VZ6ow==
capture-exit@^2.0.0:
version "2.0.0"