From 132dfacd5a9b276a3c668b32ea71d40bc8f343a5 Mon Sep 17 00:00:00 2001 From: aliMohtarami Date: Sat, 18 Jan 2025 12:17:16 +0330 Subject: [PATCH] fix user edit profile service --- src/users/dto/create-user.dto.ts | 4 +++ src/users/dto/update-user.dto.ts | 9 +++++ src/users/users.controller.ts | 10 +++--- src/users/users.service.ts | 58 ++++++++++++++++++++++++++------ 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/users/dto/create-user.dto.ts b/src/users/dto/create-user.dto.ts index 0594f8a..32c3316 100644 --- a/src/users/dto/create-user.dto.ts +++ b/src/users/dto/create-user.dto.ts @@ -8,6 +8,10 @@ export class CreateUserDto { @IsNotEmpty({ message: "Password is required" }) password: string; + @IsString() + @IsNotEmpty({ message: "Password is required" }) + role: string; + @IsString() @IsNotEmpty({ message: "First name is required" }) firstName: string; diff --git a/src/users/dto/update-user.dto.ts b/src/users/dto/update-user.dto.ts index ff19282..d529a2c 100644 --- a/src/users/dto/update-user.dto.ts +++ b/src/users/dto/update-user.dto.ts @@ -1,6 +1,7 @@ import { IsOptional, IsString, IsEmail, IsEnum } from 'class-validator'; import {Gender } from '../entities/user.entity'; +import { Exclude } from 'class-transformer'; export class UpdateUserDto { @IsOptional() @@ -15,6 +16,10 @@ export class UpdateUserDto { @IsString() password?: string; + @IsOptional() + @IsString() + role?: string; + @IsOptional() @IsString() firstName?: string; @@ -30,4 +35,8 @@ export class UpdateUserDto { @IsOptional() @IsEnum(Gender) gender?: Gender; + + @Exclude() + @IsOptional() + refreshToken?: string; } diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 6cb0fb5..5e86c86 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -6,18 +6,19 @@ import { LoginUserDto } from "./dto/login-user.dto"; import { JwtAuthGuard } from "src/guard/auth.guard"; import { UpdateUserDto } from "./dto/update-user.dto"; import { RoleGuard } from "src/guard/role.guard"; +import { plainToInstance } from "class-transformer"; @Controller("user") export class UsersController { constructor(private readonly usersService: UsersService) {} //register as user @Post("register") - async register(@Body() createUserDto: CreateUserDto): Promise<{ message }> { + async register(@Body() createUserDto: CreateUserDto) { return this.usersService.register(createUserDto); } //login as user @Post("login") - async login(@Body() loginUserDto: LoginUserDto): Promise<{ accessToken; refreshToken }> { + async login(@Body() loginUserDto: LoginUserDto) { return this.usersService.login(loginUserDto); } //get access token @@ -44,7 +45,8 @@ export class UsersController { @Put() async editProfile(@Request() req, @Body() updateUserDto: UpdateUserDto) { const userId = req.user.id; - return this.usersService.editProfile(userId, updateUserDto); + const { refreshToken, ...sanitizedDto } = updateUserDto; + return this.usersService.editProfile(userId, sanitizedDto); } /////////////////////////////////////admin access endpoints///////////////////////////////////////////////////////// //get users list (admin) @@ -56,7 +58,7 @@ export class UsersController { // get a specific user info (admin) @UseGuards(RoleGuard) @Get("users/:id") - async findSpecificUserInfoByUser(@Param("id") id): Promise { + async findSpecificUserInfoByUser(@Param("id") id) { return this.usersService.findSpecificUserInfoByUser(id); } // delete a specific user (admin) diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 4cc60dc..2525b72 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -7,6 +7,7 @@ import { ConfigService } from "@nestjs/config"; import { CreateUserDto } from "./dto/create-user.dto"; import { LoginUserDto } from "./dto/login-user.dto"; import { UpdateUserDto } from "./dto/update-user.dto"; +import e from "express"; @Injectable() export class UsersService { @@ -17,7 +18,7 @@ export class UsersService { ) {} // Register method - async register(createUserDto: CreateUserDto): Promise<{ message: string }> { + async register(createUserDto: CreateUserDto){ try { createUserDto.password = await bcrypt.hash(createUserDto.password, parseInt(process.env.BCRYPT_SALT_ROUNDS || "10", 10)); @@ -69,7 +70,7 @@ export class UsersService { } } // Login method - async login(loginUserDto: LoginUserDto): Promise<{ accessToken: string , refreshToken:string}> { + async login(loginUserDto: LoginUserDto){ try { const user = await this.userModel.findOne({ where: { email: loginUserDto.email, username:loginUserDto.username }, @@ -114,7 +115,7 @@ export class UsersService { } } // getting access token - async newAccessToken(refreshToken: string) { + async newAccessToken(refreshToken: string){ if (!refreshToken) { throw new HttpException("Refresh token is required.", HttpStatus.BAD_REQUEST); } @@ -146,7 +147,7 @@ export class UsersService { return { accessToken }; } //logout (delete refresh token from database) - async logout(userId: number): Promise<{ message: string }> { + async logout(userId: number){ if (!userId) { throw new HttpException("User ID is required.", HttpStatus.BAD_REQUEST); } @@ -165,7 +166,7 @@ export class UsersService { } } //get information user method - async getProfile(userId: number): Promise { + async getProfile(userId: number){ try { const user = await this.userModel.findOne({ where: { id: userId }, @@ -186,19 +187,56 @@ export class UsersService { if (!user) { throw new NotFoundException("User not found."); } - if (updateUserDto.password) { - updateUserDto.password = await bcrypt.hash(updateUserDto.password, 10); + + const { refreshToken, ...allowedUpdates } = updateUserDto; + + if (allowedUpdates.username) { + const usernameExists = await this.userModel.findOne({ + where: { username: allowedUpdates.username }, + }); + if (usernameExists && usernameExists.id !== userId) { + throw new BadRequestException("Username is already in use."); + } + } + + if (allowedUpdates.phoneNumber) { + const phoneNumberExists = await this.userModel.findOne({ + where: { phoneNumber: allowedUpdates.phoneNumber }, + }); + if (phoneNumberExists && phoneNumberExists.id !== userId) { + throw new BadRequestException("Phone number is already in use."); + } + } + + if (allowedUpdates.email) { + const emailExists = await this.userModel.findOne({ + where: { email: allowedUpdates.email }, + }); + if (emailExists && emailExists.id !== userId) { + throw new BadRequestException("Email is already in use."); + } + } + + if (allowedUpdates.password) { + allowedUpdates.password = await bcrypt.hash(allowedUpdates.password, 10); } - await user.update(updateUserDto); + + await user.update(allowedUpdates); + user = await this.userModel.findOne({ where: { id: userId }, - attributes: { exclude: ["password"] }, + attributes: { exclude: ["password", "refreshToken"] }, }); + return { message: "User account updated successfully.", user }; } catch (error) { + console.error(error) + if (error instanceof HttpException) { + throw error; + } throw new HttpException("An unexpected error occurred. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); } - } + } //get users list async findAll(): Promise { try {