refactor: controllers
This commit is contained in:
		
							parent
							
								
									d3c8626305
								
							
						
					
					
						commit
						a863806111
					
				| @ -1,14 +1,15 @@ | ||||
| import { | ||||
|   ConflictException, | ||||
|   Inject, | ||||
|   Injectable, | ||||
|   ConflictException, | ||||
|   ForbiddenException, | ||||
|   NotFoundException, | ||||
| } from '@nestjs/common' | ||||
| import { securityConfig, SecurityConfig } from 'src/common/configs' | ||||
| import { MailerService } from '@nestjs-modules/mailer' | ||||
| import { JwtService } from '@nestjs/jwt' | ||||
| import { EmailScene } from './dto/email.dto' | ||||
| import { PrismaService } from 'nestjs-prisma' | ||||
| import { EmailSendDto, EmailScene } from 'src/email/dto/email.dto' | ||||
| 
 | ||||
| @Injectable() | ||||
| export class EmailService { | ||||
| @ -55,10 +56,26 @@ export class EmailService { | ||||
|       subject: `【qiuxu.site】${this.subjectMap[scene]}`, | ||||
|       html: `您正在qiuxu.site${this.subjectMap[scene]},验证码为 <strong>${verifyCode}</strong>,30分钟内有效`, | ||||
|     }) | ||||
|     return registerToken | ||||
|     return { registerToken, userId: user.id } | ||||
|   } | ||||
| 
 | ||||
