gendesign/tradein-mvp/backend/app/api/v1
bot-backend 5eadae1e95 fix(tradein/support): address deep-review H1/M1/M2/M3/M5/L1-L5 (web chat backend)
H1 (critical): reorder send_support_message — get_or_create_thread now runs
AFTER a successful Telegram send, not before. Prod runs a single uvicorn
process with no --workers on a sync SQLAlchemy engine sharing one event
loop; holding a row-lock across the Telegram call (which can legally take
minutes on 429/5xx worker-grade retries) risked freezing the entire API on
a second concurrent request from the same user. send_message now also
accepts explicit timeout/max_retries so the interactive endpoint uses a
bounded budget instead of inheriting the long-polling worker's retry policy.

M1: web_support_messages and tg_support_messages both gain a support_chat_id
column (188 migration for the already-applied tg_support_messages table;
187 edited in place since it hasn't shipped yet). bridge._handle_group_reply
now resolves BOTH the Telegram and web candidate under the CURRENT
support_chat_id and refuses delivery loudly (logger.error) if both match,
instead of silently preferring the Telegram path — closing a cross-table
misroute risk that would surface if the support group is ever recreated.

M2: a media reply to a web-mirrored message (including photos with a
caption) is now refused in full with an explicit notice back in the topic,
instead of silently delivering just the caption text or logging a WARNING
nobody sees.

M3: list_support_messages/get_support_unread/mark_support_read switched
from async def to def — their bodies are pure sync psycopg calls; running
them as async def executed blocking DB work directly on the event loop.

M5: list_messages now takes a bounded LIMIT (last N, chronological) so a
long-lived thread doesn't return its entire history on every poll.

L1-L4: warn when Telegram doesn't return a message_id (routing dead-end),
rate limit is peeked before send and only recorded on success (failed
attempts no longer burn the budget), SlidingWindowLimiter gained the same
empty-bucket cleanup as RateLimitMiddleware, and _require_username now
strips whitespace so a proxy-injected space can't fork a second thread.

L5: removed 187 from _manifest_applied.txt — the deploy pipeline tracks
applied migrations via _schema_migrations, not this file, and keeping an
unmerged migration name out of it preserves the option to rename before
merge without tripping the "can't rename applied migrations" test.
2026-07-26 23:33:09 +03:00
..
__init__.py feat: add tradein-mvp subproject (Trade-In Estimator под /trade-in) 2026-05-21 00:25:39 +03:00
admin.py fix(tradein/cian): персист bti_data/valuation house_info в houses + дедуп УК (#2435) (#2437) 2026-07-04 20:21:32 +00:00
audit.py feat(tradein/admin): audit + analytics read API over user_events (#2520) 2026-07-13 20:26:58 +00:00
brand.py feat: add tradein-mvp subproject (Trade-In Estimator под /trade-in) 2026-05-21 00:25:39 +03:00
buildings.py feat(tradein): второй % «за 45 дней» + zhkh-знаменатель slot (146) (#2071) 2026-06-28 17:51:38 +00:00
geocode.py fix(tradein/geocoder): раз-хардкод геокодера/DaData на всю Свердловскую обл. (region 66) 2026-07-12 18:33:05 +03:00
lead.py feat(tradein/lead): persist 152-ФЗ proof-of-consent (LOW audit R2 #9) (#2511) 2026-07-12 22:13:36 +00:00
me.py feat(rbac): role-based access control via X-Authenticated-User middleware (#585) 2026-05-26 06:18:40 +00:00
search.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
support.py fix(tradein/support): address deep-review H1/M1/M2/M3/M5/L1-L5 (web chat backend) 2026-07-26 23:33:09 +03:00
trade_in.py fix(tradein/quota): per-user quota override + no-burn on empty estimate (#2517) 2026-07-13 20:18:42 +00:00