import { BadRequestException, type CanActivate, type ExecutionContext, Injectable, NotFoundException, UnauthorizedException, } from '@nestjs/common'; import type { UserSession } from '@thallesp/nestjs-better-auth'; import { CommentAccessService } from '../../application/services/comment-access.service.js'; import { IssueNotFoundException } from '../../../issue/domain/exceptions/issue-not-found.exception.js'; /** * Проверяет доступ к задаче из `:issueId` ДО вызова обработчика. * * Нужен SSE-эндпоинтам: при наличии глобального interceptor'а Nest вызывает * @Sse-обработчик уже после отправки заголовков 200, и ошибка из него доходит * до клиента только как `event: error`. Guard же отвечает честным 404. * * Сессию в request кладёт глобальный AuthGuard, который выполняется раньше. */ @Injectable() export class IssueVisibleGuard implements CanActivate { constructor(private readonly commentAccess: CommentAccessService) {} async canActivate(context: ExecutionContext): Promise { const request = context .switchToHttp() .getRequest<{ params: Record; session?: UserSession }>(); const userId = request.session?.user.id; if (!userId) { throw new UnauthorizedException(); } // Пайпы (ParseIntPipe) выполняются после guard'ов — парсим сами const issueId = Number(request.params.issueId); if (!Number.isInteger(issueId)) { throw new BadRequestException('Validation failed (numeric string is expected)'); } if (!(await this.commentAccess.isIssueVisible(issueId, userId))) { throw new NotFoundException(new IssueNotFoundException(issueId).message); } return true; } }