Files
web-bak/src/components/AuthGuard.tsx

202 lines
6.2 KiB
TypeScript
Raw Normal View History

2026-02-09 16:33:52 +08:00
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