import { ForbiddenError } from 'apollo-server'; import { Length } from 'class-validator'; import { Ctx, Field, ID, ObjectType } from 'type-graphql'; import { Column, Entity, EntityManager, getManager, JoinColumn, ManyToMany, OneToMany, OneToOne, PrimaryGeneratedColumn, Transaction, TransactionManager, } from 'typeorm'; import { BasicUser, ContextType, UserRole } from '@/types'; import { AggregateProductVariantSavesList, AnonymousAuthenticationStrategy, PasswordAuthenticationStrategy, UserProfile, } from '..'; import { AbstractEntity, Node } from '../abstract'; import { ApiClient } from '../ApiClient'; @Entity({ name: 'users' }) @ObjectType({ implements: [Node, AbstractEntity, BasicUser], }) export default class User extends AbstractEntity { @Field(() => ID) @PrimaryGeneratedColumn('uuid') id!: string; @Field(() => String, { nullable: true, }) @Column({ type: 'varchar', nullable: true, }) username?: string; @Field(() => String, { nullable: true, description: "This user's email if available.", }) async email(): Promise { return (await this.getPasswordAuth())?.email; } @Field(() => UserProfile, { nullable: false }) @Column(() => UserProfile, { prefix: 'profile', }) profile!: UserProfile; @OneToOne(() => PasswordAuthenticationStrategy, (strategy) => strategy.user, { nullable: true, }) @JoinColumn() passwordAuth?: PasswordAuthenticationStrategy; @Column({ nullable: true }) passwordAuthId?: string; @Transaction() async getPasswordAuth( @TransactionManager() manager: EntityManager = getManager() ): Promise { if (this.passwordAuthId) { this.passwordAuth = this.passwordAuth || (await manager.findOneOrFail(PasswordAuthenticationStrategy, { relations: ['user'], where: { user: this, }, })); } return this.passwordAuth; } @OneToOne( () => AnonymousAuthenticationStrategy, (strategy) => strategy.user, { nullable: true, } ) @JoinColumn() anonymousAuth?: AnonymousAuthenticationStrategy; @Column({ nullable: true }) anonymousAuthId?: string; @Transaction() async getAnonymousAuth( @TransactionManager() manager: EntityManager = getManager() ): Promise { if (this.anonymousAuthId) { this.anonymousAuth = this.anonymousAuth || (await manager.findOneOrFail(AnonymousAuthenticationStrategy, { relations: ['user'], where: { user: this, }, })); } return this.anonymousAuth; } @Field(() => Boolean, { nullable: false, name: 'isAnonymous', }) async isAnonymous(@Ctx() context?: ContextType): Promise { try { return !!(await this.getAnonymousAuth()); } catch (error) { return false; } } @Field(() => Boolean, { nullable: false, name: 'isVerified', }) async isVerified(@Ctx() context: ContextType): Promise { if (!context.user || context.user?.id !== this.id) { throw new ForbiddenError('Can not get isVerified for another user'); } const passwordAuth = await this.getPasswordAuth(); if (passwordAuth) { return passwordAuth.isVerified; } return false; } /** * Denotes the current referesh token version. If this does not match the * refresh token version that is passed in, then new refresh tokens cannot * be granted. * * Can be rotated to invalidate existing refresh token, or nullified to * revoke all refresh tokens. **/ @Column({ type: 'varchar', nullable: true, }) @Length(20) refreshTokenVersion?: string; @OneToMany(() => ApiClient, (apiClient) => apiClient.user) apiClients!: ApiClient[]; @Field(() => [UserRole], { nullable: false, }) @Column({ type: 'enum', array: true, default: [], nullable: false, enum: UserRole, }) roles!: UserRole[]; @ManyToMany( () => AggregateProductVariantSavesList, (savesList) => savesList.collaborators ) savesLists!: AggregateProductVariantSavesList[]; }