Allow retention_days of 0 to mean retain forever (closes #79)
All checks were successful
check / check (push) Successful in 3m4s
All checks were successful
check / check (push) Successful in 3m4s
RetentionDays carried gorm:"default:30", so GORM substituted 30 for a zero value while building the insert. A webhook could therefore never be configured to keep its events indefinitely: the reaper's retain-forever branch existed but was unreachable from the normal create and edit flows. Introduce database.RetentionForeverDays = 365 * 1000 as the sentinel for "retain forever" and a Webhook.BeforeSave hook that rewrites any non-positive RetentionDays to it. The rewrite has to live in the hook rather than at the call sites: GORM applies the column default while converting the model to insert values, which happens after BeforeSave, so anything later loses that race. Putting it on the model also means a future call site, such as the planned REST API, cannot bypass it. The reaper now skips a webhook when Webhook.RetainsForever reports true, which recognises the sentinel and keeps honouring the old <= 0 values for rows written before it existed. Without this the sentinel, being positive, would have produced a cutoff a thousand years in the past and a DELETE matching nothing on every sweep. Form handling is shared by create and edit through parseRetentionDays so the two cannot drift: an empty field keeps the previous behaviour (default on create, unchanged on edit), 0 is honoured, and an unparseable or negative value is a 400 that re-renders the form rather than a silently substituted default. The retention inputs drop max="365". That cap was not cosmetic: the edit form pre-fills the stored value, so a retain-forever webhook rendered 365000 into an input capped at 365 and browser validation would have blocked saving any edit to it. min becomes 0 with a hint explaining what 0 does, and the list and detail views render a RetentionLabel of "forever" instead of a raw day count. The 30-day default is consolidated into database.DefaultRetentionDays, referenced from the handler and from the create form's pre-filled value, with a test asserting it agrees with the struct tag that cannot reference it.
This commit is contained in:
@@ -114,7 +114,8 @@ func (r *RetentionReaper) run(ctx context.Context) {
|
||||
}
|
||||
|
||||
// sweep lists every webhook from the main database and reaps expired
|
||||
// rows from each per-webhook database whose RetentionDays is positive.
|
||||
// rows from each per-webhook database that has a finite retention
|
||||
// policy. Webhooks set to retain forever are skipped entirely.
|
||||
func (r *RetentionReaper) sweep(ctx context.Context) {
|
||||
var webhooks []Webhook
|
||||
|
||||
@@ -139,8 +140,13 @@ func (r *RetentionReaper) sweep(ctx context.Context) {
|
||||
|
||||
wh := webhooks[i]
|
||||
|
||||
// RetentionDays of zero or less means retain forever.
|
||||
if wh.RetentionDays <= 0 {
|
||||
// Skip retain-forever webhooks before building any query.
|
||||
// RetainsForever covers both the RetentionForeverDays
|
||||
// sentinel and the non-positive values that predate it: the
|
||||
// sentinel is a positive number, so without this the reaper
|
||||
// would compute a cutoff a thousand years in the past and
|
||||
// issue a DELETE matching nothing on every single sweep.
|
||||
if wh.RetainsForever() {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user