sync: auto-sync from GURU-5070 at 2026-05-30 14:26:39

Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-05-30 14:26:39
This commit is contained in:
2026-05-30 14:26:48 -07:00
parent 56ce575ede
commit c441bba0a2

View File

@@ -183,3 +183,48 @@ The Gitea Agent committed the work inside the guru-connect submodule and pushed
- **Parent pointer commit (claudetools, local, unpushed):** `5fce962`. - **Parent pointer commit (claudetools, local, unpushed):** `5fce962`.
- **Dashboard root:** projects/msp-tools/guru-connect/dashboard/ — React+Vite+TS, dev Vite proxy /api,/ws -> localhost:3002. - **Dashboard root:** projects/msp-tools/guru-connect/dashboard/ — React+Vite+TS, dev Vite proxy /api,/ws -> localhost:3002.
- **Coord:** guruconnect/dashboard component = built; Phase 2 dashboard lock e783feb4 released. - **Coord:** guruconnect/dashboard component = built; Phase 2 dashboard lock e783feb4 released.
---
## Update: 14:25 PT — GuruConnect v2 dashboard completed + SPA serving wired
### Session Summary (this update)
Completed the GuruConnect v2 operator dashboard, taking it from one view (Machines) to feature-complete. Built three more views and wired production serving, each following the standard sequence: explore the real v2 server API -> Coding Agent build on the established primitives -> Code Review (Opus, security-weighted) -> Gitea commit -> coord lock release + component update + server-gap todos. All work is committed and pushed to the guru-connect submodule; the claudetools parent carries local submodule-pointer commits.
Order of work: (1) Sessions view — active-session table with consent-state badges, a viewer-token Join that mints a session-scoped token and reveals it with the relay URL (the agent correctly refused to target the v1 viewer.html, which sends the raw login JWT the v2 plane rejects), and disconnect. (2) Production-serving wiring — Axum now serves the React SPA at / with a deep-link fallback, with /api/*rest and /ws/*rest returning JSON 404 so unrouted API/WS paths never leak index.html; the dead v1 portal HTML and handlers were removed (user confirmed nothing is live on the server). (3) Support Codes view — generate (copy-once reveal modal, ref-guarded to one code per open), list, cancel. (4) Users admin view — admin-gated three ways, real roles/permissions, Web Crypto password generation with copy-once reveal, and self-lockout guards.
Code Review found and gated real bugs across the passes: the Support Codes cancel endpoint returns 200 + plain-text "Code cancelled" which the shared client tried to JSON.parse (made cancel report success as failure) — fixed by making the client tolerant of non-JSON success bodies; and the generate-on-open effect created two real single-use codes per open under StrictMode — fixed with a ref guard. The Users view had a partial-update edge (failed permissions-set left the table stale) fixed by moving invalidation to onSettled with an actionable error message.
The dashboard view set is now complete (Machines, Sessions, Support Codes, Users admin) plus SPA serving. Everything is built and CI-clean but NOT deployed. The decision was made to gitignore the built SPA (server/static/app/) and build it at deploy time, and to keep sourcemaps. The next action is the v2 deploy/cutover (separate from this save).
### Key Decisions (this update)
- SPA artifact gitignored (server/static/app/), built at deploy time via npm ci && npm run build — avoids minified-bundle churn during active dashboard dev. Vite base "/" (absolute asset paths, required for BrowserRouter deep-link reloads), outDir ../server/static/app, emptyOutDir true (only clears app/, never the static root).
- v1 portal removed now rather than deferred to cutover, because the user confirmed nothing is live on the server to preserve and no active connections to drain. Deleted login/dashboard/users/index/viewer .html and the dead serve_* handlers.
- Sessions Join reveals a session-scoped viewer token instead of opening the static viewer.html (which is incompatible with the v2 viewer plane). In-dashboard web viewer deferred to Phase 2 proper.
- Support Codes shows no expiry countdown because the API's serialized SupportCode drops expires_at (only the DbSupportCode row has it). The agent refused to fake a countdown.
- Users self-demotion is guarded client-side only (server blocks self-disable/self-delete but not self-role-demotion). Filed as a server todo.
- Sourcemaps kept on the internet-facing relay (user choice).
### Problems Encountered (this update)
- Support Codes cancel surfaced success as failure: server returns 200 with plain-text body, shared client JSON.parse threw SyntaxError. Fixed in api/client.ts (try/catch around success-body parse; error-envelope path untouched). Re-reviewed, no regression.
- StrictMode double-mint: generate-on-open created two durable single-use codes per open. Fixed with a mintedFor ref that re-arms on close.
- Users partial-update: a failed permissions-set after a successful user-update left the table showing the stale row with a misleading error. Fixed: invalidate on onSettled, and a "User saved, but permissions update failed - retry permissions" message (required broadening EditUserModal onError to surface plain Error.message).
- Code Review flagged a malformed CSS comment (table.css:89) on an intermediate dashboard pass-1 state; the committed file was correct (grep confirmed) — false alarm, no edit.
### Configuration Changes (this update)
- guru-connect submodule commits: 67f3722 (SPA serving + v1 removal), 664f33d (Support Codes view), 96b4fd7 (Users admin view); earlier this day 43a9432 (dashboard pass 1) and 6ecb937 (Sessions view).
- New dashboard features: src/features/{sessions,codes,users}/, src/auth/AdminRoute.tsx, src/api/{sessions,codes,users}.ts, plus additions to src/components/ui/status.ts, src/components/layout/{icons.tsx,Sidebar.tsx}, src/App.tsx, src/api/{types.ts,index.ts,client.ts,stubs.ts}.
- Server: server/src/main.rs (SPA serving + /api,/ws JSON-404 guards + downloads nest + v1 handler removal), server/src/middleware/security_headers.rs (path-aware cache-control), dashboard/vite.config.ts (base/outDir/emptyOutDir), dashboard/README.md, guru-connect .gitignore (/server/static/app/).
- Deleted: server/static/{login,dashboard,users,index,viewer}.html.
### Pending / Incomplete Tasks (this update)
- NEXT: deploy the v2 stack (cutover). Steps: build Linux server binary (x86_64-unknown-linux-gnu, on the Linux build host — GURU-5070 builds the Windows agent/server, not the Linux release), run migrations 004_v2_secure_session_core / 005_machine_metadata / 006_widen_support_code, set env (CONNECT_TRUSTED_PROXIES, JWT_SECRET, DATABASE_URL), npm ci && npm run build the SPA into server/static/app, restart via start-server.sh, smoke test. Nothing live to preserve = zero cutover risk.
- claudetools parent is ahead of origin by the submodule-pointer commits (this /save pushes them).
- Server todos filed (guruconnect): support-code expires_at serialization; users authz gaps (self-demotion guard, last-admin protection, password-reset-reveal, username edit, /clients endpoint). Earlier open: viewer-token revocation on logout (9a462965), multi-instance DB single-use gate (542137df), agent clippy in CI (addd7eea), H.264 go-live gating.
- Dashboard later: in-dashboard web viewer (Phase 2 proper) + file transfer.
### Reference Information (this update)
- **guru-connect submodule HEAD:** 96b4fd7 (pushed to internal Gitea http://172.16.3.20:3000/azcomputerguru/guru-connect).
- **Dashboard:** projects/msp-tools/guru-connect/dashboard/ (React+Vite+TS). Build emits to ../server/static/app/ (gitignored). Dev Vite proxy /api,/ws -> localhost:3002.
- **Server SPA serving:** Axum fallback ServeDir(server/static/app).fallback(index.html); /api/*rest + /ws/*rest -> JSON 404; /downloads nested; /health,/metrics intact. axum 0.7.9, tower-http 0.6.11.
- **Coord:** guruconnect/dashboard component = built (view set complete); all session locks released.