diff --git a/audit/internal/webui/pages.go b/audit/internal/webui/pages.go index 76b1b5d..d9f3fa9 100644 --- a/audit/internal/webui/pages.go +++ b/audit/internal/webui/pages.go @@ -84,15 +84,16 @@ tbody tr:hover td{background:rgba(0,0,0,.03)} } func layoutNav(active string) string { - items := []struct{ id, icon, label string }{ - {"dashboard", "", "Dashboard"}, - {"metrics", "", "Metrics"}, - {"tests", "", "Acceptance Tests"}, - {"burn-in", "", "Burn-in"}, - {"network", "", "Network"}, - {"services", "", "Services"}, - {"export", "", "Export"}, - {"tools", "", "Tools"}, + items := []struct{ id, label, href string }{ + {"dashboard", "Dashboard", "/"}, + {"viewer", "Audit Snapshot", "/viewer"}, + {"metrics", "Metrics", "/metrics"}, + {"tests", "Acceptance Tests", "/tests"}, + {"burn-in", "Burn-in", "/burn-in"}, + {"network", "Network", "/network"}, + {"services", "Services", "/services"}, + {"export", "Export", "/export"}, + {"tools", "Tools", "/tools"}, } var b strings.Builder b.WriteString(``) return b.String() @@ -182,11 +179,6 @@ func renderDashboard(opts HandlerOptions) string { b.WriteString(``) b.WriteString(``) b.WriteString(``) - // Audit viewer iframe - b.WriteString(`
Audit Snapshot
`) - b.WriteString(``) - b.WriteString(`
`) - // Audit run output div b.WriteString(``) @@ -566,101 +558,6 @@ checkTools(); ` } -// ── Viewer (compatibility) ──────────────────────────────────────────────────── - -// renderViewerPage renders the audit snapshot as a styled HTML page. -// This endpoint is embedded as an iframe on the Dashboard page. -func renderViewerPage(title string, snapshot []byte) string { - var b strings.Builder - b.WriteString(``) - b.WriteString(`` + html.EscapeString(title) + ``) - b.WriteString(` -`) - if len(snapshot) == 0 { - b.WriteString(`

No audit snapshot available yet. Re-run audit from the Dashboard.

`) - b.WriteString(``) - return b.String() - } - - var data map[string]any - if err := json.Unmarshal(snapshot, &data); err != nil { - // Fallback: render raw JSON - b.WriteString(`
` + html.EscapeString(string(snapshot)) + `
`) - b.WriteString(``) - return b.String() - } - - // Collected at - if t, ok := data["collected_at"].(string); ok { - b.WriteString(`

Collected: ` + html.EscapeString(t) + `

`) - } - - // Hardware section - hw, _ := data["hardware"].(map[string]any) - if hw == nil { - hw = data - } - - renderHWCards(&b, hw) - - // Full JSON below - b.WriteString(`

Raw JSON

`) - pretty, _ := json.MarshalIndent(data, "", " ") - b.WriteString(`
` + html.EscapeString(string(pretty)) + `
`) - b.WriteString(``) - return b.String() -} - -func renderHWCards(b *strings.Builder, hw map[string]any) { - sections := []struct{ key, label string }{ - {"board", "Board"}, - {"cpus", "CPUs"}, - {"memory", "Memory"}, - {"storage", "Storage"}, - {"gpus", "GPUs"}, - {"nics", "NICs"}, - {"psus", "Power Supplies"}, - } - for _, s := range sections { - v, ok := hw[s.key] - if !ok { - continue - } - b.WriteString(`

` + s.label + `

`) - renderValue(b, v) - b.WriteString(`
`) - } -} - -func renderValue(b *strings.Builder, v any) { - switch val := v.(type) { - case []any: - for _, item := range val { - renderValue(b, item) - } - case map[string]any: - b.WriteString(`
`) - for k, vv := range val { - b.WriteString(fmt.Sprintf(`
%s
%s
`, - html.EscapeString(k), html.EscapeString(fmt.Sprintf("%v", vv)))) - } - b.WriteString(`
`) - } -} - -// ── Export index (compatibility) ────────────────────────────────────────────── - func renderExportIndex(exportDir string) (string, error) { entries, err := listExportFiles(exportDir) if err != nil { diff --git a/audit/internal/webui/server.go b/audit/internal/webui/server.go index 4853da7..859b1e5 100644 --- a/audit/internal/webui/server.go +++ b/audit/internal/webui/server.go @@ -153,8 +153,8 @@ func NewHandler(opts HandlerOptions) http.Handler { mux.HandleFunc("GET /api/metrics/stream", h.handleAPIMetricsStream) mux.HandleFunc("GET /api/metrics/chart/", h.handleMetricsChartSVG) - // Reanimator chart static assets - mux.Handle("GET /chart/static/", http.StripPrefix("/chart/static/", web.Static())) + // Reanimator chart static assets (viewer template expects /static/*) + mux.Handle("GET /static/", http.StripPrefix("/static/", web.Static())) // ── Pages ──────────────────────────────────────────────────────────────── mux.HandleFunc("GET /", h.handlePage)