diff --git a/.gitea/workflows/pitch-cleanup.yml b/.gitea/workflows/pitch-cleanup.yml new file mode 100644 index 0000000..6458f29 --- /dev/null +++ b/.gitea/workflows/pitch-cleanup.yml @@ -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" diff --git a/pitch-deck/app/api/admin/cleanup/route.ts b/pitch-deck/app/api/admin/cleanup/route.ts new file mode 100644 index 0000000..a765875 --- /dev/null +++ b/pitch-deck/app/api/admin/cleanup/route.ts @@ -0,0 +1,11 @@ +import { NextRequest, NextResponse } from 'next/server' +import { requireAdmin } from '@/lib/admin-auth' +import { runDataCleanup } from '@/lib/masking' + +export async function POST(request: NextRequest) { + const guard = await requireAdmin(request) + if (guard.kind === 'response') return guard.response + + const stats = await runDataCleanup() + return NextResponse.json({ success: true, stats }) +} diff --git a/pitch-deck/app/api/admin/investors/[id]/route.ts b/pitch-deck/app/api/admin/investors/[id]/route.ts index 3998a25..15ef3cf 100644 --- a/pitch-deck/app/api/admin/investors/[id]/route.ts +++ b/pitch-deck/app/api/admin/investors/[id]/route.ts @@ -1,7 +1,7 @@ import { NextRequest, NextResponse } from 'next/server' import pool from '@/lib/db' import { requireAdmin, logAdminAudit } from '@/lib/admin-auth' -import { maskOverdueInvestors } from '@/lib/masking' +import { runDataCleanup } from '@/lib/masking' interface RouteContext { params: Promise<{ id: string }> @@ -13,7 +13,7 @@ export async function GET(request: NextRequest, ctx: RouteContext) { const { id } = await ctx.params - await maskOverdueInvestors() + await runDataCleanup() const [investor, sessions, snapshots, audit] = await Promise.all([ pool.query( diff --git a/pitch-deck/app/api/admin/investors/route.ts b/pitch-deck/app/api/admin/investors/route.ts index 85d8715..d9e645f 100644 --- a/pitch-deck/app/api/admin/investors/route.ts +++ b/pitch-deck/app/api/admin/investors/route.ts @@ -1,13 +1,13 @@ import { NextRequest, NextResponse } from 'next/server' import pool from '@/lib/db' import { requireAdmin } from '@/lib/admin-auth' -import { maskOverdueInvestors } from '@/lib/masking' +import { runDataCleanup } from '@/lib/masking' export async function GET(request: NextRequest) { const guard = await requireAdmin(request) if (guard.kind === 'response') return guard.response - await maskOverdueInvestors() + await runDataCleanup() const { rows } = await pool.query( `SELECT i.id, i.email, i.name, i.company, i.status, i.last_login_at, i.login_count, i.created_at, diff --git a/pitch-deck/app/auth/page.tsx b/pitch-deck/app/auth/page.tsx index cd39c96..55b13c4 100644 --- a/pitch-deck/app/auth/page.tsx +++ b/pitch-deck/app/auth/page.tsx @@ -127,8 +127,7 @@ export default function AuthPage() {
- Datenschutzhinweis: Beim Zugriff auf diese Seite werden technische Zugriffsdaten (insbesondere IP-Adresse und Zeitpunkt) verarbeitet, um die sichere Nutzung des Zugangs zu gewährleisten und Missbrauch zu verhindern. Die Speicherung erfolgt für maximal 72 Stunden. Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse). -
+ Datenschutzhinweis (Art. 13 DSGVO): Beim Zugriff werden technische Zugriffsdaten (IP-Adresse, Zeitpunkt, Browser) sowie – soweit eingeladen – personenbezogene Kontaktdaten (E-Mail, Name, Unternehmen) verarbeitet. Zweck: Zugangsverwaltung und Missbrauchsprävention. Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse). Speicherdauer: max. 30 Tage nach letztem Zugriff; nicht aktivierte Zugänge nach 90 Tagen. Danach automatische Anonymisierung. Ihre Rechte gem. Art. 15–21 DSGVO (Auskunft, Berichtigung, Löschung, Einschränkung, Datenübertragbarkeit, Widerspruch): Anfragen an pitch@breakpilot.ai. Beschwerderecht bei der Aufsichtsbehörde: LfDI Baden-Württemberg (www.baden-wuerttemberg.datenschutz.de).Verantwortlich: Benjamin Bönisch & Sharang Parnerkar · Kontakt: info@breakpilot.com
diff --git a/pitch-deck/app/auth/verify/page.tsx b/pitch-deck/app/auth/verify/page.tsx index f3a4d4e..d363ffb 100644 --- a/pitch-deck/app/auth/verify/page.tsx +++ b/pitch-deck/app/auth/verify/page.tsx @@ -119,8 +119,7 @@ export default function VerifyPage() {- Datenschutzhinweis: Beim Zugriff auf diese Seite werden technische Zugriffsdaten (insbesondere IP-Adresse und Zeitpunkt) verarbeitet, um die sichere Nutzung des Zugangs zu gewährleisten und Missbrauch zu verhindern. Die Speicherung erfolgt für maximal 72 Stunden. Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse). Weitere Informationen zum Datenschutz erhalten Sie auf Anfrage. -
+ Datenschutzhinweis (Art. 13 DSGVO): Beim Zugriff werden technische Zugriffsdaten (IP-Adresse, Zeitpunkt, Browser) sowie – soweit eingeladen – personenbezogene Kontaktdaten (E-Mail, Name, Unternehmen) verarbeitet. Zweck: Zugangsverwaltung und Missbrauchsprävention. Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse). Speicherdauer: max. 30 Tage nach letztem Zugriff; nicht aktivierte Zugänge nach 90 Tagen. Danach automatische Anonymisierung. Ihre Rechte gem. Art. 15–21 DSGVO (Auskunft, Berichtigung, Löschung, Einschränkung, Datenübertragbarkeit, Widerspruch): Anfragen an pitch@breakpilot.ai. Beschwerderecht bei der Aufsichtsbehörde: LfDI Baden-Württemberg (www.baden-wuerttemberg.datenschutz.de).Verantwortlich: Benjamin Bönisch & Sharang Parnerkar · Kontakt: info@breakpilot.com
diff --git a/pitch-deck/app/pitch-admin/(authed)/investors/[id]/page.tsx b/pitch-deck/app/pitch-admin/(authed)/investors/[id]/page.tsx index d642dce..385c33f 100644 --- a/pitch-deck/app/pitch-admin/(authed)/investors/[id]/page.tsx +++ b/pitch-deck/app/pitch-admin/(authed)/investors/[id]/page.tsx @@ -198,15 +198,15 @@ export default function InvestorDetailPage() {