You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

192 lines
6.6 KiB

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<string>("JWT_SECRET"),
expiresIn: this.configService.get<string | number>("JWT_EXPIRES") || "1h",
},
);
const refreshToken = this.jwtService.sign(
{ id: user.id },
{
secret: this.configService.get<string>("JWT_REFRESH_SECRET"),
expiresIn: this.configService.get<string | number>("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<string>("JWT_SECRET"),
expiresIn: this.configService.get<string | number>("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<User> {
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<User[]> {
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<User> {
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" };
}
}