1 Commits

Author SHA1 Message Date
0ab7b458a4 Update terms.html
Rewrite sections 2-4: yearly listing model, fixed logo
2026-07-09 13:06:09 +00:00
4 changed files with 109 additions and 390 deletions

153
README.md
View File

@@ -1,153 +1,2 @@
# Server Status: Complete Explanation and Reference Guide
welist html
This page explains all the **Will Executor server health statuses** used throughout the BAL Web Interface. Each status type provides critical information about server operation, health checks, and potential issues for administrators monitoring the Bitcoin After Life network.
## Status Code Overview
| Status | Description | When It Occurs |
|--------|-------------|----------------|
| `OK` | Health check passed | Server is online, functioning normally |
| `TIMEOUT: <message>` | Connection timeout | Server response took too long |
| `CONNECTION: <message>` | Connection error | Network connectivity issues |
| `STATUS: <code>` | HTTP error (from server) | Server returned non-200 status |
| `WRONG REPLY: <message>` | Invalid JSON response | Server response couldn't be parsed |
| `REQUEST: <message>` | Request error | Client-side request failed |
| `BODY: <message>` | Body read error | Failed to read response body |
| `DECODE: <message>` | Decode error | Failed to decode response content |
| `KO` | Generic error | Unknown or unspecified error |
## Status Types Explained
### 🟢 Good Statuses
**`OK`** - Everything is functioning correctly:
- Server is online and responding
- Health check completed successfully
- Ready for new registration/processing
### 🟡 Warning/Moderate Issues
**`TIMEOUT: <message>`** - Connection timed out:
- Server took too than expected to respond
- Could indicate server overload or network issues
- Requires attention but server may still be functional
**`CONNECTION: <message>`** - Connection error:
- Network connectivity problems
- Server might be reachable but cannot communicate
- Potential infrastructure or configuration issue
**`STATUS: <code>`** - HTTP error:
- Server responded with an error code (4xx or 5xx)
- Could indicate misconfiguration, maintenance, or issues |
- Different error codes mean different things
### 🔴 Critical Issues
**`WRONG REPLY`** - Invalid JSON response:
- Server response format is incorrect
- May indicate server software bug or corruption
- Prevents proper data processing
**`REQUEST`** - Request error:
- Client request failed
- Network or protocol issue
- Server may be unreachable
**`BODY`** - Body read error:
- Failed to read response body after successful connection
- Could indicate server processing issues
- Data corruption or transmission problems
**`DECODE`** - Decode error:
- Failed to decode response content
- Encoding mismatch or corrupted data
- Prevents proper data interpretation
**`KO`** - Generic error:
- Unknown or unspecified error
- Fallback for all other unidentified issues
- Indicates something went wrong but exact cause unknown
## Example Status Messages
### Normal Operations
```
Status: OK
Meaning: Health check passed
Display: Server is healthy and ready
```
### Network/Time Issues
```
Status: TIMEOUT: Connection timed out after 30 seconds
Meaning: Server took too long to respond
Display: "Connection timeout (Connection timed out after 30 seconds)"
```
### HTTP Errors
```
Status: STATUS: 500
Meaning: Server returned HTTP 500 Internal Server Error
Display: "HTTP error (HTTP error)"
```
### Response Processing Issues
```
Status: WRONG REPLY: Invalid JSON
Meaning: Server response could not be parsed as JSON
Display: "Invalid JSON response (Invalid JSON)"
```
## Practical Usage Guide
### For Server Administrators
- **OK**: Check server resources and logs if needed
- **TIMEOUT/CONNECTION**: Check server network connectivity
- **STATUS**: Review server logs and restart if needed
- **WRONG REPLY/DECODE/REQUEST**: Investigate server software issues
- **KO**: Review server errors and restart
### For Users
- **OK**: Server is working correctly
- **TIMEOUT/CONNECTION**: Temporary issue, try again later
- **STATUS**: Server may be down for maintenance
- **Other errors**: Server issue, contact support if persistent
## Monitoring Tips
1. **Continuous Monitoring**: Regular checks of status codes help detect issues early
2. **Alert Setup**: Configure alerts for non-OK statuses
3. **Log Analysis**: Correlate status codes with server logs
4. **Capacity Planning**: Address persistent timeouts/connections with infrastructure improvements
## Status Processing Logic
### Status Code Processing
```javascript
const statusMap = {
'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'
};
// Extract base status from status message
const st = (data.status||'').split(':')[0];
// Display only the description (e.g., "Connection timeout" instead of "TIMEOUT: Connection timeout")
statusDisplay = statusMap[st] || '—';
```
## Related Documentation
For more details on server setup, monitoring, and troubleshooting, refer to:
- [Server Administration Guide](./administration)
- [Status Monitoring Setup](./monitoring-setup)
- [Error Resolution Handbook](./error-resolve)
## Additional Information
This status system provides a clear, human-readable way to understand server health at a glance, making it easier for administrators to quickly identify and respond to issues.

