import type { ReactNode } from 'react' import { useEffect, useState } from 'react' import { createBrowserRouter, Outlet, useLocation, useNavigate } from 'react-router-dom' import { registrationDraft } from '@/entities/registration' import type { LoginComplete } from '@/entities/session' import { mfaChallenge, useSession } from '@/entities/session' import { AccountPage } from '@/pages/account' import { ForgotPasswordPage } from '@/pages/forgot-password' import { LoginPage } from '@/pages/login' import { LoginCodePage } from '@/pages/login-code' import { LoginVerifyPage } from '@/pages/login-verify' import { NotFoundPage } from '@/pages/not-found' import { RegisterPage } from '@/pages/register' import { RegisterVerifyPage } from '@/pages/register-verify' import { ResetCompletePage } from '@/pages/reset-complete' import { ResetPasswordPage } from '@/pages/reset-password' import { ResetSentPage } from '@/pages/reset-sent' import { AuthError } from '@/shared/api' import { routes } from '@/shared/config' import { t, useLocale } from '@/shared/i18n' import { SessionCheck } from '@/shared/ui' import { AppShell } from '@/widgets/app-shell' function AuthLayout() { const { pathname } = useLocation() const locale = useLocale() useEffect(() => { document.documentElement.lang = locale document.querySelector('meta[name="description"]')?.setAttribute('content', t('app.description')) }, [locale]) // biome-ignore lint/correctness/useExhaustiveDependencies: locale 变化时需要按当前标题重算 document.title useEffect(() => { const heading = document.querySelector('h1') document.title = `${heading?.textContent || t('app.title')} · GitDataAI` heading?.focus({ preventScroll: true }) if (pathname !== routes.loginVerify) mfaChallenge.reset() if (pathname !== routes.register && pathname !== routes.registerVerify) registrationDraft.reset() if (pathname === routes.resetPassword && new URLSearchParams(location.search).has('token')) { const url = new URL(location.href) url.searchParams.delete('token') history.replaceState(null, '', `${url.pathname}${url.search}${url.hash}`) } }, [pathname, locale]) return ( ) } function LoginEntry({ children }: { children: ReactNode }) { const [unauthenticated, setUnauthenticated] = useState(false) const navigate = useNavigate() const { user, loading, task } = useSession({ onUnauthenticated: () => setUnauthenticated(true) }) useEffect(() => { if (user) navigate(routes.account, { replace: true }) }, [user, navigate]) if (unauthenticated) return <>{children} return location.reload()} /> } function useLoginComplete(): LoginComplete { const navigate = useNavigate() return (response) => { if (response.mfa_required) { if (!response.challenge) throw new AuthError('challenge', t('mfa.missingChallenge')) mfaChallenge.set(response.challenge) navigate(routes.loginVerify) return } mfaChallenge.reset() navigate(routes.account, { replace: true }) } } function LoginRoute() { const onComplete = useLoginComplete() return ( ) } function LoginCodeRoute() { const onComplete = useLoginComplete() return ( ) } function LoginVerifyRoute() { const onComplete = useLoginComplete() return } function RegisterRoute() { const navigate = useNavigate() return navigate(routes.registerVerify)} /> } function RegisterVerifyRoute() { const navigate = useNavigate() return navigate(routes.account, { replace: true })} /> } function ForgotPasswordRoute() { const navigate = useNavigate() return navigate(routes.resetSent)} /> } function ResetPasswordRoute() { const navigate = useNavigate() return navigate(routes.resetComplete, { replace: true })} /> } export const router = createBrowserRouter([ { path: routes.home, element: , children: [ { index: true, element: }, { path: routes.login, element: }, { path: routes.loginCode, element: }, { path: routes.loginVerify, element: }, { path: routes.register, element: }, { path: routes.registerVerify, element: }, { path: routes.forgotPassword, element: }, { path: routes.resetSent, element: }, { path: routes.resetPassword, element: }, { path: routes.resetComplete, element: }, { path: routes.account, element: }, ], }, { path: '*', element: , children: [{ index: true, element: }], }, ])