'use client'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; import { clearTokens, consumeHandoffCode, getMe, setTokens } from '@/lib/api'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; import { useChatStore } from '@/lib/store'; const HANDOFF_STATE_KEY = 'beaver_handoff_state'; type HandoffState = { code?: string; accessToken?: string; refreshToken?: string; nextPath?: string; }; function parseHandoffStateFromLocation(): HandoffState { if (typeof window === 'undefined') { return {}; } const query = new URLSearchParams(window.location.search); const code = query.get('code') || ''; const nextFromQuery = query.get('next') || ''; if (code) { return { code, nextPath: nextFromQuery || '/', }; } const rawHash = window.location.hash.startsWith('#') ? window.location.hash.slice(1) : window.location.hash; const hash = new URLSearchParams(rawHash); const accessToken = hash.get('access_token') || ''; if (accessToken) { return { accessToken, refreshToken: hash.get('refresh_token') || '', nextPath: hash.get('next') || '/', }; } return {}; } function loadHandoffState(): HandoffState { if (typeof window === 'undefined') { return {}; } const fromLocation = parseHandoffStateFromLocation(); if (fromLocation.code || fromLocation.accessToken) { sessionStorage.setItem(HANDOFF_STATE_KEY, JSON.stringify(fromLocation)); return fromLocation; } const cached = sessionStorage.getItem(HANDOFF_STATE_KEY) || ''; if (!cached) { return {}; } try { const parsed = JSON.parse(cached) as HandoffState; return parsed && typeof parsed === 'object' ? parsed : {}; } catch { return {}; } } function clearHandoffState(): void { if (typeof window === 'undefined') { return; } sessionStorage.removeItem(HANDOFF_STATE_KEY); } export default function HandoffPage() { const { locale } = useAppI18n(); const router = useRouter(); const setUser = useChatStore((s) => s.setUser); const [error, setError] = useState(''); useEffect(() => { let cancelled = false; const run = async () => { const handoff = loadHandoffState(); const nextPath = handoff.nextPath || '/'; if (!handoff.code && !handoff.accessToken) { clearHandoffState(); setError(pickAppText(locale, '缺少登录凭证,无法进入目标前端。', 'Missing login credentials. Unable to enter the target frontend.')); return; } window.history.replaceState(null, '', '/handoff'); try { const tokenPayload = handoff.accessToken ? { access_token: handoff.accessToken, refresh_token: handoff.refreshToken || '', } : await consumeHandoffCode(handoff.code || ''); setTokens(tokenPayload.access_token, tokenPayload.refresh_token || ''); const me = await getMe(); if (cancelled) return; clearHandoffState(); setUser(me); router.replace(nextPath.startsWith('/') ? nextPath : '/'); } catch (err) { clearHandoffState(); clearTokens(); if (cancelled) return; setError(err instanceof Error ? err.message : pickAppText(locale, '目标前端登录失败', 'Target frontend sign-in failed')); } }; void run(); return () => { cancelled = true; }; }, [router, setUser]); return (
{error}
) : ({pickAppText(locale, '正在同步登录态,请稍候。', 'Syncing your sign-in state. Please wait.')}
)}