Merge branch 'master' into refactor_remove_community_server

This commit is contained in:
Hannes Heine 2022-02-08 15:45:43 +01:00 committed by GitHub
commit 712a92b8ba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 222 additions and 342 deletions

5
.gitmodules vendored
View File

@ -1,4 +1 @@
[submodule "gn"]
path = gn
url = https://github.com/gradido/gn.git
branch = master

3
admin/.gitignore vendored
View File

@ -2,7 +2,8 @@ node_modules/
dist/
.cache/
.env
/.env
/.env.bak
# coverage folder
coverage/

1
backend/.gitignore vendored
View File

@ -1,5 +1,6 @@
/node_modules/
/.env
/.env.bak
/build/
package-json.lock
coverage

View File

@ -7,5 +7,4 @@ export const INALIENABLE_RIGHTS = [
RIGHTS.CREATE_USER,
RIGHTS.SEND_RESET_PASSWORD_EMAIL,
RIGHTS.SET_PASSWORD,
RIGHTS.CHECK_USERNAME,
]

View File

@ -17,7 +17,6 @@ export enum RIGHTS {
SEND_RESET_PASSWORD_EMAIL = 'SEND_RESET_PASSWORD_EMAIL',
SET_PASSWORD = 'SET_PASSWORD',
UPDATE_USER_INFOS = 'UPDATE_USER_INFOS',
CHECK_USERNAME = 'CHECK_USERNAME',
HAS_ELOPAGE = 'HAS_ELOPAGE',
// Admin
SEARCH_USERS = 'SEARCH_USERS',

View File

@ -4,7 +4,7 @@ import dotenv from 'dotenv'
dotenv.config()
const constants = {
DB_VERSION: '0019-replace_login_user_id_with_state_user_id',
DB_VERSION: '0020-rename_and_clean_state_users',
}
const server = {

View File

@ -1,7 +0,0 @@
import { ArgsType, Field } from 'type-graphql'
@ArgsType()
export default class CheckUsernameArgs {
@Field(() => String)
username: string
}

View File

@ -8,12 +8,6 @@ export default class UpdateUserInfosArgs {
@Field({ nullable: true })
lastName?: string
@Field({ nullable: true })
description?: string
@Field({ nullable: true })
username?: string
@Field({ nullable: true })
language?: string

View File

@ -5,10 +5,10 @@ import { AuthChecker } from 'type-graphql'
import { decode, encode } from '../../auth/JWT'
import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN } from '../../auth/ROLES'
import { RIGHTS } from '../../auth/RIGHTS'
import { ServerUserRepository } from '../../typeorm/repository/ServerUser'
import { getCustomRepository } from '@dbTools/typeorm'
import { UserRepository } from '../../typeorm/repository/User'
import { INALIENABLE_RIGHTS } from '../../auth/INALIENABLE_RIGHTS'
import { ServerUser } from '@entity/ServerUser'
const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
context.role = ROLE_UNAUTHORIZED // unauthorized user
@ -38,8 +38,7 @@ const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
// TODO this implementation is bullshit - two database queries cause our user identifiers are not aligned and vary between email, id and pubKey
const userRepository = await getCustomRepository(UserRepository)
const user = await userRepository.findByPubkeyHex(context.pubKey)
const serverUserRepository = await getCustomRepository(ServerUserRepository)
const countServerUsers = await serverUserRepository.count({ email: user.email })
const countServerUsers = await ServerUser.count({ email: user.email })
context.role = countServerUsers > 0 ? ROLE_ADMIN : ROLE_USER
context.setHeaders.push({ key: 'token', value: encode(decoded.pubKey) })

View File

@ -16,8 +16,6 @@ export class User {
this.email = json.email
this.firstName = json.first_name
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
@ -37,12 +35,6 @@ export class User {
@Field(() => String)
lastName: string
@Field(() => String, { nullable: true })
username?: string
@Field(() => String, { nullable: true })
description?: string
@Field(() => String)
pubkey: string
/*
@ -55,9 +47,6 @@ export class User {
@Field(() =>>> Boolean)
emailChecked: boolean
@Field(() => Boolean)
passphraseShown: boolean
*/
@Field(() => String)
@ -68,10 +57,6 @@ export class User {
disabled: boolean
*/
/* I suggest to have a group as type here
@Field(() => ID)
groupId: number
*/
// what is publisherId?
@Field(() => Int, { nullable: true })
publisherId?: number

View File

@ -9,7 +9,6 @@ import { CreatePendingCreations } from '../model/CreatePendingCreations'
import { UpdatePendingCreation } from '../model/UpdatePendingCreation'
import { RIGHTS } from '../../auth/RIGHTS'
import { TransactionRepository } from '../../typeorm/repository/Transaction'
import { TransactionCreationRepository } from '../../typeorm/repository/TransactionCreation'
import { UserRepository } from '../../typeorm/repository/User'
import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs'
import UpdatePendingCreationArgs from '../arg/UpdatePendingCreationArgs'
@ -198,13 +197,12 @@ export class AdminResolver {
transaction = await transactionRepository.save(transaction)
if (!transaction) throw new Error('Could not create transaction')
const transactionCreationRepository = getCustomRepository(TransactionCreationRepository)
let transactionCreation = new TransactionCreation()
transactionCreation.transactionId = transaction.id
transactionCreation.userId = pendingCreation.userId
transactionCreation.amount = parseInt(pendingCreation.amount.toString())
transactionCreation.targetDate = pendingCreation.date
transactionCreation = await transactionCreationRepository.save(transactionCreation)
transactionCreation = await TransactionCreation.save(transactionCreation)
if (!transactionCreation) throw new Error('Could not create transactionCreation')
const userTransactionRepository = getCustomRepository(UserTransactionRepository)
@ -256,9 +254,7 @@ async function getUserCreations(id: number): Promise<number[]> {
const lastMonthNumber = moment().subtract(1, 'month').format('M')
const currentMonthNumber = moment().format('M')
const transactionCreationRepository = getCustomRepository(TransactionCreationRepository)
const createdAmountsQuery = await transactionCreationRepository
.createQueryBuilder('transaction_creations')
const createdAmountsQuery = await TransactionCreation.createQueryBuilder('transaction_creations')
.select('MONTH(transaction_creations.target_date)', 'target_month')
.addSelect('SUM(transaction_creations.amount)', 'sum')
.where('transaction_creations.state_user_id = :id', { id })

View File

@ -8,7 +8,6 @@ import CONFIG from '../../config'
import { User } from '../model/User'
import { User as DbUser } from '@entity/User'
import { encode } from '../../auth/JWT'
import CheckUsernameArgs from '../arg/CheckUsernameArgs'
import CreateUserArgs from '../arg/CreateUserArgs'
import UnsecureLoginArgs from '../arg/UnsecureLoginArgs'
import UpdateUserInfosArgs from '../arg/UpdateUserInfosArgs'
@ -19,11 +18,11 @@ import { UserRepository } from '../../typeorm/repository/User'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
import { sendResetPasswordEmail } from '../../mailer/sendResetPasswordEmail'
import { sendAccountActivationEmail } from '../../mailer/sendAccountActivationEmail'
import { LoginElopageBuysRepository } from '../../typeorm/repository/LoginElopageBuys'
import { klicktippSignIn } from '../../apis/KlicktippController'
import { RIGHTS } from '../../auth/RIGHTS'
import { ServerUserRepository } from '../../typeorm/repository/ServerUser'
import { ROLE_ADMIN } from '../../auth/ROLES'
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
import { ServerUser } from '@entity/ServerUser'
const EMAIL_OPT_IN_RESET_PASSWORD = 2
const EMAIL_OPT_IN_REGISTER = 1
@ -224,8 +223,6 @@ export class UserResolver {
user.email = userEntity.email
user.firstName = userEntity.firstName
user.lastName = userEntity.lastName
user.username = userEntity.username
user.description = userEntity.description
user.pubkey = userEntity.pubKey.toString('hex')
user.language = userEntity.language
@ -278,8 +275,6 @@ export class UserResolver {
user.email = email
user.firstName = dbUser.firstName
user.lastName = dbUser.lastName
user.username = dbUser.username
user.description = dbUser.description
user.pubkey = dbUser.pubKey.toString('hex')
user.language = dbUser.language
@ -303,8 +298,7 @@ export class UserResolver {
user.coinanimation = coinanimation
// context.role is not set to the actual role yet on login
const serverUserRepository = await getCustomRepository(ServerUserRepository)
const countServerUsers = await serverUserRepository.count({ email: user.email })
const countServerUsers = await ServerUser.count({ email: user.email })
user.isAdmin = countServerUsers > 0
context.setHeaders.push({
@ -339,13 +333,6 @@ export class UserResolver {
language = DEFAULT_LANGUAGE
}
// Validate username
// TODO: never true
const username = ''
if (username.length > 3 && !this.checkUsername({ username })) {
throw new Error('Username already in use')
}
// Validate email unique
// TODO: i can register an email in upper/lower case twice
const userRepository = getCustomRepository(UserRepository)
@ -361,13 +348,10 @@ export class UserResolver {
// const encryptedPrivkey = SecretKeyCryptographyEncrypt(keyPair[1], passwordHash[1])
const emailHash = getEmailHash(email)
// Table: state_users
const dbUser = new DbUser()
dbUser.email = email
dbUser.firstName = firstName
dbUser.lastName = lastName
dbUser.username = username
dbUser.description = ''
dbUser.emailHash = emailHash
dbUser.language = language
dbUser.publisherId = publisherId
@ -591,8 +575,6 @@ export class UserResolver {
{
firstName,
lastName,
description,
username,
language,
publisherId,
password,
@ -604,17 +586,6 @@ export class UserResolver {
const userRepository = getCustomRepository(UserRepository)
const userEntity = await userRepository.findByPubkeyHex(context.pubKey)
if (username) {
throw new Error('change username currently not supported!')
// TODO: this error was thrown on login_server whenever you tried to change the username
// to anything except "" which is an exception to the rules below. Those were defined
// aswell, even tho never used.
// ^[a-zA-Z][a-zA-Z0-9_-]*$
// username must start with [a-z] or [A-Z] and than can contain also [0-9], - and _
// username already used
// userEntity.username = username
}
if (firstName) {
userEntity.firstName = firstName
}
@ -623,10 +594,6 @@ export class UserResolver {
userEntity.lastName = lastName
}
if (description) {
userEntity.description = description
}
if (language) {
if (!isLanguage(language)) {
throw new Error(`"${language}" isn't a valid language`)
@ -685,30 +652,6 @@ export class UserResolver {
return true
}
@Authorized([RIGHTS.CHECK_USERNAME])
@Query(() => Boolean)
async checkUsername(@Args() { username }: CheckUsernameArgs): Promise<boolean> {
// Username empty?
if (username === '') {
throw new Error('Username must be set.')
}
// Do we fullfil the minimum character length?
const MIN_CHARACTERS_USERNAME = 2
if (username.length < MIN_CHARACTERS_USERNAME) {
throw new Error(`Username must be at minimum ${MIN_CHARACTERS_USERNAME} characters long.`)
}
const usersFound = await DbUser.count({ username })
// Username already present?
if (usersFound !== 0) {
throw new Error(`Username "${username}" already taken.`)
}
return true
}
@Authorized([RIGHTS.HAS_ELOPAGE])
@Query(() => Boolean)
async hasElopage(@Ctx() context: any): Promise<boolean> {
@ -718,8 +661,7 @@ export class UserResolver {
return false
}
const loginElopageBuysRepository = getCustomRepository(LoginElopageBuysRepository)
const elopageBuyCount = await loginElopageBuysRepository.count({ payerEmail: userEntity.email })
const elopageBuyCount = await LoginElopageBuys.count({ payerEmail: userEntity.email })
return elopageBuyCount > 0
}
}

View File

@ -1,5 +0,0 @@
import { EntityRepository, Repository } from '@dbTools/typeorm'
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
@EntityRepository(LoginElopageBuys)
export class LoginElopageBuysRepository extends Repository<LoginElopageBuys> {}

View File

@ -1,5 +0,0 @@
import { EntityRepository, Repository } from '@dbTools/typeorm'
import { LoginEmailOptIn } from '@entity/LoginEmailOptIn'
@EntityRepository(LoginEmailOptIn)
export class LoginEmailOptInRepository extends Repository<LoginEmailOptIn> {}

View File

@ -1,5 +0,0 @@
import { EntityRepository, Repository } from '@dbTools/typeorm'
import { ServerUser } from '@entity/ServerUser'
@EntityRepository(ServerUser)
export class ServerUserRepository extends Repository<ServerUser> {}

View File

@ -1,5 +0,0 @@
import { EntityRepository, Repository } from '@dbTools/typeorm'
import { TransactionCreation } from '@entity/TransactionCreation'
@EntityRepository(TransactionCreation)
export class TransactionCreationRepository extends Repository<TransactionCreation> {}

View File

@ -28,16 +28,13 @@
*/
import { LoginElopageBuys } from '@entity/LoginElopageBuys'
import { getCustomRepository } from '@dbTools/typeorm'
import { UserResolver } from '../graphql/resolver/UserResolver'
import { LoginElopageBuysRepository } from '../typeorm/repository/LoginElopageBuys'
import { User as dbUser } from '@entity/User'
export const elopageWebhook = async (req: any, res: any): Promise<void> => {
// eslint-disable-next-line no-console
console.log('Elopage Hook received', req.body)
res.status(200).end() // Responding is important
const loginElopageBuyRepository = await getCustomRepository(LoginElopageBuysRepository)
const loginElopageBuy = new LoginElopageBuys()
const {
@ -82,7 +79,7 @@ export const elopageWebhook = async (req: any, res: any): Promise<void> => {
}
// Save the hook data
await loginElopageBuyRepository.save(loginElopageBuy)
await LoginElopageBuys.save(loginElopageBuy)
// create user for certain products
/*

View File

@ -1,20 +0,0 @@
worker_count = 2
io_worker_count = 1
data_root_folder = /opt/instance/.gradido
hedera_mirror_endpoint = hcs.testnet.mirrornode.hedera.com:5600
sibling_node_file = /opt/instance/.gradido/sibling_nodes.txt
#group_requests_endpoint = 0.0.0.0:13701
#record_requests_endpoint = 0.0.0.0:13702
#manage_network_requests_endpoint = 0.0.0.0:13703
grpc_endpoint = 0.0.0.0:13701
json_rpc_port = 13702
# larger value, larger batch, less concurrency
blockchain_append_batch_size = 1000
#blochchain_init_batch_size = 1000
#block_record_outbound_batch_size = 100
general_batch_size = 1000
group_register_topic_id = 0.0.79574
topic_reset_allowed = 1

3
database/.gitignore vendored
View File

@ -8,7 +8,8 @@ yarn-error.log*
test/unit/coverage
package-lock.json
.env
/.env
/.env.bak
.env.development.local
.env.production.local

View File

@ -0,0 +1,68 @@
import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm'
import { UserSetting } from '../UserSetting'
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
export class User extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true })
pubKey: Buffer
@Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true })
privKey: Buffer
@Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' })
email: string
@Column({
name: 'first_name',
length: 255,
nullable: true,
default: null,
collation: 'utf8mb4_unicode_ci',
})
firstName: string
@Column({
name: 'last_name',
length: 255,
nullable: true,
default: null,
collation: 'utf8mb4_unicode_ci',
})
lastName: string
@Column({ type: 'bool', default: false })
disabled: boolean
@Column({ type: 'bigint', default: 0, unsigned: true })
password: BigInt
@Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true })
emailHash: Buffer
@Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false })
createdAt: Date
@Column({ name: 'email_checked', type: 'bool', nullable: false, default: false })
emailChecked: boolean
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
language: string
@Column({ name: 'publisher_id', default: 0 })
publisherId: number
@Column({
type: 'text',
name: 'passphrase',
collation: 'utf8mb4_unicode_ci',
nullable: true,
default: null,
})
passphrase: string
@OneToMany(() => UserSetting, (userSetting) => userSetting.user)
settings: UserSetting[]
}

View File

@ -1 +1 @@
export { User } from './0019-replace_login_user_id_with_state_user_id/User'
export { User } from './0020-rename_and_clean_state_users/User'

View File

@ -0,0 +1,39 @@
/* MIGRATION TO CLEAN UP AND RENAME STATE_USERS
*
* This migration renames 'state_users` to `users`
* and removes columns with no meaningful value
*/
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
// rename `state_users` table to `users`
await queryFn('RENAME TABLE `state_users` TO `users`;')
// Remove the column `index_id` from `users`, it only contains 0 as value
await queryFn('ALTER TABLE `users` DROP COLUMN `index_id`;')
// Remove the column `username` from `users`, it contains only '' or NULL
await queryFn('ALTER TABLE `users` DROP COLUMN `username`;')
// Remove the column `description` from `users`, it contains only '' or NULL
await queryFn('ALTER TABLE `users` DROP COLUMN `description`;')
// Remove the column `passphrase_shown` from `users`, it contains only 0 as value
await queryFn('ALTER TABLE `users` DROP COLUMN `passphrase_shown`;')
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(
'ALTER TABLE `users` ADD COLUMN `passphrase_shown` tinyint(4) NOT NULL DEFAULT 0 AFTER `email_checked`;',
)
await queryFn(
"ALTER TABLE `users` ADD COLUMN `description` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT '' AFTER `disabled`;",
)
await queryFn(
'ALTER TABLE `users` ADD COLUMN `username` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL AFTER `last_name`;',
)
await queryFn(
'ALTER TABLE `users` ADD COLUMN `index_id` smallint(6) NOT NULL DEFAULT 0 AFTER `id`;',
)
await queryFn('RENAME TABLE `users` TO `state_users`;')
}

View File

@ -12,17 +12,13 @@ define(User, (faker: typeof Faker, context?: UserContext) => {
user.email = context.email ? context.email : faker.internet.email()
user.firstName = context.firstName ? context.firstName : faker.name.firstName()
user.lastName = context.lastName ? context.lastName : faker.name.lastName()
user.username = context.username ? context.username : faker.internet.userName()
user.disabled = context.disabled ? context.disabled : false
user.indexId = 0
user.description = context.description ? context.description : faker.random.words(4)
// TODO Create real password and keys/hash
user.password = context.password ? context.password : BigInt(0)
user.privKey = context.privKey ? context.privKey : randomBytes(80)
user.emailHash = context.emailHash ? context.emailHash : randomBytes(32)
user.createdAt = context.createdAt ? context.createdAt : faker.date.recent()
user.emailChecked = context.emailChecked === undefined ? false : context.emailChecked
user.passphraseShown = context.passphraseShown ? context.passphraseShown : false
user.language = context.language ? context.language : 'en'
user.publisherId = context.publisherId ? context.publisherId : 0
user.passphrase = context.passphrase ? context.passphrase : faker.random.words(24)

View File

@ -3,15 +3,12 @@ export interface UserContext {
email?: string
firstName?: string
lastName?: string
username?: string
disabled?: boolean
description?: string
password?: BigInt
privKey?: Buffer
emailHash?: Buffer
createdAt?: Date
emailChecked?: boolean
passphraseShown?: boolean
language?: string
publisherId?: number
passphrase?: string

View File

@ -3,15 +3,12 @@ export interface UserInterface {
email?: string
firstName?: string
lastName?: string
username?: string
description?: string
password?: BigInt
pubKey?: Buffer
privKey?: Buffer
emailHash?: Buffer
createdAt?: Date
emailChecked?: boolean
passphraseShown?: boolean
language?: string
disabled?: boolean
groupId?: number

View File

@ -42,15 +42,12 @@ const createUserContext = (context: UserInterface): UserContext => {
email: context.email,
firstName: context.firstName,
lastName: context.lastName,
username: context.username,
disabled: context.disabled,
description: context.description,
password: context.password,
privKey: context.privKey,
emailHash: context.emailHash,
createdAt: context.createdAt,
emailChecked: context.emailChecked,
passphraseShown: context.passphraseShown,
language: context.language,
publisherId: context.publisherId,
}
@ -59,7 +56,6 @@ const createUserContext = (context: UserInterface): UserContext => {
const createServerUserContext = (context: UserInterface): ServerUserContext => {
return {
role: context.role,
username: context.username,
password: context.serverUserPassword,
email: context.email,
activated: context.activated,

View File

@ -3,7 +3,7 @@ export const bibiBloxberg = {
firstName: 'Bibi',
lastName: 'Bloxberg',
username: 'bibi',
description: 'Hex Hex',
// description: 'Hex Hex',
password: BigInt('12825419584724616625'),
pubKey: Buffer.from('42de7e4754625b730018c3b4ea745a4d043d9d867af352d0f08871793dfa6743', 'hex'),
privKey: Buffer.from(
@ -13,7 +13,6 @@ export const bibiBloxberg = {
emailHash: Buffer.from('38a0d8c8658a5681cc1180c5d9e2b2a18e4f611db8ab3ca61de4aa91ae94219b', 'hex'),
createdAt: new Date('2021-11-26T11:32:16'),
emailChecked: true,
passphraseShown: false,
language: 'de',
disabled: false,
groupId: 1,

View File

@ -3,7 +3,7 @@ export const bobBaumeister = {
firstName: 'Bob',
lastName: 'der Baumeister',
username: 'bob',
description: 'Können wir das schaffen? Ja, wir schaffen das!',
// description: 'Können wir das schaffen? Ja, wir schaffen das!',
password: BigInt('3296644341468822636'),
pubKey: Buffer.from('a509d9a146374fc975e3677db801ae8a4a83bff9dea96da64053ff6de6b2dd7e', 'hex'),
privKey: Buffer.from(
@ -13,7 +13,6 @@ export const bobBaumeister = {
emailHash: Buffer.from('4b8ce4e175587aaf33da19e272719da1a547daff557820191fab0c65c5a3b7f1', 'hex'),
createdAt: new Date('2021-11-26T11:36:31'),
emailChecked: true,
passphraseShown: false,
language: 'de',
disabled: false,
groupId: 1,

View File

@ -3,13 +3,12 @@ export const garrickOllivander = {
firstName: 'Garrick',
lastName: 'Ollivander',
username: 'garrick',
description: `Curious ... curious ...
Renowned wandmaker Mr Ollivander owns the wand shop Ollivanders: Makers of Fine Wands Since 382 BC in Diagon Alley. His shop is widely considered the best place to purchase a wand.`,
// description: `Curious ... curious ...
// Renowned wandmaker Mr Ollivander owns the wand shop Ollivanders: Makers of Fine Wands Since 382 BC in Diagon Alley. His shop is widely considered the best place to purchase a wand.`,
password: BigInt('0'),
emailHash: Buffer.from('91e358000e908146342789979d62a7255b2b88a71dad0c6a10e32af44be57886', 'hex'),
createdAt: new Date('2022-01-10T10:23:17'),
emailChecked: false,
passphraseShown: false,
language: 'en',
disabled: false,
groupId: 1,

View File

@ -3,7 +3,7 @@ export const peterLustig = {
firstName: 'Peter',
lastName: 'Lustig',
username: 'peter',
description: 'Latzhose und Nickelbrille',
// description: 'Latzhose und Nickelbrille',
password: BigInt('3917921995996627700'),
pubKey: Buffer.from('7281e0ee3258b08801f3ec73e431b4519677f65c03b0382c63a913b5784ee770', 'hex'),
privKey: Buffer.from(
@ -13,7 +13,6 @@ export const peterLustig = {
emailHash: Buffer.from('9f700e6f6ec351a140b674c0edd4479509697b023bd8bee8826915ef6c2af036', 'hex'),
createdAt: new Date('2020-11-25T10:48:43'),
emailChecked: true,
passphraseShown: false,
language: 'de',
disabled: false,
groupId: 1,

View File

@ -3,7 +3,7 @@ export const raeuberHotzenplotz = {
firstName: 'Räuber',
lastName: 'Hotzenplotz',
username: 'räuber',
description: 'Pfefferpistole',
// description: 'Pfefferpistole',
password: BigInt('12123692783243004812'),
pubKey: Buffer.from('d7c70f94234dff071d982aa8f41583876c356599773b5911b39080da2b8c2d2b', 'hex'),
privKey: Buffer.from(
@ -13,7 +13,6 @@ export const raeuberHotzenplotz = {
emailHash: Buffer.from('ec8d34112adb40ff2f6538b05660b03440372690f034cd7d6322d17020233c77', 'hex'),
createdAt: new Date('2021-11-26T11:32:16'),
emailChecked: true,
passphraseShown: false,
language: 'de',
disabled: false,
groupId: 1,

View File

@ -32,10 +32,10 @@ WEBHOOK_ELOPAGE_SECRET=secret
GDT_API_URL=https://gdt.gradido.net
COMMUNITY_NAME=Gradido Development Stage1
COMMUNITY_NAME="Gradido Development Stage1"
COMMUNITY_URL=https://stage1.gradido.net/
COMMUNITY_REGISTER_URL=https://stage1.gradido.net/register
COMMUNITY_DESCRIPTION=Gradido Development Stage1 Test Community
COMMUNITY_DESCRIPTION="Gradido Development Stage1 Test Community"
KLICKTIPP=false
KLICKTIPP_USER=

View File

@ -14,9 +14,13 @@ set +o allexport
# NOTE: all config values will be in process.env when starting
# the services and will therefore take precedence over the .env
if [ -f "$SCRIPT_DIR/.env" ]; then
export $(cat $SCRIPT_DIR/.env | sed 's/#.*//g' | xargs)
set -o allexport
source $SCRIPT_DIR/.env
set +o allexport
else
export $(cat $SCRIPT_DIR/.env.dist | sed 's/#.*//g' | xargs)
set -o allexport
source $SCRIPT_DIR/.env.dist
set +o allexport
fi
# Configure git
@ -110,7 +114,6 @@ sudo mysql <<EOFMYSQL
EOFMYSQL
# Configure database
# TODO - do this in the start.sh to regenerate configs on each deploy
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/database/.env.template > $PROJECT_ROOT/database/.env
# Configure backend

View File

@ -10,13 +10,29 @@ PROJECT_ROOT=$SCRIPT_DIR/../..
NGINX_CONFIG_DIR=$SCRIPT_DIR/nginx/sites-available
set +o allexport
# Load .env or .env.dist if not present
# NOTE: all config values will be in process.env when starting
# the services and will therefore take precedence over the .env
# We have to load the backend .env to get DB_USERNAME, DB_PASSWORD AND JWT_SECRET
export_var(){
export $1=$(grep -v '^#' $PROJECT_ROOT/backend/.env | grep -e "$1" | sed -e 's/.*=//')
}
if [ -f "$PROJECT_ROOT/backend/.env" ]; then
export_var 'DB_USER'
export_var 'DB_PASSWORD'
export_var 'JWT_SECRET'
fi
# Load .env or .env.dist if not present
if [ -f "$SCRIPT_DIR/.env" ]; then
export $(cat $SCRIPT_DIR/.env | sed 's/#.*//g' | xargs)
set -o allexport
source $SCRIPT_DIR/.env
set +o allexport
else
export $(cat $SCRIPT_DIR/.env.dist | sed 's/#.*//g' | xargs)
set -o allexport
source $SCRIPT_DIR/.env.dist
set +o allexport
fi
# lock start
@ -64,6 +80,16 @@ case "$NGINX_SSL" in
esac
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $NGINX_CONFIG_DIR/$TEMPLATE_FILE > $NGINX_CONFIG_DIR/update-page.conf
# Regenerate .env files
cp -f $PROJECT_ROOT/database/.env $PROJECT_ROOT/database/.env.bak
cp -f $PROJECT_ROOT/backend/.env $PROJECT_ROOT/backend/.env.bak
cp -f $PROJECT_ROOT/frontend/.env $PROJECT_ROOT/frontend/.env.bak
cp -f $PROJECT_ROOT/admin/.env $PROJECT_ROOT/admin/.env.bak
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/database/.env.template > $PROJECT_ROOT/database/.env
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/backend/.env.template > $PROJECT_ROOT/backend/.env
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/frontend/.env.template > $PROJECT_ROOT/frontend/.env
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/admin/.env.template > $PROJECT_ROOT/admin/.env
# Install & build database
echo 'Updating database<br>' >> $UPDATE_HTML
cd $PROJECT_ROOT/database

3
frontend/.gitignore vendored
View File

@ -8,7 +8,8 @@ yarn-error.log*
test/unit/coverage
package-lock.json
.env
/.env
/.env.bak
.env.development.local
.env.production.local

View File

@ -22,8 +22,6 @@ export const updateUserInfos = gql`
mutation(
$firstName: String
$lastName: String
$description: String
$username: String
$password: String
$passwordNew: String
$locale: String
@ -32,8 +30,6 @@ export const updateUserInfos = gql`
updateUserInfos(
firstName: $firstName
lastName: $lastName
description: $description
username: $username
password: $password
passwordNew: $passwordNew
language: $locale

View File

@ -4,11 +4,9 @@ export const login = gql`
query($email: String!, $password: String!, $publisherId: Int) {
login(email: $email, password: $password, publisherId: $publisherId) {
email
username
firstName
lastName
language
description
coinanimation
klickTipp {
newsletterState
@ -24,11 +22,9 @@ export const verifyLogin = gql`
query {
verifyLogin {
email
username
firstName
lastName
language
description
coinanimation
klickTipp {
newsletterState
@ -93,12 +89,6 @@ export const sendResetPasswordEmail = gql`
}
`
export const checkUsername = gql`
query($username: String!) {
checkUsername(username: $username)
}
`
export const listGDTEntriesQuery = gql`
query($currentPage: Int!, $pageSize: Int!) {
listGDTEntries(currentPage: $currentPage, pageSize: $pageSize) {

View File

@ -15,18 +15,15 @@ export const mutations = {
email: (state, email) => {
state.email = email
},
username: (state, username) => {
state.username = username
},
// username: (state, username) => {
// state.username = username
// },
firstName: (state, firstName) => {
state.firstName = firstName
},
lastName: (state, lastName) => {
state.lastName = lastName
},
description: (state, description) => {
state.description = description
},
token: (state, token) => {
state.token = token
},
@ -56,10 +53,9 @@ export const actions = {
login: ({ dispatch, commit }, data) => {
commit('email', data.email)
commit('language', data.language)
commit('username', data.username)
// commit('username', data.username)
commit('firstName', data.firstName)
commit('lastName', data.lastName)
commit('description', data.description)
commit('coinanimation', data.coinanimation)
commit('newsletterState', data.klickTipp.newsletterState)
commit('hasElopage', data.hasElopage)
@ -69,10 +65,9 @@ export const actions = {
logout: ({ commit, state }) => {
commit('token', null)
commit('email', null)
commit('username', '')
// commit('username', '')
commit('firstName', '')
commit('lastName', '')
commit('description', '')
commit('coinanimation', true)
commit('newsletterState', null)
commit('hasElopage', false)
@ -96,8 +91,7 @@ try {
language: null,
firstName: '',
lastName: '',
username: '',
description: '',
// username: '',
token: null,
isAdmin: false,
coinanimation: true,

View File

@ -18,10 +18,8 @@ const {
language,
email,
token,
username,
firstName,
lastName,
description,
coinanimation,
newsletterState,
publisherId,
@ -65,14 +63,6 @@ describe('Vuex store', () => {
})
})
describe('username', () => {
it('sets the state of username', () => {
const state = { username: null }
username(state, 'user')
expect(state.username).toEqual('user')
})
})
describe('firstName', () => {
it('sets the state of firstName', () => {
const state = { firstName: null }
@ -89,14 +79,6 @@ describe('Vuex store', () => {
})
})
describe('description', () => {
it('sets the state of description', () => {
const state = { description: null }
description(state, 'Nickelbrille')
expect(state.description).toEqual('Nickelbrille')
})
})
describe('coinanimation', () => {
it('sets the state of coinanimation', () => {
const state = { coinanimation: true }
@ -169,10 +151,8 @@ describe('Vuex store', () => {
const commitedData = {
email: 'user@example.org',
language: 'de',
username: 'peter',
firstName: 'Peter',
lastName: 'Lustig',
description: 'Nickelbrille',
coinanimation: false,
klickTipp: {
newsletterState: true,
@ -182,9 +162,9 @@ describe('Vuex store', () => {
isAdmin: true,
}
it('calls eleven commits', () => {
it('calls nine commits', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenCalledTimes(11)
expect(commit).toHaveBeenCalledTimes(9)
})
it('commits email', () => {
@ -197,49 +177,39 @@ describe('Vuex store', () => {
expect(commit).toHaveBeenNthCalledWith(2, 'language', 'de')
})
it('commits username', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(3, 'username', 'peter')
})
it('commits firstName', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(4, 'firstName', 'Peter')
expect(commit).toHaveBeenNthCalledWith(3, 'firstName', 'Peter')
})
it('commits lastName', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(5, 'lastName', 'Lustig')
})
it('commits description', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(6, 'description', 'Nickelbrille')
expect(commit).toHaveBeenNthCalledWith(4, 'lastName', 'Lustig')
})
it('commits coinanimation', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(7, 'coinanimation', false)
expect(commit).toHaveBeenNthCalledWith(5, 'coinanimation', false)
})
it('commits newsletterState', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(8, 'newsletterState', true)
expect(commit).toHaveBeenNthCalledWith(6, 'newsletterState', true)
})
it('commits hasElopage', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(9, 'hasElopage', false)
expect(commit).toHaveBeenNthCalledWith(7, 'hasElopage', false)
})
it('commits publisherId', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(10, 'publisherId', 1234)
expect(commit).toHaveBeenNthCalledWith(8, 'publisherId', 1234)
})
it('commits isAdmin', () => {
login({ commit, state }, commitedData)
expect(commit).toHaveBeenNthCalledWith(11, 'isAdmin', true)
expect(commit).toHaveBeenNthCalledWith(9, 'isAdmin', true)
})
})
@ -247,9 +217,9 @@ describe('Vuex store', () => {
const commit = jest.fn()
const state = {}
it('calls eleven commits', () => {
it('calls nine commits', () => {
logout({ commit, state })
expect(commit).toHaveBeenCalledTimes(11)
expect(commit).toHaveBeenCalledTimes(9)
})
it('commits token', () => {
@ -262,49 +232,39 @@ describe('Vuex store', () => {
expect(commit).toHaveBeenNthCalledWith(2, 'email', null)
})
it('commits username', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(3, 'username', '')
})
it('commits firstName', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(4, 'firstName', '')
expect(commit).toHaveBeenNthCalledWith(3, 'firstName', '')
})
it('commits lastName', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(5, 'lastName', '')
})
it('commits description', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(6, 'description', '')
expect(commit).toHaveBeenNthCalledWith(4, 'lastName', '')
})
it('commits coinanimation', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(7, 'coinanimation', true)
expect(commit).toHaveBeenNthCalledWith(5, 'coinanimation', true)
})
it('commits newsletterState', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(8, 'newsletterState', null)
expect(commit).toHaveBeenNthCalledWith(6, 'newsletterState', null)
})
it('commits hasElopage', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(9, 'hasElopage', false)
expect(commit).toHaveBeenNthCalledWith(7, 'hasElopage', false)
})
it('commits publisherId', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(10, 'publisherId', null)
expect(commit).toHaveBeenNthCalledWith(8, 'publisherId', null)
})
it('commits isAdmin', () => {
logout({ commit, state })
expect(commit).toHaveBeenNthCalledWith(11, 'isAdmin', false)
expect(commit).toHaveBeenNthCalledWith(9, 'isAdmin', false)
})
// how to get this working?

View File

@ -1,7 +1,6 @@
import { configure, extend } from 'vee-validate'
// eslint-disable-next-line camelcase
import { required, email, min, max, is_not } from 'vee-validate/dist/rules'
import { checkUsername } from './graphql/queries'
export const loadAllRules = (i18nCallback) => {
configure({
@ -49,32 +48,6 @@ export const loadAllRules = (i18nCallback) => {
},
})
extend('gddUsernameUnique', {
async validate(value) {
this.$apollo
.query({
query: checkUsername,
variables: {
username: value,
},
})
.then((result) => {
return result.data.checkUsername
})
.catch(() => {
return false
})
},
message: (_, values) => i18nCallback.t('form.validation.usernmae-unique', values),
})
extend('gddUsernameRgex', {
validate(value) {
return !!value.match(/^[a-zA-Z][-_a-zA-Z0-9]{2,}$/)
},
message: (_, values) => i18nCallback.t('form.validation.usernmae-regex', values),
})
// eslint-disable-next-line camelcase
extend('is_not', {
// eslint-disable-next-line camelcase

View File

@ -15,7 +15,7 @@
<sidebar class="main-sidebar" :elopageUri="elopageUri" @admin="admin" @logout="logout" />
</div>
<div class="main-page ml-2 mr-2" style="width: 100%" @click="visible = false">
<div class="main-page" style="width: 100%" @click="visible = false">
<div class="main-content">
<fade-transition :duration="200" origin="center top" mode="out-in">
<router-view

View File

@ -1,7 +1,7 @@
<template>
<div>
<b-container>
<gdd-send :currentTransactionStep="currentTransactionStep" class="pt-3">
<gdd-send :currentTransactionStep="currentTransactionStep" class="pt-3 ml-2 mr-2">
<template #transaction-form>
<transaction-form :balance="balance" @set-transaction="setTransaction"></transaction-form>
</template>
@ -97,6 +97,12 @@ export default {
onReset() {
this.currentTransactionStep = 0
},
updateTransactions(pagination) {
this.$emit('update-transactions', pagination)
},
},
created() {
this.updateTransactions(0)
},
}
</script>

View File

@ -19,7 +19,6 @@ describe('UserCard_FormUserData', () => {
state: {
firstName: 'Peter',
lastName: 'Lustig',
description: '',
},
commit: storeCommitMock,
},
@ -59,10 +58,6 @@ describe('UserCard_FormUserData', () => {
expect(wrapper.findAll('div.col').at(4).text()).toBe('Lustig')
})
it('renders the description', () => {
expect(wrapper.findAll('div.col').at(6).text()).toBe('')
})
describe('edit user data', () => {
beforeEach(async () => {
await wrapper.find('svg.bi-pencil').trigger('click')
@ -80,11 +75,9 @@ describe('UserCard_FormUserData', () => {
it('does not change the userdate when cancel is clicked', async () => {
await wrapper.findAll('input').at(0).setValue('Petra')
await wrapper.findAll('input').at(1).setValue('Lustiger')
await wrapper.find('textarea').setValue('Keine Nickelbrille')
await wrapper.find('svg.bi-x-circle').trigger('click')
expect(wrapper.findAll('div.col').at(2).text()).toBe('Peter')
expect(wrapper.findAll('div.col').at(4).text()).toBe('Lustig')
expect(wrapper.findAll('div.col').at(6).text()).toBe('')
})
it('has a submit button', () => {
@ -108,7 +101,6 @@ describe('UserCard_FormUserData', () => {
jest.clearAllMocks()
await wrapper.findAll('input').at(0).setValue('Petra')
await wrapper.findAll('input').at(1).setValue('Lustiger')
await wrapper.find('textarea').setValue('Keine Nickelbrille')
await wrapper.find('form').trigger('keyup')
await wrapper.find('button[type="submit"]').trigger('click')
await flushPromises()
@ -120,7 +112,6 @@ describe('UserCard_FormUserData', () => {
variables: {
firstName: 'Petra',
lastName: 'Lustiger',
description: 'Keine Nickelbrille',
},
}),
)
@ -134,10 +125,6 @@ describe('UserCard_FormUserData', () => {
expect(storeCommitMock).toBeCalledWith('lastName', 'Lustiger')
})
it('commits description to store', () => {
expect(storeCommitMock).toBeCalledWith('description', 'Keine Nickelbrille')
})
it('toasts a success message', () => {
expect(toastSuccessMock).toBeCalledWith('settings.name.change-success')
})
@ -155,7 +142,6 @@ describe('UserCard_FormUserData', () => {
jest.clearAllMocks()
await wrapper.findAll('input').at(0).setValue('Petra')
await wrapper.findAll('input').at(1).setValue('Lustiger')
await wrapper.find('textarea').setValue('Keine Nickelbrille')
await wrapper.find('form').trigger('keyup')
await wrapper.find('button[type="submit"]').trigger('click')
await flushPromises()
@ -167,7 +153,6 @@ describe('UserCard_FormUserData', () => {
variables: {
firstName: 'Petra',
lastName: 'Lustiger',
description: 'Keine Nickelbrille',
},
}),
)

View File

@ -43,17 +43,6 @@
<b-input type="text" v-model="form.lastName"></b-input>
</b-col>
</b-row>
<b-row class="mb-3" v-show="false">
<b-col class="col-12">
<small>{{ $t('form.description') }}</small>
</b-col>
<b-col v-if="showUserData" class="col-12">
{{ form.description }}
</b-col>
<b-col v-else class="col-12">
<b-textarea rows="3" max-rows="6" v-model="form.description"></b-textarea>
</b-col>
</b-row>
<b-row class="text-right" v-if="!showUserData">
<b-col>
@ -85,7 +74,6 @@ export default {
form: {
firstName: this.$store.state.firstName,
lastName: this.$store.state.lastName,
description: this.$store.state.description,
},
loading: true,
}
@ -94,14 +82,12 @@ export default {
cancelEdit() {
this.form.firstName = this.$store.state.firstName
this.form.lastName = this.$store.state.lastName
this.form.description = this.$store.state.description
this.showUserData = true
},
loadSubmitButton() {
if (
this.form.firstName !== this.$store.state.firstName ||
this.form.lastName !== this.$store.state.lastName ||
this.form.description !== this.$store.state.description
this.form.lastName !== this.$store.state.lastName
) {
this.loading = false
} else {
@ -116,13 +102,11 @@ export default {
variables: {
firstName: this.form.firstName,
lastName: this.form.lastName,
description: this.form.description,
},
})
.then(() => {
this.$store.commit('firstName', this.form.firstName)
this.$store.commit('lastName', this.form.lastName)
this.$store.commit('description', this.form.description)
this.showUserData = true
this.$toasted.success(this.$t('settings.name.change-success'))
})

View File

@ -1,5 +1,5 @@
<template>
<div fluid="sm">
<div fluid="sm" class="mr-2">
<user-card :balance="balance" :transactionCount="transactionCount"></user-card>
<form-user-data />
<hr />
@ -34,6 +34,14 @@ export default {
balance: { type: Number, default: 0 },
transactionCount: { type: Number, default: 0 },
},
methods: {
updateTransactions(pagination) {
this.$emit('update-transactions', pagination)
},
},
created() {
this.updateTransactions(0)
},
}
</script>
<style>

View File

@ -1,6 +1,6 @@
<template>
<div class="pb-4">
<b-tabs content-class="mt-3 pt-4 pb-4" justified>
<b-tabs content-class="" justified>
<b-tab :title="'Gradido (' + $n(balance, 'decimal') + ' GDD)'" class="px-4">
<p class="tab-tex">{{ $t('transaction.gdd-text') }}</p>
@ -53,17 +53,20 @@ export default {
</script>
<style>
.nav-tabs > li > a {
/* adjust padding for height*/
padding-top: 14px;
padding-bottom: 14px;
margin-bottom: 14px;
}
.nav-tabs > li.active {
background-color: aquamarine;
font-size: larger;
.nav-tabs .nav-link {
background-color: rgba(204, 204, 204, 0.185);
}
.nav-tabs .nav-link.active {
background-color: rgb(248 249 254);
}
.nav-tabs > li > a {
outline: none !important;
.tab-content {
padding-top: 25px;
border-left: 1px inset rgba(28, 110, 164, 0.1);
border-right: 1px inset rgba(28, 110, 164, 0.1);
}
</style>

1
gn

@ -1 +0,0 @@
Subproject commit 5437e2f882c54efe4f501f7cd0d97f53806d0b74