Adds persistent storage using modernc.org/sqlite (pure Go). Login attempts are deduplicated by (username, password, ip) with counts. Sessions and session logs are tracked with UUID IDs. Includes embedded SQL migrations, configurable retention with background pruning, and an in-memory store for tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
39 lines
814 B
Go
39 lines
814 B
Go
package storage
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"time"
|
|
)
|
|
|
|
// RunRetention periodically deletes records older than retentionDays.
|
|
// It runs one prune immediately on startup, then on the given interval.
|
|
// It returns when ctx is cancelled.
|
|
func RunRetention(ctx context.Context, store Store, retentionDays int, interval time.Duration, logger *slog.Logger) {
|
|
prune := func() {
|
|
cutoff := time.Now().UTC().AddDate(0, 0, -retentionDays)
|
|
n, err := store.DeleteRecordsBefore(ctx, cutoff)
|
|
if err != nil {
|
|
logger.Error("retention prune failed", "err", err)
|
|
return
|
|
}
|
|
if n > 0 {
|
|
logger.Info("retention prune completed", "deleted_rows", n)
|
|
}
|
|
}
|
|
|
|
prune()
|
|
|
|
ticker := time.NewTicker(interval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
prune()
|
|
}
|
|
}
|
|
}
|