API v1 и OpenAPI

> Кратко. API v1 — адреса, которыми пользуется само веб-приложение. Вход — токен Bearer из POST /api/v1/auth/login, описание всех адресов — /openapi.json и страница /docs. Адреса v1 меняются вместе с вебом; для внешних систем есть стабильный API v2.

Читаю как

Описание адресов

Адрес Что
/openapi.json OpenAPI 3.1 всего API: на копии Алматы 510 адресов, 573 операции
/docs Swagger UI — можно выполнить запрос прямо со страницы («Authorize» — вставить токен)
/redoc то же в виде справочника
/api/v2/openapi.json только адреса v2 — по нему генерируют клиент

Адреса сгруппированы тегами по разделам (auth, ogc-features, arcgis, api-v2, telemetry …). /mcp в описании нет: это не REST, а JSON-RPC (см. MCP для разработчиков клиентов). Заголовок описания пока стандартный («FastAPI», версия 0.1.0).

Вход

API=https://<сервер>/tgid/almaty/map-api      # или http://127.0.0.1:<порт> на стенде
TOKEN=$(curl -s -X POST "$API/api/v1/auth/login" -H "Content-Type: application/json" \
  -d '{"username":"ivanov.test","password":"<пароль>"}' | jq -r .access_token)
curl -s "$API/api/v1/auth/me" -H "Authorization: Bearer $TOKEN"

Ответ входа: {"access_token": "…", "token_type": "bearer", "role": "viewer", "username": "ivanov.test"}. Токен — JWT, живёт JWT_EXPIRE_MINUTES минут (по умолчанию 480 — 8 часов), права — пользователя. GET /api/v1/auth/me — роль, права и город. Заблокированный пользователь теряет доступ сразу: API сверяет состояние учётной записи на каждом запросе (с коротким кешем).

GET /api/v1/auth/config — без входа: как настроен сервер. На стенде документации (копия Алматы):

{"auth_disabled":true,"auth_required_get":false,"dev_login_enabled":true,"strict_auth":false,
 "mutations_enabled":true,"topology_mutations_enabled":true,"city":"almaty","city_label":"Алматы","ai_enabled":true}

При AUTH_DISABLED=true (только стенды) каждый запрос выполняется от имени пользователя dev со всеми правами, а вход выдаёт токен на любой логин. При DEV_LOGIN_ENABLED=true вход тоже не проверяет пароль. На сервере с доступом извне оба флага выключены, а STRICT_AUTH=true не даёт API запуститься с ними.

Кроме токена входа есть ключи с назначением: ключ API v2 (scope: api), ключ ArcGIS (arcgis), токен ИИ-клиента (mcp). Каждый действует только на своих адресах; на адресах v1 ответ — 401 «Токен с ограниченным назначением … действует только на своих адресах».

Город в адресе

У API нескольких городов город задаётся префиксом пути: /city/astana/api/v1/.... За nginx адрес города — https://<сервер>/tgid/<город>/map-api/…, префикс nginx подставляет сам. Без префикса отвечают только /health, /metrics и /auth/check; неизвестный город — 404. API одного города (DB_NAME без TGID_CITY_DBS) префикс не принимает. Ссылки в ответах OGC и v2 строятся от адреса запроса; за nginx путь публикации задаёт API_PUBLIC_PREFIX (например /tgid/{city}/map-api).

Постраничная выдача

Единого правила в v1 нет — у каждого семейства адресов своё:

Где Параметры Ответ
журналы (/api/defects, /api/repairs …) и реестры оборудования page, page_size (до 200; журнал действий — до 500), search {"items", "total", "page", "page_size", "pages"}
наряды /api/v1/work-orders фильтры {"items", "counts", "overdue"}
фрагменты /fragments — {"data": [{"id", "name"}]}
OGC /api/ogc/collections/{id}/items limit (до 10 000), offset, ссылка next GeoJSON
FeatureServer …/query resultOffset, resultRecordCount (до 2000), exceededTransferLimit Esri JSON
API v2 after_id, limit (до 500) {"data", "next_after_id"}

Слишком большой limit — 422 с указанием границы.

Ошибки

{"detail": "текст"}, для правок сети — {"detail": {"code": "…", "message": "…"}}, для неверных полей — список ошибок pydantic. Язык текста — по Accept-Language. Подробно — в Устройство кода.

curl -s "$API/api/v2/defects/999999999" -H "Accept-Language: en"
# 404 {"detail":"Defect 999999999 not found"}

Долгие операции

Расчёт, выгрузка большого Excel, запекание тайлов идут в очереди Celery. Ответ запуска — {"task_id": "…"}, ход — GET /api/v1/task/{task_id}: status (PENDING, PROGRESS, SUCCESS, FAILURE), в ходе — meta с сообщением, в конце — result или error. У API нескольких городов задача чужого города — 404.

Для разработчика

  • Новый маршрут — в свой файл routers/, запрос к базе — в database/; зависимость прав — require_permission(...) или require_roles(...), запись — require_mutations_enabled. Если маршрут отдаёт данные сети, у него должна быть своя проверка входа: шлюз AUTH_REQUIRED_GET на стенде обычно выключен.
  • Веб: новый метод клиента — в свой раздел services/api/, ошибки — ApiError из services/api/http.ts (status, detail, userMessage).
  • Тесты маршрутов — tests/ в API: pytest и FastAPI TestClient с подменой зависимостей (образец — tests/test_mcp.py, tests/test_webhooks.py). Подробно — Тесты и стенды.
Хотите увидеть эту функцию на своих данных?Покажем ТГИД на демонстрации и ответим на вопросы инженеров.
Запросить демо