#!/usr/bin/env bash # Одна команда: curl … | sudo bash — спросит только ключ (PAT для ghcr.io). 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}" need_root() { if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then echo "Запустите: curl -fsSL ${RAW_BASE}/quick-install.sh | sudo bash" exit 1 fi } need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "Нужна утилита: $1 (например: apt-get install -y $1)" exit 1 } } trim() { local v="$1" v="${v//$'\r'/}" v="${v#"${v%%[![:space:]]*}"}" v="${v%"${v##*[![:space:]]}"}" 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 echo "Не удалось скачать вспомогательный файл Compose v2 (${RAW_BASE}/scripts/lib-compose-v2.sh)." >&2 rm -f "${_lib}" exit 1 fi # shellcheck disable=SC1090 source "${_lib}" rm -f "${_lib}" 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-панель не удаляю." return 0 fi echo "→ Удаляю FREE-панель Amnezia Web (освобождаю порт 8080 при необходимости)..." local c for c in amnezia-admin amnezia-web-landing; do docker rm -f "${c}" 2>/dev/null || true done docker rmi -f amnezia-admin:latest 2>/dev/null || true docker rmi -f amnezia-web-landing:latest 2>/dev/null || true rm -rf /opt/amnezia-admin 2>/dev/null || true echo "→ FREE-панель снята (контейнеры WireGuard/AmneziaWG не трогались)." } remove_free_amnezia_web_panel echo "→ Получаю актуальный тег образа..." IMAGE_TAG="$(trim "$(curl -fsSL "${RAW_BASE}/PRO_IMAGE_TAG")")" if [[ -z "${IMAGE_TAG}" ]]; then echo "Не удалось прочитать PRO_IMAGE_TAG с GitHub." exit 1 fi if [[ ! -d "${INSTALL_ROOT}/.git" ]]; then need_cmd git mkdir -p "$(dirname "${INSTALL_ROOT}")" rm -rf "${INSTALL_ROOT}" echo "→ Клонирую ${DEPLOY_REPO} → ${INSTALL_ROOT}" git clone --depth 1 --branch main "${DEPLOY_REPO}" "${INSTALL_ROOT}" else echo "→ Обновляю ${INSTALL_ROOT}" git -C "${INSTALL_ROOT}" fetch --depth 1 origin main git -C "${INSTALL_ROOT}" reset --hard "origin/main" fi echo "" # При «curl … | sudo bash» stdin — это поток скрипта, а не клавиатура. # Читаем ключ с настоящего терминала; иначе read получает EOF и ломает .env. 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" exit 1 fi echo "" GHCR_KEY="$(trim "${GHCR_KEY}")" if [[ -z "${GHCR_KEY}" ]]; then echo "Ключ пустой — выход." exit 1 fi # Порт панели на хосте (8080 часто занят старой установкой amnezia-admin и т.п.) tcp_port_in_use() { local port="$1" command -v ss >/dev/null 2>&1 || return 1 ss -tln 2>/dev/null | awk -v pt="$port" '$4 ~ ":"pt"$" {found=1} END{exit !found}' } pick_host_port() { # Явный HOST_PORT=… (часто с sudo -E) — не переопределяем. if [[ -n "${HOST_PORT:-}" ]]; then printf '%s' "${HOST_PORT}" return fi local want="8080" if ! tcp_port_in_use "${want}"; then printf '%s' "${want}" return fi echo "⚠ Порт ${want} уже занят на этом сервере (часто это старая панель или другой контейнер)." >&2 local alt="8081" if [[ -r /dev/tty ]]; then echo "Введите свободный порт для веб-панели [${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}" ]] && { 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}" } pick_awg_container_name() { if [[ -n "${AWG_CONTAINER:-}" ]]; then printf '%s' "${AWG_CONTAINER}" return fi if docker inspect amnezia-awg >/dev/null 2>&1; then printf '%s' 'amnezia-awg' return fi if docker inspect amnezia-awg2 >/dev/null 2>&1; then printf '%s' 'amnezia-awg2' return fi printf '%s' 'amnezia-awg2' } 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}" printf 'GHCR_USERNAME=%q\n' "${DEFAULT_GHCR_USERNAME}" printf 'GHCR_TOKEN=%q\n' "${GHCR_KEY}" 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}" } >"${INSTALL_ROOT}/.env" echo "→ Запуск установки из ${INSTALL_ROOT}" exec bash "${INSTALL_ROOT}/scripts/install.sh"