Skip to content

feat: rebuild the web dashboard as a typed Preact app - #38

Merged
tuxevil merged 1 commit into
tuxevil:mainfrom
CyR1en:dashboard-ux-refresh
Oct 5, 2026
Merged

tuxevil merged 1 commit into
tuxevil:mainfrom
CyR1en:dashboard-ux-refresh

Conversation

@CyR1en

@CyR1en CyR1en commented Oct 3, 2026

Copy link
Copy Markdown
Contributor

What this does

Replaces the string-built HTML in src/dashboard.ts and the global-function JS and CSS in src/static/ (about 8.6k lines) with a TypeScript + Preact single-page app in src/web. esbuild, which already ships with tsx, bundles it in memory at startup, so there is still no build step.

The dashboard is now organized around one question: does routing work right now, and for how long?

Overview

Pages

  • Overview: routing state, a "Needs you" list where every item carries its fix (Restore, Re-enable, Reset breaker), and one row per quota pool with pooled tier-weighted quota, the serving account, next up, next reset and runway. Rows expand into the routing decisions that used to sit in the Routing Inspector modal.
  • Accounts: cards or a compact sortable list, with filters in the URL. Each account opens a drawer at /dashboard/accounts/<email> with its quota windows, routing decisions, health breakdown, budgets and every action.
  • Requests: a live tail of requests and rotator events, plus the PostgreSQL history with a payload inspector and per-key totals. Replaces Spend Logs.
  • Usage: a range-aware token chart (1h to 30d), savings by model, latency percentiles and the 60-day heatmap.
  • Virtual keys and Settings: key management, routing controls and policy, the benchmark and the configuration file.

Across pages: a ⌘K command palette, light and dark themes, phone layouts and the privacy mask (?mask=1). Every view, filter and open drawer is in the URL.

Model routing decisions Accounts
Model routing decisions Accounts
Account drawer Request history
Account drawer Request history
Usage Virtual keys
Usage Virtual keys

The screenshots come from npm run dashboard:dev, which serves the real dashboard against simulated accounts.

Behavior changes

  • Data API: new /api/dashboard/{snapshot,stream,usage,activity,usage/export}. The stream sends a snapshot, then only what changed. Countdowns are absolute timestamps, so an idle rotator sends nothing. /api/status and /api/events are unchanged.
  • Auth: ?token= links and a new sign-in screen exchange the admin token for an HttpOnly, SameSite=Strict session cookie, and the token leaves the URL. Cookie-only writes must be same-origin, sign-out revokes the session, and dashboard pages get a strict CSP. Tokens the old dashboard kept in localStorage are migrated once and deleted.
  • Token usage: bucket keys are now parsed as UTC. Before, hosts ahead of UTC lost their 1h and 6h charts and hour buckets never rolled up. All-time totals no longer skip buckets that are mid-rollup. On the first request after upgrading, hour buckets older than 61 days fold into monthly totals; all-time totals don't change.
  • Docker: the image sets TUXEVIL_ROTATOR_CONTAINER=1. Inside a container the proxy exposure warning becomes a note that points at the published port, instead of a warning on every install.
  • Routes: / redirects to /dashboard, and /dashboard/logs redirects to the request history. Behind a reverse proxy, forward the Host header or dashboard actions return 403.
  • Dependencies: adds preact and @preact/signals, and makes esbuild a direct dependency.

Testing

npm run check passes with 1031 tests passing, 0 failures and 2 skipped. Source, web and test typechecks pass. ESLint reports 0 errors.

New tests cover session auth, the live diff stream, usage windows and rollups, the dashboard routes, the exposure warning and the client view-model logic. They replace the old node:vm harness.

I also built the Docker image from this branch and ran it locally:

Check Result
Image build and startup bundle Bundle ready in under 100 ms (179 KB JS)
HTTP checks: routes, CSP, cookie flags, same-origin rule, SSE stream, exports, sign-out 45/45 pass
Every page in headless Chrome No console errors or CSP violations
docker stop and restart Exit 0 in under 1s; admin token and sessions persist
A real Antigravity account Token refresh, quota poll and routing state shown correctly

Docs

New docs/dashboard.md walks through every page with screenshots. CONTRIBUTING.md documents the dev server, and the README, API reference, deployment, troubleshooting and account guides are updated for the new dashboard.

Replace the string-built HTML in src/dashboard.ts and the global-function
JS/CSS in src/static/ (about 8.6k lines in all) with a TypeScript + Preact
single-page app in src/web, bundled in memory by esbuild at startup, so
there is still no separate build step.

- Shell: a top bar with live status, add account, search and a "Needs
  you" inbox; an icon rail that expands to labels; one floating canvas;
  a Cmd/Ctrl+K command palette; a profile menu with theme, privacy mask
  and sign out. Light and dark themes, and phone layouts where the rail
  is a slide-over sheet and the drawer a bottom sheet.
- Overview: routing state and account counts, an inline attention list
  where every item carries its fix, and one row per quota pool (pooled
  tier-weighted quota, the account serving it, next up with the policy's
  pick first, next reset, runway) that expands into routing decisions.
- Accounts: cards or a compact sortable list with filters in the URL,
  and a drawer at /dashboard/accounts/<email> with quota windows,
  routing decisions, the health breakdown, budgets and every action.
- Requests (live tail plus PostgreSQL history with a payload inspector
  and per-key totals), Usage (range-aware token chart, savings, latency,
  60-day heatmap), Virtual keys, and Settings (routing controls and
  policy, benchmark, configuration file, appearance).
- Data API: /api/dashboard/{snapshot,stream,usage,activity,usage/export}.
  The stream sends a snapshot and then only what changed; countdowns are
  absolute timestamps, so an idle rotator sends nothing. /api/status and
  /api/events are unchanged.
- Auth: ?token= links and a sign-in screen exchange the admin token for
  an HttpOnly, SameSite=Strict HMAC session cookie. Cookie-only writes
  must be same-origin (403 otherwise) and sign-out revokes the session.
  Strict CSP with no inline handlers or styles.
- Token usage: bucket keys are parsed as UTC, hours are kept 61 days for
  the heatmap, and all-time totals count every tier, since rollups move
  buckets rather than copy them.
- Docker: the image sets TUXEVIL_ROTATOR_CONTAINER=1, so the proxy
  exposure warning becomes a note that points at the published port,
  and the warning title names what is exposed.
- scripts/dashboard-dev.ts (npm run dashboard:dev) runs the real routes
  against a simulated rotator with scenarios.
- Docs: new docs/dashboard.md with screenshots from the dev server;
  README, CONTRIBUTING, API reference, deployment, troubleshooting and
  the other guides updated for the new dashboard.
- Tests for session auth, live diffing, usage windows and rollups,
  routes, exposure and the client view-model logic replace the node:vm
  harness.
@tuxevil

tuxevil commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Huge thanks for this incredible WebUI overhaul. The redesign is exceptionally thoughtful and polished, and it makes Tuxevil Rotator much clearer and more pleasant to use. I really appreciate the care and effort you put into this contribution. I found one small session-revocation edge case and will take care of that before merging.

tuxevil added a commit that referenced this pull request Oct 5, 2026
@tuxevil
tuxevil merged commit 163806e into tuxevil:main Oct 5, 2026
2 checks passed
tuxevil added a commit that referenced this pull request Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants