Files
doc_ai_frontend/src/pages/AuthCallbackPage.tsx
2026-03-06 14:30:30 +08:00

82 lines
2.5 KiB
TypeScript

import { useEffect, useMemo, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useAuth, OAUTH_POST_LOGIN_REDIRECT_KEY } from '../contexts/AuthContext';
import { useLanguage } from '../contexts/LanguageContext';
function toInternalPath(urlOrPath: string): string {
try {
const parsed = new URL(urlOrPath, window.location.origin);
if (parsed.origin !== window.location.origin) {
return '/';
}
return `${parsed.pathname}${parsed.search}${parsed.hash}`;
} catch {
return '/';
}
}
export default function AuthCallbackPage() {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const { completeGoogleOAuth } = useAuth();
const { t } = useLanguage();
const [error, setError] = useState<string | null>(null);
const code = useMemo(() => searchParams.get('code') ?? '', [searchParams]);
const state = useMemo(() => searchParams.get('state') ?? '', [searchParams]);
useEffect(() => {
let mounted = true;
const run = async () => {
if (!code || !state) {
if (mounted) {
setError(t.auth.oauthFailed);
}
return;
}
const redirectUri = `${window.location.origin}/auth/google/callback`;
const result = await completeGoogleOAuth({ code, state, redirect_uri: redirectUri });
if (result.error) {
if (mounted) {
setError(result.error.message || t.auth.oauthFailed);
}
return;
}
const redirectTarget = sessionStorage.getItem(OAUTH_POST_LOGIN_REDIRECT_KEY) || '/';
navigate(toInternalPath(redirectTarget), { replace: true });
};
run();
return () => {
mounted = false;
};
}, [code, completeGoogleOAuth, navigate, state, t.auth.oauthFailed]);
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
<div className="bg-white rounded-xl shadow-xl w-full max-w-md p-6 text-center">
<h1 className="text-xl font-bold text-gray-900 mb-3">Google OAuth</h1>
{!error && <p className="text-gray-600">{t.auth.oauthExchanging}</p>}
{error && (
<>
<p className="text-red-600 text-sm mb-4">{error}</p>
<button
type="button"
onClick={() => navigate('/', { replace: true })}
className="px-4 py-2 bg-gray-900 text-white rounded-lg hover:bg-gray-800 transition-colors"
>
Back Home
</button>
</>
)}
</div>
</div>
);
}