Files
tgvpn/install.sh
T

282 lines
9.5 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# Интерактивная установка tgvpn на Linux-сервер (Docker + PostgreSQL)
set -euo pipefail
INSTALL_DIR="${INSTALL_DIR:-/opt/tgvpn}"
REPO_URL=""
USE_CURRENT_DIR=false
SKIP_DOCKER_INSTALL=false
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
info() { echo -e "${CYAN}[*]${NC} $*"; }
ok() { echo -e "${GREEN}[✓]${NC} $*"; }
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
fail() { echo -e "${RED}[✗]${NC} $*" >&2; exit 1; }
prompt() {
local label="$1"
local default="${2:-}"
local val
if [[ -n "$default" ]]; then
read -r -p "$label [$default]: " val
echo "${val:-$default}"
else
read -r -p "$label: " val
echo "$val"
fi
}
prompt_required() {
local label="$1"
local val=""
while [[ -z "$val" ]]; do
read -r -p "$label: " val
[[ -z "$val" ]] && warn "Поле обязательно."
done
echo "$val"
}
prompt_secret() {
local label="$1"
local val=""
while [[ -z "$val" ]]; do
read -r -s -p "$label: " val
echo ""
[[ -z "$val" ]] && warn "Поле обязательно."
done
echo "$val"
}
prompt_yn() {
local label="$1"
local default="${2:-y}"
local hint="Y/n"
[[ "$default" == "n" ]] && hint="y/N"
local ans
read -r -p "$label [$hint]: " ans
ans="${ans:-$default}"
[[ "$ans" =~ ^[Yy] ]]
}
need_cmd() {
command -v "$1" >/dev/null 2>&1
}
check_docker() {
need_cmd docker && docker compose version >/dev/null 2>&1
}
install_docker() {
info "Установка Docker (официальный репозиторий)..."
if need_cmd apt-get; then
sudo apt-get update -qq
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
if [[ ! -f /etc/apt/keyrings/docker.asc ]]; then
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
fi
sudo apt-get update -qq
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker "$USER" 2>/dev/null || true
ok "Docker установлен. Если команда docker требует sudo — перелогиньтесь или выполните: newgrp docker"
else
fail "Автоустановка Docker поддерживается только для Debian/Ubuntu (apt). Установите Docker вручную: https://docs.docker.com/engine/install/"
fi
}
write_env() {
local env_file="$1"
cat > "$env_file" <<EOF
# Сгенерировано install.sh $(date -Iseconds)
BOT_TOKEN=${BOT_TOKEN}
BOT_DEBUG=${BOT_DEBUG}
TELEGRAM_ADMIN_ID=${TELEGRAM_ADMIN_ID}
REMNAWAVE_PANEL_NAME=${REMNAWAVE_PANEL_NAME}
REMNAWAVE_PANEL_URL=${REMNAWAVE_PANEL_URL}
REMNAWAVE_API_TOKEN=${REMNAWAVE_API_TOKEN}
CADDY_AUTH_API_TOKEN=${CADDY_AUTH_API_TOKEN}
REMNAWAVE_SUBSCRIPTION_URL=${REMNAWAVE_SUBSCRIPTION_URL}
POSTGRES_USER=${POSTGRES_USER}
POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
POSTGRES_DB=${POSTGRES_DB}
DATABASE_URL=${DATABASE_URL}
DEFAULT_USER_DAYS=${DEFAULT_USER_DAYS}
DEFAULT_EXTERNAL_SQUAD_UUID=${DEFAULT_EXTERNAL_SQUAD_UUID}
DEFAULT_INTERNAL_SQUAD_UUIDS=${DEFAULT_INTERNAL_SQUAD_UUIDS}
EOF
chmod 600 "$env_file"
}
banner() {
echo ""
echo "========================================"
echo " tgvpn — установщик Telegram-бота"
echo " Remnawave + PostgreSQL + Docker"
echo "========================================"
echo ""
}
validate_db_password() {
if [[ "$POSTGRES_PASSWORD" =~ [:@/?#\[\] ] ]]; then
warn "В пароле PostgreSQL есть спецсимволы. Рекомендуется сгенерировать пароль (буквы/цифры)."
if ! prompt_yn "Продолжить с этим паролем?" "n"; then
fail "Укажите другой пароль или сгенерируйте автоматически."
fi
fi
}
main() {
banner
local script_dir
script_dir="$(cd "$(dirname "$0")" && pwd)"
if [[ -f "$script_dir/docker-compose.yml" ]] && [[ -f "$script_dir/main.go" ]]; then
if prompt_yn "Запустить установку из текущей папки ($script_dir)?" "y"; then
USE_CURRENT_DIR=true
INSTALL_DIR="$script_dir"
fi
fi
if ! $USE_CURRENT_DIR; then
INSTALL_DIR="$(prompt "Каталог установки" "$INSTALL_DIR")"
REPO_URL="$(prompt "URL git-репозитория (пусто — только каталог, код уже должен быть там)" "")"
fi
echo ""
info "=== Telegram ==="
BOT_TOKEN="$(prompt_secret "BOT_TOKEN (от @BotFather)")"
TELEGRAM_ADMIN_ID="$(prompt_required "TELEGRAM_ADMIN_ID (числовой ID от @userinfobot)")"
if [[ ! "$TELEGRAM_ADMIN_ID" =~ ^[0-9]+$ ]]; then
fail "TELEGRAM_ADMIN_ID должен быть числом"
fi
if prompt_yn "Включить BOT_DEBUG (подробные логи)?" "n"; then
BOT_DEBUG=true
else
BOT_DEBUG=false
fi
echo ""
info "=== Remnawave ==="
REMNAWAVE_PANEL_NAME="$(prompt "Название панели в боте" "Панель 1")"
REMNAWAVE_PANEL_URL="$(prompt_required "REMNAWAVE_PANEL_URL (https://panel.example.com)")"
if [[ ! "$REMNAWAVE_PANEL_URL" =~ ^https?:// ]]; then
fail "URL панели должен начинаться с http:// или https://"
fi
REMNAWAVE_PANEL_URL="${REMNAWAVE_PANEL_URL%/}"
REMNAWAVE_API_TOKEN="$(prompt_secret "REMNAWAVE_API_TOKEN (Settings → API Tokens)")"
CADDY_AUTH_API_TOKEN="$(prompt "CADDY_AUTH_API_TOKEN (опционально, Enter — пропустить)" "")"
REMNAWAVE_SUBSCRIPTION_URL="$(prompt "REMNAWAVE_SUBSCRIPTION_URL (опционально)" "")"
if [[ -n "$REMNAWAVE_SUBSCRIPTION_URL" ]]; then
REMNAWAVE_SUBSCRIPTION_URL="${REMNAWAVE_SUBSCRIPTION_URL%/}"
fi
echo ""
info "=== PostgreSQL ==="
POSTGRES_USER="$(prompt "Пользователь БД" "tgvpn")"
POSTGRES_DB="$(prompt "Имя базы" "tgvpn")"
if prompt_yn "Сгенерировать случайный пароль PostgreSQL?" "y"; then
POSTGRES_PASSWORD="$(openssl rand -hex 16 2>/dev/null || head -c 32 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 24)"
ok "Пароль сгенерирован (сохранён в .env)"
else
POSTGRES_PASSWORD="$(prompt_secret "POSTGRES_PASSWORD")"
fi
validate_db_password
DATABASE_URL="postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?sslmode=disable"
echo ""
info "=== Пользователи VPN (по умолчанию) ==="
DEFAULT_USER_DAYS="$(prompt "Срок подписки по умолчанию (дней)" "30")"
DEFAULT_EXTERNAL_SQUAD_UUID="$(prompt "DEFAULT_EXTERNAL_SQUAD_UUID (опционально)" "")"
DEFAULT_INTERNAL_SQUAD_UUIDS="$(prompt "DEFAULT_INTERNAL_SQUAD_UUIDS через запятую (опционально)" "")"
echo ""
if ! check_docker; then
warn "Docker или Docker Compose не найдены."
if prompt_yn "Установить Docker автоматически (Ubuntu/Debian)?" "y"; then
install_docker
if ! check_docker; then
warn "После установки может потребоваться перелогин. Запустите скрипт снова или: sudo docker compose ..."
SKIP_DOCKER_INSTALL=true
fi
else
fail "Нужны Docker и плагин compose. Установите вручную и запустите скрипт снова."
fi
else
ok "Docker и Docker Compose найдены"
fi
echo ""
info "=== Установка файлов ==="
if ! $USE_CURRENT_DIR; then
sudo mkdir -p "$INSTALL_DIR"
sudo chown "$USER:$USER" "$INSTALL_DIR"
if [[ -n "$REPO_URL" ]]; then
if [[ -d "$INSTALL_DIR/.git" ]]; then
info "Обновление репозитория..."
git -C "$INSTALL_DIR" pull --ff-only || warn "git pull не выполнен"
else
git clone "$REPO_URL" "$INSTALL_DIR"
fi
elif [[ ! -f "$INSTALL_DIR/docker-compose.yml" ]]; then
fail "В $INSTALL_DIR нет docker-compose.yml. Укажите URL репозитория или запустите скрипт из папки проекта."
fi
fi
cd "$INSTALL_DIR"
if [[ -f .env ]]; then
if ! prompt_yn ".env уже существует. Перезаписать?" "n"; then
fail "Установка отменена. Отредактируйте .env вручную."
fi
cp .env ".env.bak.$(date +%s)" 2>/dev/null || true
warn "Старая копия: .env.bak.*"
fi
write_env ".env"
ok "Создан .env"
echo ""
info "=== Запуск контейнеров ==="
DC="docker compose"
if ! docker compose version >/dev/null 2>&1; then
DC="sudo docker compose"
fi
$DC pull db 2>/dev/null || true
$DC up -d --build
echo ""
sleep 3
$DC ps
echo ""
ok "Установка завершена!"
echo ""
echo " Каталог: $INSTALL_DIR"
echo " Логи бота: $DC logs -f bot"
echo " Логи БД: $DC logs -f db"
echo " Перезапуск: $DC up -d --build"
echo ""
echo " В Telegram (аккаунт админа $TELEGRAM_ADMIN_ID):"
echo " /start"
echo " /admin check"
echo " /admin squads"
echo " /admin user"
echo ""
}
main "$@"