♻️ merge auth module into users module
This commit is contained in:
		
							parent
							
								
									c4f6e168e5
								
							
						
					
					
						commit
						aa3b2cb817
					
				@ -2,8 +2,8 @@ import { Logger, Module } from '@nestjs/common'
 | 
				
			|||||||
import { ConfigModule } from '@nestjs/config'
 | 
					import { ConfigModule } from '@nestjs/config'
 | 
				
			||||||
import { nestConfig, securityConfig, emailConfig } from 'src/common/configs'
 | 
					import { nestConfig, securityConfig, emailConfig } from 'src/common/configs'
 | 
				
			||||||
import { PrismaModule, loggingMiddleware } from 'nestjs-prisma'
 | 
					import { PrismaModule, loggingMiddleware } from 'nestjs-prisma'
 | 
				
			||||||
 | 
					import { JwtAuthStrategy } from './common/guards/jwt-auth.strategy'
 | 
				
			||||||
import { UsersModule } from './users/users.module'
 | 
					import { UsersModule } from './users/users.module'
 | 
				
			||||||
import { AuthModule } from './auth/auth.module'
 | 
					 | 
				
			||||||
import { EmailModule } from './email/email.module'
 | 
					import { EmailModule } from './email/email.module'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Module({
 | 
					@Module({
 | 
				
			||||||
@ -25,9 +25,8 @@ import { EmailModule } from './email/email.module'
 | 
				
			|||||||
    }),
 | 
					    }),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    UsersModule,
 | 
					    UsersModule,
 | 
				
			||||||
    AuthModule,
 | 
					 | 
				
			||||||
    EmailModule,
 | 
					    EmailModule,
 | 
				
			||||||
  ],
 | 
					  ],
 | 
				
			||||||
  controllers: [],
 | 
					  providers: [JwtAuthStrategy],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class AppModule {}
 | 
					export class AppModule {}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,31 +0,0 @@
 | 
				
			|||||||
import { Body, Controller, Post, Put } from '@nestjs/common'
 | 
					 | 
				
			||||||
import { AuthService } from './auth.service'
 | 
					 | 
				
			||||||
import { CreateUserDto } from 'src/users/dto/create-user.dto'
 | 
					 | 
				
			||||||
import { ApiTags, ApiOperation, ApiUnauthorizedResponse } from '@nestjs/swagger'
 | 
					 | 
				
			||||||
import { LoginInputDto } from './dto/login-input.dto'
 | 
					 | 
				
			||||||
import { TokenRefreshPayload } from './dto/token.dto'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ApiTags('Auth')
 | 
					 | 
				
			||||||
@Controller('api/auth')
 | 
					 | 
				
			||||||
export class AuthController {
 | 
					 | 
				
			||||||
  constructor(private readonly authService: AuthService) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @ApiOperation({ summary: '注册用户,返回token' })
 | 
					 | 
				
			||||||
  @Post('register')
 | 
					 | 
				
