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}, } }