init commit

This commit is contained in:
2026-07-18 10:02:43 +03:00
commit 3b4a1f5388
31 changed files with 6580 additions and 0 deletions
+208
View File
@@ -0,0 +1,208 @@
<!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>