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 getRegisterToken(email: string, scene: EmailScene) { | ||||
|     const user = await this.prismaService.user.findUnique({ where: { email } }) | ||||
|     if (user) { | ||||
|       throw new ConflictException(`邮箱${email}已注册`) | ||||
|   async sendEmailToken(email: string, scene: EmailScene) { | ||||
|     switch (scene) { | ||||
|       case EmailScene.register: | ||||
|         const user = await this.prismaService.user.findUnique({ | ||||
|           where: { email }, | ||||
|         }) | ||||
|         if (user) { | ||||
|           throw new ConflictException(`邮箱${email}已注册`) | ||||
|         } | ||||
|         break | ||||
|       case EmailScene.updatePassword: | ||||
|         await this.prismaService.user.findUniqueOrThrow({ | ||||
|           where: { email }, | ||||
|         }) | ||||
|         break | ||||
|       case EmailScene.updateEmail: | ||||
|         await this.prismaService.user.findUniqueOrThrow({ | ||||
|           where: { email }, | ||||
|         }) | ||||
|         break | ||||
|     } | ||||
|     const verificationCode = this.generateVerificationCode() | ||||
| 
 | ||||
|     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