From 6123d2d5549d441278846f3ea1a756a665d36070 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 16:22:18 +0300 Subject: [PATCH 1/5] fix: prevent network-manager auto-execution when sourced MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлена проверка BASH_SOURCE для определения способа запуска скрипта (прямой vs source). Это исправляет проблему, когда setup.sh показывал usage вместо интерактивных промптов. Changes: - Обернуть вызов main "$@" в условную проверку - При source - только загружаются функции без автовыполнения - При прямом запуске - выполняется main с аргументами Fixes: - setup.sh теперь запускается интерактивно без аргументов - network-manager.sh работает как standalone CLI - source в других скриптах не вызывает побочных эффектов 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- scripts/network-manager.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/network-manager.sh b/scripts/network-manager.sh index 5981901..a0a25f5 100755 --- a/scripts/network-manager.sh +++ b/scripts/network-manager.sh @@ -265,4 +265,7 @@ main() { esac } -main "$@" +# Execute main only when script is run directly (not when sourced) +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi From dfdc1eb15d48b86b2d899125431ba5e05254f0ed Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 20:23:10 +0300 Subject: [PATCH 2/5] fix: improve interactive prompts in network-manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправлена обработка пользовательского ввода и вывода информации в функциях list_available_networks и prompt_network_selection. Changes: - Перенаправлены info/success/warning сообщения в stderr (>&2) - list_available_networks теперь возвращает только число в stdout - Добавлена валидация что choice является числом перед сравнением - Добавлена проверка диапазона для choice (1 до max_choice) Fixes: - Исправлена ошибка "integer expression expected" при нечисловом вводе - Исправлена ошибка "syntax error: operand expected" - setup.sh теперь корректно ожидает пользовательский ввод 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- scripts/network-manager.sh | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/scripts/network-manager.sh b/scripts/network-manager.sh index a0a25f5..52b747d 100755 --- a/scripts/network-manager.sh +++ b/scripts/network-manager.sh @@ -57,13 +57,14 @@ detect_network_mode() { } list_available_networks() { - info "Listing available Docker networks..." + info "Listing available Docker networks..." >&2 local networks=$(docker network ls --format '{{.Name}}' | grep -v '^bridge$\|^host$\|^none$' || true) if [ -z "$networks" ]; then - warning "No custom Docker networks found" - echo "0. Create new isolated network" + warning "No custom Docker networks found" >&2 + echo "0. Create new isolated network" >&2 + echo "0" return 0 fi @@ -72,12 +73,12 @@ list_available_networks() { count=$((count + 1)) local subnet=$(docker network inspect "$network" --format '{{range .IPAM.Config}}{{.Subnet}}{{end}}' 2>/dev/null || echo "N/A") local driver=$(docker network inspect "$network" --format '{{.Driver}}' 2>/dev/null || echo "N/A") - echo "$count. $network ($subnet, $driver)" + echo "$count. $network ($subnet, $driver)" >&2 done <<< "$networks" - echo "$((count + 1)). Create new isolated network" + echo "$((count + 1)). Create new isolated network" >&2 - success "Found $count existing network(s)" + success "Found $count existing network(s)" >&2 echo "$count" } @@ -106,6 +107,16 @@ prompt_network_selection() { return 1 fi + if ! [[ "$choice" =~ ^[0-9]+$ ]]; then + error "Invalid choice: must be a number" + return 1 + fi + + if [ "$choice" -lt 1 ] || [ "$choice" -gt "$max_choice" ]; then + error "Invalid choice: must be between 1 and $max_choice" + return 1 + fi + if [ "$choice" -le "$count" ] && [ "$count" -gt 0 ]; then local selected=$(echo "$networks" | sed -n "${choice}p") echo "shared|$selected" From 7796d27e6b4f84280b9e190492e0a378e0a45b95 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 22:10:10 +0300 Subject: [PATCH 3/5] fix: prevent ufw-setup auto-execution and improve nginx prompts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправлена проблема с автовыполнением скриптов при source и улучшена обработка stdin/stdout в nginx-setup.sh. Changes: - Добавлена проверка BASH_SOURCE в ufw-setup.sh (строка 384) - Перенаправлены info/warning/error/success в stderr в detect_nginx_containers() - Перенаправлены все информационные сообщения в stderr в prompt_nginx_selection() Fixes: - setup.sh больше не запускает UFW конфигурацию автоматически - Nginx промпты теперь показываются корректно - Скрипт не зависает после "UFW Firewall Setup" - Пользователь видит все интерактивные промпты для выбора nginx Root Cause: - nginx-setup.sh делает source ufw-setup.sh (строка 10) - ufw-setup.sh автоматически вызывал main "$@" при source - prompt_nginx_selection() выводил info сообщения в stdout - command substitution захватывал весь вывод включая info сообщения 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- scripts/nginx-setup.sh | 38 +++++++++++++++++++------------------- scripts/ufw-setup.sh | 5 ++++- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/scripts/nginx-setup.sh b/scripts/nginx-setup.sh index 53ca25f..174739a 100755 --- a/scripts/nginx-setup.sh +++ b/scripts/nginx-setup.sh @@ -93,21 +93,21 @@ generate_nginx_config() { } detect_nginx_containers() { - info "Detecting nginx containers..." + info "Detecting nginx containers..." >&2 local nginx_containers=$(docker ps --format '{{.Names}}' | grep -i nginx || true) if [ -z "$nginx_containers" ]; then - info "No nginx containers found" + info "No nginx containers found" >&2 return 1 fi - info "Found nginx containers:" + info "Found nginx containers:" >&2 local count=0 while IFS= read -r container; do count=$((count + 1)) local network=$(docker inspect "$container" --format '{{range $net, $conf := .NetworkSettings.Networks}}{{$net}} {{end}}' 2>/dev/null | awk '{print $1}') - echo "$count. $container (network: $network)" + echo "$count. $container (network: $network)" >&2 done <<< "$nginx_containers" return 0 @@ -119,7 +119,7 @@ prompt_nginx_selection() { return 0 fi - echo "" + echo "" >&2 read -p "Use existing nginx container? [y/N]: " use_existing if [[ "$use_existing" =~ ^[Yy]$ ]]; then @@ -133,15 +133,15 @@ prompt_nginx_selection() { read -p "Enter nginx container name: " selected_nginx fi - echo "" - info "Detecting nginx config directory for: $selected_nginx" + echo "" >&2 + info "Detecting nginx config directory for: $selected_nginx" >&2 local detected_config_dir=$(docker inspect "$selected_nginx" \ --format '{{range .Mounts}}{{if eq .Destination "/etc/nginx"}}{{.Source}}{{end}}{{end}}' \ 2>/dev/null || echo "") if [ -n "$detected_config_dir" ]; then - info "Auto-detected config directory: $detected_config_dir" + info "Auto-detected config directory: $detected_config_dir" >&2 read -p "Use this directory? [Y/n]: " use_detected if [[ ! "$use_detected" =~ ^[Nn]$ ]]; then echo "${selected_nginx}|${detected_config_dir}" @@ -149,33 +149,33 @@ prompt_nginx_selection() { fi fi - echo "" - warning "Cannot auto-detect nginx config directory" - echo "Please specify the directory where nginx configs should be placed:" - echo " - For Docker nginx: volume mount path (e.g., /opt/nginx/conf.d)" - echo " - For systemd nginx: /etc/nginx/sites-available or /etc/nginx/conf.d" - echo "" + echo "" >&2 + warning "Cannot auto-detect nginx config directory" >&2 + echo "Please specify the directory where nginx configs should be placed:" >&2 + echo " - For Docker nginx: volume mount path (e.g., /opt/nginx/conf.d)" >&2 + echo " - For systemd nginx: /etc/nginx/sites-available or /etc/nginx/conf.d" >&2 + echo "" >&2 read -p "Nginx config directory: " config_dir if [ -z "$config_dir" ]; then - error "Config directory cannot be empty" + error "Config directory cannot be empty" >&2 echo "none||" return 1 fi if docker exec "$selected_nginx" test -d "$config_dir" 2>/dev/null; then - success "Directory verified: $config_dir" + success "Directory verified: $config_dir" >&2 echo "${selected_nginx}|${config_dir}" elif [ -d "$config_dir" ]; then - success "Directory verified: $config_dir" + success "Directory verified: $config_dir" >&2 echo "${selected_nginx}|${config_dir}" else - warning "Directory not found: $config_dir" + warning "Directory not found: $config_dir" >&2 read -p "Create this directory? [y/N]: " create_dir if [[ "$create_dir" =~ ^[Yy]$ ]]; then echo "${selected_nginx}|${config_dir}|CREATE" else - error "Cannot proceed without valid config directory" + error "Cannot proceed without valid config directory" >&2 echo "none||" return 1 fi diff --git a/scripts/ufw-setup.sh b/scripts/ufw-setup.sh index 9fd3782..51bf429 100755 --- a/scripts/ufw-setup.sh +++ b/scripts/ufw-setup.sh @@ -381,4 +381,7 @@ main() { fi } -main "$@" +# Execute main only when script is run directly (not when sourced) +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi From a4fdb9f3e506fa96b361c6d38aa282ecdf86f4a2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 22:20:03 +0300 Subject: [PATCH 4/5] fix: prevent ANSI codes in .env and improve final output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправлена критичная ошибка с ANSI escape кодами в .env файле и добавлен информативный итоговый вывод в конце setup.sh. Changes: - Перенаправлены все info/success/error в stderr в validate_subnet() - Перенаправлены все info/success/error в stderr в find_free_subnet() - Перенаправлены все info/success/error в stderr в create_network() - Исправлена редиректация в find_free_subnet(): &>/dev/null вместо 2>&1 >/dev/null - Заменен простой вывод в конце setup.sh на детальный summary Fixes: - .env файл больше не содержит ANSI escape кодов - deploy.sh не падает с ошибкой "command not found" - Пользователь видит полный summary с итоговыми параметрами - Показываются все конфигурационные детали: Network, Nginx, CouchDB, Domain, Backups Root Cause: - Функции validate_subnet/find_free_subnet/create_network выводили info в stdout - Command substitution $(find_free_subnet) захватывал весь stdout включая ANSI коды - ANSI коды попадали в переменную NETWORK_SUBNET - При записи в .env появлялись строки вида: $'36m[INFO]\E[0m' Impact: - Пользователь теперь видит красивый summary после завершения setup - Все параметры отображаются структурировано - Ясно указаны следующие шаги 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- scripts/network-manager.sh | 28 +++++++------- setup.sh | 79 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 18 deletions(-) diff --git a/scripts/network-manager.sh b/scripts/network-manager.sh index 52b747d..d9ab547 100755 --- a/scripts/network-manager.sh +++ b/scripts/network-manager.sh @@ -128,32 +128,32 @@ prompt_network_selection() { validate_subnet() { local subnet="$1" - info "Validating subnet: $subnet" + info "Validating subnet: $subnet" >&2 local used_subnets=$(docker network inspect $(docker network ls -q) --format '{{range .IPAM.Config}}{{.Subnet}}{{"\n"}}{{end}}' 2>/dev/null | grep -v '^$' || true) if echo "$used_subnets" | grep -q "^${subnet}$"; then - error "Subnet $subnet is already in use" + error "Subnet $subnet is already in use" >&2 return 1 fi - success "Subnet $subnet is available" + success "Subnet $subnet is available" >&2 return 0 } find_free_subnet() { - info "Searching for free subnet in range 172.24-31.0.0/16..." + info "Searching for free subnet in range 172.24-31.0.0/16..." >&2 for i in {24..31}; do local subnet="172.${i}.0.0/16" - if validate_subnet "$subnet" 2>/dev/null; then - success "Found free subnet: $subnet" + if validate_subnet "$subnet" &>/dev/null; then + success "Found free subnet: $subnet" >&2 echo "$subnet" return 0 fi done - error "No free subnets available in range 172.24-31.0.0/16" + error "No free subnets available in range 172.24-31.0.0/16" >&2 return 1 } @@ -162,32 +162,32 @@ create_network() { local subnet="${2:-}" if [ -z "$subnet" ]; then - info "Subnet not specified, auto-detecting..." + info "Subnet not specified, auto-detecting..." >&2 subnet=$(find_free_subnet) if [ $? -ne 0 ]; then - error "Failed to find free subnet" + error "Failed to find free subnet" >&2 return 1 fi fi local gateway=$(echo "$subnet" | sed 's|0/16|1|') - info "Creating Docker network: $network_name" - info " Subnet: $subnet" - info " Gateway: $gateway" + info "Creating Docker network: $network_name" >&2 + info " Subnet: $subnet" >&2 + info " Gateway: $gateway" >&2 if docker network create \ --driver bridge \ --subnet "$subnet" \ --gateway "$gateway" \ "$network_name" &> /dev/null; then - success "Network created successfully" + success "Network created successfully" >&2 echo "Network: $network_name" echo "Subnet: $subnet" echo "Gateway: $gateway" return 0 else - error "Failed to create network $network_name" + error "Failed to create network $network_name" >&2 return 1 fi } diff --git a/setup.sh b/setup.sh index 6355d97..114566c 100755 --- a/setup.sh +++ b/setup.sh @@ -611,11 +611,82 @@ main() { setup_backup_cron fi - success "Setup completed successfully!" echo "" - info "Next steps:" - echo " 1. Review configuration: cat $ENV_FILE" - echo " 2. Deploy notes: ./deploy.sh" + echo "" + success "╔════════════════════════════════════════════════════════════════════╗" + success "║ ✅ УСТАНОВКА ЗАВЕРШЕНА УСПЕШНО ║" + success "╚════════════════════════════════════════════════════════════════════╝" + echo "" + + echo "📋 ИТОГОВЫЕ ПАРАМЕТРЫ:" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo " 🌐 Network:" + echo " Mode: $NETWORK_MODE" + echo " Name: $NETWORK_NAME" + [ -n "$NETWORK_SUBNET" ] && echo " Subnet: $NETWORK_SUBNET" + echo "" + echo " 🔧 Nginx:" + echo " Container: $NGINX_CONTAINER_NAME" + echo " Config dir: $NGINX_CONFIG_DIR" + echo " Deploy own: $DEPLOY_OWN_NGINX" + echo "" + echo " 💾 CouchDB:" + echo " Container: $COUCHDB_CONTAINER_NAME" + echo " User: admin" + echo " Password: [generated - 64 chars]" + echo " Port: 5984 (localhost only)" + echo "" + echo " 🌍 Domain & SSL:" + echo " Domain: $NOTES_DOMAIN" + echo " Email: $CERTBOT_EMAIL" + echo " SSL: Let's Encrypt (автообновление каждые 60 дней)" + echo "" + + if [[ -n "${S3_ACCESS_KEY_ID:-}" ]]; then + echo " ☁️ S3 Backup:" + echo " Bucket: $S3_BUCKET_NAME" + echo " Endpoint: $S3_ENDPOINT_URL" + echo " Region: $S3_REGION" + echo "" + fi + + if [[ "$SCHEDULER_CHOICE" == "2" ]]; then + echo " 🕐 Backup Schedule:" + echo " Type: Systemd timer" + echo " Schedule: Daily at 3:00 AM" + echo "" + else + echo " 🕐 Backup Schedule:" + echo " Type: Cron" + echo " Schedule: Daily at 3:00 AM" + echo "" + fi + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "📁 КОНФИГУРАЦИЯ СОХРАНЕНА:" + echo " $ENV_FILE" + echo "" + warning "⚠️ ВАЖНО: Храните $ENV_FILE в безопасности!" + warning "⚠️ Файл содержит пароли и секретные ключи." + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + info "🚀 СЛЕДУЮЩИЕ ШАГИ:" + echo "" + echo " 1. Проверьте конфигурацию:" + echo " cat $ENV_FILE" + echo "" + echo " 2. Запустите deploy:" + echo " sudo ./deploy.sh" + echo "" + echo " 3. После успешного deploy:" + echo " - Откройте https://$NOTES_DOMAIN" + echo " - Настройте Obsidian Self-hosted LiveSync plugin" + echo " - Используйте credentials из $ENV_FILE" + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" } From 5c15e7cf43dc7142565954bcef87cf4595046778 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 18 Nov 2025 22:45:55 +0300 Subject: [PATCH 5/5] fix: redirect all output to stderr in prompt_network_selection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Исправлена финальная проблема с ANSI кодами в .env - функция prompt_network_selection() выводила info и echo в stdout. Changes: - Перенаправлены info "Select network mode:" в stderr - Перенаправлены все echo в начале функции в stderr (строки 87, 92-100) Fixes: - NETWORK_MODE больше не содержит "[INFO] Select network mode:" - .env файл полностью чистый от лишнего вывода - Command substitution $(prompt_network_selection) возвращает только "mode|name" Root Cause: - setup.sh вызывает: result=$(prompt_network_selection) - Весь stdout попадал в переменную $result - info и echo выводились в stdout вместо stderr - Результат: NETWORK_MODE=[INFO]... вместо NETWORK_MODE=shared 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- scripts/network-manager.sh | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/scripts/network-manager.sh b/scripts/network-manager.sh index d9ab547..3352b2c 100755 --- a/scripts/network-manager.sh +++ b/scripts/network-manager.sh @@ -83,21 +83,21 @@ list_available_networks() { } prompt_network_selection() { - info "Select network mode:" - echo "" + info "Select network mode:" >&2 + echo "" >&2 local count=$(list_available_networks) local networks=$(docker network ls --format '{{.Name}}' | grep -v '^bridge$\|^host$\|^none$' || true) - echo "" - echo "Options:" + echo "" >&2 + echo "Options:" >&2 if [ "$count" -gt 0 ]; then - echo " 1-${count}. Use existing network" - echo " $((count + 1)). Create new isolated network" + echo " 1-${count}. Use existing network" >&2 + echo " $((count + 1)). Create new isolated network" >&2 else - echo " 1. Create new isolated network" + echo " 1. Create new isolated network" >&2 fi - echo "" + echo "" >&2 local max_choice=$((count + 1)) read -p "Your choice [1-${max_choice}]: " choice