import { useEffect } from 'react' import { Routes, Route, Navigate } from 'react-router-dom' import { CssBaseline, ThemeProvider, CircularProgress, Box } from '@mui/material' import { darkTheme } from './theme/theme' import { useAuthStore } from './store/authStore' import AppLayout from './components/AppLayout' import LoginPage from './pages/LoginPage' import SsoCallbackPage from './pages/SsoCallbackPage' import MfaSetupPage from './pages/MfaSetupPage' import HostsPage from './pages/HostsPage' import HostDetailPage from './pages/HostDetailPage' import GroupsPage from './pages/GroupsPage' import UsersPage from './pages/UsersPage' import DashboardPage from './pages/DashboardPage' import PatchDeploymentPage from './pages/PatchDeploymentPage' import JobsPage from './pages/JobsPage' import MaintenanceWindowsPage from './pages/MaintenanceWindowsPage' import CertificatesPage from './pages/CertificatesPage' import ReportsPage from './pages/ReportsPage' import SettingsPage from './pages/SettingsPage' import ProfilePage from './pages/ProfilePage' function RequireAuth({ children }: { children: React.ReactNode }) { const isAuthenticated = useAuthStore((s) => s.isAuthenticated) const isRestoring = useAuthStore((s) => s.isRestoring) if (isRestoring) { return ( ) } return isAuthenticated ? <>{children} : } /** * Waits for Zustand persist to finish rehydrating from localStorage, * then calls restoreSession() so it can see the persisted refreshToken. * Includes a safety timeout in case anything hangs. */ function AuthRestorer({ children }: { children: React.ReactNode }) { const restoreSession = useAuthStore((s) => s.restoreSession) useEffect(() => { let cancelled = false // Safety timeout: force isRestoring=false if restoration doesn't complete in 15s const timeout = setTimeout(() => { if (!cancelled) { console.warn('[auth] Restoration timeout — forcing isRestoring=false') useAuthStore.setState({ isRestoring: false }) } }, 15_000) const doRestore = () => { if (!cancelled) restoreSession() } let unsub: (() => void) | undefined // Only call restoreSession AFTER Zustand has rehydrated the persisted state if (useAuthStore.persist.hasHydrated()) { console.warn('[auth] Store already hydrated, restoring session') doRestore() } else { console.warn('[auth] Waiting for Zustand hydration...') unsub = useAuthStore.persist.onFinishHydration(() => { console.warn('[auth] Hydration complete, restoring session') doRestore() }) } return () => { cancelled = true clearTimeout(timeout) unsub?.() } }, [restoreSession]) return <>{children} } function App() { return ( {/* Public */} } /> } /> {/* Protected — wrapped in AppLayout with sidebar navigation */} }> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> ) } export default App