fix(site-finder): вернуть доступ к профилям весов — владелец из сессии (#2782) #2788

Merged
bot-backend merged 1 commit from fix/2782-weight-profiles-unreachable into main 2026-08-07 10:22:56 +00:00

1 commit

Author SHA1 Message Date
efddb1c7c7 fix(site-finder): вернуть доступ к профилям весов — владелец из сессии (#2782)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 23s
CI / changes (pull_request) Successful in 23s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 2m18s
CI / openapi-codegen-check (pull_request) Successful in 3m48s
CRUD именованных профилей POI-весов был недостижим никому без DevTools.
Панель отпирала его значением `admin_token` из localStorage, а записать это
значение было нечем: инпут удалили в #442. Поле «User ID» тоже не показывалось —
оно само стояло за `{!!adminToken && ...}`.

Признак, за которым была заперта фича, ничего не решал. Сервер перестал читать
`X-Admin-Token` в #437, последний `verify_admin_token` удалён в #2775. Проверено
живым запросом на проде: GET weight-profiles с корректным токеном, с мусорным и
без заголовка вовсе дают один и тот же 200. Настоящая защита — два других слоя,
тоже прод-проверенных: Caddy basic_auth (без кред 401 на периметре, клиентский
X-Authenticated-User не проходит) и rbac_guard (`role != admin` → 403 admin only
для pilot и analyst).

Поэтому мёртвый механизм убран целиком, а не наполовину: гейт `!!adminToken`,
оба чтения localStorage, отправка заголовка во всех четырёх хуках и пропсы
`userId`/`adminToken` панели. Владельца профилей панель берёт из `/api/v1/me` —
там же, где его берут RouteGuard и Topbar (тот же queryKey, лишнего запроса нет).
Профили и так per-user, а единственный сохранённый на проде профиль лежит под
`user_id='admin'` — ровно под именем basic_auth-аккаунта, так что миграции нет.

Заодно чинится вторая оборванная проводка на том же пути. `analyze` отправлял
`profile_id` без `profile_user_id`, а `resolve_weights()` ищет профиль как
`get_profile(db, user_id, profile_id)` — при `user_id=None` веса молча падали на
системные, при этом ответ рапортовал `weights_profile.source = "profile"`.
Проверено на проде: `profile_id=1` без `profile_user_id` → `tram_stop −0.5`
вместо `−0.4` из профиля. Без этой правки выбор профиля в разблокированном
дропдауне показывал бы одни ползунки, а score считался бы по другим весам.
Ветка вынесена в `buildAnalyzeOptions()` — она дублировалась в двух местах.

Микрокопия: «Укажите User ID и Admin Token для сохранения профилей» обещала поле,
которого нет. Текст называет причину, а не действие, и в проде вообще
недостижим — без входа страницу не отдаёт Caddy.

Refs #2782
2026-08-07 15:12:52 +05:00