Generate, list, and cancel attended-support codes (XXX-XXX-XXX), built
on the v2 codes API and existing UI primitives.
- Codes table: code in mono, status badge (pending+pulse/connected/
completed/cancelled), bound client/machine, created-by, created
(relative + absolute tooltip). Sticky header, skeleton load,
actionable empty/error states.
- Generate opens a focused reveal modal showing the code large in
JetBrains Mono with copy and a read-aloud instruction; the code is
announced character-by-character for screen readers. Mint is ref-
guarded so it creates exactly one code per open (no StrictMode dupe).
- Cancel via confirm dialog (POST /api/codes/:code/cancel), disabled for
non-cancellable statuses; invalidates the codes query. List polls 7s.
- Shared API client now tolerates non-JSON 200 bodies, so the cancel
endpoint's plain-text "Code cancelled" success no longer surfaces as a
failure. Error-envelope handling unchanged.
Passed Code Review (no blockers after fixes) and local gates
(tsc/lint/build green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Axum now serves the v2 React/Vite dashboard SPA at / with a client-side
routing fallback, and the dead v1 HTML portal is removed (nothing was
live on the server to preserve).
- SPA served from server/static/app via ServeDir with a fallback to
index.html, so deep links (/machines, /sessions) resolve to the SPA.
- /api/*rest and /ws/*rest return JSON 404 so unrouted API/WS paths never
leak index.html to clients; real /api, /ws, /health, /metrics, and the
/downloads nest keep precedence (matchit static-over-wildcard).
- Path-aware Cache-Control: hashed /assets immutable, index.html no-cache.
- Vite builds to server/static/app (base /); the artifact is gitignored
and rebuilt at deploy time (npm ci && npm run build).
- Removed v1 portal files (login/dashboard/users/index/viewer .html) and
their dead serve_* handlers; the SPA owns /, /login, /dashboard, /users.
Verified locally: server boots, / and deep links serve the SPA, unknown
/api path returns JSON 404 (not HTML), /health and /downloads intact.
cargo build + clippy -D warnings green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Active-sessions table with consent-state badges, viewer-token Join,
and disconnect, built on the v2 session API and existing UI primitives.
- Sessions table: machine, mode (managed/attended), consent badge
(granted/pending+pulse/denied/not_required), viewers, started,
duration, status. Sticky header, skeleton load, empty/error states.
- Join action mints a session-scoped viewer token
(POST /api/sessions/:id/viewer-token) and reveals it with the
/ws/viewer relay URL and copy buttons. The static viewer.html is
intentionally not targeted: it sends the raw login JWT, which the v2
viewer plane rejects. In-dashboard web viewer ships in a later pass.
- Authz split mirrors the server mint gate: admin or control permission
gets Control; view permission gets View only; neither hides the action.
Server remains authoritative; the minted token carries the signed
access claim.
- Disconnect via confirm dialog (DELETE /api/sessions/:id), invalidates
the sessions query. List polls every 8s so consent transitions surface.
Passed Code Review (no blockers) and local gates (tsc/lint/build green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>