fenghuo/apps/web/app/auth/callback/page.tsx

121 lines
3.3 KiB
TypeScript
Raw Normal View History

2025-05-28 08:23:14 +08:00
'use client';
import { useEffect, useState } from 'react';
import { useSearchParams, useRouter } from 'next/navigation';
import { userManager } from '@/lib/oidc-config';
import { Loader2, CheckCircle, XCircle } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@repo/ui/components/card';
import { Alert, AlertDescription } from '@repo/ui/components/alert';
export default function CallbackPage() {
const searchParams = useSearchParams();
const router = useRouter();
const [status, setStatus] = useState<'loading' | 'success' | 'error'>('loading');
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const handleCallback = async () => {
try {
if (!userManager) {
throw new Error('用户管理器未初始化');
}
// 处理OIDC回调
const user = await userManager.signinRedirectCallback();
if (user) {
setStatus('success');
// 延迟跳转到首页
setTimeout(() => {
router.push('/');
}, 2000);
} else {
throw new Error('未收到用户信息');
}
} catch (err) {
console.error('回调处理失败:', err);
setError(err instanceof Error ? err.message : '未知错误');
setStatus('error');
}
};
// 检查是否有授权码或错误参数
const code = searchParams.get('code');
const error = searchParams.get('error');
const errorDescription = searchParams.get('error_description');
if (error) {
setError(`${error}: ${errorDescription || '授权失败'}`);
setStatus('error');
return;
}
if (code) {
handleCallback();
} else {
setError('缺少授权码');
setStatus('error');
}
}, [searchParams, router]);
const getStatusIcon = () => {
switch (status) {
case 'loading':
return <Loader2 className="h-8 w-8 animate-spin text-blue-500" />;
case 'success':
return <CheckCircle className="h-8 w-8 text-green-500" />;
case 'error':
return <XCircle className="h-8 w-8 text-red-500" />;
}
};
const getStatusMessage = () => {
switch (status) {
case 'loading':
return '正在处理登录回调...';
case 'success':
return '登录成功!正在跳转...';
case 'error':
return '登录失败';
}
};
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900">
<Card className="w-full max-w-md">
<CardHeader className="text-center">
<div className="flex justify-center mb-4">{getStatusIcon()}</div>
<CardTitle className="text-xl">{getStatusMessage()}</CardTitle>
</CardHeader>
<CardContent>
{status === 'error' && error && (
<Alert variant="destructive">
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{status === 'loading' && (
<div className="text-center text-sm text-gray-600 dark:text-gray-400">
<p>...</p>
</div>
)}
{status === 'success' && (
<div className="text-center text-sm text-gray-600 dark:text-gray-400">
<p>...</p>
</div>
)}
{status === 'error' && (
<div className="text-center mt-4">
<button onClick={() => router.push('/')} className="text-blue-600 hover:text-blue-800 text-sm">
</button>
</div>
)}
</CardContent>
</Card>
</div>
);
}