Compare commits

..

2 Commits

2 changed files with 77 additions and 44 deletions

View File

@@ -151,6 +151,8 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
.invoice .row:last-child{border-bottom:0} .invoice .row:last-child{border-bottom:0}
.invoice .row span:first-child{color:var(--muted)} .invoice .row span:first-child{color:var(--muted)}
.invoice .row span:last-child{font-family:var(--mono);text-align:right;word-break:break-all} .invoice .row span:last-child{font-family:var(--mono);text-align:right;word-break:break-all}
.invoice .row a{color:var(--accent);text-decoration:none}
.invoice .row a:hover{text-decoration:underline}
#qrcode{margin:18px auto 4px;width:160px} #qrcode{margin:18px auto 4px;width:160px}
#qrcode canvas,#qrcode img{width:160px!important;height:160px!important;max-width:100%} #qrcode canvas,#qrcode img{width:160px!important;height:160px!important;max-width:100%}
#qr-overlay{ #qr-overlay{
@@ -286,9 +288,12 @@ footer a{color:var(--accent);text-decoration:none}
<h2>Add your Will Executor</h2> <h2>Add your Will Executor</h2>
<p class="lead">Enter your server URL. It will be checked online before it appears in the list.</p> <p class="lead">Enter your server URL. It will be checked online before it appears in the list.</p>
<div class="form-row"> <div class="form-row">
<input type="text" id="url" placeholder="https://bitcoin-after.life:9137"> <input type="text" id="url" placeholder="https://we.bitcoin-after.life">
<button class="btn" id="submit">Add server</button>
</div> </div>
<div style="margin-top:8px">
<input type="text" id="contact" placeholder="Email, npub or Telegram (optional)" style="width:100%;font:inherit;font-size:14px;padding:9px 12px;border:1px solid var(--line-strong);border-radius:8px;background:var(--panel);color:var(--ink)">
</div>
<button class="btn" id="submit" style="margin-top:8px">Add server</button>
<div class="ok-line" id="iv-status"></div> <div class="ok-line" id="iv-status"></div>
@@ -310,7 +315,9 @@ footer a{color:var(--accent);text-decoration:none}
<div class="row"><span>Chain</span><span id="iv-chain"></span></div> <div class="row"><span>Chain</span><span id="iv-chain"></span></div>
<div class="row"><span>Address</span><span id="iv-addr"></span></div> <div class="row"><span>Address</span><span id="iv-addr"></span></div>
<div class="row"><span>Balance</span><span id="iv-balance"></span></div> <div class="row"><span>Balance</span><span id="iv-balance"></span></div>
<div class="row"><span>Contact</span><span id="iv-contact"></span></div>
<div class="row"><span>Server</span><span id="iv-url"></span></div> <div class="row"><span>Server</span><span id="iv-url"></span></div>
<div class="row"><span>Will Executor</span><span><a id="iv-weinfo" href="#" target="_blank"></a></span></div>
<div id="qrcode"></div> <div id="qrcode"></div>
<button class="btn-share" id="share-btn">Share invoice</button> <button class="btn-share" id="share-btn">Share invoice</button>
<p class="note"> <p class="note">
@@ -502,6 +509,9 @@ document.getElementById('submit').addEventListener('click', async () => {
document.getElementById('iv-addr').textContent = '—'; document.getElementById('iv-addr').textContent = '—';
document.getElementById('iv-balance').textContent = '—'; document.getElementById('iv-balance').textContent = '—';
document.getElementById('iv-url').textContent = '—'; document.getElementById('iv-url').textContent = '—';
document.getElementById('iv-contact').textContent = '—';
document.getElementById('iv-weinfo').href = '#';
document.getElementById('iv-weinfo').textContent = '—';
document.getElementById('qrcode').innerHTML = ''; document.getElementById('qrcode').innerHTML = '';
document.getElementById('qrcode').style.cursor = ''; document.getElementById('qrcode').style.cursor = '';
document.getElementById('qr-overlay').classList.remove('show'); document.getElementById('qr-overlay').classList.remove('show');
@@ -514,7 +524,7 @@ document.getElementById('submit').addEventListener('click', async () => {
const response = await fetch('/data', { const response = await fetch('/data', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url, chain }) body: JSON.stringify({ url, chain, contact: document.getElementById('contact').value.trim() || undefined })
}); });
const result = await response.json(); const result = await response.json();
if (!response.ok) { if (!response.ok) {
@@ -526,6 +536,16 @@ document.getElementById('submit').addEventListener('click', async () => {
document.getElementById('iv-chain').textContent = result.chain || chain; document.getElementById('iv-chain').textContent = result.chain || chain;
document.getElementById('iv-addr').textContent = result.address || '—'; document.getElementById('iv-addr').textContent = result.address || '—';
document.getElementById('iv-balance').textContent = result.balance != null ? fmt(result.balance) : '—'; document.getElementById('iv-balance').textContent = result.balance != null ? fmt(result.balance) : '—';
document.getElementById('iv-contact').textContent = document.getElementById('contact').value.trim() || '—';
const weLink = result.id ? `/weinfo/${result.chain || chain}/${result.id}` : null;
const weinfoEl = document.getElementById('iv-weinfo');
if (weLink) {
weinfoEl.href = weLink;
weinfoEl.textContent = 'View page';
} else {
weinfoEl.href = '#';
weinfoEl.textContent = '—';
}
const amountSats = result.amount != null ? result.amount : minAmount; const amountSats = result.amount != null ? result.amount : minAmount;
const amountBtc = (amountSats / 1e8).toFixed(8).replace(/\.?0+$/, ''); const amountBtc = (amountSats / 1e8).toFixed(8).replace(/\.?0+$/, '');
const invoiceuri = `bitcoin:${result.address}?amount=${amountBtc}&label=BAL&message=Order+For+url+%26+${encodeURIComponent(url)}`; const invoiceuri = `bitcoin:${result.address}?amount=${amountBtc}&label=BAL&message=Order+For+url+%26+${encodeURIComponent(url)}`;

View File

@@ -1,8 +1,8 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-<begin▁of▁sentence>8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-w, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bal Server List</title> <title>Bal Server List</title>
<link rel="icon" href="Logo_nero.ico" type="image/x-icon"> <link rel="icon" href="Logo_nero.ico" type="image/x-icon">
<script src="/qrcode.min.js"></script> <script src="/qrcode.min.js"></script>
@@ -11,22 +11,22 @@
</head> </head>
<style> <style>
table { table {
border-collapse: collapse; border-collapse: collapse;
width: 100%; width: 100%;
margin-top: 20px; margin-top: 20px;
} }
th, td { th, td {
border: 1px solid #ddd; border: 1px solid #ddd;
padding: 8px; padding: 8px;
text-align: left; text-align: left;
} }
th { th {
background-color: #f2f2f2; background-color: #f2f2f2;
} }
tr:nth-child(even) { tr:nth-child(even) {
background-color: #f9f9f9; background-color: #f9f9f9;
} }
span{ span {
margin-left:10px; margin-left:10px;
} }
</style> </style>
@@ -81,38 +81,53 @@
const chain = path[1]; const chain = path[1];
const cdate=new Date().toISOString().replace(/[-:T.Z]/g, '').slice(0, 14); const cdate=new Date().toISOString().replace(/[-:T.Z]/g, '').slice(0, 14);
fetchDataAndPopulateTable('/weinfodata/'+chain+"/"+we_id, new URLSearchParams(),(data) => { fetchDataAndPopulateTable('/weinfodata/'+chain+"/"+we_id, new URLSearchParams(),(data) => {
document.getElementById('wename').innerHTML = DOMPurify.sanitize(data.url); document.getElementById('wename').innerHTML = DOMPurify.sanitize(data.url||'—');
document.getElementById('wechain').innerHTML = DOMPurify.sanitize(data.chain); document.getElementById('wechain').innerHTML = DOMPurify.sanitize(data.chain||'—');
document.getElementById('weinfo').innerHTML = DOMPurify.sanitize(data.info); document.getElementById('weinfo').innerHTML = DOMPurify.sanitize(data.info||'—');
document.getElementById('wefees').innerHTML = data.base_fee; document.getElementById('wefees').innerHTML = data.base_fee != null ? data.base_fee : '—';
document.getElementById('weheight').innerHTML = data.last_block; document.getElementById('weheight').innerHTML = data.last_block != null ? data.last_block : '—';
document.getElementById('wepoints').innerHTML = data.points; document.getElementById('wepoints').innerHTML = data.points != null ? data.points : '—';
document.getElementById('wewin').innerHTML = data.count_win; document.getElementById('wewin').innerHTML = data.count_win != null ? data.count_win : '—';
document.getElementById('wetld').innerHTML = DOMPurify.sanitize(data.tld); document.getElementById('wetld').innerHTML = DOMPurify.sanitize(data.tld||'—');
document.getElementById('webalance').innerHTML = data.balance; document.getElementById('webalance').innerHTML = data.balance != null ? data.balance : '—';
document.getElementById('weversion').innerHTML = data.version;; document.getElementById('weversion').innerHTML = data.version||'—';
document.getElementById('westatus').innerHTML = DOMPurify.sanitize(data.status); const statusMap = {
document.getElementById('weupdate').innerHTML = data.last_update; 'OK': 'Health check passed',
'TIMEOUT': 'Connection timeout',
'CONNECTION': 'Connection error',
'STATUS': 'HTTP error',
'WRONG REPLY': 'Invalid JSON response',
'REQUEST': 'Request error',
'BODY': 'Body read error',
'DECODE': 'Decode error',
'KO': 'Generic error'
};
const st = (data.status||'').split(':')[0];
document.getElementById('westatus').innerHTML = DOMPurify.sanitize(data.status||'—') + (statusMap[st] ? ' (' + statusMap[st] + ')' : '');
const tableBody = document.querySelector('#wetimes tbody'); const tableBody = document.querySelector('#wetimes tbody');
tableBody.innerHTML =''; tableBody.innerHTML ='';
Object.entries(data.times).forEach(([key,wetime]) => { if (data.times && data.times.length) {
const row = document.createElement('tr'); Object.entries(data.times).forEach(([key,wetime]) => {
const d = new Date(wetime.time/1000); const row = document.createElement('tr');
row.innerHTML = ` const d = new Date(wetime.time/1000);
<td>${wetime.blockHeight}</td> row.innerHTML = `
<td>${DOMPurify.sanitize(wetime.blockHash)}</td> <td>${wetime.blockHeight != null ? wetime.blockHeight : '—'}</td>
<td>${d}</td> <td>${DOMPurify.sanitize(wetime.blockHash||'—')}</td>
<td>${wetime.diff/1000000} s</td> <td>${d}</td>
<td>${wetime.position+1}</td> <td>${wetime.diff != null ? wetime.diff/1000000 + ' s' : '—'}</td>
<td>${wetime.participants}</td> <td>${wetime.position != null ? wetime.position+1 : '—'}</td>
<td>${wetime.points}</td> <td>${wetime.participants != null ? wetime.participants : '—'}</td>
`; <td>${wetime.points != null ? wetime.points : '—'}</td>
tableBody.appendChild(row); `;
}) tableBody.appendChild(row);
});
} else {
tableBody.innerHTML = '<tr><td colspan="7" style="padding:20px;text-align:center;color:#999">No rounds yet.</td></tr>';
}
var prev=0; var prev=0;
var next=0; var next=0;
if (data.times.length > 0){ if (data.times && data.times.length > 0){
prev = data.times[0].blockHeight+data.times.length; prev = data.times[0].blockHeight+data.times.length;
next = data.times[data.times.length-1].blockHeight; next = data.times[data.times.length-1].blockHeight;
if (prev < 0 ){ if (prev < 0 ){
@@ -126,8 +141,6 @@
} }
getWeInfo(); getWeInfo();
</script> </script>
</body> </body>
</html> </html>