chore: add register of user
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsEmail, IsMobilePhone, IsNotEmpty, IsNumberString, IsString, Length, MinLength } from "class-validator";
|
||||
|
||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class CompleteRegistrationDto {
|
||||
@IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY })
|
||||
@IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT })
|
||||
@ApiProperty({ description: "phone number", default: "09123456789" })
|
||||
phone: string;
|
||||
|
||||
@ApiProperty({ description: "OTP code received via SMS", example: "56893" })
|
||||
@IsNotEmpty({ message: AuthMessage.OTP_NOT_EMPTY })
|
||||
@IsNumberString(undefined, { message: AuthMessage.OTP_FORMAT_INVALID })
|
||||
@Length(5, 5, { message: AuthMessage.OTP_FORMAT_INVALID })
|
||||
code: string;
|
||||
|
||||
@IsNotEmpty({ message: AuthMessage.FIRST_NAME_NOT_EMPTY })
|
||||
@IsString({ message: AuthMessage.FIRST_NAME_NOT_EMPTY })
|
||||
@Length(2, 50, { message: AuthMessage.FIRST_NAME_SHOULD_BE_BETWEEN_2_AND_50 })
|
||||
@ApiProperty({ description: "First name", example: "mahyar" })
|
||||
firstName: string;
|
||||
|
||||
@IsNotEmpty({ message: AuthMessage.LAST_NAME_NOT_EMPTY })
|
||||
@IsString({ message: AuthMessage.LAST_NAME_NOT_EMPTY })
|
||||
@Length(2, 50, { message: AuthMessage.LAST_NAME_SHOULD_BE_BETWEEN_2_AND_50 })
|
||||
@ApiProperty({ description: "Last name", example: "jamshidi" })
|
||||
lastName: string;
|
||||
|
||||
@IsNotEmpty({ message: AuthMessage.EMAIL_NOT_EMPTY })
|
||||
@IsEmail({}, { message: AuthMessage.INVALID_EMAIL_FORMAT })
|
||||
@ApiProperty({ description: "Email", example: "ma@gmail.com" })
|
||||
email: string;
|
||||
|
||||
@IsNotEmpty({ message: AuthMessage.BIRTH_DATE_NOT_EMPTY })
|
||||
@IsString({ message: AuthMessage.BIRTH_DATE_NOT_EMPTY })
|
||||
@ApiProperty({ description: "Birth date", example: "1403/01/01" })
|
||||
birthDate: string;
|
||||
|
||||
@IsNotEmpty({ message: AuthMessage.NATIONAL_NOT_EMPTY })
|
||||
@IsNumberString({ no_symbols: true }, { message: AuthMessage.NATIONAL_CODE_INCORRECT })
|
||||
@Length(10, 10, { message: AuthMessage.NATIONAL_CODE_INCORRECT })
|
||||
@ApiProperty({ description: "iranian format (10 char)", example: "4569852169" })
|
||||
nationalCode: string;
|
||||
|
||||
@IsNotEmpty({ message: AuthMessage.PASSWORD_NOT_EMPTY })
|
||||
@IsString({ message: AuthMessage.PASSWORD_FORMAT_INVALID })
|
||||
@ApiProperty({ description: "password", example: "12S345SS678" })
|
||||
@MinLength(8, { message: AuthMessage.PASSWORD_LENGTH })
|
||||
password: string;
|
||||
}
|
||||
+7
-5
@@ -1,14 +1,16 @@
|
||||
import { IsEmail, IsNotEmpty } from "@nestjs/class-validator";
|
||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsEmail, IsNotEmpty, IsString } from "class-validator";
|
||||
|
||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class LoginPasswordDTO {
|
||||
@IsNotEmpty({ message: AuthMessage.EmailNotEmpty })
|
||||
@IsEmail(undefined, { message: AuthMessage.IncorrectEmail })
|
||||
@ApiProperty({ description: "email of user", default: "user@abrclick.ir" })
|
||||
@IsNotEmpty({ message: AuthMessage.EMAIL_NOT_EMPTY })
|
||||
@IsEmail(undefined, { message: AuthMessage.INVALID_EMAIL_FORMAT })
|
||||
@ApiProperty({ description: "email of user", default: "user@test.ir" })
|
||||
email: string;
|
||||
|
||||
@IsNotEmpty({ message: AuthMessage.PasswordNotEmpty })
|
||||
@IsString({ message: AuthMessage.INVALID_PASS_FORMAT })
|
||||
@ApiProperty({ type: "string", description: "password user", example: "@1234" })
|
||||
password: string;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsMobilePhone, IsNotEmpty } from "class-validator";
|
||||
|
||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||
|
||||
export class RequestOtpDto {
|
||||
@IsNotEmpty({ message: AuthMessage.PHONE_NOT_EMPTY })
|
||||
@IsMobilePhone("fa-IR", {}, { message: AuthMessage.INVALID_PHONE_FORMAT })
|
||||
@ApiProperty({ description: "phone number", default: "09123456789" })
|
||||
phone: string;
|
||||
}
|
||||
@@ -1,4 +1,49 @@
|
||||
import { Controller } from "@nestjs/common";
|
||||
import { Body, Controller, Post } from "@nestjs/common";
|
||||
import { ApiOperation, ApiTags } from "@nestjs/swagger";
|
||||
|
||||
import { AuthService } from "./auth.service";
|
||||
import { CompleteRegistrationDto } from "./DTO/complete-register.dto";
|
||||
import { RequestOtpDto } from "./DTO/request-otp.dto";
|
||||
|
||||
@ApiTags("Auth")
|
||||
@Controller("auth")
|
||||
export class AuthController {}
|
||||
export class AuthController {
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
@ApiOperation({ summary: "Initiate registration" })
|
||||
@Post("register/initiate")
|
||||
async register(@Body() registerDto: RequestOtpDto) {
|
||||
return this.authService.initiateRegistration(registerDto);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "complete registration ==> step 2" })
|
||||
@Post("register/complete")
|
||||
async completeRegistration(@Body() completeRegistrationDto: CompleteRegistrationDto) {
|
||||
return this.authService.completeRegistration(completeRegistrationDto);
|
||||
}
|
||||
|
||||
// @Post("login")
|
||||
// async login(@Body() loginDto: LoginDto) {
|
||||
// return this.authService.login(loginDto);
|
||||
// }
|
||||
|
||||
// @Post("otp/register/send")
|
||||
// async sendOtp(@Body() sendOtpDto: RequestOtpDto) {
|
||||
// return this.authService.requestRegisterOTP(sendOtpDto);
|
||||
// }
|
||||
|
||||
// @Post("otp/verify")
|
||||
// async verifyOtp(@Body() verifyOtpDto: VerifyOtpDto) {
|
||||
// return this.authService.verifyOtp(verifyOtpDto);
|
||||
// }
|
||||
|
||||
// @Post("forgot-password")
|
||||
// async forgotPassword(@Body() forgotPasswordDto: ForgotPasswordDto) {
|
||||
// return this.authService.forgotPassword(forgotPasswordDto);
|
||||
// }
|
||||
|
||||
// @Post("reset-password")
|
||||
// async resetPassword(@Body() resetPasswordDto: ResetPasswordDto) {
|
||||
// return this.authService.resetPassword(resetPasswordDto);
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -2,10 +2,11 @@ import { Module } from "@nestjs/common";
|
||||
|
||||
import { AuthController } from "./auth.controller";
|
||||
import { AuthService } from "./auth.service";
|
||||
import { UsersModule } from "../users/users.module";
|
||||
import { UtilsModule } from "../utils/utils.module";
|
||||
|
||||
@Module({
|
||||
imports: [UtilsModule],
|
||||
imports: [UtilsModule, UsersModule],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService],
|
||||
})
|
||||
|
||||
@@ -1,20 +1,55 @@
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import { UsersService } from "../users/providers/users.service";
|
||||
import { LoginPasswordDTO } from "./DTOs/loginPassword.dto";
|
||||
import { PasswordService } from "../utils/providers/password.service";
|
||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||
|
||||
import { CompleteRegistrationDto } from "./DTO/complete-register.dto";
|
||||
import { LoginPasswordDTO } from "./DTO/loginPassword.dto";
|
||||
import { RequestOtpDto } from "./DTO/request-otp.dto";
|
||||
import { AuthMessage, UserMessage } from "../../common/enums/message.enum";
|
||||
import { UsersService } from "../users/providers/users.service";
|
||||
import { OTPService } from "../utils/providers/otp.service";
|
||||
import { PasswordService } from "../utils/providers/password.service";
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
private readonly logger = new Logger(AuthService.name);
|
||||
constructor(
|
||||
private readonly userSerivce: UsersService,
|
||||
private readonly userService: UsersService,
|
||||
private readonly passwordService: PasswordService,
|
||||
private readonly otpService: OTPService,
|
||||
) {}
|
||||
//****************** */
|
||||
async initiateRegistration(requestOtpDto: RequestOtpDto) {
|
||||
const { phone } = requestOtpDto;
|
||||
const otpCode = await this.otpService.generateAndSetInCache(phone, "REGISTER");
|
||||
// await SmsService.sendOtp(phone, otpCode);
|
||||
this.logger.log(`OTP sent to ${phone}: ${otpCode}`);
|
||||
|
||||
return {
|
||||
message: AuthMessage.OTP_SENT,
|
||||
otpCode,
|
||||
};
|
||||
}
|
||||
//****************** */
|
||||
async completeRegistration(completeRegistrationDto: CompleteRegistrationDto) {
|
||||
const { phone, code } = completeRegistrationDto;
|
||||
const isValid = await this.otpService.verifyOtp(phone, code, "REGISTER");
|
||||
|
||||
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
|
||||
|
||||
await this.otpService.delOtpFormCache(phone, "REGISTER");
|
||||
const hashedPassword = await this.passwordService.hashPassword(completeRegistrationDto.password);
|
||||
const user = await this.userService.createUser(completeRegistrationDto, hashedPassword);
|
||||
|
||||
return {
|
||||
message: AuthMessage.USER_REGISTER_SUCCESS,
|
||||
user,
|
||||
};
|
||||
}
|
||||
//****************** */
|
||||
|
||||
async loginWithPassword(loginDto: LoginPasswordDTO) {
|
||||
const { email, password } = loginDto;
|
||||
|
||||
const user = await this.userSerivce.findOneWithEmail(email);
|
||||
const user = await this.userService.findOneWithEmail(email);
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
|
||||
const passCompare = await this.passwordService.comparePassword(password, user.password);
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export class UserRegisterDto {}
|
||||
@@ -15,7 +15,7 @@ export class User extends BaseEntity {
|
||||
password: string;
|
||||
|
||||
@Column({ type: "varchar", length: 150 })
|
||||
fistName: string;
|
||||
firstName: string;
|
||||
|
||||
@Column({ type: "varchar", length: 200 })
|
||||
lastName: string;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||
|
||||
import { UserRepository } from "./users.repository";
|
||||
import { User } from "../entities/user.entity";
|
||||
import { UserMessage } from "../../../common/enums/message.enum";
|
||||
import { RoleRepository } from "./roles.repository";
|
||||
import { UserRepository } from "./users.repository";
|
||||
import { roles } from "../../../../db/seeders/role.seeder";
|
||||
import { UserMessage } from "../../../common/enums/message.enum";
|
||||
import { CompleteRegistrationDto } from "../../auth/DTO/complete-register.dto";
|
||||
import { User } from "../entities/user.entity";
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
@@ -25,4 +26,12 @@ export class UsersService {
|
||||
this.logger.log({ createdRoles });
|
||||
return createdRoles;
|
||||
}
|
||||
|
||||
async createUser(registerDto: CompleteRegistrationDto, hashedPassword: string): Promise<User> {
|
||||
const existEmail = await this.userRepository.findOneWithEmail(registerDto.email);
|
||||
if (existEmail) throw new BadRequestException(UserMessage.EMAIL_EXIST);
|
||||
|
||||
const user = this.userRepository.create({ ...registerDto, password: hashedPassword });
|
||||
return await this.userRepository.save(user);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export type OtpCacheKeyType = "LOGIN" | "REGISTER" | "FORGOT_PASSWORD";
|
||||
@@ -1,9 +1,9 @@
|
||||
import { randomInt } from "node:crypto";
|
||||
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import { CacheService } from "./cache.service";
|
||||
import { AuthMessage } from "../../../common/enums/message.enum";
|
||||
import { OtpCacheKeyType } from "../interfaces/IOtpKey";
|
||||
|
||||
@Injectable()
|
||||
export class OTPService {
|
||||
@@ -13,17 +13,21 @@ export class OTPService {
|
||||
return randomInt(10000, 99999).toString();
|
||||
}
|
||||
|
||||
async generateAndStore(identifier: string, cacheKey: string = "Login") {
|
||||
async generateAndSetInCache(identifier: string, cacheKey: OtpCacheKeyType) {
|
||||
const otp = this.generateOTP();
|
||||
const key = `${identifier}:${cacheKey}:OTP`;
|
||||
await this.cacheService.set(key, otp);
|
||||
return otp;
|
||||
}
|
||||
|
||||
async verifyOtp(identifier: string, otpCode: string, cacheKey: string = "Login") {
|
||||
const key = `${identifier}:${cacheKey}:Otp`;
|
||||
const codeInCache = await this.cacheService.get(key);
|
||||
async verifyOtp(identifier: string, otpCode: string, cacheKey: OtpCacheKeyType) {
|
||||
const key = `${identifier}:${cacheKey}:OTP`;
|
||||
const codeInCache = await this.cacheService.get<string | null>(key);
|
||||
return codeInCache && otpCode === codeInCache;
|
||||
}
|
||||
|
||||
if (!codeInCache || otpCode !== codeInCache) throw new BadRequestException(AuthMessage.INVALID_OTP);
|
||||
async delOtpFormCache(identifier: string, cacheKey: OtpCacheKeyType) {
|
||||
const key = `${identifier}:${cacheKey}:OTP`;
|
||||
await this.cacheService.del(key);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user