262 lines
9.6 KiB
HTML
262 lines
9.6 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Bitcoin Core Admin Dashboard</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;700&family=IBM+Plex+Mono:wght@400;600&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="/static/styles.css">
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.2/dist/chart.umd.min.js" defer></script>
|
|
<script src="/static/app.js" defer></script>
|
|
</head>
|
|
<body>
|
|
<div class="background-layer"></div>
|
|
|
|
<main class="container">
|
|
<section id="login-view" class="panel login-panel">
|
|
<h1>Node Admin Login</h1>
|
|
<p class="subtle">Credentials come from the app environment file.</p>
|
|
<form id="login-form">
|
|
<label>
|
|
Username
|
|
<input type="text" id="login-username" autocomplete="username" required>
|
|
</label>
|
|
<label>
|
|
Password
|
|
<input type="password" id="login-password" autocomplete="current-password" required>
|
|
</label>
|
|
<button type="submit">Sign in</button>
|
|
</form>
|
|
<p id="login-error" class="error"></p>
|
|
</section>
|
|
|
|
<section id="app-view" class="hidden">
|
|
<header class="app-header panel">
|
|
<div>
|
|
<p class="eyebrow">Bitcoin Core</p>
|
|
<h1>Admin Dashboard</h1>
|
|
<p id="live-status" class="subtle">Not connected</p>
|
|
</div>
|
|
<div class="header-actions">
|
|
<button id="refresh-btn" class="secondary">Refresh</button>
|
|
<button id="settings-btn" class="secondary">Node Settings</button>
|
|
<button id="logout-btn" class="danger">Log out</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="panel sync-panel">
|
|
<div class="sync-head">
|
|
<h2>Chain Sync</h2>
|
|
<span id="sync-percent" class="sync-percent">-</span>
|
|
</div>
|
|
<div id="sync-track" class="sync-track" role="progressbar" aria-label="Chain sync progress" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0">
|
|
<div id="sync-fill" class="sync-fill"></div>
|
|
</div>
|
|
<p id="sync-summary" class="sync-summary">Waiting for chain data.</p>
|
|
<p id="sync-details" class="subtle">-</p>
|
|
</section>
|
|
|
|
<section class="cards-grid">
|
|
<article class="panel stat-card">
|
|
<h2>Chain</h2>
|
|
<p id="stat-chain" class="stat-value">-</p>
|
|
</article>
|
|
<article class="panel stat-card">
|
|
<h2>Blocks</h2>
|
|
<p id="stat-blocks" class="stat-value">-</p>
|
|
</article>
|
|
<article class="panel stat-card">
|
|
<h2>Headers</h2>
|
|
<p id="stat-headers" class="stat-value">-</p>
|
|
</article>
|
|
<article class="panel stat-card">
|
|
<h2>Peers</h2>
|
|
<p id="stat-peers" class="stat-value">-</p>
|
|
</article>
|
|
<article class="panel stat-card">
|
|
<h2>Mempool TX</h2>
|
|
<p id="stat-mempool-tx" class="stat-value">-</p>
|
|
</article>
|
|
<article class="panel stat-card">
|
|
<h2>Mempool Size</h2>
|
|
<p id="stat-mempool-size" class="stat-value">-</p>
|
|
</article>
|
|
<article class="panel stat-card">
|
|
<h2>Difficulty</h2>
|
|
<p id="stat-difficulty" class="stat-value">-</p>
|
|
</article>
|
|
<article class="panel stat-card">
|
|
<h2>Uptime</h2>
|
|
<p id="stat-uptime" class="stat-value">-</p>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="panel chart-toolbar">
|
|
<label>
|
|
Chart Window
|
|
<select id="chart-window">
|
|
<option value="5m">Last 5 minutes</option>
|
|
<option value="30m" selected>Last 30 minutes</option>
|
|
<option value="2h">Last 2 hours</option>
|
|
<option value="6h">Last 6 hours</option>
|
|
<option value="all">All collected</option>
|
|
</select>
|
|
</label>
|
|
<div>
|
|
<p class="subtle">History is sampled server-side (default every 60s). While this page is open, live refresh runs every 15s.</p>
|
|
<p id="chart-history-info" class="subtle chart-history-info">History status: -</p>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="charts-grid">
|
|
<article class="panel chart-panel">
|
|
<h2>Blocks vs Headers</h2>
|
|
<div class="chart-frame">
|
|
<canvas id="chart-blocks"></canvas>
|
|
</div>
|
|
</article>
|
|
<article class="panel chart-panel">
|
|
<h2>Mempool Bytes</h2>
|
|
<div class="chart-frame">
|
|
<canvas id="chart-mempool"></canvas>
|
|
</div>
|
|
</article>
|
|
<article class="panel chart-panel">
|
|
<h2>Peers</h2>
|
|
<div class="chart-frame">
|
|
<canvas id="chart-peers"></canvas>
|
|
</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="actions-grid">
|
|
<article class="panel">
|
|
<h2>Node Controls</h2>
|
|
<p class="subtle">`start`/`restart` run over SSH using your saved config path.</p>
|
|
<div class="button-row">
|
|
<button id="action-stop" class="danger">Stop Node</button>
|
|
<button id="action-start">Start Node</button>
|
|
<button id="action-restart">Restart Node</button>
|
|
</div>
|
|
</article>
|
|
<article class="panel">
|
|
<h2>Quick RPC Calls</h2>
|
|
<div class="button-row wrap">
|
|
<button class="secondary quick-rpc" data-method="getblockchaininfo">Blockchain</button>
|
|
<button class="secondary quick-rpc" data-method="getnetworkinfo">Network</button>
|
|
<button class="secondary quick-rpc" data-method="getmempoolinfo">Mempool</button>
|
|
<button class="secondary quick-rpc" data-method="getwalletinfo">Wallet</button>
|
|
<button class="secondary quick-rpc" data-method="getpeerinfo">Peers</button>
|
|
<button class="secondary quick-rpc" data-method="getrawmempool">Raw Mempool</button>
|
|
</div>
|
|
</article>
|
|
</section>
|
|
|
|
<section class="panel explorer-panel">
|
|
<div class="explorer-head">
|
|
<h2>RPC Explorer</h2>
|
|
<span id="command-count" class="subtle"></span>
|
|
</div>
|
|
|
|
<div class="explorer-grid">
|
|
<div class="command-list-wrap">
|
|
<input type="text" id="command-search" placeholder="Filter methods (e.g. getblock)" autocomplete="off">
|
|
<div id="command-list" class="command-list"></div>
|
|
</div>
|
|
|
|
<div>
|
|
<form id="rpc-form" class="rpc-form">
|
|
<label>
|
|
Method
|
|
<input type="text" id="rpc-method" placeholder="getblockchaininfo" required>
|
|
</label>
|
|
<label>
|
|
Params (JSON array)
|
|
<textarea id="rpc-params" rows="4" placeholder="[]"></textarea>
|
|
</label>
|
|
<label>
|
|
Wallet override (optional)
|
|
<input type="text" id="rpc-wallet" placeholder="walletname">
|
|
</label>
|
|
<div class="button-row">
|
|
<button type="submit">Execute RPC</button>
|
|
<button type="button" id="rpc-help-btn" class="secondary">Method Help</button>
|
|
</div>
|
|
</form>
|
|
<p id="rpc-error" class="error"></p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="panel result-panel">
|
|
<h2>Result</h2>
|
|
<pre id="rpc-output">Run a command to inspect response data.</pre>
|
|
</section>
|
|
</section>
|
|
</main>
|
|
|
|
<div id="settings-modal" class="modal hidden" role="dialog" aria-modal="true">
|
|
<div class="panel modal-panel">
|
|
<div class="explorer-head">
|
|
<h2>Node Settings</h2>
|
|
<button id="settings-close" class="secondary">Close</button>
|
|
</div>
|
|
<form id="settings-form" class="settings-grid">
|
|
<label>
|
|
RPC URL
|
|
<input type="url" name="rpc_url" placeholder="http://127.0.0.1:8332" required>
|
|
</label>
|
|
<label>
|
|
RPC Username
|
|
<input type="text" name="rpc_username" required>
|
|
</label>
|
|
<label>
|
|
RPC Password
|
|
<input type="password" name="rpc_password" required>
|
|
</label>
|
|
<label>
|
|
Wallet (optional)
|
|
<input type="text" name="rpc_wallet" placeholder="walletname">
|
|
</label>
|
|
<label>
|
|
Node Config Path
|
|
<input type="text" name="config_path" placeholder="/etc/bitcoin/bitcoin.conf">
|
|
</label>
|
|
<label>
|
|
Bitcoin Binary
|
|
<input type="text" name="bitcoin_binary" placeholder="bitcoind">
|
|
</label>
|
|
<label>
|
|
SSH Host (optional if derivable from RPC URL)
|
|
<input type="text" name="ssh_host" placeholder="node.example.com">
|
|
</label>
|
|
<label>
|
|
SSH Port
|
|
<input type="number" name="ssh_port" min="1" max="65535" value="22">
|
|
</label>
|
|
<label>
|
|
SSH Username
|
|
<input type="text" name="ssh_username" placeholder="bitcoinadmin">
|
|
</label>
|
|
<label>
|
|
SSH Password / Key Passphrase
|
|
<input type="password" name="ssh_password">
|
|
</label>
|
|
<label>
|
|
SSH Private Key Path (optional)
|
|
<input type="text" name="ssh_key_path" placeholder="~/.ssh/id_rsa">
|
|
</label>
|
|
|
|
<div class="button-row modal-actions">
|
|
<button type="submit">Save Settings</button>
|
|
</div>
|
|
</form>
|
|
<p class="subtle">Settings persist in the local SQLite database (`data/dashboard.db`).</p>
|
|
<p id="settings-error" class="error"></p>
|
|
</div>
|
|
</div>
|
|
</body>
|
|
</html>
|