# Профиль и аватар пользователя Эндпоинты управления профилем, загрузкой аватаров и поиска пользователей. ## Быстрый старт ```bash # 1. Получить профиль curl http://localhost:8000/api/v1/users/me \ -H "Authorization: Bearer $TOKEN" # 2. Обновить ФИО и команду curl -X PATCH http://localhost:8000/api/v1/users/me \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"name_user":"Новое имя","team_id":"uuid-team"}' # 3. Загрузить аватар curl -X POST http://localhost:8000/api/v1/users/me/avatar \ -H "Authorization: Bearer $TOKEN" \ -F "file=@/path/to/photo.jpg" # 4. Удалить аватар curl -X DELETE http://localhost:8000/api/v1/users/me/avatar \ -H "Authorization: Bearer $TOKEN" # 5. Сменить пароль curl -X POST http://localhost:8000/api/v1/users/me/password \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"current_password":"oldPassword123","new_password":"newSecurePassword456"}' # 6. Поиск пользователей (для пикера участников) curl 'http://localhost:8000/api/v1/users?q=john' \ -H "Authorization: Bearer $TOKEN" # 7. Получить справочник команд curl http://localhost:8000/api/v1/teams \ -H "Authorization: Bearer $TOKEN" ``` ## Эндпоинты ### GET /api/v1/users/me **Получить профиль текущего аутентифицированного пользователя.** **Требует auth:** JWT Bearer token **Response (200 OK):** ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "name_user": "Иван Петров", "team_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "team_name": "Backend", "avatar_path": "avatars/550e8400-e29b-41d4-a716-446655440000.jpg", "avatar_url": "/media/avatars/550e8400-e29b-41d4-a716-446655440000.jpg", "created_at": "2026-07-16T12:00:00Z" } ``` **Поля:** - `email` — адрес электронной почты (read-only) - `name_user` — отображаемое имя (редактируемое) - `team_id` — UUID команды (опционально, редактируемое) - `team_name` — имя команды (null если не привязан) - `avatar_path` — путь к файлу аватара (null если нет) - `avatar_url` — готовый URL для отображения; при отсутствии аватара фронтенд показывает заглушку с инициалами --- ### PATCH /api/v1/users/me **Изменить ФИО и/или команду текущего пользователя; email — read-only.** **Требует auth:** JWT Bearer token **Request body:** (все поля опциональны) ```json { "name_user": "Новое имя", "team_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8" } ``` **Поля:** - `name_user` (str, 1..255) — новое ФИО - `team_id` (UUID или null) — переназначить команду; явное `null` снимает привязку; отсутствие поля команду не трогает **Response (200 OK):** обновленный профиль (как GET /me) **Коды ошибок:** - `404` — team_not_found (если передан несуществующий `team_id`) - `422` — Unprocessable Entity (валидация: name_user пуст и т.д.) --- ### POST /api/v1/users/me/avatar **Загрузить аватар текущего пользователя.** **Требует auth:** JWT Bearer token **Request body:** `multipart/form-data` - `file` — файл изображения (обязателен) **Требования:** - **Формат:** JPEG, PNG или WebP (проверяется по magic bytes, не по расширению) - **Максимальный размер:** 2 МБ - Старый аватар автоматически удаляется при загрузке нового **Response (200 OK):** обновленный профиль (как GET /me, с новым `avatar_url`) **Коды ошибок:** - `413` — avatar_too_large (файл больше 2 МБ) - `415` — avatar_invalid_type (не JPEG/PNG/WebP; проверяется по magic bytes) - `422` — Unprocessable Entity (невалидный multipart и т.д.) **Примеры:** ```bash # JPEG curl -X POST http://localhost:8000/api/v1/users/me/avatar \ -H "Authorization: Bearer $TOKEN" \ -F "file=@photo.jpg" # PNG curl -X POST http://localhost:8000/api/v1/users/me/avatar \ -H "Authorization: Bearer $TOKEN" \ -F "file=@photo.png" # WebP curl -X POST http://localhost:8000/api/v1/users/me/avatar \ -H "Authorization: Bearer $TOKEN" \ -F "file=@photo.webp" ``` --- ### DELETE /api/v1/users/me/avatar **Удалить аватар текущего пользователя.** **Требует auth:** JWT Bearer token **Response (204 No Content)** — без тела **Примечание:** После удаления `avatar_path` становится `null`, фронтенд отображает заглушку с инициалами. --- ### POST /api/v1/users/me/password **Сменить пароль текущего пользователя.** **Требует auth:** JWT Bearer token **Request body:** ```json { "current_password": "oldPassword123", "new_password": "newSecurePassword456" } ``` **Поля:** - `current_password` (str) — текущий пароль (обязательно, для верификации) - `new_password` (str, ≥8) — новый пароль (политика как при регистрации: минимум 8 символов) **Response (204 No Content)** — без тела **Коды ошибок:** - `400` — invalid_current_password (текущий пароль не совпадает) - `422` — Unprocessable Entity (новый пароль короче 8 символов) **Примечание:** Refresh-сессии сознательно **не отзываются** (см. ADR-005 `docs/architecture/adr/005-password-reset-deferred.md`) — пользователь останется залогинен на других устройствах. Массовый отзыв всех сессий появится вместе со сбросом пароля по email (v0.1.0). --- ### GET /api/v1/users **Поиск пользователей для пикера участников конференции.** **Требует auth:** JWT Bearer token **Query параметры:** - `q` (str, опционально) — текст для поиска (по имени и email) **Поведение:** - Без `q` — возвращает полный список (с ограничением ~20 результатов) - С `q` — полнотекстовый поиск (также ~20 результатов) - Поиск регистронезависим **Response (200 OK):** ```json [ { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "ivan@example.com", "name_user": "Иван Петров", "avatar_url": "/media/avatars/550e8400-e29b-41d4-a716-446655440000.jpg" }, { "id": "6ba7b811-9dad-11d1-80b4-00c04fd430c8", "email": "maria@example.com", "name_user": "Мария Сидорова", "avatar_url": null } ] ``` **Примеры:** ```bash # Полный список curl http://localhost:8000/api/v1/users \ -H "Authorization: Bearer $TOKEN" # Поиск по имени curl 'http://localhost:8000/api/v1/users?q=ivan' \ -H "Authorization: Bearer $TOKEN" # Поиск по email curl 'http://localhost:8000/api/v1/users?q=example.com' \ -H "Authorization: Bearer $TOKEN" ``` --- ## Аватары ### Хранение Аватары хранятся в каталоге `MEDIA_ROOT/avatars/`: ``` MEDIA_ROOT/ └── avatars/ ├── 550e8400-e29b-41d4-a716-446655440000.jpg ├── 6ba7b811-9dad-11d1-80b4-00c04fd430c8.png └── ... ``` **Адресация:** - Backend сохраняет: `user.avatar_path = "avatars/{user_id}.{ext}"` - Frontend отображает: `avatar_url = "/media/avatars/{user_id}.{ext}"` - Nginx раздаёт напрямую (location `/media/` → alias `MEDIA_ROOT`) ### Валидация **Magic bytes (не расширение):** - JPEG: `FF D8 FF` - PNG: `89 50 4E 47` - WebP: `52 49 46 46 ... 57 45 42 50` **Размер:** max 2 МБ (413 Payload Too Large) ### Заглушка (дефолт) Если `avatar_path` = `null`: - Фронтенд отображает цветную заглушку с инициалами (2 буквы ФИО) - Цвет выбирается детерминированно по хэшу `user_id` (всегда одинаковый для пользователя) --- ## Типы данных ### UserProfileOut ```json { "id": "uuid", "email": "string", "name_user": "string", "team_id": "uuid or null", "team_name": "string or null", "avatar_path": "string or null", "avatar_url": "string or null", "created_at": "datetime" } ``` ### UserListItemOut ```json { "id": "uuid", "email": "string", "name_user": "string", "avatar_url": "string or null" } ``` --- ## Примечания - **Email read-only:** адрес электронной почты менять нельзя (требует переверификации, которая не реализована в v0.0.1) - **Таймзона:** все `created_at` сохраняются в UTC (ISO 8601) - **Команда опциональна:** пользователь может работать без привязки к команде (`team_id` = null) - **Безопасность:** пароли никогда не передаются в API; для смены пароля используйте `POST /api/v1/users/me/password`. Сброс пароля по email появится в v0.1.0 (см. ADR-005) --- ## Ссылки - [Справочник команд](./teams.md) — GET /api/v1/teams - [Администраторский API](./admin.md) — GET/PATCH /api/v1/admin/users/{id}, POST /api/v1/admin/users/{id}/avatar - [Аутентификация](./auth.md) — регистрация, логин - [Конференции](./conferences.md) — параметр `participants` при создании/редактировании