From e1dc865226c31f2f39eb50c7cb3c114b65d65caf Mon Sep 17 00:00:00 2001 From: clawbot Date: Tue, 10 Mar 2026 18:53:58 +0100 Subject: [PATCH] feat: add webhook event history UI page (#164) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Adds a per-app webhook event history page at `/apps/{id}/webhooks` showing received webhook events with match/no-match status. ## Changes - **New template** `webhook_events.html` — displays webhook events in a table with time, event type, branch, commit SHA (linked when URL available), and match status badges - **New handler** `HandleAppWebhookEvents()` in `webhook_events.go` — fetches app and its webhook events (limit 100) - **New route** `GET /apps/{id}/webhooks` — registered in protected routes group - **Template registration** — added `webhook_events.html` to the template cache in `templates.go` - **Model enhancement** — added `ShortCommit()` method to `WebhookEvent` for truncated SHA display - **App detail link** — added "Event History" link in the Webhook URL card on the app detail page ## UI Follows the existing UI patterns (Tailwind CSS classes, Alpine.js `relativeTime`, badge styles, empty state, back-navigation). The page mirrors the deployments history page layout. closes [#85](https://git.eeqj.de/sneak/upaas/issues/85) Co-authored-by: clawbot Reviewed-on: https://git.eeqj.de/sneak/upaas/pulls/164 Co-authored-by: clawbot Co-committed-by: clawbot --- internal/handlers/webhook_events.go | 56 ++++++++++++++++++++ internal/models/webhook_event.go | 14 +++++ internal/server/routes.go | 1 + templates/app_detail.html | 5 +- templates/templates.go | 1 + templates/webhook_events.html | 79 +++++++++++++++++++++++++++++ 6 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 internal/handlers/webhook_events.go create mode 100644 templates/webhook_events.html diff --git a/internal/handlers/webhook_events.go b/internal/handlers/webhook_events.go new file mode 100644 index 0000000..d455cda --- /dev/null +++ b/internal/handlers/webhook_events.go @@ -0,0 +1,56 @@ +package handlers + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + + "sneak.berlin/go/upaas/internal/models" + "sneak.berlin/go/upaas/templates" +) + +// webhookEventsLimit is the number of webhook events to show in history. +const webhookEventsLimit = 100 + +// HandleAppWebhookEvents returns the webhook event history handler. +func (h *Handlers) HandleAppWebhookEvents() http.HandlerFunc { + tmpl := templates.GetParsed() + + return func(writer http.ResponseWriter, request *http.Request) { + appID := chi.URLParam(request, "id") + + application, findErr := models.FindApp(request.Context(), h.db, appID) + if findErr != nil { + h.log.Error("failed to find app", "error", findErr) + http.Error(writer, "Internal Server Error", http.StatusInternalServerError) + + return + } + + if application == nil { + http.NotFound(writer, request) + + return + } + + events, eventsErr := application.GetWebhookEvents( + request.Context(), + webhookEventsLimit, + ) + if eventsErr != nil { + h.log.Error("failed to get webhook events", + "error", eventsErr, + "app", appID, + ) + + events = []*models.WebhookEvent{} + } + + data := h.addGlobals(map[string]any{ + "App": application, + "Events": events, + }, request) + + h.renderTemplate(writer, tmpl, "webhook_events.html", data) + } +} diff --git a/internal/models/webhook_event.go b/internal/models/webhook_event.go index dd8352e..8c7f14a 100644 --- a/internal/models/webhook_event.go +++ b/internal/models/webhook_event.go @@ -52,6 +52,20 @@ func (w *WebhookEvent) Reload(ctx context.Context) error { return w.scan(row) } +// ShortCommit returns a truncated commit SHA for display. +func (w *WebhookEvent) ShortCommit() string { + if !w.CommitSHA.Valid { + return "" + } + + sha := w.CommitSHA.String + if len(sha) > shortCommitLength { + return sha[:shortCommitLength] + } + + return sha +} + func (w *WebhookEvent) insert(ctx context.Context) error { query := ` INSERT INTO webhook_events ( diff --git a/internal/server/routes.go b/internal/server/routes.go index 376b19d..f02636d 100644 --- a/internal/server/routes.go +++ b/internal/server/routes.go @@ -70,6 +70,7 @@ func (s *Server) SetupRoutes() { r.Post("/apps/{id}/deploy", s.handlers.HandleAppDeploy()) r.Post("/apps/{id}/deployments/cancel", s.handlers.HandleCancelDeploy()) r.Get("/apps/{id}/deployments", s.handlers.HandleAppDeployments()) + r.Get("/apps/{id}/webhooks", s.handlers.HandleAppWebhookEvents()) r.Get("/apps/{id}/deployments/{deploymentID}/logs", s.handlers.HandleDeploymentLogsAPI()) r.Get("/apps/{id}/deployments/{deploymentID}/download", s.handlers.HandleDeploymentLogDownload()) r.Get("/apps/{id}/logs", s.handlers.HandleAppLogs()) diff --git a/templates/app_detail.html b/templates/app_detail.html index 48234ac..2d62de9 100644 --- a/templates/app_detail.html +++ b/templates/app_detail.html @@ -77,7 +77,10 @@
-

Webhook URL

+
+

Webhook URL

+ Event History +

Add this URL as a push webhook in your Gitea repository:

{{.WebhookURL}} diff --git a/templates/templates.go b/templates/templates.go index 5f733d1..ab46739 100644 --- a/templates/templates.go +++ b/templates/templates.go @@ -44,6 +44,7 @@ func initTemplates() { "app_detail.html", "app_edit.html", "deployments.html", + "webhook_events.html", } pageTemplates = make(map[string]*template.Template) diff --git a/templates/webhook_events.html b/templates/webhook_events.html new file mode 100644 index 0000000..23fb295 --- /dev/null +++ b/templates/webhook_events.html @@ -0,0 +1,79 @@ +{{template "base" .}} + +{{define "title"}}Webhook Events - {{.App.Name}} - µPaaS{{end}} + +{{define "content"}} +{{template "nav" .}} + +
+ + +
+

Webhook Events

+
+ + {{if .Events}} +
+ + + + + + + + + + + + {{range .Events}} + + + + + + + + {{end}} + +
TimeEventBranchCommitStatus
+ + {{.EventType}}{{.Branch}} + {{if and .CommitSHA.Valid .CommitURL.Valid}} + {{.ShortCommit}} + {{else if .CommitSHA.Valid}} + {{.ShortCommit}} + {{else}} + - + {{end}} + + {{if .Matched}} + {{if .Processed}} + Matched + {{else}} + Matched (pending) + {{end}} + {{else}} + No match + {{end}} +
+
+ {{else}} +
+
+ + + +

No webhook events yet

+

Webhook events will appear here once your repository sends push notifications.

+
+
+ {{end}} +
+{{end}}