CC2 R4 #4: /api/users/me/gdpr-export alias

- New auth.gdpr.me_router prefix /api/users/me with:
  - GET/POST /gdpr-export → Art.20 JSON download with Content-Disposition
  - POST /gdpr-erase → Art.17 erasure request
  - GET /gdpr-consent → consent history for caller
- jsonable_encoder fixes datetime serialisation in JSONResponse
- admin_users.html: 'Izvezi moje podatke' now POSTs to alias and uses
  filename from Content-Disposition header
- 401 enforced on no-auth, 200 on valid Bearer (verified live)
This commit is contained in:
Damir Radulić
2026-05-05 00:47:22 +02:00
parent ca92717039
commit a0db65fc31
14 changed files with 4796 additions and 30 deletions
+6 -5
View File
@@ -457,12 +457,13 @@ $('#menuLogout').addEventListener('click', async () => {
location.href = '/sport/static/login.html';
});
$('#menuExport').addEventListener('click', async () => {
const r = await api('/gdpr/export'); if (!r) return;
const data = await r.json();
const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'});
const r = await api('/users/me/gdpr-export', {method:'POST'}); if (!r) return;
const blob = await r.blob();
const cd = r.headers.get('content-disposition') || '';
const m = cd.match(/filename="?([^";]+)"?/);
const fn = m ? m[1] : `pgz_data_export_${Date.now()}.json`;
const u = URL.createObjectURL(blob);
const a = document.createElement('a'); a.href = u;
a.download = `pgz_data_export_${data.subject.id}_${Date.now()}.json`;
const a = document.createElement('a'); a.href = u; a.download = fn;
a.click(); URL.revokeObjectURL(u);
toast('Podaci preuzeti (Art. 20 GDPR)');
});