itcloud/tech_spec_cloud_media_stora...

507 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Техническое задание (ТЗ)
## Проект: облачное хранилище фото и видео (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. Примечания по лицензиям и приватности
- Пользовательские файлы и метаданные считаются приватными, но админ может всё просматривать.
- Логи не должны содержать содержимое файлов и чувствительные данные (пароли/токены).
- При шаринге — показывать минимум метаданных.