check / check (push) Successful in 3m29s
Pure code movement. Every declaration of internal/handlers/source_management.go moves unchanged into one of: webhook_list.go, webhook_create.go, webhook_detail.go, webhook_edit.go and webhook_delete.go for the webhook pages; event_log.go for the event log; entrypoint.go for the entrypoint handlers; target_create.go, target_delete.go and target_toggle.go for the target handlers; and shared.go for the helpers several of them use. Only each file's package line and imports are new. The README and a middleware comment that named the removed file now name the new ones. Model: opus-5-5
134 lines
3.3 KiB
Go
134 lines
3.3 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/go-chi/chi"
|
|
"sneak.berlin/go/webhooker/internal/database"
|
|
"sneak.berlin/go/webhooker/internal/reqtls"
|
|
)
|
|
|
|
// HandleSourceDetail shows details for a specific webhook.
|
|
func (h *Handlers) HandleSourceDetail() 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
|
|
}
|
|
|
|
h.renderSourceDetail(w, r, webhook, targetFormInput{}, "")
|
|
}
|
|
}
|
|
|
|
// renderSourceDetail loads and renders a source detail page. With a
|
|
// targetErr, it is the page shown again for a refused add target
|
|
// form: it answers 400, and the form opens on targetForm's type with
|
|
// its values and the message.
|
|
func (h *Handlers) renderSourceDetail(
|
|
w http.ResponseWriter,
|
|
r *http.Request,
|
|
webhook database.Webhook,
|
|
targetForm targetFormInput,
|
|
targetErr string,
|
|
) {
|
|
var entrypoints []database.Entrypoint
|
|
|
|
h.db.DB().Where(
|
|
"webhook_id = ?", webhook.ID,
|
|
).Find(&entrypoints)
|
|
|
|
var targets []database.Target
|
|
|
|
h.db.DB().Where(
|
|
"webhook_id = ?", webhook.ID,
|
|
).Find(&targets)
|
|
|
|
entrypointViews := NewEntrypointViews(entrypoints)
|
|
|
|
var events []RecentEventView
|
|
|
|
if h.dbMgr.DBExists(webhook.ID) {
|
|
webhookDB, err := h.dbMgr.GetDB(webhook.ID)
|
|
if err != nil {
|
|
h.serverError(w, r, "failed to get webhook database", err)
|
|
|
|
return
|
|
}
|
|
|
|
events, err = loadRecentEvents(
|
|
webhookDB, webhook.ID, singleHTTPTargetID(targets),
|
|
)
|
|
if err != nil {
|
|
h.serverError(w, r, "failed to load recent events", err)
|
|
|
|
return
|
|
}
|
|
|
|
err = addEntrypointEvents(
|
|
webhookDB, &webhook, entrypointViews, time.Now(),
|
|
)
|
|
if err != nil {
|
|
h.serverError(w, r, "failed to count entrypoint events", err)
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
scheme := "http"
|
|
if reqtls.IsTLS(r) {
|
|
scheme = "https"
|
|
}
|
|
|
|
// The host is the client's Host header, unvalidated. It is
|
|
// inert only because source_detail.html renders BaseURL as
|
|
// text, inside a <code> element and in an entrypoint's delete
|
|
// prompt; putting it in an href or any other URL context
|
|
// needs it constrained first.
|
|
baseURL := scheme + "://" + r.Host
|
|
|
|
// The template calls Webhook methods, which take pointer
|
|
// receivers; html/template cannot address a value stored in a map.
|
|
data := map[string]any{
|
|
tmplKeyWebhook: &webhook,
|
|
// Targets are projected to a display-safe view: a
|
|
// target's stored config blob holds a credential, and it
|
|
// must never reach a template.
|
|
"Entrypoints": entrypointViews,
|
|
"Targets": h.targetRows(&webhook, targets),
|
|
"Events": events,
|
|
"BaseURL": baseURL,
|
|
"Stats": h.loadWebhookStats(webhook.ID, entrypoints, targets),
|
|
tmplKeyTargetForm: targetForm,
|
|
"TargetError": targetErr,
|
|
// The add target form's selects start on its expiry and
|
|
// rotation through Alpine, so no choice is selected here.
|
|
tmplKeyArchiveExpiryChoices: archiveExpiryChoices(),
|
|
tmplKeyArchiveRotationChoices: archiveRotationChoices(),
|
|
}
|
|
|
|
status := http.StatusOK
|
|
if targetErr != "" {
|
|
status = http.StatusBadRequest
|
|
}
|
|
|
|
h.renderTemplateStatus(w, r, "source_detail.html", data, status)
|
|
}
|