Версия:

Скопируйте этот скрипт

#!/bin/bash

# ====================================================================
# CCS Deployment - Мастер первичной установки (ROOT VERSION)
# ====================================================================
#
# Этот скрипт запускается от root и автоматически:
# - Клонирует проект CCS.Deployment
# - Создает пользователя для работы с CCS
# - Запускает мастер установки
#
# Создание и запуск:
# vim first.sh && chmod +x first.sh && ./first.sh
#
# Использование с конфигурацией:
# ./first.sh /path/to/config.cfg
#
# ====================================================================

set -e

# Конфигурация репозиториев
declare -A REPO_URLS
REPO_URLS[1]="https://gitflic.ru/project/ccsmskru/ccs-deployment.git"
REPO_URLS[2]="https://github.com/CCSMSKRU/CCS.Deployment.git"

declare -A REPO_NAMES
REPO_NAMES[1]="GitFlic"
REPO_NAMES[2]="GitHub"

declare -A REPO_DESCRIPTIONS
REPO_DESCRIPTIONS[1]="GitFlic (рекомендуется для России)"
REPO_DESCRIPTIONS[2]="GitHub (международный)"

# Цвета для вывода (определяем в начале скрипта)
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
MAGENTA='\033[0;35m'
NC='\033[0m' # No Color

# Флаг «репозиторий только что клонирован»
JUST_CLONED=0

# Утилита: определение ОС и пакетного менеджера
detect_os_pm() {
    OS_ID="unknown"; OS_FAMILY="unknown"; PM=""
    if [ -f /etc/os-release ]; then
        . /etc/os-release
        OS_ID="$ID"
    fi
    case "$OS_ID" in
        debian|ubuntu)
            OS_FAMILY="debian"; PM="apt" ;;
        rhel|centos|rocky|almalinux|fedora)
            OS_FAMILY="rhel"; PM="dnf" ;;
        *)
            OS_FAMILY="unknown" ;;
    esac
}

# Установка базовых зависимостей под root (для всей системы), включая Fail2Ban
ensure_system_dependencies() {
    detect_os_pm
    echo -e "${BLUE}Проверка и установка системных зависимостей...${NC}"
    case "$OS_FAMILY" in
        debian)
            apt update -y >/dev/null 2>&1 || true
            # Базовые инструменты и контейнеры
            apt install -y git curl vim mc pwgen htop tmux rsync python3 ufw pipx podman cron >/dev/null 2>&1 || true
            # Компоненты для egress-политик: nftables и DNS-утилиты для резолва
            apt install -y nftables bind9-dnsutils >/dev/null 2>&1 || true
            # Fail2Ban (системный)
            apt install -y fail2ban >/dev/null 2>&1 || true
            if [ ! -f "/etc/fail2ban/jail.local" ]; then
                BANACTION="iptables-multiport"
                if command -v ufw >/dev/null 2>&1; then BANACTION="ufw"; fi
                if command -v firewall-cmd >/dev/null 2>&1; then BANACTION="firewallcmd-rich-rules"; fi
                cat > /etc/fail2ban/jail.local <<EOF
[DEFAULT]
ignoreip = 127.0.0.1/8 ::1
bantime  = 1h
findtime = 10m
maxretry = 5
backend  = systemd
banaction = ${BANACTION}

[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = systemd
EOF
            fi
            systemctl enable fail2ban >/dev/null 2>&1 || true
            systemctl restart fail2ban >/dev/null 2>&1 || systemctl start fail2ban >/dev/null 2>&1 || true

            # Включаем и запускаем cron (для задач бэкапа)
            systemctl enable cron >/dev/null 2>&1 || true
            systemctl restart cron >/dev/null 2>&1 || systemctl start cron >/dev/null 2>&1 || true

            # Разрешить непривилегированные порты с 80 для rootless публикации 80/443
            if [ ! -f /etc/sysctl.d/99-ccs-rootless.conf ] || ! grep -q "^net.ipv4.ip_unprivileged_port_start\s*=\s*80" /etc/sysctl.d/99-ccs-rootless.conf 2>/dev/null; then
                echo "net.ipv4.ip_unprivileged_port_start = 80" > /etc/sysctl.d/99-ccs-rootless.conf
                sysctl --system >/dev/null 2>&1 || true
            fi

            # Базовые правила firewall (если ufw установлен)
            if command -v ufw >/dev/null 2>&1; then
                ufw allow 22/tcp >/dev/null 2>&1 || true
                ufw allow 80/tcp >/dev/null 2>&1 || true
                ufw allow 443/tcp >/dev/null 2>&1 || true
                ufw --force enable >/dev/null 2>&1 || true
            fi
            ;;
        rhel)
            $PM install -y git curl vim mc pwgen htop tmux rsync python3 podman >/dev/null 2>&1 || true
            # Компоненты для egress-политик
            $PM install -y nftables bind-utils >/dev/null 2>&1 || true
            # Fail2Ban
            $PM install -y fail2ban >/dev/null 2>&1 || true
            systemctl enable fail2ban >/dev/null 2>&1 || true
            systemctl restart fail2ban >/dev/null 2>&1 || systemctl start fail2ban >/dev/null 2>&1 || true

            # Включаем crond на RHEL-семействе
            systemctl enable crond >/dev/null 2>&1 || true
            systemctl restart crond >/dev/null 2>&1 || systemctl start crond >/dev/null 2>&1 || true

            # Разрешить непривилегированные порты с 80 для rootless публикации 80/443
            if [ ! -f /etc/sysctl.d/99-ccs-rootless.conf ] || ! grep -q "^net.ipv4.ip_unprivileged_port_start\s*=\s*80" /etc/sysctl.d/99-ccs-rootless.conf 2>/dev/null; then
                echo "net.ipv4.ip_unprivileged_port_start = 80" > /etc/sysctl.d/99-ccs-rootless.conf
                sysctl --system >/dev/null 2>&1 || true
            fi

            # Firewalld базовые правила, если доступен
            if command -v firewall-cmd >/dev/null 2>&1; then
                systemctl enable firewalld >/dev/null 2>&1 || true
                systemctl start firewalld >/dev/null 2>&1 || true
                firewall-cmd --add-port=80/tcp --permanent >/dev/null 2>&1 || true
                firewall-cmd --add-port=443/tcp --permanent >/dev/null 2>&1 || true
                firewall-cmd --reload >/dev/null 2>&1 || true
            fi
            ;;
        *)
            echo -e "${YELLOW}Неизвестная ОС — пропускаем автоустановку зависимостей${NC}"
            ;;
    esac

    # Настройка логирования контейнеров на journald (как в prepareOS/start.sh)
    mkdir -p /var/log/journal 2>/dev/null || true
    conf_file="/usr/share/containers/containers.conf"
    if [ -f "$conf_file" ]; then
        if grep -q "log_driver\s*=\s*\"k8s-file\"" "$conf_file"; then
            sed -i 's/log_driver\s*=\s*"k8s-file"/log_driver = "journald"/' "$conf_file" 2>/dev/null || true
        elif ! grep -q "log_driver" "$conf_file"; then
            echo "log_driver = \"journald\"" >> "$conf_file"
        fi
        sed -i '/# log_driver = "journald"/s/# //' "$conf_file" 2>/dev/null || true
    fi
}

# Немедленно обеспечим системные зависимости (root), чтобы дальше всё работало предсказуемо
ensure_system_dependencies

# --------------------------------------------------------------------
# Добавление SSH ключа пользователя
# --------------------------------------------------------------------

add_ssh_key_to_user() {
    local target_user="$1"
    local creation_type="$2"  # "new" or "existing"
    local target_home
    target_home=$(eval echo ~"$target_user")

    local default_choice="N"
    [ "$creation_type" == "new" ] && default_choice="Y"

    echo -e "\n${BLUE}--- Настройка SSH-ключа для пользователя $target_user ---${NC}"

    local prompt_msg="Добавить публичный SSH-ключ для беспарольного входа? [Y/n]"
    [ "$default_choice" == "N" ] && prompt_msg="Добавить публичный SSH-ключ для беспарольного входа? [y/N]"

    # Попытка получить ранее сделанный выбор из конфига
    local saved_ssh_choice=$(get_config "ADD_SSH_KEY" "")
    if [ -n "$saved_ssh_choice" ]; then
        default_choice="$saved_ssh_choice"
        if [[ "$default_choice" =~ ^[Yy]$ ]]; then
            prompt_msg="Добавить публичный SSH-ключ для беспарольного входа? [Y/n]"
        else
            prompt_msg="Добавить публичный SSH-ключ для беспарольного входа? [y/N]"
        fi
    fi

    echo -ne "${YELLOW}$prompt_msg: ${NC}"

    read user_ssh_choice
    user_ssh_choice=${user_ssh_choice:-$default_choice}

    # Сохраняем выбор в конфиг
    save_config "ADD_SSH_KEY" "$user_ssh_choice" "Add SSH key choice"

    if [[ "$user_ssh_choice" =~ ^[Yy]$ ]]; then
        echo -e "\n${CYAN}Как получить публичный ключ на вашем компьютере:${NC}"
        echo -e "${WHITE}Windows (PowerShell):${NC}  cat ~\.ssh\id_rsa.pub"
        echo -e "${WHITE}Windows (Git Bash):${NC}   cat ~/.ssh/id_rsa.pub"
        echo -e "${WHITE}Mac / Linux:${NC}          cat ~/.ssh/id_rsa.pub"
        echo -e "${YELLOW}(Если ключа нет, создайте его командой: ssh-keygen)${NC}\n"

        echo -e "${BLUE}Вставьте ваш публичный ключ (начинается с ssh-rsa, ecdsa-sha2-... и т.д.):${NC}"
        read -r ssh_pub_key

        if [ -n "$ssh_pub_key" ]; then
            local ssh_dir="$target_home/.ssh"
            local auth_file="$ssh_dir/authorized_keys"

            mkdir -p "$ssh_dir"
            chmod 700 "$ssh_dir"
            echo "$ssh_pub_key" >> "$auth_file"
            chmod 600 "$auth_file"
            chown -R "$target_user:$target_user" "$ssh_dir"

            echo -e "${GREEN}✓ SSH-ключ успешно добавлен для пользователя $target_user${NC}"
        else
            echo -e "${YELLOW}Ключ не введен. Пропуск.${NC}"
        fi
    else
        echo -e "${BLUE}Добавление SSH-ключ пропущено.${NC}"
    fi
}

# --------------------------------------------------------------------
# Egress (nftables) — функции установки root-компонентов (идемпотентно)
# --------------------------------------------------------------------

# Находим корень репозитория с файлами prepareOS. Поддерживаем несколько сценариев размещения first.sh
find_repo_root_for_egress() {
    local cand
    # 1) Относительно расположения текущего скрипта (если скопирован как /root/first.sh, этот путь не сработает)
    cand="$(cd "$(dirname "$0")/.." 2>/dev/null && pwd 2>/dev/null)"
    if [ -n "$cand" ] && [ -f "$cand/prepareOS/ccs_egress_guard.sh" ]; then
        echo "$cand"; return 0
    fi
    # 2) DEPLOY_DIR, если уже определён в окружении (позже может быть переопределён), проверим на существование
    if [ -n "$DEPLOY_DIR" ] && [ -f "$DEPLOY_DIR/prepareOS/ccs_egress_guard.sh" ]; then
        echo "$DEPLOY_DIR"; return 0
    fi
    # 3) Стандартное расположение в домашнем каталоге будущего пользователя
    if [ -n "$CCS_HOME" ] && [ -f "$CCS_HOME/CCS.Deployment/prepareOS/ccs_egress_guard.sh" ]; then
        echo "$CCS_HOME/CCS.Deployment"; return 0
    fi
    # 4) Текущая директория (на случай запуска из корня репозитория)
    if [ -f "prepareOS/ccs_egress_guard.sh" ]; then
        echo "$(pwd)"; return 0
    fi
    echo ""; return 1
}

