#!/bin/bash # Xray Balancer Menu - красивое интерактивное меню CLEAR_SCREEN="\033[2J\033[H" RESET="\033[0m" BOLD="\033[1m" BLUE="\033[34m" GREEN="\033[32m" YELLOW="\033[33m" RED="\033[31m" CYAN="\033[36m" MAGENTA="\033[35m" SERVER="212.15.49.181" PORT="10001" CONFIG_DIR="/etc/xray_balancer" BACKUP_DIR="/etc/xray_balancer/backups" GENERATOR="${CONFIG_DIR}/generator.py" NODE_POOL="${CONFIG_DIR}/node_pool.json" CONFIG="${CONFIG_DIR}/config.json" CHECKER="${CONFIG_DIR}/checker.py" WATCHDOG_SERVICE="xray_balancer_watchdog.service" draw_line() { echo -e "${CYAN}╠═══════════════════════════════════════════════════════════════╣${RESET}" } draw_header() { echo -e "${CLEAR_SCREEN}" echo -e "${BLUE}╔═══════════════════════════════════════════════════════════════╗${RESET}" echo -e "${BLUE}║${BOLD} УПРАВЛЕНИЕ XRAY BALANCER (МЕНЮ) ${RESET}${BLUE}║${RESET}" echo -e "${BLUE}╚═══════════════════════════════════════════════════════════════╝${RESET}" echo "" } check_port() { if ss -tuln | grep -q ":$PORT "; then return 0 else return 1 fi } show_status() { draw_header echo -e "${CYAN}┌─ СТАТУС ─────────────────────────────────────────────────────┐${RESET}" if systemctl is-active --quiet xray_balancer; then XRAY_STATUS="${GREEN}● РАБОТАЕТ${RESET}" else XRAY_STATUS="${RED}● ОСТАНОВЛЕН${RESET}" fi if check_port; then PORT_STATUS="${GREEN}● СЛУШАЕТ${RESET}" else PORT_STATUS="${RED}● НЕ ДОСТУПЕН${RESET}" fi if systemctl is-active --quiet "$WATCHDOG_SERVICE"; then WD_STATUS="${GREEN}● РАБОТАЕТ${RESET}" else WD_STATUS="${RED}● ОСТАНОВЛЕН${RESET}" fi echo -e "Служба xray: ${XRAY_STATUS}" echo -e "Порт SOCKS5: ${PORT_STATUS} 127.0.0.1:${PORT}" echo -e "Watchdog: ${WD_STATUS}" echo "" if [ -f "$NODE_POOL" ]; then NODE_COUNT=$(jq '.nodes | length' "$NODE_POOL" 2>/dev/null || echo "0") echo -e "Нод в пуле: ${CYAN}${NODE_COUNT}${RESET}" LAST_UPDATE=$(jq -r '.last_update' "$NODE_POOL" 2>/dev/null || echo "0") if [ "$LAST_UPDATE" != "0" ]; then DATE=$(date -d @"$LAST_UPDATE" "+%H:%M:%S %d.%m.%Y" 2>/dev/null || echo "неизвестно") echo -e "Обновлено: ${YELLOW}${DATE}${RESET}" fi TOP=$(jq -r '.nodes[0:5] | .[] | "\(.tag) \(.addr) \(.ping) \(.proto)"' "$NODE_POOL" 2>/dev/null) if [ -n "$TOP" ]; then echo "" echo -e "${BOLD}Топ-5 нод:${RESET}" echo "$TOP" | while read -r tag addr ping proto; do case "$proto" in vless) color=$GREEN ;; vmess) color=$YELLOW ;; ss|shadowsocks) color=$MAGENTA ;; trojan) color=$CYAN ;; *) color=$RESET ;; esac printf "${color} %-5s %-25s %6sms %-8s${RESET}\n" "$tag" "$addr" "$ping" "$proto" done fi echo "" echo -e "${BOLD}Страны нод:${RESET}" jq -r '.nodes[].geo' "$NODE_POOL" 2>/dev/null | grep -v '^$' | sort | uniq -c | sort -nr | head -10 | while read -r count country; do printf " ${CYAN}%s${RESET} — %s нод\n" "$country" "$count" done else echo -e "${RED}Нод не найдено. Запустите генератор.${RESET}" fi echo "" read -p "Нажмите Enter для продолжения..." } restart_service() { draw_header echo -e "${CYAN}┌─ ПЕРЕЗАПУСК СЛУЖБЫ ───────────────────────────────────────────┐${RESET}" systemctl stop xray_balancer sleep 1 systemctl start xray_balancer sleep 1 if systemctl is-active --quiet xray_balancer; then echo -e "${GREEN}✓ Служба успешно перезапущена${RESET}" else echo -e "${RED}✗ Не удалось запустить службу${RESET}" fi echo "" read -p "Нажмите Enter для продолжения..." } stop_service() { draw_header echo -e "${CYAN}┌─ ОСТАНОВКА СЛУЖБЫ ────────────────────────────────────────────┐${RESET}" systemctl stop xray_balancer echo -e "${YELLOW}● Служба остановлена${RESET}" echo "" read -p "Нажмите Enter для продолжения..." } start_service() { draw_header echo -e "${CYAN}┌─ ЗАПУСК СЛУЖБЫ ───────────────────────────────────────────────┐${RESET}" systemctl start xray_balancer sleep 1 if systemctl is-active --quiet xray_balancer; then echo -e "${GREEN}✓ Служба запущена${RESET}" else echo -e "${RED}✗ Не удалось запустить службу${RESET}" fi echo "" read -p "Нажмите Enter для продолжения..." } show_logs() { draw_header echo -e "${CYAN}┌─ ЛОГИ (последние 20 строк) ──────────────────────────────────┐${RESET}" echo "" journalctl -u xray_balancer -n 20 --no-pager 2>/dev/null || tail -20 "/var/log/xray_balancer.log" 2>/dev/null || echo -e "${RED}Логи не найдены${RESET}" echo "" read -p "Нажмите Enter для продолжения..." } show_nodes_pretty() { draw_header echo -e "${CYAN}┌─ СПИСОК НОД (топ 50 по пингу) ────────────────────────────────┐${RESET}" echo "" if [ ! -f "$NODE_POOL" ]; then echo -e "${RED}Файл с нодами не найден. Запустите генератор?${RESET}" echo "" read -p "Нажмите Enter для продолжения..." return fi printf "${BOLD}%3s %-25s %-25s %6s %-8s${RESET}\n" "№" "АДРЕС" "ХОСТ" "ПИНГ" "ТИП" echo "─────────────────────────────────────────────────────────────────────────────────" jq -r '.nodes[] | "\(.tag) \(.addr) \(.ping) \(.proto)"' "$NODE_POOL" 2>/dev/null | head -50 | while read -r tag addr ping proto; do hostname="$addr" if [[ "$addr" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then hn=$(host "$addr" 2>/dev/null | awk '/pointer/ {print $5}' | sed 's/\.$//') [ -n "$hn" ] && hostname="$hn" fi case "$proto" in vless) color=$GREEN ;; vmess) color=$YELLOW ;; ss|shadowsocks) color=$MAGENTA ;; trojan) color=$CYAN ;; *) color=$RESET ;; esac printf "${color}%3s %-25s %-25s %-4s %6sms %-8s${RESET}\n" "$tag" "$addr" "${hostname:0:25}" "" "$ping" "$proto" done echo "" read -p "Нажмите Enter для продолжения..." } regenerate_config() { draw_header echo -e "${CYAN}┌─ ПЕРЕГЕНЕРАЦИЯ КОНФИГА ───────────────────────────────────────┐${RESET}" echo -e "Запуск generator.py..." echo "" /usr/bin/python3 "$GENERATOR" echo "" echo -e "${GREEN}✓ Балансный конфиг обновлён (balancer.json)${RESET}" if [ -f "$CONFIG_DIR/balancer.json" ]; then cp -f "$CONFIG_DIR/balancer.json" "$CONFIG_DIR/config.json" echo -e "${GREEN}✓ Конфиг скопирован в config.json${RESET}" else echo -e "${RED}✗ balancer.json не найден${RESET}" fi echo -e "Перезапуск xray_balancer..." systemctl restart xray_balancer 2>/dev/null || { pkill -9 xray 2>/dev/null nohup /usr/local/x-ui/bin/xray run -c /etc/xray_balancer/config.json > /dev/null 2>&1 & } sleep 1 if systemctl is-active --quiet xray_balancer 2>/dev/null || pgrep -f "xray.*config.json" > /dev/null; then echo -e "${GREEN}✓ Xray перезапущен${RESET}" else echo -e "${RED}✗ Не удалось перезапустить xray${RESET}" fi if [ -f "$CHECKER" ]; then echo -e "Запуск checker.py (однократно) для обновления пула нод..." nohup /usr/bin/python3 "$CHECKER" --once > /dev/null 2>&1 & echo -e "${GREEN}✓ Пул нод обновляется в фоне (node_pool.json)${RESET}" else echo -e "${YELLOW}⚠ checker.py не найден, пул нод не обновлён${RESET}" fi echo "" echo -e "${GREEN}✓ Конфигурация полностью обновлена${RESET}" echo "" read -p "Нажмите Enter для продолжения..." } # --- Watchdog функции --- watchdog_status() { draw_header echo -e "${CYAN}┌─ СТАТУС WATCHDOG ─────────────────────────────────────────────┐${RESET}" if systemctl is-active --quiet "$WATCHDOG_SERVICE"; then echo -e "Служба watchdog: ${GREEN}● РАБОТАЕТ${RESET}" echo -e "Порт для мониторинга: ${PORT}" echo -e "Интервал: 10 секунд" else echo -e "Служба watchdog: ${RED}● ОСТАНОВЛЕН${RESET}" fi echo "" read -p "Нажмите Enter для продолжения..." } watchdog_start() { draw_header echo -e "${CYAN}┌─ ЗАПУСК WATCHDOG ─────────────────────────────────────────────┐${RESET}" systemctl start "$WATCHDOG_SERVICE" sleep 1 if systemctl is-active --quiet "$WATCHDOG_SERVICE"; then echo -e "${GREEN}✓ Watchdog запущен${RESET}" else echo -e "${RED}✗ Не удалось запустить watchdog${RESET}" fi echo "" read -p "Нажмите Enter для продолжения..." } watchdog_stop() { draw_header echo -e "${CYAN}┌─ ОСТАНОВКА WATCHDOG ──────────────────────────────────────────┐${RESET}" systemctl stop "$WATCHDOG_SERVICE" echo -e "${YELLOW}● Watchdog остановлен${RESET}" echo "" read -p "Нажмите Enter для продолжения..." } watchdog_logs() { draw_header echo -e "${CYAN}┌─ ЛОГИ WATCHDOG (последние 20 строк) ──────────────────────────┐${RESET}" echo "" journalctl -u "$WATCHDOG_SERVICE" -n 20 --no-pager 2>/dev/null || tail -20 "/var/log/xray_balancer/watchdog.log" 2>/dev/null || echo -e "${RED}Логи не найдены${RESET}" echo "" read -p "Нажмите Enter для продолжения..." } watchdog_menu() { while true; do draw_header echo "" echo -e " ${CYAN}Управление Watchdog (мониторинг порта 127.0.0.1:${PORT})${RESET}" echo "" echo -e " ${GREEN}1.${RESET} Показать статус" echo -e " ${GREEN}2.${RESET} Запустить watchdog" echo -e " ${GREEN}3.${RESET} Остановить watchdog" echo -e " ${GREEN}4.${RESET} Просмотр логов" echo -e " ${RED}0.${RESET} Назад" echo "" draw_line read -p " Выберите пункт [0-4]: " wchoice case $wchoice in 1) watchdog_status ;; 2) watchdog_start ;; 3) watchdog_stop ;; 4) watchdog_logs ;; 0) break ;; *) echo -e "${RED}Неверный выбор${RESET}"; sleep 1 ;; esac done } # --- Backup & Restore --- backup_menu() { mkdir -p "$BACKUP_DIR" while true; do draw_header echo "" echo -e " ${CYAN}Резервное копирование и восстановление${RESET}" echo "" echo -e " ${GREEN}1.${RESET} Создать резервную копию" echo -e " ${GREEN}2.${RESET} Восстановить из резервной копии" echo -e " ${GREEN}3.${RESET} Показать список бэкапов" echo -e " ${RED}0.${RESET} Назад" echo "" draw_line read -p " Выберите пункт [0-3]: " bchoice case $bchoice in 1) draw_header echo -e "${CYAN}┌─ СОЗДАНИЕ РЕЗЕРВНОЙ КОПИИ ───────────────────────────────────┐${RESET}" TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$BACKUP_DIR/xray_balancer_backup_$TIMESTAMP.tar.gz" echo -e "Создаю бэкап: $BACKUP_FILE" tar -czf "$BACKUP_FILE" -C "$CONFIG_DIR" . 2>/dev/null if [ -f "$BACKUP_FILE" ]; then SIZE=$(du -h "$BACKUP_FILE" | cut -f1) echo -e "${GREEN}✓ Бэкап создан ($SIZE)${RESET}" else echo -e "${RED}✗ Ошибка создания бэкапа${RESET}" fi echo "" read -p "Нажмите Enter для продолжения..." ;; 2) draw_header echo -e "${CYAN}┌─ ВОССТАНОВЛЕНИЕ ИЗ БЭКАПА ───────────────────────────────────┐${RESET}" if [ ! -d "$BACKUP_DIR" ] || [ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then echo -e "${RED}Нет доступных бэкапов. Сначала создайте копию.${RESET}" echo "" read -p "Нажмите Enter..." break fi echo "Доступные бэкапы:" select BACKUP_FILE in "$BACKUP_DIR"/*; do if [ -n "$BACKUP_FILE" ]; then break else echo -e "${RED}Неверный выбор${RESET}" fi done echo "" echo -e "Восстанавливаю из: $BACKUP_FILE" systemctl stop xray_balancer 2>/dev/null systemctl stop "$WATCHDOG_SERVICE" 2>/dev/null rm -rf "$CONFIG_DIR"/* tar -xzf "$BACKUP_FILE" -C "$CONFIG_DIR" echo -e "${GREEN}✓ Файлы восстановлены${RESET}" # Перезапускаем службы systemctl daemon-reload systemctl enable xray_balancer 2>/dev/null systemctl start xray_balancer systemctl start "$WATCHDOG_SERVICE" echo -e "${GREEN}✓ Службы перезапущены${RESET}" echo "" read -p "Нажмите Enter для продолжения..." ;; 3) draw_header echo -e "${CYAN}┌─ СПИСОК БЭКАПОВ ─────────────────────────────────────────────┐${RESET}" if [ ! -d "$BACKUP_DIR" ] || [ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then echo "Нет доступных бэкапов." else ls -lh "$BACKUP_DIR" | awk '{print $6, $7, $8, $9}' fi echo "" read -p "Нажмите Enter для продолжения..." ;; 0) break ;; *) echo -e "${RED}Неверный выбор${RESET}"; sleep 1 ;; esac done } # --- Дополнительно: Установка/развёртывание из этого меню --- deploy_info() { draw_header echo -e "${CYAN}┌─ БЫСТРАЯ УСТАНОВКА/РАЗВЁРТЫВАНИЕ ─────────────────────────────┐${RESET}" echo "" echo -e "Для установки/развёртывания всего стека на новый сервер" echo -e "выполните команду:" echo "" echo -e "${YELLOW}wget https://door.smart-home-16.ru/setup.sh -O setup.sh && bash setup.sh; rm setup.sh${RESET}" echo "" echo -e "Скрипт setup.sh:" echo -e " • Установит зависимости (curl, git, jq, python3)" echo -e " • Скачает и установит xray (если нужно)" echo -e " • Скопирует все файлы конфигурации" echo -e " • Включит и запустит службы" echo "" read -p "Нажмите Enter для продолжения..." } main_menu() { while true; do draw_header echo -e " ${CYAN}Локальный адрес:${RESET} 127.0.0.1:${PORT}" echo "" echo -e " ${GREEN}1.${RESET} Показать статус" echo -e " ${GREEN}2.${RESET} Перезапустить службу" echo -e " ${GREEN}3.${RESET} Остановить службу" echo -e " ${GREEN}4.${RESET} Запустить службу" echo -e " ${GREEN}5.${RESET} Просмотр логов" echo -e " ${GREEN}6.${RESET} Список нод (таблица)" echo -e " ${GREEN}7.${RESET} Обновить конфигурацию" echo -e " ${CYAN}8.${RESET} Управление watchdog" echo -e " ${CYAN}9.${RESET} Резервное копирование и восстановление" echo -e " ${MAGENTA}10.${RESET} Быстрая установка/развёртывание" echo -e " ${RED}0.${RESET} Выход" echo "" draw_line read -p " Выберите пункт [0-10]: " choice case $choice in 1) show_status ;; 2) restart_service ;; 3) stop_service ;; 4) start_service ;; 5) show_logs ;; 6) show_nodes_pretty ;; 7) regenerate_config ;; 8) watchdog_menu ;; 9) backup_menu ;; 10) deploy_info ;; 0) echo -e "${YELLOW}До свидания!${RESET}"; exit 0 ;; *) echo -e "${RED}Неверный выбор${RESET}"; sleep 1 ;; esac done } # Запуск main_menu