check / check (push) Successful in 4m45s
The receiver dropped the query string of every request it received, so a sender's URL parameters were silently lost. Each event now keeps it, as sent, in a new `raw_query` column of the per-webhook `events` table; a resubmitted copy carries its original's. The event log and the event's page show it in the shared request block, the event log leaving out one over 32 KiB with a link, as for headers. The archive and log targets carry it. HTTP targets gain "Pass the query string on to this target", off by default: on, deliveries, replays and resubmits append it to the target URL, joined with `&` to one already there. The access log still hides it. Model: opus-5-5
60 lines
2.9 KiB
Go
60 lines
2.9 KiB
Go
package database
|
|
|
|
import (
|
|
"time"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Event represents a captured webhook event
|
|
//
|
|
//nolint:lll // a struct tag cannot wrap
|
|
type Event struct {
|
|
BaseModel
|
|
|
|
// CreatedAt and DeletedAt repeat the BaseModel fields only to index
|
|
// them for retention, which finds events by age. Its lookups carry
|
|
// GORM's "deleted_at IS NULL" (see DeliveryResult) and compare
|
|
// created_at with <, so their index has deleted_at first: SQLite
|
|
// narrows by a < only on the last column it uses. Its final delete
|
|
// has no deleted_at condition and uses the index on created_at
|
|
// alone. The other tables keep the unindexed BaseModel created_at.
|
|
// DeletedAt is also the second column of the resubmitted_from_id
|
|
// index, for the reason DeliveryResult gives. The entrypoint_id
|
|
// index, for the webhook page's entrypoint list, has it second too,
|
|
// resubmitted_from_id third, and created_at last, which the list
|
|
// compares with a range.
|
|
CreatedAt time.Time `gorm:"index;index:idx_events_deleted_at_created_at,priority:2;index:idx_events_entrypoint_id,priority:4" json:"createdAt"`
|
|
DeletedAt gorm.DeletedAt `gorm:"index:idx_events_deleted_at_created_at,priority:1;index:idx_events_resubmitted_from_id,priority:2;index:idx_events_entrypoint_id,priority:2" json:"deletedAt,omitzero"`
|
|
|
|
WebhookID string `gorm:"type:uuid;not null" json:"webhookId"`
|
|
EntrypointID string `gorm:"type:uuid;not null;index:idx_events_entrypoint_id,priority:1" json:"entrypointId"`
|
|
|
|
// Request data. RawQuery is the receiving request's query string
|
|
// as sent, without the leading "?".
|
|
Method string `gorm:"not null" json:"method"`
|
|
RawQuery string `gorm:"type:text" json:"rawQuery"`
|
|
Headers string `gorm:"type:text" json:"headers"` // JSON
|
|
Body string `gorm:"type:text" json:"body"`
|
|
ContentType string `json:"contentType"`
|
|
|
|
// BodyBytes is the size of Body in bytes, recorded when the event
|
|
// is stored, so that the recent events list, which reads only the
|
|
// start of each body, knows the whole body's size.
|
|
BodyBytes int64 `gorm:"not null" json:"bodyBytes"`
|
|
|
|
// ResubmittedFromID names the event this one was copied from by
|
|
// an operator resubmit. It is nil for an event that arrived on
|
|
// the receiver, which is every event created before the column
|
|
// existed. It is not a foreign key: the source event can be
|
|
// reaped by retention while its copies remain, and the id is
|
|
// kept as the record of where the copy came from either way.
|
|
ResubmittedFromID *string `gorm:"type:uuid;index:idx_events_resubmitted_from_id,priority:1;index:idx_events_entrypoint_id,priority:3" json:"resubmittedFromId,omitempty"`
|
|
|
|
// Relations. No model marshals the record it belongs to, so
|
|
// Webhook and Entrypoint are left out of the JSON.
|
|
Webhook Webhook `json:"-"`
|
|
Entrypoint Entrypoint `json:"-"`
|
|
Deliveries []Delivery `json:"deliveries,omitempty"`
|
|
}
|