use params instead of query for send/identifier route

This commit is contained in:
einhornimmond 2024-01-30 21:39:12 +01:00
parent 90dae9171a
commit ee40603637
19 changed files with 328 additions and 94 deletions

View File

@ -12,7 +12,7 @@ Decimal.set({
})
const constants = {
DB_VERSION: '0080-fill_linked_user_gradidoId_of_contributions',
DB_VERSION: '0081-user_join_community',
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
LOG4JS_CONFIG: 'log4js-config.json',
// default log level on production should be info

View File

@ -3,11 +3,11 @@ import { ArgsType, Field } from 'type-graphql'
@ArgsType()
export class UserArgs {
@Field({ nullable: false })
@Field()
@IsString()
identifier: string
@Field({ nullable: true })
@Field()
@IsString()
communityIdentifier?: string
communityIdentifier: string
}

View File

@ -10,6 +10,9 @@ export class User {
this.id = user.id
this.foreign = user.foreign
this.communityUuid = user.communityUuid
if (user.community) {
this.communityName = user.community.name
}
this.gradidoID = user.gradidoID
this.alias = user.alias
if (user.emailContact) {

View File

@ -432,7 +432,7 @@ export class TransactionResolver {
const senderUser = getUser(context)
if (!recipientCommunityIdentifier || (await isHomeCommunity(recipientCommunityIdentifier))) {
// processing sendCoins within sender and recepient are both in home community
// processing sendCoins within sender and recipient are both in home community
const recipientUser = await findUserByIdentifier(
recipientIdentifier,
recipientCommunityIdentifier,

View File

@ -428,7 +428,7 @@ export class UserResolver {
const userContact = await DbUserContact.findOneOrFail({
where: { emailVerificationCode: code },
relations: ['user'],
}).catch(() => {
}).catch((e) => {
throw new LogError('Could not login with emailVerificationCode')
})
logger.debug('userContact loaded...')
@ -821,11 +821,6 @@ export class UserResolver {
): Promise<User> {
const foundDbUser = await findUserByIdentifier(identifier, communityIdentifier)
const modelUser = new User(foundDbUser)
if (!foundDbUser.communityUuid) {
modelUser.communityName = (await Promise.resolve(getHomeCommunity())).name
} else {
modelUser.communityName = await getCommunityName(foundDbUser.communityUuid)
}
return modelUser
}
}

View File

@ -1,3 +1,5 @@
import { FindOptionsWhere } from '@dbTools/typeorm'
import { Community } from '@entity/Community'
import { User as DbUser } from '@entity/User'
import { UserContact as DbUserContact } from '@entity/UserContact'
import { validate, version } from 'uuid'
@ -6,15 +8,26 @@ import { LogError } from '@/server/LogError'
import { VALID_ALIAS_REGEX } from './validateAlias'
/**
*
* @param identifier could be gradidoID, alias or email of user
* @param communityIdentifier could be uuid or name of community
* @returns
*/
export const findUserByIdentifier = async (
identifier: string,
communityIdentifier?: string,
communityIdentifier: string,
): Promise<DbUser> => {
let user: DbUser | null
const communityWhere: FindOptionsWhere<Community> =
validate(communityIdentifier) && version(communityIdentifier) === 4
? { communityUuid: communityIdentifier }
: { name: communityIdentifier }
if (validate(identifier) && version(identifier) === 4) {
user = await DbUser.findOne({
where: { gradidoID: identifier, communityUuid: communityIdentifier },
relations: ['emailContact'],
where: { gradidoID: identifier, community: communityWhere },
relations: ['emailContact', 'community'],
})
if (!user) {
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)
@ -24,28 +37,24 @@ export const findUserByIdentifier = async (
where: {
email: identifier,
emailChecked: true,
user: {
community: communityWhere,
},
},
relations: ['user'],
relations: ['user', 'user.community'],
})
if (!userContact) {
throw new LogError('No user with this credentials', identifier)
throw new LogError('No user with this credentials', identifier, communityIdentifier)
}
if (!userContact.user) {
throw new LogError('No user to given contact', identifier)
}
if (userContact.user.communityUuid !== communityIdentifier) {
throw new LogError(
'Found user to given contact, but belongs to other community',
identifier,
communityIdentifier,
)
throw new LogError('No user to given contact', identifier, communityIdentifier)
}
user = userContact.user
user.emailContact = userContact
} else if (VALID_ALIAS_REGEX.exec(identifier)) {
user = await DbUser.findOne({
where: { alias: identifier, communityUuid: communityIdentifier },
relations: ['emailContact'],
where: { alias: identifier, community: communityWhere },
relations: ['emailContact', 'community'],
})
if (!user) {
throw new LogError('No user found to given identifier(s)', identifier, communityIdentifier)

View File

@ -50,6 +50,7 @@ const communityDbUser: dbUser = {
},
foreign: false,
communityUuid: '55555555-4444-4333-2222-11111111',
community: null,
}
const communityUser = new User(communityDbUser)

View File

@ -0,0 +1,70 @@
import {
BaseEntity,
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
OneToMany,
JoinColumn,
} from 'typeorm'
import { User } from '../User'
@Entity('communities')
export class Community extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ name: 'foreign', type: 'bool', nullable: false, default: true })
foreign: boolean
@Column({ name: 'url', length: 255, nullable: false })
url: string
@Column({ name: 'public_key', type: 'binary', length: 32, nullable: false })
publicKey: Buffer
@Column({ name: 'private_key', type: 'binary', length: 64, nullable: true })
privateKey: Buffer | null
@Column({
name: 'community_uuid',
type: 'char',
length: 36,
nullable: true,
collation: 'utf8mb4_unicode_ci',
})
communityUuid: string | null
@Column({ name: 'authenticated_at', type: 'datetime', nullable: true })
authenticatedAt: Date | null
@Column({ name: 'name', type: 'varchar', length: 40, nullable: true })
name: string | null
@Column({ name: 'description', type: 'varchar', length: 255, nullable: true })
description: string | null
@CreateDateColumn({ name: 'creation_date', type: 'datetime', nullable: true })
creationDate: Date | null
@CreateDateColumn({
name: 'created_at',
type: 'datetime',
default: () => 'CURRENT_TIMESTAMP(3)',
nullable: false,
})
createdAt: Date
@UpdateDateColumn({
name: 'updated_at',
type: 'datetime',
onUpdate: 'CURRENT_TIMESTAMP(3)',
nullable: true,
})
updatedAt: Date | null
@OneToMany(() => User, (user) => user.community)
@JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' })
users: User[]
}

View File

@ -0,0 +1,138 @@
import {
BaseEntity,
Entity,
PrimaryGeneratedColumn,
Column,
DeleteDateColumn,
OneToMany,
JoinColumn,
OneToOne,
ManyToOne,
} from 'typeorm'
import { Contribution } from '../Contribution'
import { ContributionMessage } from '../ContributionMessage'
import { UserContact } from '../UserContact'
import { UserRole } from '../UserRole'
import { Community } from '../Community'
@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' })
export class User extends BaseEntity {
@PrimaryGeneratedColumn('increment', { unsigned: true })
id: number
@Column({ type: 'bool', default: false })
foreign: boolean
@Column({
name: 'gradido_id',
length: 36,
nullable: false,
collation: 'utf8mb4_unicode_ci',
})
gradidoID: string
@Column({
name: 'community_uuid',
type: 'char',
length: 36,
nullable: true,
collation: 'utf8mb4_unicode_ci',
})
communityUuid: string
@ManyToOne(() => Community, (community) => community.users)
@JoinColumn({ name: 'community_uuid', referencedColumnName: 'communityUuid' })
community: Community | null
@Column({
name: 'alias',
length: 20,
nullable: true,
default: null,
collation: 'utf8mb4_unicode_ci',
})
alias: string
@OneToOne(() => UserContact, (emailContact: UserContact) => emailContact.user)
@JoinColumn({ name: 'email_id' })
emailContact: UserContact
@Column({ name: 'email_id', type: 'int', unsigned: true, nullable: true, default: null })
emailId: number | null
@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({ name: 'created_at', default: () => 'CURRENT_TIMESTAMP(3)', nullable: false })
createdAt: Date
@DeleteDateColumn({ name: 'deleted_at', nullable: true })
deletedAt: Date | null
@Column({ type: 'bigint', default: 0, unsigned: true })
password: BigInt
@Column({
name: 'password_encryption_type',
type: 'int',
unsigned: true,
nullable: false,
default: 0,
})
passwordEncryptionType: number
@Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false })
language: string
@Column({ type: 'bool', default: false })
hideAmountGDD: boolean
@Column({ type: 'bool', default: false })
hideAmountGDT: boolean
@OneToMany(() => UserRole, (userRole) => userRole.user)
@JoinColumn({ name: 'user_id' })
userRoles: UserRole[]
@Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null })
referrerId?: number | null
@Column({
name: 'contribution_link_id',
type: 'int',
unsigned: true,
nullable: true,
default: null,
})
contributionLinkId?: number | null
@Column({ name: 'publisher_id', default: 0 })
publisherId: number
@OneToMany(() => Contribution, (contribution) => contribution.user)
@JoinColumn({ name: 'user_id' })
contributions?: Contribution[]
@OneToMany(() => ContributionMessage, (message) => message.user)
@JoinColumn({ name: 'user_id' })
messages?: ContributionMessage[]
@OneToMany(() => UserContact, (userContact: UserContact) => userContact.user)
@JoinColumn({ name: 'user_id' })
userContacts?: UserContact[]
}

