Files
2026-07-18 10:02:43 +03:00

209 lines
10 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta name="theme-color" content="#0F172A" />
<title>AmneziaWG Profiler</title>
<link rel="stylesheet" href="style.css" />
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2322C55E' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z'/%3E%3C/svg%3E" />
</head>
<body>
<!-- ── inline SVG icon sprite (Lucide geometry; no external CDN) ────────────── -->
<svg width="0" height="0" style="position:absolute" aria-hidden="true" focusable="false">
<symbol id="i-shield" viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></symbol>
<symbol id="i-refresh" viewBox="0 0 24 24"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"/><path d="M3 21v-5h5"/></symbol>
<symbol id="i-x" viewBox="0 0 24 24"><path d="M18 6 6 18"/><path d="M6 6l12 12"/></symbol>
<symbol id="i-message" viewBox="0 0 24 24"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></symbol>
<symbol id="i-arrow-down" viewBox="0 0 24 24"><path d="M12 5v14"/><path d="M19 12l-7 7-7-7"/></symbol>
<symbol id="i-arrow-up" viewBox="0 0 24 24"><path d="M12 19V5"/><path d="M5 12l7-7 7 7"/></symbol>
<symbol id="i-plus" viewBox="0 0 24 24"><path d="M5 12h14"/><path d="M12 5v14"/></symbol>
<symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.3-4.3"/></symbol>
<symbol id="i-trash" viewBox="0 0 24 24"><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M10 11v6"/><path d="M14 11v6"/></symbol>
<symbol id="i-download" viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></symbol>
<symbol id="i-play" viewBox="0 0 24 24"><path d="M6 3l14 9-14 9V3z"/></symbol>
<symbol id="i-stop" viewBox="0 0 24 24"><rect x="6" y="6" width="12" height="12" rx="2"/></symbol>
<symbol id="i-rotate" viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8"/><path d="M21 3v5h-5"/></symbol>
<symbol id="i-pause" viewBox="0 0 24 24"><path d="M10 4H6v16h4z"/><path d="M18 4h-4v16h4z"/></symbol>
</svg>
<main id="app">
<!-- Loading placeholder -->
<div id="loading" class="center muted">
<svg class="ic ic-lg spin" aria-hidden="true"><use href="#i-refresh" /></svg>
<div>Loading…</div>
</div>
<!-- Setup wizard (shown when server not initialised) -->
<section id="setup" class="view hidden">
<h1>Server setup</h1>
<div class="card">
<div class="card-head">
<h2>1 · Install dependencies</h2>
<span id="depsBadge" class="badge"></span>
</div>
<p class="muted">Installs AmneziaWG, qrencode, jq and nftables for this OS.</p>
<button id="installDepsBtn" class="btn btn-primary">Install dependencies</button>
</div>
<div class="card" id="initCard">
<div class="card-head"><h2>2 · Initialise server</h2></div>
<p class="muted">Generates keys &amp; obfuscation parameters, writes configs and starts the service.</p>
<form id="initForm" class="form">
<label>Interface <input name="interface" placeholder="awg0" /></label>
<label>Network CIDR <input name="network" placeholder="10.0.0.0/24" /></label>
<label>Listen port <input name="port" inputmode="numeric" placeholder="51820" /></label>
<label>Public IP / host <input name="public_ip" placeholder="auto-detect" /></label>
<label>Client DNS <input name="dns" placeholder="1.1.1.1" /></label>
<label>MTU <input name="mtu" inputmode="numeric" placeholder="1420" /></label>
<button type="submit" class="btn btn-primary">Initialise server</button>
</form>
</div>
</section>
<!-- Dashboard -->
<section id="dashboard" class="view hidden">
<div class="stat-grid">
<div class="stat">
<div class="stat-val" id="stTotal"><span class="sk sk-num"></span></div>
<div class="stat-label">Clients</div>
</div>
<div class="stat">
<div class="stat-val" id="stActive"><span class="sk sk-num"></span></div>
<div class="stat-label">Active</div>
</div>
<div class="stat">
<div class="stat-val stat-online" id="stOnline"><span class="sk sk-num"></span></div>
<div class="stat-label">Online</div>
</div>
</div>
<div class="card">
<div class="card-head">
<h2>Server</h2>
<span id="ifaceBadge" class="badge"><span class="badge-dot"></span></span>
</div>
<dl class="kv">
<dt>Interface</dt><dd id="svIface"></dd>
<dt>Endpoint</dt><dd id="svEndpoint" class="mono"></dd>
<dt>Network</dt><dd id="svNetwork" class="mono"></dd>
<dt>Public key</dt><dd id="svKey" class="mono ellipsis"></dd>
</dl>
<div class="btn-row">
<button class="btn" data-server="start"><svg class="ic ic-sm" aria-hidden="true"><use href="#i-play" /></svg>Start</button>
<button class="btn" data-server="restart"><svg class="ic ic-sm" aria-hidden="true"><use href="#i-rotate" /></svg>Restart</button>
<button class="btn btn-danger-ghost" data-server="stop"><svg class="ic ic-sm" aria-hidden="true"><use href="#i-stop" /></svg>Stop</button>
<button class="btn" id="syncBtn"><svg class="ic ic-sm" aria-hidden="true"><use href="#i-refresh" /></svg>Sync</button>
</div>
</div>
<div class="section-head">
<h2>Clients</h2>
<div class="section-actions">
<button id="refreshBtn" class="icon-btn" title="Refresh" aria-label="Refresh">
<svg class="ic" aria-hidden="true"><use href="#i-refresh" /></svg>
</button>
<button id="createBtn" class="btn btn-primary btn-sm"><svg class="ic ic-sm" aria-hidden="true"><use href="#i-plus" /></svg>New</button>
</div>
</div>
<div class="search-wrap">
<svg class="ic search-ic" aria-hidden="true"><use href="#i-search" /></svg>
<input id="clientSearch" type="search" autocomplete="off" spellcheck="false"
placeholder="Search by name, IP or comment…" aria-label="Search clients" />
</div>
<div class="list-filter">
<span class="filter-item">
<span>Hide offline</span>
<label class="switch" title="Hide offline clients">
<input type="checkbox" id="hideOffline" aria-label="Hide offline clients" />
<span class="slider"></span>
</label>
</span>
<span class="filter-item">
<span>Hide disabled</span>
<label class="switch" title="Hide disabled clients">
<input type="checkbox" id="hideDisabled" aria-label="Hide disabled clients" />
<span class="slider"></span>
</label>
</span>
</div>
<div id="clientList" class="client-list"></div>
<div id="clientEmpty" class="center muted hidden">No clients yet — create one.</div>
<div id="clientNoMatch" class="center muted hidden">No clients match your filters.</div>
</section>
</main>
<!-- Create client modal -->
<div id="createModal" class="modal hidden">
<div class="modal-card">
<h2>New client</h2>
<form id="createForm" class="form">
<label>Name
<input name="name" autocomplete="off" placeholder="phone" required />
</label>
<div class="btn-row end">
<button type="button" class="btn" data-close>Cancel</button>
<button type="submit" class="btn btn-primary">Create</button>
</div>
</form>
</div>
</div>
<!-- Client detail / QR modal -->
<div id="detailModal" class="modal hidden">
<div class="modal-card">
<div class="card-head">
<h2 id="dName">Client</h2>
<button class="icon-btn" data-close aria-label="Close">
<svg class="ic" aria-hidden="true"><use href="#i-x" /></svg>
</button>
</div>
<dl class="kv">
<dt>Status</dt><dd id="dStatus"></dd>
<dt>IP</dt><dd id="dIP" class="mono"></dd>
<dt>Endpoint</dt><dd id="dEndpoint" class="mono"></dd>
<dt>Handshake</dt><dd id="dHandshake"></dd>
<dt>Received</dt><dd id="dRx" class="mono"></dd>
<dt>Sent</dt><dd id="dTx" class="mono"></dd>
</dl>
<div class="stats-meta">
<span class="muted">Stats since <span id="dSince"></span></span>
<button id="dResetStats" type="button" class="btn btn-sm">
<svg class="ic ic-sm" aria-hidden="true"><use href="#i-rotate" /></svg>Reset stats
</button>
</div>
<div class="comment-block">
<div class="comment-head">
<span class="comment-label">
<svg class="ic ic-sm" aria-hidden="true"><use href="#i-message" /></svg>Comment
</span>
<button id="dCommentEdit" type="button" class="btn btn-sm">Edit</button>
</div>
<div id="dCommentView" class="comment-view muted"></div>
<div id="dCommentEditor" class="comment-editor hidden">
<textarea id="dCommentText" rows="2" maxlength="500"
placeholder="Add a note for this profile…"></textarea>
<div class="btn-row end">
<button id="dCommentCancel" type="button" class="btn btn-sm">Cancel</button>
<button id="dCommentSave" type="button" class="btn btn-primary btn-sm">Save</button>
</div>
</div>
</div>
<div class="qr-wrap"><img id="dQR" alt="Client configuration QR code" /></div>
<div class="btn-row">
<a id="dDownload" class="btn btn-primary" download><svg class="ic ic-sm" aria-hidden="true"><use href="#i-download" /></svg>Download .conf</a>
<button id="dDelete" class="btn btn-danger"><svg class="ic ic-sm" aria-hidden="true"><use href="#i-trash" /></svg>Delete</button>
</div>
</div>
</div>
<div id="toast" class="toast hidden" role="status" aria-live="polite"></div>
<script src="app.js"></script>
</body>
</html>