ensure_ccs_egress_installed() {
    local target_user="$1"
    [ -z "$target_user" ] && return 0

    local repo_root
    repo_root="$(find_repo_root_for_egress)"
    if [ -z "$repo_root" ]; then
        echo -e "${YELLOW}[egress] Источники для установки не найдены (prepareOS/ccs_egress_guard.sh). Пропуск установки egress-компонентов.${NC}"
        return 0
    fi

    local BIN_DIR="/usr/local/bin"
    local SYS_UNIT_DIR="/etc/systemd/system"
    local POLICY_DIR="/etc/polkit-1/rules.d"

    # Скрипты
    install -Dm755 "$repo_root/prepareOS/ccs_egress_guard.sh" "$BIN_DIR/ccs_egress_guard.sh"
    install -Dm755 "$repo_root/prepareOS/ccs_container_runner.sh" "$BIN_DIR/ccs_container_runner.sh"

    # systemd юниты
    install -Dm644 "$repo_root/prepareOS/systemd/ccs-container@.service" "$SYS_UNIT_DIR/ccs-container@.service"
    install -Dm644 "$repo_root/prepareOS/systemd/ccs-egress-guard.service" "$SYS_UNIT_DIR/ccs-egress-guard.service"
    systemctl daemon-reload >/dev/null 2>&1 || true
    systemctl enable ccs-egress-guard.service >/dev/null 2>&1 || true

    # Директории для системных политик/окружения контейнеров
    mkdir -p /var/lib/ccs-egress/policies /var/lib/ccs-egress/containers 2>/dev/null || true

    # polkit правило: разрешить выбранному пользователю управление ccs-юнитами без sudo
    mkdir -p "$POLICY_DIR" 2>/dev/null || true
    cat > "$POLICY_DIR/50-ccs-egress.rules" <<POLKIT
polkit.addRule(function(action, subject) {
  if (subject.isInGroup("${target_user}") || subject.user == "${target_user}") {
    if (action.id == "org.freedesktop.systemd1.manage-units") {
      var unit = action.lookup("unit");
      if (unit && (unit.match(/^ccs-container@.*\.service$/) || unit == "ccs-egress-guard.service")) {
        return polkit.Result.YES;
      }
    }
  }
});
POLKIT

    # Диагностика (краткая)
    echo -e "${BLUE}[egress] Установлены компоненты egress (идемпотентно).${NC}"
}

ensure_podman_network_ccs_egress() {
    local NET_NAME="ccs-egress"
    if ! podman network inspect "$NET_NAME" >/dev/null 2>&1; then
        podman network create "$NET_NAME" >/dev/null 2>&1 || true
        echo -e "${BLUE}[egress] Создана сеть Podman: ${NET_NAME}${NC}"
    fi
}

# Установка certbot (кросс‑дистрибутивно, с резервными сценариями)
install_certbot() {
    detect_os_pm
    echo -e "${BLUE}Проверка и установка certbot...${NC}"

    # Уже установлен?
    if command -v certbot >/dev/null 2>&1 || command -v certbot-3 >/dev/null 2>&1; then
        echo -e "${GREEN}certbot уже установлен (${YELLOW}$(command -v certbot || command -v certbot-3)${GREEN})${NC}"
        return 0
    fi

    case "$OS_FAMILY" in
        debian)
            # Основной вариант — пакет certbot
            apt update -y >/dev/null 2>&1 || true
            apt install -y certbot >/dev/null 2>&1 || true
            if command -v certbot >/dev/null 2>&1; then
                echo -e "${GREEN}Установлен certbot из apt${NC}"
                return 0
            fi
            # Резерв — python3-certbot (в некоторых репозиториях)
            apt install -y python3-certbot >/dev/null 2>&1 || true
            if command -v certbot >/dev/null 2>&1; then
                echo -e "${GREEN}Установлен certbot (python3-certbot) из apt${NC}"
                return 0
            fi
            # Последняя линия — snapd
            apt install -y snapd >/dev/null 2>&1 || true
            systemctl enable snapd >/dev/null 2>&1 || true
            systemctl start snapd >/dev/null 2>&1 || true
            snap install core >/dev/null 2>&1 || true
            snap refresh core >/dev/null 2>&1 || true
            snap install --classic certbot >/dev/null 2>&1 || true
            if [ -x "/snap/bin/certbot" ]; then
                ln -sf /snap/bin/certbot /usr/bin/certbot >/dev/null 2>&1 || true
            fi
            ;;
        rhel)
            # Попытка через dnf (может требовать EPEL в некоторых версиях)
            $PM install -y certbot >/dev/null 2>&1 || true
            if command -v certbot >/dev/null 2>&1; then
                echo -e "${GREEN}Установлен certbot из dnf${NC}"
                return 0
            fi
            $PM install -y python3-certbot >/dev/null 2>&1 || true
            if command -v certbot >/dev/null 2>&1; then
                echo -e "${GREEN}Установлен certbot (python3-certbot) из dnf${NC}"
                return 0
            fi
            # Резерв — snapd
            $PM install -y snapd >/dev/null 2>&1 || true
            systemctl enable snapd >/dev/null 2>&1 || true
            systemctl start snapd >/dev/null 2>&1 || true
            snap install core >/dev/null 2>&1 || true
            snap refresh core >/dev/null 2>&1 || true
            snap install --classic certbot >/dev/null 2>&1 || true
            if [ -x "/snap/bin/certbot" ]; then
                ln -sf /snap/bin/certbot /usr/bin/certbot >/dev/null 2>&1 || true
            fi
            ;;
        *)
            echo -e "${YELLOW}Неизвестная ОС — пропускаем установку certbot${NC}"
            ;;
    esac

    if command -v certbot >/dev/null 2>&1 || command -v certbot-3 >/dev/null 2>&1; then
        echo -e "${GREEN}certbot установлен (${YELLOW}$(command -v certbot || command -v certbot-3)${GREEN})${NC}"
        return 0
    else
        echo -e "${RED}Не удалось установить certbot автоматически. Установите вручную и перезапустите установку.${NC}"
        return 1
    fi
}

install_certbot || true

# Краткий отчёт о системной подготовке
print_prepare_report() {
    echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
    echo -e "${BLUE}                     ОТЧЁТ О ПОДГОТОВКЕ СИСТЕМЫ                     ${NC}"
    echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
    echo "Дата/время: $(date)"

    # Sysctl для rootless портов
    UNPRIV_START=$(cat /proc/sys/net/ipv4/ip_unprivileged_port_start 2>/dev/null || echo "unknown")
    echo "net.ipv4.ip_unprivileged_port_start: ${UNPRIV_START}"

    # Лог‑драйвер контейнеров
    CONF_FILE="/usr/share/containers/containers.conf"
    if [ -f "$CONF_FILE" ]; then
        LOG_DRIVER=$(grep -E '^\s*log_driver\s*=\s*' "$CONF_FILE" | sed -E 's/^[^=]+=\s*"?([^" ]+)"?.*/\1/' | tail -n1)
        [ -z "$LOG_DRIVER" ] && LOG_DRIVER="(не задан)"
    else
        LOG_DRIVER="(файл не найден)"
    fi
    echo "containers.conf log_driver: ${LOG_DRIVER}"

    # Fail2Ban
    if command -v systemctl >/dev/null 2>&1; then
        if systemctl is-active --quiet fail2ban 2>/dev/null; then
            echo "Fail2Ban: активен"
        elif systemctl is-enabled --quiet fail2ban 2>/dev/null; then
            echo "Fail2Ban: установлен, но не активен"
        else
            echo "Fail2Ban: не установлен/не включён"
        fi
    fi

    # Firewall
    if command -v ufw >/dev/null 2>&1; then
        UFW_STATUS=$(ufw status 2>/dev/null | head -n1)
        echo "UFW: ${UFW_STATUS}"
    elif command -v firewall-cmd >/dev/null 2>&1; then
        if systemctl is-active --quiet firewalld 2>/dev/null; then
            echo "firewalld: активен"
        else
            echo "firewalld: не активен"
        fi
    else
        echo "Firewall: не обнаружен (UFW/firewalld)"
    fi

    echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
}

print_prepare_report

