StampLogin appends one login_events row per user per day via an atomic INSERT ... SELECT ... WHERE NOT EXISTS, run through withUser so the throttle probe is itself RLS-scoped to the caller. DeleteUser now deletes login_events explicitly (no FK = no cascade — the summary_actions footgun, repeated). Extends the two-user RLS isolation proof and the delete-account proof to cover login_events, and adds throttle / new-day / user-scoping tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
42 lines
1.6 KiB
Go
42 lines
1.6 KiB
Go
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
|
|
}
|