Add AuthCallback component for handling authentication status and redirection
Some checks failed
CI - Build and Push / Build and Push Docker Image (push) Failing after 14s
Some checks failed
CI - Build and Push / Build and Push Docker Image (push) Failing after 14s
This commit is contained in:
@@ -140,5 +140,19 @@
|
|||||||
"wantToJoin": "Want to Join Our Friend Links?",
|
"wantToJoin": "Want to Join Our Friend Links?",
|
||||||
"joinDescription": "If you have a gaming-related website or community, we'd love to add you to our friend links network!",
|
"joinDescription": "If you have a gaming-related website or community, we'd love to add you to our friend links network!",
|
||||||
"contactUs": "Contact Us"
|
"contactUs": "Contact Us"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"success": "Login Successful!",
|
||||||
|
"failed": "Login Failed",
|
||||||
|
"error": "Authentication Error",
|
||||||
|
"processing": "Processing...",
|
||||||
|
"successMessage": "You have successfully logged in with your Steam account.",
|
||||||
|
"failedMessage": "Steam authentication was not successful. Please try again.",
|
||||||
|
"errorMessage": "An error occurred during authentication. Please try again later.",
|
||||||
|
"processingMessage": "Processing your authentication...",
|
||||||
|
"steamId": "Steam ID",
|
||||||
|
"redirecting": "Redirecting you to the home page in 5 seconds...",
|
||||||
|
"backToHome": "Back to Home",
|
||||||
|
"tryAgain": "Try Again"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,5 +139,19 @@
|
|||||||
"wantToJoin": "想加入我们的友情链接吗?",
|
"wantToJoin": "想加入我们的友情链接吗?",
|
||||||
"joinDescription": "如果您有游戏相关的网站或社区,我们很乐意将您添加到我们的友情链接网络中!",
|
"joinDescription": "如果您有游戏相关的网站或社区,我们很乐意将您添加到我们的友情链接网络中!",
|
||||||
"contactUs": "联系我们"
|
"contactUs": "联系我们"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"success": "登录成功!",
|
||||||
|
"failed": "登录失败",
|
||||||
|
"error": "认证错误",
|
||||||
|
"processing": "处理中...",
|
||||||
|
"successMessage": "您已成功使用 Steam 账户登录。",
|
||||||
|
"failedMessage": "Steam 认证未成功,请重试。",
|
||||||
|
"errorMessage": "认证过程中发生错误,请稍后重试。",
|
||||||
|
"processingMessage": "正在处理您的认证...",
|
||||||
|
"steamId": "Steam ID",
|
||||||
|
"redirecting": "5 秒后将跳转到首页...",
|
||||||
|
"backToHome": "返回首页",
|
||||||
|
"tryAgain": "重试"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ import Friends from './pages/Friends.tsx'
|
|||||||
import Blog from './pages/Blog.tsx'
|
import Blog from './pages/Blog.tsx'
|
||||||
import Servers from './pages/Servers.tsx'
|
import Servers from './pages/Servers.tsx'
|
||||||
import Forum from './pages/Forum.tsx'
|
import Forum from './pages/Forum.tsx'
|
||||||
|
import AuthCallback from './pages/AuthCallback.tsx'
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
@@ -26,6 +27,7 @@ createRoot(document.getElementById('root')!).render(
|
|||||||
<Route path="/blog" element={<Blog />} />
|
<Route path="/blog" element={<Blog />} />
|
||||||
<Route path="/servers" element={<Servers />} />
|
<Route path="/servers" element={<Servers />} />
|
||||||
<Route path="/forum" element={<Forum />} />
|
<Route path="/forum" element={<Forum />} />
|
||||||
|
<Route path="/auth/callback" element={<AuthCallback />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
</ServerProvider>
|
</ServerProvider>
|
||||||
|
|||||||
239
src/pages/AuthCallback.tsx
Normal file
239
src/pages/AuthCallback.tsx
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import '../App.css'
|
||||||
|
|
||||||
|
interface AuthStatus {
|
||||||
|
status: 'success' | 'failed' | 'error' | null
|
||||||
|
steamId?: string
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function AuthCallback() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const [searchParams] = useSearchParams()
|
||||||
|
const [authStatus, setAuthStatus] = useState<AuthStatus>({ status: null })
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Parse query parameters
|
||||||
|
const status = searchParams.get('status') as 'success' | 'failed' | 'error' | null
|
||||||
|
const steamId = searchParams.get('steamId') || undefined
|
||||||
|
const message = searchParams.get('message') || undefined
|
||||||
|
|
||||||
|
setAuthStatus({ status, steamId, message })
|
||||||
|
|
||||||
|
// Auto-redirect to home after 5 seconds on success
|
||||||
|
if (status === 'success') {
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
navigate('/')
|
||||||
|
}, 5000)
|
||||||
|
return () => clearTimeout(timer)
|
||||||
|
}
|
||||||
|
}, [searchParams, navigate])
|
||||||
|
|
||||||
|
const getStatusIcon = () => {
|
||||||
|
switch (authStatus.status) {
|
||||||
|
case 'success':
|
||||||
|
return '✅'
|
||||||
|
case 'failed':
|
||||||
|
return '❌'
|
||||||
|
case 'error':
|
||||||
|
return '⚠️'
|
||||||
|
default:
|
||||||
|
return '🔄'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusTitle = () => {
|
||||||
|
switch (authStatus.status) {
|
||||||
|
case 'success':
|
||||||
|
return t('auth.success')
|
||||||
|
case 'failed':
|
||||||
|
return t('auth.failed')
|
||||||
|
case 'error':
|
||||||
|
return t('auth.error')
|
||||||
|
default:
|
||||||
|
return t('auth.processing')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getStatusMessage = () => {
|
||||||
|
if (authStatus.message) {
|
||||||
|
return authStatus.message
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (authStatus.status) {
|
||||||
|
case 'success':
|
||||||
|
return t('auth.successMessage')
|
||||||
|
case 'failed':
|
||||||
|
return t('auth.failedMessage')
|
||||||
|
case 'error':
|
||||||
|
return t('auth.errorMessage')
|
||||||
|
default:
|
||||||
|
return t('auth.processingMessage')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="auth-callback-container">
|
||||||
|
<div className="auth-callback-card">
|
||||||
|
<div className="auth-icon">{getStatusIcon()}</div>
|
||||||
|
<h1 className="auth-title">{getStatusTitle()}</h1>
|
||||||
|
<p className="auth-message">{getStatusMessage()}</p>
|
||||||
|
|
||||||
|
{authStatus.steamId && (
|
||||||
|
<div className="auth-details">
|
||||||
|
<p>
|
||||||
|
<strong>{t('auth.steamId')}:</strong> {authStatus.steamId}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{authStatus.status === 'success' && (
|
||||||
|
<p className="auth-redirect">{t('auth.redirecting')}</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="auth-actions">
|
||||||
|
<button
|
||||||
|
className="btn-primary"
|
||||||
|
onClick={() => navigate('/')}
|
||||||
|
>
|
||||||
|
{t('auth.backToHome')}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{authStatus.status !== 'success' && (
|
||||||
|
<button
|
||||||
|
className="btn-secondary"
|
||||||
|
onClick={() => window.location.href = '/api/authenticator/steam/login'}
|
||||||
|
>
|
||||||
|
{t('auth.tryAgain')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>{`
|
||||||
|
.auth-callback-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2rem;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-callback-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 3rem;
|
||||||
|
max-width: 500px;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-icon {
|
||||||
|
font-size: 4rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
animation: bounce 1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0%, 100% { transform: translateY(0); }
|
||||||
|
50% { transform: translateY(-10px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-title {
|
||||||
|
font-size: 2rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-message {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: #666;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-details {
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-details p {
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-redirect {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #999;
|
||||||
|
font-style: italic;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary, .btn-secondary {
|
||||||
|
padding: 0.75rem 2rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: #667eea;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
background: #5568d3;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: #e0e0e0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
background: #d0d0d0;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.auth-callback-card {
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-title {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary, .btn-secondary {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AuthCallback
|
||||||
Reference in New Issue
Block a user