All checks were successful
Check / check (push) Successful in 4s
## 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](#85)
Co-authored-by: clawbot <clawbot@noreply.git.eeqj.de>
Reviewed-on: #164
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
57 lines
1.2 KiB
Go
57 lines
1.2 KiB
Go
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)
|
|
}
|
|
}
|