Skip to content

Account deletion#23

Open
jaymenelson56 wants to merge 3 commits intomainfrom
account_deletion
Open

Account deletion#23
jaymenelson56 wants to merge 3 commits intomainfrom
account_deletion

Conversation

@jaymenelson56
Copy link
Collaborator

Changes

  • Code reviewed by self
  • Tests added/updated (deferred - manual testing completed)
  • No console errors
  • Branch up to date with main

Optional Notices

  • Database migrations (migration 0013_customuser_deletion_requested_at)
  • Docker rebuild required

Description

Implements account deletion functionality with 7-day grace period for GDPR/CCPA compliance.

Backend:

  • Added deletion_requested_at field to CustomUser model
  • Created three new API endpoints:
    • POST /api/auth/request-deletion/ - Request account deletion (requires password confirmation)
    • POST /api/auth/cancel-deletion/ - Cancel pending deletion
    • GET /api/auth/export-data/ - Export user data as JSON
  • Built management command delete_expired_accounts to remove accounts after 7-day grace period
  • Updated UserSerializer to include deletion_requested_at field
  • Verified all foreign keys use CASCADE for proper cleanup

Frontend:

  • Created /settings page with account deletion UI
  • Shows countdown timer and deletion date when deletion is pending
  • Displays cancel deletion button during grace period
  • Integrates with Zustand auth store
  • Export data button downloads user data as JSON

Deferred to separate tickets:

  • Email notifications (deletion confirmed, deletion completed, cancellation)
  • Automated scheduling of delete_expired_accounts command (cron/Celery/platform scheduler)

Testing

Manual testing completed:

  1. Registered new account
  2. Navigated to /settings
  3. Clicked "Delete Account" → entered password → confirmed deletion request
  4. Verified countdown timer appears with correct days remaining
  5. Clicked "Cancel Deletion" → verified warning disappears
  6. Requested deletion again
  7. Manually backdated deletion timestamp to 8 days ago via Django shell
  8. Ran management command → verified account and all related data CASCADE deleted
  9. Clicked "Export Data" → verified JSON download contains user habits, notes, verses
  10. Verified all foreign key relationships use CASCADE (UserProfile, UserHabit, StudyNote, Deck, UserVerseState, ReviewLog, RecentVerse)

Commands Required for Testing or After Merge

After merging:

# Rebuild containers (new migration added)
docker compose down
docker compose up -d --build

# Run migrations
docker compose exec backend python manage.py migrate

To test deletion flow:

# 1. Register a new account at http://localhost:3000/register
# 2. Navigate to http://localhost:3000/settings
# 3. Click "Delete Account" and confirm with password
# 4. To simulate 7-day expiration, manually backdate the deletion request:

docker compose exec backend python manage.py shell
from api.models import CustomUser
from django.utils import timezone
from datetime import timedelta

# Get the most recently created user
user = CustomUser.objects.latest('created_at')
print(f"Backdating deletion for: {user.email}")

# Set deletion request to 8 days ago (past 7-day grace period)
user.deletion_requested_at = timezone.now() - timedelta(days=8)
user.save()
exit()
# 5. Run the deletion command
docker compose exec backend python manage.py delete_expired_accounts
# Expected output: "Deleting account: <email>" and "Successfully deleted 1 account(s)"

# 6. Run again to verify cleanup
docker compose exec backend python manage.py delete_expired_accounts
# Expected output: "No accounts to delete"

To verify CASCADE deletion worked:

docker compose exec backend python manage.py shell
from api.models import CustomUser, UserHabit, StudyNote, RecentVerse

# Check user is gone
print(f"Users remaining: {CustomUser.objects.count()}")

# Verify related data was CASCADE deleted
print(f"Orphaned habits: {UserHabit.objects.filter(user__isnull=True).count()}")  # Should be 0
print(f"Orphaned notes: {StudyNote.objects.filter(user__isnull=True).count()}")  # Should be 0
print(f"Orphaned verses: {RecentVerse.objects.filter(user__isnull=True).count()}")  # Should be 0

- Add deletion_requested_at field to CustomUser model
- Implement request/cancel deletion endpoints with password confirmation
- Add user data export endpoint (GDPR compliance)
- Create management command to delete expired accounts
- Build frontend settings page with deletion UI and countdown
- Update UserSerializer to include deletion_requested_at
- All foreign keys use CASCADE for proper cleanup
- Scheduling setup deferred to infrastructure ticket
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant