diff --git a/setup_production_server.sh b/setup_production_server.sh index 305cf3c..ee5cd9e 100644 --- a/setup_production_server.sh +++ b/setup_production_server.sh @@ -4,6 +4,7 @@ # MY UPLOADER BOT - ПОЛНАЯ АВТОМАТИЧЕСКАЯ УСТАНОВКА НА PRODUCTION СЕРВЕР # ============================================================================= # Клонирует все модули из projscale.dev и настраивает полную систему +# Поддерживает типы нод: main (основная) и regular (обычная) # ============================================================================= set -euo pipefail # Выход при любой ошибке @@ -18,6 +19,19 @@ CYAN='\033[0;36m' WHITE='\033[1;37m' NC='\033[0m' # No Color +# Конфигурация +DOMAIN="$1" +NODE_TYPE="${2:-regular}" # main или regular, по умолчанию: regular +EMAIL="${3:-admin@${1}}" +PROJECT_DIR="/home/myuploader" +SERVICE_USER="myuploader" +PROGRESS_FILE="/home/myuploader/.installation_progress" + +# Репозитории для клонирования +UPLOADER_REPO="https://git.projscale.dev/my-dev/uploader-bot" +CONVERTER_REPO="https://git.projscale.dev/my-dev/converter-module" +WEB2_REPO="https://git.projscale.dev/my-dev/web2-client" + # Функция логирования log() { echo -e "${WHITE}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" @@ -39,38 +53,73 @@ log_info() { echo -e "${BLUE}[INFO]${NC} $1" } +# Функция записи прогресса +log_progress() { + local step="$1" + echo "$step:$(date '+%Y-%m-%d %H:%M:%S')" >> "$PROGRESS_FILE" + log_success "Этап завершен: $step" +} + +# Функция проверки завершенности этапа +is_step_completed() { + local step="$1" + if [ -f "$PROGRESS_FILE" ]; then + grep -q "^$step:" "$PROGRESS_FILE" + else + return 1 + fi +} + +# Функция пропуска уже выполненных этапов +check_and_skip() { + local step="$1" + local description="$2" + + if is_step_completed "$step"; then + log_info "⏭️ Пропускаем: $description (уже выполнено)" + return 0 + else + log "🔄 Выполняется: $description" + return 1 + fi +} + # Проверка аргументов if [ $# -lt 1 ]; then - echo -e "${RED}Использование: $0 [email]${NC}" + echo -e "${RED}Использование: $0 [node_type] [email]${NC}" echo "Примеры:" - echo " $0 my-uploader.example.com" - echo " $0 my-uploader.example.com admin@example.com" + echo " $0 my-uploader.example.com regular" + echo " $0 my-uploader.example.com main admin@example.com" + echo "" + echo "Типы нод:" + echo " main - Основная нода (с web2-client, полные возможности)" + echo " regular - Обычная нода (только uploader-bot + converter-module)" exit 1 fi -DOMAIN="$1" -EMAIL="${2:-admin@${DOMAIN}}" -PROJECT_DIR="/home/myuploader" -SERVICE_USER="myuploader" - -# Репозитории для клонирования -UPLOADER_REPO="https://git.projscale.dev/my-dev/uploader-bot" -CONVERTER_REPO="https://git.projscale.dev/my-dev/converter-module" -WEB2_REPO="https://git.projscale.dev/my-dev/web2-client" +# Создание директории прогресса +mkdir -p "$(dirname "$PROGRESS_FILE")" +touch "$PROGRESS_FILE" echo "" echo -e "${PURPLE}================================================${NC}" echo -e "${WHITE}🚀 MY UPLOADER BOT - PRODUCTION SETUP${NC}" echo -e "${PURPLE}================================================${NC}" echo -e "${CYAN}Домен:${NC} $DOMAIN" +echo -e "${CYAN}Тип ноды:${NC} $NODE_TYPE" echo -e "${CYAN}Email:${NC} $EMAIL" echo -e "${CYAN}Пользователь:${NC} $SERVICE_USER" echo -e "${CYAN}Директория:${NC} $PROJECT_DIR" +echo -e "${CYAN}Прогресс файл:${NC} $PROGRESS_FILE" echo "" echo -e "${CYAN}Репозитории:${NC}" echo -e " 📦 Uploader Bot: $UPLOADER_REPO" echo -e " 🔄 Converter: $CONVERTER_REPO" -echo -e " 🌐 Web2 Client: $WEB2_REPO" +if [ "$NODE_TYPE" = "main" ]; then + echo -e " 🌐 Web2 Client: $WEB2_REPO" +else + echo -e " 🌐 Web2 Client: ${YELLOW}пропускается (обычная нода)${NC}" +fi echo -e "${PURPLE}================================================${NC}" echo "" @@ -92,37 +141,39 @@ echo -e "${WHITE} 🔄 ШАГ 1: ОБНОВЛЕНИЕ СИСТЕМЫ${NC}" echo -e "${BLUE}==========================================${NC}" echo "" -log "Обновление списка пакетов..." -apt update -y +if ! check_and_skip "system_update" "Обновление системы"; then + log "Обновление списка пакетов..." + apt update -y -log "Обновление системы..." -apt upgrade -y + log "Обновление системы..." + apt upgrade -y -log "Установка базовых пакетов..." -apt install -y \ - curl \ - wget \ - git \ - unzip \ - htop \ - nano \ - vim \ - tree \ - jq \ - software-properties-common \ - apt-transport-https \ - ca-certificates \ - gnupg \ - lsb-release \ - ufw \ - fail2ban \ - netstat-nat \ - python3 \ - python3-pip \ - nodejs \ - npm + log "Установка базовых пакетов..." + apt install -y \ + curl \ + wget \ + git \ + unzip \ + htop \ + nano \ + vim \ + tree \ + jq \ + software-properties-common \ + apt-transport-https \ + ca-certificates \ + gnupg \ + lsb-release \ + ufw \ + fail2ban \ + netstat-nat \ + python3 \ + python3-pip \ + nodejs \ + npm -log_success "Система обновлена" + log_progress "system_update" +fi echo "" echo -e "${BLUE}==========================================${NC}" @@ -130,40 +181,42 @@ echo -e "${WHITE} 🐳 ШАГ 2: УСТАНОВКА DOCKER${NC}" echo -e "${BLUE}==========================================${NC}" echo "" -# Удаление старых версий Docker -log "Удаление старых версий Docker..." -apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null || true +if ! check_and_skip "docker_install" "Установка Docker"; then + # Удаление старых версий Docker + log "Удаление старых версий Docker..." + apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null || true -# Установка Docker -log "Добавление Docker GPG ключа..." -curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + # Установка Docker + log "Добавление Docker GPG ключа..." + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg -log "Добавление Docker репозитория..." -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + log "Добавление Docker репозитория..." + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null -log "Обновление списка пакетов..." -apt update -y + log "Обновление списка пакетов..." + apt update -y -log "Установка Docker..." -apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + log "Установка Docker..." + apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -# Установка Docker Compose standalone -log "Установка Docker Compose..." -DOCKER_COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4) -curl -L "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose -chmod +x /usr/local/bin/docker-compose + # Установка Docker Compose standalone + log "Установка Docker Compose..." + DOCKER_COMPOSE_VERSION=$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep 'tag_name' | cut -d\" -f4) + curl -L "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose -# Запуск Docker -log "Запуск Docker..." -systemctl start docker -systemctl enable docker + # Запуск Docker + log "Запуск Docker..." + systemctl start docker + systemctl enable docker -# Проверка Docker -log "Проверка Docker..." -docker --version -docker-compose --version + # Проверка Docker + log "Проверка Docker..." + docker --version + docker-compose --version -log_success "Docker установлен" + log_progress "docker_install" +fi echo "" echo -e "${BLUE}==========================================${NC}" @@ -171,15 +224,17 @@ echo -e "${WHITE} 🌐 ШАГ 3: УСТАНОВКА NGINX${NC}" echo -e "${BLUE}==========================================${NC}" echo "" -log "Установка Nginx..." -apt install -y nginx +if ! check_and_skip "nginx_install" "Установка Nginx"; then + log "Установка Nginx..." + apt install -y nginx -log "Запуск Nginx..." -systemctl start nginx -systemctl enable nginx + log "Запуск Nginx..." + systemctl start nginx + systemctl enable nginx -log "Nginx версия: $(nginx -v 2>&1)" -log_success "Nginx установлен" + log "Nginx версия: $(nginx -v 2>&1)" + log_progress "nginx_install" +fi echo "" echo -e "${BLUE}==========================================${NC}" @@ -187,18 +242,20 @@ echo -e "${WHITE} 🔐 ШАГ 4: УСТАНОВКА CERTBOT${NC}" echo -e "${BLUE}==========================================${NC}" echo "" -log "Установка snapd (если нужно)..." -apt install -y snapd +if ! check_and_skip "certbot_install" "Установка Certbot"; then + log "Установка snapd (если нужно)..." + apt install -y snapd -log "Установка Certbot через snap..." -snap install core; snap refresh core -snap install --classic certbot + log "Установка Certbot через snap..." + snap install core; snap refresh core + snap install --classic certbot -log "Создание симлинка для certbot..." -ln -sf /snap/bin/certbot /usr/bin/certbot + log "Создание симлинка для certbot..." + ln -sf /snap/bin/certbot /usr/bin/certbot -log "Certbot версия: $(certbot --version)" -log_success "Certbot установлен" + log "Certbot версия: $(certbot --version)" + log_progress "certbot_install" +fi echo "" echo -e "${BLUE}==========================================${NC}" @@ -206,15 +263,18 @@ echo -e "${WHITE} 👤 ШАГ 5: СОЗДАНИЕ ПОЛЬЗОВАТЕЛЯ СЕ echo -e "${BLUE}==========================================${NC}" echo "" -log "Создание пользователя $SERVICE_USER..." -if ! id "$SERVICE_USER" &>/dev/null; then - useradd -m -s /bin/bash "$SERVICE_USER" - usermod -aG sudo "$SERVICE_USER" - usermod -aG docker "$SERVICE_USER" - log_success "Пользователь $SERVICE_USER создан" -else - usermod -aG docker "$SERVICE_USER" - log_info "Пользователь $SERVICE_USER уже существует" +if ! check_and_skip "service_user" "Создание пользователя сервиса"; then + log "Создание пользователя $SERVICE_USER..." + if ! id "$SERVICE_USER" &>/dev/null; then + useradd -m -s /bin/bash "$SERVICE_USER" + usermod -aG sudo "$SERVICE_USER" + usermod -aG docker "$SERVICE_USER" + log_success "Пользователь $SERVICE_USER создан" + else + usermod -aG docker "$SERVICE_USER" + log_info "Пользователь $SERVICE_USER уже существует" + fi + log_progress "service_user" fi echo "" @@ -223,40 +283,49 @@ echo -e "${WHITE} 📂 ШАГ 6: КЛОНИРОВАНИЕ РЕПОЗИТОРИЕ echo -e "${BLUE}==========================================${NC}" echo "" -log "Создание директории проекта..." -mkdir -p "$PROJECT_DIR" -chown "$SERVICE_USER:$SERVICE_USER" "$PROJECT_DIR" +if ! check_and_skip "repositories_clone" "Клонирование репозиториев"; then + log "Создание директории проекта..." + mkdir -p "$PROJECT_DIR" + chown "$SERVICE_USER:$SERVICE_USER" "$PROJECT_DIR" -# Переключение на пользователя myuploader для клонирования -cd "$PROJECT_DIR" + # Переключение на пользователя myuploader для клонирования + cd "$PROJECT_DIR" -log "Клонирование uploader-bot..." -if [ -d "uploader-bot" ]; then - log_warning "Директория uploader-bot уже существует, удаляем..." - rm -rf uploader-bot + log "Клонирование uploader-bot..." + if [ -d "uploader-bot" ]; then + log_warning "Директория uploader-bot уже существует, удаляем..." + rm -rf uploader-bot + fi + sudo -u "$SERVICE_USER" git clone "$UPLOADER_REPO" uploader-bot + log_success "uploader-bot склонирован" + + log "Клонирование converter-module..." + if [ -d "converter-module" ]; then + log_warning "Директория converter-module уже существует, удаляем..." + rm -rf converter-module + fi + sudo -u "$SERVICE_USER" git clone "$CONVERTER_REPO" converter-module + log_success "converter-module склонирован" + + # Клонирование web2-client только для основной ноды + if [ "$NODE_TYPE" = "main" ]; then + log "Клонирование web2-client..." + if [ -d "web2-client" ]; then + log_warning "Директория web2-client уже существует, удаляем..." + rm -rf web2-client + fi + sudo -u "$SERVICE_USER" git clone "$WEB2_REPO" web2-client + log_success "web2-client склонирован" + else + log_info "⏭️ Пропускаем web2-client (обычная нода)" + fi + + # Проверка структуры + log "Структура проекта:" + sudo -u "$SERVICE_USER" tree -L 2 "$PROJECT_DIR" || ls -la "$PROJECT_DIR" + + log_progress "repositories_clone" fi -sudo -u "$SERVICE_USER" git clone "$UPLOADER_REPO" uploader-bot -log_success "uploader-bot склонирован" - -log "Клонирование converter-module..." -if [ -d "converter-module" ]; then - log_warning "Директория converter-module уже существует, удаляем..." - rm -rf converter-module -fi -sudo -u "$SERVICE_USER" git clone "$CONVERTER_REPO" converter-module -log_success "converter-module склонирован" - -log "Клонирование web2-client..." -if [ -d "web2-client" ]; then - log_warning "Директория web2-client уже существует, удаляем..." - rm -rf web2-client -fi -sudo -u "$SERVICE_USER" git clone "$WEB2_REPO" web2-client -log_success "web2-client склонирован" - -# Проверка структуры -log "Структура проекта:" -sudo -u "$SERVICE_USER" tree -L 2 "$PROJECT_DIR" || ls -la "$PROJECT_DIR" echo "" echo -e "${BLUE}==========================================${NC}" @@ -264,25 +333,29 @@ echo -e "${WHITE} ⚙️ ШАГ 7: НАСТРОЙКА ОКРУЖЕНИЯ${NC}" echo -e "${BLUE}==========================================${NC}" echo "" -# Переход в основную директорию uploader-bot -MAIN_PROJECT_DIR="$PROJECT_DIR/uploader-bot" -cd "$MAIN_PROJECT_DIR" +if ! check_and_skip "environment_setup" "Настройка окружения"; then + # Переход в основную директорию uploader-bot + MAIN_PROJECT_DIR="$PROJECT_DIR/uploader-bot" + cd "$MAIN_PROJECT_DIR" -log "Генерация .env файла для uploader-bot..." + log "Генерация .env файла для uploader-bot..." -# Генерация случайных ключей -SECRET_KEY=$(openssl rand -hex 32) -JWT_SECRET=$(openssl rand -hex 32) -ENCRYPTION_KEY=$(openssl rand -hex 16) -POSTGRES_PASSWORD=$(openssl rand -hex 16) -NODE_ID="production-node-$(date +%s)" + # Генерация случайных ключей + SECRET_KEY=$(openssl rand -hex 32) + JWT_SECRET=$(openssl rand -hex 32) + ENCRYPTION_KEY=$(openssl rand -hex 16) + POSTGRES_PASSWORD=$(openssl rand -hex 16) + NODE_ID="$NODE_TYPE-node-$(date +%s)" -# Создание .env файла -cat > "$MAIN_PROJECT_DIR/.env" << EOF + # Создание .env файла + cat > "$MAIN_PROJECT_DIR/.env" << EOF # MY UPLOADER BOT - PRODUCTION CONFIGURATION NODE_ENV=production DEBUG=false +# Node Configuration +NODE_TYPE=$NODE_TYPE + # Domain MY_NETWORK_DOMAIN=$DOMAIN @@ -331,16 +404,21 @@ LOG_FORMAT=json GRAFANA_PASSWORD=admin123 EOF -chown "$SERVICE_USER:$SERVICE_USER" "$MAIN_PROJECT_DIR/.env" -log_success "Файл .env создан" + chown "$SERVICE_USER:$SERVICE_USER" "$MAIN_PROJECT_DIR/.env" + log_success "Файл .env создан" -# Создание симлинков для модулей в uploader-bot -log "Создание симлинков для модулей..." -sudo -u "$SERVICE_USER" mkdir -p "$MAIN_PROJECT_DIR/modules" -sudo -u "$SERVICE_USER" ln -sf "$PROJECT_DIR/converter-module" "$MAIN_PROJECT_DIR/modules/converter-module" -sudo -u "$SERVICE_USER" ln -sf "$PROJECT_DIR/web2-client" "$MAIN_PROJECT_DIR/modules/web2-client" + # Создание симлинков для модулей в uploader-bot + log "Создание симлинков для модулей..." + sudo -u "$SERVICE_USER" mkdir -p "$MAIN_PROJECT_DIR/modules" + sudo -u "$SERVICE_USER" ln -sf "$PROJECT_DIR/converter-module" "$MAIN_PROJECT_DIR/modules/converter-module" + + if [ "$NODE_TYPE" = "main" ]; then + sudo -u "$SERVICE_USER" ln -sf "$PROJECT_DIR/web2-client" "$MAIN_PROJECT_DIR/modules/web2-client" + fi -log_success "Симлинки созданы" + log_success "Симлинки созданы" + log_progress "environment_setup" +fi echo "" echo -e "${BLUE}==========================================${NC}" @@ -348,13 +426,29 @@ echo -e "${WHITE} 🌐 ШАГ 8: НАСТРОЙКА NGINX${NC}" echo -e "${BLUE}==========================================${NC}" echo "" -log "Создание Nginx конфигурации..." +if ! check_and_skip "nginx_config" "Настройка Nginx"; then + log "Настройка Nginx конфигурации..." -# Создание базового nginx конфига -mkdir -p /etc/nginx/sites-available -mkdir -p /etc/nginx/sites-enabled + # Сначала добавляем rate limiting в основной конфиг nginx + log "Добавление rate limiting в nginx.conf..." -cat > "/etc/nginx/sites-available/$DOMAIN" << EOF + # Создаем backup оригинального конфига + cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.backup + + # Добавляем rate limiting в блок http + if ! grep -q "limit_req_zone" /etc/nginx/nginx.conf; then + sed -i '/http {/a\\n\t# Rate limiting zones\n\tlimit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;\n\tlimit_req_zone $binary_remote_addr zone=upload:10m rate=5r/s;\n' /etc/nginx/nginx.conf + fi + + log "Создание site конфигурации для типа ноды: $NODE_TYPE..." + + # Создание базового nginx конфига для сайта + mkdir -p /etc/nginx/sites-available + mkdir -p /etc/nginx/sites-enabled + + if [ "$NODE_TYPE" = "main" ]; then + # Конфигурация для основной ноды (с web2-client) + cat > "/etc/nginx/sites-available/$DOMAIN" << EOF server { listen 80; server_name $DOMAIN; @@ -375,13 +469,25 @@ server { add_header X-XSS-Protection "1; mode=block"; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; - # Rate limiting - limit_req_zone \$binary_remote_addr zone=api:10m rate=10r/s; - limit_req zone=api burst=20 nodelay; - - # Main API (uploader-bot) + # Web2 Client (main page for main nodes) location / { - proxy_pass http://127.0.0.1:15100; + limit_req zone=api burst=20 nodelay; + + proxy_pass http://127.0.0.1:3000/; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + } + + # API endpoints + location /api/ { + limit_req zone=api burst=20 nodelay; + + proxy_pass http://127.0.0.1:15100/; proxy_http_version 1.1; proxy_set_header Upgrade \$http_upgrade; proxy_set_header Connection 'upgrade'; @@ -394,27 +500,34 @@ server { proxy_connect_timeout 75s; } - # Web2 Client - location /web/ { - proxy_pass http://127.0.0.1:3000/; + # Upload endpoint with stricter rate limiting + location /api/upload { + limit_req zone=upload burst=10 nodelay; + + proxy_pass http://127.0.0.1:15100/upload; proxy_http_version 1.1; - proxy_set_header Upgrade \$http_upgrade; - proxy_set_header Connection 'upgrade'; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; + proxy_read_timeout 600s; + proxy_send_timeout 600s; + client_max_body_size 100M; } # Converter API (on-demand, через uploader-bot) location /converter/ { + limit_req zone=upload burst=10 nodelay; + proxy_pass http://127.0.0.1:15100/api/convert/; proxy_http_version 1.1; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto \$scheme; - proxy_read_timeout 600s; # Увеличенный таймаут для конвертации + proxy_read_timeout 600s; + proxy_send_timeout 600s; + client_max_body_size 100M; } # Health check (no rate limiting) @@ -429,22 +542,127 @@ server { expires 30d; add_header Cache-Control "public, immutable"; } + + # Disable access to hidden files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } } EOF + else + # Конфигурация для обычной ноды (только API) + cat > "/etc/nginx/sites-available/$DOMAIN" << EOF +server { + listen 80; + server_name $DOMAIN; + + # Redirect all HTTP requests to HTTPS + return 301 https://\$server_name\$request_uri; +} -# Активация сайта -ln -sf "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/" +server { + listen 443 ssl http2; + server_name $DOMAIN; + + # SSL configuration will be added by certbot + + # Security headers + add_header X-Frame-Options DENY; + add_header X-Content-Type-Options nosniff; + add_header X-XSS-Protection "1; mode=block"; + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"; + + # Main API (uploader-bot) + location / { + limit_req zone=api burst=20 nodelay; + + proxy_pass http://127.0.0.1:15100; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_cache_bypass \$http_upgrade; + proxy_read_timeout 300s; + proxy_connect_timeout 75s; + } + + # Upload endpoint with stricter rate limiting + location /upload { + limit_req zone=upload burst=10 nodelay; + + proxy_pass http://127.0.0.1:15100/upload; + proxy_http_version 1.1; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_read_timeout 600s; + proxy_send_timeout 600s; + client_max_body_size 100M; + } + + # Converter API (on-demand, через uploader-bot) + location /converter/ { + limit_req zone=upload burst=10 nodelay; + + proxy_pass http://127.0.0.1:15100/api/convert/; + proxy_http_version 1.1; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto \$scheme; + proxy_read_timeout 600s; + proxy_send_timeout 600s; + client_max_body_size 100M; + } + + # Health check (no rate limiting) + location /health { + proxy_pass http://127.0.0.1:15100/health; + access_log off; + } + + # Static files + location /static/ { + alias $MAIN_PROJECT_DIR/static/; + expires 30d; + add_header Cache-Control "public, immutable"; + } + + # Disable access to hidden files + location ~ /\. { + deny all; + access_log off; + log_not_found off; + } +} +EOF + fi -# Удаление дефолтного сайта -rm -f /etc/nginx/sites-enabled/default + # Активация сайта + ln -sf "/etc/nginx/sites-available/$DOMAIN" "/etc/nginx/sites-enabled/" -# Проверка конфигурации -if nginx -t; then - systemctl reload nginx - log_success "Nginx конфигурация создана" -else - log_error "Ошибка в конфигурации Nginx" - exit 1 + # Удаление дефолтного сайта + rm -f /etc/nginx/sites-enabled/default + + # Проверка конфигурации + log "Проверка конфигурации Nginx..." + if nginx -t; then + systemctl reload nginx + log_success "Nginx конфигурация создана и применена" + else + log_error "Ошибка в конфигурации Nginx" + # Показываем конкретную ошибку + nginx -t + exit 1 + fi + + log_progress "nginx_config" fi echo "" @@ -453,25 +671,28 @@ echo -e "${WHITE} 🔐 ШАГ 9: ПОЛУЧЕНИЕ SSL СЕРТИФИКАТА${ echo -e "${BLUE}==========================================${NC}" echo "" -log "Получение SSL сертификата для $DOMAIN..." +if ! check_and_skip "ssl_certificate" "Получение SSL сертификата"; then + log "Получение SSL сертификата для $DOMAIN..." -# Проверка что домен указывает на этот сервер -DOMAIN_IP=$(dig +short "$DOMAIN" | tail -n1) -SERVER_IP=$(curl -s ifconfig.me || curl -s ipinfo.io/ip) + # Проверка что домен указывает на этот сервер + DOMAIN_IP=$(dig +short "$DOMAIN" | tail -n1) + SERVER_IP=$(curl -s ifconfig.me || curl -s ipinfo.io/ip) -if [ "$DOMAIN_IP" != "$SERVER_IP" ]; then - log_warning "Домен $DOMAIN не указывает на этот сервер" - log_warning "Домен IP: $DOMAIN_IP, Сервер IP: $SERVER_IP" - log_warning "Попытка получения сертификата может не удаться" -fi + if [ "$DOMAIN_IP" != "$SERVER_IP" ]; then + log_warning "Домен $DOMAIN не указывает на этот сервер" + log_warning "Домен IP: $DOMAIN_IP, Сервер IP: $SERVER_IP" + log_warning "Попытка получения сертификата может не удаться" + fi -# Получение SSL сертификата -if certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --email "$EMAIL" --redirect; then - log_success "SSL сертификат получен" -else - log_error "Не удалось получить SSL сертификат" - log_warning "Можете получить его позже командой:" - log_warning "sudo certbot --nginx -d $DOMAIN" + # Получение SSL сертификата + if certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --email "$EMAIL" --redirect; then + log_success "SSL сертификат получен" + log_progress "ssl_certificate" + else + log_error "Не удалось получить SSL сертификат" + log_warning "Можете получить его позже командой:" + log_warning "sudo certbot --nginx -d $DOMAIN" + fi fi echo "" @@ -480,37 +701,38 @@ echo -e "${WHITE} 🔒 ШАГ 10: НАСТРОЙКА БЕЗОПАСНОСТИ${N echo -e "${BLUE}==========================================${NC}" echo "" -log "Настройка UFW firewall..." +if ! check_and_skip "security_setup" "Настройка безопасности"; then + log "Настройка UFW firewall..." -# Сброс UFW -ufw --force reset + # Сброс UFW + ufw --force reset -# Базовые правила -ufw default deny incoming -ufw default allow outgoing + # Базовые правила + ufw default deny incoming + ufw default allow outgoing -# Разрешение SSH -ufw allow ssh + # Разрешение SSH + ufw allow ssh -# Разрешение HTTP/HTTPS -ufw allow 80/tcp -ufw allow 443/tcp + # Разрешение HTTP/HTTPS + ufw allow 80/tcp + ufw allow 443/tcp -# Включение UFW -ufw --force enable + # Включение UFW + ufw --force enable -log "Настройка Fail2Ban..." + log "Настройка Fail2Ban..." -# Создание кастомного фильтра для nginx -cat > /etc/fail2ban/filter.d/nginx-my-uploader.conf << 'EOF' + # Создание кастомного фильтра для nginx + cat > /etc/fail2ban/filter.d/nginx-my-uploader.conf << 'EOF' [Definition] failregex = ^ -.*"(GET|POST|HEAD).*" (4\d\d|5\d\d) .*$ ^ -.*".*" (4\d\d|5\d\d) .*$ ignoreregex = EOF -# Конфигурация jail для MY Uploader -cat > /etc/fail2ban/jail.d/my-uploader.conf << 'EOF' + # Конфигурация jail для MY Uploader + cat > /etc/fail2ban/jail.d/my-uploader.conf << 'EOF' [nginx-my-uploader] enabled = true port = http,https @@ -522,11 +744,13 @@ bantime = 3600 action = iptables-multiport[name=nginx-my-uploader, port="http,https", protocol=tcp] EOF -# Перезапуск Fail2Ban -systemctl restart fail2ban -systemctl enable fail2ban + # Перезапуск Fail2Ban + systemctl restart fail2ban + systemctl enable fail2ban -log_success "Безопасность настроена" + log_success "Безопасность настроена" + log_progress "security_setup" +fi echo "" echo -e "${BLUE}==========================================${NC}" @@ -534,96 +758,125 @@ echo -e "${WHITE} 🐳 ШАГ 11: СБОРКА И ЗАПУСК${NC}" echo -e "${BLUE}==========================================${NC}" echo "" -cd "$MAIN_PROJECT_DIR" +if ! check_and_skip "docker_build" "Сборка и запуск"; then + MAIN_PROJECT_DIR="$PROJECT_DIR/uploader-bot" + cd "$MAIN_PROJECT_DIR" -log "Сборка Docker образов..." + log "Сборка Docker образов..." -# Проверяем наличие docker-compose файлов -if [ -f "docker-compose.production.yml" ]; then - COMPOSE_FILE="docker-compose.production.yml" -elif [ -f "docker-compose.yml" ]; then - COMPOSE_FILE="docker-compose.yml" -else - log_error "Не найден docker-compose файл" - exit 1 + # Проверяем наличие docker-compose файлов + if [ -f "docker-compose.production.yml" ]; then + COMPOSE_FILE="docker-compose.production.yml" + elif [ -f "docker-compose.yml" ]; then + COMPOSE_FILE="docker-compose.yml" + else + log_error "Не найден docker-compose файл" + exit 1 + fi + + log "Используется $COMPOSE_FILE" + + # Сборка образов от имени пользователя сервиса + log "Сборка основного приложения..." + sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" build + + # Сборка converter image (если есть профиль build-only) + log "Сборка converter image..." + sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" --profile build-only build converter-build 2>/dev/null || \ + sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" build converter 2>/dev/null || \ + log_warning "Converter build пропущен (нет соответствующего сервиса)" + + log "Запуск приложения..." + if [ "$NODE_TYPE" = "main" ]; then + # Для основной ноды запускаем все сервисы + sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" up -d + log "🚀 Запущены все сервисы для основной ноды" + else + # Для обычной ноды запускаем только необходимые сервисы + sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" up -d uploader-bot converter-builder watchtower 2>/dev/null || \ + sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" up -d + log "🚀 Запущены основные сервисы для обычной ноды" + fi + + # Ожидание запуска + log "Ожидание запуска сервисов..." + sleep 15 + + # Проверка статуса + log "Проверка статуса контейнеров..." + sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" ps + + log_success "Приложение запущено" + log_progress "docker_build" fi -log "Используется $COMPOSE_FILE" - -# Сборка образов от имени пользователя сервиса -log "Сборка основного приложения..." -sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" build - -# Сборка converter image (если есть профиль build-only) -log "Сборка converter image..." -sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" --profile build-only build converter-build 2>/dev/null || \ -sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" build converter 2>/dev/null || \ -log_warning "Converter build пропущен (нет соответствующего сервиса)" - -log "Запуск приложения..." -sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" up -d - -# Ожидание запуска -log "Ожидание запуска сервисов..." -sleep 15 - -# Проверка статуса -log "Проверка статуса контейнеров..." -sudo -u "$SERVICE_USER" docker-compose -f "$COMPOSE_FILE" ps - -log_success "Приложение запущено" - echo "" echo -e "${BLUE}==========================================${NC}" echo -e "${WHITE} 🏁 ШАГ 12: ФИНАЛЬНАЯ ПРОВЕРКА${NC}" echo -e "${BLUE}==========================================${NC}" echo "" -log "Проверка доступности сервисов..." +if ! check_and_skip "final_check" "Финальная проверка"; then + MAIN_PROJECT_DIR="$PROJECT_DIR/uploader-bot" + + log "Проверка доступности сервисов..." -# Проверка локального API -if curl -f -s http://localhost:15100/health > /dev/null; then - log_success "Локальный API доступен" -else - log_error "Локальный API недоступен" -fi + # Проверка локального API + if curl -f -s http://localhost:15100/health > /dev/null; then + log_success "Локальный API доступен" + else + log_error "Локальный API недоступен" + fi -# Проверка web2-client -if curl -f -s http://localhost:3000/health > /dev/null 2>&1; then - log_success "Web2-client доступен" -else - log_warning "Web2-client может быть недоступен" -fi + # Проверка web2-client только для основной ноды + if [ "$NODE_TYPE" = "main" ]; then + if curl -f -s http://localhost:3000/health > /dev/null 2>&1; then + log_success "Web2-client доступен" + else + log_warning "Web2-client может быть недоступен" + fi + fi -# Проверка HTTPS API -if curl -f -s "https://$DOMAIN/health" > /dev/null; then - log_success "HTTPS API доступен" -else - log_warning "HTTPS API пока недоступен (может потребоваться время)" -fi + # Проверка HTTPS API + if curl -f -s "https://$DOMAIN/health" > /dev/null; then + log_success "HTTPS API доступен" + else + log_warning "HTTPS API пока недоступен (может потребоваться время)" + fi -# Создание управляющих скриптов -log "Создание управляющих скриптов..." + # Создание управляющих скриптов + log "Создание управляющих скриптов..." + + # Определяем какой compose file использовать + if [ -f "$MAIN_PROJECT_DIR/docker-compose.production.yml" ]; then + COMPOSE_FILE="docker-compose.production.yml" + else + COMPOSE_FILE="docker-compose.yml" + fi -cat > "$MAIN_PROJECT_DIR/start.sh" << EOF + cat > "$MAIN_PROJECT_DIR/start.sh" << EOF #!/bin/bash cd $MAIN_PROJECT_DIR -docker-compose -f $COMPOSE_FILE up -d -echo "✅ MY Uploader Bot запущен" +if [ "$NODE_TYPE" = "main" ]; then + docker-compose -f $COMPOSE_FILE up -d +else + docker-compose -f $COMPOSE_FILE up -d uploader-bot converter-builder watchtower 2>/dev/null || docker-compose -f $COMPOSE_FILE up -d +fi +echo "✅ MY Uploader Bot запущен (тип ноды: $NODE_TYPE)" docker-compose -f $COMPOSE_FILE ps EOF -cat > "$MAIN_PROJECT_DIR/stop.sh" << EOF + cat > "$MAIN_PROJECT_DIR/stop.sh" << EOF #!/bin/bash cd $MAIN_PROJECT_DIR docker-compose -f $COMPOSE_FILE down echo "🛑 MY Uploader Bot остановлен" EOF -cat > "$MAIN_PROJECT_DIR/status.sh" << EOF + cat > "$MAIN_PROJECT_DIR/status.sh" << EOF #!/bin/bash cd $MAIN_PROJECT_DIR -echo "📊 Статус MY Uploader Bot:" +echo "📊 Статус MY Uploader Bot (тип ноды: $NODE_TYPE):" docker-compose -f $COMPOSE_FILE ps echo "" echo "🌐 API статус:" @@ -631,34 +884,49 @@ curl -s http://localhost:15100/health | jq . 2>/dev/null || echo "API недос echo "" echo "🔒 SSL сертификат:" sudo certbot certificates | grep -A2 -B2 $DOMAIN || echo "Нет SSL сертификата" +echo "" +echo "📈 Прогресс установки:" +cat $PROGRESS_FILE 2>/dev/null || echo "Файл прогресса не найден" EOF -cat > "$MAIN_PROJECT_DIR/logs.sh" << EOF + cat > "$MAIN_PROJECT_DIR/logs.sh" << EOF #!/bin/bash cd $MAIN_PROJECT_DIR docker-compose -f $COMPOSE_FILE logs -f EOF -cat > "$MAIN_PROJECT_DIR/rebuild.sh" << EOF + cat > "$MAIN_PROJECT_DIR/rebuild.sh" << EOF #!/bin/bash cd $MAIN_PROJECT_DIR echo "🔄 Пересборка и перезапуск..." docker-compose -f $COMPOSE_FILE down docker-compose -f $COMPOSE_FILE build -docker-compose -f $COMPOSE_FILE up -d +if [ "$NODE_TYPE" = "main" ]; then + docker-compose -f $COMPOSE_FILE up -d +else + docker-compose -f $COMPOSE_FILE up -d uploader-bot converter-builder watchtower 2>/dev/null || docker-compose -f $COMPOSE_FILE up -d +fi echo "✅ Пересборка завершена" docker-compose -f $COMPOSE_FILE ps EOF -chmod +x "$MAIN_PROJECT_DIR"/{start,stop,status,logs,rebuild}.sh -chown "$SERVICE_USER:$SERVICE_USER" "$MAIN_PROJECT_DIR"/{start,stop,status,logs,rebuild}.sh + chmod +x "$MAIN_PROJECT_DIR"/{start,stop,status,logs,rebuild}.sh + chown "$SERVICE_USER:$SERVICE_USER" "$MAIN_PROJECT_DIR"/{start,stop,status,logs,rebuild}.sh -# Настройка автозапуска -log "Настройка автозапуска..." + # Настройка автозапуска + log "Настройка автозапуска..." -cat > /etc/systemd/system/my-uploader-bot.service << EOF + if [ "$NODE_TYPE" = "main" ]; then + SERVICE_EXEC_START="/usr/local/bin/docker-compose -f $COMPOSE_FILE up -d" + SERVICE_DESCRIPTION="MY Uploader Bot (Main Node)" + else + SERVICE_EXEC_START="/usr/local/bin/docker-compose -f $COMPOSE_FILE up -d uploader-bot converter-builder watchtower" + SERVICE_DESCRIPTION="MY Uploader Bot (Regular Node)" + fi + + cat > /etc/systemd/system/my-uploader-bot.service << EOF [Unit] -Description=MY Uploader Bot +Description=$SERVICE_DESCRIPTION Requires=docker.service After=docker.service @@ -668,7 +936,7 @@ RemainAfterExit=yes User=$SERVICE_USER Group=$SERVICE_USER WorkingDirectory=$MAIN_PROJECT_DIR -ExecStart=/usr/local/bin/docker-compose -f $COMPOSE_FILE up -d +ExecStart=$SERVICE_EXEC_START ExecStop=/usr/local/bin/docker-compose -f $COMPOSE_FILE down TimeoutStartSec=300 @@ -676,10 +944,12 @@ TimeoutStartSec=300 WantedBy=multi-user.target EOF -systemctl daemon-reload -systemctl enable my-uploader-bot.service + systemctl daemon-reload + systemctl enable my-uploader-bot.service -log_success "Автозапуск настроен" + log_success "Автозапуск настроен" + log_progress "final_check" +fi echo "" echo -e "${GREEN}================================================${NC}" @@ -687,19 +957,37 @@ echo -e "${WHITE}🎉 УСТАНОВКА ЗАВЕРШЕНА УСПЕШНО!${NC} echo -e "${GREEN}================================================${NC}" echo "" echo -e "${CYAN}🌐 Домен:${NC} https://$DOMAIN" +echo -e "${CYAN}🏷️ Тип ноды:${NC} $NODE_TYPE" echo -e "${CYAN}📁 Проект:${NC} $MAIN_PROJECT_DIR" echo -e "${CYAN}👤 Пользователь:${NC} $SERVICE_USER" +echo -e "${CYAN}📊 Прогресс:${NC} $PROGRESS_FILE" echo "" echo -e "${YELLOW}📦 Установленные модули:${NC}" echo -e " 🚀 Uploader Bot: $PROJECT_DIR/uploader-bot" echo -e " 🔄 Converter: $PROJECT_DIR/converter-module" -echo -e " 🌐 Web2 Client: $PROJECT_DIR/web2-client" +if [ "$NODE_TYPE" = "main" ]; then + echo -e " 🌐 Web2 Client: $PROJECT_DIR/web2-client" +else + echo -e " 🌐 Web2 Client: ${YELLOW}пропущен (обычная нода)${NC}" +fi echo "" -echo -e "${YELLOW}🌍 Доступные endpoints:${NC}" -echo -e " 📊 API: https://$DOMAIN/" -echo -e " 💻 Web Interface: https://$DOMAIN/web/" -echo -e " 🔄 Converter API: https://$DOMAIN/converter/" -echo -e " ❤️ Health Check: https://$DOMAIN/health" + +# Показываем разные endpoints в зависимости от типа ноды +if [ "$NODE_TYPE" = "main" ]; then + echo -e "${YELLOW}🌍 Доступные endpoints (основная нода):${NC}" + echo -e " 🌐 Web Interface: https://$DOMAIN/" + echo -e " 📊 API: https://$DOMAIN/api/" + echo -e " 📤 Upload: https://$DOMAIN/api/upload" + echo -e " 🔄 Converter API: https://$DOMAIN/converter/" + echo -e " ❤️ Health Check: https://$DOMAIN/health" +else + echo -e "${YELLOW}🌍 Доступные endpoints (обычная нода):${NC}" + echo -e " 📊 API: https://$DOMAIN/" + echo -e " 📤 Upload: https://$DOMAIN/upload" + echo -e " 🔄 Converter API: https://$DOMAIN/converter/" + echo -e " ❤️ Health Check: https://$DOMAIN/health" +fi + echo "" echo -e "${YELLOW}📋 Полезные команды:${NC}" echo -e "${WHITE}# Статус системы${NC}" @@ -722,6 +1010,12 @@ echo "sudo systemctl start my-uploader-bot" echo "sudo systemctl stop my-uploader-bot" echo "sudo systemctl status my-uploader-bot" echo "" +echo -e "${YELLOW}🔄 Повторная установка:${NC}" +echo "# Для полной переустановки:" +echo "sudo rm -f $PROGRESS_FILE" +echo "# Для сброса определенного этапа отредактируйте:" +echo "sudo nano $PROGRESS_FILE" +echo "" echo -e "${YELLOW}🌍 MY Network API:${NC}" echo "curl https://$DOMAIN/api/my/bootstrap/config" echo "" @@ -732,6 +1026,9 @@ echo "" echo -e "${GREEN}✅ MY Uploader Bot готов к работе!${NC}" echo "" +# Отметка полной установки +log_progress "installation_complete" + # Финальная проверка доступности log "Финальная проверка через 5 секунд..." sleep 5