Corp/docs/guides/API.md

202 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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` вручную.