123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- import { MailService } from "#services/mail-service.js";
- import { logger } from "#logger";
- import { db } from "#db";
- import { sql } from "slonik";
- import { ZDbShema } from "#db-shema";
- import { z } from "zod";
- // dayjs
- import { dayjs, DayjsUtils } from "#dayjs";
- class confirmPinsService {
- // privalte
- private genRandom4DigitNumber() {
- return Math.floor(1000 + Math.random() * 9000);
- }
- private async deleteConfirmPin(transactionId: string, email: string) {
- await db.any(
- sql.unsafe`delete from users.user_confirm_pins where transaction_id = ${transactionId} and email = ${email}`,
- );
- }
- private async pinTriesIncrement(transactionId: string, email: string) {
- await db.any(
- sql.unsafe`update users.user_confirm_pins set wrong_pin_tries = wrong_pin_tries + 1 where transaction_id = ${transactionId} and email = ${email}`,
- );
- }
- //
- // public
- //
- async sendConfirmPin(transactionId: string, email: string) {
- const confirmPin = this.genRandom4DigitNumber();
- // удаляем если пин уже есть
- await this.deleteConfirmPin(transactionId, email);
- // отправка
- const mailBody = `
- <div>
- <h1>Ваш временный код:</h1>
- <h4> <a href="${confirmPin}">${confirmPin}</a> </h4>
- </div>
- `;
- await MailService.sendMail(email, "Ваш код для EVENT", mailBody);
- // бд
- await db.any(
- sql.unsafe`
- insert into users.user_confirm_pins (
- transaction_id,
- email,
- confirm_pin,
- create_time)
- values (
- ${transactionId},
- ${email},
- ${confirmPin},
- ${DayjsUtils.createDayjsUtcWithoutOffset().toISOString()})`,
- );
- logger.info("Отправлен временный код: ", {
- email,
- confirmPin,
- });
- }
- async checkConfirmPin(
- transactionId: string,
- email: string,
- confirmPin: number,
- ): Promise<
- | "correct"
- | "rotten"
- | "tooManyTries"
- | {
- status: "wrong";
- triesRemained?: number;
- }
- > {
- const pinInfo = await db.maybeOne(
- sql.type(
- z.object({
- confirm_pin: ZDbShema.users.user_confirm_pins.confirm_pin,
- create_time: ZDbShema.users.user_confirm_pins.create_time,
- wrong_pin_tries: ZDbShema.users.user_confirm_pins.wrong_pin_tries,
- }),
- )`select confirm_pin, create_time, wrong_pin_tries from users.user_confirm_pins where transaction_id = ${transactionId} and email = ${email}`,
- );
- // не существует
- if (!pinInfo) {
- return "rotten";
- }
- // много попыток
- if (
- pinInfo.wrong_pin_tries >
- Number(process.env.CONFIRM_PIN_MAX_TRIES) - 1
- ) {
- return "tooManyTries";
- }
- // просрочка
- if (
- DayjsUtils.createDayjsUtcWithoutOffset().isAfter(
- dayjs
- .utc(pinInfo.create_time)
- .add(Number(process.env.CONFIRM_PIN_LIFETIME_MINS), "minutes"),
- )
- ) {
- await this.deleteConfirmPin(transactionId, email);
- return "rotten";
- }
- // неправильный
- if (pinInfo.confirm_pin !== confirmPin) {
- await this.pinTriesIncrement(transactionId, email);
- const triesRemained =
- Number(process.env.CONFIRM_PIN_MAX_TRIES) - 1 - pinInfo.wrong_pin_tries;
- return {
- status: "wrong",
- triesRemained,
- };
- }
- // правильный
- await this.deleteConfirmPin(transactionId, email);
- return "correct";
- }
- }
- export const ConfirmPinsService = new confirmPinsService();
|