import { HttpException, HttpStatus, Injectable, UnauthorizedException, BadRequestException, NotFoundException, UseGuards } from "@nestjs/common"; import { InjectModel } from "@nestjs/sequelize"; import { User } from "./entities/user.entity"; import * as bcrypt from "bcrypt"; import { JwtService } from "@nestjs/jwt"; 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"; @Injectable() export class UsersService { constructor( @InjectModel(User) private readonly userModel: typeof User, private readonly jwtService: JwtService, private readonly configService: ConfigService, ) {} // Register method async register(createUserDto: CreateUserDto): Promise<{ message }> { try { createUserDto.password = await bcrypt.hash(createUserDto.password, parseInt(process.env.BCRYPT_SALT_ROUNDS || "10", 10)); const userExists = await this.userModel.findOne({ where: { email: createUserDto.email }, }); if (userExists) { throw new BadRequestException("Email is already registered."); } const user = await this.userModel.create(createUserDto); return { message: "user registered successful" }; } catch (error) { if (error instanceof HttpException) { throw error; } throw new HttpException("An unexpected error occurred. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); } } // Login method async login(loginUserDto: LoginUserDto): Promise<{ accessToken: string; refreshToken: string }> { try { const user = await this.userModel.findOne({ where: { email: loginUserDto.email }, }); if (!user) { throw new UnauthorizedException("Invalid email or password."); } const passwordMatch = await bcrypt.compare(loginUserDto.password, user.password); if (!passwordMatch) { throw new UnauthorizedException("Invalid email or password."); } const accessToken = this.jwtService.sign( { id: user.id, role: user.role }, { secret: this.configService.get("JWT_SECRET"), expiresIn: this.configService.get("JWT_EXPIRES") || "1h", }, ); const refreshToken = this.jwtService.sign( { id: user.id }, { secret: this.configService.get("JWT_REFRESH_SECRET"), expiresIn: this.configService.get("JWT_REFRESH_EXPIRES") || "7d", }, ); await this.userModel.update( { refreshToken }, // { where: { id: user.id } }, ); return { accessToken: accessToken, refreshToken: refreshToken }; } catch (error) { if (error instanceof HttpException) { throw error; } throw new HttpException("An unexpected error occurred. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); } } // getting access token async newAccessToken(refreshToken: string) { if (!refreshToken) { throw new HttpException({ message: "Refresh token is required." }, HttpStatus.BAD_REQUEST); } let decoded; try { decoded = this.jwtService.verify(refreshToken, { secret: process.env.JWT_REFRESH_SECRET }); } catch (error) { throw new HttpException({ message: "Invalid or expired token." }, HttpStatus.UNAUTHORIZED); } const user = await this.userModel.findOne({ where: { id: decoded.id }, }); if (!user) { throw new HttpException({ message: "User not found." }, HttpStatus.NOT_FOUND); } const accessToken = this.jwtService.sign( { id: user.id, role: user.role }, { secret: this.configService.get("JWT_SECRET"), expiresIn: this.configService.get("JWT_EXPIRES") || "1h", }, ); return { accessToken }; } //logout (delete refresh token from database) async logout(userId: number) { const user = await this.userModel.findOne({ where: { id: userId }, }); if (!user) { throw new HttpException("User not found.", HttpStatus.NOT_FOUND); } await this.userModel.update({ refreshToken: null }, { where: { id: userId } }); return { message: "logout is successful" }; } //get information user method async getProfile(userId: number): Promise { try { const user = await this.userModel.findOne({ where: { id: userId }, attributes: { exclude: ["password"] }, }); if (!user) { throw new HttpException("User not found.", HttpStatus.NOT_FOUND); } return user; } catch (error) { throw new HttpException("An unexpected error occurred. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); } } //edit profile user method async editProfile(userId: number, updateUserDto: UpdateUserDto) { try { let user = await this.userModel.findOne({ where: { id: userId } }); if (!user) { throw new NotFoundException("User not found."); } if (updateUserDto.password) { updateUserDto.password = await bcrypt.hash(updateUserDto.password, 10); } await user.update(updateUserDto); user = await this.userModel.findOne({ where: { id: userId }, attributes: { exclude: ["password"] }, }); return { message: "User account updated successfully.", user }; } catch (error) { throw new HttpException("An unexpected error occurred. Please try again later.", HttpStatus.INTERNAL_SERVER_ERROR); } } //get users list async findAll(): Promise { try { return await this.userModel.findAll(); } catch (error) { throw new HttpException("An unexpected error occurred while fetching users.", HttpStatus.INTERNAL_SERVER_ERROR); } } //get users list async findSpecificUserInfoByUser(userId: number): Promise { try { const user = await this.userModel.findByPk(userId); if (!user) { throw new HttpException("User not found.", HttpStatus.NOT_FOUND); } return user; } catch (error) { throw new HttpException("An unexpected error occurred while fetching user information.", HttpStatus.INTERNAL_SERVER_ERROR); } } //delete a specific user by admin async deleteUser(userId: number) { const user = await this.userModel.findOne({ where: { id: userId }, }); if (!user) { throw new HttpException("User not found.", HttpStatus.NOT_FOUND); } await this.userModel.destroy({ where: { id: userId }, }); return { message: "user deleted successful" }; } }