feat(user): magic-link expired-token cleanup loop (ADR-0028 Phase A consequence) (#65)
All checks were successful
CI/CD Pipeline / Build Docker Cache (push) Successful in 11s
CI/CD Pipeline / CI Pipeline (push) Successful in 4m27s
CI/CD Pipeline / Trigger Docker Push (push) Successful in 6s

Co-authored-by: Gabriel Radureau <arcodange@gmail.com>
Co-committed-by: Gabriel Radureau <arcodange@gmail.com>
This commit was merged in pull request #65.
This commit is contained in:
2026-05-05 13:07:01 +02:00
committed by arcodange
parent 6ed95165d3
commit b6a6a2b3d7
4 changed files with 139 additions and 2 deletions

View File

@@ -0,0 +1,56 @@
package user
import (
"context"
"time"
"github.com/rs/zerolog/log"
)
// MagicLinkCleanupRunner periodically deletes expired magic-link tokens
// (ADR-0028 Phase A consequence — the rows accumulate without cleanup
// otherwise, and stale rows are pure overhead since the token plaintext
// is never stored).
type MagicLinkCleanupRunner struct {
repo MagicLinkRepository
}
// NewMagicLinkCleanupRunner creates a new cleanup runner.
func NewMagicLinkCleanupRunner(repo MagicLinkRepository) *MagicLinkCleanupRunner {
return &MagicLinkCleanupRunner{repo: repo}
}
// StartCleanupLoop runs the cleanup pass every `interval`. Stops when ctx
// is cancelled. interval <= 0 disables the loop.
func (r *MagicLinkCleanupRunner) StartCleanupLoop(ctx context.Context, interval time.Duration) {
if interval <= 0 {
return
}
go func() {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
_, _ = r.runOnce(ctx)
}
}
}()
}
// runOnce performs a single cleanup pass. Returns the count of deleted rows.
// Exposed for testing — tests drive runOnce directly instead of waiting on
// the ticker.
func (r *MagicLinkCleanupRunner) runOnce(ctx context.Context) (int64, error) {
n, err := r.repo.DeleteExpiredMagicLinkTokens(ctx, time.Now())
if err != nil {
log.Error().Ctx(ctx).Err(err).Msg("magic-link cleanup: delete failed")
return 0, err
}
if n > 0 {
log.Trace().Ctx(ctx).Int64("deleted", n).Msg("magic-link cleanup: removed expired tokens")
}
return n, nil
}