import { Inject, Injectable, ForbiddenException } from '@nestjs/common' import * as bcrypt from 'bcrypt' import { PrismaService } from 'nestjs-prisma' import { JwtService } from '@nestjs/jwt' import { securityConfig, SecurityConfig } from 'src/common/configs' import { CreateUserDto } from 'src/users/dto/create-user.dto' import { EmailSendDto, EmailScene } from 'src/email/dto/email.dto' import { EmailService } from 'src/email/email.service' import { TokenService } from './token.service' import { UpdatePassword } from './dto/update-password.dto' import { DeleteUserDto } from './dto/delete-user.dto' @Injectable() export class UsersService { constructor( private jwtService: JwtService, private prismaService: PrismaService, private emailService: EmailService, private tokenService: TokenService, @Inject(securityConfig.KEY) private secureConfig: SecurityConfig, ) {} async register(userToCreate: CreateUserDto) { await this.verifyEmail( userToCreate.email, userToCreate.token, userToCreate.verifyCode, EmailScene.register, ) const hashedPassword = await bcrypt.hash( userToCreate.password, this.secureConfig.bcryptSaltOrRound, ) const user = await this.prismaService.user.create({ data: { username: userToCreate.username, email: userToCreate.email, password: hashedPassword, }, }) return this.tokenService.generateTokens({ userId: user.id }) } async deleteUser(userToDelete: DeleteUserDto, userId: string) { await this.verifyEmail( userToDelete.email, userToDelete.token, userToDelete.verifyCode, EmailScene.deleteUser, ) const user = await this.prismaService.user.findUniqueOrThrow({ where: { email: userToDelete.email }, }) if (user.id !== userId) { throw new ForbiddenException() } const passwordValid = await bcrypt.compare( user.password, userToDelete.password, ) if (!passwordValid) { throw new ForbiddenException('Invalid password') } return this.prismaService.user.delete({ where: { email: userToDelete.email }, }) } async updatePassword(payload: UpdatePassword) { await this.verifyEmail( payload.email, payload.token, payload.verifyCode, EmailScene.updatePassword, ) const hashedPassword = await bcrypt.hash( payload.password, this.secureConfig.bcryptSaltOrRound, ) const user = await this.prismaService.user.update({ where: { email: payload.email }, data: { password: hashedPassword }, }) return user } private async verifyEmail( email: string, token: string, verifyCode: string, scene: EmailScene, ) { const payload = this.jwtService.verify(token, { secret: this.emailService.getEmailJwtSecret(verifyCode, scene), }) if (payload.email !== email || payload.scene !== scene) { throw new ForbiddenException('请输入正确的邮箱验证码') } } }