auto broadcast pending txns, show net info

This commit is contained in:
Dobromir Popov
2025-09-30 01:33:23 +03:00
parent b96778196c
commit 499ad9690e
2 changed files with 147 additions and 42 deletions

View File

@@ -280,15 +280,39 @@
function renderTx(tx) {
const li = document.createElement('li');
const amount = Number(tx.amount).toFixed(8);
const amount = Math.abs(Number(tx.amount)).toFixed(8);
const type = tx.category === 'send' ? 'Sent' : 'Received';
const confirmations = tx.confirmations || 0;
let status, statusClass, tooltip = '';
let ageInfo = '';
if (confirmations === 0) {
status = 'Pending';
statusClass = 'status-pending';
tooltip = 'title="0 confirmations - waiting for network confirmation"';
// Calculate transaction age
const txTime = tx.time || tx.timereceived;
if (txTime) {
const now = Math.floor(Date.now() / 1000);
const ageSeconds = now - txTime;
const ageMinutes = Math.floor(ageSeconds / 60);
const ageHours = Math.floor(ageMinutes / 60);
let ageText = '';
if (ageHours > 0) {
ageText = `${ageHours}h ${ageMinutes % 60}m ago`;
} else if (ageMinutes > 0) {
ageText = `${ageMinutes}m ago`;
} else {
ageText = `${ageSeconds}s ago`;
}
ageInfo = `<div class="muted" style="font-size: 11px;">Age: ${ageText}</div>`;
tooltip = `title="0 confirmations - waiting for network confirmation (${ageText})"`;
} else {
tooltip = 'title="0 confirmations - waiting for network confirmation"';
}
} else if (confirmations < 20) {
status = `Immature (${confirmations}/20)`;
statusClass = 'status-immature';
@@ -302,6 +326,7 @@
li.innerHTML = `
<div><strong>${type}</strong> ${amount} RIN <span class="status-badge ${statusClass}" ${tooltip}>${status}</span></div>
<div class="muted">${tx.time ? new Date(tx.time * 1000).toLocaleString() : ''}</div>
${ageInfo}
<div class="muted"><a href="https://explorer.rin.so/tx/${tx.txid}" target="_blank" class="tx-link">${tx.txid}</a></div>
`;
return li;
@@ -403,55 +428,94 @@
}
async function fetchNetworkInfo() {
let res = await fetch('/api/network', { headers: authHeaders() });
let data = await res.json();
try {
let res = await fetch('/api/network', { headers: authHeaders() });
let data = await res.json();
// If token is invalid, refresh and retry
if (data.error === 'invalid_token') {
await refreshToken();
res = await fetch('/api/network', { headers: authHeaders() });
data = await res.json();
// If token is invalid, refresh and retry
if (data.error === 'invalid_token') {
await refreshToken();
res = await fetch('/api/network', { headers: authHeaders() });
data = await res.json();
}
if (data.error) {
networkInfo.innerHTML = `<div class="muted">Error: ${data.error}</div>`;
return;
}
const network = data.network;
const blockchain = data.blockchain;
// Build peers list
let peersHtml = '';
if (data.peers && data.peers.length > 0) {
peersHtml = '<div style="margin-top: 12px;"><div class="muted" style="font-size: 12px; margin-bottom: 8px;">Connected Peers</div><div style="max-height: 200px; overflow-y: auto;">';
data.peers.forEach(peer => {
const relayStatus = peer.relaytxes ? '✓ Relay' : '✗ No Relay';
const syncStatus = peer.synced_blocks === blockchain.blocks ? '✓ Synced' : `${peer.synced_blocks}`;
peersHtml += `
<div style="padding: 6px; margin-bottom: 4px; background: rgba(9, 19, 30, 0.6); border-radius: 4px; font-size: 11px;">
<div style="font-weight: 600;">${peer.addr}</div>
<div class="muted">${relayStatus} | ${peer.version} | Ping: ${peer.pingtime}ms | Blocks: ${syncStatus}</div>
</div>
`;
});
peersHtml += '</div></div>';
}
networkInfo.innerHTML = `
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px;">
<div>
<div class="muted">Network Connections</div>
<div style="font-size: 18px; font-weight: 600;">${network.connections}</div>
</div>
<div>
<div class="muted">Mempool Size</div>
<div style="font-size: 18px; font-weight: 600;">${data.mempool_size}</div>
</div>
<div>
<div class="muted">Block Height</div>
<div style="font-size: 18px; font-weight: 600;">${blockchain.blocks}</div>
</div>
<div>
<div class="muted">Chain</div>
<div style="font-size: 18px; font-weight: 600;">${blockchain.chain}</div>
</div>
</div>
${peersHtml}
<div class="muted" style="font-size: 12px; margin-top: 12px;">
Network Active: ${network.networkactive ? 'Yes' : 'No'} |
Relay Fee: ${network.relayfee} RIN |
Difficulty: ${Number(blockchain.difficulty).toFixed(2)}
</div>
`;
} catch (err) {
console.error('Network info fetch failed:', err);
networkInfo.innerHTML = `<div class="muted">Error loading network info: ${err.message}</div>`;
}
}
if (data.error) {
networkInfo.innerHTML = `<div class="muted">Error: ${data.error}</div>`;
return;
async function rebroadcastPending() {
try {
const res = await fetch('/api/rebroadcast', {
method: 'POST',
headers: authHeaders(),
});
const data = await res.json();
console.log(`Rebroadcasted ${data.count} pending transactions`);
} catch (err) {
console.error('Rebroadcast failed:', err);
}
const network = data.network;
const blockchain = data.blockchain;
networkInfo.innerHTML = `
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px;">
<div>
<div class="muted">Network Connections</div>
<div style="font-size: 18px; font-weight: 600;">${network.connections}</div>
</div>
<div>
<div class="muted">Mempool Size</div>
<div style="font-size: 18px; font-weight: 600;">${data.mempool_size}</div>
</div>
<div>
<div class="muted">Block Height</div>
<div style="font-size: 18px; font-weight: 600;">${blockchain.blocks}</div>
</div>
<div>
<div class="muted">Chain</div>
<div style="font-size: 18px; font-weight: 600;">${blockchain.chain}</div>
</div>
</div>
<div class="muted" style="font-size: 12px;">
Network Active: ${network.networkactive ? 'Yes' : 'No'} |
Relay Fee: ${network.relayfee} RIN |
Difficulty: ${blockchain.difficulty.toFixed(2)}
</div>
`;
}
async function refresh() {
await Promise.all([fetchBalances(), fetchTransactions(), fetchNetworkInfo()]);
}
// Auto-rebroadcast pending transactions every minute
setInterval(rebroadcastPending, 60000);
document.getElementById('refreshButton').addEventListener('click', refresh);
document.getElementById('generateButton').addEventListener('click', generateAddress);
document.getElementById('sendButton').addEventListener('click', sendCoins);