package webui import ( "encoding/json" "fmt" "html" "net/url" "os" "path/filepath" "sort" "strings" ) // ── Layout ──────────────────────────────────────────────────────────────────── func layoutHead(title string) string { return ` ` + html.EscapeString(title) + ` ` } func layoutNav(active string) string { 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() } // renderPage dispatches to the appropriate page renderer. func renderPage(page string, opts HandlerOptions) string { var pageID, title, body string switch page { case "dashboard", "": pageID = "dashboard" title = "Dashboard" body = renderDashboard(opts) case "metrics": pageID = "metrics" title = "Live Metrics" body = renderMetrics() case "tests": pageID = "tests" title = "Acceptance Tests" body = renderTests() case "burn-in": pageID = "burn-in" title = "Burn-in Tests" body = renderBurnIn() case "network": pageID = "network" title = "Network" body = renderNetwork() case "services": pageID = "services" title = "Services" body = renderServices() case "export": pageID = "export" title = "Export" body = renderExport(opts.ExportDir) case "tools": pageID = "tools" title = "Tools" body = renderTools() default: pageID = "dashboard" title = "Not Found" body = `
Page not found.
` } return layoutHead(opts.Title+" — "+title) + layoutNav(pageID) + `

` + html.EscapeString(title) + `

` + body + `
` } // ── Dashboard ───────────────────────────────────────────────────────────────── func renderDashboard(opts HandlerOptions) string { var b strings.Builder b.WriteString(`
`) // Left: health summary b.WriteString(`
`) b.WriteString(renderHealthCard(opts)) b.WriteString(`
`) // Right: quick actions b.WriteString(`
`) b.WriteString(`
Quick Actions
`) b.WriteString(`⬇ Download Support Bundle`) b.WriteString(`📄 Open audit.json`) b.WriteString(`📁 Browse Export Files`) b.WriteString(`
`) b.WriteString(`
`) b.WriteString(`
`) b.WriteString(`
`) // Audit run output div b.WriteString(``) b.WriteString(``) return b.String() } func renderHealthCard(opts HandlerOptions) string { data, err := loadSnapshot(filepath.Join(opts.ExportDir, "runtime-health.json")) if err != nil { return `
Runtime Health
No data
` } var health map[string]any if err := json.Unmarshal(data, &health); err != nil { return `
Runtime Health
Parse error
` } status := fmt.Sprintf("%v", health["status"]) badge := "badge-ok" if status == "PARTIAL" { badge = "badge-warn" } else if status == "FAIL" || status == "FAILED" { badge = "badge-err" } var b strings.Builder b.WriteString(`
Runtime Health
`) b.WriteString(fmt.Sprintf(`
%s
`, badge, html.EscapeString(status))) if issues, ok := health["issues"].([]any); ok && len(issues) > 0 { b.WriteString(`
Issues:
`) for _, issue := range issues { if m, ok := issue.(map[string]any); ok { b.WriteString(html.EscapeString(fmt.Sprintf("%v: %v", m["code"], m["message"])) + "
") } } b.WriteString(`
`) } b.WriteString(`
`) return b.String() } // ── Metrics ─────────────────────────────────────────────────────────────────── func renderMetrics() string { return `

Live metrics — updated every 2 seconds. Charts use go-analyze/charts (grafana theme).

Server
Server metrics
` } // ── Acceptance Tests ────────────────────────────────────────────────────────── func renderTests() string { return `

Run hardware acceptance tests and view results.

` + renderSATCard("nvidia", "NVIDIA GPU", `
`) + renderSATCard("memory", "Memory", "") + renderSATCard("storage", "Storage", "") + renderSATCard("cpu", "CPU", `
`) + `
` } func renderSATCard(id, label, extra string) string { return fmt.Sprintf(`
%s
%s
`, label, extra, id) } // ── Burn-in ─────────────────────────────────────────────────────────────────── func renderBurnIn() string { return `

Long-running GPU and system stress tests. Check Metrics page for live telemetry.

GPU Platform Stress
CPU Stress
` } // ── Network ─────────────────────────────────────────────────────────────────── func renderNetwork() string { return `
Network Interfaces

Loading...

DHCP
Static IPv4
` } // ── Services ────────────────────────────────────────────────────────────────── func renderServices() string { return `
Bee Services

Loading...

` } // ── Export ──────────────────────────────────────────────────────────────────── func renderExport(exportDir string) string { entries, _ := listExportFiles(exportDir) var rows strings.Builder for _, e := range entries { rows.WriteString(fmt.Sprintf(`%s`, url.QueryEscape(e), html.EscapeString(e))) } if len(entries) == 0 { rows.WriteString(`No export files found.`) } return `
Support Bundle

Creates a tar.gz archive of all audit files, SAT results, and logs.

⬇ Download Support Bundle
Export Files
` + rows.String() + `
File
` } func listExportFiles(exportDir string) ([]string, error) { var entries []string err := filepath.Walk(strings.TrimSpace(exportDir), func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } rel, err := filepath.Rel(exportDir, path) if err != nil { return err } entries = append(entries, rel) return nil }) if err != nil && !os.IsNotExist(err) { return nil, err } sort.Strings(entries) return entries, nil } // ── Tools ───────────────────────────────────────────────────────────────────── func renderTools() string { return `
Tool Check

Click Check to verify installed tools.

` } func renderExportIndex(exportDir string) (string, error) { entries, err := listExportFiles(exportDir) if err != nil { return "", err } var body strings.Builder body.WriteString(`Bee Export Files`) body.WriteString(`

Bee Export Files

`) return body.String(), nil }