From 45464c070ff95ccc95e9c522e01380a8fe113196 Mon Sep 17 00:00:00 2001 From: realrafi Date: Sun, 26 Jul 2026 14:55:37 +0330 Subject: [PATCH] commit for push --- package.json | 2 + pnpm-lock.yaml | 6 +++ src/auth/auth.controller.ts | 10 +++++ src/auth/auth.service.ts | 49 ++++++++++++++++++++--- src/users/repositories/user.repository.ts | 33 +++++++++++---- src/users/users.service.ts | 23 ++++++++++- 6 files changed, 108 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index b494542..b72a720 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "class-transformer": "^0.5.1", "class-validator": "^0.15.1", "joi": "^18.2.3", + "ms": "^2.1.3", "passport-jwt": "^4.0.1", "pg": "^8.22.0", "reflect-metadata": "^0.2.2", @@ -48,6 +49,7 @@ "@types/bcrypt": "^6.0.0", "@types/express": "^5.0.0", "@types/jest": "^30.0.0", + "@types/ms": "^2.1.0", "@types/node": "^22.10.7", "@types/passport-jwt": "^4.0.1", "@types/supertest": "^6.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f87cba0..fe3777c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: joi: specifier: ^18.2.3 version: 18.2.3 + ms: + specifier: ^2.1.3 + version: 2.1.3 passport-jwt: specifier: ^4.0.1 version: 4.0.1 @@ -87,6 +90,9 @@ importers: '@types/jest': specifier: ^30.0.0 version: 30.0.0 + '@types/ms': + specifier: ^2.1.0 + version: 2.1.0 '@types/node': specifier: ^22.10.7 version: 22.20.1 diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index c1b7247..648fa7e 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -45,4 +45,14 @@ export class AuthController { ) { return this.authService.checkLoginCredentials(dto); } + + @Post('verify-login') + @ApiOperation({summary: 'verifies OTP and then login'}) + @ApiResponse({status: 200, description: 'User Logged in successfully!'}) + @ApiResponse({status: 400, description: 'Bad Request From The User'}) + async verifyOtpAndLogin( + @Body() verifyOtpDto: VerifyOtpDto + ) { + return this.authService.verifyOtpAndLogin(verifyOtpDto); + } } \ No newline at end of file diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index bac0539..549c6e5 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -11,6 +11,7 @@ import { LoginCredentialDto } from './DTO/login-credential.dto'; import { UsersService } from 'src/users/users.service'; import * as bcrypt from 'bcrypt'; import { JwtService } from '@nestjs/jwt'; +import ms from 'ms'; @Injectable() export class AuthService { @@ -20,7 +21,7 @@ export class AuthService { private readonly configService: ConfigService, private readonly OtpRepository: OtpRepository, private readonly userService: UsersService, - private readonly jwtService: JwtService + private readonly jwtService: JwtService, ) {} async sendOTP(phone: string) { @@ -43,7 +44,6 @@ export class AuthService { { status: OtpStatus.EXPIRED }, ); - await this.OtpRepository.save( this.OtpRepository.create({ phoneNumber: phone, @@ -123,9 +123,46 @@ export class AuthService { await this.sendOTP(phoneNumber); } - // async VerifyOtpAndLogin(dto: VerifyOtpDto) { - // await this.verifyOTP(dto); + async verifyOtpAndLogin(dto: VerifyOtpDto) { + await this.verifyOTP(dto); - // const user = await this.userService.findOneByPhoneOrFail(dto.phoneNumber); - // } + const user = await this.userService.findUserWithRolesByPhoneOrFail( + dto.phoneNumber, + ); + + const roles = user.roles.map((r) => r.role); + + const payload = { + sub: user.id, + phone: user.phone, + roles, + }; + + const accessToken = await this.jwtService.signAsync(payload, { + secret: this.configService.getOrThrow('JWT_ACCESS_SECRET'), + expiresIn: this.configService.getOrThrow( + 'JWT_ACCESS_EXPIRES', + ) as ms.StringValue, + }); + + const refreshToken = await this.jwtService.signAsync(payload, { + secret: this.configService.getOrThrow('JWT_REFRESH_SECRET'), + expiresIn: this.configService.getOrThrow( + 'JWT_REFRESH_EXPIRES', + ) as ms.StringValue, + }); + + await this.userService.updateRefreshToken(user.id, refreshToken); + + return { + user: { + userId: user.id, + username: user.firstName + ' ' + user.lastName, + userPhone: user.phone, + userEmail: user.email, + }, + accessToken, + refreshToken, + }; + } } diff --git a/src/users/repositories/user.repository.ts b/src/users/repositories/user.repository.ts index b4a8e09..3452c2c 100644 --- a/src/users/repositories/user.repository.ts +++ b/src/users/repositories/user.repository.ts @@ -1,11 +1,28 @@ -import { Repository } from "typeorm"; -import { User } from "../entities/user.entity"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Injectable } from "@nestjs/common"; +import { Repository } from 'typeorm'; +import { User } from '../entities/user.entity'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Injectable } from '@nestjs/common'; @Injectable() export class UserRepository extends Repository { - constructor(@InjectRepository(User) userRepository: Repository) { - super(userRepository.target, userRepository.manager, userRepository.queryRunner); - } -} \ No newline at end of file + constructor(@InjectRepository(User) userRepository: Repository) { + super( + userRepository.target, + userRepository.manager, + userRepository.queryRunner, + ); + } + + findUserWithRolesByPhone(phone: string): Promise { + return this.createQueryBuilder('users') + .leftJoinAndSelect('users.roles', 'roles') + .where("users.phone = :phone", {phone}) + .select( + [ + 'users', + 'roles.id', + 'roles.role' + ] + ).getOne(); + } +} diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 92bc62d..3f98b12 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -30,7 +30,12 @@ export class UsersService { } async findOneByPhoneOrFail(phone: string): Promise { - const user = await this.userRepository.findOne({ where: { phone } }); + const user = await this.userRepository.findOne({ + where: { phone }, + relations: { + roles: true, + }, + }); if (!user) { throw new NotFoundException(UserMessages.USER_NOT_FOUND); @@ -39,6 +44,22 @@ export class UsersService { return user; } + async findUserWithRolesByPhoneOrFail(phone: string): Promise { + const user = await this.userRepository.findUserWithRolesByPhone(phone); + + if (!user) + throw new NotFoundException(UserMessages.USER_NOT_FOUND); + + return user; + } + + async updateRefreshToken(id: string, newRefreshToken: string) { + const user = await this.findOneOrFail(id); + + user.refreshToken = newRefreshToken; + await this.userRepository.save(user); + } + async createUser(userDto: CreateUserDto): Promise { const existingUser = await this.userRepository.findOne({ where: [