import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { verify } from 'argon2'; import { PrismaService } from '@/src/core/prisma/prisma.service'; import { RedisService } from '@/src/core/redis/redis.service'; import { DeactivateAccountInput } from '@/src/module/auth/deactivate/inputs/deactivate-account.input'; import { MailService } from '@/src/module/libs/mail/mail.service'; import { TelegramService } from '@/src/module/libs/telegram/telegram.service'; import { generateToken } from '@/src/shared/util/generate-token.util'; import { getSessionMetadata } from '@/src/shared/util/session-metadata.util'; import { destroySession } from '@/src/shared/util/session.util'; import { TokenType, User } from '@prisma/generated'; import { ProcessEnv } from '../../../shared/types/env'; import type { Request } from 'express'; @Injectable() export class DeactivateService { constructor( private readonly prismaService: PrismaService, private readonly redisService: RedisService, private readonly configService: ConfigService, private readonly mailService: MailService, private readonly telegramService: TelegramService, ) { } public async deactivate(req: Request, input: DeactivateAccountInput, user: User, userAgent: string) { const { email, password, pin } = input; if (email !== user.email) { throw new BadRequestException('Неверная почта'); } const isValidPassword = await verify(user.password, password); if (!isValidPassword) { throw new BadRequestException('Неверный пароль'); } if (!pin) { await this.sendDeactivationToken(req, user, userAgent); return { message: 'Требуется ввести код подтверждения' }; } await this.validateDeactivateToken(req, pin); return { user }; } public async sendDeactivationToken(req: Request, user: User, userAgent: string) { const deactivateToken = await generateToken( this.prismaService, user, TokenType.DEACTIVATE_ACCOUNT, false, ); const metadata = getSessionMetadata(req, userAgent); await this.mailService.sendDeactivateToken(user.email, deactivateToken.token, metadata); if (deactivateToken.user?.notificationSettings?.telegramNotifications && deactivateToken.user.telegramId) { await this.telegramService.sendDeactivateToken(deactivateToken.user.telegramId, deactivateToken.token, metadata); } return true; } private async validateDeactivateToken(req: Request, token: string) { const existingToken = await this.prismaService.token.findUnique({ where: { token, type: TokenType.DEACTIVATE_ACCOUNT }, }); if (!existingToken?.userId) { throw new NotFoundException('Токен не найден'); } const hasExpired = new Date(existingToken.expiresIn) < new Date(); if (hasExpired) { throw new BadRequestException('Токен истек'); } const user = await this.prismaService.user.update({ where: { id: existingToken.userId }, data: { isDeactivated: true, deactivatedAt: new Date(), }, }); await this.prismaService.token.delete({ where: { id: existingToken.id, type: TokenType.DEACTIVATE_ACCOUNT, }, }); await this.clearSessions(user.id); return destroySession(req, this.configService); } private async clearSessions(userId: string) { const keys = await this.redisService.keys('*'); for (const key of keys) { const sessionData = await this.redisService.get(key); if (sessionData) { const session = JSON.parse(sessionData); if (session.userId === userId) { await this.redisService.del(key); } } } } }