dorfteich/apps/web/src/auth/guards.tsx
Claude Fable 5 0bc80c9f93 Add auth, settings, and admin UI to the SPA
The web app grows its account surface: login (with next-redirect,
unverified-hint + resend), signup (react-hook-form + shared Zod
schemas, field-level api errors, closed-registration state fed by the
new public GET /auth/registration), e-mail verification, forgot/reset
password; a settings page with profile (locale applies immediately),
password change, and active-session management; a Site-Admin page for
instance name, default locale, and registration mode. AuthProvider
holds /auth/me, applies the profile locale, and backs route guards
(RequireAuth/RequireAnonymous/RequireSiteAdmin); the top bar gains a
user menu. All strings ship in the new auth/settings namespaces (de+
en); the exception filter now preserves handler-specific error codes.
Verified live: signup → Mailpit → verify → login → profile through
the Vite proxy.

Closes #16
Closes #17
Closes #18
Closes #19

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-05 05:35:56 +02:00

31 lines
1011 B
TypeScript

import { Navigate, Outlet, useLocation } from 'react-router-dom';
import { useAuth } from './auth-context';
/** Wraps private routes: anonymous visitors land on /login and return after. */
export function RequireAuth(): React.JSX.Element {
const { user, isLoading } = useAuth();
const location = useLocation();
if (isLoading) return <></>;
if (!user) {
const next = encodeURIComponent(location.pathname + location.search);
return <Navigate to={`/login?next=${next}`} replace />;
}
return <Outlet />;
}
/** Wraps auth pages: signed-in users have no business on /login etc. */
export function RequireAnonymous(): React.JSX.Element {
const { user, isLoading } = useAuth();
if (isLoading) return <></>;
if (user) return <Navigate to="/" replace />;
return <Outlet />;
}
export function RequireSiteAdmin(): React.JSX.Element {
const { user, isLoading } = useAuth();
if (isLoading) return <></>;
if (!user?.isSiteAdmin) return <Navigate to="/" replace />;
return <Outlet />;
}