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:
File diff suppressed because one or more lines are too long
@@ -8,7 +8,7 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="logo.png" />
|
<link rel="icon" type="image/svg+xml" href="logo.png" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Lightrag</title>
|
<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">
|
<link rel="stylesheet" crossorigin href="/webui/assets/index-mPRIIErN.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { HashRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
|
import { HashRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useAuthStore } from '@/stores/state'
|
import { useAuthStore } from '@/stores/state'
|
||||||
|
import { navigationService } from '@/services/navigation'
|
||||||
import { getAuthStatus } from '@/api/lightrag'
|
import { getAuthStatus } from '@/api/lightrag'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { Toaster } from 'sonner'
|
import { Toaster } from 'sonner'
|
||||||
@@ -65,9 +66,26 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// After checking, if still not authenticated, redirect to login
|
// After checking, if still not authenticated
|
||||||
if (!isAuthenticated) {
|
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}</>
|
return <>{children}</>
|
||||||
|
@@ -2,7 +2,6 @@ import axios, { AxiosError } from 'axios'
|
|||||||
import { backendBaseUrl } from '@/lib/constants'
|
import { backendBaseUrl } from '@/lib/constants'
|
||||||
import { errorMessage } from '@/lib/utils'
|
import { errorMessage } from '@/lib/utils'
|
||||||
import { useSettingsStore } from '@/stores/settings'
|
import { useSettingsStore } from '@/stores/settings'
|
||||||
import { useAuthStore } from '@/stores/state'
|
|
||||||
import { navigationService } from '@/services/navigation'
|
import { navigationService } from '@/services/navigation'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
@@ -174,10 +173,6 @@ axiosInstance.interceptors.response.use(
|
|||||||
(error: AxiosError) => {
|
(error: AxiosError) => {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
localStorage.removeItem('LIGHTRAG-API-TOKEN');
|
|
||||||
sessionStorage.clear();
|
|
||||||
useAuthStore.getState().logout();
|
|
||||||
|
|
||||||
// Use navigation service to handle redirection
|
// Use navigation service to handle redirection
|
||||||
navigationService.navigateToLogin();
|
navigationService.navigateToLogin();
|
||||||
|
|
||||||
|
@@ -32,7 +32,7 @@ export default function AppSettings({ className }: AppSettingsProps) {
|
|||||||
return (
|
return (
|
||||||
<Popover open={opened} onOpenChange={setOpened}>
|
<Popover open={opened} onOpenChange={setOpened}>
|
||||||
<PopoverTrigger asChild>
|
<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" />
|
<PaletteIcon className="h-5 w-5" />
|
||||||
</Button>
|
</Button>
|
||||||
</PopoverTrigger>
|
</PopoverTrigger>
|
||||||
|
@@ -4,8 +4,6 @@ import { useAuthStore } from '@/stores/state'
|
|||||||
import { loginToServer, getAuthStatus } from '@/api/lightrag'
|
import { loginToServer, getAuthStatus } from '@/api/lightrag'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { navigationService } from '@/services/navigation'
|
|
||||||
|
|
||||||
import { Card, CardContent, CardHeader } from '@/components/ui/Card'
|
import { Card, CardContent, CardHeader } from '@/components/ui/Card'
|
||||||
import Input from '@/components/ui/Input'
|
import Input from '@/components/ui/Input'
|
||||||
import Button from '@/components/ui/Button'
|
import Button from '@/components/ui/Button'
|
||||||
@@ -21,12 +19,11 @@ const LoginPage = () => {
|
|||||||
const [password, setPassword] = useState('')
|
const [password, setPassword] = useState('')
|
||||||
const [checkingAuth, setCheckingAuth] = useState(true)
|
const [checkingAuth, setCheckingAuth] = useState(true)
|
||||||
|
|
||||||
// Reset application state on first mount
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigationService.resetAllApplicationState();
|
console.log('LoginPage mounted')
|
||||||
}, []); // Empty dependency array means this runs only once on mount
|
}, []);
|
||||||
|
|
||||||
// Check if authentication is configured
|
// Check if authentication is configured, skip login if not
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true; // Flag to prevent state updates after unmount
|
let isMounted = true; // Flag to prevent state updates after unmount
|
||||||
|
|
||||||
|
@@ -6,8 +6,7 @@ import { useSettingsStore } from '@/stores/settings'
|
|||||||
import { useAuthStore } from '@/stores/state'
|
import { useAuthStore } from '@/stores/state'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { navigationService } from '@/services/navigation'
|
||||||
|
|
||||||
import { ZapIcon, GithubIcon, LogOutIcon } from 'lucide-react'
|
import { ZapIcon, GithubIcon, LogOutIcon } from 'lucide-react'
|
||||||
|
|
||||||
interface NavigationTabProps {
|
interface NavigationTabProps {
|
||||||
@@ -56,12 +55,10 @@ function TabsNavigation() {
|
|||||||
|
|
||||||
export default function SiteHeader() {
|
export default function SiteHeader() {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const navigate = useNavigate()
|
const { isGuestMode } = useAuthStore()
|
||||||
const { logout, isGuestMode } = useAuthStore()
|
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
logout()
|
navigationService.navigateToLogin();
|
||||||
navigate('/login')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -20,32 +20,51 @@ class NavigationService {
|
|||||||
resetAllApplicationState() {
|
resetAllApplicationState() {
|
||||||
console.log('Resetting all application state...');
|
console.log('Resetting all application state...');
|
||||||
|
|
||||||
// Clear authentication state
|
|
||||||
localStorage.removeItem('LIGHTRAG-API-TOKEN');
|
|
||||||
sessionStorage.clear();
|
|
||||||
useAuthStore.getState().logout();
|
|
||||||
|
|
||||||
// Reset graph state
|
// Reset graph state
|
||||||
const graphStore = useGraphStore.getState();
|
const graphStore = useGraphStore.getState();
|
||||||
|
const sigma = graphStore.sigmaInstance;
|
||||||
graphStore.reset();
|
graphStore.reset();
|
||||||
graphStore.setGraphDataFetchAttempted(false);
|
graphStore.setGraphDataFetchAttempted(false);
|
||||||
graphStore.setLabelsFetchAttempted(false);
|
graphStore.setLabelsFetchAttempted(false);
|
||||||
|
graphStore.setSigmaInstance(null);
|
||||||
|
|
||||||
// Reset backend state
|
// Reset backend state
|
||||||
useBackendState.getState().clear();
|
useBackendState.getState().clear();
|
||||||
|
|
||||||
// Reset retrieval history while preserving other user preferences
|
// Reset retrieval history while preserving other user preferences
|
||||||
useSettingsStore.getState().setRetrievalHistory([]);
|
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
|
* Handle direct access to login page
|
||||||
* to ensure a clean environment for the next session
|
* @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() {
|
navigateToLogin() {
|
||||||
// Reset state before navigation
|
|
||||||
this.resetAllApplicationState();
|
|
||||||
|
|
||||||
|
this.resetAllApplicationState();
|
||||||
|
useAuthStore.getState().logout();
|
||||||
|
|
||||||
if (this.navigate) {
|
if (this.navigate) {
|
||||||
this.navigate('/login');
|
this.navigate('/login');
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user