Files
web-bak/src/components/AuthGuard.tsx
wdvipa 28040495c8 111
2026-02-09 16:33:52 +08:00

202 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { Spin } from 'antd'
import type { AppDispatch } from '../store/store'
import {
login,
verifyToken,
restoreAuthState,
clearError,
clearAuthState,
selectIsAuthenticated,
selectAuthLoading,
selectAuthError,
selectToken
} from '../store/slices/authSlice'
import LoginPage from './LoginPage'
import InstallPage from './InstallPage'
import apiClient from '../services/apiClient'
interface AuthGuardProps {
children: React.ReactNode
}
/**
* 认证守卫组件
* 负责验证用户登录状态,未登录时显示登录页面
*/
const AuthGuard: React.FC<AuthGuardProps> = ({ children }) => {
const dispatch = useDispatch<AppDispatch>()
const isAuthenticated = useSelector(selectIsAuthenticated)
const authLoading = useSelector(selectAuthLoading)
const authError = useSelector(selectAuthError)
const token = useSelector(selectToken)
const [isInitializing, setIsInitializing] = useState(true)
const [loginError, setLoginError] = useState<string | null>(null)
const [systemInitialized, setSystemInitialized] = useState<boolean | null>(null)
// 调试:监听认证状态变化
useEffect(() => {
console.log('🔐 AuthGuard - 认证状态变化:', {
isAuthenticated,
authLoading,
token: token ? '***' : null,
isInitializing,
systemInitialized
})
}, [isAuthenticated, authLoading, token, isInitializing, systemInitialized])
// 组件挂载时检查系统初始化状态
useEffect(() => {
const initializeAuth = async () => {
try {
console.log('🔐 检查系统初始化状态...')
// 首先检查系统是否已初始化
// const initResult = await apiClient.get<any>('/')
const initResult = await apiClient.get<any>('/api/auth/check-initialization')
if (initResult.success) {
setSystemInitialized(initResult.isInitialized)
console.log(`🔐 系统初始化状态: ${initResult.isInitialized ? '已初始化' : '未初始化'}`)
// 如果系统已初始化,继续认证流程
if (initResult.isInitialized) {
console.log('🔐 初始化认证状态...')
// 先尝试从本地存储恢复状态
dispatch(restoreAuthState())
// 等待一个tick让状态更新
await new Promise(resolve => setTimeout(resolve, 0))
// 获取恢复后的token
const currentToken = localStorage.getItem('auth_token')
if (currentToken) {
console.log('🔐 找到本地token验证有效性...')
// 验证token是否仍然有效
try {
const result = await dispatch(verifyToken(currentToken))
if (verifyToken.fulfilled.match(result)) {
console.log('✅ Token验证成功')
} else {
console.log('❌ Token验证失败:', result.payload)
setLoginError('登录已过期,请重新登录')
}
} catch (error) {
console.log('❌ Token验证出错:', error)
setLoginError('登录验证失败,请重新登录')
}
} else {
console.log('🔐 未找到本地token')
}
}
} else {
console.error('检查系统初始化状态失败')
setSystemInitialized(false) // 默认为未初始化
}
} catch (error) {
console.error('初始化检查失败:', error)
setSystemInitialized(false) // 出错时默认为未初始化
} finally {
setIsInitializing(false)
}
}
initializeAuth()
}, [dispatch])
// 监听token过期事件
useEffect(() => {
const handleTokenExpired = () => {
console.log('🔐 Token过期清除认证状态')
dispatch(clearAuthState())
setLoginError('登录已过期,请重新登录')
}
window.addEventListener('auth:token-expired', handleTokenExpired)
return () => {
window.removeEventListener('auth:token-expired', handleTokenExpired)
}
}, [dispatch])
// 处理登录
const handleLogin = async (username: string, password: string) => {
try {
console.log('🔐 尝试登录:', username)
setLoginError(null)
dispatch(clearError())
const result = await dispatch(login({ username, password }))
if (login.fulfilled.match(result)) {
console.log('✅ 登录成功')
setLoginError(null)
} else if (login.rejected.match(result)) {
const errorMessage = result.payload || '登录失败'
console.log('❌ 登录失败:', errorMessage)
setLoginError(errorMessage)
throw new Error(errorMessage)
}
} catch (error: any) {
console.error('登录过程出错:', error)
throw error // 重新抛出错误让LoginPage显示加载状态
}
}
// 清除登录错误
useEffect(() => {
if (authError && authError !== loginError) {
setLoginError(authError)
}
}, [authError, loginError])
// 处理安装完成
const handleInstallComplete = () => {
console.log('🔐 安装完成,刷新初始化状态')
setSystemInitialized(true)
setLoginError(null)
}
// 初始化加载中
if (isInitializing || systemInitialized === null) {
return (
<div style={{
minHeight: '100vh',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
}}>
<Spin size="large" style={{ color: 'white' }} />
</div>
)
}
// 系统未初始化,显示安装页面
if (!systemInitialized) {
return (
<InstallPage onInstallComplete={handleInstallComplete} />
)
}
// 系统已初始化但未登录,显示登录页面
if (!isAuthenticated) {
return (
<LoginPage
onLogin={handleLogin}
loading={authLoading}
error={loginError || undefined}
/>
)
}
// 已登录,显示主应用
return <>{children}</>
}
export default AuthGuard