feat: admin frontend - React + Vite, auth pages, user management, roles, permissions, webhooks, devices, logs
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { Link, useLocation, useNavigate, useSearchParams } from 'react-router-dom'
|
||||
import { Button, Result, Spin, Typography, message } from 'antd'
|
||||
|
||||
import { useAuth } from '@/app/providers/auth-context'
|
||||
import { AuthLayout } from '@/layouts'
|
||||
import { parseOAuthCallbackHash, sanitizeAuthRedirect } from '@/lib/auth/oauth'
|
||||
import { getErrorMessage } from '@/lib/errors'
|
||||
import { exchangeOAuthHandoff } from '@/services/auth'
|
||||
|
||||
const { Paragraph } = Typography
|
||||
|
||||
export function OAuthCallbackPage() {
|
||||
const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading')
|
||||
const [errorMessage, setErrorMessage] = useState('')
|
||||
const [searchParams] = useSearchParams()
|
||||
const location = useLocation()
|
||||
const navigate = useNavigate()
|
||||
const { onLoginSuccess } = useAuth()
|
||||
|
||||
const redirect = sanitizeAuthRedirect(searchParams.get('redirect'))
|
||||
const callbackPayload = useMemo(() => parseOAuthCallbackHash(location.hash), [location.hash])
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
|
||||
const consumeHandoff = async () => {
|
||||
if (callbackPayload.status === 'error') {
|
||||
if (!cancelled) {
|
||||
setStatus('error')
|
||||
setErrorMessage(callbackPayload.message || '第三方登录失败,请重试')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (!callbackPayload.code) {
|
||||
if (!cancelled) {
|
||||
setStatus('error')
|
||||
setErrorMessage('缺少OAuth登录交接码,请重新发起登录')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const tokenBundle = await exchangeOAuthHandoff(callbackPayload.code)
|
||||
await onLoginSuccess(tokenBundle)
|
||||
|
||||
if (!cancelled) {
|
||||
setStatus('success')
|
||||
message.success('第三方登录成功')
|
||||
navigate(redirect, { replace: true })
|
||||
}
|
||||
} catch (error) {
|
||||
if (!cancelled) {
|
||||
setStatus('error')
|
||||
setErrorMessage(getErrorMessage(error, '第三方登录失败,请重试'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void consumeHandoff()
|
||||
|
||||
return () => {
|
||||
cancelled = true
|
||||
}
|
||||
}, [callbackPayload.code, callbackPayload.message, callbackPayload.status, navigate, onLoginSuccess, redirect])
|
||||
|
||||
if (status === 'loading') {
|
||||
return (
|
||||
<AuthLayout>
|
||||
<div style={{ textAlign: 'center', padding: '48px 0' }}>
|
||||
<Spin size="large" />
|
||||
<Paragraph type="secondary" style={{ marginTop: 16 }}>
|
||||
正在完成第三方登录...
|
||||
</Paragraph>
|
||||
</div>
|
||||
</AuthLayout>
|
||||
)
|
||||
}
|
||||
|
||||
if (status === 'success') {
|
||||
return (
|
||||
<AuthLayout>
|
||||
<Result
|
||||
status="success"
|
||||
title="登录成功"
|
||||
subTitle="正在跳转到目标页面..."
|
||||
/>
|
||||
</AuthLayout>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthLayout>
|
||||
<Result
|
||||
status="error"
|
||||
title="第三方登录失败"
|
||||
subTitle={errorMessage}
|
||||
extra={[
|
||||
<Link key="login" to={`/login${redirect !== '/dashboard' ? `?redirect=${encodeURIComponent(redirect)}` : ''}`}>
|
||||
<Button type="primary">返回登录</Button>
|
||||
</Link>,
|
||||
]}
|
||||
/>
|
||||
</AuthLayout>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user