itcloud/tech_spec_cloud_media_stora...

507 lines
21 KiB
Markdown
Raw Permalink Normal View History

2025-12-30 13:35:19 +01:00
# Техническое задание (ТЗ)
## Проект: облачное хранилище фото и видео (S3 + Python backend + SQLite → PostgreSQL)
**Формат фронтенда:** статический сайт (SPA) в папке `static/`, хостинг в S3
**UI:** Material UI (MUI)
**Backend:** Python (FastAPI)
**Хранилище файлов:** S3 / S3-compatible (MinIO и т.п.)
**База данных:** SQLite на старте, в будущем — PostgreSQL (миграции через Alembic)
---
## 1. Цели и принципы
### 1.1 Цель продукта
Создать удобное, быстрое и безопасное облачное хранилище для фото и видео с фокусом на:
- максимально простой и быстрый загрузчик (drag&drop, пакетные загрузки, большие файлы);
- удобную библиотеку (лента/сеткой), быстрый просмотр, поиск/фильтры;
- безопасный доступ и шаринг ссылками;
- готовность к росту: миграция SQLite → PostgreSQL без переписывания бизнес-логики.
### 1.2 Принципы реализации
- **SOLID / DRY / Clean Architecture**: разделение слоёв (API → Service → Repository → Infrastructure).
- **Докстринги обязательны** для публичных методов/классов и основных сервисов.
- **Минимум комментариев в коде**: предпочтение самодокументирующемуся коду и докстрингам.
- **Стабильный API-контракт**: OpenAPI/Swagger (FastAPI).
- **Сразу закладываем расширяемость** (теги, альбомы, шаринг, фоновые задачи).
---
## 2. Область охвата (Scope)
### 2.1 MVP (минимально жизнеспособная версия)
1) Регистрация/логин (минимум: email+пароль)
2) Загрузка фото/видео в S3 (через pre-signed URLs)
3) Библиотека медиа:
- список/сетка,
- просмотр (лайтбокс) фото,
- просмотр/проигрывание видео,
- базовые сортировки (по дате добавления, по дате съёмки).
4) Удаление (корзина/soft-delete) и восстановление.
5) Простой шаринг публичной ссылкой (срок действия).
6) Генерация превью для изображений (thumbnails).
7) Хранение метаданных (SQLite), подготовлено для PostgreSQL (ORM + миграции).
### 2.2 Версия v1 (после MVP)
- Альбомы/папки (логическая группировка).
- Теги и поиск по тегам/дате/типу/размеру.
- Извлечение EXIF (камера, дата съёмки, гео — опционально).
- Превью/постер для видео.
- Множественный выбор, пакетные операции (переместить/удалить/добавить теги).
- Пользовательские квоты и статистика хранения.
- Логи действий (аудит: загрузка/удаление/шаринг).
### 2.3 Вне рамок (Non-goals) на старте
- Полноценное видеотранскодирование в HLS/DASH.
- Распознавание лиц/умные альбомы.
- Коллаборация “семейные библиотеки” (несколько владельцев одного альбома).
- End-to-end шифрование на клиенте.
---
## 3. Роли и сценарии
### 3.1 Роли
- **User**: загружает и управляет своей медиатекой.
- **Admin** (опционально в v1): администрирование пользователей/квот и доступ ко всем облакам пользователей.
### 3.2 Ключевые user stories (MVP)
1) Как пользователь, я хочу быстро перетащить папку/файлы и загрузить их в облако.
2) Как пользователь, я хочу просматривать фото в полноэкранном режиме и листать стрелками.
3) Как пользователь, я хочу смотреть видео прямо в браузере.
4) Как пользователь, я хочу удалить файлы и при необходимости восстановить из корзины.
5) Как пользователь, я хочу поделиться ссылкой на один файл/альбом на ограниченное время.
---
## 4. UX/UI требования (фронтенд)
### 4.1 Технологическое решение фронтенда
**Рекомендуемый компромисс “JS чистый + MUI”:**
- Реализовать SPA на **React + MUI**,
- Сборка (Vite) выдаёт статические артефакты в `static/`,
- На проде в S3 лежит **только** `static/` (HTML/CSS/JS).
> Важно: Material UI (MUI) — React-библиотека. “Чистый JS без сборки” возможен через CDN + UMD, но это хуже по производительности и DX. В ТЗ закладываем правильную схему: разработка с Vite, деплой артефактов в `static/`.
### 4.2 Страницы/экраны (MVP)
1) **Login / Register**
2) **Library** (главная)
- переключатель “Grid / List”,
- фильтр по типу (photo/video),
- сортировка,
- строка поиска (в MVP можно скрыть за feature-flag).
3) **Viewer**
- фото: зум, листание, скачать,
- видео: плеер (HTML5), скачать.
4) **Upload**
- drag&drop зона,
- прогресс по файлу и общий прогресс,
- повтор при ошибке.
5) **Trash**
- список удалённых, restore/purge.
6) **Shared view** (страница по публичной ссылке)
### 4.3 Компоненты UI
- AppBar + боковое меню (Drawer)
- MediaGrid (виртуализация списка, infinite scroll)
- MediaCard (thumbnail, иконка видео, размер/дата)
- UploadDialog (dropzone + очередь + прогресс)
- ViewerModal (keyboard nav: ← → Esc)
- FiltersBar (chips/select)
### 4.4 Нефункциональные требования к UI
- **Responsive**: desktop-first, корректно на мобилках.
- **Оптимизация**:
- thumbnails грузятся лениво,
- viewer тянет оригинал только по запросу,
- infinite scroll.
- **Удобство**:
- drag&drop в любом месте Library,
- горячие клавиши в Viewer,
- понятные ошибки и “повторить загрузку”.
---
## 5. Архитектура (общая)
### 5.1 Компоненты
1) **Static SPA** (S3 hosting)
2) **Backend API** (Python)
3) **Database** (SQLite → PostgreSQL)
4) **Object storage** (S3)
5) **Background worker** (опционально сразу, обязательно для v1) — генерация превью/постеров
### 5.2 Потоки
**Upload (рекомендуемый):**
1) SPA запрашивает у backend `create_upload` (получает pre-signed URL / multipart init).
2) SPA грузит файл напрямую в S3.
3) SPA вызывает `finalize_upload` (backend фиксирует метаданные, ставит задачу на превью).
4) Backend возвращает Asset ID.
**Download/View:**
- SPA запрашивает `get_asset_download_url` → получает краткоживущую signed-ссылку на S3 (оригинал/thumbnail).
---
## 6. Backend требования
### 6.1 Стек (рекомендуется)
- **FastAPI** (ASGI)
- **Pydantic** (схемы)
- **SQLAlchemy 2.x (async)** + **Alembic** (миграции)
- **SQLite** (MVP), конфигурация диалекта для лёгкого перехода на PostgreSQL
- S3 SDK: boto3 (sync) или aioboto3 (async) / или минимальный клиент через presigned
- Auth: JWT access + refresh (HTTP-only cookies) или Bearer tokens
- Logging: structlog/loguru + стандартный logging
- Tests: pytest + httpx
### 6.2 Слоистая архитектура (Clean)
- `api/` — роуты, схемы, зависимости, auth middleware
- `services/` — бизнес-логика (upload, library, share)
- `repositories/` — доступ к данным (CRUD)
- `infra/` — S3 client, db session factory, config, background tasks
- `domain/` — модели домена/интерфейсы (по необходимости)
### 6.3 Обязательные нефункциональные требования
- Валидация входных данных (Pydantic).
- Единый формат ошибок (problem+json либо собственный стандарт).
- Rate limiting (минимально на login/share) — можно отложить до v1.
- Безопасность:
- пароли хранить только в виде хеша (argon2/bcrypt),
- все ссылки на S3 — только signed,
- CORS настроен строго,
- CSRF защита при cookie-based auth.
- Наблюдаемость:
- структурные логи,
- correlation id (request id),
- healthcheck endpoint.
---
## 7. Модель данных (SQLite → PostgreSQL)
### 7.1 Основные сущности
**users**
- `id` (UUID)
- `email` (unique)
- `password_hash`
- `created_at`, `updated_at`
- `is_active`
**assets**
- `id` (UUID)
- `user_id` (FK users)
- `type` enum: `photo|video`
- `status` enum: `uploading|ready|failed|deleted`
- `original_filename`
- `content_type`
- `size_bytes`
- `sha256` (optional, for dedup)
- `captured_at` (datetime, optional)
- `created_at`
- `deleted_at` (nullable)
- `storage_key_original` (S3 object key)
- `storage_key_thumb` (nullable)
- `width`, `height` (nullable)
- `duration_sec` (nullable)
**albums** (v1)
- `id` UUID
- `user_id`
- `title`
- `created_at`
**album_items** (v1)
- `album_id`
- `asset_id`
- `position` (int)
**tags** (v1)
- `id`, `user_id`, `name`
**asset_tags** (v1)
- `asset_id`, `tag_id`
**shares**
- `id` UUID
- `owner_user_id`
- `asset_id` (nullable) — если шаринг одного файла
- `album_id` (nullable) — если шаринг альбома
- `token` (unique, random)
- `expires_at` (nullable)
- `password_hash` (nullable)
- `created_at`
- `revoked_at` (nullable)
**auth_sessions** (опционально)
- `id`
- `user_id`
- `refresh_token_hash`
- `created_at`, `expires_at`, `revoked_at`
### 7.2 Требования к миграциям
- Все изменения БД — только через Alembic.
- Никаких raw SQL “внутри приложения”, кроме миграций.
- Схема должна работать в SQLite и PostgreSQL:
- UUID хранить как TEXT (SQLite) и UUID (PostgreSQL) через тип-переопределения,
- JSON поля избегать в MVP (или хранить TEXT).
---
## 8. Object Storage (S3)
### 8.1 Bucket и ключи
- Bucket: `MEDIA_BUCKET`
- Ключ оригинала: `u/{user_id}/o/{yyyy}/{mm}/{asset_id}{ext}`
- Ключ превью: `u/{user_id}/t/{yyyy}/{mm}/{asset_id}.jpg`
- (v1) ключ постера для видео: `u/{user_id}/p/{yyyy}/{mm}/{asset_id}.jpg`
### 8.2 Политики доступа
- Bucket **private**.
- Доступ только через pre-signed URLs с коротким TTL:
- thumbnails: 530 минут,
- originals: 110 минут (настраиваемо).
---
## 9. API (контракт)
### 9.1 Общие правила
- Версионирование: `/api/v1`
- Авторизация: `Authorization: Bearer <token>` (или cookie-based)
- Ответы: JSON
- Ошибки: единый формат `{ "error": { "code": "...", "message": "...", "details": ... } }`
### 9.2 Эндпоинты (MVP)
#### Auth
- `POST /api/v1/auth/register`
- `POST /api/v1/auth/login`
- `POST /api/v1/auth/logout`
- `GET /api/v1/auth/me`
- (опц) `POST /api/v1/auth/refresh`
#### Assets (library)
- `GET /api/v1/assets?cursor=&limit=&type=&sort=`
- `GET /api/v1/assets/{asset_id}`
- `DELETE /api/v1/assets/{asset_id}` (soft-delete)
- `POST /api/v1/assets/{asset_id}/restore`
- `DELETE /api/v1/assets/{asset_id}/purge` (hard-delete, только из Trash)
#### Upload (direct-to-S3)
- `POST /api/v1/uploads/create`
- вход: `original_filename, content_type, size_bytes`
- выход: `asset_id, upload_method, presigned_url|presigned_post|multipart`
- `POST /api/v1/uploads/{asset_id}/finalize`
- вход: `etag` (или parts list), `sha256` (опц)
- выход: `asset`
#### URLs (signed access)
- `GET /api/v1/assets/{asset_id}/download-url?kind=original|thumb`
- `GET /api/v1/assets/{asset_id}/stream-url` (для видео, kind=original на старте)
#### Shares
- `POST /api/v1/shares` (создать ссылку)
- `GET /api/v1/shares/{token}` (получить метаданные)
- `GET /api/v1/shares/{token}/download-url?asset_id=&kind=`
- `POST /api/v1/shares/{token}/revoke`
### 9.3 Пагинация
- Cursor-based (рекомендуется): `cursor` = base64(last_created_at, last_id)
- Ответ: `{ items: [...], next_cursor: "...", has_more: true }`
---
## 10. Превью (thumbnails) и обработка
### 10.1 MVP: только изображения
- После `finalize_upload` backend ставит задачу “generate_thumbnail(asset_id)”.
**Режим исполнения (на выбор):**
1) **Inline** (допустимо в MVP): генерация превью синхронно при finalize, если файл небольшой.
2) **Background worker** (лучше): Celery/RQ + Redis, либо встроенный простейший worker.
Рекомендуемый минимум:
- Redis + RQ.
### 10.2 Технические требования к thumbnails
- Формат: JPEG
- Максимальная сторона: 512px или 1024px (настраиваемо)
- Прогрессивный JPEG (желательно)
- Сохранение в S3 по `storage_key_thumb`
- При ошибке: `assets.status = failed` или отдельное поле `thumb_status`
### 10.3 v1: видео постеры
- Генерация постера (кадр на 13 секунде) через ffmpeg.
- Хранение отдельным ключом.
---
## 11. Безопасность
### 11.1 Аутентификация и пароли
- Хэширование argon2id (предпочтительно) или bcrypt.
- Ограничение попыток логина (v1).
- Сессии/refresh токены можно отзывать.
### 11.2 Доступ к файлам
- Все доступы к S3 только через signed URLs.
- Проверка владения assetом во всех endpoints.
- Подпись ссылок короткая и на конкретный объект.
### 11.3 Публичные ссылки
- token должен быть криптостойким (>= 128 бит энтропии).
- Возможность ограничить срок действия.
- (v1) опциональный пароль на ссылку.
---
## 12. Производительность и ограничения
### 12.1 Ограничения MVP
- Максимальный размер видео: конфиг `MAX_UPLOAD_SIZE_BYTES` (например 520 ГБ).
- Для больших файлов — multipart upload (S3 multipart).
- Лимит выдачи списка: 50200 элементов, infinite scroll.
### 12.2 Кэширование
- Thumbnails: CDN-friendly, но доступ через signed URL (TTL).
- Статические файлы фронтенда: cache-control immutable для hashed assets.
---
## 13. Конфигурация (env)
### 13.1 Backend env variables (пример)
- `APP_ENV=dev|prod`
- `DATABASE_URL=sqlite+aiosqlite:///./app.db` (позже: `postgresql+asyncpg://...`)
- `S3_ENDPOINT_URL=` (если MinIO)
- `S3_REGION=`
- `S3_ACCESS_KEY_ID=`
- `S3_SECRET_ACCESS_KEY=`
- `MEDIA_BUCKET=`
- `SIGNED_URL_TTL_SECONDS=600`
- `CORS_ORIGINS=https://your-domain`
- `JWT_SECRET=`
- `JWT_ACCESS_TTL_SECONDS=900`
- `JWT_REFRESH_TTL_SECONDS=1209600`
- `MAX_UPLOAD_SIZE_BYTES=`
---
## 14. Деплой и окружения
### 14.1 Static (S3)
- Папка `static/` заливается в S3 bucket для сайта.
### 14.2 Backend
- Docker-образ, запускается как контейнер (uvicorn/gunicorn).
- Рекомендуемый `docker-compose` для dev: backend + minio + sqlite (локально) + redis.
### 14.3 База данных
- MVP: SQLite файл в volume.
- v1: PostgreSQL отдельным сервисом, переключение через `DATABASE_URL` + миграции alembic.
---
## 15. Структура репозитория (рекомендация)
```
repo/
backend/
src/
app/
api/
v1/
services/
repositories/
infra/
domain/
main.py
alembic/
tests/
pyproject.toml
Dockerfile
frontend/
src/
public/
vite.config.js
package.json
static/ # build output (deploy to S3)
docker-compose.yml
README.md
```
---
## 16. Критерии приёмки (Acceptance Criteria)
### 16.1 MVP
- Пользователь может зарегистрироваться/войти.
- Пользователь может загрузить:
- минимум 1 фото и 1 видео,
- пакет из 100+ фото,
- большой файл (в пределах лимита), без падения сервера.
- В библиотеке отображаются thumbnails для фото.
- Просмотр фото/видео работает в браузере.
- Удаление перемещает в корзину, restore возвращает.
- Public share link открывается без логина (если активен) и даёт доступ только к расшаренному объекту.
- База — SQLite, миграции работают, есть путь миграции на PostgreSQL (без изменения кода сервисов).
### 16.2 Качество кода
- Линтер/форматтер настроен (ruff + black или ruff format).
- Тесты покрывают ключевые сценарии (auth, create/finalize upload, list assets, share).
- Докстринги присутствуют в слоях services/repositories/infra.
---
## 17. План работ (укрупнённо)
### Этап 1 — фундамент (1)
- Скелет backend (FastAPI), конфиг, подключение БД, миграции.
- User + Auth (register/login/me).
- S3 интеграция: pre-signed upload + signed download.
### Этап 2 — assets (2)
- CRUD метаданных assets.
- Library list с пагинацией.
- Trash (soft-delete/restore/purge).
### Этап 3 — frontend MVP (3)
- Login/Register.
- Library grid + infinite scroll.
- Upload dialog + прогресс.
- Viewer modal.
- Trash page.
### Этап 4 — thumbnails (4)
- Генерация превью (inline или background).
- Отображение превью в Library.
### Этап 5 — shares (5)
- Создание/открытие share links.
- Shared view UI.
---
## 18. Риски и решения
- **Большие видео**: сразу закладываем multipart upload, иначе упремся в лимиты/таймауты.
- **MUI и “чистый JS”**: реальный путь — React+MUI и сборка в `static/`.
- **SQLite ограничения**: используем репозитории/ORM и Alembic, чтобы миграция на PostgreSQL была сменой `DATABASE_URL`.
- **Стоимость S3**: thumbnails уменьшают трафик; оригиналы только по запросу.
---
## 19. Доп. ПО (если понадобятся очереди/воркеры)
- **Redis** (рекомендуется)
- **RQ** (проще)
- Для видео в v1: **ffmpeg** (в контейнере воркера)
---
## 20. Примечания по лицензиям и приватности
- Пользовательские файлы и метаданные считаются приватными, но админ может всё просматривать.
- Логи не должны содержать содержимое файлов и чувствительные данные (пароли/токены).
- При шаринге — показывать минимум метаданных.