View File

@@ -117,8 +117,7 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
.num{font-family:var(--mono);font-variant-numeric:tabular-nums}
.score{font-family:var(--display);font-weight:600;font-size:15px}
.add-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:24px;margin:40px 0;align-items:stretch}
.add-grid>.panel{display:flex;flex-direction:column}
.add-grid{display:grid;grid-template-columns:1.3fr 1fr;gap:24px;margin:40px 0;align-items:start}
.panel{
background:var(--panel);border:1px solid var(--line);
border-radius:var(--radius);padding:24px;box-shadow:var(--shadow);
@@ -140,17 +139,6 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
.btn:disabled{opacity:.45;cursor:not-allowed;filter:none}
.note{font-size:13px;color:var(--muted);margin-top:16px;line-height:1.55}
.note strong{color:var(--ink)}
.freshness-note{
font-size:12px;line-height:1.4;color:var(--muted);
background:var(--accent-soft);border:1px solid var(--line);border-radius:8px;
padding:7px 12px;margin:0 0 12px;
}
.freshness-note strong{color:var(--ink)}
.btn-share{
display:block;margin:0 auto 8px;font:inherit;font-size:13px;cursor:pointer;
padding:8px 16px;border-radius:8px;border:1px solid var(--line-strong);
background:var(--panel);color:var(--accent);
}
.invoice{display:none}
.invoice.show{display:block}
@@ -158,18 +146,8 @@ tbody tr:hover{background:color-mix(in srgb,var(--accent-soft) 50%,transparent)}
.invoice .row:last-child{border-bottom:0}
.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 a{color:var(--accent);text-decoration:none}
.invoice .row a:hover{text-decoration:underline}
#qrcode{margin:18px auto 4px;width:160px}
#qrcode canvas,#qrcode img{width:160px!important;height:160px!important;max-width:100%}
#qr-overlay{
display:none;position:fixed;inset:0;z-index:999;
background:rgba(0,0,0,.7);cursor:pointer;
align-items:center;justify-content:center;
}
#qr-overlay.show{display:flex}
#qr-big{padding:24px;background:#fff;border-radius:12px}
#qr-big canvas,#qr-big img{width:280px!important;height:280px!important;max-width:90vw}
#qrcode{margin:18px auto 4px;width:160px;height:160px}
#qrcode img{width:160px;height:160px}
.ok-line{color:var(--ok);font-weight:600;font-size:14px;margin-bottom:14px}
.err-line{color:#d32f2f;font-weight:600;font-size:14px;margin-bottom:14px}
.terms-box{display:none;margin:16px 0;padding:20px;background:var(--panel);border:1px solid var(--line);border-radius:var(--radius)}
@@ -265,10 +243,6 @@ footer a{color:var(--accent);text-decoration:none}
<a class="btn-ghost" id="download" href="#" download="willexecutors.json">↓ JSON</a>
</div>
<p class="freshness-note">
<strong>Fee/description/version</strong> refresh about once a day (timing varies). <strong>Deposits</strong> are credited instantly after 3 confirmations (~30 min).
</p>
<div class="table-card">
<table id="tbl">
<thead>
@@ -278,6 +252,7 @@ footer a{color:var(--accent);text-decoration:none}
<th>Info</th>
<th class="num">Fee</th>
<th>Version</th>
<th class="num">Balance</th>
<th class="num">Height</th>
<th class="num">Wins</th>
<th class="num">Score</th>
@@ -297,21 +272,18 @@ footer a{color:var(--accent);text-decoration:none}
<div class="add-grid">
<div class="panel">
<h2>Add your Will Executor</h2>
<p class="lead">Enter your server URL. It will appear in the list once your deposit is confirmed (typically about 30 minutes).</p>
<p class="lead">Enter your server URL. It will be checked online before it appears in the list.</p>
<div class="form-row">
<input type="text" id="url" placeholder="https://we.bitcoin-after.life">
<input type="text" id="url" placeholder="https://bitcoin-after.life:9137">
<button class="btn" id="submit">Add server</button>
</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="terms-box" id="terms-box">
<p class="lead">You are about to add this server:</p>
<p><a id="terms-url" href="#" target="_blank" rel="noopener" class="mono"></a></p>
<p style="font-size:13px;margin-top:4px"><strong>Make sure the link above works correctly before paying. No refunds after payment. URL changes require proof of ownership — see the <a href="/terms.html" target="_blank">Terms</a>.</strong></p>
<p style="font-size:13px;margin-top:4px"><strong>Make sure the link above works correctly before paying. No refunds or URL changes after payment.</strong></p>
<p style="font-size:14px">
<label>
<input type="checkbox" id="terms-check">
@@ -322,31 +294,26 @@ footer a{color:var(--accent);text-decoration:none}
</div>
<div class="invoice" id="invoice">
<div class="row"><span>Annual fee</span><span id="iv-min"></span></div>
<div class="row"><span>Minimum amount</span><span id="iv-min"></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>Balance</span><span id="iv-balance"></span></div>
<div class="row"><span>Donations</span><span id="iv-donations"></span></div>
<div class="row"><span>Contact</span><span id="iv-contact"></span></div>
<div class="row"><span>Status</span><span id="iv-status2"></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 class="row" id="iv-nextdep-row" style="display:none"><span>Next renewal</span><span id="iv-nextdep"></span></div>
<div id="qrcode"></div>
<button class="btn-share" id="share-btn">Share invoice</button>
</div>
<p class="note">
<strong>Please make sure your server is online before you attempt to add it.</strong>
</p>
</div>
</div>
<div class="panel">
<h2>How ranking works</h2>
<p class="lead">Ordered by excess donated, then total paid, then base fee (lower first), then WeTime points.</p>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Yearly fee</span><b class="mono"><span id="thr-min">50,000</span> sats</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Listing period</span><b>365 days</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Renewal window</span><b>from 6 months before expiry</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;font-size:13px"><span style="color:var(--muted)">Plugin integration</span><b>automatic</b></div>
<p class="lead">Bid-based ranking: higher contributions rank higher.</p>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Deposit threshold</span><b class="mono"><span id="thr-min">50,000</span> sats</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--line);font-size:13px"><span style="color:var(--muted)">Plugin integration</span><b>automatic</b></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;font-size:13px"><span style="color:var(--muted)">Early-adopter guarantee</span><b>12 months</b></div>
</div>
</div>
@@ -361,13 +328,13 @@ footer a{color:var(--accent);text-decoration:none}
<div class="info-block">
<span class="ic">02</span>
<h3>Ranking System</h3>
<p>This leaderboard is ordered by excess donated (over the yearly fee), then total amount paid, then lower base fee, then WeTime points.</p>
<p>This leaderboard operates on a bid-based ranking system where higher contributions result in better positioning.</p>
</div>
<div class="info-block">
<span class="ic">03</span>
<h3>Listing &amp; Renewal</h3>
<p>A flat yearly fee buys 365 days of presence in the list, starting when your payment reaches 3 confirmations. You can renew for one additional year at any time from 6 months before expiry; listings that are not renewed are removed at expiry. See the <a href="/terms.html" target="_blank">Terms</a> for details.</p>
<h3>Early Adopter Incentives</h3>
<p>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.</p>
</div>
<div class="info-block">
@@ -384,7 +351,6 @@ footer a{color:var(--accent);text-decoration:none}
<div class="wrap">
<span>Bitcoin After Life — Will Executor List</span>
<a href="mailto:welist@bitcoin-after.life">welist@bitcoin-after.life</a>
<a href="/terms.html">Terms</a>
<a class="mono" id="commit-link" href="https://bitcoin-after.life/gitea/bitcoinafterlife/bal-welist-website/commit/COMMIT_HASH_PLACEHOLDER">COMMIT_HASH_PLACEHOLDER</a>
</div>
</footer>
@@ -404,7 +370,9 @@ function getChain() {
function getFiltered() {
return allServers
.filter(d => (d.url + " " + (d.info||"")).toLowerCase().includes(q.toLowerCase()));
.filter(d => withTor ? true : !d.onion)
.filter(d => (d.url + " " + (d.info||"")).toLowerCase().includes(q.toLowerCase()))
.sort((a, b) => (b.balance||0) - (a.balance||0) || (a.base_fee||0) - (b.base_fee||0) || (b.points||0) - (a.points||0));
}
function render() {
@@ -414,7 +382,7 @@ function render() {
tb.innerHTML = ""; ml.innerHTML = "";
if (!list.length) {
tb.innerHTML = '<tr><td colspan="8" style="padding:28px;text-align:center;color:var(--muted)">No servers found.</td></tr>';
tb.innerHTML = '<tr><td colspan="9" style="padding:28px;text-align:center;color:var(--muted)">No servers found.</td></tr>';
}
list.forEach((d, i) => {
@@ -428,6 +396,7 @@ function render() {
<td class="info">${DOMPurify.sanitize(d.info||'—')}</td>
<td class="num">${fmt(d.base_fee)}</td>
<td class="mono">${DOMPurify.sanitize(d.version||'—')}</td>
<td class="num">${fmt(d.balance)}</td>
<td class="num">${fmt(d.last_block)}</td>
<td class="num">${d.count_win != null ? d.count_win : '—'}</td>
<td class="num"><span class="score">${fmt(d.points)}</span></td>
@@ -442,18 +411,18 @@ function render() {
<div><span>Score</span><b>${fmt(d.points)}</b></div>
<div><span>Wins</span><b>${d.count_win != null ? d.count_win : '—'}</b></div>
<div><span>Fee</span><b>${fmt(d.base_fee)} sat</b></div>
<div><span>Balance</span><b>${fmt(d.balance)}</b></div>
<div><span>Version</span><b>${DOMPurify.sanitize(d.version||'—')}</b></div>
<div><span>Height</span><b>${fmt(d.last_block)}</b></div>
</div>
</div>`);
});
const online = allServers.filter(d => d.status === "OK").length;
const online = allServers.filter(d => d.status === "online" || d.status === "OK").length;
document.getElementById('s-online').textContent = online;
document.getElementById('s-total').textContent = allServers.length;
// Highest known block across all servers (the list is ranked by fee/points, not by height)
const maxBlock = allServers.reduce((m, d) => Math.max(m, d.last_block || 0), 0);
document.getElementById('s-block').textContent = maxBlock ? fmt(maxBlock) : '—';
const top = getFiltered()[0];
document.getElementById('s-block').textContent = top ? fmt(top.last_block) : '—';
}
async function fetchData() {
@@ -522,50 +491,18 @@ document.getElementById('submit').addEventListener('click', async () => {
document.getElementById('iv-chain').textContent = '—';
document.getElementById('iv-addr').textContent = '—';
document.getElementById('iv-balance').textContent = '—';
document.getElementById('iv-donations').textContent = '—';
document.getElementById('iv-status2').textContent = '—';
document.getElementById('iv-url').textContent = '—';
document.getElementById('iv-contact').textContent = '—';
document.getElementById('iv-weinfo').href = '#';
document.getElementById('iv-weinfo').textContent = '—';
document.getElementById('iv-nextdep-row').style.display = 'none';
document.getElementById('iv-nextdep').textContent = '—';
document.getElementById('iv-min').parentElement.style.display = '';
document.getElementById('iv-addr').parentElement.style.display = '';
document.getElementById('qrcode').style.display = '';
document.getElementById('share-btn').style.display = '';
document.getElementById('qrcode').innerHTML = '';
document.getElementById('qrcode').style.cursor = '';
document.getElementById('qr-overlay').classList.remove('show');
document.getElementById('share-btn').textContent = 'Share invoice';
document.getElementById('terms-box').classList.remove('show');
document.getElementById('terms-box').style.display = '';
document.getElementById('terms-check').checked = false;
document.getElementById('terms-accept').disabled = true;
const chain = getChain();
const infoUrl = url + '/' + chain + '/info';
document.getElementById('terms-url').href = infoUrl;
document.getElementById('terms-url').textContent = url;
document.getElementById('terms-box').classList.add('show');
document.getElementById('terms-box').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
});
document.getElementById('terms-check').addEventListener('change', function () {
document.getElementById('terms-accept').disabled = !this.checked;
});
document.getElementById('terms-accept').addEventListener('click', async function () {
document.getElementById('terms-box').classList.remove('show');
const urlInput = document.getElementById('url');
let url = urlInput.value.trim();
if (!url.startsWith('http://') && !url.startsWith('https://')) {
url = 'https://' + url;
}
const chain = getChain();
try {
const response = await fetch('/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url, chain, contact: document.getElementById('contact').value.trim() || undefined })
body: JSON.stringify({ url, chain })
});
const result = await response.json();
if (!response.ok) {
@@ -576,86 +513,48 @@ document.getElementById('terms-accept').addEventListener('click', async function
document.getElementById('iv-min').textContent = fmt(result.min_amount != null ? result.min_amount : minAmount) + ' sats';
document.getElementById('iv-chain').textContent = result.chain || chain;
document.getElementById('iv-addr').textContent = result.address || '—';
const balStr = result.balance != null ? fmt(result.balance) : null;
const uncStr = result.unconfirmed_balance != null ? fmt(result.unconfirmed_balance) : null;
document.getElementById('iv-balance').textContent = balStr ? (uncStr ? balStr + ' (' + uncStr + ')' : balStr) : '—';
document.getElementById('iv-donations').textContent = result.donation != null ? fmt(result.donation) : '—';
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 serverUrl = result.url || url;
document.getElementById('iv-url').textContent = serverUrl;
if (result.payment_needed === false) {
document.getElementById('iv-min').parentElement.style.display = 'none';
document.getElementById('iv-addr').parentElement.style.display = 'none';
document.getElementById('qrcode').style.display = 'none';
document.getElementById('share-btn').style.display = 'none';
document.getElementById('iv-nextdep').textContent = result.next_deposit_date || '—';
document.getElementById('iv-nextdep-row').style.display = 'flex';
document.getElementById('iv-status').className = 'ok-line';
document.getElementById('iv-status').textContent = 'Server is already listed. Next renewal: ' + (result.next_deposit_date || '—');
const inv = document.getElementById('invoice');
inv.classList.add('show');
inv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
return;
}
const amountSats = result.min_amount != null ? result.min_amount : minAmount;
const amountBtc = (amountSats / 1e8).toFixed(8).replace(/\.?0+$/, '');
const invoiceuri = `bitcoin:${result.address}?amount=${amountBtc}&label=BAL&message=Order+For+url+%26+${encodeURIComponent(url)}`;
document.getElementById('iv-balance').textContent = result.balance != null ? fmt(result.balance) : '—';
document.getElementById('iv-status2').textContent = result.status || '—';
const invoiceuri = `bitcoin:${result.address}?label=BAL&message=Order+For+url+%26+${encodeURIComponent(url)}`;
const qrEl = document.getElementById('qrcode');
qrEl.innerHTML = '';
new QRCode(qrEl, { text: invoiceuri, width: 160, height: 160 });
qrEl.style.cursor = 'pointer';
qrEl.onclick = function () {
const overlay = document.getElementById('qr-overlay');
const big = document.getElementById('qr-big');
big.innerHTML = '';
new QRCode(big, { text: invoiceuri, width: 280, height: 280 });
overlay.classList.add('show');
};
document.getElementById('qr-overlay').onclick = function () {
this.classList.remove('show');
};
const shareBtn = document.getElementById('share-btn');
shareBtn.textContent = 'Share invoice';
shareBtn.onclick = function () {
if (navigator.share) {
navigator.share({ title: 'BAL Invoice', text: invoiceuri });
} else {
navigator.clipboard.writeText(invoiceuri).then(() => {
this.textContent = 'Copied!';
setTimeout(() => this.textContent = 'Share invoice', 2000);
});
}
};
document.getElementById('iv-status').className = 'ok-line';
document.getElementById('iv-status').textContent = '✓ Server verified: ' + serverUrl + '. Pay the invoice — it will be listed after 3 confirmations.';
const inv = document.getElementById('invoice');
inv.classList.add('show');
inv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
new QRCode(qrEl, invoiceuri);
const serverUrl = result.url || url;
const infoUrl = serverUrl + '/' + (result.chain || chain) + '/info';
document.getElementById('iv-url').textContent = serverUrl;
document.getElementById('terms-url').href = infoUrl;
document.getElementById('terms-url').textContent = serverUrl;
document.getElementById('terms-box').classList.add('show');
document.getElementById('terms-box').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
} catch (err) {
document.getElementById('iv-status').className = 'err-line';
document.getElementById('iv-status').textContent = 'Network error. Please try again.';
}
});
document.getElementById('terms-check').addEventListener('change', function () {
document.getElementById('terms-accept').disabled = !this.checked;
});
document.getElementById('terms-accept').addEventListener('click', function () {
document.getElementById('terms-box').classList.remove('show');
const inv = document.getElementById('invoice');
document.getElementById('iv-status').className = 'ok-line';
document.getElementById('iv-status').textContent = '✓ Server added: ' + document.getElementById('iv-url').textContent + '. Pay the invoice to complete.';
inv.classList.add('show');
inv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
});
document.getElementById('url').addEventListener('keydown', e => {
if (e.key === 'Enter') document.getElementById('submit').click();
});
(async function init() {
await Promise.all([fetchData(), fetchMinAmount()]);
(function init() {
const blob = new Blob([JSON.stringify(allServers, null, 2)], { type: 'application/json' });
document.getElementById('download').href = URL.createObjectURL(blob);
fetchData();
fetchMinAmount();
})();
</script>
<div id="qr-overlay"><div id="qr-big"></div></div>
</body>
</html>

View File

@@ -7,12 +7,9 @@
<link rel="icon" href="Logo_nero.ico" type="image/x-icon">
<!--
Sections 2-4 rewritten on 2026-07 for the yearly-listing model
(flat yearly fee, renewal from 6 months before expiry). The list
is ordered by total excess donated, then total amount paid, then
base fee, then WeTime points as a tie-breaker (see Section 3),
matching the server-side get_list() ordering. Section 1, header,
footer and the dynamic min-deposit script are unchanged from the
previous version.
(flat yearly fee, renewal from 6 months before expiry, list
ordered by points). Section 1, header, footer and the dynamic
min-deposit script are unchanged from the previous version.
-->
<style>
:root{
@@ -238,7 +235,7 @@ l-49 -25 -880 0 -880 0 -3 704 c-2 557 0 706 10 713 7 4 407 7 888 5 818 -2
<section class="info-section">
<h2>2. Listing &amp; Renewal</h2>
<p class="sub">A listing works like a hosting plan: a flat yearly fee buys your server one year of presence in the list. Your paid amount accumulates and never decays — it is preserved across renewals and also determines your position in the list (see Section&nbsp;3). Payments are non-refundable (see Section&nbsp;1) — double-check your server URL before paying.</p>
<p class="sub">A listing works like a hosting plan: a flat yearly fee buys your server one year of presence in the list. No balances, no ranking by deposit, no daily decay. Payments are non-refundable (see Section&nbsp;1) — double-check your server URL before paying.</p>
<div class="info-grid">
<div class="info-block">
<span class="ic">01 · PAYMENT</span>
@@ -255,7 +252,7 @@ l-49 -25 -880 0 -880 0 -3 704 c-2 557 0 706 10 713 7 4 407 7 888 5 818 -2
<div class="info-block">
<span class="ic">03 · CAPACITY</span>
<h3>No Cap on Listings</h3>
<p>There is no fixed number of slots: every server with remaining listing time is shown. Paying buys presence in the list; the total amount paid also affects your position in it (see Section&nbsp;3).</p>
<p>There is no fixed number of slots: every server with remaining listing time is shown. Paying buys presence in the list, not a position in it (see Section&nbsp;3).</p>
<p>Removal from the list does not affect users who already have your server in their plugin: the plugin's server list is add-only. Being listed matters for reaching new users.</p>
</div>
<div class="info-block">
@@ -286,19 +283,13 @@ l-49 -25 -880 0 -880 0 -3 704 c-2 557 0 706 10 713 7 4 407 7 888 5 818 -2
<section class="info-section">
<h2>3. List Order &amp; Server Page</h2>
<p class="sub">The list is ordered by four criteria: first by total excess donated (higher first), then by total amount paid in balance (higher first), then by the base fee charged (lower first), and finally by WeTime points (higher first) as a tie-breaker.</p>
<p class="sub">The list is ordered by points, earned by proving your server is online and synchronized. Payment does not affect position.</p>
<div class="info-grid">
<div class="info-block">
<span class="ic">ORDER</span>
<h3>How Position Is Determined</h3>
<p>Servers are sorted by four criteria, in order: first by the total excess donated (higher first), then by the total paid amount (higher first), then by the base fee they charge (lower first), and finally by WeTime points (higher first) to break any remaining ties.</p>
<p>Because excess donated comes first and never decays, over-paying or tipping more — on top of any payment — directly and permanently moves a server up the list. Paying more than the exact yearly fee is the fastest way to improve position.</p>
</div>
<div class="info-block">
<span class="ic">POINTS</span>
<h3>WeTime Points</h3>
<h3>Points &amp; WeTime</h3>
<p>Points are earned by participating in WeTime rounds. Each executor periodically submits its block hash; the faster the response within the same block, the more points awarded. The win count tracks how many times an executor was the fastest responder.</p>
<p>Points are not transferable, not redeemable and have no monetary value: within this list they act only as a tie-breaker. Points measure presence and reactivity — they do not certify how a server will handle inheritance transactions.</p>
<p>Points are not transferable, not redeemable and have no monetary value: they exist only to order this list. Points measure presence and reactivity — they do not certify how a server will handle inheritance transactions.</p>
</div>
<div class="info-block">
<span class="ic">DETAILS</span>

View File

@@ -1,8 +1,8 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta charset="UTF-<begin▁of▁sentence>8" />
<meta name="viewport" content="width=device-w, initial-scale=1.0" />
<title>Bal Server List</title>
<link rel="icon" href="Logo_nero.ico" type="image/x-icon">
<script src="/qrcode.min.js"></script>
@@ -26,7 +26,7 @@
tr:nth-child(even) {
background-color: #f9f9f9;
}
span {
span{
margin-left:10px;
}
</style>
@@ -41,7 +41,6 @@
<div><span>Version:</span><span id="weversion">&nbsp;</span></div>
<div><span>Fees:</span><span id="wefees">&nbsp;</span></div>
<div><span>Tld:</span><span id="wetld">&nbsp;</span></div>
<div><span>Last Update:</span><span id="weupdate">&nbsp;</span></div>
</div>
<div style="float:left;border:1;margin-right:50px;">
<div><span>Height:</span><span id="weheight">&nbsp;</span></div>
@@ -50,9 +49,8 @@
</div>
<div style="border:1;margin-right:50px;">
<div><span>Balance:</span><span id="webalance">&nbsp;</span></div>
<div><span>Donations:</span><span id="wedonations">&nbsp;</span></div>
<div><span>Status:</span><span id="westatus">&nbsp;</span></div>
<div><span>Expiry:</span><span id="weexpiry">&nbsp;</span></div>
<div><span>Last Update:</span><span id="weupdate">&nbsp;</span></div>
</div>
<div>
<table id="wetimes">
@@ -83,58 +81,38 @@
const chain = path[1];
const cdate=new Date().toISOString().replace(/[-:T.Z]/g, '').slice(0, 14);
fetchDataAndPopulateTable('/weinfodata/'+chain+"/"+we_id, new URLSearchParams(),(data) => {
document.getElementById('wename').innerHTML = DOMPurify.sanitize(data.url||'—');
document.getElementById('wechain').innerHTML = DOMPurify.sanitize(data.chain||'—');
document.getElementById('weinfo').innerHTML = DOMPurify.sanitize(data.info||'—');
document.getElementById('wefees').innerHTML = data.base_fee != null ? data.base_fee : '—';
document.getElementById('weheight').innerHTML = data.last_block != null ? data.last_block : '—';
document.getElementById('wepoints').innerHTML = data.points != null ? data.points : '—';
document.getElementById('wewin').innerHTML = data.count_win != null ? data.count_win : '—';
document.getElementById('wetld').innerHTML = DOMPurify.sanitize(data.tld||'—');
const bal = data.balance != null ? data.balance : null;
const unc = data.unconfirmed_balance != null ? data.unconfirmed_balance : null;
document.getElementById('webalance').innerHTML = bal ? (unc ? bal + ' (' + unc + ')' : bal) : '—';
document.getElementById('wedonations').innerHTML = data.donation != null ? data.donation : '—';
document.getElementById('weversion').innerHTML = data.version||'—';
document.getElementById('weexpiry').innerHTML = '<b>' + (data.expiry_date || '—') + '</b>';
document.getElementById('weupdate').innerHTML = data.last_update || '—';
const statusMap = {
'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 = statusMap[st] || '—';
document.getElementById('wename').innerHTML = DOMPurify.sanitize(data.url);
document.getElementById('wechain').innerHTML = DOMPurify.sanitize(data.chain);
document.getElementById('weinfo').innerHTML = DOMPurify.sanitize(data.info);
document.getElementById('wefees').innerHTML = data.base_fee;
document.getElementById('weheight').innerHTML = data.last_block;
document.getElementById('wepoints').innerHTML = data.points;
document.getElementById('wewin').innerHTML = data.count_win;
document.getElementById('wetld').innerHTML = DOMPurify.sanitize(data.tld);
document.getElementById('webalance').innerHTML = data.balance;
document.getElementById('weversion').innerHTML = data.version;;
document.getElementById('westatus').innerHTML = DOMPurify.sanitize(data.status);
document.getElementById('weupdate').innerHTML = data.last_update;
const tableBody = document.querySelector('#wetimes tbody');
tableBody.innerHTML ='';
if (data.times && data.times.length) {
Object.entries(data.times).forEach(([key,wetime]) => {
const row = document.createElement('tr');
const d = new Date(wetime.time/1000);
row.innerHTML = `
<td>${wetime.blockHeight != null ? wetime.blockHeight : '—'}</td>
<td>${DOMPurify.sanitize(wetime.blockHash||'—')}</td>
<td>${wetime.blockHeight}</td>
<td>${DOMPurify.sanitize(wetime.blockHash)}</td>
<td>${d}</td>
<td>${wetime.diff != null ? wetime.diff/1000000 + ' s' : '—'}</td>
<td>${wetime.position != null ? wetime.position+1 : '—'}</td>
<td>${wetime.participants != null ? wetime.participants : '—'}</td>
<td>${wetime.points != null ? wetime.points : '—'}</td>
<td>${wetime.diff/1000000} s</td>
<td>${wetime.position+1}</td>
<td>${wetime.participants}</td>
<td>${wetime.points}</td>
`;
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 next=0;
if (data.times && data.times.length > 0){
if (data.times.length > 0){
prev = data.times[0].blockHeight+data.times.length;
next = data.times[data.times.length-1].blockHeight;
if (prev < 0 ){
@@ -148,6 +126,8 @@
}
getWeInfo();
</script>
</body>
</html>