yangdx
commited on
Commit
·
f9b14ab
1
Parent(s):
e901bfa
Refactor navigation and authentication flow
Browse files- Move navigation setup to AppRouter
- Prevent protected route logic to handle login 401
lightrag_webui/src/App.tsx
CHANGED
@@ -5,11 +5,9 @@ import MessageAlert from '@/components/MessageAlert'
|
|
5 |
import ApiKeyAlert from '@/components/ApiKeyAlert'
|
6 |
import StatusIndicator from '@/components/graph/StatusIndicator'
|
7 |
import { healthCheckInterval } from '@/lib/constants'
|
8 |
-
import { useBackendState
|
9 |
import { useSettingsStore } from '@/stores/settings'
|
10 |
import { useEffect } from 'react'
|
11 |
-
import { useNavigate } from 'react-router-dom'
|
12 |
-
import { navigationService } from '@/services/navigation'
|
13 |
import SiteHeader from '@/features/SiteHeader'
|
14 |
import { InvalidApiKeyError, RequireApiKeError } from '@/api/lightrag'
|
15 |
|
@@ -21,22 +19,13 @@ import ApiSite from '@/features/ApiSite'
|
|
21 |
import { Tabs, TabsContent } from '@/components/ui/Tabs'
|
22 |
|
23 |
function App() {
|
24 |
-
const navigate = useNavigate();
|
25 |
const message = useBackendState.use.message()
|
26 |
-
|
27 |
-
// Initialize navigation service
|
28 |
-
useEffect(() => {
|
29 |
-
navigationService.setNavigate(navigate);
|
30 |
-
}, [navigate]);
|
31 |
const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
|
32 |
const currentTab = useSettingsStore.use.currentTab()
|
33 |
const [apiKeyInvalid, setApiKeyInvalid] = useState(false)
|
34 |
|
35 |
// Health check
|
36 |
useEffect(() => {
|
37 |
-
const { isAuthenticated } = useAuthStore.getState();
|
38 |
-
if (!enableHealthCheck || !isAuthenticated) return
|
39 |
-
|
40 |
// Check immediately
|
41 |
useBackendState.getState().check()
|
42 |
|
|
|
5 |
import ApiKeyAlert from '@/components/ApiKeyAlert'
|
6 |
import StatusIndicator from '@/components/graph/StatusIndicator'
|
7 |
import { healthCheckInterval } from '@/lib/constants'
|
8 |
+
import { useBackendState } from '@/stores/state'
|
9 |
import { useSettingsStore } from '@/stores/settings'
|
10 |
import { useEffect } from 'react'
|
|
|
|
|
11 |
import SiteHeader from '@/features/SiteHeader'
|
12 |
import { InvalidApiKeyError, RequireApiKeError } from '@/api/lightrag'
|
13 |
|
|
|
19 |
import { Tabs, TabsContent } from '@/components/ui/Tabs'
|
20 |
|
21 |
function App() {
|
|
|
22 |
const message = useBackendState.use.message()
|
|
|
|
|
|
|
|
|
|
|
23 |
const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
|
24 |
const currentTab = useSettingsStore.use.currentTab()
|
25 |
const [apiKeyInvalid, setApiKeyInvalid] = useState(false)
|
26 |
|
27 |
// Health check
|
28 |
useEffect(() => {
|
|
|
|
|
|
|
29 |
// Check immediately
|
30 |
useBackendState.getState().check()
|
31 |
|
lightrag_webui/src/AppRouter.tsx
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
import { HashRouter as Router, Routes, Route,
|
2 |
import { useEffect, useState } from 'react'
|
3 |
import { useAuthStore } from '@/stores/state'
|
4 |
import { navigationService } from '@/services/navigation'
|
@@ -16,6 +16,12 @@ interface ProtectedRouteProps {
|
|
16 |
const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
|
17 |
const { isAuthenticated } = useAuthStore()
|
18 |
const [isChecking, setIsChecking] = useState(true)
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
useEffect(() => {
|
21 |
let isMounted = true; // Flag to prevent state updates after unmount
|
@@ -71,29 +77,33 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
|
|
71 |
// Get current path and check if it's a direct access
|
72 |
const currentPath = window.location.hash.slice(1); // Remove the '#' from hash
|
73 |
const isLoginPage = currentPath === '/login';
|
74 |
-
|
75 |
-
|
76 |
-
// Handle direct access to root path
|
77 |
-
if (isDirectAccess && currentPath === '/') {
|
78 |
-
navigationService.resetAllApplicationState();
|
79 |
-
}
|
80 |
-
|
81 |
// Skip redirect if already on login page
|
82 |
if (isLoginPage) {
|
83 |
return null;
|
84 |
}
|
85 |
|
86 |
-
//
|
87 |
-
|
88 |
-
|
|
|
|
|
|
|
|
|
89 |
}
|
90 |
|
91 |
return <>{children}</>
|
92 |
}
|
93 |
|
94 |
-
const
|
95 |
const [initializing, setInitializing] = useState(true)
|
96 |
const { isAuthenticated } = useAuthStore()
|
|
|
|
|
|
|
|
|
|
|
|
|
97 |
|
98 |
// Check token validity and auth configuration on app initialization
|
99 |
useEffect(() => {
|
@@ -152,21 +162,27 @@ const AppRouter = () => {
|
|
152 |
return null
|
153 |
}
|
154 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
return (
|
156 |
<ThemeProvider>
|
157 |
<Router>
|
158 |
-
<
|
159 |
-
|
160 |
-
<Route
|
161 |
-
path="/*"
|
162 |
-
element={
|
163 |
-
<ProtectedRoute>
|
164 |
-
<App />
|
165 |
-
</ProtectedRoute>
|
166 |
-
}
|
167 |
-
/>
|
168 |
-
</Routes>
|
169 |
-
<Toaster position="top-center" />
|
170 |
</Router>
|
171 |
</ThemeProvider>
|
172 |
)
|
|
|
1 |
+
import { HashRouter as Router, Routes, Route, useNavigate } from 'react-router-dom'
|
2 |
import { useEffect, useState } from 'react'
|
3 |
import { useAuthStore } from '@/stores/state'
|
4 |
import { navigationService } from '@/services/navigation'
|
|
|
16 |
const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
|
17 |
const { isAuthenticated } = useAuthStore()
|
18 |
const [isChecking, setIsChecking] = useState(true)
|
19 |
+
const navigate = useNavigate()
|
20 |
+
|
21 |
+
// Set navigate function for navigation service
|
22 |
+
useEffect(() => {
|
23 |
+
navigationService.setNavigate(navigate)
|
24 |
+
}, [navigate])
|
25 |
|
26 |
useEffect(() => {
|
27 |
let isMounted = true; // Flag to prevent state updates after unmount
|
|
|
77 |
// Get current path and check if it's a direct access
|
78 |
const currentPath = window.location.hash.slice(1); // Remove the '#' from hash
|
79 |
const isLoginPage = currentPath === '/login';
|
80 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
// Skip redirect if already on login page
|
82 |
if (isLoginPage) {
|
83 |
return null;
|
84 |
}
|
85 |
|
86 |
+
// For non-login pages, handle state reset and navigation
|
87 |
+
if (!isLoginPage) {
|
88 |
+
// Use navigation service for redirection
|
89 |
+
console.log('Not authenticated, redirecting to login');
|
90 |
+
navigationService.navigateToLogin();
|
91 |
+
return null;
|
92 |
+
}
|
93 |
}
|
94 |
|
95 |
return <>{children}</>
|
96 |
}
|
97 |
|
98 |
+
const AppContent = () => {
|
99 |
const [initializing, setInitializing] = useState(true)
|
100 |
const { isAuthenticated } = useAuthStore()
|
101 |
+
const navigate = useNavigate()
|
102 |
+
|
103 |
+
// Set navigate function for navigation service
|
104 |
+
useEffect(() => {
|
105 |
+
navigationService.setNavigate(navigate)
|
106 |
+
}, [navigate])
|
107 |
|
108 |
// Check token validity and auth configuration on app initialization
|
109 |
useEffect(() => {
|
|
|
162 |
return null
|
163 |
}
|
164 |
|
165 |
+
return (
|
166 |
+
<Routes>
|
167 |
+
<Route path="/login" element={<LoginPage />} />
|
168 |
+
<Route
|
169 |
+
path="/*"
|
170 |
+
element={
|
171 |
+
<ProtectedRoute>
|
172 |
+
<App />
|
173 |
+
</ProtectedRoute>
|
174 |
+
}
|
175 |
+
/>
|
176 |
+
</Routes>
|
177 |
+
)
|
178 |
+
}
|
179 |
+
|
180 |
+
const AppRouter = () => {
|
181 |
return (
|
182 |
<ThemeProvider>
|
183 |
<Router>
|
184 |
+
<AppContent />
|
185 |
+
<Toaster position="bottom-center" />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
186 |
</Router>
|
187 |
</ThemeProvider>
|
188 |
)
|
lightrag_webui/src/api/lightrag.ts
CHANGED
@@ -173,9 +173,12 @@ axiosInstance.interceptors.response.use(
|
|
173 |
(error: AxiosError) => {
|
174 |
if (error.response) {
|
175 |
if (error.response?.status === 401) {
|
176 |
-
//
|
|
|
|
|
|
|
|
|
177 |
navigationService.navigateToLogin();
|
178 |
-
|
179 |
// Return a never-resolving promise to prevent further execution
|
180 |
return new Promise(() => {});
|
181 |
}
|
|
|
173 |
(error: AxiosError) => {
|
174 |
if (error.response) {
|
175 |
if (error.response?.status === 401) {
|
176 |
+
// For login API, throw error directly
|
177 |
+
if (error.config?.url?.includes('/login')) {
|
178 |
+
throw error;
|
179 |
+
}
|
180 |
+
// For other APIs, navigate to login page
|
181 |
navigationService.navigateToLogin();
|
|
|
182 |
// Return a never-resolving promise to prevent further execution
|
183 |
return new Promise(() => {});
|
184 |
}
|
lightrag_webui/src/features/LoginPage.tsx
CHANGED
@@ -48,7 +48,7 @@ const LoginPage = () => {
|
|
48 |
toast.info(status.message)
|
49 |
}
|
50 |
navigate('/')
|
51 |
-
return
|
52 |
}
|
53 |
} catch (error) {
|
54 |
console.error('Failed to check auth configuration:', error)
|
@@ -95,11 +95,17 @@ const LoginPage = () => {
|
|
95 |
} else {
|
96 |
toast.success(t('login.successMessage'))
|
97 |
}
|
98 |
-
|
|
|
99 |
navigate('/')
|
100 |
} catch (error) {
|
101 |
console.error('Login failed...', error)
|
102 |
toast.error(t('login.errorInvalidCredentials'))
|
|
|
|
|
|
|
|
|
|
|
103 |
} finally {
|
104 |
setLoading(false)
|
105 |
}
|
|
|
48 |
toast.info(status.message)
|
49 |
}
|
50 |
navigate('/')
|
51 |
+
return // Exit early, no need to set checkingAuth to false
|
52 |
}
|
53 |
} catch (error) {
|
54 |
console.error('Failed to check auth configuration:', error)
|
|
|
95 |
} else {
|
96 |
toast.success(t('login.successMessage'))
|
97 |
}
|
98 |
+
|
99 |
+
// Navigate to home page after successful login
|
100 |
navigate('/')
|
101 |
} catch (error) {
|
102 |
console.error('Login failed...', error)
|
103 |
toast.error(t('login.errorInvalidCredentials'))
|
104 |
+
|
105 |
+
// Clear any existing auth state
|
106 |
+
useAuthStore.getState().logout()
|
107 |
+
// Clear local storage
|
108 |
+
localStorage.removeItem('LIGHTRAG-API-TOKEN')
|
109 |
} finally {
|
110 |
setLoading(false)
|
111 |
}
|
lightrag_webui/src/services/navigation.ts
CHANGED
@@ -61,13 +61,28 @@ class NavigationService {
|
|
61 |
* @param skipReset whether to skip state reset (used for direct access scenario where reset is already handled)
|
62 |
*/
|
63 |
navigateToLogin() {
|
|
|
|
|
|
|
|
|
64 |
|
65 |
-
|
66 |
-
|
67 |
|
68 |
-
|
69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
}
|
|
|
|
|
71 |
}
|
72 |
}
|
73 |
|
|
|
61 |
* @param skipReset whether to skip state reset (used for direct access scenario where reset is already handled)
|
62 |
*/
|
63 |
navigateToLogin() {
|
64 |
+
if (!this.navigate) {
|
65 |
+
console.error('Navigation function not set');
|
66 |
+
return;
|
67 |
+
}
|
68 |
|
69 |
+
// First navigate to login page
|
70 |
+
this.navigate('/login');
|
71 |
|
72 |
+
// Then reset state after navigation
|
73 |
+
setTimeout(() => {
|
74 |
+
this.resetAllApplicationState();
|
75 |
+
useAuthStore.getState().logout();
|
76 |
+
}, 0);
|
77 |
+
}
|
78 |
+
|
79 |
+
navigateToHome() {
|
80 |
+
if (!this.navigate) {
|
81 |
+
console.error('Navigation function not set');
|
82 |
+
return;
|
83 |
}
|
84 |
+
|
85 |
+
this.navigate('/');
|
86 |
}
|
87 |
}
|
88 |
|