import { PrismaService } from '@/src/prisma.service'; import { Injectable, BadRequestException, UnauthorizedException, } from '@nestjs/common'; import { userChangePasswordValidator } from './dtoValidator'; import { RedisHandlers } from '@/src/utils/store/redisHandlers'; import { PasswordHandlers } from '@/src/utils/store/loginHandler'; @Injectable() export class ChangePasswordService { constructor( private readonly prisma: PrismaService, private readonly redis: RedisHandlers, private readonly passHandlers: PasswordHandlers, ) { } private async syncPasswordHistory( foundUser: any, dto: userChangePasswordValidator, hashPassword: string, ) { const passwordHistory = await this.prisma.password_history.findFirst({ where: { userUUID: foundUser.uu_id }, }); console.log('passwordHistory', passwordHistory); console.log('dto', dto); console.log('hashPassword', hashPassword); if (passwordHistory) { if (!passwordHistory.old_password_first) { await this.prisma.password_history.update({ where: { id: passwordHistory.id }, data: { password: hashPassword, old_password_first: dto.password, old_password_first_modified_at: new Date(), }, }); } else if (!passwordHistory.old_password_second) { await this.prisma.password_history.update({ where: { id: passwordHistory.id }, data: { password: hashPassword, old_password_second: dto.password, old_password_second_modified_at: new Date(), }, }); } else if (!passwordHistory.old_password_third) { await this.prisma.password_history.update({ where: { id: passwordHistory.id }, data: { password: hashPassword, old_password_third: dto.password, old_password_third_modified_at: new Date(), }, }); } else { const firstTimestamp = new Date( passwordHistory.old_password_first_modified_at, ).getTime(); const secondTimestamp = new Date( passwordHistory.old_password_second_modified_at, ).getTime(); const thirdTimestamp = new Date( passwordHistory.old_password_third_modified_at, ).getTime(); let oldestIndex = 'first'; let oldestTimestamp = firstTimestamp; if (secondTimestamp < oldestTimestamp) { oldestIndex = 'second'; oldestTimestamp = secondTimestamp; } if (thirdTimestamp < oldestTimestamp) { oldestIndex = 'third'; } await this.prisma.password_history.update({ where: { id: passwordHistory.id }, data: { password: hashPassword, ...(oldestIndex === 'first' ? { old_password_first: dto.password, old_password_first_modified_at: new Date(), } : oldestIndex === 'second' ? { old_password_second: dto.password, old_password_second_modified_at: new Date(), } : { old_password_third: dto.password, old_password_third_modified_at: new Date(), }), }, }); } } else { await this.prisma.password_history.create({ data: { userUUID: foundUser.uu_id, password: hashPassword, old_password_first: dto.password, old_password_first_modified_at: new Date(), old_password_second: '', old_password_second_modified_at: new Date(), old_password_third: '', old_password_third_modified_at: new Date(), }, }); } } async run(dto: userChangePasswordValidator, req: Request) { const isValid = () => { const isOldPasswordDifferent = dto.password !== dto.oldPassword; const isPasswordMatchesWithRePassword = dto.password === dto.rePassword; return isOldPasswordDifferent && isPasswordMatchesWithRePassword; }; if (!isValid()) { throw new BadRequestException( 'Passwords do not match or new password is the same as old password', ); } const accessObject = await this.redis.getLoginFromRedis(req); const userFromRedis = accessObject?.value.users; if (!userFromRedis) { throw new UnauthorizedException('User not authenticated'); } const foundUser = await this.prisma.users.findFirstOrThrow({ where: { uu_id: userFromRedis.uu_id }, }); if (foundUser.password_token) { throw new BadRequestException('Set password first before changing'); } const isPasswordValid = this.passHandlers.check_password( foundUser.uu_id, dto.oldPassword, foundUser.hash_password, ); if (!isPasswordValid) { throw new UnauthorizedException('Invalid password'); } const passwordHistory = await this.prisma.password_history.findFirst({ where: { userUUID: foundUser.uu_id, OR: [ { old_password_first: { equals: dto.password, mode: 'insensitive', }, }, { old_password_second: { equals: dto.password, mode: 'insensitive', }, }, { old_password_third: { equals: dto.password, mode: 'insensitive', }, }, ], }, }); if (passwordHistory) { throw new UnauthorizedException( 'Invalid password, new password can not be same as old password', ); } const hashPassword = this.passHandlers.create_hashed_password( foundUser.uu_id, dto.password, ); await this.prisma.users.update({ where: { id: foundUser.id }, data: { hash_password: hashPassword, }, }); await this.syncPasswordHistory(foundUser, dto, hashPassword); return { message: 'Password changed successfully' }; } }