121 lines
3.3 KiB
TypeScript
121 lines
3.3 KiB
TypeScript
'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>
|
||
);
|
||
}
|