From c4186415c45a6e183373ec23ba6af67348f7358d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=91=D0=BE=D0=B1?= =?UTF-8?q?=D1=8B=D1=80=D0=B5=D0=B2?= Date: Sat, 6 Jun 2026 01:32:41 +0300 Subject: [PATCH] fix: avoid GHCR 403 in default installer --- .env.example | 8 ++- README.md | 169 ++++++++++++++++++++------------------------- quick-install.sh | 97 ++++++++++++-------------- scripts/install.sh | 34 ++++++++- scripts/update.sh | 35 +++++++++- 5 files changed, 191 insertions(+), 152 deletions(-) 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**: - -

-Панель: пользователи AmneziaWG, инстансы, время, статусы -

-Панель: заголовок, форма смены пароля -

- -## Одна команда — 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 "Обновлено."