package api import ( "fmt" "net/http" "github.com/alexei/docker-watcher/internal/crypto" "github.com/alexei/docker-watcher/internal/webhook" ) // settingsRequest is the expected JSON body for updating settings. type settingsRequest struct { Domain string `json:"domain"` ServerIP string `json:"server_ip"` Network string `json:"network"` SubdomainPattern string `json:"subdomain_pattern"` NotificationURL string `json:"notification_url"` NpmURL string `json:"npm_url"` NpmEmail string `json:"npm_email"` NpmPassword string `json:"npm_password"` PollingInterval string `json:"polling_interval"` } // getSettings handles GET /api/settings. func (s *Server) getSettings(w http.ResponseWriter, r *http.Request) { settings, err := s.store.GetSettings() if err != nil { respondError(w, http.StatusInternalServerError, "failed to get settings: "+err.Error()) return } // Return settings without sensitive fields. respondJSON(w, http.StatusOK, map[string]any{ "domain": settings.Domain, "server_ip": settings.ServerIP, "network": settings.Network, "subdomain_pattern": settings.SubdomainPattern, "notification_url": settings.NotificationURL, "npm_url": settings.NpmURL, "npm_email": settings.NpmEmail, "has_npm_password": settings.NpmPassword != "", "polling_interval": settings.PollingInterval, "updated_at": settings.UpdatedAt, }) } // updateSettings handles PUT /api/settings. func (s *Server) updateSettings(w http.ResponseWriter, r *http.Request) { var req settingsRequest if !decodeJSON(w, r, &req) { return } existing, err := s.store.GetSettings() if err != nil { respondError(w, http.StatusInternalServerError, "failed to get settings: "+err.Error()) return } updated := existing if req.Domain != "" { updated.Domain = req.Domain } if req.ServerIP != "" { updated.ServerIP = req.ServerIP } if req.Network != "" { updated.Network = req.Network } if req.SubdomainPattern != "" { updated.SubdomainPattern = req.SubdomainPattern } // Allow clearing notification URL. updated.NotificationURL = req.NotificationURL if req.NpmURL != "" { updated.NpmURL = req.NpmURL } if req.NpmEmail != "" { updated.NpmEmail = req.NpmEmail } if req.NpmPassword != "" { encPassword, err := crypto.Encrypt(s.encKey, req.NpmPassword) if err != nil { respondError(w, http.StatusInternalServerError, "failed to encrypt npm password: "+err.Error()) return } updated.NpmPassword = encPassword } if req.PollingInterval != "" { updated.PollingInterval = req.PollingInterval } if err := s.store.UpdateSettings(updated); err != nil { respondError(w, http.StatusInternalServerError, "failed to update settings: "+err.Error()) return } respondJSON(w, http.StatusOK, map[string]string{"status": "updated"}) } // getWebhookURL handles GET /api/settings/webhook-url. func (s *Server) getWebhookURL(w http.ResponseWriter, r *http.Request) { settings, err := s.store.GetSettings() if err != nil { respondError(w, http.StatusInternalServerError, "failed to get settings: "+err.Error()) return } webhookURL := "" if settings.WebhookSecret != "" && settings.Domain != "" { webhookURL = fmt.Sprintf("https://%s/api/webhook/%s", settings.Domain, settings.WebhookSecret) } respondJSON(w, http.StatusOK, map[string]string{ "webhook_url": webhookURL, "webhook_secret": settings.WebhookSecret, }) } // regenerateWebhookSecret handles POST /api/settings/regenerate. func (s *Server) regenerateWebhookSecret(w http.ResponseWriter, r *http.Request) { secret, err := webhook.RegenerateWebhookSecret(s.store) if err != nil { respondError(w, http.StatusInternalServerError, "failed to regenerate webhook secret: "+err.Error()) return } settings, err := s.store.GetSettings() if err != nil { respondError(w, http.StatusInternalServerError, "failed to get settings: "+err.Error()) return } webhookURL := "" if settings.Domain != "" { webhookURL = fmt.Sprintf("https://%s/api/webhook/%s", settings.Domain, secret) } respondJSON(w, http.StatusOK, map[string]string{ "webhook_url": webhookURL, "webhook_secret": secret, }) }