Operator Dashboard

Enter your node ID to view earnings and manage payouts.

Don't have a node? Become an operator →

+ escapeHtml(String(RATE)) + '/int', ''] ]; items.forEach(([label, value, style]) => { const labelSpan = document.createElement('span'); labelSpan.style.color = 'var(--text-dim)'; labelSpan.textContent = label; const valueSpan = document.createElement('span'); valueSpan.style.color = 'var(--text)'; if (style) valueSpan.style.cssText += style; valueSpan.innerHTML = value; // Already escaped above detailsEl.appendChild(labelSpan); detailsEl.appendChild(valueSpan); }); // Add escapeHtml helper function function escapeHtml(text) { const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; return text ? text.replace(/[&<>"']/g, m => map[m]) : ''; } } catch {} } async function requestCashout() { const amount = document.getElementById('cashoutAmount').value.trim(); const resultEl = document.getElementById('cashoutResult'); resultEl.innerHTML = 'Processing...'; try { const body = { nodeId }; if (amount) body.amount_ints = parseInt(amount); const res = await fetch('/mesh/cashout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); const data = await res.json(); if (data.error) { resultEl.innerHTML = ''; resultEl.querySelector('span').textContent = data.error; } else { const ints = data.cashed_out_ints || data.amount_ints || 0; const usd = (ints * RATE).toFixed(2); resultEl.innerHTML = `✓ Cashed out ${ints.toLocaleString()} ints ($${usd}) — transfer ${data.transfer_id ? 'initiated' : 'queued'}!`; // Refresh stats setTimeout(loadDashboard, 2000); } } catch (e) { resultEl.innerHTML = 'Request failed: '; resultEl.querySelector('span').textContent += e.message; } }