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).
40 lines
1.2 KiB
TypeScript
40 lines
1.2 KiB
TypeScript
import { Navigate } from "react-router-dom";
|
|
import { FullPageSpinner } from "@/components/common/spinner";
|
|
import { AppShell } from "@/components/app-shell";
|
|
import { useAuthStore } from "@/stores/auth";
|
|
import { useMe, useServerInfo } from "@/hooks/use-server";
|
|
|
|
/**
|
|
* Root guard: boots server info + current session before rendering the app
|
|
* shell. Redirects to /setup on first run and /login when unauthenticated.
|
|
*/
|
|
export function RootGuard() {
|
|
const token = useAuthStore((s) => s.token);
|
|
const serverInfo = useServerInfo();
|
|
const me = useMe(!!token);
|
|
|
|
if (serverInfo.isLoading || serverInfo.isPending) {
|
|
return <FullPageSpinner label="Connecting to urapt…" />;
|
|
}
|
|
|
|
// First-run setup takes priority: no admin exists yet.
|
|
if (serverInfo.data?.needs_setup) {
|
|
return <Navigate to="/setup" replace />;
|
|
}
|
|
|
|
if (!token) {
|
|
return <Navigate to="/login" replace />;
|
|
}
|
|
|
|
if (me.isLoading || me.isPending) {
|
|
return <FullPageSpinner label="Loading session…" />;
|
|
}
|
|
|
|
// Token present but invalid (401 already cleared the store; handle races).
|
|
if (me.isError || !me.data) {
|
|
return <Navigate to="/login" replace />;
|
|
}
|
|
|
|
return <AppShell />;
|
|
}
|