feat(auth): AuthProvider context + useAuth hook
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
import { useEffect, useState, useCallback, type ReactNode } from "react";
|
||||
import { AuthContext, type AuthContextValue, type AuthStatus } from "./useAuth";
|
||||
import { authApi } from "./api";
|
||||
import type { User } from "@/types";
|
||||
|
||||
// Re-export useAuth so consumers can import both the context provider and
|
||||
// the hook from the same module. The hook itself lives in `./useAuth` so
|
||||
// the type definitions stay tree-shakeable independent of the provider.
|
||||
export { useAuth } from "./useAuth";
|
||||
|
||||
/**
|
||||
* Wraps the app and exposes the auth context. On mount it probes
|
||||
* /api/auth/me to decide whether the existing `cyclone_session`
|
||||
* cookie is still valid. Callers (route guard, sidebar, RoleGate,
|
||||
* Login page) read the resulting `status` + `user` via `useAuth()`.
|
||||
*
|
||||
* - "loading" → while the probe is in flight
|
||||
* - "authenticated" → probe succeeded; user populated
|
||||
* - "unauthenticated"→ probe failed; user is null
|
||||
*
|
||||
* `login` and `logout` mutate local state directly so the UI flips
|
||||
* without waiting for a second /me round-trip.
|
||||
*/
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
const [status, setStatus] = useState<AuthStatus>("loading");
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
setStatus("loading");
|
||||
try {
|
||||
const me = await authApi.me();
|
||||
setUser(me as User);
|
||||
setStatus("authenticated");
|
||||
} catch {
|
||||
setUser(null);
|
||||
setStatus("unauthenticated");
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void refresh();
|
||||
}, [refresh]);
|
||||
|
||||
const login = useCallback(async (username: string, password: string) => {
|
||||
const u = await authApi.login(username, password);
|
||||
setUser(u as User);
|
||||
setStatus("authenticated");
|
||||
}, []);
|
||||
|
||||
const logout = useCallback(async () => {
|
||||
await authApi.logout().catch(() => undefined);
|
||||
setUser(null);
|
||||
setStatus("unauthenticated");
|
||||
}, []);
|
||||
|
||||
const value: AuthContextValue = { status, user, login, logout, refresh };
|
||||
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
|
||||
}
|
||||
Reference in New Issue
Block a user