import { Injectable } from '@nestjs/common' import { RpcException } from '@nestjs/microservices' import { RpcStatus } from '@teacinema/common' import { createHash } from 'node:crypto' import { RedisService } from '@/infra/redis/redis.service' @Injectable() export class OtpService { public constructor(private readonly redisService: RedisService) {} public async send(indentifier: string, type: 'email' | 'phone') { const { code, hash } = this.generateCode() await this.redisService.set( `otp:${type}:${indentifier}`, hash, 'EX', 60 * 5 ) return code } public async verify( indentifier: string, code: string, type: 'email' | 'phone' ) { const stroredHash = await this.redisService.get( `otp:${type}:${indentifier}` ) if (!stroredHash) { throw new RpcException({ code: RpcStatus.NOT_FOUND, details: 'Invalid or expired code' }) } const hash = createHash('sha256').update(code).digest('hex') if (stroredHash !== hash) { throw new RpcException({ code: RpcStatus.NOT_FOUND, details: 'Invalid or expired code' }) } await this.redisService.del(`otp:${type}:${indentifier}`) } private generateCode() { // todo посмотреть готовые бибилотеки для генерации friendly otp const code = Math.floor(100_000 + Math.random() * 900_000) const hash = createHash('sha256').update(String(code)).digest('hex') return { code, hash } } }