package runners import ( "context" "log/slog" "time" ) // AuditRetentionRepo is the minimal surface this runner needs. // internal/audit.Reader satisfies it (PurgeBefore). type AuditRetentionRepo interface { PurgeBefore(ctx context.Context, before time.Time) (int64, error) } // AuditRetention deletes audit_logs older than retention. Periodic; idempotent. type AuditRetention struct { repo AuditRetentionRepo retention time.Duration log *slog.Logger } // NewAuditRetention builds the audit-log retention purger. func NewAuditRetention(repo AuditRetentionRepo, retention time.Duration, log *slog.Logger) *AuditRetention { if log == nil { log = slog.Default() } return &AuditRetention{repo: repo, retention: retention, log: log} } // Run purges once. A non-positive retention disables the purge (safety: never // delete the whole table when misconfigured). func (r *AuditRetention) Run(ctx context.Context) { if r.retention <= 0 { return } cutoff := time.Now().Add(-r.retention) rows, err := r.repo.PurgeBefore(ctx, cutoff) if err != nil { r.log.Error("audit_retention.purge", "err", err.Error()) return } if rows > 0 { r.log.Info("audit_retention.purged", "count", rows, "before", cutoff.UTC().Format(time.RFC3339)) } }