# Техническое задание (ТЗ) ## Проект: облачное хранилище фото и видео (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: 5–30 минут, - originals: 1–10 минут (настраиваемо). --- ## 9. API (контракт) ### 9.1 Общие правила - Версионирование: `/api/v1` - Авторизация: `Authorization: Bearer ` (или 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: видео постеры - Генерация постера (кадр на 1–3 секунде) через 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` (например 5–20 ГБ). - Для больших файлов — multipart upload (S3 multipart). - Лимит выдачи списка: 50–200 элементов, 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. Примечания по лицензиям и приватности - Пользовательские файлы и метаданные считаются приватными, но админ может всё просматривать. - Логи не должны содержать содержимое файлов и чувствительные данные (пароли/токены). - При шаринге — показывать минимум метаданных.