202 lines
6.2 KiB
TypeScript
202 lines
6.2 KiB
TypeScript
|
|
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
|