feat(api): add REST API endpoints for repository webhook delivery history (#38775)

Webhook delivery history (request/response, status, timestamp) was only
reachable through the repo settings UI, which shows the last ~10
deliveries with no filtering. There was no way to fetch this data
programmatically for debugging integrations or auditing webhook
activity.

This adds two read endpoints at the repository level:

- `GET /repos/{owner}/{repo}/hooks/{id}/deliveries` — paginated list of
  a hook's deliveries, most recent first
- `GET /repos/{owner}/{repo}/hooks/{id}/deliveries/{uuid}` — a single
  delivery, including request/response headers, body, and status

Both reuse the existing `hook_task` storage and retention policy (the
`CleanupHookTaskTable` cron job) that already back the "Recent
Deliveries" web UI, and are gated by the same `reqAdmin()` +
`reqWebhooksEnabled()` guard as the existing hook endpoints.

Org, user, and admin/system-level webhooks are intentionally out of
scope for this PR to keep it reviewable; they can follow as separate
PRs using the same pattern.

## Usage

```bash
curl -H "Authorization: token $TOKEN" \
  https://gitea.example.com/api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries

curl -H "Authorization: token $TOKEN" \
  https://gitea.example.com/api/v1/repos/{owner}/{repo}/hooks/{id}/deliveries/{uuid}
```

## Testing

- Unit tests for the new model-layer query functions
  (`ListHookTasks`, `GetHookTaskByUUID`)
- Integration tests covering: list + get, an undelivered task's response
  is `null`, a hook/delivery UUID from another repository 404s instead
  of leaking across repos, and pagination (`page`/`limit`, `Link` and
  `X-Total-Count` headers, correct ordering/no overlap across pages)
- `make lint-go`, `make generate-swagger`

Fixes: https://github.com/go-gitea/gitea/issues/38463.
Co-authored-by: bircni <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
authored and GitHub committed 2026-10-24 00:18:51 +00:00
1 parent bcb4559c10
commit 96bee6c5a5
11 files changed
+824 -6

No files matched your search

+44
View File
@@ -46,6 +46,50 @@ type Hook struct {
// HookList represents a list of API hook.
type HookList []*Hook
// HookDeliveryRequest represents the request information of a webhook delivery
type HookDeliveryRequest struct {
// The URL the delivery was sent to
URL string `json:"url"`
// The HTTP method used for the delivery
HTTPMethod string `json:"http_method"`
// The headers sent with the delivery
Headers map[string]string `json:"headers"`
// The body sent with the delivery
Body string `json:"body"`
}
// HookDeliveryResponse represents the response information of a webhook delivery
type HookDeliveryResponse struct {
// The HTTP status code of the response
Status int `json:"status"`
// The headers received in the response
Headers map[string]string `json:"headers"`
// The body received in the response
Body string `json:"body"`
}
// HookDelivery represents a single delivery attempt for a webhook
type HookDelivery struct {
// The unique identifier of the delivery
UUID string `json:"uuid"`
// The event type that triggered this delivery
EventType string `json:"event_type"`
// Whether the delivery has been sent
IsDelivered bool `json:"is_delivered"`
// Whether the delivery was successful (2xx response)
IsSucceeded bool `json:"is_succeeded"`
// swagger:strfmt date-time
// The date and time when the delivery was sent
Delivered *time.Time `json:"delivered_at"`
// The request that was sent
Request *HookDeliveryRequest `json:"request"`
// The response that was received
Response *HookDeliveryResponse `json:"response"`
}
// HookDeliveryList represents a list of API hook deliveries.
type HookDeliveryList []*HookDelivery
// CreateHookOptionConfig has all config options in it
// required are "content_type" and "url" Required
type CreateHookOptionConfig map[string]string