package handlers import ( "fmt" "net/http" "os" "runtime" "time" "git.dws.rip/DWS/dyn/internal/config" "git.dws.rip/DWS/dyn/internal/database" "git.dws.rip/DWS/dyn/internal/dns" "github.com/gin-gonic/gin" ) // DebugHandler provides diagnostic endpoints for production troubleshooting type DebugHandler struct { db *database.DB dns *dns.Client config *config.Config startTime string } // NewDebugHandler creates a new debug handler func NewDebugHandler(db *database.DB, dnsClient *dns.Client, cfg *config.Config) *DebugHandler { return &DebugHandler{ db: db, dns: dnsClient, config: cfg, startTime: time.Now().Format(time.RFC3339), } } // Health returns detailed health status func (h *DebugHandler) Health(c *gin.Context) { health := gin.H{ "status": "healthy", "version": getVersion(), "uptime": h.startTime, "go_version": runtime.Version(), } // Check database if err := h.db.Ping(); err != nil { health["database"] = gin.H{"status": "unhealthy", "error": err.Error()} health["status"] = "degraded" } else { health["database"] = gin.H{"status": "healthy"} } // Check Technitium connectivity if err := h.testTechnitiumConnection(); err != nil { health["technitium"] = gin.H{"status": "unhealthy", "error": err.Error()} health["status"] = "degraded" } else { health["technitium"] = gin.H{"status": "healthy"} } // Config (without secrets) health["config"] = gin.H{ "base_domain": h.config.BaseDomain, "space_subdomain": h.config.SpaceSubdomain, "zone": h.config.GetZone(), "technitium_url": h.config.TechnitiumURL, "rate_limit_ip": h.config.RateLimitPerIP, "rate_limit_token": h.config.RateLimitPerToken, } c.JSON(http.StatusOK, health) } // TestDNS attempts to create and delete a test DNS record func (h *DebugHandler) TestDNS(c *gin.Context) { testSubdomain := "test-health-check-" + fmt.Sprintf("%d", time.Now().Unix()) testIP := "1.2.3.4" zone := h.config.GetZone() results := gin.H{ "zone": zone, "subdomain": testSubdomain, "test_ip": testIP, } // Try to add a record err := h.dns.AddARecord(zone, testSubdomain, testIP, 60) if err != nil { results["add_record"] = gin.H{"success": false, "error": err.Error()} c.JSON(http.StatusServiceUnavailable, results) return } results["add_record"] = gin.H{"success": true} // Try to add wildcard err = h.dns.AddWildcardARecord(zone, testSubdomain, testIP, 60) if err != nil { results["add_wildcard"] = gin.H{"success": false, "error": err.Error()} c.JSON(http.StatusServiceUnavailable, results) return } results["add_wildcard"] = gin.H{"success": true} // Cleanup - delete the test records err = h.dns.DeleteRecord(zone, testSubdomain, "A") if err != nil { results["cleanup"] = gin.H{"warning": "Failed to cleanup test record", "error": err.Error()} } else { results["cleanup"] = gin.H{"success": true} } // Delete wildcard err = h.dns.DeleteRecord(zone, "*."+testSubdomain, "A") if err != nil { results["cleanup_wildcard"] = gin.H{"warning": "Failed to cleanup wildcard", "error": err.Error()} } results["overall"] = "success" c.JSON(http.StatusOK, results) } // ConfigDebug shows current configuration (sanitized) func (h *DebugHandler) ConfigDebug(c *gin.Context) { cfg := gin.H{ "server_port": h.config.ServerPort, "database_path": h.config.DatabasePath, "technitium_url": h.config.TechnitiumURL, "base_domain": h.config.BaseDomain, "space_subdomain": h.config.SpaceSubdomain, "zone": h.config.GetZone(), "rate_limit_per_ip": h.config.RateLimitPerIP, "rate_limit_per_token": h.config.RateLimitPerToken, "trusted_proxies": h.config.TrustedProxies, // Don't expose credentials! "has_token": h.config.TechnitiumToken != "", "has_username": h.config.TechnitiumUsername != "", "has_password": h.config.TechnitiumPassword != "", } c.JSON(http.StatusOK, cfg) } // Stats returns database statistics func (h *DebugHandler) Stats(c *gin.Context) { stats, err := h.db.GetStats() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, stats) } func (h *DebugHandler) testTechnitiumConnection() error { // Try a simple operation - add and immediately delete a test record testSubdomain := "conn-test" + fmt.Sprintf("%d", time.Now().Unix()) err := h.dns.AddARecord(h.config.GetZone(), testSubdomain, "127.0.0.1", 1) if err != nil { return err } // Cleanup h.dns.DeleteRecord(h.config.GetZone(), testSubdomain, "A") return nil } func getVersion() string { if v := os.Getenv("VERSION"); v != "" { return v } return "dev" }