# Профиль и аватар пользователя

Эндпоинты управления профилем, загрузкой аватаров и поиска пользователей.

## Быстрый старт

```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` при создании/редактировании
