После Postman demo audit — в cheatsheet не хватало живых примеров,
которые можно скопировать и сразу проверить. Добавил:
* FK-зависимость:
GET /dictionaries/satellite_type/dependents →
[{sourceDict: spacecraft, sourceField: satellite_type_code,
onClose: BLOCK, activeRecordsInSourceDict: 14}]
+ список других живых FK на staging (ground_station, country etc).
* Локализация записи через Accept-Language:
ru: НАСА / Глобальный мониторинг суши и атмосферы
en: NASA / Global land and atmosphere monitoring
+ warning что ?locale= игнорируется и schema endpoint вообще
не локализуется.
* Time-travel: 14.05.2026 12:00 → 200, 14.05.2026 00:00 → 404
record_not_active (ожидаемое поведение, не bug).
* History и changelog/diff живые примеры с test1.
Также убрал password-grant из Auth (используется client_credentials —
это правильный flow для server-to-server интеграторов). И добавил
dependents проверку в 60-second smoke.
Tested: GET /dictionaries/spacecraft с Accept-Language: en-US возвращает
schemaJson с descriptions на ru (defaultLocale). Header игнорируется на
этом endpoint.
Локализация (Accept-Language header) работает только на records-side
(read-api DictionaryReadController). DictionaryDefinitionController
(writer/ordinis-app) возвращает schema без locale negotiation.
Cheatsheet обновлён, добавлен warning в query-params секцию.
После testing на staging выявлено: backend парсит локаль ТОЛЬКО из
Accept-Language header. ?locale=en-US query param сейчас игнорируется
(сервер возвращает default ru-RU вне зависимости от значения).
Реальный demo (spacecraft/1999-068A):
default (без header): operator: "НАСА"
Accept-Language: en-US: operator: "NASA"
?locale=en-US (без header): operator: "НАСА" ← не работает
Cheatsheet + quickstart обновлены. Postman collection тоже (используется
header). ?locale= query — отдельный backend feature request в roadmap.
User report: token has roles в resource_access.ordinis.roles (client-scoped),
backend WorkflowRoles читал hardcoded realm_access.roles → forbidden_role
для всех reviewer actions.
Backend:
- WorkflowRoles: depends OrdinisAuthProperties → reads via rolesClaim path
(же что ScopeContext). Default realm_access.roles, на staging/prod env
var ORDINIS_AUTH_ROLES_CLAIM=resource_access.ordinis.roles (уже wired
в helm helper).
- Nested claim path resolver — mirror ScopeContext.readClaimByPath
- New super-role ADMIN ('ordinis:admin') — holder автоматически проходит
любой workflow check без явного назначения отдельных ролей. Convenience
для small teams.
Frontend (usePermissions.ts):
- tokenRoles() читает оба claim path (realm_access.roles +
resource_access.ordinis.roles) и объединяет — UI gate работает
независимо от backend config
- hasRole() recognizes 'ordinis:admin' как super-role override
- ROLE_ADMIN exported
Docs:
- docs/operator/rbac.md: добавлен ordinis:admin row, JWT claim path note,
Admin profile updated (super-role vs composite explanation)
Status table в pending-endpoints.md обновлён: endpoints #1, #2, #4, #5
landed в MR !95-!98 в session 2026-05-11. Workflow API (#3) deferred —
требует отдельной design discussion + sprint (state machine + RBAC pool
model parallel to record-level DraftController architecture).
5 endpoints нужны admin-UI для завершения UI-фич:
1. GET /dictionaries/{name}/changelog — History tab + TimeTravel
2. GET /dictionaries/{name}/snapshots — TimeTravel slider marks
3. POST drafts/.../{review,decision,publish} — workflow state machine
4. PATCH /dictionaries/{name}/schema — Fields tab inline edit
5. GET /dictionaries/graph/outgoing — catalog → FK count column
Каждый endpoint содержит: purpose, route, query/body, response example,
errors, frontend integration estimate. Total frontend work after backend
ship — ~14h (1.5 sprint day).
nginx с `alias` имеет известный bug: `try_files $uri` резолвит $uri от
document root, не от alias path. Для hashed assets (первое regex location)
try_files уже был убран ранее, но оставался в fallback unhashed location —
из-за этого `/docs/_assets/tabs-extension.js` (и .css) валились в 404
несмотря на наличие файлов в /usr/share/nginx/html/_assets/.
Теперь fallback просто alias'ит без try_files. Если файла нет — nginx сам
отдаст 404 от open() failure (нормальное поведение).
Diplodoc CLI 4.60 транформирует `{% list tabs %}` встроенным плагином,
но runtime assets (`_assets/tabs-extension.{js,css}`) НЕ копирует
автоматически — в отличие от mermaid/latex/page-constructor, у которых
bundle paths захардкожены в CLI.
Решение:
- добавил devDep `@diplodoc/tabs-extension@^3` (содержит runtime/)
- post-build `copy-tabs-runtime` script: копирует
`build/runtime/index.{js,css}` → `_dist/_assets/tabs-extension.{js,css}`
- index.md: 4 секции "С чего начать" обёрнуты в `{% list tabs %}` —
выглядит компактнее и интерактивнее чем плоский список заголовков
Verified локально: `yfm-tabs` HTML рендерится, assets в `_dist/_assets/`.
nginx fallback rule для `/docs/_assets/*` уже работает.
Diplodoc CLI 4.60 не генерит sourcemaps, но оставляет
`//# sourceMappingURL=app.css.map` комментарии в JS/CSS bundles.
DevTools тянет .map файлы → 404 в console (6 ошибок на /docs/).
Nginx теперь возвращает 204 No Content для *.map под /docs/ —
DevTools тихо проглатывает, console зелёная.
`{% list tabs %}` требует @diplodoc/tabs-extension package + plugin config,
не входит в base @diplodoc/cli build output. Без него HTML refs
/_assets/tabs-extension.{css,js} → 404 в browser console.
Convert на plain ### headers — same content, no missing assets, проще
для переводов и navigation.
Diplodoc CLI 4.60 bug: HTML refs hashed asset names (например
`_bundle/app-34c68624e23be7ce.css`) но actual built _bundle/ contains
unhashed files (`app.css`). Browser получает 404 на все css/js
references → page broken.
Workaround в nginx — strip hash через regex location:
^/docs/(_bundle|_assets)/(.+?)-<hex8+>(\.<ext>)$
→ alias /usr/share/nginx/html/$1/$2$3
Также убран try_files который incorrectly matched $uri (URL path с hash)
вместо aliased file path → false 404.
Verified locally — 200 OK на:
/docs/_bundle/app-34c68624e23be7ce.css
/docs/_bundle/vendor-*.css
/docs/_bundle/react-*.js
/docs/integration/api/auth.html
Corepack может hang в CI runner на integrity-check либо non-TTY prompt
при первом activate. Заменяем на прямой npm install с pinned version
9.15.4 (latest stable v9, lockfile compat: lockfileVersion '9.0').
Изменения:
- ordinis-admin-ui/Dockerfile: corepack → npm install -g pnpm@9.15.4
- docs/Dockerfile: same swap
- .gitlab-ci.yml build-docs: same swap
Verified locally: docker build ordinis-admin-ui за 23 sec, no hang.
OpenApiConfig:
- Добавлена OAuth2 security scheme (authorization_code flow с Keycloak
realm `nstart`). Auth/token URLs из ordinis.openapi.oauth.issuer-uri
configuration property (default: auth.nstart.space/realms/nstart).
- BearerAuth scheme сохранён secondary — для pre-issued tokens из CI/scripts.
- Servers list переупорядочен — prod первый, staging второй.
- Description обновлён с pointer на /docs/ для full integration guide.
application.yml:
- springdoc.swagger-ui.oauth: client-id (env override), use-pkce-with-
authorization-code-grant=true. Public client `ordinis-swagger`
настраивается отдельно в Keycloak — see keycloak-setup-swagger-client.md
в ordinis-infra repo.
- ORDINIS_OPENAPI_OAUTH_ISSUER_URI env var для override realm на разных
окружениях.
docs/integration/api/read-endpoints.md:
- Swagger UI section обновлён: акцент на Authorize button + Keycloak
PKCE login (no client_secret needed), tip про bearerAuth fallback.
docs/index.md:
- Добавлен tab "Я хочу пощупать API" с link на Swagger UI.
После deploy:
- https://ordinis.k8s.264.nstart.cloud/swagger-ui.html — Swagger UI
- https://ordinis.k8s.264.nstart.cloud/v3/api-docs — OpenAPI spec
Authorize → выбрать oauth2 → Keycloak login (PKCE) → Try it out с JWT.
Evening update к v1.0:
- SPOF на staging registry CLOSED (postgis-barman mirrored в repo.nstart.cloud:18.3-3.6.2-2)
- Monitoring artifacts ready (k6 prod variant, synthetic smoke + CronJob, 2 alerts)
- KafkaTopics READY=False janitorial fix (script ready)
- CI build job на 401 known-issue (manual fallback)
Section 0.1 добавлена с подробным postmortem image arch incident:
1. arm64 image из mac M-series попал в registry без --platform
2. kubelet cache на node удерживал stale arm64 layer под тем же тегом
3. Pod stuck Terminating 14 мин (CNPG gracePeriod=1800s)
Lessons закреплены в docker/postgis-barman/README.md (anti-footgun).
Triage card для on-call: где взять unseal-keys (1Password placeholder,
уточнить с DevOps lead), как запустить script либо ручную процедуру,
что проверить после, troubleshooting типовых проблем.
Также: §7 — план миграции на auto-unseal (Transit / Yandex KMS) когда
определимся с master Vault или KMS-backend.
Альтум-команда обнаружила что /api/v1/dictionaries/{name}/records даёт 500
и spacecraft_codes ссылаются на несуществующий "RESURS-P-3". Оба бага мои:
1. Read-api живёт на /api/v1/{name}/records (БЕЗ /dictionaries/ префикса).
/dictionaries/* — это admin-write контроллер, у него GET-list нет.
Fix: docs/integration/altum-imaging-order.md + docs/tech/api-integration.md
обновлены с правильными URL'ами + примером ответа из live staging.
2. spacecraft seed имел только 4 КА (Terra, Sentinel-1A/2A, Кондор-ФКА),
а mission.spacecraft_codes ссылался на пустые/RESURS-P-3 — каскад в
"Заказ съёмки" не работал бы.
Fix: добавлено 10 российских КА (Ресурс-П 1-4, Канопус-В 1+5, Метеор-М 2+2-2,
Электро-Л 2, Арктика-М 1) с COSPAR designators как businessKey.
3. mission.records.json + instrument.records.json: spacecraft_codes теперь
ссылается на реально существующие COSPAR-ключи. Каскад работает:
instrument MSU-MR → spacecraft [2014-037A, 2019-038A] → Метеор-М № 2 / 2-2.
После redeploy и идемпотентного пересева:
spacecraft 14 (было 4, +10 РФ)
mission 9 (existing 9, references фиксированы)
instrument 11 (было 10, +МСУ-ГСМ Арктики)