chore(openspec): archive completed changes and sync main specs

- archive local-fs-storage-backend, sqlite-metadata-backend,
  storage-orphan-reconciliation, metadata-managed-jwt-secret, default-local-backends
- sync specs: storage-backends, metadata-backends, storage-reconciliation,
  app-secrets; update photos/share-links
- defaults reflected in specs: SQLite and local FS
This commit is contained in:
iu
2026-10-11 10:19:44 +03:00
parent cb272398ce
commit 8638d1bbfc
33 changed files with 392 additions and 13 deletions
@@ -0,0 +1,108 @@
# Spec Delta
## Purpose
Выбор и работа объектного хранилища: S3-совместимое (Garage) или локальная файловая
система. Блобы всегда отдаются через бэкенд по подписанному URL, хранилище клиенту не
раскрывается.
## ADDED Requirements
### Requirement: Выбор бэкенда хранилища
Система SHALL поддерживать выбор бэкенда объектного хранилища параметром конфигурации,
при этом S3-совместимый бэкенд SHALL оставаться значением по умолчанию.
#### Scenario: Выбор S3-совместимого бэкенда
- **WHEN** в конфигурации задан S3-совместимый бэкенд
- **THEN** система хранит и отдаёт блобы через S3-совместимое хранилище
#### Scenario: Выбор локальной ФС
- **WHEN** в конфигурации задан бэкенд локальной ФС
- **THEN** система хранит и отдаёт блобы из каталога локальной файловой системы
#### Scenario: Неполная конфигурация
- **WHEN** конфигурация выбранного бэкенда неполна
- **THEN** система отказывается стартовать с явной ошибкой конфигурации
### Requirement: Единая абстракция хранилища
Операции записи, чтения, удаления и проверки доступности SHALL выполняться через единый
интерфейс хранилища, одинаковый для всех бэкендов; ключи объектов SHALL оставаться
непрозрачными и независимыми от бэкенда.
#### Scenario: Ключи переносимы между бэкендами
- **WHEN** один и тот же ключ объекта используется на разных бэкендах
- **THEN** он адресует тот же логический блоб без изменения формата
#### Scenario: Удаление объекта
- **WHEN** система удаляет объект через интерфейс хранилища
- **THEN** блоб удаляется выбранным бэкендом
### Requirement: Отдача блобов через бэкенд
Клиент SHALL получать любой блоб по URL, обслуживаемому бэкендом, с ограниченным сроком
действия; ни один бэкенд SHALL не отдавать клиенту ссылку напрямую на объектное
хранилище. Для анонимного доступа по share-ссылке подписанный URL SHALL быть действителен
без пользовательской сессии.
#### Scenario: Свежий URL просмотра
- **WHEN** клиент запрашивает фото
- **THEN** система возвращает подписанный URL бэкенда со сроком действия до 1 часа
#### Scenario: Доступ по share-ссылке
- **WHEN** аноним открывает альбом по действующей share-ссылке
- **THEN** полученные подписанные URL позволяют загрузить фото без сессии
#### Scenario: Истёкшая подпись
- **WHEN** клиент обращается к URL с истёкшей или неверной подписью
- **THEN** система отказывает в доступе
### Requirement: Хранилище не раскрывается клиенту
Адрес и учётные данные объектного хранилища SHALL оставаться внутренними; клиент SHALL
взаимодействовать с медиа только через бэкенд, независимо от бэкенда хранения.
#### Scenario: S3-совместимый бэкенд
- **WHEN** хранилищем выбран S3-совместимый бэкенд
- **THEN** выдаваемый клиенту URL указывает на бэкенд, а не на S3-эндпоинт
#### Scenario: Локальная ФС
- **WHEN** хранилищем выбрана локальная ФС
- **THEN** выдаваемый клиенту URL указывает на бэкенд
#### Scenario: Хранилище недоступно извне
- **WHEN** объектное хранилище недоступно из внешней сети
- **THEN** просмотр и скачивание медиа продолжают работать
### Requirement: Локальное хранение на ФС
Бэкенд локальной ФС SHALL хранить блобы в пределах заданного корневого каталога по
непрозрачным ключам объектов и SHALL отвергать ключи, выводящие за пределы корня.
#### Scenario: Запись и чтение блоба
- **WHEN** система сохраняет объект с ключом
- **THEN** блоб размещается под корневым каталогом и позже читается по тому же ключу
#### Scenario: Попытка выхода из корня
- **WHEN** ключ содержит элементы обхода каталога
- **THEN** система отвергает операцию и не обращается за пределы корня
### Requirement: Диапазонные запросы и тип содержимого
При отдаче блоба система SHALL возвращать корректный тип содержимого и SHALL
поддерживать диапазонные запросы для докачки и больших файлов.
#### Scenario: Запрос диапазона
- **WHEN** клиент запрашивает часть блоба через диапазон
- **THEN** система возвращает запрошенный диапазон и соответствующий статус
#### Scenario: Тип содержимого
- **WHEN** клиент загружает блоб
- **THEN** ответ содержит тип содержимого, соответствующий объекту
### Requirement: Доступность выбранного хранилища
Система SHALL сообщать о доступности выбранного бэкенда хранилища и отражать его в
состоянии здоровья сервиса.
#### Scenario: Хранилище доступно
- **WHEN** выбранный бэкенд отвечает на проверку
- **THEN** состоянии здоровья отражает хранилище как доступное
#### Scenario: Хранилище недоступно
- **WHEN** выбранный бэкенд не отвечает на проверку
- **THEN** состояние здоровья отражает деградацию