import { BadRequestException , UnprocessableEntityException , NotFoundException , ForbiddenException } from '@nestjs/common' ; import { Repository , EntityRepository } from 'typeorm' ; import * as jwt from 'jsonwebtoken' ; import keymachine from 'keymachine' ; import * as argon2 from 'argon2' ; import { UsersEntity } from '../Entities/users.entity' ; import { UpdateUserDto } from 'src/v1/User/Dto/update-user.dto' ; import { CreateAccountDto } from 'src/v1/Auth/Dto/create-account.dto' ; import { LoginDto } from 'src/v1/Auth/Dto/login.dto' ; import { GenerateRecoveryKeyDto } from 'src/v1/Auth/Dto/generate-recovery-key.dto' ; import { RecoverAccountDto } from 'src/v1/Auth/Dto/recover-account.dto' ; import { configService } from '../Services/config.service' ; @ EntityRepository ( UsersEntity ) export class UsersRepository extends Repository < UsersEntity > { constructor ( ) { super ( ) ; } async getUserByUsername ( username ) { try { const profile = await this . findOneOrFail ( { username } ) ; return profile ; } catch ( err ) { throw new NotFoundException ( 'User could not found by given username' ) ; } } async getUserByEmail ( emailParam ) { try { return await this . findOneOrFail ( { email : emailParam } ) ; } catch ( err ) { throw new NotFoundException ( 'User could not found by given email' ) ; } } async searchUserByUsername ( { searchValue } ) { const [ users ] = await this . findAndCount ( { where : { username : new RegExp ( searchValue , 'i' ) } , take : 5 , order : { full_name : 'ASC' } } ) ; return { users } ; } async validateUser ( dto ) { let user ; try { user = await this . findOneOrFail ( { where : { $or : [ { email : dto . email , } , { username : dto . username , } , ] } } ) ; } catch ( err ) { throw new BadRequestException ( 'Account is does not exists or not verified yet' ) ; } let isVerified ; try { isVerified = await argon2 . verify ( user . password , dto . password ) ; } catch { throw new BadRequestException ( 'Your current password has the old hashing algorithm, please update your password' ) ; } if ( ! isVerified ) throw new BadRequestException ( 'Incorrect account credentials' ) ; return user ; } async createUser ( dto ) { const newUser = new UsersEntity ( { email : dto . email , username : dto . username , password : dto . password , full_name : dto . fullName , } ) ; try { await this . save ( newUser ) ; } catch ( error ) { throw new UnprocessableEntityException ( error . errmsg ) ; } } async updateUser ( username , dto ) { let profile ; try { profile = await this . findOneOrFail ( { username } ) ; } catch ( error ) { throw new NotFoundException ( 'User could not found by given username' ) ; } if ( dto . fullName ) profile . full_name = dto . fullName ; if ( dto . link ) profile . link = dto . link ; if ( dto . biography ) profile . biography = dto . biography ; if ( dto . password ) { const hashedPassword = await argon2 . hash ( dto . password ) ; if ( ! await argon2 . verify ( profile . password , dto . oldPassword ) ) { throw new BadRequestException ( 'Old password does not match' ) ; } profile . password = hashedPassword ; } return await this . save ( profile ) ; } async triggerRefreshToken ( query ) { const profile = await this . findOneOrFail ( { where : { $or : [ { email : query , } , { username : query , } , ] } } ) ; profile . refresh_token = jwt . sign ( { username : profile . username , iat : Date . now ( ) } , configService . getEnv ( 'SECRET_FOR_REFRESH_TOKEN' ) ) ; await this . save ( profile ) ; return profile . refresh_token ; } async verifyUpdatedEmail ( decodedToken ) { try { const account = await this . findOneOrFail ( { email : decodedToken . email , username : decodedToken . username , } ) ; account . email = decodedToken . newEmail ; await this . save ( account ) ; } catch ( err ) { throw new BadRequestException ( 'Verification token is not valid' ) ; } } async disableUser ( username ) { let user ; try { user = await this . findOneOrFail ( { username } ) ; } catch ( err ) { throw new NotFoundException ( 'User could not found' ) ; } user . is_active = false ; await this . save ( user ) ; } async activateUser ( decodedToken ) { try { const account = await this . findOneOrFail ( { email : decodedToken . email , username : decodedToken . username , } ) ; account . is_active = true ; await this . save ( account ) ; } catch ( err ) { throw new BadRequestException ( 'Activation token is not valid' ) ; } } async banOrUnbanUser ( operatorRole , username , banSituation ) { let user ; try { user = await this . findOneOrFail ( { username } ) ; } catch ( err ) { throw new NotFoundException ( 'User could not found by given username' ) ; } if ( operatorRole <= user . role ) throw new ForbiddenException ( 'Operation not permitted' ) ; user . is_banned = banSituation ; await this . save ( user ) ; } async generateRecoveryKey ( dto ) { let account ; try { account = await this . findOneOrFail ( { email : dto . email } ) ; } catch ( err ) { throw new NotFoundException ( 'User could not found by given email' ) ; } if ( account . is_banned ) throw new BadRequestException ( 'Account is banned' ) ; if ( ! account . is_active ) throw new BadRequestException ( 'Account is not active' ) ; const generatedKey = keymachine ( ) ; account . recovery_key = generatedKey ; return { account : await this . save ( account ) , generatedKey , } ; } async recoverAccount ( dto ) { let account ; try { account = await this . findOneOrFail ( { email : dto . email , recovery_key : dto . recoveryKey } ) ; } catch ( error ) { throw new BadRequestException ( 'Recovery key does not match' ) ; } account . password = await argon2 . hash ( dto . password ) ; account . recovery_key = null ; await this . save ( account ) ; } }