#!/usr/bin/env bash set -euo pipefail ################################# # КОНФИГУРАЦИЯ И ПЕРЕМЕННЫЕ ################################# PROJECT_DIR="/opt/3dp-manager" SOURCE_DIR="/opt/3dp-manager-src" DEFAULT_REPO="https://github.com/iqubik/3dp-manager.git" DEFAULT_BRANCH="dp-custom" # Цвета RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${GREEN}[INFO]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } die() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } usage() { cat <<'EOF' Использование: install-custom.sh [-r ] [-b ] [-p ] [-s ] Параметры: -r Git URL форка (по умолчанию: https://github.com/iqubik/3dp-manager.git) -b Ветка форка (по умолчанию: dp-custom) -p Папка установки (по умолчанию: /opt/3dp-manager) -s Папка исходников (по умолчанию: /opt/3dp-manager-src) -h Показать эту справку Пример: ./install-custom.sh -r https://github.com/iqubik/3dp-manager.git -b dp-custom EOF } need_root() { [[ $EUID -eq 0 ]] || die "Этот скрипт должен быть запущен от имени root" } need_cmd() { command -v "$1" >/dev/null 2>&1 || die "Не найдена команда: $1" } resolve_compose_cmd() { if docker compose version >/dev/null 2>&1; then COMPOSE_CMD=("docker" "compose") return 0 fi if command -v docker-compose >/dev/null 2>&1; then COMPOSE_CMD=("docker-compose") return 0 fi warn "Не найден Docker Compose. Пытаемся установить..." apt-get update apt-get install -y docker-compose-plugin || true if docker compose version >/dev/null 2>&1; then COMPOSE_CMD=("docker" "compose") return 0 fi apt-get install -y docker-compose || true if command -v docker-compose >/dev/null 2>&1; then COMPOSE_CMD=("docker-compose") return 0 fi die "Не удалось установить Docker Compose. Установите docker compose plugin (v2) или docker-compose (v1)." } check_containers_running() { log "Проверка статуса контейнеров..." local timeout=${1:-60} local elapsed=0 local failed=0 while [ $elapsed -lt $timeout ]; do failed=0 while IFS=$'\t' read -r container_name status; do if [ -n "$container_name" ] && [ -n "$status" ]; then if ! echo "$status" | grep -qiE "^up|running|healthy|restarting"; then failed=1 warn "Контейнер $container_name в статусе: $status" fi fi done < <("${COMPOSE_CMD[@]}" ps --format "table {{.Name}}\t{{.Status}}" --all 2>/dev/null | tail -n +2) if [ $failed -eq 0 ]; then log "Все контейнеры запущены успешно" return 0 fi sleep 2 elapsed=$((elapsed + 2)) done return 1 } get_random_port() { local MIN=${1:-3000} local MAX=${2:-6999} while :; do PORT=$(shuf -i "$MIN-$MAX" -n 1) if ! ss -ltun | awk '{print $4}' | grep -q ":$PORT\$"; then echo "$PORT" return fi done } ################################# # PARSE ARGS ################################# REPO_URL="$DEFAULT_REPO" BRANCH="$DEFAULT_BRANCH" while getopts ":r:b:p:s:h" opt; do case "$opt" in r) REPO_URL="$OPTARG" ;; b) BRANCH="$OPTARG" ;; p) PROJECT_DIR="$OPTARG" ;; s) SOURCE_DIR="$OPTARG" ;; h) usage; exit 0 ;; :) die "Параметр -$OPTARG требует значение" ;; \?) die "Неизвестный параметр: -$OPTARG" ;; esac done ################################# # ASCII-баннер ################################# echo "===================================================" echo " ____ ____ _ ___ _ " echo " / __ \___ ____ / __ \(_) (_)___ ______(_)___ ___ " echo " / / / / _ \/ __ \/ /_/ / / / / __ \/ ___/ / __ \`__ \ " echo " / /_/ / __/ / / / ____/ / / / /_/ / / / / / / / / /" echo "/_____/\___/_/ /_/_/ /_/_/_/\__, /_/ /_/_/ /_/ /_/ " echo " /____/ " echo "" echo " 3DP-MANAGER — CUSTOM INSTALL " echo "===================================================" echo "" log "Репозиторий: $REPO_URL" log "Ветка: $BRANCH" ################################# # ПРОВЕРКИ ################################# need_root . /etc/os-release if [[ "$ID" != "ubuntu" && "$ID" != "debian" ]]; then die "Этот скрипт поддерживает только Ubuntu или Debian: $ID" fi if [ "$(free -m | grep Mem: | awk '{print $2}')" -lt 2000 ]; then if [ "$(free -m | grep Swap: | awk '{print $2}')" -eq 0 ]; then log "Мало RAM и нет Swap. Создаем swap-файл 2GB..." fallocate -l 2G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=2048 chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo '/swapfile none swap sw 0 0' >> /etc/fstab log "Swap создан." fi fi log "Проверка зависимостей..." need_cmd curl if ! command -v jq &> /dev/null; then apt-get update && apt-get install -y jq; fi if ! command -v openssl &> /dev/null; then apt-get install -y openssl; fi if ! command -v tar &> /dev/null; then apt-get install -y tar; fi if ! command -v hostname &> /dev/null; then apt-get install -y net-tools || apt-get install -y hostname; fi need_cmd git ################################# # DOCKER ################################# log "Проверка Docker" if command -v docker >/dev/null 2>&1; then log "Docker уже установлен" else log "Docker не найден, будет установлен из официального репозитория" apt update apt install -y ca-certificates curl install -m 0755 -d /etc/apt/keyrings if [[ "$ID" == "ubuntu" ]]; then curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc || die "Ошибка добавления ключа Docker" else curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc || die "Ошибка добавления ключа Docker" fi chmod a+r /etc/apt/keyrings/docker.asc CODENAME=${UBUNTU_CODENAME:-$VERSION_CODENAME} tee /etc/apt/sources.list.d/docker.sources < /dev/null; then log "Установка Hysteria 2..." bash <(curl -fsSL https://get.hy2.sh/) < /dev/null || true RANDOM_FREE_PORT=$(get_random_port 10000 20000) GENERATED_PASSWORD=$(openssl rand -base64 16 | tr -dc 'A-Za-z0-9' | cut -c1-16) GENERATED_OBFS_PASSWORD=$(openssl rand -base64 16 | tr -dc 'A-Za-z0-9' | cut -c1-16) echo "=== Настройка Hysteria 2 ===" read -e -p "Введите email для уведомлений Let's Encrypt: " HYSTERIA_EMAIL HYSTERIA_EMAIL=$(echo "$HYSTERIA_EMAIL" | tr -cd 'a-zA-Z0-9.@_-') cat > /etc/hysteria/config.yaml < server/.env < client/nginx-client.conf < docker-compose.yml < client/nginx-client.conf < docker-compose.yml </dev/null | grep -q "Status: active"; then log "Найден активный UFW. Настраиваю правила..." ufw allow 443/tcp ufw allow 443/udp ufw allow 8443/tcp ufw allow 8443/udp ufw allow 10000:60000/tcp ufw allow 10000:60000/udp fi echo "" echo "===================================================" if [[ "$USE_SSL" == "true" ]]; then echo -e "${GREEN}✔ Установка завершена! Доступно по адресу: https://${UI_HOST}:${FINAL_PORT}${NC}" else echo -e "${GREEN}✔ Установка завершена! Доступно по адресу: http://${UI_HOST}:${FINAL_PORT}${NC}" fi echo -e "${GREEN}Логин: ${ADMIN_USER}${NC}" echo -e "${GREEN}Пароль: ${ADMIN_PASS}${NC}" echo "" echo "Немедленно измените пароль в Настройках утилиты!" echo "==================================================="