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
+3 -3
View File
@@ -68,7 +68,7 @@ label.lbl { font-size:11px; color:var(--text-3); display:block; margin-bottom:4p
<main class="main">
<div class="header">
<h2 id="pageTitle">Skeniraj račun (OCR)</h2>
<span class="meta" id="metaInfo">Tesseract + DeepSeek V3 · /api/erp</span>
<span class="meta" id="metaInfo">Tesseract + Ri.NET AI Engine · /api/erp</span>
</div>
<!-- OCR -->
@@ -78,7 +78,7 @@ label.lbl { font-size:11px; color:var(--text-3); display:block; margin-bottom:4p
<div id="ocrDrop" style="border:2px dashed var(--border);border-radius:8px;padding:34px;text-align:center;cursor:pointer;background:var(--bg-3)">
<div style="font-size:36px;color:var(--accent);margin-bottom:6px"></div>
<div style="font-size:14px;font-weight:600">Povuci datoteku ovdje ili klikni za odabir</div>
<div style="font-size:11px;color:var(--text-3);margin-top:6px">Tesseract OCR (hrv+eng) + DeepSeek V3 LLM ekstrakcija polja</div>
<div style="font-size:11px;color:var(--text-3);margin-top:6px">Tesseract OCR (hrv+eng) + Ri.NET AI Engine LLM ekstrakcija polja</div>
<input id="ocrFile" type="file" accept=".pdf,.jpg,.jpeg,.png,.tif,.tiff,.webp" style="display:none">
</div>
<div id="ocrStatus" style="margin-top:10px;font-size:12px;color:var(--text-2);min-height:18px"></div>
@@ -219,7 +219,7 @@ async function ocrHandle(file) {
if (!r.ok) { ocrSet('❌ Upload pao: '+r.status,'var(--red)'); return; }
const j = await r.json();
ocrUploadId = j.upload_id;
ocrSet(`✓ Uploaded #${ocrUploadId} (${j.size} B). Pokrećem OCR + DeepSeek V3 ekstrakciju…`,'var(--accent)');
ocrSet(`✓ Uploaded #${ocrUploadId} (${j.size} B). Pokrećem OCR + Ri.NET AI Engine ekstrakciju…`,'var(--accent)');
const fd2 = new FormData();
fd2.append('upload_id', ocrUploadId);