Files
ds-galo/openspec/changes/local-fs-storage-backend/tasks.md
T
iu ec8d907565 feat(storage): serve media through backend and add local FS backend
- introduce storage.Backend with S3 and local file-system implementations
- deliver all blobs via signed GET/HEAD /api/objects/<key> (Range, Content-Type)
- stream S3 objects through the backend; remove presigned URL path
- share shared HMAC Signer between backends
- remove S3_PUBLIC_URL; rename S3_PRESIGN_TTL to OBJECT_URL_TTL
- make storage label in health dynamic; nginx proxy_buffering off for objects
2026-10-11 09:15:03 +03:00

5.0 KiB
Raw Blame History

Tasks

1. Интерфейс хранилища и перевод S3

  • 1.1 Ввести интерфейс storage.Backend (Put/Delete/Get/Ping/EnsureBucket/Bucket/URL) и сделать S3-клиент его реализацией; проверка — go build ./... проходит.
  • 1.2 Заменить PresignGet на URL(ctx, key) во всех вызовах; проверка — go build ./... и go vet ./... без ошибок.
  • 1.3 Перевести photos.Service, albums.Service, app.Server и cmd/* на тип storage.Backend; проверка — сборка cmd/server и cmd/backfill-exif успешна.
  • 1.4 Прогнать существующие S3-тесты (под S3_ENDPOINT); проверка — тесты зелёные или корректно пропущены без env.

2. Бэкенд локальной ФС

  • 2.1 Реализовать fs.Backend (Put/Get/Delete/Ping/EnsureBucket/Bucket) с раскладкой <root>/<key> и нормализацией пути; проверка — юнит-тесты записи, чтения, удаления и отказа на ../абсолютный ключ проходят.
  • 2.2 Реализовать URL с HMAC-SHA256 подписью (key+exp, URL_SECRET с fallback на JWT_SECRET) и сроком OBJECT_URL_TTL; проверка — юнит-тесты валидной, истёкшей и подделанной подписи проходят.

3. Маршрут отдачи объектов

  • 3.1 Добавить публичный GET /api/objects/<key> с проверкой подписи и срока и стримингом через http.ServeContent; проверка — httptest-тесты: валидный доступ (200), истёкшая подпись (403), Range (206), отсутствующий ключ (404).
  • 3.2 Проверить вывод Content-Type по расширению с fallback application/octet-stream; проверка — тест на .jpg/.webp и неизвестное расширение.
  • 3.3 Убедиться, что share-ссылки отдают рабочие подписанные URL на обоих бэкендах; проверка — интеграционный тест share/* с fs и s3.

4. Конфигурация, health, проводка, документация

  • 4.1 Ввести STORAGE_BACKEND=s3|fs, FS_ROOT, URL_SECRET с условной валидацией; проверка — юнит-тесты config.Load для s3, fs и неполной конфигурации.
  • 4.2 Сделать метку хранилища в health динамической по выбранному бэкенду; проверка — тест /api/health для s3 и fs.
  • 4.3 Обновить .env.example и project.md; проверка — документированный запуск с STORAGE_BACKEND=fs стартует и отдаёт файл.

5. Интеграционные проверки

  • 5.1 Прогнать полный поток (загрузка → превью → просмотр → скачивание → share) на fs; проверка — наблюдаемые результаты совпадают с s3.
  • 5.2 Убедиться, что дефолт s3 не меняет текущее развёртывание; проверка — запуск без новых переменных проходит как раньше.

6. Доставка только через бэкенд (без presign)

  • 6.1 Вынести подпись URL в общий Signer и использовать его в FS и S3; проверка — юнит-тесты подписи и go build ./... проходят.
  • 6.2 Сделать ServeObject обязательной частью storage.Backend; app.serveObject диспетчеризуется без type-assert; проверка — go build ./....
  • 6.3 Реализовать S3.ServeObject через ranged GetObject + стриминг и HEAD через HeadObject; проверка — httptest-тесты 200/206/416/404.
  • 6.4 Удалить presign-путь (PresignClient/PresignGetObject), вернуть S3.URL как app-signed; проверка — код presign отсутствует, тесты отдачи проходят.
  • 6.5 Конфиг: переименовать S3_PRESIGN_TTL → OBJECT_URL_TTL, удалить S3_PUBLIC_URL; обновить .env.example, project.md, deploy/nginx-sse.conf (proxy_buffering off для /api/objects/); проверка — юнит-тесты config.Load и документированный запуск.
  • 6.6 Интеграция: полный поток и share на s3 в proxy-режиме при недоступном извне хранилище; проверка — тест зелёный, наружу уходит URL бэкенда, не S3.