Initial WireGuard admin panel: FastAPI UI, peer key management, live monitoring, Docker deploy
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
commit
998dfe702c
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}WireGuard Admin{% endblock %}</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<span class="brand">WireGuard Admin</span>
|
||||
<a href="/">Dashboard</a>
|
||||
<a href="/peers">Peers</a>
|
||||
<a href="/settings">Settings</a>
|
||||
<form method="post" action="/logout" class="inline">
|
||||
<button type="submit" class="link">Logout</button>
|
||||
</form>
|
||||
</nav>
|
||||
<main>
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Dashboard - WireGuard Admin{% endblock %}
|
||||
{% block content %}
|
||||
<h1>Dashboard</h1>
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<h3>Interface</h3>
|
||||
<p>{{ status.name }}
|
||||
{% if status.up %}<span class="badge ok">up</span>
|
||||
{% else %}<span class="badge err">down</span>{% endif %}
|
||||
</p>
|
||||
<p class="muted">Port {{ status.listen_port or settings.wg_port }}</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Peers</h3>
|
||||
<p><span id="peer-count">{{ peers | length }}</span> total,
|
||||
<span id="online-count">-</span> online</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Traffic</h3>
|
||||
<p>RX <span id="total-rx">{{ status.total_rx | fmt_bytes }}</span></p>
|
||||
<p>TX <span id="total-tx">{{ status.total_tx | fmt_bytes }}</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Peers</h2>
|
||||
<table id="peer-table">
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Address</th><th>Status</th><th>Handshake</th><th>RX</th><th>TX</th></tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
{% block scripts %}<script src="/static/status.js"></script>{% endblock %}
|
||||
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Login - WireGuard Admin</title>
|
||||
<link rel="stylesheet" href="/static/style.css">
|
||||
</head>
|
||||
<body class="login-page">
|
||||
<div class="login-box">
|
||||
<h1>WireGuard Admin</h1>
|
||||
{% if error %}<p class="error">{{ error }}</p>{% endif %}
|
||||
<form method="post" action="/login">
|
||||
<label>Username <input name="username" autofocus required></label>
|
||||
<label>Password <input name="password" type="password" required></label>
|
||||
<button type="submit">Sign in</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,48 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ peer.name }} - WireGuard Admin{% endblock %}
|
||||
{% block content %}
|
||||
<h1>{{ peer.name }}</h1>
|
||||
<div class="cards">
|
||||
<div class="card">
|
||||
<h3>Details</h3>
|
||||
<p>Address: {{ peer.address }}</p>
|
||||
<p>Public key: <code>{{ peer.public_key }}</code></p>
|
||||
<p>Enabled:
|
||||
{% if peer.enabled %}<span class="badge ok">yes</span>
|
||||
{% else %}<span class="badge err">no</span>{% endif %}
|
||||
</p>
|
||||
<p class="muted">Created {{ peer.created_at.strftime("%Y-%m-%d %H:%M") }}</p>
|
||||
{% if peer.expires_at %}<p class="muted">Expires {{ peer.expires_at.strftime("%Y-%m-%d %H:%M") }}</p>{% endif %}
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Live status</h3>
|
||||
{% if peer_status %}
|
||||
<p>Status: {% if peer_status.online %}<span class="badge ok">online</span>{% else %}<span class="badge muted">offline</span>{% endif %}</p>
|
||||
<p>Endpoint: {{ peer_status.endpoint or "-" }}</p>
|
||||
<p>Handshake: {{ peer_status.latest_handshake.strftime("%Y-%m-%d %H:%M:%S") if peer_status.latest_handshake else "never" }}</p>
|
||||
<p>RX {{ peer_status.rx_bytes | fmt_bytes }} / TX {{ peer_status.tx_bytes | fmt_bytes }}</p>
|
||||
{% else %}
|
||||
<p class="muted">Not present on interface (disabled or interface down).</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Client config</h3>
|
||||
<img src="/peers/{{ peer.id }}/qr" alt="QR code" class="qr">
|
||||
<p><a href="/peers/{{ peer.id }}/config" class="button">Download .conf</a></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions-row">
|
||||
<form method="post" action="/peers/{{ peer.id }}/toggle" class="inline">
|
||||
<button type="submit">{{ "Disable" if peer.enabled else "Enable" }}</button>
|
||||
</form>
|
||||
<form method="post" action="/peers/{{ peer.id }}/rotate" class="inline"
|
||||
onsubmit="return confirm('Rotate keys? The old client config stops working immediately.')">
|
||||
<button type="submit">Rotate keys</button>
|
||||
</form>
|
||||
<form method="post" action="/peers/{{ peer.id }}/delete" class="inline"
|
||||
onsubmit="return confirm('Delete peer {{ peer.name }}?')">
|
||||
<button type="submit" class="danger">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,40 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Peers - WireGuard Admin{% endblock %}
|
||||
{% block content %}
|
||||
<h1>Peers</h1>
|
||||
|
||||
<form method="post" action="/peers" class="add-peer">
|
||||
<input name="name" placeholder="Peer name" required>
|
||||
<label class="muted">Expires <input name="expires_at" type="datetime-local"></label>
|
||||
<button type="submit">Add peer</button>
|
||||
</form>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Address</th><th>Enabled</th><th>Status</th><th>Created</th><th></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for peer in peers %}
|
||||
{% set ps = status.peers.get(peer.public_key) %}
|
||||
<tr>
|
||||
<td><a href="/peers/{{ peer.id }}">{{ peer.name }}</a></td>
|
||||
<td>{{ peer.address }}</td>
|
||||
<td>{% if peer.enabled %}<span class="badge ok">enabled</span>{% else %}<span class="badge err">disabled</span>{% endif %}</td>
|
||||
<td>{% if ps and ps.online %}<span class="badge ok">online</span>{% else %}<span class="badge muted">offline</span>{% endif %}</td>
|
||||
<td class="muted">{{ peer.created_at.strftime("%Y-%m-%d") }}</td>
|
||||
<td class="actions">
|
||||
<form method="post" action="/peers/{{ peer.id }}/toggle" class="inline">
|
||||
<button type="submit">{{ "Disable" if peer.enabled else "Enable" }}</button>
|
||||
</form>
|
||||
<form method="post" action="/peers/{{ peer.id }}/delete" class="inline"
|
||||
onsubmit="return confirm('Delete peer {{ peer.name }}?')">
|
||||
<button type="submit" class="danger">Delete</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="muted">No peers yet. Add one above.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,18 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}Settings - WireGuard Admin{% endblock %}
|
||||
{% block content %}
|
||||
<h1>Settings</h1>
|
||||
<div class="card">
|
||||
<table class="kv">
|
||||
<tr><th>Interface</th><td>{{ settings.wg_interface }}</td></tr>
|
||||
<tr><th>Server public key</th><td><code>{{ server_public }}</code></td></tr>
|
||||
<tr><th>Endpoint host</th><td>{{ settings.wg_host }}</td></tr>
|
||||
<tr><th>Listen port</th><td>{{ settings.wg_port }}</td></tr>
|
||||
<tr><th>Subnet</th><td>{{ settings.wg_subnet }}</td></tr>
|
||||
<tr><th>Client DNS</th><td>{{ settings.wg_dns }}</td></tr>
|
||||
<tr><th>Client AllowedIPs</th><td>{{ settings.wg_allowed_ips }}</td></tr>
|
||||
<tr><th>Keepalive</th><td>{{ settings.wg_persistent_keepalive }}s</td></tr>
|
||||
</table>
|
||||
<p class="muted">Settings are read from environment variables / .env and require a restart to change.</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user