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).
3.7 KiB
urapt-web
The web UI for urapt, a self-hostable
APT repository server. A React single-page app that talks to a urapt-server's
REST API (/api/v1) to manage repositories, members, distributions, packages,
tokens, and users — full management parity with the urapt CLI.
Stack
- Vite + React 18 + TypeScript
- Tailwind CSS + shadcn/ui (New York, neutral)
- React Router v7, TanStack Query v5, Zustand, Vitest
Requirements
- Node.js 20+ (see
.nvmrc) and pnpm 10+
Development
Start a local urapt-server (from the repo root):
make run-server # listens on 0.0.0.0:8080
Then in urapt-web/:
pnpm install
pnpm dev # http://localhost:5173
Vite proxies /api and /apt to http://localhost:8080 (override with the
URAPT_SERVER env var), so the UI is same-origin with the API in dev — no CORS
needed. If needs_setup is true (no users yet), you'll be sent to the first-run
setup screen to create the admin account.
Scripts
| Script | Description |
|---|---|
pnpm dev |
Start the Vite dev server |
pnpm build |
Type-check and build to dist/ |
pnpm preview |
Preview the production build locally |
pnpm typecheck |
Run tsc with no emit |
pnpm lint |
Run ESLint |
pnpm format |
Run Prettier (write) |
pnpm test |
Run Vitest once |
Configuration
| Env var | Default | Notes |
|---|---|---|
VITE_API_BASE_URL |
/api/v1 |
REST API base. Override only for cross-origin dev (requires CORS on the server). |
URAPT_SERVER |
http://localhost:8080 |
Dev proxy target only. |
Deployment (same-origin)
The UI is designed to be served from the same origin as a urapt-server,
behind a reverse proxy that serves the SPA from dist/ and forwards /api/v1
and /apt to urapt-server. No CORS, no server changes — the API base is just
/api/v1.
pnpm build # outputs dist/
Caddy
apt.example.com {
# SPA
root * /srv/urapt-web/dist
try_files {path} /index.html
file_server
# REST API + APT endpoint
reverse_proxy /api/* 127.0.0.1:8080
reverse_proxy /apt/* 127.0.0.1:8080
}
nginx
server {
listen 80;
server_name apt.example.com;
root /srv/urapt-web/dist;
location / { try_files $uri /index.html; }
location /api/ { proxy_pass http://127.0.0.1:8080; }
location /apt/ { proxy_pass http://127.0.0.1:8080; }
}
Architecture
src/
lib/api/ typed client mirroring urapt's shared/api + shared/models
(client, errors, endpoints, types)
stores/ Zustand auth store (persisted token)
hooks/ shared server/user queries, logout
components/ app shell, common UI (copy button, confirm, states), shadcn ui
features/ auth, setup, repos (tabs), tokens, users, settings
routes.tsx route tree with lazy-loaded pages + protected/admin guards
The API client (src/lib/api/) is hand-written and typed against urapt's Go
DTOs in shared/api/api.go and shared/models/models.go. A 401 anywhere
clears the local session and redirects to /login.
License
GPL-3.0-or-later, matching the urapt project.