// auth-gate.jsx - owns the "logged in or not" state. // // The SPA boots into , which calls GET /auth/me. On 401 it then // calls GET /auth/setup-required and renders or // (defined in screens-auth.jsx, Task 16). On 200 it renders the real . // // This component is the SINGLE source of truth for the auth check - no other // component should redirect to a login page or wipe data on 401. Other code // surfaces auth failure by calling window.AuthGate.bounce(), which re-mounts // the gate so the next /auth/me request decides what to do. (function () { const API = '/api/v1'; const LAST_PATH_KEY = 'df.auth.last_path'; async function authFetch(path, opts) { return fetch(API + path, { credentials: 'include', ...opts, headers: { ...(opts && opts.headers), 'Content-Type': 'application/json', ...((opts && opts.method && opts.method !== 'GET') ? { 'X-Requested-With': 'dragonflight-ui' } : {}), }, }); } function AuthGate({ children }) { const [state, setState] = React.useState({ kind: 'loading' }); const check = React.useCallback(async () => { setState({ kind: 'loading' }); try { const r = await authFetch('/auth/me'); if (r.status === 200) { const me = await r.json(); window.ZAMPP_DATA = window.ZAMPP_DATA || {}; window.ZAMPP_DATA.ME = me; setState({ kind: 'authed' }); return; } } catch (_) { /* fall through to setup/login decision */ } const setup = await authFetch('/auth/setup-required').then(r => r.json()).catch(() => ({ required: false })); setState({ kind: setup.required ? 'setup' : 'login' }); }, []); React.useEffect(() => { check(); }, [check]); // Global hook: anything else (e.g. data.jsx 401 handler) can re-trigger // the gate after auth state changes server-side. Saves current path so // the user is restored to where they were after login. React.useEffect(() => { window.AuthGate = { bounce(reason) { try { sessionStorage.setItem(LAST_PATH_KEY, location.pathname + location.search); } catch {} if (reason) console.warn('[AuthGate] bounce:', reason); check(); }, signedIn() { check(); }, }; }, [check]); if (state.kind === 'loading') { return (
Loading…
); } if (state.kind === 'setup') return React.createElement(window.SetupScreen, { onDone: () => window.AuthGate.signedIn() }); if (state.kind === 'login') return React.createElement(window.LoginScreen, { onDone: () => window.AuthGate.signedIn() }); return children; } window.AuthGate = window.AuthGate || {}; window.AuthGateComponent = AuthGate; })();