diff --git a/README.md b/README.md index aba16c0..d39e9f3 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,24 @@ Base project **[amnezia_web](https://github.com/andrey271192/amnezia_web)** remains a lighter open panel. This repository contains the full PRO feature set by default. +## Что нового в 1.3.0 + +- **Установка протоколов прямо с сайта** (раздел «Протоколы / инстансы»), несколько версий рядом на разных портах: + | Вариант | Для кого | + |---|---| + | AmneziaWG 3.1 | новейший: защита заголовков, случайные хвосты; свежее приложение Amnezia | + | AmneziaWG 3.0 | HeaderProtectionKey, паддинг | + | AmneziaWG 2.0 | рекомендуемый по умолчанию (S3/S4) | + | AmneziaWG 1.x | роутеры Keenetic/OpenWrt и старые приложения (без S3/S4) | + | WireGuard (Legacy) | любой WG-клиент, без обфускации | +- **Исправлено:** конфиг клиента для AWG 1.x получал лишние S3/S4/I1 — рукопожатие было, интернета нет. Теперь в .conf попадают только параметры, которые есть на сервере. +- **Исправлено:** при обновлении установщик записывал контейнеры панели в `AWG_PROFILES`, и переключение инстанса создавало клиента не там. +- **Исправлено:** проверка занятого UDP-порта смотрела внутрь контейнера панели и ничего не находила. +- **Домен сервера** — поле «Домен или IP сервера в конфигах клиентов»: подставляется в Endpoint всех выгрузок, домен проверяется на резолв. +- **Настоящий каскад** — в разделе «Cloudflare WARP / каскад» вставьте клиентский .conf другого сервера: отмеченные клиенты выходят в интернет через него (переживает перезапуск контейнера). +- **Светлая / тёмная тема** — кнопка «◐ Тема» в шапке (по умолчанию как в системе). +- Старые установки не затрагиваются: контейнеры, поставленные приложением Amnezia, остаются как были. + ## Что открывается по какому порту | Адрес | Назначение | diff --git a/package.json b/package.json index 6f6bd78..f7e7bce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "amnezia-admin", - "version": "1.2.39", + "version": "1.3.0", "private": false, "description": "amnezia_web-PRO — веб-панель AmneziaWG: несколько инстансов, WARP, каскад/Endpoint, экспорт .conf (Docker)", "license": "MIT", diff --git a/public/app.js b/public/app.js index 15fd542..14ddf2f 100644 --- a/public/app.js +++ b/public/app.js @@ -629,6 +629,7 @@ loginForm.addEventListener("submit", async (ev) => { await loadTimeSyncCaps(); await loadClients(); await loadInstances(); + await loadSettings(); } catch (e) { loginError.textContent = String(e.message || e); } @@ -647,6 +648,7 @@ logoutBtn.addEventListener("click", async () => { refreshBtn.addEventListener("click", () => { loadClients(); loadInstances(); + loadSettings(); }); const cascadeForm = document.querySelector("#cascade-form"); @@ -1730,6 +1732,7 @@ async function boot() { await loadTimeSyncCaps(); await loadClients(); await loadInstances(); + await loadSettings(); } else { showLogin(); loginPassword.focus(); @@ -1738,3 +1741,50 @@ async function boot() { initMtprotoLinkCopy(); boot(); + +// ── Тема, адрес сервера, вышестоящий сервер каскада ── +document.querySelector("#theme-toggle")?.addEventListener("click", () => { + const next = document.documentElement.dataset.theme === "light" ? "dark" : "light"; + document.documentElement.dataset.theme = next; + try { localStorage.setItem("theme", next); } catch { /* private mode */ } +}); + +async function loadSettings() { + const input = document.querySelector("#settings-endpoint"); + const hint = document.querySelector("#settings-hint"); + if (!input) return; + try { + const st = await api("/api/settings"); + input.value = st.endpointHost || ""; + if (hint) hint.textContent = `Сейчас в конфигах клиентов: ${st.effectiveEndpoint || "адрес из браузера"}. Новый адрес действует на все выгрузки .conf, в том числе старых клиентов.`; + } catch { /* старый сервер без /api/settings */ } +} + +document.querySelector("#settings-form")?.addEventListener("submit", async (ev) => { + ev.preventDefault(); + const endpointHost = document.querySelector("#settings-endpoint")?.value.trim() || ""; + try { + const r = await api("/api/settings", { method: "POST", body: JSON.stringify({ endpointHost }) }); + setStatus(r.resolved ? `Адрес сохранён: ${endpointHost} → ${r.resolved.join(", ")}` : "Адрес сохранён.", false); + await loadSettings(); + } catch (e) { + setStatus(String(e.message || e), true); + } +}); + +document.querySelector("#upstream-form")?.addEventListener("submit", async (ev) => { + ev.preventDefault(); + const configText = document.querySelector("#upstream-conf")?.value || ""; + const b = ev.target.querySelector("button[type=submit]"); + if (b) b.disabled = true; + try { + setStatus("Подключаю вышестоящий сервер…", false); + const r = await api("/api/cascade/upstream", { method: "POST", body: JSON.stringify({ configText }) }); + setStatus(r.handshake ? "Каскад подключён: рукопожатие есть. Отметьте клиентов ниже и нажмите «Применить»." : "Туннель поднят, но рукопожатия пока нет — проверьте Endpoint и UDP-порт вышестоящего сервера.", !r.handshake); + await loadClients(); + } catch (e) { + setStatus(String(e.message || e), true); + } finally { + if (b) b.disabled = false; + } +}); diff --git a/public/index.html b/public/index.html index 3c77ece..6f4aa3d 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,8 @@ - + +