mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-24 21:30:52 +00:00
Update install_bot.sh
This commit is contained in:
772
install_bot.sh
772
install_bot.sh
@@ -3,141 +3,86 @@ set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
STATE_FILE="$SCRIPT_DIR/.bot_install_state"
|
||||
DEFAULT_INSTALL_PATH="$SCRIPT_DIR"
|
||||
DEFAULT_ENV_FILE=".env"
|
||||
BACKUP_DIR="$SCRIPT_DIR/backups"
|
||||
|
||||
# Util functions
|
||||
log() {
|
||||
printf '\n%s\n' "$1"
|
||||
# Цвета для красивого вывода
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
PURPLE='\033[0;35m'
|
||||
CYAN='\033[0;36m'
|
||||
WHITE='\033[1;37m'
|
||||
NC='\033[0m' # No Color
|
||||
BOLD='\033[1m'
|
||||
|
||||
# Символы для UI
|
||||
CHECK="✓"
|
||||
CROSS="✗"
|
||||
ARROW="➜"
|
||||
STAR="★"
|
||||
GEAR="⚙"
|
||||
|
||||
# Утилиты для вывода
|
||||
print_header() {
|
||||
echo -e "\n${CYAN}${BOLD}╔════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${CYAN}${BOLD}║${NC} ${WHITE}${BOLD}$1${NC}${CYAN}${BOLD}║${NC}"
|
||||
echo -e "${CYAN}${BOLD}╚════════════════════════════════════════════════════════════╝${NC}\n"
|
||||
}
|
||||
|
||||
error() {
|
||||
printf 'Error: %s\n' "$1" >&2
|
||||
print_section() {
|
||||
echo -e "\n${BLUE}${BOLD}${ARROW} $1${NC}"
|
||||
echo -e "${BLUE}─────────────────────────────────────────────────────${NC}"
|
||||
}
|
||||
|
||||
ask_yes_no() {
|
||||
local prompt="$1"
|
||||
local default="$2"
|
||||
local reply
|
||||
while true; do
|
||||
read -rp "$prompt" reply || reply=""
|
||||
if [[ -z "$reply" ]]; then
|
||||
reply="$default"
|
||||
fi
|
||||
case "${reply,,}" in
|
||||
y|yes)
|
||||
return 0
|
||||
;;
|
||||
n|no)
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
echo "Пожалуйста, ответьте 'y' или 'n'."
|
||||
;;
|
||||
esac
|
||||
done
|
||||
print_success() {
|
||||
echo -e "${GREEN}${CHECK} $1${NC}"
|
||||
}
|
||||
|
||||
prompt_value() {
|
||||
local var_name="$1"
|
||||
local prompt="$2"
|
||||
local default_value="$3"
|
||||
local reply
|
||||
if [[ -n "$default_value" ]]; then
|
||||
read -rp "$prompt [$default_value]: " reply || reply=""
|
||||
print_error() {
|
||||
echo -e "${RED}${CROSS} $1${NC}" >&2
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠ $1${NC}"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${CYAN}ℹ $1${NC}"
|
||||
}
|
||||
|
||||
print_status() {
|
||||
local status=$1
|
||||
local text=$2
|
||||
if [[ "$status" == "running" ]]; then
|
||||
echo -e "${GREEN}● ${text}${NC}"
|
||||
elif [[ "$status" == "stopped" ]]; then
|
||||
echo -e "${RED}● ${text}${NC}"
|
||||
else
|
||||
read -rp "$prompt: " reply || reply=""
|
||||
fi
|
||||
if [[ -z "$reply" ]]; then
|
||||
printf '%s' "$default_value"
|
||||
else
|
||||
printf '%s' "$reply"
|
||||
fi
|
||||
}
|
||||
|
||||
escape_env_value() {
|
||||
printf '%s' "$1" | sed 's/\\\\/\\\\\\\\/g; s/"/\\"/g'
|
||||
}
|
||||
|
||||
write_env_file() {
|
||||
local env_file="$1"
|
||||
shift
|
||||
local entries=("$@")
|
||||
{
|
||||
for entry in "${entries[@]}"; do
|
||||
local key=${entry%%=*}
|
||||
local value=${entry#*=}
|
||||
printf '%s="%s"\n' "$key" "$(escape_env_value "$value")"
|
||||
done
|
||||
} >"$env_file"
|
||||
}
|
||||
|
||||
get_env_value() {
|
||||
local key="$1"
|
||||
local env_file="$2"
|
||||
if [[ -f "$env_file" ]]; then
|
||||
local line
|
||||
line=$(grep -E "^${key}=" "$env_file" | tail -n1 || true)
|
||||
if [[ -n "$line" ]]; then
|
||||
printf '%s' "${line#*=}" | sed 's/^"//; s/"$//'
|
||||
return
|
||||
fi
|
||||
fi
|
||||
printf ''
|
||||
}
|
||||
|
||||
ensure_directory_structure() {
|
||||
local path="$1"
|
||||
mkdir -p "$path/logs" "$path/data" "$path/data/backups" "$path/data/referral_qr"
|
||||
chmod -R 755 "$path/logs" "$path/data"
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
sudo chown -R 1000:1000 "$path/logs" "$path/data" || true
|
||||
else
|
||||
chown -R 1000:1000 "$path/logs" "$path/data" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
sync_project_files() {
|
||||
local destination="$1"
|
||||
if [[ "$SCRIPT_DIR" == "$destination" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
log "Синхронизируем файлы проекта в $destination"
|
||||
local excludes=(--exclude '.git' --exclude '__pycache__' --exclude 'logs' --exclude 'data' --exclude '.bot_install_state' --exclude '.env')
|
||||
if command -v rsync >/dev/null 2>&1; then
|
||||
rsync -a --delete "${excludes[@]}" "$SCRIPT_DIR/" "$destination/"
|
||||
else
|
||||
(cd "$SCRIPT_DIR" && tar cf - --exclude='.git' --exclude='__pycache__' --exclude='logs' --exclude='data' --exclude='.bot_install_state' --exclude='.env' .) \
|
||||
| (cd "$destination" && tar xf -)
|
||||
echo -e "${YELLOW}● ${text}${NC}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Загрузка состояния
|
||||
load_state() {
|
||||
if [[ -f "$STATE_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$STATE_FILE"
|
||||
return 0
|
||||
else
|
||||
INSTALL_PATH="$DEFAULT_INSTALL_PATH"
|
||||
ENV_FILE="$DEFAULT_ENV_FILE"
|
||||
print_error "Установка бота не найдена. Запустите ./install.sh сначала."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
save_state() {
|
||||
cat >"$STATE_FILE" <<EOF_STATE
|
||||
INSTALL_PATH="$INSTALL_PATH"
|
||||
ENV_FILE="$ENV_FILE"
|
||||
INSTALLED_AT="$(date --iso-8601=seconds)"
|
||||
EOF_STATE
|
||||
}
|
||||
|
||||
# Определение команды docker compose
|
||||
resolve_compose_command() {
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
COMPOSE_BIN=(docker compose)
|
||||
elif docker-compose version >/dev/null 2>&1; then
|
||||
COMPOSE_BIN=(docker-compose)
|
||||
else
|
||||
error "Docker Compose не найден. Установите docker compose или docker-compose."
|
||||
print_error "Docker Compose не найден."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
@@ -146,117 +91,524 @@ run_compose() {
|
||||
(cd "$INSTALL_PATH" && "${COMPOSE_BIN[@]}" "$@")
|
||||
}
|
||||
|
||||
show_services_state() {
|
||||
log "Текущее состояние сервисов:"
|
||||
run_compose ps
|
||||
# Получение статуса сервисов
|
||||
get_service_status() {
|
||||
local service=$1
|
||||
local status
|
||||
status=$(run_compose ps -q "$service" 2>/dev/null | xargs -r docker inspect -f '{{.State.Status}}' 2>/dev/null || echo "not_found")
|
||||
echo "$status"
|
||||
}
|
||||
|
||||
follow_logs() {
|
||||
if ask_yes_no "Хотите посмотреть последние логи бота? [y/N]: " "n"; then
|
||||
run_compose logs --tail=50 bot || true
|
||||
fi
|
||||
}
|
||||
|
||||
start_services() {
|
||||
log "Запускаем сервисы бота..."
|
||||
run_compose up -d
|
||||
show_services_state
|
||||
follow_logs
|
||||
}
|
||||
|
||||
perform_installation() {
|
||||
INSTALL_PATH=$(prompt_value "INSTALL_PATH" "Укажите путь установки бота" "$DEFAULT_INSTALL_PATH")
|
||||
INSTALL_PATH=${INSTALL_PATH%/}
|
||||
if [[ ! -d "$INSTALL_PATH" ]]; then
|
||||
mkdir -p "$INSTALL_PATH"
|
||||
log "Создана директория установки $INSTALL_PATH"
|
||||
fi
|
||||
|
||||
sync_project_files "$INSTALL_PATH"
|
||||
|
||||
ENV_FILE="$INSTALL_PATH/$DEFAULT_ENV_FILE"
|
||||
ensure_directory_structure "$INSTALL_PATH"
|
||||
|
||||
local existing_env
|
||||
if [[ -f "$ENV_FILE" ]]; then
|
||||
existing_env="true"
|
||||
else
|
||||
existing_env="false"
|
||||
fi
|
||||
|
||||
log "Заполняем параметры окружения (.env)."
|
||||
local bot_token admin_ids api_url api_key auth_type username password secret_key web_api_token
|
||||
bot_token=$(prompt_value "BOT_TOKEN" "Введите BOT_TOKEN" "$(get_env_value BOT_TOKEN "$ENV_FILE")")
|
||||
admin_ids=$(prompt_value "ADMIN_IDS" "Введите ADMIN_IDS (через запятую)" "$(get_env_value ADMIN_IDS "$ENV_FILE")")
|
||||
|
||||
printf '\nREMNAWAVE_API_URL должен быть в формате https://panel.example.com или http://remnawave:3000 при локальном запуске.\n'
|
||||
api_url=$(prompt_value "REMNAWAVE_API_URL" "Введите REMNAWAVE_API_URL" "$(get_env_value REMNAWAVE_API_URL "$ENV_FILE")")
|
||||
api_key=$(prompt_value "REMNAWAVE_API_KEY" "Введите REMNAWAVE_API_KEY" "$(get_env_value REMNAWAVE_API_KEY "$ENV_FILE")")
|
||||
|
||||
local default_auth
|
||||
default_auth=$(get_env_value REMNAWAVE_AUTH_TYPE "$ENV_FILE")
|
||||
auth_type=$(prompt_value "REMNAWAVE_AUTH_TYPE" "Введите тип авторизации REMNAWAVE (api_key/basic_auth)" "${default_auth:-api_key}")
|
||||
|
||||
username=""
|
||||
password=""
|
||||
if [[ "${auth_type}" == "basic_auth" ]]; then
|
||||
username=$(prompt_value "REMNAWAVE_USERNAME" "Введите REMNAWAVE_USERNAME" "$(get_env_value REMNAWAVE_USERNAME "$ENV_FILE")")
|
||||
password=$(prompt_value "REMNAWAVE_PASSWORD" "Введите REMNAWAVE_PASSWORD" "$(get_env_value REMNAWAVE_PASSWORD "$ENV_FILE")")
|
||||
else
|
||||
if ask_yes_no "Указать REMNAWAVE_USERNAME/REMNAWAVE_PASSWORD? [y/N]: " "n"; then
|
||||
username=$(prompt_value "REMNAWAVE_USERNAME" "Введите REMNAWAVE_USERNAME" "$(get_env_value REMNAWAVE_USERNAME "$ENV_FILE")")
|
||||
password=$(prompt_value "REMNAWAVE_PASSWORD" "Введите REMNAWAVE_PASSWORD" "$(get_env_value REMNAWAVE_PASSWORD "$ENV_FILE")")
|
||||
# Мониторинг сервисов
|
||||
show_monitoring() {
|
||||
print_header "МОНИТОРИНГ СЕРВИСОВ БОТА"
|
||||
|
||||
print_section "Статус контейнеров"
|
||||
|
||||
local services=("bot" "postgres" "redis")
|
||||
local all_running=true
|
||||
|
||||
for service in "${services[@]}"; do
|
||||
local status
|
||||
status=$(get_service_status "$service")
|
||||
local uptime=""
|
||||
|
||||
if [[ "$status" == "running" ]]; then
|
||||
uptime=$(run_compose ps "$service" 2>/dev/null | tail -n1 | awk '{for(i=1;i<=NF;i++){if($i~/Up/){print $(i+1), $(i+2); break}}}')
|
||||
print_status "running" "$service: работает (uptime: $uptime)"
|
||||
elif [[ "$status" == "exited" ]] || [[ "$status" == "stopped" ]]; then
|
||||
print_status "stopped" "$service: остановлен"
|
||||
all_running=false
|
||||
else
|
||||
print_status "unknown" "$service: не найден"
|
||||
all_running=false
|
||||
fi
|
||||
done
|
||||
|
||||
# Статистика ресурсов
|
||||
print_section "Использование ресурсов"
|
||||
|
||||
local stats
|
||||
stats=$(docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}" 2>/dev/null | grep -E "bot|postgres|redis" || echo "")
|
||||
|
||||
if [[ -n "$stats" ]]; then
|
||||
echo -e "${WHITE}${BOLD}КОНТЕЙНЕР CPU ПАМЯТЬ${NC}"
|
||||
echo "$stats" | tail -n+2 | while IFS=$'\t' read -r name cpu mem; do
|
||||
echo -e "${CYAN}${name}${NC} ${YELLOW}${cpu}${NC} ${PURPLE}${mem}${NC}"
|
||||
done
|
||||
else
|
||||
print_warning "Статистика недоступна"
|
||||
fi
|
||||
|
||||
# Размер логов
|
||||
print_section "Размер логов"
|
||||
if [[ -d "$INSTALL_PATH/logs" ]]; then
|
||||
local log_size
|
||||
log_size=$(du -sh "$INSTALL_PATH/logs" 2>/dev/null | cut -f1)
|
||||
echo -e "${CYAN}Логи: ${YELLOW}${log_size}${NC}"
|
||||
fi
|
||||
|
||||
# Последние ошибки
|
||||
print_section "Последние ошибки (если есть)"
|
||||
local errors
|
||||
errors=$(run_compose logs --tail=100 bot 2>/dev/null | grep -i "error\|exception\|critical" | tail -n 5 || echo "")
|
||||
|
||||
if [[ -n "$errors" ]]; then
|
||||
echo "$errors" | while read -r line; do
|
||||
print_error "$line"
|
||||
done
|
||||
else
|
||||
print_success "Ошибок не обнаружено"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if $all_running; then
|
||||
print_success "Все сервисы работают нормально!"
|
||||
else
|
||||
print_warning "Некоторые сервисы не запущены"
|
||||
fi
|
||||
|
||||
secret_key=$(prompt_value "REMNAWAVE_SECRET_KEY" "Введите REMNAWAVE_SECRET_KEY (для установок eGames, формат XXXXXXX:DDDDDDDD, можно оставить пустым)" "$(get_env_value REMNAWAVE_SECRET_KEY "$ENV_FILE")")
|
||||
web_api_token=$(prompt_value "WEB_API_DEFAULT_TOKEN" "Введите WEB_API_DEFAULT_TOKEN" "$(get_env_value WEB_API_DEFAULT_TOKEN "$ENV_FILE")")
|
||||
|
||||
local entries=()
|
||||
entries+=("BOT_TOKEN=$bot_token")
|
||||
entries+=("ADMIN_IDS=$admin_ids")
|
||||
entries+=("REMNAWAVE_API_URL=$api_url")
|
||||
entries+=("REMNAWAVE_API_KEY=$api_key")
|
||||
entries+=("REMNAWAVE_AUTH_TYPE=$auth_type")
|
||||
entries+=("REMNAWAVE_USERNAME=$username")
|
||||
entries+=("REMNAWAVE_PASSWORD=$password")
|
||||
entries+=("REMNAWAVE_SECRET_KEY=$secret_key")
|
||||
entries+=("WEB_API_DEFAULT_TOKEN=$web_api_token")
|
||||
|
||||
write_env_file "$ENV_FILE" "${entries[@]}"
|
||||
log "Файл окружения сохранен: $ENV_FILE"
|
||||
|
||||
save_state
|
||||
log "Информация об установке сохранена ($STATE_FILE)."
|
||||
|
||||
resolve_compose_command
|
||||
start_services
|
||||
}
|
||||
|
||||
show_existing_installation() {
|
||||
log "Найдена предыдущая установка бота."
|
||||
log "Путь установки: $INSTALL_PATH"
|
||||
log "Файл окружения: $ENV_FILE"
|
||||
resolve_compose_command
|
||||
show_services_state
|
||||
if ask_yes_no "Перезапустить сервисы бота? [y/N]: " "n"; then
|
||||
run_compose restart
|
||||
show_services_state
|
||||
# Обновление из Git
|
||||
update_from_git() {
|
||||
print_header "ОБНОВЛЕНИЕ ИЗ GIT РЕПОЗИТОРИЯ"
|
||||
|
||||
if [[ ! -d "$INSTALL_PATH/.git" ]]; then
|
||||
print_error "Git репозиторий не найден в $INSTALL_PATH"
|
||||
print_info "Инициализируем репозиторий..."
|
||||
|
||||
local repo_url
|
||||
read -rp "Введите URL Git репозитория: " repo_url
|
||||
|
||||
if [[ -z "$repo_url" ]]; then
|
||||
print_error "URL не указан"
|
||||
return 1
|
||||
fi
|
||||
|
||||
(cd "$INSTALL_PATH" && git init && git remote add origin "$repo_url")
|
||||
fi
|
||||
follow_logs
|
||||
|
||||
print_section "Проверка обновлений"
|
||||
|
||||
(cd "$INSTALL_PATH" && git fetch origin 2>&1)
|
||||
|
||||
local current_commit
|
||||
local remote_commit
|
||||
current_commit=$(cd "$INSTALL_PATH" && git rev-parse HEAD 2>/dev/null || echo "unknown")
|
||||
remote_commit=$(cd "$INSTALL_PATH" && git rev-parse origin/main 2>/dev/null || git rev-parse origin/master 2>/dev/null || echo "unknown")
|
||||
|
||||
if [[ "$current_commit" == "$remote_commit" ]]; then
|
||||
print_success "Бот уже имеет последнюю версию"
|
||||
return 0
|
||||
fi
|
||||
|
||||
print_info "Найдены обновления"
|
||||
echo -e "${CYAN}Текущий коммит: ${YELLOW}${current_commit:0:8}${NC}"
|
||||
echo -e "${CYAN}Новый коммит: ${YELLOW}${remote_commit:0:8}${NC}"
|
||||
|
||||
# Показываем изменения
|
||||
print_section "Список изменений"
|
||||
(cd "$INSTALL_PATH" && git log --oneline HEAD..origin/main 2>/dev/null || git log --oneline HEAD..origin/master 2>/dev/null || true)
|
||||
|
||||
echo ""
|
||||
read -rp "$(echo -e ${YELLOW}Применить обновления? [y/N]: ${NC})" confirm
|
||||
|
||||
if [[ "${confirm,,}" != "y" ]]; then
|
||||
print_warning "Обновление отменено"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Создаем резервную копию перед обновлением
|
||||
print_info "Создаем резервную копию перед обновлением..."
|
||||
create_backup "pre-update"
|
||||
|
||||
print_section "Применение обновлений"
|
||||
|
||||
# Останавливаем бота
|
||||
print_info "Останавливаем сервисы..."
|
||||
run_compose down
|
||||
|
||||
# Обновляем код
|
||||
print_info "Обновляем код..."
|
||||
(cd "$INSTALL_PATH" && git pull origin main 2>/dev/null || git pull origin master 2>/dev/null)
|
||||
|
||||
# Перезапускаем
|
||||
print_info "Пересобираем и запускаем сервисы..."
|
||||
run_compose up -d --build
|
||||
|
||||
print_success "Обновление завершено!"
|
||||
|
||||
# Показываем логи
|
||||
echo ""
|
||||
read -rp "$(echo -e ${YELLOW}Показать логи запуска? [y/N]: ${NC})" show_logs
|
||||
if [[ "${show_logs,,}" == "y" ]]; then
|
||||
run_compose logs --tail=50 -f bot
|
||||
fi
|
||||
}
|
||||
|
||||
# Создание резервной копии
|
||||
create_backup() {
|
||||
local backup_type=${1:-manual}
|
||||
local timestamp
|
||||
timestamp=$(date +%Y%m%d_%H%M%S)
|
||||
local backup_name="backup_${backup_type}_${timestamp}"
|
||||
local backup_path="$BACKUP_DIR/$backup_name"
|
||||
|
||||
print_header "СОЗДАНИЕ РЕЗЕРВНОЙ КОПИИ"
|
||||
|
||||
mkdir -p "$BACKUP_DIR"
|
||||
mkdir -p "$backup_path"
|
||||
|
||||
print_section "Архивирование данных"
|
||||
|
||||
# Копируем конфигурацию
|
||||
print_info "Сохраняем конфигурацию..."
|
||||
cp "$INSTALL_PATH/.env" "$backup_path/" 2>/dev/null || true
|
||||
cp "$INSTALL_PATH/docker-compose.yml" "$backup_path/" 2>/dev/null || true
|
||||
|
||||
# Экспортируем базу данных
|
||||
if [[ $(get_service_status "postgres") == "running" ]]; then
|
||||
print_info "Экспортируем базу данных PostgreSQL..."
|
||||
run_compose exec -T postgres pg_dump -U postgres remnawave_bot > "$backup_path/database.sql" 2>/dev/null || {
|
||||
print_warning "Не удалось экспортировать БД"
|
||||
}
|
||||
fi
|
||||
|
||||
# Копируем данные
|
||||
if [[ -d "$INSTALL_PATH/data" ]]; then
|
||||
print_info "Копируем пользовательские данные..."
|
||||
cp -r "$INSTALL_PATH/data" "$backup_path/" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# Создаем архив
|
||||
print_info "Создаем архив..."
|
||||
(cd "$BACKUP_DIR" && tar -czf "${backup_name}.tar.gz" "$backup_name" && rm -rf "$backup_name")
|
||||
|
||||
local backup_size
|
||||
backup_size=$(du -h "$BACKUP_DIR/${backup_name}.tar.gz" | cut -f1)
|
||||
|
||||
print_success "Резервная копия создана: $BACKUP_DIR/${backup_name}.tar.gz"
|
||||
echo -e "${CYAN}Размер: ${YELLOW}${backup_size}${NC}"
|
||||
|
||||
# Очистка старых бэкапов (оставляем последние 10)
|
||||
print_info "Очистка старых бэкапов..."
|
||||
(cd "$BACKUP_DIR" && ls -t backup_*.tar.gz 2>/dev/null | tail -n +11 | xargs -r rm -f)
|
||||
|
||||
local backup_count
|
||||
backup_count=$(ls -1 "$BACKUP_DIR"/backup_*.tar.gz 2>/dev/null | wc -l)
|
||||
print_info "Всего резервных копий: $backup_count"
|
||||
}
|
||||
|
||||
# Восстановление из резервной копии
|
||||
restore_backup() {
|
||||
print_header "ВОССТАНОВЛЕНИЕ ИЗ РЕЗЕРВНОЙ КОПИИ"
|
||||
|
||||
if [[ ! -d "$BACKUP_DIR" ]] || [[ -z "$(ls -A "$BACKUP_DIR"/*.tar.gz 2>/dev/null)" ]]; then
|
||||
print_error "Резервные копии не найдены"
|
||||
return 1
|
||||
fi
|
||||
|
||||
print_section "Доступные резервные копии"
|
||||
|
||||
local backups=()
|
||||
local i=1
|
||||
while IFS= read -r backup; do
|
||||
local backup_name
|
||||
local backup_size
|
||||
local backup_date
|
||||
backup_name=$(basename "$backup")
|
||||
backup_size=$(du -h "$backup" | cut -f1)
|
||||
backup_date=$(stat -c %y "$backup" 2>/dev/null | cut -d' ' -f1,2 | cut -d'.' -f1 || stat -f "%Sm" "$backup")
|
||||
|
||||
echo -e "${CYAN}[$i]${NC} ${WHITE}$backup_name${NC}"
|
||||
echo -e " Размер: ${YELLOW}$backup_size${NC}, Дата: ${PURPLE}$backup_date${NC}"
|
||||
backups+=("$backup")
|
||||
((i++))
|
||||
done < <(ls -t "$BACKUP_DIR"/*.tar.gz 2>/dev/null)
|
||||
|
||||
echo ""
|
||||
read -rp "Выберите номер резервной копии для восстановления [1-$((i-1))]: " selection
|
||||
|
||||
if [[ ! "$selection" =~ ^[0-9]+$ ]] || [[ "$selection" -lt 1 ]] || [[ "$selection" -ge "$i" ]]; then
|
||||
print_error "Неверный выбор"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local selected_backup="${backups[$((selection-1))]}"
|
||||
|
||||
print_warning "ВНИМАНИЕ: Текущие данные будут перезаписаны!"
|
||||
read -rp "$(echo -e ${RED}${BOLD}Продолжить восстановление? [y/N]: ${NC})" confirm
|
||||
|
||||
if [[ "${confirm,,}" != "y" ]]; then
|
||||
print_warning "Восстановление отменено"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Создаем резервную копию перед восстановлением
|
||||
print_info "Создаем резервную копию текущего состояния..."
|
||||
create_backup "pre-restore"
|
||||
|
||||
print_section "Восстановление данных"
|
||||
|
||||
# Останавливаем сервисы
|
||||
print_info "Останавливаем сервисы..."
|
||||
run_compose down
|
||||
|
||||
# Распаковываем бэкап
|
||||
print_info "Распаковываем резервную копию..."
|
||||
local temp_dir
|
||||
temp_dir=$(mktemp -d)
|
||||
tar -xzf "$selected_backup" -C "$temp_dir"
|
||||
|
||||
local backup_folder
|
||||
backup_folder=$(ls "$temp_dir")
|
||||
|
||||
# Восстанавливаем конфигурацию
|
||||
if [[ -f "$temp_dir/$backup_folder/.env" ]]; then
|
||||
print_info "Восстанавливаем конфигурацию..."
|
||||
cp "$temp_dir/$backup_folder/.env" "$INSTALL_PATH/"
|
||||
fi
|
||||
|
||||
# Восстанавливаем данные
|
||||
if [[ -d "$temp_dir/$backup_folder/data" ]]; then
|
||||
print_info "Восстанавливаем пользовательские данные..."
|
||||
rm -rf "$INSTALL_PATH/data"
|
||||
cp -r "$temp_dir/$backup_folder/data" "$INSTALL_PATH/"
|
||||
fi
|
||||
|
||||
# Запускаем сервисы
|
||||
print_info "Запускаем сервисы..."
|
||||
run_compose up -d
|
||||
|
||||
# Восстанавливаем БД
|
||||
if [[ -f "$temp_dir/$backup_folder/database.sql" ]]; then
|
||||
print_info "Ожидаем запуска PostgreSQL..."
|
||||
sleep 5
|
||||
print_info "Восстанавливаем базу данных..."
|
||||
run_compose exec -T postgres psql -U postgres remnawave_bot < "$temp_dir/$backup_folder/database.sql" 2>/dev/null || {
|
||||
print_warning "Не удалось восстановить БД (возможно, структура уже актуальна)"
|
||||
}
|
||||
fi
|
||||
|
||||
# Очистка
|
||||
rm -rf "$temp_dir"
|
||||
|
||||
print_success "Восстановление завершено!"
|
||||
|
||||
echo ""
|
||||
show_monitoring
|
||||
}
|
||||
|
||||
# Просмотр логов
|
||||
view_logs() {
|
||||
print_header "ПРОСМОТР ЛОГОВ"
|
||||
|
||||
echo -e "${CYAN}[1]${NC} Логи бота (последние 100 строк)"
|
||||
echo -e "${CYAN}[2]${NC} Логи PostgreSQL (последние 100 строк)"
|
||||
echo -e "${CYAN}[3]${NC} Логи Redis (последние 100 строк)"
|
||||
echo -e "${CYAN}[4]${NC} Все логи (последние 100 строк)"
|
||||
echo -e "${CYAN}[5]${NC} Следить за логами в реальном времени"
|
||||
echo -e "${CYAN}[6]${NC} Поиск по логам"
|
||||
|
||||
echo ""
|
||||
read -rp "Выберите опцию [1-6]: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
run_compose logs --tail=100 bot
|
||||
;;
|
||||
2)
|
||||
run_compose logs --tail=100 postgres
|
||||
;;
|
||||
3)
|
||||
run_compose logs --tail=100 redis
|
||||
;;
|
||||
4)
|
||||
run_compose logs --tail=100
|
||||
;;
|
||||
5)
|
||||
print_info "Нажмите Ctrl+C для выхода"
|
||||
run_compose logs -f
|
||||
;;
|
||||
6)
|
||||
read -rp "Введите текст для поиска: " search_term
|
||||
run_compose logs | grep -i "$search_term" --color=always | tail -n 50
|
||||
;;
|
||||
*)
|
||||
print_error "Неверный выбор"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Управление сервисами
|
||||
manage_services() {
|
||||
print_header "УПРАВЛЕНИЕ СЕРВИСАМИ"
|
||||
|
||||
echo -e "${CYAN}[1]${NC} Запустить все сервисы"
|
||||
echo -e "${CYAN}[2]${NC} Остановить все сервисы"
|
||||
echo -e "${CYAN}[3]${NC} Перезапустить все сервисы"
|
||||
echo -e "${CYAN}[4]${NC} Пересобрать и запустить"
|
||||
echo -e "${CYAN}[5]${NC} Остановить и удалить контейнеры"
|
||||
|
||||
echo ""
|
||||
read -rp "Выберите опцию [1-5]: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
print_info "Запускаем сервисы..."
|
||||
run_compose up -d
|
||||
print_success "Сервисы запущены"
|
||||
show_monitoring
|
||||
;;
|
||||
2)
|
||||
print_info "Останавливаем сервисы..."
|
||||
run_compose stop
|
||||
print_success "Сервисы остановлены"
|
||||
;;
|
||||
3)
|
||||
print_info "Перезапускаем сервисы..."
|
||||
run_compose restart
|
||||
print_success "Сервисы перезапущены"
|
||||
show_monitoring
|
||||
;;
|
||||
4)
|
||||
print_info "Пересобираем и запускаем..."
|
||||
run_compose up -d --build
|
||||
print_success "Сервисы пересобраны и запущены"
|
||||
show_monitoring
|
||||
;;
|
||||
5)
|
||||
print_warning "Контейнеры будут удалены (данные сохранятся в volumes)"
|
||||
read -rp "$(echo -e ${YELLOW}Продолжить? [y/N]: ${NC})" confirm
|
||||
if [[ "${confirm,,}" == "y" ]]; then
|
||||
run_compose down
|
||||
print_success "Контейнеры остановлены и удалены"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
print_error "Неверный выбор"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Очистка системы
|
||||
cleanup_system() {
|
||||
print_header "ОЧИСТКА СИСТЕМЫ"
|
||||
|
||||
echo -e "${CYAN}[1]${NC} Очистить старые логи (старше 7 дней)"
|
||||
echo -e "${CYAN}[2]${NC} Очистить старые резервные копии (оставить 5 последних)"
|
||||
echo -e "${CYAN}[3]${NC} Очистить неиспользуемые Docker образы"
|
||||
echo -e "${CYAN}[4]${NC} Полная очистка (всё вышеперечисленное)"
|
||||
|
||||
echo ""
|
||||
read -rp "Выберите опцию [1-4]: " choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
print_info "Очищаем старые логи..."
|
||||
find "$INSTALL_PATH/logs" -type f -mtime +7 -delete 2>/dev/null || true
|
||||
print_success "Старые логи удалены"
|
||||
;;
|
||||
2)
|
||||
print_info "Очищаем старые бэкапы..."
|
||||
(cd "$BACKUP_DIR" && ls -t backup_*.tar.gz 2>/dev/null | tail -n +6 | xargs -r rm -f)
|
||||
print_success "Старые бэкапы удалены"
|
||||
;;
|
||||
3)
|
||||
print_info "Очищаем неиспользуемые Docker образы..."
|
||||
docker image prune -f
|
||||
print_success "Неиспользуемые образы удалены"
|
||||
;;
|
||||
4)
|
||||
print_info "Выполняем полную очистку..."
|
||||
find "$INSTALL_PATH/logs" -type f -mtime +7 -delete 2>/dev/null || true
|
||||
(cd "$BACKUP_DIR" && ls -t backup_*.tar.gz 2>/dev/null | tail -n +6 | xargs -r rm -f)
|
||||
docker image prune -f
|
||||
docker volume prune -f
|
||||
print_success "Полная очистка завершена"
|
||||
;;
|
||||
*)
|
||||
print_error "Неверный выбор"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Главное меню
|
||||
show_menu() {
|
||||
clear
|
||||
echo -e "${PURPLE}${BOLD}"
|
||||
cat << "EOF"
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ ██████╗ ██████╗ ████████╗ ███╗ ███╗ ██████╗ ██████╗
|
||||
║ ██╔══██╗██╔═══██╗╚══██╔══╝ ████╗ ████║██╔════╝ ██╔══██╗
|
||||
║ ██████╔╝██║ ██║ ██║ ██╔████╔██║██║ ███╗██████╔╝
|
||||
║ ██╔══██╗██║ ██║ ██║ ██║╚██╔╝██║██║ ██║██╔══██╗
|
||||
║ ██████╔╝╚██████╔╝ ██║ ██║ ╚═╝ ██║╚██████╔╝██║ ██║
|
||||
║ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝
|
||||
║ ║
|
||||
║ Система управления Telegram ботом ║
|
||||
╚════════════════════════════════════════════════════════════╝
|
||||
EOF
|
||||
echo -e "${NC}"
|
||||
|
||||
echo -e "${WHITE}${BOLD}Путь установки:${NC} ${CYAN}$INSTALL_PATH${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "${GREEN}${BOLD}[1]${NC} ${STAR} Мониторинг и статус сервисов"
|
||||
echo -e "${BLUE}${BOLD}[2]${NC} ${GEAR} Управление сервисами"
|
||||
echo -e "${YELLOW}${BOLD}[3]${NC} 📋 Просмотр логов"
|
||||
echo -e "${PURPLE}${BOLD}[4]${NC} 🔄 Обновление из Git"
|
||||
echo -e "${CYAN}${BOLD}[5]${NC} 💾 Создать резервную копию"
|
||||
echo -e "${YELLOW}${BOLD}[6]${NC} 📦 Восстановить из резервной копии"
|
||||
echo -e "${RED}${BOLD}[7]${NC} 🧹 Очистка системы"
|
||||
echo -e "${WHITE}${BOLD}[0]${NC} 🚪 Выход"
|
||||
|
||||
echo ""
|
||||
}
|
||||
|
||||
main() {
|
||||
load_state
|
||||
if [[ -f "$STATE_FILE" ]]; then
|
||||
if ask_yes_no "Обнаружена существующая установка. Переустановить? [y/N]: " "n"; then
|
||||
perform_installation
|
||||
else
|
||||
show_existing_installation
|
||||
fi
|
||||
else
|
||||
perform_installation
|
||||
fi
|
||||
resolve_compose_command
|
||||
|
||||
while true; do
|
||||
show_menu
|
||||
read -rp "$(echo -e ${WHITE}${BOLD}Выберите опцию: ${NC})" choice
|
||||
|
||||
case $choice in
|
||||
1)
|
||||
show_monitoring
|
||||
;;
|
||||
2)
|
||||
manage_services
|
||||
;;
|
||||
3)
|
||||
view_logs
|
||||
;;
|
||||
4)
|
||||
update_from_git
|
||||
;;
|
||||
5)
|
||||
create_backup "manual"
|
||||
;;
|
||||
6)
|
||||
restore_backup
|
||||
;;
|
||||
7)
|
||||
cleanup_system
|
||||
;;
|
||||
0)
|
||||
print_success "До свидания!"
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
print_error "Неверный выбор. Попробуйте снова."
|
||||
;;
|
||||
esac
|
||||
|
||||
echo ""
|
||||
read -rp "$(echo -e ${CYAN}Нажмите Enter для продолжения...${NC})"
|
||||
done
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user