Close DV-01–04,07,09,13,14: unify operator-deck UX in server/web.

Standardize neon cyan on #00e8f5, wire SacredPageHeader across Builds/Path Tracer/LOTL, dedupe Pages.css, align Path Tracer chrome, drop dead wealth-deck CSS, add prefers-color-scheme shell + HelpTip, sidebar version from package.json build inject, and Vitest CSS regression guards.
This commit is contained in:
AetherForge
2026-06-07 06:33:51 -07:00
parent 85d55df37c
commit 74c006a04b
35 changed files with 1144 additions and 36 deletions

View File

@@ -102,10 +102,112 @@ func NewPathTracerHandler(hub *WSHub) *PathTracerHandler {
sessions: make(map[string]*TraceSession),
stopCh: make(chan struct{}),
}
h.loadPersistedSessions()
go h.sessionCleanupLoop()
return h
}
// pathTraceSessionPersist is the SQLite JSON shape (includes WG client keys for QR after restart).
type pathTraceSessionPersist struct {
ID string `json:"id"`
AgentIDs []string `json:"agent_ids"`
Hops []*HopInfo `json:"hops"`
Ready bool `json:"ready"`
Error string `json:"error,omitempty"`
CreatedAt time.Time `json:"created_at"`
ServiceGraph map[string]ServiceGraphHost `json:"service_graph,omitempty"`
DiscoverInProgress bool `json:"discover_in_progress,omitempty"`
DiscoverError string `json:"discover_error,omitempty"`
DiscoveredAt *time.Time `json:"discovered_at,omitempty"`
NetworkHints json.RawMessage `json:"network_hints,omitempty"`
ClientPrivKey string `json:"client_priv_key,omitempty"`
ClientPubKey string `json:"client_pub_key,omitempty"`
}
func (h *PathTracerHandler) loadPersistedSessions() {
if h.hub == nil || h.hub.db == nil {
return
}
cutoff := time.Now().Add(-pathTraceSessionTTL)
if n, err := h.hub.db.DeleteExpiredPathTraceSessions(cutoff); err == nil && n > 0 {
log.Printf("[pathtrace] startup sweep removed %d expired session(s)", n)
}
rows, err := h.hub.db.ListPathTraceSessions()
if err != nil {
log.Printf("[pathtrace] load sessions failed: %v", err)
return
}
h.mu.Lock()
defer h.mu.Unlock()
for _, row := range rows {
if time.Since(row.CreatedAt) > pathTraceSessionTTL {
_ = h.hub.db.DeletePathTraceSession(row.ID)
continue
}
var rec pathTraceSessionPersist
if err := json.Unmarshal(row.Payload, &rec); err != nil {
_ = h.hub.db.DeletePathTraceSession(row.ID)
continue
}
h.sessions[rec.ID] = &TraceSession{
ID: rec.ID,
AgentIDs: rec.AgentIDs,
Hops: rec.Hops,
Ready: rec.Ready,
Error: rec.Error,
CreatedAt: rec.CreatedAt,
ServiceGraph: rec.ServiceGraph,
DiscoverInProgress: rec.DiscoverInProgress,
DiscoverError: rec.DiscoverError,
DiscoveredAt: rec.DiscoveredAt,
NetworkHints: rec.NetworkHints,
clientPrivKey: rec.ClientPrivKey,
clientPubKey: rec.ClientPubKey,
}
}
if len(rows) > 0 {
log.Printf("[pathtrace] restored %d session(s) from SQLite", len(h.sessions))
}
}
func (h *PathTracerHandler) persistSession(sess *TraceSession) {
if h.hub == nil || h.hub.db == nil || sess == nil {
return
}
h.mu.Lock()
rec := pathTraceSessionPersist{
ID: sess.ID,
AgentIDs: sess.AgentIDs,
Hops: sess.Hops,
Ready: sess.Ready,
Error: sess.Error,
CreatedAt: sess.CreatedAt,
ServiceGraph: sess.ServiceGraph,
DiscoverInProgress: sess.DiscoverInProgress,
DiscoverError: sess.DiscoverError,
DiscoveredAt: sess.DiscoveredAt,
NetworkHints: sess.NetworkHints,
ClientPrivKey: sess.clientPrivKey,
ClientPubKey: sess.clientPubKey,
}
h.mu.Unlock()
raw, err := json.Marshal(rec)
if err != nil {
log.Printf("[pathtrace] marshal session %s: %v", sess.ID[:min(8, len(sess.ID))], err)
return
}
if err := h.hub.db.UpsertPathTraceSession(sess.ID, sess.CreatedAt, raw); err != nil {
log.Printf("[pathtrace] persist session %s: %v", sess.ID[:min(8, len(sess.ID))], err)
}
}
func (h *PathTracerHandler) deletePersistedSession(id string) {
if h.hub == nil || h.hub.db == nil || id == "" {
return
}
_ = h.hub.db.DeletePathTraceSession(id)
}
func (h *PathTracerHandler) sessionCleanupLoop() {
ticker := time.NewTicker(pathTraceCleanupInterval)
defer ticker.Stop()
@@ -133,6 +235,7 @@ func (h *PathTracerHandler) expireSessions() {
for _, sess := range expired {
log.Printf("[pathtrace] session %s expired after %s", sess.ID[:8], pathTraceSessionTTL)
h.teardownHops(sess.Hops)
h.deletePersistedSession(sess.ID)
}
}
@@ -201,6 +304,7 @@ func (h *PathTracerHandler) Start(w http.ResponseWriter, r *http.Request) {
h.mu.Lock()
h.sessions[sess.ID] = sess
h.mu.Unlock()
h.persistSession(sess)
// Orchestrate asynchronously so the HTTP response returns quickly.
go h.orchestrate(sess)
@@ -301,6 +405,7 @@ func (h *PathTracerHandler) Delete(w http.ResponseWriter, r *http.Request) {
h.mu.Lock()
delete(h.sessions, id)
h.mu.Unlock()
h.deletePersistedSession(id)
writeJSON(w, map[string]interface{}{"ok": true})
}
@@ -777,6 +882,7 @@ func (h *PathTracerHandler) orchestrate(sess *TraceSession) {
}
log.Printf("[pathtrace] session %s: orchestration complete, ready=%v", sess.ID[:8], allReady)
h.persistSession(sess)
}
func (h *PathTracerHandler) teardownHops(hops []*HopInfo) {