Corp/docs/guides/API.md

14 KiB
Raw Blame History

HTTP API

Этот документ описывает текущий HTTP API EUSRR. Полный список операций и схем ответов генерируется из кода; вручную поддерживаемый список endpoint-ов здесь намеренно не дублируется.

Источники истины

  • URL верхнего уровня: backend/api/urls.py
  • API v1: backend/api/v1/urls.py
  • authentication: backend/api/auth/
  • OpenAPI tags и metadata: backend/api/schema.py
  • frontend transport: frontend/src/lib/api/client.ts
  • доменные frontend clients: frontend/src/lib/api/
  • проверка документации: backend/tests/api/test_documentation.py

Если этот guide расходится с /api/schema/, верной считается OpenAPI-схема.

Локальный запуск

Backend запускается как ASGI-приложение. Один процесс обслуживает HTTP и WebSocket:

cd backend
python manage.py migrate
daphne -b 0.0.0.0 -p 8000 eusrr_backend.asgi:application

Frontend:

cd frontend
cp .env.example .env.local
npm install
npm run dev

Для стандартной локальной конфигурации:

NEXT_PUBLIC_BACKEND_URL=http://localhost:8000
NEXT_PUBLIC_WS_URL=ws://localhost:8000/ws/

Документация, доступная из приложения

URL Назначение
/api/schema/ OpenAPI 3 schema
/api/docs/ Swagger UI
/api/redoc/ ReDoc
/api/ws/schema/ AsyncAPI schema для /ws/
/api/ws/docs/ HTML-страница WebSocket API

Эти страницы доступны без аутентификации. Бизнес-endpoint-ы по умолчанию требуют авторизованного пользователя и Django model permissions.

Пространства URL

Prefix Содержимое
/api/auth/ JWT, QR login, регистрация, password reset, auth-сессии
/api/v1/ Основной versioned REST API
/api/v1/auth/ Runtime alias authentication API для старых клиентов; в OpenAPI не дублируется
/api/v1/communications/ Чаты, сообщения и опросы
/api/v1/notifications/ Уведомления, preferences и Web Push
/api/v1/schedule/ Календари, события, правила, occurrences и relations
/api/v1/procurement/ Закупки и оборудование
/api/v1/finance/ Финансы и payroll
/api/v1/tasks/ Задачи, доски и automation

Версии /api/v2/ в текущем проекте нет. Не добавляйте ссылки на неё и не используйте старые VITE_API_URL/Vite-примеры: frontend построен на Next.js.

Аутентификация

Основной способ — JWT:

curl -X POST http://localhost:8000/api/auth/token/ \
  -H 'Content-Type: application/json' \
  -d '{"email":"user@example.com","password":"change-me"}'

Вместо email можно передать phone или phone_number. Успешный ответ содержит access и refresh.

Защищённый запрос:

curl http://localhost:8000/api/v1/employees/me/ \
  -H 'Authorization: Bearer ACCESS_TOKEN'

Обновление access token:

curl -X POST http://localhost:8000/api/auth/token/refresh/ \
  -H 'Content-Type: application/json' \
  -d '{"refresh":"REFRESH_TOKEN"}'

Каждая пара токенов привязана к записи UserAuthSession через claim session_id. Удалённая или отозванная сессия инвалидирует и HTTP, и WebSocket-доступ. Управление сессиями:

  • GET /api/auth/sessions/
  • DELETE /api/auth/sessions/{session_id}/
  • POST /api/auth/sessions/logout-others/

Frontend хранит access/refresh token в localStorage, добавляет Bearer header, выполняет единичный refresh при 401 и повторяет исходный запрос. Ошибка refresh очищает локальную сессию.

Новые пароли проходят стандартные Django validators: минимум 8 символов, запрет полностью цифровых и общеупотребительных паролей, проверка сходства с данными пользователя там, где пользователь известен. LDAP/AD может применять более строгую доменную политику дополнительно.

DRF ограничивает анонимные запросы до 60/min, авторизованные — до 600/min. Для login действует отдельный предел 10/min, для registration и email/reset операций — 5/hour, refresh — 60/min. Значения задаются в REST_FRAMEWORK.DEFAULT_THROTTLE_RATES.

Формат запросов и ответов

  • JSON используется по умолчанию.
  • Для загрузки файлов передавайте FormData; API client не устанавливает Content-Type вручную, чтобы браузер добавил multipart boundary.
  • Стандартная пагинация: page и page_size, значение по умолчанию — 20.
  • Максимальный page_size базового paginator — 200. Клиенты, которым нужен полный справочник, обходят страницы по полю next, а не запрашивают неограниченную выдачу.
  • Ручная выдача notifications ограничена 100 элементами; message history и messages-around — суммарно 100 сообщениями за запрос.
  • 204 No Content не содержит JSON-тело.
  • 401 означает отсутствующую, истёкшую или отозванную auth-сессию.
  • 403 означает, что пользователь распознан, но не имеет требуемого model/object permission.
  • 400 обычно содержит field-level validation errors; не преобразуйте его в общий 500 на frontend.

Глобальный поиск не расширяет права пользователя: результаты чатов, сообщений, документов, календарных событий, заявлений, закупок и уведомлений повторно проверяются по object-level правилам соответствующего домена.

