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"}, {"install", "Install to Disk", "/install"}, } 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() case "install": pageID = "install" title = "Install to Disk" body = renderInstall() 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.

` } // ── Install to Disk ────────────────────────────────────────────────────────── func renderInstall() string { return `
Install Live System to Disk
Warning: Installing will completely erase the selected disk and write the live system onto it. All existing data on the target disk will be lost. This operation cannot be undone.
Loading disk list…
` } 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 }