3 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
d708f15019 |
fix(tradein/support): один повтор терял каждое одиннадцатое сообщение в поддержку
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 11s
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) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m50s
Замер прода 01.09.2026 из контейнера бота: канал до api.telegram.org рвётся всплесками, доля отказов на попытку 15-38% (пять проб: 3/8, 15/40, 5/20, 3/20, 1/25), в логе long-polling'а 353 ConnectTimeout за сутки. Транспорт ни при чём — httpx и сырой сокет отваливаются одинаково (25% против 35% в чередующемся замере), и прокси не помогает, а мешает: через SCRAPER_PROXY_URL 0 из 20. Ручка веб-поддержки ходила с max_retries=1, то есть двумя попытками. При 30% отказов на попытку до пользователя доходило ~9% отказов — каждое одиннадцатое сообщение возвращало 502 «сервис недоступен». Два других числа из того же замера задают конструкцию. Успешный запрос отвечает за 0.13с (максимум из 25 проб — 0.18с), а неудачный НИКОГДА не отваливается быстро: все отказы упираются в таймаут целиком (10.02с при timeout=10.0). Значит десятисекундный таймаут не покупал ничего, кроме цены за неудачу, — снижен до 5с, это ~28-кратный запас к измеренному максимуму. И экспоненциальная пауза 2→4→8с здесь бессмысленна: отказ — неустановленное соединение, а не троттлинг, пережидать нечего; она лишь добавляла 14с к ожиданию. Правка: бюджет ручки — 3 повтора, таймаут 5с, потолок паузы 1с. Худший случай 4 попытки × 5с + 3 паузы × 1с = 23с и требует четырёх отказов подряд; типичный случай не меняется (0.13с). Расчётная потеря падает с ~9% до ~0.8%. В TelegramClient добавлен необязательный max_backoff. Воркерная политика НЕ меняется: без явного потолка откат прежний экспоненциальный до 30с, а retry_after из 429 уважается целиком — эту границу держит отдельный тест, потому что первая версия правки её сломала (капала 60с до 30с и для воркера тоже). Потолок на retry_after применяется только когда его передали явно: интерактивному пути нельзя ждать Telegram-овские 30-60с, за ним стоит открытый запрос от браузера. Тесты: 8 новых (потолок на network/429/5xx, неизменность воркерного пути, арифметика «max_retries=N → N+1 попыток», границы бюджета ручки). |
||
|
|
40fc94ee91 |
feat(tradein/support): чат поддержки без входа — экран логина и «доступа нет»
All checks were successful
CI / changes (pull_request) Successful in 12s
CI Trade-In / changes (pull_request) Successful in 12s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Successful in 1m42s
CI Trade-In / backend-tests (pull_request) Successful in 2m31s
После cutover'а на свою авторизацию (#2558) единственным каналом в поддержку остался чат ЗА логином, а самая частая причина писать в поддержку — как раз «не могу войти». 2026-07-31 это выстрелило: «Практика» весь день билась в форму входа (5 неудачных попыток с трёх разных IP, ни одной успешной) и сообщить об этом из продукта не могла ничем — на /login не было ни чата, ни контакта. Backend — 4 ручки /api/v1/trade-in/support/anon/* (public в rbac_guard): - Идентичность анонима — opaque-токен в httpOnly+Secure куке; тред живёт в тех же web_support_threads под ключом `anon:<token>`. Двоеточие делает коллизию с реальным логином структурно невозможной (CHECK миграции 193 разрешает только `^[A-Za-z0-9._-]{3,64}$`) — аноним не может попасть в чужой тред. - Изоляция та же, что у авторизованной ветки: thread_id снаружи не принимается ни в каком виде, тред резолвится ИСКЛЮЧИТЕЛЬНО из куки. - Форма куки валидируется — мусор из браузера не становится ключом треда. - В Telegram-топик уходит не токен (это bearer треда), а `anon-<6 hex sha256>`; зеркало помечено «[С САЙТА · БЕЗ ВХОДА]» — оператору важно, что аккаунта нет. - Анти-абуз: два бюджета — per-token (12/мин) и per-IP (10/10мин). Второй ловит обход ротацией куки, без него публичная ручка записи в общий топик беззащитна. - Кука и запись в БД — только после успешного sendMessage (порядок операций H1), неудачная отправка не закрепляет за посетителем пустой тред. Frontend: - `SupportScope = "auth" | "anon"` в useSupportChat: scope выбирает базовый путь и входит в ключ кэша (иначе после логина в панели висела бы переписка анонима). Дефолт "auth" — существующие места монтирования не меняются. - `AnonSupportWidget` монтируется на /login и в NoAccessScreen — обе точки тупики, из которых пользователю больше некуда идти. На /login добавлена подсказка. Ответы оператора маршрутизируются без изменений в bridge.py: реплай резолвится по topic_message_id → thread_id, кто автор треда — там неважно. Тесты: 13 новых на анонимную ветку + 2 на границу public/authed в rbac_guard. 62 passed (test_support + test_rbac). |
||
| ca1015bd4e |
feat(tradein/support): веб-чат поддержки — серверная часть поверх Telegram-моста (#2532)
All checks were successful
Deploy Trade-In / changes (push) Successful in 14s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 4m59s
Deploy Trade-In / build-backend (push) Successful in 1m10s
Deploy Trade-In / deploy (push) Successful in 1m17s
|