Fix redirect loop on session timeout

This commit is contained in:
2026-06-10 05:05:29 +00:00
parent 3161ef7d91
commit b7211ee43f
2 changed files with 10 additions and 6 deletions
+8 -5
View File
@@ -1,5 +1,4 @@
import { useEffect, useState, useCallback, useRef } from 'react'; import { useEffect, useState, useCallback, useRef } from 'react';
import { useNavigate } from 'react-router-dom';
import { import {
Dialog, Dialog,
DialogContent, DialogContent,
@@ -15,18 +14,22 @@ const AUTO_REDIRECT_SECONDS = 5;
export default function SessionTimeoutModal() { export default function SessionTimeoutModal() {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [secondsLeft, setSecondsLeft] = useState(AUTO_REDIRECT_SECONDS); const [secondsLeft, setSecondsLeft] = useState(AUTO_REDIRECT_SECONDS);
const navigate = useNavigate();
const timerRef = useRef<ReturnType<typeof setInterval>>(); const timerRef = useRef<ReturnType<typeof setInterval>>();
const wasOpenRef = useRef(false);
const redirectToLogin = useCallback(() => { const redirectToLogin = useCallback(() => {
tokenManager.clearToken(); tokenManager.clearToken();
navigate('/login', { replace: true }); window.location.href = '/login';
}, [navigate]); }, []);
useEffect(() => { useEffect(() => {
const onSessionExpired = () => { const onSessionExpired = () => {
setOpen(true); setOpen(true);
setSecondsLeft(AUTO_REDIRECT_SECONDS); // Only reset the countdown when the modal transitions from closed → open
if (!wasOpenRef.current) {
setSecondsLeft(AUTO_REDIRECT_SECONDS);
}
wasOpenRef.current = true;
}; };
window.addEventListener('session:expired', onSessionExpired); window.addEventListener('session:expired', onSessionExpired);
return () => window.removeEventListener('session:expired', onSessionExpired); return () => window.removeEventListener('session:expired', onSessionExpired);
+2 -1
View File
@@ -47,13 +47,14 @@ const userNavItems = [
{ title: "Activity", url: "/activity", icon: Activity }, { title: "Activity", url: "/activity", icon: Activity },
{ title: "CLI Guide", url: "/cli-guide", icon: BookOpen }, { title: "CLI Guide", url: "/cli-guide", icon: BookOpen },
{ title: "ZeroTier Devices", url: "/org/zerotier/devices", icon: Monitor }, { title: "ZeroTier Devices", url: "/org/zerotier/devices", icon: Monitor },
{ title: "My Memberships", url: "/org/my-memberships", icon: Layers },
]; ];
// Visible to ALL org members // Visible to ALL org members
const orgMemberNavItems = [ const orgMemberNavItems = [
{ title: "Overview", url: "/org", icon: Building2 }, { title: "Overview", url: "/org", icon: Building2 },
{ title: "My Memberships", url: "/org/my-memberships", icon: Layers },
]; ];
// Visible to org admins/owners only (management) // Visible to org admins/owners only (management)