feat: GDPR data controls — soft-delete + data export - #35
Open
TrainTravel wants to merge 3 commits into
Open
Conversation
Surfaces right-to-erasure (Article 17) from a new AccountScreen reachable from a footer link on HomeScreen. Deletion is scheduled 30 days out via the schedule-account-deletion edge function and stored on a new profiles table. Returning during the window reveals a yellow banner with a one-click cancel. v1 lazy-purges on return after the deadline (no cron yet) — documented as a known limitation.
Surfaces right-to-portability (Article 20) as a "Download my data" button on the AccountScreen. The new export-user-data edge function aggregates every row tagged with the caller's user_id across journal_entries, thoughts, clusters, cluster_thoughts, proposals, experiments, and experiment_checkins into a single versioned JSON payload. The browser writes it to disk via a Blob + download attribute — no new dependency.
Post-rebase onto current main (object-form t() / bilingual() landed in PR #32 between this branch's original commits and main). Ran scripts/codemod-t-object-form.mjs against the new GDPR files. 27 call sites converted across AccountScreen.tsx + HomeScreen.tsx. All tests still pass (AccountScreen 11/11; full suite green modulo pre-existing useJournal startFreeWrite). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two GDPR Tier-1 features in one PR, surfaced from a new
AccountScreen:How it works
AccountScreenreachable from a footer "Account & data" link onHomeScreen.schedule-account-deletionedge function → setsprofiles.scheduled_deletion_at = now() + 30 days→ signs the user out.cancel-account-deletion.hard-delete-accountif the user returns after the deadline (CASCADE throughthoughts,clusters,journal_entries,experiments,feedback,profiles, and finallyauth.users).export-user-data→ returns versioned JSON (schema_version: 1) with every row tagged with the caller'suser_id→ browser saves asoutputfirst-export-YYYY-MM-DD.jsonviaBlob+<a download>(no new dependency).⚠ DEPLOYMENT STEPS — must run before merging
Apply migration:
Or apply
supabase/migrations/20260523145704_account_soft_delete.sqlvia the dashboard. Createspublic.profileswith strict RLS (id = auth.uid()).Set secret:
Required by all 4 new edge functions for cross-table writes.
Deploy edge functions:
v1 limitation (documented in code + CHANGELOG)
No
pg_cronjob yet. A user who never returns after day 30 is not actually purged until they return. Production should add a scheduled job that invokeshard-delete-accountfor every row withscheduled_deletion_at < now(). Captured as a follow-up.Test plan
npx tsc --noEmit— cleannpx vitest run— 195 passed / 196 total (only pre-existinguseJournal startFreeWritefails; confirmed unchanged on main)AccountScreen.test.tsx(render, modal flow, banner, cancel, lazy purge, export click, in-flight disabled state)useJournal.test.ts(openAccount step transition + goHome from account)e2e/gdpr-account.spec.ts(footer link opens screen, delete schedules POST, banner+cancel, download triggers .json)t()calls in AccountScreen + HomeScreen to object form (matches post-refactor: t() / bilingual() positional → object form #32 main)Out of scope (follow-up PRs)
/privacypage from PR docs: sync CLAUDE.md memory with latest CHANGELOG state #34 has the route + UI; legal text still placeholder)pg_cronhard-delete worker🤖 Generated with Claude Code