pos-be/src/modules/booking-online/authentication/data/services/verification.service.ts

111 lines
3.3 KiB
TypeScript

import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { VerificationModel } from '../models/verification.model';
import { BookingVerification } from '../../domain/entities/booking-verification.entity';
import { UnprocessableEntityException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { WhatsappService } from 'src/services/whatsapp/whatsapp.service';
export class VerificationService {
constructor(
@InjectRepository(VerificationModel)
private readonly verificationRepository: Repository<VerificationModel>,
private readonly jwtService: JwtService,
) {}
maxAttempts = 3;
expiredTime = 5 * 60 * 1000;
expiredTimeRegister = 1 * 60 * 1000;
async generateToken(payload: BookingVerification) {
return this.jwtService.sign({
phone_number: payload.phone_number,
name: payload.name,
created_at: payload.created_at,
});
}
async register(data: BookingVerification) {
const isProduction = process.env.NODE_ENV === 'true';
const currentTime = Math.floor(Date.now()); // current time in seconds
// Generate a 4 digit OTP code
const otpCode = Math.floor(1000 + Math.random() * 9000).toString();
let verification = await this.verificationRepository.findOne({
where: { phone_number: data.phone_number },
});
if (
isProduction &&
verification.updated_at &&
currentTime - verification.updated_at < this.expiredTimeRegister
) {
throw new UnprocessableEntityException('Please try again in 1 minute');
}
data.code = otpCode;
data.tried = 0;
data.updated_at = currentTime;
if (verification) {
// Update existing record
verification = this.verificationRepository.merge(verification, data);
} else {
// Create new record
verification = this.verificationRepository.create(data);
}
const payload = await this.verificationRepository.save(verification);
const notificationService = new WhatsappService();
notificationService.sendOtpNotification({
phone: data.phone_number,
code: otpCode,
});
return payload;
}
async findByPhoneNumber(phoneNumber: string) {
return this.verificationRepository.findOne({
where: { phone_number: phoneNumber },
});
}
async verify(data: BookingVerification): Promise<BookingVerification> {
const verification = await this.findByPhoneNumber(data.phone_number);
if (!verification) {
throw new UnprocessableEntityException('Phone number not found');
}
if (verification.tried >= this.maxAttempts) {
throw new UnprocessableEntityException(
'Too many attempts, please resend OTP Code',
);
}
if (verification.code != data.code) {
verification.tried++;
await this.verificationRepository.save(verification);
throw new UnprocessableEntityException('Invalid verification code');
}
const currentTime = Math.floor(Date.now());
if (
verification.updated_at &&
currentTime - verification.updated_at > this.expiredTime
) {
throw new UnprocessableEntityException('Verification code expired');
}
return verification;
}
async update(id: string, data: BookingVerification) {
return this.verificationRepository.update(id, data);
}
async delete(id: string) {
return this.verificationRepository.delete(id);
}
}