import { createReadStream, createWriteStream } from 'node:fs'; import { mkdir, rm, stat } from 'node:fs/promises'; import { dirname, resolve, sep } from 'node:path'; import type { Readable } from 'node:stream'; import { pipeline } from 'node:stream/promises'; import type { IFileStorage } from '../../domain/ports/file-storage.port.js'; /** Хранилище на локальном диске — для разработки и тестов */ export class LocalFileStorage implements IFileStorage { private readonly root: string; constructor(root: string) { this.root = resolve(root); } async put(key: string, body: Readable): Promise { const path = this.resolveKey(key); await mkdir(dirname(path), { recursive: true }); await pipeline(body, createWriteStream(path)); } async get(key: string): Promise { const path = this.resolveKey(key); await stat(path); return createReadStream(path); } async delete(key: string): Promise { await rm(this.resolveKey(key), { force: true }); } /** Ключи генерирует приложение, но выход за пределы root всё равно запрещаем */ private resolveKey(key: string): string { const path = resolve(this.root, key); if (!path.startsWith(this.root + sep)) { throw new Error(`Недопустимый ключ хранилища: ${key}`); } return path; } }