A target's settings move from the per-type JSON string in targets.config to columns: url, headers, timeout_seconds, forward_query, archive_expiry and archive_rotation. A column a type does not use stays empty. headers holds the header map as JSON text through GORM's JSON serializer, and archive_expiry holds a time.Duration, 0 meaning never, so nothing that reads a target parses it. The forms check each setting once, with the same refusals and messages, and the edit form is filled from the columns, so the per-type config structs, their parsers and the "could not be read" branches are gone. A delivery task carries the six settings in place of the JSON string. Model: opus-5-5
This commit is contained in:
+14
-70
@@ -1,7 +1,6 @@
|
||||
package archive
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
@@ -18,8 +17,9 @@ import (
|
||||
"sneak.berlin/go/webhooker/internal/gormlog"
|
||||
)
|
||||
|
||||
// ExpiryNever is the expiry sentinel (and default) that
|
||||
// disables pruning so archived rows are kept forever.
|
||||
// ExpiryNever is the archive expiry, as the target forms take it,
|
||||
// that disables pruning so archived rows are kept forever. It is
|
||||
// stored as an ArchiveExpiry of 0.
|
||||
const ExpiryNever = "never"
|
||||
|
||||
// archiveReopenDebounce bounds how often an archive file is
|
||||
@@ -79,19 +79,6 @@ var (
|
||||
)
|
||||
)
|
||||
|
||||
// DatabaseTargetConfig is the optional per-target JSON config
|
||||
// for a database (archive) target.
|
||||
type DatabaseTargetConfig struct {
|
||||
// Expiry is a Go duration (e.g. "720h") after which
|
||||
// archived rows are pruned, or "never" (the default) to
|
||||
// keep them forever.
|
||||
Expiry string `json:"expiry"`
|
||||
|
||||
// Rotation is none (the default), monthly, daily or hourly: see
|
||||
// archivePeriod.
|
||||
Rotation string `json:"rotation"`
|
||||
}
|
||||
|
||||
// archivedEvent is one fully captured webhook event stored in a
|
||||
// database target's archive for long-term retention. It is a
|
||||
// self-contained copy — independent of the per-webhook event
|
||||
@@ -112,63 +99,20 @@ type archivedEvent struct {
|
||||
ArchivedAt time.Time `gorm:"index"`
|
||||
}
|
||||
|
||||
// ParseExpiry reads the optional expiry from a database
|
||||
// target's config JSON. An empty config, an empty expiry, or
|
||||
// the literal "never" all mean keep forever, returned as a zero
|
||||
// duration. Any other value must parse as a positive Go
|
||||
// duration; a set-but-invalid value (unparseable, zero, or
|
||||
// negative) is an error rather than a silent default, matching
|
||||
// ValidateExpiry at target creation.
|
||||
func ParseExpiry(
|
||||
configJSON string,
|
||||
) (time.Duration, error) {
|
||||
if configJSON == "" {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
var cfg DatabaseTargetConfig
|
||||
|
||||
err := json.Unmarshal([]byte(configJSON), &cfg)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf(
|
||||
"parsing database target config: %w", err,
|
||||
)
|
||||
}
|
||||
|
||||
if cfg.Expiry == "" || cfg.Expiry == ExpiryNever {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
dur, err := time.ParseDuration(cfg.Expiry)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf(
|
||||
"parsing archive expiry %q: %w", cfg.Expiry, err,
|
||||
)
|
||||
}
|
||||
|
||||
if dur <= 0 {
|
||||
return 0, fmt.Errorf(
|
||||
"%w: %q", errArchiveExpiryNotPositive, cfg.Expiry,
|
||||
)
|
||||
}
|
||||
|
||||
return dur, nil
|
||||
}
|
||||
|
||||
// ValidateExpiry checks a user-supplied archive expiry
|
||||
// for a database target at configuration time. Valid values are
|
||||
// empty, "never" (both meaning keep forever), or a positive Go
|
||||
// duration such as "720h". Anything else is an error, so a bad
|
||||
// expiry is rejected when the target is created rather than
|
||||
// failing every subsequent delivery.
|
||||
func ValidateExpiry(expiry string) error {
|
||||
// ParseExpiry reads a user-supplied archive expiry for a database
|
||||
// target, as the target forms submit it, into the ArchiveExpiry the
|
||||
// target stores. Empty and "never" both mean keep forever, which is
|
||||
// 0; anything else must be a positive Go duration such as "720h". Any
|
||||
// other value is an error, so a bad expiry is refused at the form
|
||||
// rather than stored.
|
||||
func ParseExpiry(expiry string) (time.Duration, error) {
|
||||
if expiry == "" || expiry == ExpiryNever {
|
||||
return nil
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
dur, err := time.ParseDuration(expiry)
|
||||
if err != nil {
|
||||
return fmt.Errorf(
|
||||
return 0, fmt.Errorf(
|
||||
"expiry must be %q or a Go duration "+
|
||||
"such as \"720h\": %w",
|
||||
ExpiryNever, err,
|
||||
@@ -176,12 +120,12 @@ func ValidateExpiry(expiry string) error {
|
||||
}
|
||||
|
||||
if dur <= 0 {
|
||||
return fmt.Errorf(
|
||||
return 0, fmt.Errorf(
|
||||
"%w: %q", errArchiveExpiryNotPositive, expiry,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
return dur, nil
|
||||
}
|
||||
|
||||
// archiveWriter owns one database target's archive SQLite files.
|
||||
|
||||
Reference in New Issue
Block a user