feat(webhook): inbound delivery audit log
Build / build (push) Successful in 10m35s

Persists every inbound webhook hit (project + site) so users can debug
"why didn't my deploy fire?" without grepping daemon logs. Surfaces a
14-day rolling history under the WebhookPanel on each project + site
detail page; refreshes every 30s while open. Daily cron prunes records
older than 14 days alongside the existing event log prune.

Schema:
- webhook_deliveries(id, target_type, target_id, target_name, received_at,
  source_ip, signature_state, status_code, outcome, detail, body_size)
- indexes on (target_type,target_id,received_at) and (received_at)

Backend:
- store: WebhookDelivery model + Insert/List/Prune helpers
- webhook/handler: deferred recordDelivery() captures the final outcome
  on every return path including HMAC rejects, image mismatch, no-stage,
  auto_deploy=false, and successful deploys; signatureStateFor()
  classifies "unconfigured" vs "missing" vs "invalid" vs "valid"
- api: GET /api/{projects,sites}/{id}/webhook/deliveries with
  parseLimit() helper (default 50, max 200)
- main: daily prune cron retains the last 14 days

Frontend:
- WebhookDeliveryLog.svelte: panel with refresh button, status code +
  outcome + signature badges, relative time tooltip-on-hover for
  absolute time, source IP column
- Mounted below WebhookPanel on project + site detail pages
- en/ru i18n strings for outcome/signature enums and column labels
This commit is contained in:
2026-05-07 02:40:39 +03:00
parent 831b5c1a43
commit 0f60a7a5db
12 changed files with 591 additions and 16 deletions
+16
View File
@@ -183,6 +183,22 @@ func main() {
}); err != nil {
slog.Warn("failed to schedule event prune cron", "error", err)
}
// Webhook delivery log: keep 14 days of audit trail. Same daily cadence
// so an admin always has a recent window for debugging without
// unbounded growth on a noisy CI.
if _, err := cronScheduler.AddFunc("@daily", func() {
cutoff := time.Now().UTC().AddDate(0, 0, -14).Format("2006-01-02 15:04:05")
pruned, err := db.PruneWebhookDeliveriesBefore(cutoff)
if err != nil {
slog.Error("webhook delivery prune failed", "error", err)
return
}
if pruned > 0 {
slog.Info("pruned old webhook deliveries", "count", pruned)
}
}); err != nil {
slog.Warn("failed to schedule webhook delivery prune cron", "error", err)
}
cronScheduler.Start()
// Subscribe to error events and forward notifications.