feat(tradein-ui): Cian cookies upload admin page #481
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#481
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/tradein-cian-cookies-ui"
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?
Summary
Admin UI page для upload Cian Valuation Calculator cookies — paste JSON → POST
/api/v1/admin/scrape/cian/upload-cookies(Stage 4 backend endpoint).Files
frontend/src/app/admin/scrape/cian-cookies/page.tsx(NEW, 462 lines)frontend/src/app/admin/layout.tsx(+1 line — tab entry)URL
/admin/scrape/cian-cookiesFeatures
Format auto-detection (client-side)
Paste может быть в одном из 2 форматов:
Cookie-Editor extension (массив объектов):
Simple dict (после ручной конвертации):
Page detects format и converts to dict client-side. Preview "Detected N cookies: name1, name2, ..." обновляется на каждый keystroke (pure synchronous parse).
Upload flow
/api/v1/admin/scrape/cian/upload-cookies{ok: true, userId: ..., cookieCount: ...}(success) или error message/kalkulator-nedvizhimosti/(auth check) → encrypt → DBTest current session
Separate button "Test Auth" → GET
/api/v1/admin/scrape/cian/test-authReturns:
{authenticated: true, userId: ...}или{authenticated: false, reason: ...}useQuerywithenabled: false+.refetch()— manual trigger, no auto-fetch on mount.Patterns reused
localStorage.getItem("admin_token")+X-Admin-Tokenheader (same as/admin/scrape/page.tsx)cardStyle/inputStyle/labelStylefrom@/lib/adminStylesapiFetchfrom@/lib/apiuseMutationдля POST,useQuery(enabled:false) для on-demand GETTS strict
Record<string, string>для cookie dictUploadResponse/TestAuthResponseinterfacesanytypesString(v)в conversion (handles numeric / boolean values)Security
dangerouslySetInnerHTML(JSON stays в textarea value)Push verify
LOCAL == REMOTE =
b92eb81fDecisions
/admin/scrape/cian-cookies(not/trade-in/admin/...) — no(trade-in)route group exists; admin pages live under/admin/scrape/useQuerywithenabled: falsefor test-auth — cleaner thanuseMutationдля GET, avoids auto-fetch anti-patternuploadMutation.reset()on textarea change — clears stale success/error bannersRefs
feat/tradein-cian-cookies-real-list) — fixes backend filter listTest plan
Deep Code Review — verdict
Summary
CRITICAL — routing mismatch (BLOCK)
Backend endpoints
/api/v1/admin/scrape/cian/upload-cookiesи/api/v1/admin/scrape/cian/test-authживут только вtradein-mvp/backend/app/api/v1/admin.py(lines 244, 290). В main backend (backend/app/api/v1/admin_scrape.pyи другихadmin_*.py) этих роутов НЕТ — grepcianпоbackend/app/api/v1/→ 0 matches.Caddy маршрутизация:
Tradein backend reachable ТОЛЬКО через префикс
/trade-in/api/....Что делает этот PR:
frontend/src/app/admin/scrape/cian-cookies/page.tsx(main app подgendsgn.ru/admin/...)apiFetch("/api/v1/admin/scrape/cian/upload-cookies", ...)→API_BASE_URL=""+ path → fetch кhttps://gendsgn.ru/api/v1/admin/scrape/cian/upload-cookies/trade-in/api/*→ попадает в maingendesign-backend:8000→ 404 Not Found на оба endpointsСимптом: страница откроется, paste JSON будет работать, но
Upload CookiesиTest Current Sessionбудут возвращатьAPI error 404: Not Found. Backendcian_session.upload-cookiesникогда не получит запрос.Fix options
Option A (recommended) — переместить страницу в tradein-mvp frontend:
tradein-mvp/frontend/src/app/admin/scrape/cian-cookies/page.tsxbasePath=/trade-in→ URL станетhttps://gendsgn.ru/trade-in/admin/scrape/cian-cookiesapiFetchв tradein-mvp (учитывая basePath) →/trade-in/api/v1/admin/scrape/cian/upload-cookies→ Caddy strip → tradein backend OKOption B — оставить в main frontend, но переписать paths:
Это рабочий вариант, но архитектурно странно: main
frontend/src/app/admin/дёргает tradein backend. Лучше A.Option C — добавить cian routes в main backend (с проксированием в tradein) — overkill для одной admin утилиты.
HIGH — auth pattern (NOT blocker, see note)
Per prompt narrative о PRs #437/#442/#474 «removed X-Admin-Token» — prompt narrative неверный для main backend:
grep require_admin backend/app/api/v1/→ 6 файлов (admin_weight_profiles, admin_scrape, admin_jobs, admin_leads, admin_cadastre, admin_etl) всё ещё используют X-Admin-Token.api-types.ts(auto-gen из main OpenAPI) содержит 26 ссылок наX-Admin-Token— то есть main backend admin endpoints по-прежнему требуют header.Tradein backend (
tradein-mvp/backend/app/api/v1/admin.py:1-4) — auth gate'ится Caddy basic_auth, application layer открыт. Endpointsupload_cian_cookies/test_cian_authНЕ требуютX-Admin-Token.Что делает этот PR в коде:
После fix routing (Option A или B):
/trade-in/api/*— basic_auth Caddy header не проверяет (это Authorization, не X-Admin-Token), header просто проксируется и игнорируется backendRecommendation после fix routing: убрать
X-Admin-Tokenheader и весьAdmin Tokeninput UI — для tradein endpoints не нужен. Auth — это basic_auth Caddy (browser prompt). Это уберёт confusion + dead pattern.MEDIUM — другие observations
previewKeys.length > 0без минимальной валидации (page.tsx:122). Можно paste{}→ preview "Detected 0 cookies" → canUpload=false (good due topreviewKeys.length > 0). OK.Textarea не очищается на success (
page.tsx:175). Cookies остаются в DOM/memory после успешного upload. Minor security concern (admin tool, browser context). Suggest:String(null)→"null"(page.tsx:30, 47). Cookie value ===null→ backend получит строку"null". Pertradein-mvp/backend/app/api/v1/admin.py:265, фильтрCIAN_REQUIRED_COOKIESсработает только если ключ в whitelist — null-value cookie с правильным ключом пройдёт. Edge case acceptable для admin tool.uploadMutation.reset()on textarea change (page.tsx:218-222) — корректно, очищает stale banner.Inline styles vs design tokens (
ui-ux.mdrule). Page широко использует inline hex:#1d4ed8,#5b6066,#fef2f2,#f0fdf4,#991b1b, etc. Per ui-ux.md «Inline hex вне токенов — нарушение». Но: соседняяadmin/scrape/page.tsxтоже inline-hex (legacy admin pattern, не unified в design tokens). Не блокер — consistency с existing admin style, но suggest follow-up: создатьadminStyles.tscolors export.Emoji в layout tab label (
layout.tsx:12):"Cian Cookies"— без emoji в отличие от других tabs (📊,🗺,🏗). Inconsistent + ui-ux.md «НЕ emoji в UI». Можно оставить как есть (consistency wave должен идти отдельным PR), но strict ui-ux.md говорит убрать все emoji. Acceptable как inconsistency-fix.No
dangerouslySetInnerHTML— verified. No XSS surface.TS strict — verified.
Record<string, string>,CookieEditorEntry,UploadResponse,TestAuthResponse. Noany. OK.useQuery enabled: false+.refetch()pattern — корректно, no auto-fetch on mount.JSON parse safety:
JSON.parse(input.trim())вconvertCookiesFormatобёрнут вtry/catchчерезparsePreview(page.tsx:51-60). Invalid JSON → user-friendly error в preview. OK.Cross-PR coordination
CIAN_REQUIRED_COOKIESreal list) — должен merge ПЕРВЫМ, иначе фильтрcleaned = {k: v for k, v in cookies.items() if k in CIAN_REQUIRED_COOKIES}(admin.py:265) выбросит реальные DMIR_AUTH cookies и вернёт 400.Vault cross-check
decisions/поискcian.*cookies— нет существующего decision для encryption strategy / admin auth model для tradein admin endpointscode/modules/tradein/Frontend_Cian_Cookies_Upload.mdс note о Caddy basic_auth модели (не X-Admin-Token)tradein-mvp/frontend/обслуживает/trade-in/*, mainfrontend/— gendsgn.ru/*; не пересекать»Pre-flight
feat/tradein-cian-cookies-ui(feature branch, not main) — OK--no-verify/--force/--amendin commits — OKb92eb81fVerdict
BLOCK — routing bug critical (404 at runtime), PR в текущем виде нерабочий end-to-end.
Next steps:
tradein-mvp/frontend/src/app/admin/scrape/cian-cookies/page.tsx) или Option B (заменить path на/trade-in/api/v1/admin/scrape/cian/...)X-Admin-Tokenheader + Admin Token input (tradein endpoints не требуют, auth через Caddy basic_auth)tradein-mvp/frontend/src/app/admin/layout.tsx(или соответствующий admin layout tradein), а не в mainfrontend/src/app/admin/layout.tsxonSuccess: () => setRawInput("")— clear textarea on success (security hygiene)PR не мержится до fix routing.
Fixup commit
02c2ce6pushed — addresses BLOCK routing bug.Resolution
Routing fix (Option A — moved to tradein-mvp/frontend):
frontend/src/app/admin/scrape/cian-cookies/page.tsxfrontend/src/app/admin/layout.tsx(tab removed)tradein-mvp/frontend/src/app/scrapers/cian-cookies/page.tsx(351 lines)tradein-mvp/frontend/src/components/trade-in/Topbar.tsx(+14 lines —cian-cookiesвActiveTabunion +NAV_ITEMS)Final URL:
https://gendsgn.ru/trade-in/scrapers/cian-cookiesNow requests hit
/trade-in/api/*→ Caddy → tradein backend (where cian endpoints live) → 200 OK instead of 404.Other reviewer points addressed
X-Admin-Tokenheader полностью removed (UI + headers + token state). Tradein admin endpoints не требуют — auth через Caddy basic_auth.onSuccess: () => setRawInput("")— clears textarea on successful upload (security hygiene).Topbarpattern (consistent с avito/yandex scraper pages).Deviation
URL pattern
/scrapers/cian-cookies(not/admin/scrape/cian-cookies) — tradein-mvp frontend uses flat/scrapers/структуру: avito/yandex pages там же. Matches existing convention.Verify
02c2ce6✅Ready for re-review.
Merged via deep-code-reviewer — verdict APPROVE.
Routing fix confirmed:
tradein-mvp/frontend/src/app/scrapers/cian-cookies/page.tsx(tradein app, basePath=/trade-in)apiFetchresolves totradein-mvp/frontend/src/lib/api.ts→${API_BASE_URL}${path}→/trade-in/api/v1/admin/scrape/cian/upload-cookies→ Caddyhandle_path /trade-in/api/*→ tradein-backendX-Admin-Tokenheader + localStorage state fully removed (no references anywhere in tradein-mvp/frontend)onSuccess: () => setRawInput("")frontend/src/app/admin/layout.tsx→tradein-mvp/src/components/trade-in/Topbar.tsx(/scrapers/cian-cookies)Record<string, string>,UploadResponse,TestAuthResponse,CookieEditorEntry— noanyuseQuery enabled:false+.refetch()pattern preservedMerge SHA:
65029cd1a7622ebb5825ac07e221a8a53436064b.