feat: GDPR-style user data export & account deletion#1015
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
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.
Implements a self-contained
AccountModulethat gives users the ability to (1) export all of their personal data as a downloadable JSON bundle and (2) permanently delete their account, anonymizing PII while preserving on-chain data integrity.Motivation
Users generate data across predictions, markets, competitions, notifications, leaderboard, and achievements with no way to retrieve or erase it. This is a standard legal/compliance requirement (GDPR Art. 15 & 17, CCPA) and was previously impossible.
Changes
New files
src/account/entities/data-export-job.entity.tspending → processing → ready | failed) with file path and expiry timestampsrc/account/account.service.tssrc/account/account.controller.tssrc/account/account.module.tssrc/migrations/1776300000000-AddDataExportJobAndUserSoftDelete.tsdeleted_attousers, createsdata_export_jobstable with indexesModified files
src/users/entities/user.entity.ts@DeleteDateColumn() deleted_at— TypeORM automatically excludes soft-deleted users from all queries, invalidating their JWT immediatelysrc/config/env.validation.tsEXPORT_DIR(default./exports) andEXPORT_TTL_HOURS(default48)src/app.module.tsAccountModuleAPI Endpoints
All endpoints require a valid JWT (global
JwtAuthGuardapplies).How it works
Data export (async)
POST /account/exportcreates aDataExportJobrow and returns the job ID. If a pending or processing job already exists for the user it is returned instead (idempotent).@Cron(EVERY_MINUTE)worker picks up pending jobs in batches of 5. For each job it:predictions,markets,user_achievements,user_bookmarks,user_follows,competition_participants,leaderboard_history, andnotifications<jobId>.jsonfile toEXPORT_DIRreadywith an expiry timestamp (now + EXPORT_TTL_HOURS)GET /account/export/:jobIduntilstatus === "ready", then calls the download endpoint.job.user_id !== userId→ 404. Expired files → 410 Gone.@Cron(EVERY_DAY_AT_MIDNIGHT)cleanup job deletes expired files from disk and removes their DB rows.Account deletion
DELETE /accountruns a single database transaction that:stellar_address(needed for address-indexed tables).notificationsrows byuser_address.notification_digest_staterows byuser_id.data_export_jobsfor the user.email,username,avatar_url,ban_reason,banned_at,banned_byand setsdeleted_at = NOW().After the transaction commits, export files are removed from disk.
stellar_addressis intentionally preserved to maintain on-chain data integrity (predictions, market settlements). Settingdeleted_atcauses TypeORM's soft-delete filter to exclude the row from all subsequent queries, so the user's JWT immediately stops working — they cannot log in again.Environment variables
EXPORT_DIR./exportsEXPORT_TTL_HOURS48Both variables are optional; the service works out of the box without them.
Acceptance criteria
POST /account/exportreturns a job ID; polling showspending → readyDELETE /accountanonymizes user, removes PII tables, user can no longer log indeleted_attousersand createsdata_export_jobstablecloses #1005