Перейти к основному содержимому

Выдача

Замер от: 2026-08-14 · чем проверено: ZAMER-KARTY-BLOKOV-2026-08-14.md, ZAMER-BAZY-2026-08-14.md, прогон data_api/tests Для кого: кто добавляет маршрут к данным или чинит 401/403/429 — прежде чем трогать что-либо из блока.

Что делает

Блок отдаёт собранные и посчитанные данные наружу. Штатная HTTP-дверь целевого /new — Data API (data_api/): сайт читает витрины через него, а не из базы сбора; на 14.08 это 190 маршрутов под /api/v1. Блок начинается в data_api/main.py и кончается ответом с типизированной ошибкой (envelope.error).

⚠️ Data API — не единственная дверь. Обходов три, и они разного веса:

ОбходРазмерЧем является
routes/api.php41 маршрутотдельный слой Laravel (legacy REST)
data_api/routers/compat.pyодин модульслой совместимости для прежних договоров рейтинга, оборачиваемости и отгрузок
DB::connection('kernel')одно место: app/Filament/Pages/ShipmentCalcApiPage.php:444запись отгрузки

Третий обход — запись отгрузки, то есть он в неприкосновенной области (см. «Чего делать нельзя»). Прежняя формулировка «прямые соединения Laravel» преувеличивала: соединение одно.

Точка входа в код

data_api/main.py — сборка приложения: app = FastAPI(...), мидлварь гостевого предела и подключение роутеров status, facts, dims, control, reports, compat под префиксом /api/v1. Запуск: uvicorn main:app --port 8080 из data_api/.

Чем задаётся

  • data_api/config.py — окружение: DATA_API_TOKENS (токены → области доступа), DATA_API_RATE_LIMIT_PER_MIN (предел гостевых ключей), V3_MYSQL_* (адрес базы). Токенов нет — проверка прав выключена (AUTH_DISABLED), и это состояние для разработки, в бою оно недопустимо.
  • data_api/routers/*.py — сами маршруты; каждый маршрут данных под /api/v1 требует свою область доступа. Технический /health живёт отдельно в main.py.
  • data_api/errors.py — таксономия ошибок источника: SourceUnavailable → 503, MandatorySourceError/QuerySourceError → честный 500 с кодом. Тихая пустота вместо ошибки запрещена решением DNR-007.
  • specs/reports/*.yaml42 описания отчётов и 49 их вариантов; из них собираются маршруты /api/v1/reports/*.

Какие правила обязан соблюдать

  • Области доступа объявлены в data_api/auth.py: data:read (факты и витрины), status:read (свежесть, прогоны, сверки), control:read (реестр эндпоинтов и карты полей), control:write, run:write. Последние две входят в словарь токенов, но ни один маршрут их сейчас не требует: control.py целиком read-only. Ответы 501 существуют отдельно у ещё не реализованных чтений и защищены data:read; выдавать их за записывающие мутации нельзя.
  • Гостевой предел. Токен, помеченный полномочием guest, ограничен RATE_LIMIT_GUEST_PER_MIN запросами в минуту (data_api/rate_limit.py, мидлварь в main.py). Предел намеренно не распространяется на служебные ключи: у боевого сайта всплески законны.
  • Порядок чтения (канон «всё через Data API»): сайт не читает базу сбора напрямую. Исключение — одно место записи отгрузки, см. выше.
  • Согласованное чтение. При DATA_API_READ_COHERENCE_MODE=observe|enforce читающие маршруты идут одним REPEATABLE READ снимком (data_api/read_coherence.py).

Чем проверяется

cd data_api && python3 -m pytest tests -q

Отказ в правах проверяют тесты data_api/tests по require_scope (401 без токена, 403 без области) и по гостевому пределу. Сверка ссылок документации — python3 scripts/docs/check_doc_links.py.

Чего делать нельзя

  • Не добавлять обходы. Новый маршрут к данным — только в Data API. routes/api.php и compat.py существуют ради переезда со старого контура, а не как второй вход.
  • Не трогать отгрузку. ShipmentCalcApiPage.php:444 — единственное прямое соединение Laravel с kernel, и оно в неприкосновенной области (RULES.md). Менять его — значит трогать ежедневно используемый расчёт.
  • Не глушить ошибки источника. 500 с кодом ошибки — честнее пустого списка: пустой список читается как «данных нет», хотя данных просто не удалось добыть.
  • Не выключать проверку прав в бою и не помечать боевые ключи guest: предел поставит сайт на 429 в его же законный всплеск.
  • Не менять предел гостевых ключей молча — окно фиксированное минутное, выбор осознанный и описан в шапке rate_limit.py.

Что НЕ установлено

  • Доля из 190 маршрутов «живые чтения / заглушки 501» по-прежнему не установлена. По коду поимённо установлена только конечная часть заглушек: 7 семейств фактов (data_api/routers/facts.py:18), 6 измерений (dims.py:34), 4 compat-представления (compat.py:29) — всего 17 имён. Отвечает им по одному обобщающему маршруту на модуль (501 — только перечисленным именам, иначе 404): facts.py:73, dims.py:187, compat.py:706, плюс reports/__init__.py:42 (501 любому незарегистрированному пути). Наличие 121 маршрутного декоратора в data_api/routers/reports/ доказывает регистрацию в исходниках, но не успешное чтение данных каждым маршрутом и не даёт требуемую долю. Записывающих маршрутов с control:write или run:write сейчас нет. Известно по data_api/README.md, что живы чтения status/*, facts/stocks-daily, control/*, compat/turnover|rating и reports/*.
  • Перечень токенов и их областей — секреты, живут в env боевого сервера.
  • Как именно наружный трафик доходит до Data API (nginx/прокси mp.hyp.ru/api/v1) — на серверы не ходим, не замерено.

Куда за подробностью