SPB Git forge

spb/spinza

Public
8commits 1branches 0releases
1.6 MBsize
maindefault branch
16 days agolast push
TypeScript 97.6% SQL 1.4% JavaScript 0.5%
2.7 KB · 76 lines typescript
Raw Blame History
1"use client";23import { create } from "zustand";4import { api, ApiClientError } from "@/lib/api";5import type { AdminIdentity, MaintenanceSetting, SettingsResponse, SystemResponse } from "./types";67export type GateStatus = "loading" | "authed" | "unauthed" | "forbidden" | "error";89interface AdminState {10  status: GateStatus;11  admin: AdminIdentity | null;12  error: string | null;13  maintenance: MaintenanceSetting | null;14  node: string | null;15  version: string | null;16  /** GET /api/admin/me → decides which screen the gate renders. */17  check: () => Promise<void>;18  signedIn: (admin: AdminIdentity) => void;19  /** Called by the query hook on any 401. */20  signedOut: () => void;21  signOut: () => Promise<void>;22  /** Top-bar status: maintenance pill + node/version. */23  loadStatus: () => Promise<void>;24  setMaintenance: (m: MaintenanceSetting) => void;25}2627export const useAdmin = create<AdminState>((set, get) => ({28  status: "loading",29  admin: null,30  error: null,31  maintenance: null,32  node: null,33  version: null,34  check: async () => {35    try {36      const r = await api<{ admin: AdminIdentity }>("/api/admin/me");37      set({ status: "authed", admin: r.admin, error: null });38      void get().loadStatus();39    } catch (e) {40      if (e instanceof ApiClientError) {41        if (e.status === 401) set({ status: "unauthed", admin: null, error: null });42        else if (e.status === 403) set({ status: "forbidden", admin: null, error: e.message });43        else set({ status: "error", admin: null, error: e.message });44      } else {45        set({ status: "error", admin: null, error: "Unexpected error." });46      }47    }48  },49  signedIn: (admin) => {50    set({ status: "authed", admin, error: null });51    void get().loadStatus();52  },53  signedOut: () => set({ status: "unauthed", admin: null, maintenance: null }),54  signOut: async () => {55    await api("/api/admin/auth/logout", { method: "POST" }).catch(() => {});56    get().signedOut();57  },58  loadStatus: async () => {59    const [settings, system] = await Promise.allSettled([api<SettingsResponse>("/api/admin/settings"), api<SystemResponse>("/api/admin/system")]);60    const patch: Partial<AdminState> = {};61    if (settings.status === "fulfilled") {62      const m = settings.value.settings["maintenance"] as MaintenanceSetting | undefined;63      patch.maintenance = m && typeof m.enabled === "boolean" ? m : { enabled: false, message: "" };64    }65    if (system.status === "fulfilled") {66      const svc = system.value.services.find((s) => s.name === "spinza-api") ?? system.value.services[0];67      if (svc) {68        patch.node = svc.node;69        patch.version = svc.version;70      }71    }72    set(patch);73  },74  setMaintenance: (m) => set({ maintenance: m }),75}));76