Merge branch 'bv-toaster-admin-interface' of github.com:gradido/gradido into bv-toaster-admin-interface

This commit is contained in:
Moriz Wahl 2022-03-03 20:28:12 +01:00
commit 67861c32e4
17 changed files with 638 additions and 356 deletions

View File

@ -4,7 +4,7 @@ import dotenv from 'dotenv'
dotenv.config() dotenv.config()
const constants = { const constants = {
DB_VERSION: '0025-emails_to_lower', DB_VERSION: '0027-clean_transaction_table',
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
} }

View File

@ -3,6 +3,7 @@ import { registerEnumType } from 'type-graphql'
export enum TransactionTypeId { export enum TransactionTypeId {
CREATION = 1, CREATION = 1,
SEND = 2, SEND = 2,
RECEIVE = 3,
} }
registerEnumType(TransactionTypeId, { registerEnumType(TransactionTypeId, {

View File

@ -20,8 +20,7 @@ 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 { Transaction } from '@entity/Transaction' import { Transaction } from '@entity/Transaction'
import { UserTransaction } from '@entity/UserTransaction' import { TransactionRepository } from '../../typeorm/repository/Transaction'
import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction'
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'
@ -306,42 +305,29 @@ export class AdminResolver {
} }
const receivedCallDate = new Date() const receivedCallDate = new Date()
let transaction = new Transaction()
transaction.transactionTypeId = TransactionTypeId.CREATION
transaction.memo = pendingCreation.memo
transaction.received = receivedCallDate
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')
const userTransactionRepository = getCustomRepository(UserTransactionRepository) const transactionRepository = getCustomRepository(TransactionRepository)
const lastUserTransaction = await userTransactionRepository.findLastForUser( const lastUserTransaction = await transactionRepository.findLastForUser(pendingCreation.userId)
pendingCreation.userId,
)
let newBalance = 0 let newBalance = 0
if (!lastUserTransaction) { if (lastUserTransaction) {
newBalance = 0
} else {
newBalance = calculateDecay( newBalance = calculateDecay(
lastUserTransaction.balance, Number(lastUserTransaction.balance),
lastUserTransaction.balanceDate, lastUserTransaction.balanceDate,
receivedCallDate, receivedCallDate,
).balance ).balance
} }
newBalance = Number(newBalance) + Number(parseInt(pendingCreation.amount.toString())) newBalance = Number(newBalance) + Number(parseInt(pendingCreation.amount.toString()))
const newUserTransaction = new UserTransaction() const transaction = new Transaction()
newUserTransaction.userId = pendingCreation.userId transaction.typeId = TransactionTypeId.CREATION
newUserTransaction.transactionId = transaction.id transaction.memo = pendingCreation.memo
newUserTransaction.transactionTypeId = transaction.transactionTypeId transaction.userId = pendingCreation.userId
newUserTransaction.balance = Number(newBalance) transaction.amount = BigInt(parseInt(pendingCreation.amount.toString()))
newUserTransaction.balanceDate = transaction.received transaction.creationDate = pendingCreation.date
transaction.balance = BigInt(newBalance)
await userTransactionRepository.save(newUserTransaction).catch((error) => { transaction.balanceDate = receivedCallDate
throw new Error('Error saving user transaction: ' + error) await transaction.save()
})
let userBalance = await Balance.findOne({ userId: pendingCreation.userId }) let userBalance = await Balance.findOne({ userId: pendingCreation.userId })
if (!userBalance) { if (!userBalance) {
@ -388,7 +374,7 @@ async function getUserCreations(ids: number[], includePending = true): Promise<C
SELECT MONTH(date) AS month, sum(amount) AS sum, userId AS id FROM SELECT MONTH(date) AS month, sum(amount) AS sum, userId AS id FROM
(SELECT creation_date AS date, amount AS amount, user_id AS userId FROM transactions (SELECT creation_date AS date, amount AS amount, user_id AS userId FROM transactions
WHERE user_id IN (${ids.toString()}) WHERE user_id IN (${ids.toString()})
AND transaction_type_id = ${TransactionTypeId.CREATION} AND type_id = ${TransactionTypeId.CREATION}
AND creation_date >= ${dateFilter} AND creation_date >= ${dateFilter}
${unionString}) AS result ${unionString}) AS result
GROUP BY month, userId GROUP BY month, userId

View File

@ -4,7 +4,7 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-non-null-assertion */
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql' import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
import { getCustomRepository, getConnection, QueryRunner, In } from '@dbTools/typeorm' import { getCustomRepository, getConnection, QueryRunner } from '@dbTools/typeorm'
import CONFIG from '../../config' import CONFIG from '../../config'
import { sendTransactionReceivedEmail } from '../../mailer/sendTransactionReceivedEmail' import { sendTransactionReceivedEmail } from '../../mailer/sendTransactionReceivedEmail'
@ -18,10 +18,9 @@ import Paginated from '../arg/Paginated'
import { Order } from '../enum/Order' import { Order } from '../enum/Order'
import { UserRepository } from '../../typeorm/repository/User' import { UserRepository } from '../../typeorm/repository/User'
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 { Transaction as dbTransaction } from '@entity/Transaction' import { Transaction as dbTransaction } from '@entity/Transaction'
import { Balance as dbBalance } from '@entity/Balance' import { Balance as dbBalance } from '@entity/Balance'
@ -33,168 +32,46 @@ import { TransactionType } from '../enum/TransactionType'
import { hasUserAmount, isHexPublicKey } from '../../util/validate' import { hasUserAmount, isHexPublicKey } from '../../util/validate'
import { RIGHTS } from '../../auth/RIGHTS' import { RIGHTS } from '../../auth/RIGHTS'
// Helper function
async function calculateAndAddDecayTransactions(
userTransactions: dbUserTransaction[],
user: dbUser,
decay: boolean,
skipFirstTransaction: boolean,
): Promise<Transaction[]> {
const finalTransactions: Transaction[] = []
const transactionIds: number[] = []
const involvedUserIds: number[] = []
userTransactions.forEach((userTransaction: dbUserTransaction) => {
transactionIds.push(userTransaction.transactionId)
})
const transactions = await dbTransaction.find({ where: { id: In(transactionIds) } })
const transactionIndiced: dbTransaction[] = []
transactions.forEach((transaction: dbTransaction) => {
transactionIndiced[transaction.id] = transaction
involvedUserIds.push(transaction.userId)
if (transaction.transactionTypeId === TransactionTypeId.SEND) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
involvedUserIds.push(transaction.sendReceiverUserId!) // TODO ensure not null properly
}
})
// remove duplicates
// https://stackoverflow.com/questions/1960473/get-all-unique-values-in-a-javascript-array-remove-duplicates
const involvedUsersUnique = involvedUserIds.filter((v, i, a) => a.indexOf(v) === i)
const userRepository = getCustomRepository(UserRepository)
const userIndiced = await userRepository.getUsersIndiced(involvedUsersUnique)
for (let i = 0; i < userTransactions.length; i++) {
const userTransaction = userTransactions[i]
const transaction = transactionIndiced[userTransaction.transactionId]
const finalTransaction = new Transaction()
finalTransaction.transactionId = transaction.id
finalTransaction.date = transaction.received.toISOString()
finalTransaction.memo = transaction.memo
finalTransaction.totalBalance = roundFloorFrom4(userTransaction.balance)
const previousTransaction = i > 0 ? userTransactions[i - 1] : null
if (previousTransaction) {
const currentTransaction = userTransaction
const decay = calculateDecay(
previousTransaction.balance,
previousTransaction.balanceDate,
currentTransaction.balanceDate,
)
const balance = previousTransaction.balance - decay.balance
if (CONFIG.DECAY_START_TIME < currentTransaction.balanceDate) {
finalTransaction.decay = decay
finalTransaction.decay.balance = roundFloorFrom4(balance)
if (
previousTransaction.balanceDate < CONFIG.DECAY_START_TIME &&
currentTransaction.balanceDate > CONFIG.DECAY_START_TIME
) {
finalTransaction.decay.decayStartBlock = (
CONFIG.DECAY_START_TIME.getTime() / 1000
).toString()
}
}
} else {
finalTransaction.firstTransaction = true
}
// sender or receiver when user has sent money
// group name if creation
// type: gesendet / empfangen / geschöpft
// transaktion nr / id
// date
// balance
if (userTransaction.transactionTypeId === TransactionTypeId.CREATION) {
// creation
finalTransaction.name = 'Gradido Akademie'
finalTransaction.type = TransactionType.CREATION
// finalTransaction.targetDate = creation.targetDate
finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion
} else if (userTransaction.transactionTypeId === TransactionTypeId.SEND) {
// send coin
let otherUser: dbUser | undefined
finalTransaction.balance = roundFloorFrom4(Number(transaction.amount)) // Todo unsafe conversion
if (transaction.userId === user.id) {
finalTransaction.type = TransactionType.SEND
otherUser = userIndiced.find((u) => u.id === transaction.sendReceiverUserId)
// finalTransaction.pubkey = sendCoin.recipiantPublic
} else if (transaction.sendReceiverUserId === user.id) {
finalTransaction.type = TransactionType.RECIEVE
otherUser = userIndiced.find((u) => u.id === transaction.userId)
// finalTransaction.pubkey = sendCoin.senderPublic
} else {
throw new Error('invalid transaction')
}
if (otherUser) {
finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName
finalTransaction.email = otherUser.email
}
}
if (i > 0 || !skipFirstTransaction) {
finalTransactions.push(finalTransaction)
}
if (i === userTransactions.length - 1 && decay) {
const now = new Date()
const decay = calculateDecay(userTransaction.balance, userTransaction.balanceDate, now)
const balance = userTransaction.balance - decay.balance
const decayTransaction = new Transaction()
decayTransaction.type = 'decay'
decayTransaction.balance = roundCeilFrom4(balance)
decayTransaction.decayDuration = decay.decayDuration
decayTransaction.decayStart = decay.decayStart
decayTransaction.decayEnd = decay.decayEnd
finalTransactions.push(decayTransaction)
}
}
return finalTransactions
}
// helper helper function // helper helper function
async function updateStateBalance( async function updateStateBalance(
user: dbUser, user: dbUser,
centAmount: number, balance: number,
received: Date, received: Date,
queryRunner: QueryRunner, queryRunner: QueryRunner,
): Promise<dbBalance> { ): Promise<dbBalance> {
let balance = await dbBalance.findOne({ userId: user.id }) let userBalance = await dbBalance.findOne({ userId: user.id })
if (!balance) { if (!userBalance) {
balance = new dbBalance() userBalance = new dbBalance()
balance.userId = user.id userBalance.userId = user.id
balance.amount = centAmount userBalance.amount = balance
balance.modified = received userBalance.modified = received
} else { } else {
const decayedBalance = calculateDecay(balance.amount, balance.recordDate, received).balance userBalance.amount = balance
balance.amount = Number(decayedBalance) + centAmount userBalance.modified = new Date()
balance.modified = new Date()
} }
if (balance.amount <= 0) { if (userBalance.amount <= 0) {
throw new Error('error new balance <= 0') throw new Error('error new balance <= 0')
} }
balance.recordDate = received userBalance.recordDate = received
return queryRunner.manager.save(balance).catch((error) => { return queryRunner.manager.save(userBalance).catch((error) => {
throw new Error('error saving balance:' + error) throw new Error('error saving balance:' + error)
}) })
} }
// helper helper function async function calculateNewBalance(
async function addUserTransaction( userId: number,
user: dbUser, transactionDate: Date,
transaction: dbTransaction,
centAmount: number, centAmount: number,
queryRunner: QueryRunner, ): Promise<number> {
): Promise<dbUserTransaction> {
let newBalance = centAmount let newBalance = centAmount
const userTransactionRepository = getCustomRepository(UserTransactionRepository) const transactionRepository = getCustomRepository(TransactionRepository)
const lastUserTransaction = await userTransactionRepository.findLastForUser(user.id) const lastUserTransaction = await transactionRepository.findLastForUser(userId)
if (lastUserTransaction) { if (lastUserTransaction) {
newBalance += Number( newBalance += Number(
calculateDecay( calculateDecay(
Number(lastUserTransaction.balance), Number(lastUserTransaction.balance),
lastUserTransaction.balanceDate, lastUserTransaction.balanceDate,
transaction.received, transactionDate,
).balance, ).balance,
) )
} }
@ -203,18 +80,8 @@ async function addUserTransaction(
throw new Error('error new balance <= 0') throw new Error('error new balance <= 0')
} }
const newUserTransaction = new dbUserTransaction() return newBalance
newUserTransaction.userId = user.id
newUserTransaction.transactionId = transaction.id
newUserTransaction.transactionTypeId = transaction.transactionTypeId
newUserTransaction.balance = newBalance
newUserTransaction.balanceDate = transaction.received
return queryRunner.manager.save(newUserTransaction).catch((error) => {
throw new Error('Error saving user transaction: ' + error)
})
} }
@Resolver() @Resolver()
export class TransactionResolver { export class TransactionResolver {
@Authorized([RIGHTS.TRANSACTION_LIST]) @Authorized([RIGHTS.TRANSACTION_LIST])
@ -245,22 +112,119 @@ export class TransactionResolver {
if (offset && order === Order.ASC) { if (offset && order === Order.ASC) {
offset-- offset--
} }
const userTransactionRepository = getCustomRepository(UserTransactionRepository) const transactionRepository = getCustomRepository(TransactionRepository)
const [userTransactions, userTransactionsCount] = const [userTransactions, userTransactionsCount] = await transactionRepository.findByUserPaged(
await userTransactionRepository.findByUserPaged(user.id, limit, offset, order, onlyCreations) user.id,
limit,
offset,
order,
onlyCreations,
)
skipFirstTransaction = userTransactionsCount > offset + limit skipFirstTransaction = userTransactionsCount > offset + limit
const decay = !(currentPage > 1) const decay = !(currentPage > 1)
let transactions: Transaction[] = [] const transactions: Transaction[] = []
if (userTransactions.length) { if (userTransactions.length) {
if (order === Order.DESC) { if (order === Order.DESC) {
userTransactions.reverse() userTransactions.reverse()
} }
transactions = await calculateAndAddDecayTransactions( const involvedUserIds: number[] = []
userTransactions,
user, userTransactions.forEach((transaction: dbTransaction) => {
decay, involvedUserIds.push(transaction.userId)
skipFirstTransaction, if (
) transaction.typeId === TransactionTypeId.SEND ||
transaction.typeId === TransactionTypeId.RECEIVE
) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
involvedUserIds.push(transaction.linkedUserId!) // TODO ensure not null properly
}
})
// remove duplicates
// https://stackoverflow.com/questions/1960473/get-all-unique-values-in-a-javascript-array-remove-duplicates
const involvedUsersUnique = involvedUserIds.filter((v, i, a) => a.indexOf(v) === i)
const userRepository = getCustomRepository(UserRepository)
const userIndiced = await userRepository.getUsersIndiced(involvedUsersUnique)
for (let i = 0; i < userTransactions.length; i++) {
const userTransaction = userTransactions[i]
const finalTransaction = new Transaction()
finalTransaction.transactionId = userTransaction.id
finalTransaction.date = userTransaction.balanceDate.toISOString()
finalTransaction.memo = userTransaction.memo
finalTransaction.totalBalance = roundFloorFrom4(Number(userTransaction.balance))
const previousTransaction = i > 0 ? userTransactions[i - 1] : null
if (previousTransaction) {
const currentTransaction = userTransaction
const decay = calculateDecay(
Number(previousTransaction.balance),
previousTransaction.balanceDate,
currentTransaction.balanceDate,
)
const balance = Number(previousTransaction.balance) - decay.balance
if (CONFIG.DECAY_START_TIME < currentTransaction.balanceDate) {
finalTransaction.decay = decay
finalTransaction.decay.balance = roundFloorFrom4(balance)
if (
previousTransaction.balanceDate < CONFIG.DECAY_START_TIME &&
currentTransaction.balanceDate > CONFIG.DECAY_START_TIME
) {
finalTransaction.decay.decayStartBlock = (
CONFIG.DECAY_START_TIME.getTime() / 1000
).toString()
}
}
}
finalTransaction.balance = roundFloorFrom4(Number(userTransaction.amount)) // Todo unsafe conversion
const otherUser = userIndiced.find((u) => u.id === userTransaction.linkedUserId)
switch (userTransaction.typeId) {
case TransactionTypeId.CREATION:
finalTransaction.name = 'Gradido Akademie'
finalTransaction.type = TransactionType.CREATION
break
case TransactionTypeId.SEND:
finalTransaction.type = TransactionType.SEND
if (otherUser) {
finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName
finalTransaction.email = otherUser.email
}
break
case TransactionTypeId.RECEIVE:
finalTransaction.type = TransactionType.RECIEVE
if (otherUser) {
finalTransaction.name = otherUser.firstName + ' ' + otherUser.lastName
finalTransaction.email = otherUser.email
}
break
default:
throw new Error('invalid transaction')
}
if (i > 0 || !skipFirstTransaction) {
transactions.push(finalTransaction)
}
if (i === userTransactions.length - 1 && decay) {
const now = new Date()
const decay = calculateDecay(
Number(userTransaction.balance),
userTransaction.balanceDate,
now,
)
const balance = Number(userTransaction.balance) - decay.balance
const decayTransaction = new Transaction()
decayTransaction.type = 'decay'
decayTransaction.balance = roundCeilFrom4(balance)
decayTransaction.decayDuration = decay.decayDuration
decayTransaction.decayStart = decay.decayStart
decayTransaction.decayEnd = decay.decayEnd
transactions.push(decayTransaction)
}
}
if (order === Order.DESC) { if (order === Order.DESC) {
transactions.reverse() transactions.reverse()
} }
@ -323,87 +287,62 @@ export class TransactionResolver {
throw new Error('invalid recipient public key') throw new Error('invalid recipient public key')
} }
const centAmount = Math.trunc(amount * 10000) const centAmount = Math.round(amount * 10000)
const queryRunner = getConnection().createQueryRunner() const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect() await queryRunner.connect()
await queryRunner.startTransaction('READ UNCOMMITTED') await queryRunner.startTransaction('READ UNCOMMITTED')
try { try {
const receivedCallDate = new Date()
// transaction // transaction
const transaction = new dbTransaction() const transactionSend = new dbTransaction()
transaction.transactionTypeId = TransactionTypeId.SEND transactionSend.typeId = TransactionTypeId.SEND
transaction.memo = memo transactionSend.memo = memo
transaction.userId = senderUser.id transactionSend.userId = senderUser.id
transaction.pubkey = senderUser.pubKey transactionSend.linkedUserId = recipientUser.id
transaction.sendReceiverUserId = recipientUser.id transactionSend.amount = BigInt(centAmount)
transaction.sendReceiverPublicKey = recipientUser.pubKey const sendBalance = await calculateNewBalance(senderUser.id, receivedCallDate, -centAmount)
transaction.amount = BigInt(centAmount) transactionSend.balance = BigInt(Math.trunc(sendBalance))
transactionSend.balanceDate = receivedCallDate
transactionSend.sendSenderFinalBalance = transactionSend.balance
await queryRunner.manager.insert(dbTransaction, transactionSend)
await queryRunner.manager.insert(dbTransaction, transaction) const transactionReceive = new dbTransaction()
transactionReceive.typeId = TransactionTypeId.RECEIVE
// Insert Transaction: sender - amount transactionReceive.memo = memo
const senderUserTransactionBalance = await addUserTransaction( transactionReceive.userId = recipientUser.id
senderUser, transactionReceive.linkedUserId = senderUser.id
transaction, transactionReceive.amount = BigInt(centAmount)
-centAmount, const receiveBalance = await calculateNewBalance(
queryRunner, recipientUser.id,
) receivedCallDate,
// Insert Transaction: recipient + amount
const recipiantUserTransactionBalance = await addUserTransaction(
recipientUser,
transaction,
centAmount, centAmount,
queryRunner,
) )
transactionReceive.balance = BigInt(Math.trunc(receiveBalance))
transactionReceive.balanceDate = receivedCallDate
transactionReceive.sendSenderFinalBalance = transactionSend.balance
transactionReceive.linkedTransactionId = transactionSend.id
await queryRunner.manager.insert(dbTransaction, transactionReceive)
// Update Balance: sender - amount // Save linked transaction id for send
const senderStateBalance = await updateStateBalance( transactionSend.linkedTransactionId = transactionReceive.id
senderUser, await queryRunner.manager.update(dbTransaction, { id: transactionSend.id }, transactionSend)
-centAmount,
transaction.received,
queryRunner,
)
// Update Balance: recipiant + amount // Update Balance sender
const recipiantStateBalance = await updateStateBalance( await updateStateBalance(senderUser, Math.trunc(sendBalance), receivedCallDate, queryRunner)
// Update Balance recipient
await updateStateBalance(
recipientUser, recipientUser,
centAmount, Math.trunc(receiveBalance),
transaction.received, receivedCallDate,
queryRunner, queryRunner,
) )
if (senderStateBalance.amount !== senderUserTransactionBalance.balance) {
throw new Error('db data corrupted, sender')
}
if (recipiantStateBalance.amount !== recipiantUserTransactionBalance.balance) {
throw new Error('db data corrupted, recipiant')
}
// TODO: WTF?
// I just assume that due to implicit type conversion the decimal places were cut.
// Using `Math.trunc` to simulate this behaviour
transaction.sendSenderFinalBalance = BigInt(Math.trunc(senderStateBalance.amount))
await queryRunner.manager.save(transaction).catch((error) => {
throw new Error('error saving transaction with tx hash: ' + error)
})
await queryRunner.commitTransaction() await queryRunner.commitTransaction()
} catch (e) { } catch (e) {
await queryRunner.rollbackTransaction() await queryRunner.rollbackTransaction()
// TODO: This is broken code - we should never correct an autoincrement index in production throw new Error(`Transaction was not successful: ${e}`)
// according to dario it is required tho to properly work. The index of the table is used as
// index for the transaction which requires a chain without gaps
const count = await queryRunner.manager.count(dbTransaction)
// fix autoincrement value which seems not effected from rollback
await queryRunner
.query('ALTER TABLE `transactions` auto_increment = ?', [count])
.catch((error) => {
// eslint-disable-next-line no-console
console.log('problems with reset auto increment: %o', error)
})
throw e
} finally { } finally {
await queryRunner.release() await queryRunner.release()
} }

View File

@ -1,17 +1,17 @@
import { EntityRepository, Repository } from '@dbTools/typeorm' import { EntityRepository, Repository } from '@dbTools/typeorm'
import { Transaction } from '@entity/Transaction'
import { Order } from '../../graphql/enum/Order' import { Order } from '../../graphql/enum/Order'
import { UserTransaction } from '@entity/UserTransaction'
import { TransactionTypeId } from '../../graphql/enum/TransactionTypeId' import { TransactionTypeId } from '../../graphql/enum/TransactionTypeId'
@EntityRepository(UserTransaction) @EntityRepository(Transaction)
export class UserTransactionRepository extends Repository<UserTransaction> { export class TransactionRepository extends Repository<Transaction> {
findByUserPaged( findByUserPaged(
userId: number, userId: number,
limit: number, limit: number,
offset: number, offset: number,
order: Order, order: Order,
onlyCreation?: boolean, onlyCreation?: boolean,
): Promise<[UserTransaction[], number]> { ): Promise<[Transaction[], number]> {
if (onlyCreation) { if (onlyCreation) {
return this.createQueryBuilder('userTransaction') return this.createQueryBuilder('userTransaction')
.where('userTransaction.userId = :userId', { userId }) .where('userTransaction.userId = :userId', { userId })
@ -31,10 +31,10 @@ export class UserTransactionRepository extends Repository<UserTransaction> {
.getManyAndCount() .getManyAndCount()
} }
findLastForUser(userId: number): Promise<UserTransaction | undefined> { findLastForUser(userId: number): Promise<Transaction | undefined> {
return this.createQueryBuilder('userTransaction') return this.createQueryBuilder('userTransaction')
.where('userTransaction.userId = :userId', { userId }) .where('userTransaction.userId = :userId', { userId })
.orderBy('userTransaction.transactionId', 'DESC') .orderBy('userTransaction.balanceDate', 'DESC')
.getOne() .getOne()
} }
} }

View File

@ -0,0 +1,83 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity('transactions')
export class Transaction extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'user_id', unsigned: true, nullable: false })
userId: number
@Column({ name: 'transaction_id', unsigned: true, nullable: false })
transactionId: number
@Column({ name: 'transaction_type_id', unsigned: true, nullable: false })
transactionTypeId: number
@Column({ type: 'bigint', nullable: false })
amount: BigInt
@Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' })
memo: string
@Column({
name: 'send_sender_final_balance',
type: 'bigint',
nullable: true,
default: null,
})
sendSenderFinalBalance: BigInt | null
@Column({ name: 'balance', type: 'bigint', default: 0 })
balance: BigInt
@Column({
name: 'balance_date',
type: 'timestamp',
default: () => 'CURRENT_TIMESTAMP',
nullable: false,
})
balanceDate: Date
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP', nullable: false })
received: Date
@Column({ name: 'creation_date', type: 'timestamp', nullable: true, default: null })
creationDate: Date
@Column({
name: 'linked_user_id',
type: 'int',
unsigned: true,
nullable: true,
default: null,
})
linkedUserId?: number | null
@Column({
name: 'linked_state_user_transaction_id',
type: 'int',
unsigned: true,
nullable: true,
default: null,
})
linkedStateUserTransactionId?: number | null
@Column({ type: 'binary', length: 64, nullable: true, default: null })
signature: Buffer
@Column({ name: 'tx_hash', type: 'binary', length: 48, default: null, nullable: true })
txHash: 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
}

View File

@ -0,0 +1,59 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
@Entity('transactions')
export class Transaction extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'user_id', unsigned: true, nullable: false })
userId: number
@Column({ name: 'type_id', unsigned: true, nullable: false })
typeId: number
@Column({ type: 'bigint', nullable: false })
amount: BigInt
@Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' })
memo: string
@Column({
name: 'send_sender_final_balance',
type: 'bigint',
nullable: true,
default: null,
})
sendSenderFinalBalance: BigInt | null
@Column({ name: 'balance', type: 'bigint', default: 0 })
balance: BigInt
@Column({
name: 'balance_date',
type: 'timestamp',
default: () => 'CURRENT_TIMESTAMP',
nullable: false,
})
balanceDate: Date
@Column({ name: 'creation_date', type: 'timestamp', nullable: true, default: null })
creationDate: Date
@Column({
name: 'linked_user_id',
type: 'int',
unsigned: true,
nullable: true,
default: null,
})
linkedUserId?: number | null
@Column({
name: 'linked_transaction_id',
type: 'int',
unsigned: true,
nullable: true,
default: null,
})
linkedTransactionId?: number | null
}

View File

@ -1 +1 @@
export { Transaction } from './0024-combine_transaction_tables/Transaction' export { Transaction } from './0027-clean_transaction_table/Transaction'

View File

@ -1 +0,0 @@
export { UserTransaction } from './0001-init_db/UserTransaction'

View File

@ -6,7 +6,6 @@ import { ServerUser } from './ServerUser'
import { Transaction } from './Transaction' import { Transaction } from './Transaction'
import { User } from './User' import { User } from './User'
import { UserSetting } from './UserSetting' import { UserSetting } from './UserSetting'
import { UserTransaction } from './UserTransaction'
import { AdminPendingCreation } from './AdminPendingCreation' import { AdminPendingCreation } from './AdminPendingCreation'
export const entities = [ export const entities = [
@ -19,5 +18,4 @@ export const entities = [
Transaction, Transaction,
User, User,
UserSetting, UserSetting,
UserTransaction,
] ]

View File

@ -1,6 +1,6 @@
/* MIGRATION TO COMBINE ALL TRANSACTION TABLES /* MIGRATION TO COMBINE SEVERAL TRANSACTION TABLES
* *
* Combine all transaction tables into one table with all data * Combine several transaction tables into one table with all data
*/ */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */

View File

@ -0,0 +1,218 @@
/* MIGRATION TO COMBINE AND REFACTOR SOME TRANSACTION TABLES
*
* Combine `state_user_transactions` and `transactions` tables.
* This changes the structure of transactions from 1 transaction for
* each send-coins to two transactions per send-coin
*/
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
/*
* This migration has a possible incompatibility
* due to the construction of the tables.
* For our production data it works well.
* With this migration we decide for int instead of bigint
* to handle things more easily
*
* CREATE TABLE `transactions` (
* `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
* ...
* )
* CREATE TABLE `state_user_transactions` (
* ...
* `transaction_id` int(10) unsigned NOT NULL,
* ...
* )
*/
// rename `state_user_id` to `user_id`
await queryFn('ALTER TABLE `state_user_transactions` RENAME COLUMN state_user_id TO user_id;')
// Create new `amount` column, with a temporary default of null
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `amount` bigint(20) DEFAULT NULL AFTER `transaction_type_id`;',
)
// Create new `send_sender_final_balance`
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `send_sender_final_balance` bigint(20) DEFAULT NULL AFTER `amount`;',
)
// Create new `memo` column, with a temporary default of null
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `memo` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `amount`;',
)
// Create new `received` column, with a temporary default of null
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `received` timestamp NULL DEFAULT NULL AFTER `balance_date`;',
)
// Create new `creation_date` column
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `creation_date` timestamp NULL DEFAULT NULL AFTER `received`;',
)
// Create new `linked_user_id` column (former `send_receiver_user_id`)
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `linked_user_id` int(10) unsigned DEFAULT NULL AFTER `creation_date`;',
)
// Create new `linked_state_user_transaction_id`
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `linked_state_user_transaction_id` int(10) unsigned DEFAULT NULL AFTER `linked_user_id`;',
)
// Create new `tx_hash`
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `tx_hash` binary(48) DEFAULT NULL AFTER `linked_state_user_transaction_id`;',
)
// Create new `signature`
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `signature` binary(64) DEFAULT NULL AFTER `tx_hash`;',
)
// Create new `pubkey`
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `pubkey` binary(32) DEFAULT NULL AFTER `signature`;',
)
// Create new `creation_ident_hash`
await queryFn(
'ALTER TABLE `state_user_transactions` ADD COLUMN `creation_ident_hash` binary(32) DEFAULT NULL AFTER `pubkey`;',
)
// Insert Data from `transactions` for creations
await queryFn(`
UPDATE state_user_transactions
LEFT JOIN transactions ON state_user_transactions.transaction_id = transactions.id
SET state_user_transactions.amount = transactions.amount,
state_user_transactions.send_sender_final_balance = transactions.send_sender_final_balance,
state_user_transactions.memo = transactions.memo,
state_user_transactions.received = transactions.received,
state_user_transactions.creation_date = transactions.creation_date,
state_user_transactions.linked_user_id = transactions.send_receiver_user_id,
state_user_transactions.linked_state_user_transaction_id = NULL,
state_user_transactions.tx_hash = transactions.tx_hash,
state_user_transactions.signature = transactions.signature,
state_user_transactions.pubkey = transactions.pubkey,
state_user_transactions.creation_ident_hash = transactions.creation_ident_hash
WHERE state_user_transactions.transaction_type_id = 1;
`)
// Insert Data from `transactions` for sendCoin sender
await queryFn(`
UPDATE state_user_transactions
LEFT JOIN transactions ON state_user_transactions.transaction_id = transactions.id
SET state_user_transactions.amount = transactions.amount,
state_user_transactions.send_sender_final_balance = transactions.send_sender_final_balance,
state_user_transactions.memo = transactions.memo,
state_user_transactions.received = transactions.received,
state_user_transactions.creation_date = transactions.creation_date,
state_user_transactions.linked_user_id = transactions.send_receiver_user_id,
state_user_transactions.linked_state_user_transaction_id = (
SELECT id FROM state_user_transactions AS sut
WHERE sut.transaction_type_id = 2
AND sut.transaction_id = state_user_transactions.transaction_id
AND sut.user_id = transactions.send_receiver_user_id
),
state_user_transactions.tx_hash = transactions.tx_hash,
state_user_transactions.signature = transactions.signature,
state_user_transactions.pubkey = transactions.pubkey,
state_user_transactions.creation_ident_hash = transactions.creation_ident_hash
WHERE state_user_transactions.transaction_type_id = 2
AND state_user_transactions.user_id = transactions.user_id;
`)
// Insert Data from `transactions` for sendCoin receiver
await queryFn(`
UPDATE state_user_transactions
LEFT JOIN transactions ON state_user_transactions.transaction_id = transactions.id
SET state_user_transactions.amount = transactions.amount,
state_user_transactions.send_sender_final_balance = transactions.send_sender_final_balance,
state_user_transactions.memo = transactions.memo,
state_user_transactions.received = transactions.received,
state_user_transactions.creation_date = transactions.creation_date,
state_user_transactions.linked_user_id = transactions.user_id,
state_user_transactions.linked_state_user_transaction_id = (
SELECT id FROM state_user_transactions AS sut
WHERE sut.transaction_type_id = 2
AND sut.transaction_id = state_user_transactions.transaction_id
AND sut.user_id = transactions.user_id
),
state_user_transactions.tx_hash = transactions.tx_hash,
state_user_transactions.signature = transactions.signature,
state_user_transactions.pubkey = transactions.send_receiver_public_key,
state_user_transactions.creation_ident_hash = transactions.creation_ident_hash,
state_user_transactions.transaction_type_id = 3
WHERE state_user_transactions.transaction_type_id = 2
AND state_user_transactions.user_id = transactions.send_receiver_user_id;
`)
// Modify defaults after our inserts
await queryFn('ALTER TABLE `state_user_transactions` MODIFY COLUMN `amount` bigint(20) NOT NULL;')
await queryFn(
'ALTER TABLE `state_user_transactions` MODIFY COLUMN `memo` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL',
)
await queryFn(
'ALTER TABLE `state_user_transactions` MODIFY COLUMN `received` timestamp NOT NULL DEFAULT current_timestamp()',
)
// Drop table `transactions`
await queryFn('DROP TABLE `transactions`;')
// Rename table `transaction_send_coins` to `transactions`
await queryFn('RENAME TABLE `state_user_transactions` TO `transactions`;')
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn('RENAME TABLE `transactions` TO `state_user_transactions`;')
await queryFn(`CREATE TABLE \`transactions\` (
\`id\` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
\`transaction_type_id\` int(10) unsigned NOT NULL,
\`user_id\` int(10) unsigned NOT NULL,
\`amount\` bigint(20) NOT NULL,
\`tx_hash\` binary(48) DEFAULT NULL,
\`memo\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
\`received\` timestamp NOT NULL DEFAULT current_timestamp(),
\`signature\` binary(64) DEFAULT NULL,
\`pubkey\` binary(32) DEFAULT NULL,
\`creation_ident_hash\` binary(32) DEFAULT NULL,
\`creation_date\` timestamp NULL DEFAULT NULL,
\`send_receiver_public_key\` binary(32) DEFAULT NULL,
\`send_receiver_user_id\` int(10) unsigned DEFAULT NULL,
\`send_sender_final_balance\` bigint(20) DEFAULT NULL,
PRIMARY KEY (\`id\`)
) ENGINE=InnoDB AUTO_INCREMENT=3424 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`)
await queryFn(`
INSERT INTO transactions (
id, transaction_type_id, user_id, amount,
tx_hash, memo, received, signature, pubkey,
creation_ident_hash, creation_date,
send_receiver_public_key, send_receiver_user_id,
send_sender_final_balance
)
SELECT transaction_id AS id, transaction_type_id,
user_id, amount, tx_hash, memo, received,
signature, pubkey, creation_ident_hash,
creation_date, send_receiver_public_key,
linked_user_id AS send_receiver_user_id,
send_sender_final_balance
FROM state_user_transactions LEFT JOIN (
SELECT id, pubkey AS send_receiver_public_key
FROM state_user_transactions AS sut
WHERE sut.transaction_type_id = 3
) AS sutj ON sutj.id = state_user_transactions.id
WHERE transaction_type_id IN (1,2)
`)
await queryFn(
'UPDATE state_user_transactions SET transaction_type_id = 2 WHERE transaction_type_id = 3;',
)
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `creation_ident_hash`;')
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `pubkey`;')
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `signature`;')
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `tx_hash`;')
await queryFn(
'ALTER TABLE `state_user_transactions` DROP COLUMN `linked_state_user_transaction_id`;',
)
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `linked_user_id`;')
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `creation_date`;')
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `received`;')
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `memo`;')
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `send_sender_final_balance`;')
await queryFn('ALTER TABLE `state_user_transactions` DROP COLUMN `amount`;')
await queryFn('ALTER TABLE `state_user_transactions` RENAME COLUMN user_id TO state_user_id;')
}

