feat: nginx landing on :80 with Kaskad support footer; sync admin footer text

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Андрей Бобырев
2026-05-14 15:51:52 +03:00
parent 1ab5739ad0
commit 5a5fc8b240
9 changed files with 256 additions and 14 deletions

View File

@@ -39,8 +39,11 @@ cd /opt/amnezia-admin && chmod +x scripts/install.sh && sudo SKIP_DOWNLOAD=1 bas
| `ADMIN_PASSWORD` | _(генерируется)_ | Первый пароль вместо файла |
| `SKIP_DOWNLOAD` | `0` | `1` — не качать GitHub, собрать из `INSTALL_DIR` |
| `ALLOW_DEFAULT_PASSWORD` | `0` | `1` — см. раздел «Пароль» ниже |
| `SKIP_LANDING` | `0` | `1` — не ставить публичную страницу с футером поддержки |
| `LANDING_PORT` | `80` | Порт nginx-лендинга (если `80` занят — например `8081`) |
| `LANDING_CONTAINER` | `amnezia-web-landing` | Имя контейнера лендинга |
После установки откройте `http://IP_СЕРВЕРА:8080`.
После установки: **админ-панель** `http://IP:8080` (или ваш `HOST_PORT`), **страница с поддержкой проекта** `http://IP/` на порту лендинга (по умолчанию **80**). Кнопка на лендинге ведёт на админку с тем же `HOST_PORT`.
---
@@ -111,20 +114,22 @@ ADMIN_PASSWORD=localtest node server.js
## Support links
Те же способы поддержки и контакты, что в проекте [domen_hydra](https://github.com/andrey271192/domen_hydra):
Как на главной странице установки (**лендинг на порту 80**): **Kaskad WebUI** · GitHub · Boosty · Ozon СБП · Telegram · текст поддержки.
Если проект оказался полезным — буду рад поддержке. Это помогает развивать и поддерживать его дальше.
Поддержать проект — поставь звезду на GitHub **И донат**. Связаться с автором — Telegram.
| Способ | Ссылка |
|--------|--------|
| GitHub | [Amnezia_web](https://github.com/andrey271192/Amnezia_web) |
| Boosty | [boosty.to/andrey27/donate](https://boosty.to/andrey27/donate) |
| Ozon СБП | [ссылка оплаты Ozon](https://finance.ozon.ru/apps/sbp/ozonbankpay/019dc200-2a5d-7931-a619-782d285f6798) |
| Telegram | [@Iot_andrey](https://t.me/Iot_andrey) — вопросы, идеи, обратная связь |
| Ozon СБП | [Ozon СБП](https://finance.ozon.ru/apps/sbp/ozonbankpay/019dc200-2a5d-7931-a619-782d285f6798) |
| Telegram | [@lot_andrey](https://t.me/lot_andrey) |
Файл [`.github/FUNDING.yml`](.github/FUNDING.yml) задаёт кнопку **Sponsor** на GitHub (как в [domen_hydra](https://github.com/andrey271192/domen_hydra)).
Ссылки совпадают с блоком в репозитории [domen_hydra](https://github.com/andrey271192/domen_hydra) (Boosty / Ozon), отображаются на **публичной странице** после установки и в футере админ-панели.
Тот же блок поддержки продублирован в **футере веб-интерфейса**.
Файл [`.github/FUNDING.yml`](.github/FUNDING.yml) задаёт кнопку **Sponsor** на GitHub.
Тот же блок поддержки продублирован в **футере веб-интерфейса** и на **`http://сервер:80/`** (если лендинг не отключён).
---

3
landing/Dockerfile Normal file
View File

@@ -0,0 +1,3 @@
FROM nginx:1.27-alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY index.html styles.css admin-port.js /usr/share/nginx/html/

1
landing/admin-port.js Normal file
View File

@@ -0,0 +1 @@
window.__AMNEZIA_ADMIN_PORT__ = "8080";

59
landing/index.html Normal file
View File

@@ -0,0 +1,59 @@
<!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&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
</head>
<body class="page">
<main class="landing-main">
<p class="eyebrow">Этот сервер</p>
<h1>Управление AmneziaWG</h1>
<p class="lead">
Здесь установлена админ-панель для клиентов VPN. Перейдите по ссылке ниже (порт задаётся при установке, по умолчанию <strong>8080</strong>).
</p>
<p class="admin-row" id="admin-row"></p>
</main>
<footer class="support-footer">
<div class="support-line">
<strong class="support-title">Kaskad WebUI</strong>
<span class="support-sep" aria-hidden="true">·</span>
<a href="https://github.com/andrey271192/Amnezia_web" target="_blank" rel="noopener noreferrer">⭐ GitHub</a>
<span class="support-sep" aria-hidden="true">·</span>
<a href="https://boosty.to/andrey27/donate" target="_blank" rel="noopener noreferrer">💖 Boosty</a>
<span class="support-sep" aria-hidden="true">·</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="support-sep" aria-hidden="true">·</span>
<a href="https://t.me/lot_andrey" target="_blank" rel="noopener noreferrer">✉️ Telegram&nbsp;@lot_andrey</a>
</div>
<p class="support-blurb">
Поддержать проект — поставь звезду на GitHub И донат. Связаться с автором — Telegram.
</p>
</footer>
<script src="admin-port.js"></script>
<script>
(function () {
var port =
(typeof window.__AMNEZIA_ADMIN_PORT__ !== "undefined" && window.__AMNEZIA_ADMIN_PORT__) || "8080";
var h = location.hostname || "";
var proto = location.protocol || "http:";
var url = proto + "//" + h + ":" + port + "/";
var el = document.getElementById("admin-row");
if (el) {
el.innerHTML =
'<a class="admin-btn" href="' +
url +
'">Открыть админ-панель</a> <span class="muted-inline">' +
url +
"</span>";
}
})();
</script>
</body>
</html>

20
landing/nginx.conf Normal file
View File

@@ -0,0 +1,20 @@
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
gzip on;
gzip_types text/css text/plain application/json application/javascript;
add_header X-Content-Type-Options nosniff always;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.css$ {
expires 1h;
add_header Cache-Control "public, max-age=3600";
}
}

124
landing/styles.css Normal file
View File

@@ -0,0 +1,124 @@
:root {
--bg: #070b10;
--card: #111722;
--line: rgba(255, 255, 255, 0.08);
--text: #f4f7ff;
--muted: #94a3b8;
--accent: #7dd3fc;
font-family: "DM Sans", system-ui, sans-serif;
}
* {
box-sizing: border-box;
}
body.page {
margin: 0;
min-height: 100vh;
display: flex;
flex-direction: column;
background: radial-gradient(circle at 20% 20%, rgba(125, 211, 252, 0.08), transparent 35%),
radial-gradient(circle at 80% 0%, rgba(94, 234, 212, 0.06), transparent 40%),
var(--bg);
color: var(--text);
}
.landing-main {
flex: 1;
max-width: 560px;
margin: 0 auto;
padding: clamp(2rem, 6vw, 4rem) 1.25rem;
}
.eyebrow {
letter-spacing: 0.18em;
text-transform: uppercase;
font-size: 0.72rem;
color: var(--accent);
margin: 0 0 0.5rem;
}
.landing-main h1 {
margin: 0 0 1rem;
font-size: clamp(1.5rem, 4vw, 1.85rem);
font-weight: 700;
}
.lead {
margin: 0 0 1.25rem;
line-height: 1.6;
color: var(--muted);
}
.admin-row {
margin: 0;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 0.5rem;
}
.admin-btn {
display: inline-block;
padding: 0.55rem 1rem;
border-radius: 11px;
font-weight: 600;
text-decoration: none;
background: linear-gradient(135deg, #38bdf8, #22d3ee);
color: #041018;
}
.admin-btn:hover {
filter: brightness(1.05);
}
.muted-inline {
font-size: 0.82rem;
color: var(--muted);
word-break: break-all;
}
.support-footer {
flex-shrink: 0;
margin-top: auto;
padding: 1.25rem clamp(1rem, 3vw, 1.5rem) 1.5rem;
border-top: 1px solid var(--line);
background: rgba(10, 15, 22, 0.92);
}
.support-line {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.35rem 0.5rem;
font-size: 0.92rem;
color: var(--muted);
}
.support-title {
color: var(--text);
font-weight: 700;
}
.support-sep {
opacity: 0.55;
user-select: none;
}
.support-line a {
color: var(--accent);
text-decoration: none;
white-space: nowrap;
}
.support-line a:hover {
text-decoration: underline;
}
.support-blurb {
margin: 0.65rem 0 0;
font-size: 0.82rem;
line-height: 1.45;
color: var(--muted);
max-width: 62rem;
}

View File

@@ -87,7 +87,7 @@
<footer class="support-footer">
<div class="support-line">
<strong class="support-title">Amnezia Admin WebUI</strong>
<strong class="support-title">Kaskad WebUI</strong>
<span class="support-sep" aria-hidden="true">·</span>
<a href="https://github.com/andrey271192/Amnezia_web" target="_blank" rel="noopener noreferrer">⭐ GitHub</a>
<span class="support-sep" aria-hidden="true">·</span>
@@ -95,11 +95,10 @@
<span class="support-sep" aria-hidden="true">·</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="support-sep" aria-hidden="true">·</span>
<a href="https://t.me/Iot_andrey" target="_blank" rel="noopener noreferrer">✉️ Telegram&nbsp;@Iot_andrey</a>
<a href="https://t.me/lot_andrey" target="_blank" rel="noopener noreferrer">✉️ Telegram&nbsp;@lot_andrey</a>
</div>
<p class="support-blurb">
Если проект оказался полезным — буду рад поддержке. Это помогает развивать и поддерживать его дальше.
Вопросы, идеи и обратная связь — в Telegram.
Поддержать проект — поставь звезду на GitHub И донат. Связаться с автором — Telegram.
</p>
</footer>

View File

@@ -8,6 +8,9 @@ INSTALL_DIR="${INSTALL_DIR:-/opt/amnezia-admin}"
DATA_DIR="${DATA_DIR:-/opt/amnezia-admin-data}"
CONTAINER_NAME="${CONTAINER_NAME:-amnezia-admin}"
HOST_PORT="${HOST_PORT:-8080}"
LANDING_CONTAINER="${LANDING_CONTAINER:-amnezia-web-landing}"
LANDING_IMAGE="${LANDING_IMAGE:-amnezia-web-landing:latest}"
LANDING_PORT="${LANDING_PORT:-80}"
need_root() {
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
@@ -81,6 +84,8 @@ elif [[ "${ALLOW_DEFAULT_PASSWORD:-}" == "1" ]] || [[ "${ALLOW_DEFAULT_PASSWORD:
RUN_ENV+=( -e "ALLOW_DEFAULT_PASSWORD=1" )
fi
IP="$(hostname -I 2>/dev/null | awk '{print $1}' || true)"
docker run -d --name "${CONTAINER_NAME}" --restart unless-stopped \
-p "${HOST_PORT}:3980" \
-v /var/run/docker.sock:/var/run/docker.sock \
@@ -88,10 +93,28 @@ docker run -d --name "${CONTAINER_NAME}" --restart unless-stopped \
"${RUN_ENV[@]}" \
amnezia-admin:latest
IP="$(hostname -I 2>/dev/null | awk '{print $1}' || true)"
if [[ "${SKIP_LANDING:-}" != "1" ]] && [[ -d "${INSTALL_DIR}/landing" ]]; then
printf "window.__AMNEZIA_ADMIN_PORT__='%s';\n" "${HOST_PORT}" >"${INSTALL_DIR}/landing/admin-port.js"
echo "→ Сборка образа ${LANDING_IMAGE} (страница на порту ${LANDING_PORT})..."
docker build -t "${LANDING_IMAGE}" "${INSTALL_DIR}/landing"
docker rm -f "${LANDING_CONTAINER}" 2>/dev/null || true
if docker run -d --name "${LANDING_CONTAINER}" --restart unless-stopped \
-p "${LANDING_PORT}:80" \
"${LANDING_IMAGE}"; then
echo "→ Лендинг с блоком поддержки: http://${IP:-SERVER_IP}:${LANDING_PORT}/"
else
echo "Не удалось запустить лендинг (часто порт ${LANDING_PORT} занят). Поставьте LANDING_PORT=8081 или SKIP_LANDING=1."
fi
else
echo "→ Лендинг пропущен (SKIP_LANDING=1 или нет каталога landing)."
fi
echo ""
echo "=== Готово ==="
echo "Откройте в браузере: http://${IP:-SERVER_IP}:${HOST_PORT}"
echo "Админ-панель: http://${IP:-SERVER_IP}:${HOST_PORT}"
if [[ "${SKIP_LANDING:-}" != "1" ]]; then
echo "Страница с поддержкой проекта: http://${IP:-SERVER_IP}:${LANDING_PORT}/"
fi
if [[ -f "${PASS_FILE}" ]]; then
echo "Первый пароль: $(cat "${PASS_FILE}")"
fi

View File

@@ -16,9 +16,17 @@ need_root() {
need_root
echo "→ Останавливаю лендинг ${LANDING_CONTAINER:-amnezia-web-landing}..."
docker rm -f "${LANDING_CONTAINER:-amnezia-web-landing}" 2>/dev/null || echo "(лендинг уже отсутствует)"
echo "→ Останавливаю контейнер ${CONTAINER_NAME}..."
docker rm -f "${CONTAINER_NAME}" 2>/dev/null || echo "(контейнер уже отсутствует)"
if [[ "${REMOVE_LANDING_IMAGE:-}" == "1" ]]; then
echo "→ Удаляю образ ${LANDING_IMAGE:-amnezia-web-landing:latest}..."
docker rmi "${LANDING_IMAGE:-amnezia-web-landing:latest}" 2>/dev/null || true
fi
if [[ "${REMOVE_IMAGE:-}" == "1" ]]; then
echo "→ Удаляю образ ${IMAGE_NAME}..."
docker rmi "${IMAGE_NAME}" 2>/dev/null || true
@@ -35,4 +43,4 @@ if [[ "${REMOVE_SRC:-}" == "1" ]]; then
fi
echo "Готово."
echo "Подсказка: REMOVE_DATA=1 REMOVE_SRC=1 REMOVE_IMAGE=1 curl ... | sudo bash — полная очистка."
echo "Подсказка: REMOVE_LANDING_IMAGE=1 — удалить образ лендинга; REMOVE_DATA=1 REMOVE_SRC=1 REMOVE_IMAGE=1 curl ... | sudo bash — полная очистка."