chore: change the login and regitster flow to single route
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import { randomBytes } from "node:crypto";
|
||||
|
||||
import { BadRequestException, Injectable, Logger } from "@nestjs/common";
|
||||
import { DataSource } from "typeorm";
|
||||
|
||||
import { TokensService } from "./tokens.service";
|
||||
@@ -10,13 +12,14 @@ import { AdminsService } from "../../users/providers/admins.service";
|
||||
import { UsersService } from "../../users/providers/users.service";
|
||||
import { OTPService } from "../../utils/providers/otp.service";
|
||||
import { PasswordService } from "../../utils/providers/password.service";
|
||||
import { randomizeCase } from "../../utils/providers/string.utils";
|
||||
import { ChangePasswordDto } from "../DTO/change-password.dto";
|
||||
import { CompleteRegistrationDto } from "../DTO/complete-register.dto";
|
||||
import { CheckUserExistDto, LoginPasswordDTO } from "../DTO/loginPassword.dto";
|
||||
import { RequestOtpDto } from "../DTO/request-otp.dto";
|
||||
import { VerifyOtpDto } from "../DTO/verify-otp.dto";
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
private readonly logger = new Logger(AuthService.name);
|
||||
constructor(
|
||||
private readonly usersService: UsersService,
|
||||
private readonly adminsService: AdminsService,
|
||||
@@ -27,78 +30,7 @@ export class AuthService {
|
||||
private readonly notificationQueue: NotificationQueue,
|
||||
private readonly referralsService: ReferralsService,
|
||||
) {}
|
||||
//****************** */
|
||||
//****************** */
|
||||
async initiateRegistration(requestOtpDto: RequestOtpDto) {
|
||||
const { phone } = requestOtpDto;
|
||||
const existUser = await this.usersService.findOneWithPhone(phone);
|
||||
if (existUser) throw new BadRequestException(AuthMessage.PHONE_EXISTS);
|
||||
|
||||
const existCode = await this.otpService.checkExistOtp(phone, "REGISTER");
|
||||
if (existCode) {
|
||||
return {
|
||||
message: AuthMessage.OTP_ALREADY_SENT,
|
||||
ttlSecond: existCode,
|
||||
};
|
||||
}
|
||||
const otpCode = await this.otpService.generateAndSetInCache(phone, "REGISTER");
|
||||
//
|
||||
// await this.smsService.sendSmsVerifyCode(phone, otpCode);
|
||||
await this.notificationQueue.addLoginOtpNotification({ phone, code: otpCode });
|
||||
|
||||
return {
|
||||
message: AuthMessage.OTP_SENT,
|
||||
};
|
||||
}
|
||||
//****************** */
|
||||
//****************** */
|
||||
async completeRegistration(completeRegistrationDto: CompleteRegistrationDto) {
|
||||
const { phone, code, referralCode } = completeRegistrationDto;
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
|
||||
try {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
//
|
||||
const isValid = await this.otpService.verifyOtp(phone, code, "REGISTER");
|
||||
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
|
||||
|
||||
const existUser = await this.usersService.findOneWithPhone(phone);
|
||||
if (existUser) throw new BadRequestException(AuthMessage.PHONE_EXISTS);
|
||||
|
||||
const hashedPassword = await this.passwordService.hashPassword(completeRegistrationDto.password);
|
||||
const user = await this.usersService.createUser(completeRegistrationDto, hashedPassword, queryRunner);
|
||||
|
||||
if (referralCode) await this.referralsService.useReferralCode({ referralCode, userId: user.id }, queryRunner);
|
||||
|
||||
const tokens = await this.tokensService.generateTokens(user, queryRunner);
|
||||
|
||||
const superAdmins = await this.adminsService.getSuperAdmins(queryRunner);
|
||||
|
||||
for (const admin of superAdmins) {
|
||||
await this.notificationQueue.addNewCustomerNotification(admin.id, {
|
||||
userPhone: admin.phone,
|
||||
userEmail: admin.email,
|
||||
fullName: `${user.firstName} ${user.lastName}`,
|
||||
mobile: user.phone,
|
||||
});
|
||||
}
|
||||
|
||||
await this.otpService.delOtpFormCache(phone, "REGISTER");
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return {
|
||||
message: AuthMessage.USER_REGISTER_SUCCESS,
|
||||
...tokens,
|
||||
};
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
//****************** */
|
||||
//****************** */
|
||||
async loginWithPassword(loginDto: LoginPasswordDTO) {
|
||||
@@ -150,18 +82,10 @@ export class AuthService {
|
||||
//****************** */
|
||||
//****************** */
|
||||
|
||||
async requestLoginOtp(requestOtpDto: RequestOtpDto, isAdmin: boolean = false) {
|
||||
async requestLoginOtp(requestOtpDto: RequestOtpDto) {
|
||||
const { phone } = requestOtpDto;
|
||||
const user = await this.usersService.findOneWithPhone(phone);
|
||||
if (!user) throw new BadRequestException(AuthMessage.PHONE_NOT_FOUND);
|
||||
|
||||
//check the if the method call is from admin or not
|
||||
|
||||
const isUserAdmin = this.checkUserIsAdmin(user.roles);
|
||||
|
||||
if (isAdmin && !isUserAdmin) throw new BadRequestException(AuthMessage.NOT_ADMIN);
|
||||
|
||||
const existCode = await this.otpService.checkExistOtp(phone, "LOGIN");
|
||||
const existCode = await this.otpService.checkExistOtp(phone, "LOGIN/REGISTER");
|
||||
if (existCode) {
|
||||
return {
|
||||
message: AuthMessage.OTP_ALREADY_SENT,
|
||||
@@ -169,9 +93,9 @@ export class AuthService {
|
||||
};
|
||||
}
|
||||
|
||||
const otpCode = await this.otpService.generateAndSetInCache(phone, "LOGIN");
|
||||
const otpCode = await this.otpService.generateAndSetInCache(phone, "LOGIN/REGISTER");
|
||||
//
|
||||
// await this.smsService.sendSmsVerifyCode(phone, otpCode);
|
||||
this.logger.log(`otp code: ${otpCode} for phone: ${phone}`);
|
||||
await this.notificationQueue.addLoginOtpNotification({ phone, code: otpCode });
|
||||
|
||||
return {
|
||||
@@ -183,20 +107,67 @@ export class AuthService {
|
||||
//****************** */
|
||||
|
||||
async verifyLoginOtp(verifyOtpDto: VerifyOtpDto) {
|
||||
const { code, phone } = verifyOtpDto;
|
||||
const { code, phone, referralCode } = verifyOtpDto;
|
||||
|
||||
const user = await this.checkUserLoginCredentialWithPhone(phone, code);
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
|
||||
if (!user.roles.some((role) => !role.isAdmin)) throw new BadRequestException(AuthMessage.ADMIN_CAN_NOT_LOGIN);
|
||||
// if (user.roles.some((role) => role.isAdmin)) throw new BadRequestException(AuthMessage.ADMIN_CAN_NOT_LOGIN);
|
||||
try {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
const tokens = await this.tokensService.generateTokens(user);
|
||||
const isValid = await this.otpService.verifyOtp(phone, code, "LOGIN/REGISTER");
|
||||
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
|
||||
|
||||
await this.notificationQueue.addLoginNotification(user.id, { userPhone: user.phone, userEmail: user.email });
|
||||
return {
|
||||
message: AuthMessage.LOGIN_SUCCESS,
|
||||
...tokens,
|
||||
};
|
||||
let user = await this.usersService.findOneWithPhone(phone);
|
||||
let isNewUser = false;
|
||||
|
||||
if (!user) {
|
||||
const defaultUserPassword = randomizeCase(randomBytes(6).toString("hex"));
|
||||
isNewUser = true;
|
||||
|
||||
const hashedPassword = await this.passwordService.hashPassword(defaultUserPassword);
|
||||
user = await this.usersService.createUser(verifyOtpDto, hashedPassword, queryRunner);
|
||||
|
||||
if (referralCode) await this.referralsService.useReferralCode({ referralCode, userId: user.id }, queryRunner);
|
||||
|
||||
const superAdmins = await this.adminsService.getSuperAdmins(queryRunner);
|
||||
|
||||
for (const admin of superAdmins) {
|
||||
await this.notificationQueue.addNewCustomerNotification(admin.id, {
|
||||
userPhone: admin.phone,
|
||||
userEmail: admin.email,
|
||||
fullName: `${user.firstName} ${user.lastName}`,
|
||||
mobile: user.phone,
|
||||
});
|
||||
}
|
||||
|
||||
await this.notificationQueue.addUserPasswordNotification(user.id, {
|
||||
userPhone: user.phone,
|
||||
userEmail: user.email,
|
||||
password: defaultUserPassword,
|
||||
});
|
||||
}
|
||||
|
||||
if (!user.roles.some((role) => !role.isAdmin)) throw new BadRequestException(AuthMessage.ADMIN_CAN_NOT_LOGIN);
|
||||
|
||||
await this.notificationQueue.addLoginNotification(user.id, { userPhone: user.phone, userEmail: user.email });
|
||||
|
||||
const tokens = await this.tokensService.generateTokens(user, queryRunner);
|
||||
|
||||
await this.otpService.delOtpFormCache(phone, "LOGIN/REGISTER");
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return {
|
||||
message: isNewUser ? AuthMessage.USER_REGISTER_SUCCESS : AuthMessage.LOGIN_SUCCESS,
|
||||
...tokens,
|
||||
};
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
//****************** */
|
||||
@@ -204,7 +175,9 @@ export class AuthService {
|
||||
async adminVerifyLoginOtp(verifyOtpDto: VerifyOtpDto) {
|
||||
const { code, phone } = verifyOtpDto;
|
||||
|
||||
const user = await this.checkUserLoginCredentialWithPhone(phone, code);
|
||||
const user = await this.checkUserAuthCredentialWithPhone(phone, code);
|
||||
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
|
||||
const isUserAdmin = this.checkUserIsAdmin(user.roles);
|
||||
|
||||
@@ -260,23 +233,18 @@ export class AuthService {
|
||||
|
||||
return user;
|
||||
}
|
||||
//****************** */
|
||||
//****************** */
|
||||
|
||||
private async checkUserLoginCredentialWithPhone(phone: string, otpCode: string) {
|
||||
const isValid = await this.otpService.verifyOtp(phone, otpCode, "LOGIN");
|
||||
private async checkUserAuthCredentialWithPhone(phone: string, otpCode: string) {
|
||||
const isValid = await this.otpService.verifyOtp(phone, otpCode, "LOGIN/REGISTER");
|
||||
if (!isValid) throw new BadRequestException(AuthMessage.INVALID_OTP);
|
||||
|
||||
await this.otpService.delOtpFormCache(phone, "LOGIN");
|
||||
await this.otpService.delOtpFormCache(phone, "LOGIN/REGISTER");
|
||||
|
||||
const user = await this.usersService.findOneWithPhone(phone);
|
||||
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
|
||||
|
||||
return user;
|
||||
}
|
||||
// private checkUserPerm(user: User, perm: PermissionEnum) {
|
||||
// return user.roles.some((role) => role?.permissions?.some((p) => p.name === perm));
|
||||
// }
|
||||
|
||||
//****************** */
|
||||
//****************** */
|
||||
private checkUserIsAdmin(roles: Role[]) {
|
||||
|
||||
Reference in New Issue
Block a user