package handlers import ( "net/http" "github.com/go-chi/chi" "github.com/google/uuid" "sneak.berlin/go/webhooker/internal/database" ) // HandleEntrypointCreate handles adding a new entrypoint. func (h *Handlers) HandleEntrypointCreate() 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") var webhook database.Webhook err := h.db.DB().Where( "id = ? AND user_id = ?", sourceID, userID, ).First(&webhook).Error if err != nil { h.renderError(w, r, http.StatusNotFound) return } // The body size cap is enforced by the MaxBodySize // middleware, which runs before CSRF parses the form. err = r.ParseForm() if err != nil { h.renderError(w, r, http.StatusBadRequest) return } description := r.PostFormValue("description") entrypoint := &database.Entrypoint{ WebhookID: webhook.ID, Path: uuid.New().String(), Description: description, Active: true, } err = h.db.DB().Create(entrypoint).Error if err != nil { h.serverError(w, r, "failed to create entrypoint", err) return } http.Redirect( w, r, withNotice("/hook/"+webhook.ID, entrypointAdded), http.StatusSeeOther, ) } } // HandleEntrypointEdit handles changing an entrypoint's description. // It writes only the description column, so the entrypoint keeps its // URL, and an activate or deactivate saved since the page was shown // is not undone. func (h *Handlers) HandleEntrypointEdit() 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") var webhook database.Webhook err := h.db.DB().Where( "id = ? AND user_id = ?", sourceID, userID, ).First(&webhook).Error if err != nil { h.renderError(w, r, http.StatusNotFound) return } // The body size cap is enforced by the MaxBodySize // middleware, which runs before CSRF parses the form. err = r.ParseForm() if err != nil { h.renderError(w, r, http.StatusBadRequest) return } result := h.db.DB().Model(&database.Entrypoint{}).Where( "id = ? AND webhook_id = ?", entrypointID, webhook.ID, ).Update("description", r.PostFormValue("description")) if result.Error != nil { h.serverError( w, r, "failed to edit entrypoint", result.Error, ) return } // The id came from the URL and may name another webhook's // entrypoint, which this webhook does not have. if result.RowsAffected == 0 { h.renderError(w, r, http.StatusNotFound) return } http.Redirect( w, r, withNotice("/hook/"+webhook.ID, entrypointSaved), http.StatusSeeOther, ) } } // HandleEntrypointDelete handles deleting an entrypoint. func (h *Handlers) HandleEntrypointDelete() http.HandlerFunc { return h.deleteChildResource( "entrypointID", &database.Entrypoint{}, "failed to delete entrypoint", nil, entrypointDeleted, ) } // HandleEntrypointToggle handles toggling an entrypoint's // active state. func (h *Handlers) HandleEntrypointToggle() http.HandlerFunc { return h.toggleChildResource( "entrypointID", func(webhookID, childID string) (bool, error) { var ep database.Entrypoint err := h.db.DB().Where( "id = ? AND webhook_id = ?", childID, webhookID, ).First(&ep).Error if err != nil { return false, err } // Only the active column: saving the whole row would // write back the description read above over an edit // saved since. active := !ep.Active return active, h.db.DB().Model(&ep). Update("active", active).Error }, "failed to toggle entrypoint", entrypointActivated, entrypointDeactivated, ) }