909145e5c8
Global administrators can set delivery journal retention (7-365 days) in SQLite; SEND_LOG_RETENTION_DAYS seeds the initial default and the log-tailer re-reads the value every prune cycle without a restart. Co-authored-by: Cursor <cursoragent@cursor.com>
104 lines
3.1 KiB
Go
104 lines
3.1 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
const (
|
|
// SendLogRetentionDaysKey is the settings table key for delivery-journal
|
|
// retention (architecture.md § Persistence).
|
|
SendLogRetentionDaysKey = "send_log_retention_days"
|
|
SendLogRetentionDaysMin = 7
|
|
SendLogRetentionDaysMax = 365
|
|
// SendLogRetentionDaysDefault matches SEND_LOG_RETENTION_DAYS when unset.
|
|
SendLogRetentionDaysDefault = 90
|
|
)
|
|
|
|
// ErrSendLogRetentionDaysOutOfRange is returned when retention is outside the
|
|
// allowed panel range.
|
|
var ErrSendLogRetentionDaysOutOfRange = errors.New("send log retention days out of range")
|
|
|
|
// ValidateSendLogRetentionDays checks the panel-allowed retention window.
|
|
func ValidateSendLogRetentionDays(days int) error {
|
|
if days < SendLogRetentionDaysMin || days > SendLogRetentionDaysMax {
|
|
return fmt.Errorf("%w: must be between %d and %d days", ErrSendLogRetentionDaysOutOfRange, SendLogRetentionDaysMin, SendLogRetentionDaysMax)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func sendLogRetentionFallback(envDefault int) int {
|
|
if envDefault > 0 {
|
|
if err := ValidateSendLogRetentionDays(envDefault); err == nil {
|
|
return envDefault
|
|
}
|
|
}
|
|
return SendLogRetentionDaysDefault
|
|
}
|
|
|
|
// GetSendLogRetentionDays returns the effective retention window. When the
|
|
// setting is missing or invalid, envDefault is used (guide § Environment
|
|
// variables: SEND_LOG_RETENTION_DAYS).
|
|
func (s *Store) GetSendLogRetentionDays(envDefault int) (int, error) {
|
|
raw, err := s.GetSetting(SendLogRetentionDaysKey)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if raw == "" {
|
|
return sendLogRetentionFallback(envDefault), nil
|
|
}
|
|
days, err := strconv.Atoi(raw)
|
|
if err != nil || ValidateSendLogRetentionDays(days) != nil {
|
|
return sendLogRetentionFallback(envDefault), nil
|
|
}
|
|
return days, nil
|
|
}
|
|
|
|
// SetSendLogRetentionDays persists the panel-configured retention window.
|
|
func (s *Store) SetSendLogRetentionDays(days int) error {
|
|
if err := ValidateSendLogRetentionDays(days); err != nil {
|
|
return err
|
|
}
|
|
return s.SetSetting(SendLogRetentionDaysKey, strconv.Itoa(days))
|
|
}
|
|
|
|
// EnsureSendLogRetentionDays seeds the setting from envDefault when it has
|
|
// never been written (first panel start after upgrade).
|
|
func (s *Store) EnsureSendLogRetentionDays(envDefault int) error {
|
|
raw, err := s.GetSetting(SendLogRetentionDaysKey)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if raw != "" {
|
|
return nil
|
|
}
|
|
return s.SetSendLogRetentionDays(sendLogRetentionFallback(envDefault))
|
|
}
|
|
|
|
// GetSetting returns a settings value or empty string when missing.
|
|
func (s *Store) GetSetting(key string) (string, error) {
|
|
var value string
|
|
err := s.db.QueryRow("SELECT value FROM settings WHERE key = ?", key).Scan(&value)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return "", nil
|
|
}
|
|
if err != nil {
|
|
return "", fmt.Errorf("get setting %s: %w", key, err)
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
// SetSetting upserts a settings key.
|
|
func (s *Store) SetSetting(key, value string) error {
|
|
_, err := s.db.Exec(
|
|
"INSERT INTO settings (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value",
|
|
key, value,
|
|
)
|
|
if err != nil {
|
|
return fmt.Errorf("set setting %s: %w", key, err)
|
|
}
|
|
return nil
|
|
}
|