60 lines
1.5 KiB
TypeScript
60 lines
1.5 KiB
TypeScript
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 }
|
|
}
|
|
}
|