View File

@ -0,0 +1,73 @@
/* MIGRATION TO CLEAN THE TRANSACTION TABLE
*
* Remove several unused fields or those with duplicate data
* and rename fields to a proper name in `transactions` .
*
* This migration has data loss
*/
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// drop column `transaction_id`, it is not needed
await queryFn('ALTER TABLE `transactions` DROP COLUMN `transaction_id`;')
// drop column `received`, it is a duplicate of balance_date
await queryFn('ALTER TABLE `transactions` DROP COLUMN `received`;')
// drop column `tx_hash`, it is not needed
await queryFn('ALTER TABLE `transactions` DROP COLUMN `tx_hash`;')
// drop column `signature`, it is not needed
await queryFn('ALTER TABLE `transactions` DROP COLUMN `signature`;')
// drop column `pubkey`, it is not needed
await queryFn('ALTER TABLE `transactions` DROP COLUMN `pubkey`;')
// drop column `creation_ident_hash`, it is not needed
await queryFn('ALTER TABLE `transactions` DROP COLUMN `creation_ident_hash`;')
// rename `transaction_type_id` to `type_id`
await queryFn('ALTER TABLE `transactions` RENAME COLUMN transaction_type_id TO type_id;')
// rename `linked_state_user_transaction_id` to `linked_transaction_id`
await queryFn(
'ALTER TABLE `transactions` RENAME COLUMN linked_state_user_transaction_id TO linked_transaction_id;',
)
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// Not all data is recoverable here, some data is simulated,
// but we have data loss on:
// - transaction_id (we have data here, but its not the same as before)
// - tx_hash (null)
// - signature (null)
// - pubkey (null)
// - creation_ident_hash (null)
await queryFn(
'ALTER TABLE `transactions` RENAME COLUMN linked_transaction_id TO linked_state_user_transaction_id;',
)
await queryFn('ALTER TABLE `transactions` RENAME COLUMN type_id TO transaction_type_id;')
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `creation_ident_hash` binary(32) DEFAULT NULL AFTER `linked_state_user_transaction_id`;',
)
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `pubkey` binary(32) DEFAULT NULL AFTER `linked_state_user_transaction_id`;',
)
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `signature` binary(64) DEFAULT NULL AFTER `linked_state_user_transaction_id`;',
)
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `tx_hash` binary(48) DEFAULT NULL AFTER `linked_state_user_transaction_id`;',
)
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `received` timestamp NULL DEFAULT NULL AFTER `balance_date`;',
)
await queryFn('UPDATE `transactions` SET `received` = `balance_date`;')
await queryFn(
'ALTER TABLE `transactions` MODIFY COLUMN `received` timestamp NOT NULL DEFAULT current_timestamp();',
)
await queryFn(
'ALTER TABLE `transactions` ADD COLUMN `transaction_id` int(10) unsigned DEFAULT NULL AFTER `user_id`;',
)
await queryFn('UPDATE `transactions` SET `transaction_id` = `id`;')
await queryFn(
'ALTER TABLE `transactions` MODIFY COLUMN `transaction_id` int(10) unsigned NOT NULL;',
)
}

