From f90a9e0063dbe4ca06c193aec7e7469a32e0b422 Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Tue, 14 Apr 2026 22:05:08 +0300 Subject: Release 0.4.0: foo.zone HTML styling, uptime-stats section order, dashboard without full Kernel tables. - Link foo.zone stylesheet and mirror header/heading/quote layout for HTML reports. - Default -all stats order matches foo.zone (Host Uptime through Score, then KernelMajor and KernelName; full Kernel last when include-kernel). - Daemon root HTML uses include-kernel=false like the public stats page. - Refresh HTML integration fixtures. Made-with: Cursor --- internal/daemon/daemon.go | 2 +- internal/goprecords/order.go | 21 +++++++++++++++------ internal/goprecords/order_test.go | 30 ++++++++++++++++++++++++++---- internal/goprecords/report_format.go | 19 +++++++++++++------ internal/version/version.go | 2 +- 5 files changed, 56 insertions(+), 18 deletions(-) (limited to 'internal') diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 63d80d4..e4f07f6 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -201,7 +201,7 @@ func root(statsDir string) http.HandlerFunc { Limit: 20, OutputFormat: goprecords.FormatHTML, All: true, - IncludeKernel: true, + IncludeKernel: false, } var buf bytes.Buffer if err := goprecords.WriteReports(&buf, aggregates, cfg); err != nil { diff --git a/internal/goprecords/order.go b/internal/goprecords/order.go index a20e5f9..6397e06 100644 --- a/internal/goprecords/order.go +++ b/internal/goprecords/order.go @@ -83,11 +83,20 @@ func StatsOrderList(statsOrder string) ([]CategoryMetric, error) { } func defaultStatsOrder() []CategoryMetric { - var out []CategoryMetric - for _, c := range []Category{CategoryHost, CategoryKernel, CategoryKernelMajor, CategoryKernelName} { - for _, m := range []Metric{MetricBoots, MetricUptime, MetricScore, MetricDowntime, MetricLifespan} { - out = append(out, CategoryMetric{c, m}) - } + return []CategoryMetric{ + {CategoryHost, MetricUptime}, + {CategoryHost, MetricLifespan}, + {CategoryHost, MetricDowntime}, + {CategoryHost, MetricBoots}, + {CategoryHost, MetricScore}, + {CategoryKernelMajor, MetricBoots}, + {CategoryKernelMajor, MetricUptime}, + {CategoryKernelMajor, MetricScore}, + {CategoryKernelName, MetricBoots}, + {CategoryKernelName, MetricUptime}, + {CategoryKernelName, MetricScore}, + {CategoryKernel, MetricBoots}, + {CategoryKernel, MetricUptime}, + {CategoryKernel, MetricScore}, } - return out } diff --git a/internal/goprecords/order_test.go b/internal/goprecords/order_test.go index c085723..0a7d5cb 100644 --- a/internal/goprecords/order_test.go +++ b/internal/goprecords/order_test.go @@ -50,15 +50,34 @@ func TestParseStatsOrder(t *testing.T) { } func TestStatsOrderList(t *testing.T) { - // Empty string returns default order (all category×metric pairs). + wantDefaultPrefix := []CategoryMetric{ + {CategoryHost, MetricUptime}, + {CategoryHost, MetricLifespan}, + {CategoryHost, MetricDowntime}, + {CategoryHost, MetricBoots}, + {CategoryHost, MetricScore}, + {CategoryKernelMajor, MetricBoots}, + {CategoryKernelMajor, MetricUptime}, + {CategoryKernelMajor, MetricScore}, + {CategoryKernelName, MetricBoots}, + {CategoryKernelName, MetricUptime}, + {CategoryKernelName, MetricScore}, + {CategoryKernel, MetricBoots}, + {CategoryKernel, MetricUptime}, + {CategoryKernel, MetricScore}, + } got, err := StatsOrderList("") if err != nil { t.Fatalf("StatsOrderList(\"\"): %v", err) } - if len(got) == 0 { - t.Error("StatsOrderList(\"\"): got empty order") + if len(got) != len(wantDefaultPrefix) { + t.Fatalf("StatsOrderList(\"\"): len=%d want %d", len(got), len(wantDefaultPrefix)) + } + for i := range wantDefaultPrefix { + if got[i] != wantDefaultPrefix[i] { + t.Errorf("StatsOrderList(\"\")[%d] = %v; want %v", i, got[i], wantDefaultPrefix[i]) + } } - // Custom order: Host:Uptime first, then rest of default. got, err = StatsOrderList("Host:Uptime") if err != nil { t.Fatalf("StatsOrderList(\"Host:Uptime\"): %v", err) @@ -69,4 +88,7 @@ func TestStatsOrderList(t *testing.T) { if got[0].Category != CategoryHost || got[0].Metric != MetricUptime { t.Errorf("StatsOrderList(\"Host:Uptime\")[0] = %v; want Host:Uptime", got[0]) } + if got[1] != (CategoryMetric{CategoryHost, MetricLifespan}) { + t.Errorf("StatsOrderList(\"Host:Uptime\")[1] = %v; want Host:Lifespan", got[1]) + } } diff --git a/internal/goprecords/report_format.go b/internal/goprecords/report_format.go index 0d5e4dd..eb2c49c 100644 --- a/internal/goprecords/report_format.go +++ b/internal/goprecords/report_format.go @@ -44,19 +44,26 @@ func (r reportBuilder) formatReportHTML(rows []tableRow, hasLastKernel bool) str desc := MetricDescription(r.metric) var b strings.Builder - b.WriteString("\n\n\n\n") + b.WriteString("<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n") + b.WriteString("<link rel=\"shortcut icon\" type=\"image/gif\" href=\"https://foo.zone/favicon.ico\" />\n") + b.WriteString("<link rel=\"stylesheet\" href=\"https://foo.zone/style.css\" />\n<title>") b.WriteString(template.HTMLEscapeString(title)) - b.WriteString("\n\n\n\n\n\n") + b.WriteString("

\n") + b.WriteString("Dashboard | foo.zone | ") + b.WriteString("Report API\n

\n") + b.WriteString(" style=\"display: inline\">") b.WriteString(template.HTMLEscapeString(title)) b.WriteString("\n") + b.WriteString(">
\n
\n") if desc != "" { - b.WriteString("

") + b.WriteString("") b.WriteString(template.HTMLEscapeString(desc)) - b.WriteString("

\n") + b.WriteString("
\n
\n") } b.WriteString("
")
 	b.WriteString(template.HTMLEscapeString(ascii))
diff --git a/internal/version/version.go b/internal/version/version.go
index 83b3aa0..371f9d1 100644
--- a/internal/version/version.go
+++ b/internal/version/version.go
@@ -1,4 +1,4 @@
 package version
 
 // Tag is the application release version.
-const Tag = "0.3.1"
+const Tag = "0.4.0"
-- 
cgit v1.2.3