mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into login_call_resetPassword
This commit is contained in:
commit
b54b96feca
@ -39,6 +39,7 @@ yarn seed
|
||||
## Seeded Users
|
||||
|
||||
| email | password | admin |
|
||||
|------------------------|------------|---------|
|
||||
| peter@lustig.de | `Aa12345_` | `true` |
|
||||
| bibi@bloxberg.de | `Aa12345_` | `false` |
|
||||
| raeuber@hotzenplotz.de | `Aa12345_` | `false` |
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, JoinColumn, OneToOne } from 'typeorm'
|
||||
import { User } from '../User'
|
||||
|
||||
@Entity('state_balances')
|
||||
export class Balance extends BaseEntity {
|
||||
@ -16,4 +17,8 @@ export class Balance extends BaseEntity {
|
||||
|
||||
@Column({ type: 'bigint' })
|
||||
amount: number
|
||||
|
||||
@OneToOne(() => User, { nullable: false })
|
||||
@JoinColumn({ name: 'state_user_id' })
|
||||
user: User
|
||||
}
|
||||
|
||||
21
database/entity/0001-init_db/TransactionSignature.ts
Normal file
21
database/entity/0001-init_db/TransactionSignature.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm'
|
||||
import { Transaction } from './Transaction'
|
||||
|
||||
@Entity('transaction_signatures')
|
||||
export class TransactionSignature extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column({ name: 'transaction_id' })
|
||||
transactionId: number
|
||||
|
||||
@Column({ type: 'binary', length: 64 })
|
||||
signature: Buffer
|
||||
|
||||
@Column({ type: 'binary', length: 32 })
|
||||
pubkey: Buffer
|
||||
|
||||
@ManyToOne(() => Transaction)
|
||||
@JoinColumn({ name: 'transaction_id' })
|
||||
transaction: Transaction
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
||||
import { Balance } from '../Balance'
|
||||
|
||||
// Moriz: I do not like the idea of having two user tables
|
||||
@Entity('state_users')
|
||||
@ -29,4 +30,7 @@ export class User extends BaseEntity {
|
||||
|
||||
@Column()
|
||||
disabled: boolean
|
||||
|
||||
@OneToOne(() => Balance, (balance) => balance.user)
|
||||
balance: Balance
|
||||
}
|
||||
|
||||
1
database/entity/TransactionSignature.ts
Normal file
1
database/entity/TransactionSignature.ts
Normal file
@ -0,0 +1 @@
|
||||
export { TransactionSignature } from './0001-init_db/TransactionSignature'
|
||||
@ -8,6 +8,7 @@ import { Migration } from './Migration'
|
||||
import { ServerUser } from './ServerUser'
|
||||
import { Transaction } from './Transaction'
|
||||
import { TransactionCreation } from './TransactionCreation'
|
||||
import { TransactionSignature } from './TransactionSignature'
|
||||
import { TransactionSendCoin } from './TransactionSendCoin'
|
||||
import { User } from './User'
|
||||
import { UserSetting } from './UserSetting'
|
||||
@ -25,6 +26,7 @@ export const entities = [
|
||||
ServerUser,
|
||||
Transaction,
|
||||
TransactionCreation,
|
||||
TransactionSignature,
|
||||
TransactionSendCoin,
|
||||
User,
|
||||
UserSetting,
|
||||
|
||||
18
database/src/factories/balance.factory.ts
Normal file
18
database/src/factories/balance.factory.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import Faker from 'faker'
|
||||
import { define } from 'typeorm-seeding'
|
||||
import { Balance } from '../../entity/Balance'
|
||||
import { BalanceContext } from '../interface/TransactionContext'
|
||||
|
||||
define(Balance, (faker: typeof Faker, context?: BalanceContext) => {
|
||||
if (!context || !context.user) {
|
||||
throw new Error('Balance: No user present!')
|
||||
}
|
||||
|
||||
const balance = new Balance()
|
||||
balance.modified = context.modified ? context.modified : faker.date.recent()
|
||||
balance.recordDate = context.recordDate ? context.recordDate : faker.date.recent()
|
||||
balance.amount = context.amount ? context.amount : 10000000
|
||||
balance.user = context.user
|
||||
|
||||
return balance
|
||||
})
|
||||
18
database/src/factories/transaction-creation.factory.ts
Normal file
18
database/src/factories/transaction-creation.factory.ts
Normal file
@ -0,0 +1,18 @@
|
||||
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
|
||||
})
|
||||
18
database/src/factories/transaction-signature.factory.ts
Normal file
18
database/src/factories/transaction-signature.factory.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import Faker from 'faker'
|
||||
import { define } from 'typeorm-seeding'
|
||||
import { TransactionSignature } from '../../entity/TransactionSignature'
|
||||
import { TransactionSignatureContext } from '../interface/TransactionContext'
|
||||
import { randomBytes } from 'crypto'
|
||||
|
||||
define(TransactionSignature, (faker: typeof Faker, context?: TransactionSignatureContext) => {
|
||||
if (!context || !context.transaction) {
|
||||
throw new Error('TransactionSignature: No transaction present!')
|
||||
}
|
||||
|
||||
const transactionSignature = new TransactionSignature()
|
||||
transactionSignature.signature = context.signature ? context.signature : randomBytes(64)
|
||||
transactionSignature.pubkey = context.pubkey ? context.pubkey : randomBytes(32)
|
||||
transactionSignature.transaction = context.transaction
|
||||
|
||||
return transactionSignature
|
||||
})
|
||||
20
database/src/factories/transaction.factory.ts
Normal file
20
database/src/factories/transaction.factory.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import Faker from 'faker'
|
||||
import { define } from 'typeorm-seeding'
|
||||
import { Transaction } from '../../entity/Transaction'
|
||||
import { TransactionContext } from '../interface/TransactionContext'
|
||||
import { randomBytes } from 'crypto'
|
||||
|
||||
define(Transaction, (faker: typeof Faker, context?: TransactionContext) => {
|
||||
if (!context) context = {}
|
||||
|
||||
const transaction = new Transaction()
|
||||
transaction.transactionTypeId = context.transactionTypeId ? context.transactionTypeId : 2
|
||||
transaction.txHash = context.txHash ? context.txHash : randomBytes(48)
|
||||
transaction.memo = context.memo || context.memo === '' ? context.memo : faker.lorem.sentence()
|
||||
transaction.received = context.received ? context.received : new Date()
|
||||
transaction.blockchainTypeId = context.blockchainTypeId ? context.blockchainTypeId : 1
|
||||
if (context.transactionSendCoin) transaction.transactionSendCoin = context.transactionSendCoin
|
||||
if (context.transactionCreation) transaction.transactionCreation = context.transactionCreation
|
||||
|
||||
return transaction
|
||||
})
|
||||
19
database/src/factories/user-transaction.factory.ts
Normal file
19
database/src/factories/user-transaction.factory.ts
Normal file
@ -0,0 +1,19 @@
|
||||
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
|
||||
})
|
||||
@ -9,6 +9,7 @@ import { CreatePeterLustigSeed } from './seeds/users/peter-lustig.admin.seed'
|
||||
import { CreateBibiBloxbergSeed } from './seeds/users/bibi-bloxberg.seed'
|
||||
import { CreateRaeuberHotzenplotzSeed } from './seeds/users/raeuber-hotzenplotz.seed'
|
||||
import { CreateBobBaumeisterSeed } from './seeds/users/bob-baumeister.seed'
|
||||
import { DecayStartBlockSeed } from './seeds/decay-start-block.seed'
|
||||
|
||||
const run = async (command: string) => {
|
||||
// Database actions not supported by our migration library
|
||||
@ -59,6 +60,7 @@ const run = async (command: string) => {
|
||||
root: process.cwd(),
|
||||
configName: 'ormconfig.js',
|
||||
})
|
||||
await runSeeder(DecayStartBlockSeed)
|
||||
await runSeeder(CreatePeterLustigSeed)
|
||||
await runSeeder(CreateBibiBloxbergSeed)
|
||||
await runSeeder(CreateRaeuberHotzenplotzSeed)
|
||||
|
||||
52
database/src/interface/TransactionContext.ts
Normal file
52
database/src/interface/TransactionContext.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { Transaction } from '../../entity/Transaction'
|
||||
import { TransactionSendCoin } from '../../entity/TransactionSendCoin'
|
||||
import { TransactionCreation } from '../../entity/TransactionCreation'
|
||||
import { User } from '../../entity/User'
|
||||
|
||||
export interface TransactionContext {
|
||||
transactionTypeId?: number
|
||||
txHash?: Buffer
|
||||
memo?: string
|
||||
received?: Date
|
||||
blockchainTypeId?: number
|
||||
transactionSendCoin?: TransactionSendCoin
|
||||
transactionCreation?: TransactionCreation
|
||||
}
|
||||
|
||||
export interface BalanceContext {
|
||||
modified?: Date
|
||||
recordDate?: Date
|
||||
amount?: number
|
||||
user?: User
|
||||
}
|
||||
|
||||
export interface TransactionSendCoinContext {
|
||||
senderPublic?: Buffer
|
||||
userId?: number
|
||||
recipiantPublic?: Buffer
|
||||
recipiantUserId?: number
|
||||
amount?: number
|
||||
senderFinalBalance?: number
|
||||
transaction?: Transaction
|
||||
}
|
||||
|
||||
export interface TransactionCreationContext {
|
||||
userId?: number
|
||||
amount?: number
|
||||
targetDate?: Date
|
||||
transaction?: Transaction
|
||||
}
|
||||
|
||||
export interface UserTransactionContext {
|
||||
userId?: number
|
||||
transactionId?: number
|
||||
transactionTypeId?: number
|
||||
balance?: number
|
||||
balanceDate?: Date
|
||||
}
|
||||
|
||||
export interface TransactionSignatureContext {
|
||||
signature?: Buffer
|
||||
pubkey?: Buffer
|
||||
transaction?: Transaction
|
||||
}
|
||||
@ -27,4 +27,14 @@ export interface UserInterface {
|
||||
modified?: Date
|
||||
// flag for admin
|
||||
isAdmin?: boolean
|
||||
// flag for balance (creation of 1000 GDD)
|
||||
addBalance?: boolean
|
||||
// balance
|
||||
balanceModified?: Date
|
||||
recordDate?: Date
|
||||
targetDate?: Date
|
||||
amount?: number
|
||||
creationTxHash?: Buffer
|
||||
signature?: Buffer
|
||||
signaturePubkey?: Buffer
|
||||
}
|
||||
|
||||
17
database/src/seeds/decay-start-block.seed.ts
Normal file
17
database/src/seeds/decay-start-block.seed.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { Factory, Seeder } from 'typeorm-seeding'
|
||||
import { Transaction } from '../../entity/Transaction'
|
||||
|
||||
export class DecayStartBlockSeed implements Seeder {
|
||||
public async run(factory: Factory): Promise<void> {
|
||||
await factory(Transaction)({
|
||||
transactionTypeId: 9,
|
||||
txHash: Buffer.from(
|
||||
'9c9c4152b8a4cfbac287eee18d2d262e9de756fae726fc0ca36b788564973fff00000000000000000000000000000000',
|
||||
'hex',
|
||||
),
|
||||
memo: '',
|
||||
received: new Date('2021-11-30T09:13:26'),
|
||||
blockchainTypeId: 1,
|
||||
}).create()
|
||||
}
|
||||
}
|
||||
@ -5,16 +5,28 @@ import {
|
||||
ServerUserContext,
|
||||
LoginUserRolesContext,
|
||||
} from '../../interface/UserContext'
|
||||
import {
|
||||
BalanceContext,
|
||||
TransactionContext,
|
||||
TransactionCreationContext,
|
||||
UserTransactionContext,
|
||||
TransactionSignatureContext,
|
||||
} from '../../interface/TransactionContext'
|
||||
import { UserInterface } from '../../interface/UserInterface'
|
||||
import { User } from '../../../entity/User'
|
||||
import { LoginUser } from '../../../entity/LoginUser'
|
||||
import { LoginUserBackup } from '../../../entity/LoginUserBackup'
|
||||
import { ServerUser } from '../../../entity/ServerUser'
|
||||
import { LoginUserRoles } from '../../../entity/LoginUserRoles'
|
||||
import { Balance } from '../../../entity/Balance'
|
||||
import { Transaction } from '../../../entity/Transaction'
|
||||
import { TransactionSignature } from '../../../entity/TransactionSignature'
|
||||
import { UserTransaction } from '../../../entity/UserTransaction'
|
||||
import { TransactionCreation } from '../../../entity/TransactionCreation'
|
||||
import { Factory } from 'typeorm-seeding'
|
||||
|
||||
export const userSeeder = async (factory: Factory, userData: UserInterface): Promise<void> => {
|
||||
await factory(User)(createUserContext(userData)).create()
|
||||
const user = await factory(User)(createUserContext(userData)).create()
|
||||
const loginUser = await factory(LoginUser)(createLoginUserContext(userData)).create()
|
||||
await factory(LoginUserBackup)(createLoginUserBackupContext(userData, loginUser)).create()
|
||||
|
||||
@ -25,9 +37,26 @@ export const userSeeder = async (factory: Factory, userData: UserInterface): Pro
|
||||
// It works with LoginRoles empty!!
|
||||
await factory(LoginUserRoles)(createLoginUserRolesContext(loginUser)).create()
|
||||
}
|
||||
|
||||
if (userData.addBalance) {
|
||||
// create some GDD for the user
|
||||
await factory(Balance)(createBalanceContext(userData, user)).create()
|
||||
const transaction = await factory(Transaction)(
|
||||
createTransactionContext(userData, 1, 'Herzlich Willkommen bei Gradido!'),
|
||||
).create()
|
||||
await factory(TransactionCreation)(
|
||||
createTransactionCreationContext(userData, user, transaction),
|
||||
).create()
|
||||
await factory(UserTransaction)(
|
||||
createUserTransactionContext(userData, user, transaction),
|
||||
).create()
|
||||
await factory(TransactionSignature)(
|
||||
createTransactionSignatureContext(userData, transaction),
|
||||
).create()
|
||||
}
|
||||
}
|
||||
|
||||
export const createUserContext = (context: UserInterface): UserContext => {
|
||||
const createUserContext = (context: UserInterface): UserContext => {
|
||||
return {
|
||||
pubkey: context.pubKey,
|
||||
email: context.email,
|
||||
@ -38,7 +67,7 @@ export const createUserContext = (context: UserInterface): UserContext => {
|
||||
}
|
||||
}
|
||||
|
||||
export const createLoginUserContext = (context: UserInterface): LoginUserContext => {
|
||||
const createLoginUserContext = (context: UserInterface): LoginUserContext => {
|
||||
return {
|
||||
email: context.email,
|
||||
firstName: context.firstName,
|
||||
@ -59,7 +88,7 @@ export const createLoginUserContext = (context: UserInterface): LoginUserContext
|
||||
}
|
||||
}
|
||||
|
||||
export const createLoginUserBackupContext = (
|
||||
const createLoginUserBackupContext = (
|
||||
context: UserInterface,
|
||||
loginUser: LoginUser,
|
||||
): LoginUserBackupContext => {
|
||||
@ -70,7 +99,7 @@ export const createLoginUserBackupContext = (
|
||||
}
|
||||
}
|
||||
|
||||
export const createServerUserContext = (context: UserInterface): ServerUserContext => {
|
||||
const createServerUserContext = (context: UserInterface): ServerUserContext => {
|
||||
return {
|
||||
role: context.role,
|
||||
username: context.username,
|
||||
@ -83,9 +112,69 @@ export const createServerUserContext = (context: UserInterface): ServerUserConte
|
||||
}
|
||||
}
|
||||
|
||||
export const createLoginUserRolesContext = (loginUser: LoginUser): LoginUserRolesContext => {
|
||||
const createLoginUserRolesContext = (loginUser: LoginUser): LoginUserRolesContext => {
|
||||
return {
|
||||
userId: loginUser.id,
|
||||
roleId: 1,
|
||||
}
|
||||
}
|
||||
|
||||
const createBalanceContext = (context: UserInterface, user: User): BalanceContext => {
|
||||
return {
|
||||
modified: context.balanceModified,
|
||||
recordDate: context.recordDate,
|
||||
amount: context.amount,
|
||||
user,
|
||||
}
|
||||
}
|
||||
|
||||
const createTransactionContext = (
|
||||
context: UserInterface,
|
||||
type: number,
|
||||
memo: string,
|
||||
): TransactionContext => {
|
||||
return {
|
||||
transactionTypeId: type,
|
||||
txHash: context.creationTxHash,
|
||||
memo,
|
||||
received: context.recordDate,
|
||||
}
|
||||
}
|
||||
|
||||
const createTransactionCreationContext = (
|
||||
context: UserInterface,
|
||||
user: User,
|
||||
transaction: Transaction,
|
||||
): TransactionCreationContext => {
|
||||
return {
|
||||
userId: user.id,
|
||||
amount: context.amount,
|
||||
targetDate: context.targetDate,
|
||||
transaction,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
const createTransactionSignatureContext = (
|
||||
context: UserInterface,
|
||||
transaction: Transaction,
|
||||
): TransactionSignatureContext => {
|
||||
return {
|
||||
signature: context.signature,
|
||||
pubkey: context.signaturePubkey,
|
||||
transaction,
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,4 +22,21 @@ export const bibiBloxberg = {
|
||||
'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,
|
||||
addBalance: true,
|
||||
balanceModified: new Date('2021-11-30T10:37:11'),
|
||||
recordDate: new Date('2021-11-30T10:37:11'),
|
||||
targetDate: new Date('2021-08-01 00:00:00'),
|
||||
amount: 10000000,
|
||||
creationTxHash: Buffer.from(
|
||||
'51103dc0fc2ca5d5d75a9557a1e899304e5406cfdb1328d8df6414d527b0118100000000000000000000000000000000',
|
||||
'hex',
|
||||
),
|
||||
signature: Buffer.from(
|
||||
'2a2c71f3e41adc060bbc3086577e2d57d24eeeb0a7727339c3f85aad813808f601d7e1df56a26e0929d2e67fc054fca429ccfa283ed2782185c7f009fe008f0c',
|
||||
'hex',
|
||||
),
|
||||
signaturePubkey: Buffer.from(
|
||||
'7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770',
|
||||
'hex',
|
||||
),
|
||||
}
|
||||
|
||||
@ -22,4 +22,21 @@ export const bobBaumeister = {
|
||||
'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,
|
||||
addBalance: true,
|
||||
balanceModified: new Date('2021-11-30T10:37:14'),
|
||||
recordDate: new Date('2021-11-30T10:37:14'),
|
||||
targetDate: new Date('2021-08-01 00:00:00'),
|
||||
amount: 10000000,
|
||||
creationTxHash: Buffer.from(
|
||||
'be095dc87acb94987e71168fee8ecbf50ecb43a180b1006e75d573b35725c69c00000000000000000000000000000000',
|
||||
'hex',
|
||||
),
|
||||
signature: Buffer.from(
|
||||
'1fbd6b9a3d359923b2501557f3bc79fa7e428127c8090fb16bc490b4d87870ab142b3817ddd902d22f0b26472a483233784a0e460c0622661752a13978903905',
|
||||
'hex',
|
||||
),
|
||||
signaturePubkey: Buffer.from(
|
||||
'7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770',
|
||||
'hex',
|
||||
),
|
||||
}
|
||||
|
||||
@ -22,4 +22,21 @@ export const raeuberHotzenplotz = {
|
||||
'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,
|
||||
addBalance: true,
|
||||
balanceModified: new Date('2021-11-30T10:37:13'),
|
||||
recordDate: new Date('2021-11-30T10:37:13'),
|
||||
targetDate: new Date('2021-08-01 00:00:00'),
|
||||
amount: 10000000,
|
||||
creationTxHash: Buffer.from(
|
||||
'23ba44fd84deb59b9f32969ad0cb18bfa4588be1bdb99c396888506474c16c1900000000000000000000000000000000',
|
||||
'hex',
|
||||
),
|
||||
signature: Buffer.from(
|
||||
'756d3da061687c575d1dbc5073908f646aa5f498b0927b217c83b48af471450e571dfe8421fb8e1f1ebd1104526b7e7c6fa78684e2da59c8f7f5a8dc3d9e5b0b',
|
||||
'hex',
|
||||
),
|
||||
signaturePubkey: Buffer.from(
|
||||
'7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770',
|
||||
'hex',
|
||||
),
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user