import { Inject, Injectable } from '@nestjs/common'; import { FILE_REPOSITORY, type IFileRepository } from '../../domain/repositories/file.repository.interface.js'; import { StoredFile } from '../../domain/entities/stored-file.entity.js'; import { FileNotFoundException } from '../../domain/exceptions/file-not-found.exception.js'; import { FileOwnerRegistry } from './file-owner.registry.js'; import { FileDto } from '../dto/file.dto.js'; import { FileApplicationMapper } from '../mappers/file-application.mapper.js'; @Injectable() export class FileAccessService { constructor( @Inject(FILE_REPOSITORY) private readonly fileRepository: IFileRepository, private readonly fileOwners: FileOwnerRegistry, ) {} /** * Файл виден загрузившему и всем, кому виден хоть один его владелец * (например, задача, к которой он прикреплён). Иначе — FileNotFoundException, * чтобы не раскрывать существование чужих файлов. */ async getVisibleFile(id: string, userId: string): Promise { const file = await this.fileRepository.findById(id); if (!file) { throw new FileNotFoundException(id); } if (file.isUploadedBy(userId) || (await this.fileOwners.isVisibleThroughAnyOwner(id, userId))) { return file; } throw new FileNotFoundException(id); } /** * Файл, который пользователь может прикрепить к задаче/комментарию: * невидимый — FileNotFoundException, видимый, но чужой — FileForbiddenException. */ async getAttachableFile(id: string, userId: string): Promise { const file = await this.getVisibleFile(id, userId); file.assertOwnedBy(userId); return FileApplicationMapper.toDto(file); } }