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?",
|
||||
"joinDescription": "If you have a gaming-related website or community, we'd love to add you to our friend links network!",
|
||||
"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": "想加入我们的友情链接吗?",
|
||||
"joinDescription": "如果您有游戏相关的网站或社区,我们很乐意将您添加到我们的友情链接网络中!",
|
||||
"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 Servers from './pages/Servers.tsx'
|
||||
import Forum from './pages/Forum.tsx'
|
||||
import AuthCallback from './pages/AuthCallback.tsx'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
@@ -26,6 +27,7 @@ createRoot(document.getElementById('root')!).render(
|
||||
<Route path="/blog" element={<Blog />} />
|
||||
<Route path="/servers" element={<Servers />} />
|
||||
<Route path="/forum" element={<Forum />} />
|
||||
<Route path="/auth/callback" element={<AuthCallback />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
</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