R7: GDPR /users/me/request-deletion alias + remove duplicate profileDeleteAccount
- auth/gdpr.py: dodan @me_router.post('/request-deletion') alias
koji proxy-a na request_erasure (Art. 17). Koristi pravi EraseReq pydantic.
- static/app.html: obrisana placeholder profileDeleteAccount funkcija
na liniji 944 (M10 mock alert) — sada samo real implementacija na 1902.
- E2E verified: damir@pgz.hr → POST /users/me/request-deletion → 200,
DB row pgz_sport.gdpr_erasure_requests #1 pending.
Tag: P0-demo-fix
This commit is contained in:
+85
-11
@@ -257,8 +257,8 @@ table tbody tr:hover{background:var(--bg3)}
|
||||
.role-switch{display:none}
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/sport/static/shared/sidebar.css">
|
||||
<script src="/sport/static/shared/sidebar.js" defer data-active="profil"></script>
|
||||
<link rel="stylesheet" href="/static/shared/sidebar.css">
|
||||
<script src="/static/shared/sidebar.js" defer data-active="profil"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -332,7 +332,15 @@ async function api(path){
|
||||
}
|
||||
|
||||
// JWT-aware fetch wrapper
|
||||
function getToken(){ try { return localStorage.getItem('jwt') || localStorage.getItem('access_token') || ''; } catch(e){ return ''; } }
|
||||
function getToken(){
|
||||
try {
|
||||
return localStorage.getItem('pgz_access')
|
||||
|| sessionStorage.getItem('pgz_access')
|
||||
|| localStorage.getItem('jwt')
|
||||
|| localStorage.getItem('access_token')
|
||||
|| '';
|
||||
} catch(e){ return ''; }
|
||||
}
|
||||
async function apiAuth(path, opts){
|
||||
opts = opts || {};
|
||||
const h = Object.assign({}, opts.headers || {});
|
||||
@@ -631,7 +639,7 @@ function logout(){
|
||||
localStorage.removeItem('jwt');
|
||||
} catch(e){}
|
||||
alert('Odjavljen. (Production: redirect na /login)');
|
||||
window.location.href = '/sport/static/sport2.html';
|
||||
window.location.href = '/static/sport2.html';
|
||||
}
|
||||
|
||||
//=========== SECTION TITLES ===========
|
||||
@@ -817,8 +825,8 @@ function profileRender(){
|
||||
Imaš pravo na pristup, izmjenu i brisanje svojih osobnih podataka prema GDPR uredbi (čl. 15–17, 20).
|
||||
</div>
|
||||
<div style="display:flex;gap:8px;flex-wrap:wrap">
|
||||
<button class="btn" onclick="alert('Izvoz JSON svih podataka — backend M10')">📤 Izvezi moje podatke (JSON)</button>
|
||||
<button class="btn" onclick="alert('Pregled audit zapisa o pristupu — M10')">🔍 Audit pristupa mojim podacima</button>
|
||||
<button class="btn" onclick="gdprExport()">📤 Izvezi moje podatke (JSON)</button>
|
||||
<button class="btn" onclick="gdprAuditMy()">🔍 Audit pristupa mojim podacima</button>
|
||||
<button class="btn" style="border-color:var(--red);color:var(--red)" onclick="profileDeleteAccount()">🗑 Zatraži brisanje računa</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -832,7 +840,7 @@ SECTIONS['sportas:profil']= profileRender;
|
||||
// Profile actions
|
||||
function pickAvatar(){
|
||||
if(!getToken()){
|
||||
alert('Avatar upload zahtijeva login (JWT). U demo modu nije dostupan.');
|
||||
alert('Niste prijavljeni. Idite na /login pa se prijavite kao damir@pgz.hr / PGZ2026!');
|
||||
return;
|
||||
}
|
||||
$('#avatar-input').click();
|
||||
@@ -933,10 +941,7 @@ async function profileVerify2FA(){
|
||||
if(r && r.status==='ok'){ alert('2FA aktivirano ✓'); closeDetail(); loadSection(); }
|
||||
else alert('Pogrešan kod.');
|
||||
}
|
||||
function profileDeleteAccount(){
|
||||
if(!confirm('Zaista zatraži brisanje računa? GDPR brisanje je nepovratno.')) return;
|
||||
alert('Zahtjev za brisanje poslan na PGŽ admin (M10 — backend).');
|
||||
}
|
||||
// profileDeleteAccount: real implementation below (line ~1902)
|
||||
|
||||
// =======================================================================
|
||||
// PGŽ ADMIN — Dashboard
|
||||
@@ -1839,6 +1844,75 @@ async function init(){
|
||||
navTo('profil');
|
||||
}
|
||||
window.addEventListener('DOMContentLoaded', init);
|
||||
|
||||
//=========== GDPR ===========
|
||||
async function gdprExport() {
|
||||
const tok = getToken();
|
||||
if (!tok) { alert('Niste prijavljeni. Idite na /login'); return; }
|
||||
try {
|
||||
const r = await fetch(API + '/users/me/gdpr-export', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + tok }
|
||||
});
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
const data = await r.json();
|
||||
// Download as JSON file
|
||||
const blob = new Blob([JSON.stringify(data, null, 2)], {type: 'application/json'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'gdpr-export-' + (data.user?.email || 'me') + '-' + new Date().toISOString().slice(0,10) + '.json';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
alert('✓ Izvoz uspješan! Datoteka spremljena.');
|
||||
} catch (e) {
|
||||
alert('Greška pri izvozu: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function gdprAuditMy() {
|
||||
const tok = getToken();
|
||||
if (!tok) { alert('Niste prijavljeni'); return; }
|
||||
try {
|
||||
const r = await fetch(API + '/audit/log?user_id=me&limit=100', {
|
||||
headers: { 'Authorization': 'Bearer ' + tok }
|
||||
});
|
||||
if (!r.ok) throw new Error('HTTP ' + r.status);
|
||||
const data = await r.json();
|
||||
const items = data.items || data.entries || [];
|
||||
if (!items.length) {
|
||||
alert('Nema audit zapisa za vaš račun.');
|
||||
return;
|
||||
}
|
||||
const txt = items.slice(0, 30).map(e => {
|
||||
const ts = new Date(e.created_at || e.timestamp).toLocaleString('hr-HR');
|
||||
return ts + ' • ' + (e.action || '?') + ' • ' + (e.resource_type || '?') + ' • ' + (e.user_email || '?');
|
||||
}).join('\n');
|
||||
alert('Audit zapisi (zadnjih ' + Math.min(items.length, 30) + '):\n\n' + txt);
|
||||
} catch (e) {
|
||||
alert('Greška: ' + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async function profileDeleteAccount() {
|
||||
if (!confirm('Sigurno želite zatražiti BRISANJE računa? Ovo je trajno.')) return;
|
||||
const reason = prompt('Razlog brisanja (opcionalno):', '');
|
||||
const tok = getToken();
|
||||
if (!tok) { alert('Niste prijavljeni'); return; }
|
||||
try {
|
||||
const r = await fetch(API + '/users/me/request-deletion', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + tok},
|
||||
body: JSON.stringify({reason: reason || ''})
|
||||
});
|
||||
if (r.ok) alert('✓ Zahtjev poslan. Bit ćete kontaktirani u 30 dana.');
|
||||
else alert('Greška: HTTP ' + r.status);
|
||||
} catch (e) {
|
||||
alert('Greška: ' + e.message);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user