Files
vidconf/docs/api/users.md

10 KiB
Raw Blame History

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

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

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

# 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):

{
  "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: (все поля опциональны)

{
  "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 и т.д.)

Примеры:

# 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:

{
  "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):

[
  {
    "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
  }
]

Примеры:

# Полный список
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

{
  "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

{
  "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)

Ссылки