Первоначальная версия VidConf

This commit is contained in:
2026-07-23 01:04:01 +03:00
commit 896455381a
335 changed files with 61527 additions and 0 deletions

308
docs/api/users.md Normal file
View File

@@ -0,0 +1,308 @@
# Профиль и аватар пользователя
Эндпоинты управления профилем, загрузкой аватаров и поиска пользователей.
## Быстрый старт
```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` при создании/редактировании