package store import ( "context" "fmt" "github.com/jackc/pgx/v5" ) // StampLogin records that the user was active today, throttled to one row per // user per day. It is the read-side counterpart to SetAction: the middleware // calls it on every authenticated request, but the append happens at most once a // day so login_events stays small and the signal clean (one row = one active // day, not one request). // // The check-and-insert is a single atomic statement: the INSERT ... SELECT ... // WHERE NOT EXISTS only writes when no row for this user has seen_at in today // (date_trunc('day', NOW()), server timezone). It runs through withUser, so the // NOT EXISTS probe is itself RLS-scoped to the calling user via the // tapir.current_user_id GUC — one user's stamp can never be suppressed or // triggered by another user's rows. The explicit user_id predicate also keeps the // probe on the (user_id, seen_at) index. // // A unique constraint is deliberately not used: under concurrent same-day // requests the worst case is two rows for one day, which the gate query collapses // to a single week bucket anyway — not worth a write-blocking constraint. func (s *Store) StampLogin(ctx context.Context, userID string) error { if err := s.withUser(ctx, userID, func(tx pgx.Tx) error { _, err := tx.Exec(ctx, `INSERT INTO login_events (user_id) SELECT $1 WHERE NOT EXISTS ( SELECT 1 FROM login_events WHERE user_id = $1 AND seen_at >= date_trunc('day', NOW()) )`, userID) return err }); err != nil { return fmt.Errorf("store: stamp login: %w", err) } return nil }