feat(store): throttled per-day StampLogin + login_events delete cascade
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>
This commit is contained in:
@@ -13,10 +13,11 @@ import (
|
||||
// Deleting the users row cascades (ON DELETE CASCADE) to videos, transcripts,
|
||||
// summaries (→ sink_deliveries), video_connections, and the user_identities map
|
||||
// — referential-integrity cascades bypass RLS, so a user's child rows are removed
|
||||
// even though the deleting connection is scoped. summary_actions is the exception:
|
||||
// it carries a user_id but has NO foreign key to users (migration 002), so the
|
||||
// cascade does not reach it; it is deleted explicitly in the same scoped
|
||||
// transaction. Deleting an absent user is a no-op (idempotent).
|
||||
// even though the deleting connection is scoped. summary_actions and login_events
|
||||
// are the exceptions: each carries a user_id but has NO foreign key to users
|
||||
// (migrations 002 and 010), so the cascade does not reach them; they are deleted
|
||||
// explicitly in the same scoped transaction. Deleting an absent user is a no-op
|
||||
// (idempotent).
|
||||
//
|
||||
// This is tapir-side only (decision 2026-06-03): it removes all tapir data; the
|
||||
// Dex login identity is left untouched — a later login simply re-enters
|
||||
@@ -28,6 +29,10 @@ func (s *Store) DeleteUser(ctx context.Context, userID string) error {
|
||||
`DELETE FROM summary_actions WHERE user_id = $1`, userID); err != nil {
|
||||
return fmt.Errorf("store: delete summary_actions: %w", err)
|
||||
}
|
||||
if _, err := tx.Exec(ctx,
|
||||
`DELETE FROM login_events WHERE user_id = $1`, userID); err != nil {
|
||||
return fmt.Errorf("store: delete login_events: %w", err)
|
||||
}
|
||||
if _, err := tx.Exec(ctx,
|
||||
`DELETE FROM users WHERE id = $1`, userID); err != nil {
|
||||
return fmt.Errorf("store: delete user: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user