Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
558bedb5e9 |
@@ -21,8 +21,6 @@ trial run of the finished image: https://git.eeqj.de/sneak/dnswatcher/issues/149
|
|||||||
|
|
||||||
- 2026-10-02: an apex domain is not counted or listed as a hostname; its records
|
- 2026-10-02: an apex domain is not counted or listed as a hostname; its records
|
||||||
show under Domains, and notifications about them say `Domain:` (closes #224).
|
show under Domains, and notifications about them say `Domain:` (closes #224).
|
||||||
- 2026-10-02: the dashboard lists each nameserver's record types in one fixed
|
|
||||||
order, the README's, then any other type, not a random one (closes #226).
|
|
||||||
- 2026-10-02: the dashboard and `/api/v1/status` show why a nameserver query or
|
- 2026-10-02: the dashboard and `/api/v1/status` show why a nameserver query or
|
||||||
a certificate check failed, which only the state file showed (closes #225).
|
a certificate check failed, which only the state file showed (closes #225).
|
||||||
- 2026-10-02: a name's CNAME is stored once per nameserver, not once per record
|
- 2026-10-02: a name's CNAME is stored once per nameserver, not once per record
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
|
||||||
"embed"
|
"embed"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html/template"
|
"html/template"
|
||||||
"maps"
|
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -132,37 +129,16 @@ func joinStrings(items []string, sep string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// formatRecords formats a map of record type → values into a
|
// formatRecords formats a map of record type → values into a
|
||||||
// compact display string. Record types are listed in the order the
|
// compact display string.
|
||||||
// README lists them, any other type after them in alphabetical order,
|
|
||||||
// so rows of nameservers with the same records read the same.
|
|
||||||
func formatRecords(records map[string][]string) string {
|
func formatRecords(records map[string][]string) string {
|
||||||
if len(records) == 0 {
|
if len(records) == 0 {
|
||||||
return "-"
|
return "-"
|
||||||
}
|
}
|
||||||
|
|
||||||
order := []string{"A", "AAAA", "CNAME", "MX", "TXT", "SRV", "CAA", "NS"}
|
|
||||||
|
|
||||||
position := func(rtype string) int {
|
|
||||||
i := slices.Index(order, rtype)
|
|
||||||
if i < 0 {
|
|
||||||
return len(order)
|
|
||||||
}
|
|
||||||
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
rtypes := slices.Collect(maps.Keys(records))
|
|
||||||
slices.SortFunc(rtypes, func(a, b string) int {
|
|
||||||
return cmp.Or(
|
|
||||||
cmp.Compare(position(a), position(b)),
|
|
||||||
strings.Compare(a, b),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
var parts []string
|
var parts []string
|
||||||
|
|
||||||
for _, rtype := range rtypes {
|
for rtype, values := range records {
|
||||||
for _, v := range records[rtype] {
|
for _, v := range values {
|
||||||
parts = append(parts, rtype+": "+v)
|
parts = append(parts, rtype+": "+v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package handlers_test
|
package handlers_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -81,45 +80,6 @@ func TestFormatRecords(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestFormatRecordsTypeOrder checks that record types are listed in
|
|
||||||
// the README's order (A, AAAA, CNAME, MX, TXT, SRV, CAA, NS), with
|
|
||||||
// any other type after them in alphabetical order.
|
|
||||||
func TestFormatRecordsTypeOrder(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
got := handlers.FormatRecords(map[string][]string{
|
|
||||||
"SOA": {"ns1.example.com. hostmaster.example.com. 1 2 3 4 5"},
|
|
||||||
"NS": {"ns1.example.com.", "ns2.example.com."},
|
|
||||||
"CAA": {`0 issue "letsencrypt.org"`},
|
|
||||||
"DNAME": {"example.net."},
|
|
||||||
"TXT": {"v=spf1 -all"},
|
|
||||||
"SRV": {"10 5 443 www.example.com."},
|
|
||||||
"MX": {"10 mail.example.com."},
|
|
||||||
"CNAME": {"www.example.com."},
|
|
||||||
"AAAA": {"2001:db8::1"},
|
|
||||||
"A": {"192.0.2.1"},
|
|
||||||
})
|
|
||||||
|
|
||||||
want := strings.Join([]string{
|
|
||||||
"A: 192.0.2.1",
|
|
||||||
"AAAA: 2001:db8::1",
|
|
||||||
"CNAME: www.example.com.",
|
|
||||||
"MX: 10 mail.example.com.",
|
|
||||||
"TXT: v=spf1 -all",
|
|
||||||
"SRV: 10 5 443 www.example.com.",
|
|
||||||
`CAA: 0 issue "letsencrypt.org"`,
|
|
||||||
"NS: ns1.example.com.",
|
|
||||||
"NS: ns2.example.com.",
|
|
||||||
"DNAME: example.net.",
|
|
||||||
"SOA: ns1.example.com. hostmaster.example.com. 1 2 3 4 5",
|
|
||||||
}, ", ")
|
|
||||||
|
|
||||||
if got != want {
|
|
||||||
t.Errorf("FormatRecords lists types out of order:\n got %q\nwant %q",
|
|
||||||
got, want)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// dashboardRow returns the table row of page that contains name.
|
// dashboardRow returns the table row of page that contains name.
|
||||||
func dashboardRow(t *testing.T, page string, name string) string {
|
func dashboardRow(t *testing.T, page string, name string) string {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -195,13 +155,4 @@ func TestDashboardShowsDomainRecordsUnderDomains(t *testing.T) {
|
|||||||
if !strings.Contains(words, footer) {
|
if !strings.Contains(words, footer) {
|
||||||
t.Errorf("dashboard does not say %q", footer)
|
t.Errorf("dashboard does not say %q", footer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// With the tags taken out, the summary bar starts "Domains 1
|
|
||||||
// Hostnames 1".
|
|
||||||
text := regexp.MustCompile(`<[^>]*>`).ReplaceAllString(page, " ")
|
|
||||||
summary := "Domains 1 Hostnames 1"
|
|
||||||
|
|
||||||
if !strings.Contains(strings.Join(strings.Fields(text), " "), summary) {
|
|
||||||
t.Errorf("summary bar does not say %q", summary)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user