fix(pitch-deck): GDPR compliance — automated cleanup, full Art. 13 notice
Build pitch-deck / build-push-deploy (push) Successful in 1m37s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 38s
CI / test-python-voice (push) Successful in 32s
CI / test-bqas (push) Successful in 30s

- runDataCleanup() replaces maskOverdueInvestors(): now also anonymizes
  never-activated invites after 90 days, deletes sessions + magic links
  older than 30 days, NULLs IPs in audit logs older than 30 days, and
  redacts email from audit log details JSONB for masked investors
- New /api/admin/cleanup POST endpoint for scheduled invocation
- New .gitea/workflows/pitch-cleanup.yml: daily cron at 02:00 UTC calls
  the cleanup endpoint so anonymization is genuinely automatic, not lazy
- Switch masking window from first_activity_at to last_login_at (30 days
  of inactivity; resets on each login)
- Both auth pages: DSGVO footer now covers all Art. 13 requirements —
  data categories, retention cutoffs, Art. 15–21 rights, contact address,
  LfDI Baden-Württemberg as supervisory authority

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-05-01 15:11:51 +02:00
parent 2f861cd6d7
commit 5946aa47d5
8 changed files with 137 additions and 25 deletions
+36
View File
@@ -0,0 +1,36 @@
# Daily GDPR data cleanup for the pitch deck.
# Calls /api/admin/cleanup which runs runDataCleanup():
# - anonymizes investors inactive 30+ days
# - anonymizes never-activated invites after 90 days
# - deletes sessions + magic links older than 30 days
# - anonymizes IPs in audit logs older than 30 days
#
# Requires Gitea Actions secret: PITCH_ADMIN_SECRET
name: Pitch deck — GDPR cleanup
on:
schedule:
- cron: '0 2 * * *'
jobs:
cleanup:
runs-on: docker
container:
image: alpine:3.19
steps:
- name: Run data cleanup
env:
PITCH_ADMIN_SECRET: ${{ secrets.PITCH_ADMIN_SECRET }}
run: |
apk add --no-cache curl
RESPONSE=$(curl -sSf -w "\n%{http_code}" -X POST \
-H "Authorization: Bearer $PITCH_ADMIN_SECRET" \
-H "Content-Type: application/json" \
https://pitch.breakpilot.com/api/admin/cleanup) \
|| { echo "Cleanup request failed"; exit 1; }
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
BODY=$(echo "$RESPONSE" | head -n-1)
echo "Response: $BODY"
[ "$HTTP_CODE" = "200" ] || { echo "Unexpected status $HTTP_CODE"; exit 1; }
echo "GDPR cleanup completed successfully"