Refactor navigation and authentication flow, prevent mounting login page multiple times

- Improved protected route handling
- Enhanced direct login access detection
- Centralized navigation logic
- Optimized state reset process
- Fixed logout navigation behavior
This commit is contained in:
yangdx
2025-03-19 12:45:08 +08:00
parent bc4c16b06a
commit 50a8b5fb6d
8 changed files with 120 additions and 94 deletions

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@
<link rel="icon" type="image/svg+xml" href="logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lightrag</title>
<script type="module" crossorigin src="/webui/assets/index-BiPN9eZH.js"></script>
<script type="module" crossorigin src="/webui/assets/index-CMFGmyjk.js"></script>
<link rel="stylesheet" crossorigin href="/webui/assets/index-mPRIIErN.css">
</head>
<body>

View File

@@ -1,6 +1,7 @@
import { HashRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
import { useEffect, useState } from 'react'
import { useAuthStore } from '@/stores/state'
import { navigationService } from '@/services/navigation'
import { getAuthStatus } from '@/api/lightrag'
import { toast } from 'sonner'
import { Toaster } from 'sonner'
@@ -65,9 +66,26 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
return null
}
// After checking, if still not authenticated, redirect to login
// After checking, if still not authenticated
if (!isAuthenticated) {
return <Navigate to="/login" replace />
// Get current path and check if it's a direct access
const currentPath = window.location.hash.slice(1); // Remove the '#' from hash
const isLoginPage = currentPath === '/login';
const isDirectAccess = !document.referrer;
// Handle direct access to root path
if (isDirectAccess && currentPath === '/') {
navigationService.resetAllApplicationState();
}
// Skip redirect if already on login page
if (isLoginPage) {
return null;
}
// Use React Router's Navigate for redirection
console.log('Not authenticated, redirecting to login');
return <Navigate to="/login" replace />;
}
return <>{children}</>

View File

@@ -2,7 +2,6 @@ import axios, { AxiosError } from 'axios'
import { backendBaseUrl } from '@/lib/constants'
import { errorMessage } from '@/lib/utils'
import { useSettingsStore } from '@/stores/settings'
import { useAuthStore } from '@/stores/state'
import { navigationService } from '@/services/navigation'
// Types
@@ -174,10 +173,6 @@ axiosInstance.interceptors.response.use(
(error: AxiosError) => {
if (error.response) {
if (error.response?.status === 401) {
localStorage.removeItem('LIGHTRAG-API-TOKEN');
sessionStorage.clear();
useAuthStore.getState().logout();
// Use navigation service to handle redirection
navigationService.navigateToLogin();

View File

@@ -32,7 +32,7 @@ export default function AppSettings({ className }: AppSettingsProps) {
return (
<Popover open={opened} onOpenChange={setOpened}>
<PopoverTrigger asChild>
<Button variant="ghost" size="icon" className={cn("h-9 w-9", className)}>
<Button variant="ghost" size="icon" className={cn('h-9 w-9', className)}>
<PaletteIcon className="h-5 w-5" />
</Button>
</PopoverTrigger>

View File

@@ -4,8 +4,6 @@ import { useAuthStore } from '@/stores/state'
import { loginToServer, getAuthStatus } from '@/api/lightrag'
import { toast } from 'sonner'
import { useTranslation } from 'react-i18next'
import { navigationService } from '@/services/navigation'
import { Card, CardContent, CardHeader } from '@/components/ui/Card'
import Input from '@/components/ui/Input'
import Button from '@/components/ui/Button'
@@ -21,12 +19,11 @@ const LoginPage = () => {
const [password, setPassword] = useState('')
const [checkingAuth, setCheckingAuth] = useState(true)
// Reset application state on first mount
useEffect(() => {
navigationService.resetAllApplicationState();
}, []); // Empty dependency array means this runs only once on mount
console.log('LoginPage mounted')
}, []);
// Check if authentication is configured
// Check if authentication is configured, skip login if not
useEffect(() => {
let isMounted = true; // Flag to prevent state updates after unmount

View File

@@ -6,8 +6,7 @@ import { useSettingsStore } from '@/stores/settings'
import { useAuthStore } from '@/stores/state'
import { cn } from '@/lib/utils'
import { useTranslation } from 'react-i18next'
import { useNavigate } from 'react-router-dom'
import { navigationService } from '@/services/navigation'
import { ZapIcon, GithubIcon, LogOutIcon } from 'lucide-react'
interface NavigationTabProps {
@@ -56,12 +55,10 @@ function TabsNavigation() {
export default function SiteHeader() {
const { t } = useTranslation()
const navigate = useNavigate()
const { logout, isGuestMode } = useAuthStore()
const { isGuestMode } = useAuthStore()
const handleLogout = () => {
logout()
navigate('/login')
navigationService.navigateToLogin();
}
return (

View File

@@ -20,31 +20,50 @@ class NavigationService {
resetAllApplicationState() {
console.log('Resetting all application state...');
// Clear authentication state
localStorage.removeItem('LIGHTRAG-API-TOKEN');
sessionStorage.clear();
useAuthStore.getState().logout();
// Reset graph state
const graphStore = useGraphStore.getState();
const sigma = graphStore.sigmaInstance;
graphStore.reset();
graphStore.setGraphDataFetchAttempted(false);
graphStore.setLabelsFetchAttempted(false);
graphStore.setSigmaInstance(null);
// Reset backend state
useBackendState.getState().clear();
// Reset retrieval history while preserving other user preferences
useSettingsStore.getState().setRetrievalHistory([]);
// Clear authentication state
sessionStorage.clear();
if (sigma) {
sigma.getGraph().clear();
sigma.kill();
useGraphStore.getState().setSigmaInstance(null);
}
}
/**
* Navigate to login page after resetting application state
* to ensure a clean environment for the next session
* Handle direct access to login page
* @returns true if it's a direct access, false if navigated from another page
*/
handleDirectLoginAccess() {
const isDirectAccess = !document.referrer;
if (isDirectAccess) {
this.resetAllApplicationState();
}
return isDirectAccess;
}
/**
* Navigate to login page and reset application state
* @param skipReset whether to skip state reset (used for direct access scenario where reset is already handled)
*/
navigateToLogin() {
// Reset state before navigation
this.resetAllApplicationState();
useAuthStore.getState().logout();
if (this.navigate) {
this.navigate('/login');