A Vite + React 18 + TypeScript SPA, same-origin with the API (no CORS, no Go server changes). Full management parity with the urapt CLI: - First-run setup (admin bootstrap via needs_setup), login/logout - Repositories: list, create, tabbed detail (overview edit/delete, members CRUD, distributions/components/architectures manager, package list + .deb upload + detail + delete, apt-config panel) - API tokens: show-once creation + revoke - Admin users: toggle admin / delete - Settings + dark mode Stack: Tailwind + shadcn/ui (New York), React Router v7 (lazy routes), TanStack Query v5, Zustand, Vitest. Hand-written typed API client mirroring shared/api + shared/models. Dev proxy forwards /api + /apt to a local urapt-server; deploy behind nginx/Caddy (see urapt-web/README).
36 lines
961 B
TypeScript
36 lines
961 B
TypeScript
import { create } from "zustand";
|
|
import { persist } from "zustand/middleware";
|
|
import type { User } from "@/lib/api/types";
|
|
|
|
const STORAGE_KEY = "urapt.auth";
|
|
|
|
interface AuthState {
|
|
token: string | null;
|
|
user: User | null;
|
|
setSession: (token: string, user: User) => void;
|
|
setUser: (user: User | null) => void;
|
|
clear: () => void;
|
|
}
|
|
|
|
export const useAuthStore = create<AuthState>()(
|
|
persist(
|
|
(set) => ({
|
|
token: null,
|
|
user: null,
|
|
setSession: (token, user) => set({ token, user }),
|
|
setUser: (user) => set({ user }),
|
|
clear: () => set({ token: null, user: null }),
|
|
}),
|
|
{
|
|
name: STORAGE_KEY,
|
|
// Only persist the token; user is re-fetched on boot via GET /me.
|
|
partialize: (s) => ({ token: s.token }) as AuthState,
|
|
},
|
|
),
|
|
);
|
|
|
|
/** Synchronous token getter for the API client (avoids React render cycles). */
|
|
export function getToken(): string | null {
|
|
return useAuthStore.getState().token;
|
|
}
|