update password
This commit is contained in:
		
							parent
							
								
									aa3b2cb817
								
							
						
					
					
						commit
						f1457864df
					
				@ -3,7 +3,8 @@ import { IsEmail, IsNotEmpty } from 'class-validator'
 | 
			
		||||
 | 
			
		||||
export enum EmailScene {
 | 
			
		||||
  register = 'register',
 | 
			
		||||
  forgetPassword = 'forgetPassword',
 | 
			
		||||
  updatePassword = 'updatePassword',
 | 
			
		||||
  updateEmail = 'updateEmail',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class EmailSendDto {
 | 
			
		||||
 | 
			
		||||
@ -1,22 +1,16 @@
 | 
			
		||||
import { Body, Controller, Query, Post } from '@nestjs/common'
 | 
			
		||||
import { Body, Controller, Post } from '@nestjs/common'
 | 
			
		||||
import { EmailService } from './email.service'
 | 
			
		||||
import { ApiTags, ApiOperation, ApiQuery } from '@nestjs/swagger'
 | 
			
		||||
import { EmailSendDto, EmailScene } from './dto/email.dto'
 | 
			
		||||
import { ApiTags, ApiOperation } from '@nestjs/swagger'
 | 
			
		||||
import { EmailSendDto } from './dto/email.dto'
 | 
			
		||||
 | 
			
		||||
@ApiTags('Email')
 | 
			
		||||
@Controller('api/email')
 | 
			
		||||
export class EmailController {
 | 
			
		||||
  constructor(private readonly emailService: EmailService) {}
 | 
			
		||||
 | 
			
		||||
  // @ApiOperation({ summary: '测试邮件' })
 | 
			
		||||
  // @Post('test')
 | 
			
		||||
  // async sendEmailTo(@Body() payload: EmailDto) {
 | 
			
		||||
  //   return this.emailService.sendEmailTo(payload.email)
 | 
			
		||||
  // }
 | 
			
		||||
 | 
			
		||||
  @ApiOperation({ summary: '发送邮箱验证码' })
 | 
			
		||||
  @Post('verificationCode')
 | 
			
		||||
  async getRegisterToken(@Body() payload: EmailSendDto) {
 | 
			
		||||
    return this.emailService.getRegisterToken(payload.email, payload.scene)
 | 
			
		||||
  @Post('verifyCode')
 | 
			
		||||
  async sendEmailCode(@Body() payload: EmailSendDto) {
 | 
			
		||||
    return this.emailService.sendEmailToken(payload.email, payload.scene)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,11 @@ import { PrismaService } from 'nestjs-prisma'
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class EmailService {
 | 
			
		||||
  private subjectMap = {
 | 
			
		||||
    [EmailScene.register]: '注册账号',
 | 
			
		||||
    [EmailScene.updatePassword]: '修改密码',
 | 
			
		||||
    [EmailScene.updateEmail]: '修改邮箱',
 | 
			
		||||
  }
 | 
			
		||||
  constructor(
 | 
			
		||||
    private prismaService: PrismaService,
 | 
			
		||||
    private mailerService: MailerService,
 | 
			
		||||
@ -15,42 +20,46 @@ export class EmailService {
 | 
			
		||||
    private secureConfig: SecurityConfig,
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async sendEmailTo(email: string) {
 | 
			
		||||
    return this.mailerService.sendMail({
 | 
			
		||||
      to: email, // list of receivers
 | 
			
		||||
      subject: 'Testing Nest Mailermodule with template ✔',
 | 
			
		||||
      template: 'index', // The `.pug` or `.hbs` extension is appended automatically.
 | 
			
		||||
      context: {
 | 
			
		||||
        // Data to be sent to template engine.
 | 
			
		||||
        code: 'cf1a3f828287',
 | 
			
		||||
        username: 'john doe',
 | 
			
		||||
      },
 | 
			
		||||
  async sendEmailToken(email: string, scene: EmailScene) {
 | 
			
		||||
    switch (scene) {
 | 
			
		||||
      case EmailScene.register:
 | 
			
		||||
        const user = await this.prismaService.user.findUnique({
 | 
			
		||||
          where: { email },
 | 
			
		||||
        })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async getRegisterToken(email: string, scene: EmailScene) {
 | 
			
		||||
    const user = await this.prismaService.user.findUnique({ where: { email } })
 | 
			
		||||
        if (user) {
 | 
			
		||||
          throw new ConflictException(`邮箱${email}已注册`)
 | 
			
		||||
        }
 | 
			
		||||
    const verificationCode = this.generateVerificationCode()
 | 
			
		||||
        break
 | 
			
		||||
      case EmailScene.updatePassword:
 | 
			
		||||
        await this.prismaService.user.findUniqueOrThrow({
 | 
			
		||||
          where: { email },
 | 
			
		||||
        })
 | 
			
		||||
        break
 | 
			
		||||
      case EmailScene.updateEmail:
 | 
			
		||||
        await this.prismaService.user.findUniqueOrThrow({
 | 
			
		||||
          where: { email },
 | 
			
		||||
        })
 | 
			
		||||
        break
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const verifyCode = this.generateVerifyCode()
 | 
			
		||||
    const registerToken = this.jwtService.sign(
 | 
			
		||||
      { email, scene },
 | 
			
		||||
      { secret: this.getEmailJwtSecret(verificationCode, scene) },
 | 
			
		||||
      { secret: this.getEmailJwtSecret(verifyCode, scene) },
 | 
			
		||||
    )
 | 
			
		||||
    await this.mailerService.sendMail({
 | 
			
		||||
      to: email,
 | 
			
		||||
      subject: '注册qiuxu.site',
 | 
			
		||||
      html: `您正在注册qiuxu.site,验证码为 <strong>${verificationCode}</strong>,30分钟内有效`,
 | 
			
		||||
      subject: `【qiuxu.site】${this.subjectMap[scene]}`,
 | 
			
		||||
      html: `您正在qiuxu.site${this.subjectMap[scene]},验证码为 <strong>${verifyCode}</strong>,30分钟内有效`,
 | 
			
		||||
    })
 | 
			
		||||
    return registerToken
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getEmailJwtSecret(verificationCode: string, scene: EmailScene) {
 | 
			
		||||
    return this.secureConfig.jwt_access_secret + verificationCode + scene
 | 
			
		||||
  getEmailJwtSecret(verifyCode: string, scene: EmailScene) {
 | 
			
		||||
    return this.secureConfig.jwt_access_secret + verifyCode + scene
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private generateVerificationCode() {
 | 
			
		||||
  private generateVerifyCode() {
 | 
			
		||||
    return Math.floor(Math.random() * 899999 + 100000).toString()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,7 +22,7 @@ export class CreateUserDto {
 | 
			
		||||
  /** @description 验证码 */
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @Length(6, 6)
 | 
			
		||||
  verificationCode: string
 | 
			
		||||
  verifyCode: string
 | 
			
		||||
 | 
			
		||||
  /** @description 发送邮箱接口返回的Token */
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										17
									
								
								src/users/dto/update-password.dto.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/users/dto/update-password.dto.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
import { IsEmail, IsNotEmpty, IsStrongPassword } from 'class-validator'
 | 
			
		||||
 | 
			
		||||
export class UpdatePassword {
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @IsEmail()
 | 
			
		||||
  email: string
 | 
			
		||||
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  verifyCode: string
 | 
			
		||||
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  token: string
 | 
			
		||||
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @IsStrongPassword()
 | 
			
		||||
  password: string
 | 
			
		||||
}
 | 
			
		||||
@ -1,4 +1,11 @@
 | 
			
		||||
import { Controller, Get, Post, Body, UseInterceptors } from '@nestjs/common'
 | 
			
		||||
import {
 | 
			
		||||
  Controller,
 | 
			
		||||
  Get,
 | 
			
		||||
  Post,
 | 
			
		||||
  Patch,
 | 
			
		||||
  Body,
 | 
			
		||||
  UseInterceptors,
 | 
			
		||||
} from '@nestjs/common'
 | 
			
		||||
import { UsersService } from './users.service'
 | 
			
		||||
import { ApiTags, ApiOperation } from '@nestjs/swagger'
 | 
			
		||||
import { User } from 'src/common/decorators/user.decorator'
 | 
			
		||||
@ -7,6 +14,7 @@ import { PasswordInterceptor } from 'src/common/interceptors/password.intercepto
 | 
			
		||||
import { PrismaService } from 'nestjs-prisma'
 | 
			
		||||
import { UserEntity } from './entities/user.entity'
 | 
			
		||||
import { CreateUserDto } from './dto/create-user.dto'
 | 
			
		||||
import { UpdatePassword } from './dto/update-password.dto'
 | 
			
		||||
 | 
			
		||||
@ApiTags('User')
 | 
			
		||||
@Controller('api/users')
 | 
			
		||||
@ -29,4 +37,17 @@ export class UsersController {
 | 
			
		||||
  async register(@Body() userData: CreateUserDto) {
 | 
			
		||||
    return this.userService.register(userData)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @ApiOperation({ summary: '修改密码' })
 | 
			
		||||
  @UseInterceptors(PasswordInterceptor)
 | 
			
		||||
  @Patch('me/password')
 | 
			
		||||
  async updatePassord(@Body() payload: UpdatePassword): Promise<UserEntity> {
 | 
			
		||||
    return this.userService.updatePassword(payload)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // @ApiOperation({ summary: '修改邮箱' })
 | 
			
		||||
  // @Patch('me/email')
 | 
			
		||||
  // async updateEmail(@Body() payload: unknown) {
 | 
			
		||||
  //   return
 | 
			
		||||
  // }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -6,9 +6,8 @@ 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 { Prisma } from '@prisma/client'
 | 
			
		||||
import { TokenService } from './token.service'
 | 
			
		||||
 | 
			
		||||
import { UpdatePassword } from './dto/update-password.dto'
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class UsersService {
 | 
			
		||||
  constructor(
 | 
			
		||||
@ -24,27 +23,9 @@ export class UsersService {
 | 
			
		||||
    await this.verifyEmail(
 | 
			
		||||
      userToCreate.email,
 | 
			
		||||
      userToCreate.token,
 | 
			
		||||
      userToCreate.verificationCode,
 | 
			
		||||
      userToCreate.verifyCode,
 | 
			
		||||
      EmailScene.register,
 | 
			
		||||
    )
 | 
			
		||||
    return this.createUser(userToCreate)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async verifyEmail(
 | 
			
		||||
    email: string,
 | 
			
		||||
    token: string,
 | 
			
		||||
    verificationCode: string,
 | 
			
		||||
    scene: EmailScene,
 | 
			
		||||
  ) {
 | 
			
		||||
    const payload = this.jwtService.verify<EmailSendDto>(token, {
 | 
			
		||||
      secret: this.emailService.getEmailJwtSecret(verificationCode, scene),
 | 
			
		||||
    })
 | 
			
		||||
    if (payload.email !== email || payload.scene !== scene) {
 | 
			
		||||
      throw new ForbiddenException('请输入正确的邮箱验证码')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private async createUser(userToCreate: Prisma.UserCreateInput) {
 | 
			
		||||
    const hashedPassword = await bcrypt.hash(
 | 
			
		||||
      userToCreate.password,
 | 
			
		||||
      this.secureConfig.bcryptSaltOrRound,
 | 
			
		||||
@ -58,4 +39,36 @@ export class UsersService {
 | 
			
		||||
    })
 | 
			
		||||
    return this.tokenService.generateTokens({ userId: user.id })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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<EmailSendDto>(token, {
 | 
			
		||||
      secret: this.emailService.getEmailJwtSecret(verifyCode, scene),
 | 
			
		||||
    })
 | 
			
		||||
    if (payload.email !== email || payload.scene !== scene) {
 | 
			
		||||
      throw new ForbiddenException('请输入正确的邮箱验证码')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user