From 5c308274f28e5eae2ce4f7cc4e77198249e7037c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=BB=D1=8C=D1=8F=D1=81=20=D0=A1=D1=83=D0=BB=D1=82?= =?UTF-8?q?=D0=B0=D0=BD=D0=BE=D0=B2?= Date: Thu, 17 Sep 2026 22:02:10 +0300 Subject: [PATCH] =?UTF-8?q?docs(log):=20auth/gps/chat=20=D1=84=D0=B8=D0=BA?= =?UTF-8?q?=D1=81=D1=8B=20=E2=80=94=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BD=D1=8B?= =?UTF-8?q?,=20429-=D1=88=D1=82=D0=BE=D1=80=D0=BC,=20=D1=81=D0=BA=D0=B0?= =?UTF-8?q?=D1=87=D1=83=D1=89=D0=B8=D0=B9=20=D1=87=D0=B0=D1=82,=20=D1=83?= =?UTF-8?q?=D0=BF=D0=BE=D0=BC=D0=B8=D0=BD=D0=B0=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IMPLEMENTATION_LOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/IMPLEMENTATION_LOG.md b/IMPLEMENTATION_LOG.md index 6d4626e..3ad6d62 100644 --- a/IMPLEMENTATION_LOG.md +++ b/IMPLEMENTATION_LOG.md @@ -581,3 +581,21 @@ - настройка «Строк в ячейке» (Авто / 1–8) в диалоге «Настройки реестра», хранится в `registry-rowlines-`; CSS `-webkit-line-clamp` на текстовых ячейках (титул, описание, исполнители, автор, текстовые поля; readView inline-редакторов — кламп только на read-view, редактор не затронут); оценка высоты виртуализатора 32 px в однострочном режиме. - Как проверялось: `npm run check` (heap 6144), `npm run build`, `npx vitest run` (129/129), eslint 0 errors. Деплой через GitHub Actions — success, health 200. - Подводные камни: (1) кламп не применяется к file/contract-number/составным полям — намеренно; (2) при `table-fixed` колонки без заданной ширины делят оставшееся место поровну (ожидаемо); (3) ширины/строки хранятся в localStorage — не мигрируют между устройствами (единообразно с прочими настройками реестра; серверный вариант — отдельная задача); (4) настройки реестра надо проверить на проде глазами: браузерная сессия WebBridge истекла, ручная проверка отложена за пользователем. + +--- + +## [Авторизация + GPS + чат] Логины, 429-шторм, скачущий чат, упоминания + +- Статус: ✅ done (коммиты `c1d5b1c` gps, `d011265` auth, `267af04` chat; деплой на iistwin.ru) +- Зачем: (1) пользователя периодически выкидывало на логин на том же устройстве; (2) «превышено количество запросов» и CRM переставал показывать формы; (3) сообщения в чате задачи «прыгали»; (4) жалоба на упоминания (@) в чате задачи. +- Расследование (3 explore-агента + логи прода через SSH): + - **429-шторм:** логи показали 8642 ответа 429 за 30 минут, почти все /api/gps/*. Цепочка: Traccar → POST /api/gps/ingest ~1/сек → SSE gps_asset_moved broadcast всем клиентам орг. на каждую точку (server/gps/geozone.service.ts) → useGpsLiveUpdates (client/src/hooks/useGps.ts) invalidateQueries без throttle → 2+ GET/сек → per-user бакет apiLimiter (1500/15мин) исчерпан → ВСЕ запросы устройства 429. + - **Логины:** (A) access-cookie 15 мин (TTL урезан 8 сентября, 06bfd2a), а refresh-retry был отключён для всех /api/auth/* (queryClient.ts:218) — холодный старт после 15 мин простоя = гарантированный логаут при живом 30-дневном refresh; (B) мёртвый grace-период ротации: markSessionReplaced не чистил refreshTokenHash → повтор старого токена шёл в isReplaced→deny, сервер стирал ОБА cookie общего cookie-jar → разлогинивались все вкладки/PWA устройства; гонки между refreshSession (singleton внутри вкладки), keep-alive useAuth и сырым fetch в useOfflineSync; (C) любой не-ok рефреша (429/500/сеть) трактовался как смерть сессии; refreshLimiter 120/15мин по IP без per-user ключа — офисный NAT делил бакет. + - **Чат:** автоскролл TaskChat без atBottom-гварда (любое новое сообщение дёргало чат вниз); картинки без размеров дёргали measureElement виртуализатора. + - **Упоминания:** код цел; UX-ловушки — голый @ не открывал список, текстовый триггер от 2 букв, мёртвый fallback-резолв (Response без .json()). Во время 429-шторма endpoint /api/mentions/search просто отдавал 429. +- Что изменено: + - GPS (c1d5b1c): server/gps/sse-throttle.ts — публикация gps_asset_moved не чаще 3 сек на asset (запись в БД не тронута); useGpsLiveUpdates — trailing throttle инвалидаций 60 сек; позиции polling 60 сек, статусы объектов 5 мин. Модель «точки пишутся в БД, карта читает по интервалу». +4 unit-теста. + - Auth (d011265): /api/auth/me при 401 → одна попытка refresh (единственное исключение из /api/auth/*); grace-окно ротации починено — повтор старого токена в пределах 5 мин выдаёт новую пару от successor, cookie НЕ стираются при stale-токенах, revoke family только при реальном reuse за пределами окна; refreshSession различает 401/403/400 (мёртвая сессия) vs 429/5xx/сеть (transient, без logout); единый refresh-путь (useAuth, useOfflineSync → refreshSession) + межвкладочная дедупликация BroadcastChannel('auth-refresh'); refreshLimiter per-user keyGenerator. +5 тестов в token-security.test.ts. + - Чат (267af04): atBottom-гвард автоскролла (как useChatController); картинки w-180 h-180 фиксировано; упоминания: голый @ открывает список, текстовый триггер от 3 букв, поиск по имени/отчеству/фамилии/email ILIKE, починен fallback-резолв; groupByDate/flatten в useMemo. +- Как проверялось: npm run check (heap 6144), build, vitest 138/138 (было 129: +4 троттл, +5 token-security, корректировки под новую логику), eslint 0 errors по изменённым файлам. +- Подводные камни: (1) каскад ротаций в grace-окне плодит строки user_sessions при шторме из N вкладок — безвредно, TTL-очистка сессий вне скоупа; (2) authService.refreshTokens() в client/src/services/auth.service.ts стал мёртвым — кандидат на чистку; (3) waiter BroadcastChannel по таймауту 8с отдаёт transient — вкладка повторит сама; (4) после деплоя мониторить логи на 429 (должно уйти к нулю при открытой GPS-странице).