Проблема
Нет глобальной обработки 401 — протухшая сессия «гниёт» молча
src/api.ts:40-43 бросает ApiError, а auth.tsx проверяет сессию лишь раз при монтировании. Ops-дашборд, оставленный открытым после истечения сессии, продолжает поллить (Dashboard.tsx, Ops.tsx, SendLog.tsx с refetchInterval) и показывает сырые ошибки или устаревшие данные; пользователя никогда не выкидывает на /admin/login.
Фикс: в request() на 401 (для не-auth путей) вызывать инъектируемый onUnauthorized (или диспатчить событие, которое слушает AuthProvider → setUser(null)), тогда RequireAuth редиректит.
Ошибки delete/мутаций невидимы
ResourceTable.tsx:68-73 — у deleteMutation нет onError и рендера ошибки. Удаление домена с ящиками (409) молча ничего не делает. Тот же паттерн: Sync.tsx:74 (apply), Engine.tsx:15 (restart), FeatureFlags.tsx (toggle/reset), Fetchmail.tsx:56 (toggle/remove), Suppressions.tsx:40 (remove).
Фикс: рендерить <ApiErrorMessage error={deleteMutation.error} /> — компонент уже есть (ResourceTable.tsx:281), но в самой таблице не используется.
Прочее (мельче)
- Логин теряет
from-редирект: Login.tsx:53 идёт на /admin/ немедленно, обгоняя ветку <Navigate to={next}> (строки 42-45). Использовать location.state.from.
- Глобальный
retry:1 (App.tsx:36) ретраит 4xx (401/403/404/422). Гардить: ретраить только status >= 500.
refetchOnWindowFocus:false глобально (App.tsx:37) без staleTime — для ops-дашборда сомнительно; страницы без refetchInterval (Quarantine, Suppressions, AccessLists, Fetchmail, Sync) показывают устаревшее после alt-tab. Включить focus-refetch с staleTime 15-30s.
MailboxDetail.tsx:30: !mailbox.data рендерит «notFound» даже при 500/сетевой ошибке — проверять mailbox.isError отдельно.
Проблема
Нет глобальной обработки 401 — протухшая сессия «гниёт» молча
src/api.ts:40-43бросаетApiError, аauth.tsxпроверяет сессию лишь раз при монтировании. Ops-дашборд, оставленный открытым после истечения сессии, продолжает поллить (Dashboard.tsx,Ops.tsx,SendLog.tsxсrefetchInterval) и показывает сырые ошибки или устаревшие данные; пользователя никогда не выкидывает на/admin/login.Фикс: в
request()на 401 (для не-auth путей) вызывать инъектируемыйonUnauthorized(или диспатчить событие, которое слушаетAuthProvider→setUser(null)), тогдаRequireAuthредиректит.Ошибки delete/мутаций невидимы
ResourceTable.tsx:68-73— уdeleteMutationнетonErrorи рендера ошибки. Удаление домена с ящиками (409) молча ничего не делает. Тот же паттерн:Sync.tsx:74(apply),Engine.tsx:15(restart),FeatureFlags.tsx(toggle/reset),Fetchmail.tsx:56(toggle/remove),Suppressions.tsx:40(remove).Фикс: рендерить
<ApiErrorMessage error={deleteMutation.error} />— компонент уже есть (ResourceTable.tsx:281), но в самой таблице не используется.Прочее (мельче)
from-редирект:Login.tsx:53идёт на/admin/немедленно, обгоняя ветку<Navigate to={next}>(строки 42-45). Использоватьlocation.state.from.retry:1(App.tsx:36) ретраит 4xx (401/403/404/422). Гардить: ретраить толькоstatus >= 500.refetchOnWindowFocus:falseглобально (App.tsx:37) безstaleTime— для ops-дашборда сомнительно; страницы безrefetchInterval(Quarantine, Suppressions, AccessLists, Fetchmail, Sync) показывают устаревшее после alt-tab. Включить focus-refetch сstaleTime15-30s.MailboxDetail.tsx:30:!mailbox.dataрендерит «notFound» даже при 500/сетевой ошибке — проверятьmailbox.isErrorотдельно.