			||||||
  async register(@Body() userData: CreateUserDto) {
 | 
					 | 
				
			||||||
    return this.authService.register(userData)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @ApiOperation({ summary: '登录用户,返回token' })
 | 
					 | 
				
			||||||
  @Post('login')
 | 
					 | 
				
			||||||
  async login(@Body() user: LoginInputDto) {
 | 
					 | 
				
			||||||
    return this.authService.login(user.email, user.password)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @ApiOperation({ summary: '刷新token' })
 | 
					 | 
				
			||||||
  @ApiUnauthorizedResponse({ description: 'Unauthorized' })
 | 
					 | 
				
			||||||
  @Put('token')
 | 
					 | 
				
			||||||
  async refreshToken(@Body() payload: TokenRefreshPayload) {
 | 
					 | 
				
			||||||
    return this.authService.refreshToken(payload.refreshToken)
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,12 +0,0 @@
 | 
				
			|||||||
import { Module } from '@nestjs/common'
 | 
					 | 
				
			||||||
import { AuthService } from './auth.service'
 | 
					 | 
				
			||||||
import { AuthController } from './auth.controller'
 | 
					 | 
				
			||||||
import { JwtService } from '@nestjs/jwt'
 | 
					 | 
				
			||||||
import { JwtStrategy } from './strategies/jwt.strategy'
 | 
					 | 
				
			||||||
import { EmailService } from 'src/email/email.service'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Module({
 | 
					 | 
				
			||||||
  controllers: [AuthController],
 | 
					 | 
				
			||||||
  providers: [AuthService, JwtService, JwtStrategy, EmailService],
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
export class AuthModule {}
 | 
					 | 
				
			||||||
@ -1,95 +0,0 @@
 | 
				
			|||||||
import {
 | 
					 | 
				
			||||||
  Inject,
 | 
					 | 
				
			||||||
  Injectable,
 | 
					 | 
				
			||||||
  ForbiddenException,
 | 
					 | 
				
			||||||
  UnauthorizedException,
 | 
					 | 
				
			||||||
} from '@nestjs/common'
 | 
					 | 
				
			||||||
import * as bcrypt from 'bcrypt'
 | 
					 | 
				
			||||||
import { PrismaService } from 'nestjs-prisma'
 | 
					 | 
				
			||||||
import { Token, TokenPayload } from './dto/token.dto'
 | 
					 | 
				
			||||||
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'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Injectable()
 | 
					 | 
				
			||||||
export class AuthService {
 | 
					 | 
				
			||||||
  constructor(
 | 
					 | 
				
			||||||
    private jwtService: JwtService,
 | 
					 | 
				
			||||||
    private prismaService: PrismaService,
 | 
					 | 
				
			||||||
    private emailService: EmailService,
 | 
					 | 
				
			||||||
    @Inject(securityConfig.KEY)
 | 
					 | 
				
			||||||
    private secureConfig: SecurityConfig,
 | 
					 | 
				
			||||||
  ) {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async register(userToCreate: CreateUserDto) {
 | 
					 | 
				
			||||||
    const tokenPayload = this.jwtService.verify<EmailSendDto>(
 | 
					 | 
				
			||||||
      userToCreate.token,
 | 
					 | 
				
			||||||
      {
 | 
					 | 
				
			||||||
        secret: this.emailService.getEmailJwtSecret(
 | 
					 | 
				
			||||||
          userToCreate.verificationCode,
 | 
					 | 
				
			||||||
          EmailScene.register,
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    if (
 | 
					 | 
				
			||||||
      tokenPayload.email !== userToCreate.email ||
 | 
					 | 
				
			||||||
      tokenPayload.scene !== 'register'
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
      throw new ForbiddenException('请输入正确的邮箱')
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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.generateTokens({ userId: user.id })
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  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 ForbiddenException('Invalid password')
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return this.generateTokens({ userId: user.id })
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  async refreshToken(token: string) {
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      const { userId } = this.jwtService.verify<TokenPayload>(token, {
 | 
					 | 
				
			||||||
        secret: this.secureConfig.jwt_refresh_secret,
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
      return this.generateTokens({ userId })
 | 
					 | 
				
			||||||
    } catch (e) {
 | 
					 | 
				
			||||||
      console.error(e)
 | 
					 | 
				
			||||||
      throw new UnauthorizedException(e.message)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private 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,6 +1,6 @@
 | 
				
			|||||||
import { createParamDecorator, ExecutionContext } from '@nestjs/common'
 | 
					import { createParamDecorator, ExecutionContext } from '@nestjs/common'
 | 
				
			||||||
import { type Request } from 'express'
 | 
					import { type Request } from 'express'
 | 
				
			||||||
import { TokenPayload } from 'src/auth/dto/token.dto'
 | 
					import { TokenPayload } from 'src/users/dto/token.dto'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const User = createParamDecorator(
 | 
					export const User = createParamDecorator(
 | 
				
			||||||
  (key: keyof TokenPayload, ctx: ExecutionContext) => {
 | 
					  (key: keyof TokenPayload, ctx: ExecutionContext) => {
 | 
				
			||||||
 | 
				
			|||||||
@ -2,7 +2,7 @@ import {
 | 
				
			|||||||
  Catch,
 | 
					  Catch,
 | 
				
			||||||
  Logger,
 | 
					  Logger,
 | 
				
			||||||
  ArgumentsHost,
 | 
					  ArgumentsHost,
 | 
				
			||||||
  ForbiddenException,
 | 
					  UnauthorizedException,
 | 
				
			||||||
} from '@nestjs/common'
 | 
					} from '@nestjs/common'
 | 
				
			||||||
import { BaseExceptionFilter } from '@nestjs/core'
 | 
					import { BaseExceptionFilter } from '@nestjs/core'
 | 
				
			||||||
import { JsonWebTokenError } from 'jsonwebtoken'
 | 
					import { JsonWebTokenError } from 'jsonwebtoken'
 | 
				
			||||||
@ -13,6 +13,6 @@ export class JwtExceptionsFilter extends BaseExceptionFilter {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  catch(exception: JsonWebTokenError, host: ArgumentsHost) {
 | 
					  catch(exception: JsonWebTokenError, host: ArgumentsHost) {
 | 
				
			||||||
    this.logger.error(exception)
 | 
					    this.logger.error(exception)
 | 
				
			||||||
    super.catch(new ForbiddenException(exception.message), host)
 | 
					    super.catch(new UnauthorizedException(exception.message), host)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,11 +1,11 @@
 | 
				
			|||||||
import { Strategy, ExtractJwt } from 'passport-jwt'
 | 
					import { Strategy, ExtractJwt } from 'passport-jwt'
 | 
				
			||||||
import { PassportStrategy } from '@nestjs/passport'
 | 
					import { PassportStrategy } from '@nestjs/passport'
 | 
				
			||||||
import { Injectable, Inject } from '@nestjs/common'
 | 
					import { Injectable, Inject } from '@nestjs/common'
 | 
				
			||||||
import { TokenPayload } from '../dto/token.dto'
 | 
					import { TokenPayload } from 'src/users/dto/token.dto'
 | 
				
			||||||
import { securityConfig, SecurityConfig } from 'src/common/configs'
 | 
					import { securityConfig, SecurityConfig } from 'src/common/configs'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class JwtStrategy extends PassportStrategy(Strategy) {
 | 
					export class JwtAuthStrategy extends PassportStrategy(Strategy) {
 | 
				
			||||||
  constructor(
 | 
					  constructor(
 | 
				
			||||||
    @Inject(securityConfig.KEY)
 | 
					    @Inject(securityConfig.KEY)
 | 
				
			||||||
    readonly secureConfig: SecurityConfig,
 | 
					    readonly secureConfig: SecurityConfig,
 | 
				
			||||||
@ -15,7 +15,7 @@ export class EmailController {
 | 
				
			|||||||
  // }
 | 
					  // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @ApiOperation({ summary: '发送邮箱验证码' })
 | 
					  @ApiOperation({ summary: '发送邮箱验证码' })
 | 
				
			||||||
  @Post()
 | 
					  @Post('verificationCode')
 | 
				
			||||||
  async getRegisterToken(@Body() payload: EmailSendDto) {
 | 
					  async getRegisterToken(@Body() payload: EmailSendDto) {
 | 
				
			||||||
    return this.emailService.getRegisterToken(payload.email, payload.scene)
 | 
					    return this.emailService.getRegisterToken(payload.email, payload.scene)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -33,7 +33,7 @@ export class EmailService {
 | 
				
			|||||||
    if (user) {
 | 
					    if (user) {
 | 
				
			||||||
      throw new ConflictException(`邮箱${email}已注册`)
 | 
					      throw new ConflictException(`邮箱${email}已注册`)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const verificationCode = this.generateRandomNum()
 | 
					    const verificationCode = this.generateVerificationCode()
 | 
				
			||||||
    const registerToken = this.jwtService.sign(
 | 
					    const registerToken = this.jwtService.sign(
 | 
				
			||||||
      { email, scene },
 | 
					      { email, scene },
 | 
				
			||||||
      { secret: this.getEmailJwtSecret(verificationCode, scene) },
 | 
					      { secret: this.getEmailJwtSecret(verificationCode, scene) },
 | 
				
			||||||
@ -50,7 +50,7 @@ export class EmailService {
 | 
				
			|||||||
    return this.secureConfig.jwt_access_secret + verificationCode + scene
 | 
					    return this.secureConfig.jwt_access_secret + verificationCode + scene
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private generateRandomNum() {
 | 
					  private generateVerificationCode() {
 | 
				
			||||||
    return Math.floor(Math.random() * 899999 + 100000).toString()
 | 
					    return Math.floor(Math.random() * 899999 + 100000).toString()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										24
									
								
								src/users/token.controller.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/users/token.controller.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,24 @@
 | 
				
			|||||||
 | 
					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) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @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)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										50
									
								
								src/users/token.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/users/token.service.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					import { Inject, Injectable, ForbiddenException } from '@nestjs/common'
 | 
				
			||||||
 | 
					import * as bcrypt from 'bcrypt'
 | 
				
			||||||
 | 
					import { PrismaService } from 'nestjs-prisma'
 | 
				
			||||||
 | 
					import { Token, TokenPayload } 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 ForbiddenException('Invalid password')
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return this.generateTokens({ userId: user.id })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async refreshToken(token: string) {
 | 
				
			||||||
 | 
					    const { userId } = this.jwtService.verify<TokenPayload>(token, {
 | 
				
			||||||
 | 
					      secret: this.secureConfig.jwt_refresh_secret,
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    return this.generateTokens({ userId })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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,11 +1,12 @@
 | 
				
			|||||||
import { Controller, Get, UseInterceptors } from '@nestjs/common'
 | 
					import { Controller, Get, Post, Body, UseInterceptors } from '@nestjs/common'
 | 
				
			||||||
import { UsersService } from './users.service'
 | 
					import { UsersService } from './users.service'
 | 
				
			||||||
import { ApiOperation, ApiTags } from '@nestjs/swagger'
 | 
					import { ApiTags, ApiOperation } from '@nestjs/swagger'
 | 
				
			||||||
import { User } from 'src/common/decorators/user.decorator'
 | 
					import { User } from 'src/common/decorators/user.decorator'
 | 
				
			||||||
import { NeedAuth } from 'src/common/decorators/need-auth.decorator'
 | 
					import { NeedAuth } from 'src/common/decorators/need-auth.decorator'
 | 
				
			||||||
import { PasswordInterceptor } from 'src/common/interceptors/password.interceptor'
 | 
					import { PasswordInterceptor } from 'src/common/interceptors/password.interceptor'
 | 
				
			||||||
import { PrismaService } from 'nestjs-prisma'
 | 
					import { PrismaService } from 'nestjs-prisma'
 | 
				
			||||||
import { UserEntity } from './entities/user.entity'
 | 
					import { UserEntity } from './entities/user.entity'
 | 
				
			||||||
 | 
					import { CreateUserDto } from './dto/create-user.dto'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ApiTags('User')
 | 
					@ApiTags('User')
 | 
				
			||||||
@Controller('api/users')
 | 
					@Controller('api/users')
 | 
				
			||||||
@ -22,4 +23,10 @@ export class UsersController {
 | 
				
			|||||||
  async getUserInfo(@User('userId') userId: string): Promise<UserEntity> {
 | 
					  async getUserInfo(@User('userId') userId: string): Promise<UserEntity> {
 | 
				
			||||||
    return this.prismaService.user.findUniqueOrThrow({ where: { id: userId } })
 | 
					    return this.prismaService.user.findUniqueOrThrow({ where: { id: userId } })
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @ApiOperation({ summary: '注册用户' })
 | 
				
			||||||
 | 
					  @Post()
 | 
				
			||||||
 | 
					  async register(@Body() userData: CreateUserDto) {
 | 
				
			||||||
 | 
					    return this.userService.register(userData)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,14 @@
 | 
				
			|||||||
import { Module } from '@nestjs/common'
 | 
					import { Module } from '@nestjs/common'
 | 
				
			||||||
import { UsersService } from './users.service'
 | 
					import { UsersService } from './users.service'
 | 
				
			||||||
import { UsersController } from './users.controller'
 | 
					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'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Module({
 | 
					@Module({
 | 
				
			||||||
  controllers: [UsersController],
 | 
					  controllers: [UsersController, TokenController],
 | 
				
			||||||
  providers: [UsersService],
 | 
					  providers: [UsersService, JwtService, EmailService, TokenService],
 | 
				
			||||||
  exports: [UsersService],
 | 
					  exports: [UsersService],
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
export class UsersModule {}
 | 
					export class UsersModule {}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,61 @@
 | 
				
			|||||||
import { Injectable } from '@nestjs/common'
 | 
					import { Inject, Injectable, ForbiddenException } from '@nestjs/common'
 | 
				
			||||||
 | 
					import * as bcrypt from 'bcrypt'
 | 
				
			||||||
import { PrismaService } from 'nestjs-prisma'
 | 
					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 { Prisma } from '@prisma/client'
 | 
				
			||||||
 | 
					import { TokenService } from './token.service'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class UsersService {
 | 
					export class UsersService {
 | 
				
			||||||
  constructor(private prismaService: PrismaService) {}
 | 
					  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.verificationCode,
 | 
				
			||||||
 | 
					      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,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    const user = await this.prismaService.user.create({
 | 
				
			||||||
 | 
					      data: {
 | 
				
			||||||
 | 
					        username: userToCreate.username,
 | 
				
			||||||
 | 
					        email: userToCreate.email,
 | 
				
			||||||
 | 
					        password: hashedPassword,
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    return this.tokenService.generateTokens({ userId: user.id })
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user