mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 585-Coin-animation-on-and-off-switchable
This commit is contained in:
commit
161fae9ba2
1
.env.local.dist
Normal file
1
.env.local.dist
Normal file
@ -0,0 +1 @@
|
||||
// for local .env entries
|
||||
1
.env.shell
Normal file
1
.env.shell
Normal file
@ -0,0 +1 @@
|
||||
BUILD_COMMIT="$(git rev-parse HEAD)"
|
||||
13
.github/workflows/test.yml
vendored
13
.github/workflows/test.yml
vendored
@ -1,6 +1,5 @@
|
||||
name: gradido test CI
|
||||
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
@ -417,8 +416,6 @@ jobs:
|
||||
env:
|
||||
MARIADB_ALLOW_EMPTY_PASSWORD: 1
|
||||
MARIADB_USER: root
|
||||
# ports:
|
||||
# - 3306:3306
|
||||
options: --health-cmd="mysqladmin ping"
|
||||
--health-interval=5s
|
||||
--health-timeout=5s
|
||||
@ -427,11 +424,14 @@ jobs:
|
||||
- name: get mariadb container id
|
||||
run: echo "::set-output name=id::$(docker container ls | grep mariadb | awk '{ print $1 }')"
|
||||
id: mariadb_container
|
||||
- name: show networks
|
||||
run: echo "$(docker network ls)"
|
||||
- name: get automatic created network
|
||||
run: echo "::set-output name=id::$(docker network ls | grep github_network | awk '{ print $1 }')"
|
||||
id: network
|
||||
- name: Start database migration
|
||||
run: docker run --network ${{ steps.network.outputs.id }} --name=database --env NODE_ENV=production --env DB_HOST=mariadb -d gradido/database:production_up
|
||||
- name: get database migration container id
|
||||
run: echo "::set-output name=id::$(docker container ls | grep database | awk '{ print $1 }')"
|
||||
id: database_container
|
||||
- name: Start Login-Server
|
||||
run: docker run --network ${{ steps.network.outputs.id }} --name=login-server -d gradido/login_server:with-config
|
||||
- name: get login-server container id
|
||||
@ -452,12 +452,13 @@ jobs:
|
||||
path: /tmp
|
||||
- name: Load Docker Image
|
||||
run: docker load < /tmp/community_server.tar
|
||||
|
||||
# for debugging login-server
|
||||
- name: check login-server
|
||||
run: docker logs ${{ steps.login_server_container.outputs.id }}
|
||||
- name: check mariadb
|
||||
run: docker logs ${{ steps.mariadb_container.outputs.id }}
|
||||
- name: check migration
|
||||
run: docker logs ${{ steps.database_container.outputs.id }}
|
||||
##########################################################################
|
||||
# UNIT TESTS BACKEND COMMUNITY-SERVER #######################################
|
||||
##########################################################################
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ messages.pot
|
||||
.skeema
|
||||
nbproject
|
||||
.metadata
|
||||
/.env
|
||||
|
||||
4563
backend/package-lock.json
generated
4563
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,8 @@
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"mysql2": "^2.3.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"type-graphql": "^1.1.1"
|
||||
"type-graphql": "^1.1.1",
|
||||
"typeorm": "^0.2.37"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.12",
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
import { createConnection, Connection } from 'mysql2/promise'
|
||||
import CONFIG from '../config'
|
||||
|
||||
const connection = async (): Promise<Connection> => {
|
||||
const con = await createConnection({
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
user: CONFIG.DB_USER,
|
||||
password: CONFIG.DB_PASSWORD,
|
||||
database: CONFIG.DB_DATABASE,
|
||||
})
|
||||
|
||||
await con.connect()
|
||||
|
||||
return con
|
||||
}
|
||||
|
||||
export default connection
|
||||
@ -9,7 +9,7 @@ export class TransactionListInput {
|
||||
items: number
|
||||
|
||||
@Field(() => String)
|
||||
order: string
|
||||
order: 'ASC' | 'DESC'
|
||||
}
|
||||
|
||||
@ArgsType()
|
||||
|
||||
@ -1,29 +1,43 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { ObjectType, Field, Int } from 'type-graphql'
|
||||
import { Transaction } from '../../typeorm/entity/Transaction'
|
||||
|
||||
@ObjectType()
|
||||
export class Decay {
|
||||
constructor(json: any) {
|
||||
this.balance = Number(json.balance)
|
||||
this.decayStart = json.decay_start
|
||||
this.decayEnd = json.decay_end
|
||||
this.decayDuration = json.decay_duration
|
||||
this.decayStartBlock = json.decay_start_block
|
||||
if (json) {
|
||||
this.balance = Number(json.balance)
|
||||
this.decayStart = json.decay_start
|
||||
this.decayEnd = json.decay_end
|
||||
this.decayDuration = json.decay_duration
|
||||
this.decayStartBlock = json.decay_start_block
|
||||
}
|
||||
}
|
||||
|
||||
static async getDecayStartBlock(): Promise<Transaction | undefined> {
|
||||
if (!this.decayStartBlockTransaction) {
|
||||
this.decayStartBlockTransaction = await Transaction.getDecayStartBlock()
|
||||
}
|
||||
return this.decayStartBlockTransaction
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
balance: number
|
||||
|
||||
// timestamp in seconds
|
||||
@Field(() => Int, { nullable: true })
|
||||
decayStart?: number
|
||||
decayStart: string
|
||||
|
||||
// timestamp in seconds
|
||||
@Field(() => Int, { nullable: true })
|
||||
decayEnd?: number
|
||||
decayEnd: string
|
||||
|
||||
@Field(() => String, { nullable: true })
|
||||
decayDuration?: string
|
||||
decayDuration?: number
|
||||
|
||||
@Field(() => Int, { nullable: true })
|
||||
decayStartBlock?: number
|
||||
decayStartBlock?: string
|
||||
|
||||
static decayStartBlockTransaction: Transaction | undefined
|
||||
}
|
||||
|
||||
@ -10,18 +10,11 @@ import { Decay } from './Decay'
|
||||
|
||||
@ObjectType()
|
||||
export class Transaction {
|
||||
constructor(json: any) {
|
||||
this.type = json.type
|
||||
this.balance = Number(json.balance)
|
||||
this.decayStart = json.decay_start
|
||||
this.decayEnd = json.decay_end
|
||||
this.decayDuration = json.decay_duration
|
||||
this.memo = json.memo
|
||||
this.transactionId = json.transaction_id
|
||||
this.name = json.name
|
||||
this.email = json.email
|
||||
this.date = json.date
|
||||
this.decay = json.decay ? new Decay(json.decay) : undefined
|
||||
constructor() {
|
||||
this.type = ''
|
||||
this.balance = 0
|
||||
this.totalBalance = 0
|
||||
this.memo = ''
|
||||
}
|
||||
|
||||
@Field(() => String)
|
||||
@ -30,14 +23,17 @@ export class Transaction {
|
||||
@Field(() => Number)
|
||||
balance: number
|
||||
|
||||
@Field({ nullable: true })
|
||||
decayStart?: number
|
||||
@Field(() => Number)
|
||||
totalBalance: number
|
||||
|
||||
@Field({ nullable: true })
|
||||
decayEnd?: number
|
||||
decayStart?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
decayDuration?: string
|
||||
decayEnd?: string
|
||||
|
||||
@Field({ nullable: true })
|
||||
decayDuration?: number
|
||||
|
||||
@Field(() => String)
|
||||
memo: string
|
||||
@ -60,15 +56,12 @@ export class Transaction {
|
||||
|
||||
@ObjectType()
|
||||
export class TransactionList {
|
||||
constructor(json: any) {
|
||||
this.gdtSum = Number(json.gdtSum)
|
||||
this.count = json.count
|
||||
this.balance = Number(json.balance)
|
||||
this.decay = Number(json.decay)
|
||||
this.decayDate = json.decay_date
|
||||
this.transactions = json.transactions.map((el: any) => {
|
||||
return new Transaction(el)
|
||||
})
|
||||
constructor() {
|
||||
this.gdtSum = 0
|
||||
this.count = 0
|
||||
this.balance = 0
|
||||
this.decay = 0
|
||||
this.decayDate = ''
|
||||
}
|
||||
|
||||
@Field(() => Number)
|
||||
|
||||
@ -16,6 +16,7 @@ export class User {
|
||||
this.lastName = json.last_name
|
||||
this.username = json.username
|
||||
this.description = json.description
|
||||
this.pubkey = json.public_hex
|
||||
this.language = json.language
|
||||
this.publisherId = json.publisher_id
|
||||
}
|
||||
@ -35,6 +36,8 @@ export class User {
|
||||
@Field(() => String)
|
||||
description: string
|
||||
|
||||
@Field(() => String)
|
||||
pubkey: string
|
||||
/*
|
||||
@Field(() => String)
|
||||
pubkey: string
|
||||
|
||||
@ -5,14 +5,41 @@ import { Resolver, Query, Ctx, Authorized } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { Balance } from '../models/Balance'
|
||||
import { apiGet } from '../../apis/HttpRequest'
|
||||
import { User as dbUser } from '../../typeorm/entity/User'
|
||||
import { Balance as dbBalance } from '../../typeorm/entity/Balance'
|
||||
import { calculateDecay } from '../../util/decay'
|
||||
import { roundFloorFrom4 } from '../../util/round'
|
||||
|
||||
@Resolver()
|
||||
export class BalanceResolver {
|
||||
@Authorized()
|
||||
@Query(() => Balance)
|
||||
async balance(@Ctx() context: any): Promise<Balance> {
|
||||
const result = await apiGet(CONFIG.COMMUNITY_API_URL + 'getBalance/' + context.sessionId)
|
||||
// get public key for current logged in user
|
||||
const result = await apiGet(CONFIG.LOGIN_API_URL + 'login?session_id=' + context.sessionId)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
return new Balance(result.data)
|
||||
|
||||
// load user and balance
|
||||
const userEntity = await dbUser.findByPubkeyHex(result.data.user.public_hex)
|
||||
const balanceEntity = await dbBalance.findByUser(userEntity.id)
|
||||
let balance: Balance
|
||||
const now = new Date()
|
||||
if (balanceEntity) {
|
||||
balance = new Balance({
|
||||
balance: roundFloorFrom4(balanceEntity.amount),
|
||||
decay: roundFloorFrom4(
|
||||
await calculateDecay(balanceEntity.amount, balanceEntity.recordDate, now),
|
||||
),
|
||||
decay_date: now.toString(),
|
||||
})
|
||||
} else {
|
||||
balance = new Balance({
|
||||
balance: 0,
|
||||
decay: 0,
|
||||
decay_date: now.toString(),
|
||||
})
|
||||
}
|
||||
|
||||
return balance
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import CONFIG from '../../config'
|
||||
import { GdtEntryList } from '../models/GdtEntryList'
|
||||
import { GdtTransactionSessionIdInput } from '../inputs/GdtInputs'
|
||||
import { apiGet } from '../../apis/HttpRequest'
|
||||
import { User as dbUser } from '../../typeorm/entity/User'
|
||||
|
||||
@Resolver()
|
||||
export class GdtResolver {
|
||||
@ -17,13 +18,20 @@ export class GdtResolver {
|
||||
{ currentPage = 1, pageSize = 5, order = 'DESC' }: GdtTransactionSessionIdInput,
|
||||
@Ctx() context: any,
|
||||
): Promise<GdtEntryList> {
|
||||
const result = await apiGet(
|
||||
`${CONFIG.COMMUNITY_API_URL}listGDTTransactions/${currentPage}/${pageSize}/${order}/${context.sessionId}`,
|
||||
// get public key for current logged in user
|
||||
const result = await apiGet(CONFIG.LOGIN_API_URL + 'login?session_id=' + context.sessionId)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
|
||||
// load user
|
||||
const userEntity = await dbUser.findByPubkeyHex(result.data.user.public_hex)
|
||||
|
||||
const resultGDT = await apiGet(
|
||||
`${CONFIG.GDT_API_URL}/GdtEntries/listPerEmailApi/${userEntity.email}/${currentPage}/${pageSize}/${order}`,
|
||||
)
|
||||
if (!result.success) {
|
||||
if (!resultGDT.success) {
|
||||
throw new Error(result.data)
|
||||
}
|
||||
|
||||
return new GdtEntryList(result.data)
|
||||
return new GdtEntryList(resultGDT.data)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import { Resolver, Query, Args, Authorized, Ctx } from 'type-graphql'
|
||||
import { Resolver, Query, Args, Authorized, Ctx, Mutation } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { TransactionList } from '../models/Transaction'
|
||||
import { TransactionListInput, TransactionSendArgs } from '../inputs/TransactionInput'
|
||||
import { apiGet, apiPost } from '../../apis/HttpRequest'
|
||||
import { User as dbUser } from '../../typeorm/entity/User'
|
||||
import { Balance as dbBalance } from '../../typeorm/entity/Balance'
|
||||
import listTransactions from './listTransactions'
|
||||
import { roundFloorFrom4 } from '../../util/round'
|
||||
import { calculateDecay } from '../../util/decay'
|
||||
|
||||
@Resolver()
|
||||
export class TransactionResolver {
|
||||
@ -15,15 +20,38 @@ export class TransactionResolver {
|
||||
@Args() { firstPage = 1, items = 25, order = 'DESC' }: TransactionListInput,
|
||||
@Ctx() context: any,
|
||||
): Promise<TransactionList> {
|
||||
const result = await apiGet(
|
||||
`${CONFIG.COMMUNITY_API_URL}listTransactions/${firstPage}/${items}/${order}/${context.sessionId}`,
|
||||
)
|
||||
// get public key for current logged in user
|
||||
const result = await apiGet(CONFIG.LOGIN_API_URL + 'login?session_id=' + context.sessionId)
|
||||
if (!result.success) throw new Error(result.data)
|
||||
return new TransactionList(result.data)
|
||||
|
||||
// load user
|
||||
const userEntity = await dbUser.findByPubkeyHex(result.data.user.public_hex)
|
||||
|
||||
const transactions = await listTransactions(firstPage, items, order, userEntity)
|
||||
|
||||
// get gdt sum
|
||||
const resultGDTSum = await apiPost(`${CONFIG.GDT_API_URL}/GdtEntries/sumPerEmailApi`, {
|
||||
email: userEntity.email,
|
||||
})
|
||||
if (!resultGDTSum.success) throw new Error(resultGDTSum.data)
|
||||
transactions.gdtSum = resultGDTSum.data.sum
|
||||
|
||||
// get balance
|
||||
const balanceEntity = await dbBalance.findByUser(userEntity.id)
|
||||
if (balanceEntity) {
|
||||
const now = new Date()
|
||||
transactions.balance = roundFloorFrom4(balanceEntity.amount)
|
||||
transactions.decay = roundFloorFrom4(
|
||||
await calculateDecay(balanceEntity.amount, balanceEntity.recordDate, now),
|
||||
)
|
||||
transactions.decayDate = now.toString()
|
||||
}
|
||||
|
||||
return transactions
|
||||
}
|
||||
|
||||
@Authorized()
|
||||
@Query(() => String)
|
||||
@Mutation(() => String)
|
||||
async sendCoins(
|
||||
@Args() { email, amount, memo }: TransactionSendArgs,
|
||||
@Ctx() context: any,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
|
||||
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware } from 'type-graphql'
|
||||
import { Resolver, Query, Args, Arg, Authorized, Ctx, UseMiddleware, Mutation } from 'type-graphql'
|
||||
import CONFIG from '../../config'
|
||||
import { CheckUsernameResponse } from '../models/CheckUsernameResponse'
|
||||
import { LoginViaVerificationCode } from '../models/LoginViaVerificationCode'
|
||||
@ -66,8 +66,8 @@ export class UserResolver {
|
||||
return 'success'
|
||||
}
|
||||
|
||||
@Query(() => String)
|
||||
async create(
|
||||
@Mutation(() => String)
|
||||
async createUser(
|
||||
@Args() { email, firstName, lastName, password, language }: CreateUserArgs,
|
||||
): Promise<string> {
|
||||
const payload = {
|
||||
@ -104,7 +104,7 @@ export class UserResolver {
|
||||
return new SendPasswordResetEmailResponse(response.data)
|
||||
}
|
||||
|
||||
@Query(() => String)
|
||||
@Mutation(() => String)
|
||||
async resetPassword(
|
||||
@Args()
|
||||
{ sessionId, email, password }: ChangePasswordArgs,
|
||||
@ -122,7 +122,7 @@ export class UserResolver {
|
||||
}
|
||||
|
||||
@Authorized()
|
||||
@Query(() => UpdateUserInfosResponse)
|
||||
@Mutation(() => UpdateUserInfosResponse)
|
||||
async updateUserInfos(
|
||||
@Args()
|
||||
{
|
||||
|
||||
19
backend/src/graphql/resolvers/index.ts
Normal file
19
backend/src/graphql/resolvers/index.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { UserResolver } from './UserResolver'
|
||||
import { BalanceResolver } from './BalanceResolver'
|
||||
import { GdtResolver } from './GdtResolver'
|
||||
import { TransactionResolver } from './TransactionResolver'
|
||||
import { KlicktippResolver } from './KlicktippResolver'
|
||||
import { NonEmptyArray } from 'type-graphql'
|
||||
|
||||
export { UserResolver, BalanceResolver, GdtResolver, TransactionResolver, KlicktippResolver }
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
const resolvers = (): NonEmptyArray<Function> => [
|
||||
UserResolver,
|
||||
BalanceResolver,
|
||||
GdtResolver,
|
||||
TransactionResolver,
|
||||
KlicktippResolver,
|
||||
]
|
||||
|
||||
export default resolvers
|
||||
194
backend/src/graphql/resolvers/listTransactions.ts
Normal file
194
backend/src/graphql/resolvers/listTransactions.ts
Normal file
@ -0,0 +1,194 @@
|
||||
import { User as dbUser } from '../../typeorm/entity/User'
|
||||
import { TransactionList, Transaction } from '../models/Transaction'
|
||||
import { UserTransaction as dbUserTransaction } from '../../typeorm/entity/UserTransaction'
|
||||
import { Transaction as dbTransaction } from '../../typeorm/entity/Transaction'
|
||||
import { Decay } from '../models/Decay'
|
||||
import { calculateDecayWithInterval } from '../../util/decay'
|
||||
import { roundFloorFrom4 } from '../../util/round'
|
||||
|
||||
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
|
||||
.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()
|
||||
|
||||
const transactionIndiced: dbTransaction[] = []
|
||||
transactions.forEach((transaction: dbTransaction) => {
|
||||
transactionIndiced[transaction.id] = transaction
|
||||
if (transaction.transactionTypeId === 2) {
|
||||
involvedUserIds.push(transaction.transactionSendCoin.userId)
|
||||
involvedUserIds.push(transaction.transactionSendCoin.recipiantUserId)
|
||||
}
|
||||
})
|
||||
// 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 userIndiced = await dbUser.getUsersIndiced(involvedUsersUnique)
|
||||
|
||||
const decayStartTransaction = await Decay.getDecayStartBlock()
|
||||
|
||||
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 prev = i > 0 ? userTransactions[i - 1] : null
|
||||
|
||||
if (prev && prev.balance > 0) {
|
||||
const current = userTransaction
|
||||
const decay = await calculateDecayWithInterval(
|
||||
prev.balance,
|
||||
prev.balanceDate,
|
||||
current.balanceDate,
|
||||
)
|
||||
const balance = prev.balance - decay.balance
|
||||
|
||||
if (balance) {
|
||||
finalTransaction.decay = decay
|
||||
finalTransaction.decay.balance = roundFloorFrom4(balance)
|
||||
if (
|
||||
decayStartTransaction &&
|
||||
prev.transactionId < decayStartTransaction.id &&
|
||||
current.transactionId > decayStartTransaction.id
|
||||
) {
|
||||
finalTransaction.decay.decayStartBlock = (
|
||||
decayStartTransaction.received.getTime() / 1000
|
||||
).toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sender or receiver when user has sended money
|
||||
// group name if creation
|
||||
// type: gesendet / empfangen / geschöpft
|
||||
// transaktion nr / id
|
||||
// date
|
||||
// balance
|
||||
if (userTransaction.transactionTypeId === 1) {
|
||||
// creation
|
||||
const creation = transaction.transactionCreation
|
||||
|
||||
finalTransaction.name = 'Gradido Akademie'
|
||||
finalTransaction.type = 'creation'
|
||||
// finalTransaction.targetDate = creation.targetDate
|
||||
finalTransaction.balance = roundFloorFrom4(creation.amount)
|
||||
} else if (userTransaction.transactionTypeId === 2) {
|
||||
// send coin
|
||||
const sendCoin = transaction.transactionSendCoin
|
||||
let otherUser: dbUser | undefined
|
||||
finalTransaction.balance = roundFloorFrom4(sendCoin.amount)
|
||||
if (sendCoin.userId === user.id) {
|
||||
finalTransaction.type = 'send'
|
||||
otherUser = userIndiced[sendCoin.recipiantUserId]
|
||||
// finalTransaction.pubkey = sendCoin.recipiantPublic
|
||||
} else if (sendCoin.recipiantUserId === user.id) {
|
||||
finalTransaction.type = 'receive'
|
||||
otherUser = userIndiced[sendCoin.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 = await calculateDecayWithInterval(
|
||||
userTransaction.balance,
|
||||
userTransaction.balanceDate,
|
||||
now.getTime(),
|
||||
)
|
||||
const balance = userTransaction.balance - decay.balance
|
||||
if (balance) {
|
||||
const decayTransaction = new Transaction()
|
||||
decayTransaction.type = 'decay'
|
||||
decayTransaction.balance = roundFloorFrom4(balance)
|
||||
decayTransaction.decayDuration = decay.decayDuration
|
||||
decayTransaction.decayStart = decay.decayStart
|
||||
decayTransaction.decayEnd = decay.decayEnd
|
||||
finalTransactions.push(decayTransaction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return finalTransactions
|
||||
}
|
||||
|
||||
export default async function listTransactions(
|
||||
firstPage: number,
|
||||
items: number,
|
||||
order: 'ASC' | 'DESC',
|
||||
user: dbUser,
|
||||
): Promise<TransactionList> {
|
||||
let limit = items
|
||||
let offset = 0
|
||||
let skipFirstTransaction = false
|
||||
if (firstPage > 1) {
|
||||
offset = (firstPage - 1) * items - 1
|
||||
limit++
|
||||
}
|
||||
|
||||
if (offset && order === 'ASC') {
|
||||
offset--
|
||||
}
|
||||
let [userTransactions, userTransactionsCount] = await dbUserTransaction.findByUserPaged(
|
||||
user.id,
|
||||
limit,
|
||||
offset,
|
||||
order,
|
||||
)
|
||||
skipFirstTransaction = userTransactionsCount > offset + limit
|
||||
const decay = !(firstPage > 1)
|
||||
let transactions: Transaction[] = []
|
||||
if (userTransactions.length) {
|
||||
if (order === 'DESC') {
|
||||
userTransactions = userTransactions.reverse()
|
||||
}
|
||||
transactions = await calculateAndAddDecayTransactions(
|
||||
userTransactions,
|
||||
user,
|
||||
decay,
|
||||
skipFirstTransaction,
|
||||
)
|
||||
if (order === 'DESC') {
|
||||
transactions = transactions.reverse()
|
||||
}
|
||||
}
|
||||
|
||||
const transactionList = new TransactionList()
|
||||
transactionList.count = userTransactionsCount
|
||||
transactionList.transactions = transactions
|
||||
return transactionList
|
||||
}
|
||||
14
backend/src/graphql/schema.ts
Normal file
14
backend/src/graphql/schema.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { GraphQLSchema } from 'graphql'
|
||||
import { buildSchema } from 'type-graphql'
|
||||
|
||||
import resolvers from './resolvers'
|
||||
import { isAuthorized } from '../auth/auth'
|
||||
|
||||
const schema = async (): Promise<GraphQLSchema> => {
|
||||
return buildSchema({
|
||||
resolvers: resolvers(),
|
||||
authChecker: isAuthorized,
|
||||
})
|
||||
}
|
||||
|
||||
export default schema
|
||||
@ -2,93 +2,58 @@
|
||||
|
||||
import 'reflect-metadata'
|
||||
import express from 'express'
|
||||
import cors from 'cors'
|
||||
import { buildSchema } from 'type-graphql'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
import { RowDataPacket } from 'mysql2/promise'
|
||||
|
||||
import connection from './database/connection'
|
||||
// config
|
||||
import CONFIG from './config'
|
||||
|
||||
// TODO move to extern
|
||||
import { UserResolver } from './graphql/resolvers/UserResolver'
|
||||
import { BalanceResolver } from './graphql/resolvers/BalanceResolver'
|
||||
import { GdtResolver } from './graphql/resolvers/GdtResolver'
|
||||
import { TransactionResolver } from './graphql/resolvers/TransactionResolver'
|
||||
import { KlicktippResolver } from './graphql/resolvers/KlicktippResolver'
|
||||
// database
|
||||
import connection from './typeorm/connection'
|
||||
import getDBVersion from './typeorm/getDBVersion'
|
||||
|
||||
import { isAuthorized } from './auth/auth'
|
||||
// server
|
||||
import cors from './server/cors'
|
||||
import context from './server/context'
|
||||
import plugins from './server/plugins'
|
||||
|
||||
// graphql
|
||||
import schema from './graphql/schema'
|
||||
|
||||
// TODO implement
|
||||
// import queryComplexity, { simpleEstimator, fieldConfigEstimator } from "graphql-query-complexity";
|
||||
|
||||
const DB_VERSION = '0001-init_db'
|
||||
|
||||
const context = (args: any) => {
|
||||
const authorization = args.req.headers.authorization
|
||||
let token = null
|
||||
if (authorization) {
|
||||
token = authorization.replace(/^Bearer /, '')
|
||||
}
|
||||
const context = {
|
||||
token,
|
||||
setHeaders: [],
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
async function main() {
|
||||
// check for correct database version
|
||||
// open mysql connection
|
||||
const con = await connection()
|
||||
const [rows] = await con.query(`SELECT * FROM migrations ORDER BY version DESC LIMIT 1;`)
|
||||
if (
|
||||
(<RowDataPacket>rows).length === 0 ||
|
||||
!(<RowDataPacket>rows)[0].fileName ||
|
||||
(<RowDataPacket>rows)[0].fileName.indexOf(DB_VERSION) === -1
|
||||
) {
|
||||
throw new Error(`Wrong database version - the backend requires '${DB_VERSION}'`)
|
||||
if (!con || !con.isConnected) {
|
||||
throw new Error(`Couldn't open connection to database`)
|
||||
}
|
||||
|
||||
// const connection = await createConnection()
|
||||
const schema = await buildSchema({
|
||||
resolvers: [UserResolver, BalanceResolver, TransactionResolver, GdtResolver, KlicktippResolver],
|
||||
authChecker: isAuthorized,
|
||||
})
|
||||
|
||||
// Graphiql interface
|
||||
let playground = false
|
||||
if (CONFIG.GRAPHIQL) {
|
||||
playground = true
|
||||
// check for correct database version
|
||||
const dbVersion = await getDBVersion()
|
||||
if (!dbVersion || dbVersion.indexOf(DB_VERSION) === -1) {
|
||||
throw new Error(
|
||||
`Wrong database version - the backend requires '${DB_VERSION}' but found '${
|
||||
dbVersion || 'None'
|
||||
}'`,
|
||||
)
|
||||
}
|
||||
|
||||
// Express Server
|
||||
const server = express()
|
||||
|
||||
const corsOptions = {
|
||||
origin: '*',
|
||||
exposedHeaders: ['token'],
|
||||
}
|
||||
|
||||
server.use(cors(corsOptions))
|
||||
|
||||
const plugins = [
|
||||
{
|
||||
requestDidStart() {
|
||||
return {
|
||||
willSendResponse(requestContext: any) {
|
||||
const { setHeaders = [] } = requestContext.context
|
||||
setHeaders.forEach(({ key, value }: { [key: string]: string }) => {
|
||||
requestContext.response.http.headers.append(key, value)
|
||||
})
|
||||
return requestContext
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
// cors
|
||||
server.use(cors)
|
||||
|
||||
// Apollo Server
|
||||
const apollo = new ApolloServer({ schema, playground, context, plugins })
|
||||
const apollo = new ApolloServer({
|
||||
schema: await schema(),
|
||||
playground: CONFIG.GRAPHIQL,
|
||||
context,
|
||||
plugins,
|
||||
})
|
||||
apollo.applyMiddleware({ app: server })
|
||||
|
||||
// Start Server
|
||||
|
||||
@ -5,7 +5,7 @@ import jwt from 'jsonwebtoken'
|
||||
import CONFIG from '../config/'
|
||||
|
||||
export default (token: string): any => {
|
||||
if (!token) return null
|
||||
if (!token) return new Error('401 Unauthorized')
|
||||
let sessionId = null
|
||||
try {
|
||||
const decoded = jwt.verify(token, CONFIG.JWT_SECRET)
|
||||
@ -15,6 +15,6 @@ export default (token: string): any => {
|
||||
sessionId,
|
||||
}
|
||||
} catch (err) {
|
||||
return null
|
||||
throw new Error('403.13 - Client certificate revoked')
|
||||
}
|
||||
}
|
||||
|
||||
14
backend/src/server/context.ts
Normal file
14
backend/src/server/context.ts
Normal file
@ -0,0 +1,14 @@
|
||||
const context = (args: any) => {
|
||||
const authorization = args.req.headers.authorization
|
||||
let token = null
|
||||
if (authorization) {
|
||||
token = authorization.replace(/^Bearer /, '')
|
||||
}
|
||||
const context = {
|
||||
token,
|
||||
setHeaders: [],
|
||||
}
|
||||
return context
|
||||
}
|
||||
|
||||
export default context
|
||||
8
backend/src/server/cors.ts
Normal file
8
backend/src/server/cors.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import cors from 'cors'
|
||||
|
||||
const corsOptions = {
|
||||
origin: '*',
|
||||
exposedHeaders: ['token'],
|
||||
}
|
||||
|
||||
export default cors(corsOptions)
|
||||
17
backend/src/server/plugins.ts
Normal file
17
backend/src/server/plugins.ts
Normal file
@ -0,0 +1,17 @@
|
||||
const plugins = [
|
||||
{
|
||||
requestDidStart() {
|
||||
return {
|
||||
willSendResponse(requestContext: any) {
|
||||
const { setHeaders = [] } = requestContext.context
|
||||
setHeaders.forEach(({ key, value }: { [key: string]: string }) => {
|
||||
requestContext.response.http.headers.append(key, value)
|
||||
})
|
||||
return requestContext
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
export default plugins
|
||||
24
backend/src/typeorm/connection.ts
Normal file
24
backend/src/typeorm/connection.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { createConnection, Connection } from 'typeorm'
|
||||
import CONFIG from '../config'
|
||||
import path from 'path'
|
||||
|
||||
const connection = async (): Promise<Connection | null> => {
|
||||
let con = null
|
||||
try {
|
||||
con = await createConnection({
|
||||
name: 'default',
|
||||
type: 'mysql',
|
||||
host: CONFIG.DB_HOST,
|
||||
port: CONFIG.DB_PORT,
|
||||
username: CONFIG.DB_USER,
|
||||
password: CONFIG.DB_PASSWORD,
|
||||
database: CONFIG.DB_DATABASE,
|
||||
entities: [path.join(__dirname, 'entity', '*.{ts,js}')],
|
||||
synchronize: false,
|
||||
})
|
||||
} catch (error) {}
|
||||
|
||||
return con
|
||||
}
|
||||
|
||||
export default connection
|
||||
23
backend/src/typeorm/entity/Balance.ts
Normal file
23
backend/src/typeorm/entity/Balance.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
|
||||
@Entity('state_balances')
|
||||
export class Balance extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column({ name: 'state_user_id' })
|
||||
userId: number
|
||||
|
||||
@Column({ type: 'datetime' })
|
||||
modified: Date
|
||||
|
||||
@Column({ name: 'record_date', type: 'datetime' })
|
||||
recordDate: Date
|
||||
|
||||
@Column({ type: 'bigint' })
|
||||
amount: number
|
||||
|
||||
static findByUser(userId: number): Promise<Balance | undefined> {
|
||||
return this.createQueryBuilder('balance').where('balance.userId = :userId', { userId }).getOne()
|
||||
}
|
||||
}
|
||||
25
backend/src/typeorm/entity/Group.ts
Normal file
25
backend/src/typeorm/entity/Group.ts
Normal file
@ -0,0 +1,25 @@
|
||||
/* import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm";
|
||||
import { User } from "./User"
|
||||
|
||||
@Entity()
|
||||
export class Group {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
alias: string;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column()
|
||||
url: string;
|
||||
|
||||
@Column()
|
||||
description: string;
|
||||
|
||||
@OneToMany(type => User, user => user.group)
|
||||
users: User[];
|
||||
|
||||
} */
|
||||
13
backend/src/typeorm/entity/Migration.ts
Normal file
13
backend/src/typeorm/entity/Migration.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
|
||||
@Entity('migrations')
|
||||
export class Migration extends BaseEntity {
|
||||
@PrimaryGeneratedColumn() // This is actually not a primary column
|
||||
version: number
|
||||
|
||||
@Column({ length: 256, nullable: true, default: null })
|
||||
fileName: string
|
||||
|
||||
@Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP' })
|
||||
date: Date
|
||||
}
|
||||
45
backend/src/typeorm/entity/Transaction.ts
Normal file
45
backend/src/typeorm/entity/Transaction.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne } from 'typeorm'
|
||||
import { TransactionCreation } from './TransactionCreation'
|
||||
import { TransactionSendCoin } from './TransactionSendCoin'
|
||||
|
||||
@Entity('transactions')
|
||||
export class Transaction extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column({ name: 'transaction_type_id' })
|
||||
transactionTypeId: number
|
||||
|
||||
@Column({ name: 'tx_hash', type: 'binary', length: 48 })
|
||||
txHash: Buffer
|
||||
|
||||
@Column()
|
||||
memo: string
|
||||
|
||||
@Column({ type: 'timestamp' })
|
||||
received: Date
|
||||
|
||||
@Column({ name: 'blockchain_type_id' })
|
||||
blockchainTypeId: number
|
||||
|
||||
@OneToOne(() => TransactionSendCoin, (transactionSendCoin) => transactionSendCoin.transaction)
|
||||
transactionSendCoin: TransactionSendCoin
|
||||
|
||||
@OneToOne(() => TransactionCreation, (transactionCreation) => transactionCreation.transaction)
|
||||
transactionCreation: TransactionCreation
|
||||
|
||||
static async findByTransactionTypeId(transactionTypeId: number): Promise<Transaction[]> {
|
||||
return this.createQueryBuilder('transaction')
|
||||
.where('transaction.transactionTypeId = :transactionTypeId', {
|
||||
transactionTypeId: transactionTypeId,
|
||||
})
|
||||
.getMany()
|
||||
}
|
||||
|
||||
static async getDecayStartBlock(): Promise<Transaction | undefined> {
|
||||
return this.createQueryBuilder('transaction')
|
||||
.where('transaction.transactionTypeId = :transactionTypeId', { transactionTypeId: 9 })
|
||||
.orderBy('received', 'ASC')
|
||||
.getOne()
|
||||
}
|
||||
}
|
||||
32
backend/src/typeorm/entity/TransactionCreation.ts
Normal file
32
backend/src/typeorm/entity/TransactionCreation.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import {
|
||||
BaseEntity,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
Timestamp,
|
||||
OneToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm'
|
||||
import { Transaction } from './Transaction'
|
||||
|
||||
@Entity('transaction_creations')
|
||||
export class TransactionCreation extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column({ name: 'transaction_id' })
|
||||
transactionId: number
|
||||
|
||||
@Column({ name: 'state_user_id' })
|
||||
userId: number
|
||||
|
||||
@Column()
|
||||
amount: number
|
||||
|
||||
@Column({ name: 'target_date', type: 'timestamp' })
|
||||
targetDate: Timestamp
|
||||
|
||||
@OneToOne(() => Transaction)
|
||||
@JoinColumn({ name: 'transaction_id' })
|
||||
transaction: Transaction
|
||||
}
|
||||
30
backend/src/typeorm/entity/TransactionSendCoin.ts
Normal file
30
backend/src/typeorm/entity/TransactionSendCoin.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToOne, JoinColumn } from 'typeorm'
|
||||
import { Transaction } from './Transaction'
|
||||
|
||||
@Entity('transaction_send_coins')
|
||||
export class TransactionSendCoin extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column({ name: 'transaction_id' })
|
||||
transactionId: number
|
||||
|
||||
@Column({ name: 'sender_public_key', type: 'binary', length: 32 })
|
||||
senderPublic: Buffer
|
||||
|
||||
@Column({ name: 'state_user_id' })
|
||||
userId: number
|
||||
|
||||
@Column({ name: 'receiver_public_key', type: 'binary', length: 32 })
|
||||
recipiantPublic: Buffer
|
||||
|
||||
@Column({ name: 'receiver_user_id' })
|
||||
recipiantUserId: number
|
||||
|
||||
@Column()
|
||||
amount: number
|
||||
|
||||
@OneToOne(() => Transaction)
|
||||
@JoinColumn({ name: 'transaction_id' })
|
||||
transaction: Transaction
|
||||
}
|
||||
47
backend/src/typeorm/entity/User.ts
Normal file
47
backend/src/typeorm/entity/User.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
|
||||
// import { Group } from "./Group"
|
||||
@Entity('state_users')
|
||||
export class User extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
// @ManyToOne(type => Group, group => group.users)
|
||||
// group: Group;
|
||||
|
||||
@Column({ type: 'binary', length: 32, name: 'public_key' })
|
||||
pubkey: Buffer
|
||||
|
||||
@Column()
|
||||
email: string
|
||||
|
||||
@Column({ name: 'first_name' })
|
||||
firstName: string
|
||||
|
||||
@Column({ name: 'last_name' })
|
||||
lastName: string
|
||||
|
||||
@Column()
|
||||
username: string
|
||||
|
||||
@Column()
|
||||
disabled: boolean
|
||||
|
||||
static findByPubkeyHex(pubkeyHex: string): Promise<User> {
|
||||
return this.createQueryBuilder('user')
|
||||
.where('hex(user.pubkey) = :pubkeyHex', { pubkeyHex })
|
||||
.getOneOrFail()
|
||||
}
|
||||
|
||||
static async getUsersIndiced(userIds: number[]): Promise<User[]> {
|
||||
const users = await this.createQueryBuilder('user')
|
||||
.select(['user.id', 'user.firstName', 'user.lastName', 'user.email'])
|
||||
.where('user.id IN (:...users)', { users: userIds })
|
||||
.getMany()
|
||||
const usersIndiced: User[] = []
|
||||
users.forEach((value) => {
|
||||
usersIndiced[value.id] = value
|
||||
})
|
||||
return usersIndiced
|
||||
}
|
||||
}
|
||||
36
backend/src/typeorm/entity/UserTransaction.ts
Normal file
36
backend/src/typeorm/entity/UserTransaction.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm'
|
||||
|
||||
@Entity('state_user_transactions')
|
||||
export class UserTransaction extends BaseEntity {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column({ name: 'state_user_id' })
|
||||
userId: number
|
||||
|
||||
@Column({ name: 'transaction_id' })
|
||||
transactionId: number
|
||||
|
||||
@Column({ name: 'transaction_type_id' })
|
||||
transactionTypeId: number
|
||||
|
||||
@Column({ name: 'balance', type: 'bigint' })
|
||||
balance: number
|
||||
|
||||
@Column({ name: 'balance_date', type: 'timestamp' })
|
||||
balanceDate: Date
|
||||
|
||||
static findByUserPaged(
|
||||
userId: number,
|
||||
limit: number,
|
||||
offset: number,
|
||||
order: 'ASC' | 'DESC',
|
||||
): Promise<[UserTransaction[], number]> {
|
||||
return this.createQueryBuilder('userTransaction')
|
||||
.where('userTransaction.userId = :userId', { userId })
|
||||
.orderBy('userTransaction.balanceDate', order)
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.getManyAndCount()
|
||||
}
|
||||
}
|
||||
15
backend/src/typeorm/getDBVersion.ts
Normal file
15
backend/src/typeorm/getDBVersion.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { getConnection } from 'typeorm'
|
||||
import { Migration } from './entity/Migration'
|
||||
|
||||
const getDBVersion = async (): Promise<string | null> => {
|
||||
const connection = getConnection()
|
||||
const migrations = connection.getRepository(Migration)
|
||||
try {
|
||||
const dbVersion = await migrations.findOne({ order: { version: 'DESC' } })
|
||||
return dbVersion ? dbVersion.fileName : null
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export default getDBVersion
|
||||
55
backend/src/util/decay.ts
Normal file
55
backend/src/util/decay.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { Decay } from '../graphql/models/Decay'
|
||||
|
||||
function decayFormula(amount: number, durationInSeconds: number): number {
|
||||
return amount * Math.pow(0.99999997802044727, durationInSeconds)
|
||||
}
|
||||
|
||||
async function calculateDecay(amount: number, from: Date, to: Date): Promise<number> {
|
||||
// load decay start block
|
||||
const decayStartBlock = await Decay.getDecayStartBlock()
|
||||
|
||||
// if decay hasn't started yet we return input amount
|
||||
if (!decayStartBlock) return amount
|
||||
|
||||
const decayDuration = (to.getTime() - from.getTime()) / 1000
|
||||
return decayFormula(amount, decayDuration)
|
||||
}
|
||||
|
||||
async function calculateDecayWithInterval(
|
||||
amount: number,
|
||||
from: number | Date,
|
||||
to: number | Date,
|
||||
): Promise<Decay> {
|
||||
const decayStartBlock = await Decay.getDecayStartBlock()
|
||||
|
||||
const result = new Decay(undefined)
|
||||
result.balance = amount
|
||||
const fromMillis = typeof from === 'number' ? from : from.getTime()
|
||||
const toMillis = typeof to === 'number' ? to : to.getTime()
|
||||
result.decayStart = (fromMillis / 1000).toString()
|
||||
result.decayEnd = (toMillis / 1000).toString()
|
||||
|
||||
// (amount, from.getTime(), to.getTime())
|
||||
|
||||
// if no decay start block exist or decay startet after end date
|
||||
if (!decayStartBlock || decayStartBlock.received.getTime() > toMillis) {
|
||||
return result
|
||||
}
|
||||
const decayStartBlockMillis = decayStartBlock.received.getTime()
|
||||
|
||||
// if decay start date is before start date we calculate decay for full duration
|
||||
if (decayStartBlockMillis < fromMillis) {
|
||||
result.decayDuration = toMillis - fromMillis
|
||||
}
|
||||
// if decay start in between start date and end date we caculcate decay from decay start time to end date
|
||||
else {
|
||||
result.decayDuration = toMillis - decayStartBlockMillis
|
||||
result.decayStart = (decayStartBlockMillis / 1000).toString()
|
||||
}
|
||||
// js use timestamp in milliseconds but we calculate with seconds
|
||||
result.decayDuration /= 1000
|
||||
result.balance = decayFormula(amount, result.decayDuration)
|
||||
return result
|
||||
}
|
||||
|
||||
export { calculateDecay, calculateDecayWithInterval }
|
||||
17
backend/src/util/round.ts
Normal file
17
backend/src/util/round.ts
Normal file
@ -0,0 +1,17 @@
|
||||
function roundCeilFrom4(decimal: number): number {
|
||||
return Math.ceil(decimal / 100) / 100
|
||||
}
|
||||
|
||||
function roundFloorFrom4(decimal: number): number {
|
||||
return Math.floor(decimal / 100) / 100
|
||||
}
|
||||
|
||||
function roundCeilFrom2(decimal: number): number {
|
||||
return Math.ceil(decimal / 100)
|
||||
}
|
||||
|
||||
function roundFloorFrom2(decimal: number): number {
|
||||
return Math.floor(decimal / 100)
|
||||
}
|
||||
|
||||
export { roundCeilFrom4, roundFloorFrom4, roundCeilFrom2, roundFloorFrom2 }
|
||||
@ -166,6 +166,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
|
||||
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
|
||||
|
||||
"@sqltools/formatter@^1.2.2":
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@sqltools/formatter/-/formatter-1.2.3.tgz#1185726610acc37317ddab11c3c7f9066966bd20"
|
||||
integrity sha512-O3uyB/JbkAEMZaP3YqyHH7TMnex7tWyCbCI4EfJdOCoN6HIhqdJBWTM6aCCiWQ/5f5wxjgU735QAIpJbjDvmzg==
|
||||
|
||||
"@szmarczak/http-timer@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
|
||||
@ -396,6 +401,11 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/zen-observable@0.8.3":
|
||||
version "0.8.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3"
|
||||
integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^4.28.0":
|
||||
version "4.28.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.0.tgz#1a66f03b264844387beb7dc85e1f1d403bd1803f"
|
||||
@ -527,6 +537,11 @@ ansi-colors@^4.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
|
||||
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
|
||||
|
||||
ansi-regex@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
|
||||
integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
|
||||
|
||||
ansi-regex@^4.1.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
||||
@ -537,6 +552,11 @@ ansi-regex@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
|
||||
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
|
||||
|
||||
ansi-styles@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
|
||||
integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
|
||||
|
||||
ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
@ -551,6 +571,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
dependencies:
|
||||
color-convert "^2.0.1"
|
||||
|
||||
any-promise@^1.0.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
|
||||
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
||||
@ -709,6 +734,11 @@ apollo-utilities@^1.0.1, apollo-utilities@^1.3.0:
|
||||
ts-invariant "^0.4.0"
|
||||
tslib "^1.10.0"
|
||||
|
||||
app-root-path@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad"
|
||||
integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==
|
||||
|
||||
arg@^4.1.0:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
|
||||
@ -721,6 +751,11 @@ argparse@^1.0.7:
|
||||
dependencies:
|
||||
sprintf-js "~1.0.2"
|
||||
|
||||
argparse@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
|
||||
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
|
||||
|
||||
array-flatten@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
|
||||
@ -780,6 +815,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
@ -840,6 +880,14 @@ buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
buffer@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
|
||||
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
busboy@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b"
|
||||
@ -883,6 +931,17 @@ camelcase@^5.3.1:
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
|
||||
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
|
||||
|
||||
chalk@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
|
||||
integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
|
||||
dependencies:
|
||||
ansi-styles "^2.2.1"
|
||||
escape-string-regexp "^1.0.2"
|
||||
has-ansi "^2.0.0"
|
||||
strip-ansi "^3.0.0"
|
||||
supports-color "^2.0.0"
|
||||
|
||||
chalk@^2.0.0:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
@ -908,6 +967,14 @@ chalk@^4.0.0:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@^4.1.0:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chokidar@^3.2.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
|
||||
@ -942,6 +1009,27 @@ cli-boxes@^2.2.0:
|
||||
resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
|
||||
integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
|
||||
|
||||
cli-highlight@^2.1.11:
|
||||
version "2.1.11"
|
||||
resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf"
|
||||
integrity sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
highlight.js "^10.7.1"
|
||||
mz "^2.4.0"
|
||||
parse5 "^5.1.1"
|
||||
parse5-htmlparser2-tree-adapter "^6.0.0"
|
||||
yargs "^16.0.0"
|
||||
|
||||
cliui@^7.0.2:
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
|
||||
integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
|
||||
dependencies:
|
||||
string-width "^4.2.0"
|
||||
strip-ansi "^6.0.0"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
clone-response@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
|
||||
@ -1169,6 +1257,11 @@ dotenv@^10.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
|
||||
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
|
||||
|
||||
dotenv@^8.2.0:
|
||||
version "8.6.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.6.0.tgz#061af664d19f7f4d8fc6e4ff9b584ce237adcb8b"
|
||||
integrity sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==
|
||||
|
||||
duplexer3@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||
@ -1253,6 +1346,11 @@ es-to-primitive@^1.2.1:
|
||||
is-date-object "^1.0.1"
|
||||
is-symbol "^1.0.2"
|
||||
|
||||
escalade@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
|
||||
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
|
||||
|
||||
escape-goat@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
|
||||
@ -1263,7 +1361,7 @@ escape-html@~1.0.3:
|
||||
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
|
||||
integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
|
||||
|
||||
escape-string-regexp@^1.0.5:
|
||||
escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
|
||||
@ -1557,6 +1655,11 @@ fastq@^1.6.0:
|
||||
dependencies:
|
||||
reusify "^1.0.4"
|
||||
|
||||
figlet@^1.1.1:
|
||||
version "1.5.2"
|
||||
resolved "https://registry.yarnpkg.com/figlet/-/figlet-1.5.2.tgz#dda34ff233c9a48e36fcff6741aeb5bafe49b634"
|
||||
integrity sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==
|
||||
|
||||
file-entry-cache@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
||||
@ -1658,6 +1761,11 @@ generate-function@^2.3.1:
|
||||
dependencies:
|
||||
is-property "^1.0.2"
|
||||
|
||||
get-caller-file@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
|
||||
@ -1794,6 +1902,13 @@ graphql@^15.5.1:
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad"
|
||||
integrity sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw==
|
||||
|
||||
has-ansi@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
|
||||
integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
|
||||
dependencies:
|
||||
ansi-regex "^2.0.0"
|
||||
|
||||
has-bigints@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
|
||||
@ -1826,6 +1941,11 @@ has@^1.0.3:
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
|
||||
highlight.js@^10.7.1:
|
||||
version "10.7.3"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
|
||||
integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
|
||||
|
||||
hosted-git-info@^2.1.4:
|
||||
version "2.8.9"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
|
||||
@ -1883,6 +2003,11 @@ iconv-lite@^0.6.2:
|
||||
dependencies:
|
||||
safer-buffer ">= 2.1.2 < 3.0.0"
|
||||
|
||||
ieee754@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
ignore-by-default@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
|
||||
@ -2115,6 +2240,13 @@ js-yaml@^3.13.1:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
||||
js-yaml@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
|
||||
integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
json-buffer@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
|
||||
@ -2394,6 +2526,11 @@ minimist@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||
|
||||
mkdirp@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
|
||||
@ -2428,6 +2565,15 @@ mysql2@^2.3.0:
|
||||
seq-queue "^0.0.5"
|
||||
sqlstring "^2.3.2"
|
||||
|
||||
mz@^2.4.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
|
||||
integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
object-assign "^4.0.1"
|
||||
thenify-all "^1.0.0"
|
||||
|
||||
named-placeholders@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.2.tgz#ceb1fbff50b6b33492b5cf214ccf5e39cef3d0e8"
|
||||
@ -2493,7 +2639,7 @@ normalize-url@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
|
||||
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
|
||||
|
||||
object-assign@^4:
|
||||
object-assign@^4, object-assign@^4.0.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||
@ -2608,6 +2754,11 @@ parent-module@^1.0.0:
|
||||
dependencies:
|
||||
callsites "^3.0.0"
|
||||
|
||||
parent-require@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parent-require/-/parent-require-1.0.0.tgz#746a167638083a860b0eef6732cb27ed46c32977"
|
||||
integrity sha1-dGoWdjgIOoYLDu9nMssn7UbDKXc=
|
||||
|
||||
parse-json@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
|
||||
@ -2616,6 +2767,23 @@ parse-json@^4.0.0:
|
||||
error-ex "^1.3.1"
|
||||
json-parse-better-errors "^1.0.1"
|
||||
|
||||
parse5-htmlparser2-tree-adapter@^6.0.0:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
|
||||
integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
|
||||
dependencies:
|
||||
parse5 "^6.0.1"
|
||||
|
||||
parse5@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
|
||||
integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
|
||||
|
||||
parse5@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
|
||||
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
|
||||
|
||||
parseurl@^1.3.2, parseurl@~1.3.3:
|
||||
version "1.3.3"
|
||||
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
|
||||
@ -2830,6 +2998,11 @@ registry-url@^5.0.0:
|
||||
dependencies:
|
||||
rc "^1.2.8"
|
||||
|
||||
require-directory@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
|
||||
|
||||
require-from-string@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
|
||||
@ -2894,6 +3067,11 @@ safe-buffer@^5.0.1:
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
sax@>=0.6.0:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
|
||||
|
||||
semver-diff@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b"
|
||||
@ -3094,6 +3272,13 @@ string.prototype.trimstart@^1.0.4:
|
||||
call-bind "^1.0.2"
|
||||
define-properties "^1.1.3"
|
||||
|
||||
strip-ansi@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
|
||||
integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
|
||||
dependencies:
|
||||
ansi-regex "^2.0.0"
|
||||
|
||||
strip-ansi@^5.1.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
|
||||
@ -3134,6 +3319,11 @@ subscriptions-transport-ws@^0.9.19:
|
||||
symbol-observable "^1.0.4"
|
||||
ws "^5.2.0 || ^6.0.0 || ^7.0.0"
|
||||
|
||||
supports-color@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
|
||||
|
||||
supports-color@^5.3.0, supports-color@^5.5.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
@ -3175,6 +3365,20 @@ text-table@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
|
||||
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
|
||||
|
||||
thenify-all@^1.0.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
|
||||
integrity sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=
|
||||
dependencies:
|
||||
thenify ">= 3.1.0 < 4"
|
||||
|
||||
"thenify@>= 3.1.0 < 4":
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
|
||||
integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
|
||||
dependencies:
|
||||
any-promise "^1.0.0"
|
||||
|
||||
to-readable-stream@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
|
||||
@ -3295,6 +3499,29 @@ typedarray-to-buffer@^3.1.5:
|
||||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typeorm@^0.2.37:
|
||||
version "0.2.37"
|
||||
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.37.tgz#1a5e59216077640694d27c04c99ed3f968d15dc8"
|
||||
integrity sha512-7rkW0yCgFC24I5T0f3S/twmLSuccPh1SQmxET/oDWn2sSDVzbyWdnItSdKy27CdJGTlKHYtUVeOcMYw5LRsXVw==
|
||||
dependencies:
|
||||
"@sqltools/formatter" "^1.2.2"
|
||||
app-root-path "^3.0.0"
|
||||
buffer "^6.0.3"
|
||||
chalk "^4.1.0"
|
||||
cli-highlight "^2.1.11"
|
||||
debug "^4.3.1"
|
||||
dotenv "^8.2.0"
|
||||
glob "^7.1.6"
|
||||
js-yaml "^4.0.0"
|
||||
mkdirp "^1.0.4"
|
||||
reflect-metadata "^0.1.13"
|
||||
sha.js "^2.4.11"
|
||||
tslib "^2.1.0"
|
||||
xml2js "^0.4.23"
|
||||
yargonaut "^1.1.4"
|
||||
yargs "^17.0.1"
|
||||
zen-observable-ts "^1.0.0"
|
||||
|
||||
typescript@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.4.tgz#3f85b986945bcf31071decdd96cf8bfa65f9dcbc"
|
||||
@ -3441,6 +3668,15 @@ word-wrap@^1.2.3:
|
||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
@ -3466,6 +3702,19 @@ xdg-basedir@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
|
||||
integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
|
||||
|
||||
xml2js@^0.4.23:
|
||||
version "0.4.23"
|
||||
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
|
||||
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
|
||||
dependencies:
|
||||
sax ">=0.6.0"
|
||||
xmlbuilder "~11.0.0"
|
||||
|
||||
xmlbuilder@~11.0.0:
|
||||
version "11.0.1"
|
||||
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
|
||||
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
|
||||
|
||||
xss@^1.0.8:
|
||||
version "1.0.9"
|
||||
resolved "https://registry.yarnpkg.com/xss/-/xss-1.0.9.tgz#3ffd565571ff60d2e40db7f3b80b4677bec770d2"
|
||||
@ -3474,6 +3723,11 @@ xss@^1.0.8:
|
||||
commander "^2.20.3"
|
||||
cssfilter "0.0.10"
|
||||
|
||||
y18n@^5.0.5:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||
|
||||
yallist@^2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
|
||||
@ -3484,6 +3738,46 @@ yallist@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
|
||||
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
|
||||
|
||||
yargonaut@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/yargonaut/-/yargonaut-1.1.4.tgz#c64f56432c7465271221f53f5cc517890c3d6e0c"
|
||||
integrity sha512-rHgFmbgXAAzl+1nngqOcwEljqHGG9uUZoPjsdZEs1w5JW9RXYzrSvH/u70C1JE5qFi0qjsdhnUX/dJRpWqitSA==
|
||||
dependencies:
|
||||
chalk "^1.1.1"
|
||||
figlet "^1.1.1"
|
||||
parent-require "^1.0.0"
|
||||
|
||||
yargs-parser@^20.2.2:
|
||||
version "20.2.9"
|
||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||
|
||||
yargs@^16.0.0:
|
||||
version "16.2.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
|
||||
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
||||
dependencies:
|
||||
cliui "^7.0.2"
|
||||
escalade "^3.1.1"
|
||||
get-caller-file "^2.0.5"
|
||||
require-directory "^2.1.1"
|
||||
string-width "^4.2.0"
|
||||
y18n "^5.0.5"
|
||||
yargs-parser "^20.2.2"
|
||||
|
||||
yargs@^17.0.1:
|
||||
version "17.2.0"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.2.0.tgz#ec529632b2cb9044f3927f4b45f9cc4ae2535653"
|
||||
integrity sha512-UPeZv4h9Xv510ibpt5rdsUNzgD78nMa1rhxxCgvkKiq06hlKCEHJLiJ6Ub8zDg/wR6hedEI6ovnd2vCvJ4nusA==
|
||||
dependencies:
|
||||
cliui "^7.0.2"
|
||||
escalade "^3.1.1"
|
||||
get-caller-file "^2.0.5"
|
||||
require-directory "^2.1.1"
|
||||
string-width "^4.2.0"
|
||||
y18n "^5.0.5"
|
||||
yargs-parser "^20.2.2"
|
||||
|
||||
yn@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||
@ -3497,7 +3791,15 @@ zen-observable-ts@^0.8.21:
|
||||
tslib "^1.9.3"
|
||||
zen-observable "^0.8.0"
|
||||
|
||||
zen-observable@^0.8.0:
|
||||
zen-observable-ts@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83"
|
||||
integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==
|
||||
dependencies:
|
||||
"@types/zen-observable" "0.8.3"
|
||||
zen-observable "0.8.15"
|
||||
|
||||
zen-observable@0.8.15, zen-observable@^0.8.0:
|
||||
version "0.8.15"
|
||||
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
|
||||
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
|
||||
|
||||
2
database/build/.env.dist
Normal file
2
database/build/.env.dist
Normal file
@ -0,0 +1,2 @@
|
||||
// For production you need to put your env file in here.
|
||||
// Please copy the dist file from the root folder in here and rename it to .env
|
||||
@ -25,6 +25,12 @@ export default async (): Promise<void> => {
|
||||
DEFAULT CHARACTER SET utf8mb4
|
||||
DEFAULT COLLATE utf8mb4_unicode_ci;`)
|
||||
|
||||
// Create Database `gradido_community_test` for tests
|
||||
await con.query(`
|
||||
CREATE DATABASE IF NOT EXISTS ${CONFIG.DB_DATABASE}_test
|
||||
DEFAULT CHARACTER SET utf8mb4
|
||||
DEFAULT COLLATE utf8mb4_unicode_ci;`)
|
||||
|
||||
// Check if old migration table is present, delete if needed
|
||||
const [rows] = await con.query(`SHOW TABLES FROM \`${CONFIG.DB_DATABASE}\` LIKE 'migrations';`)
|
||||
if ((<RowDataPacket>rows).length > 0) {
|
||||
|
||||
@ -2,7 +2,10 @@
|
||||
# For that to work, node v12.19.0 needs to be installed with nvm for root
|
||||
# or NPM_BIN Path and NVM_DIR must be adjusted
|
||||
|
||||
cd /var/www/html/gradido/frontend
|
||||
cd /var/www/html/gradido
|
||||
eval "echo \"$(cat .env.shell)\"" > .env
|
||||
export BUILD_COMMIT="$(git rev-parse HEAD)"
|
||||
cd frontend
|
||||
|
||||
NPM_BIN=/root/.nvm/versions/node/v12.19.0/bin/npm
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@ services:
|
||||
environment:
|
||||
- NODE_ENV="development"
|
||||
# - DEBUG=true
|
||||
- NUXT_BUILD=/tmp/nuxt # avoid file permission issues when `rm -rf .nuxt/`
|
||||
volumes:
|
||||
# This makes sure the docker container has its own node modules.
|
||||
# Therefore it is possible to have a different node version on the host machine
|
||||
@ -140,11 +139,13 @@ services:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./skeema/Dockerfile
|
||||
target: skeema_run
|
||||
target: skeema_dev_run
|
||||
depends_on:
|
||||
- mariadb
|
||||
networks:
|
||||
- internal-net
|
||||
volumes:
|
||||
- ./login_server/skeema/gradido_login:/skeema/gradido_login
|
||||
|
||||
volumes:
|
||||
frontend_node_modules:
|
||||
|
||||
@ -22,14 +22,13 @@ services:
|
||||
# Envs used in Dockerfile
|
||||
# - DOCKER_WORKDIR="/app"
|
||||
# - PORT=3000
|
||||
- BUILD_DATE
|
||||
- BUILD_VERSION
|
||||
- BUILD_COMMIT
|
||||
# - BUILD_DATE="1970-01-01T00:00:00.00Z"
|
||||
# - BUILD_VERSION="0.0.0.0"
|
||||
# - BUILD_COMMIT="0000000"
|
||||
- NODE_ENV="production"
|
||||
# Application only envs
|
||||
#- HOST=0.0.0.0 # This is nuxt specific, alternative value is HOST=webapp
|
||||
#env_file:
|
||||
# - ./frontend/.env
|
||||
# env_file:
|
||||
# - ./.env
|
||||
# - ./frontend/.env
|
||||
|
||||
#########################################################
|
||||
## MARIADB ##############################################
|
||||
@ -157,6 +156,19 @@ services:
|
||||
volumes:
|
||||
- ./community_server/config/php-fpm/php-ini-overrides.ini:/etc/php/7.4/fpm/conf.d/99-overrides.ini
|
||||
|
||||
#########################################################
|
||||
## skeema for updating dbs if changes happend ###########
|
||||
#########################################################
|
||||
skeema:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./skeema/Dockerfile
|
||||
target: skeema_run
|
||||
depends_on:
|
||||
- mariadb
|
||||
networks:
|
||||
- internal-net
|
||||
|
||||
#########################################################
|
||||
## GRADIDO NODE v1 ######################################
|
||||
#########################################################
|
||||
|
||||
103
docu/graphics/AnachBüberweisen.drawio
Normal file
103
docu/graphics/AnachBüberweisen.drawio
Normal file
@ -0,0 +1,103 @@
|
||||
<mxfile host="65bd71144e">
|
||||
<diagram id="ZvjvITeOyjQP4YKfGS0G" name="Page-1">
|
||||
<mxGraphModel dx="923" dy="562" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="29" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#008a00;strokeColor=#005700;fontColor=#ffffff;" vertex="1" parent="1">
|
||||
<mxGeometry x="660" y="135" width="120" height="215" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="28" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#60a917;strokeColor=#2D7600;fontColor=#ffffff;" vertex="1" parent="1">
|
||||
<mxGeometry x="540" y="135" width="120" height="215" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="27" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e3c800;strokeColor=#B09500;fontColor=#000000;" vertex="1" parent="1">
|
||||
<mxGeometry x="420" y="135" width="120" height="215" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="26" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f0a30a;strokeColor=#BD7000;fontColor=#000000;" vertex="1" parent="1">
|
||||
<mxGeometry x="300" y="135" width="120" height="215" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="25" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fa6800;strokeColor=#C73500;fontColor=#000000;" vertex="1" parent="1">
|
||||
<mxGeometry x="180" y="135" width="120" height="215" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="24" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e51400;strokeColor=#B20000;fontColor=#ffffff;" vertex="1" parent="1">
|
||||
<mxGeometry x="70" y="135" width="110" height="215" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;fillColor=#008a00;strokeColor=#005700;jumpSize=6;endSize=6;startSize=6;strokeWidth=3;" edge="1" parent="1" source="2" target="3">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fillColor=#008a00;strokeColor=#005700;strokeWidth=3;" edge="1" parent="1" source="2" target="4">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="2" value="1.5.0<br>Register" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#2A2A2A;strokeColor=#F0F0F0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="80" y="200" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="3" target="7">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;fillColor=#60a917;strokeColor=#2D7600;" edge="1" parent="1" source="3" target="10">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="3" value="1.6.0<br>Kubernetes" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#2A2A2A;strokeColor=#F0F0F0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="200" y="150" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;fillColor=#60a917;strokeColor=#2D7600;" edge="1" parent="1" source="4" target="7">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;fillColor=#60a917;strokeColor=#2D7600;" edge="1" parent="1" source="4" target="10">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="4" value="1.6.0<br>Overview<br>Page" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#2A2A2A;strokeColor=#F0F0F0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="200" y="250" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;fillColor=#e3c800;strokeColor=#B09500;" edge="1" parent="1" source="7" target="15">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="7" value="1.7.0<br>Federation" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#2A2A2A;strokeColor=#F0F0F0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="150" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;fillColor=#e3c800;strokeColor=#B09500;" edge="1" parent="1" source="10" target="15">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="10" value="1.7.0<br>Statistics(?)" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#2A2A2A;strokeColor=#F0F0F0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="250" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;fillColor=#f0a30a;strokeColor=#BD7000;strokeWidth=3;" edge="1" parent="1" source="15" target="18">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="15" value="1.8.0<br>Activities" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#2A2A2A;strokeColor=#F0F0F0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="440" y="200" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;fillColor=#fa6800;strokeColor=#C73500;strokeWidth=3;" edge="1" parent="1" source="18" target="19">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="18" value="1.X.0<br>???" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#2A2A2A;strokeColor=#F0F0F0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="560" y="200" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="19" value="Von A nach B überweisen&nbsp;" style="whiteSpace=wrap;html=1;aspect=fixed;fillColor=#2A2A2A;strokeColor=#F0F0F0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="680" y="200" width="80" height="80" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="30" value="The background color symbolizes how far away we are from the goal. The arrow color symbolizes how sure we can be that this will be the course of action" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="70" y="640" width="330" height="50" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="31" value="Our current release" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=top;whiteSpace=wrap;rounded=0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="70" y="360" width="110" height="240" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="32" value="The Release 1.6.0 will make sure we can actually deploy multiple instances of our Software.<br>Additionally we will create the Overview Page in the Frontend" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=top;whiteSpace=wrap;rounded=0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="185" y="360" width="110" height="240" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="33" value="The Release 1.7.0 will focus on the Federation. Maybe we will be able to send Coins at this stage, but its likely this will be done in the next steps. Here we most likely get to know the other communties only.<br>In the Frontend we consider to show some statisticts with the amount of known Communities" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=top;whiteSpace=wrap;rounded=0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="305" y="361" width="110" height="239" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="35" value="The Release 1.8.0 will implement Activities in order to allow other communities to actually create GDD." style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=top;whiteSpace=wrap;rounded=0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="425" y="360" width="110" height="240" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="36" value="More Steps might be needed to have the usable full functionallity of sending from A to B." style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=top;whiteSpace=wrap;rounded=0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="545" y="360" width="110" height="240" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="37" value="Be aware that we might decide to split some Releases into two. Especially the Federation Release might need a followup Release in oder to actually send coins, since this will not be part of the Federation itself." style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#F0F0F0;" vertex="1" parent="1">
|
||||
<mxGeometry x="70" y="710" width="330" height="50" as="geometry"/>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
||||
BIN
docu/graphics/AnachBüberweisen.png
Normal file
BIN
docu/graphics/AnachBüberweisen.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
@ -1,4 +1,3 @@
|
||||
LOGIN_API_URL=http://localhost/login_api/
|
||||
COMMUNITY_API_URL=http://localhost/api/
|
||||
ALLOW_REGISTER=true
|
||||
GRAPHQL_URI=http://localhost:4000/graphql
|
||||
//BUILD_COMMIT=0000000
|
||||
@ -1,69 +1,114 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="decayinformation">
|
||||
<span v-if="decaytyp === 'short'">
|
||||
<div v-if="decay.balance > 0">
|
||||
<span v-if="decay.balance > 0">
|
||||
{{ decay ? ' -' + decay.balance + ' ' + decayStartBlockTextShort : '' }}
|
||||
</div>
|
||||
<div v-else>
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ $t('decay.noDecay') }}
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div v-if="decaytyp === 'new'">
|
||||
<b-list-group style="border: 0px">
|
||||
<b-list-group-item style="border: 0px; background-color: #f1f1f1">
|
||||
<div class="d-flex" v-if="!decay.decayStartBlock">
|
||||
<div style="width: 100%" class="text-center pb-3">
|
||||
<b-icon icon="droplet-half" height="12" class="mb-2" />
|
||||
<b>{{ $t('decay.calculation_decay') }}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex" v-if="!decay.decayStartBlock">
|
||||
<div style="width: 100%" class="text-center pb-3">
|
||||
<b-icon icon="droplet-half" height="12" class="mb-2" />
|
||||
<b>{{ $t('decay.calculation_decay') }}</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex">
|
||||
<div style="width: 40%" class="text-right pr-3 mr-2">
|
||||
<div v-if="!decay.decayStartBlock">{{ $t('decay.last_transaction') }}</div>
|
||||
</div>
|
||||
<div style="width: 60%">
|
||||
<div v-if="decay.decayStartBlock > 0">
|
||||
<div class="display-4">{{ $t('decay.Starting_block_decay') }}</div>
|
||||
<div>
|
||||
{{ $t('decay.decay_introduced') }} :
|
||||
{{ $d($moment.unix(decay.decayStart), 'long') }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span v-if="!decay.decayStart">
|
||||
{{ $d($moment.unix(decay.decayStart), 'long') }}
|
||||
{{ $i18n.locale === 'de' ? 'Uhr' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div v-if="!decay.decayStartBlock">{{ $t('decay.last_transaction') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<div v-if="decay.decayStartBlock > 0">
|
||||
<div class="display-4">{{ $t('decay.Starting_block_decay') }}</div>
|
||||
<div>
|
||||
{{ $t('decay.decay_introduced') }} :
|
||||
{{ $d($moment.unix(decay.decayStart), 'long') }}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span v-if="decay.decayStart">
|
||||
{{ $d($moment.unix(decay.decayStart), 'long') }}
|
||||
{{ $i18n.locale === 'de' ? 'Uhr' : '' }}
|
||||
</span>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div v-if="!decay.decayStartBlock">{{ $t('decay.past_time') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<div v-if="decay.decayStartBlock > 0">{{ $t('decay.since_introduction') }}</div>
|
||||
<span v-if="duration">
|
||||
<span v-if="duration.years > 0">{{ duration.years }} {{ $t('decay.year') }},</span>
|
||||
<span v-if="duration.months > 0">{{ duration.months }} {{ $t('decay.months') }},</span>
|
||||
<span v-if="duration.days > 0">{{ duration.days }} {{ $t('decay.days') }},</span>
|
||||
<span v-if="duration.hours > 0">{{ duration.hours }} {{ $t('decay.hours') }},</span>
|
||||
<span v-if="duration.minutes > 0">
|
||||
{{ duration.minutes }} {{ $t('decay.minutes') }},
|
||||
</span>
|
||||
<span v-if="duration.seconds > 0">
|
||||
{{ duration.seconds }} {{ $t('decay.seconds') }}
|
||||
</span>
|
||||
</span>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<div class="d-flex">
|
||||
<div style="width: 40%" class="text-right pr-3 mr-2">
|
||||
<div v-if="!decay.decayStartBlock">{{ $t('decay.past_time') }}</div>
|
||||
<div v-if="decay.balance > 0">
|
||||
<!-- Decay-->
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div>{{ $t('decay.decay') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<div>- {{ decay.balance }}</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<hr class="mt-2 mb-2" />
|
||||
<b-row>
|
||||
<b-col class="text-center pt-3 pb-2">
|
||||
<b>{{ $t('decay.calculation_total') }}</b>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<!-- Type-->
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div v-if="type === 'send'">{{ $t('decay.sent') }}</div>
|
||||
<div v-if="type === 'receive'">{{ $t('decay.received') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<div v-if="type === 'send'">- {{ balance }}</div>
|
||||
<div v-if="type === 'receive'">+ {{ balance }}</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<!-- Decay-->
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div>{{ $t('decay.decay') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<div>- {{ decay.balance }}</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<!-- Total-->
|
||||
<b-row>
|
||||
<b-col cols="6" class="text-right">
|
||||
<div>{{ $t('decay.total') }}</div>
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
<div v-if="type === 'send'">
|
||||
<b>- {{ parseInt(balance) + decay.balance }}</b>
|
||||
</div>
|
||||
<div style="width: 60%">
|
||||
<div v-if="decay.decayStartBlock > 0">{{ $t('decay.since_introduction') }}</div>
|
||||
<span v-if="duration">
|
||||
<span v-if="duration.years > 0">{{ duration.years }} {{ $t('decay.year') }},</span>
|
||||
<span v-if="duration.months > 0">
|
||||
{{ duration.months }} {{ $t('decay.months') }},
|
||||
</span>
|
||||
<span v-if="duration.days > 0">{{ duration.days }} {{ $t('decay.days') }},</span>
|
||||
<span v-if="duration.hours > 0">{{ duration.hours }} {{ $t('decay.hours') }},</span>
|
||||
<span v-if="duration.minutes > 0">
|
||||
{{ duration.minutes }} {{ $t('decay.minutes') }},
|
||||
</span>
|
||||
<span v-if="duration.seconds > 0">
|
||||
{{ duration.seconds }} {{ $t('decay.seconds') }}
|
||||
</span>
|
||||
</span>
|
||||
<div v-if="type === 'receive'">
|
||||
<b>{{ parseInt(balance) - decay.balance }}</b>
|
||||
</div>
|
||||
</div>
|
||||
</b-list-group-item>
|
||||
</b-list-group>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -71,6 +116,8 @@
|
||||
export default {
|
||||
name: 'DecayInformation',
|
||||
props: {
|
||||
balance: { type: Number },
|
||||
type: { type: String, default: '' },
|
||||
decay: {
|
||||
balance: '',
|
||||
decayDuration: '',
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
containsUppercaseCharacter: true,
|
||||
containsNumericCharacter: true,
|
||||
atLeastEightCharactera: true,
|
||||
atLeastOneSpecialCharater: true,
|
||||
noWhitespaceCharacters: true,
|
||||
}"
|
||||
:label="register ? $t('form.password') : $t('form.password_new')"
|
||||
:showAllErrors="true"
|
||||
|
||||
@ -3,7 +3,7 @@ import LanguageSwitch from './LanguageSwitch'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const updateUserInfosQueryMock = jest.fn().mockResolvedValue({
|
||||
const updateUserInfosMutationMock = jest.fn().mockResolvedValue({
|
||||
data: {
|
||||
updateUserInfos: {
|
||||
validValues: 1,
|
||||
@ -28,7 +28,7 @@ describe('LanguageSwitch', () => {
|
||||
locale: 'en',
|
||||
},
|
||||
$apollo: {
|
||||
query: updateUserInfosQueryMock,
|
||||
mutate: updateUserInfosMutationMock,
|
||||
},
|
||||
}
|
||||
|
||||
@ -119,7 +119,7 @@ describe('LanguageSwitch', () => {
|
||||
describe('calls the API', () => {
|
||||
it("with locale 'en'", () => {
|
||||
wrapper.findAll('li').at(0).find('a').trigger('click')
|
||||
expect(updateUserInfosQueryMock).toBeCalledWith(
|
||||
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
email: 'he@ho.he',
|
||||
@ -131,7 +131,7 @@ describe('LanguageSwitch', () => {
|
||||
|
||||
it("with locale 'de'", () => {
|
||||
wrapper.findAll('li').at(1).find('a').trigger('click')
|
||||
expect(updateUserInfosQueryMock).toBeCalledWith(
|
||||
expect(updateUserInfosMutationMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
email: 'he@ho.he',
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<script>
|
||||
import { localeChanged } from 'vee-validate'
|
||||
import locales from '../locales/'
|
||||
import { updateUserInfos } from '../graphql/queries'
|
||||
import { updateUserInfos } from '../graphql/mutations'
|
||||
|
||||
export default {
|
||||
name: 'LanguageSwitch',
|
||||
@ -36,8 +36,8 @@ export default {
|
||||
this.setLocale(locale)
|
||||
if (this.$store.state.email) {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: updateUserInfos,
|
||||
.mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
email: this.$store.state.email,
|
||||
locale: locale,
|
||||
|
||||
@ -14,7 +14,6 @@ export default {
|
||||
return {
|
||||
selected: null,
|
||||
options: [
|
||||
{ value: null, text: this.$t('select_language') },
|
||||
{ value: 'de', text: this.$t('languages.de') },
|
||||
{ value: 'en', text: this.$t('languages.en') },
|
||||
],
|
||||
|
||||
@ -19,54 +19,54 @@
|
||||
|
||||
<!-- type -->
|
||||
<b-row>
|
||||
<div class="col-6 text-right">
|
||||
<b-col cols="6" class="text-right">
|
||||
{{ getLinesByType(gdtEntryType).description }}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
{{ getLinesByType(gdtEntryType).descriptiontext }}
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<!-- credit -->
|
||||
<b-row>
|
||||
<div class="col-6 text-right">
|
||||
<b-col cols="6" class="text-right">
|
||||
{{ $t('gdt.credit') }}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
{{ getLinesByType(gdtEntryType).credittext }}
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<!-- Message-->
|
||||
<b-row v-if="comment && gdtEntryType !== 7">
|
||||
<div class="col-6 text-right">
|
||||
<b-col cols="6" class="text-right">
|
||||
{{ $t('form.memo') }}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
{{ comment }}
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
|
||||
<!-- date-->
|
||||
<b-row class="gdt-list-row text-header">
|
||||
<div class="col-6 text-right">
|
||||
<b-col cols="6" class="text-right">
|
||||
{{ $t('form.date') }}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
</b-col>
|
||||
<b-col cols="6">
|
||||
{{ $d($moment(date), 'long') }} {{ $i18n.locale === 'de' ? 'Uhr' : '' }}
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<!-- collaps trancaction info-->
|
||||
<b-collapse :id="'a' + date + ''" class="pb-4">
|
||||
<transaction-collapse
|
||||
:amount="amount"
|
||||
:gdtEntryType="gdtEntryType"
|
||||
:factor="factor"
|
||||
:gdt="gdt"
|
||||
></transaction-collapse>
|
||||
</b-collapse>
|
||||
<!-- collaps trancaction info-->
|
||||
<b-collapse :id="'a' + date + ''" class="mt-2 pb-4">
|
||||
<transaction-collapse
|
||||
:amount="amount"
|
||||
:gdtEntryType="gdtEntryType"
|
||||
:factor="factor"
|
||||
:gdt="gdt"
|
||||
></transaction-collapse>
|
||||
</b-collapse>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -1,21 +1,24 @@
|
||||
<template>
|
||||
<div class="gdt-transaction-collapse">
|
||||
<div
|
||||
class="gdt-transaction-collapse p-2 pt-4 pb-4 mb-4"
|
||||
style="border: 0px; background-color: #f1f1f1"
|
||||
>
|
||||
<b-row class="gdt-list-collapse-header-text text-center pb-3">
|
||||
<div id="collapse-headline" class="col h4">
|
||||
{{ getLinesByType(gdtEntryType).headline }}
|
||||
</div>
|
||||
<b-col id="collapse-headline">
|
||||
<b>{{ getLinesByType(gdtEntryType).headline }}</b>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<b-row class="gdt-list-collapse-box--all">
|
||||
<div class="col-6 text-right collapse-col-left">
|
||||
<b-col cols="6" class="text-right collapse-col-left">
|
||||
<div id="collapse-first">{{ getLinesByType(gdtEntryType).first }}</div>
|
||||
<div id="collapse-second">{{ getLinesByType(gdtEntryType).second }}</div>
|
||||
</div>
|
||||
<div class="col-6 collapse-col-right">
|
||||
</b-col>
|
||||
<b-col cols="6" class="collapse-col-right">
|
||||
<div id="collapse-firstMath">{{ getLinesByType(gdtEntryType).firstMath }}</div>
|
||||
<div id="collapse-secondMath">
|
||||
{{ getLinesByType(gdtEntryType).secondMath }}
|
||||
</div>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
</template>
|
||||
@ -30,6 +33,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
getLinesByType(givenType) {
|
||||
if (givenType === 2 || givenType === 3 || givenType === 5 || givenType === 6) givenType = 1
|
||||
|
||||
const linesByType = {
|
||||
1: {
|
||||
headline: this.$t('gdt.calculation'),
|
||||
|
||||
@ -1,25 +1,35 @@
|
||||
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env)
|
||||
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env).
|
||||
// The whole contents is exposed to the client
|
||||
|
||||
// Load Package Details for some default values
|
||||
const pkg = require('../../package')
|
||||
|
||||
const version = {
|
||||
APP_VERSION: pkg.version,
|
||||
BUILD_COMMIT: process.env.BUILD_COMMIT || null,
|
||||
// self reference of `version.BUILD_COMMIT` is not possible at this point, hence the duplicate code
|
||||
BUILD_COMMIT_SHORT: (process.env.BUILD_COMMIT || '0000000').substr(0, 7),
|
||||
}
|
||||
|
||||
const environment = {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
DEBUG: process.env.NODE_ENV !== 'production' || false,
|
||||
PRODUCTION: process.env.NODE_ENV === 'production' || false,
|
||||
ALLOW_REGISTER: process.env.ALLOW_REGISTER !== 'false',
|
||||
}
|
||||
|
||||
const server = {
|
||||
LOGIN_API_URL: process.env.LOGIN_API_URL || 'http://localhost/login_api/',
|
||||
COMMUNITY_API_URL: process.env.COMMUNITY_API_URL || 'http://localhost/api/',
|
||||
GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000/graphql',
|
||||
}
|
||||
|
||||
const options = {
|
||||
ALLOW_REGISTER: process.env.ALLOW_REGISTER !== 'false',
|
||||
}
|
||||
|
||||
const CONFIG = {
|
||||
...version,
|
||||
...environment,
|
||||
...server,
|
||||
APP_VERSION: pkg.version,
|
||||
...options,
|
||||
}
|
||||
|
||||
export default CONFIG
|
||||
|
||||
@ -11,3 +11,59 @@ export const unsubscribeNewsletter = gql`
|
||||
unsubscribeNewsletter(email: $email)
|
||||
}
|
||||
`
|
||||
|
||||
export const resetPassword = gql`
|
||||
mutation($sessionId: Float!, $email: String!, $password: String!) {
|
||||
resetPassword(sessionId: $sessionId, email: $email, password: $password)
|
||||
}
|
||||
`
|
||||
|
||||
export const updateUserInfos = gql`
|
||||
mutation(
|
||||
$email: String!
|
||||
$firstName: String
|
||||
$lastName: String
|
||||
$description: String
|
||||
$username: String
|
||||
$password: String
|
||||
$passwordNew: String
|
||||
$locale: String
|
||||
) {
|
||||
updateUserInfos(
|
||||
email: $email
|
||||
firstName: $firstName
|
||||
lastName: $lastName
|
||||
description: $description
|
||||
username: $username
|
||||
password: $password
|
||||
passwordNew: $passwordNew
|
||||
language: $locale
|
||||
) {
|
||||
validValues
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const registerUser = gql`
|
||||
mutation(
|
||||
$firstName: String!
|
||||
$lastName: String!
|
||||
$email: String!
|
||||
$password: String!
|
||||
$language: String!
|
||||
) {
|
||||
createUser(
|
||||
email: $email
|
||||
firstName: $firstName
|
||||
lastName: $lastName
|
||||
password: $password
|
||||
language: $language
|
||||
)
|
||||
}
|
||||
`
|
||||
|
||||
export const sendCoins = gql`
|
||||
mutation($email: String!, $amount: Float!, $memo: String!) {
|
||||
sendCoins(email: $email, amount: $amount, memo: $memo)
|
||||
}
|
||||
`
|
||||
|
||||
@ -22,12 +22,6 @@ export const logout = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const resetPassword = gql`
|
||||
query($sessionId: Float!, $email: String!, $password: String!) {
|
||||
resetPassword(sessionId: $sessionId, email: $email, password: $password)
|
||||
}
|
||||
`
|
||||
|
||||
export const loginViaEmailVerificationCode = gql`
|
||||
query($optin: String!) {
|
||||
loginViaEmailVerificationCode(optin: $optin) {
|
||||
@ -37,32 +31,6 @@ export const loginViaEmailVerificationCode = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const updateUserInfos = gql`
|
||||
query(
|
||||
$email: String!
|
||||
$firstName: String
|
||||
$lastName: String
|
||||
$description: String
|
||||
$username: String
|
||||
$password: String
|
||||
$passwordNew: String
|
||||
$locale: String
|
||||
) {
|
||||
updateUserInfos(
|
||||
email: $email
|
||||
firstName: $firstName
|
||||
lastName: $lastName
|
||||
description: $description
|
||||
username: $username
|
||||
password: $password
|
||||
passwordNew: $passwordNew
|
||||
language: $locale
|
||||
) {
|
||||
validValues
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const transactionsQuery = gql`
|
||||
query($firstPage: Int = 1, $items: Int = 25, $order: String = "DESC") {
|
||||
transactionList(firstPage: $firstPage, items: $items, order: $order) {
|
||||
@ -94,30 +62,6 @@ export const transactionsQuery = gql`
|
||||
}
|
||||
`
|
||||
|
||||
export const resgisterUserQuery = gql`
|
||||
query(
|
||||
$firstName: String!
|
||||
$lastName: String!
|
||||
$email: String!
|
||||
$password: String!
|
||||
$language: String!
|
||||
) {
|
||||
create(
|
||||
email: $email
|
||||
firstName: $firstName
|
||||
lastName: $lastName
|
||||
password: $password
|
||||
language: $language
|
||||
)
|
||||
}
|
||||
`
|
||||
|
||||
export const sendCoins = gql`
|
||||
query($email: String!, $amount: Float!, $memo: String!) {
|
||||
sendCoins(email: $email, amount: $amount, memo: $memo)
|
||||
}
|
||||
`
|
||||
|
||||
export const sendResetPasswordEmail = gql`
|
||||
query($email: String!) {
|
||||
sendResetPasswordEmail(email: $email) {
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
},
|
||||
"decay": {
|
||||
"calculation_decay": "Berechnung der Vergänglichkeit",
|
||||
"calculation_total": "Berechnung der Gesamtsumme",
|
||||
"created": "Geschöpft",
|
||||
"days": "Tage",
|
||||
"decay": "Vergänglichkeit",
|
||||
@ -31,32 +32,27 @@
|
||||
"since_introduction": "seit Einführung der Vergänglichkeit",
|
||||
"Starting_block_decay": "Startblock Vergänglichkeit",
|
||||
"toCommunity": "An die Gemeinschaft",
|
||||
"total": "Gesamt",
|
||||
"year": "Jahre"
|
||||
},
|
||||
"error": {
|
||||
"change-password": "Fehler beim Ändern des Passworts",
|
||||
"error": "Fehler",
|
||||
"no-account": "Leider konnten wir keinen Account finden mit diesen Daten!"
|
||||
"no-account": "Leider konnten wir keinen Account finden mit diesen Daten!",
|
||||
"session-expired": "Sitzung abgelaufen!"
|
||||
},
|
||||
"form": {
|
||||
"amount": "Betrag",
|
||||
"at": "am",
|
||||
"cancel": "Abbrechen",
|
||||
"change": "ändern",
|
||||
"change-name": "Name ändern",
|
||||
"change-password": "Passwort ändern",
|
||||
"changeLanguage": "Sprache ändern",
|
||||
"change_username_info": "Einmal gespeichert, kann der Username ncht mehr geändert werden!",
|
||||
"close": "schließen",
|
||||
"date": "Datum",
|
||||
"description": "Beschreibung",
|
||||
"edit": "bearbeiten",
|
||||
"email": "E-Mail",
|
||||
"email_repeat": "eMail wiederholen",
|
||||
"firstname": "Vorname",
|
||||
"from": "von",
|
||||
"lastname": "Nachname",
|
||||
"max_gdd_info": "Maximale anzahl GDD zum versenden erreicht!",
|
||||
"memo": "Nachricht",
|
||||
"message": "Nachricht",
|
||||
"password": "Passwort",
|
||||
@ -78,7 +74,6 @@
|
||||
"time": "Zeit",
|
||||
"to": "bis",
|
||||
"to1": "an",
|
||||
"username": "Username",
|
||||
"validation": {
|
||||
"gddSendAmount": "Das Feld {_field_} muss eine Zahl zwischen {min} und {max} mit höchstens zwei Nachkommastellen sein",
|
||||
"is-not": "Du kannst dir selbst keine Gradidos überweisen",
|
||||
@ -103,28 +98,40 @@
|
||||
},
|
||||
"imprint": "Impressum",
|
||||
"language": "Sprache",
|
||||
"languages": {
|
||||
"de": "Deutsch",
|
||||
"en": "English"
|
||||
},
|
||||
"login": "Anmeldung",
|
||||
"logout": "Abmelden",
|
||||
"members_area": "Mitgliederbereich",
|
||||
"message": "hallo gradido !!",
|
||||
"privacy_policy": "Datenschutzerklärung",
|
||||
"reset": "Passwort zurücksetzen",
|
||||
"reset-password": {
|
||||
"not-authenticated": "Leider konnten wir dich nicht authentifizieren. Bitte wende dich an den Support.",
|
||||
"text": "Jetzt kannst du ein neues Passwort speichern, mit dem du dich zukünftig in der Gradido-App anmelden kannst.",
|
||||
"title": "Passwort zurücksetzen"
|
||||
},
|
||||
"select_language": "Bitte wähle eine Sprache für die App und Newsletter",
|
||||
"send": "Senden",
|
||||
"setting": {
|
||||
"changeNewsletter": "Newsletter Status ändern",
|
||||
"newsletter": "Newsletter",
|
||||
"newsletterFalse": "Du bist aus Newslettersystem ausgetragen.",
|
||||
"newsletterTrue": "Du bist im Newslettersystem eingetraten."
|
||||
"settings": {
|
||||
"language": {
|
||||
"changeLanguage": "Sprache ändern",
|
||||
"de": "Deutsch",
|
||||
"en": "English",
|
||||
"select_language": "Bitte wähle eine Sprache.",
|
||||
"success": "Deine Sprache wurde erfolgreich geändert."
|
||||
},
|
||||
"name": {
|
||||
"change-name": "Name ändern",
|
||||
"change-success": "Dein Name wurde erfolgreich geändert."
|
||||
},
|
||||
"newsletter": {
|
||||
"newsletter": "Newsletter",
|
||||
"newsletterFalse": "Du bist aus Newslettersystem ausgetragen.",
|
||||
"newsletterTrue": "Du bist im Newslettersystem eingetraten."
|
||||
},
|
||||
"password": {
|
||||
"change-password": "Passwort ändern",
|
||||
"forgot_pwd": "Passwort vergessen?",
|
||||
"reset": "Passwort zurücksetzen",
|
||||
"reset-password": {
|
||||
"not-authenticated": "Leider konnten wir dich nicht authentifizieren. Bitte wende dich an den Support.",
|
||||
"text": "Jetzt kannst du ein neues Passwort speichern, mit dem du dich zukünftig in der Gradido-App anmelden kannst."
|
||||
},
|
||||
"send_now": "Jetzt senden",
|
||||
"subtitle": "Wenn du dein Passwort vergessen hast, kannst du es hier zurücksetzen."
|
||||
}
|
||||
},
|
||||
"signup": "Registrieren",
|
||||
"site": {
|
||||
@ -139,34 +146,21 @@
|
||||
},
|
||||
"login": {
|
||||
"community": "Tausend Dank, weil du bei uns bist!",
|
||||
"forgot_pwd": "Passwort vergessen?",
|
||||
"new_wallet": "Neues Konto erstellen",
|
||||
"remember": "Passwort merken",
|
||||
"signin": "Anmelden"
|
||||
},
|
||||
"navbar": {
|
||||
"activity": "Aktivität",
|
||||
"my-profil": "Mein Profil",
|
||||
"settings": "Einstellungen",
|
||||
"support": "Support"
|
||||
},
|
||||
"overview": {
|
||||
"account_overview": "Kontoübersicht",
|
||||
"add_work": "neuer Gemeinschaftsbeitrag",
|
||||
"send_gradido": "Gradido versenden",
|
||||
"since_last_month": "seid letzten Monat"
|
||||
},
|
||||
"password": {
|
||||
"send_now": "Jetzt senden",
|
||||
"subtitle": "Wenn du dein Passwort vergessen hast, kannst du es hier zurücksetzen.",
|
||||
"title": "Passwort zurücksetzen"
|
||||
},
|
||||
"signup": {
|
||||
"agree": "Ich stimme der <a href='https://gradido.net/de/datenschutz/' target='_blank' >Datenschutzerklärung</a> zu.",
|
||||
"dont_match": "Die Passwörter stimmen nicht überein.",
|
||||
"lowercase": "Ein Kleinbuchstabe erforderlich.",
|
||||
"minimum": "Mindestens 8 Zeichen.",
|
||||
"no-whitespace": "Keine Leerzeichen und Tabulatoren",
|
||||
"one_number": "Eine Zahl erforderlich.",
|
||||
"special-char": "Ein Sonderzeichen erforderlich (z.B. _ oder ä)",
|
||||
"subtitle": "Werde Teil der Gemeinschaft!",
|
||||
"title": "Erstelle dein Gradido-Konto",
|
||||
"uppercase": "Ein Großbuchstabe erforderlich."
|
||||
@ -188,6 +182,5 @@
|
||||
"show_all": "Alle <strong>{count}</strong> Transaktionen ansehen"
|
||||
},
|
||||
"transactions": "Transaktionen",
|
||||
"welcome": "Willkommen!",
|
||||
"whitepaper": "Whitepaper"
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
},
|
||||
"decay": {
|
||||
"calculation_decay": "Calculation of Decay",
|
||||
"calculation_total": "Calculation of the grand total",
|
||||
"created": "Created",
|
||||
"days": "Days",
|
||||
"decay": "Decay",
|
||||
@ -31,32 +32,27 @@
|
||||
"since_introduction": "Since the introduction of Decay",
|
||||
"Starting_block_decay": "Starting Block Decay",
|
||||
"toCommunity": "To the community",
|
||||
"total": "Total",
|
||||
"year": "Years"
|
||||
},
|
||||
"error": {
|
||||
"change-password": "Error while changing password",
|
||||
"error": "Error",
|
||||
"no-account": "Unfortunately we could not find an account to the given data!"
|
||||
"no-account": "Unfortunately we could not find an account to the given data!",
|
||||
"session-expired": "The session expired"
|
||||
},
|
||||
"form": {
|
||||
"amount": "Amount",
|
||||
"at": "at",
|
||||
"cancel": "Cancel",
|
||||
"change": "change",
|
||||
"change-name": "Change name",
|
||||
"change-password": "Change password",
|
||||
"changeLanguage": "Change language",
|
||||
"change_username_info": "Once saved, the username cannot be changed again!",
|
||||
"close": "Close",
|
||||
"date": "Date",
|
||||
"description": "Description",
|
||||
"edit": "Edit",
|
||||
"email": "Email",
|
||||
"email_repeat": "Repeat Email",
|
||||
"firstname": "Firstname",
|
||||
"from": "from",
|
||||
"lastname": "Lastname",
|
||||
"max_gdd_info": "Maximum number of GDDs to be sent has been reached!",
|
||||
"memo": "Message",
|
||||
"message": "Message",
|
||||
"password": "Password",
|
||||
@ -78,7 +74,6 @@
|
||||
"time": "Time",
|
||||
"to": "to",
|
||||
"to1": "to",
|
||||
"username": "Username",
|
||||
"validation": {
|
||||
"gddSendAmount": "The {_field_} field must be a number between {min} and {max} with at most two digits",
|
||||
"is-not": "You cannot send Gradidos to yourself",
|
||||
@ -103,28 +98,40 @@
|
||||
},
|
||||
"imprint": "Legal notice",
|
||||
"language": "Language",
|
||||
"languages": {
|
||||
"de": "Deutsch",
|
||||
"en": "English"
|
||||
},
|
||||
"login": "Login",
|
||||
"logout": "Logout",
|
||||
"members_area": "Member's area",
|
||||
"message": "hello gradido !!",
|
||||
"privacy_policy": "Privacy policy",
|
||||
"reset": "Reset password",
|
||||
"reset-password": {
|
||||
"not-authenticated": "Unfortunately we could not authenticate you. Please contact the support.",
|
||||
"text": "Now you can save a new password to login to the Gradido-App in the future.",
|
||||
"title": "Reset Password"
|
||||
},
|
||||
"select_language": "Please choose a language for the app and newsletter",
|
||||
"send": "Send",
|
||||
"setting": {
|
||||
"changeNewsletter": "Newsletter status change",
|
||||
"newsletter": "Newsletter",
|
||||
"newsletterFalse": "You are unsubscribed from newsletter system.",
|
||||
"newsletterTrue": "You are subscribed to newsletter system."
|
||||
"settings": {
|
||||
"language": {
|
||||
"changeLanguage": "Change language",
|
||||
"de": "Deutsch",
|
||||
"en": "English",
|
||||
"select_language": "Please choose a language.",
|
||||
"success": "Your language has been successfully updated."
|
||||
},
|
||||
"name": {
|
||||
"change-name": "Change name",
|
||||
"change-success": "Your name has been successfully changed."
|
||||
},
|
||||
"newsletter": {
|
||||
"newsletter": "Newsletter",
|
||||
"newsletterFalse": "You are unsubscribed from newsletter system.",
|
||||
"newsletterTrue": "You are subscribed to newsletter system."
|
||||
},
|
||||
"password": {
|
||||
"change-password": "Change password",
|
||||
"forgot_pwd": "Forgot password?",
|
||||
"reset": "Reset password",
|
||||
"reset-password": {
|
||||
"not-authenticated": "Unfortunately we could not authenticate you. Please contact the support.",
|
||||
"text": "Now you can save a new password to login to the Gradido-App in the future."
|
||||
},
|
||||
"send_now": "Send now",
|
||||
"subtitle": "If you have forgotten your password, you can reset it here."
|
||||
}
|
||||
},
|
||||
"signup": "Sign up",
|
||||
"site": {
|
||||
@ -139,34 +146,21 @@
|
||||
},
|
||||
"login": {
|
||||
"community": "A thousand thanks for being with us!",
|
||||
"forgot_pwd": "Forgot password?",
|
||||
"new_wallet": "Create new account",
|
||||
"remember": "Remember password",
|
||||
"signin": "Sign in"
|
||||
},
|
||||
"navbar": {
|
||||
"activity": "Activity",
|
||||
"my-profil": "My profile",
|
||||
"settings": "Settings",
|
||||
"support": "Support"
|
||||
},
|
||||
"overview": {
|
||||
"account_overview": "Account overview",
|
||||
"add_work": "New Community Contribution",
|
||||
"send_gradido": "Send Gradido",
|
||||
"since_last_month": "since last month"
|
||||
},
|
||||
"password": {
|
||||
"send_now": "Send now",
|
||||
"subtitle": "If you have forgotten your password, you can reset it here.",
|
||||
"title": "Reset password"
|
||||
},
|
||||
"signup": {
|
||||
"agree": "I agree to the <a href='https://gradido.net/en/datenschutz/' target='_blank' > privacy policy</a>.",
|
||||
"dont_match": "Passwords don't match.",
|
||||
"lowercase": "One lowercase letter required.",
|
||||
"minimum": "8 characters minimum.",
|
||||
"no-whitespace": "No white spaces and tabs",
|
||||
"one_number": "One number required.",
|
||||
"special-char": "One special character required (e.g. _ or ä)",
|
||||
"subtitle": "Become a part of the community!",
|
||||
"title": "Create your Gradido account",
|
||||
"uppercase": "One uppercase letter required."
|
||||
@ -188,6 +182,5 @@
|
||||
"show_all": "View all <strong>{count}</strong> transactions."
|
||||
},
|
||||
"transactions": "Transactions",
|
||||
"welcome": "Welcome!",
|
||||
"whitepaper": "Whitepaper"
|
||||
}
|
||||
|
||||
@ -21,6 +21,12 @@ const authLink = new ApolloLink((operation, forward) => {
|
||||
},
|
||||
})
|
||||
return forward(operation).map((response) => {
|
||||
if (response.errors && response.errors[0].message === '403.13 - Client certificate revoked') {
|
||||
response.errors[0].message = i18n.t('error.session-expired')
|
||||
store.dispatch('logout', null)
|
||||
if (router.currentRoute.path !== '/login') router.push('/login')
|
||||
return response
|
||||
}
|
||||
const newToken = operation.getContext().response.headers.get('token')
|
||||
if (newToken) store.commit('token', newToken)
|
||||
return response
|
||||
|
||||
@ -112,6 +112,20 @@ export const loadAllRules = (i18nCallback) => {
|
||||
message: (_, values) => i18nCallback.t('site.signup.minimum', values),
|
||||
})
|
||||
|
||||
extend('atLeastOneSpecialCharater', {
|
||||
validate(value) {
|
||||
return !!value.match(/[^a-zA-Z0-9]/)
|
||||
},
|
||||
message: (_, values) => i18nCallback.t('site.signup.special-char', values),
|
||||
})
|
||||
|
||||
extend('noWhitespaceCharacters', {
|
||||
validate(value) {
|
||||
return !!value.match(/[^ \t\n\r]/)
|
||||
},
|
||||
message: (_, values) => i18nCallback.t('site.signup.no-whitespace', values),
|
||||
})
|
||||
|
||||
extend('samePassword', {
|
||||
validate(value, [pwd]) {
|
||||
return value === pwd
|
||||
|
||||
@ -59,6 +59,21 @@ describe('ContentFooter', () => {
|
||||
'https://github.com/gradido/gradido/releases/latest',
|
||||
)
|
||||
})
|
||||
|
||||
it('has last commit hash', async () => {
|
||||
wrapper.setData({ shortHash: 'ACCEDED' })
|
||||
wrapper.setData({ hash: 'ACCEDEDC001D00DC001D00DC001D00DC001CAFA' })
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('div.copyright').findAll('a').at(2).text()).toEqual('(ACCEDED)')
|
||||
})
|
||||
|
||||
it('links to last release commit', async () => {
|
||||
wrapper.setData({ hash: 'ACCEDEDC001D00DC001D00DC001D00DC001CAFA' })
|
||||
await wrapper.vm.$nextTick()
|
||||
expect(wrapper.find('div.copyright').findAll('a').at(2).attributes('href')).toEqual(
|
||||
'https://github.com/gradido/gradido/commit/ACCEDEDC001D00DC001D00DC001D00DC001CAFA',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('links to gradido.net', () => {
|
||||
|
||||
@ -15,6 +15,13 @@
|
||||
<a href="https://github.com/gradido/gradido/releases/latest" target="_blank">
|
||||
App version {{ version }}
|
||||
</a>
|
||||
<a
|
||||
v-if="hash"
|
||||
:href="'https://github.com/gradido/gradido/commit/' + hash"
|
||||
target="_blank"
|
||||
>
|
||||
({{ shortHash }})
|
||||
</a>
|
||||
</div>
|
||||
</b-col>
|
||||
</b-row>
|
||||
@ -59,6 +66,8 @@ export default {
|
||||
return {
|
||||
year: new Date().getFullYear(),
|
||||
version: CONFIG.APP_VERSION,
|
||||
hash: CONFIG.BUILD_COMMIT,
|
||||
shortHash: CONFIG.BUILD_COMMIT_SHORT,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ describe('AccountOverview', () => {
|
||||
},
|
||||
},
|
||||
$apollo: {
|
||||
query: sendMock,
|
||||
mutate: sendMock,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@ import GddTransactionListFooter from './AccountOverview/GddTransactionListFooter
|
||||
import TransactionForm from './AccountOverview/GddSend/TransactionForm.vue'
|
||||
import TransactionConfirmation from './AccountOverview/GddSend/TransactionConfirmation.vue'
|
||||
import TransactionResult from './AccountOverview/GddSend/TransactionResult.vue'
|
||||
import { sendCoins } from '../../graphql/queries.js'
|
||||
import { sendCoins } from '../../graphql/mutations.js'
|
||||
|
||||
const EMPTY_TRANSACTION_DATA = {
|
||||
email: '',
|
||||
@ -105,8 +105,8 @@ export default {
|
||||
async sendTransaction() {
|
||||
this.loading = true
|
||||
this.$apollo
|
||||
.query({
|
||||
query: sendCoins,
|
||||
.mutate({
|
||||
mutation: sendCoins,
|
||||
variables: this.transactionData,
|
||||
})
|
||||
.then(() => {
|
||||
|
||||
@ -54,7 +54,7 @@ describe('GddTransactionList', () => {
|
||||
await wrapper.setProps({
|
||||
transactions: [
|
||||
{
|
||||
balance: '19.93',
|
||||
balance: 19.93,
|
||||
date: '2021-05-25T17:38:13+00:00',
|
||||
memo: 'Alles Gute zum Geburtstag',
|
||||
name: 'Bob der Baumeister',
|
||||
@ -63,7 +63,7 @@ describe('GddTransactionList', () => {
|
||||
decay: { balance: '0.5' },
|
||||
},
|
||||
{
|
||||
balance: '1000',
|
||||
balance: 1000,
|
||||
date: '2021-04-29T15:34:49+00:00',
|
||||
memo: 'Gut das du da bist!',
|
||||
name: 'Gradido Akademie',
|
||||
@ -71,7 +71,7 @@ describe('GddTransactionList', () => {
|
||||
type: 'creation',
|
||||
},
|
||||
{
|
||||
balance: '314.98',
|
||||
balance: 314.98,
|
||||
date: '2021-04-29T17:26:40+00:00',
|
||||
memo: 'Für das Fahrrad!',
|
||||
name: 'Jan Ulrich',
|
||||
|
||||
@ -18,13 +18,13 @@
|
||||
</b-button>
|
||||
</div>
|
||||
|
||||
<b-row>
|
||||
<div>
|
||||
<!-- ICON -->
|
||||
<div class="col-1 gdd-transaction-list-item-icon">
|
||||
<b-icon :icon="getProperties(type).icon" :class="getProperties(type).class" />
|
||||
</div>
|
||||
|
||||
<div class="col col-11">
|
||||
<div>
|
||||
<!-- Betrag / Name Email -->
|
||||
<b-row>
|
||||
<div class="col-5 text-right">
|
||||
@ -70,13 +70,19 @@
|
||||
</div>
|
||||
</b-row>
|
||||
</div>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<!-- Collaps Start -->
|
||||
|
||||
<b-collapse v-if="type != 'decay'" class="pb-4" :id="'a' + date + ''">
|
||||
<div style="border: 0px; background-color: #f1f1f1" class="p-2 pb-4 mb-4">
|
||||
<decay-information v-if="decay" decaytyp="new" :decay="decay" />
|
||||
<decay-information
|
||||
v-if="decay"
|
||||
decaytyp="new"
|
||||
:balance="balance"
|
||||
:decay="decay"
|
||||
:type="type"
|
||||
/>
|
||||
</div>
|
||||
</b-collapse>
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="gdt-transaction-list">
|
||||
<div class="list-group">
|
||||
<div class="list-group" style="background-color: #fff">
|
||||
<div v-if="transactionGdtCount === 0">
|
||||
{{ $t('gdt.no-transactions') }}
|
||||
</div>
|
||||
|
||||
@ -39,11 +39,11 @@ describe('ForgotPassword', () => {
|
||||
})
|
||||
|
||||
it('has a title', () => {
|
||||
expect(wrapper.find('h1').text()).toEqual('site.password.title')
|
||||
expect(wrapper.find('h1').text()).toEqual('settings.password.reset')
|
||||
})
|
||||
|
||||
it('has a subtitle', () => {
|
||||
expect(wrapper.find('p.text-lead').text()).toEqual('site.password.subtitle')
|
||||
expect(wrapper.find('p.text-lead').text()).toEqual('settings.password.subtitle')
|
||||
})
|
||||
|
||||
describe('back button', () => {
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
<div class="header-body text-center mb-7">
|
||||
<b-row class="justify-content-center">
|
||||
<b-col xl="5" lg="6" md="8" class="px-2">
|
||||
<h1>{{ $t('site.password.title') }}</h1>
|
||||
<p class="text-lead">{{ $t('site.password.subtitle') }}</p>
|
||||
<h1>{{ $t('settings.password.reset') }}</h1>
|
||||
<p class="text-lead">{{ $t('settings.password.subtitle') }}</p>
|
||||
</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
@ -22,7 +22,7 @@
|
||||
<input-email v-model="form.email"></input-email>
|
||||
<div class="text-center">
|
||||
<b-button type="submit" variant="primary">
|
||||
{{ $t('site.password.send_now') }}
|
||||
{{ $t('settings.password.send_now') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-form>
|
||||
|
||||
@ -71,7 +71,7 @@ describe('Login', () => {
|
||||
describe('links', () => {
|
||||
it('has a link "Forgot Password?"', () => {
|
||||
expect(wrapper.findAllComponents(RouterLinkStub).at(0).text()).toEqual(
|
||||
'site.login.forgot_pwd',
|
||||
'settings.password.forgot_pwd',
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@
|
||||
<b-row class="mt-3">
|
||||
<b-col cols="6">
|
||||
<router-link to="/password">
|
||||
{{ $t('site.login.forgot_pwd') }}
|
||||
{{ $t('settings.password.forgot_pwd') }}
|
||||
</router-link>
|
||||
</b-col>
|
||||
<b-col cols="6" class="text-right" v-show="allowRegister">
|
||||
|
||||
@ -5,7 +5,7 @@ import Register from './Register'
|
||||
|
||||
const localVue = global.localVue
|
||||
|
||||
const resgisterUserQueryMock = jest.fn()
|
||||
const registerUserMutationMock = jest.fn()
|
||||
const routerPushMock = jest.fn()
|
||||
|
||||
describe('Register', () => {
|
||||
@ -20,11 +20,12 @@ describe('Register', () => {
|
||||
push: routerPushMock,
|
||||
},
|
||||
$apollo: {
|
||||
query: resgisterUserQueryMock,
|
||||
mutate: registerUserMutationMock,
|
||||
},
|
||||
$store: {
|
||||
state: {
|
||||
language: null,
|
||||
email: 'peter@lustig.de',
|
||||
language: 'en',
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -54,11 +55,11 @@ describe('Register', () => {
|
||||
|
||||
describe('links', () => {
|
||||
it('has a link "Back"', () => {
|
||||
expect(wrapper.findAllComponents(RouterLinkStub).at(0).text()).toEqual('back')
|
||||
expect(wrapper.find('.test-button-back').text()).toEqual('back')
|
||||
})
|
||||
|
||||
it('links to /login when clicking "Back"', () => {
|
||||
expect(wrapper.findAllComponents(RouterLinkStub).at(0).props().to).toBe('/login')
|
||||
expect(wrapper.find('.test-button-back').props().to).toBe('/login')
|
||||
})
|
||||
})
|
||||
|
||||
@ -88,17 +89,17 @@ describe('Register', () => {
|
||||
it('has Language selected field', () => {
|
||||
expect(wrapper.find('.selectedLanguage').exists()).toBeTruthy()
|
||||
})
|
||||
it('selected Language value de', async () => {
|
||||
it('selects Language value en', async () => {
|
||||
wrapper.find('.selectedLanguage').findAll('option').at(1).setSelected()
|
||||
expect(wrapper.find('.selectedLanguage').element.value).toBe('de')
|
||||
expect(wrapper.find('.selectedLanguage').element.value).toBe('en')
|
||||
})
|
||||
|
||||
it('has 1 checkbox input fields', () => {
|
||||
expect(wrapper.find('#registerCheckbox').exists()).toBeTruthy()
|
||||
})
|
||||
|
||||
it('has no submit button when not completely filled', () => {
|
||||
expect(wrapper.find('button[type="submit"]').exists()).toBe(false)
|
||||
it('has disabled submit button when not completely filled', () => {
|
||||
expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
it('displays a message that Email is required', async () => {
|
||||
@ -126,73 +127,23 @@ describe('Register', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('resetForm', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.find('#registerFirstname').setValue('Max')
|
||||
wrapper.find('#registerLastname').setValue('Mustermann')
|
||||
wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net')
|
||||
wrapper.find('input[name="form.password"]').setValue('Aa123456')
|
||||
wrapper.find('input[name="form.passwordRepeat"]').setValue('Aa123456')
|
||||
wrapper.find('.language-switch-select').findAll('option').at(1).setSelected()
|
||||
wrapper.find('input[name="site.signup.agree"]').setChecked(true)
|
||||
})
|
||||
|
||||
it('reset selected value language', async () => {
|
||||
await wrapper.find('button.ml-2').trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.find('.language-switch-select').element.value).toBe(undefined)
|
||||
})
|
||||
|
||||
it('resets the firstName field after clicking the reset button', async () => {
|
||||
await wrapper.find('button.ml-2').trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.find('#registerFirstname').element.value).toBe('')
|
||||
})
|
||||
|
||||
it('resets the lastName field after clicking the reset button', async () => {
|
||||
await wrapper.find('button.ml-2').trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.find('#registerLastname').element.value).toBe('')
|
||||
})
|
||||
|
||||
it('resets the email field after clicking the reset button', async () => {
|
||||
await wrapper.find('button.ml-2').trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.find('#Email-input-field').element.value).toBe('')
|
||||
})
|
||||
|
||||
it.skip('resets the password field after clicking the reset button', async () => {
|
||||
await wrapper.find('button.ml-2').trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.find('input[name="form.password"]').element.value).toBe('')
|
||||
})
|
||||
|
||||
it.skip('resets the passwordRepeat field after clicking the reset button', async () => {
|
||||
await wrapper.find('button.ml-2').trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.find('input[name="form.passwordRepeat"]').element.value).toBe('')
|
||||
})
|
||||
|
||||
it('resets the firstName field after clicking the reset button', async () => {
|
||||
await wrapper.find('button.ml-2').trigger('click')
|
||||
await flushPromises()
|
||||
expect(wrapper.find('input[name="site.signup.agree"]').props.checked).not.toBeTruthy()
|
||||
})
|
||||
})
|
||||
|
||||
describe('API calls', () => {
|
||||
beforeEach(() => {
|
||||
wrapper.find('#registerFirstname').setValue('Max')
|
||||
wrapper.find('#registerLastname').setValue('Mustermann')
|
||||
wrapper.find('#Email-input-field').setValue('max.mustermann@gradido.net')
|
||||
wrapper.find('input[name="form.password"]').setValue('Aa123456')
|
||||
wrapper.find('input[name="form.passwordRepeat"]').setValue('Aa123456')
|
||||
wrapper.find('input[name="form.password"]').setValue('Aa123456_')
|
||||
wrapper.find('input[name="form.passwordRepeat"]').setValue('Aa123456_')
|
||||
wrapper.find('.language-switch-select').findAll('option').at(1).setSelected()
|
||||
})
|
||||
|
||||
it('has enabled submit button when completely filled', () => {
|
||||
expect(wrapper.find('button[type="submit"]').attributes('disabled')).toBe('disabled')
|
||||
})
|
||||
|
||||
describe('server sends back error', () => {
|
||||
beforeEach(async () => {
|
||||
resgisterUserQueryMock.mockRejectedValue({ message: 'Ouch!' })
|
||||
registerUserMutationMock.mockRejectedValue({ message: 'Ouch!' })
|
||||
await wrapper.find('form').trigger('submit')
|
||||
await flushPromises()
|
||||
})
|
||||
@ -217,7 +168,7 @@ describe('Register', () => {
|
||||
|
||||
describe('server sends back success', () => {
|
||||
beforeEach(() => {
|
||||
resgisterUserQueryMock.mockResolvedValue({
|
||||
registerUserMutationMock.mockResolvedValue({
|
||||
data: {
|
||||
create: 'success',
|
||||
},
|
||||
@ -227,14 +178,14 @@ describe('Register', () => {
|
||||
it('routes to "/thx/register"', async () => {
|
||||
await wrapper.find('form').trigger('submit')
|
||||
await flushPromises()
|
||||
expect(resgisterUserQueryMock).toBeCalledWith(
|
||||
expect(registerUserMutationMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
email: 'max.mustermann@gradido.net',
|
||||
firstName: 'Max',
|
||||
lastName: 'Mustermann',
|
||||
password: 'Aa123456',
|
||||
language: 'de',
|
||||
password: 'Aa123456_',
|
||||
language: 'en',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
@ -116,13 +116,19 @@
|
||||
</span>
|
||||
</b-alert>
|
||||
|
||||
<div
|
||||
class="text-center"
|
||||
v-if="namesFilled && emailFilled && form.agree && languageFilled"
|
||||
>
|
||||
<div class="text-center">
|
||||
<div class="text-center">
|
||||
<b-button class="ml-2" @click="resetForm()">{{ $t('form.reset') }}</b-button>
|
||||
<b-button type="submit" variant="primary">{{ $t('signup') }}</b-button>
|
||||
<b-button class="ml-2 test-button-back" to="/login">
|
||||
{{ $t('back') }}
|
||||
</b-button>
|
||||
|
||||
<b-button
|
||||
:disabled="!(namesFilled && emailFilled && form.agree && languageFilled)"
|
||||
type="submit"
|
||||
variant="primary"
|
||||
>
|
||||
{{ $t('signup') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</div>
|
||||
</b-form>
|
||||
@ -131,9 +137,6 @@
|
||||
</b-card>
|
||||
</b-col>
|
||||
</b-row>
|
||||
<div class="text-center py-lg-4">
|
||||
<router-link to="/login" class="mt-3">{{ $t('back') }}</router-link>
|
||||
</div>
|
||||
</b-container>
|
||||
</div>
|
||||
</template>
|
||||
@ -141,7 +144,7 @@
|
||||
import InputEmail from '../../components/Inputs/InputEmail.vue'
|
||||
import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConfirmation.vue'
|
||||
import LanguageSwitchSelect from '../../components/LanguageSwitchSelect.vue'
|
||||
import { resgisterUserQuery } from '../../graphql/queries'
|
||||
import { registerUser } from '../../graphql/mutations'
|
||||
|
||||
export default {
|
||||
components: { InputPasswordConfirmation, InputEmail, LanguageSwitchSelect },
|
||||
@ -172,26 +175,10 @@ export default {
|
||||
getValidationState({ dirty, validated, valid = null }) {
|
||||
return dirty || validated ? valid : null
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
email: '',
|
||||
password: {
|
||||
password: '',
|
||||
passwordRepeat: '',
|
||||
},
|
||||
agree: false,
|
||||
}
|
||||
this.language = ''
|
||||
this.$nextTick(() => {
|
||||
this.$refs.observer.reset()
|
||||
})
|
||||
},
|
||||
async onSubmit() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: resgisterUserQuery,
|
||||
.mutate({
|
||||
mutation: registerUser,
|
||||
variables: {
|
||||
email: this.form.email,
|
||||
firstName: this.form.firstname,
|
||||
@ -238,7 +225,7 @@ export default {
|
||||
return this.form.email !== ''
|
||||
},
|
||||
languageFilled() {
|
||||
return this.language !== null && this.language !== ''
|
||||
return !!this.language
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import flushPromises from 'flush-promises'
|
||||
const localVue = global.localVue
|
||||
|
||||
const apolloQueryMock = jest.fn().mockRejectedValue({ message: 'error' })
|
||||
const apolloMutationMock = jest.fn()
|
||||
|
||||
const toasterMock = jest.fn()
|
||||
const routerPushMock = jest.fn()
|
||||
@ -36,6 +37,7 @@ describe('ResetPassword', () => {
|
||||
}),
|
||||
},
|
||||
$apollo: {
|
||||
mutate: apolloMutationMock,
|
||||
query: apolloQueryMock,
|
||||
},
|
||||
}
|
||||
@ -69,8 +71,10 @@ describe('ResetPassword', () => {
|
||||
})
|
||||
|
||||
it('has a message suggesting to contact the support', () => {
|
||||
expect(wrapper.find('div.header').text()).toContain('reset-password.title')
|
||||
expect(wrapper.find('div.header').text()).toContain('reset-password.not-authenticated')
|
||||
expect(wrapper.find('div.header').text()).toContain('settings.password.reset')
|
||||
expect(wrapper.find('div.header').text()).toContain(
|
||||
'settings.password.reset-password.not-authenticated',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -97,8 +101,10 @@ describe('ResetPassword', () => {
|
||||
|
||||
describe('Register header', () => {
|
||||
it('has a welcome message', async () => {
|
||||
expect(wrapper.find('div.header').text()).toContain('reset-password.title')
|
||||
expect(wrapper.find('div.header').text()).toContain('reset-password.text')
|
||||
expect(wrapper.find('div.header').text()).toContain('settings.password.reset')
|
||||
expect(wrapper.find('div.header').text()).toContain(
|
||||
'settings.password.reset-password.text',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -138,15 +144,15 @@ describe('ResetPassword', () => {
|
||||
beforeEach(async () => {
|
||||
await wrapper.setData({ authenticated: true, sessionId: 1 })
|
||||
await wrapper.vm.$nextTick()
|
||||
await wrapper.findAll('input').at(0).setValue('Aa123456')
|
||||
await wrapper.findAll('input').at(1).setValue('Aa123456')
|
||||
await wrapper.findAll('input').at(0).setValue('Aa123456_')
|
||||
await wrapper.findAll('input').at(1).setValue('Aa123456_')
|
||||
await flushPromises()
|
||||
await wrapper.find('form').trigger('submit')
|
||||
})
|
||||
|
||||
describe('server response with error', () => {
|
||||
beforeEach(() => {
|
||||
apolloQueryMock.mockRejectedValue({ message: 'error' })
|
||||
apolloMutationMock.mockRejectedValue({ message: 'error' })
|
||||
})
|
||||
it('toasts an error message', () => {
|
||||
expect(toasterMock).toHaveBeenCalledWith('error')
|
||||
@ -155,19 +161,19 @@ describe('ResetPassword', () => {
|
||||
|
||||
describe('server response with success', () => {
|
||||
beforeEach(() => {
|
||||
apolloQueryMock.mockResolvedValue({
|
||||
apolloMutationMock.mockResolvedValue({
|
||||
data: {
|
||||
resetPassword: 'success',
|
||||
},
|
||||
})
|
||||
})
|
||||
it('calls the API', () => {
|
||||
expect(apolloQueryMock).toBeCalledWith(
|
||||
expect(apolloMutationMock).toBeCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
sessionId: 1,
|
||||
email: 'user@example.org',
|
||||
password: 'Aa123456',
|
||||
password: 'Aa123456_',
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
@ -5,13 +5,13 @@
|
||||
<div class="header-body text-center mb-7">
|
||||
<b-row class="justify-content-center">
|
||||
<b-col xl="5" lg="6" md="8" class="px-2">
|
||||
<h1>{{ $t('reset-password.title') }}</h1>
|
||||
<h1>{{ $t('settings.password.reset') }}</h1>
|
||||
<div class="pb-4" v-if="!pending">
|
||||
<span v-if="authenticated">
|
||||
{{ $t('reset-password.text') }}
|
||||
{{ $t('settings.password.reset-password.text') }}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ $t('reset-password.not-authenticated') }}
|
||||
{{ $t('settings.password.reset-password.not-authenticated') }}
|
||||
</span>
|
||||
</div>
|
||||
</b-col>
|
||||
@ -29,7 +29,7 @@
|
||||
<input-password-confirmation v-model="form" :register="register" />
|
||||
<div class="text-center">
|
||||
<b-button type="submit" variant="primary" class="mt-4">
|
||||
{{ $t('reset') }}
|
||||
{{ $t('settings.password.reset') }}
|
||||
</b-button>
|
||||
</div>
|
||||
</b-form>
|
||||
@ -48,7 +48,8 @@
|
||||
</template>
|
||||
<script>
|
||||
import InputPasswordConfirmation from '../../components/Inputs/InputPasswordConfirmation'
|
||||
import { resetPassword, loginViaEmailVerificationCode } from '../../graphql/queries'
|
||||
import { loginViaEmailVerificationCode } from '../../graphql/queries'
|
||||
import { resetPassword } from '../../graphql/mutations'
|
||||
|
||||
export default {
|
||||
name: 'ResetPassword',
|
||||
@ -71,8 +72,8 @@ export default {
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: resetPassword,
|
||||
.mutate({
|
||||
mutation: resetPassword,
|
||||
variables: {
|
||||
sessionId: this.sessionId,
|
||||
email: this.email,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<b-card class="bg-transparent">
|
||||
<b-card class="bg-transparent border-0">
|
||||
<div class="w-100 text-center">
|
||||
<vue-qrcode
|
||||
v-if="$store.state.email"
|
||||
|
||||
@ -29,7 +29,7 @@ describe('UserCard_FormUserData', () => {
|
||||
error: toastErrorMock,
|
||||
},
|
||||
$apollo: {
|
||||
query: mockAPIcall,
|
||||
mutate: mockAPIcall,
|
||||
},
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ describe('UserCard_FormUserData', () => {
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessMock).toBeCalledWith('site.profil.user-data.change-success')
|
||||
expect(toastSuccessMock).toBeCalledWith('settings.name.change-success')
|
||||
})
|
||||
|
||||
it('has an edit button again', () => {
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<b-card id="userdata_form" class="bg-transparent" style="background-color: #ebebeba3 !important">
|
||||
<b-card id="userdata_form" class="card-border-radius card-background-gray">
|
||||
<div>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a @click="showUserData ? (showUserData = !showUserData) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.change-name') }}</span>
|
||||
<span class="pointer mr-3">{{ $t('settings.name.change-name') }}</span>
|
||||
<b-icon v-if="showUserData" class="pointer ml-3" icon="pencil"></b-icon>
|
||||
<b-icon v-else icon="x-circle" class="pointer ml-3" variant="danger"></b-icon>
|
||||
</a>
|
||||
@ -72,7 +72,7 @@
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import { updateUserInfos } from '../../../graphql/queries'
|
||||
import { updateUserInfos } from '../../../graphql/mutations'
|
||||
|
||||
export default {
|
||||
name: 'FormUserData',
|
||||
@ -108,8 +108,8 @@ export default {
|
||||
async onSubmit(event) {
|
||||
event.preventDefault()
|
||||
this.$apollo
|
||||
.query({
|
||||
query: updateUserInfos,
|
||||
.mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
email: this.$store.state.email,
|
||||
firstName: this.form.firstName,
|
||||
@ -122,7 +122,7 @@ export default {
|
||||
this.$store.commit('lastName', this.form.lastName)
|
||||
this.$store.commit('description', this.form.description)
|
||||
this.showUserData = true
|
||||
this.$toasted.success(this.$t('site.profil.user-data.change-success'))
|
||||
this.$toasted.success(this.$t('settings.name.change-success'))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
|
||||
@ -21,7 +21,7 @@ describe('UserCard_FormUserMail', () => {
|
||||
},
|
||||
},
|
||||
$apollo: {
|
||||
query: mockAPIcall,
|
||||
mutate: mockAPIcall,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import { updateUserInfos } from '../../../graphql/queries'
|
||||
import { updateUserInfos } from '../../../graphql/mutations'
|
||||
|
||||
export default {
|
||||
name: 'FormUserMail',
|
||||
@ -45,8 +45,8 @@ export default {
|
||||
methods: {
|
||||
async onSubmit() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: updateUserInfos,
|
||||
.mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
email: this.$store.state.email,
|
||||
newEmail: this.newEmail,
|
||||
|
||||
@ -25,7 +25,7 @@ describe('UserCard_FormUserPasswort', () => {
|
||||
error: toastErrorMock,
|
||||
},
|
||||
$apollo: {
|
||||
query: changePasswordProfileMock,
|
||||
mutate: changePasswordProfileMock,
|
||||
},
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ describe('UserCard_FormUserPasswort', () => {
|
||||
})
|
||||
|
||||
it('has a change password button with text "form.change-password"', () => {
|
||||
expect(wrapper.find('a').text()).toEqual('form.change-password')
|
||||
expect(wrapper.find('a').text()).toEqual('settings.password.change-password')
|
||||
})
|
||||
|
||||
it('has a change password button with a pencil icon', () => {
|
||||
@ -105,12 +105,14 @@ describe('UserCard_FormUserPasswort', () => {
|
||||
describe('validation', () => {
|
||||
it('displays all password requirements', () => {
|
||||
const feedbackArray = wrapper.findAll('div.invalid-feedback').at(1).findAll('span')
|
||||
expect(feedbackArray).toHaveLength(5)
|
||||
expect(feedbackArray).toHaveLength(7)
|
||||
expect(feedbackArray.at(0).text()).toBe('validations.messages.required')
|
||||
expect(feedbackArray.at(1).text()).toBe('site.signup.lowercase')
|
||||
expect(feedbackArray.at(2).text()).toBe('site.signup.uppercase')
|
||||
expect(feedbackArray.at(3).text()).toBe('site.signup.one_number')
|
||||
expect(feedbackArray.at(4).text()).toBe('site.signup.minimum')
|
||||
expect(feedbackArray.at(5).text()).toBe('site.signup.special-char')
|
||||
expect(feedbackArray.at(6).text()).toBe('site.signup.no-whitespace')
|
||||
})
|
||||
|
||||
it('removes first message when a character is given', async () => {
|
||||
@ -125,7 +127,7 @@ describe('UserCard_FormUserPasswort', () => {
|
||||
await wrapper.findAll('input').at(1).setValue('a')
|
||||
await flushPromises()
|
||||
const feedbackArray = wrapper.findAll('div.invalid-feedback').at(1).findAll('span')
|
||||
expect(feedbackArray).toHaveLength(3)
|
||||
expect(feedbackArray).toHaveLength(4)
|
||||
expect(feedbackArray.at(0).text()).toBe('site.signup.uppercase')
|
||||
})
|
||||
|
||||
@ -133,7 +135,7 @@ describe('UserCard_FormUserPasswort', () => {
|
||||
await wrapper.findAll('input').at(1).setValue('Aa')
|
||||
await flushPromises()
|
||||
const feedbackArray = wrapper.findAll('div.invalid-feedback').at(1).findAll('span')
|
||||
expect(feedbackArray).toHaveLength(2)
|
||||
expect(feedbackArray).toHaveLength(3)
|
||||
expect(feedbackArray.at(0).text()).toBe('site.signup.one_number')
|
||||
})
|
||||
|
||||
@ -141,14 +143,22 @@ describe('UserCard_FormUserPasswort', () => {
|
||||
await wrapper.findAll('input').at(1).setValue('Aa1')
|
||||
await flushPromises()
|
||||
const feedbackArray = wrapper.findAll('div.invalid-feedback').at(1).findAll('span')
|
||||
expect(feedbackArray).toHaveLength(1)
|
||||
expect(feedbackArray).toHaveLength(2)
|
||||
expect(feedbackArray.at(0).text()).toBe('site.signup.minimum')
|
||||
})
|
||||
|
||||
it('removes all messages when all rules are fulfilled', async () => {
|
||||
it('removes the first five messages when a eight lowercase, uppercase and numeric characters are given', async () => {
|
||||
await wrapper.findAll('input').at(1).setValue('Aa123456')
|
||||
await flushPromises()
|
||||
const feedbackArray = wrapper.findAll('div.invalid-feedback').at(1).findAll('span')
|
||||
expect(feedbackArray).toHaveLength(1)
|
||||
expect(feedbackArray.at(0).text()).toBe('site.signup.special-char')
|
||||
})
|
||||
|
||||
it('removes all messages when a eight lowercase, uppercase and numeric characters are given', async () => {
|
||||
await wrapper.findAll('input').at(1).setValue('Aa123456_')
|
||||
await flushPromises()
|
||||
const feedbackArray = wrapper.findAll('div.invalid-feedback').at(1).findAll('span')
|
||||
expect(feedbackArray).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
@ -164,8 +174,8 @@ describe('UserCard_FormUserPasswort', () => {
|
||||
},
|
||||
})
|
||||
await form.findAll('input').at(0).setValue('1234')
|
||||
await form.findAll('input').at(1).setValue('Aa123456')
|
||||
await form.findAll('input').at(2).setValue('Aa123456')
|
||||
await form.findAll('input').at(1).setValue('Aa123456_')
|
||||
await form.findAll('input').at(2).setValue('Aa123456_')
|
||||
await form.trigger('submit')
|
||||
await flushPromises()
|
||||
})
|
||||
@ -176,7 +186,7 @@ describe('UserCard_FormUserPasswort', () => {
|
||||
variables: {
|
||||
email: 'user@example.org',
|
||||
password: '1234',
|
||||
passwordNew: 'Aa123456',
|
||||
passwordNew: 'Aa123456_',
|
||||
},
|
||||
}),
|
||||
)
|
||||
@ -197,8 +207,8 @@ describe('UserCard_FormUserPasswort', () => {
|
||||
message: 'error',
|
||||
})
|
||||
await form.findAll('input').at(0).setValue('1234')
|
||||
await form.findAll('input').at(1).setValue('Aa123456')
|
||||
await form.findAll('input').at(2).setValue('Aa123456')
|
||||
await form.findAll('input').at(1).setValue('Aa123456_')
|
||||
await form.findAll('input').at(2).setValue('Aa123456_')
|
||||
await form.trigger('submit')
|
||||
await flushPromises()
|
||||
})
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<b-card id="change_pwd" class="bg-transparent" style="background-color: #ebebeba3 !important">
|
||||
<b-card id="change_pwd" class="card-border-radius card-background-gray">
|
||||
<div>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a @click="showPassword ? (showPassword = !showPassword) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.change-password') }}</span>
|
||||
<span class="pointer mr-3">{{ $t('settings.password.change-password') }}</span>
|
||||
<b-icon v-if="showPassword" class="pointer ml-3" icon="pencil"></b-icon>
|
||||
<b-icon v-else icon="x-circle" class="pointer ml-3" variant="danger"></b-icon>
|
||||
</a>
|
||||
@ -42,7 +42,7 @@
|
||||
<script>
|
||||
import InputPassword from '../../../components/Inputs/InputPassword'
|
||||
import InputPasswordConfirmation from '../../../components/Inputs/InputPasswordConfirmation'
|
||||
import { updateUserInfos } from '../../../graphql/queries'
|
||||
import { updateUserInfos } from '../../../graphql/mutations'
|
||||
|
||||
export default {
|
||||
name: 'FormUserPasswort',
|
||||
@ -73,8 +73,8 @@ export default {
|
||||
},
|
||||
async onSubmit() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: updateUserInfos,
|
||||
.mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
email: this.$store.state.email,
|
||||
password: this.form.password,
|
||||
|
||||
@ -35,7 +35,7 @@ describe('UserCard_FormUsername', () => {
|
||||
error: toastErrorMock,
|
||||
},
|
||||
$apollo: {
|
||||
query: mockAPIcall,
|
||||
mutate: mockAPIcall,
|
||||
},
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ describe('UserCard_FormUsername', () => {
|
||||
})
|
||||
|
||||
it('toasts an success message', () => {
|
||||
expect(toastSuccessMock).toBeCalledWith('site.profil.user-data.change-success')
|
||||
expect(toastSuccessMock).toBeCalledWith('settings.name.change-success')
|
||||
})
|
||||
|
||||
it('has no edit button anymore', () => {
|
||||
|
||||
@ -67,7 +67,7 @@
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import { updateUserInfos } from '../../../graphql/queries'
|
||||
import { updateUserInfos } from '../../../graphql/mutations'
|
||||
|
||||
export default {
|
||||
name: 'FormUsername',
|
||||
@ -87,8 +87,8 @@ export default {
|
||||
},
|
||||
async onSubmit() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: updateUserInfos,
|
||||
.mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
email: this.$store.state.email,
|
||||
username: this.form.username,
|
||||
@ -98,7 +98,7 @@ export default {
|
||||
this.$store.commit('username', this.form.username)
|
||||
this.username = this.form.username
|
||||
this.showUsername = true
|
||||
this.$toasted.success(this.$t('site.profil.user-data.change-success'))
|
||||
this.$toasted.success(this.$t('settings.name.change-success'))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toasted.error(error.message)
|
||||
|
||||
@ -25,7 +25,7 @@ describe('UserCard_Language', () => {
|
||||
error: toastErrorMock,
|
||||
},
|
||||
$apollo: {
|
||||
query: mockAPIcall,
|
||||
mutate: mockAPIcall,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
<template>
|
||||
<b-card
|
||||
id="formuserlanguage"
|
||||
class="bg-transparent"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<b-card id="formuserlanguage" class="card-border-radius card-background-gray">
|
||||
<div>
|
||||
<b-row class="mb-4 text-right">
|
||||
<b-col class="text-right">
|
||||
<a @click="showLanguage ? (showLanguage = !showLanguage) : cancelEdit()">
|
||||
<span class="pointer mr-3">{{ $t('form.changeLanguage') }}</span>
|
||||
<span class="pointer mr-3">{{ $t('settings.language.changeLanguage') }}</span>
|
||||
<b-icon v-if="showLanguage" class="pointer ml-3" icon="pencil"></b-icon>
|
||||
<b-icon v-else icon="x-circle" class="pointer ml-3" variant="danger"></b-icon>
|
||||
</a>
|
||||
@ -23,13 +19,13 @@
|
||||
<b>{{ $t('language') }}</b>
|
||||
</small>
|
||||
</b-col>
|
||||
<b-col class="col-12">{{ $store.state.language }}</b-col>
|
||||
<b-col class="col-12">{{ $t(buildTagFromLanguageString()) }}</b-col>
|
||||
</b-row>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<div>
|
||||
<b-form @submit.stop.prevent="handleSubmit(onSubmit)">
|
||||
<b-form @submit.stop.prevent="onSubmit">
|
||||
<b-row class="mb-2">
|
||||
<b-col class="col-12">
|
||||
<small>
|
||||
@ -46,7 +42,6 @@
|
||||
<div class="text-right" ref="submitButton">
|
||||
<b-button
|
||||
:variant="loading ? 'default' : 'success'"
|
||||
@click="onSubmit"
|
||||
type="submit"
|
||||
class="mt-4"
|
||||
:disabled="loading"
|
||||
@ -62,8 +57,9 @@
|
||||
</b-card>
|
||||
</template>
|
||||
<script>
|
||||
import { localeChanged } from 'vee-validate'
|
||||
import LanguageSwitchSelect from '../../../components/LanguageSwitchSelect.vue'
|
||||
import { updateUserInfos } from '../../../graphql/queries'
|
||||
import { updateUserInfos } from '../../../graphql/mutations'
|
||||
|
||||
export default {
|
||||
name: 'FormUserLanguage',
|
||||
@ -87,22 +83,31 @@ export default {
|
||||
cancelEdit() {
|
||||
this.showLanguage = true
|
||||
},
|
||||
|
||||
async onSubmit() {
|
||||
this.$apollo
|
||||
.query({
|
||||
query: updateUserInfos,
|
||||
.mutate({
|
||||
mutation: updateUserInfos,
|
||||
variables: {
|
||||
language: this.$store.state.language,
|
||||
email: this.$store.state.email,
|
||||
locale: this.language,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.$store.commit('language', this.language)
|
||||
this.$i18n.locale = this.language
|
||||
localeChanged(this.language)
|
||||
this.cancelEdit()
|
||||
this.$toasted.success(this.$t('settings.language.success'))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.language = this.$store.state.language
|
||||
this.$toasted.error(error.message)
|
||||
})
|
||||
},
|
||||
|
||||
buildTagFromLanguageString() {
|
||||
return 'languages.' + this.$store.state.language
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -74,7 +74,7 @@ describe('UserCard_Newsletter', () => {
|
||||
})
|
||||
|
||||
it('toasts a success message', () => {
|
||||
expect(toastSuccessMock).toBeCalledWith('setting.newsletterFalse')
|
||||
expect(toastSuccessMock).toBeCalledWith('settings.newsletter.newsletterFalse')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
<template>
|
||||
<b-card
|
||||
id="formusernewsletter"
|
||||
class="bg-transparent"
|
||||
style="background-color: #ebebeba3 !important"
|
||||
>
|
||||
<b-card id="formusernewsletter" class="card-border-radius card-background-gray">
|
||||
<div>
|
||||
<b-row class="mb-3">
|
||||
<b-col class="mb-2 col-12">
|
||||
<small>
|
||||
<b>{{ $t('setting.newsletter') }}</b>
|
||||
<b>{{ $t('settings.newsletter.newsletter') }}</b>
|
||||
</small>
|
||||
</b-col>
|
||||
<b-col class="col-12">
|
||||
@ -19,7 +15,11 @@
|
||||
switch
|
||||
@change="onSubmit"
|
||||
>
|
||||
{{ newsletterState ? $t('setting.newsletterTrue') : $t('setting.newsletterFalse') }}
|
||||
{{
|
||||
newsletterState
|
||||
? $t('settings.newsletter.newsletterTrue')
|
||||
: $t('settings.newsletter.newsletterFalse')
|
||||
}}
|
||||
</b-form-checkbox>
|
||||
</b-col>
|
||||
</b-row>
|
||||
@ -50,8 +50,8 @@ export default {
|
||||
this.$store.commit('newsletterState', this.newsletterState)
|
||||
this.$toasted.success(
|
||||
this.newsletterState
|
||||
? this.$t('setting.newsletterTrue')
|
||||
: this.$t('setting.newsletterFalse'),
|
||||
? this.$t('settings.newsletter.newsletterTrue')
|
||||
: this.$t('settings.newsletter.newsletterFalse'),
|
||||
)
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@ -35,4 +35,16 @@ export default {
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
<style>
|
||||
.card-border-radius {
|
||||
border-radius: 0px 5px 5px 0px !important;
|
||||
}
|
||||
@media screen and (max-width: 1235px) {
|
||||
.card-border-radius {
|
||||
border-radius: 0px !important;
|
||||
}
|
||||
}
|
||||
.card-background-gray {
|
||||
background-color: #ebebeba3 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { createLocalVue } from '@vue/test-utils'
|
||||
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
|
||||
import Vuex from 'vuex'
|
||||
import Vue from 'vue'
|
||||
|
||||
import { ValidationProvider, ValidationObserver, extend } from 'vee-validate'
|
||||
import * as rules from 'vee-validate/dist/rules'
|
||||
@ -47,3 +48,8 @@ global.localVue.component('validation-provider', ValidationProvider)
|
||||
global.localVue.component('validation-observer', ValidationObserver)
|
||||
global.localVue.directive('click-outside', clickOutside)
|
||||
global.localVue.directive('focus', focus)
|
||||
|
||||
// throw errors for vue warnings to force the programmers to take care about warnings
|
||||
Vue.config.warnHandler = (w) => {
|
||||
throw new Error(w)
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
const path = require('path')
|
||||
const dotenv = require('dotenv-webpack')
|
||||
const webpack = require('webpack')
|
||||
const Dotenv = require('dotenv-webpack')
|
||||
|
||||
// vue.config.js
|
||||
module.exports = {
|
||||
@ -23,8 +24,17 @@ module.exports = {
|
||||
assets: path.join(__dirname, 'src/assets'),
|
||||
},
|
||||
},
|
||||
// eslint-disable-next-line new-cap
|
||||
plugins: [new dotenv()],
|
||||
plugins: [
|
||||
new Dotenv(),
|
||||
new webpack.DefinePlugin({
|
||||
// Those are Environment Variables transmitted via Docker
|
||||
// 'process.env.DOCKER_WORKDIR': JSON.stringify(process.env.DOCKER_WORKDIR),
|
||||
// 'process.env.BUILD_DATE': JSON.stringify(process.env.BUILD_DATE),
|
||||
// 'process.env.BUILD_VERSION': JSON.stringify(process.env.BUILD_VERSION),
|
||||
'process.env.BUILD_COMMIT': JSON.stringify(process.env.BUILD_COMMIT),
|
||||
// 'process.env.PORT': JSON.stringify(process.env.PORT),
|
||||
}),
|
||||
],
|
||||
},
|
||||
css: {
|
||||
// Enable CSS source maps.
|
||||
|
||||
Binary file not shown.
@ -7,8 +7,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-06-21 13:37+0200\n"
|
||||
"PO-Revision-Date: 2021-06-21 13:38+0200\n"
|
||||
"POT-Creation-Date: 2021-09-23 17:56+0200\n"
|
||||
"PO-Revision-Date: 2021-09-27 13:31+0200\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: de_DE\n"
|
||||
@ -455,11 +455,10 @@ msgstr "Gradido: Passwort zurücksetzen"
|
||||
#: src/cpp/SingletonManager/SessionManager.cpp:604
|
||||
msgid ""
|
||||
"Please enter a valid password with at least 8 characters, upper and lower "
|
||||
"case letters, at least one number and one special character (@$!%*?&+-_)!"
|
||||
"case letters, at least one number and one special character!"
|
||||
msgstr ""
|
||||
"Bitte gebe ein gültiges Password ein mit mindestens 8 Zeichen, Groß- und "
|
||||
"Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen (@$!%*?&+-_) "
|
||||
"ein!"
|
||||
"Kleinbuchstaben, mindestens einer Zahl und einem Sonderzeichen!"
|
||||
|
||||
#: src/cpp/SingletonManager/SessionManager.cpp:610
|
||||
msgid "Your password is to short!"
|
||||
@ -478,8 +477,8 @@ msgid "Your password does not contain any number!"
|
||||
msgstr "Dein Passwort enthält keine Zahlen!"
|
||||
|
||||
#: src/cpp/SingletonManager/SessionManager.cpp:630
|
||||
msgid "Your password does not contain special characters (@$!%*?&+-)!"
|
||||
msgstr "Dein Passwort enthält keine Sonderzeichen (@$!%*?&+-)!"
|
||||
msgid "Your password does not contain special characters!"
|
||||
msgstr "Dein Passwort enthält keine Sonderzeichen!"
|
||||
|
||||
#~ msgid "Account"
|
||||
#~ msgstr "Konto"
|
||||
|
||||
@ -57,6 +57,7 @@ namespace ServerConfig {
|
||||
int g_FakeLoginSleepTime = 820;
|
||||
std::string g_versionString = "";
|
||||
bool g_disableEmail = false;
|
||||
bool g_resendUnfinishedTransactionOnStart = false;
|
||||
ServerSetupType g_ServerSetupType = SERVER_TYPE_PRODUCTION;
|
||||
std::string g_devDefaultGroup = "";
|
||||
std::string g_gRPCRelayServerFullURL;
|
||||
@ -259,23 +260,7 @@ namespace ServerConfig {
|
||||
g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_ALLOW_ALL_PASSWORDS);
|
||||
}
|
||||
|
||||
|
||||
g_gRPCRelayServerFullURL = cfg.getString("grpc.server", "");
|
||||
|
||||
// unsecure flags
|
||||
//g_AllowUnsecureFlags
|
||||
if (cfg.getInt("unsecure.allow_passwort_via_json_request", 0) == 1) {
|
||||
g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_PASSWORD_REQUESTS);
|
||||
}
|
||||
if (cfg.getInt("unsecure.allow_auto_sign_transactions", 0) == 1) {
|
||||
g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_AUTO_SIGN_TRANSACTIONS);
|
||||
}
|
||||
if (cfg.getInt("unsecure.allow_cors_all", 0) == 1) {
|
||||
g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_CORS_ALL);
|
||||
}
|
||||
if (cfg.getInt("unsecure.allow_all_passwords", 0) == 1) {
|
||||
g_AllowUnsecureFlags = (AllowUnsecure)(g_AllowUnsecureFlags | UNSECURE_ALLOW_ALL_PASSWORDS);
|
||||
}
|
||||
g_resendUnfinishedTransactionOnStart = cfg.getBool("dev.resend_unfinished_transactions_on_start", false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -73,6 +73,7 @@ namespace ServerConfig {
|
||||
extern int g_FakeLoginSleepTime;
|
||||
extern std::string g_versionString;
|
||||
extern bool g_disableEmail;
|
||||
extern bool g_resendUnfinishedTransactionOnStart;
|
||||
extern ServerSetupType g_ServerSetupType;
|
||||
extern std::string g_devDefaultGroup;
|
||||
extern std::string g_gRPCRelayServerFullURL;
|
||||
|
||||
@ -46,22 +46,22 @@ bool SessionManager::init()
|
||||
case VALIDATE_NAME: mValidations[i] = new Poco::RegularExpression("^[^<>&;]{2,}$"); break;
|
||||
case VALIDATE_USERNAME: mValidations[i] = new Poco::RegularExpression("^[a-zA-Z][a-zA-Z0-9_-]*$"); break;
|
||||
case VALIDATE_EMAIL: mValidations[i] = new Poco::RegularExpression("^[a-zA-Z0-9.!#$%&?*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"); break;
|
||||
case VALIDATE_PASSWORD: mValidations[i] = new Poco::RegularExpression("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[@$!%*?&+-_])[A-Za-z0-9@$!%*?&+-_]{8,}$"); break;
|
||||
case VALIDATE_PASSWORD: mValidations[i] = new Poco::RegularExpression("^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^a-zA-Z0-9 \\t\\n\\r]).{8,}$"); break;
|
||||
case VALIDATE_PASSPHRASE: mValidations[i] = new Poco::RegularExpression("^(?:[a-z]* ){23}[a-z]*\s*$"); break;
|
||||
case VALIDATE_GROUP_ALIAS: mValidations[i] = new Poco::RegularExpression("^[a-z0-9-]{3,120}"); break;
|
||||
case VALIDATE_HEDERA_ID: mValidations[i] = new Poco::RegularExpression("^[0-9]*\.[0-9]*\.[0-9]\.$"); break;
|
||||
case VALIDATE_HAS_NUMBER: mValidations[i] = new Poco::RegularExpression(".*[0-9].*"); break;
|
||||
case VALIDATE_HAS_NUMBER: mValidations[i] = new Poco::RegularExpression("[0-9]"); break;
|
||||
case VALIDATE_ONLY_INTEGER: mValidations[i] = new Poco::RegularExpression("^[0-9]*$"); break;
|
||||
case VALIDATE_ONLY_DECIMAL: mValidations[i] = new Poco::RegularExpression("^[0-9]*(\.|,)[0-9]*$"); break;
|
||||
case VALIDATE_ONLY_HEX: mValidations[i] = new Poco::RegularExpression("^(0x)?[a-fA-F0-9]*$"); break;
|
||||
//case VALIDATE_ONLY_URL: mValidations[i] = new Poco::RegularExpression("^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}$"); break;
|
||||
case VALIDATE_ONLY_URL: mValidations[i] = new Poco::RegularExpression("^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\/?"); break;
|
||||
case VALIDATE_HAS_SPECIAL_CHARACTER: mValidations[i] = new Poco::RegularExpression(".*[@$!%*?&+-].*"); break;
|
||||
case VALIDATE_HAS_SPECIAL_CHARACTER: mValidations[i] = new Poco::RegularExpression("[^a-zA-Z0-9 \\t\\n\\r]"); break;
|
||||
case VALIDATE_HAS_UPPERCASE_LETTER:
|
||||
mValidations[i] = new Poco::RegularExpression(".*[A-Z].*");
|
||||
mValidations[i] = new Poco::RegularExpression("[A-Z]");
|
||||
ServerConfig::g_ServerKeySeed->put(i, DRRandom::r64());
|
||||
break;
|
||||
case VALIDATE_HAS_LOWERCASE_LETTER: mValidations[i] = new Poco::RegularExpression(".*[a-z].*"); break;
|
||||
case VALIDATE_HAS_LOWERCASE_LETTER: mValidations[i] = new Poco::RegularExpression("[a-z]"); break;
|
||||
default: printf("[SessionManager::%s] unknown validation type\n", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
@ -601,7 +601,7 @@ bool SessionManager::checkPwdValidation(const std::string& pwd, NotificationList
|
||||
if (!isValid(pwd, VALIDATE_PASSWORD)) {
|
||||
errorReciver->addError(new Error(
|
||||
lang->gettext("Password"),
|
||||
lang->gettext("Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character (@$!%*?&+-_)!")));
|
||||
lang->gettext("Please enter a valid password with at least 8 characters, upper and lower case letters, at least one number and one special character!")));
|
||||
|
||||
// @$!%*?&+-
|
||||
if (pwd.size() < 8) {
|
||||
@ -627,7 +627,7 @@ bool SessionManager::checkPwdValidation(const std::string& pwd, NotificationList
|
||||
else if (!isValid(pwd, VALIDATE_HAS_SPECIAL_CHARACTER)) {
|
||||
errorReciver->addError(new Error(
|
||||
lang->gettext("Password"),
|
||||
lang->gettext("Your password does not contain special characters (@$!%*?&+-)!")));
|
||||
lang->gettext("Your password does not contain special characters!")));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@ -204,7 +204,7 @@ namespace model {
|
||||
}
|
||||
}
|
||||
// try not finished but signed transactions again
|
||||
if (!finished) {
|
||||
if (!finished && ServerConfig::g_resendUnfinishedTransactionOnStart) {
|
||||
transaction->ifEnoughSignsProceed(nullptr);
|
||||
}
|
||||
|
||||
|
||||
@ -85,7 +85,7 @@ enum PageState {
|
||||
<form method="POST">
|
||||
<p>
|
||||
Bitte denke dir ein sicheres Passwort aus, das mindestens 8 Zeichen lang ist, einen Klein- und einen Großbuchstaben enthält,
|
||||
eine Zahl und eines der folgenden Sonderzeichen: @$!%*?&+-
|
||||
eine Zahl und ein Sonderzeichen.
|
||||
</p>
|
||||
<label class="form-label" for="register-password">Passwort</label>
|
||||
<input class="form-control" id="register-password" type="password" name="register-password"/>
|
||||
|
||||
@ -7,7 +7,22 @@ WORKDIR /go/src/github.com/skeema/skeema
|
||||
RUN go install github.com/skeema/skeema@v1.5.3
|
||||
|
||||
#########################################################################################################
|
||||
# Run skeema
|
||||
# Run skeema for dev (dynamic)
|
||||
#########################################################################################################
|
||||
FROM skeema_build as skeema_dev_run
|
||||
|
||||
ENV DOCKER_WORKDIR="/skeema"
|
||||
|
||||
RUN mkdir -p ${DOCKER_WORKDIR}
|
||||
WORKDIR ${DOCKER_WORKDIR}
|
||||
|
||||
COPY ./skeema/.skeema .
|
||||
COPY ./mariadb/.skeema.login .
|
||||
|
||||
CMD cp .skeema.login ./gradido_login/.skeema && skeema push --allow-unsafe && rm ./gradido_login/.skeema
|
||||
|
||||
#########################################################################################################
|
||||
# Run skeema
|
||||
#########################################################################################################
|
||||
FROM skeema_build as skeema_run
|
||||
|
||||
@ -20,4 +35,5 @@ COPY ./skeema/.skeema .
|
||||
COPY ./login_server/skeema/ .
|
||||
COPY ./mariadb/.skeema.login ./gradido_login/.skeema
|
||||
|
||||
CMD skeema push --allow-unsafe
|
||||
CMD skeema push --allow-unsafe
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user