diff --git a/internal/handlers/source_management.go b/internal/handlers/source_management.go index 61bfc2c..26441ca 100644 --- a/internal/handlers/source_management.go +++ b/internal/handlers/source_management.go @@ -527,6 +527,144 @@ func (h *Handlers) HandleTargetCreate() http.HandlerFunc { } } +// HandleEntrypointDelete handles deleting an individual entrypoint. +func (h *Handlers) HandleEntrypointDelete() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + userID, ok := h.getUserID(r) + if !ok { + http.Redirect(w, r, "/pages/login", http.StatusSeeOther) + return + } + + sourceID := chi.URLParam(r, "sourceID") + entrypointID := chi.URLParam(r, "entrypointID") + + // Verify webhook ownership + var webhook database.Webhook + if err := h.db.DB().Where("id = ? AND user_id = ?", sourceID, userID).First(&webhook).Error; err != nil { + http.NotFound(w, r) + return + } + + // Delete entrypoint (must belong to this webhook) + result := h.db.DB().Where("id = ? AND webhook_id = ?", entrypointID, webhook.ID).Delete(&database.Entrypoint{}) + if result.Error != nil { + h.log.Error("failed to delete entrypoint", "error", result.Error) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/source/"+webhook.ID, http.StatusSeeOther) + } +} + +// HandleEntrypointToggle handles toggling the active state of an entrypoint. +func (h *Handlers) HandleEntrypointToggle() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + userID, ok := h.getUserID(r) + if !ok { + http.Redirect(w, r, "/pages/login", http.StatusSeeOther) + return + } + + sourceID := chi.URLParam(r, "sourceID") + entrypointID := chi.URLParam(r, "entrypointID") + + // Verify webhook ownership + var webhook database.Webhook + if err := h.db.DB().Where("id = ? AND user_id = ?", sourceID, userID).First(&webhook).Error; err != nil { + http.NotFound(w, r) + return + } + + // Find the entrypoint + var entrypoint database.Entrypoint + if err := h.db.DB().Where("id = ? AND webhook_id = ?", entrypointID, webhook.ID).First(&entrypoint).Error; err != nil { + http.NotFound(w, r) + return + } + + // Toggle active state + entrypoint.Active = !entrypoint.Active + if err := h.db.DB().Save(&entrypoint).Error; err != nil { + h.log.Error("failed to toggle entrypoint", "error", err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/source/"+webhook.ID, http.StatusSeeOther) + } +} + +// HandleTargetDelete handles deleting an individual target. +func (h *Handlers) HandleTargetDelete() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + userID, ok := h.getUserID(r) + if !ok { + http.Redirect(w, r, "/pages/login", http.StatusSeeOther) + return + } + + sourceID := chi.URLParam(r, "sourceID") + targetID := chi.URLParam(r, "targetID") + + // Verify webhook ownership + var webhook database.Webhook + if err := h.db.DB().Where("id = ? AND user_id = ?", sourceID, userID).First(&webhook).Error; err != nil { + http.NotFound(w, r) + return + } + + // Delete target (must belong to this webhook) + result := h.db.DB().Where("id = ? AND webhook_id = ?", targetID, webhook.ID).Delete(&database.Target{}) + if result.Error != nil { + h.log.Error("failed to delete target", "error", result.Error) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/source/"+webhook.ID, http.StatusSeeOther) + } +} + +// HandleTargetToggle handles toggling the active state of a target. +func (h *Handlers) HandleTargetToggle() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + userID, ok := h.getUserID(r) + if !ok { + http.Redirect(w, r, "/pages/login", http.StatusSeeOther) + return + } + + sourceID := chi.URLParam(r, "sourceID") + targetID := chi.URLParam(r, "targetID") + + // Verify webhook ownership + var webhook database.Webhook + if err := h.db.DB().Where("id = ? AND user_id = ?", sourceID, userID).First(&webhook).Error; err != nil { + http.NotFound(w, r) + return + } + + // Find the target + var target database.Target + if err := h.db.DB().Where("id = ? AND webhook_id = ?", targetID, webhook.ID).First(&target).Error; err != nil { + http.NotFound(w, r) + return + } + + // Toggle active state + target.Active = !target.Active + if err := h.db.DB().Save(&target).Error; err != nil { + h.log.Error("failed to toggle target", "error", err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + + http.Redirect(w, r, "/source/"+webhook.ID, http.StatusSeeOther) + } +} + // getUserID extracts the user ID from the session. func (h *Handlers) getUserID(r *http.Request) (string, bool) { sess, err := h.session.Get(r) diff --git a/internal/server/routes.go b/internal/server/routes.go index d0c1773..34ec050 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -105,8 +105,12 @@ func (s *Server) SetupRoutes() { r.Post("/edit", s.h.HandleSourceEditSubmit()) // Handle edit submission r.Post("/delete", s.h.HandleSourceDelete()) // Delete webhook r.Get("/logs", s.h.HandleSourceLogs()) // View webhook logs - r.Post("/entrypoints", s.h.HandleEntrypointCreate()) // Add entrypoint - r.Post("/targets", s.h.HandleTargetCreate()) // Add target + r.Post("/entrypoints", s.h.HandleEntrypointCreate()) // Add entrypoint + r.Post("/entrypoints/{entrypointID}/delete", s.h.HandleEntrypointDelete()) // Delete entrypoint + r.Post("/entrypoints/{entrypointID}/toggle", s.h.HandleEntrypointToggle()) // Toggle entrypoint active + r.Post("/targets", s.h.HandleTargetCreate()) // Add target + r.Post("/targets/{targetID}/delete", s.h.HandleTargetDelete()) // Delete target + r.Post("/targets/{targetID}/toggle", s.h.HandleTargetToggle()) // Toggle target active }) // Entrypoint endpoint — accepts incoming webhook POST requests only. diff --git a/templates/source_detail.html b/templates/source_detail.html index 8a5b65e..437ad4b 100644 --- a/templates/source_detail.html +++ b/templates/source_detail.html @@ -49,11 +49,21 @@
{{if .Description}}{{.Description}}{{else}}Entrypoint{{end}} - {{if .Active}} - Active - {{else}} - Inactive - {{end}} +
+ {{if .Active}} + Active + {{else}} + Inactive + {{end}} +
+ +
+
+ +
+
{{$.BaseURL}}/webhook/{{.Path}}
@@ -110,6 +120,14 @@ {{else}} Inactive {{end}} +
+ +
+
+ +
{{if .Config}}