generated from nathanwoodburn/python-webserver-template
Compare commits
2 Commits
3117288872
...
d3b00a81ba
| Author | SHA1 | Date | |
|---|---|---|---|
|
d3b00a81ba
|
|||
|
91b6e32b98
|
@@ -85,6 +85,12 @@ def block_route(block_id):
|
|||||||
return render_template("index.html", datetime=current_datetime)
|
return render_template("index.html", datetime=current_datetime)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/header/<path:block_id>")
|
||||||
|
def header_route(block_id):
|
||||||
|
current_datetime = datetime.now().strftime("%d %b %Y %I:%M %p")
|
||||||
|
return render_template("index.html", datetime=current_datetime)
|
||||||
|
|
||||||
|
|
||||||
@app.route("/address/<path:address>")
|
@app.route("/address/<path:address>")
|
||||||
def address_route(address):
|
def address_route(address):
|
||||||
current_datetime = datetime.now().strftime("%d %b %Y %I:%M %p")
|
current_datetime = datetime.now().strftime("%d %b %Y %I:%M %p")
|
||||||
|
|||||||
@@ -148,6 +148,11 @@
|
|||||||
document.querySelector('[data-tab="block"]').click();
|
document.querySelector('[data-tab="block"]').click();
|
||||||
searchBlock();
|
searchBlock();
|
||||||
break;
|
break;
|
||||||
|
case 'header':
|
||||||
|
document.getElementById('block-input').value = value;
|
||||||
|
document.querySelector('[data-tab="block"]').click();
|
||||||
|
searchHeader();
|
||||||
|
break;
|
||||||
case 'tx':
|
case 'tx':
|
||||||
document.getElementById('tx-input').value = value;
|
document.getElementById('tx-input').value = value;
|
||||||
document.querySelector('[data-tab="tx"]').click();
|
document.querySelector('[data-tab="tx"]').click();
|
||||||
@@ -239,6 +244,108 @@
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format block data nicely
|
||||||
|
function formatBlockData(block) {
|
||||||
|
if (!block || block.error) {
|
||||||
|
return `<div class="error">Error: ${block.error || 'Invalid block data'}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatValue = (value) => (value / 1e6).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' HNS';
|
||||||
|
const formatTime = (timestamp) => new Date(timestamp * 1000).toLocaleString();
|
||||||
|
|
||||||
|
let html = `
|
||||||
|
<div class="tx-details">
|
||||||
|
<div class="tx-section">
|
||||||
|
<h4>Block Info</h4>
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item full-width"><strong>Hash:</strong> <span class="mono">${block.hash}</span></div>
|
||||||
|
<div class="info-item"><strong>Height:</strong> ${block.height.toLocaleString()}</div>
|
||||||
|
<div class="info-item"><strong>Confirmations:</strong> ${block.depth.toLocaleString()}</div>
|
||||||
|
<div class="info-item"><strong>Time:</strong> ${formatTime(block.time)}</div>
|
||||||
|
<div class="info-item"><strong>Transactions:</strong> ${block.txs.length.toLocaleString()}</div>
|
||||||
|
<div class="info-item full-width"><strong>Previous Block:</strong> <span class="mono">${block.prevBlock}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Merkle Root:</strong> <span class="mono">${block.merkleRoot}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Witness Root:</strong> <span class="mono">${block.witnessRoot}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Tree Root:</strong> <span class="mono">${block.treeRoot}</span></div>
|
||||||
|
<div class="info-item"><strong>Version:</strong> ${block.version}</div>
|
||||||
|
<div class="info-item"><strong>Bits:</strong> ${block.bits}</div>
|
||||||
|
<div class="info-item"><strong>Nonce:</strong> ${block.nonce.toLocaleString()}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tx-section">
|
||||||
|
<h4>Transactions (${block.txs.length})</h4>
|
||||||
|
<div class="tx-io-list">
|
||||||
|
${block.txs.map((tx, i) => {
|
||||||
|
const totalOutput = tx.outputs.reduce((sum, out) => sum + out.value, 0);
|
||||||
|
return `
|
||||||
|
<div class="tx-io-item" style="cursor: pointer;" onclick="window.location.href='/tx/${tx.hash}'">
|
||||||
|
<div class="tx-io-header">
|
||||||
|
<span class="tx-io-index">#${i}</span>
|
||||||
|
<span class="tx-io-value">${formatValue(totalOutput)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="tx-io-hash mono">${tx.hash}</div>
|
||||||
|
<div style="display: flex; justify-content: space-between; margin-top: 0.5rem; font-size: 0.85rem; color: #b0b0b0;">
|
||||||
|
<span>${tx.inputs.length} input${tx.inputs.length !== 1 ? 's' : ''}</span>
|
||||||
|
<span>${tx.outputs.length} output${tx.outputs.length !== 1 ? 's' : ''}</span>
|
||||||
|
<span>Fee: ${formatValue(tx.fee)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tx-section">
|
||||||
|
<button class="secondary-btn" onclick="this.nextElementSibling.style.display = this.nextElementSibling.style.display === 'none' ? 'block' : 'none'">Show Raw JSON</button>
|
||||||
|
<pre style="display: none;">${JSON.stringify(block, null, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format block header data nicely
|
||||||
|
function formatHeaderData(header) {
|
||||||
|
if (!header || header.error) {
|
||||||
|
return `<div class="error">Error: ${header.error || 'Invalid header data'}</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatTime = (timestamp) => new Date(timestamp * 1000).toLocaleString();
|
||||||
|
|
||||||
|
let html = `
|
||||||
|
<div class="tx-details">
|
||||||
|
<div class="tx-section">
|
||||||
|
<h4>Block Header Info</h4>
|
||||||
|
<div class="info-grid">
|
||||||
|
<div class="info-item full-width"><strong>Hash:</strong> <span class="mono">${header.hash}</span></div>
|
||||||
|
<div class="info-item"><strong>Height:</strong> ${header.height.toLocaleString()}</div>
|
||||||
|
<div class="info-item"><strong>Version:</strong> ${header.version}</div>
|
||||||
|
<div class="info-item"><strong>Time:</strong> ${formatTime(header.time)}</div>
|
||||||
|
<div class="info-item"><strong>Bits:</strong> ${header.bits}</div>
|
||||||
|
<div class="info-item"><strong>Nonce:</strong> ${header.nonce.toLocaleString()}</div>
|
||||||
|
<div class="info-item full-width"><strong>Previous Block:</strong> <span class="mono">${header.prevBlock}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Merkle Root:</strong> <span class="mono">${header.merkleRoot}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Witness Root:</strong> <span class="mono">${header.witnessRoot}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Tree Root:</strong> <span class="mono">${header.treeRoot}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Reserved Root:</strong> <span class="mono">${header.reservedRoot}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Extra Nonce:</strong> <span class="mono">${header.extraNonce}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Mask:</strong> <span class="mono">${header.mask}</span></div>
|
||||||
|
<div class="info-item full-width"><strong>Chainwork:</strong> <span class="mono">${header.chainwork}</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="tx-section">
|
||||||
|
<button class="secondary-btn" onclick="this.nextElementSibling.style.display = this.nextElementSibling.style.display === 'none' ? 'block' : 'none'">Show Raw JSON</button>
|
||||||
|
<pre style="display: none;">${JSON.stringify(header, null, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
// Format transaction data nicely
|
// Format transaction data nicely
|
||||||
function formatTransactionData(tx) {
|
function formatTransactionData(tx) {
|
||||||
if (!tx || tx.error) {
|
if (!tx || tx.error) {
|
||||||
@@ -378,7 +485,14 @@
|
|||||||
}
|
}
|
||||||
updateURL('block', blockId);
|
updateURL('block', blockId);
|
||||||
const data = await apiCall(`block/${blockId}`);
|
const data = await apiCall(`block/${blockId}`);
|
||||||
displayResult('block-result', data);
|
|
||||||
|
// Use formatted display instead of raw JSON
|
||||||
|
const resultElement = document.getElementById('block-result');
|
||||||
|
if (data.error) {
|
||||||
|
resultElement.innerHTML = `<div class="error">Error: ${data.error}</div>`;
|
||||||
|
} else {
|
||||||
|
resultElement.innerHTML = formatBlockData(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchHeader() {
|
async function searchHeader() {
|
||||||
@@ -387,8 +501,16 @@
|
|||||||
alert('Please enter a block height or hash');
|
alert('Please enter a block height or hash');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
updateURL('header', blockId);
|
||||||
const data = await apiCall(`header/${blockId}`);
|
const data = await apiCall(`header/${blockId}`);
|
||||||
displayResult('block-result', data);
|
|
||||||
|
// Use formatted display instead of raw JSON
|
||||||
|
const resultElement = document.getElementById('block-result');
|
||||||
|
if (data.error) {
|
||||||
|
resultElement.innerHTML = `<div class="error">Error: ${data.error}</div>`;
|
||||||
|
} else {
|
||||||
|
resultElement.innerHTML = formatHeaderData(data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function searchTx() {
|
async function searchTx() {
|
||||||
|
|||||||
Reference in New Issue
Block a user