feat(auth): RequireAuth route guard
This commit is contained in:
@@ -0,0 +1,43 @@
|
|||||||
|
import { Navigate, useLocation } from "react-router-dom";
|
||||||
|
import { useAuth } from "./useAuth";
|
||||||
|
import type { ReactNode } from "react";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Route guard. Wrap any subtree that requires a logged-in operator:
|
||||||
|
*
|
||||||
|
* <Route element={<RequireAuth><Layout/></RequireAuth>}>
|
||||||
|
* <Route path="/" element={<Dashboard />} />
|
||||||
|
* ...
|
||||||
|
* </Route>
|
||||||
|
*
|
||||||
|
* Three branches, in priority order:
|
||||||
|
*
|
||||||
|
* 1. `status === "loading"` → render a centered "Loading…"
|
||||||
|
* placeholder so we don't bounce the user to /login before the
|
||||||
|
* cookie has had a chance to prove itself via /api/auth/me.
|
||||||
|
* 2. `status === "unauthenticated"` → redirect to
|
||||||
|
* `/login?next=<current path+search>`. The Login page reads
|
||||||
|
* `next` and bounces the operator back here on success.
|
||||||
|
* 3. otherwise → render children (status is
|
||||||
|
* "authenticated").
|
||||||
|
*/
|
||||||
|
export function RequireAuth({ children }: { children: ReactNode }) {
|
||||||
|
const { status } = useAuth();
|
||||||
|
const location = useLocation();
|
||||||
|
if (status === "loading") {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center text-muted-foreground text-sm">
|
||||||
|
Loading…
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (status === "unauthenticated") {
|
||||||
|
return (
|
||||||
|
<Navigate
|
||||||
|
to={`/login?next=${encodeURIComponent(location.pathname + location.search)}`}
|
||||||
|
replace
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <>{children}</>;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user