- Add request logging middleware to main.go - Add debug handler with health, config, stats, and test-dns endpoints - Add detailed logging to DynDNS handler - Add logging to Technitium DNS client - Add database Ping() and GetStats() methods - New endpoints: - /health - detailed health status with database and DNS checks - /debug/config - sanitized configuration - /debug/stats - database statistics - /debug/test-dns - live DNS test endpoint This will help diagnose production issues with DNS updates.
170 lines
4.6 KiB
Go
170 lines
4.6 KiB
Go
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"
|
|
}
|