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
179 lines
4.1 KiB
Go
179 lines
4.1 KiB
Go
package handlers
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"time"
|
|
|
|
"sneak.berlin/go/webhooker/internal/database"
|
|
)
|
|
|
|
// WebhookListItem holds data for the webhook list view.
|
|
type WebhookListItem struct {
|
|
database.Webhook
|
|
|
|
EntrypointCount int
|
|
InactiveEntrypointCount int
|
|
TargetCount int
|
|
InactiveTargetCount int
|
|
|
|
// EventCount is how many events the webhook holds, LastEventAt
|
|
// when the newest arrived (nil before the first), and
|
|
// FailedLast24Hours how many of its deliveries failed in the last
|
|
// 24 hours. When the webhook's event database could not be read,
|
|
// EventsUnreadable is set and these three are not known.
|
|
EventCount int64
|
|
LastEventAt *time.Time
|
|
FailedLast24Hours int64
|
|
EventsUnreadable bool
|
|
}
|
|
|
|
// HandleSourceList shows a list of user's webhooks.
|
|
func (h *Handlers) HandleSourceList() 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
|
|
}
|
|
|
|
var webhooks []database.Webhook
|
|
|
|
err := h.db.DB().Where(
|
|
"user_id = ?", userID,
|
|
).Order("created_at DESC").Find(&webhooks).Error
|
|
if err != nil {
|
|
h.serverError(w, r, "failed to list webhooks", err)
|
|
|
|
return
|
|
}
|
|
|
|
items, err := h.buildWebhookListItems(webhooks)
|
|
if err != nil {
|
|
h.serverError(w, r, "failed to list webhooks", err)
|
|
|
|
return
|
|
}
|
|
|
|
data := map[string]any{
|
|
"Webhooks": items,
|
|
}
|
|
|
|
h.renderTemplate(w, r, "sources_list.html", data)
|
|
}
|
|
}
|
|
|
|
// buildWebhookListItems builds the list's entry for each webhook. It
|
|
// fails when the main database cannot be read. A webhook whose event
|
|
// database cannot be read is marked on its own entry, and the error is
|
|
// logged.
|
|
func (h *Handlers) buildWebhookListItems(
|
|
webhooks []database.Webhook,
|
|
) ([]WebhookListItem, error) {
|
|
items := make([]WebhookListItem, len(webhooks))
|
|
since := time.Now().Add(-longWindow)
|
|
|
|
for i := range webhooks {
|
|
item := &items[i]
|
|
item.Webhook = webhooks[i]
|
|
|
|
var err error
|
|
|
|
item.EntrypointCount, item.InactiveEntrypointCount, err =
|
|
h.countWithInactive(&database.Entrypoint{}, item.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
item.TargetCount, item.InactiveTargetCount, err =
|
|
h.countWithInactive(&database.Target{}, item.ID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Opening an event database that does not exist would create
|
|
// it, and it would hold nothing to count.
|
|
if !h.dbMgr.DBExists(item.ID) {
|
|
continue
|
|
}
|
|
|
|
err = h.readListEventFigures(item, since)
|
|
if err != nil {
|
|
h.log.Error(
|
|
"failed to read webhook list figures",
|
|
"webhook_id", item.ID,
|
|
"error", err,
|
|
)
|
|
|
|
item.EventsUnreadable = true
|
|
}
|
|
}
|
|
|
|
return items, nil
|
|
}
|
|
|
|
// countWithInactive returns how many entrypoints or targets, as model
|
|
// says, a webhook has, and how many of them are inactive.
|
|
func (h *Handlers) countWithInactive(
|
|
model any, webhookID string,
|
|
) (int, int, error) {
|
|
var active []bool
|
|
|
|
err := h.db.DB().Model(model).
|
|
Where("webhook_id = ?", webhookID).
|
|
Pluck("active", &active).Error
|
|
if err != nil {
|
|
return 0, 0, fmt.Errorf(
|
|
"reading active flags of webhook %s: %w", webhookID, err,
|
|
)
|
|
}
|
|
|
|
inactive := 0
|
|
|
|
for _, a := range active {
|
|
if !a {
|
|
inactive++
|
|
}
|
|
}
|
|
|
|
return len(active), inactive, nil
|
|
}
|
|
|
|
// readListEventFigures fills in the figures the list shows from the
|
|
// webhook's event database, with the statistics pane's own queries:
|
|
// the event count and last arrival from the event totals row, and the
|
|
// deliveries that failed since the given time from the deliveries'
|
|
// status index.
|
|
func (h *Handlers) readListEventFigures(
|
|
item *WebhookListItem, since time.Time,
|
|
) error {
|
|
webhookDB, err := h.dbMgr.GetDB(item.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var totals database.EventTotals
|
|
|
|
err = webhookDB.Take(&totals).Error
|
|
if err != nil {
|
|
return fmt.Errorf("reading event totals: %w", err)
|
|
}
|
|
|
|
item.EventCount = totals.Events - totals.EventsRemoved
|
|
item.LastEventAt = totals.LastEventAt
|
|
|
|
byTarget, err := finishedByTarget(webhookDB, since)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, f := range byTarget {
|
|
item.FailedLast24Hours += f.Failed
|
|
}
|
|
|
|
return nil
|
|
}
|