Add file module for uploads and storage

- File model + migration: file metadata, content lives in object storage
- IFileStorage port with local disk and S3-compatible (RustFS/MinIO) adapters
- POST /files, GET /files/:id/download, DELETE /files/:id
- MIME sniffed from content; only safe images served inline, nosniff header
- FileOwnerRegistry: owner modules register to grant visibility and keep
  their files from GC; FileGcService removes orphaned files hourly
- TransformResponseInterceptor passes StreamableFile through

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
vadimandClaude Opus 5.5 committed 2026-10-02 09:17:55 +03:00
1 parent 5cfc8986d7
commit f32c6e348d
49 files changed
+1992 -2

No files matched your search

@@ -0,0 +1,24 @@
import { Inject, Injectable } from '@nestjs/common';
import { FILE_REPOSITORY, type IFileRepository } from '../../../domain/repositories/file.repository.interface.js';
import { GetFilesQuery } from './get-files.query.js';
import { FileDto } from '../../dto/file.dto.js';
import { FileApplicationMapper } from '../../mappers/file-application.mapper.js';
/**
* Метаданные файлов по id — для модулей-владельцев, раскрывающих свои связи
* в FileDto (по аналогии с GetUsersUseCase). Доступ проверяет вызывающий:
* он отдаёт только файлы своих сущностей, видимых пользователю.
*/
@Injectable()
export class GetFilesUseCase {
constructor(
@Inject(FILE_REPOSITORY)
private readonly fileRepository: IFileRepository,
) {}
async execute(query: GetFilesQuery): Promise<FileDto[]> {
const ids = [...new Set(query.ids)];
const files = await this.fileRepository.findByIds(ids);
return files.map(FileApplicationMapper.toDto);
}
}