# 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: ```bash cd backend python manage.py migrate daphne -b 0.0.0.0 -p 8000 eusrr_backend.asgi:application ``` Frontend: ```bash cd frontend cp .env.example .env.local npm install npm run dev ``` Для стандартной локальной конфигурации: ```dotenv 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: ```bash 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`. Защищённый запрос: ```bash curl http://localhost:8000/api/v1/employees/me/ \ -H 'Authorization: Bearer ACCESS_TOKEN' ``` Обновление access token: ```bash 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: ```ts 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 доступа к чату: ```text 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: ```text 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. ## Проверка ```bash cd backend pytest tests/api/test_documentation.py tests/api/auth tests/api/v1 ``` Точечные suites предпочтительнее ручной коллекции curl-команд. Для диагностики контракта: ```bash 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.