Corp/docs/guides/API.md

15 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.

Task checklist templates

The checklist template library is separate from board layout templates. An authenticated employee can create personal templates from one or more checklist item titles. Staff and superusers can publish shared templates visible to every employee. Only the author can update or delete a template.

  • GET /api/v1/tasks/checklist-templates/ — list personal and shared templates (standard paginated DRF response).
  • POST /api/v1/tasks/checklist-templates/ — create a template from {"name":"Quality check","items":["Inspect","Sign off"],"is_shared":false}; alternatively supply source_task_id instead of items to save a snapshot of an accessible task's checklist.
  • PATCH /api/v1/tasks/checklist-templates/{id}/ — edit an owned template.
  • DELETE /api/v1/tasks/checklist-templates/{id}/ — delete an owned template.
  • POST /api/v1/tasks/checklist-templates/{id}/apply/ with {"task_id":42} — append a template's items to an accessible task.

Titles are normalized and limited to 500 characters each. A template has 1–100 items; its snapshot does not include completion state or actors. Applying it adds uncompleted items to the end of the task checklist, transactionally, leaving existing items untouched. Applying it to a draft task from the frontend form adds draft items; persistence happens when the task is saved.