# Функция для проверки надежности пароля
# Перемещена вверх, чтобы быть определённой до первого вызова
check_password_strength() {
    local password="$1"
    local errors=()

    # Проверяем минимальную длину
    if [ ${#password} -lt 8 ]; then
        errors+=("Пароль слишком короткий (минимум 8 символов)")
    fi

    # Проверяем наличие заглавных букв
    if [[ ! "$password" =~ [A-Z] ]]; then
        errors+=("Отсутствуют заглавные буквы (A-Z)")
    fi

    # Проверяем наличие строчных букв
    if [[ ! "$password" =~ [a-z] ]]; then
        errors+=("Отсутствуют строчные буквы (a-z)")
    fi

    # Проверяем наличие цифр
    if [[ ! "$password" =~ [0-9] ]]; then
        errors+=("Отсутствуют цифры (0-9)")
    fi

    # Проверяем наличие специальных символов
    if [[ ! "$password" =~ [^A-Za-z0-9] ]]; then
        errors+=("Отсутствуют специальные символы (!@#%^&* и т.д.)")
    fi

    # Проверяем на простые пароли
    local simple_passwords=("password" "123456" "qwerty" "admin" "root" "user" "test" "pass" "login")
    local lower_password=$(echo "$password" | tr '[:upper:]' '[:lower:]')
    for simple in "${simple_passwords[@]}"; do
        if [[ "$lower_password" == *"$simple"* ]]; then
            errors+=("Пароль содержит простые/распространенные комбинации")
            break
        fi
    done

    # Возвращаем результат
    if [ ${#errors[@]} -eq 0 ]; then
        return 0  # Пароль надежный
    else
        # Выводим ошибки
        echo -e "${RED}Пароль не соответствует требованиям безопасности:${NC}"
        for error in "${errors[@]}"; do
            echo -e "${YELLOW}  • $error${NC}"
        done
        return 1  # Пароль не надежный
    fi
}

# Функция для поиска конфигурационных файлов
find_config_files() {
    local search_dirs=(".")
    local config_files=()

    # Ищем файлы конфигурации в текущей директории
    for dir in "${search_dirs[@]}"; do
        if [ -d "$dir" ]; then
            # Простой поиск файлов по шаблону ccs_install_*.cfg
            while IFS= read -r file; do
                if [ -f "$file" ]; then
                    local mtime=$(stat -c %Y "$file" 2>/dev/null || stat -f %m "$file" 2>/dev/null || echo "0")
                    config_files+=("$mtime $file")
                fi
            done < <(find "$dir" -maxdepth 1 -name "ccs_install_*.cfg" -type f 2>/dev/null)
        fi
    done

    # Сортируем по времени и берем последние 10 файлов
    if [ ${#config_files[@]} -gt 0 ]; then
        printf '%s\n' "${config_files[@]}" | sort -rn -k1 | head -10 | cut -d' ' -f2-
    fi
}

# Функция для выбора конфигурационного файла
select_config_file() {
    local config_files
    mapfile -t config_files < <(find_config_files)

    if [ ${#config_files[@]} -eq 0 ]; then
        echo "Конфигурационные файлы не найдены."
        return 1
    fi

    echo -e "${BLUE}Найдены следующие конфигурационные файлы:${NC}"
    echo "0) Не использовать конфигурацию (стандартная установка)"

    local i=1
    for file in "${config_files[@]}"; do
        local file_date=$(stat -c %Y "$file" 2>/dev/null || date -r "$file" +%s 2>/dev/null || echo "0")
        local formatted_date=$(date -d "@$file_date" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || date -r "$file" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "неизвестно")
        local file_size=$(du -h "$file" 2>/dev/null | cut -f1 || echo "?")
        echo "$i) $(basename "$file") (${formatted_date}, ${file_size})"
        ((i++))
    done

    echo ""
    while true; do
        read -p "Выберите конфигурационный файл (0-$((${#config_files[@]}))) [по умолчанию: 1]: " choice
        choice=${choice:-1}

        if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 0 ] && [ "$choice" -le "${#config_files[@]}" ]; then
            if [ "$choice" -eq 0 ]; then
        echo "Выбрана стандартная установка без конфигурации"
        CONFIG_INPUT=""
        return 1
    else
                local selected_file="${config_files[$((choice-1))]}"
                if [ -f "$selected_file" ] && [ -r "$selected_file" ]; then
                    echo "Выбран конфигурационный файл: $selected_file"
                    CONFIG_INPUT="$selected_file"
                    return 0
                else
                    echo -e "${RED}Файл недоступен для чтения: $selected_file${NC}"
                fi
            fi
        else
            echo -e "${YELLOW}Введите число от 0 до ${#config_files[@]}${NC}"
        fi
    done
}

# Проверяем аргументы командной строки и ищем конфиги
CONFIG_INPUT=""
CONFIG_SOURCE_INFO=""

if [ $# -gt 0 ]; then
    CONFIG_INPUT="$1"
    if [ ! -f "$CONFIG_INPUT" ]; then
        echo "ОШИБКА: Файл конфигурации '$CONFIG_INPUT' не найден!"
        exit 1
    fi
    echo "Используется конфигурационный файл: $CONFIG_INPUT"
    CONFIG_SOURCE_INFO="Конфигурация загружена из файла командной строки: $CONFIG_INPUT"
else
    # Если конфиг не указан в аргументах, ищем автоматически
    echo -e "${BLUE}Поиск конфигурационных файлов...${NC}"
    if select_config_file; then
        echo "Используется найденный конфигурационный файл: $CONFIG_INPUT"
        CONFIG_SOURCE_INFO="Конфигурация загружена из выбранного файла: $CONFIG_INPUT"
        echo ""
    else
        echo "Продолжаем без конфигурационного файла"
        CONFIG_SOURCE_INFO="Новая установка без использования существующей конфигурации"
        echo ""
    fi
fi

# Функция для загрузки конфигурации
load_config() {
    if [ -n "$CONFIG_INPUT" ] && [ -f "$CONFIG_INPUT" ]; then
        echo "Загружаем настройки из конфигурационного файла..."
        # Загружаем переменные из конфига, игнорируя комментарии и пустые строки
        while IFS= read -r line; do
            # Пропускаем комментарии и пустые строки
            if [[ "$line" =~ ^[[:space:]]*# ]] || [[ -z "$line" ]]; then
                continue
            fi
            # Экспортируем переменные
            if [[ "$line" =~ ^[A-Z_][A-Z0-9_]*= ]]; then
                eval "export $line"
            fi
        done < "$CONFIG_INPUT"
        echo "✓ Конфигурация загружена"
        echo ""
    fi
}

# Функция для получения значения из загруженной конфигурации или окружения
get_config() {
    local param_name="$1"
    local default_val="$2"
    local val
    val=$(eval echo "\${$param_name}")
    echo "${val:-$default_val}"
}

# Отключаем немедленный выход при ошибках для корректной обработки сигналов
set +e
trap 'set +e; cleanup_on_exit' SIGINT SIGTERM
set -e

# Путь к файлу с паролями (в директории скрипта)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PASSWORD_ENV_FILE="$SCRIPT_DIR/.ccs_install_env"

# Функция для генерации случайного пароля
generate_password() {
    local length=${1:-16}
    local password=""

    # Используем комбинацию методов для генерации надежного пароля
    if command -v openssl >/dev/null 2>&1; then
        # Генерируем пароль с использованием всех символов включая спецсимволы
        # Исключаем только проблематичные символы для bash и SQL: ' " \ ` $
        password=$(openssl rand -base64 $((length * 2)) | tr -dc 'A-Za-z0-9!@#%^&*()_+=-[]{}|;:,.<>?' | head -c ${length})

        # Если получился слишком короткий пароль, дополняем
        while [ ${#password} -lt $length ]; do
            additional=$(openssl rand -base64 10 | tr -dc 'A-Za-z0-9!@#%^&*()_+=-[]{}|;:,.<>?' | head -c $((length - ${#password})))
            password="${password}${additional}"
        done
    elif [ -f /dev/urandom ]; then
        # Альтернативный метод с /dev/urandom
        password=$(tr -dc 'A-Za-z0-9!@#%^&*()_+=-[]{}|;:,.<>?' < /dev/urandom | head -c ${length})
    else
        # Fallback метод - менее безопасный, но с некоторыми спецсимволами
        base=$(date +%s | sha256sum | base64 | head -c $((length - 4)))
        # Добавляем гарантированные спецсимволы
        password="${base}!@#$"
        password=$(echo "$password" | head -c ${length})
    fi

    # Проверяем что пароль содержит минимум по одному символу каждого типа
    if [[ ! "$password" =~ [A-Z] ]] || [[ ! "$password" =~ [a-z] ]] || [[ ! "$password" =~ [0-9] ]] || [[ ! "$password" =~ [^A-Za-z0-9] ]]; then
        # Если не хватает разнообразия, принудительно добавляем нужные символы
        password="${password:0:$((length-4))}A1!@"
        # Перемешиваем символы для большей случайности
        password=$(echo "$password" | fold -w1 | shuf | tr -d '\n')
    fi

    echo "$password"
}

# Функция для получения пароля из файла паролей
get_password_from_env() {
    local username="$1"
    local password_var="PASS_$(echo "$username" | tr '[:lower:]' '[:upper:]')"

    if [ -f "$PASSWORD_ENV_FILE" ]; then
        # Ищем пароль в файле
        local password_line
        password_line=$(grep "^${password_var}=" "$PASSWORD_ENV_FILE" 2>/dev/null | head -n1)
        if [ -n "$password_line" ]; then
            # Удаляем кавычки если они есть
            local password="${password_line#*=}"
            password="${password#\"}"
            password="${password%\"}"
            echo "$password"
            return 0
        fi
    fi
    return 1
}

# Функция для сохранения пароля в файл паролей
save_password_to_env() {
    local username="$1"
    local password="$2"
    local password_var="PASS_$(echo "$username" | tr '[:lower:]' '[:upper:]')"

    # Создаем файл если его нет
    if [ ! -f "$PASSWORD_ENV_FILE" ]; then
        cat > "$PASSWORD_ENV_FILE" << EOF
# ====================================================================
# CCS Deployment - Users password file
# Created: $(date)
# ====================================================================
#
# WARNING! This file contains user passwords.
# Ensure its security and limited access!
#
# Format: PASS_USERNAME=password
#

EOF
        chmod 600 "$PASSWORD_ENV_FILE"
    fi

    # Проверяем есть ли уже пароль для этого пользователя
    if grep -q "^${password_var}=" "$PASSWORD_ENV_FILE" 2>/dev/null; then
        # Обновляем существующий пароль
        sed -i "s/^${password_var}=.*/${password_var}=\"${password}\"/g" "$PASSWORD_ENV_FILE"
    else
        # Добавляем новый пароль
        echo "${password_var}=\"${password}\"" >> "$PASSWORD_ENV_FILE"
    fi

    # Устанавливаем безопасные права доступа
    chmod 600 "$PASSWORD_ENV_FILE"
}

# Создаем файл конфигурации для сохранения решений
if [ -n "$CONFIG_INPUT" ] && [ -f "$CONFIG_INPUT" ]; then
    CONFIG_PATH="$CONFIG_INPUT"
    CONFIG_FILE=$(basename "$CONFIG_PATH")
    echo -e "${YELLOW}Будет обновлен существующий файл конфигурации: $CONFIG_PATH${NC}"
else
    CONFIG_FILE="ccs_install_$(date +%Y%m%d_%H%M%S).cfg"
    SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    CONFIG_PATH="$SCRIPT_DIR/$CONFIG_FILE"
    echo -e "${GREEN}Конфигурация будет сохранена в НОВЫЙ файл: $CONFIG_PATH${NC}"
fi

# Функция для сохранения параметров в конфигурацию
save_config() {
    local param_name="$1"
    local param_value="$2"
    local param_comment="$3"

    # Немедленно обновляем значение переменной в текущей сессии
    eval "export ${param_name}=\"${param_value}\""

    # Создаем временный файл
    local tmp_file=$(mktemp)
    local found=0

    if [ -f "$CONFIG_PATH" ]; then
        # Читаем существующий файл и обновляем значение если параметр уже есть
        while IFS= read -r line || [ -n "$line" ]; do
            if [[ "$line" =~ ^${param_name}= ]]; then
                # Мы нашли строку с параметром. Обновляем значение.
                # Комментарий выше мы не трогаем, если он есть.
                echo "${param_name}=\"${param_value}\"" >> "$tmp_file"
                found=1
            else
                echo "$line" >> "$tmp_file"
            fi
        done < "$CONFIG_PATH"
    fi

    # Если параметр не был найден, добавляем его в конец с комментарием
    if [ $found -eq 0 ]; then
        if [ -s "$tmp_file" ]; then
            echo "" >> "$tmp_file"
        fi
        if [ -n "$param_comment" ]; then
            echo "# $param_comment" >> "$tmp_file"
        fi
        echo "${param_name}=\"${param_value}\"" >> "$tmp_file"
    fi

    # Заменяем оригинальный файл временным
    cat "$tmp_file" > "$CONFIG_PATH"
    rm "$tmp_file"

    # Немедленно синхронизируем с диском для надежности
    sync
}

# Функция для корректного завершения при прерывании
cleanup_on_exit() {
    echo ""
    echo -e "${YELLOW}Скрипт прерван пользователем или произошла ошибка${NC}"

    # Обновляем статус в конфигурации
    save_config "INSTALL_STATUS" "interrupted" "Installation status: interrupted - by user or error"
    save_config "INTERRUPT_DATE" "$(date)" "Interrupt date and time"

    # Копируем конфигурацию в домашнюю директорию если пользователь уже определен
    if [ -n "$CCS_USER" ] && [ -n "$CCS_HOME" ]; then
        FINAL_CONFIG_PATH="$CCS_HOME/$CONFIG_FILE"
        cp "$CONFIG_PATH" "$FINAL_CONFIG_PATH" 2>/dev/null || true
        chown "$CCS_USER:$CCS_USER" "$FINAL_CONFIG_PATH" 2>/dev/null || true
        chmod 600 "$FINAL_CONFIG_PATH" 2>/dev/null || true
    fi

    echo -e "${BLUE}Partial configuration saved to: $CONFIG_PATH${NC}"
    exit 1
}

# Загружаем конфигурацию если она указана (делаем это ДО инициализации/перезаписи файла CONFIG_PATH)
load_config

# Инициализация конфигурационного файла (если он еще не существует)
if [ ! -f "$CONFIG_PATH" ]; then
    cat > "$CONFIG_PATH" << EOF
# ====================================================================
# CCS Deployment - Installation configuration
# Created: $(date)
# Host: $(hostname)
# ====================================================================
#
# This file contains all the decisions made during the installation.
# Can be used for automatic configuration in the future.
#
# $CONFIG_SOURCE_INFO
#

EOF
fi

echo -e "${GREEN}Конфигурация будет сохранена в: $CONFIG_PATH${NC}"
echo ""

# Логотип и приветствие
echo -e "${BLUE}"
cat << 'EOF'
╔══════════════════════════════════════════════════════════════════╗
║                    CCS DEPLOYMENT MASTER                        ║
║                 Универсальная система установки                 ║
║                      (ROOT VERSION)                             ║
╚══════════════════════════════════════════════════════════════════╝
EOF
echo -e "${NC}"

echo -e "${GREEN}Добро пожаловать в мастер установки CCS Deployment!${NC}"
echo -e "${YELLOW}Этот скрипт настроит всю систему от начала до конца${NC}"
echo ""

# Проверяем что запущен от root
if [ "$EUID" -ne 0 ]; then
    echo -e "${RED}ОШИБКА: Этот скрипт должен запускаться от root!${NC}"
    echo -e "${YELLOW}Запустите: sudo ./first.sh${NC}"
    exit 1
fi

echo -e "${GREEN}✓ Скрипт запущен от root${NC}"

# Сохраняем информацию об источнике конфигурации в новый файл
save_config "CONFIG_SOURCE_INFO" "$CONFIG_SOURCE_INFO" "Configuration source for this installation"

# Определяем ОС
if [ -f /etc/os-release ]; then
    . /etc/os-release
    OS_NAME="$ID"
    OS_VERSION="$VERSION_ID"
    echo -e "${BLUE}Обнаружена ОС: $PRETTY_NAME${NC}"

    # Сохраняем информацию об ОС
    save_config "OS_NAME" "$OS_NAME" "OS Identifier"
    save_config "OS_VERSION" "$OS_VERSION" "OS Version"
    save_config "OS_PRETTY_NAME" "$PRETTY_NAME" "OS Pretty Name"
else
    echo -e "${RED}Не удалось определить операционную систему${NC}"
    exit 1
fi

# Проверяем базовые инструменты и устанавливаем если нужно
echo -e "${BLUE}Проверка и установка базовых зависимостей...${NC}"

REQUIRED_TOOLS=("git" "curl" "wget" "vim")
MISSING_TOOLS=()

for tool in "${REQUIRED_TOOLS[@]}"; do
    if ! command -v "$tool" >/dev/null 2>&1; then
        MISSING_TOOLS+=("$tool")
    fi
done

if [ ${#MISSING_TOOLS[@]} -gt 0 ]; then
    echo -e "${YELLOW}Устанавливаем недостающие пакеты: ${MISSING_TOOLS[*]}${NC}"

    case "$OS_NAME" in
        "debian"|"ubuntu")
            apt update
            apt install -y git curl wget vim sudo
            ;;
        "rhel"|"centos"|"rocky"|"fedora"|"almalinux")
            if command -v dnf >/dev/null 2>&1; then
                dnf install -y git curl wget vim sudo
            else
                yum install -y git curl wget vim sudo
            fi
            ;;
        *)
            echo -e "${RED}Неподдерживаемая ОС для автоматической установки пакетов${NC}"
            echo -e "${YELLOW}Установите вручную: git curl wget vim${NC}"
            exit 1
            ;;
    esac

    echo -e "${GREEN}Базовые инструменты установлены${NC}"
else
    echo -e "${GREEN}✓ Все базовые инструменты уже установлены${NC}"
fi

# Создание или выбор пользователя для CCS
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}                    НАСТРОЙКА ПОЛЬЗОВАТЕЛЯ                        ${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
echo ""

# Показываем существующих пользователей (исключаем системных)
echo -e "${BLUE}Существующие пользователи в системе:${NC}"
existing_users_array=()
while IFS= read -r user; do
    existing_users_array+=("$user")
done < <(getent passwd | awk -F: '$3 >= 1000 && $3 < 65534 {print $1}')

if [ ${#existing_users_array[@]} -gt 0 ]; then
    for i in "${!existing_users_array[@]}"; do
        echo "$((i+1))) ${existing_users_array[$i]}"
    done
else
    echo -e "${YELLOW}Обычных пользователей не найдено${NC}"
fi
echo ""

while true; do
    # Используем значение из конфига как значение по умолчанию
    default_user_choice="${USER_CHOICE:-}"
    default_ccs_user="${CCS_USER:-}"

    if [ -n "$default_user_choice" ] && [ -n "$default_ccs_user" ]; then
        # Если есть сохраненный пользователь, проверяем его существование
        if [ "$default_user_choice" = "0" ]; then
            # Был создан новый пользователь
            if id "$default_ccs_user" &>/dev/null; then
                echo -e "${GREEN}Найден пользователь из конфигурации: $default_ccs_user${NC}"
                echo -e "${GREEN}Выберите действие:${NC}"
                echo "0) Создать нового пользователя для CCS"
                if [ ${#existing_users_array[@]} -gt 0 ]; then
                    echo "1-${#existing_users_array[@]}) Использовать существующего пользователя (номер из списка выше)"
                fi
                echo ""

                if [ ${#existing_users_array[@]} -gt 0 ]; then
                    read -p "Выберите действие (0-${#existing_users_array[@]}) [Enter - использовать $default_ccs_user, или новый выбор]: " user_choice
                else
                    read -p "Выберите действие (0) [Enter - использовать $default_ccs_user, или новый выбор]: " user_choice
                fi

                user_choice=${user_choice:-$default_user_choice}

                if [ "$user_choice" = "$default_user_choice" ] || [ -z "$user_choice" ]; then
                    # Используем сохраненного пользователя
                    CCS_USER="$default_ccs_user"

                    # Сохраняем "0", так как в конфиге было "0" (создание нового)
                    save_config "USER_CHOICE" "0" "Action choice: 0 - user from config was newly created"
                    save_config "CCS_USER" "$CCS_USER" "Username used"
                    save_config "USER_CREATION" "existing" "User creation type: already created in previous run"

                    # Убеждаемся что пользователь в нужных группах
                    usermod -aG sudo "$CCS_USER" 2>/dev/null || usermod -aG wheel "$CCS_USER" 2>/dev/null || true
                    usermod -aG adm "$CCS_USER" 2>/dev/null || true
                    loginctl enable-linger "$CCS_USER" 2>/dev/null || true

                    echo -e "${GREEN}✓ Будет использован пользователь из конфига: $CCS_USER${NC}"
                    break
                fi
            else
                echo -e "${YELLOW}Пользователь из конфига ($default_ccs_user) больше не существует${NC}"
                default_user_choice=""
            fi
        else
            # Был выбран существующий пользователь
            if id "$default_ccs_user" &>/dev/null; then
                # Находим текущий индекс пользователя в списке
                current_idx=0
                for i in "${!existing_users_array[@]}"; do
                    if [ "${existing_users_array[$i]}" = "$default_ccs_user" ]; then
                        current_idx=$((i+1))
                        break
                    fi
                done

                echo -e "${GREEN}Найден пользователь из конфигурации: $default_ccs_user${NC}"
                echo -e "${GREEN}Выберите действие:${NC}"
                echo "0) Создать нового пользователя для CCS"
                if [ ${#existing_users_array[@]} -gt 0 ]; then
                    echo "1-${#existing_users_array[@]}) Использовать существующего пользователя (номер из списка выше)"
                fi
                echo ""

                if [ ${#existing_users_array[@]} -gt 0 ]; then
                    read -p "Выберите действие (0-${#existing_users_array[@]}) [Enter - использовать $default_ccs_user, или новый выбор]: " user_choice
                else
                    read -p "Выберите действие (0) [Enter - использовать $default_ccs_user, или новый выбор]: " user_choice
                fi

                user_choice=${user_choice:-$default_user_choice}

                if [ "$user_choice" = "$default_user_choice" ] || [ -z "$user_choice" ]; then
                    # Используем сохраненного пользователя
                    CCS_USER="$default_ccs_user"

                    # Сохраняем актуальный индекс (если он изменился в списке)
                    if [ $current_idx -gt 0 ]; then
                        save_config "USER_CHOICE" "$current_idx" "Action choice: index of existing user"
                    else
                        save_config "USER_CHOICE" "0" "Action choice: fallback to 0 (user not in list)"
                    fi

                    save_config "CCS_USER" "$CCS_USER" "Username used"
                    save_config "USER_CREATION" "existing" "User creation type: existing user from previous run"

                    # Убеждаемся что пользователь в нужных группах
                    usermod -aG sudo "$CCS_USER" 2>/dev/null || usermod -aG wheel "$CCS_USER" 2>/dev/null || true
                    usermod -aG adm "$CCS_USER" 2>/dev/null || true
                    loginctl enable-linger "$CCS_USER" 2>/dev/null || true

                    echo -e "${GREEN}✓ Будет использован пользователь из конфига: $CCS_USER${NC}"
                    break
                fi
            else
                echo -e "${YELLOW}Пользователь из конфига ($default_ccs_user) больше не существует${NC}"
                default_user_choice=""
            fi
        fi
    fi

    # Стандартная логика выбора если нет валидного пользователя из конфига
    if [ -z "$user_choice" ]; then
        # Определяем значение по умолчанию
        final_default_choice="0"
        if [ -n "$default_user_choice" ] && [[ "$default_user_choice" =~ ^[0-9]+$ ]]; then
            # Проверяем что индекс существующего пользователя в допустимых пределах
            if [ "$default_user_choice" -le "${#existing_users_array[@]}" ]; then
                final_default_choice="$default_user_choice"
            fi
        fi

        echo -e "${GREEN}Выберите действие:${NC}"
        echo "0) Создать нового пользователя для CCS"
        if [ ${#existing_users_array[@]} -gt 0 ]; then
            echo "1-${#existing_users_array[@]}) Использовать существующего пользователя (номер из списка выше)"
        fi
        echo ""

        if [ ${#existing_users_array[@]} -gt 0 ]; then
            read -p "Выберите действие (0-${#existing_users_array[@]}) [по умолчанию: $final_default_choice]: " user_choice
            user_choice=${user_choice:-$final_default_choice}
        else
            read -p "Выберите действие (0) [по умолчанию: $final_default_choice]: " user_choice
            user_choice=${user_choice:-$final_default_choice}
        fi
    fi

    # Проверяем корректность выбора
    if [[ "$user_choice" =~ ^[0-9]+$ ]]; then
        if [ "$user_choice" -eq 0 ]; then
            # Создание нового пользователя
            save_config "USER_CHOICE" "$user_choice" "Action choice: 0 - create new user, 1+ - use existing by index"

            while true; do
                # Используем значение из конфига как значение по умолчанию
                default_ccs_user="${CCS_USER:-ccsuser}"
                read -p "Введите имя нового пользователя [по умолчанию: $default_ccs_user]: " CCS_USER
                CCS_USER=${CCS_USER:-$default_ccs_user}

                # Проверяем что пользователь не существует
                if id "$CCS_USER" &>/dev/null; then
                    echo -e "${YELLOW}Пользователь $CCS_USER уже существует${NC}"
                else
                    break
                fi
            done

            # Ввод пароля с новой логикой генерации и сохранения
            CCS_PASSWORD=""

            # Сначала проверяем есть ли уже сохраненный пароль для этого пользователя
            saved_password=""
            if saved_password=$(get_password_from_env "$CCS_USER" 2>/dev/null); then
                echo -e "${GREEN}✓ Найден сохраненный пароль для пользователя $CCS_USER${NC}"
                CCS_PASSWORD="$saved_password"
            else
                # Проверяем есть ли пароль из конфига
                config_password="${CCS_PASSWORD:-}"
                if [ -n "$config_password" ]; then
                    echo -e "${GREEN}Найден пароль в конфигурационном файле${NC}"
                    CCS_PASSWORD="$config_password"
                else
                    # Предлагаем ввести пароль или сгенерировать автоматически
                    echo -e "${BLUE}Настройка пароля для пользователя $CCS_USER:${NC}"
                    echo "1) Ввести пароль вручную"
                    echo "2) Сгенерировать автоматически (рекомендуется)"

                    read -p "Выберите вариант (1-2) [по умолчанию: 2 - автогенерация]: " pass_choice
                    pass_choice=${pass_choice:-2}

                    case "$pass_choice" in
                        1)
                            # Цикл ввода пароля с проверкой надежности
                            while true; do
                                read -s -p "Введите пароль для пользователя $CCS_USER: " CCS_PASSWORD
                                echo

                                if [ -z "$CCS_PASSWORD" ]; then
                                    echo -e "${YELLOW}Пустой пароль, генерируем автоматически...${NC}"
                                    CCS_PASSWORD=$(generate_password 16)
                                    break
                                fi

                                # Проверяем надежность введенного пароля
                                if check_password_strength "$CCS_PASSWORD"; then
                                    echo -e "${GREEN}✓ Пароль соответствует требованиям безопасности${NC}"
                                    break
                                else
                                    echo ""
                                    echo -e "${BLUE}Рекомендации для надежного пароля:${NC}"
                                    echo -e "${YELLOW}  • Минимум 8 символов${NC}"
                                    echo -e "${YELLOW}  • Заглавные буквы (A-Z)${NC}"
                                    echo -e "${YELLOW}  • Строчные буквы (a-z)${NC}"
                                    echo -e "${YELLOW}  • Цифры (0-9)${NC}"
                                    echo -e "${YELLOW}  • Специальные символы (!@#%^&* и т.д.)${NC}"
                                    echo ""

            while true; do
                read -p "Что делать? (1 - ввести другой пароль, 2 - оставить как есть, 3 - сгенерировать автоматически) [по умолчанию: 1]: " action
                action=${action:-1}

                                        case "$action" in
                                            1)
                                                echo -e "${BLUE}Введите новый пароль...${NC}"
                                                break
                                                ;;
                                            2)
                                                echo -e "${YELLOW}⚠ ВНИМАНИЕ: Используется пароль, не соответствующий требованиям безопасности!${NC}"
                                                break 2  # Выходим из обоих циклов
                                                ;;
                                            3)
                                                echo -e "${BLUE}Генерируем надежный пароль...${NC}"
                                                CCS_PASSWORD=$(generate_password 16)
                                                break 2  # Выходим из обоих циклов
                                                ;;
                                            *)
                                                echo -e "${YELLOW}Введите 1, 2 или 3${NC}"
                                                ;;
                                        esac
                                    done
                                fi
                            done
                            ;;
                        2|*)
                            echo -e "${BLUE}Генерируем случайный пароль...${NC}"
                            CCS_PASSWORD=$(generate_password 16)
                            ;;
                    esac
                fi

                # Сохраняем пароль в файл паролей
                save_password_to_env "$CCS_USER" "$CCS_PASSWORD"
                echo -e "${GREEN}✓ Пароль сохранен в файл $PASSWORD_ENV_FILE${NC}"
                echo -e "${YELLOW}ВНИМАНИЕ: Пароль доступен в файле $PASSWORD_ENV_FILE${NC}"
            fi

            # Поиск свободного UID
            min_uid=1000
            max_uid=2000
            next_uid=$min_uid

            while [ $next_uid -le $max_uid ]; do
                if ! getent passwd $next_uid &>/dev/null; then
                    break
                fi
                next_uid=$((next_uid + 1))
            done

            if [ $next_uid -gt $max_uid ]; then
                echo -e "${RED}Не удалось найти свободный UID${NC}"
                exit 1
            fi

            echo -e "${BLUE}Создание пользователя $CCS_USER с UID $next_uid...${NC}"

            # Создаем пользователя
            groupadd -g $next_uid $CCS_USER
            useradd -m -u $next_uid -g $CCS_USER -s /bin/bash "$CCS_USER"
            echo -e "$CCS_PASSWORD\n$CCS_PASSWORD" | passwd "$CCS_USER"

            # Добавляем в нужные группы
            usermod -aG sudo "$CCS_USER" 2>/dev/null || usermod -aG wheel "$CCS_USER" 2>/dev/null || true
            usermod -aG adm "$CCS_USER" 2>/dev/null || true

            # Включаем lingering для systemd
            loginctl enable-linger "$CCS_USER" 2>/dev/null || true

            echo -e "${GREEN}✓ Пользователь $CCS_USER создан${NC}"

            # Сохраняем в конфигурацию
            save_config "CCS_USER" "$CCS_USER" "Created username"
            # Пароль сохраняется как пустое поле для возможности ручного заполнения
            save_config "CCS_PASSWORD" "" "User password (fill manually for automation)"
            save_config "CCS_UID" "$next_uid" "User UID"
            save_config "USER_CREATION" "new" "User creation type: new - created new, existing - used existing"
            break

        elif [ "$user_choice" -ge 1 ] && [ "$user_choice" -le "${#existing_users_array[@]}" ]; then
            # Использование существующего пользователя по номеру
            CCS_USER="${existing_users_array[$((user_choice-1))]}"

            save_config "USER_CHOICE" "$user_choice" "Action choice: 0 - create new user, 1+ - use existing by index"

            if id "$CCS_USER" &>/dev/null; then
                echo -e "${GREEN}✓ Будет использован пользователь: $CCS_USER${NC}"

                # Убеждаемся что пользователь в нужных группах
                usermod -aG sudo "$CCS_USER" 2>/dev/null || usermod -aG wheel "$CCS_USER" 2>/dev/null || true
                usermod -aG adm "$CCS_USER" 2>/dev/null || true
                loginctl enable-linger "$CCS_USER" 2>/dev/null || true

                save_config "CCS_USER" "$CCS_USER" "Username used"
                save_config "USER_CREATION" "existing" "User creation type: new - created new, existing - used existing"
                break
            else
                echo -e "${RED}Пользователь $CCS_USER не найден${NC}"
            fi
        else
            if [ ${#existing_users_array[@]} -gt 0 ]; then
                echo -e "${YELLOW}Введите число от 0 до ${#existing_users_array[@]}${NC}"
            else
                echo -e "${YELLOW}Введите 0${NC}"
            fi
        fi
    else
        if [ ${#existing_users_array[@]} -gt 0 ]; then
            echo -e "${YELLOW}Введите число от 0 до ${#existing_users_array[@]}${NC}"
        else
            echo -e "${YELLOW}Введите 0${NC}"
        fi
    fi
done

# Настройка переменных путей
CCS_HOME=$(eval echo ~$CCS_USER)
DEPLOY_DIR="$CCS_HOME/CCS.Deployment"

# Добавление SSH ключа
add_ssh_key_to_user "$CCS_USER" "$(get_config "USER_CREATION")"

BACKUP_DIR="$CCS_HOME/CCS.Deployment.backup.$(date +%Y%m%d_%H%M%S)"

echo -e "${BLUE}Домашняя директория пользователя: $CCS_HOME${NC}"

# Rootless режим: отключаем установку rootful-компонентов и диагностику

# --------------------------------------------------------------------
# Rootless egress (nftables per-user) — установка сервиса/таймера (идемпотентно)
# --------------------------------------------------------------------

find_repo_root_for_rootless_egress() {
    local cand
    # 1) Относительно расположения текущего скрипта
    cand="$(cd "$(dirname "$0")/.." 2>/dev/null && pwd 2>/dev/null)"
    if [ -n "$cand" ] && [ -f "$cand/prepareOS/ccs_egress_guard_rootless.sh" ]; then
        echo "$cand"; return 0
    fi
    # 2) DEPLOY_DIR, если определён
    if [ -n "$DEPLOY_DIR" ] && [ -f "$DEPLOY_DIR/prepareOS/ccs_egress_guard_rootless.sh" ]; then
        echo "$DEPLOY_DIR"; return 0
    fi
    # 3) Текущая директория (на случай запуска из корня репозитория)
    if [ -f "prepareOS/ccs_egress_guard_rootless.sh" ]; then
        echo "$(pwd)"; return 0
    fi
    echo ""; return 1
}

install_rootless_egress_timer() {
    local repo_root
    repo_root="$(find_repo_root_for_rootless_egress || true)"
    if [ -z "$repo_root" ]; then
        echo -e "${YELLOW}[rootless-egress] Источники скриптов не найдены. Пропуск установки таймера.${NC}"
        return 0
    fi

    local BIN_DIR="/usr/local/bin"
    local SYS_UNIT_DIR="/etc/systemd/system"

    # Скрипты
    install -Dm755 "$repo_root/prepareOS/ccs_egress_guard_rootless.sh" "$BIN_DIR/ccs_egress_guard_rootless.sh"
    install -Dm755 "$repo_root/prepareOS/ccs_egress_guard_rootless_all.sh" "$BIN_DIR/ccs_egress_guard_rootless_all.sh"

    # systemd units (service + timer)
    install -Dm644 "$repo_root/prepareOS/systemd/ccs-rootless-egress.service" "$SYS_UNIT_DIR/ccs-rootless-egress.service"
    install -Dm644 "$repo_root/prepareOS/systemd/ccs-rootless-egress.timer" "$SYS_UNIT_DIR/ccs-rootless-egress.timer"

    systemctl daemon-reload >/dev/null 2>&1 || true
    # Включаем таймер и запускаем немедленно (идемпотентно)
    systemctl enable --now ccs-rootless-egress.timer >/dev/null 2>&1 || true

    echo -e "${BLUE}[rootless-egress] Установлены скрипты и включён таймер ccs-rootless-egress.timer${NC}"
}

# Выполним установку таймера прямо сейчас (идемпотентно):
install_rootless_egress_timer

# === Запрос: Получить/Обновить CCS.Deployment сейчас? (по умолчанию Да) ===
DO_REPO=1
echo ""
echo -e "${BLUE}Получить/обновить репозиторий CCS.Deployment сейчас? (Y/n) [по умолчанию: Y]${NC}"
read -r WANT_REPO
if [[ $WANT_REPO =~ ^[Nn]$ ]]; then
    DO_REPO=0
    echo -e "${YELLOW}Шаг получения/обновления репозитория будет пропущен по вашему выбору.${NC}"
fi

if [ "$DO_REPO" = 1 ]; then
    # Если директория существует и это уже git-репозиторий — не трогаем (никаких бэкапов/перемещений)
    if [ -d "$DEPLOY_DIR/.git" ]; then
        echo -e "${GREEN}Найдена существующая установка CCS.Deployment в $DEPLOY_DIR — клонирование будет пропущено${NC}"
    else
        # Директория есть, но это не git-репозиторий — сделаем резервную копию и подготовим место
        if [ -d "$DEPLOY_DIR" ]; then
            echo -e "${YELLOW}Обнаружен каталог $DEPLOY_DIR без .git${NC}"
            echo -e "${BLUE}Создаем резервную копию в: $BACKUP_DIR${NC}"
            mv "$DEPLOY_DIR" "$BACKUP_DIR"
            chown -R "$CCS_USER:$CCS_USER" "$BACKUP_DIR"
        fi
    fi
fi

# Выбор репозитория (как в оригинале)
if [ "$DO_REPO" = 1 ]; then
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}                      ВЫБОР РЕПОЗИТОРИЯ                           ${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
echo ""

echo -e "${BLUE}Выберите репозиторий для клонирования:${NC}"
# Выводим репозитории в правильном порядке
echo "1) ${REPO_DESCRIPTIONS[1]}: ${REPO_URLS[1]}"
echo "2) ${REPO_DESCRIPTIONS[2]}: ${REPO_URLS[2]}"
echo "3) Ввести URL вручную"

while true; do
    # Используем значение из конфига как значение по умолчанию
    default_repo_choice="${REPO_CHOICE:-}"
    default_repo_url="${REPO_URL:-}"
    default_repo_service="${REPO_SERVICE:-}"

    if [ -n "$default_repo_choice" ] && [ -n "$default_repo_url" ]; then
        # Если есть сохраненный выбор репозитория
        if [ "$default_repo_choice" = "1" ] || [ "$default_repo_choice" = "2" ]; then
            # Проверяем соответствие URL из конфига с текущим URL для данного выбора
            if [ "$default_repo_url" = "${REPO_URLS[$default_repo_choice]}" ]; then
                echo -e "${GREEN}Найден репозиторий из конфигурации: ${REPO_NAMES[$default_repo_choice]}${NC}"
                read -p "Выберите вариант (1-3) [Enter - использовать ${REPO_NAMES[$default_repo_choice]}, или новый выбор]: " repo_choice

                repo_choice=${repo_choice:-$default_repo_choice}

                if [ "$repo_choice" = "$default_repo_choice" ]; then
                    REPO_URL="$default_repo_url"
                    # Проставим выбор и сервис, если ранее не сохранялся
                    repo_choice="$default_repo_choice"
                    if [ -z "$default_repo_service" ]; then
                        if [ "$repo_choice" = "1" ] || [ "$repo_choice" = "2" ]; then
                            REPO_SERVICE="${REPO_NAMES[$repo_choice]}"
                        else
                            REPO_SERVICE="Custom"
                        fi
                    else
                        REPO_SERVICE="$default_repo_service"
                    fi
                    echo -e "${GREEN}✓ Используется репозиторий из конфига: ${REPO_NAMES[$default_repo_choice]}${NC}"
                    break
                fi
            else
                echo -e "${YELLOW}URL репозитория изменился с момента создания конфига${NC}"
            fi
        elif [ "$default_repo_choice" = "3" ]; then
            echo -e "${GREEN}Найден пользовательский репозиторий из конфигурации: $default_repo_url${NC}"
            read -p "Выберите вариант (1-3) [Enter - использовать $default_repo_url, или новый выбор]: " repo_choice

            repo_choice=${repo_choice:-3}

            if [ "$repo_choice" = "3" ]; then
                REPO_URL="$default_repo_url"
                # Проставим выбор и сервис для кастомного URL
                repo_choice=3
                REPO_SERVICE="Custom"
                echo -e "${GREEN}✓ Используется репозиторий из конфига: $default_repo_url${NC}"
                break
            fi
        fi
    fi

    # Стандартная логика выбора с рекомендуемым значением по умолчанию
    if [ -n "$default_repo_choice" ] && [ -z "$repo_choice" ]; then
        read -p "Выберите вариант (1-3) [по умолчанию из конфига: $default_repo_choice]: " repo_choice
        repo_choice=${repo_choice:-$default_repo_choice}
    else
        # Рекомендуемое значение по умолчанию - GitFlic для России
        read -p "Выберите вариант (1-3) [по умолчанию: 1 - GitFlic (рекомендуется)]: " repo_choice
        repo_choice=${repo_choice:-1}
    fi

    case $repo_choice in
        1)
            REPO_URL="${REPO_URLS[1]}"
            REPO_SERVICE="${REPO_NAMES[1]}"
            break
            ;;
        2)
            REPO_URL="${REPO_URLS[2]}"
            REPO_SERVICE="${REPO_NAMES[2]}"
            break
            ;;
        3)
            # Используем значение из конфига для custom URL
            default_repo_url="${REPO_URL:-}"
            if [ -n "$default_repo_url" ]; then
                read -p "Введите URL репозитория [по умолчанию из конфига: $default_repo_url]: " REPO_URL
                REPO_URL=${REPO_URL:-$default_repo_url}
            else
                read -p "Введите URL репозитория: " REPO_URL
            fi
            REPO_SERVICE="Custom"
            break
            ;;
        *)
            echo -e "${YELLOW}Введите число от 1 до 3${NC}"
            ;;
    esac
done

# Сохраняем выбор репозитория
# Подстраховка: если repo_choice не установлен, вычислим его по REPO_URL
if [ -z "$repo_choice" ]; then
    if [ "$REPO_URL" = "${REPO_URLS[1]}" ]; then
        repo_choice=1
        REPO_SERVICE="${REPO_NAMES[1]}"
    elif [ "$REPO_URL" = "${REPO_URLS[2]}" ]; then
        repo_choice=2
        REPO_SERVICE="${REPO_NAMES[2]}"
    else
        repo_choice=3
        [ -z "$REPO_SERVICE" ] && REPO_SERVICE="Custom"
    fi
fi
save_config "REPO_CHOICE" "$repo_choice" "Repository choice: 1 - GitFlic, 2 - GitHub, 3 - Custom URL"
save_config "REPO_URL" "$REPO_URL" "Repository URL"
save_config "REPO_SERVICE" "$REPO_SERVICE" "Repository service"

# Выбор ветки с улучшенной логикой
default_branch_from_config="${BRANCH:-}"
if [ -n "$default_branch_from_config" ]; then
    echo -e "${GREEN}Найдена ветка из конфигурации: $default_branch_from_config${NC}"
    read -p "Введите название ветки [Enter - использовать $default_branch_from_config, или введите новое]: " BRANCH
    BRANCH=${BRANCH:-$default_branch_from_config}

    if [ "$BRANCH" = "$default_branch_from_config" ]; then
        echo -e "${GREEN}✓ Используется ветка из конфига: $BRANCH${NC}"
    fi
else
    default_branch="master"
    read -p "Введите название ветки [по умолчанию: $default_branch]: " BRANCH
    BRANCH=${BRANCH:-$default_branch}
fi

# Сохраняем выбор ветки
save_config "BRANCH" "$BRANCH" "Selected repository branch"

# Сохраняем пути
save_config "CCS_HOME" "$CCS_HOME" "User home directory"
save_config "DEPLOY_DIR" "$DEPLOY_DIR" "CCS.Deployment installation directory"
save_config "PREPARE_DIR" "$PREPARE_DIR" "Management scripts directory"

echo -e "${BLUE}Настройки клонирования:${NC}"
echo -e "Пользователь: ${YELLOW}$CCS_USER${NC}"
echo -e "Репозиторий: ${YELLOW}$REPO_URL${NC}"
echo -e "Ветка: ${YELLOW}$BRANCH${NC}"
echo -e "Директория: ${YELLOW}$DEPLOY_DIR${NC}"

# Подтверждение
# Используем значение из конфига для значения по умолчанию
default_confirm=$(get_config "INSTALL_CONFIRM" "y")
prompt_confirm="Y/n"
[[ $default_confirm =~ ^[Nn]$ ]] && prompt_confirm="y/N"

read -p "Продолжить? ($prompt_confirm) [по умолчанию: $default_confirm]: " confirm
confirm=${confirm:-$default_confirm}

# Сохраняем решение в конфигурацию
save_config "INSTALL_CONFIRM" "$confirm" "Installation confirmation before clone"

if [[ $confirm =~ ^[Nn]$ ]]; then
    echo -e "${YELLOW}Установка отменена${NC}"
    exit 0
fi # DO_REPO

# Клонирование репозитория
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}                    КЛОНИРОВАНИЕ РЕПОЗИТОРИЯ                      ${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
echo ""

# Создаем .ssh директорию для пользователя
mkdir -p "$CCS_HOME/.ssh"
chmod 700 "$CCS_HOME/.ssh"
chown "$CCS_USER:$CCS_USER" "$CCS_HOME/.ssh"

# Выбор метода авторизации
AUTH_METHOD=""

# Генерируем ключ для JSON хранилища токенов
GIT_AUTH_KEY="${REPO_URL}__${BRANCH}"
GIT_TOKENS_FILE="$CCS_HOME/.git_tokens.json"

# Пытаемся получить существующую авторизацию из JSON если файл существует
SAVED_AUTH_TYPE=""
SAVED_USERNAME=""
SAVED_TOKEN=""

# Функция для чтения из JSON через python (так как мы еще не склонировали репозиторий с утилитами, но python3 должен быть)
get_json_val() {
    local key=$1
    python3 -c "import sys, json; print(json.load(sys.stdin).get('$key', ''))" 2>/dev/null
}

if [ -f "$GIT_TOKENS_FILE" ]; then
    AUTH_DATA=$(python3 -c "import sys, json; data=json.load(open('$GIT_TOKENS_FILE')); print(json.dumps(data.get('$GIT_AUTH_KEY', {})))" 2>/dev/null)
    if [ -n "$AUTH_DATA" ] && [ "$AUTH_DATA" != "{}" ]; then
        SAVED_AUTH_TYPE=$(echo "$AUTH_DATA" | get_json_val "auth_type")
        SAVED_USERNAME=$(echo "$AUTH_DATA" | get_json_val "username")
        SAVED_TOKEN=$(echo "$AUTH_DATA" | get_json_val "token")
    fi
fi

# Если данных нет в JSON, пробуем взять из конфига .cfg (обратная совместимость)
if [ -z "$SAVED_AUTH_TYPE" ]; then
    case "${AUTH_METHOD_CHOICE:-}" in
        1) SAVED_AUTH_TYPE="ssh" ;;
        2) SAVED_AUTH_TYPE="public" ;;
        3) SAVED_AUTH_TYPE="token" ;;
    esac
    SAVED_USERNAME="${GIT_USERNAME:-}"
    SAVED_TOKEN="${GIT_TOKEN:-}"
fi

    if [ -n "$SAVED_AUTH_TYPE" ]; then
        echo -e "${BLUE}Обнаружены сохраненные данные авторизации:${NC}"
        echo -e "Метод: ${YELLOW}$SAVED_AUTH_TYPE${NC}"
        if [ "$SAVED_AUTH_TYPE" = "token" ]; then
            echo -e "Пользователь: ${YELLOW}$SAVED_USERNAME${NC}"
        fi
        echo ""
        read -p "Использовать сохраненные данные? (Y/n) [по умолчанию: Y]: " use_saved
        use_saved=${use_saved:-y}
        if [[ $use_saved =~ ^[Yy]$ ]]; then
            AUTH_METHOD="$SAVED_AUTH_TYPE"
            GIT_USERNAME="$SAVED_USERNAME"
            GIT_TOKEN="$SAVED_TOKEN"
            echo -e "${GREEN}✓ Используются сохраненные данные авторизации${NC}"
        else
            # Если пользователь отказался использовать сохраненные данные, сбрасываем их
            GIT_USERNAME=""
            GIT_TOKEN=""
            SAVED_AUTH_TYPE=""
        fi
    fi

if [ -z "$AUTH_METHOD" ]; then
    echo -e "${BLUE}Выберите метод авторизации для репозитория:${NC}"
    echo "1) Публичный доступ (без авторизации)"
    echo "2) SSH ключ (рекомендуется)"
    echo "3) Токен"

    # Определяем дефолтный выбор
    default_choice="3"
    case "$SAVED_AUTH_TYPE" in
        "public") default_choice="1" ;;
        "ssh")    default_choice="2" ;;
        "token")  default_choice="3" ;;
    esac

    read -p "Ваш выбор [$default_choice]: " auth_choice
    auth_choice=${auth_choice:-$default_choice}

    case $auth_choice in
        1) AUTH_METHOD="public" ;;
        2) AUTH_METHOD="ssh" ;;
        3) AUTH_METHOD="token" ;;
        *) AUTH_METHOD="public" ;;
    esac
fi

# Сохраняем выбор метода в конфиг .cfg для совместимости
case "$AUTH_METHOD" in
    "ssh")    save_config "AUTH_METHOD_CHOICE" "1" "Auth method: SSH" ;;
    "public") save_config "AUTH_METHOD_CHOICE" "2" "Auth method: Public" ;;
    "token")  save_config "AUTH_METHOD_CHOICE" "3" "Auth method: Token" ;;
esac

# Если выбран токен, запрашиваем данные если их еще нет
if [ "$AUTH_METHOD" = "token" ] && [ -z "$GIT_TOKEN" ]; then
    echo -e "${BLUE}Настройка авторизации по токену...${NC}"

    # Инструкции
    if [[ "$REPO_URL" == *"github.com"* ]]; then
        echo -e "${YELLOW}Получите 'Personal Access Token' (classic) с правами 'repo' в настройках GitHub.${NC}"
    elif [[ "$REPO_URL" == *"gitflic.ru"* ]]; then
        echo -e "${YELLOW}Получите 'Токен доступа' в настройках вашего проекта на GitFlic (Настройки -> Токены доступа).${NC}"
    fi

    # Если ранее был введен логин, предлагаем его. Иначе пусто.
    if [ -n "$GIT_USERNAME" ]; then
        read -p "Введите имя пользователя (логин): [${GIT_USERNAME}] " git_username
        GIT_USERNAME=${git_username:-${GIT_USERNAME}}
    else
        read -p "Введите имя пользователя (логин): " GIT_USERNAME
    fi

    if [ -n "$GIT_TOKEN" ]; then
        echo -e "${GREEN}Обнаружен сохраненный токен${NC}"
        read -p "Использовать его? (Y/n) [по умолчанию: Y]: " use_saved_token
        use_saved_token=${use_saved_token:-y}
        if [[ ! $use_saved_token =~ ^[Yy]$ ]]; then
            read -sp "Введите НОВЫЙ токен: " git_token
            echo ""
            GIT_TOKEN="$git_token"
        fi
    else
        read -sp "Введите токен: " git_token
        echo ""
        GIT_TOKEN="$git_token"
    fi

    if [ -z "$GIT_USERNAME" ] || [ -z "$GIT_TOKEN" ]; then
        echo -e "${RED}Ошибка: Имя пользователя и токен обязательны для этого метода.${NC}"
        exit 1
    fi
fi

# Сохраняем переменные в конфиг .cfg
if [ "$AUTH_METHOD" = "token" ]; then
    save_config "GIT_USERNAME" "$GIT_USERNAME" "Username for token auth"
    if [ -n "$GIT_TOKEN" ]; then
        save_config "GIT_TOKEN" "$GIT_TOKEN" "Deployment token"
    fi
fi

# Подготавливаем JSON для сохранения в .git_tokens.json позже
AUTH_JSON=$(python3 -c "import json, sys; print(json.dumps({'auth_type': sys.argv[1], 'username': sys.argv[2], 'token': sys.argv[3]}))" "$AUTH_METHOD" "$GIT_USERNAME" "$GIT_TOKEN")

# Выполняем клонирование в зависимости от выбранного метода
if [ "$AUTH_METHOD" = "token" ]; then
    echo -e "${BLUE}Попытка клонирования через HTTPS с токеном...${NC}"
    if [ -d "$DEPLOY_DIR/.git" ]; then
        echo -e "${GREEN}Репозиторий уже существует: ${DEPLOY_DIR} — пропускаем клонирование с токеном${NC}"
    else
        # Формируем URL с токеном (аналогично логике в main.sh)
        # GitFlic HTTPS: https://gitflic.ru/project/user/repo.git
        TOKEN_URL="https://${GIT_USERNAME}:${GIT_TOKEN}@${REPO_URL#https://}"

        # Убеждаемся в наличии .git для GitFlic
        if [[ "$TOKEN_URL" == *gitflic.ru* ]] && [[ "$TOKEN_URL" != *.git ]]; then
            TOKEN_URL="${TOKEN_URL}.git"
        fi

        if sudo -u "$CCS_USER" git clone -b "$BRANCH" "$TOKEN_URL" "$DEPLOY_DIR"; then
            echo -e "${GREEN}✓ Репозиторий успешно склонирован через токен${NC}"
            JUST_CLONED=1

            # Сохраняем данные в новый формат .git_tokens.json
            echo -e "${BLUE}Сохраняем данные авторизации в .git_tokens.json...${NC}"
            KEY="${REPO_URL}__${BRANCH}"
            # Убеждаемся, что python3 и утилита доступны
            if sudo -u "$CCS_USER" test -f "$DEPLOY_DIR/git_auth_utils.py"; then
                sudo -u "$CCS_USER" python3 "$DEPLOY_DIR/git_auth_utils.py" "$CCS_HOME/.git_tokens.json" set "$KEY" "$AUTH_JSON" > /dev/null
            else
                # Если утилиты еще нет (хотя после клона должна быть), сохраняем напрямую через python
                sudo -u "$CCS_USER" python3 -c "import json, sys, os; f='$CCS_HOME/.git_tokens.json'; data=json.load(open(f)) if os.path.exists(f) else {}; data['$KEY']=json.loads(sys.argv[1]); json.dump(data, open(f, 'w'), indent=4)" "$AUTH_JSON"
            fi
        else
            echo -e "${RED}Не удалось склонировать репозиторий через токен${NC}"
            exit 1
        fi
    fi
fi

if [ "$AUTH_METHOD" = "public" ] || [ "$AUTH_METHOD" = "https" ]; then
    echo -e "${BLUE}Попытка клонирования через HTTPS (публичный доступ)...${NC}"
    if [ -d "$DEPLOY_DIR/.git" ]; then
        echo -e "${GREEN}Репозиторий уже существует: ${DEPLOY_DIR} — пропускаем клонирование HTTPS${NC}"
    else
        if sudo -u "$CCS_USER" git clone -b "$BRANCH" "$REPO_URL" "$DEPLOY_DIR" 2>/dev/null; then
            echo -e "${GREEN}✓ Репозиторий успешно склонирован через HTTPS${NC}"
            AUTH_METHOD="https"
            JUST_CLONED=1

            # Сохраняем информацию о публичном доступе
            echo -e "${BLUE}Сохраняем данные авторизации (public) в .git_tokens.json...${NC}"
            KEY="${REPO_URL}__${BRANCH}"
            if sudo -u "$CCS_USER" test -f "$DEPLOY_DIR/git_auth_utils.py"; then
                sudo -u "$CCS_USER" python3 "$DEPLOY_DIR/git_auth_utils.py" "$CCS_HOME/.git_tokens.json" set "$KEY" "$AUTH_JSON" > /dev/null
            else
                sudo -u "$CCS_USER" python3 -c "import json, sys, os; f='$CCS_HOME/.git_tokens.json'; data=json.load(open(f)) if os.path.exists(f) else {}; data['$KEY']=json.loads(sys.argv[1]); json.dump(data, open(f, 'w'), indent=4)" "$AUTH_JSON"
            fi
        else
            echo -e "${YELLOW}HTTPS клонирование не удалось, переходим к SSH...${NC}"
            AUTH_METHOD="ssh_fallback"
        fi
    fi
fi

# SSH авторизация (если выбрана или как fallback)
if [ "$AUTH_METHOD" = "ssh" ] || [ "$AUTH_METHOD" = "ssh_fallback" ]; then
    echo -e "${BLUE}Настройка SSH авторизации...${NC}"

    # Конвертируем HTTPS URL в SSH
    if [[ "$REPO_URL" == https://github.com/* ]]; then
        SSH_URL="git@github.com:${REPO_URL#https://github.com/}"
        SSH_URL="${SSH_URL%.git}.git"
    elif [[ "$REPO_URL" == https://gitflic.ru/* ]]; then
        SSH_URL="git@gitflic.ru:${REPO_URL#https://gitflic.ru/project/}"
        SSH_URL="${SSH_URL%.git}.git"
    else
        echo -e "${RED}Не удалось определить SSH URL для $REPO_URL${NC}"
        exit 1
    fi

    # Генерируем SSH ключ
    SSH_KEY_FILE="$CCS_HOME/.ssh/id_rsa"
    if [ ! -f "$SSH_KEY_FILE" ]; then
        echo -e "${YELLOW}Генерируем SSH ключ для пользователя $CCS_USER...${NC}"
        sudo -u "$CCS_USER" ssh-keygen -t rsa -b 4096 -f "$SSH_KEY_FILE" -N "" -C "$CCS_USER@$(hostname)-ccs-deployment"
    fi

    # Показываем публичный ключ
    echo -e "${MAGENTA}╔════════════════════════════════════════════════════════════════════╗${NC}"
    echo -e "${MAGENTA}║                  SSH КЛЮЧ ДЛЯ ПОЛЬЗОВАТЕЛЯ $CCS_USER                  ║${NC}"
    echo -e "${MAGENTA}╚════════════════════════════════════════════════════════════════════╝${NC}"
    echo -e "${YELLOW}"
    cat "$SSH_KEY_FILE.pub"
    echo -e "${NC}"
    echo -e "${BLUE}Добавьте этот ключ в ваш Git сервис и нажмите Enter для продолжения...${NC}"
    read

    # Инструкции по добавлению ключа
    echo -e "${BLUE}╔════════════════════════════════════════════════════════════════════╗${NC}"
    echo -e "${BLUE}║                   ИНСТРУКЦИИ ПО ДОБАВЛЕНИЮ КЛЮЧА                 ║${NC}"
    echo -e "${BLUE}╚════════════════════════════════════════════════════════════════════╝${NC}"

    case "$REPO_SERVICE" in
        "GitHub")
            echo -e "${BLUE}1. Откройте: https://github.com/settings/keys${NC}"
            echo -e "${BLUE}2. Нажмите 'New SSH key'${NC}"
            echo -e "${BLUE}3. Вставьте публичный ключ выше${NC}"
            echo -e "${BLUE}4. Нажмите 'Add SSH key'${NC}"
            ;;
        "GitFlic")
            echo -e "${BLUE}1. Откройте: https://gitflic.ru/settings/keys${NC}"
            echo -e "${BLUE}2. Нажмите 'Добавить ключ'${NC}"
            echo -e "${BLUE}3. Вставьте публичный ключ выше${NC}"
            echo -e "${BLUE}4. Нажмите 'Добавить'${NC}"
            ;;
    esac

    echo ""
    echo -e "${YELLOW}После добавления ключа нажмите Enter...${NC}"
    read

    # Добавляем хост в known_hosts
    ssh_host=$(echo "$SSH_URL" | cut -d'@' -f2 | cut -d':' -f1)
    if ! sudo -u "$CCS_USER" ssh-keygen -F "$ssh_host" >/dev/null 2>&1; then
        echo -e "${BLUE}Добавляем $ssh_host в known_hosts...${NC}"
        sudo -u "$CCS_USER" ssh-keyscan "$ssh_host" >> "$CCS_HOME/.ssh/known_hosts" 2>/dev/null
    fi

    # Пробуем SSH клонирование
    echo -e "${BLUE}Попытка клонирования через SSH...${NC}"
    if [ -d "$DEPLOY_DIR/.git" ]; then
        echo -e "${GREEN}Репозиторий уже существует: ${DEPLOY_DIR} — пропускаем клонирование SSH${NC}"
    else
        # Сохраняем информацию об SSH авторизации
        echo -e "${BLUE}Сохраняем данные авторизации (ssh) в .git_tokens.json...${NC}"
        KEY="${REPO_URL}__${BRANCH}"
        if sudo -u "$CCS_USER" test -f "$DEPLOY_DIR/git_auth_utils.py"; then
            sudo -u "$CCS_USER" python3 "$DEPLOY_DIR/git_auth_utils.py" "$CCS_HOME/.git_tokens.json" set "$KEY" "$AUTH_JSON" > /dev/null
        else
            sudo -u "$CCS_USER" python3 -c "import json, sys, os; f='$CCS_HOME/.git_tokens.json'; data=json.load(open(f)) if os.path.exists(f) else {}; data['$KEY']=json.loads(sys.argv[1]); json.dump(data, open(f, 'w'), indent=4)" "$AUTH_JSON"
        fi

        if sudo -u "$CCS_USER" git clone -b "$BRANCH" "$SSH_URL" "$DEPLOY_DIR"; then
            echo -e "${GREEN}✓ Репозиторий успешно склонирован через SSH${NC}"
            AUTH_METHOD="ssh"
            JUST_CLONED=1

            # Сохраняем SSH настройки
            save_config "SSH_URL" "$SSH_URL" "SSH repository URL"

            # Сохраняем информацию об SSH авторизации в новый формат
            echo -e "${BLUE}Сохраняем данные авторизации (SSH) в .git_tokens.json...${NC}"
            AUTH_JSON="{\"auth_type\": \"ssh\", \"username\": \"\", \"token\": \"\"}"
            KEY="${REPO_URL}__${BRANCH}"
            if sudo -u "$CCS_USER" test -f "$DEPLOY_DIR/git_auth_utils.py"; then
                sudo -u "$CCS_USER" python3 "$DEPLOY_DIR/git_auth_utils.py" "$CCS_HOME/.git_tokens.json" set "$KEY" "$AUTH_JSON" > /dev/null
            fi
        else
            echo -e "${RED}Не удалось склонировать репозиторий${NC}"
            exit 1
        fi
    fi
fi

# Сохраняем метод аутентификации
save_config "AUTH_METHOD" "$AUTH_METHOD" "Authentication method: https or ssh"

# Проверяем структуру и устанавливаем права
if [ ! -f "$DEPLOY_DIR/start_template.sh" ] || [ ! -d "$DEPLOY_DIR/prepareOS" ]; then
    echo -e "${RED}ОШИБКА: Неверная структура проекта${NC}"
    exit 1
fi
# Закрываем блок DO_REPO для выбора/клонирования/подтверждения
fi

if [ "$DO_REPO" = 1 ]; then
    chown -R "$CCS_USER:$CCS_USER" "$DEPLOY_DIR"
    echo -e "${GREEN}✓ Репозиторий CCS.Deployment готов${NC}"
fi

# -------------------------------------------------
# (Опционально) Предложить обновить репозиторий git
# Пропускаем, если только что склонировали в рамках этого запуска
# -------------------------------------------------
if [ "$DO_REPO" = 1 ] && [ "$JUST_CLONED" != "1" ] && [ -d "$DEPLOY_DIR/.git" ]; then
    echo ""
    echo -e "${BLUE}Обновить CCS.Deployment из удалённого репозитория сейчас? (y/N)${NC}"
    read -r want_update
    if [[ $want_update =~ ^[Yy]$ ]]; then
        echo -e "${BLUE}Подготовка к обновлению репозитория...${NC}"

        # Если используется токен, обновляем remote URL
        if [ "$AUTH_METHOD" = "token" ] && [ -n "$GIT_TOKEN" ]; then
            # Формируем URL с токеном
            U_REPO_URL="$REPO_URL"
            # Конвертируем SSH URL в HTTPS если нужно (для токенов нужен HTTPS)
            if [[ "$U_REPO_URL" == git@github.com:* ]]; then
                U_REPO_URL="https://github.com/${U_REPO_URL#git@github.com:}"
            elif [[ "$U_REPO_URL" == git@gitflic.ru:* ]]; then
                U_REPO_URL="https://gitflic.ru/project/${U_REPO_URL#git@gitflic.ru:}"
            fi

            # Убеждаемся в наличии .git для GitFlic
            if [[ "$U_REPO_URL" == *gitflic.ru* ]] && [[ "$U_REPO_URL" != *.git ]]; then
                U_REPO_URL="${U_REPO_URL}.git"
            fi

            # Формируем финальный URL с авторизацией
            if [ -n "$GIT_USERNAME" ] && [ -n "$GIT_TOKEN" ] && [ -n "$U_REPO_URL" ]; then
                U_TOKEN_URL="https://${GIT_USERNAME}:${GIT_TOKEN}@${U_REPO_URL#https://}"

                # Обновляем remote URL от имени пользователя
                sudo -u "$CCS_USER" git -C "$DEPLOY_DIR" remote set-url origin "$U_TOKEN_URL"
            else
                echo -e "${YELLOW}Предупреждение: Недостаточно данных для обновления токена в git remote (URL: $U_REPO_URL, User: $GIT_USERNAME). Пропускаем set-url.${NC}"
            fi
        fi

    # Все операции git выполняем от имени пользователя, чтобы не ломать владельца файлов
    sudo -u "$CCS_USER" bash -lc "
set -e
cd \"$DEPLOY_DIR\" || exit 0

DIRTY=\$(git status --porcelain 2>/dev/null || true)
STASHED=0
if [ -n \"\$DIRTY\" ]; then
  echo \"Обнаружены незакоммиченные изменения.\"
  echo \"1) Сохранить во временный stash и продолжить [по умолчанию]\"
  echo \"2) Отменить обновление\"
  read -p \"Ваш выбор (1-2) [по умолчанию: 1]: \" choice
  choice=\${choice:-1}
  if [ \"\$choice\" = \"1\" ]; then
    git stash push -u -m \"ccs-deployment-auto-stash-\$(date +%F_%T)\" || true
    STASHED=1
  else
    echo \"Обновление отменено пользователем.\"
    exit 0
  fi
fi

git fetch --all --prune || true
if ! git pull --ff-only; then
  echo -e \"${YELLOW}Fast-forward недоступен. Возможны конфликты.${NC}\"
  read -p \"Попробовать rebase? (y/N) [по умолчанию: N]: \" rb
  rb=\${rb:-n}
  if [[ \$rb =~ ^[Yy]\$ ]]; then
    git pull --rebase || true
  else
    echo \"Пропущено обновление: fast-forward недоступен.\"
  fi
fi

if [ \"\$STASHED\" = \"1\" ]; then
  read -p \"Применить сохранённые изменения (stash pop) сейчас? (y/N) [по умолчанию: N]: \" ap
  ap=\${ap:-n}
  if [[ \$ap =~ ^[Yy]\$ ]]; then
    git stash pop || true
  fi
fi

echo \"Текущий коммит: \$(git rev-parse --short HEAD) - \$(git log -1 --oneline)\"
" || echo -e "${YELLOW}Предупреждение: обновление репозитория завершилось с предупреждениями/ошибками${NC}"
    fi
fi

# Создаем копию скриптов подготовки
PREPARE_DIR="$CCS_HOME/ccs-prepare"
echo -e "${BLUE}Подготовка скриптов управления в: $PREPARE_DIR${NC}"

# Всегда после получения или обновления репозитория — кладём cert.sh в домашний каталог root
if [ -f "$DEPLOY_DIR/prepareOS/cert.sh" ]; then
    ROOT_HOME=$(eval echo ~root)
    cp -f "$DEPLOY_DIR/prepareOS/cert.sh" "$ROOT_HOME/cert.sh"
    chmod +x "$ROOT_HOME/cert.sh" 2>/dev/null || true
    echo -e "${GREEN}✓ Скопирован скрипт выпуска сертификатов: ${ROOT_HOME}/cert.sh${NC}"
fi

rm -rf "$PREPARE_DIR" 2>/dev/null || true
mkdir -p "$PREPARE_DIR"

# Копируем все необходимые файлы
cp -r "$DEPLOY_DIR/prepareOS/"* "$PREPARE_DIR/"
cp "$DEPLOY_DIR/funcs.sh" "$PREPARE_DIR/"
cp "$DEPLOY_DIR/log_colors.sh" "$PREPARE_DIR/"

# Создаем симлинк на основной проект
ln -sf "$DEPLOY_DIR" "$PREPARE_DIR/CCS.Deployment"

# Устанавливаем права
chown -R "$CCS_USER:$CCS_USER" "$PREPARE_DIR"
chmod +x "$PREPARE_DIR"/*.sh

echo -e "${GREEN}✓ Скрипты управления готовы${NC}"

# =============================
# Автоподготовка домашней директории пользователя
# Копируем стартовый скрипт и конфиг, если их ещё нет, и выполняем первый запуск
# =============================

echo -e "${BLUE}Подготавливаем рабочее окружение пользователя (${CCS_USER})...${NC}"

# 1) start.sh в домашней директории пользователя
USER_START_SH="$CCS_HOME/start.sh"
if [ ! -f "$USER_START_SH" ]; then
    echo -e "${YELLOW}Копируем стартовый скрипт: ${DEPLOY_DIR}/start_template.sh → ${USER_START_SH}${NC}"
    cp "$DEPLOY_DIR/start_template.sh" "$USER_START_SH"
    chown "$CCS_USER:$CCS_USER" "$USER_START_SH"
    chmod +x "$USER_START_SH"
else
    echo -e "${GREEN}✓ Найден существующий ${USER_START_SH} — пропускаем копирование${NC}"
fi

# 2) config.sh в домашней директории пользователя
USER_CONFIG_SH="$CCS_HOME/config.sh"
if [ ! -f "$USER_CONFIG_SH" ]; then
    echo -e "${YELLOW}Копируем шаблон конфига: ${DEPLOY_DIR}/config_template.sh → ${USER_CONFIG_SH}${NC}"
    cp "$DEPLOY_DIR/config_template.sh" "$USER_CONFIG_SH"
    chown "$CCS_USER:$CCS_USER" "$USER_CONFIG_SH"
    chmod 600 "$USER_CONFIG_SH"
    # Гарантируем, что конфиг в состоянии черновика
    sed -i 's/^config_is_ready=.*/config_is_ready=0/' "$USER_CONFIG_SH" 2>/dev/null || true
else
    echo -e "${GREEN}✓ Найден существующий ${USER_CONFIG_SH} — пропускаем копирование${NC}"
fi

# 2.5) Немедленное применение egress-политики для пользователя (rootless)
# Это гарантирует, что даже до первого срабатывания таймера у пользователя будут верные правила.
if [ -f "/usr/local/bin/ccs_egress_guard_rootless.sh" ]; then
    echo -e "${BLUE}Применяем правила исходящего трафика (egress guard) для $CCS_USER...${NC}"
    /usr/local/bin/ccs_egress_guard_rootless.sh "$CCS_USER" 2>/dev/null || true
fi

# 3) Предложение запустить настройку под пользователем (пока без автозапуска)
echo ""
# Используем значение из конфига для значения по умолчанию
default_want_user_setup=$(get_config "WANT_USER_SETUP" "n")
prompt_want_user_setup="y/N"
[[ $default_want_user_setup =~ ^[Yy]$ ]] && prompt_want_user_setup="Y/n"

echo -e "${BLUE}Запустить настройку под пользователем сейчас? ($prompt_want_user_setup) [по умолчанию: $default_want_user_setup]${NC}"
read -r WANT_USER_SETUP
WANT_USER_SETUP=${WANT_USER_SETUP:-$default_want_user_setup}

# Сохраняем решение в конфигурацию
save_config "WANT_USER_SETUP" "$WANT_USER_SETUP" "User setup after first run: y/n"

if [[ $WANT_USER_SETUP =~ ^[Yy]$ ]]; then
    echo -e "${YELLOW}Функционал автоматического запуска мастера под пользователем пока не готов.${NC}"
    echo -e "${BLUE}Пожалуйста, выполните вручную:${NC}"
    echo -e "  1) su - $CCS_USER"
    echo -e "  2) vim ~/config.sh  # заполните и установите config_is_ready=1"
    echo -e "  3) ./start.sh install db nginx <ваши_сервисы> backup"
else
    echo -e "${YELLOW}Автозапуск настройки под пользователем пропущен (по умолчанию).${NC}"
fi

# Сохраняем финальную информацию в конфигурацию
save_config "INSTALL_STATUS" "completed" "Installation status: completed - success, failed - error"
save_config "INSTALL_DATE" "$(date)" "Installation completion date and time"
save_config "CONFIG_FILE_LOCATION" "$CONFIG_PATH" "Path to this configuration file"

# Копируем конфигурационный файл в домашнюю директорию пользователя
FINAL_CONFIG_PATH="$CCS_HOME/$CONFIG_FILE"
cp "$CONFIG_PATH" "$FINAL_CONFIG_PATH"
chown "$CCS_USER:$CCS_USER" "$FINAL_CONFIG_PATH"
chmod 600 "$FINAL_CONFIG_PATH"

# (Ранее здесь запускался интерактивный мастер пользователя из ccs-prepare. Теперь
# мы выполняем автоматическую инициализацию и выводим понятные следующие шаги.)

# Финальное сообщение с информацией о конфигурации
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
echo -e "${BLUE}                     УСТАНОВКА ЗАВЕРШЕНА                         ${NC}"
echo -e "${BLUE}═══════════════════════════════════════════════════════════════════${NC}"
echo ""
echo -e "${GREEN}CCS.Deployment готов к использованию!${NC}"
echo -e "${YELLOW}Для работы с системой используйте пользователя: $CCS_USER${NC}"
echo -e "${YELLOW}Путь к проекту: $DEPLOY_DIR${NC}"
echo -e "${YELLOW}Стартовый скрипт пользователя: ${USER_START_SH}${NC}"
echo -e "${YELLOW}Конфиг пользователя: ${USER_CONFIG_SH}${NC}"
echo ""
echo -e "${BLUE}Конфигурация установки сохранена в:${NC}"
echo -e "${YELLOW}  Системная: $CONFIG_PATH${NC}"
echo -e "${YELLOW}  Пользователя: $FINAL_CONFIG_PATH${NC}"
echo ""
echo -e "${GREEN}Этот файл конфигурации можно использовать для автоматической${NC}"
echo -e "${GREEN}установки в будущем или для документирования настроек.${NC}"

# Информация о Fail2Ban
F2B_STATUS="не установлен"
if command -v fail2ban-client >/dev/null 2>&1; then
  if systemctl is-active --quiet fail2ban 2>/dev/null; then
    F2B_STATUS="установлен и активен"
  else
    F2B_STATUS="установлен, но не активен"
  fi
fi

echo ""
echo -e "${BLUE}Статус безопасности:${NC}"
echo -e "Fail2Ban: ${YELLOW}${F2B_STATUS}${NC}"
if [ "$F2B_STATUS" != "установлен и активен" ]; then
  echo -e "Под пользователем можно открыть мастер: ${YELLOW}$PREPARE_DIR/start.sh${NC} → меню 'Развертывание CCS' → 'Установка и настройка Fail2Ban'"
fi

echo ""
echo -e "${BLUE}Дальнейшие шаги (под пользователем ${YELLOW}$CCS_USER${BLUE}):${NC}"
cat <<EOS
1) Войти под пользователем:
   su - $CCS_USER

2) Открыть и настроить конфиг (установить config_is_ready=1 после заполнения):
   vim ~/config.sh

3) Установить сервисы из конфига (пример):
   ./start.sh install db nginx ccs_app ccs_app_front backup

4) ПРИ ПРОБЛЕМАХ СО СКАЧИВАНИЕМ ОБРАЗОВ (Docker Hub limit):
   Если возникает ошибка "toomanyrequests", выполните авторизацию:
   podman login docker.io

5) Получить SSL-сертификаты (опционально сейчас, можно позже):
   ~/ccs-prepare/start.sh → Управление SSL сертификатами → Получить Let's Encrypt сертификаты

6) Для доступа к БД через SSH-туннель (пример):
   ssh -N -L 3306:127.0.0.1:33060 $CCS_USER@<server>
   # затем в клиенте БД подключайтесь к localhost:3306
EOS