package checker import ( "encoding/json" "fmt" "html/template" "sort" "strings" ) // ── HTML report ─────────────────────────────────────────────────────────────── // zmLevelDisplayOrder defines the severity order used for sorting and display. var zmLevelDisplayOrder = []string{"CRITICAL", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"} var zmLevelRank = func() map[string]int { m := make(map[string]int, len(zmLevelDisplayOrder)) for i, l := range zmLevelDisplayOrder { m[l] = len(zmLevelDisplayOrder) - i } return m }() type zmLevelCount struct { Level string Count int } type zmModuleGroup struct { Name string Position int // first-seen index, used as tiebreaker in sort Results []ZonemasterTestResult Levels []zmLevelCount // sorted by severity desc, zeros omitted Worst string Open bool } type zmTemplateData struct { Domain string CreatedAt string HashID string Language string Modules []zmModuleGroup Totals []zmLevelCount // sorted by severity desc, zeros omitted } var zonemasterHTMLTemplate = template.Must( template.New("zonemaster"). Funcs(template.FuncMap{ "badgeClass": func(level string) string { switch strings.ToUpper(level) { case "CRITICAL": return "badge-critical" case "ERROR": return "badge-error" case "WARNING": return "badge-warning" case "NOTICE": return "badge-notice" case "INFO": return "badge-info" default: return "badge-debug" } }, }). Parse(` Zonemaster{{if .Domain}} — {{.Domain}}{{end}}

Zonemaster{{if .Domain}} — {{.Domain}}{{end}}

{{- if .CreatedAt}}Run at {{.CreatedAt}}{{end -}} {{- if and .CreatedAt .HashID}} · {{end -}} {{- if .HashID}}ID: {{.HashID}}{{end -}}
{{- range .Totals}} {{.Level}} {{.Count}} {{- end}}
{{range .Modules -}} {{.Name}} {{- range .Levels}} {{.Count}} {{- end}}
{{- range .Results}}
{{.Level}}
{{.Message}}
{{- if .Testcase}}
{{.Testcase}}
{{end}}
{{- end}}
{{end -}} `), ) // GetHTMLReport implements sdk.CheckerHTMLReporter. func (p *zonemasterProvider) GetHTMLReport(raw json.RawMessage) (string, error) { var data ZonemasterData if err := json.Unmarshal(raw, &data); err != nil { return "", fmt.Errorf("failed to unmarshal zonemaster results: %w", err) } // Group results by module, preserving first-seen order. moduleOrder := []string{} moduleMap := map[string][]ZonemasterTestResult{} for _, r := range data.Results { if _, seen := moduleMap[r.Module]; !seen { moduleOrder = append(moduleOrder, r.Module) } moduleMap[r.Module] = append(moduleMap[r.Module], r) } totalCounts := map[string]int{} var modules []zmModuleGroup for _, name := range moduleOrder { rs := moduleMap[name] counts := map[string]int{} for _, r := range rs { lvl := strings.ToUpper(r.Level) counts[lvl]++ totalCounts[lvl]++ } // Find worst level and build sorted level-count slice. worst := "" worstRank := -1 var levels []zmLevelCount for _, l := range zmLevelDisplayOrder { if n, ok := counts[l]; ok && n > 0 { levels = append(levels, zmLevelCount{Level: l, Count: n}) if zmLevelRank[l] > worstRank { worstRank = zmLevelRank[l] worst = l } } } // Append any unknown levels last. for l, n := range counts { if _, known := zmLevelRank[l]; !known { levels = append(levels, zmLevelCount{Level: l, Count: n}) } } modules = append(modules, zmModuleGroup{ Name: name, Position: len(modules), Results: rs, Levels: levels, Worst: worst, Open: worst == "CRITICAL" || worst == "ERROR", }) } // Sort modules: most severe first, then by original appearance order. sort.Slice(modules, func(i, j int) bool { ri, rj := zmLevelRank[modules[i].Worst], zmLevelRank[modules[j].Worst] if ri != rj { return ri > rj } return modules[i].Position < modules[j].Position }) // Build sorted totals slice. var totals []zmLevelCount for _, l := range zmLevelDisplayOrder { if n, ok := totalCounts[l]; ok && n > 0 { totals = append(totals, zmLevelCount{Level: l, Count: n}) } } domain := "" if d, ok := data.Params["domain"]; ok { domain = fmt.Sprintf("%v", d) } lang := data.Language if lang == "" { lang = "en" } td := zmTemplateData{ Domain: domain, CreatedAt: data.CreatedAt, HashID: data.HashID, Language: lang, Modules: modules, Totals: totals, } var buf strings.Builder if err := zonemasterHTMLTemplate.Execute(&buf, td); err != nil { return "", fmt.Errorf("failed to render zonemaster HTML report: %w", err) } return buf.String(), nil }