diff --git a/pgz_sport_api.py b/pgz_sport_api.py index aa5eea0..5a41bb2 100644 --- a/pgz_sport_api.py +++ b/pgz_sport_api.py @@ -1431,6 +1431,13 @@ try: except Exception as e: print(f'[AUTH/M10] gdpr routers fail: {e}') +# === Round 3 / CC6 — M11 Blockchain audit (Polygon PoS sealing) === +try: + from audit_seal_router import router as audit_seal_router + app.include_router(audit_seal_router, prefix='/api') + print('[AUDIT/M11] polygon seal router loaded (/api/audit/seal*)') +except Exception as e: + print(f'[AUDIT/M11] polygon seal router fail: {e}') @app.get("/sport-3d") @@ -1459,6 +1466,14 @@ def serve_erp(): return FileResponse(p) return {"error": "erp.html not found"} +@app.get("/crm") +@app.get("/crm/") +def serve_crm(): + p = HTML_DIR / "crm.html" + if p.exists(): + return FileResponse(p) + return {"error": "crm.html not found"} + @app.get("/login") @app.get("/login/") def serve_login(): diff --git a/static/sport2.html b/static/sport2.html index b8851f7..29d27a0 100644 --- a/static/sport2.html +++ b/static/sport2.html @@ -615,9 +615,80 @@ function loadSection(id){ case 'manifestacije': return loadManifestacije(); case 'mreza': return loadMreza(); case 'forenzika': return loadForenzika(); + case 'audit': return loadAudit(); } } +//=========== AUDIT LOG (Polygon PoS) =========== +async function loadAudit(){ + const root = $('#pg-audit'); + root.innerHTML = '
Učitavanje audit zapisa…
'; + let r; + try{ + const resp = await fetch(API+'/audit/seal/list?limit=100'); + if(!resp.ok) throw new Error('HTTP '+resp.status); + r = await resp.json(); + }catch(e){ + root.innerHTML = '
Greška: '+esc(e.message||String(e))+'
'; + return; + } + const wallet = r.wallet || ''; + const live = r.live ? '🟢 LIVE Polygon' : '⏳ Pending mode'; + const rows = (r.rows||[]).map(s => { + const tx = s.tx_hash || ''; + const isPending = tx.startsWith('pending:'); + const txCell = isPending + ? `PENDING${esc(tx.slice(0,32))}…` + : `${esc(tx.slice(0,18))}…${esc(tx.slice(-6))}`; + const statusCls = s.status==='confirmed'?'gr':(s.status==='broadcast'?'gd':(s.status==='failed'?'rd':'')); + return ` + ${s.id} + ${esc((s.created_at||'').replace('T',' ').slice(0,19))} + ${esc(s.action)} + ${esc(s.ref_type||'')} ${esc(s.ref_id||'')} + ${esc((s.data_hash||'').slice(0,12))}… + ${txCell} + ${esc(s.status||'')} + ${esc(s.user_email||'')} + `; + }).join(''); + root.innerHTML = ` +
+
+
🔐 Polygon PoS Audit Sealing
+
+ Ključne akcije (odobrenje sufinanciranja, isplata, validacija liječničkog pregleda, izmjena članstva) + pečate se 0-MATIC self-tx-om s SHA-256 hash-em payload-a u data polju. + Svaki zapis je nepromjenjiv i provjerljiv preko polygonscan.com. +
+
+
Wallet: ${esc(wallet)}
+
Chain: Polygon PoS (137)
+
Mode: ${live}
+
+
+
+
📊 Statistika
+
+
${r.count||0} sealed zapisa
+
Najnoviji prikazani prvi
+
+
+
+ ${rows ? ` +
+
📜 Audit zapisi
+
+ + + + + ${rows} +
#VrijemeAkcijaReferencaSHA-256Polygon TXStatusKorisnik
+
` : '
Nema audit zapisa.
'} + `; +} + //=========== DASHBOARD =========== async function loadDash(){ const root = $('#pg-dashboard');