"use client"; import React from "react"; import { useRouter } from "next/navigation"; import { getMe } from "@/lib/frontend/apiClient"; import { buildLoginUrl, LOGIN_REASONS } from "@/lib/frontend/authRedirect"; import { AuthProvider as AuthContextProvider } from "@/components/auth/authContext"; /** * AuthProvider (RHL-020) * * Responsibilities: * - Run a session check via GET /api/auth/me. * - Store the result in AuthContext. * - Redirect to /login when unauthenticated (reason=expired, next=current URL). * * Important UX improvement: * - We no longer render full-screen "solo spinners" here. * - The AppShell stays visible and AuthGate renders the loading/error UI inside main content. * * Important performance/UX improvement: * - We do NOT re-check the session on every route change. * - The session check runs: * - once on mount * - and again only when the user hits "retry" * * @param {{ children: React.ReactNode }} props */ export default function AuthProvider({ children }) { const router = useRouter(); // Prevent double redirects in quick re-renders. const didRedirectRef = React.useRef(false); // Auth state exposed via context. const [auth, setAuth] = React.useState({ status: "loading", user: null, error: null, }); // Retry tick triggers a refetch without tying auth checks to route changes. const [retryTick, setRetryTick] = React.useState(0); const retry = React.useCallback(() => { setRetryTick((n) => n + 1); }, []); React.useEffect(() => { let cancelled = false; async function runSessionCheck() { setAuth({ status: "loading", user: null, error: null }); try { const res = await getMe(); if (cancelled) return; if (res?.user) { // Authenticated session. didRedirectRef.current = false; setAuth({ status: "authenticated", user: res.user, error: null }); return; } // Unauthenticated session (frontend-friendly endpoint returns 200 + user:null). setAuth({ status: "unauthenticated", user: null, error: null }); if (!didRedirectRef.current) { didRedirectRef.current = true; // Preserve the current URL as "next" so the user returns to the same page after login. const next = typeof window !== "undefined" ? `${window.location.pathname}${window.location.search}` : "/"; const loginUrl = buildLoginUrl({ reason: LOGIN_REASONS.EXPIRED, next, }); router.replace(loginUrl); } } catch (err) { if (cancelled) return; setAuth({ status: "error", user: null, error: "Sitzung konnte nicht geprüft werden. Bitte erneut versuchen.", }); } } runSessionCheck(); return () => { cancelled = true; }; }, [router, retryTick]); return ( {children} ); }