122 lines
3.8 KiB
TypeScript
122 lines
3.8 KiB
TypeScript
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<ProcessEnv>,
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|