Frontend proxy

Browser вызывает относительные пути /api/.... Next.js route frontend/src/app/api/[...path]/route.ts проксирует их на ${NEXT_PUBLIC_BACKEND_URL}/api/... и сохраняет query string, authorization headers, multipart body и streaming GET responses.

Используйте собранный client:

import { apiClient } from "@/lib/api";

const currentUser = await apiClient.getCurrentUser();

Новый доменный API добавляется отдельным модулем в frontend/src/lib/api/ и подключается в frontend/src/lib/api/index.ts. Не создавайте второй fetch-wrapper и не встраивайте абсолютный backend URL в компоненты.

Communications API

Router публикует:

  • communications/chats
  • communications/messages
  • communications/polls

Кроме CRUD доступны actions для участников, ролей, поиска, истории, отметки прочитанного, загрузок, реакций, пересылки, bulk delete и голосований. Их точные URL и payload смотрите в Swagger: они формируются из backend/communications/api/viewsets.py.

Упоминания в сообщениях

Доступные для @ участники запрашиваются с учётом object-level доступа к чату:

GET /api/v1/communications/chats/{chat_id}/mention-candidates/?q=Ива

Ответ содержит results с полями id, name, email, avatar. Автор запроса исключён; в личном чате возвращается только собеседник, в групповом — активные участники, в глобальном — активные пользователи. Параметр q ищет каждый введённый фрагмент по фамилии, имени, отчеству, email и LDAP-логину, поэтому составные ФИО можно вводить в любом порядке.

При создании multipart-сообщения или PATCH передавайте mentioned_user_ids — JSON-массив выбранных ID. Сервер повторно проверяет доступ участников и наличие видимого @Полное имя в content; подставить посторонний ID нельзя. В ответе сообщения поле mentions содержит проверенные {user_id, name, start, end} для безопасного выделения текста. Максимум — 50 выбранных пользователей на сообщение.

Список и detail чата возвращают has_unread_mention. Значение остаётся true после перезагрузки и сбрасывается вместе с read marker; frontend показывает его отдельным значком @ на аватаре чата. Упоминание создаёт chat_mention даже при выключенных уведомлениях конкретного чата, не создавая второе обычное уведомление.

Для отметки прочитанного используйте HTTP action:

POST /api/v1/communications/chats/{chat_id}/mark-read/

WebSocket action mark_read оставлен только как deprecated compatibility path и не выполняет запись.

Добавление endpoint-а

  1. Добавьте serializer, permissions и view/viewset в доменный модуль.
  2. Зарегистрируйте URL под /api/v1/; unversioned бизнес-URL не добавляются.
  3. Добавьте extend_schema там, где drf-spectacular не может вывести контракт автоматически.
  4. Проверьте, что object permissions фильтруют и list, и detail queryset.
  5. Добавьте backend tests для успешного запроса, 401, 403, validation и ownership boundaries.
  6. Добавьте или расширьте доменный frontend client.
  7. Убедитесь, что endpoint появился в /api/schema/ с правильным tag.

Проверка

cd backend
pytest tests/api/test_documentation.py tests/api/auth tests/api/v1

Точечные suites предпочтительнее ручной коллекции curl-команд. Для диагностики контракта:

curl -fsS http://localhost:8000/api/schema/ -H 'Accept: application/json'

При изменении WebSocket-протокола дополнительно обновите backend/docs/architecture/websocket-asyncapi.yaml и выполните realtime tests.

Типовые ошибки

  • 404 на /api/v2/...: такой версии нет; используйте /api/v1/....
  • 401 после успешного login: проверьте, что отправляется access token, а auth-сессия не отозвана.
  • Повторяющиеся refresh-запросы: используйте общий apiClient, в нём refresh уже сериализован.
  • CORS в локальной разработке: browser должен обращаться к Next.js /api/...; прямой backend-доступ разрешён только origin-ам из CORS_ALLOWED_ORIGINS.
  • Ошибка загрузки файла: не задавайте multipart Content-Type вручную.

Task board templates

Authenticated employees can save the structure of an accessible, non-archived task board as a personal template. Staff and superusers can also publish templates into the company-wide library. Templates contain active columns, nested columns and their rows (including names, colors and positions); they do not contain tasks, members, attachments, comments, automations or board permissions.

  • GET /api/v1/tasks/board-templates/ — list the current user's personal templates and all company-wide templates.
  • POST /api/v1/tasks/board-templates/ — create a snapshot from an accessible board. Example: {"name":"Sprint","source_board_id":42,"is_shared":false}. Setting is_shared: true requires staff or superuser privileges.
  • PATCH /api/v1/tasks/board-templates/{id}/ — rename or change the description of an owned template; only administrators can mark it as shared.
  • DELETE /api/v1/tasks/board-templates/{id}/ — delete an owned template.
  • POST /api/v1/tasks/board-templates/{id}/instantiate/ — create a new board from any visible template. Optional JSON: {"name":"Sprint 24"}. The new board always belongs to the caller and starts with access_scope: "private", no tasks, and no members. The caller may subsequently edit its access using the normal board update endpoint.

Templates are independent snapshots: changes to a source board do not retroactively modify stored templates or boards created from them. Changing the template's metadata does not change its saved structure.