feat: add otp and hashing modules

This commit is contained in:
2026-02-21 09:10:45 +03:30
parent e657264807
commit 20c89c472c
12 changed files with 1122 additions and 1298 deletions
+2 -2
View File
@@ -33,13 +33,13 @@ export class OTP {
@Index()
@Column({ type: 'timestamptz' })
expiredAt: Date;
expiresAt: Date;
@Column({ type: 'boolean', default: false })
used: boolean;
@BeforeInsert()
setExpiry() {
this.expiredAt = new Date(Date.now() + 2 * 60 * 1000);
this.expiresAt = new Date(Date.now() + 2 * 60 * 1000);
}
}
+1
View File
@@ -6,5 +6,6 @@ import { OTP } from './otp.entity';
@Module({
imports: [TypeOrmModule.forFeature([OTP])],
providers: [OtpService],
exports: [OtpService],
})
export class OtpModule {}
+53 -4
View File
@@ -1,7 +1,13 @@
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import {
HttpException,
HttpStatus,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { LessThan, Repository } from 'typeorm';
import { OTP } from '../otp.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Cron } from '@nestjs/schedule';
@Injectable()
export class OtpService {
@@ -31,9 +37,52 @@ export class OtpService {
});
if (lastOTP && Date.now() - lastOTP.createdAt.getTime() < 60_000) {
// throw new TooMany();
throw new HttpException(
'Too many requests! Try again after 1 min.',
HttpStatus.TOO_MANY_REQUESTS,
);
}
return '';
const code = this.generateOTPCode();
// TODO: Hash the code
const otp = this.otpRepository.create({ phone, code });
await this.otpRepository.save(otp);
return code;
}
/**
* Verify OTP
*/
public async verifyOTP(phone: string, code: string): Promise<OTP> {
const otp = await this.otpRepository.findOne({
where: { phone, used: false },
order: { createdAt: 'DESC' },
});
if (!otp) throw new UnauthorizedException('Invalid OTP');
if (otp.expiresAt < new Date()) {
throw new UnauthorizedException('OTP expired');
}
// TODO: Compared hashed otp
if (otp.code !== code) {
throw new UnauthorizedException('Invalid OTP');
}
otp.used = true;
await this.otpRepository.save(otp);
return otp;
}
@Cron('0 */10 * * * *')
public async cleanupExpired(): Promise<void> {
await this.otpRepository.delete({
expiresAt: LessThan(new Date()),
});
}
}