View File

@ -2,7 +2,6 @@ import Faker from 'faker'
import { define } from 'typeorm-seeding' import { define } from 'typeorm-seeding'
import { Transaction } from '../../entity/Transaction' import { Transaction } from '../../entity/Transaction'
import { TransactionContext } from '../interface/TransactionContext' import { TransactionContext } from '../interface/TransactionContext'
import { randomBytes } from 'crypto'
define(Transaction, (faker: typeof Faker, context?: TransactionContext) => { define(Transaction, (faker: typeof Faker, context?: TransactionContext) => {
if (!context) { if (!context) {
@ -10,18 +9,13 @@ define(Transaction, (faker: typeof Faker, context?: TransactionContext) => {
} }
const transaction = new Transaction() const transaction = new Transaction()
transaction.transactionTypeId = context.transactionTypeId // || 2 transaction.typeId = context.typeId // || 2
transaction.userId = context.userId transaction.userId = context.userId
transaction.amount = context.amount transaction.amount = context.amount
transaction.txHash = context.txHash || randomBytes(48)
transaction.memo = context.memo transaction.memo = context.memo
transaction.received = context.received || new Date()
transaction.signature = context.signature || randomBytes(64)
transaction.pubkey = context.pubkey || randomBytes(32)
transaction.creationIdentHash = context.creationIdentHash || randomBytes(32)
transaction.creationDate = context.creationDate || new Date() transaction.creationDate = context.creationDate || new Date()
transaction.sendReceiverPublicKey = context.sendReceiverPublicKey || null // transaction.sendReceiverPublicKey = context.sendReceiverPublicKey || null
transaction.sendReceiverUserId = context.sendReceiverUserId || null transaction.linkedUserId = context.sendReceiverUserId || null
transaction.sendSenderFinalBalance = context.sendSenderFinalBalance || null transaction.sendSenderFinalBalance = context.sendSenderFinalBalance || null
return transaction return transaction

View File

@ -1,19 +0,0 @@
import Faker from 'faker'
import { define } from 'typeorm-seeding'
import { UserTransaction } from '../../entity/UserTransaction'
import { UserTransactionContext } from '../interface/TransactionContext'
define(UserTransaction, (faker: typeof Faker, context?: UserTransactionContext) => {
if (!context || !context.userId || !context.transactionId) {
throw new Error('UserTransaction: No userId and/or transactionId present!')
}
const userTransaction = new UserTransaction()
userTransaction.userId = context.userId
userTransaction.transactionId = context.transactionId
userTransaction.transactionTypeId = context.transactionTypeId ? context.transactionTypeId : 1
userTransaction.balance = context.balance ? context.balance : 100000
userTransaction.balanceDate = context.balanceDate ? context.balanceDate : new Date()
return userTransaction
})

View File

@ -1,18 +1,13 @@
import { Transaction } from '../../entity/Transaction'
import { User } from '../../entity/User' import { User } from '../../entity/User'
export interface TransactionContext { export interface TransactionContext {
transactionTypeId: number typeId: number
userId: number userId: number
balance: BigInt
balanceDate: Date
amount: BigInt amount: BigInt
txHash?: Buffer
memo: string memo: string
received?: Date
signature?: Buffer
pubkey?: Buffer
creationIdentHash?: Buffer
creationDate?: Date creationDate?: Date
sendReceiverPublicKey?: Buffer
sendReceiverUserId?: number sendReceiverUserId?: number
sendSenderFinalBalance?: BigInt sendSenderFinalBalance?: BigInt
} }
@ -23,23 +18,3 @@ export interface BalanceContext {
amount?: number amount?: number
user?: User user?: User
} }
export interface TransactionSendCoinContext {
senderPublic?: Buffer
userId?: number
recipiantPublic?: Buffer
recipiantUserId?: number
amount?: number
senderFinalBalance?: number
transaction?: Transaction
}
export interface UserTransactionContext {
userId?: number
transactionId?: number
transactionTypeId?: number
balance?: number
balanceDate?: Date
signature?: Buffer
pubkey?: Buffer
}

View File

@ -1,15 +1,10 @@
import { UserContext, ServerUserContext } from '../../interface/UserContext' import { UserContext, ServerUserContext } from '../../interface/UserContext'
import { import { BalanceContext, TransactionContext } from '../../interface/TransactionContext'
BalanceContext,
TransactionContext,
UserTransactionContext,
} from '../../interface/TransactionContext'
import { UserInterface } from '../../interface/UserInterface' import { UserInterface } from '../../interface/UserInterface'
import { User } from '../../../entity/User' import { User } from '../../../entity/User'
import { ServerUser } from '../../../entity/ServerUser' 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 { 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> => {
@ -22,12 +17,9 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro
if (userData.addBalance) { if (userData.addBalance) {
// 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)( await factory(Transaction)(
createTransactionContext(userData, user, 1, 'Herzlich Willkommen bei Gradido!'), createTransactionContext(userData, user, 1, 'Herzlich Willkommen bei Gradido!'),
).create() ).create()
await factory(UserTransaction)(
createUserTransactionContext(userData, user, transaction),
).create()
} }
} }
@ -76,28 +68,12 @@ const createTransactionContext = (
memo: string, memo: string,
): TransactionContext => { ): TransactionContext => {
return { return {
transactionTypeId: type, typeId: type,
userId: user.id, userId: user.id,
amount: BigInt(context.amount || 100000), amount: BigInt(context.amount || 100000),
txHash: context.creationTxHash, balance: BigInt(context.amount || 100000),
balanceDate: new Date(context.recordDate || Date.now()),
memo, memo,
received: context.recordDate,
creationDate: context.creationDate, creationDate: context.creationDate,
} }
} }
const createUserTransactionContext = (
context: UserInterface,
user: User,
transaction: Transaction,
): UserTransactionContext => {
return {
userId: user.id,
transactionId: transaction.id,
transactionTypeId: transaction.transactionTypeId,
balance: context.amount,
balanceDate: context.recordDate,
signature: context.signature,
pubkey: context.pubKey,
}
}