Files

69 lines
2.0 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import {
clearLoginState,
exchangeKeycloakCallback,
loadLoginState,
parseAuthCallbackUrl,
} from '@/lib/keycloak-oidc';
import { getMe } from '@/lib/api';
import { pickAppText } from '@/lib/i18n/core';
import { useAppI18n } from '@/lib/i18n/provider';
import { useChatStore } from '@/lib/store';
export default function AuthCallbackPage() {
const { locale } = useAppI18n();
const router = useRouter();
const setUser = useChatStore((s) => s.setUser);
const [error, setError] = useState('');
useEffect(() => {
let cancelled = false;
const completeLogin = async () => {
const callback = parseAuthCallbackUrl();
if (callback.error) {
throw new Error(callback.errorDescription || callback.error);
}
if (!callback.code || !callback.state) {
throw new Error('Missing Keycloak callback code or state');
}
const loginState = loadLoginState();
if (!loginState || loginState.state !== callback.state) {
throw new Error('Invalid Keycloak login state');
}
const result = await exchangeKeycloakCallback({ code: callback.code, state: loginState });
clearLoginState();
const user = await getMe().catch(() => result.user);
if (cancelled) return;
setUser(user);
router.replace(loginState.nextPath || '/');
};
completeLogin().catch((exc) => {
clearLoginState();
if (!cancelled) {
setError(exc instanceof Error ? exc.message : String(exc));
}
});
return () => {
cancelled = true;
};
}, [router, setUser]);
return (
<div className="flex min-h-screen items-center justify-center px-4">
<div className="max-w-lg text-center text-sm text-muted-foreground">
{error
? pickAppText(locale, `登录失败:${error}`, `Sign-in failed: ${error}`)
: pickAppText(locale, '正在完成登录...', 'Completing sign-in...')}
</div>
</div>
);
}