View File

@ -1 +1 @@
export { Community } from './0068-community_tables_public_key_length/Community'
export { Community } from './0081-user_join_community/Community'

View File

@ -1 +1 @@
export { User } from './0073-introduce_foreign_user_in_users_table/User'
export { User } from './0081-user_join_community/User'

View File

@ -0,0 +1,11 @@
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(
'ALTER TABLE users MODIFY community_uuid VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;',
)
}
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
await queryFn(
'ALTER TABLE users MODIFY community_uuid VARCHAR(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;',
)
}

View File

@ -4,7 +4,7 @@ import dotenv from 'dotenv'
dotenv.config()
const constants = {
DB_VERSION: '0080-fill_linked_user_gradidoId_of_contributions',
DB_VERSION: '0081-user_join_community',
LOG4JS_CONFIG: 'log4js-config.json',
// default log level on production should be info
LOG_LEVEL: process.env.LOG_LEVEL ?? 'info',

View File

@ -10,7 +10,7 @@ Decimal.set({
})
const constants = {
DB_VERSION: '0080-fill_linked_user_gradidoId_of_contributions',
DB_VERSION: '0081-user_join_community',
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
LOG4JS_CONFIG: 'log4js-config.json',
// default log level on production should be info

View File

@ -1,16 +1,23 @@
<template>
<div class="community-switch">
<b-dropdown no-flip :text="value.name">
<b-dropdown-item
v-for="community in communities"
@click.prevent="updateCommunity(community)"
:key="community.id"
:title="community.description"
:active="value.uuid === community.uuid"
>
{{ community.name }}
</b-dropdown-item>
</b-dropdown>
<div v-if="!validCommunityIdentifier">
<b-dropdown no-flip :text="value.name">
<b-dropdown-item
v-for="community in communities"
@click.prevent="updateCommunity(community)"
:key="community.id"
:title="community.description"
:active="value.uuid === community.uuid"
>
{{ community.name }}
</b-dropdown-item>
</b-dropdown>
</div>
<div v-else class="mb-4 mt-2">
<b-row>
<b-col class="font-weight-bold" :title="value.description">{{ value.name }}</b-col>
</b-row>
</div>
</div>
</template>
<script>
@ -26,6 +33,7 @@ export default {
data() {
return {
communities: [],
validCommunityIdentifier: false,
}
},
methods: {
@ -33,6 +41,24 @@ export default {
this.$emit('input', community)
},
setDefaultCommunity() {
// when we already get an identifier via url we choose this if the community exist
if (this.communityIdentifier) {
const foundCommunity = this.communities.find((community) => {
if (
community.uuid === this.communityIdentifier ||
community.name === this.communityIdentifier
) {
this.validCommunityIdentifier = true
return true
}
return false
})
if (foundCommunity) {
this.updateCommunity(foundCommunity)
return
}
this.toastError('invalid community identifier in url')
}
// set default community, the only one which isn't foreign
// we assume it is only one entry with foreign = false
if (this.value.uuid === '' && this.communities.length) {
@ -48,8 +74,10 @@ export default {
query: selectCommunities,
},
},
mounted() {
this.setDefaultCommunity()
computed: {
communityIdentifier() {
return this.$route.params && this.$route.params.communityIdentifier
},
},
updated() {
this.setDefaultCommunity()

View File

@ -55,22 +55,15 @@
</b-row>
<b-row>
<b-col class="font-weight-bold">
<div v-if="!communityUuid">
<community-switch
v-model="form.targetCommunity"
:disabled="isBalanceDisabled"
/>
</div>
<div v-else class="mb-4">
<b-row>
<b-col class="font-weight-bold">{{ recipientCommunity.name }}</b-col>
</b-row>
</div>
<community-switch
v-model="form.targetCommunity"
:disabled="isBalanceDisabled"
/>
</b-col>
</b-row>
</b-col>
<b-col cols="12" v-if="radioSelected === sendTypes.send">
<div v-if="!gradidoID">
<div v-if="!userIdentifier">
<input-identifier
:name="$t('form.recipient')"
:label="$t('form.recipient')"
@ -150,7 +143,7 @@ import InputIdentifier from '@/components/Inputs/InputIdentifier'
import InputAmount from '@/components/Inputs/InputAmount'
import InputTextarea from '@/components/Inputs/InputTextarea'
import CommunitySwitch from '@/components/CommunitySwitch.vue'
import { userAndCommunity } from '@/graphql/queries'
import { user } from '@/graphql/queries'
import { isEmpty } from 'lodash'
import { COMMUNITY_NAME } from '@/config'
@ -193,11 +186,7 @@ export default {
this.$refs.formValidator.validate()
},
onSubmit() {
if (this.gradidoID) this.form.identifier = this.gradidoID
if (this.communityUuid) {
this.recipientCommunity.uuid = this.communityUuid
this.form.targetCommunity = this.recipientCommunity
}
if (this.userIdentifier) this.form.identifier = this.userIdentifier.identifier
this.$emit('set-transaction', {
selected: this.radioSelected,
identifier: this.form.identifier,
@ -214,29 +203,23 @@ export default {
this.form.memo = ''
this.form.targetCommunity = { uuid: '', name: COMMUNITY_NAME }
this.$refs.formValidator.validate()
if (this.$route.query && !isEmpty(this.$route.query))
this.$router.replace({ query: undefined })
if (this.$route.query && !isEmpty(this.$route.query)) this.$router.replace('send')
},
},
apollo: {
UserName: {
query() {
return userAndCommunity
return user
},
fetchPolicy: 'network-only',
variables() {
return {
identifier: this.gradidoID,
communityUuid: this.communityUuid,
}
return this.userIdentifier
},
skip() {
return !this.gradidoID
return !this.userIdentifier
},
update({ user, community }) {
update({ user }) {
this.userName = `${user.firstName} ${user.lastName}`
this.recipientCommunity.name = community.name
this.recipientCommunity.uuid = this.communityUuid
},
error({ message }) {
this.toastError(message)
@ -261,11 +244,18 @@ export default {
sendTypes() {
return SEND_TYPES
},
gradidoID() {
return this.$route.query && this.$route.query.gradidoID
},
communityUuid() {
return this.$route.query && this.$route.query.communityUuid
userIdentifier() {
if (
this.$route.params &&
this.$route.params.userIdentifier &&
this.$route.params.communityIdentifier
) {
return {
identifier: this.$route.params.userIdentifier,
communityIdentifier: this.$route.params.communityIdentifier,
}
}
return null
},
},
mounted() {

View File

@ -285,23 +285,10 @@ export const openCreations = gql`
`
export const user = gql`
query($identifier: String!) {
user(identifier: $identifier) {
firstName
lastName
communityName
}
}
`
export const userAndCommunity = gql`
query($identifier: String!, $communityUuid: String!) {
user(identifier: $identifier) {
query($identifier: String!, $communityIdentifier: String!) {
user(identifier: $identifier, communityIdentifier: $communityIdentifier) {
firstName
lastName
}
community(communityUuid: $communityUuid) {
name
}
}
`

View File

@ -172,7 +172,7 @@ export default {
throw new Error(`undefined transactionData.selected : ${this.transactionData.selected}`)
}
this.loading = false
this.$router.push({ query: { gradidoID: undefined, communityUuid: undefined } })
this.$router.push('send')
},
onBack() {
this.currentTransactionStep = TRANSACTION_STEPS.transactionForm

View File

@ -19,7 +19,9 @@ const routes = [
},
},
{
path: '/send',
// userIdentifier can be username, email or gradidoID
// communityIdentifier can be community name or community UUID
path: '/send/:communityIdentifier?/:userIdentifier?',
component: () => import('@/pages/Send'),
meta: {
requiresAuth: true,