fix(#264): apiFetch — global X-Session-Id header (custom POI scoring в UI) #266
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#266
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/264-session-id-global-apifetch"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Симптом
После добавления custom POI на /site-finder блок "Вклад по категориям" не обновлялся и score не менялся.
Backend корректно возвращает
custom_poi_score_itemsпри наличииX-Session-Idheader. Frontend делал POST /analyze без него → backend получал no session → возвращалcustom_poi_score_items: [].Verified: ручной curl с X-Session-Id → score 1.87, 72 entries (3 custom); без header → score 13.81, custom POIs пусты.
Fix
frontend/src/lib/api.ts— добавленwithSessionHeader(init)helper:getOrCreateSessionIdиз@/lib/sessionIdtypeof window === "undefined"→ skipX-Session-Idпервым, затемbase.headers→ user-supplied headers побеждают (backward compat)apiFetchиapiFetchWithStatususeCustomPois.ts— не изменён. ExplicitsessionHeaders()calls оставлены для readability.Invalidation
useAddCustomPoi.onSuccessуже invalidates["analyze", parcelCad]query (строки 76-78).Regression
Existing analyze без custom POI: backend возвращает
custom_poi_score_items: [], score не меняется.Closes #264
Deep Code Review — verdict APPROVE
Files: 1 (P2:
frontend/src/lib/api.ts) · +27/-4 · 1 commit cleanЧто проверено
withSessionHeader()— SSR guardtypeof window === "undefined"корректный, ранний return доgetOrCreateSessionId()(тот тоже SSR-safe, дублирующая защита — OK)."X-Session-Id": sessionIdпервым,...(base.headers ?? {})вторым → user-supplied headers побеждают. Backward compat соблюдён.apiFetch/apiFetchWithStatus: сначалаwithSessionHeader(init), потом ещё раз spreadContent-Typeповерхmerged.headers— повторение, но семантически корректно (Content-Type первым, мерж не теряет X-Session-Id).x_session_id: Annotated[str | None, Header()] = Noneвparcels.py:1063иcustom_pois.py— strip/None-проверка устойчива к pустому header.useCustomPois.tsоставлен с явнымsessionHeaders()— нет конфликта: то же значение, последний spread (user headers) их же и перезапишет.Связь с #261 / #262
Reviewer #262 указал: «#261 описывает симптом «row не сохраняется», но
db.commit()в main есть — root cause не там». Этот PR — кандидат на actual root cause #261: безX-Session-Idbackend_resolve_session_idгенерил новый UUID на каждый запрос → POST/custom-poisсохранял с одним user_id, GET читал с другим → визуально «не сохраняется». После merge — обязательный smoke test #261.Не критично (pre-existing, не блокирует)
invalidateQueries({ queryKey: ["analyze", parcelCad] })вuseAddCustomPoi.onSuccess— no-op, т.к.useSiteAnalysisэтоuseMutation, неuseQuery. Score обновится только когда user снова дёрнет analyze (re-submit cad / change weights). UX-доработка — отдельным PR (можно сделатьmutation.mutate()вызов в onSuccess SiteMap, илиsetDataвuseSiteAnalysis). Текущий fix описанный симптом починит, т.к. PR-author тестил после click «Применить» / повторного POST.HeadersInitшире чемRecord<string, string>, но в codebase никто не передаётHeaders/string[][]вapiFetch— runtime spread безопасен.Pre-merge checks
Post-merge smoke test (после deploy)
Merging via deep-code-reviewer (merge style — preserves PR commit message + closes #264).