2026-07-21 10:36:55 +02:00
<!doctype html>
< html lang = "it" >
< head >
< meta charset = "utf-8" >
2026-07-21 10:46:55 +02:00
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< title > PLM Lottery — Admin</ title >
2026-07-22 15:39:47 +02:00
< link rel = "icon" type = "image/svg+xml" href = "/logo.svg" >
2026-07-22 23:20:40 +02:00
< link rel = "stylesheet" href = "/admin.css" >
2026-07-21 10:36:55 +02:00
</ head >
< body >
2026-07-21 14:28:29 +02:00
< section id = "login-section" >
< header >
< h1 > PLM Lottery — Admin</ h1 >
< p > Accesso riservato</ p >
</ header >
2026-07-21 10:36:55 +02:00
2026-07-21 14:28:29 +02:00
< div class = "card" >
< label for = "admin-token" > Admin token</ label >
< input id = "admin-token" type = "password" placeholder = "valore di ADMIN_TOKEN" autofocus >
< button onclick = "adminLogin()" id = "login-btn" > Accedi</ button >
</ div >
</ section >
2026-07-21 10:36:55 +02:00
2026-07-21 15:06:38 +02:00
< div id = "dashboard-section" class = "hidden" >
< nav class = "navbar" >
< span class = "brand" > PLM Lottery — Admin</ span >
< span class = "nav-tab active" id = "nav-parametri" onclick = "switchView('parametri')" > Parametri</ span >
< span class = "nav-tab" id = "nav-utenti" onclick = "switchView('utenti')" > Utenti</ span >
< span class = "nav-tab" id = "nav-round" onclick = "switchView('round')" > Round</ span >
< span class = "nav-tab" id = "nav-pending" onclick = "switchView('pending')" > Transazioni pendenti</ span >
< span class = "nav-tab" id = "nav-audit" onclick = "switchView('audit')" > Audit log</ span >
2026-07-31 15:50:40 +02:00
< span class = "nav-tab" id = "nav-bugreports" onclick = "switchView('bugreports')" > Segnalazioni bug</ span >
2026-07-21 15:06:38 +02:00
< span class = "spacer" ></ span >
2026-07-22 10:16:51 +02:00
< span class = "chain-status-pill" >
< span class = "status-dot" id = "chain-status-dot" ></ span >
< span id = "chain-status-label" > Connessione…</ span >
</ span >
< span class = "chain-block mono" id = "chain-block" > Blocco —</ span >
< button class = "secondary" style = "margin:8px 0 8px 14px" onclick = "adminLogout()" > Esci</ button >
2026-07-21 15:06:38 +02:00
</ nav >
2026-07-21 10:36:55 +02:00
2026-07-21 15:06:38 +02:00
< main >
2026-07-21 10:36:55 +02:00
2026-07-21 15:06:38 +02:00
< div class = "view active" id = "view-parametri" >
< h2 class = "section-title" > Parametri</ h2 >
< p class = "hint" > Configurazione operativa, salvata nel database — modificabile in qualsiasi momento senza riavviare il server.</ p >
2026-07-21 10:36:55 +02:00
2026-07-22 10:36:36 +02:00
< div class = "card" id = "maintenance-card" >
< h2 > Manutenzione</ h2 >
< p class = "hint" id = "maintenance-hint" >
Interrompe l'apertura di nuovi round dopo quello in corso, senza troncare il round attuale — chiusura,
estrazione e pagamento del vincitore avvengono normalmente. Gli utenti vedono un avviso di manutenzione.
</ p >
< div class = "row-between" >
< span class = "chain-status-pill" >
< span class = "status-dot" id = "maintenance-dot" ></ span >
< span id = "maintenance-status-label" > —</ span >
</ span >
< button id = "maintenance-btn" class = "secondary" style = "width:auto;margin-top:0" onclick = "toggleMaintenance()" > …</ button >
</ div >
</ div >
2026-07-21 15:06:38 +02:00
< div class = "card" >
2026-08-04 14:13:00 +02:00
<!-- B-66: no fee_address means no round can open at all (the payout pays the
30% commission to it, so it cannot even be built). Shown here because
this is the one screen that can fix it. -->
< div class = "warning-banner hidden" id = "admin-fee-address-warning" >
⚠️ Nessun fee address configurato: finché resta vuoto < strong > non si aprirà nessun round</ strong >
(il payout non sarebbe costruibile). Impostalo qui sotto e salva.
</ div >
2026-07-21 15:06:38 +02:00
< div class = "grid-2" >
< div >
< label for = "admin-fee-address" > Fee address (dove finisce il 30% di ogni round)</ label >
< input id = "admin-fee-address" class = "mono" placeholder = "plm1q..." >
< label for = "admin-bet-amount" > Bet amount (PLM)</ label >
< input id = "admin-bet-amount" inputmode = "decimal" placeholder = "es. 10" >
</ div >
< div >
< label for = "admin-round-duration" > Durata round (secondi)</ label >
< input id = "admin-round-duration" inputmode = "numeric" placeholder = "es. 600" >
< label for = "admin-round-cooldown" > Pausa tra un round e il successivo (secondi)</ label >
< input id = "admin-round-cooldown" inputmode = "numeric" placeholder = "es. 30" >
2026-07-21 16:03:50 +02:00
< label for = "admin-draw-animation" > Durata animazione estrazione (secondi)</ label >
< input id = "admin-draw-animation" inputmode = "numeric" placeholder = "es. 20" >
2026-07-21 15:06:38 +02:00
< label for = "admin-fee-rate" > Fee rate di rete (sat/vB)</ label >
< input id = "admin-fee-rate" inputmode = "numeric" placeholder = "es. 1" >
< label for = "admin-rbf-timeout" > Timeout prima del fee-bump RBF (secondi)</ label >
< input id = "admin-rbf-timeout" inputmode = "numeric" placeholder = "es. 900" >
</ div >
</ div >
< button onclick = "adminSave()" id = "save-btn" > Salva</ button >
</ div >
2026-07-21 14:28:29 +02:00
</ div >
2026-07-21 15:06:38 +02:00
< div class = "view" id = "view-utenti" >
< h2 class = "section-title" > Utenti</ h2 >
2026-07-22 12:00:09 +02:00
< p class = "hint" > Elenco utenti registrati, con saldo interno, accesso alla chiave privata per interventi manuali (es. restituire fondi bloccati) e reset password per chi resta bloccato fuori dall'account.</ p >
2026-07-21 15:06:38 +02:00
< div class = "warning-banner" >
2026-07-22 12:00:09 +02:00
⚠ La chiave privata dà accesso completo ai fondi dell'utente: ogni visualizzazione viene registrata nell'audit log, non condividerla né salvarla altrove. La password esistente di un utente non è mai recuperabile (è salvata solo come hash Argon2) — "Reset" ne genera una nuova al posto della vecchia, anche questo audit-loggato.
2026-07-21 15:06:38 +02:00
</ div >
< div class = "card" >
< div class = "table-wrap" >
< table >
< thead >
2026-07-22 12:00:09 +02:00
< tr >< th > ID</ th >< th > Username</ th >< th > Indirizzo</ th >< th > Saldo (PLM)</ th >< th > Registrato</ th >< th > Chiave</ th >< th > Password</ th ></ tr >
2026-07-21 15:06:38 +02:00
</ thead >
< tbody id = "users-tbody" ></ tbody >
</ table >
</ div >
</ div >
2026-07-21 14:28:29 +02:00
</ div >
2026-07-21 15:06:38 +02:00
< div class = "view" id = "view-round" >
< h2 class = "section-title" > Round</ h2 >
< p class = "hint" > Ultimi round: stato, vincitore, importi e transazione di payout.</ p >
< div class = "card" >
< div class = "table-wrap" >
< table >
< thead >
< tr >< th > ID</ th >< th > Stato</ th >< th > Apertura</ th >< th > Vincitore</ th >< th > Pool (PLM)</ th >< th > Vincita (PLM)</ th >< th > Fee (PLM)</ th >< th > Payout txid</ th ></ tr >
</ thead >
< tbody id = "rounds-tbody" ></ tbody >
</ table >
</ div >
</ div >
</ div >
< div class = "view" id = "view-pending" >
< h2 class = "section-title" > Transazioni pendenti</ h2 >
< p class = "hint" > Bet, payout e prelievi non ancora confermati — candidati al fee-bump RBF se scade il timeout.</ p >
< div class = "card" >
< div class = "table-wrap" >
< table >
< thead >
< tr >< th > ID</ th >< th > Tipo</ th >< th > Stato</ th >< th > Txid</ th >< th > Fee rate</ th >< th > Tentativi</ th >< th > Trasmessa</ th ></ tr >
</ thead >
< tbody id = "pending-tbody" ></ tbody >
</ table >
</ div >
</ div >
</ div >
< div class = "view" id = "view-audit" >
< h2 class = "section-title" > Audit log</ h2 >
< p class = "hint" > Ultimi eventi registrati dal sistema (config, bet, payout, accessi a chiavi private, ecc.).</ p >
< div class = "card" >
< div class = "table-wrap" >
< table >
< thead >
< tr >< th > ID</ th >< th > Evento</ th >< th > Dettagli</ th >< th > Utente</ th >< th > Round</ th >< th > Quando</ th ></ tr >
</ thead >
< tbody id = "audit-tbody" ></ tbody >
</ table >
</ div >
</ div >
</ div >
2026-07-31 15:50:40 +02:00
< div class = "view" id = "view-bugreports" >
< h2 class = "section-title" > Segnalazioni bug</ h2 >
< p class = "hint" > Segnalazioni inviate dagli utenti tramite la pagina "Segnala un bug".</ p >
< div class = "card" >
< div class = "table-wrap" >
< table >
< thead >
< tr >< th > ID</ th >< th > Descrizione</ th >< th > Contatto</ th >< th > Utente</ th >< th > Quando</ th >< th > Stato</ th ></ tr >
</ thead >
< tbody id = "bugreports-tbody" ></ tbody >
</ table >
</ div >
</ div >
</ div >
2026-07-21 15:06:38 +02:00
</ main >
</ div >
2026-07-21 14:22:03 +02:00
2026-07-21 10:46:55 +02:00
< div id = "toast-container" aria-live = "polite" ></ div >
2026-07-21 10:36:55 +02:00
2026-07-23 11:05:38 +02:00
< script src = "/admin.js" ></ script >
2026-07-21 10:36:55 +02:00
</ body >
</ html >