package web import ( "net/http" "antidrift/internal/settings" "github.com/gin-gonic/gin" ) // SetSettings injects the persisted settings, their file path, and the applier // closure (built by main, which owns adapter construction). Mirrors the // controller's Set* injection so web never imports ai/tasks/knowledge. func (s *Server) SetSettings(path string, current settings.Settings, apply func(settings.Settings) error) { s.settingsMu.Lock() s.settingsPath = path s.settings = current s.applyFn = apply s.settingsMu.Unlock() } func (s *Server) handleGetSettings(c *gin.Context) { s.settingsMu.Lock() cur := s.settings s.settingsMu.Unlock() c.JSON(http.StatusOK, cur) } // handlePostSettings applies the new settings first, then persists them. The // applier checks the backend before mutating any state, so an invalid backend // yields 400 with nothing saved and the prior wiring intact. If Save fails after // a successful apply, the running adapters are already rewired but the file and // the in-memory copy are left unchanged; that surfaces as 500. func (s *Server) handlePostSettings(c *gin.Context) { var req settings.Settings if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "invalid json"}) return } s.settingsMu.Lock() apply := s.applyFn path := s.settingsPath s.settingsMu.Unlock() if apply == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "settings not wired"}) return } if err := apply(req); err != nil { // Apply validates the backend before mutating anything, so any error // (invalid backend or otherwise) means nothing changed; surface it as 400. c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := settings.Save(path, req); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } s.settingsMu.Lock() s.settings = req s.settingsMu.Unlock() s.broadcast() c.JSON(http.StatusOK, req) }