import { createContext, useContext } from "react"; import type { User } from "@/types"; /** * Tri-state lifecycle for the auth surface. * * - "loading" → we haven't asked /api/auth/me yet, or the result * is still in flight. Route guards render a * placeholder so we don't bounce the user to * /login before the cookie has had a chance to * prove itself. * - "authenticated" → /me returned a real User. The rest of the app * can safely render protected surfaces. * - "unauthenticated"→ /me failed (no cookie, expired cookie, server * down). Route guards redirect to /login. */ export type AuthStatus = "loading" | "authenticated" | "unauthenticated"; export interface AuthContextValue { status: AuthStatus; user: User | null; /** * Authenticate against /api/auth/login. Throws `ApiError` on failure * (with `code` = "invalid_credentials" | "account_disabled" | * "rate_limited" | "error"). On success, status flips to * "authenticated" and `user` is populated. */ login: (username: string, password: string) => Promise; /** * Hit /api/auth/logout and clear local state. Always succeeds * locally — even if the server is unreachable, the operator is * effectively signed out from the SPA's perspective. */ logout: () => Promise; /** * Re-run the /api/auth/me probe. Useful after a profile edit on the * admin pages so the sidebar reflects the new role without a * full reload. */ refresh: () => Promise; } export const AuthContext = createContext(null); /** * Read the current auth context. Throws when used outside of an * `` so consumers fail loudly during development instead * of silently rendering the unauthenticated branch. */ export function useAuth(): AuthContextValue { const ctx = useContext(AuthContext); if (!ctx) { throw new Error("useAuth must be used inside "); } return ctx; }