111 lines
3.3 KiB
TypeScript
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);
|
|
}
|
|
}
|