2026-09-19 21:50:06 +03:00
|
|
|
import { Navigate, Outlet, useLocation } from 'react-router';
|
|
|
|
|
|
|
|
|
|
import { ErrorNotice, LoadingNotice } from '@/components/ui/ErrorNotice';
|
|
|
|
|
import { useCurrentUser } from '@/lib/hooks';
|
2026-09-19 21:53:35 +03:00
|
|
|
import { isUnauthorized } from '@/lib/http';
|
2026-09-19 21:50:06 +03:00
|
|
|
|
|
|
|
|
interface AuthGuardProps {
|
|
|
|
|
/**
|
|
|
|
|
* Если `true`, пользователей с незавершённым онбордингом пускаем внутрь
|
|
|
|
|
* (нужно для /onboarding и /settings).
|
|
|
|
|
*/
|
|
|
|
|
allowIncompleteOnboarding?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Защита маршрутов: проверяет GET /users/@me.
|
|
|
|
|
* Неавторизованных отправляет на /login, не прошедших онбординг — на /onboarding.
|
|
|
|
|
*/
|
|
|
|
|
export function AuthGuard({ allowIncompleteOnboarding = false }: AuthGuardProps) {
|
|
|
|
|
const location = useLocation();
|
|
|
|
|
const currentUser = useCurrentUser();
|
|
|
|
|
|
|
|
|
|
if (currentUser.isPending) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex min-h-full items-center justify-center p-6">
|
|
|
|
|
<LoadingNotice />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentUser.isError) {
|
|
|
|
|
if (isUnauthorized(currentUser.error)) {
|
|
|
|
|
return <Navigate to="/login" replace state={{ from: location.pathname }} />;
|
|
|
|
|
}
|
|
|
|
|
return (
|
|
|
|
|
<div className="mx-auto flex min-h-full w-full max-w-md flex-col justify-center p-6">
|
|
|
|
|
<ErrorNotice error={currentUser.error} onRetry={() => void currentUser.refetch()} />
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const user = currentUser.data;
|
|
|
|
|
if (!allowIncompleteOnboarding && !user.onboarding_completed) {
|
|
|
|
|
return <Navigate to="/onboarding" replace />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return <Outlet context={{ user }} />;
|
|
|
|
|
}
|