fix(ui): restore index.html; add Ozon to panel promo

Repair broken HTML merge. Extend /api/panel/footer with PANEL_FOOTER_OZON_URL (default Ozon СБП) and labels in strip+footer.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Андрей Бобырев
2026-05-15 04:12:25 +03:00
parent 9d751922fb
commit c72a7b9c02
6 changed files with 45 additions and 18 deletions

View File

@@ -66,7 +66,9 @@ cd /opt/amnezia-admin && chmod +x scripts/install.sh && sudo SKIP_DOWNLOAD=1 bas
| `COMMUNITY_UPGRADE_PITCH` | _(текст по умолчанию в коде)_ | Текст под заголовком базовой версии | | `COMMUNITY_UPGRADE_PITCH` | _(текст по умолчанию в коде)_ | Текст под заголовком базовой версии |
| `PANEL_FOOTER_DONATE_URL` | Как **`COMMUNITY_UPGRADE_URL`** | Ссылка «донат» в **шапке и подвале** веб‑панели (**не** добавляется на nginxлендинг пользователей). | | `PANEL_FOOTER_DONATE_URL` | Как **`COMMUNITY_UPGRADE_URL`** | Ссылка «донат» в **шапке и подвале** веб‑панели (**не** добавляется на nginxлендинг пользователей). |
| `PANEL_FOOTER_TELEGRAM_URL` | `https://t.me/lot_andrey` | Ссылка Telegramспонсора / канала (там же: только панель). | | `PANEL_FOOTER_TELEGRAM_URL` | `https://t.me/lot_andrey` | Ссылка Telegramспонсора / канала (там же: только панель). |
| `PANEL_FOOTER_OZON_URL` | Ссылка на **Ozon СБП** автора (дефолт в `server.js`) | Донат через Ozon — **шапка и подвал** панели с остальными ссылками. |
| `PANEL_FOOTER_DONATE_LABEL` | `Поддержать проект` | Текст якоря доната | | `PANEL_FOOTER_DONATE_LABEL` | `Поддержать проект` | Текст якоря доната |
| `PANEL_FOOTER_OZON_LABEL` | `Донат · Ozon СБП` | Текст якоря Ozon |
| `PANEL_FOOTER_TELEGRAM_LABEL` | `Telegramканал спонсора` | Текст якоря Telegram | | `PANEL_FOOTER_TELEGRAM_LABEL` | `Telegramканал спонсора` | Текст якоря Telegram |
| `PANEL_FOOTER_PROMO_SUBTITLE` | _(строка по умолчанию в коде)_ | Поясняющий текст в верхней полосе панели | | `PANEL_FOOTER_PROMO_SUBTITLE` | _(строка по умолчанию в коде)_ | Поясняющий текст в верхней полосе панели |
| `SKIP_LANDING` | `0` | `1` — без лендинга на порту 80 | | `SKIP_LANDING` | `0` | `1` — без лендинга на порту 80 |

View File

