Initial commit: DNS Test - DNS health checking tool
Go backend (miekg/dns) + Nuxt 3 frontend (Tailwind CSS v4). 8 check categories, 52 checks total: - Overview: @ record, WWW, MX with ASN/provider lookup - Domain Registration: expiry, registrar (RDAP + whois fallback) - Parent Delegation: NS records, glue, consistency - Nameservers: 17 checks (reachability, auth, recursion, TCP/UDP, AXFR, etc.) - SOA: serial consistency, timing values - Mail (MX): 11 checks (CNAME, PTR, public IPs, consistency) - Mail Auth: SPF, DKIM, DMARC - WWW: A record, CNAME Features: - SSE streaming (results appear as each category completes) - SQLite history (modernc.org/sqlite) - Rate limiting, CORS, request logging - Dark mode, responsive design
This commit is contained in:
145
backend/internal/checker/checker.go
Normal file
145
backend/internal/checker/checker.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package checker
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/intodns/backend/internal/resolver"
|
||||
)
|
||||
|
||||
// Checker orchestrates all DNS health checks for a domain.
|
||||
type Checker struct {
|
||||
resolver *resolver.Resolver
|
||||
}
|
||||
|
||||
// NewChecker creates a Checker with a default Resolver.
|
||||
func NewChecker() *Checker {
|
||||
return &Checker{
|
||||
resolver: resolver.NewResolver(),
|
||||
}
|
||||
}
|
||||
|
||||
// StreamEvent is sent for each completed category during streaming.
|
||||
type StreamEvent struct {
|
||||
Domain string `json:"domain,omitempty"`
|
||||
Category Category `json:"category"`
|
||||
Done bool `json:"done,omitempty"`
|
||||
}
|
||||
|
||||
// Check runs all category checks in parallel and returns a Report.
|
||||
func (c *Checker) Check(domain string) *Report {
|
||||
start := time.Now()
|
||||
|
||||
type catResult struct {
|
||||
index int
|
||||
cat Category
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
results := make(chan catResult, 8)
|
||||
|
||||
categories := c.categoryFuncs()
|
||||
|
||||
for _, cf := range categories {
|
||||
wg.Add(1)
|
||||
go func(idx int, fn func(string, *resolver.Resolver) Category) {
|
||||
defer wg.Done()
|
||||
cat := fn(domain, c.resolver)
|
||||
results <- catResult{index: idx, cat: cat}
|
||||
}(cf.index, cf.fn)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(results)
|
||||
}()
|
||||
|
||||
ordered := make([]Category, len(categories))
|
||||
for cr := range results {
|
||||
ordered[cr.index] = cr.cat
|
||||
}
|
||||
|
||||
var summary Summary
|
||||
for _, cat := range ordered {
|
||||
for _, check := range cat.Checks {
|
||||
switch check.Status {
|
||||
case StatusPass:
|
||||
summary.Pass++
|
||||
case StatusWarn:
|
||||
summary.Warn++
|
||||
case StatusFail:
|
||||
summary.Fail++
|
||||
case StatusInfo:
|
||||
summary.Info++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &Report{
|
||||
Domain: domain,
|
||||
Timestamp: time.Now().UTC().Format(time.RFC3339),
|
||||
DurationMs: time.Since(start).Milliseconds(),
|
||||
Summary: summary,
|
||||
Categories: ordered,
|
||||
}
|
||||
}
|
||||
|
||||
// CheckStream runs all categories in parallel and sends each category as it completes.
|
||||
func (c *Checker) CheckStream(domain string) <-chan StreamEvent {
|
||||
out := make(chan StreamEvent, 6)
|
||||
|
||||
go func() {
|
||||
defer close(out)
|
||||
|
||||
type catResult struct {
|
||||
index int
|
||||
cat Category
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
results := make(chan catResult, 8)
|
||||
|
||||
categories := c.categoryFuncs()
|
||||
|
||||
for _, cf := range categories {
|
||||
wg.Add(1)
|
||||
go func(idx int, fn func(string, *resolver.Resolver) Category) {
|
||||
defer wg.Done()
|
||||
cat := fn(domain, c.resolver)
|
||||
results <- catResult{index: idx, cat: cat}
|
||||
}(cf.index, cf.fn)
|
||||
}
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(results)
|
||||
}()
|
||||
|
||||
for cr := range results {
|
||||
out <- StreamEvent{
|
||||
Domain: domain,
|
||||
Category: cr.cat,
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
type catFunc struct {
|
||||
index int
|
||||
fn func(string, *resolver.Resolver) Category
|
||||
}
|
||||
|
||||
func (c *Checker) categoryFuncs() []catFunc {
|
||||
return []catFunc{
|
||||
{0, checkOverview},
|
||||
{1, checkDomainWhois},
|
||||
{2, checkParent},
|
||||
{3, checkNS},
|
||||
{4, checkSOA},
|
||||
{5, checkMX},
|
||||
{6, checkMail},
|
||||
{7, checkWWW},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user