# Самокритика v0.5 — что починено и что осталось **Дата:** 2026-05-14 **Перед этим:** [critique-v0.4.md](critique-v0.4.md) — 20 пунктов критики. ## ✅ Починено в v0.5 (12 пунктов) ### Безопасность (4) - **#6 Cookie `Secure` флаг.** Подтверждено: `set-cookie: ...; HttpOnly; Max-Age=43200; Path=/; SameSite=lax; Secure`. - **#7 Rate-limit на /login.** In-memory bucket per-IP, 5 попыток / 60 сек. Считает **только failed** попытки, при успехе bucket очищается. Подтверждено: 5 раз 401, 6-й → 429. - **#8 CSRF tokens для login/logout.** Signed token через `itsdangerous`, hidden input в формах, `verify_csrf_token` на POST. Подтверждено: невалидный токен → 403, отсутствие → 422. - **#9 Cashier/viewer больше не видят KPI флагов.** Карточка рендерится только если `can_see(managers)`. Аналогично карточка «Расхождения» прячется без `can_see(reconciliation)`. ### Audit (1) - **#13 Авто-аудит login/logout.** Записываются: `login_success`, `login_failed`, `login_blocked` (rate-limit hit), `logout`. С IP-адресом в `note`. Подтверждено в БД (видны записи за последние минуты). ### Технические (2) - **#10 Lifespan handler** вместо `@app.on_event("startup")`. FastAPI 0.95+ совместимо. - **#11 N+1 устранён.** `joinedload(Operation.branch)` в operations / reconciliation / dashboard; `joinedload(User.branch)` и `joinedload(TgFlag.manager)` в managers. ### UI/UX (5) - **#3 Нерабочие кнопки → `disabled` + tooltip.** «Добавить операцию», «связать вручную», «одиночная» — `cursor-not-allowed`, `title="Появится на E1"`. Никто больше не кликнет ожидая результата. - **#15 Селектор периода.** На дашборде `?period=today|7d|30d`, по умолчанию `7d`. KPI и график подстраиваются. - **#16 Mobile responsive.** Sidebar схлопывается под 768px, появляется кнопка «☰», KPI грид становится 1/2/4 колонки по breakpoint'ам. - **#17 Wallets — статус «не синхронизировано».** Если `last_synced_at` старше 10 минут или null — badge «⊘ не синхронизировано», цифра выцветает. Сверху страницы — баннер «это seed, реальные данные на E2». - **#18 Inline `