Skip to content

feat: PII Export & Delete Workflow (GDPR-ready) (#76)#464

Open
qiridigital wants to merge 1 commit intorohitdash08:mainfrom
qiridigital:feat/gdpr-pii-export
Open

feat: PII Export & Delete Workflow (GDPR-ready) (#76)#464
qiridigital wants to merge 1 commit intorohitdash08:mainfrom
qiridigital:feat/gdpr-pii-export

Conversation

@qiridigital
Copy link

Summary

Implements the GDPR PII Export & Delete Workflow as described in #76.

Acceptance Criteria

  • Export package generation
  • Irreversible deletion workflow
  • Audit trail logging

Backend

services/privacy.py

  • export_user_data(user_id) - Collects ALL personal records across every model: profile, categories, expenses, recurring expenses, bills, reminders, subscriptions, and audit logs. Returns a versioned JSON-serialisable dictionary.
  • delete_user_data(user_id) - Permanently deletes all user data. Creates an anonymised GDPR_DELETE audit log entry before removal. Explicitly deletes child records for compatibility with databases that don't support CASCADE.

routes/privacy.py

  • GET /privacy/export - GDPR Art. 20 (Right to Data Portability). Returns all user data as JSON.
  • POST /privacy/delete - GDPR Art. 17 (Right to Erasure). Requires confirm:true in the body to prevent accidental deletion. Invalidates Redis cache for the user.
  • Both endpoints require JWT authentication.

Frontend

api/privacy.ts

  • exportPII() - Fetches the export JSON
  • deletePII() - Sends confirmed deletion request

Account Page Updates

  • Export My Data button - Downloads a timestamped JSON file
  • Delete My Account - Two-step confirmation (click once to reveal confirmation, click again to execute). Shows permanent warning. Clears local auth tokens and redirects to sign-in on success.

Tests (test_privacy.py)

8 tests covering:

  • Export: empty user, seeded data, auth required
  • Delete: confirmation required, missing body, full deletion, auth required, irreversibility (login fails after delete)

/claim #76

- Backend: services/privacy.py with export_user_data() collecting all
  user records (profile, categories, expenses, recurring, bills, reminders,
  subscriptions, audit logs) and delete_user_data() for irreversible
  account deletion with anonymised audit trail
- Backend: 
outes/privacy.py with:
  - GET /privacy/export – GDPR Art. 20 data portability
  - POST /privacy/delete – GDPR Art. 17 right to erasure (requires
    explicit {confirm: true} in body)
- Frontend: �pi/privacy.ts typed API client
- Frontend: Account page updated with Data Privacy section:
  - Export My Data button (downloads JSON file)
  - Delete My Account with two-step confirmation
  - Clears auth tokens and redirects to sign-in on deletion
- Tests: 	est_privacy.py with 8 tests covering export (empty, seeded,
  auth), delete (confirmation required, no body, full delete, auth,
  irreversibility)
- Blueprint registered at /privacy prefix
- Cache invalidation on delete

Closes rohitdash08#76
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements a GDPR-oriented “export my data” and “delete my account” workflow across backend + frontend, with automated tests to validate expected behaviors.

Changes:

  • Added backend privacy service + routes for PII export (GET /privacy/export) and irreversible deletion (POST /privacy/delete).
  • Added frontend API helpers and Account page UI for exporting a JSON file and performing a confirmed delete.
  • Added backend test suite coverage for export/delete scenarios.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/backend/app/services/privacy.py Implements cross-model export + user data deletion with audit logging.
packages/backend/app/routes/privacy.py Exposes authenticated privacy endpoints and cache invalidation on delete.
packages/backend/app/routes/init.py Registers the new /privacy blueprint.
packages/backend/tests/test_privacy.py Adds tests covering export correctness, auth requirements, and deletion behavior.
app/src/api/privacy.ts Adds frontend API calls for export/delete.
app/src/pages/Account.tsx Adds “Data Privacy” UI section with export + two-step delete confirmation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return jsonify(error="user not found"), 404

# Invalidate all cached data for this user
cache_delete_patterns([f"user:{uid}:*"])
return jsonify(error="user not found"), 404

# Invalidate all cached data for this user
cache_delete_patterns([f"user:{uid}:*"])
Comment on lines +80 to +81
a.click();
URL.revokeObjectURL(url);
Comment on lines +12 to +21
from ..models import (
AuditLog,
Bill,
Category,
Expense,
RecurringExpense,
Reminder,
User,
UserSubscription,
)
Comment on lines +160 to +167
# Delete child records explicitly for databases without CASCADE support
Reminder.query.filter_by(user_id=user_id).delete()
Expense.query.filter_by(user_id=user_id).delete()
RecurringExpense.query.filter_by(user_id=user_id).delete()
Bill.query.filter_by(user_id=user_id).delete()
Category.query.filter_by(user_id=user_id).delete()
UserSubscription.query.filter_by(user_id=user_id).delete()
AuditLog.query.filter_by(user_id=user_id).delete()
Comment on lines +155 to +157
# Log the deletion request before wiping data
db.session.add(
AuditLog(user_id=None, action=f"GDPR_DELETE:user_id={user_id}")
Comment on lines +161 to +167
Reminder.query.filter_by(user_id=user_id).delete()
Expense.query.filter_by(user_id=user_id).delete()
RecurringExpense.query.filter_by(user_id=user_id).delete()
Bill.query.filter_by(user_id=user_id).delete()
Category.query.filter_by(user_id=user_id).delete()
UserSubscription.query.filter_by(user_id=user_id).delete()
AuditLog.query.filter_by(user_id=user_id).delete()
Comment on lines +48 to +51
categories = [
{"id": c.id, "name": c.name, "created_at": _serialize_date(c.created_at)}
for c in Category.query.filter_by(user_id=user_id).all()
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants