Add admin panel to the test UI

Lets an operator load and update fee_address/bet_amount_sats from a
form (using the X-Admin-Token header) instead of curl/Swagger, with
inline status feedback and the same request/response log as the rest
of the page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 10:32:05 +02:00
co-authored by Claude Sonnet 5
parent f783cfaf80
commit 41863691a0
+62
View File
@@ -63,6 +63,29 @@
<button onclick="withdraw()">Preleva</button>
</section>
<section id="admin-section">
<h3>Admin</h3>
<p style="font-size:0.85rem;color:#555;margin-top:-8px">
Configurazione operativa (indirizzo fee, importo bet), salvata nel database e modificabile in
qualsiasi momento senza riavviare il server. Serve il token admin (variabile <code>ADMIN_TOKEN</code>
nel file <code>.env</code> del server).
</p>
<label>Admin token</label>
<input id="admin-token" type="password" placeholder="valore di ADMIN_TOKEN">
<button onclick="adminLoad()">Carica configurazione attuale</button>
<div id="admin-form" class="hidden">
<label>Fee address (dove finisce il 30% di ogni round)</label>
<input id="admin-fee-address" placeholder="plm1q...">
<label>Bet amount (sats, 1 PLM = 100000000 sats)</label>
<input id="admin-bet-amount" placeholder="es. 1000000000 per 10 PLM">
<button onclick="adminSave()">Salva</button>
<div id="admin-status" style="margin-top:8px;font-size:0.9rem"></div>
</div>
</section>
<h3>Log</h3>
<pre id="log"></pre>
@@ -143,6 +166,45 @@ async function withdraw() {
}
if (token) showDashboard();
async function callAdmin(method, path, body) {
const adminToken = document.getElementById('admin-token').value;
const headers = { 'Content-Type': 'application/json', 'X-Admin-Token': adminToken };
const res = await fetch(path, { method, headers, body: body ? JSON.stringify(body) : undefined });
const data = await res.json().catch(() => ({}));
log({ request: method + ' ' + path, status: res.status, response: data });
if (!res.ok) throw new Error(data.detail || res.statusText);
return data;
}
function adminStatus(msg, ok) {
const el = document.getElementById('admin-status');
el.textContent = msg;
el.style.color = ok ? '#2a7a2a' : '#a02020';
}
async function adminLoad() {
try {
const data = await callAdmin('GET', '/admin/config');
document.getElementById('admin-fee-address').value = data.fee_address;
document.getElementById('admin-bet-amount').value = data.bet_amount_sats;
document.getElementById('admin-form').classList.remove('hidden');
adminStatus('Configurazione caricata.', true);
} catch (e) {
adminStatus('Errore: token admin non valido o server non raggiungibile.', false);
}
}
async function adminSave() {
const feeAddress = document.getElementById('admin-fee-address').value;
const betAmount = parseInt(document.getElementById('admin-bet-amount').value, 10);
try {
await callAdmin('PUT', '/admin/config', { fee_address: feeAddress, bet_amount_sats: betAmount });
adminStatus('Salvato.', true);
} catch (e) {
adminStatus('Errore nel salvataggio: ' + e.message, false);
}
}
</script>
</body>