mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into admin_user_search_optimization
This commit is contained in:
commit
e64705d906
13
.github/workflows/test.yml
vendored
13
.github/workflows/test.yml
vendored
@ -431,7 +431,7 @@ jobs:
|
|||||||
unit_test_backend:
|
unit_test_backend:
|
||||||
name: Unit tests - Backend
|
name: Unit tests - Backend
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_backend,build_test_mariadb]
|
needs: [build_test_mariadb]
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -448,13 +448,6 @@ jobs:
|
|||||||
path: /tmp
|
path: /tmp
|
||||||
- name: Load Docker Image
|
- name: Load Docker Image
|
||||||
run: docker load < /tmp/mariadb.tar
|
run: docker load < /tmp/mariadb.tar
|
||||||
- name: Download Docker Image (Backend)
|
|
||||||
uses: actions/download-artifact@v2
|
|
||||||
with:
|
|
||||||
name: docker-backend-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/backend.tar
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# UNIT TESTS BACKEND #####################################################
|
# UNIT TESTS BACKEND #####################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -469,7 +462,7 @@ jobs:
|
|||||||
run: sleep 30s
|
run: sleep 30s
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: backend Unit tests | test
|
- name: backend Unit tests | test
|
||||||
run: cd database && yarn && yarn build && cd ../backend && yarn && yarn CI_workflow_test
|
run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test
|
||||||
# run: docker-compose -f docker-compose.yml -f docker-compose.test.yml exec -T backend yarn test
|
# run: docker-compose -f docker-compose.yml -f docker-compose.test.yml exec -T backend yarn test
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# COVERAGE CHECK BACKEND #################################################
|
# COVERAGE CHECK BACKEND #################################################
|
||||||
@ -480,7 +473,7 @@ jobs:
|
|||||||
report_name: Coverage Backend
|
report_name: Coverage Backend
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./backend/coverage/lcov.info
|
result_path: ./backend/coverage/lcov.info
|
||||||
min_coverage: 38
|
min_coverage: 48
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|||||||
@ -1,21 +1,18 @@
|
|||||||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||||
module.exports = async () => {
|
module.exports = {
|
||||||
process.env.TZ = 'UTC'
|
|
||||||
return {
|
|
||||||
verbose: true,
|
verbose: true,
|
||||||
preset: 'ts-jest',
|
preset: 'ts-jest',
|
||||||
collectCoverage: true,
|
collectCoverage: true,
|
||||||
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
|
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**'],
|
||||||
|
setupFiles: ['<rootDir>/test/testSetup.ts'],
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'@entity/(.*)': '<rootDir>/../database/build/entity/$1',
|
'@entity/(.*)':
|
||||||
// This is hack to fix a problem with the library `ts-mysql-migrate` which does differentiate between its ts/js state
|
process.env.NODE_ENV === 'development'
|
||||||
'@dbTools/(.*)': '<rootDir>/../database/src/$1',
|
? '<rootDir>/../database/entity/$1'
|
||||||
/*
|
: '<rootDir>/../database/build/entity/$1',
|
||||||
'@dbTools/(.*)':
|
'@dbTools/(.*)':
|
||||||
process.env.NODE_ENV === 'development'
|
process.env.NODE_ENV === 'development'
|
||||||
? '<rootDir>/../database/src/$1'
|
? '<rootDir>/../database/src/$1'
|
||||||
: '<rootDir>/../database/build/src/$1',
|
: '<rootDir>/../database/build/src/$1',
|
||||||
*/
|
|
||||||
},
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,8 +13,7 @@
|
|||||||
"start": "node build/index.js",
|
"start": "node build/index.js",
|
||||||
"dev": "nodemon -w src --ext ts --exec ts-node src/index.ts",
|
"dev": "nodemon -w src --ext ts --exec ts-node src/index.ts",
|
||||||
"lint": "eslint . --ext .js,.ts",
|
"lint": "eslint . --ext .js,.ts",
|
||||||
"CI_workflow_test": "jest --runInBand --coverage ",
|
"test": "TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles"
|
||||||
"test": "NODE_ENV=development jest --runInBand --coverage "
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/jest": "^27.0.2",
|
"@types/jest": "^27.0.2",
|
||||||
|
|||||||
@ -4,7 +4,7 @@ import dotenv from 'dotenv'
|
|||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
const constants = {
|
const constants = {
|
||||||
DB_VERSION: '0023-users_disabled_soft_delete',
|
DB_VERSION: '0024-combine_transaction_tables',
|
||||||
DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0
|
DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,22 +8,21 @@ import { PendingCreation } from '../model/PendingCreation'
|
|||||||
import { CreatePendingCreations } from '../model/CreatePendingCreations'
|
import { CreatePendingCreations } from '../model/CreatePendingCreations'
|
||||||
import { UpdatePendingCreation } from '../model/UpdatePendingCreation'
|
import { UpdatePendingCreation } from '../model/UpdatePendingCreation'
|
||||||
import { RIGHTS } from '../../auth/RIGHTS'
|
import { RIGHTS } from '../../auth/RIGHTS'
|
||||||
import { TransactionRepository } from '../../typeorm/repository/Transaction'
|
|
||||||
import { UserRepository } from '../../typeorm/repository/User'
|
import { UserRepository } from '../../typeorm/repository/User'
|
||||||
import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs'
|
import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs'
|
||||||
import UpdatePendingCreationArgs from '../arg/UpdatePendingCreationArgs'
|
import UpdatePendingCreationArgs from '../arg/UpdatePendingCreationArgs'
|
||||||
import SearchUsersArgs from '../arg/SearchUsersArgs'
|
import SearchUsersArgs from '../arg/SearchUsersArgs'
|
||||||
import moment from 'moment'
|
import moment from 'moment'
|
||||||
import { Transaction } from '@entity/Transaction'
|
import { Transaction } from '@entity/Transaction'
|
||||||
import { TransactionCreation } from '@entity/TransactionCreation'
|
|
||||||
import { UserTransaction } from '@entity/UserTransaction'
|
import { UserTransaction } from '@entity/UserTransaction'
|
||||||
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
|
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
|
||||||
import { BalanceRepository } from '../../typeorm/repository/Balance'
|
|
||||||
import { calculateDecay } from '../../util/decay'
|
import { calculateDecay } from '../../util/decay'
|
||||||
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
|
import { AdminPendingCreation } from '@entity/AdminPendingCreation'
|
||||||
import { hasElopageBuys } from '../../util/hasElopageBuys'
|
import { hasElopageBuys } from '../../util/hasElopageBuys'
|
||||||
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
|
import { TransactionTypeId } from '../enum/TransactionTypeId'
|
||||||
|
import { Balance } from '@entity/Balance'
|
||||||
|
|
||||||
// const EMAIL_OPT_IN_REGISTER = 1
|
// const EMAIL_OPT_IN_REGISTER = 1
|
||||||
// const EMAIL_OPT_UNKNOWN = 3 // elopage?
|
// const EMAIL_OPT_UNKNOWN = 3 // elopage?
|
||||||
@ -240,23 +239,17 @@ export class AdminResolver {
|
|||||||
if (moderatorUser.id === pendingCreation.userId)
|
if (moderatorUser.id === pendingCreation.userId)
|
||||||
throw new Error('Moderator can not confirm own pending creation')
|
throw new Error('Moderator can not confirm own pending creation')
|
||||||
|
|
||||||
const transactionRepository = getCustomRepository(TransactionRepository)
|
|
||||||
const receivedCallDate = new Date()
|
const receivedCallDate = new Date()
|
||||||
let transaction = new Transaction()
|
let transaction = new Transaction()
|
||||||
transaction.transactionTypeId = 1
|
transaction.transactionTypeId = TransactionTypeId.CREATION
|
||||||
transaction.memo = pendingCreation.memo
|
transaction.memo = pendingCreation.memo
|
||||||
transaction.received = receivedCallDate
|
transaction.received = receivedCallDate
|
||||||
transaction = await transactionRepository.save(transaction)
|
transaction.userId = pendingCreation.userId
|
||||||
|
transaction.amount = BigInt(parseInt(pendingCreation.amount.toString()))
|
||||||
|
transaction.creationDate = pendingCreation.date
|
||||||
|
transaction = await transaction.save()
|
||||||
if (!transaction) throw new Error('Could not create transaction')
|
if (!transaction) throw new Error('Could not create transaction')
|
||||||
|
|
||||||
let transactionCreation = new TransactionCreation()
|
|
||||||
transactionCreation.transactionId = transaction.id
|
|
||||||
transactionCreation.userId = pendingCreation.userId
|
|
||||||
transactionCreation.amount = parseInt(pendingCreation.amount.toString())
|
|
||||||
transactionCreation.targetDate = pendingCreation.date
|
|
||||||
transactionCreation = await TransactionCreation.save(transactionCreation)
|
|
||||||
if (!transactionCreation) throw new Error('Could not create transactionCreation')
|
|
||||||
|
|
||||||
const userTransactionRepository = getCustomRepository(UserTransactionRepository)
|
const userTransactionRepository = getCustomRepository(UserTransactionRepository)
|
||||||
const lastUserTransaction = await userTransactionRepository.findLastForUser(
|
const lastUserTransaction = await userTransactionRepository.findLastForUser(
|
||||||
pendingCreation.userId,
|
pendingCreation.userId,
|
||||||
@ -284,15 +277,15 @@ export class AdminResolver {
|
|||||||
throw new Error('Error saving user transaction: ' + error)
|
throw new Error('Error saving user transaction: ' + error)
|
||||||
})
|
})
|
||||||
|
|
||||||
const balanceRepository = getCustomRepository(BalanceRepository)
|
let userBalance = await Balance.findOne({ userId: pendingCreation.userId })
|
||||||
let userBalance = await balanceRepository.findByUser(pendingCreation.userId)
|
if (!userBalance) {
|
||||||
|
userBalance = new Balance()
|
||||||
if (!userBalance) userBalance = balanceRepository.create()
|
|
||||||
userBalance.userId = pendingCreation.userId
|
userBalance.userId = pendingCreation.userId
|
||||||
|
}
|
||||||
userBalance.amount = Number(newBalance)
|
userBalance.amount = Number(newBalance)
|
||||||
userBalance.modified = receivedCallDate
|
userBalance.modified = receivedCallDate
|
||||||
userBalance.recordDate = receivedCallDate
|
userBalance.recordDate = receivedCallDate
|
||||||
await balanceRepository.save(userBalance)
|
await userBalance.save()
|
||||||
await AdminPendingCreation.delete(pendingCreation)
|
await AdminPendingCreation.delete(pendingCreation)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -306,12 +299,13 @@ async function getUserCreations(id: number): Promise<number[]> {
|
|||||||
const lastMonthNumber = moment().subtract(1, 'month').format('M')
|
const lastMonthNumber = moment().subtract(1, 'month').format('M')
|
||||||
const currentMonthNumber = moment().format('M')
|
const currentMonthNumber = moment().format('M')
|
||||||
|
|
||||||
const createdAmountsQuery = await TransactionCreation.createQueryBuilder('transaction_creations')
|
const createdAmountsQuery = await Transaction.createQueryBuilder('transactions')
|
||||||
.select('MONTH(transaction_creations.target_date)', 'target_month')
|
.select('MONTH(transactions.creation_date)', 'target_month')
|
||||||
.addSelect('SUM(transaction_creations.amount)', 'sum')
|
.addSelect('SUM(transactions.amount)', 'sum')
|
||||||
.where('transaction_creations.state_user_id = :id', { id })
|
.where('transactions.user_id = :id', { id })
|
||||||
|
.andWhere('transactions.transaction_type_id = :type', { type: TransactionTypeId.CREATION })
|
||||||
.andWhere({
|
.andWhere({
|
||||||
targetDate: Raw((alias) => `${alias} >= :date and ${alias} < :endDate`, {
|
creationDate: Raw((alias) => `${alias} >= :date and ${alias} < :endDate`, {
|
||||||
date: dateBeforeLastMonth,
|
date: dateBeforeLastMonth,
|
||||||
endDate: dateNextMonth,
|
endDate: dateNextMonth,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -4,11 +4,11 @@
|
|||||||
import { Resolver, Query, Ctx, Authorized } from 'type-graphql'
|
import { Resolver, Query, Ctx, Authorized } from 'type-graphql'
|
||||||
import { getCustomRepository } from '@dbTools/typeorm'
|
import { getCustomRepository } from '@dbTools/typeorm'
|
||||||
import { Balance } from '../model/Balance'
|
import { Balance } from '../model/Balance'
|
||||||
import { BalanceRepository } from '../../typeorm/repository/Balance'
|
|
||||||
import { UserRepository } from '../../typeorm/repository/User'
|
import { UserRepository } from '../../typeorm/repository/User'
|
||||||
import { calculateDecay } from '../../util/decay'
|
import { calculateDecay } from '../../util/decay'
|
||||||
import { roundFloorFrom4 } from '../../util/round'
|
import { roundFloorFrom4 } from '../../util/round'
|
||||||
import { RIGHTS } from '../../auth/RIGHTS'
|
import { RIGHTS } from '../../auth/RIGHTS'
|
||||||
|
import { Balance as dbBalance } from '@entity/Balance'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class BalanceResolver {
|
export class BalanceResolver {
|
||||||
@ -16,11 +16,10 @@ export class BalanceResolver {
|
|||||||
@Query(() => Balance)
|
@Query(() => Balance)
|
||||||
async balance(@Ctx() context: any): Promise<Balance> {
|
async balance(@Ctx() context: any): Promise<Balance> {
|
||||||
// load user and balance
|
// load user and balance
|
||||||
const balanceRepository = getCustomRepository(BalanceRepository)
|
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
|
|
||||||
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
|
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
const balanceEntity = await balanceRepository.findByUser(userEntity.id)
|
const balanceEntity = await dbBalance.findOne({ userId: userEntity.id })
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
|
|
||||||
// No balance found
|
// No balance found
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
|
||||||
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
||||||
import { getCustomRepository, getConnection, QueryRunner } from '@dbTools/typeorm'
|
import { getCustomRepository, getConnection, QueryRunner, In } from '@dbTools/typeorm'
|
||||||
|
|
||||||
import CONFIG from '../../config'
|
import CONFIG from '../../config'
|
||||||
import { sendTransactionReceivedEmail } from '../../mailer/sendTransactionReceivedEmail'
|
import { sendTransactionReceivedEmail } from '../../mailer/sendTransactionReceivedEmail'
|
||||||
@ -16,15 +16,12 @@ import Paginated from '../arg/Paginated'
|
|||||||
|
|
||||||
import { Order } from '../enum/Order'
|
import { Order } from '../enum/Order'
|
||||||
|
|
||||||
import { BalanceRepository } from '../../typeorm/repository/Balance'
|
|
||||||
import { UserRepository } from '../../typeorm/repository/User'
|
import { UserRepository } from '../../typeorm/repository/User'
|
||||||
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
|
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
|
||||||
import { TransactionRepository } from '../../typeorm/repository/Transaction'
|
|
||||||
|
|
||||||
import { User as dbUser } from '@entity/User'
|
import { User as dbUser } from '@entity/User'
|
||||||
import { UserTransaction as dbUserTransaction } from '@entity/UserTransaction'
|
import { UserTransaction as dbUserTransaction } from '@entity/UserTransaction'
|
||||||
import { Transaction as dbTransaction } from '@entity/Transaction'
|
import { Transaction as dbTransaction } from '@entity/Transaction'
|
||||||
import { TransactionSendCoin as dbTransactionSendCoin } from '@entity/TransactionSendCoin'
|
|
||||||
import { Balance as dbBalance } from '@entity/Balance'
|
import { Balance as dbBalance } from '@entity/Balance'
|
||||||
|
|
||||||
import { apiPost } from '../../apis/HttpRequest'
|
import { apiPost } from '../../apis/HttpRequest'
|
||||||
@ -50,15 +47,13 @@ async function calculateAndAddDecayTransactions(
|
|||||||
transactionIds.push(userTransaction.transactionId)
|
transactionIds.push(userTransaction.transactionId)
|
||||||
})
|
})
|
||||||
|
|
||||||
const transactionRepository = getCustomRepository(TransactionRepository)
|
const transactions = await dbTransaction.find({ where: { id: In(transactionIds) } })
|
||||||
const transactions = await transactionRepository.joinFullTransactionsByIds(transactionIds)
|
|
||||||
|
|
||||||
const transactionIndiced: dbTransaction[] = []
|
const transactionIndiced: dbTransaction[] = []
|
||||||
transactions.forEach((transaction: dbTransaction) => {
|
transactions.forEach((transaction: dbTransaction) => {
|
||||||
transactionIndiced[transaction.id] = transaction
|
transactionIndiced[transaction.id] = transaction
|
||||||
|
involvedUserIds.push(transaction.userId)
|
||||||
if (transaction.transactionTypeId === TransactionTypeId.SEND) {
|
if (transaction.transactionTypeId === TransactionTypeId.SEND) {
|
||||||
involvedUserIds.push(transaction.transactionSendCoin.userId)
|
involvedUserIds.push(transaction.sendReceiverUserId!) // TODO ensure not null properly
|
||||||
involvedUserIds.push(transaction.transactionSendCoin.recipiantUserId)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// remove duplicates
|
// remove duplicates
|
||||||
@ -108,24 +103,21 @@ async function calculateAndAddDecayTransactions(
|
|||||||
// balance
|
// balance
|
||||||
if (userTransaction.transactionTypeId === TransactionTypeId.CREATION) {
|
if (userTransaction.transactionTypeId === TransactionTypeId.CREATION) {
|
||||||
// creation
|
// creation
|
||||||
const creation = transaction.transactionCreation
|
|
||||||
|
|
||||||
finalTransaction.name = 'Gradido Akademie'
|
finalTransaction.name = 'Gradido Akademie'
|
||||||
finalTransaction.type = TransactionType.CREATION
|
finalTransaction.type = TransactionType.CREATION
|
||||||
// finalTransaction.targetDate = creation.targetDate
|
// finalTransaction.targetDate = creation.targetDate
|
||||||
finalTransaction.balance = roundFloorFrom4(creation.amount)
|
finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion
|
||||||
} else if (userTransaction.transactionTypeId === TransactionTypeId.SEND) {
|
} else if (userTransaction.transactionTypeId === TransactionTypeId.SEND) {
|
||||||
// send coin
|
// send coin
|
||||||
const sendCoin = transaction.transactionSendCoin
|
|
||||||
let otherUser: dbUser | undefined
|
let otherUser: dbUser | undefined
|
||||||
finalTransaction.balance = roundFloorFrom4(sendCoin.amount)
|
finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion
|
||||||
if (sendCoin.userId === user.id) {
|
if (transaction.userId === user.id) {
|
||||||
finalTransaction.type = TransactionType.SEND
|
finalTransaction.type = TransactionType.SEND
|
||||||
otherUser = userIndiced[sendCoin.recipiantUserId]
|
otherUser = userIndiced.find((u) => u.id === transaction.sendReceiverUserId)
|
||||||
// finalTransaction.pubkey = sendCoin.recipiantPublic
|
// finalTransaction.pubkey = sendCoin.recipiantPublic
|
||||||
} else if (sendCoin.recipiantUserId === user.id) {
|
} else if (transaction.sendReceiverUserId === user.id) {
|
||||||
finalTransaction.type = TransactionType.RECIEVE
|
finalTransaction.type = TransactionType.RECIEVE
|
||||||
otherUser = userIndiced[sendCoin.userId]
|
otherUser = userIndiced.find((u) => u.id === transaction.userId)
|
||||||
// finalTransaction.pubkey = sendCoin.senderPublic
|
// finalTransaction.pubkey = sendCoin.senderPublic
|
||||||
} else {
|
} else {
|
||||||
throw new Error('invalid transaction')
|
throw new Error('invalid transaction')
|
||||||
@ -153,61 +145,9 @@ async function calculateAndAddDecayTransactions(
|
|||||||
finalTransactions.push(decayTransaction)
|
finalTransactions.push(decayTransaction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return finalTransactions
|
return finalTransactions
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function
|
|
||||||
async function listTransactions(
|
|
||||||
currentPage: number,
|
|
||||||
pageSize: number,
|
|
||||||
order: Order,
|
|
||||||
user: dbUser,
|
|
||||||
onlyCreations: boolean,
|
|
||||||
): Promise<TransactionList> {
|
|
||||||
let limit = pageSize
|
|
||||||
let offset = 0
|
|
||||||
let skipFirstTransaction = false
|
|
||||||
if (currentPage > 1) {
|
|
||||||
offset = (currentPage - 1) * pageSize - 1
|
|
||||||
limit++
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset && order === Order.ASC) {
|
|
||||||
offset--
|
|
||||||
}
|
|
||||||
const userTransactionRepository = getCustomRepository(UserTransactionRepository)
|
|
||||||
let [userTransactions, userTransactionsCount] = await userTransactionRepository.findByUserPaged(
|
|
||||||
user.id,
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
order,
|
|
||||||
onlyCreations,
|
|
||||||
)
|
|
||||||
skipFirstTransaction = userTransactionsCount > offset + limit
|
|
||||||
const decay = !(currentPage > 1)
|
|
||||||
let transactions: Transaction[] = []
|
|
||||||
if (userTransactions.length) {
|
|
||||||
if (order === Order.DESC) {
|
|
||||||
userTransactions = userTransactions.reverse()
|
|
||||||
}
|
|
||||||
transactions = await calculateAndAddDecayTransactions(
|
|
||||||
userTransactions,
|
|
||||||
user,
|
|
||||||
decay,
|
|
||||||
skipFirstTransaction,
|
|
||||||
)
|
|
||||||
if (order === Order.DESC) {
|
|
||||||
transactions = transactions.reverse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const transactionList = new TransactionList()
|
|
||||||
transactionList.count = userTransactionsCount
|
|
||||||
transactionList.transactions = transactions
|
|
||||||
return transactionList
|
|
||||||
}
|
|
||||||
|
|
||||||
// helper helper function
|
// helper helper function
|
||||||
async function updateStateBalance(
|
async function updateStateBalance(
|
||||||
user: dbUser,
|
user: dbUser,
|
||||||
@ -215,8 +155,7 @@ async function updateStateBalance(
|
|||||||
received: Date,
|
received: Date,
|
||||||
queryRunner: QueryRunner,
|
queryRunner: QueryRunner,
|
||||||
): Promise<dbBalance> {
|
): Promise<dbBalance> {
|
||||||
const balanceRepository = getCustomRepository(BalanceRepository)
|
let balance = await dbBalance.findOne({ userId: user.id })
|
||||||
let balance = await balanceRepository.findByUser(user.id)
|
|
||||||
if (!balance) {
|
if (!balance) {
|
||||||
balance = new dbBalance()
|
balance = new dbBalance()
|
||||||
balance.userId = user.id
|
balance.userId = user.id
|
||||||
@ -289,43 +228,66 @@ export class TransactionResolver {
|
|||||||
): Promise<TransactionList> {
|
): Promise<TransactionList> {
|
||||||
// load user
|
// load user
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
let userEntity: dbUser | undefined
|
const user = userId
|
||||||
if (userId) {
|
? await userRepository.findOneOrFail({ id: userId }, { withDeleted: true })
|
||||||
userEntity = await userRepository.findOneOrFail({ id: userId }, { withDeleted: true })
|
: await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
} else {
|
let limit = pageSize
|
||||||
userEntity = await userRepository.findByPubkeyHex(context.pubKey)
|
let offset = 0
|
||||||
|
let skipFirstTransaction = false
|
||||||
|
if (currentPage > 1) {
|
||||||
|
offset = (currentPage - 1) * pageSize - 1
|
||||||
|
limit++
|
||||||
|
}
|
||||||
|
if (offset && order === Order.ASC) {
|
||||||
|
offset--
|
||||||
|
}
|
||||||
|
const userTransactionRepository = getCustomRepository(UserTransactionRepository)
|
||||||
|
const [userTransactions, userTransactionsCount] =
|
||||||
|
await userTransactionRepository.findByUserPaged(user.id, limit, offset, order, onlyCreations)
|
||||||
|
skipFirstTransaction = userTransactionsCount > offset + limit
|
||||||
|
const decay = !(currentPage > 1)
|
||||||
|
let transactions: Transaction[] = []
|
||||||
|
if (userTransactions.length) {
|
||||||
|
if (order === Order.DESC) {
|
||||||
|
userTransactions.reverse()
|
||||||
|
}
|
||||||
|
transactions = await calculateAndAddDecayTransactions(
|
||||||
|
userTransactions,
|
||||||
|
user,
|
||||||
|
decay,
|
||||||
|
skipFirstTransaction,
|
||||||
|
)
|
||||||
|
if (order === Order.DESC) {
|
||||||
|
transactions.reverse()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const transactions = await listTransactions(
|
const transactionList = new TransactionList()
|
||||||
currentPage,
|
transactionList.count = userTransactionsCount
|
||||||
pageSize,
|
transactionList.transactions = transactions
|
||||||
order,
|
|
||||||
userEntity,
|
|
||||||
onlyCreations,
|
|
||||||
)
|
|
||||||
|
|
||||||
// get gdt sum
|
// get gdt sum
|
||||||
transactions.gdtSum = null
|
transactionList.gdtSum = null
|
||||||
try {
|
try {
|
||||||
const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, {
|
const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, {
|
||||||
email: userEntity.email,
|
email: user.email,
|
||||||
})
|
})
|
||||||
if (resultGDTSum.success) transactions.gdtSum = Number(resultGDTSum.data.sum) || 0
|
if (resultGDTSum.success) transactionList.gdtSum = Number(resultGDTSum.data.sum) || 0
|
||||||
} catch (err: any) {}
|
} catch (err: any) {}
|
||||||
|
|
||||||
// get balance
|
// get balance
|
||||||
const balanceRepository = getCustomRepository(BalanceRepository)
|
const balanceEntity = await dbBalance.findOne({ userId: user.id })
|
||||||
const balanceEntity = await balanceRepository.findByUser(userEntity.id)
|
|
||||||
if (balanceEntity) {
|
if (balanceEntity) {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
transactions.balance = roundFloorFrom4(balanceEntity.amount)
|
transactionList.balance = roundFloorFrom4(balanceEntity.amount)
|
||||||
transactions.decay = roundFloorFrom4(
|
// TODO: Add a decay object here instead of static data representing the decay.
|
||||||
|
transactionList.decay = roundFloorFrom4(
|
||||||
calculateDecay(balanceEntity.amount, balanceEntity.recordDate, now).balance,
|
calculateDecay(balanceEntity.amount, balanceEntity.recordDate, now).balance,
|
||||||
)
|
)
|
||||||
transactions.decayDate = now.toString()
|
transactionList.decayDate = now.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
return transactions
|
return transactionList
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authorized([RIGHTS.SEND_COINS])
|
@Authorized([RIGHTS.SEND_COINS])
|
||||||
@ -333,20 +295,19 @@ export class TransactionResolver {
|
|||||||
async sendCoins(
|
async sendCoins(
|
||||||
@Args() { email, amount, memo }: TransactionSendArgs,
|
@Args() { email, amount, memo }: TransactionSendArgs,
|
||||||
@Ctx() context: any,
|
@Ctx() context: any,
|
||||||
): Promise<string> {
|
): Promise<boolean> {
|
||||||
// TODO this is subject to replay attacks
|
// TODO this is subject to replay attacks
|
||||||
// validate sender user (logged in)
|
|
||||||
const userRepository = getCustomRepository(UserRepository)
|
const userRepository = getCustomRepository(UserRepository)
|
||||||
const senderUser = await userRepository.findByPubkeyHex(context.pubKey)
|
const senderUser = await userRepository.findByPubkeyHex(context.pubKey)
|
||||||
if (senderUser.pubKey.length !== 32) {
|
if (senderUser.pubKey.length !== 32) {
|
||||||
throw new Error('invalid sender public key')
|
throw new Error('invalid sender public key')
|
||||||
}
|
}
|
||||||
|
// validate amount
|
||||||
if (!hasUserAmount(senderUser, amount)) {
|
if (!hasUserAmount(senderUser, amount)) {
|
||||||
throw new Error("user hasn't enough GDD")
|
throw new Error("user hasn't enough GDD or amount is < 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate recipient user
|
// validate recipient user
|
||||||
// TODO: the detour over the public key is unnecessary
|
|
||||||
const recipientUser = await dbUser.findOne({ email: email }, { withDeleted: true })
|
const recipientUser = await dbUser.findOne({ email: email }, { withDeleted: true })
|
||||||
if (!recipientUser) {
|
if (!recipientUser) {
|
||||||
throw new Error('recipient not known')
|
throw new Error('recipient not known')
|
||||||
@ -358,11 +319,6 @@ export class TransactionResolver {
|
|||||||
throw new Error('invalid recipient public key')
|
throw new Error('invalid recipient public key')
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate amount
|
|
||||||
if (amount <= 0) {
|
|
||||||
throw new Error('invalid amount')
|
|
||||||
}
|
|
||||||
|
|
||||||
const centAmount = Math.trunc(amount * 10000)
|
const centAmount = Math.trunc(amount * 10000)
|
||||||
|
|
||||||
const queryRunner = getConnection().createQueryRunner()
|
const queryRunner = getConnection().createQueryRunner()
|
||||||
@ -370,17 +326,16 @@ export class TransactionResolver {
|
|||||||
await queryRunner.startTransaction('READ UNCOMMITTED')
|
await queryRunner.startTransaction('READ UNCOMMITTED')
|
||||||
try {
|
try {
|
||||||
// transaction
|
// transaction
|
||||||
let transaction = new dbTransaction()
|
const transaction = new dbTransaction()
|
||||||
transaction.transactionTypeId = TransactionTypeId.SEND
|
transaction.transactionTypeId = TransactionTypeId.SEND
|
||||||
transaction.memo = memo
|
transaction.memo = memo
|
||||||
|
transaction.userId = senderUser.id
|
||||||
|
transaction.pubkey = senderUser.pubKey
|
||||||
|
transaction.sendReceiverUserId = recipientUser.id
|
||||||
|
transaction.sendReceiverPublicKey = recipientUser.pubKey
|
||||||
|
transaction.amount = BigInt(centAmount)
|
||||||
|
|
||||||
// TODO: NO! this is problematic in its construction
|
await queryRunner.manager.insert(dbTransaction, transaction)
|
||||||
const insertResult = await queryRunner.manager.insert(dbTransaction, transaction)
|
|
||||||
transaction = await queryRunner.manager
|
|
||||||
.findOneOrFail(dbTransaction, insertResult.generatedMaps[0].id)
|
|
||||||
.catch((error) => {
|
|
||||||
throw new Error('error loading saved transaction: ' + error)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Insert Transaction: sender - amount
|
// Insert Transaction: sender - amount
|
||||||
const senderUserTransactionBalance = await addUserTransaction(
|
const senderUserTransactionBalance = await addUserTransaction(
|
||||||
@ -421,18 +376,10 @@ export class TransactionResolver {
|
|||||||
throw new Error('db data corrupted, recipiant')
|
throw new Error('db data corrupted, recipiant')
|
||||||
}
|
}
|
||||||
|
|
||||||
// transactionSendCoin
|
// TODO: WTF?
|
||||||
const transactionSendCoin = new dbTransactionSendCoin()
|
// I just assume that due to implicit type conversion the decimal places were cut.
|
||||||
transactionSendCoin.transactionId = transaction.id
|
// Using `Math.trunc` to simulate this behaviour
|
||||||
transactionSendCoin.userId = senderUser.id
|
transaction.sendSenderFinalBalance = BigInt(Math.trunc(senderStateBalance.amount))
|
||||||
transactionSendCoin.senderPublic = senderUser.pubKey
|
|
||||||
transactionSendCoin.recipiantUserId = recipientUser.id
|
|
||||||
transactionSendCoin.recipiantPublic = recipientUser.pubKey
|
|
||||||
transactionSendCoin.amount = centAmount
|
|
||||||
transactionSendCoin.senderFinalBalance = senderStateBalance.amount
|
|
||||||
await queryRunner.manager.save(transactionSendCoin).catch((error) => {
|
|
||||||
throw new Error('error saving transaction send coin: ' + error)
|
|
||||||
})
|
|
||||||
|
|
||||||
await queryRunner.manager.save(transaction).catch((error) => {
|
await queryRunner.manager.save(transaction).catch((error) => {
|
||||||
throw new Error('error saving transaction with tx hash: ' + error)
|
throw new Error('error saving transaction with tx hash: ' + error)
|
||||||
@ -468,6 +415,6 @@ export class TransactionResolver {
|
|||||||
memo,
|
memo,
|
||||||
})
|
})
|
||||||
|
|
||||||
return 'success'
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,14 +6,13 @@ import gql from 'graphql-tag'
|
|||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import createServer from '../../server/createServer'
|
import createServer from '../../server/createServer'
|
||||||
import { resetDB, initialize } from '@dbTools/helpers'
|
import { resetDB, initialize } from '@dbTools/helpers'
|
||||||
import { getRepository } from 'typeorm'
|
|
||||||
import { LoginUser } from '@entity/LoginUser'
|
|
||||||
import { LoginUserBackup } from '@entity/LoginUserBackup'
|
|
||||||
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
import CONFIG from '../../config'
|
import CONFIG from '../../config'
|
||||||
import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail'
|
import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail'
|
||||||
import { klicktippSignIn } from '../../apis/KlicktippController'
|
// import { klicktippSignIn } from '../../apis/KlicktippController'
|
||||||
|
|
||||||
|
jest.setTimeout(10000)
|
||||||
|
|
||||||
jest.mock('../../mailer/sendAccountActivationEmail', () => {
|
jest.mock('../../mailer/sendAccountActivationEmail', () => {
|
||||||
return {
|
return {
|
||||||
@ -22,12 +21,14 @@ jest.mock('../../mailer/sendAccountActivationEmail', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
jest.mock('../../apis/KlicktippController', () => {
|
jest.mock('../../apis/KlicktippController', () => {
|
||||||
return {
|
return {
|
||||||
__esModule: true,
|
__esModule: true,
|
||||||
klicktippSignIn: jest.fn(),
|
klicktippSignIn: jest.fn(),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
let mutate: any
|
let mutate: any
|
||||||
let con: any
|
let con: any
|
||||||
@ -40,6 +41,11 @@ beforeAll(async () => {
|
|||||||
await resetDB()
|
await resetDB()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await resetDB(true)
|
||||||
|
await con.close()
|
||||||
|
})
|
||||||
|
|
||||||
describe('UserResolver', () => {
|
describe('UserResolver', () => {
|
||||||
describe('createUser', () => {
|
describe('createUser', () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
@ -84,70 +90,32 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('valid input data', () => {
|
describe('valid input data', () => {
|
||||||
let loginUser: LoginUser[]
|
|
||||||
let user: User[]
|
let user: User[]
|
||||||
let loginUserBackup: LoginUserBackup[]
|
|
||||||
let loginEmailOptIn: LoginEmailOptIn[]
|
let loginEmailOptIn: LoginEmailOptIn[]
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
loginUser = await getRepository(LoginUser).createQueryBuilder('login_user').getMany()
|
user = await User.find()
|
||||||
user = await getRepository(User).createQueryBuilder('state_user').getMany()
|
loginEmailOptIn = await LoginEmailOptIn.find()
|
||||||
loginUserBackup = await getRepository(LoginUserBackup)
|
|
||||||
.createQueryBuilder('login_user_backup')
|
|
||||||
.getMany()
|
|
||||||
loginEmailOptIn = await getRepository(LoginEmailOptIn)
|
|
||||||
.createQueryBuilder('login_email_optin')
|
|
||||||
.getMany()
|
|
||||||
emailOptIn = loginEmailOptIn[0].verificationCode.toString()
|
emailOptIn = loginEmailOptIn[0].verificationCode.toString()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('filling all tables', () => {
|
describe('filling all tables', () => {
|
||||||
it('saves the user in login_user table', () => {
|
it('saves the user in login_user table', () => {
|
||||||
expect(loginUser).toEqual([
|
expect(user).toEqual([
|
||||||
{
|
{
|
||||||
id: expect.any(Number),
|
id: expect.any(Number),
|
||||||
email: 'peter@lustig.de',
|
email: 'peter@lustig.de',
|
||||||
firstName: 'Peter',
|
firstName: 'Peter',
|
||||||
lastName: 'Lustig',
|
lastName: 'Lustig',
|
||||||
username: '',
|
|
||||||
description: '',
|
|
||||||
password: '0',
|
password: '0',
|
||||||
pubKey: null,
|
pubKey: null,
|
||||||
privKey: null,
|
privKey: null,
|
||||||
emailHash: expect.any(Buffer),
|
emailHash: expect.any(Buffer),
|
||||||
createdAt: expect.any(Date),
|
createdAt: expect.any(Date),
|
||||||
emailChecked: false,
|
emailChecked: false,
|
||||||
passphraseShown: false,
|
|
||||||
language: 'de',
|
|
||||||
disabled: false,
|
|
||||||
groupId: 1,
|
|
||||||
publisherId: 1234,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('saves the user in state_user table', () => {
|
|
||||||
expect(user).toEqual([
|
|
||||||
{
|
|
||||||
id: expect.any(Number),
|
|
||||||
indexId: 0,
|
|
||||||
groupId: 0,
|
|
||||||
pubkey: expect.any(Buffer),
|
|
||||||
email: 'peter@lustig.de',
|
|
||||||
firstName: 'Peter',
|
|
||||||
lastName: 'Lustig',
|
|
||||||
username: '',
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('saves the user in login_user_backup table', () => {
|
|
||||||
expect(loginUserBackup).toEqual([
|
|
||||||
{
|
|
||||||
id: expect.any(Number),
|
|
||||||
passphrase: expect.any(String),
|
passphrase: expect.any(String),
|
||||||
userId: loginUser[0].id,
|
language: 'de',
|
||||||
mnemonicType: 2,
|
deletedAt: null,
|
||||||
|
publisherId: 1234,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
@ -156,7 +124,7 @@ describe('UserResolver', () => {
|
|||||||
expect(loginEmailOptIn).toEqual([
|
expect(loginEmailOptIn).toEqual([
|
||||||
{
|
{
|
||||||
id: expect.any(Number),
|
id: expect.any(Number),
|
||||||
userId: loginUser[0].id,
|
userId: user[0].id,
|
||||||
verificationCode: expect.any(String),
|
verificationCode: expect.any(String),
|
||||||
emailOptInTypeId: 1,
|
emailOptInTypeId: 1,
|
||||||
createdAt: expect.any(Date),
|
createdAt: expect.any(Date),
|
||||||
@ -196,9 +164,7 @@ describe('UserResolver', () => {
|
|||||||
mutation,
|
mutation,
|
||||||
variables: { ...variables, email: 'bibi@bloxberg.de', language: 'es' },
|
variables: { ...variables, email: 'bibi@bloxberg.de', language: 'es' },
|
||||||
})
|
})
|
||||||
await expect(
|
await expect(User.find()).resolves.toEqual(
|
||||||
getRepository(LoginUser).createQueryBuilder('login_user').getMany(),
|
|
||||||
).resolves.toEqual(
|
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
email: 'bibi@bloxberg.de',
|
email: 'bibi@bloxberg.de',
|
||||||
@ -215,9 +181,7 @@ describe('UserResolver', () => {
|
|||||||
mutation,
|
mutation,
|
||||||
variables: { ...variables, email: 'raeuber@hotzenplotz.de', publisherId: undefined },
|
variables: { ...variables, email: 'raeuber@hotzenplotz.de', publisherId: undefined },
|
||||||
})
|
})
|
||||||
await expect(
|
await expect(User.find()).resolves.toEqual(
|
||||||
getRepository(LoginUser).createQueryBuilder('login_user').getMany(),
|
|
||||||
).resolves.toEqual(
|
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
email: 'raeuber@hotzenplotz.de',
|
email: 'raeuber@hotzenplotz.de',
|
||||||
@ -265,23 +229,17 @@ describe('UserResolver', () => {
|
|||||||
let emailOptIn: string
|
let emailOptIn: string
|
||||||
|
|
||||||
describe('valid optin code and valid password', () => {
|
describe('valid optin code and valid password', () => {
|
||||||
let loginUser: any
|
|
||||||
let newLoginUser: any
|
|
||||||
let newUser: any
|
let newUser: any
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await mutate({ mutation: createUserMutation, variables: createUserVariables })
|
await mutate({ mutation: createUserMutation, variables: createUserVariables })
|
||||||
const loginEmailOptIn = await getRepository(LoginEmailOptIn)
|
const loginEmailOptIn = await LoginEmailOptIn.find()
|
||||||
.createQueryBuilder('login_email_optin')
|
|
||||||
.getMany()
|
|
||||||
loginUser = await getRepository(LoginUser).createQueryBuilder('login_user').getMany()
|
|
||||||
emailOptIn = loginEmailOptIn[0].verificationCode.toString()
|
emailOptIn = loginEmailOptIn[0].verificationCode.toString()
|
||||||
result = await mutate({
|
result = await mutate({
|
||||||
mutation: setPasswordMutation,
|
mutation: setPasswordMutation,
|
||||||
variables: { code: emailOptIn, password: 'Aa12345_' },
|
variables: { code: emailOptIn, password: 'Aa12345_' },
|
||||||
})
|
})
|
||||||
newLoginUser = await getRepository(LoginUser).createQueryBuilder('login_user').getMany()
|
newUser = await User.find()
|
||||||
newUser = await getRepository(User).createQueryBuilder('state_user').getMany()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@ -289,38 +247,27 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('sets email checked to true', () => {
|
it('sets email checked to true', () => {
|
||||||
expect(newLoginUser[0].emailChecked).toBeTruthy()
|
expect(newUser[0].emailChecked).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('updates the password', () => {
|
it('updates the password', () => {
|
||||||
expect(newLoginUser[0].password).toEqual('3917921995996627700')
|
expect(newUser[0].password).toEqual('3917921995996627700')
|
||||||
})
|
|
||||||
|
|
||||||
it('updates the public Key on both user tables', () => {
|
|
||||||
expect(newLoginUser[0].pubKey).toEqual(expect.any(Buffer))
|
|
||||||
expect(newLoginUser[0].pubKey).not.toEqual(loginUser[0].pubKey)
|
|
||||||
expect(newLoginUser[0].pubKey).toEqual(newUser[0].pubkey)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('updates the private Key', () => {
|
|
||||||
expect(newLoginUser[0].privKey).toEqual(expect.any(Buffer))
|
|
||||||
expect(newLoginUser[0].privKey).not.toEqual(loginUser[0].privKey)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('removes the optin', async () => {
|
it('removes the optin', async () => {
|
||||||
await expect(
|
await expect(LoginEmailOptIn.find()).resolves.toHaveLength(0)
|
||||||
getRepository(LoginEmailOptIn).createQueryBuilder('login_email_optin').getMany(),
|
|
||||||
).resolves.toHaveLength(0)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
it('calls the klicktipp API', () => {
|
it('calls the klicktipp API', () => {
|
||||||
expect(klicktippSignIn).toBeCalledWith(
|
expect(klicktippSignIn).toBeCalledWith(
|
||||||
loginUser[0].email,
|
user[0].email,
|
||||||
loginUser[0].language,
|
user[0].language,
|
||||||
loginUser[0].firstName,
|
user[0].firstName,
|
||||||
loginUser[0].lastName,
|
user[0].lastName,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
it('returns true', () => {
|
it('returns true', () => {
|
||||||
expect(result).toBeTruthy()
|
expect(result).toBeTruthy()
|
||||||
@ -330,9 +277,7 @@ describe('UserResolver', () => {
|
|||||||
describe('no valid password', () => {
|
describe('no valid password', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await mutate({ mutation: createUserMutation, variables: createUserVariables })
|
await mutate({ mutation: createUserMutation, variables: createUserVariables })
|
||||||
const loginEmailOptIn = await getRepository(LoginEmailOptIn)
|
const loginEmailOptIn = await LoginEmailOptIn.find()
|
||||||
.createQueryBuilder('login_email_optin')
|
|
||||||
.getMany()
|
|
||||||
emailOptIn = loginEmailOptIn[0].verificationCode.toString()
|
emailOptIn = loginEmailOptIn[0].verificationCode.toString()
|
||||||
result = await mutate({
|
result = await mutate({
|
||||||
mutation: setPasswordMutation,
|
mutation: setPasswordMutation,
|
||||||
@ -380,8 +325,3 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(async () => {
|
|
||||||
await resetDB(true)
|
|
||||||
await con.close()
|
|
||||||
})
|
|
||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql'
|
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql'
|
||||||
import { getConnection, getCustomRepository, getRepository, QueryRunner } from '@dbTools/typeorm'
|
import { getConnection, getCustomRepository, QueryRunner } from '@dbTools/typeorm'
|
||||||
import CONFIG from '../../config'
|
import CONFIG from '../../config'
|
||||||
import { User } from '../model/User'
|
import { User } from '../model/User'
|
||||||
import { User as DbUser } from '@entity/User'
|
import { User as DbUser } from '@entity/User'
|
||||||
|
|||||||
@ -4,8 +4,7 @@
|
|||||||
import { ApolloLogPlugin, LogMutateData } from 'apollo-log'
|
import { ApolloLogPlugin, LogMutateData } from 'apollo-log'
|
||||||
import cloneDeep from 'lodash.clonedeep'
|
import cloneDeep from 'lodash.clonedeep'
|
||||||
|
|
||||||
const plugins = [
|
const setHeadersPlugin = {
|
||||||
{
|
|
||||||
requestDidStart() {
|
requestDidStart() {
|
||||||
return {
|
return {
|
||||||
willSendResponse(requestContext: any) {
|
willSendResponse(requestContext: any) {
|
||||||
@ -21,8 +20,9 @@ const plugins = [
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
ApolloLogPlugin({
|
|
||||||
|
const apolloLogPlugin = ApolloLogPlugin({
|
||||||
mutate: (data: LogMutateData) => {
|
mutate: (data: LogMutateData) => {
|
||||||
// We need to deep clone the object in order to not modify the actual request
|
// We need to deep clone the object in order to not modify the actual request
|
||||||
const dataCopy = cloneDeep(data)
|
const dataCopy = cloneDeep(data)
|
||||||
@ -37,7 +37,9 @@ const plugins = [
|
|||||||
|
|
||||||
return dataCopy
|
return dataCopy
|
||||||
},
|
},
|
||||||
}),
|
})
|
||||||
]
|
|
||||||
|
const plugins =
|
||||||
|
process.env.NODE_ENV === 'development' ? [setHeadersPlugin] : [setHeadersPlugin, apolloLogPlugin]
|
||||||
|
|
||||||
export default plugins
|
export default plugins
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
import { EntityRepository, Repository } from '@dbTools/typeorm'
|
|
||||||
import { Balance } from '@entity/Balance'
|
|
||||||
|
|
||||||
@EntityRepository(Balance)
|
|
||||||
export class BalanceRepository extends Repository<Balance> {
|
|
||||||
findByUser(userId: number): Promise<Balance | undefined> {
|
|
||||||
return this.createQueryBuilder('balance').where('balance.userId = :userId', { userId }).getOne()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
import { EntityRepository, Repository } from '@dbTools/typeorm'
|
|
||||||
import { Transaction } from '@entity/Transaction'
|
|
||||||
|
|
||||||
@EntityRepository(Transaction)
|
|
||||||
export class TransactionRepository extends Repository<Transaction> {
|
|
||||||
async joinFullTransactionsByIds(transactionIds: number[]): Promise<Transaction[]> {
|
|
||||||
return this.createQueryBuilder('transaction')
|
|
||||||
.where('transaction.id IN (:...transactions)', { transactions: transactionIds })
|
|
||||||
.leftJoinAndSelect(
|
|
||||||
'transaction.transactionSendCoin',
|
|
||||||
'transactionSendCoin',
|
|
||||||
// 'transactionSendCoin.transaction_id = transaction.id',
|
|
||||||
)
|
|
||||||
.leftJoinAndSelect(
|
|
||||||
'transaction.transactionCreation',
|
|
||||||
'transactionCreation',
|
|
||||||
// 'transactionSendCoin.transaction_id = transaction.id',
|
|
||||||
)
|
|
||||||
.getMany()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -10,16 +10,11 @@ export class UserRepository extends Repository<User> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getUsersIndiced(userIds: number[]): Promise<User[]> {
|
async getUsersIndiced(userIds: number[]): Promise<User[]> {
|
||||||
if (!userIds.length) return []
|
return this.createQueryBuilder('user')
|
||||||
const users = await this.createQueryBuilder('user')
|
.withDeleted() // We need to show the name for deleted users for old transactions
|
||||||
.select(['user.id', 'user.firstName', 'user.lastName', 'user.email'])
|
.select(['user.id', 'user.firstName', 'user.lastName', 'user.email'])
|
||||||
.where('user.id IN (:...users)', { users: userIds })
|
.where('user.id IN (:...userIds)', { userIds })
|
||||||
.getMany()
|
.getMany()
|
||||||
const usersIndiced: User[] = []
|
|
||||||
users.forEach((value) => {
|
|
||||||
usersIndiced[value.id] = value
|
|
||||||
})
|
|
||||||
return usersIndiced
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async findBySearchCriteriaPagedFiltered(
|
async findBySearchCriteriaPagedFiltered(
|
||||||
|
|||||||
6
backend/test/testSetup.ts
Normal file
6
backend/test/testSetup.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/* eslint-disable no-console */
|
||||||
|
|
||||||
|
// disable console.info for apollo log
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
|
console.info = () => {}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
||||||
import { TransactionCreation } from '../TransactionCreation'
|
import { TransactionCreation } from './TransactionCreation'
|
||||||
import { TransactionSendCoin } from '../TransactionSendCoin'
|
import { TransactionSendCoin } from './TransactionSendCoin'
|
||||||
|
|
||||||
@Entity('transactions')
|
@Entity('transactions')
|
||||||
export class Transaction extends BaseEntity {
|
export class Transaction extends BaseEntity {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
||||||
import { TransactionCreation } from '../TransactionCreation'
|
import { TransactionCreation } from '../0001-init_db/TransactionCreation'
|
||||||
import { TransactionSendCoin } from '../TransactionSendCoin'
|
import { TransactionSendCoin } from '../0001-init_db/TransactionSendCoin'
|
||||||
|
|
||||||
@Entity('transactions')
|
@Entity('transactions')
|
||||||
export class Transaction extends BaseEntity {
|
export class Transaction extends BaseEntity {
|
||||||
|
|||||||
@ -0,0 +1,70 @@
|
|||||||
|
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||||
|
|
||||||
|
@Entity('transactions')
|
||||||
|
export class Transaction extends BaseEntity {
|
||||||
|
// TODO the id is defined as bigint(20) - there might be problems with that: https://github.com/typeorm/typeorm/issues/2400
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ name: 'transaction_type_id', unsigned: true, nullable: false })
|
||||||
|
transactionTypeId: number
|
||||||
|
|
||||||
|
@Column({ name: 'user_id', unsigned: true, nullable: false })
|
||||||
|
userId: number
|
||||||
|
|
||||||
|
@Column({ type: 'bigint', nullable: false })
|
||||||
|
amount: BigInt
|
||||||
|
|
||||||
|
@Column({ name: 'tx_hash', type: 'binary', length: 48, default: null, nullable: true })
|
||||||
|
txHash: Buffer
|
||||||
|
|
||||||
|
@Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
memo: string
|
||||||
|
|
||||||
|
@Column({ type: 'timestamp', nullable: false, default: () => 'CURRENT_TIMESTAMP' })
|
||||||
|
received: Date
|
||||||
|
|
||||||
|
@Column({ type: 'binary', length: 64, nullable: true, default: null })
|
||||||
|
signature: Buffer
|
||||||
|
|
||||||
|
@Column({ type: 'binary', length: 32, nullable: true, default: null })
|
||||||
|
pubkey: Buffer
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'creation_ident_hash',
|
||||||
|
type: 'binary',
|
||||||
|
length: 32,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
creationIdentHash: Buffer
|
||||||
|
|
||||||
|
@Column({ name: 'creation_date', type: 'timestamp', nullable: true, default: null })
|
||||||
|
creationDate: Date
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'send_receiver_public_key',
|
||||||
|
type: 'binary',
|
||||||
|
length: 32,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
sendReceiverPublicKey: Buffer | null
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'send_receiver_user_id',
|
||||||
|
type: 'int',
|
||||||
|
unsigned: true,
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
sendReceiverUserId?: number | null
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'send_sender_final_balance',
|
||||||
|
type: 'bigint',
|
||||||
|
nullable: true,
|
||||||
|
default: null,
|
||||||
|
})
|
||||||
|
sendSenderFinalBalance: BigInt | null
|
||||||
|
}
|
||||||
@ -1 +1 @@
|
|||||||
export { Transaction } from './0016-transaction_signatures/Transaction'
|
export { Transaction } from './0024-combine_transaction_tables/Transaction'
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export { TransactionCreation } from './0001-init_db/TransactionCreation'
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export { TransactionSendCoin } from './0001-init_db/TransactionSendCoin'
|
|
||||||
@ -4,8 +4,6 @@ import { LoginEmailOptIn } from './LoginEmailOptIn'
|
|||||||
import { Migration } from './Migration'
|
import { Migration } from './Migration'
|
||||||
import { ServerUser } from './ServerUser'
|
import { ServerUser } from './ServerUser'
|
||||||
import { Transaction } from './Transaction'
|
import { Transaction } from './Transaction'
|
||||||
import { TransactionCreation } from './TransactionCreation'
|
|
||||||
import { TransactionSendCoin } from './TransactionSendCoin'
|
|
||||||
import { User } from './User'
|
import { User } from './User'
|
||||||
import { UserSetting } from './UserSetting'
|
import { UserSetting } from './UserSetting'
|
||||||
import { UserTransaction } from './UserTransaction'
|
import { UserTransaction } from './UserTransaction'
|
||||||
@ -19,8 +17,6 @@ export const entities = [
|
|||||||
Migration,
|
Migration,
|
||||||
ServerUser,
|
ServerUser,
|
||||||
Transaction,
|
Transaction,
|
||||||
TransactionCreation,
|
|
||||||
TransactionSendCoin,
|
|
||||||
User,
|
User,
|
||||||
UserSetting,
|
UserSetting,
|
||||||
UserTransaction,
|
UserTransaction,
|
||||||
|
|||||||
@ -5,19 +5,20 @@
|
|||||||
* This also removes the trailing space
|
* This also removes the trailing space
|
||||||
*/
|
*/
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
const TARGET_MNEMONIC_TYPE = 2
|
const TARGET_MNEMONIC_TYPE = 2
|
||||||
const PHRASE_WORD_COUNT = 24
|
const PHRASE_WORD_COUNT = 24
|
||||||
const WORDS_MNEMONIC_0 = fs
|
const WORDS_MNEMONIC_0 = fs
|
||||||
.readFileSync('src/config/mnemonic.uncompressed_buffer18112.txt')
|
.readFileSync(path.resolve(__dirname, '../src/config/mnemonic.uncompressed_buffer18112.txt'))
|
||||||
.toString()
|
.toString()
|
||||||
.split(',')
|
.split(',')
|
||||||
const WORDS_MNEMONIC_1 = fs
|
const WORDS_MNEMONIC_1 = fs
|
||||||
.readFileSync('src/config/mnemonic.uncompressed_buffer18113.txt')
|
.readFileSync(path.resolve(__dirname, '../src/config/mnemonic.uncompressed_buffer18113.txt'))
|
||||||
.toString()
|
.toString()
|
||||||
.split(',')
|
.split(',')
|
||||||
const WORDS_MNEMONIC_2 = fs
|
const WORDS_MNEMONIC_2 = fs
|
||||||
.readFileSync('src/config/mnemonic.uncompressed_buffer13116.txt')
|
.readFileSync(path.resolve(__dirname, '../src/config/mnemonic.uncompressed_buffer13116.txt'))
|
||||||
.toString()
|
.toString()
|
||||||
.split(',')
|
.split(',')
|
||||||
const WORDS_MNEMONIC = [WORDS_MNEMONIC_0, WORDS_MNEMONIC_1, WORDS_MNEMONIC_2]
|
const WORDS_MNEMONIC = [WORDS_MNEMONIC_0, WORDS_MNEMONIC_1, WORDS_MNEMONIC_2]
|
||||||
|
|||||||
125
database/migrations/0024-combine_transaction_tables.ts
Normal file
125
database/migrations/0024-combine_transaction_tables.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/* MIGRATION TO COMBINE ALL TRANSACTION TABLES
|
||||||
|
*
|
||||||
|
* Combine all transaction tables into one table with all data
|
||||||
|
*/
|
||||||
|
|
||||||
|
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
// Create new `user_id` column (former `state_user_id`), with a temporary default of null
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `transactions` ADD COLUMN `user_id` int(10) unsigned DEFAULT NULL AFTER `transaction_type_id`;',
|
||||||
|
)
|
||||||
|
// Create new `amount` column, with a temporary default of null
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `transactions` ADD COLUMN `amount` bigint(20) DEFAULT NULL AFTER `user_id`;',
|
||||||
|
)
|
||||||
|
// Create new `creation_ident_hash` column (former `ident_hash`)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `transactions` ADD COLUMN `creation_ident_hash` binary(32) DEFAULT NULL AFTER `pubkey`;',
|
||||||
|
)
|
||||||
|
// Create new `creation_date` column (former `target_date`)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `transactions` ADD COLUMN `creation_date` timestamp NULL DEFAULT NULL AFTER `creation_ident_hash`;',
|
||||||
|
)
|
||||||
|
// Create new `send_receiver_public_key` column (former `receiver_public_key`)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `transactions` ADD COLUMN `send_receiver_public_key` binary(32) DEFAULT NULL AFTER `creation_date`;',
|
||||||
|
)
|
||||||
|
// Create new `send_receiver_user_id` column (former `receiver_user_id`)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `transactions` ADD COLUMN `send_receiver_user_id` int(10) unsigned DEFAULT NULL AFTER `send_receiver_public_key`;',
|
||||||
|
)
|
||||||
|
// Create new `send_sender_final_balance` column (former `sender_final_balance`)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `transactions` ADD COLUMN `send_sender_final_balance` bigint(20) DEFAULT NULL AFTER `send_receiver_user_id`;',
|
||||||
|
)
|
||||||
|
|
||||||
|
// Insert Data from `transaction_creations`
|
||||||
|
await queryFn(`
|
||||||
|
UPDATE transactions
|
||||||
|
INNER JOIN transaction_creations ON transaction_creations.transaction_id = transactions.id
|
||||||
|
SET transactions.user_id = transaction_creations.state_user_id,
|
||||||
|
transactions.amount = transaction_creations.amount,
|
||||||
|
transactions.creation_ident_hash = transaction_creations.ident_hash,
|
||||||
|
transactions.creation_date = transaction_creations.target_date;
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Insert Data from `transaction_send_coins`
|
||||||
|
// Note: we drop `sender_public_key` in favor of `pubkey` from the original `transactions` table
|
||||||
|
// the data from `transaction_send_coins` seems incomplete for half the dataset (zeroed pubkey)
|
||||||
|
// with one key being different.
|
||||||
|
await queryFn(`
|
||||||
|
UPDATE transactions
|
||||||
|
INNER JOIN transaction_send_coins ON transaction_send_coins.transaction_id = transactions.id
|
||||||
|
SET transactions.user_id = transaction_send_coins.state_user_id,
|
||||||
|
transactions.amount = transaction_send_coins.amount,
|
||||||
|
transactions.send_receiver_public_key = transaction_send_coins.receiver_public_key,
|
||||||
|
transactions.send_receiver_user_id = transaction_send_coins.receiver_user_id,
|
||||||
|
transactions.send_sender_final_balance = transaction_send_coins.sender_final_balance;
|
||||||
|
`)
|
||||||
|
|
||||||
|
// Modify defaults after our inserts
|
||||||
|
await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `user_id` int(10) unsigned NOT NULL;')
|
||||||
|
await queryFn('ALTER TABLE `transactions` MODIFY COLUMN `amount` bigint(20) NOT NULL;')
|
||||||
|
|
||||||
|
// Drop table `transaction_creations`
|
||||||
|
await queryFn('DROP TABLE `transaction_creations`;')
|
||||||
|
// Drop table `transaction_send_coins`
|
||||||
|
await queryFn('DROP TABLE `transaction_send_coins`;')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(`
|
||||||
|
CREATE TABLE \`transaction_send_coins\` (
|
||||||
|
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
\`transaction_id\` int(10) unsigned NOT NULL,
|
||||||
|
\`sender_public_key\` binary(32) NOT NULL,
|
||||||
|
\`state_user_id\` int(10) unsigned DEFAULT 0,
|
||||||
|
\`receiver_public_key\` binary(32) NOT NULL,
|
||||||
|
\`receiver_user_id\` int(10) unsigned DEFAULT 0,
|
||||||
|
\`amount\` bigint(20) NOT NULL,
|
||||||
|
\`sender_final_balance\` bigint(20) NOT NULL,
|
||||||
|
PRIMARY KEY (\`id\`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=659 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
`)
|
||||||
|
await queryFn(`
|
||||||
|
CREATE TABLE \`transaction_creations\` (
|
||||||
|
\`id\` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
\`transaction_id\` int(10) unsigned NOT NULL,
|
||||||
|
\`state_user_id\` int(10) unsigned NOT NULL,
|
||||||
|
\`amount\` bigint(20) NOT NULL,
|
||||||
|
\`ident_hash\` binary(32) DEFAULT NULL,
|
||||||
|
\`target_date\` timestamp NULL DEFAULT NULL,
|
||||||
|
PRIMARY KEY (\`id\`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=2769 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
`)
|
||||||
|
|
||||||
|
await queryFn(`
|
||||||
|
INSERT INTO transaction_send_coins
|
||||||
|
( transaction_id, sender_public_key, state_user_id,
|
||||||
|
receiver_public_key, receiver_user_id,
|
||||||
|
amount, sender_final_balance )
|
||||||
|
( SELECT id AS transaction_id, IF(pubkey, pubkey, 0x00000000000000000000000000000000) AS sender_public_key, user_id AS state_user_id,
|
||||||
|
send_receiver_public_key AS receiver_public_key, send_receiver_user_id AS receiver_user_id,
|
||||||
|
amount, send_sender_final_balance AS sender_final_balance
|
||||||
|
FROM transactions
|
||||||
|
WHERE transaction_type_id = 2 );
|
||||||
|
`)
|
||||||
|
|
||||||
|
await queryFn(`
|
||||||
|
INSERT INTO transaction_creations
|
||||||
|
( transaction_id, state_user_id,
|
||||||
|
amount, ident_hash, target_date )
|
||||||
|
( SELECT id AS transaction_id, user_id AS state_user_id,
|
||||||
|
amount, creation_ident_hash AS ident_hash, creation_date AS target_date
|
||||||
|
FROM transactions
|
||||||
|
WHERE transaction_type_id = 1 );
|
||||||
|
`)
|
||||||
|
|
||||||
|
await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_sender_final_balance`;')
|
||||||
|
await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_receiver_user_id`;')
|
||||||
|
await queryFn('ALTER TABLE `transactions` DROP COLUMN `send_receiver_public_key`;')
|
||||||
|
await queryFn('ALTER TABLE `transactions` DROP COLUMN `creation_date`;')
|
||||||
|
await queryFn('ALTER TABLE `transactions` DROP COLUMN `creation_ident_hash`;')
|
||||||
|
await queryFn('ALTER TABLE `transactions` DROP COLUMN `amount`;')
|
||||||
|
await queryFn('ALTER TABLE `transactions` DROP COLUMN `user_id`;')
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "tsc --build",
|
"build": "mkdir -p build/src/config/ && cp src/config/*.txt build/src/config/ && tsc --build",
|
||||||
"clean": "tsc --build --clean",
|
"clean": "tsc --build --clean",
|
||||||
"up": "node build/src/index.js up",
|
"up": "node build/src/index.js up",
|
||||||
"down": "node build/src/index.js down",
|
"down": "node build/src/index.js down",
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
import Faker from 'faker'
|
|
||||||
import { define } from 'typeorm-seeding'
|
|
||||||
import { TransactionCreation } from '../../entity/TransactionCreation'
|
|
||||||
import { TransactionCreationContext } from '../interface/TransactionContext'
|
|
||||||
|
|
||||||
define(TransactionCreation, (faker: typeof Faker, context?: TransactionCreationContext) => {
|
|
||||||
if (!context || !context.userId || !context.transaction) {
|
|
||||||
throw new Error('TransactionCreation: No userId and/or transaction present!')
|
|
||||||
}
|
|
||||||
|
|
||||||
const transactionCreation = new TransactionCreation()
|
|
||||||
transactionCreation.userId = context.userId
|
|
||||||
transactionCreation.amount = context.amount ? context.amount : 100000
|
|
||||||
transactionCreation.targetDate = context.targetDate ? context.targetDate : new Date()
|
|
||||||
transactionCreation.transaction = context.transaction
|
|
||||||
|
|
||||||
return transactionCreation
|
|
||||||
})
|
|
||||||
@ -5,17 +5,24 @@ import { TransactionContext } from '../interface/TransactionContext'
|
|||||||
import { randomBytes } from 'crypto'
|
import { randomBytes } from 'crypto'
|
||||||
|
|
||||||
define(Transaction, (faker: typeof Faker, context?: TransactionContext) => {
|
define(Transaction, (faker: typeof Faker, context?: TransactionContext) => {
|
||||||
if (!context) context = {}
|
if (!context) {
|
||||||
|
throw new Error('TransactionContext not well defined.')
|
||||||
|
}
|
||||||
|
|
||||||
const transaction = new Transaction()
|
const transaction = new Transaction()
|
||||||
transaction.transactionTypeId = context.transactionTypeId ? context.transactionTypeId : 2
|
transaction.transactionTypeId = context.transactionTypeId // || 2
|
||||||
transaction.txHash = context.txHash ? context.txHash : randomBytes(48)
|
transaction.userId = context.userId
|
||||||
transaction.memo = context.memo || context.memo === '' ? context.memo : faker.lorem.sentence()
|
transaction.amount = context.amount
|
||||||
transaction.received = context.received ? context.received : new Date()
|
transaction.txHash = context.txHash || randomBytes(48)
|
||||||
transaction.signature = context.signature ? context.signature : randomBytes(64)
|
transaction.memo = context.memo
|
||||||
transaction.pubkey = context.signaturePubkey ? context.signaturePubkey : randomBytes(32)
|
transaction.received = context.received || new Date()
|
||||||
if (context.transactionSendCoin) transaction.transactionSendCoin = context.transactionSendCoin
|
transaction.signature = context.signature || randomBytes(64)
|
||||||
if (context.transactionCreation) transaction.transactionCreation = context.transactionCreation
|
transaction.pubkey = context.pubkey || randomBytes(32)
|
||||||
|
transaction.creationIdentHash = context.creationIdentHash || randomBytes(32)
|
||||||
|
transaction.creationDate = context.creationDate || new Date()
|
||||||
|
transaction.sendReceiverPublicKey = context.sendReceiverPublicKey || null
|
||||||
|
transaction.sendReceiverUserId = context.sendReceiverUserId || null
|
||||||
|
transaction.sendSenderFinalBalance = context.sendSenderFinalBalance || null
|
||||||
|
|
||||||
return transaction
|
return transaction
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import Faker from 'faker'
|
import Faker from 'faker'
|
||||||
import { define } from 'typeorm-seeding'
|
import { define } from 'typeorm-seeding'
|
||||||
import { User } from '../../entity/User'
|
import { User } from '../../entity/User'
|
||||||
import { randomBytes, randomInt } from 'crypto'
|
import { randomBytes } from 'crypto'
|
||||||
import { UserContext } from '../interface/UserContext'
|
import { UserContext } from '../interface/UserContext'
|
||||||
|
|
||||||
define(User, (faker: typeof Faker, context?: UserContext) => {
|
define(User, (faker: typeof Faker, context?: UserContext) => {
|
||||||
|
|||||||
@ -1,18 +1,20 @@
|
|||||||
import { Transaction } from '../../entity/Transaction'
|
import { Transaction } from '../../entity/Transaction'
|
||||||
import { TransactionSendCoin } from '../../entity/TransactionSendCoin'
|
|
||||||
import { TransactionCreation } from '../../entity/TransactionCreation'
|
|
||||||
import { User } from '../../entity/User'
|
import { User } from '../../entity/User'
|
||||||
|
|
||||||
export interface TransactionContext {
|
export interface TransactionContext {
|
||||||
transactionTypeId?: number
|
transactionTypeId: number
|
||||||
|
userId: number
|
||||||
|
amount: BigInt
|
||||||
txHash?: Buffer
|
txHash?: Buffer
|
||||||
memo?: string
|
memo: string
|
||||||
received?: Date
|
received?: Date
|
||||||
blockchainTypeId?: number
|
|
||||||
signature?: Buffer
|
signature?: Buffer
|
||||||
signaturePubkey?: Buffer
|
pubkey?: Buffer
|
||||||
transactionSendCoin?: TransactionSendCoin
|
creationIdentHash?: Buffer
|
||||||
transactionCreation?: TransactionCreation
|
creationDate?: Date
|
||||||
|
sendReceiverPublicKey?: Buffer
|
||||||
|
sendReceiverUserId?: number
|
||||||
|
sendSenderFinalBalance?: BigInt
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BalanceContext {
|
export interface BalanceContext {
|
||||||
@ -32,13 +34,6 @@ export interface TransactionSendCoinContext {
|
|||||||
transaction?: Transaction
|
transaction?: Transaction
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TransactionCreationContext {
|
|
||||||
userId?: number
|
|
||||||
amount?: number
|
|
||||||
targetDate?: Date
|
|
||||||
transaction?: Transaction
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserTransactionContext {
|
export interface UserTransactionContext {
|
||||||
userId?: number
|
userId?: number
|
||||||
transactionId?: number
|
transactionId?: number
|
||||||
|
|||||||
@ -11,7 +11,6 @@ export interface UserInterface {
|
|||||||
emailChecked?: boolean
|
emailChecked?: boolean
|
||||||
language?: string
|
language?: string
|
||||||
deletedAt?: Date
|
deletedAt?: Date
|
||||||
groupId?: number
|
|
||||||
publisherId?: number
|
publisherId?: number
|
||||||
passphrase?: string
|
passphrase?: string
|
||||||
// from server user
|
// from server user
|
||||||
@ -27,9 +26,8 @@ export interface UserInterface {
|
|||||||
// balance
|
// balance
|
||||||
balanceModified?: Date
|
balanceModified?: Date
|
||||||
recordDate?: Date
|
recordDate?: Date
|
||||||
targetDate?: Date
|
creationDate?: Date
|
||||||
amount?: number
|
amount?: number
|
||||||
creationTxHash?: Buffer
|
creationTxHash?: Buffer
|
||||||
signature?: Buffer
|
signature?: Buffer
|
||||||
signaturePubkey?: Buffer
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { UserContext, ServerUserContext } from '../../interface/UserContext'
|
|||||||
import {
|
import {
|
||||||
BalanceContext,
|
BalanceContext,
|
||||||
TransactionContext,
|
TransactionContext,
|
||||||
TransactionCreationContext,
|
|
||||||
UserTransactionContext,
|
UserTransactionContext,
|
||||||
} from '../../interface/TransactionContext'
|
} from '../../interface/TransactionContext'
|
||||||
import { UserInterface } from '../../interface/UserInterface'
|
import { UserInterface } from '../../interface/UserInterface'
|
||||||
@ -11,7 +10,6 @@ import { ServerUser } from '../../../entity/ServerUser'
|
|||||||
import { Balance } from '../../../entity/Balance'
|
import { Balance } from '../../../entity/Balance'
|
||||||
import { Transaction } from '../../../entity/Transaction'
|
import { Transaction } from '../../../entity/Transaction'
|
||||||
import { UserTransaction } from '../../../entity/UserTransaction'
|
import { UserTransaction } from '../../../entity/UserTransaction'
|
||||||
import { TransactionCreation } from '../../../entity/TransactionCreation'
|
|
||||||
import { Factory } from 'typeorm-seeding'
|
import { Factory } from 'typeorm-seeding'
|
||||||
|
|
||||||
export const userSeeder = async (factory: Factory, userData: UserInterface): Promise<void> => {
|
export const userSeeder = async (factory: Factory, userData: UserInterface): Promise<void> => {
|
||||||
@ -25,10 +23,7 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro
|
|||||||
// create some GDD for the user
|
// create some GDD for the user
|
||||||
await factory(Balance)(createBalanceContext(userData, user)).create()
|
await factory(Balance)(createBalanceContext(userData, user)).create()
|
||||||
const transaction = await factory(Transaction)(
|
const transaction = await factory(Transaction)(
|
||||||
createTransactionContext(userData, 1, 'Herzlich Willkommen bei Gradido!'),
|
createTransactionContext(userData, user, 1, 'Herzlich Willkommen bei Gradido!'),
|
||||||
).create()
|
|
||||||
await factory(TransactionCreation)(
|
|
||||||
createTransactionCreationContext(userData, user, transaction),
|
|
||||||
).create()
|
).create()
|
||||||
await factory(UserTransaction)(
|
await factory(UserTransaction)(
|
||||||
createUserTransactionContext(userData, user, transaction),
|
createUserTransactionContext(userData, user, transaction),
|
||||||
@ -76,27 +71,18 @@ const createBalanceContext = (context: UserInterface, user: User): BalanceContex
|
|||||||
|
|
||||||
const createTransactionContext = (
|
const createTransactionContext = (
|
||||||
context: UserInterface,
|
context: UserInterface,
|
||||||
|
user: User,
|
||||||
type: number,
|
type: number,
|
||||||
memo: string,
|
memo: string,
|
||||||
): TransactionContext => {
|
): TransactionContext => {
|
||||||
return {
|
return {
|
||||||
transactionTypeId: type,
|
transactionTypeId: type,
|
||||||
|
userId: user.id,
|
||||||
|
amount: BigInt(context.amount || 100000),
|
||||||
txHash: context.creationTxHash,
|
txHash: context.creationTxHash,
|
||||||
memo,
|
memo,
|
||||||
received: context.recordDate,
|
received: context.recordDate,
|
||||||
}
|
creationDate: context.creationDate,
|
||||||
}
|
|
||||||
|
|
||||||
const createTransactionCreationContext = (
|
|
||||||
context: UserInterface,
|
|
||||||
user: User,
|
|
||||||
transaction: Transaction,
|
|
||||||
): TransactionCreationContext => {
|
|
||||||
return {
|
|
||||||
userId: user.id,
|
|
||||||
amount: context.amount,
|
|
||||||
targetDate: context.targetDate,
|
|
||||||
transaction,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,6 +98,6 @@ const createUserTransactionContext = (
|
|||||||
balance: context.amount,
|
balance: context.amount,
|
||||||
balanceDate: context.recordDate,
|
balanceDate: context.recordDate,
|
||||||
signature: context.signature,
|
signature: context.signature,
|
||||||
pubkey: context.signaturePubkey,
|
pubkey: context.pubKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
export const bibiBloxberg = {
|
import { UserInterface } from '../../interface/UserInterface'
|
||||||
|
|
||||||
|
export const bibiBloxberg: UserInterface = {
|
||||||
email: 'bibi@bloxberg.de',
|
email: 'bibi@bloxberg.de',
|
||||||
firstName: 'Bibi',
|
firstName: 'Bibi',
|
||||||
lastName: 'Bloxberg',
|
lastName: 'Bloxberg',
|
||||||
username: 'bibi',
|
|
||||||
// description: 'Hex Hex',
|
// description: 'Hex Hex',
|
||||||
password: BigInt('12825419584724616625'),
|
password: BigInt('12825419584724616625'),
|
||||||
pubKey: Buffer.from('42de7e4754625b730018c3b4ea745a4d043d9d867af352d0f08871793dfa6743', 'hex'),
|
pubKey: Buffer.from('42de7e4754625b730018c3b4ea745a4d043d9d867af352d0f08871793dfa6743', 'hex'),
|
||||||
@ -14,16 +15,13 @@ export const bibiBloxberg = {
|
|||||||
createdAt: new Date('2021-11-26T11:32:16'),
|
createdAt: new Date('2021-11-26T11:32:16'),
|
||||||
emailChecked: true,
|
emailChecked: true,
|
||||||
language: 'de',
|
language: 'de',
|
||||||
disabled: false,
|
|
||||||
groupId: 1,
|
|
||||||
passphrase:
|
passphrase:
|
||||||
'knife normal level all hurdle crucial color avoid warrior stadium road bachelor affair topple hawk pottery right afford immune two ceiling budget glance hour ',
|
'knife normal level all hurdle crucial color avoid warrior stadium road bachelor affair topple hawk pottery right afford immune two ceiling budget glance hour ',
|
||||||
mnemonicType: 2,
|
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
addBalance: true,
|
addBalance: true,
|
||||||
balanceModified: new Date('2021-11-30T10:37:11'),
|
balanceModified: new Date('2021-11-30T10:37:11'),
|
||||||
recordDate: new Date('2021-11-30T10:37:11'),
|
recordDate: new Date('2021-11-30T10:37:11'),
|
||||||
targetDate: new Date('2021-08-01 00:00:00'),
|
creationDate: new Date('2021-08-01 00:00:00'),
|
||||||
amount: 10000000,
|
amount: 10000000,
|
||||||
creationTxHash: Buffer.from(
|
creationTxHash: Buffer.from(
|
||||||
'51103dc0fc2ca5d5d75a9557a1e899304e5406cfdb1328d8df6414d527b0118100000000000000000000000000000000',
|
'51103dc0fc2ca5d5d75a9557a1e899304e5406cfdb1328d8df6414d527b0118100000000000000000000000000000000',
|
||||||
@ -33,8 +31,4 @@ export const bibiBloxberg = {
|
|||||||
'2a2c71f3e41adc060bbc3086577e2d57d24eeeb0a7727339c3f85aad813808f601d7e1df56a26e0929d2e67fc054fca429ccfa283ed2782185c7f009fe008f0c',
|
'2a2c71f3e41adc060bbc3086577e2d57d24eeeb0a7727339c3f85aad813808f601d7e1df56a26e0929d2e67fc054fca429ccfa283ed2782185c7f009fe008f0c',
|
||||||
'hex',
|
'hex',
|
||||||
),
|
),
|
||||||
signaturePubkey: Buffer.from(
|
|
||||||
'7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770',
|
|
||||||
'hex',
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
export const bobBaumeister = {
|
import { UserInterface } from '../../interface/UserInterface'
|
||||||
|
|
||||||
|
export const bobBaumeister: UserInterface = {
|
||||||
email: 'bob@baumeister.de',
|
email: 'bob@baumeister.de',
|
||||||
firstName: 'Bob',
|
firstName: 'Bob',
|
||||||
lastName: 'der Baumeister',
|
lastName: 'der Baumeister',
|
||||||
username: 'bob',
|
|
||||||
// description: 'Können wir das schaffen? Ja, wir schaffen das!',
|
// description: 'Können wir das schaffen? Ja, wir schaffen das!',
|
||||||
password: BigInt('3296644341468822636'),
|
password: BigInt('3296644341468822636'),
|
||||||
pubKey: Buffer.from('a509d9a146374fc975e3677db801ae8a4a83bff9dea96da64053ff6de6b2dd7e', 'hex'),
|
pubKey: Buffer.from('a509d9a146374fc975e3677db801ae8a4a83bff9dea96da64053ff6de6b2dd7e', 'hex'),
|
||||||
@ -14,16 +15,13 @@ export const bobBaumeister = {
|
|||||||
createdAt: new Date('2021-11-26T11:36:31'),
|
createdAt: new Date('2021-11-26T11:36:31'),
|
||||||
emailChecked: true,
|
emailChecked: true,
|
||||||
language: 'de',
|
language: 'de',
|
||||||
disabled: false,
|
|
||||||
groupId: 1,
|
|
||||||
passphrase:
|
passphrase:
|
||||||
'detail master source effort unable waste tilt flush domain orchard art truck hint barrel response gate impose peanut secret merry three uncle wink resource ',
|
'detail master source effort unable waste tilt flush domain orchard art truck hint barrel response gate impose peanut secret merry three uncle wink resource ',
|
||||||
mnemonicType: 2,
|
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
addBalance: true,
|
addBalance: true,
|
||||||
balanceModified: new Date('2021-11-30T10:37:14'),
|
balanceModified: new Date('2021-11-30T10:37:14'),
|
||||||
recordDate: new Date('2021-11-30T10:37:14'),
|
recordDate: new Date('2021-11-30T10:37:14'),
|
||||||
targetDate: new Date('2021-08-01 00:00:00'),
|
creationDate: new Date('2021-08-01 00:00:00'),
|
||||||
amount: 10000000,
|
amount: 10000000,
|
||||||
creationTxHash: Buffer.from(
|
creationTxHash: Buffer.from(
|
||||||
'be095dc87acb94987e71168fee8ecbf50ecb43a180b1006e75d573b35725c69c00000000000000000000000000000000',
|
'be095dc87acb94987e71168fee8ecbf50ecb43a180b1006e75d573b35725c69c00000000000000000000000000000000',
|
||||||
@ -33,8 +31,4 @@ export const bobBaumeister = {
|
|||||||
'1fbd6b9a3d359923b2501557f3bc79fa7e428127c8090fb16bc490b4d87870ab142b3817ddd902d22f0b26472a483233784a0e460c0622661752a13978903905',
|
'1fbd6b9a3d359923b2501557f3bc79fa7e428127c8090fb16bc490b4d87870ab142b3817ddd902d22f0b26472a483233784a0e460c0622661752a13978903905',
|
||||||
'hex',
|
'hex',
|
||||||
),
|
),
|
||||||
signaturePubkey: Buffer.from(
|
|
||||||
'7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770',
|
|
||||||
'hex',
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
export const garrickOllivander = {
|
import { UserInterface } from '../../interface/UserInterface'
|
||||||
|
|
||||||
|
export const garrickOllivander: UserInterface = {
|
||||||
email: 'garrick@ollivander.com',
|
email: 'garrick@ollivander.com',
|
||||||
firstName: 'Garrick',
|
firstName: 'Garrick',
|
||||||
lastName: 'Ollivander',
|
lastName: 'Ollivander',
|
||||||
username: 'garrick',
|
|
||||||
// description: `Curious ... curious ...
|
// description: `Curious ... curious ...
|
||||||
// Renowned wandmaker Mr Ollivander owns the wand shop Ollivanders: Makers of Fine Wands Since 382 BC in Diagon Alley. His shop is widely considered the best place to purchase a wand.`,
|
// Renowned wandmaker Mr Ollivander owns the wand shop Ollivanders: Makers of Fine Wands Since 382 BC in Diagon Alley. His shop is widely considered the best place to purchase a wand.`,
|
||||||
password: BigInt('0'),
|
password: BigInt('0'),
|
||||||
@ -10,11 +11,8 @@ export const garrickOllivander = {
|
|||||||
createdAt: new Date('2022-01-10T10:23:17'),
|
createdAt: new Date('2022-01-10T10:23:17'),
|
||||||
emailChecked: false,
|
emailChecked: false,
|
||||||
language: 'en',
|
language: 'en',
|
||||||
disabled: false,
|
|
||||||
groupId: 1,
|
|
||||||
passphrase:
|
passphrase:
|
||||||
'human glide theory clump wish history other duty door fringe neck industry ostrich equal plate diesel tornado neck people antenna door category moon hen ',
|
'human glide theory clump wish history other duty door fringe neck industry ostrich equal plate diesel tornado neck people antenna door category moon hen ',
|
||||||
mnemonicType: 2,
|
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
addBalance: false,
|
addBalance: false,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
export const peterLustig = {
|
import { UserInterface } from '../../interface/UserInterface'
|
||||||
|
|
||||||
|
export const peterLustig: UserInterface = {
|
||||||
email: 'peter@lustig.de',
|
email: 'peter@lustig.de',
|
||||||
firstName: 'Peter',
|
firstName: 'Peter',
|
||||||
lastName: 'Lustig',
|
lastName: 'Lustig',
|
||||||
username: 'peter',
|
|
||||||
// description: 'Latzhose und Nickelbrille',
|
// description: 'Latzhose und Nickelbrille',
|
||||||
password: BigInt('3917921995996627700'),
|
password: BigInt('3917921995996627700'),
|
||||||
pubKey: Buffer.from('7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770', 'hex'),
|
pubKey: Buffer.from('7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770', 'hex'),
|
||||||
@ -14,11 +15,8 @@ export const peterLustig = {
|
|||||||
createdAt: new Date('2020-11-25T10:48:43'),
|
createdAt: new Date('2020-11-25T10:48:43'),
|
||||||
emailChecked: true,
|
emailChecked: true,
|
||||||
language: 'de',
|
language: 'de',
|
||||||
disabled: false,
|
|
||||||
groupId: 1,
|
|
||||||
passphrase:
|
passphrase:
|
||||||
'okay property choice naive calm present weird increase stuff royal vibrant frame attend wood one else tribe pull hedgehog woman kitchen hawk snack smart ',
|
'okay property choice naive calm present weird increase stuff royal vibrant frame attend wood one else tribe pull hedgehog woman kitchen hawk snack smart ',
|
||||||
mnemonicType: 2,
|
|
||||||
role: 'admin',
|
role: 'admin',
|
||||||
serverUserPassword: '$2y$10$TzIWLeZoKs251gwrhSQmHeKhKI/EQ4EV5ClfAT8Ufnb4lcUXPa5X.',
|
serverUserPassword: '$2y$10$TzIWLeZoKs251gwrhSQmHeKhKI/EQ4EV5ClfAT8Ufnb4lcUXPa5X.',
|
||||||
activated: 1,
|
activated: 1,
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
export const raeuberHotzenplotz = {
|
import { UserInterface } from '../../interface/UserInterface'
|
||||||
|
|
||||||
|
export const raeuberHotzenplotz: UserInterface = {
|
||||||
email: 'raeuber@hotzenplotz.de',
|
email: 'raeuber@hotzenplotz.de',
|
||||||
firstName: 'Räuber',
|
firstName: 'Räuber',
|
||||||
lastName: 'Hotzenplotz',
|
lastName: 'Hotzenplotz',
|
||||||
username: 'räuber',
|
|
||||||
// description: 'Pfefferpistole',
|
// description: 'Pfefferpistole',
|
||||||
password: BigInt('12123692783243004812'),
|
password: BigInt('12123692783243004812'),
|
||||||
pubKey: Buffer.from('d7c70f94234dff071d982aa8f41583876c356599773b5911b39080da2b8c2d2b', 'hex'),
|
pubKey: Buffer.from('d7c70f94234dff071d982aa8f41583876c356599773b5911b39080da2b8c2d2b', 'hex'),
|
||||||
@ -14,16 +15,13 @@ export const raeuberHotzenplotz = {
|
|||||||
createdAt: new Date('2021-11-26T11:32:16'),
|
createdAt: new Date('2021-11-26T11:32:16'),
|
||||||
emailChecked: true,
|
emailChecked: true,
|
||||||
language: 'de',
|
language: 'de',
|
||||||
disabled: false,
|
|
||||||
groupId: 1,
|
|
||||||
passphrase:
|
passphrase:
|
||||||
'gospel trip tenant mouse spider skill auto curious man video chief response same little over expire drum display fancy clinic keen throw urge basket ',
|
'gospel trip tenant mouse spider skill auto curious man video chief response same little over expire drum display fancy clinic keen throw urge basket ',
|
||||||
mnemonicType: 2,
|
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
addBalance: true,
|
addBalance: true,
|
||||||
balanceModified: new Date('2021-11-30T10:37:13'),
|
balanceModified: new Date('2021-11-30T10:37:13'),
|
||||||
recordDate: new Date('2021-11-30T10:37:13'),
|
recordDate: new Date('2021-11-30T10:37:13'),
|
||||||
targetDate: new Date('2021-08-01 00:00:00'),
|
creationDate: new Date('2021-08-01 00:00:00'),
|
||||||
amount: 10000000,
|
amount: 10000000,
|
||||||
creationTxHash: Buffer.from(
|
creationTxHash: Buffer.from(
|
||||||
'23ba44fd84deb59b9f32969ad0cb18bfa4588be1bdb99c396888506474c16c1900000000000000000000000000000000',
|
'23ba44fd84deb59b9f32969ad0cb18bfa4588be1bdb99c396888506474c16c1900000000000000000000000000000000',
|
||||||
@ -33,8 +31,4 @@ export const raeuberHotzenplotz = {
|
|||||||
'756d3da061687c575d1dbc5073908f646aa5f498b0927b217c83b48af471450e571dfe8421fb8e1f1ebd1104526b7e7c6fa78684e2da59c8f7f5a8dc3d9e5b0b',
|
'756d3da061687c575d1dbc5073908f646aa5f498b0927b217c83b48af471450e571dfe8421fb8e1f1ebd1104526b7e7c6fa78684e2da59c8f7f5a8dc3d9e5b0b',
|
||||||
'hex',
|
'hex',
|
||||||
),
|
),
|
||||||
signaturePubkey: Buffer.from(
|
|
||||||
'7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770',
|
|
||||||
'hex',
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -70,8 +70,8 @@ const dateTimeFormats = {
|
|||||||
long: {
|
long: {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: 'long',
|
month: 'long',
|
||||||
day: 'long',
|
day: 'numeric',
|
||||||
weekday: 'short',
|
weekday: 'long',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
},
|
},
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user