|   getEmailJwtSecret(verifyCode: string, scene: EmailScene) { | ||||
|   // TODO: 做成守卫?
 | ||||
|   async verifyEmail(data: { | ||||
|     email: string | ||||
|     token: string | ||||
|     verifyCode: string | ||||
|     scene: EmailScene | ||||
|   }) { | ||||
|     const { email, token, verifyCode, scene } = data | ||||
|     const payload = this.jwtService.verify<EmailSendDto>(token, { | ||||
|       secret: this.getEmailJwtSecret(verifyCode, scene), | ||||
|     }) | ||||
|     if (payload.email !== email || payload.scene !== scene) { | ||||
|       throw new ForbiddenException('请输入正确的邮箱验证码') | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private getEmailJwtSecret(verifyCode: string, scene: EmailScene) { | ||||
|     return this.secureConfig.jwt_access_secret + verifyCode + scene | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import { IsEmail, IsNotEmpty, IsStrongPassword } from 'class-validator' | ||||
| 
 | ||||
| export class ForgetPassword { | ||||
| export class ResetPassword { | ||||
|   @IsNotEmpty() | ||||
|   @IsEmail() | ||||
|   email: string | ||||
							
								
								
									
										93
									
								
								src/users/me.controller.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/users/me.controller.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,93 @@ | ||||
| import { | ||||
|   Controller, | ||||
|   Get, | ||||
|   Delete, | ||||
|   Patch, | ||||
|   Put, | ||||
|   Body, | ||||
|   UseInterceptors, | ||||
|   BadRequestException, | ||||
| } from '@nestjs/common' | ||||
| import { MeService } from './me.service' | ||||
| import { ApiTags, ApiOperation, ApiUnauthorizedResponse } from '@nestjs/swagger' | ||||
| import { User } from 'src/common/decorators/user.decorator' | ||||
| import { NeedAuth } from 'src/common/decorators/need-auth.decorator' | ||||
| import { PasswordInterceptor } from 'src/common/interceptors/password.interceptor' | ||||
| import { PrismaService } from 'nestjs-prisma' | ||||
| import { UserEntity } from './entities/user.entity' | ||||
| import { DeleteUserDto } from './dto/delete-user.dto' | ||||
| import { ChangePassword } from './dto/change-password.dto' | ||||
| import { UpdateUserDto } from './dto/update-user.dto' | ||||
| import { TokenRefreshPayload } from './dto/token.dto' | ||||
| 
 | ||||
| @Controller('api/users/me') | ||||
| @ApiTags('Me') | ||||
| export class MeController { | ||||
|   constructor( | ||||
|     private readonly meService: MeService, | ||||
|     private readonly prismaService: PrismaService, | ||||
|   ) {} | ||||
| 
 | ||||
|   @Get() | ||||
|   @ApiOperation({ summary: '获取用户信息' }) | ||||
|   @UseInterceptors(PasswordInterceptor) | ||||
|   @NeedAuth() | ||||
|   async getUserInfo(@User('userId') userId: string): Promise<UserEntity> { | ||||
|     return this.prismaService.user.findUniqueOrThrow({ | ||||
|       where: { id: userId }, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   @Patch() | ||||
|   @ApiOperation({ summary: '修改用户信息(用户名等)' }) | ||||
|   @UseInterceptors(PasswordInterceptor) | ||||
|   @NeedAuth() | ||||
|   async updateUserInfo( | ||||
|     @Body() payload: UpdateUserDto, | ||||
|     @User('userId') userId: string, | ||||
|   ): Promise<UserEntity> { | ||||
|     if (Object.keys(payload).length === 0) { | ||||
|       throw new BadRequestException() | ||||
|     } | ||||
|     return this.prismaService.user.update({ | ||||
|       where: { id: userId }, | ||||
|       data: payload, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   @Delete() | ||||
|   @NeedAuth() | ||||
|   @ApiOperation({ summary: '删除用户' }) | ||||
|   async deleteUser( | ||||
|     @Body() userData: DeleteUserDto, | ||||
|     @User('userId') userId: string, | ||||
|   ) { | ||||
|     return this.meService.deleteUser(userData, userId) | ||||
|   } | ||||
| 
 | ||||
|   @Patch('?field=password') | ||||
|   @NeedAuth() | ||||
|   @ApiOperation({ summary: '修改密码' }) | ||||
|   @UseInterceptors(PasswordInterceptor) | ||||
|   async changePassword( | ||||
|     @Body() payload: ChangePassword, | ||||
|     @User('userId') userId: string, | ||||
|   ) { | ||||
|     return this.meService.changePassword(payload, userId) | ||||
|   } | ||||
| 
 | ||||
|   @Patch('?field=email') | ||||
|   @NeedAuth() | ||||
|   @ApiOperation({ summary: '修改邮箱(TODO)' }) | ||||
|   @UseInterceptors(PasswordInterceptor) | ||||
|   async updateEmail(@Body() payload: unknown) { | ||||
|     return '修改邮箱' | ||||
|   } | ||||
| 
 | ||||
|   @Put('token') | ||||
|   @ApiOperation({ summary: '刷新token' }) | ||||
|   @ApiUnauthorizedResponse({ description: 'Unauthorized' }) | ||||
|   async updateAccessToken(@Body() payload: TokenRefreshPayload) { | ||||
|     return this.meService.updateAccessToken(payload.refreshToken) | ||||
|   } | ||||
| } | ||||
							
								
								
									
										95
									
								
								src/users/me.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								src/users/me.service.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| import { | ||||
|   Inject, | ||||
|   Injectable, | ||||
|   ForbiddenException, | ||||
|   UnauthorizedException, | ||||
| } 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 { EmailScene } from 'src/email/dto/email.dto' | ||||
| import { EmailService } from 'src/email/email.service' | ||||
| import { DeleteUserDto } from './dto/delete-user.dto' | ||||
| import { ChangePassword } from './dto/change-password.dto' | ||||
| import { TokenContnet } from './dto/token.dto' | ||||
| 
 | ||||
| @Injectable() | ||||
| export class MeService { | ||||
|   constructor( | ||||
|     private jwtService: JwtService, | ||||
|     private prismaService: PrismaService, | ||||
|     private emailService: EmailService, | ||||
|     @Inject(securityConfig.KEY) private secureConfig: SecurityConfig, | ||||
|   ) {} | ||||
| 
 | ||||
|   async deleteUser(userToDelete: DeleteUserDto, userId: string) { | ||||
|     const { email, token, verifyCode, password } = userToDelete | ||||
|     await this.emailService.verifyEmail({ | ||||
|       email, | ||||
|       token, | ||||
|       verifyCode, | ||||
|       scene: 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(password, user.password) | ||||
|     if (!passwordValid) { | ||||
|       throw new ForbiddenException('Invalid password') | ||||
|     } | ||||
| 
 | ||||
|     return this.prismaService.user.delete({ | ||||
|       where: { email: userToDelete.email }, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   async changePassword(payload: ChangePassword, userId: string) { | ||||
|     const user = await this.prismaService.user.findUniqueOrThrow({ | ||||
|       where: { id: userId }, | ||||
|     }) | ||||
|     await this.checkPassword(payload.oldPassword, user.password) | ||||
|     const hashedPassword = await bcrypt.hash( | ||||
|       payload.newPassword, | ||||
|       this.secureConfig.bcryptSaltOrRound, | ||||
|     ) | ||||
|     return this.prismaService.user.update({ | ||||
|       where: { id: userId }, | ||||
|       data: { password: hashedPassword }, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   async updateAccessToken(refreshToken: string) { | ||||
|     const { userId, iat } = this.jwtService.verify<TokenContnet>(refreshToken, { | ||||
|       secret: this.secureConfig.jwt_refresh_secret, | ||||
|     }) | ||||
|     const user = await this.prismaService.user.findUniqueOrThrow({ | ||||
|       where: { id: userId }, | ||||
|     }) | ||||
|     // TODO:不使用updatedAt,而是自定义的一个refreshTime字段
 | ||||
|     if (iat * 1000 < user.updatedAt.getTime()) { | ||||
|       throw new UnauthorizedException('token失效,请重新登录') | ||||
|     } | ||||
|     return { | ||||
|       userId, | ||||
|       refreshToken: refreshToken, | ||||
|       accessToken: this.jwtService.sign( | ||||
|         { userId }, | ||||
|         { | ||||
|           secret: this.secureConfig.jwt_refresh_secret, | ||||
|           expiresIn: this.secureConfig.refreshIn, | ||||
|         }, | ||||
|       ), | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private async checkPassword(pwd: string, hashPwd: string) { | ||||
|     const valid = await bcrypt.compare(pwd, hashPwd) | ||||
|     if (!valid) { | ||||
|       throw new ForbiddenException('Invalid password') | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -1,25 +0,0 @@ | ||||
| import { Controller, Post, Put, Body } from '@nestjs/common' | ||||
| import { ApiTags, ApiOperation, ApiUnauthorizedResponse } from '@nestjs/swagger' | ||||
| import { LoginInputDto } from './dto/login-input.dto' | ||||
| import { TokenRefreshPayload } from './dto/token.dto' | ||||
| import { TokenService } from './token.service' | ||||
| 
 | ||||
| @ApiTags('token') | ||||
| @Controller('api/token') | ||||
| export class TokenController { | ||||
|   constructor(private tokenService: TokenService) {} | ||||
| 
 | ||||
|   // TODO: 限制调用频率,避免暴力破解
 | ||||
|   @ApiOperation({ summary: '登录用户' }) | ||||
|   @Post() | ||||
|   async login(@Body() user: LoginInputDto) { | ||||
|     return this.tokenService.login(user.email, user.password) | ||||
|   } | ||||
| 
 | ||||
|   @ApiOperation({ summary: '刷新token' }) | ||||
|   @ApiUnauthorizedResponse({ description: 'Unauthorized' }) | ||||
|   @Put() | ||||
|   async refreshToken(@Body() payload: TokenRefreshPayload) { | ||||
|     return this.tokenService.refreshToken(payload.refreshToken) | ||||
|   } | ||||
| } | ||||
| @ -1,65 +0,0 @@ | ||||
| import { Inject, Injectable, UnauthorizedException } from '@nestjs/common' | ||||
| import * as bcrypt from 'bcrypt' | ||||
| import { PrismaService } from 'nestjs-prisma' | ||||
| import { Token, TokenPayload, TokenContnet } from './dto/token.dto' | ||||
| import { JwtService } from '@nestjs/jwt' | ||||
| import { securityConfig, SecurityConfig } from 'src/common/configs' | ||||
| 
 | ||||
| @Injectable() | ||||
| export class TokenService { | ||||
|   constructor( | ||||
|     private jwtService: JwtService, | ||||
|     private prismaService: PrismaService, | ||||
|     @Inject(securityConfig.KEY) | ||||
|     private secureConfig: SecurityConfig, | ||||
|   ) {} | ||||
| 
 | ||||
|   async login(email: string, password: string) { | ||||
|     const user = await this.prismaService.user.findUniqueOrThrow({ | ||||
|       where: { email }, | ||||
|     }) | ||||
| 
 | ||||
|     const passwordValid = await bcrypt.compare(password, user.password) | ||||
| 
 | ||||
|     if (!passwordValid) { | ||||
|       throw new UnauthorizedException('Invalid password') | ||||
|     } | ||||
| 
 | ||||
|     return this.generateTokens({ userId: user.id }) | ||||
|   } | ||||
| 
 | ||||
|   async refreshToken(token: string) { | ||||
|     const { userId, iat } = this.jwtService.verify<TokenContnet>(token, { | ||||
|       secret: this.secureConfig.jwt_refresh_secret, | ||||
|     }) | ||||
|     const user = await this.prismaService.user.findUniqueOrThrow({ | ||||
|       where: { id: userId }, | ||||
|     }) | ||||
|     if (iat * 1000 < user.updatedAt.getTime()) { | ||||
|       throw new UnauthorizedException('token失效,请重新登录') | ||||
|     } | ||||
|     return { | ||||
|       refreshToken: token, | ||||
|       accessToken: this.jwtService.sign( | ||||
|         { userId }, | ||||
|         { | ||||
|           secret: this.secureConfig.jwt_refresh_secret, | ||||
|           expiresIn: this.secureConfig.refreshIn, | ||||
|         }, | ||||
|       ), | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   generateTokens(payload: TokenPayload): Token { | ||||
|     const accessToken = this.jwtService.sign(payload, { | ||||
|       secret: this.secureConfig.jwt_access_secret, | ||||
|       expiresIn: this.secureConfig.expiresIn, | ||||
|     }) | ||||
|     const refreshToken = this.jwtService.sign(payload, { | ||||
|       secret: this.secureConfig.jwt_refresh_secret, | ||||
|       expiresIn: this.secureConfig.refreshIn, | ||||
|     }) | ||||
| 
 | ||||
|     return { accessToken, refreshToken } | ||||
|   } | ||||
| } | ||||
| @ -1,105 +1,34 @@ | ||||
| import { | ||||
|   Controller, | ||||
|   Get, | ||||
|   Post, | ||||
|   Delete, | ||||
|   Put, | ||||
|   Patch, | ||||
|   Body, | ||||
|   UseInterceptors, | ||||
|   BadRequestException, | ||||
| } from '@nestjs/common' | ||||
| import { Controller, Post, Patch, Body, Param } from '@nestjs/common' | ||||
| import { UsersService } from './users.service' | ||||
| import { ApiTags, ApiOperation } from '@nestjs/swagger' | ||||
| import { User } from 'src/common/decorators/user.decorator' | ||||
| import { NeedAuth } from 'src/common/decorators/need-auth.decorator' | ||||
| import { PasswordInterceptor } from 'src/common/interceptors/password.interceptor' | ||||
| import { PrismaService } from 'nestjs-prisma' | ||||
| import { UserEntity } from './entities/user.entity' | ||||
| import { CreateUserDto } from './dto/create-user.dto' | ||||
| import { ForgetPassword } from './dto/forget-password.dto' | ||||
| import { DeleteUserDto } from './dto/delete-user.dto' | ||||
| import { ChangePassword } from './dto/change-password.dto' | ||||
| import { UpdateUserDto } from './dto/update-user.dto' | ||||
| import { LoginInputDto } from './dto/login-input.dto' | ||||
| import { ResetPassword } from './dto/reset-password.dto' | ||||
| 
 | ||||
| @Controller('api/users') | ||||
| @ApiTags('Users') | ||||
| export class UsersController { | ||||
|   constructor( | ||||
|     private readonly userService: UsersService, | ||||
|     private readonly prismaService: PrismaService, | ||||
|   ) {} | ||||
|   constructor(private readonly usersService: UsersService) {} | ||||
| 
 | ||||
|   @ApiTags('Me') | ||||
|   @ApiOperation({ summary: '获取用户信息' }) | ||||
|   @UseInterceptors(PasswordInterceptor) | ||||
|   @NeedAuth() | ||||
|   @Get('me') | ||||
|   async getUserInfo(@User('userId') userId: string): Promise<UserEntity> { | ||||
|     return this.prismaService.user.findUniqueOrThrow({ where: { id: userId } }) | ||||
|   } | ||||
| 
 | ||||
|   @ApiTags('Me') | ||||
|   @ApiOperation({ summary: '修改用户信息(用户名等)' }) | ||||
|   @UseInterceptors(PasswordInterceptor) | ||||
|   @NeedAuth() | ||||
|   @Patch('me') | ||||
|   async updateUserInfo( | ||||
|     @Body() payload: UpdateUserDto, | ||||
|     @User('userId') userId: string, | ||||
|   ): Promise<UserEntity> { | ||||
|     if (Object.keys(payload).length === 0) { | ||||
|       throw new BadRequestException() | ||||
|     } | ||||
|     return this.prismaService.user.update({ | ||||
|       where: { id: userId }, | ||||
|       data: payload, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   @ApiTags('User') | ||||
|   @ApiOperation({ summary: '注册用户' }) | ||||
|   @Post() | ||||
|   async register(@Body() userData: CreateUserDto) { | ||||
|     return this.userService.register(userData) | ||||
|   @ApiOperation({ summary: '邮箱注册' }) | ||||
|   async registerByEmail(@Body() userData: CreateUserDto) { | ||||
|     return this.usersService.registerByEmail(userData) | ||||
|   } | ||||
| 
 | ||||
|   @ApiTags('Me') | ||||
|   @NeedAuth() | ||||
|   @ApiOperation({ summary: '删除用户' }) | ||||
|   @Delete('me') | ||||
|   async deleteUser( | ||||
|     @Body() userData: DeleteUserDto, | ||||
|     @User('userId') userId: string, | ||||
|   // TODO: 限制调用频率,避免暴力破解
 | ||||
|   @Post('token') | ||||
|   @ApiOperation({ summary: '邮箱登录' }) | ||||
|   async loginByEmail(@Body() user: LoginInputDto) { | ||||
|     return this.usersService.loginByEmail(user.email, user.password) | ||||
|   } | ||||
| 
 | ||||
|   @Patch(':id/?field=password') | ||||
|   @ApiOperation({ summary: '找回密码' }) | ||||
|   async forgetPassword( | ||||
|     @Body() payload: ResetPassword, | ||||
|     @Param('id') userId: string, | ||||
|   ) { | ||||
|     return this.userService.deleteUser(userData, userId) | ||||
|   } | ||||
| 
 | ||||
|   @ApiTags('Me') | ||||
|   @NeedAuth() | ||||
|   @ApiOperation({ summary: '修改密码' }) | ||||
|   @UseInterceptors(PasswordInterceptor) | ||||
|   @Patch('me/password') | ||||
|   async changePassword( | ||||
|     @Body() payload: ChangePassword, | ||||
|     @User('userId') userId: string, | ||||
|   ) { | ||||
|     return this.userService.changePasswor(payload, userId) | ||||
|   } | ||||
| 
 | ||||
|   @ApiTags('User') | ||||
|   @ApiOperation({ summary: '忘记密码' }) | ||||
|   @UseInterceptors(PasswordInterceptor) | ||||
|   @Patch('password') | ||||
|   async forgetPassword(@Body() payload: ForgetPassword): Promise<UserEntity> { | ||||
|     return this.userService.forgetPassword(payload) | ||||
|   } | ||||
| 
 | ||||
|   @ApiTags('Me') | ||||
|   @NeedAuth() | ||||
|   @ApiOperation({ summary: '修改邮箱(TODO)' }) | ||||
|   @UseInterceptors(PasswordInterceptor) | ||||
|   @Patch('me/email') | ||||
|   async updateEmail(@Body() payload: unknown) { | ||||
|     return '修改邮箱' | ||||
|     return this.usersService.resetPasswordByEmail(payload, userId) | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -3,12 +3,12 @@ import { UsersService } from './users.service' | ||||
| import { UsersController } from './users.controller' | ||||
| import { JwtService } from '@nestjs/jwt' | ||||
| import { EmailService } from 'src/email/email.service' | ||||
| import { TokenController } from './token.controller' | ||||
| import { TokenService } from './token.service' | ||||
| import { MeService } from './me.service' | ||||
| import { MeController } from './me.controller' | ||||
| 
 | ||||
| @Module({ | ||||
|   controllers: [UsersController, TokenController], | ||||
|   providers: [UsersService, JwtService, EmailService, TokenService], | ||||
|   controllers: [UsersController, MeController], | ||||
|   providers: [UsersService, JwtService, EmailService, MeService], | ||||
|   exports: [UsersService], | ||||
| }) | ||||
| export class UsersModule {} | ||||
|  | ||||
| @ -1,15 +1,13 @@ | ||||
| import { Inject, Injectable, ForbiddenException } from '@nestjs/common' | ||||
| import { Inject, Injectable, UnauthorizedException } 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 { securityConfig, type SecurityConfig } from 'src/common/configs' | ||||
| import { CreateUserDto } from 'src/users/dto/create-user.dto' | ||||
| import { EmailSendDto, EmailScene } from 'src/email/dto/email.dto' | ||||
| import { EmailScene } from 'src/email/dto/email.dto' | ||||
| import { EmailService } from 'src/email/email.service' | ||||
| import { TokenService } from './token.service' | ||||
| import { ForgetPassword } from './dto/forget-password.dto' | ||||
| import { DeleteUserDto } from './dto/delete-user.dto' | ||||
| import { ChangePassword } from './dto/change-password.dto' | ||||
| import { ResetPassword } from './dto/reset-password.dto' | ||||
| import { Token, TokenPayload } from './dto/token.dto' | ||||
| 
 | ||||
| @Injectable() | ||||
| export class UsersService { | ||||
| @ -17,109 +15,70 @@ export class UsersService { | ||||
|     private jwtService: JwtService, | ||||
|     private prismaService: PrismaService, | ||||
|     private emailService: EmailService, | ||||
|     private tokenService: TokenService, | ||||
|     @Inject(securityConfig.KEY) | ||||
|     private secureConfig: SecurityConfig, | ||||
|     @Inject(securityConfig.KEY) private secureConfig: SecurityConfig, | ||||
|   ) {} | ||||
| 
 | ||||
|   async register(userToCreate: CreateUserDto) { | ||||
|     await this.verifyEmail( | ||||
|       userToCreate.email, | ||||
|       userToCreate.token, | ||||
|       userToCreate.verifyCode, | ||||
|       EmailScene.register, | ||||
|     ) | ||||
|   async registerByEmail(userToCreate: CreateUserDto) { | ||||
|     const { email, token, verifyCode, username, password } = userToCreate | ||||
|     await this.emailService.verifyEmail({ | ||||
|       email, | ||||
|       token, | ||||
|       verifyCode, | ||||
|       scene: EmailScene.register, | ||||
|     }) | ||||
|     const hashedPassword = await bcrypt.hash( | ||||
|       userToCreate.password, | ||||
|       password, | ||||
|       this.secureConfig.bcryptSaltOrRound, | ||||
|     ) | ||||
|     const user = await this.prismaService.user.create({ | ||||
|       data: { | ||||
|         username: userToCreate.username, | ||||
|         email: userToCreate.email, | ||||
|         password: hashedPassword, | ||||
|       }, | ||||
|       data: { username, email, password: hashedPassword }, | ||||
|     }) | ||||
|     return this.tokenService.generateTokens({ userId: user.id }) | ||||
|     return this.generateTokens({ userId: user.id }) | ||||
|   } | ||||
| 
 | ||||
|   async deleteUser(userToDelete: DeleteUserDto, userId: string) { | ||||
|     await this.verifyEmail( | ||||
|       userToDelete.email, | ||||
|       userToDelete.token, | ||||
|       userToDelete.verifyCode, | ||||
|       EmailScene.deleteUser, | ||||
|     ) | ||||
|   async loginByEmail(email: string, password: string) { | ||||
|     const user = await this.prismaService.user.findUniqueOrThrow({ | ||||
|       where: { email: userToDelete.email }, | ||||
|       where: { email }, | ||||
|     }) | ||||
|     if (user.id !== userId) { | ||||
|       throw new ForbiddenException() | ||||
|     } | ||||
|     const passwordValid = await bcrypt.compare( | ||||
|       user.password, | ||||
|       userToDelete.password, | ||||
|     ) | ||||
| 
 | ||||
|     const passwordValid = await bcrypt.compare(password, user.password) | ||||
| 
 | ||||
|     if (!passwordValid) { | ||||
|       throw new ForbiddenException('Invalid password') | ||||
|       throw new UnauthorizedException('Invalid password') | ||||
|     } | ||||
| 
 | ||||
|     return this.prismaService.user.delete({ | ||||
|       where: { email: userToDelete.email }, | ||||
|     }) | ||||
|     return this.generateTokens({ userId: user.id }) | ||||
|   } | ||||
| 
 | ||||
|   async changePasswor(payload: ChangePassword, userId: string) { | ||||
|     const user = await this.prismaService.user.findFirstOrThrow({ | ||||
|       where: { id: userId }, | ||||
|   async resetPasswordByEmail(data: ResetPassword, userId: string) { | ||||
|     const { email, token, verifyCode, password } = data | ||||
|     await this.emailService.verifyEmail({ | ||||
|       email, | ||||
|       token, | ||||
|       verifyCode, | ||||
|       scene: EmailScene.forgetPassword, | ||||
|     }) | ||||
|     await this.checkPassword(payload.oldPassword, user.password) | ||||
|     const hashedPassword = await bcrypt.hash( | ||||
|       payload.newPassword, | ||||
|       this.secureConfig.bcryptSaltOrRound, | ||||
|     ) | ||||
|     return this.prismaService.user.update({ | ||||
|       where: { id: userId }, | ||||
|       data: { password: hashedPassword }, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   async forgetPassword(payload: ForgetPassword) { | ||||
|     await this.verifyEmail( | ||||
|       payload.email, | ||||
|       payload.token, | ||||
|       payload.verifyCode, | ||||
|       EmailScene.forgetPassword, | ||||
|     ) | ||||
|     const hashedPassword = await bcrypt.hash( | ||||
|       payload.password, | ||||
|       password, | ||||
|       this.secureConfig.bcryptSaltOrRound, | ||||
|     ) | ||||
|     const user = await this.prismaService.user.update({ | ||||
|       where: { email: payload.email }, | ||||
|       where: { id: userId }, | ||||
|       data: { password: hashedPassword }, | ||||
|     }) | ||||
|     return user | ||||
|     return this.generateTokens({ userId: user.id }) | ||||
|   } | ||||
| 
 | ||||
|   private async checkPassword(pwd: string, hashPwd: string) { | ||||
|     const valid = await bcrypt.compare(pwd, hashPwd) | ||||
|     if (!valid) { | ||||
|       throw new ForbiddenException('Invalid password') | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   private async verifyEmail( | ||||
|     email: string, | ||||
|     token: string, | ||||
|     verifyCode: string, | ||||
|     scene: EmailScene, | ||||
|   ) { | ||||
|     const payload = this.jwtService.verify<EmailSendDto>(token, { | ||||
|       secret: this.emailService.getEmailJwtSecret(verifyCode, scene), | ||||
|   private generateTokens(payload: TokenPayload): Token { | ||||
|     const accessToken = this.jwtService.sign(payload, { | ||||
|       secret: this.secureConfig.jwt_access_secret, | ||||
|       expiresIn: this.secureConfig.expiresIn, | ||||
|     }) | ||||
|     if (payload.email !== email || payload.scene !== scene) { | ||||
|       throw new ForbiddenException('请输入正确的邮箱验证码') | ||||
|     } | ||||
|     const refreshToken = this.jwtService.sign(payload, { | ||||
|       secret: this.secureConfig.jwt_refresh_secret, | ||||
|       expiresIn: this.secureConfig.refreshIn, | ||||
|     }) | ||||
| 
 | ||||
|     return { accessToken, refreshToken, ...payload } | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user