This commit is contained in:
2026-03-18 16:34:29 -04:00
parent 9140486fda
commit 0851a74fe6

View File

@@ -9,242 +9,267 @@
<script src="purify.min.js"></script> <script src="purify.min.js"></script>
<script src="bal.js"></script> <script src="bal.js"></script>
<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;
} }
body>div:first-of-type{ body>div:first-of-type{
text-align:center; text-align:center;
} }
</style> </style>
</head> </head>
<body> <body>
<div> <div>
<a href="/"><img alt="BAL LOGO" src="/Logo_nero.png"></a> <a href="/"><img alt="BAL LOGO" src="/Logo_nero.png"></a>
<h1>Bal Server List</h1> <h1>Bal Server List</h1>
<div> <div>
<select id="chain" name="chain"> <select id="chain" name="chain">
<option selected value="bitcoin">Bitcoin</option> <option selected value="bitcoin">Bitcoin</option>
<option value="testnet">Testnet</option> <option value="testnet">Testnet</option>
<option value="testnet4">Testnet4</option> <option value="testnet4">Testnet4</option>
<!--option>Regtest</option--> <!--option>Regtest</option-->
</select> </select>
</div> </div>
<div> <div>
<label for="onion">Include tor: </label><input type="checkbox" name="onion" id="onion" value="yes" checked> <label for="onion">Include tor: </label><input type="checkbox" name="onion" id="onion" value="yes" checked>
</div> </div>
<div> <div>
<a href="" id="download">Download</a> <a href="" id="download">Download</a>
</div> </div>
<table id="willexecutors"> <table id="willexecutors">
<thead> <thead>
<tr> <tr>
<th>Url</th> <th>Url</th>
<th>Info</th> <th>Info</th>
<th>Fees</th> <th>Fees</th>
<th>Version</th> <th>Version</th>
<th>Balance</th> <th>Balance</th>
<th>Status</th> <th>Status</th>
<th>Height</th> <th>Height</th>
<th>Wins</th> <th>Wins</th>
<th>Score</th> <th>Score</th>
</tr> </tr>
</thead> </thead>
<tbody><!-- --></tbody> <tbody><!-- --></tbody>
</table> </table>
</div> </div>
<hr>
<form id="myServer"> <form id="myServer">
<input type="text" id="url" name="url" required placeholder="https://bitcoin-after.life:9137"> <label for="url">Add your Will-Executor url:<br><input type="text" id="url" name="url" required placeholder="https://bitcoin-after.life:9137">
<button type="submit">Submit</button> <button type="submit">Submit</button>
</form> </form>
<div id="result_text"></div> <div id="result_text"></div>
<div> <div>
<a id="aqr"> <a id="aqr">
<div id="qrcode"></div> <div id="qrcode"></div>
</a> </a>
</div>
<strong>
Please make sure your server is online before you attempt to add it.<br>
Servers that are offline will not appear in this list.<br>
They will be checked regularly until they come back online.
</strong>
<hr>
<div>
<b>Will Executor Server List</b>
<div>This page displays all Will Executor servers that manage the inheritance processing for the Bitcoin After Life plugin on Electrum. Will Executors operate in a competitive environment, racing to
broadcast inheritance transactions to network nodes and earn the associated transaction fees.</div>
<b>Listing Requirements</b>
<div>To be included in this list, a minimum contribution of 5,000 Satoshi is required.</div>
<b>Benefits of Listing</b>
<div>Listed Will Executors are automatically integrated into the plugin when inheritance arrangements are made, providing direct access to potential clients.</div>
<b>Ranking System</b>
<div>This leaderboard operates on a bid-based ranking system where higher contributions result in better positioning.</div>
<b>Early Adopter Incentives</b>
<div>To support the project's initial launch phase, we plan to maintain the first Will Executor servers for a minimum of 12 months, rewarding early participants. Subsequently, premium positions will be
available through an auction system based on protocol revenue generation.</div>
<b>Revenue Expectations</b>
<div>Operating as a Will Executor is not a get-rich-quick opportunity. This service requires patience and long-term commitment, but can provide substantial returns over time for dedicated operators.</div>
</div> </div>
<p>Please make sure your server is online before you try to add it</p>
<p>Server not online will be removed from this list</p>
<script> <script>
class BalServerList { class BalServerList {
constructor() { constructor() {
this.chain = 'bitcoin'; this.chain = 'bitcoin';
this.page = 0; this.page = 0;
this.limit = 100; this.limit = 100;
this.init(); this.init();
} }
init() { init() {
this.parseUrlParams(); this.parseUrlParams();
this.bindEvents(); this.bindEvents();
this.fetchDataAndPopulateTable(); this.fetchDataAndPopulateTable();
} }
parseUrlParams() { parseUrlParams() {
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
this.page = parseInt(urlParams.get('page')) || 0; this.page = parseInt(urlParams.get('page')) || 0;
this.limit = parseInt(urlParams.get('limit')) || 100; this.limit = parseInt(urlParams.get('limit')) || 100;
this.chain = urlParams.get('chain') || 'bitcoin'; this.chain = urlParams.get('chain') || 'bitcoin';
const chainSelect = document.getElementById('chain'); const chainSelect = document.getElementById('chain');
chainSelect.value = this.chain; chainSelect.value = this.chain;
} }
bindEvents() { bindEvents() {
document.getElementById('onion').addEventListener('change', () => { document.getElementById('onion').addEventListener('change', () => {
this.fetchDataAndPopulateTable(); this.fetchDataAndPopulateTable();
}); });
document.getElementById('chain').addEventListener('change', (e) => { document.getElementById('chain').addEventListener('change', (e) => {
this.chain = this.getChain(); this.chain = this.getChain();
this.fetchDataAndPopulateTable(); this.fetchDataAndPopulateTable();
}); });
document.getElementById('myServer').addEventListener('submit', (e) => { document.getElementById('myServer').addEventListener('submit', (e) => {
this.handleServerSubmission(e); this.handleServerSubmission(e);
}); });
} }
async handleServerSubmission(e) { async handleServerSubmission(e) {
e.preventDefault(); e.preventDefault();
const urlInput = document.getElementById('url'); const urlInput = document.getElementById('url');
let url = urlInput.value.trim(); let url = urlInput.value.trim();
if (!url) return; if (!url) return;
url = this.formatUrl(url); url = this.formatUrl(url);
urlInput.value = url; urlInput.value = url;
try { try {
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: this.chain }) body: JSON.stringify({ url, chain: this.chain })
}); });
const result = await response.json(); const result = await response.json();
if (!response.ok) { if (!response.ok) {
this.showError(result); this.showError(result);
} else { } else {
this.showSuccess(result, url); this.showSuccess(result, url);
} }
} catch (error) { } catch (error) {
console.error('Submission error:', error); console.error('Submission error:', error);
this.showError('Network error. Please try again.'); this.showError('Network error. Please try again.');
}
} }
}
formatUrl(url) { formatUrl(url) {
if (!url.startsWith('http://') && !url.startsWith('https://')) { if (!url.startsWith('http://') && !url.startsWith('https://')) {
return 'https://' + url; return 'https://' + url;
}
return url;
} }
return url;
}
showError(message) { showError(message) {
const resultDiv = document.getElementById('result_text'); const resultDiv = document.getElementById('result_text');
resultDiv.innerHTML = `<p style="color:red">${DOMPurify.sanitize(message)}</p>`; resultDiv.innerHTML = `<p style="color:red">${DOMPurify.sanitize(message)}</p>`;
} }
showSuccess(result, url) { showSuccess(result, url) {
const invoiceuri = `bitcoin:${result.address}?label=BAL&message=Order+For+url+%26+${encodeURIComponent(result.url)}`; const invoiceuri = `bitcoin:${result.address}?label=BAL&message=Order+For+url+%26+${encodeURIComponent(result.url)}`;
const cleanHtml = DOMPurify.sanitize(` const cleanHtml = DOMPurify.sanitize(`
<p style="color:green">${url} successfully added</p> <p style="color:green">${url} successfully added</p>
<div> <div>
Please Pay this invoice to complete the process:<br/> Please Pay this invoice to complete the process:<br/>
<b><u>Less than ${result.min_amount} sats are considered donations! if you want your server to be listed you have to send at least ${result.min_amount} sats</u></b><br/> <b><u>Less than ${result.min_amount} sats are considered donations! if you want your server to be listed you have to send at least ${result.min_amount} sats</u></b><br/>
<b>Chain:</b> ${result.chain}<br/> <b>Chain:</b> ${result.chain}<br/>
<b>Address:</b> ${result.address}<br/> <b>Address:</b> ${result.address}<br/>
<b>Balance:</b> ${result.balance}<br/> <b>Balance:</b> ${result.balance}<br/>
<b>Status:</b> ${result.status}<br/> <b>Status:</b> ${result.status}<br/>
</div> </div>
`); `);
document.getElementById("result_text").innerHTML = cleanHtml; document.getElementById("result_text").innerHTML = cleanHtml;
// Generate QR code // Generate QR code
const qrElement = document.getElementById("qrcode"); const qrElement = document.getElementById("qrcode");
qrElement.innerHTML = ""; qrElement.innerHTML = "";
new QRCode(qrElement, invoiceuri); new QRCode(qrElement, invoiceuri);
document.getElementById("aqr").href = invoiceuri; document.getElementById("aqr").href = invoiceuri;
} }
getChain() { getChain() {
const select = document.getElementById('chain'); const select = document.getElementById('chain');
return select.value; return select.value;
} }
async fetchDataAndPopulateTable() { async fetchDataAndPopulateTable() {
try { try {
const params = new URLSearchParams({ const params = new URLSearchParams({
page: this.page, page: this.page,
limit: this.limit limit: this.limit
}); });
if (!document.getElementById("onion").checked) { if (!document.getElementById("onion").checked) {
params.append("onion", "no"); params.append("onion", "no");
} }
const response = await fetch(`/data/${this.chain}?${params.toString()}`); const response = await fetch(`/data/${this.chain}?${params.toString()}`);
if (!response.ok) { if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`); throw new Error(`HTTP error! status: ${response.status}`);
} }
const data = await response.json(); const data = await response.json();
this.downloadLink(data); this.downloadLink(data);
this.populateTable(data); this.populateTable(data);
} catch (error) { } catch (error) {
console.error('Error fetching data:', error); console.error('Error fetching data:', error);
this.showTableError(); this.showTableError();
}
} }
}
showTableError() { showTableError() {
const tableBody = document.querySelector('#willexecutors tbody'); const tableBody = document.querySelector('#willexecutors tbody');
tableBody.innerHTML = '<tr><td colspan="6">Error loading data. Please try again later.</td></tr>'; tableBody.innerHTML = '<tr><td colspan="6">Error loading data. Please try again later.</td></tr>';
} }
downloadLink(data) { downloadLink(data) {
const jsonString = JSON.stringify(data); const jsonString = JSON.stringify(data);
const blob = new Blob([jsonString], { type: 'application/json;charset=utf-8' }); const blob = new Blob([jsonString], { type: 'application/json;charset=utf-8' });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const downloadLink = document.getElementById('download'); const downloadLink = document.getElementById('download');
downloadLink.href = url; downloadLink.href = url;
downloadLink.setAttribute('download', 'willexecutors.json'); downloadLink.setAttribute('download', 'willexecutors.json');
} }
get_url(id,url){ get_url(id,url){
} }
populateTable(willexecutors) { populateTable(willexecutors) {
const tableBody = document.querySelector('#willexecutors tbody'); const tableBody = document.querySelector('#willexecutors tbody');
tableBody.innerHTML = ''; tableBody.innerHTML = '';
if (Object.keys(willexecutors).length === 0) { if (Object.keys(willexecutors).length === 0) {
tableBody.innerHTML = '<tr><td colspan="9">No servers found</td></tr>'; tableBody.innerHTML = '<tr><td colspan="9">No servers found</td></tr>';
return; return;
} }
Object.entries(willexecutors).forEach(([key, we]) => { Object.entries(willexecutors).forEach(([key, we]) => {
const row = document.createElement('tr'); const row = document.createElement('tr');
this.appendRawTd(`<a href='/weinfo/${we.chain}/${we.id}'>${we.url}</a>`, row); this.appendRawTd(`<a href='/weinfo/${we.chain}/${we.id}'>${we.url}</a>`, row);
@@ -257,26 +282,24 @@ class BalServerList {
this.appendTd(we.count_win, row); this.appendTd(we.count_win, row);
this.appendTd(we.points, row); this.appendTd(we.points, row);
tableBody.appendChild(row); tableBody.appendChild(row);
}); });
} }
appendTd(content, tr) { appendTd(content, tr) {
const td = document.createElement('td'); const td = document.createElement('td');
td.textContent = content; td.textContent = content;
tr.appendChild(td); tr.appendChild(td);
} }
appendRawTd(content,tr){ appendRawTd(content,tr){
const td = document.createElement('td'); const td = document.createElement('td');
td.innerHTML = DOMPurify.sanitize(content); td.innerHTML = DOMPurify.sanitize(content);
tr.appendChild(td); tr.appendChild(td);
} }
} }
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
new BalServerList(); new BalServerList();
}); });
</script> </script>
</body> </body>
</html> </html>