ui

package
v1.14.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 31, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

README

longue-vue UI

Vite + React + TypeScript SPA. Served by longue-vue at /ui/ in production via go:embed; run standalone during development.

See ADR-0006 for scope and rationale.

What it does today

  • List pages for all nine top-level kinds — Clusters, Namespaces, Nodes, Workloads, Pods, Services, Ingresses, PersistentVolumes, PersistentVolumeClaims — each with kind-specific columns (Node role / zone / instance-type / CPU-mem / Ready status; Ingress / Service load-balancer address; PVC bound PV; Workload container images; etc.).
  • Detail pages for the core drill-down chain:
    • Cluster → namespaces + nodes + PVs inside it.
    • Namespace → workloads + pods + services + ingresses + PVCs ("application = namespace" view).
    • Workload → its pods (via workload_id), unique nodes they run on, container template ("application = workload" view).
    • Pod → containers with image_id digests + backlinks to parent workload / namespace.
    • Node → full enriched view (Identity, OS & runtime, Networking, capacity + allocatable table, Conditions with per-row health colouring, Taints, Labels) plus an impact-analysis callout and a workload-grouped pod breakdown.
    • Ingress → load-balancer block first (MetalLB / Kube-VIP / hardware LB VIP), then routing rules and TLS.
  • Component search (/ui/search/image) — case-insensitive substring match across every container's image string on both Workloads and Pods (init containers included). Query is kept in the URL so auditors can bookmark or share a hit list ("every asset running log4j:2.15").
  • Auth — paste a bearer token on /ui/login, held in sessionStorage (cleared on tab close, per ADR-0006). No cookies, no CORS — the SPA is same-origin as the API.

Development loop

# 1. Once per checkout (or after dep bumps):
make ui-install

# 2. Terminal A — start longue-vue (API + embedded production bundle):
LONGUE_VUE_DATABASE_URL=postgres://... LONGUE_VUE_API_TOKEN=dev ./bin/longue-vue

# 3. Terminal B — Vite dev server with HMR, proxies /v1 /healthz /metrics -> :8080:
make ui-dev
# → open http://localhost:5173/ui/

For a one-command demo with seeded data, run bash scripts/seed-demo.sh after longue-vue is up — it populates a realistic prod / staging inventory so every list page has something to show.

Production build

make ui-build      # produces ui/dist/
make build         # longue-vue binary embeds ui/dist via //go:embed

Tests

The UI is covered by a Vitest suite — foundation tests over api.ts, hooks.ts, kv.ts, me.tsx, and shared components, plus render-level smoke tests for every page. The network is mocked with MSW; default handlers live in src/test/handlers.ts and tests override them per-case.

make ui-test         # one-shot, used by CI and `make check`
cd ui && npm run test:watch     # interactive
cd ui && npm run test:coverage  # writes coverage/ HTML report

Test files are co-located next to the unit they cover (foo.tsx → foo.test.tsx). Shared infrastructure (MSW server, fixtures, render helper) lives in src/test/.

Skipping the UI

Backend-only workflows (no Node toolchain) can use make build-noui. /ui/ then replies 404; /v1/* is unaffected.

Layout

ui/
├── embed.go              # //go:embed all:dist (default build)
├── embed_noui.go         # stub under -tags noui
├── vite.config.ts        # base: '/ui/' + dev proxy to longue-vue
├── tsconfig.json
├── package.json
├── index.html
└── src/
    ├── main.tsx          # router + strict mode
    ├── App.tsx           # routes + auth gate + chrome
    ├── api.ts            # hand-written typed fetch wrapper
    ├── hooks.ts          # useResource / useResources (loading/error/data)
    ├── components.tsx    # shared primitives: AsyncView, LayerPill,
    │                     # LoadBalancerAddresses, Labels, KV, IdLink, …
    ├── styles.css        # dark theme, status pills, LB chips, kv-list grid
    └── pages/
        ├── Login.tsx     # bearer token -> sessionStorage
        ├── Lists.tsx     # all nine list pages in one file
        ├── Details.tsx   # Cluster / Namespace / Workload / Pod / Node / Ingress detail
        └── Search.tsx    # image-substring search (workloads + pods)

Design choices

  • Hand-written API client (src/api.ts) over OpenAPI codegen — the surface is still small and the hand-written form is easier to skim for now. Swap to codegen (openapi-typescript-codegen or similar) when the number of endpoints makes drift likely.
  • List and detail pages in two files (Lists.tsx, Details.tsx) instead of one-file-per-page. Keeps imports and shared helpers (NamespaceLink, useNamespaceIndex) close to their call sites; every page stays small enough that a single file scan is fine.
  • useResource / useResources hooks fold the loading / error / data tri-state plus the "401 → drop token → /login" rule in one place so every page reads like a straight render.
  • AsyncView nesting instead of Promise.all in every page — a top- level useResources(() => [...]) works when all fetches share the same input keys, but when a second fetch depends on the first's result (NamespaceDetail → Cluster, Node → pods filtered by node.name) a chain of useResource is clearer than building a reducer.

Documentation

Overview

Package ui owns the embedded SPA bundle served by longue-vue under /ui/.

Two build modes:

  • default: //go:embed pulls ui/dist into the binary; Handler() serves it. `npm run build` (see Makefile: make ui-build) must have produced dist/ before `go build` runs. CI does this automatically.
  • -tags noui: compiles embed_noui.go instead, returning a stub handler that replies 404. Lets backend-only workflows skip the Node toolchain.

index.html is rewritten with a small shim that rewrites asset URLs so the bundle works at /ui/. See vite.config.ts `base: '/ui/'`.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Handler

func Handler() http.Handler

Handler returns an http.Handler that serves the embedded SPA bundle. Intended to be mounted at /ui/ by the main mux:

mux.Handle("/ui/", http.StripPrefix("/ui", ui.Handler()))

Unknown paths under /ui/ fall back to index.html so client-side routing works on page reload (e.g. /ui/clusters → served index.html → React Router renders the Clusters page).

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL