feat: kaskad memo site with Basic Auth

Flask + Docker, header support links, install/uninstall curl scripts, password file or ADMIN_PASSWORD.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Андрей Бобырев
2026-05-14 20:25:02 +03:00
commit 23f5649651
11 changed files with 666 additions and 0 deletions

144
templates/index.html Normal file
View File

@@ -0,0 +1,144 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Каскад AmneziaWG — памятка</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=DM+Sans:wght@400;600;700&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
</head>
<body>
<div class="shell">
<header class="site-header">
<div class="support-line">
<span class="support-title">Поддержка автора</span>
<span class="sep" aria-hidden="true">·</span>
<a href="https://github.com/andrey271192/kaskad_web_vpn" target="_blank" rel="noopener noreferrer">GitHub</a>
<span class="sep">·</span>
<a href="https://boosty.to/andrey27/donate" target="_blank" rel="noopener noreferrer">Boosty</a>
<span class="sep">·</span>
<a href="https://finance.ozon.ru/apps/sbp/ozonbankpay/019dc200-2a5d-7931-a619-782d285f6798" target="_blank" rel="noopener noreferrer">Ozon&nbsp;СБП</a>
<span class="sep">·</span>
<a href="https://t.me/lot_andrey" target="_blank" rel="noopener noreferrer">Telegram @lot_andrey</a>
</div>
<p class="support-blurb">Звезда репозиторию и донат помогают развивать утилиты. На этой странице нет сторонней рекламы.</p>
</header>
<p class="eyebrow">VPN · каскад</p>
<h1>Каскад для AmneziaWG</h1>
<p class="lead">
Краткая памятка: клиент подключается сначала к вашему <strong>промежуточному узлу</strong> (Endpoint),
а ключ и peer создаются на основном сервере AmneziaWG. Эта страница только объясняет шаги и может собрать текст-памятку;
генерация <span class="inline-code">.conf</span> выполняется в вашей <strong>админ-панели</strong> или вручную.
</p>
{% if panel_url %}
<p class="muted">Ваша панель: <a href="{{ panel_url }}">{{ panel_url }}</a></p>
{% endif %}
<details class="panel-fold" open>
<summary>
<span class="fold-arrow" aria-hidden="true"></span>
<span class="fold-h">Что такое каскад</span>
</summary>
<div class="fold-body">
<p class="muted">
В конфиге клиента поле <span class="inline-code">Endpoint</span> указывает на IP или DNS узла «перед» VPS
(домашний роутер с пробросом UDP, другой VPS и т.д.). На этом узле нужен проброс порта на ваш основной сервер WireGuard/AWG.
Сервер AmneziaWG добавляет peer и выдаёт готовый <span class="inline-code">.conf</span>.
</p>
</div>
</details>
<details class="panel-fold" open>
<summary>
<span class="fold-arrow" aria-hidden="true"></span>
<span class="fold-h">Через панель Amnezia Admin</span>
</summary>
<div class="fold-body">
<p class="muted">
Если установлена <strong>Amnezia Admin WebUI</strong>, откройте блок <strong>«Новый клиент под каскад»</strong>:
укажите Endpoint (ваш публичный IP/DNS узла и при необходимости порт), затем скачайте <span class="inline-code">.conf</span>.
</p>
<p class="muted">
Репозиторий панели:
<a href="https://github.com/andrey271192/Amnezia_web" target="_blank" rel="noopener noreferrer">github.com/andrey271192/Amnezia_web</a>
</p>
</div>
</details>
<details class="panel-fold">
<summary>
<span class="fold-arrow" aria-hidden="true"></span>
<span class="fold-h">Собрать памятку (локально в браузере)</span>
</summary>
<div class="fold-body">
<p class="muted">Данные не отправляются на сервер — только текст для копирования.</p>
<div class="grid-form">
<label for="ep-host">Endpoint (хост для клиента)</label>
<input id="ep-host" type="text" placeholder="203.0.113.50 или ddns.example.com" autocomplete="off">
<label for="ep-port">Порт Endpoint (если пусто — как у сервера)</label>
<input id="ep-port" type="number" min="1" max="65535" placeholder="51820" autocomplete="off">
<label for="memo-name">Имя клиента (произвольно)</label>
<input id="memo-name" type="text" placeholder="Телефон через узел X" autocomplete="off">
<div class="btn-row">
<button type="button" class="btn" id="btn-build">Собрать текст</button>
<button type="button" class="btn" id="btn-copy">Копировать</button>
</div>
</div>
<pre class="out-box" id="out-memo" aria-live="polite"></pre>
</div>
</details>
<details class="panel-fold">
<summary>
<span class="fold-arrow" aria-hidden="true"></span>
<span class="fold-h">Проброс UDP</span>
</summary>
<div class="fold-body">
<p class="muted">
На промежуточном узле пробросьте UDP с выбранного порта на IP VPS и порт, где слушает ваш инстанс AmneziaWG.
Убедитесь, что фаервол не режет входящий UDP.
</p>
</div>
</details>
<footer class="site-footer">
Сервис: HTTP Basic Auth (realm «kaskad»). Установка одной ссылкой — см. README репозитория kaskad_web_vpn.
</footer>
</div>
<script>
(function () {
function buildText() {
var host = (document.getElementById("ep-host").value || "").trim();
var port = (document.getElementById("ep-port").value || "").trim();
var name = (document.getElementById("memo-name").value || "").trim() || "клиент";
var epLine = host ? (host + (port ? ":" + port : "")) : "(укажите Endpoint)";
return (
"Памятка по каскаду\n" +
"— Имя: " + name + "\n" +
"— Endpoint для клиента: " + epLine + "\n" +
"— На промежуточном узле: проброс UDP на VPS и порт WG/AWG.\n" +
"— На основном сервере: создать peer (панель Amnezia Admin → «Новый клиент под каскад») и выдать .conf.\n"
);
}
document.getElementById("btn-build").addEventListener("click", function () {
document.getElementById("out-memo").textContent = buildText();
});
document.getElementById("btn-copy").addEventListener("click", async function () {
var t = document.getElementById("out-memo").textContent || buildText();
try {
await navigator.clipboard.writeText(t);
} catch (e) {
alert("Не удалось скопировать — выделите текст вручную.");
}
});
})();
</script>
</body>
</html>