From fee25f191e040bc9c71d49d880c6ca200f62ea70 Mon Sep 17 00:00:00 2001 From: anten-ka Date: Mon, 6 Apr 2026 21:40:34 +0300 Subject: [PATCH] v2.2.1: unified menu with bot management, grouped sections, telemt download fix, QR cleanup, version bump --- gotelegram-bot/README.md | 217 +++-- gotelegram-bot/config.example.env | 2 +- gotelegram-bot/requirements.txt | 2 +- install.sh | 1242 ++++++++++++++++++----------- install_gotelegram_bot.sh | 264 +++--- lib/backup.sh | 682 ++++++++-------- lib/telemt_config.sh | 564 ++++++------- lib/templates_catalog.sh | 560 ++++++------- lib/website.sh | 680 ++++++++-------- 9 files changed, 2306 insertions(+), 1907 deletions(-) diff --git a/gotelegram-bot/README.md b/gotelegram-bot/README.md index a01a37b..fa9cbfc 100644 --- a/gotelegram-bot/README.md +++ b/gotelegram-bot/README.md @@ -1,70 +1,147 @@ -# GoTelegram MTProxy Bot - -Telegram-бот для управления MTProxy на сервере — те же функции, что и у CLI `gotelegram`, но через бота. - -## Команды - -| Команда | Описание | -|--------|----------| -| `/start`, `/help` | Справка | -| `/install` | Установить или обновить прокси (выбор домена и порта) | -| `/status` | Статус и данные подключения (IP, порт, secret, ссылка) | -| `/link` | Только ссылка `tg://proxy` | -| `/restart` | Перезапустить контейнер | -| `/logs` | Последние логи контейнера | -| `/remove` | Удалить прокси | -| `/promo` | Промо хостинга | - -## Установка на сервер - -### Публичный репозиторий (одной командой) - -```bash -curl -sL https://raw.githubusercontent.com/anten-ka/gotelegram_pro/main/install_gotelegram_bot.sh -o /tmp/install_gotelegram_bot.sh && sudo bash /tmp/install_gotelegram_bot.sh -``` - -При установке скрипт запросит **BOT_TOKEN** (получить у [@BotFather](https://t.me/BotFather)). - -### Закрытый репозиторий (установка по ключу) - -Для **приватного** репо используется клонирование по **SSH-ключу** или по **токену (PAT)**. Подробно: **[INSTALL_PRIVATE.md](../INSTALL_PRIVATE.md)** в корне репозитория. - -Кратко: -- **По SSH:** скопируйте `bootstrap_install.sh` на сервер, затем - `GIT_REPO_SSH=git@github.com:USER/REPO.git sudo bash bootstrap_install.sh` -- **По токену:** - `GITHUB_TOKEN=ghp_xxx GIT_REPO_HTTPS=https://github.com/USER/REPO.git sudo -E bash bootstrap_install.sh` -- Или клонируйте репо вручную и запустите: - `sudo ./install_gotelegram_bot.sh` - -### Локально (файлы уже рядом со скриптом) - -```bash -sudo ./install_gotelegram_bot.sh -``` - -## Конфигурация - -Файл: `/opt/gotelegram-bot/.env` - -- **BOT_TOKEN** — токен от @BotFather (обязательно). -- **ALLOWED_IDS** — опционально. Список ID пользователей через запятую; если не задан, бот доступен всем. - -После изменения `.env` перезапуск сервиса: - -```bash -sudo systemctl restart gotelegram-bot -``` - -## Требования на сервере - -- Linux (systemd), Docker, Python 3. -- Перед использованием бота на сервере должен быть установлен Docker (бот сам поднимает контейнер `nineseconds/mtg:2` по команде `/install`). - -## Управление сервисом - -```bash -sudo systemctl status gotelegram-bot -sudo systemctl restart gotelegram-bot -journalctl -u gotelegram-bot -f -``` +# GoTelegram v2.2 Bot + +Production-quality Telegram bot for managing MTProxy (telemt engine) on Linux servers. + +## Features + +- **Complete CLI Feature Parity** - All menu items from CLI version + - Install (Quick/Stealth modes) + - Status monitoring + - Proxy link generation + - Share with QR codes + - Service restart + - Logs viewing + - Mode/template changes + - Backup/restore + - telemt updates + - Website/SSL management + - Remove installation + - Promotional links + +- **Template Browsing** - Browse categories → templates → preview → install +- **V1 Migration** - Detects old mtg Docker container and offers migration +- **Access Control** - ALLOWED_IDS from .env +- **Async/Await** - Full async support via python-telegram-bot v21+ +- **Inline Keyboards** - Modern UI with callback-based navigation +- **Shell Integration** - Executes system commands via asyncio subprocess +- **Error Handling** - Production-ready error handling + +## Installation + +### Prerequisites + +- Python 3.8+ +- Linux system with systemd +- telemt installed and running +- Telegram Bot Token from @BotFather + +### Setup + +1. Install dependencies: +```bash +pip install -r requirements.txt +``` + +2. Create .env file: +```bash +cp config.example.env .env +# Edit .env and set your BOT_TOKEN +nano .env +``` + +3. (Optional) Restrict access to specific users: +```bash +# Edit .env and uncomment ALLOWED_IDS +# ALLOWED_IDS=123456789,987654321 +``` + +### Running the Bot + +```bash +python3 bot.py +``` + +For systemd service: + +```bash +[Unit] +Description=GoTelegram Bot +After=network.target + +[Service] +Type=simple +User=gotelegram +WorkingDirectory=/opt/gotelegram/bot +ExecStart=/usr/bin/python3 /opt/gotelegram/bot/bot.py +Restart=always +RestartSec=10 + +[Install] +WantedBy=multi-user.target +``` + +## Configuration + +### .env Variables + +- `BOT_TOKEN` - Telegram bot token (required) +- `ALLOWED_IDS` - Comma-separated user IDs (optional, all users allowed if empty) + +### System Paths + +- `GOTELEGRAM_CONFIG` - `/opt/gotelegram/config.json` +- `TELEMT_CONFIG` - `/etc/telemt/config.toml` +- `TELEMT_SERVICE` - `telemt` (systemd service name) +- `WEBSITE_ROOT` - `/var/www/gotelegram-site` +- `BACKUP_DIR` - `/opt/gotelegram/backups` +- `TEMPLATES_CATALOG` - `/opt/gotelegram/templates_catalog.json` + +## Architecture + +### Single File Design +All functionality in one `bot.py` for simplicity and ease of deployment. + +### Command Handlers +- `/start` - Main menu +- `/help` - Help text +- `/status` - Quick status +- `/logs` - Recent logs + +### Callback Handlers +Organized by feature: +- Installation (quick/stealth modes) +- Status monitoring +- Backup/restore +- SSL management +- Updates +- Removal + +### Shell Integration +Async subprocess wrapper: +```python +code, stdout, stderr = await sh("command", "arg1", "arg2") +``` + +## Callback Data Convention + +- `menu_*` - Menu items +- `install_mode_*` - Install options +- `quick_dom_*` - Domain selection +- `stealth_cat_*` - Template categories +- `stealth_tpl_*` - Template selection +- `stealth_confirm_*` - Confirm installation +- `backup_*` - Backup operations +- `ssl_*` - SSL operations +- `restore_backup_*` - Restore operations + +## Credits + +- **telemt** - MTProxy engine foundation +- **HTML5UP** - Beautiful web templates +- **Learning Zone** - Educational resources +- **Start Bootstrap** - Bootstrap framework +- **Community** - Your feedback and support + +## License + +GoTelegram v2.2 - Open source community project diff --git a/gotelegram-bot/config.example.env b/gotelegram-bot/config.example.env index 59750e9..a2bf535 100644 --- a/gotelegram-bot/config.example.env +++ b/gotelegram-bot/config.example.env @@ -6,4 +6,4 @@ BOT_TOKEN=your_bot_token_from_@BotFather # Comma-separated list of allowed Telegram user IDs # Leave empty to allow all users -# ALLOWED_IDS=123456789,987654321 \ No newline at end of file +# ALLOWED_IDS=123456789,987654321 diff --git a/gotelegram-bot/requirements.txt b/gotelegram-bot/requirements.txt index 7c6803c..46050d1 100644 --- a/gotelegram-bot/requirements.txt +++ b/gotelegram-bot/requirements.txt @@ -1,3 +1,3 @@ python-telegram-bot>=21.0 python-dotenv>=1.0.0 -toml>=0.10.2 \ No newline at end of file +toml>=0.10.2 diff --git a/install.sh b/install.sh index 48acebd..353f62d 100644 --- a/install.sh +++ b/install.sh @@ -1,460 +1,782 @@ -#!/bin/bash -# ══════════════════════════════════════════════════════════════════════════════ -# GoTelegram v2.2 — MTProxy на ядре telemt (Rust + Tokio) -# Anti-DPI • Fake TLS • TCP Splice • JA3/JA4 Resistance -# -# Установка: -# curl -sL URL/install.sh | sudo bash -# ══════════════════════════════════════════════════════════════════════════════ - -set -uo pipefail - -# Путь к скрипту и библиотекам -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -LIB_DIR="$SCRIPT_DIR/lib" - -# Загружаем библиотеки -source "$LIB_DIR/common.sh" -source "$LIB_DIR/telemt.sh" -source "$LIB_DIR/telemt_config.sh" -source "$LIB_DIR/website.sh" -source "$LIB_DIR/templates_catalog.sh" -source "$LIB_DIR/backup.sh" - -# ── Главное меню ───────────────────────────────────────────────────────────── -show_main_menu() { - local status - status=$(telemt_status) - - local status_badge - case "$status" in - running) status_badge="${GREEN}● Работает${NC}" ;; - stopped) status_badge="${YELLOW}○ Остановлен${NC}" ;; - *) status_badge="${RED}✗ Не установлен${NC}" ;; - esac - - echo "" - echo -e " ${BOLD}${WHITE}Главное меню${NC} │ ${status_badge}" - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" - 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 -e " ${CYAN} 6)${NC} 📋 Логи" - echo -e " ${CYAN} 7)${NC} 🎭 Сменить режим / шаблон" - echo -e " ${CYAN} 8)${NC} 💾 Бекап конфигурации" - echo -e " ${CYAN} 9)${NC} 📦 Восстановить из бекапа" - echo -e " ${CYAN}10)${NC} ⬆️ Обновить telemt" - echo -e " ${CYAN}11)${NC} 🌐 Управление сайтом (SSL)" - echo -e " ${CYAN}12)${NC} 🗑 Удалить прокси" - echo -e " ${CYAN}13)${NC} 🏷 Промо" - echo -e " ${CYAN} 0)${NC} 🚪 Выход" - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" - echo -ne " ${WHITE}Выбор:${NC} " -} - -# ── Установка: выбор режима ────────────────────────────────────────────────── -menu_install() { - # Проверяем v1 - if detect_v1_installation; then - echo "" - echo -e " ${YELLOW}⚠️ Обнаружена установка GoTelegram v1 (mtg)${NC}" - echo -e " ${DIM}Контейнер: ${V1_CONTAINER_NAME}${NC}" - echo "" - if ! migrate_v1_to_v2; then - return - fi - fi - - echo "" - echo -e " ${BOLD}${WHITE}🎭 Выберите режим маскировки:${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" - echo -e " ${CYAN}1)${NC} ${GREEN}⚡ Quick${NC} — маскировка под популярный сайт" - echo -e " ${DIM}Быстро, без домена. telemt маскирует трафик${NC}" - echo -e " ${DIM}под выбранный сайт (google.com и т.д.)${NC}" - echo "" - echo -e " ${CYAN}2)${NC} ${MAGENTA}🛡 Stealth${NC} — свой сайт + полная маскировка" - echo -e " ${DIM}nginx + SSL + HTML-шаблон + telemt.${NC}" - echo -e " ${DIM}DPI видит реальный сайт с реальным сертификатом.${NC}" - echo -e " ${DIM}Требует: домен, направленный на этот сервер.${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" - echo -ne " ${WHITE}Выбор (1/2):${NC} " - read -r mode_choice - mode_choice="${mode_choice:-}" - - case "$mode_choice" in - 1) install_quick_mode ;; - 2) install_stealth_mode ;; - *) log_error "Неверный выбор: ${mode_choice:-<пусто>}" ;; - esac -} - -# ── Quick-режим ────────────────────────────────────────────────────────────── -install_quick_mode() { - log_step "Установка Quick-режима" - - # Выбор домена - local domain - domain=$(select_quick_domain) - [ $? -ne 0 ] && return - - # Выбор порта - local port - port=$(select_port) - [ $? -ne 0 ] && return - - # Генерация секрета - local secret - secret=$(generate_hex 32) - - # Подтверждение - local ip - ip=$(get_server_ip) - echo "" - echo -e " ${BOLD}${WHITE}📋 Конфигурация:${NC}" - echo -e " IP: ${CYAN}${ip}${NC}" - echo -e " Порт: ${CYAN}${port}${NC}" - echo -e " Маскировка: ${CYAN}${domain}${NC}" - echo -e " Режим: ${GREEN}Quick${NC}" - echo "" - - if ! confirm "Установить прокси?"; then - return - fi - - # Установка - ensure_deps - install_telemt_full || return - - # Генерируем конфиг telemt - generate_telemt_toml "$secret" "$port" "quick" "$domain" "443" - - # Валидация - validate_telemt_config || return - - # Запуск - start_telemt || return - - # Сохраняем GoTelegram конфиг - save_gotelegram_config "telemt" "quick" "$port" "$secret" "$domain" "" "" - - # Благодарности - show_credits - - # Результат - show_proxy_info - log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Quick-режим)" -} - -# ── Stealth-режим ──────────────────────────────────────────────────────────── -install_stealth_mode() { - log_step "Установка Stealth-режима" - - # Ввод домена - echo "" - echo -ne " ${WHITE}Введите ваш домен (например, example.com):${NC} " - read -r user_domain - - if [ -z "$user_domain" ] || ! validate_domain "$user_domain"; then - log_error "Некорректный домен: ${user_domain:-<пусто>}" - return - fi - - # Проверяем DNS - local resolved_ip server_ip - resolved_ip=$(dig +short "$user_domain" A 2>/dev/null | head -1) - server_ip=$(get_server_ip) - - if [ -n "$resolved_ip" ] && [ "$resolved_ip" != "$server_ip" ]; then - log_warning "Домен $user_domain указывает на $resolved_ip, а не на $server_ip" - if ! confirm "Продолжить всё равно?"; then - return - fi - fi - - # Email для Let's Encrypt - echo -ne " ${WHITE}Email для SSL (Enter = без email):${NC} " - read -r ssl_email - - # Выбор шаблона - local template_dir - template_dir=$(interactive_template_selection) - [ $? -ne 0 ] && return - - # Выбор порта для telemt (внутренний) - local telemt_port=8443 - echo "" - echo -e " ${DIM}telemt будет слушать на порту $telemt_port (внутренний)${NC}" - echo -e " ${DIM}nginx будет на 443 (внешний) и проксировать трафик${NC}" - - # Генерация секрета - local secret - secret=$(generate_hex 32) - - # Подтверждение - echo "" - echo -e " ${BOLD}${WHITE}📋 Конфигурация:${NC}" - echo -e " Домен: ${CYAN}${user_domain}${NC}" - echo -e " Порт: ${CYAN}443 (nginx) → $telemt_port (telemt)${NC}" - echo -e " Режим: ${MAGENTA}Stealth${NC}" - echo "" - - if ! confirm "Установить прокси + сайт?"; then - return - fi - - # Установка - ensure_deps - install_telemt_full || return - - # Конфиг telemt: маскировка на localhost (nginx) - generate_telemt_toml "$secret" "$telemt_port" "stealth" "127.0.0.1" "443" - - # Настройка сайта (nginx + certbot + шаблон) - setup_stealth_mode "$user_domain" "$template_dir" "$telemt_port" "$ssl_email" || return - - # Запуск telemt - start_telemt || return - - # Сохраняем конфиг - local tpl_id - tpl_id=$(basename "$template_dir") - save_gotelegram_config "telemt" "stealth" "443" "$secret" "127.0.0.1" "$user_domain" "$tpl_id" - - # Результат - show_proxy_info - echo -e " ${WHITE}Сайт:${NC} ${GREEN}https://${user_domain}${NC}" - log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Stealth-режим)" -} - -# ── Статус ─────────────────────────────────────────────────────────────────── -menu_status() { - show_proxy_info - - # Дополнительно для stealth - local mode - mode=$(config_get mode 2>/dev/null) - if [ "$mode" = "stealth" ]; then - local domain - domain=$(config_get domain 2>/dev/null) - if [ -n "$domain" ]; then - local ssl_expiry - ssl_expiry=$(get_ssl_expiry "$domain") - local nginx_st - nginx_st=$(nginx_status) - echo -e " ${WHITE}nginx:${NC} ${nginx_st}" - echo -e " ${WHITE}SSL до:${NC} ${ssl_expiry}" - echo -e " ${WHITE}Сайт:${NC} https://${domain}" - echo "" - fi - fi -} - -# ── Ссылка ─────────────────────────────────────────────────────────────────── -menu_link() { - local secret port ip link - secret=$(get_config_value secret) - port=$(get_config_value port) - ip=$(get_server_ip) - link=$(generate_proxy_link "$ip" "$port" "$secret") - - echo "" - echo -e " ${BOLD}${WHITE}🔗 Ссылка для подключения:${NC}" - echo "" - echo -e " ${GREEN}${link}${NC}" - echo "" - - if command -v qrencode &>/dev/null; then - qrencode -t UTF8 -m 2 "$link" 2>/dev/null - fi -} - -# ── Поделиться ─────────────────────────────────────────────────────────────── -menu_share() { - local secret port ip link - secret=$(get_config_value secret) - port=$(get_config_value port) - ip=$(get_server_ip) - link=$(generate_proxy_link "$ip" "$port" "$secret") - - echo "" - echo -e " ${BOLD}📤 Перешлите это сообщение:${NC}" - echo "" - echo "🔐 MTProxy для Telegram (GoTelegram v${GOTELEGRAM_VERSION})" - echo "" - echo "🌍 Сервер: $ip" - echo "🔌 Порт: $port" - echo "" - echo "👉 Подключиться одним нажатием:" - echo "$link" - echo "" - echo "Просто нажмите на ссылку или настройте вручную." - echo "" -} - -# ── Перезапуск ─────────────────────────────────────────────────────────────── -menu_restart() { - restart_telemt - local mode - mode=$(config_get mode 2>/dev/null) - if [ "$mode" = "stealth" ]; then - restart_nginx - fi -} - -# ── Логи ───────────────────────────────────────────────────────────────────── -menu_logs() { - echo "" - echo -e " ${BOLD}${WHITE}📋 Логи telemt (последние 40 строк):${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" - telemt_logs 40 - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" -} - -# ── Смена режима / шаблона ─────────────────────────────────────────────────── -menu_change_mode() { - local current_mode - current_mode=$(config_get mode 2>/dev/null) - echo "" - echo -e " ${WHITE}Текущий режим:${NC} ${CYAN}${current_mode}${NC}" - echo "" - echo -e " ${CYAN}1)${NC} Сменить шаблон сайта (только stealth)" - echo -e " ${CYAN}2)${NC} Переключить режим (quick ↔ stealth)" - echo -e " ${CYAN}0)${NC} Назад" - echo -ne " ${WHITE}Выбор:${NC} " - read -r ch - - case "$ch" in - 1) - if [ "$current_mode" != "stealth" ]; then - log_error "Смена шаблона доступна только в stealth-режиме" - return - fi - local template_dir - template_dir=$(interactive_template_selection) - [ $? -ne 0 ] && return - switch_template "$template_dir" - ;; - 2) - log_warning "Переключение режима требует переустановки." - if confirm "Переустановить прокси?"; then - menu_install - fi - ;; - esac -} - -# ── Управление сайтом ─────────────────────────────────────────────────────── -menu_website() { - local mode - mode=$(config_get mode 2>/dev/null) - if [ "$mode" != "stealth" ]; then - log_info "Управление сайтом доступно только в stealth-режиме" - return - fi - - local domain - domain=$(config_get domain 2>/dev/null) - - echo "" - echo -e " ${BOLD}${WHITE}🌐 Управление сайтом${NC}" - echo -e " Домен: ${CYAN}${domain}${NC}" - echo -e " SSL до: $(get_ssl_expiry "$domain")" - echo "" - echo -e " ${CYAN}1)${NC} Обновить SSL сертификат" - echo -e " ${CYAN}2)${NC} Перезапустить nginx" - echo -e " ${CYAN}3)${NC} Сменить шаблон" - echo -e " ${CYAN}0)${NC} Назад" - echo -ne " ${WHITE}Выбор:${NC} " - read -r ch - - case "$ch" in - 1) renew_ssl_certificate ;; - 2) restart_nginx ;; - 3) - local template_dir - template_dir=$(interactive_template_selection) - [ $? -ne 0 ] && return - switch_template "$template_dir" - ;; - esac -} - -# ── Удаление ───────────────────────────────────────────────────────────────── -menu_remove() { - echo "" - log_warning "Это удалит прокси и все настройки." - if ! confirm "Вы уверены?"; then - return - fi - - # Предлагаем бекап - if confirm "Сделать бекап перед удалением?"; then - interactive_backup - fi - - remove_telemt - - local mode - mode=$(config_get mode 2>/dev/null) - if [ "$mode" = "stealth" ]; then - remove_stealth_mode - fi - - rm -f "$GOTELEGRAM_CONFIG" - log_success "GoTelegram полностью удалён" -} - -# ── Промо ──────────────────────────────────────────────────────────────────── -menu_promo() { - echo "" - echo -e " ${YELLOW}╔══════════════════════════════════════════════════════╗${NC}" - echo -e " ${YELLOW}║${NC} ${BOLD}💰 ХОСТИНГ СО СКИДКОЙ ДО -60%${NC} ${YELLOW}║${NC}" - echo -e " ${YELLOW}║${NC} Ссылка: ${CYAN}https://vk.cc/ct29NQ${NC} ${YELLOW}║${NC}" - echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}" - echo -e " ${YELLOW}║${NC} Промокоды: OFF60, antenka20, antenka6, antenka12 ${YELLOW}║${NC}" - echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}" - echo -e " ${YELLOW}║${NC} Донат: ${CYAN}https://pay.cloudtips.ru/p/7410814f${NC} ${YELLOW}║${NC}" - echo -e " ${YELLOW}╚══════════════════════════════════════════════════════╝${NC}" - echo "" -} - -# ── Точка входа ────────────────────────────────────────────────────────────── -main() { - check_root - init_dirs - show_banner - - # Pre-flight - check_os - check_disk_space 500 - - while true; do - show_main_menu - read -r choice - case "$choice" in - 1) menu_install ;; - 2) menu_status ;; - 3) menu_link ;; - 4) menu_share ;; - 5) menu_restart ;; - 6) menu_logs ;; - 7) menu_change_mode ;; - 8) interactive_backup ;; - 9) interactive_restore ;; - 10) update_telemt ;; - 11) menu_website ;; - 12) menu_remove ;; - 13) menu_promo ;; - 0|q|exit) echo ""; log_info "До встречи! 👋"; exit 0 ;; - *) log_error "Неверный выбор" ;; - esac - - echo "" - echo -ne " ${DIM}Нажмите Enter для возврата в меню...${NC}" - read -r - done -} - -main "$@" +#!/bin/bash +# ══════════════════════════════════════════════════════════════════════════════ +# GoTelegram v2.2.1 — MTProxy на ядре telemt (Rust + Tokio) +# Anti-DPI • Fake TLS • TCP Splice • JA3/JA4 Resistance +# +# Установка: +# curl -sL URL/install.sh | sudo bash +# ══════════════════════════════════════════════════════════════════════════════ + +set -uo pipefail + +# Путь к скрипту и библиотекам +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +LIB_DIR="$SCRIPT_DIR/lib" + +# Загружаем библиотеки +source "$LIB_DIR/common.sh" +source "$LIB_DIR/telemt.sh" +source "$LIB_DIR/telemt_config.sh" +source "$LIB_DIR/website.sh" +source "$LIB_DIR/templates_catalog.sh" +source "$LIB_DIR/backup.sh" + +# ── Главное меню ───────────────────────────────────────────────────────────── +show_main_menu() { + local proxy_status bot_status + proxy_status=$(telemt_status) + bot_status=$(bot_service_status) + + local proxy_badge bot_badge + case "$proxy_status" in + running) proxy_badge="${GREEN}● Работает${NC}" ;; + stopped) proxy_badge="${YELLOW}○ Остановлен${NC}" ;; + *) proxy_badge="${RED}✗ Не установлен${NC}" ;; + esac + case "$bot_status" in + running) bot_badge="${GREEN}● Бот работает${NC}" ;; + stopped) bot_badge="${YELLOW}○ Бот остановлен${NC}" ;; + *) bot_badge="${DIM}нет${NC}" ;; + esac + + echo "" + echo -e " ${BOLD}${WHITE}Главное меню${NC} │ Proxy: ${proxy_badge} │ ${bot_badge}" + echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" + echo -e " ${DIM}── Прокси ──${NC}" + 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 -e " ${CYAN} 6)${NC} 📋 Логи" + echo -e " ${CYAN} 7)${NC} 🎭 Сменить режим / шаблон" + echo -e " ${DIM}── Управление ──${NC}" + echo -e " ${CYAN} 8)${NC} 💾 Бекап конфигурации" + echo -e " ${CYAN} 9)${NC} 📦 Восстановить из бекапа" + echo -e " ${CYAN}10)${NC} ⬆️ Обновить telemt" + echo -e " ${CYAN}11)${NC} 🌐 Управление сайтом (SSL)" + echo -e " ${DIM}── Бот и прочее ──${NC}" + echo -e " ${CYAN}12)${NC} 🤖 Telegram-бот" + echo -e " ${CYAN}13)${NC} 🗑 Удалить всё" + echo -e " ${CYAN}14)${NC} 🏷 Промо" + echo -e " ${CYAN} 0)${NC} 🚪 Выход" + echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" + echo -ne " ${WHITE}Выбор:${NC} " +} + +# ── Установка: выбор режима ────────────────────────────────────────────────── +menu_install() { + # Проверяем v1 + if detect_v1_installation; then + echo "" + echo -e " ${YELLOW}⚠️ Обнаружена установка GoTelegram v1 (mtg)${NC}" + echo -e " ${DIM}Контейнер: ${V1_CONTAINER_NAME}${NC}" + echo "" + if ! migrate_v1_to_v2; then + return + fi + fi + + echo "" + echo -e " ${BOLD}${WHITE}🎭 Выберите режим маскировки:${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + echo -e " ${CYAN}1)${NC} ${GREEN}⚡ Quick${NC} — маскировка под популярный сайт" + echo -e " ${DIM}Быстро, без домена. telemt маскирует трафик${NC}" + echo -e " ${DIM}под выбранный сайт (google.com и т.д.)${NC}" + echo "" + echo -e " ${CYAN}2)${NC} ${MAGENTA}🛡 Stealth${NC} — свой сайт + полная маскировка" + echo -e " ${DIM}nginx + SSL + HTML-шаблон + telemt.${NC}" + echo -e " ${DIM}DPI видит реальный сайт с реальным сертификатом.${NC}" + echo -e " ${DIM}Требует: домен, направленный на этот сервер.${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + echo -ne " ${WHITE}Выбор (1/2):${NC} " + read -r mode_choice + mode_choice="${mode_choice:-}" + + case "$mode_choice" in + 1) install_quick_mode ;; + 2) install_stealth_mode ;; + *) log_error "Неверный выбор: ${mode_choice:-<пусто>}" ;; + esac +} + +# ── Quick-режим ────────────────────────────────────────────────────────────── +install_quick_mode() { + log_step "Установка Quick-режима" + + # Выбор домена + local domain + domain=$(select_quick_domain) + [ $? -ne 0 ] && return + + # Выбор порта + local port + port=$(select_port) + [ $? -ne 0 ] && return + + # Генерация секрета + local secret + secret=$(generate_hex 32) + + # Подтверждение + local ip + ip=$(get_server_ip) + echo "" + echo -e " ${BOLD}${WHITE}📋 Конфигурация:${NC}" + echo -e " IP: ${CYAN}${ip}${NC}" + echo -e " Порт: ${CYAN}${port}${NC}" + echo -e " Маскировка: ${CYAN}${domain}${NC}" + echo -e " Режим: ${GREEN}Quick${NC}" + echo "" + + if ! confirm "Установить прокси?"; then + return + fi + + # Установка + ensure_deps + install_telemt_full || return + + # Генерируем конфиг telemt + generate_telemt_toml "$secret" "$port" "quick" "$domain" "443" + + # Валидация + validate_telemt_config || return + + # Запуск + start_telemt || return + + # Сохраняем GoTelegram конфиг + save_gotelegram_config "telemt" "quick" "$port" "$secret" "$domain" "" "" + + # Благодарности + show_credits + + # Результат + show_proxy_info + log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Quick-режим)" +} + +# ── Stealth-режим ──────────────────────────────────────────────────────────── +install_stealth_mode() { + log_step "Установка Stealth-режима" + + # Ввод домена + echo "" + echo -ne " ${WHITE}Введите ваш домен (например, example.com):${NC} " + read -r user_domain + + if [ -z "$user_domain" ] || ! validate_domain "$user_domain"; then + log_error "Некорректный домен: ${user_domain:-<пусто>}" + return + fi + + # Проверяем DNS + local resolved_ip server_ip + resolved_ip=$(dig +short "$user_domain" A 2>/dev/null | head -1) + server_ip=$(get_server_ip) + + if [ -n "$resolved_ip" ] && [ "$resolved_ip" != "$server_ip" ]; then + log_warning "Домен $user_domain указывает на $resolved_ip, а не на $server_ip" + if ! confirm "Продолжить всё равно?"; then + return + fi + fi + + # Email для Let's Encrypt + echo -ne " ${WHITE}Email для SSL (Enter = без email):${NC} " + read -r ssl_email + + # Выбор шаблона + local template_dir + template_dir=$(interactive_template_selection) + [ $? -ne 0 ] && return + + # Выбор порта для telemt (внутренний) + local telemt_port=8443 + echo "" + echo -e " ${DIM}telemt будет слушать на порту $telemt_port (внутренний)${NC}" + echo -e " ${DIM}nginx будет на 443 (внешний) и проксировать трафик${NC}" + + # Генерация секрета + local secret + secret=$(generate_hex 32) + + # Подтверждение + echo "" + echo -e " ${BOLD}${WHITE}📋 Конфигурация:${NC}" + echo -e " Домен: ${CYAN}${user_domain}${NC}" + echo -e " Порт: ${CYAN}443 (nginx) → $telemt_port (telemt)${NC}" + echo -e " Режим: ${MAGENTA}Stealth${NC}" + echo "" + + if ! confirm "Установить прокси + сайт?"; then + return + fi + + # Установка + ensure_deps + install_telemt_full || return + + # Конфиг telemt: маскировка на localhost (nginx) + generate_telemt_toml "$secret" "$telemt_port" "stealth" "127.0.0.1" "443" + + # Настройка сайта (nginx + certbot + шаблон) + setup_stealth_mode "$user_domain" "$template_dir" "$telemt_port" "$ssl_email" || return + + # Запуск telemt + start_telemt || return + + # Сохраняем конфиг + local tpl_id + tpl_id=$(basename "$template_dir") + save_gotelegram_config "telemt" "stealth" "443" "$secret" "127.0.0.1" "$user_domain" "$tpl_id" + + # Результат + show_proxy_info + echo -e " ${WHITE}Сайт:${NC} ${GREEN}https://${user_domain}${NC}" + log_success "GoTelegram v${GOTELEGRAM_VERSION} установлен! (Stealth-режим)" +} + +# ── Статус ─────────────────────────────────────────────────────────────────── +menu_status() { + show_proxy_info + + # Дополнительно для stealth + local mode + mode=$(config_get mode 2>/dev/null) + if [ "$mode" = "stealth" ]; then + local domain + domain=$(config_get domain 2>/dev/null) + if [ -n "$domain" ]; then + local ssl_expiry + ssl_expiry=$(get_ssl_expiry "$domain") + local nginx_st + nginx_st=$(nginx_status) + echo -e " ${WHITE}nginx:${NC} ${nginx_st}" + echo -e " ${WHITE}SSL до:${NC} ${ssl_expiry}" + echo -e " ${WHITE}Сайт:${NC} https://${domain}" + echo "" + fi + fi +} + +# ── Ссылка ─────────────────────────────────────────────────────────────────── +menu_link() { + local secret port ip link + secret=$(get_config_value secret) + port=$(get_config_value port) + ip=$(get_server_ip) + link=$(generate_proxy_link "$ip" "$port" "$secret") + + echo "" + echo -e " ${BOLD}${WHITE}🔗 Ссылка для подключения:${NC}" + echo "" + echo -e " ${GREEN}${link}${NC}" + echo "" + + if command -v qrencode &>/dev/null; then + qrencode -t UTF8 -m 2 "$link" 2>/dev/null + fi +} + +# ── Поделиться ─────────────────────────────────────────────────────────────── +menu_share() { + local secret port ip link + secret=$(get_config_value secret) + port=$(get_config_value port) + ip=$(get_server_ip) + link=$(generate_proxy_link "$ip" "$port" "$secret") + + echo "" + echo -e " ${BOLD}📤 Перешлите это сообщение:${NC}" + echo "" + echo "🔐 MTProxy для Telegram (GoTelegram v${GOTELEGRAM_VERSION})" + echo "" + echo "🌍 Сервер: $ip" + echo "🔌 Порт: $port" + echo "" + echo "👉 Подключиться одним нажатием:" + echo "$link" + echo "" + echo "Просто нажмите на ссылку или настройте вручную." + echo "" +} + +# ── Перезапуск ─────────────────────────────────────────────────────────────── +menu_restart() { + restart_telemt + local mode + mode=$(config_get mode 2>/dev/null) + if [ "$mode" = "stealth" ]; then + restart_nginx + fi +} + +# ── Логи ───────────────────────────────────────────────────────────────────── +menu_logs() { + echo "" + echo -e " ${BOLD}${WHITE}📋 Логи telemt (последние 40 строк):${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + telemt_logs 40 + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" +} + +# ── Смена режима / шаблона ─────────────────────────────────────────────────── +menu_change_mode() { + local current_mode + current_mode=$(config_get mode 2>/dev/null) + echo "" + echo -e " ${WHITE}Текущий режим:${NC} ${CYAN}${current_mode}${NC}" + echo "" + echo -e " ${CYAN}1)${NC} Сменить шаблон сайта (только stealth)" + echo -e " ${CYAN}2)${NC} Переключить режим (quick ↔ stealth)" + echo -e " ${CYAN}0)${NC} Назад" + echo -ne " ${WHITE}Выбор:${NC} " + read -r ch + + case "$ch" in + 1) + if [ "$current_mode" != "stealth" ]; then + log_error "Смена шаблона доступна только в stealth-режиме" + return + fi + local template_dir + template_dir=$(interactive_template_selection) + [ $? -ne 0 ] && return + switch_template "$template_dir" + ;; + 2) + log_warning "Переключение режима требует переустановки." + if confirm "Переустановить прокси?"; then + menu_install + fi + ;; + esac +} + +# ── Управление сайтом ─────────────────────────────────────────────────────── +menu_website() { + local mode + mode=$(config_get mode 2>/dev/null) + if [ "$mode" != "stealth" ]; then + log_info "Управление сайтом доступно только в stealth-режиме" + return + fi + + local domain + domain=$(config_get domain 2>/dev/null) + + echo "" + echo -e " ${BOLD}${WHITE}🌐 Управление сайтом${NC}" + echo -e " Домен: ${CYAN}${domain}${NC}" + echo -e " SSL до: $(get_ssl_expiry "$domain")" + echo "" + echo -e " ${CYAN}1)${NC} Обновить SSL сертификат" + echo -e " ${CYAN}2)${NC} Перезапустить nginx" + echo -e " ${CYAN}3)${NC} Сменить шаблон" + echo -e " ${CYAN}0)${NC} Назад" + echo -ne " ${WHITE}Выбор:${NC} " + read -r ch + + case "$ch" in + 1) renew_ssl_certificate ;; + 2) restart_nginx ;; + 3) + local template_dir + template_dir=$(interactive_template_selection) + [ $? -ne 0 ] && return + switch_template "$template_dir" + ;; + esac +} + +# ── Удаление ───────────────────────────────────────────────────────────────── +menu_remove() { + echo "" + echo -e " ${BOLD}${RED}🗑 Удаление GoTelegram${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + echo -e " ${CYAN}1)${NC} Удалить только прокси (telemt)" + echo -e " ${CYAN}2)${NC} Удалить только Telegram-бота" + echo -e " ${CYAN}3)${NC} Удалить всё (прокси + бот + настройки)" + echo -e " ${CYAN}0)${NC} Назад" + echo -ne " ${WHITE}Выбор:${NC} " + read -r rm_choice + + case "$rm_choice" in + 1) + log_warning "Это удалит прокси и все его настройки." + if ! confirm "Удалить прокси?"; then return; fi + if confirm "Сделать бекап перед удалением?"; then + interactive_backup + fi + remove_telemt + local mode + mode=$(config_get mode 2>/dev/null) + if [ "$mode" = "stealth" ]; then + remove_stealth_mode + fi + rm -f "$GOTELEGRAM_CONFIG" + log_success "Прокси удалён" + ;; + 2) + bot_remove + ;; + 3) + log_warning "Это удалит ВСЁ: прокси, бот, сайт, настройки." + if ! confirm "Вы точно уверены?"; then return; fi + if confirm "Сделать бекап перед удалением?"; then + interactive_backup + fi + # Прокси + remove_telemt + local mode + mode=$(config_get mode 2>/dev/null) + if [ "$mode" = "stealth" ]; then + remove_stealth_mode + fi + rm -f "$GOTELEGRAM_CONFIG" + # Бот + if [ "$(bot_service_status)" != "not_installed" ]; then + systemctl stop "$BOT_SERVICE" 2>/dev/null + systemctl disable "$BOT_SERVICE" 2>/dev/null + rm -f "/etc/systemd/system/${BOT_SERVICE}.service" + systemctl daemon-reload + rm -rf "$BOT_DIR" + fi + log_success "GoTelegram полностью удалён (прокси + бот)" + ;; + esac +} + +# ── Telegram-бот ──────────────────────────────────────────────────────────── +BOT_DIR="/opt/gotelegram-bot" +BOT_SERVICE="gotelegram-bot" + +bot_service_status() { + if ! systemctl list-unit-files "$BOT_SERVICE.service" &>/dev/null 2>&1; then + echo "not_installed" + elif systemctl is-active "$BOT_SERVICE" &>/dev/null 2>&1; then + echo "running" + else + echo "stopped" + fi +} + +menu_bot() { + local st + st=$(bot_service_status) + + echo "" + echo -e " ${BOLD}${WHITE}🤖 Telegram-бот${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + + case "$st" in + running) + echo -e " Статус: ${GREEN}● Работает${NC}" + echo "" + 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} ⚙️ Настройки (.env)" + echo -e " ${CYAN}6)${NC} 🗑 Удалить бота" + ;; + stopped) + echo -e " Статус: ${YELLOW}○ Остановлен${NC}" + echo "" + echo -e " ${CYAN}1)${NC} 📊 Статус бота" + echo -e " ${CYAN}2)${NC} 📋 Логи бота" + echo -e " ${CYAN}3)${NC} ▶️ Запустить бота" + echo -e " ${CYAN}5)${NC} ⚙️ Настройки (.env)" + echo -e " ${CYAN}6)${NC} 🗑 Удалить бота" + ;; + *) + echo -e " Статус: ${RED}✗ Не установлен${NC}" + echo "" + echo -e " ${DIM}Бот позволяет управлять прокси прямо из Telegram:${NC}" + echo -e " ${DIM}статус, перезапуск, смена режима, бекап, QR-код.${NC}" + echo "" + echo -e " ${CYAN}1)${NC} 🔧 Установить бота" + ;; + esac + + echo -e " ${CYAN}0)${NC} « Назад" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + echo -ne " ${WHITE}Выбор:${NC} " + read -r ch + + case "$st" in + running) + case "$ch" in + 1) bot_show_status ;; + 2) bot_show_logs ;; + 3) systemctl restart "$BOT_SERVICE" && log_success "Бот перезапущен" ;; + 4) systemctl stop "$BOT_SERVICE" && log_info "Бот остановлен" ;; + 5) bot_edit_config ;; + 6) bot_remove ;; + esac + ;; + stopped) + case "$ch" in + 1) bot_show_status ;; + 2) bot_show_logs ;; + 3) systemctl start "$BOT_SERVICE" && log_success "Бот запущен" ;; + 5) bot_edit_config ;; + 6) bot_remove ;; + esac + ;; + *) + case "$ch" in + 1) bot_install ;; + esac + ;; + esac +} + +bot_install() { + log_step "Установка Telegram-бота" + + # Python + if ! command -v python3 &>/dev/null; then + log_info "Установка Python3..." + if command -v apt-get &>/dev/null; then + apt-get update -qq && apt-get install -y -qq python3 python3-pip python3-venv + elif command -v dnf &>/dev/null; then + dnf install -y -q python3 python3-pip + elif command -v yum &>/dev/null; then + yum install -y -q python3 python3-pip + fi + fi + + # Копируем файлы бота + mkdir -p "$BOT_DIR" + if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then + cp "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/" + cp "$SCRIPT_DIR/gotelegram-bot/requirements.txt" "$BOT_DIR/" + [ -f "$SCRIPT_DIR/gotelegram-bot/config.example.env" ] && \ + cp "$SCRIPT_DIR/gotelegram-bot/config.example.env" "$BOT_DIR/" + else + log_error "Файлы бота не найдены в $SCRIPT_DIR/gotelegram-bot/" + return 1 + fi + + # Каталог шаблонов + [ -f "$SCRIPT_DIR/templates_catalog.json" ] && \ + cp "$SCRIPT_DIR/templates_catalog.json" "$GOTELEGRAM_DIR/" + + # Venv + if [ ! -d "$BOT_DIR/venv" ]; then + log_info "Создание виртуального окружения..." + python3 -m venv "$BOT_DIR/venv" + fi + log_info "Установка зависимостей..." + "$BOT_DIR/venv/bin/pip" install -r "$BOT_DIR/requirements.txt" -q + + # Конфигурация + if [ ! -f "$BOT_DIR/.env" ]; then + echo "" + echo -e " ${YELLOW}Введите BOT_TOKEN от @BotFather:${NC}" + local token="" + while [ -z "$token" ]; do + echo -ne " ${WHITE}Token:${NC} " + read -r token + token=$(echo "$token" | tr -d '[:space:]') + [ -z "$token" ] && log_error "Токен не может быть пустым" + done + + echo -ne " ${WHITE}ID администратора (Enter = доступ для всех):${NC} " + read -r admin_id + + { + echo "BOT_TOKEN=$token" + [ -n "$admin_id" ] && echo "ALLOWED_IDS=$admin_id" + } > "$BOT_DIR/.env" + + chmod 600 "$BOT_DIR/.env" + log_success ".env создан" + else + log_info ".env уже существует, настройки сохранены" + fi + + # Systemd + cat > "/etc/systemd/system/${BOT_SERVICE}.service" << SVCEOF +[Unit] +Description=GoTelegram v${GOTELEGRAM_VERSION} Telegram Bot +After=network.target + +[Service] +Type=simple +WorkingDirectory=$BOT_DIR +ExecStart=$BOT_DIR/venv/bin/python $BOT_DIR/bot.py +Restart=always +RestartSec=5 +Environment=PATH=$BOT_DIR/venv/bin:/usr/bin + +[Install] +WantedBy=multi-user.target +SVCEOF + + systemctl daemon-reload + systemctl enable "$BOT_SERVICE" &>/dev/null + systemctl restart "$BOT_SERVICE" 2>/dev/null || systemctl start "$BOT_SERVICE" + + echo "" + log_success "Бот установлен и запущен!" + echo -e " ${DIM}Проверка: systemctl status $BOT_SERVICE${NC}" + echo -e " ${DIM}Логи: journalctl -u $BOT_SERVICE -f${NC}" +} + +bot_show_status() { + echo "" + echo -e " ${BOLD}${WHITE}📊 Статус Telegram-бота${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + systemctl status "$BOT_SERVICE" --no-pager -l 2>/dev/null | head -15 | while IFS= read -r line; do + echo " $line" + done + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + + if [ -f "$BOT_DIR/.env" ]; then + local has_token has_ids + has_token=$(grep -c "BOT_TOKEN=" "$BOT_DIR/.env" 2>/dev/null || echo 0) + has_ids=$(grep "ALLOWED_IDS=" "$BOT_DIR/.env" 2>/dev/null | cut -d= -f2) + echo -e " Token: ${has_token:+${GREEN}✓ настроен${NC}}" + echo -e " Доступ: ${has_ids:+ID: $has_ids}${has_ids:-${YELLOW}все пользователи${NC}}" + fi +} + +bot_show_logs() { + echo "" + echo -e " ${BOLD}${WHITE}📋 Логи бота (последние 30 строк):${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + journalctl -u "$BOT_SERVICE" --no-pager -n 30 2>/dev/null | while IFS= read -r line; do + echo " $line" + done + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" +} + +bot_edit_config() { + echo "" + echo -e " ${BOLD}${WHITE}⚙️ Настройки бота${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + + if [ -f "$BOT_DIR/.env" ]; then + echo -e " ${DIM}Текущий .env:${NC}" + while IFS= read -r line; do + # Маскируем токен для безопасности + if [[ "$line" == BOT_TOKEN=* ]]; then + local tok="${line#BOT_TOKEN=}" + echo -e " BOT_TOKEN=${tok:0:10}...${tok: -5}" + else + echo " $line" + fi + done < "$BOT_DIR/.env" + fi + + echo "" + echo -e " ${CYAN}1)${NC} Сменить BOT_TOKEN" + echo -e " ${CYAN}2)${NC} Изменить ALLOWED_IDS" + echo -e " ${CYAN}0)${NC} Назад" + echo -ne " ${WHITE}Выбор:${NC} " + read -r ch + + case "$ch" in + 1) + echo -ne " ${WHITE}Новый BOT_TOKEN:${NC} " + read -r new_token + new_token=$(echo "$new_token" | tr -d '[:space:]') + if [ -n "$new_token" ]; then + sed -i "s|^BOT_TOKEN=.*|BOT_TOKEN=$new_token|" "$BOT_DIR/.env" + systemctl restart "$BOT_SERVICE" + log_success "Токен обновлён, бот перезапущен" + else + log_error "Пустой токен" + fi + ;; + 2) + echo -ne " ${WHITE}ALLOWED_IDS (через запятую, пусто = все):${NC} " + read -r new_ids + new_ids=$(echo "$new_ids" | tr -d '[:space:]') + if grep -q "^ALLOWED_IDS=" "$BOT_DIR/.env" 2>/dev/null; then + if [ -n "$new_ids" ]; then + sed -i "s|^ALLOWED_IDS=.*|ALLOWED_IDS=$new_ids|" "$BOT_DIR/.env" + else + sed -i '/^ALLOWED_IDS=/d' "$BOT_DIR/.env" + fi + else + [ -n "$new_ids" ] && echo "ALLOWED_IDS=$new_ids" >> "$BOT_DIR/.env" + fi + systemctl restart "$BOT_SERVICE" + log_success "Доступ обновлён, бот перезапущен" + ;; + esac +} + +bot_remove() { + echo "" + log_warning "Это удалит Telegram-бота и все его настройки." + if ! confirm "Удалить бота?"; then + return + fi + + systemctl stop "$BOT_SERVICE" 2>/dev/null + systemctl disable "$BOT_SERVICE" 2>/dev/null + rm -f "/etc/systemd/system/${BOT_SERVICE}.service" + systemctl daemon-reload + rm -rf "$BOT_DIR" + log_success "Бот полностью удалён" +} + +# ── Промо ──────────────────────────────────────────────────────────────────── +menu_promo() { + echo "" + echo -e " ${YELLOW}╔══════════════════════════════════════════════════════╗${NC}" + echo -e " ${YELLOW}║${NC} ${BOLD}💰 ХОСТИНГ СО СКИДКОЙ ДО -60%${NC} ${YELLOW}║${NC}" + echo -e " ${YELLOW}║${NC} Ссылка: ${CYAN}https://vk.cc/ct29NQ${NC} ${YELLOW}║${NC}" + echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}" + echo -e " ${YELLOW}║${NC} Промокоды: OFF60, antenka20, antenka6, antenka12 ${YELLOW}║${NC}" + echo -e " ${YELLOW}║${NC} ${YELLOW}║${NC}" + echo -e " ${YELLOW}║${NC} Донат: ${CYAN}https://pay.cloudtips.ru/p/7410814f${NC} ${YELLOW}║${NC}" + echo -e " ${YELLOW}╚══════════════════════════════════════════════════════╝${NC}" + echo "" +} + +# ── Точка входа ────────────────────────────────────────────────────────────── +main() { + check_root + init_dirs + show_banner + + # Pre-flight + check_os + check_disk_space 500 + + while true; do + show_main_menu + read -r choice + case "$choice" in + 1) menu_install ;; + 2) menu_status ;; + 3) menu_link ;; + 4) menu_share ;; + 5) menu_restart ;; + 6) menu_logs ;; + 7) menu_change_mode ;; + 8) interactive_backup ;; + 9) interactive_restore ;; + 10) update_telemt ;; + 11) menu_website ;; + 12) menu_bot ;; + 13) menu_remove ;; + 14) menu_promo ;; + 0|q|exit) echo ""; log_info "До встречи! 👋"; exit 0 ;; + *) log_error "Неверный выбор" ;; + esac + + echo "" + echo -ne " ${DIM}Нажмите Enter для возврата в меню...${NC}" + read -r + done +} + +main "$@" diff --git a/install_gotelegram_bot.sh b/install_gotelegram_bot.sh index c5d4306..bb0f1a6 100644 --- a/install_gotelegram_bot.sh +++ b/install_gotelegram_bot.sh @@ -1,132 +1,132 @@ -#!/bin/bash -# GoTelegram v2.2 — Установка Telegram-бота -# Создаёт venv, ставит зависимости, настраивает systemd - -set -e -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -CYAN='\033[0;36m' -NC='\033[0m' - -BOT_DIR="/opt/gotelegram-bot" -SERVICE_NAME="gotelegram-bot" -GOTELEGRAM_DIR="/opt/gotelegram" - -if [ "$EUID" -ne 0 ]; then - echo -e "${RED}Запустите с sudo.${NC}" - exit 1 -fi - -echo -e "${CYAN}╔═══════════════════════════════════════════╗${NC}" -echo -e "${CYAN}║${NC} ${GREEN}GoTelegram v2.2 — Установка бота${NC} ${CYAN}║${NC}" -echo -e "${CYAN}╚═══════════════════════════════════════════╝${NC}" -echo "" - -# ── Python ─────────────────────────────────────────────────────────────────── -if ! command -v python3 &>/dev/null; then - echo -e "${YELLOW}[*] Установка python3...${NC}" - if command -v apt-get &>/dev/null; then - apt-get update -qq && apt-get install -y -qq python3 python3-pip python3-venv - elif command -v dnf &>/dev/null; then - dnf install -y -q python3 python3-pip - elif command -v yum &>/dev/null; then - yum install -y -q python3 python3-pip - fi -fi - -# ── Каталог бота ───────────────────────────────────────────────────────────── -mkdir -p "$BOT_DIR" -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then - echo -e "${GREEN}[*] Копирование файлов бота...${NC}" - cp "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/" - cp "$SCRIPT_DIR/gotelegram-bot/requirements.txt" "$BOT_DIR/" - [ -f "$SCRIPT_DIR/gotelegram-bot/config.example.env" ] && cp "$SCRIPT_DIR/gotelegram-bot/config.example.env" "$BOT_DIR/" -else - echo -e "${RED}Файлы бота не найдены в $SCRIPT_DIR/gotelegram-bot/${NC}" - exit 1 -fi - -# Копируем каталог шаблонов -if [ -f "$SCRIPT_DIR/templates_catalog.json" ]; then - mkdir -p "$GOTELEGRAM_DIR" - cp "$SCRIPT_DIR/templates_catalog.json" "$GOTELEGRAM_DIR/" - echo -e "${GREEN}[*] Каталог шаблонов скопирован${NC}" -fi - -# ── Virtual environment ────────────────────────────────────────────────────── -if [ ! -d "$BOT_DIR/venv" ]; then - echo -e "${GREEN}[*] Создание виртуального окружения...${NC}" - python3 -m venv "$BOT_DIR/venv" -fi - -echo -e "${GREEN}[*] Установка зависимостей...${NC}" -"$BOT_DIR/venv/bin/pip" install -r "$BOT_DIR/requirements.txt" -q - -# ── Конфигурация ───────────────────────────────────────────────────────────── -if [ ! -f "$BOT_DIR/.env" ]; then - echo "" - echo -e "${YELLOW}Введите BOT_TOKEN от @BotFather:${NC}" - TOKEN="" - while [ -z "$TOKEN" ]; do - read -r TOKEN - TOKEN=$(echo "$TOKEN" | tr -d '[:space:]') - [ -z "$TOKEN" ] && echo -e "${RED}Токен не может быть пустым.${NC}" - done - - echo -ne "${YELLOW}ID администратора (Enter = доступ для всех):${NC} " - read -r ADMIN_ID - - { - echo "BOT_TOKEN=$TOKEN" - [ -n "$ADMIN_ID" ] && echo "ALLOWED_IDS=$ADMIN_ID" - } > "$BOT_DIR/.env" - - chmod 600 "$BOT_DIR/.env" - echo -e "${GREEN}[*] .env создан${NC}" -else - echo -e "${GREEN}[*] .env уже существует${NC}" -fi - -# ── Systemd ────────────────────────────────────────────────────────────────── -cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF -[Unit] -Description=GoTelegram v2.2 Telegram Bot -After=network.target - -[Service] -Type=simple -WorkingDirectory=$BOT_DIR -ExecStart=$BOT_DIR/venv/bin/python $BOT_DIR/bot.py -Restart=always -RestartSec=5 -Environment=PATH=$BOT_DIR/venv/bin:/usr/bin - -[Install] -WantedBy=multi-user.target -EOF - -systemctl daemon-reload -systemctl enable "$SERVICE_NAME" -systemctl restart "$SERVICE_NAME" 2>/dev/null || systemctl start "$SERVICE_NAME" - -echo "" -echo -e "${GREEN}╔═══════════════════════════════════════════╗${NC}" -echo -e "${GREEN}║ ✅ Бот установлен и запущен! ║${NC}" -echo -e "${GREEN}╚═══════════════════════════════════════════╝${NC}" -echo "" -echo -e "Проверка: ${CYAN}systemctl status $SERVICE_NAME${NC}" -echo -e "Логи: ${CYAN}journalctl -u $SERVICE_NAME -f${NC}" -echo -e "Настройки: ${CYAN}$BOT_DIR/.env${NC}" -echo "" - -# Благодарности -echo -e "${CYAN}─────────────────────────────────────────────${NC}" -echo -e "💜 Спасибо авторам открытых проектов:" -echo -e " ${CYAN}telemt${NC} — MTProxy engine (Rust)" -echo -e " ${CYAN}HTML5 UP${NC} — шаблоны сайтов (CC BY 3.0)" -echo -e " ${CYAN}learning-zone${NC} — 150+ HTML5 шаблонов" -echo -e " ${CYAN}Start Bootstrap${NC} — Bootstrap шаблоны (MIT)" -echo -e "${CYAN}─────────────────────────────────────────────${NC}" +#!/bin/bash +# GoTelegram v2.2.1 — Установка Telegram-бота +# Создаёт venv, ставит зависимости, настраивает systemd + +set -e +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +CYAN='\033[0;36m' +NC='\033[0m' + +BOT_DIR="/opt/gotelegram-bot" +SERVICE_NAME="gotelegram-bot" +GOTELEGRAM_DIR="/opt/gotelegram" + +if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Запустите с sudo.${NC}" + exit 1 +fi + +echo -e "${CYAN}╔═══════════════════════════════════════════╗${NC}" +echo -e "${CYAN}║${NC} ${GREEN}GoTelegram v2.2.1 — Установка бота${NC} ${CYAN}║${NC}" +echo -e "${CYAN}╚═══════════════════════════════════════════╝${NC}" +echo "" + +# ── Python ─────────────────────────────────────────────────────────────────── +if ! command -v python3 &>/dev/null; then + echo -e "${YELLOW}[*] Установка python3...${NC}" + if command -v apt-get &>/dev/null; then + apt-get update -qq && apt-get install -y -qq python3 python3-pip python3-venv + elif command -v dnf &>/dev/null; then + dnf install -y -q python3 python3-pip + elif command -v yum &>/dev/null; then + yum install -y -q python3 python3-pip + fi +fi + +# ── Каталог бота ───────────────────────────────────────────────────────────── +mkdir -p "$BOT_DIR" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then + echo -e "${GREEN}[*] Копирование файлов бота...${NC}" + cp "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/" + cp "$SCRIPT_DIR/gotelegram-bot/requirements.txt" "$BOT_DIR/" + [ -f "$SCRIPT_DIR/gotelegram-bot/config.example.env" ] && cp "$SCRIPT_DIR/gotelegram-bot/config.example.env" "$BOT_DIR/" +else + echo -e "${RED}Файлы бота не найдены в $SCRIPT_DIR/gotelegram-bot/${NC}" + exit 1 +fi + +# Копируем каталог шаблонов +if [ -f "$SCRIPT_DIR/templates_catalog.json" ]; then + mkdir -p "$GOTELEGRAM_DIR" + cp "$SCRIPT_DIR/templates_catalog.json" "$GOTELEGRAM_DIR/" + echo -e "${GREEN}[*] Каталог шаблонов скопирован${NC}" +fi + +# ── Virtual environment ────────────────────────────────────────────────────── +if [ ! -d "$BOT_DIR/venv" ]; then + echo -e "${GREEN}[*] Создание виртуального окружения...${NC}" + python3 -m venv "$BOT_DIR/venv" +fi + +echo -e "${GREEN}[*] Установка зависимостей...${NC}" +"$BOT_DIR/venv/bin/pip" install -r "$BOT_DIR/requirements.txt" -q + +# ── Конфигурация ───────────────────────────────────────────────────────────── +if [ ! -f "$BOT_DIR/.env" ]; then + echo "" + echo -e "${YELLOW}Введите BOT_TOKEN от @BotFather:${NC}" + TOKEN="" + while [ -z "$TOKEN" ]; do + read -r TOKEN + TOKEN=$(echo "$TOKEN" | tr -d '[:space:]') + [ -z "$TOKEN" ] && echo -e "${RED}Токен не может быть пустым.${NC}" + done + + echo -ne "${YELLOW}ID администратора (Enter = доступ для всех):${NC} " + read -r ADMIN_ID + + { + echo "BOT_TOKEN=$TOKEN" + [ -n "$ADMIN_ID" ] && echo "ALLOWED_IDS=$ADMIN_ID" + } > "$BOT_DIR/.env" + + chmod 600 "$BOT_DIR/.env" + echo -e "${GREEN}[*] .env создан${NC}" +else + echo -e "${GREEN}[*] .env уже существует${NC}" +fi + +# ── Systemd ────────────────────────────────────────────────────────────────── +cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF +[Unit] +Description=GoTelegram v2.2.1 Telegram Bot +After=network.target + +[Service] +Type=simple +WorkingDirectory=$BOT_DIR +ExecStart=$BOT_DIR/venv/bin/python $BOT_DIR/bot.py +Restart=always +RestartSec=5 +Environment=PATH=$BOT_DIR/venv/bin:/usr/bin + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable "$SERVICE_NAME" +systemctl restart "$SERVICE_NAME" 2>/dev/null || systemctl start "$SERVICE_NAME" + +echo "" +echo -e "${GREEN}╔═══════════════════════════════════════════╗${NC}" +echo -e "${GREEN}║ ✅ Бот установлен и запущен! ║${NC}" +echo -e "${GREEN}╚═══════════════════════════════════════════╝${NC}" +echo "" +echo -e "Проверка: ${CYAN}systemctl status $SERVICE_NAME${NC}" +echo -e "Логи: ${CYAN}journalctl -u $SERVICE_NAME -f${NC}" +echo -e "Настройки: ${CYAN}$BOT_DIR/.env${NC}" +echo "" + +# Благодарности +echo -e "${CYAN}─────────────────────────────────────────────${NC}" +echo -e "💜 Спасибо авторам открытых проектов:" +echo -e " ${CYAN}telemt${NC} — MTProxy engine (Rust)" +echo -e " ${CYAN}HTML5 UP${NC} — шаблоны сайтов (CC BY 3.0)" +echo -e " ${CYAN}learning-zone${NC} — 150+ HTML5 шаблонов" +echo -e " ${CYAN}Start Bootstrap${NC} — Bootstrap шаблоны (MIT)" +echo -e "${CYAN}─────────────────────────────────────────────${NC}" diff --git a/lib/backup.sh b/lib/backup.sh index de60d8a..07854ef 100644 --- a/lib/backup.sh +++ b/lib/backup.sh @@ -1,341 +1,341 @@ -#!/bin/bash -# GoTelegram v2.2 — Бекап и восстановление конфигурации - -# ── Создание бекапа ────────────────────────────────────────────────────────── -create_backup() { - local password="$1" - local output_dir="${2:-$BACKUP_DIR}" - local timestamp - timestamp=$(date +%Y%m%d_%H%M%S) - local backup_name="gotelegram_backup_${timestamp}" - local tmp_dir="/tmp/${backup_name}" - - mkdir -p "$tmp_dir" "$output_dir" - - # Собираем файлы - log_info "Собираю конфигурацию..." - - # telemt конфиг - if [ -f "$TELEMT_CONFIG" ]; then - cp "$TELEMT_CONFIG" "$tmp_dir/config.toml" - fi - - # GoTelegram конфиг - if [ -f "$GOTELEGRAM_CONFIG" ]; then - cp "$GOTELEGRAM_CONFIG" "$tmp_dir/gotelegram.json" - fi - - # nginx конфиг (stealth mode) - if [ -f "$NGINX_SITE_CONF" ]; then - cp "$NGINX_SITE_CONF" "$tmp_dir/nginx.conf" - fi - - # SSL сертификаты - local domain - domain=$(config_get domain 2>/dev/null) - if [ -n "$domain" ] && [ -d "/etc/letsencrypt/live/$domain" ]; then - mkdir -p "$tmp_dir/certs" - cp "/etc/letsencrypt/live/$domain/fullchain.pem" "$tmp_dir/certs/" 2>/dev/null - cp "/etc/letsencrypt/live/$domain/privkey.pem" "$tmp_dir/certs/" 2>/dev/null - log_dim "SSL сертификаты включены" - fi - - # Шаблон сайта (если есть) - if [ -d "$WEBSITE_ROOT" ] && [ -f "$WEBSITE_ROOT/index.html" ]; then - mkdir -p "$tmp_dir/site" - cp -r "$WEBSITE_ROOT"/* "$tmp_dir/site/" - log_dim "Шаблон сайта включён" - fi - - # Метаданные - local ip mode engine - ip=$(get_server_ip) - mode=$(config_get mode 2>/dev/null || echo "unknown") - engine=$(config_get engine 2>/dev/null || echo "telemt") - - cat > "$tmp_dir/metadata.json" << EOMETA -{ - "backup_version": "1.0", - "gotelegram_version": "$GOTELEGRAM_VERSION", - "created_at": "$(date -Iseconds)", - "hostname": "$(hostname)", - "ip": "$ip", - "engine": "$engine", - "mode": "$mode", - "port": $(config_get port 2>/dev/null || echo "443"), - "domain": "$(config_get domain 2>/dev/null)" -} -EOMETA - - # Архивируем - local tar_file="/tmp/${backup_name}.tar.gz" - if ! tar czf "$tar_file" -C /tmp "$backup_name" 2>/dev/null; then - log_error "Ошибка создания архива" - rm -rf "$tmp_dir" - rm -f "$tar_file" - return 1 - fi - - if [ ! -f "$tar_file" ]; then - log_error "Архив не создан" - rm -rf "$tmp_dir" - return 1 - fi - - # Шифруем если задан пароль - local final_file="" - if [ -n "$password" ]; then - final_file="${output_dir}/${backup_name}.tar.gz.enc" - openssl enc -aes-256-cbc -salt -pbkdf2 -in "$tar_file" -out "$final_file" -pass "pass:${password}" 2>/dev/null - if [ $? -ne 0 ]; then - log_error "Ошибка шифрования" - rm -f "$tar_file" - rm -rf "$tmp_dir" - return 1 - fi - rm -f "$tar_file" - log_success "Бекап зашифрован (AES-256-CBC)" - else - final_file="${output_dir}/${backup_name}.tar.gz" - mv "$tar_file" "$final_file" - fi - - # SHA256 подпись - sha256sum "$final_file" > "${final_file}.sha256" 2>/dev/null - - # Очистка - rm -rf "$tmp_dir" - - local size - size=$(du -h "$final_file" | cut -f1) - log_success "Бекап создан: $final_file ($size)" - echo "$final_file" - return 0 -} - -# ── Восстановление из бекапа ──────────────────────────────────────────────── -restore_backup() { - local backup_file="$1" - local password="$2" - - if [ ! -f "$backup_file" ]; then - log_error "Файл не найден: $backup_file" - return 1 - fi - - local tmp_dir="/tmp/gotelegram_restore_$$" - mkdir -p "$tmp_dir" - - # Расшифровываем если нужно - local tar_file="" - if echo "$backup_file" | grep -q '\.enc$'; then - if [ -z "$password" ]; then - echo -ne " Введите пароль от бекапа: " - read -rs password - echo "" - fi - tar_file="/tmp/gotelegram_restore_$$.tar.gz" - openssl enc -aes-256-cbc -d -pbkdf2 -in "$backup_file" -out "$tar_file" -pass "pass:${password}" 2>/dev/null - if [ $? -ne 0 ]; then - log_error "Неверный пароль или повреждённый файл" - rm -rf "$tmp_dir" "$tar_file" - return 1 - fi - else - tar_file="$backup_file" - fi - - # Распаковываем - tar xzf "$tar_file" -C "$tmp_dir" 2>/dev/null - if [ $? -ne 0 ]; then - log_error "Ошибка распаковки архива" - rm -rf "$tmp_dir" - return 1 - fi - - # Находим папку бекапа - local backup_dir - backup_dir=$(find "$tmp_dir" -maxdepth 1 -type d -name "gotelegram_backup_*" | head -1) - [ -z "$backup_dir" ] && backup_dir="$tmp_dir" - - # Проверяем метаданные - if [ -f "$backup_dir/metadata.json" ]; then - local bk_version bk_mode bk_ip - bk_version=$(jq -r '.gotelegram_version // "unknown"' "$backup_dir/metadata.json") - bk_mode=$(jq -r '.mode // "unknown"' "$backup_dir/metadata.json") - bk_ip=$(jq -r '.ip // "unknown"' "$backup_dir/metadata.json") - echo "" - echo -e " ${BOLD}${WHITE}📦 Бекап:${NC}" - echo -e " Версия: $bk_version | Режим: $bk_mode | IP: $bk_ip" - echo -e " Дата: $(jq -r '.created_at' "$backup_dir/metadata.json")" - echo "" - fi - - if ! confirm "Восстановить конфигурацию? Текущие настройки будут перезаписаны."; then - rm -rf "$tmp_dir" - return 0 - fi - - # Останавливаем сервисы - stop_telemt 2>/dev/null - systemctl stop nginx 2>/dev/null - - # Восстанавливаем telemt конфиг - if [ -f "$backup_dir/config.toml" ]; then - mkdir -p /etc/telemt - cp "$backup_dir/config.toml" "$TELEMT_CONFIG" - chmod 600 "$TELEMT_CONFIG" - log_success "telemt конфиг восстановлен" - fi - - # Восстанавливаем GoTelegram конфиг - if [ -f "$backup_dir/gotelegram.json" ]; then - mkdir -p "$GOTELEGRAM_DIR" - cp "$backup_dir/gotelegram.json" "$GOTELEGRAM_CONFIG" - log_success "GoTelegram конфиг восстановлен" - fi - - # Восстанавливаем nginx конфиг - if [ -f "$backup_dir/nginx.conf" ]; then - mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled - cp "$backup_dir/nginx.conf" "$NGINX_SITE_CONF" - ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK" - log_success "nginx конфиг восстановлен" - fi - - # Восстанавливаем SSL - if [ -d "$backup_dir/certs" ]; then - local domain - domain=$(config_get domain 2>/dev/null) - if [ -n "$domain" ]; then - local cert_dir="/etc/letsencrypt/live/$domain" - mkdir -p "$cert_dir" - cp "$backup_dir/certs/"* "$cert_dir/" 2>/dev/null - log_success "SSL сертификаты восстановлены" - fi - fi - - # Восстанавливаем шаблон сайта - if [ -d "$backup_dir/site" ]; then - mkdir -p "$WEBSITE_ROOT" - cp -r "$backup_dir/site"/* "$WEBSITE_ROOT/" - chown -R www-data:www-data "$WEBSITE_ROOT" 2>/dev/null - log_success "Шаблон сайта восстановлен" - fi - - # Запускаем сервисы - if is_telemt_installed; then - start_telemt - fi - systemctl start nginx 2>/dev/null - - # Очистка - rm -rf "$tmp_dir" - [ "$tar_file" != "$backup_file" ] && rm -f "$tar_file" - - log_success "Восстановление завершено!" - show_proxy_info - return 0 -} - -# ── Список бекапов ─────────────────────────────────────────────────────────── -list_backups() { - if [ ! -d "$BACKUP_DIR" ] || [ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then - log_info "Бекапов нет" - return 1 - fi - - echo "" - echo -e " ${BOLD}${WHITE}📦 Доступные бекапы:${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" - - local i=1 - for f in "$BACKUP_DIR"/gotelegram_backup_*.tar.gz*; do - [ -f "$f" ] || continue - [[ "$f" == *.sha256 ]] && continue - local size date_str name - size=$(du -h "$f" | cut -f1) - name=$(basename "$f") - date_str=$(echo "$name" | grep -oE '[0-9]{8}_[0-9]{6}' | head -1) - local encrypted="" - [[ "$f" == *.enc ]] && encrypted=" 🔒" - echo -e " ${CYAN}${i})${NC} ${name} (${size})${encrypted}" - ((i++)) - done - echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" -} - -# ── Очистка старых бекапов ─────────────────────────────────────────────────── -cleanup_old_backups() { - local keep="${1:-5}" - local count - count=$(find "$BACKUP_DIR" -name "gotelegram_backup_*.tar.gz*" ! -name "*.sha256" 2>/dev/null | wc -l) - - if [ "$count" -gt "$keep" ]; then - local to_delete=$((count - keep)) - find "$BACKUP_DIR" -name "gotelegram_backup_*.tar.gz*" ! -name "*.sha256" 2>/dev/null | sort | head -n "$to_delete" | while read -r f; do - rm -f "$f" "${f}.sha256" - done - log_dim "Удалено $to_delete старых бекапов (оставлено $keep)" - fi -} - -# ── Интерактивный бекап ────────────────────────────────────────────────────── -interactive_backup() { - echo "" - echo -e " ${BOLD}${WHITE}💾 Создание бекапа${NC}" - echo -ne " Зашифровать бекап паролем? [Y/n]: " - read -r use_pass - - local password="" - if [[ ! "$use_pass" =~ ^[Nn] ]]; then - echo -ne " Введите пароль: " - read -rs password - echo "" - echo -ne " Повторите пароль: " - read -rs password2 - echo "" - if [ "$password" != "$password2" ]; then - log_error "Пароли не совпадают" - return 1 - fi - if [ ${#password} -lt 6 ]; then - log_error "Пароль слишком короткий (минимум 6 символов)" - return 1 - fi - fi - - create_backup "$password" - cleanup_old_backups -} - -# ── Интерактивное восстановление ───────────────────────────────────────────── -interactive_restore() { - list_backups || return 1 - - echo -ne " Номер бекапа (или путь к файлу): " - read -r choice - - local backup_file="" - if [[ "$choice" =~ ^[0-9]+$ ]]; then - local i=1 - for f in "$BACKUP_DIR"/gotelegram_backup_*.tar.gz*; do - [ -f "$f" ] || continue - [[ "$f" == *.sha256 ]] && continue - if [ "$i" -eq "$choice" ]; then - backup_file="$f" - break - fi - ((i++)) - done - elif [ -f "$choice" ]; then - backup_file="$choice" - fi - - if [ -z "$backup_file" ]; then - log_error "Бекап не найден" - return 1 - fi - - restore_backup "$backup_file" -} +#!/bin/bash +# GoTelegram v2.2 — Бекап и восстановление конфигурации + +# ── Создание бекапа ────────────────────────────────────────────────────────── +create_backup() { + local password="$1" + local output_dir="${2:-$BACKUP_DIR}" + local timestamp + timestamp=$(date +%Y%m%d_%H%M%S) + local backup_name="gotelegram_backup_${timestamp}" + local tmp_dir="/tmp/${backup_name}" + + mkdir -p "$tmp_dir" "$output_dir" + + # Собираем файлы + log_info "Собираю конфигурацию..." + + # telemt конфиг + if [ -f "$TELEMT_CONFIG" ]; then + cp "$TELEMT_CONFIG" "$tmp_dir/config.toml" + fi + + # GoTelegram конфиг + if [ -f "$GOTELEGRAM_CONFIG" ]; then + cp "$GOTELEGRAM_CONFIG" "$tmp_dir/gotelegram.json" + fi + + # nginx конфиг (stealth mode) + if [ -f "$NGINX_SITE_CONF" ]; then + cp "$NGINX_SITE_CONF" "$tmp_dir/nginx.conf" + fi + + # SSL сертификаты + local domain + domain=$(config_get domain 2>/dev/null) + if [ -n "$domain" ] && [ -d "/etc/letsencrypt/live/$domain" ]; then + mkdir -p "$tmp_dir/certs" + cp "/etc/letsencrypt/live/$domain/fullchain.pem" "$tmp_dir/certs/" 2>/dev/null + cp "/etc/letsencrypt/live/$domain/privkey.pem" "$tmp_dir/certs/" 2>/dev/null + log_dim "SSL сертификаты включены" + fi + + # Шаблон сайта (если есть) + if [ -d "$WEBSITE_ROOT" ] && [ -f "$WEBSITE_ROOT/index.html" ]; then + mkdir -p "$tmp_dir/site" + cp -r "$WEBSITE_ROOT"/* "$tmp_dir/site/" + log_dim "Шаблон сайта включён" + fi + + # Метаданные + local ip mode engine + ip=$(get_server_ip) + mode=$(config_get mode 2>/dev/null || echo "unknown") + engine=$(config_get engine 2>/dev/null || echo "telemt") + + cat > "$tmp_dir/metadata.json" << EOMETA +{ + "backup_version": "1.0", + "gotelegram_version": "$GOTELEGRAM_VERSION", + "created_at": "$(date -Iseconds)", + "hostname": "$(hostname)", + "ip": "$ip", + "engine": "$engine", + "mode": "$mode", + "port": $(config_get port 2>/dev/null || echo "443"), + "domain": "$(config_get domain 2>/dev/null)" +} +EOMETA + + # Архивируем + local tar_file="/tmp/${backup_name}.tar.gz" + if ! tar czf "$tar_file" -C /tmp "$backup_name" 2>/dev/null; then + log_error "Ошибка создания архива" + rm -rf "$tmp_dir" + rm -f "$tar_file" + return 1 + fi + + if [ ! -f "$tar_file" ]; then + log_error "Архив не создан" + rm -rf "$tmp_dir" + return 1 + fi + + # Шифруем если задан пароль + local final_file="" + if [ -n "$password" ]; then + final_file="${output_dir}/${backup_name}.tar.gz.enc" + openssl enc -aes-256-cbc -salt -pbkdf2 -in "$tar_file" -out "$final_file" -pass "pass:${password}" 2>/dev/null + if [ $? -ne 0 ]; then + log_error "Ошибка шифрования" + rm -f "$tar_file" + rm -rf "$tmp_dir" + return 1 + fi + rm -f "$tar_file" + log_success "Бекап зашифрован (AES-256-CBC)" + else + final_file="${output_dir}/${backup_name}.tar.gz" + mv "$tar_file" "$final_file" + fi + + # SHA256 подпись + sha256sum "$final_file" > "${final_file}.sha256" 2>/dev/null + + # Очистка + rm -rf "$tmp_dir" + + local size + size=$(du -h "$final_file" | cut -f1) + log_success "Бекап создан: $final_file ($size)" + echo "$final_file" + return 0 +} + +# ── Восстановление из бекапа ──────────────────────────────────────────────── +restore_backup() { + local backup_file="$1" + local password="$2" + + if [ ! -f "$backup_file" ]; then + log_error "Файл не найден: $backup_file" + return 1 + fi + + local tmp_dir="/tmp/gotelegram_restore_$$" + mkdir -p "$tmp_dir" + + # Расшифровываем если нужно + local tar_file="" + if echo "$backup_file" | grep -q '\.enc$'; then + if [ -z "$password" ]; then + echo -ne " Введите пароль от бекапа: " + read -rs password + echo "" + fi + tar_file="/tmp/gotelegram_restore_$$.tar.gz" + openssl enc -aes-256-cbc -d -pbkdf2 -in "$backup_file" -out "$tar_file" -pass "pass:${password}" 2>/dev/null + if [ $? -ne 0 ]; then + log_error "Неверный пароль или повреждённый файл" + rm -rf "$tmp_dir" "$tar_file" + return 1 + fi + else + tar_file="$backup_file" + fi + + # Распаковываем + tar xzf "$tar_file" -C "$tmp_dir" 2>/dev/null + if [ $? -ne 0 ]; then + log_error "Ошибка распаковки архива" + rm -rf "$tmp_dir" + return 1 + fi + + # Находим папку бекапа + local backup_dir + backup_dir=$(find "$tmp_dir" -maxdepth 1 -type d -name "gotelegram_backup_*" | head -1) + [ -z "$backup_dir" ] && backup_dir="$tmp_dir" + + # Проверяем метаданные + if [ -f "$backup_dir/metadata.json" ]; then + local bk_version bk_mode bk_ip + bk_version=$(jq -r '.gotelegram_version // "unknown"' "$backup_dir/metadata.json") + bk_mode=$(jq -r '.mode // "unknown"' "$backup_dir/metadata.json") + bk_ip=$(jq -r '.ip // "unknown"' "$backup_dir/metadata.json") + echo "" + echo -e " ${BOLD}${WHITE}📦 Бекап:${NC}" + echo -e " Версия: $bk_version | Режим: $bk_mode | IP: $bk_ip" + echo -e " Дата: $(jq -r '.created_at' "$backup_dir/metadata.json")" + echo "" + fi + + if ! confirm "Восстановить конфигурацию? Текущие настройки будут перезаписаны."; then + rm -rf "$tmp_dir" + return 0 + fi + + # Останавливаем сервисы + stop_telemt 2>/dev/null + systemctl stop nginx 2>/dev/null + + # Восстанавливаем telemt конфиг + if [ -f "$backup_dir/config.toml" ]; then + mkdir -p /etc/telemt + cp "$backup_dir/config.toml" "$TELEMT_CONFIG" + chmod 600 "$TELEMT_CONFIG" + log_success "telemt конфиг восстановлен" + fi + + # Восстанавливаем GoTelegram конфиг + if [ -f "$backup_dir/gotelegram.json" ]; then + mkdir -p "$GOTELEGRAM_DIR" + cp "$backup_dir/gotelegram.json" "$GOTELEGRAM_CONFIG" + log_success "GoTelegram конфиг восстановлен" + fi + + # Восстанавливаем nginx конфиг + if [ -f "$backup_dir/nginx.conf" ]; then + mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled + cp "$backup_dir/nginx.conf" "$NGINX_SITE_CONF" + ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK" + log_success "nginx конфиг восстановлен" + fi + + # Восстанавливаем SSL + if [ -d "$backup_dir/certs" ]; then + local domain + domain=$(config_get domain 2>/dev/null) + if [ -n "$domain" ]; then + local cert_dir="/etc/letsencrypt/live/$domain" + mkdir -p "$cert_dir" + cp "$backup_dir/certs/"* "$cert_dir/" 2>/dev/null + log_success "SSL сертификаты восстановлены" + fi + fi + + # Восстанавливаем шаблон сайта + if [ -d "$backup_dir/site" ]; then + mkdir -p "$WEBSITE_ROOT" + cp -r "$backup_dir/site"/* "$WEBSITE_ROOT/" + chown -R www-data:www-data "$WEBSITE_ROOT" 2>/dev/null + log_success "Шаблон сайта восстановлен" + fi + + # Запускаем сервисы + if is_telemt_installed; then + start_telemt + fi + systemctl start nginx 2>/dev/null + + # Очистка + rm -rf "$tmp_dir" + [ "$tar_file" != "$backup_file" ] && rm -f "$tar_file" + + log_success "Восстановление завершено!" + show_proxy_info + return 0 +} + +# ── Список бекапов ─────────────────────────────────────────────────────────── +list_backups() { + if [ ! -d "$BACKUP_DIR" ] || [ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then + log_info "Бекапов нет" + return 1 + fi + + echo "" + echo -e " ${BOLD}${WHITE}📦 Доступные бекапы:${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" + + local i=1 + for f in "$BACKUP_DIR"/gotelegram_backup_*.tar.gz*; do + [ -f "$f" ] || continue + [[ "$f" == *.sha256 ]] && continue + local size date_str name + size=$(du -h "$f" | cut -f1) + name=$(basename "$f") + date_str=$(echo "$name" | grep -oE '[0-9]{8}_[0-9]{6}' | head -1) + local encrypted="" + [[ "$f" == *.enc ]] && encrypted=" 🔒" + echo -e " ${CYAN}${i})${NC} ${name} (${size})${encrypted}" + ((i++)) + done + echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" +} + +# ── Очистка старых бекапов ─────────────────────────────────────────────────── +cleanup_old_backups() { + local keep="${1:-5}" + local count + count=$(find "$BACKUP_DIR" -name "gotelegram_backup_*.tar.gz*" ! -name "*.sha256" 2>/dev/null | wc -l) + + if [ "$count" -gt "$keep" ]; then + local to_delete=$((count - keep)) + find "$BACKUP_DIR" -name "gotelegram_backup_*.tar.gz*" ! -name "*.sha256" 2>/dev/null | sort | head -n "$to_delete" | while read -r f; do + rm -f "$f" "${f}.sha256" + done + log_dim "Удалено $to_delete старых бекапов (оставлено $keep)" + fi +} + +# ── Интерактивный бекап ────────────────────────────────────────────────────── +interactive_backup() { + echo "" + echo -e " ${BOLD}${WHITE}💾 Создание бекапа${NC}" + echo -ne " Зашифровать бекап паролем? [Y/n]: " + read -r use_pass + + local password="" + if [[ ! "$use_pass" =~ ^[Nn] ]]; then + echo -ne " Введите пароль: " + read -rs password + echo "" + echo -ne " Повторите пароль: " + read -rs password2 + echo "" + if [ "$password" != "$password2" ]; then + log_error "Пароли не совпадают" + return 1 + fi + if [ ${#password} -lt 6 ]; then + log_error "Пароль слишком короткий (минимум 6 символов)" + return 1 + fi + fi + + create_backup "$password" + cleanup_old_backups +} + +# ── Интерактивное восстановление ───────────────────────────────────────────── +interactive_restore() { + list_backups || return 1 + + echo -ne " Номер бекапа (или путь к файлу): " + read -r choice + + local backup_file="" + if [[ "$choice" =~ ^[0-9]+$ ]]; then + local i=1 + for f in "$BACKUP_DIR"/gotelegram_backup_*.tar.gz*; do + [ -f "$f" ] || continue + [[ "$f" == *.sha256 ]] && continue + if [ "$i" -eq "$choice" ]; then + backup_file="$f" + break + fi + ((i++)) + done + elif [ -f "$choice" ]; then + backup_file="$choice" + fi + + if [ -z "$backup_file" ]; then + log_error "Бекап не найден" + return 1 + fi + + restore_backup "$backup_file" +} diff --git a/lib/telemt_config.sh b/lib/telemt_config.sh index 142f271..7a19ea0 100644 --- a/lib/telemt_config.sh +++ b/lib/telemt_config.sh @@ -1,282 +1,282 @@ -#!/bin/bash -# GoTelegram v2.2 — Генерация TOML конфигурации для telemt - -# ── Популярные домены (не заблокированные в РФ) ────────────────────────────── -QUICK_DOMAINS=( - "google.com" - "microsoft.com" - "cloudflare.com" - "apple.com" - "amazon.com" - "github.com" - "stackoverflow.com" - "medium.com" - "wikipedia.org" - "coursera.org" - "udemy.com" - "habr.com" - "stepik.org" - "duolingo.com" - "khanacademy.org" - "bbc.com" - "reuters.com" - "nytimes.com" - "ted.com" - "zoom.us" -) - -# ── Генерация TOML конфига ─────────────────────────────────────────────────── -generate_telemt_toml() { - local secret="$1" - local port="${2:-443}" - local mask_mode="${3:-quick}" # quick | stealth - local mask_host="${4:-google.com}" - local mask_port="${5:-443}" - local output="${6:-$TELEMT_CONFIG}" - - mkdir -p "$(dirname "$output")" - - cat > "$output" << EOTOML -# GoTelegram v${GOTELEGRAM_VERSION} — telemt configuration -# Сгенерировано: $(date -Iseconds) -# Режим: ${mask_mode} - -# ── Основные настройки ─────────────────────────────────────────────────────── -[stats] - statsd_address = "" - -# ── Секреты ────────────────────────────────────────────────────────────────── -[[users]] - name = "main" - secret = "${secret}" - -# ── Привязка ───────────────────────────────────────────────────────────────── -[listen] - bind_to = "0.0.0.0:${port}" - -# ── TLS маскировка ─────────────────────────────────────────────────────────── -[security] - # Маскировочный хост — куда перенаправлять неопознанные подключения - # quick: внешний сайт | stealth: локальный nginx - host = "${mask_host}:${mask_port}" - -EOTOML - - chmod 600 "$output" - log_success "Конфиг telemt записан: $output" - log_dim "Режим: $mask_mode, маскировка: $mask_host:$mask_port" -} - -# ── Добавление дополнительного секрета ─────────────────────────────────────── -add_secret_to_config() { - local name="$1" - local secret="$2" - local config="${3:-$TELEMT_CONFIG}" - - if [ ! -f "$config" ]; then - log_error "Конфиг не найден: $config" - return 1 - fi - - # Добавляем новый блок [[users]] - cat >> "$config" << EOSECRET - -[[users]] - name = "${name}" - secret = "${secret}" -EOSECRET - - log_success "Добавлен секрет: $name" -} - -# ── Чтение текущего конфига ────────────────────────────────────────────────── -get_config_value() { - local key="$1" - local config="${2:-$TELEMT_CONFIG}" - - if [ ! -f "$config" ]; then return 1; fi - - case "$key" in - secret) - grep -m1 'secret\s*=' "$config" | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ' - ;; - port) - grep 'bind_to\s*=' "$config" | sed 's/.*:\([0-9]*\)".*/\1/' - ;; - mask_host) - grep -A10 '\[security\]' "$config" | grep 'host\s*=' | sed 's/.*=\s*"\(.*\)".*/\1/' - ;; - *) - grep "$key" "$config" | head -1 | sed 's/.*=\s*"\?\(.*\)"\?/\1/' | tr -d ' "' - ;; - esac -} - -# ── Валидация конфига ──────────────────────────────────────────────────────── -validate_telemt_config() { - local config="${1:-$TELEMT_CONFIG}" - - if [ ! -f "$config" ]; then - log_error "Конфиг не найден: $config" - return 1 - fi - - # Проверяем обязательные поля - local secret port host - secret=$(get_config_value secret "$config") - port=$(get_config_value port "$config") - host=$(get_config_value mask_host "$config") - - local errors=0 - - if [ -z "$secret" ]; then - log_error "Не задан secret" - ((errors++)) - elif [ ${#secret} -lt 32 ]; then - log_warning "Secret слишком короткий (${#secret} символов, рекомендуется 32+)" - fi - - if [ -z "$port" ]; then - log_error "Не задан порт (bind_to)" - ((errors++)) - elif [ "$port" -lt 1 ] || [ "$port" -gt 65535 ] 2>/dev/null; then - log_error "Порт вне диапазона: $port" - ((errors++)) - fi - - if [ -z "$host" ]; then - log_error "Не задан маскировочный хост (security.host)" - ((errors++)) - fi - - if [ $errors -gt 0 ]; then - log_error "Найдено ошибок: $errors" - return 1 - fi - - log_success "Конфиг валиден" - return 0 -} - -# ── Выбор домена (интерактивный) ───────────────────────────────────────────── -select_quick_domain() { - echo "" - echo -e " ${BOLD}${WHITE}🌐 Выберите домен для маскировки (Fake TLS):${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" - - local i=1 - local row="" - for d in "${QUICK_DOMAINS[@]}"; do - printf " ${CYAN}%2d)${NC} %-25s" "$i" "$d" - if (( i % 2 == 0 )); then - echo "" - fi - ((i++)) - done - if (( (i-1) % 2 != 0 )); then echo ""; fi - - echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" - echo -ne " ${WHITE}Выбор (1-${#QUICK_DOMAINS[@]}):${NC} " - read -r choice - - if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#QUICK_DOMAINS[@]} ]; then - echo "${QUICK_DOMAINS[$((choice-1))]}" - return 0 - fi - - log_error "Неверный выбор" - return 1 -} - -# ── Выбор порта (интерактивный) ────────────────────────────────────────────── -select_port() { - echo "" - echo -e " ${BOLD}${WHITE}🔌 Выберите порт:${NC}" - - # Проверяем стандартные порты - local busy_443 busy_8443 - busy_443=$(check_port 443) - busy_8443=$(check_port 8443) - - local label_443="443 (рекомендуется)" - local label_8443="8443" - [ -n "$busy_443" ] && label_443="443 ⚠️ занят" - [ -n "$busy_8443" ] && label_8443="8443 ⚠️ занят" - - echo -e " ${CYAN}1)${NC} $label_443" - echo -e " ${CYAN}2)${NC} $label_8443" - echo -e " ${CYAN}3)${NC} Свой порт" - - if [ -n "$busy_443" ]; then - echo -e " ${DIM} ⚠ Порт 443 занят: $(echo "$busy_443" | head -c 60)${NC}" - fi - - echo -ne " ${WHITE}Выбор:${NC} " - read -r choice - - case "$choice" in - 1) echo "443" ;; - 2) echo "8443" ;; - 3) - echo -ne " Введите порт (1-65535): " - read -r custom_port - if [[ "$custom_port" =~ ^[0-9]+$ ]] && [ "$custom_port" -ge 1 ] && [ "$custom_port" -le 65535 ]; then - echo "$custom_port" - else - log_error "Неверный порт" - return 1 - fi - ;; - *) echo "443" ;; - esac -} - -# ── Генерация ссылки tg://proxy ────────────────────────────────────────────── -generate_proxy_link() { - local ip="${1:-$(get_server_ip)}" - local port="${2:-443}" - local secret="$3" - echo "tg://proxy?server=${ip}&port=${port}&secret=${secret}" -} - -# ── Вывод информации о прокси ──────────────────────────────────────────────── -show_proxy_info() { - local config="${1:-$TELEMT_CONFIG}" - local secret port mask_host ip link status - - secret=$(get_config_value secret "$config") - port=$(get_config_value port "$config") - mask_host=$(get_config_value mask_host "$config") - ip=$(get_server_ip) - link=$(generate_proxy_link "$ip" "$port" "$secret") - status=$(telemt_status) - - local mode - mode=$(config_get mode 2>/dev/null || echo "quick") - - local status_icon status_text - case "$status" in - running) status_icon="✅"; status_text="Работает" ;; - stopped) status_icon="⏸️"; status_text="Остановлен" ;; - *) status_icon="❌"; status_text="Не установлен" ;; - esac - - echo "" - echo -e " ${BOLD}${WHITE}${status_icon} Статус прокси: ${status_text}${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" - echo -e " ${WHITE}Ядро:${NC} telemt (Rust)" - echo -e " ${WHITE}IP:${NC} ${CYAN}${ip}${NC}" - echo -e " ${WHITE}Порт:${NC} ${CYAN}${port}${NC}" - echo -e " ${WHITE}Режим:${NC} ${CYAN}${mode}${NC}" - echo -e " ${WHITE}Маскировка:${NC} ${CYAN}${mask_host}${NC}" - echo -e " ${WHITE}Secret:${NC} ${CYAN}${secret:0:16}...${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" - echo -e " ${WHITE}Ссылка:${NC}" - echo -e " ${GREEN}${link}${NC}" - echo "" - - # QR если доступен - if command -v qrencode &>/dev/null; then - qrencode -t UTF8 -m 2 "$link" 2>/dev/null - fi -} +#!/bin/bash +# GoTelegram v2.2 — Генерация TOML конфигурации для telemt + +# ── Популярные домены (не заблокированные в РФ) ────────────────────────────── +QUICK_DOMAINS=( + "google.com" + "microsoft.com" + "cloudflare.com" + "apple.com" + "amazon.com" + "github.com" + "stackoverflow.com" + "medium.com" + "wikipedia.org" + "coursera.org" + "udemy.com" + "habr.com" + "stepik.org" + "duolingo.com" + "khanacademy.org" + "bbc.com" + "reuters.com" + "nytimes.com" + "ted.com" + "zoom.us" +) + +# ── Генерация TOML конфига ─────────────────────────────────────────────────── +generate_telemt_toml() { + local secret="$1" + local port="${2:-443}" + local mask_mode="${3:-quick}" # quick | stealth + local mask_host="${4:-google.com}" + local mask_port="${5:-443}" + local output="${6:-$TELEMT_CONFIG}" + + mkdir -p "$(dirname "$output")" + + cat > "$output" << EOTOML +# GoTelegram v${GOTELEGRAM_VERSION} — telemt configuration +# Сгенерировано: $(date -Iseconds) +# Режим: ${mask_mode} + +# ── Основные настройки ─────────────────────────────────────────────────────── +[stats] + statsd_address = "" + +# ── Секреты ────────────────────────────────────────────────────────────────── +[[users]] + name = "main" + secret = "${secret}" + +# ── Привязка ───────────────────────────────────────────────────────────────── +[listen] + bind_to = "0.0.0.0:${port}" + +# ── TLS маскировка ─────────────────────────────────────────────────────────── +[security] + # Маскировочный хост — куда перенаправлять неопознанные подключения + # quick: внешний сайт | stealth: локальный nginx + host = "${mask_host}:${mask_port}" + +EOTOML + + chmod 600 "$output" + log_success "Конфиг telemt записан: $output" + log_dim "Режим: $mask_mode, маскировка: $mask_host:$mask_port" +} + +# ── Добавление дополнительного секрета ─────────────────────────────────────── +add_secret_to_config() { + local name="$1" + local secret="$2" + local config="${3:-$TELEMT_CONFIG}" + + if [ ! -f "$config" ]; then + log_error "Конфиг не найден: $config" + return 1 + fi + + # Добавляем новый блок [[users]] + cat >> "$config" << EOSECRET + +[[users]] + name = "${name}" + secret = "${secret}" +EOSECRET + + log_success "Добавлен секрет: $name" +} + +# ── Чтение текущего конфига ────────────────────────────────────────────────── +get_config_value() { + local key="$1" + local config="${2:-$TELEMT_CONFIG}" + + if [ ! -f "$config" ]; then return 1; fi + + case "$key" in + secret) + grep -m1 'secret\s*=' "$config" | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' ' + ;; + port) + grep 'bind_to\s*=' "$config" | sed 's/.*:\([0-9]*\)".*/\1/' + ;; + mask_host) + grep -A10 '\[security\]' "$config" | grep 'host\s*=' | sed 's/.*=\s*"\(.*\)".*/\1/' + ;; + *) + grep "$key" "$config" | head -1 | sed 's/.*=\s*"\?\(.*\)"\?/\1/' | tr -d ' "' + ;; + esac +} + +# ── Валидация конфига ──────────────────────────────────────────────────────── +validate_telemt_config() { + local config="${1:-$TELEMT_CONFIG}" + + if [ ! -f "$config" ]; then + log_error "Конфиг не найден: $config" + return 1 + fi + + # Проверяем обязательные поля + local secret port host + secret=$(get_config_value secret "$config") + port=$(get_config_value port "$config") + host=$(get_config_value mask_host "$config") + + local errors=0 + + if [ -z "$secret" ]; then + log_error "Не задан secret" + ((errors++)) + elif [ ${#secret} -lt 32 ]; then + log_warning "Secret слишком короткий (${#secret} символов, рекомендуется 32+)" + fi + + if [ -z "$port" ]; then + log_error "Не задан порт (bind_to)" + ((errors++)) + elif [ "$port" -lt 1 ] || [ "$port" -gt 65535 ] 2>/dev/null; then + log_error "Порт вне диапазона: $port" + ((errors++)) + fi + + if [ -z "$host" ]; then + log_error "Не задан маскировочный хост (security.host)" + ((errors++)) + fi + + if [ $errors -gt 0 ]; then + log_error "Найдено ошибок: $errors" + return 1 + fi + + log_success "Конфиг валиден" + return 0 +} + +# ── Выбор домена (интерактивный) ───────────────────────────────────────────── +select_quick_domain() { + echo "" + echo -e " ${BOLD}${WHITE}🌐 Выберите домен для маскировки (Fake TLS):${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" + + local i=1 + local row="" + for d in "${QUICK_DOMAINS[@]}"; do + printf " ${CYAN}%2d)${NC} %-25s" "$i" "$d" + if (( i % 2 == 0 )); then + echo "" + fi + ((i++)) + done + if (( (i-1) % 2 != 0 )); then echo ""; fi + + echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" + echo -ne " ${WHITE}Выбор (1-${#QUICK_DOMAINS[@]}):${NC} " + read -r choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#QUICK_DOMAINS[@]} ]; then + echo "${QUICK_DOMAINS[$((choice-1))]}" + return 0 + fi + + log_error "Неверный выбор" + return 1 +} + +# ── Выбор порта (интерактивный) ────────────────────────────────────────────── +select_port() { + echo "" + echo -e " ${BOLD}${WHITE}🔌 Выберите порт:${NC}" + + # Проверяем стандартные порты + local busy_443 busy_8443 + busy_443=$(check_port 443) + busy_8443=$(check_port 8443) + + local label_443="443 (рекомендуется)" + local label_8443="8443" + [ -n "$busy_443" ] && label_443="443 ⚠️ занят" + [ -n "$busy_8443" ] && label_8443="8443 ⚠️ занят" + + echo -e " ${CYAN}1)${NC} $label_443" + echo -e " ${CYAN}2)${NC} $label_8443" + echo -e " ${CYAN}3)${NC} Свой порт" + + if [ -n "$busy_443" ]; then + echo -e " ${DIM} ⚠ Порт 443 занят: $(echo "$busy_443" | head -c 60)${NC}" + fi + + echo -ne " ${WHITE}Выбор:${NC} " + read -r choice + + case "$choice" in + 1) echo "443" ;; + 2) echo "8443" ;; + 3) + echo -ne " Введите порт (1-65535): " + read -r custom_port + if [[ "$custom_port" =~ ^[0-9]+$ ]] && [ "$custom_port" -ge 1 ] && [ "$custom_port" -le 65535 ]; then + echo "$custom_port" + else + log_error "Неверный порт" + return 1 + fi + ;; + *) echo "443" ;; + esac +} + +# ── Генерация ссылки tg://proxy ────────────────────────────────────────────── +generate_proxy_link() { + local ip="${1:-$(get_server_ip)}" + local port="${2:-443}" + local secret="$3" + echo "tg://proxy?server=${ip}&port=${port}&secret=${secret}" +} + +# ── Вывод информации о прокси ──────────────────────────────────────────────── +show_proxy_info() { + local config="${1:-$TELEMT_CONFIG}" + local secret port mask_host ip link status + + secret=$(get_config_value secret "$config") + port=$(get_config_value port "$config") + mask_host=$(get_config_value mask_host "$config") + ip=$(get_server_ip) + link=$(generate_proxy_link "$ip" "$port" "$secret") + status=$(telemt_status) + + local mode + mode=$(config_get mode 2>/dev/null || echo "quick") + + local status_icon status_text + case "$status" in + running) status_icon="✅"; status_text="Работает" ;; + stopped) status_icon="⏸️"; status_text="Остановлен" ;; + *) status_icon="❌"; status_text="Не установлен" ;; + esac + + echo "" + echo -e " ${BOLD}${WHITE}${status_icon} Статус прокси: ${status_text}${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" + echo -e " ${WHITE}Ядро:${NC} telemt (Rust)" + echo -e " ${WHITE}IP:${NC} ${CYAN}${ip}${NC}" + echo -e " ${WHITE}Порт:${NC} ${CYAN}${port}${NC}" + echo -e " ${WHITE}Режим:${NC} ${CYAN}${mode}${NC}" + echo -e " ${WHITE}Маскировка:${NC} ${CYAN}${mask_host}${NC}" + echo -e " ${WHITE}Secret:${NC} ${CYAN}${secret:0:16}...${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" + echo -e " ${WHITE}Ссылка:${NC}" + echo -e " ${GREEN}${link}${NC}" + echo "" + + # QR если доступен + if command -v qrencode &>/dev/null; then + qrencode -t UTF8 -m 2 "$link" 2>/dev/null + fi +} diff --git a/lib/templates_catalog.sh b/lib/templates_catalog.sh index 157ac2b..f4fa514 100644 --- a/lib/templates_catalog.sh +++ b/lib/templates_catalog.sh @@ -1,280 +1,280 @@ -#!/bin/bash -# GoTelegram v2.2 — Каталог шаблонов сайтов -# Выбор из ~200 шаблонов, превью-ссылки, скачивание через git sparse-checkout - -CATALOG_FILE="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")/templates_catalog.json" -TEMPLATES_CACHE="/tmp/gotelegram_templates" - -# ── Загрузка каталога ──────────────────────────────────────────────────────── -load_catalog() { - if [ ! -f "$CATALOG_FILE" ]; then - log_error "Каталог шаблонов не найден: $CATALOG_FILE" - return 1 - fi - return 0 -} - -# ── Категории ──────────────────────────────────────────────────────────────── -get_categories() { - jq -r '.categories[] | "\(.id)|\(.name)|\(.icon)|\(.templates | length)"' "$CATALOG_FILE" 2>/dev/null -} - -get_category_name() { - local cat_id="$1" - jq -r ".categories[] | select(.id == \"$cat_id\") | .name" "$CATALOG_FILE" 2>/dev/null -} - -# ── Шаблоны по категории ──────────────────────────────────────────────────── -get_templates_by_category() { - local cat_id="$1" - jq -r ".categories[] | select(.id == \"$cat_id\") | .templates[] | \"\(.id)|\(.name)|\(.source)|\(.preview_url)\"" "$CATALOG_FILE" 2>/dev/null -} - -# ── Информация о шаблоне ──────────────────────────────────────────────────── -get_template_info() { - local tpl_id="$1" - jq ".categories[].templates[] | select(.id == \"$tpl_id\")" "$CATALOG_FILE" 2>/dev/null -} - -get_template_field() { - local tpl_id="$1" - local field="$2" - jq -r ".categories[].templates[] | select(.id == \"$tpl_id\") | .$field" "$CATALOG_FILE" 2>/dev/null -} - -# ── Интерактивный выбор категории ──────────────────────────────────────────── -select_category() { - load_catalog || return 1 - - echo "" - echo -e " ${BOLD}${WHITE}📂 Категории шаблонов сайтов:${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" - - local cats=() - local i=1 - while IFS='|' read -r id name icon count; do - [ "$count" -eq 0 ] && continue - local emoji - case "$icon" in - briefcase) emoji="🏢" ;; - shopping-cart) emoji="🛒" ;; - heart) emoji="🏥" ;; - book) emoji="🎓" ;; - palette) emoji="📸" ;; - home) emoji="🏠" ;; - utensils) emoji="🍕" ;; - rocket) emoji="🎨" ;; - chart-bar) emoji="🔧" ;; - *) emoji="📄" ;; - esac - printf " ${CYAN}%2d)${NC} ${emoji} %-30s ${DIM}(%d шаблонов)${NC}\n" "$i" "$name" "$count" - cats+=("$id") - ((i++)) - done < <(get_categories) - - printf " ${CYAN}%2d)${NC} 🎲 Случайный шаблон\n" "$i" - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" - echo -ne " ${WHITE}Выбор:${NC} " - read -r choice - - # Случайный - if [ "$choice" -eq "$i" ] 2>/dev/null; then - local random_cat="${cats[$((RANDOM % ${#cats[@]}))]}" - echo "$random_cat" - return 0 - fi - - if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then - echo "${cats[$((choice-1))]}" - return 0 - fi - - log_error "Неверный выбор" - return 1 -} - -# ── Интерактивный выбор шаблона ────────────────────────────────────────────── -select_template() { - local cat_id="$1" - local cat_name - cat_name=$(get_category_name "$cat_id") - - echo "" - echo -e " ${BOLD}${WHITE}📋 $cat_name — доступные шаблоны:${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" - - local tpls=() - local i=1 - while IFS='|' read -r id name source preview; do - printf " ${CYAN}%2d)${NC} %-30s ${DIM}[%s]${NC}\n" "$i" "$name" "$source" - tpls+=("$id") - ((i++)) - done < <(get_templates_by_category "$cat_id") - - if [ ${#tpls[@]} -eq 0 ]; then - log_info "В этой категории нет шаблонов" - return 1 - fi - - echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" - echo -ne " ${WHITE}Выбор (1-$((i-1))):${NC} " - read -r choice - - if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then - local selected_id="${tpls[$((choice-1))]}" - - # Показываем превью - show_template_preview "$selected_id" - - echo "$selected_id" - return 0 - fi - - log_error "Неверный выбор" - return 1 -} - -# ── Показ превью шаблона ──────────────────────────────────────────────────── -show_template_preview() { - local tpl_id="$1" - local info - info=$(get_template_info "$tpl_id") - - local name source preview_url repo_url description - name=$(echo "$info" | jq -r '.name') - source=$(echo "$info" | jq -r '.source') - preview_url=$(echo "$info" | jq -r '.preview_url // empty') - repo_url=$(echo "$info" | jq -r '.repo_url // empty') - description=$(echo "$info" | jq -r '.description // "—"') - - echo "" - echo -e " ${BOLD}${WHITE}🔍 Превью шаблона:${NC}" - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" - echo -e " ${WHITE}Название:${NC} $name" - echo -e " ${WHITE}Источник:${NC} $source" - echo -e " ${WHITE}Описание:${NC} $description" - - if [ -n "$preview_url" ]; then - echo "" - echo -e " ${GREEN}👁 Превью:${NC} ${CYAN}${preview_url}${NC}" - echo -e " ${DIM}Откройте ссылку в браузере для просмотра шаблона${NC}" - fi - - if [ -n "$repo_url" ]; then - echo -e " ${DIM}📦 Репо: ${repo_url}${NC}" - fi - - # Благодарность автору - echo "" - echo -e " ${MAGENTA}💜 Спасибо авторам ${source} за открытый код!${NC}" - - echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" - echo "" - - if ! confirm "Установить этот шаблон?"; then - return 1 - fi - return 0 -} - -# ── Скачивание шаблона ─────────────────────────────────────────────────────── -download_template() { - local tpl_id="$1" - local output_dir="${2:-$TEMPLATES_CACHE}" - local info - info=$(get_template_info "$tpl_id") - - local repo_url sparse_path source name - repo_url=$(echo "$info" | jq -r '.repo_url') - sparse_path=$(echo "$info" | jq -r '.sparse_path') - source=$(echo "$info" | jq -r '.source') - name=$(echo "$info" | jq -r '.name') - - local clone_dir="$output_dir/${tpl_id}" - rm -rf "$clone_dir" - mkdir -p "$clone_dir" - - log_info "Скачивание шаблона \"$name\"..." - - # Для HTML5 UP — отдельный репо с папками - if [ "$source" = "html5up" ]; then - local tmp_clone="/tmp/html5up_clone_$$" - rm -rf "$tmp_clone" - - # Sparse checkout - git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null - if [ $? -ne 0 ]; then - # Fallback: полный clone - git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null - fi - - if [ -d "$tmp_clone" ]; then - cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null - if [ -d "$tmp_clone/$sparse_path" ]; then - cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/" - fi - cd - >/dev/null - fi - rm -rf "$tmp_clone" - - # Для learning-zone — один большой репо - elif [ "$source" = "learning-zone" ]; then - local tmp_clone="/tmp/lz_clone_$$" - rm -rf "$tmp_clone" - - git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null - if [ $? -ne 0 ]; then - git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null - fi - - if [ -d "$tmp_clone" ]; then - cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null - if [ -d "$tmp_clone/$sparse_path" ]; then - cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/" - fi - cd - >/dev/null - fi - rm -rf "$tmp_clone" - - # Для StartBootstrap — каждый шаблон в своём репо - elif [ "$source" = "startbootstrap" ]; then - git clone --depth 1 "$repo_url" "$clone_dir" 2>/dev/null - # Убираем .git - rm -rf "$clone_dir/.git" - fi - - # Проверяем результат - if [ -f "$clone_dir/index.html" ]; then - log_success "Шаблон \"$name\" скачан" - echo "$clone_dir" - return 0 - else - log_error "Шаблон не содержит index.html" - log_dim "Путь: $clone_dir" - ls -la "$clone_dir" 2>/dev/null - return 1 - fi -} - -# ── Полный интерактивный процесс выбора ────────────────────────────────────── -interactive_template_selection() { - load_catalog || return 1 - - # Выбор категории - local cat_id - cat_id=$(select_category) - [ $? -ne 0 ] && return 1 - - # Выбор шаблона - local tpl_id - tpl_id=$(select_template "$cat_id") - [ $? -ne 0 ] && return 1 - - # Скачивание - local template_dir - template_dir=$(download_template "$tpl_id") - [ $? -ne 0 ] && return 1 - - echo "$template_dir" - return 0 -} +#!/bin/bash +# GoTelegram v2.2 — Каталог шаблонов сайтов +# Выбор из ~200 шаблонов, превью-ссылки, скачивание через git sparse-checkout + +CATALOG_FILE="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")/templates_catalog.json" +TEMPLATES_CACHE="/tmp/gotelegram_templates" + +# ── Загрузка каталога ──────────────────────────────────────────────────────── +load_catalog() { + if [ ! -f "$CATALOG_FILE" ]; then + log_error "Каталог шаблонов не найден: $CATALOG_FILE" + return 1 + fi + return 0 +} + +# ── Категории ──────────────────────────────────────────────────────────────── +get_categories() { + jq -r '.categories[] | "\(.id)|\(.name)|\(.icon)|\(.templates | length)"' "$CATALOG_FILE" 2>/dev/null +} + +get_category_name() { + local cat_id="$1" + jq -r ".categories[] | select(.id == \"$cat_id\") | .name" "$CATALOG_FILE" 2>/dev/null +} + +# ── Шаблоны по категории ──────────────────────────────────────────────────── +get_templates_by_category() { + local cat_id="$1" + jq -r ".categories[] | select(.id == \"$cat_id\") | .templates[] | \"\(.id)|\(.name)|\(.source)|\(.preview_url)\"" "$CATALOG_FILE" 2>/dev/null +} + +# ── Информация о шаблоне ──────────────────────────────────────────────────── +get_template_info() { + local tpl_id="$1" + jq ".categories[].templates[] | select(.id == \"$tpl_id\")" "$CATALOG_FILE" 2>/dev/null +} + +get_template_field() { + local tpl_id="$1" + local field="$2" + jq -r ".categories[].templates[] | select(.id == \"$tpl_id\") | .$field" "$CATALOG_FILE" 2>/dev/null +} + +# ── Интерактивный выбор категории ──────────────────────────────────────────── +select_category() { + load_catalog || return 1 + + echo "" + echo -e " ${BOLD}${WHITE}📂 Категории шаблонов сайтов:${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + + local cats=() + local i=1 + while IFS='|' read -r id name icon count; do + [ "$count" -eq 0 ] && continue + local emoji + case "$icon" in + briefcase) emoji="🏢" ;; + shopping-cart) emoji="🛒" ;; + heart) emoji="🏥" ;; + book) emoji="🎓" ;; + palette) emoji="📸" ;; + home) emoji="🏠" ;; + utensils) emoji="🍕" ;; + rocket) emoji="🎨" ;; + chart-bar) emoji="🔧" ;; + *) emoji="📄" ;; + esac + printf " ${CYAN}%2d)${NC} ${emoji} %-30s ${DIM}(%d шаблонов)${NC}\n" "$i" "$name" "$count" + cats+=("$id") + ((i++)) + done < <(get_categories) + + printf " ${CYAN}%2d)${NC} 🎲 Случайный шаблон\n" "$i" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + echo -ne " ${WHITE}Выбор:${NC} " + read -r choice + + # Случайный + if [ "$choice" -eq "$i" ] 2>/dev/null; then + local random_cat="${cats[$((RANDOM % ${#cats[@]}))]}" + echo "$random_cat" + return 0 + fi + + if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then + echo "${cats[$((choice-1))]}" + return 0 + fi + + log_error "Неверный выбор" + return 1 +} + +# ── Интерактивный выбор шаблона ────────────────────────────────────────────── +select_template() { + local cat_id="$1" + local cat_name + cat_name=$(get_category_name "$cat_id") + + echo "" + echo -e " ${BOLD}${WHITE}📋 $cat_name — доступные шаблоны:${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" + + local tpls=() + local i=1 + while IFS='|' read -r id name source preview; do + printf " ${CYAN}%2d)${NC} %-30s ${DIM}[%s]${NC}\n" "$i" "$name" "$source" + tpls+=("$id") + ((i++)) + done < <(get_templates_by_category "$cat_id") + + if [ ${#tpls[@]} -eq 0 ]; then + log_info "В этой категории нет шаблонов" + return 1 + fi + + echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}" + echo -ne " ${WHITE}Выбор (1-$((i-1))):${NC} " + read -r choice + + if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then + local selected_id="${tpls[$((choice-1))]}" + + # Показываем превью + show_template_preview "$selected_id" + + echo "$selected_id" + return 0 + fi + + log_error "Неверный выбор" + return 1 +} + +# ── Показ превью шаблона ──────────────────────────────────────────────────── +show_template_preview() { + local tpl_id="$1" + local info + info=$(get_template_info "$tpl_id") + + local name source preview_url repo_url description + name=$(echo "$info" | jq -r '.name') + source=$(echo "$info" | jq -r '.source') + preview_url=$(echo "$info" | jq -r '.preview_url // empty') + repo_url=$(echo "$info" | jq -r '.repo_url // empty') + description=$(echo "$info" | jq -r '.description // "—"') + + echo "" + echo -e " ${BOLD}${WHITE}🔍 Превью шаблона:${NC}" + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + echo -e " ${WHITE}Название:${NC} $name" + echo -e " ${WHITE}Источник:${NC} $source" + echo -e " ${WHITE}Описание:${NC} $description" + + if [ -n "$preview_url" ]; then + echo "" + echo -e " ${GREEN}👁 Превью:${NC} ${CYAN}${preview_url}${NC}" + echo -e " ${DIM}Откройте ссылку в браузере для просмотра шаблона${NC}" + fi + + if [ -n "$repo_url" ]; then + echo -e " ${DIM}📦 Репо: ${repo_url}${NC}" + fi + + # Благодарность автору + echo "" + echo -e " ${MAGENTA}💜 Спасибо авторам ${source} за открытый код!${NC}" + + echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}" + echo "" + + if ! confirm "Установить этот шаблон?"; then + return 1 + fi + return 0 +} + +# ── Скачивание шаблона ─────────────────────────────────────────────────────── +download_template() { + local tpl_id="$1" + local output_dir="${2:-$TEMPLATES_CACHE}" + local info + info=$(get_template_info "$tpl_id") + + local repo_url sparse_path source name + repo_url=$(echo "$info" | jq -r '.repo_url') + sparse_path=$(echo "$info" | jq -r '.sparse_path') + source=$(echo "$info" | jq -r '.source') + name=$(echo "$info" | jq -r '.name') + + local clone_dir="$output_dir/${tpl_id}" + rm -rf "$clone_dir" + mkdir -p "$clone_dir" + + log_info "Скачивание шаблона \"$name\"..." + + # Для HTML5 UP — отдельный репо с папками + if [ "$source" = "html5up" ]; then + local tmp_clone="/tmp/html5up_clone_$$" + rm -rf "$tmp_clone" + + # Sparse checkout + git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null + if [ $? -ne 0 ]; then + # Fallback: полный clone + git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null + fi + + if [ -d "$tmp_clone" ]; then + cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null + if [ -d "$tmp_clone/$sparse_path" ]; then + cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/" + fi + cd - >/dev/null + fi + rm -rf "$tmp_clone" + + # Для learning-zone — один большой репо + elif [ "$source" = "learning-zone" ]; then + local tmp_clone="/tmp/lz_clone_$$" + rm -rf "$tmp_clone" + + git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null + if [ $? -ne 0 ]; then + git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null + fi + + if [ -d "$tmp_clone" ]; then + cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null + if [ -d "$tmp_clone/$sparse_path" ]; then + cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/" + fi + cd - >/dev/null + fi + rm -rf "$tmp_clone" + + # Для StartBootstrap — каждый шаблон в своём репо + elif [ "$source" = "startbootstrap" ]; then + git clone --depth 1 "$repo_url" "$clone_dir" 2>/dev/null + # Убираем .git + rm -rf "$clone_dir/.git" + fi + + # Проверяем результат + if [ -f "$clone_dir/index.html" ]; then + log_success "Шаблон \"$name\" скачан" + echo "$clone_dir" + return 0 + else + log_error "Шаблон не содержит index.html" + log_dim "Путь: $clone_dir" + ls -la "$clone_dir" 2>/dev/null + return 1 + fi +} + +# ── Полный интерактивный процесс выбора ────────────────────────────────────── +interactive_template_selection() { + load_catalog || return 1 + + # Выбор категории + local cat_id + cat_id=$(select_category) + [ $? -ne 0 ] && return 1 + + # Выбор шаблона + local tpl_id + tpl_id=$(select_template "$cat_id") + [ $? -ne 0 ] && return 1 + + # Скачивание + local template_dir + template_dir=$(download_template "$tpl_id") + [ $? -ne 0 ] && return 1 + + echo "$template_dir" + return 0 +} diff --git a/lib/website.sh b/lib/website.sh index 5d0ab5f..dec3381 100644 --- a/lib/website.sh +++ b/lib/website.sh @@ -1,340 +1,340 @@ -#!/bin/bash -# GoTelegram v2.2 — Управление сайтом (nginx + certbot + шаблоны) - -# ── Установка nginx ────────────────────────────────────────────────────────── -install_nginx() { - if command -v nginx &>/dev/null; then - log_dim "nginx уже установлен" - return 0 - fi - log_info "Установка nginx..." - case "$(get_pkg_manager)" in - apt) apt-get update -qq && apt-get install -y -qq nginx ;; - dnf) dnf install -y -q nginx ;; - yum) yum install -y -q nginx ;; - esac - systemctl enable nginx 2>/dev/null -} - -# ── Установка certbot ──────────────────────────────────────────────────────── -install_certbot() { - if command -v certbot &>/dev/null; then - log_dim "certbot уже установлен" - return 0 - fi - log_info "Установка certbot..." - case "$(get_pkg_manager)" in - apt) apt-get install -y -qq certbot python3-certbot-nginx ;; - dnf) dnf install -y -q certbot python3-certbot-nginx ;; - yum) yum install -y -q certbot python3-certbot-nginx ;; - esac -} - -# ── Генерация nginx конфига ────────────────────────────────────────────────── -generate_nginx_config() { - local domain="$1" - local proxy_port="${2:-443}" - local use_ssl="${3:-true}" - - mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled - - cat > "$NGINX_SITE_CONF" << 'EONGINX' -# GoTelegram v2.2 — nginx config -# Обслуживает сайт-маскировку для telemt stealth mode - -server { - listen 80; - listen [::]:80; - server_name DOMAIN_PLACEHOLDER; - - # Let's Encrypt ACME challenge - location /.well-known/acme-challenge/ { - root /var/www/certbot; - allow all; - } - - # Редирект на HTTPS - location / { - return 301 https://$server_name$request_uri; - } -} - -server { - listen SSL_PORT_PLACEHOLDER ssl http2; - listen [::]:SSL_PORT_PLACEHOLDER ssl http2; - server_name DOMAIN_PLACEHOLDER; - - # SSL сертификаты - ssl_certificate /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/privkey.pem; - - # Современные TLS настройки - ssl_protocols TLSv1.2 TLSv1.3; - ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; - ssl_prefer_server_ciphers off; - ssl_session_cache shared:SSL:10m; - ssl_session_timeout 1d; - ssl_session_tickets off; - - # OCSP stapling - ssl_stapling on; - ssl_stapling_verify on; - - # Security headers - add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; - add_header X-Content-Type-Options "nosniff" always; - add_header X-Frame-Options "SAMEORIGIN" always; - add_header X-XSS-Protection "1; mode=block" always; - add_header Referrer-Policy "strict-origin-when-cross-origin" always; - - # Корень сайта - root /var/www/gotelegram-site; - index index.html; - - location / { - try_files $uri $uri/ =404; - expires 30d; - } - - # Кеширование статики - location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { - expires 1y; - add_header Cache-Control "public, immutable"; - } - - # Скрываем служебные файлы - location ~ /\. { deny all; } - location = /robots.txt { allow all; log_not_found off; access_log off; } - location = /favicon.ico { log_not_found off; access_log off; } -} -EONGINX - - # Подставляем значения (используем | как разделитель, чтобы / в домене не ломал sed) - local escaped_domain - escaped_domain=$(printf '%s\n' "$domain" | sed 's/[&/\]/\\&/g') - sed -i "s|DOMAIN_PLACEHOLDER|${escaped_domain}|g" "$NGINX_SITE_CONF" - sed -i "s|SSL_PORT_PLACEHOLDER|443|g" "$NGINX_SITE_CONF" - - # Активируем сайт - rm -f /etc/nginx/sites-enabled/default 2>/dev/null - ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK" - - log_success "nginx конфиг создан для $domain" -} - -# ── Временный конфиг (до получения SSL) ────────────────────────────────────── -generate_nginx_temp_config() { - local domain="$1" - - cat > "$NGINX_SITE_CONF" << EONGINX_TEMP -# GoTelegram — временный конфиг (до получения SSL) -server { - listen 80; - listen [::]:80; - server_name ${domain}; - - location /.well-known/acme-challenge/ { - root /var/www/certbot; - allow all; - } - - root /var/www/gotelegram-site; - index index.html; - - location / { - try_files \$uri \$uri/ =404; - } -} -EONGINX_TEMP - - rm -f /etc/nginx/sites-enabled/default 2>/dev/null - ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK" - mkdir -p /var/www/certbot -} - -# ── Получение SSL сертификата ──────────────────────────────────────────────── -obtain_ssl_certificate() { - local domain="$1" - local email="${2:-}" - - if [ ! -d "/etc/letsencrypt/live/$domain" ]; then - log_info "Получение SSL сертификата для $domain..." - - # Временный конфиг для ACME challenge - generate_nginx_temp_config "$domain" - systemctl restart nginx 2>/dev/null - - local certbot_args=( - certonly - --webroot - -w /var/www/certbot - -d "$domain" - --non-interactive - --agree-tos - ) - - if [ -n "$email" ]; then - certbot_args+=(--email "$email") - else - certbot_args+=(--register-unsafely-without-email) - fi - - if certbot "${certbot_args[@]}" 2>/dev/null; then - log_success "SSL сертификат получен для $domain" - return 0 - else - log_error "Не удалось получить SSL сертификат" - log_dim "Убедитесь что домен $domain направлен на IP этого сервера" - log_dim "и порт 80 открыт в файрволе." - return 1 - fi - else - log_dim "SSL сертификат уже существует для $domain" - return 0 - fi -} - -# ── Авто-обновление сертификата ────────────────────────────────────────────── -setup_ssl_auto_renewal() { - # Certbot systemd timer (предпочтительно) - if [ -f /etc/systemd/system/certbot.timer ] || [ -f /lib/systemd/system/certbot.timer ]; then - systemctl enable certbot.timer 2>/dev/null - systemctl start certbot.timer 2>/dev/null - log_success "Авто-обновление SSL через systemd timer" - return 0 - fi - - # Fallback: cron - if ! crontab -l 2>/dev/null | grep -q "certbot renew"; then - (crontab -l 2>/dev/null; echo "0 3 * * * certbot renew --quiet --post-hook 'systemctl reload nginx'") | crontab - - log_success "Авто-обновление SSL через cron (3:00 ежедневно)" - fi -} - -# ── Обновление сертификата вручную ─────────────────────────────────────────── -renew_ssl_certificate() { - log_info "Обновление SSL сертификата..." - if certbot renew --quiet --post-hook "systemctl reload nginx" 2>/dev/null; then - log_success "Сертификат обновлён" - return 0 - else - log_error "Ошибка обновления сертификата" - return 1 - fi -} - -# ── Дата истечения SSL ─────────────────────────────────────────────────────── -get_ssl_expiry() { - local domain="$1" - local cert="/etc/letsencrypt/live/$domain/fullchain.pem" - if [ -f "$cert" ]; then - openssl x509 -enddate -noout -in "$cert" 2>/dev/null | sed 's/notAfter=//' - else - echo "N/A" - fi -} - -# ── Деплой шаблона сайта ───────────────────────────────────────────────────── -deploy_template_to_nginx() { - local template_dir="$1" - - if [ ! -d "$template_dir" ] || [ ! -f "$template_dir/index.html" ]; then - log_error "Шаблон не содержит index.html: $template_dir" - return 1 - fi - - # Бекапим старый сайт - if [ -d "$WEBSITE_ROOT" ] && [ "$(ls -A "$WEBSITE_ROOT" 2>/dev/null)" ]; then - local backup_name="site_backup_$(date +%Y%m%d_%H%M%S)" - mv "$WEBSITE_ROOT" "/tmp/$backup_name" 2>/dev/null - log_dim "Старый сайт сохранён в /tmp/$backup_name" - fi - - mkdir -p "$WEBSITE_ROOT" - cp -r "$template_dir"/* "$WEBSITE_ROOT/" - chown -R www-data:www-data "$WEBSITE_ROOT" 2>/dev/null || chown -R nginx:nginx "$WEBSITE_ROOT" 2>/dev/null - chmod -R 755 "$WEBSITE_ROOT" - - log_success "Шаблон развёрнут в $WEBSITE_ROOT" -} - -# ── Полная установка stealth-режима ────────────────────────────────────────── -setup_stealth_mode() { - local domain="$1" - local template_dir="$2" - local proxy_port="${3:-443}" - local email="${4:-}" - - log_step "Настройка stealth-режима" - - # 1. Устанавливаем nginx - run_with_spinner "Установка nginx" install_nginx || return 1 - - # 2. Устанавливаем certbot - run_with_spinner "Установка certbot" install_certbot || return 1 - - # 3. Деплоим шаблон сайта - deploy_template_to_nginx "$template_dir" || return 1 - - # 4. Получаем SSL - obtain_ssl_certificate "$domain" "$email" || return 1 - - # 5. Генерируем полный nginx конфиг с SSL - generate_nginx_config "$domain" "$proxy_port" - - # 6. Тестируем и перезапускаем nginx - if nginx -t 2>/dev/null; then - systemctl restart nginx - log_success "nginx запущен с SSL" - else - log_error "Ошибка в конфигурации nginx" - nginx -t - return 1 - fi - - # 7. Настраиваем авто-обновление SSL - setup_ssl_auto_renewal - - # 8. Показываем благодарности авторам шаблонов - show_credits - - log_success "Stealth-режим настроен: https://${domain}" - return 0 -} - -# ── Управление nginx ──────────────────────────────────────────────────────── -nginx_status() { - if systemctl is-active --quiet nginx 2>/dev/null; then - echo "running" - else - echo "stopped" - fi -} - -restart_nginx() { - if nginx -t 2>/dev/null; then - systemctl restart nginx 2>/dev/null - log_success "nginx перезапущен" - else - log_error "Ошибка конфигурации nginx" - nginx -t - return 1 - fi -} - -# ── Удаление stealth-режима ────────────────────────────────────────────────── -remove_stealth_mode() { - log_info "Удаление stealth-режима..." - rm -f "$NGINX_SITE_CONF" "$NGINX_SITE_LINK" - rm -rf "$WEBSITE_ROOT" - systemctl restart nginx 2>/dev/null - log_success "Stealth-режим удалён (nginx оставлен)" -} - -# ── Смена шаблона ──────────────────────────────────────────────────────────── -switch_template() { - local new_template_dir="$1" - deploy_template_to_nginx "$new_template_dir" - # nginx не требует перезапуска — статика обновилась на месте - log_success "Шаблон сайта обновлён" -} +#!/bin/bash +# GoTelegram v2.2 — Управление сайтом (nginx + certbot + шаблоны) + +# ── Установка nginx ────────────────────────────────────────────────────────── +install_nginx() { + if command -v nginx &>/dev/null; then + log_dim "nginx уже установлен" + return 0 + fi + log_info "Установка nginx..." + case "$(get_pkg_manager)" in + apt) apt-get update -qq && apt-get install -y -qq nginx ;; + dnf) dnf install -y -q nginx ;; + yum) yum install -y -q nginx ;; + esac + systemctl enable nginx 2>/dev/null +} + +# ── Установка certbot ──────────────────────────────────────────────────────── +install_certbot() { + if command -v certbot &>/dev/null; then + log_dim "certbot уже установлен" + return 0 + fi + log_info "Установка certbot..." + case "$(get_pkg_manager)" in + apt) apt-get install -y -qq certbot python3-certbot-nginx ;; + dnf) dnf install -y -q certbot python3-certbot-nginx ;; + yum) yum install -y -q certbot python3-certbot-nginx ;; + esac +} + +# ── Генерация nginx конфига ────────────────────────────────────────────────── +generate_nginx_config() { + local domain="$1" + local proxy_port="${2:-443}" + local use_ssl="${3:-true}" + + mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled + + cat > "$NGINX_SITE_CONF" << 'EONGINX' +# GoTelegram v2.2 — nginx config +# Обслуживает сайт-маскировку для telemt stealth mode + +server { + listen 80; + listen [::]:80; + server_name DOMAIN_PLACEHOLDER; + + # Let's Encrypt ACME challenge + location /.well-known/acme-challenge/ { + root /var/www/certbot; + allow all; + } + + # Редирект на HTTPS + location / { + return 301 https://$server_name$request_uri; + } +} + +server { + listen SSL_PORT_PLACEHOLDER ssl http2; + listen [::]:SSL_PORT_PLACEHOLDER ssl http2; + server_name DOMAIN_PLACEHOLDER; + + # SSL сертификаты + ssl_certificate /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/privkey.pem; + + # Современные TLS настройки + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 1d; + ssl_session_tickets off; + + # OCSP stapling + ssl_stapling on; + ssl_stapling_verify on; + + # Security headers + add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + # Корень сайта + root /var/www/gotelegram-site; + index index.html; + + location / { + try_files $uri $uri/ =404; + expires 30d; + } + + # Кеширование статики + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Скрываем служебные файлы + location ~ /\. { deny all; } + location = /robots.txt { allow all; log_not_found off; access_log off; } + location = /favicon.ico { log_not_found off; access_log off; } +} +EONGINX + + # Подставляем значения (используем | как разделитель, чтобы / в домене не ломал sed) + local escaped_domain + escaped_domain=$(printf '%s\n' "$domain" | sed 's/[&/\]/\\&/g') + sed -i "s|DOMAIN_PLACEHOLDER|${escaped_domain}|g" "$NGINX_SITE_CONF" + sed -i "s|SSL_PORT_PLACEHOLDER|443|g" "$NGINX_SITE_CONF" + + # Активируем сайт + rm -f /etc/nginx/sites-enabled/default 2>/dev/null + ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK" + + log_success "nginx конфиг создан для $domain" +} + +# ── Временный конфиг (до получения SSL) ────────────────────────────────────── +generate_nginx_temp_config() { + local domain="$1" + + cat > "$NGINX_SITE_CONF" << EONGINX_TEMP +# GoTelegram — временный конфиг (до получения SSL) +server { + listen 80; + listen [::]:80; + server_name ${domain}; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + allow all; + } + + root /var/www/gotelegram-site; + index index.html; + + location / { + try_files \$uri \$uri/ =404; + } +} +EONGINX_TEMP + + rm -f /etc/nginx/sites-enabled/default 2>/dev/null + ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK" + mkdir -p /var/www/certbot +} + +# ── Получение SSL сертификата ──────────────────────────────────────────────── +obtain_ssl_certificate() { + local domain="$1" + local email="${2:-}" + + if [ ! -d "/etc/letsencrypt/live/$domain" ]; then + log_info "Получение SSL сертификата для $domain..." + + # Временный конфиг для ACME challenge + generate_nginx_temp_config "$domain" + systemctl restart nginx 2>/dev/null + + local certbot_args=( + certonly + --webroot + -w /var/www/certbot + -d "$domain" + --non-interactive + --agree-tos + ) + + if [ -n "$email" ]; then + certbot_args+=(--email "$email") + else + certbot_args+=(--register-unsafely-without-email) + fi + + if certbot "${certbot_args[@]}" 2>/dev/null; then + log_success "SSL сертификат получен для $domain" + return 0 + else + log_error "Не удалось получить SSL сертификат" + log_dim "Убедитесь что домен $domain направлен на IP этого сервера" + log_dim "и порт 80 открыт в файрволе." + return 1 + fi + else + log_dim "SSL сертификат уже существует для $domain" + return 0 + fi +} + +# ── Авто-обновление сертификата ────────────────────────────────────────────── +setup_ssl_auto_renewal() { + # Certbot systemd timer (предпочтительно) + if [ -f /etc/systemd/system/certbot.timer ] || [ -f /lib/systemd/system/certbot.timer ]; then + systemctl enable certbot.timer 2>/dev/null + systemctl start certbot.timer 2>/dev/null + log_success "Авто-обновление SSL через systemd timer" + return 0 + fi + + # Fallback: cron + if ! crontab -l 2>/dev/null | grep -q "certbot renew"; then + (crontab -l 2>/dev/null; echo "0 3 * * * certbot renew --quiet --post-hook 'systemctl reload nginx'") | crontab - + log_success "Авто-обновление SSL через cron (3:00 ежедневно)" + fi +} + +# ── Обновление сертификата вручную ─────────────────────────────────────────── +renew_ssl_certificate() { + log_info "Обновление SSL сертификата..." + if certbot renew --quiet --post-hook "systemctl reload nginx" 2>/dev/null; then + log_success "Сертификат обновлён" + return 0 + else + log_error "Ошибка обновления сертификата" + return 1 + fi +} + +# ── Дата истечения SSL ─────────────────────────────────────────────────────── +get_ssl_expiry() { + local domain="$1" + local cert="/etc/letsencrypt/live/$domain/fullchain.pem" + if [ -f "$cert" ]; then + openssl x509 -enddate -noout -in "$cert" 2>/dev/null | sed 's/notAfter=//' + else + echo "N/A" + fi +} + +# ── Деплой шаблона сайта ───────────────────────────────────────────────────── +deploy_template_to_nginx() { + local template_dir="$1" + + if [ ! -d "$template_dir" ] || [ ! -f "$template_dir/index.html" ]; then + log_error "Шаблон не содержит index.html: $template_dir" + return 1 + fi + + # Бекапим старый сайт + if [ -d "$WEBSITE_ROOT" ] && [ "$(ls -A "$WEBSITE_ROOT" 2>/dev/null)" ]; then + local backup_name="site_backup_$(date +%Y%m%d_%H%M%S)" + mv "$WEBSITE_ROOT" "/tmp/$backup_name" 2>/dev/null + log_dim "Старый сайт сохранён в /tmp/$backup_name" + fi + + mkdir -p "$WEBSITE_ROOT" + cp -r "$template_dir"/* "$WEBSITE_ROOT/" + chown -R www-data:www-data "$WEBSITE_ROOT" 2>/dev/null || chown -R nginx:nginx "$WEBSITE_ROOT" 2>/dev/null + chmod -R 755 "$WEBSITE_ROOT" + + log_success "Шаблон развёрнут в $WEBSITE_ROOT" +} + +# ── Полная установка stealth-режима ────────────────────────────────────────── +setup_stealth_mode() { + local domain="$1" + local template_dir="$2" + local proxy_port="${3:-443}" + local email="${4:-}" + + log_step "Настройка stealth-режима" + + # 1. Устанавливаем nginx + run_with_spinner "Установка nginx" install_nginx || return 1 + + # 2. Устанавливаем certbot + run_with_spinner "Установка certbot" install_certbot || return 1 + + # 3. Деплоим шаблон сайта + deploy_template_to_nginx "$template_dir" || return 1 + + # 4. Получаем SSL + obtain_ssl_certificate "$domain" "$email" || return 1 + + # 5. Генерируем полный nginx конфиг с SSL + generate_nginx_config "$domain" "$proxy_port" + + # 6. Тестируем и перезапускаем nginx + if nginx -t 2>/dev/null; then + systemctl restart nginx + log_success "nginx запущен с SSL" + else + log_error "Ошибка в конфигурации nginx" + nginx -t + return 1 + fi + + # 7. Настраиваем авто-обновление SSL + setup_ssl_auto_renewal + + # 8. Показываем благодарности авторам шаблонов + show_credits + + log_success "Stealth-режим настроен: https://${domain}" + return 0 +} + +# ── Управление nginx ──────────────────────────────────────────────────────── +nginx_status() { + if systemctl is-active --quiet nginx 2>/dev/null; then + echo "running" + else + echo "stopped" + fi +} + +restart_nginx() { + if nginx -t 2>/dev/null; then + systemctl restart nginx 2>/dev/null + log_success "nginx перезапущен" + else + log_error "Ошибка конфигурации nginx" + nginx -t + return 1 + fi +} + +# ── Удаление stealth-режима ────────────────────────────────────────────────── +remove_stealth_mode() { + log_info "Удаление stealth-режима..." + rm -f "$NGINX_SITE_CONF" "$NGINX_SITE_LINK" + rm -rf "$WEBSITE_ROOT" + systemctl restart nginx 2>/dev/null + log_success "Stealth-режим удалён (nginx оставлен)" +} + +# ── Смена шаблона ──────────────────────────────────────────────────────────── +switch_template() { + local new_template_dir="$1" + deploy_template_to_nginx "$new_template_dir" + # nginx не требует перезапуска — статика обновилась на месте + log_success "Шаблон сайта обновлён" +}