@@ -1,6 +1,6 @@
{ {
"name": "amnezia-admin", "name": "amnezia-admin",
"version": "1.2.21", "version": "1.2.22",
"private": true, "private": true,
"description": "amnezia_web — базовая панель AmneziaWG (FREE: просмотр и удаление клиентов; полная версия — PRO).", "description": "amnezia_web — базовая панель AmneziaWG (FREE: просмотр и удаление клиентов; полная версия — PRO).",
"license": "MIT", "license": "MIT",

View File

@@ -174,11 +174,15 @@ async function hydratePanelPromoFooter() {
const strip = document.getElementById("panel-promo-strip"); const strip = document.getElementById("panel-promo-strip");
const sub = document.getElementById("panel-promo-subtitle"); const sub = document.getElementById("panel-promo-subtitle");
const aDonTop = document.getElementById("panel-strip-donate"); const aDonTop = document.getElementById("panel-strip-donate");
const sepTop = document.getElementById("panel-strip-sep"); const sepDO = document.getElementById("panel-strip-sep-donate-ozon");
const aOzonTop = document.getElementById("panel-strip-ozon");
const sepOT = document.getElementById("panel-strip-sep-ozon-tg");
const aTgTop = document.getElementById("panel-strip-telegram"); const aTgTop = document.getElementById("panel-strip-telegram");
const gap1 = document.getElementById("footer-promo-gap1"); const gap1 = document.getElementById("footer-promo-gap1");
const aDonF = document.getElementById("footer-promo-donate"); const aDonF = document.getElementById("footer-promo-donate");
const sepMid = document.getElementById("footer-promo-sep-mid"); const fSepDO = document.getElementById("footer-promo-sep-donate-ozon");
const aOzonF = document.getElementById("footer-promo-ozon");
const fSepOT = document.getElementById("footer-promo-sep-ozon-tg");
const aTgF = document.getElementById("footer-promo-telegram"); const aTgF = document.getElementById("footer-promo-telegram");
try { try {
@@ -191,18 +195,25 @@ async function hydratePanelPromoFooter() {
if (sub) sub.textContent = subtitle; if (sub) sub.textContent = subtitle;
const okD = wirePanelPromoAnchor(aDonTop, j.donateUrl, j.donateLabel); const okD = wirePanelPromoAnchor(aDonTop, j.donateUrl, j.donateLabel);
const okO = wirePanelPromoAnchor(aOzonTop, j.ozonUrl, j.ozonLabel);
const okT = wirePanelPromoAnchor(aTgTop, j.telegramUrl, j.telegramLabel); const okT = wirePanelPromoAnchor(aTgTop, j.telegramUrl, j.telegramLabel);
if (sepTop) sepTop.classList.toggle("hidden", !(okD && okT));
if (sepDO) sepDO.classList.toggle("hidden", !(okD && (okO || okT)));
if (sepOT) sepOT.classList.toggle("hidden", !(okO && okT));
wirePanelPromoAnchor(aDonF, j.donateUrl, j.donateLabel); wirePanelPromoAnchor(aDonF, j.donateUrl, j.donateLabel);
wirePanelPromoAnchor(aOzonF, j.ozonUrl, j.ozonLabel);
wirePanelPromoAnchor(aTgF, j.telegramUrl, j.telegramLabel); wirePanelPromoAnchor(aTgF, j.telegramUrl, j.telegramLabel);
const footerD = aDonF && !aDonF.classList.contains("hidden"); const footerD = aDonF && !aDonF.classList.contains("hidden");
const footerO = aOzonF && !aOzonF.classList.contains("hidden");
const footerT = aTgF && !aTgF.classList.contains("hidden"); const footerT = aTgF && !aTgF.classList.contains("hidden");
if (gap1) gap1.classList.toggle("hidden", !(footerD || footerT));
if (sepMid) sepMid.classList.toggle("hidden", !(footerD && footerT));
if (strip && (okD || okT)) strip.classList.remove("hidden"); if (gap1) gap1.classList.toggle("hidden", !(footerD || footerO || footerT));
if (fSepDO) fSepDO.classList.toggle("hidden", !(footerD && (footerO || footerT)));
if (fSepOT) fSepOT.classList.toggle("hidden", !(footerO && footerT));
if (strip && (okD || okO || okT)) strip.classList.remove("hidden");
} catch { } catch {
/* офлайн или сбой — блок остаётся скрытым */ /* офлайн или сбой — блок остаётся скрытым */
} }

View File

@@ -7,7 +7,7 @@
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <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;600&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;600;700&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/styles.css?v=1.2.21"> <link rel="stylesheet" href="/styles.css?v=1.2.22">
<script> <script>
(function () { (function () {
try { try {
@@ -28,7 +28,9 @@
<p id="panel-promo-subtitle" class="panel-promo-subtitle muted"></p> <p id="panel-promo-subtitle" class="panel-promo-subtitle muted"></p>
<div class="panel-promo-links"> <div class="panel-promo-links">
<a id="panel-strip-donate" class="panel-promo-a hidden" target="_blank" rel="noopener noreferrer"></a> <a id="panel-strip-donate" class="panel-promo-a hidden" target="_blank" rel="noopener noreferrer"></a>
<span id="panel-strip-sep" class="panel-promo-sep hidden" aria-hidden="true">·</span> <span id="panel-strip-sep-donate-ozon" class="panel-promo-sep hidden" aria-hidden="true">·</span>
<a id="panel-strip-ozon" class="panel-promo-a hidden" target="_blank" rel="noopener noreferrer"></a>
<span id="panel-strip-sep-ozon-tg" class="panel-promo-sep hidden" aria-hidden="true">·</span>
<a id="panel-strip-telegram" class="panel-promo-a hidden" target="_blank" rel="noopener noreferrer"></a> <a id="panel-strip-telegram" class="panel-promo-a hidden" target="_blank" rel="noopener noreferrer"></a>
</div> </div>
</div> </div>
@@ -159,7 +161,7 @@
<div class="panel-fold-body"> <div class="panel-fold-body">
<p class="muted warp-intro mtproto-intro"> <p class="muted warp-intro mtproto-intro">
<strong>В базовой панели (FREE).</strong> Отдельный Dockerконтейнер (образ Telegram), публикация порта на хосте VPS. <strong>В базовой панели (FREE).</strong> Отдельный Dockerконтейнер (образ Telegram), публикация порта на хосте VPS.
Ссылка вида <code class="inline">tg://proxy?…</code> появляется, если задан <code class="inline">MTPRO_PUBLIC_HOST</code> или <code class="inline">CLIENT_CONFIG_ENDPOINT</code> Ссылка вида <code class="inline">tg://proxy?…</code> может появляться без env, если панель открыта по публичному IP/DNS; для фиксированного хоста задайте <code class="inline">MTPRO_PUBLIC_HOST</code> или <code class="inline">CLIENT_CONFIG_ENDPOINT</code>
(переменные контейнера панели, см. install.sh README). (переменные контейнера панели, см. install.sh README).
Сохраните <strong>секрет</strong>, который показывается один раз после установки — в интерфейсе он маскируется. Сохраните <strong>секрет</strong>, который показывается один раз после установки — в интерфейсе он маскируется.
</p> </p>
@@ -264,13 +266,15 @@
<strong class="support-title">amnezia_web</strong> <strong class="support-title">amnezia_web</strong>
<span class="support-sep" aria-hidden="true">·</span> <span class="support-sep" aria-hidden="true">·</span>
<a href="https://github.com/andrey271192/amnezia_web" target="_blank" rel="noopener noreferrer">GitHub</a> <a href="https://github.com/andrey271192/amnezia_web" target="_blank" rel="noopener noreferrer">GitHub</a>
<span id="footer-promo-gap1" class="support-sep support-footer-promo-gap hidden" aria-hidden="true">·</span> <span id="footer-promo-gap1" class="support-sep hidden" aria-hidden="true">·</span>
<a id="footer-promo-donate" class="support-promo-link hidden" target="_blank" rel="noopener noreferrer"></a> <a id="footer-promo-donate" class="support-promo-link hidden" target="_blank" rel="noopener noreferrer"></a>
<span id="footer-promo-sep-mid" class="support-sep hidden" aria-hidden="true">·</span> <span id="footer-promo-sep-donate-ozon" class="support-sep hidden" aria-hidden="true">·</span>
<a id="footer-promo-ozon" class="support-promo-link hidden" target="_blank" rel="noopener noreferrer"></a>
<span id="footer-promo-sep-ozon-tg" class="support-sep hidden" aria-hidden="true">·</span>
<a id="footer-promo-telegram" class="support-promo-link hidden" target="_blank" rel="noopener noreferrer"></a> <a id="footer-promo-telegram" class="support-promo-link hidden" target="_blank" rel="noopener noreferrer"></a>
</div> </div>
<p class="support-blurb"> <p class="support-blurb">
Базовая панель: AWGклиенты, статусы и MTProtoпрокси (Docker). Ссылки поддержки автора только в этом интерфейсе панели, не на отдельных пользовательских лендингах. Базовая панель: AWGклиенты, статусы и MTProtoпрокси (Docker). Ссылки поддержки (Boosty, Ozon СБП, Telegram) подставляются из настроек сервера только в этом интерфейсе, не на пользовательских лендингах.
</p> </p>
</footer> </footer>
@@ -308,6 +312,6 @@
</div> </div>
</dialog> </dialog>
<script src="/app.js?v=1.2.21" type="module"></script> <script src="/app.js?v=1.2.22" type="module"></script>
</body> </body>
</html> </html>

View File

@@ -265,8 +265,8 @@ elif [[ -f "${INSTALL_DIR}/.amnezia-panel-edition" ]]; then
echo "→ AMNEZIA_EDITION из ${INSTALL_DIR}/.amnezia-panel-edition: ${__PE}" echo "→ AMNEZIA_EDITION из ${INSTALL_DIR}/.amnezia-panel-edition: ${__PE}"
fi fi
fi fi
for __ce_var in COMMUNITY_UPGRADE_URL COMMUNITY_UPGRADE_PITCH PANEL_FOOTER_DONATE_URL PANEL_FOOTER_TELEGRAM_URL \ for __ce_var in COMMUNITY_UPGRADE_URL COMMUNITY_UPGRADE_PITCH PANEL_FOOTER_DONATE_URL PANEL_FOOTER_OZON_URL PANEL_FOOTER_TELEGRAM_URL \
PANEL_FOOTER_DONATE_LABEL PANEL_FOOTER_TELEGRAM_LABEL PANEL_FOOTER_PROMO_SUBTITLE \ PANEL_FOOTER_DONATE_LABEL PANEL_FOOTER_OZON_LABEL PANEL_FOOTER_TELEGRAM_LABEL PANEL_FOOTER_PROMO_SUBTITLE \
ALLOW_COMMUNITY_GITHUB_ACTIVATION COMMUNITY_PRIVATE_INSTALL_SCRIPT_URL COMMUNITY_INSTALL_FETCH_MS PRIVATE_INSTALL_SCRIPT_MAX_BYTES COMMUNITY_DISABLE_DOCKER_CLI_HELPER COMMUNITY_SKIP_REMOVE_FREE_BEFORE_PRIVATE_PRO COMMUNITY_PRO_INSTALL_HELPER_IMAGE FREE_PANEL_CONTAINER_FOR_PRO_INSTALL FREE_LANDING_CONTAINER_FOR_PRO_INSTALL STALE_PRO_PANEL_CONTAINER_FOR_PRO_INSTALL COMMUNITY_INSTALL_LOG_TAIL_BYTES COMMUNITY_INSTALL_LOG_API_CHUNK_MAX COMMUNITY_HELPER_SKIP_PREPARE_TOOLS; do ALLOW_COMMUNITY_GITHUB_ACTIVATION COMMUNITY_PRIVATE_INSTALL_SCRIPT_URL COMMUNITY_INSTALL_FETCH_MS PRIVATE_INSTALL_SCRIPT_MAX_BYTES COMMUNITY_DISABLE_DOCKER_CLI_HELPER COMMUNITY_SKIP_REMOVE_FREE_BEFORE_PRIVATE_PRO COMMUNITY_PRO_INSTALL_HELPER_IMAGE FREE_PANEL_CONTAINER_FOR_PRO_INSTALL FREE_LANDING_CONTAINER_FOR_PRO_INSTALL STALE_PRO_PANEL_CONTAINER_FOR_PRO_INSTALL COMMUNITY_INSTALL_LOG_TAIL_BYTES COMMUNITY_INSTALL_LOG_API_CHUNK_MAX COMMUNITY_HELPER_SKIP_PREPARE_TOOLS; do
if [[ -n "${!__ce_var:-}" ]]; then if [[ -n "${!__ce_var:-}" ]]; then
RUN_ENV+=( -e "${__ce_var}=${!__ce_var}" ) RUN_ENV+=( -e "${__ce_var}=${!__ce_var}" )

View File

@@ -70,6 +70,8 @@ const COMMUNITY_UPGRADE_PITCH =
"В PRO: вкл/выкл клиентов, даты и расписание отключений, переименование, экспорт .conf, каскад, Cloudflare WARP, синхронизация времени хоста. В FREE: удаление клиента с сервера + отдельный раздел установки Telegram MTProto-прокси (Docker-контейнер). Полная сборка AWG-панели — приватный репозиторий amnezia_web-PRO; доступ по подписке Boosty."; "В PRO: вкл/выкл клиентов, даты и расписание отключений, переименование, экспорт .conf, каскад, Cloudflare WARP, синхронизация времени хоста. В FREE: удаление клиента с сервера + отдельный раздел установки Telegram MTProto-прокси (Docker-контейнер). Полная сборка AWG-панели — приватный репозиторий amnezia_web-PRO; доступ по подписке Boosty.";
const PANEL_FOOTER_TELEGRAM_DEFAULT = "https://t.me/lot_andrey"; const PANEL_FOOTER_TELEGRAM_DEFAULT = "https://t.me/lot_andrey";
const PANEL_FOOTER_OZON_DEFAULT =
"https://finance.ozon.ru/apps/sbp/ozonbankpay/019dc200-2a5d-7931-a619-782d285f6798";
function sanitizeHttpsHttpUrl(raw) { function sanitizeHttpsHttpUrl(raw) {
const s = String(raw || "").trim(); const s = String(raw || "").trim();
@@ -83,7 +85,7 @@ function sanitizeHttpsHttpUrl(raw) {
return ""; return "";
} }
/** Донат + Telegramспонсор: только в админ‑панели (не пользовательские лендинги). Переопределяется через env контейнера. */ /** Донат + Ozon СБП + Telegramспонсор: только в админ‑панели (не пользовательские лендинги). Переопределяется через env контейнера. */
function panelPromoFooterPayload() { function panelPromoFooterPayload() {
const donateResolved = const donateResolved =
sanitizeHttpsHttpUrl( sanitizeHttpsHttpUrl(
@@ -93,18 +95,26 @@ function panelPromoFooterPayload() {
sanitizeHttpsHttpUrl( sanitizeHttpsHttpUrl(
process.env.PANEL_FOOTER_TELEGRAM_URL?.trim() || PANEL_FOOTER_TELEGRAM_DEFAULT, process.env.PANEL_FOOTER_TELEGRAM_URL?.trim() || PANEL_FOOTER_TELEGRAM_DEFAULT,
) || ""; ) || "";
const ozonResolved =
sanitizeHttpsHttpUrl(
process.env.PANEL_FOOTER_OZON_URL?.trim() || PANEL_FOOTER_OZON_DEFAULT,
) || "";
return { return {
donateUrl: donateResolved, donateUrl: donateResolved,
ozonUrl: ozonResolved,
telegramUrl: telegramResolved, telegramUrl: telegramResolved,
donateLabel: donateLabel:
process.env.PANEL_FOOTER_DONATE_LABEL?.trim() || "Поддержать проект", process.env.PANEL_FOOTER_DONATE_LABEL?.trim() || "Поддержать проект",
ozonLabel:
process.env.PANEL_FOOTER_OZON_LABEL?.trim() ||
"Донат · Ozon СБП",
telegramLabel: telegramLabel:
process.env.PANEL_FOOTER_TELEGRAM_LABEL?.trim() || process.env.PANEL_FOOTER_TELEGRAM_LABEL?.trim() ||
"Telegramканал спонсора", "Telegramканал спонсора",
promoSubtitle: promoSubtitle:
process.env.PANEL_FOOTER_PROMO_SUBTITLE?.trim() || process.env.PANEL_FOOTER_PROMO_SUBTITLE?.trim() ||
"Поддержите разработку и канал спонсора — ссылки есть вверху и внизу панели. На пользовательские сайты‑лендинги это не добавляется.", "Поддержите через Boosty (или вашу ссылку), Ozon СБП и Telegram — те же ссылки внизу панели. На пользовательские лендинги это не выводится.",
}; };
} }