fix(tradein): RouteGuard prod 401 → session-expired screen, stop re-subscribe storm (#800) #896

Merged
bot-reviewer merged 1 commit from feat/800-routeguard-401-session-screen into main 2026-05-31 13:17:45 +00:00
2 changed files with 13 additions and 6 deletions

View file

@ -10,15 +10,17 @@
*/
interface NoAccessScreenProps {
variant: "user" | "path";
variant: "user" | "path" | "session";
path?: string;
}
export function NoAccessScreen({ variant, path }: NoAccessScreenProps) {
const subtitle =
variant === "user"
? "Учётная запись не привязана к роли. Обратитесь к администратору GenDesign — kopylov."
: "У вашей роли нет доступа к этому разделу. Вернитесь на главную или обратитесь к администратору.";
variant === "session"
? "Сессия истекла или вы не авторизованы. Обновите страницу, чтобы войти снова."
: variant === "user"
? "Учётная запись не привязана к роли. Обратитесь к администратору GenDesign — kopylov."
: "У вашей роли нет доступа к этому разделу. Вернитесь на главную или обратитесь к администратору.";
return (
<main
@ -54,7 +56,7 @@ export function NoAccessScreen({ variant, path }: NoAccessScreenProps) {
lineHeight: 1.25,
}}
>
Доступа нет
{variant === "session" ? "Сессия истекла" : "Доступа нет"}
</h1>
<p
style={{

View file

@ -45,7 +45,12 @@ export function RouteGuard({ children }: RouteGuardProps) {
if (isLoading) return null;
if (error instanceof HTTPError && error.status === 401) {
return <>{children}</>;
// Dev without Caddy: 401 is normal, mount the app so local dev works.
// Prod: mounting children on 401 causes TanStack Query re-subscribe storm
// (each new observer on errored query triggers a refetch). Show session screen
// instead — prevents the subtree from mounting, kills the loop.
if (process.env.NODE_ENV !== "production") return <>{children}</>;
return <NoAccessScreen variant="session" />;
}
if (error instanceof HTTPError && error.status === 403) {