import { BadRequestException, HttpException, HttpStatus, Injectable, Logger, NotAcceptableException, NotFoundException, } from "@nestjs/common"; import { JwtService } from "@nestjs/jwt"; import { Types } from "mongoose"; import { LoginDtoRs } from "src/auth/dto/user/login.dto"; import { UserDbService } from "src/users/entities/db-service/user.db.service"; import { HashService } from "src/utils/hash/hash.service"; import { OtpService } from "src/utils/otp/otp.service"; import { SmsManagerService } from "src/utils/sms-manager/sms-manager.service"; // TODO FIX REGISTER TO USER.SERVICE AND AUTH IN THIS MODULE @Injectable() export class UserAuthService { private readonly logger = new Logger(UserAuthService.name); constructor( private readonly jwtService: JwtService, private readonly userDbService: UserDbService, private readonly hashService: HashService, private readonly otpCreator: OtpService, private readonly smsManagerService: SmsManagerService, ) {} async validateUser(username: string, pass: string): Promise { const user = await this.userDbService.findOne({ username }); if (!user) throw new NotFoundException("user not found"); const now = new Date().getTime(); if (user.otp == null) throw new NotAcceptableException("please get otp"); if (user.otpExpire < now) { throw new NotAcceptableException("expire otp"); } if (await this.hashService.compare(pass, user.otp)) { return user; } return false; } async login(user: any) { const payload = { username: user.username, sub: user.id, role: "user", }; const accToken = this.jwtService.sign(payload, { secret: `${process.env.SECRET}`, }); await this.userDbService.findOneAndUpdate( { username: user.username }, { tokens: { token: accToken }, otp: null, }, ); return { userId: user._id, access_token: accToken, }; } async sendOtpRequest(mobile: string): Promise { const userExist = await this.userDbService.findOne({ mobile, }); const otp = this.otpCreator.create(); const hashOtp = await this.hashService.hash(otp); const rawExpireMinutes = Number(process.env.EXP_OTP_TIME ?? "2"); const expireMinutes = Number.isFinite(rawExpireMinutes) && rawExpireMinutes > 0 ? rawExpireMinutes : 2; const otpExpire = Date.now() + expireMinutes * 60 * 1000; if (!userExist) { await this.smsSender(otp, mobile); /// create otp request const newUser = await this.userDbService.createUser({ mobile, username: mobile, otp: hashOtp, tokens: { token: "", rfToken: "", }, fullName: "", nationalCode: "", lastLogin: new Date(), clientKey: new Types.ObjectId(), birthDay: "", city: "", address: "", state: "", otpExpire, }); return new LoginDtoRs(newUser); } if (userExist) { if (userExist.otpExpire > new Date(new Date().getTime()).getTime()) throw new BadRequestException("Wait for expiry time to finish"); await this.smsSender(otp, mobile); const updateTokens = await this.userDbService.findOneAndUpdate( { username: userExist.username, }, { otp: hashOtp, otpExpire, }, ); if (updateTokens) return new LoginDtoRs(userExist); } } private async smsSender(otp: string, mobile: string) { return this.smsManagerService .verifyLookUp({ token: otp, template: process.env.AUTH_SMS_TEMPLATE, receptor: mobile, }) .then((smsRes) => { this.logger.log( `${"phone : " + mobile + " " + ", status : " + smsRes["return"]?.status + ", otp : " + otp} `, ); }) .catch((er) => { this.logger.error( `${"phone : " + mobile + " " + ", status : " + er["return"]?.status + ", otp : " + otp} `, ); throw new HttpException( " auth sms send failed", HttpStatus.INTERNAL_SERVER_ERROR, ); }); } }