diff --git a/.env.example b/.env.example
index 8223bdf..43561ea 100644
--- a/.env.example
+++ b/.env.example
@@ -1,6 +1,10 @@
-# Скопируйте в .env и заполните значения из закрытого поста Boosty (только для подписчиков).
+# Legacy GHCR mode only.
+# Обычная установка больше не требует .env и GHCR PAT:
+# curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main/quick-install.sh | sudo bash
+#
+# Этот файл нужен только если вы сознательно запускаете USE_GHCR=1.
-# Образ из GHCR (пример; ваш продавец подставит точное значение)
+# Образ из GHCR. Если package закрыт или PAT без read:packages, Docker вернёт 403 Forbidden.
GHCR_IMAGE=ghcr.io/andrey271192/amnezia-admin-pro
IMAGE_TAG=v1.0.0
diff --git a/README.md b/README.md
index 5898249..9c75fdc 100644
--- a/README.md
+++ b/README.md
@@ -1,136 +1,113 @@
-# Amnezia Admin Pro — установка (Docker / GHCR)
+# Amnezia Web PRO — установка
-Скрипты, которые снимают FREE-панель [amnezia_web](https://github.com/andrey271192/amnezia_web) и поднимают PRO-образ с GitHub Container Registry. VPN-контейнеры (**`amnezia-awg`**, **`amnezia-awg2`**) не трогаем.
+Публичный установщик для **Amnezia Web PRO**. По умолчанию ставит панель из открытого GitHub repo:
-## Что понадобится на VPS
+- source: [`andrey271192/amnezia_web-PRO_test`](https://github.com/andrey271192/amnezia_web-PRO_test)
+- текущий релиз: `v1.5.2`
+- old GHCR image больше не используется по умолчанию, чтобы у пользователей не было `403 Forbidden`.
-- **Docker** и работающий демон.
-- **`docker compose`** v2 (не старый `docker-compose` 1.x — будет `ContainerConfig` / KeyError).
-- **curl**, при первой установке — **git**.
-- **Ключ** с Boosty: GitHub PAT с `read:packages` (не светите в чатах и на скринах).
+## Быстрая установка
-`quick-install.sh` сам подтянет Compose v2, если в apt его нет (`scripts/lib-compose-v2.sh`).
-
-## Как выглядит PRO
-
-После установки — та же панель, что в **amnezia_web-PRO**:
-
-
-
-
-
-
-
-## Одна команда — FREE → PRO (спросит только ключ)
-
-На VPS **от root**:
+На VPS от root:
```bash
curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main/quick-install.sh | sudo bash
```
-Скрипт **перед установкой PRO**:
-
-1. **Убирает FREE-панель** из типового `install.sh` (**amnezia_web**): контейнеры `amnezia-admin`, `amnezia-web-landing`, локальные образы `amnezia-admin:latest` / `amnezia-web-landing:latest`, каталог сборки **`/opt/amnezia-admin`**.
-2. **Не трогает** контейнеры VPN (**AmneziaWG / AWG**): **`amnezia-awg`**, **`amnezia-awg2`** и т.п.
-3. Подставляет **`AWG_CONTAINER`** автоматически, если на сервере уже есть `amnezia-awg` или `amnezia-awg2`.
-4. Клонирует/обновляет **`/opt/amnezia-web-pro-deploy`**, тянет тег из [`PRO_IMAGE_TAG`](PRO_IMAGE_TAG), запрашивает **ключ PAT** (ввод с **`/dev/tty`**).
-
-Перед всем этим, если **`docker compose`** отсутствует, установщик **подтягивает** модуль Compose v2 (см. выше).
-
-Отключить снос FREE (редко нужно): `SKIP_REMOVE_FREE=1 curl … | sudo -E bash`.
-
-Если **нет интерактива** на stdin:
+То же самое с явным первым паролем admin:
```bash
-curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main/quick-install.sh -o /tmp/amnezia-quick-install.sh
-sudo bash /tmp/amnezia-quick-install.sh
+curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main/quick-install.sh \
+ | sudo ADMIN_PASSWORD='СЛОЖНЫЙ_ПАРОЛЬ' bash
```
-**Порт:** если после удаления FREE **8080** всё ещё занят — скрипт предложит другой или задайте `HOST_PORT=8081 curl … | sudo -E bash`.
-
-## Частые проблемы после установки
-
-### `Bind for 0.0.0.0:8080 failed: port is already allocated`
-
-На **8080** уже слушает другой процесс или контейнер (не только снятая FREE-панель). Посмотреть занятость: `ss -tlnp | grep ':8080'`. Обход: переустановка с **`HOST_PORT=8081`** (или свободный порт):
+`quick-install.sh` скачает и запустит рабочий установщик:
```bash
-HOST_PORT=8081 curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main/quick-install.sh | sudo -E bash
+https://raw.githubusercontent.com/andrey271192/amnezia_web-PRO_test/main/install.sh
```
-### `KeyError: 'ContainerConfig'` и в трассировке указан **`docker-compose` 1.29.x** (`/usr/lib/python3/...`)
+## Почему больше не GHCR
-Старый **Docker Compose v1** из пакета `docker-compose` **непонимает** образы из GHCR с современным OCI-манифестом без поля **`ContainerConfig`**. Нужна **Compose v2** — **`docker compose`**.
+Старый сценарий тянул private image:
-На актуальной ветке **`main`** установщики **не дергают legacy `docker-compose` 1.x**, а поднимают Compose v2 через APT (**если пакет виден**) либо **официальный CLI‑плагин с GitHub**.
-
-```bash
-sudo git -C /opt/amnezia-web-pro-deploy fetch origin main
-sudo git -C /opt/amnezia-web-pro-deploy reset --hard origin/main
-curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main/quick-install.sh | sudo bash
+```text
+ghcr.io/andrey271192/amnezia-admin-pro:v1.0.0
```
-Вручную: **`docker-compose-plugin`** из [репозитория Docker CE](https://docs.docker.com/engine/install/ubuntu/) или шаг из справки в **`scripts/lib-compose-v2.sh`**.
+Если у GitHub PAT нет доступа к package или package закрыт, Docker показывает:
-Если после неудачного `up` образовался полубитый контейнер:
-
-```bash
-sudo docker rm -f amnezia-admin-pro 2>/dev/null || true
-sudo bash /opt/amnezia-web-pro-deploy/scripts/install.sh
+```text
+unexpected status ... 403 Forbidden
```
-Если файл **`.env` удалён или пуст**, снова запустите **`quick-install.sh`** — он заново попросит PAT.
+Это не ошибка VPS и не ошибка Docker. Это отказ GitHub Container Registry. Чтобы не ловить эту ошибку у подписчиков, обычная установка теперь идёт из GitHub source.
-### `Unable to locate package docker-compose-plugin` (Ubuntu без репозитория Docker)
+Предупреждение Docker:
-Пакет поставляет официальный репозиторий Docker CE на Ubuntu/Debian. Если его подключать не хотите или `apt-get` всё равно пишет, что пакета нет:
-
-1. Просто снова запустите обновлённый **`quick-install.sh`** после `git pull` репозитория установки — он попытается **скачать плагин Compose v2 напрямую** с GitHub (нужны `curl` и исходящий HTTPS до `github.com`).
-
-Либо отдельно:
-
-```bash
-sudo curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main/scripts/lib-compose-v2.sh -o /tmp/lib-compose-v2.sh
-sudo bash -c 'source /tmp/lib-compose-v2.sh && ensure_compose_v2'
-sudo rm -f /tmp/lib-compose-v2.sh
-docker compose version
+```text
+WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'
```
-## Ручной способ (git + .env)
+не является причиной ошибки. Это только предупреждение Docker.
+
+## Legacy GHCR mode
+
+Оставлен только для ручного использования, если у вас реально есть доступ к package:
```bash
-git clone https://github.com/andrey271192/amnezia-web-pro-deploy.git
-cd amnezia-web-pro-deploy
-cp .env.example .env
-# заполните GHCR_* из закрытого поста Boosty
-sudo bash scripts/install.sh
+USE_GHCR=1 curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main/quick-install.sh | sudo -E bash
+```
+
+или:
+
+```bash
+USE_GHCR=1 sudo -E bash scripts/install.sh
+```
+
+Нужны:
+
+- GitHub PAT с `read:packages`;
+- доступ к package `ghcr.io/andrey271192/amnezia-admin-pro`;
+- существующий tag из `PRO_IMAGE_TAG`.
+
+## Удаление
+
+Обычная остановка панели:
+
+```bash
+curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia_web-PRO_test/main/uninstall.sh | sudo bash
+```
+
+Полная очистка с данными:
+
+```bash
+curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia_web-PRO_test/main/uninstall.sh \
+ | sudo REMOVE_DATA=1 REMOVE_DIR=1 bash
```
## Обновление
-После объявления нового тега образа обновите `IMAGE_TAG` в `.env` и выполните:
+В панели: **Настройки → О программе и обновления → Проверить обновления**.
+
+Доступно:
+
+- release channel;
+- beta channel;
+- выбор версии;
+- rollback на старый tag.
+
+Ручное обновление source-установки:
```bash
-sudo bash scripts/update.sh
+cd /opt/amnezia-web-pro
+git pull
+docker compose build
+docker compose up -d
```
-## Шаблон поста для Boosty
-
-См. [docs/BOOSTY_SUBSCRIBERS_POST.md](docs/BOOSTY_SUBSCRIBERS_POST.md).
-
-## Безопасность
-
-- Файл `.env` с токеном не должен попадать в issue, чаты и скриншоты.
-- При утечке токена автор перевыпускает его в GitHub; подписчикам выдаётся новый токен в обновлённом посте.
-
----
-
## Поддержка проекта
----
-
-- ⭐ **GitHub:** [andrey271192/amnezia-web-pro-deploy](https://github.com/andrey271192/amnezia-web-pro-deploy)
-- 💖 **Boosty:** [boosty.to/andrey27/donate](https://boosty.to/andrey27/donate)
-- 💳 **Ozon Bank (СБП):** [ссылка](https://finance.ozon.ru/apps/sbp/ozonbankpay/019dc200-2a5d-7931-a619-782d285f6798)
-- ✉️ **Telegram:** [@lot_andrey](https://t.me/lot_andrey)
+- GitHub: [andrey271192/amnezia_web-PRO_test](https://github.com/andrey271192/amnezia_web-PRO_test)
+- Boosty: [boosty.to/andrey27/donate](https://boosty.to/andrey27/donate)
+- Telegram: [@lot_andrey](https://t.me/lot_andrey)
diff --git a/quick-install.sh b/quick-install.sh
index 078631a..794dfc3 100755
--- a/quick-install.sh
+++ b/quick-install.sh
@@ -1,13 +1,10 @@
#!/usr/bin/env bash
-# Одна команда: curl … | sudo bash — спросит только ключ (PAT для ghcr.io).
+# One-command installer kept for old links. Default path now installs from the
+# open source repo, not the private GHCR image, so subscribers do not hit 403.
set -euo pipefail
-DEPLOY_REPO="${DEPLOY_REPO:-https://github.com/andrey271192/amnezia-web-pro-deploy.git}"
RAW_BASE="${RAW_BASE:-https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main}"
-INSTALL_ROOT="${INSTALL_ROOT:-/opt/amnezia-web-pro-deploy}"
-
-DEFAULT_GHCR_IMAGE="${DEFAULT_GHCR_IMAGE:-ghcr.io/andrey271192/amnezia-admin-pro}"
-DEFAULT_GHCR_USERNAME="${DEFAULT_GHCR_USERNAME:-andrey271192}"
+SOURCE_INSTALL_URL="${SOURCE_INSTALL_URL:-https://raw.githubusercontent.com/andrey271192/amnezia_web-PRO_test/main/install.sh}"
need_root() {
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
@@ -18,11 +15,38 @@ need_root() {
need_cmd() {
command -v "$1" >/dev/null 2>&1 || {
- echo "Нужна утилита: $1 (например: apt-get install -y $1)"
+ echo "Нужна утилита: $1"
exit 1
}
}
+need_root
+need_cmd curl
+
+if [[ "${USE_GHCR:-0}" != "1" ]]; then
+ echo "→ GHCR-установка отключена: старый private image часто даёт 403 Forbidden."
+ echo "→ Ставлю Amnezia Web PRO из GitHub: ${SOURCE_INSTALL_URL}"
+ echo "→ Для старого GHCR-сценария запустите: USE_GHCR=1 curl ... | sudo -E bash"
+ echo ""
+ tmp="$(mktemp)"
+ trap 'rm -f "$tmp"' EXIT
+ curl -fsSL "${SOURCE_INSTALL_URL}" -o "$tmp"
+ exec bash "$tmp"
+fi
+
+DEPLOY_REPO="${DEPLOY_REPO:-https://github.com/andrey271192/amnezia-web-pro-deploy.git}"
+INSTALL_ROOT="${INSTALL_ROOT:-/opt/amnezia-web-pro-deploy}"
+
+DEFAULT_GHCR_IMAGE="${DEFAULT_GHCR_IMAGE:-ghcr.io/andrey271192/amnezia-admin-pro}"
+DEFAULT_GHCR_USERNAME="${DEFAULT_GHCR_USERNAME:-andrey271192}"
+
+need_cmd docker
+
+if ! docker info >/dev/null 2>&1; then
+ echo "Docker daemon недоступен. Запустите Docker и повторите."
+ exit 1
+fi
+
trim() {
local v="$1"
v="${v//$'\r'/}"
@@ -31,15 +55,6 @@ trim() {
printf '%s' "$v"
}
-need_root
-need_cmd curl
-need_cmd docker
-
-if ! docker info >/dev/null 2>&1; then
- echo "Docker daemon недоступен. Запустите Docker и повторите."
- exit 1
-fi
-
if ! docker compose version >/dev/null 2>&1; then
_lib="$(mktemp)"
if ! curl -fsSL "${RAW_BASE}/scripts/lib-compose-v2.sh" -o "${_lib}"; then
@@ -53,8 +68,6 @@ if ! docker compose version >/dev/null 2>&1; then
ensure_compose_v2 || exit 1
fi
-# Снимаем FREE-панель (amnezia_web / install.sh): контейнеры + локальный образ + каталог сборки.
-# VPN/AWG контейнеры (amnezia-awg, amnezia-awg2 и т.д.) не останавливаем.
remove_free_amnezia_web_panel() {
if [[ "${SKIP_REMOVE_FREE:-}" == "1" ]]; then
echo "→ SKIP_REMOVE_FREE=1 — FREE-панель не удаляю."
@@ -93,17 +106,12 @@ else
fi
echo ""
-# При «curl … | sudo bash» stdin — это поток скрипта, а не клавиатура.
-# Читаем ключ с настоящего терминала; иначе read получает EOF и ломает .env.
+echo "Введите ключ доступа к образу (GitHub PAT с правом read:packages)."
+echo "Ввод скрыт — символы не отображаются."
if [[ -r /dev/tty ]]; then
- echo "Введите ключ доступа к образу (GitHub PAT с правом read:packages)."
- echo "Ввод скрыт — символы не отображаются."
IFS= read -rs GHCR_KEY < /dev/tty || true
else
- echo "Нет доступа к /dev/tty (интерактивный ввод недоступен)."
- echo "Скачайте скрипт и запустите файлом:"
- echo " curl -fsSL ${RAW_BASE}/quick-install.sh -o /tmp/amnezia-quick-install.sh"
- echo " sudo bash /tmp/amnezia-quick-install.sh"
+ echo "Нет доступа к /dev/tty. Скачайте скрипт и запустите файлом."
exit 1
fi
echo ""
@@ -114,7 +122,6 @@ if [[ -z "${GHCR_KEY}" ]]; then
exit 1
fi
-# Порт панели на хосте (8080 часто занят старой установкой amnezia-admin и т.п.)
tcp_port_in_use() {
local port="$1"
command -v ss >/dev/null 2>&1 || return 1
@@ -122,7 +129,6 @@ tcp_port_in_use() {
}
pick_host_port() {
- # Явный HOST_PORT=… (часто с sudo -E) — не переопределяем.
if [[ -n "${HOST_PORT:-}" ]]; then
printf '%s' "${HOST_PORT}"
return
@@ -132,31 +138,18 @@ pick_host_port() {
printf '%s' "${want}"
return
fi
- echo "⚠ Порт ${want} уже занят на этом сервере (часто это старая панель или другой контейнер)." >&2
local alt="8081"
- if [[ -r /dev/tty ]]; then
- echo "Введите свободный порт для веб-панели [${alt}]:" >&2
+ echo "⚠ Порт ${want} занят. Введите свободный порт [${alt}]:" >&2
+ IFS= read -r line < /dev/tty || true
+ line="$(trim "${line}")"
+ [[ -z "${line}" ]] && line="${alt}"
+ while tcp_port_in_use "${line}"; do
+ echo "⚠ Порт ${line} тоже занят. Укажите другой:" >&2
IFS= read -r line < /dev/tty || true
line="$(trim "${line}")"
- [[ -z "${line}" ]] && line="${alt}"
- while tcp_port_in_use "${line}"; do
- echo "⚠ Порт ${line} тоже занят. Укажите другой:" >&2
- IFS= read -r line < /dev/tty || true
- line="$(trim "${line}")"
- [[ -z "${line}" ]] && {
- echo "Порт не указан — выход." >&2
- exit 1
- }
- done
- printf '%s' "${line}"
- return
- fi
- echo "Нет /dev/tty — пробую ${alt}. При необходимости задайте HOST_PORT=… и повторите запуск." >&2
- if tcp_port_in_use "${alt}"; then
- echo "И ${alt} занят. Задайте явно: HOST_PORT=9080 curl … | sudo -E bash" >&2
- exit 1
- fi
- printf '%s' "${alt}"
+ [[ -z "${line}" ]] && exit 1
+ done
+ printf '%s' "${line}"
}
pick_awg_container_name() {
@@ -179,7 +172,6 @@ SELECTED_HOST_PORT="$(pick_host_port)"
SELECTED_AWG="$(pick_awg_container_name)"
umask 077
-# %q экранирует $ и прочее — безопасно для «source .env» в install.sh
{
printf 'GHCR_IMAGE=%q\n' "${DEFAULT_GHCR_IMAGE}"
printf 'IMAGE_TAG=%q\n' "${IMAGE_TAG}"
@@ -188,6 +180,9 @@ umask 077
printf 'HOST_PORT=%q\n' "${SELECTED_HOST_PORT}"
printf 'CONTAINER_NAME=%q\n' "${CONTAINER_NAME:-amnezia-admin-pro}"
printf 'AWG_CONTAINER=%q\n' "${SELECTED_AWG}"
+ if [[ -n "${ADMIN_PASSWORD:-}" ]]; then
+ printf 'ADMIN_PASSWORD=%q\n' "${ADMIN_PASSWORD}"
+ fi
} >"${INSTALL_ROOT}/.env"
echo "→ Запуск установки из ${INSTALL_ROOT}"
diff --git a/scripts/install.sh b/scripts/install.sh
index 007eaa6..e1c5f9b 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
-# Установка Pro с приватного GHCR (без исходников). Публичный репозиторий — только этот скрипт и compose.
+# Legacy GHCR installer. Default public install now goes through quick-install.sh
+# -> amnezia_web-PRO_test/install.sh to avoid GHCR 403 for users without package access.
set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
@@ -31,6 +32,16 @@ need_docker
ensure_compose_v2 || exit 1
COMPOSE=(docker compose)
+if [[ "${USE_GHCR:-0}" != "1" ]]; then
+ echo "Этот сценарий использует private GHCR image и может дать 403 Forbidden."
+ echo "Для обычной установки используйте:"
+ echo " curl -fsSL https://raw.githubusercontent.com/andrey271192/amnezia-web-pro-deploy/main/quick-install.sh | sudo bash"
+ echo ""
+ echo "Если точно нужен старый GHCR-режим, запустите:"
+ echo " USE_GHCR=1 sudo -E bash scripts/install.sh"
+ exit 1
+fi
+
if [[ ! -f "${ENV_FILE}" ]]; then
echo "Создайте файл .env из .env.example и вставьте GHCR_* из закрытого поста Boosty:"
echo " cp .env.example .env && nano .env"
@@ -54,6 +65,7 @@ GHCR_IMAGE="$(trim_crlf "${GHCR_IMAGE:-}")"
IMAGE_TAG="$(trim_crlf "${IMAGE_TAG:-}")"
GHCR_USERNAME="$(trim_crlf "${GHCR_USERNAME:-}")"
GHCR_TOKEN="$(trim_crlf "${GHCR_TOKEN:-}")"
+ADMIN_PASSWORD="$(trim_crlf "${ADMIN_PASSWORD:-}")"
: "${GHCR_IMAGE:?Укажите GHCR_IMAGE в .env}"
: "${IMAGE_TAG:?Укажите IMAGE_TAG в .env}"
@@ -66,8 +78,26 @@ printf '%s\n' "${GHCR_TOKEN}" | docker login ghcr.io -u "${GHCR_USERNAME}" --pas
echo "→ pull ${GHCR_IMAGE}:${IMAGE_TAG}"
"${COMPOSE[@]}" -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" pull
+RESET_PANEL_PASSWORD=0
+if [[ -n "${ADMIN_PASSWORD}" ]]; then
+ echo "→ ADMIN_PASSWORD задан — сбрасываю старый пароль панели в volume."
+ "${COMPOSE[@]}" -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" run --rm --no-deps --entrypoint sh panel -lc '
+ set -eu
+ if [ -f /data/password.hash ]; then
+ cp -p /data/password.hash "/data/password.hash.bak.$(date -u +%Y%m%dT%H%M%SZ)" 2>/dev/null || true
+ rm -f /data/password.hash
+ rm -f /data/session.secret
+ fi
+ '
+ RESET_PANEL_PASSWORD=1
+fi
+
echo "→ up -d"
-"${COMPOSE[@]}" -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" up -d
+UP_ARGS=(up -d)
+if [[ "${RESET_PANEL_PASSWORD}" == "1" ]]; then
+ UP_ARGS+=(--force-recreate)
+fi
+"${COMPOSE[@]}" -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" "${UP_ARGS[@]}"
IP="$(hostname -I 2>/dev/null | awk '{print $1}' || true)"
echo ""
diff --git a/scripts/update.sh b/scripts/update.sh
index 3fe85f4..95bb5f9 100755
--- a/scripts/update.sh
+++ b/scripts/update.sh
@@ -14,6 +14,19 @@ fi
ensure_compose_v2 || exit 1
COMPOSE=(docker compose)
+if [[ "${USE_GHCR:-0}" != "1" ]]; then
+ echo "Этот update.sh относится к legacy GHCR-режиму и может дать 403 Forbidden."
+ echo "Для source-установки обновляйтесь из панели или вручную:"
+ echo " cd /opt/amnezia-web-pro"
+ echo " git pull"
+ echo " docker compose build"
+ echo " docker compose up -d"
+ echo ""
+ echo "Если точно нужен старый GHCR-режим, запустите:"
+ echo " USE_GHCR=1 sudo -E bash scripts/update.sh"
+ exit 1
+fi
+
set -a
# shellcheck disable=SC1090
source "${ENV_FILE}"
@@ -28,9 +41,29 @@ trim_crlf() {
}
GHCR_USERNAME="$(trim_crlf "${GHCR_USERNAME:-}")"
GHCR_TOKEN="$(trim_crlf "${GHCR_TOKEN:-}")"
+ADMIN_PASSWORD="$(trim_crlf "${ADMIN_PASSWORD:-}")"
printf '%s\n' "${GHCR_TOKEN}" | docker login ghcr.io -u "${GHCR_USERNAME}" --password-stdin
"${COMPOSE[@]}" -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" pull
-"${COMPOSE[@]}" -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" up -d
+
+RESET_PANEL_PASSWORD=0
+if [[ -n "${ADMIN_PASSWORD}" ]]; then
+ echo "→ ADMIN_PASSWORD задан — сбрасываю старый пароль панели в volume."
+ "${COMPOSE[@]}" -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" run --rm --no-deps --entrypoint sh panel -lc '
+ set -eu
+ if [ -f /data/password.hash ]; then
+ cp -p /data/password.hash "/data/password.hash.bak.$(date -u +%Y%m%dT%H%M%SZ)" 2>/dev/null || true
+ rm -f /data/password.hash
+ rm -f /data/session.secret
+ fi
+ '
+ RESET_PANEL_PASSWORD=1
+fi
+
+UP_ARGS=(up -d)
+if [[ "${RESET_PANEL_PASSWORD}" == "1" ]]; then
+ UP_ARGS+=(--force-recreate)
+fi
+"${COMPOSE[@]}" -f "${COMPOSE_FILE}" --env-file "${ENV_FILE}" "${UP_ARGS[@]}"
echo "Обновлено."