mirror of
https://github.com/anten-ka/gotelegram_pro.git
synced 2026-05-19 13:26:02 +00:00
v2.2.1: unified menu with bot management, grouped sections, telemt download fix, QR cleanup, version bump
This commit is contained in:
@@ -1,70 +1,147 @@
|
|||||||
# GoTelegram MTProxy Bot
|
# GoTelegram v2.2 Bot
|
||||||
|
|
||||||
Telegram-бот для управления MTProxy на сервере — те же функции, что и у CLI `gotelegram`, но через бота.
|
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)
|
||||||
| `/start`, `/help` | Справка |
|
- Status monitoring
|
||||||
| `/install` | Установить или обновить прокси (выбор домена и порта) |
|
- Proxy link generation
|
||||||
| `/status` | Статус и данные подключения (IP, порт, secret, ссылка) |
|
- Share with QR codes
|
||||||
| `/link` | Только ссылка `tg://proxy` |
|
- Service restart
|
||||||
| `/restart` | Перезапустить контейнер |
|
- Logs viewing
|
||||||
| `/logs` | Последние логи контейнера |
|
- Mode/template changes
|
||||||
| `/remove` | Удалить прокси |
|
- Backup/restore
|
||||||
| `/promo` | Промо хостинга |
|
- telemt updates
|
||||||
|
- Website/SSL management
|
||||||
## Установка на сервер
|
- Remove installation
|
||||||
|
- Promotional links
|
||||||
### Публичный репозиторий (одной командой)
|
|
||||||
|
- **Template Browsing** - Browse categories → templates → preview → install
|
||||||
```bash
|
- **V1 Migration** - Detects old mtg Docker container and offers migration
|
||||||
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
|
- **Access Control** - ALLOWED_IDS from .env
|
||||||
```
|
- **Async/Await** - Full async support via python-telegram-bot v21+
|
||||||
|
- **Inline Keyboards** - Modern UI with callback-based navigation
|
||||||
При установке скрипт запросит **BOT_TOKEN** (получить у [@BotFather](https://t.me/BotFather)).
|
- **Shell Integration** - Executes system commands via asyncio subprocess
|
||||||
|
- **Error Handling** - Production-ready error handling
|
||||||
### Закрытый репозиторий (установка по ключу)
|
|
||||||
|
## Installation
|
||||||
Для **приватного** репо используется клонирование по **SSH-ключу** или по **токену (PAT)**. Подробно: **[INSTALL_PRIVATE.md](../INSTALL_PRIVATE.md)** в корне репозитория.
|
|
||||||
|
### Prerequisites
|
||||||
Кратко:
|
|
||||||
- **По SSH:** скопируйте `bootstrap_install.sh` на сервер, затем
|
- Python 3.8+
|
||||||
`GIT_REPO_SSH=git@github.com:USER/REPO.git sudo bash bootstrap_install.sh`
|
- Linux system with systemd
|
||||||
- **По токену:**
|
- telemt installed and running
|
||||||
`GITHUB_TOKEN=ghp_xxx GIT_REPO_HTTPS=https://github.com/USER/REPO.git sudo -E bash bootstrap_install.sh`
|
- Telegram Bot Token from @BotFather
|
||||||
- Или клонируйте репо вручную и запустите:
|
|
||||||
`sudo ./install_gotelegram_bot.sh`
|
### Setup
|
||||||
|
|
||||||
### Локально (файлы уже рядом со скриптом)
|
1. Install dependencies:
|
||||||
|
```bash
|
||||||
```bash
|
pip install -r requirements.txt
|
||||||
sudo ./install_gotelegram_bot.sh
|
```
|
||||||
```
|
|
||||||
|
2. Create .env file:
|
||||||
## Конфигурация
|
```bash
|
||||||
|
cp config.example.env .env
|
||||||
Файл: `/opt/gotelegram-bot/.env`
|
# Edit .env and set your BOT_TOKEN
|
||||||
|
nano .env
|
||||||
- **BOT_TOKEN** — токен от @BotFather (обязательно).
|
```
|
||||||
- **ALLOWED_IDS** — опционально. Список ID пользователей через запятую; если не задан, бот доступен всем.
|
|
||||||
|
3. (Optional) Restrict access to specific users:
|
||||||
После изменения `.env` перезапуск сервиса:
|
```bash
|
||||||
|
# Edit .env and uncomment ALLOWED_IDS
|
||||||
```bash
|
# ALLOWED_IDS=123456789,987654321
|
||||||
sudo systemctl restart gotelegram-bot
|
```
|
||||||
```
|
|
||||||
|
### Running the Bot
|
||||||
## Требования на сервере
|
|
||||||
|
```bash
|
||||||
- Linux (systemd), Docker, Python 3.
|
python3 bot.py
|
||||||
- Перед использованием бота на сервере должен быть установлен Docker (бот сам поднимает контейнер `nineseconds/mtg:2` по команде `/install`).
|
```
|
||||||
|
|
||||||
## Управление сервисом
|
For systemd service:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl status gotelegram-bot
|
[Unit]
|
||||||
sudo systemctl restart gotelegram-bot
|
Description=GoTelegram Bot
|
||||||
journalctl -u gotelegram-bot -f
|
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
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ BOT_TOKEN=your_bot_token_from_@BotFather
|
|||||||
|
|
||||||
# Comma-separated list of allowed Telegram user IDs
|
# Comma-separated list of allowed Telegram user IDs
|
||||||
# Leave empty to allow all users
|
# Leave empty to allow all users
|
||||||
# ALLOWED_IDS=123456789,987654321
|
# ALLOWED_IDS=123456789,987654321
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
python-telegram-bot>=21.0
|
python-telegram-bot>=21.0
|
||||||
python-dotenv>=1.0.0
|
python-dotenv>=1.0.0
|
||||||
toml>=0.10.2
|
toml>=0.10.2
|
||||||
|
|||||||
1242
install.sh
1242
install.sh
File diff suppressed because it is too large
Load Diff
@@ -1,132 +1,132 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# GoTelegram v2.2 — Установка Telegram-бота
|
# GoTelegram v2.2.1 — Установка Telegram-бота
|
||||||
# Создаёт venv, ставит зависимости, настраивает systemd
|
# Создаёт venv, ставит зависимости, настраивает systemd
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
CYAN='\033[0;36m'
|
CYAN='\033[0;36m'
|
||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
|
|
||||||
BOT_DIR="/opt/gotelegram-bot"
|
BOT_DIR="/opt/gotelegram-bot"
|
||||||
SERVICE_NAME="gotelegram-bot"
|
SERVICE_NAME="gotelegram-bot"
|
||||||
GOTELEGRAM_DIR="/opt/gotelegram"
|
GOTELEGRAM_DIR="/opt/gotelegram"
|
||||||
|
|
||||||
if [ "$EUID" -ne 0 ]; then
|
if [ "$EUID" -ne 0 ]; then
|
||||||
echo -e "${RED}Запустите с sudo.${NC}"
|
echo -e "${RED}Запустите с sudo.${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${CYAN}╔═══════════════════════════════════════════╗${NC}"
|
echo -e "${CYAN}╔═══════════════════════════════════════════╗${NC}"
|
||||||
echo -e "${CYAN}║${NC} ${GREEN}GoTelegram v2.2 — Установка бота${NC} ${CYAN}║${NC}"
|
echo -e "${CYAN}║${NC} ${GREEN}GoTelegram v2.2.1 — Установка бота${NC} ${CYAN}║${NC}"
|
||||||
echo -e "${CYAN}╚═══════════════════════════════════════════╝${NC}"
|
echo -e "${CYAN}╚═══════════════════════════════════════════╝${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# ── Python ───────────────────────────────────────────────────────────────────
|
# ── Python ───────────────────────────────────────────────────────────────────
|
||||||
if ! command -v python3 &>/dev/null; then
|
if ! command -v python3 &>/dev/null; then
|
||||||
echo -e "${YELLOW}[*] Установка python3...${NC}"
|
echo -e "${YELLOW}[*] Установка python3...${NC}"
|
||||||
if command -v apt-get &>/dev/null; then
|
if command -v apt-get &>/dev/null; then
|
||||||
apt-get update -qq && apt-get install -y -qq python3 python3-pip python3-venv
|
apt-get update -qq && apt-get install -y -qq python3 python3-pip python3-venv
|
||||||
elif command -v dnf &>/dev/null; then
|
elif command -v dnf &>/dev/null; then
|
||||||
dnf install -y -q python3 python3-pip
|
dnf install -y -q python3 python3-pip
|
||||||
elif command -v yum &>/dev/null; then
|
elif command -v yum &>/dev/null; then
|
||||||
yum install -y -q python3 python3-pip
|
yum install -y -q python3 python3-pip
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Каталог бота ─────────────────────────────────────────────────────────────
|
# ── Каталог бота ─────────────────────────────────────────────────────────────
|
||||||
mkdir -p "$BOT_DIR"
|
mkdir -p "$BOT_DIR"
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then
|
if [ -f "$SCRIPT_DIR/gotelegram-bot/bot.py" ]; then
|
||||||
echo -e "${GREEN}[*] Копирование файлов бота...${NC}"
|
echo -e "${GREEN}[*] Копирование файлов бота...${NC}"
|
||||||
cp "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/"
|
cp "$SCRIPT_DIR/gotelegram-bot/bot.py" "$BOT_DIR/"
|
||||||
cp "$SCRIPT_DIR/gotelegram-bot/requirements.txt" "$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/"
|
[ -f "$SCRIPT_DIR/gotelegram-bot/config.example.env" ] && cp "$SCRIPT_DIR/gotelegram-bot/config.example.env" "$BOT_DIR/"
|
||||||
else
|
else
|
||||||
echo -e "${RED}Файлы бота не найдены в $SCRIPT_DIR/gotelegram-bot/${NC}"
|
echo -e "${RED}Файлы бота не найдены в $SCRIPT_DIR/gotelegram-bot/${NC}"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Копируем каталог шаблонов
|
# Копируем каталог шаблонов
|
||||||
if [ -f "$SCRIPT_DIR/templates_catalog.json" ]; then
|
if [ -f "$SCRIPT_DIR/templates_catalog.json" ]; then
|
||||||
mkdir -p "$GOTELEGRAM_DIR"
|
mkdir -p "$GOTELEGRAM_DIR"
|
||||||
cp "$SCRIPT_DIR/templates_catalog.json" "$GOTELEGRAM_DIR/"
|
cp "$SCRIPT_DIR/templates_catalog.json" "$GOTELEGRAM_DIR/"
|
||||||
echo -e "${GREEN}[*] Каталог шаблонов скопирован${NC}"
|
echo -e "${GREEN}[*] Каталог шаблонов скопирован${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Virtual environment ──────────────────────────────────────────────────────
|
# ── Virtual environment ──────────────────────────────────────────────────────
|
||||||
if [ ! -d "$BOT_DIR/venv" ]; then
|
if [ ! -d "$BOT_DIR/venv" ]; then
|
||||||
echo -e "${GREEN}[*] Создание виртуального окружения...${NC}"
|
echo -e "${GREEN}[*] Создание виртуального окружения...${NC}"
|
||||||
python3 -m venv "$BOT_DIR/venv"
|
python3 -m venv "$BOT_DIR/venv"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${GREEN}[*] Установка зависимостей...${NC}"
|
echo -e "${GREEN}[*] Установка зависимостей...${NC}"
|
||||||
"$BOT_DIR/venv/bin/pip" install -r "$BOT_DIR/requirements.txt" -q
|
"$BOT_DIR/venv/bin/pip" install -r "$BOT_DIR/requirements.txt" -q
|
||||||
|
|
||||||
# ── Конфигурация ─────────────────────────────────────────────────────────────
|
# ── Конфигурация ─────────────────────────────────────────────────────────────
|
||||||
if [ ! -f "$BOT_DIR/.env" ]; then
|
if [ ! -f "$BOT_DIR/.env" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${YELLOW}Введите BOT_TOKEN от @BotFather:${NC}"
|
echo -e "${YELLOW}Введите BOT_TOKEN от @BotFather:${NC}"
|
||||||
TOKEN=""
|
TOKEN=""
|
||||||
while [ -z "$TOKEN" ]; do
|
while [ -z "$TOKEN" ]; do
|
||||||
read -r TOKEN
|
read -r TOKEN
|
||||||
TOKEN=$(echo "$TOKEN" | tr -d '[:space:]')
|
TOKEN=$(echo "$TOKEN" | tr -d '[:space:]')
|
||||||
[ -z "$TOKEN" ] && echo -e "${RED}Токен не может быть пустым.${NC}"
|
[ -z "$TOKEN" ] && echo -e "${RED}Токен не может быть пустым.${NC}"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo -ne "${YELLOW}ID администратора (Enter = доступ для всех):${NC} "
|
echo -ne "${YELLOW}ID администратора (Enter = доступ для всех):${NC} "
|
||||||
read -r ADMIN_ID
|
read -r ADMIN_ID
|
||||||
|
|
||||||
{
|
{
|
||||||
echo "BOT_TOKEN=$TOKEN"
|
echo "BOT_TOKEN=$TOKEN"
|
||||||
[ -n "$ADMIN_ID" ] && echo "ALLOWED_IDS=$ADMIN_ID"
|
[ -n "$ADMIN_ID" ] && echo "ALLOWED_IDS=$ADMIN_ID"
|
||||||
} > "$BOT_DIR/.env"
|
} > "$BOT_DIR/.env"
|
||||||
|
|
||||||
chmod 600 "$BOT_DIR/.env"
|
chmod 600 "$BOT_DIR/.env"
|
||||||
echo -e "${GREEN}[*] .env создан${NC}"
|
echo -e "${GREEN}[*] .env создан${NC}"
|
||||||
else
|
else
|
||||||
echo -e "${GREEN}[*] .env уже существует${NC}"
|
echo -e "${GREEN}[*] .env уже существует${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Systemd ──────────────────────────────────────────────────────────────────
|
# ── Systemd ──────────────────────────────────────────────────────────────────
|
||||||
cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF
|
cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=GoTelegram v2.2 Telegram Bot
|
Description=GoTelegram v2.2.1 Telegram Bot
|
||||||
After=network.target
|
After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
WorkingDirectory=$BOT_DIR
|
WorkingDirectory=$BOT_DIR
|
||||||
ExecStart=$BOT_DIR/venv/bin/python $BOT_DIR/bot.py
|
ExecStart=$BOT_DIR/venv/bin/python $BOT_DIR/bot.py
|
||||||
Restart=always
|
Restart=always
|
||||||
RestartSec=5
|
RestartSec=5
|
||||||
Environment=PATH=$BOT_DIR/venv/bin:/usr/bin
|
Environment=PATH=$BOT_DIR/venv/bin:/usr/bin
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
systemctl enable "$SERVICE_NAME"
|
systemctl enable "$SERVICE_NAME"
|
||||||
systemctl restart "$SERVICE_NAME" 2>/dev/null || systemctl start "$SERVICE_NAME"
|
systemctl restart "$SERVICE_NAME" 2>/dev/null || systemctl start "$SERVICE_NAME"
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${GREEN}╔═══════════════════════════════════════════╗${NC}"
|
echo -e "${GREEN}╔═══════════════════════════════════════════╗${NC}"
|
||||||
echo -e "${GREEN}║ ✅ Бот установлен и запущен! ║${NC}"
|
echo -e "${GREEN}║ ✅ Бот установлен и запущен! ║${NC}"
|
||||||
echo -e "${GREEN}╚═══════════════════════════════════════════╝${NC}"
|
echo -e "${GREEN}╚═══════════════════════════════════════════╝${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "Проверка: ${CYAN}systemctl status $SERVICE_NAME${NC}"
|
echo -e "Проверка: ${CYAN}systemctl status $SERVICE_NAME${NC}"
|
||||||
echo -e "Логи: ${CYAN}journalctl -u $SERVICE_NAME -f${NC}"
|
echo -e "Логи: ${CYAN}journalctl -u $SERVICE_NAME -f${NC}"
|
||||||
echo -e "Настройки: ${CYAN}$BOT_DIR/.env${NC}"
|
echo -e "Настройки: ${CYAN}$BOT_DIR/.env${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Благодарности
|
# Благодарности
|
||||||
echo -e "${CYAN}─────────────────────────────────────────────${NC}"
|
echo -e "${CYAN}─────────────────────────────────────────────${NC}"
|
||||||
echo -e "💜 Спасибо авторам открытых проектов:"
|
echo -e "💜 Спасибо авторам открытых проектов:"
|
||||||
echo -e " ${CYAN}telemt${NC} — MTProxy engine (Rust)"
|
echo -e " ${CYAN}telemt${NC} — MTProxy engine (Rust)"
|
||||||
echo -e " ${CYAN}HTML5 UP${NC} — шаблоны сайтов (CC BY 3.0)"
|
echo -e " ${CYAN}HTML5 UP${NC} — шаблоны сайтов (CC BY 3.0)"
|
||||||
echo -e " ${CYAN}learning-zone${NC} — 150+ HTML5 шаблонов"
|
echo -e " ${CYAN}learning-zone${NC} — 150+ HTML5 шаблонов"
|
||||||
echo -e " ${CYAN}Start Bootstrap${NC} — Bootstrap шаблоны (MIT)"
|
echo -e " ${CYAN}Start Bootstrap${NC} — Bootstrap шаблоны (MIT)"
|
||||||
echo -e "${CYAN}─────────────────────────────────────────────${NC}"
|
echo -e "${CYAN}─────────────────────────────────────────────${NC}"
|
||||||
|
|||||||
682
lib/backup.sh
682
lib/backup.sh
@@ -1,341 +1,341 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# GoTelegram v2.2 — Бекап и восстановление конфигурации
|
# GoTelegram v2.2 — Бекап и восстановление конфигурации
|
||||||
|
|
||||||
# ── Создание бекапа ──────────────────────────────────────────────────────────
|
# ── Создание бекапа ──────────────────────────────────────────────────────────
|
||||||
create_backup() {
|
create_backup() {
|
||||||
local password="$1"
|
local password="$1"
|
||||||
local output_dir="${2:-$BACKUP_DIR}"
|
local output_dir="${2:-$BACKUP_DIR}"
|
||||||
local timestamp
|
local timestamp
|
||||||
timestamp=$(date +%Y%m%d_%H%M%S)
|
timestamp=$(date +%Y%m%d_%H%M%S)
|
||||||
local backup_name="gotelegram_backup_${timestamp}"
|
local backup_name="gotelegram_backup_${timestamp}"
|
||||||
local tmp_dir="/tmp/${backup_name}"
|
local tmp_dir="/tmp/${backup_name}"
|
||||||
|
|
||||||
mkdir -p "$tmp_dir" "$output_dir"
|
mkdir -p "$tmp_dir" "$output_dir"
|
||||||
|
|
||||||
# Собираем файлы
|
# Собираем файлы
|
||||||
log_info "Собираю конфигурацию..."
|
log_info "Собираю конфигурацию..."
|
||||||
|
|
||||||
# telemt конфиг
|
# telemt конфиг
|
||||||
if [ -f "$TELEMT_CONFIG" ]; then
|
if [ -f "$TELEMT_CONFIG" ]; then
|
||||||
cp "$TELEMT_CONFIG" "$tmp_dir/config.toml"
|
cp "$TELEMT_CONFIG" "$tmp_dir/config.toml"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# GoTelegram конфиг
|
# GoTelegram конфиг
|
||||||
if [ -f "$GOTELEGRAM_CONFIG" ]; then
|
if [ -f "$GOTELEGRAM_CONFIG" ]; then
|
||||||
cp "$GOTELEGRAM_CONFIG" "$tmp_dir/gotelegram.json"
|
cp "$GOTELEGRAM_CONFIG" "$tmp_dir/gotelegram.json"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# nginx конфиг (stealth mode)
|
# nginx конфиг (stealth mode)
|
||||||
if [ -f "$NGINX_SITE_CONF" ]; then
|
if [ -f "$NGINX_SITE_CONF" ]; then
|
||||||
cp "$NGINX_SITE_CONF" "$tmp_dir/nginx.conf"
|
cp "$NGINX_SITE_CONF" "$tmp_dir/nginx.conf"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SSL сертификаты
|
# SSL сертификаты
|
||||||
local domain
|
local domain
|
||||||
domain=$(config_get domain 2>/dev/null)
|
domain=$(config_get domain 2>/dev/null)
|
||||||
if [ -n "$domain" ] && [ -d "/etc/letsencrypt/live/$domain" ]; then
|
if [ -n "$domain" ] && [ -d "/etc/letsencrypt/live/$domain" ]; then
|
||||||
mkdir -p "$tmp_dir/certs"
|
mkdir -p "$tmp_dir/certs"
|
||||||
cp "/etc/letsencrypt/live/$domain/fullchain.pem" "$tmp_dir/certs/" 2>/dev/null
|
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
|
cp "/etc/letsencrypt/live/$domain/privkey.pem" "$tmp_dir/certs/" 2>/dev/null
|
||||||
log_dim "SSL сертификаты включены"
|
log_dim "SSL сертификаты включены"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Шаблон сайта (если есть)
|
# Шаблон сайта (если есть)
|
||||||
if [ -d "$WEBSITE_ROOT" ] && [ -f "$WEBSITE_ROOT/index.html" ]; then
|
if [ -d "$WEBSITE_ROOT" ] && [ -f "$WEBSITE_ROOT/index.html" ]; then
|
||||||
mkdir -p "$tmp_dir/site"
|
mkdir -p "$tmp_dir/site"
|
||||||
cp -r "$WEBSITE_ROOT"/* "$tmp_dir/site/"
|
cp -r "$WEBSITE_ROOT"/* "$tmp_dir/site/"
|
||||||
log_dim "Шаблон сайта включён"
|
log_dim "Шаблон сайта включён"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Метаданные
|
# Метаданные
|
||||||
local ip mode engine
|
local ip mode engine
|
||||||
ip=$(get_server_ip)
|
ip=$(get_server_ip)
|
||||||
mode=$(config_get mode 2>/dev/null || echo "unknown")
|
mode=$(config_get mode 2>/dev/null || echo "unknown")
|
||||||
engine=$(config_get engine 2>/dev/null || echo "telemt")
|
engine=$(config_get engine 2>/dev/null || echo "telemt")
|
||||||
|
|
||||||
cat > "$tmp_dir/metadata.json" << EOMETA
|
cat > "$tmp_dir/metadata.json" << EOMETA
|
||||||
{
|
{
|
||||||
"backup_version": "1.0",
|
"backup_version": "1.0",
|
||||||
"gotelegram_version": "$GOTELEGRAM_VERSION",
|
"gotelegram_version": "$GOTELEGRAM_VERSION",
|
||||||
"created_at": "$(date -Iseconds)",
|
"created_at": "$(date -Iseconds)",
|
||||||
"hostname": "$(hostname)",
|
"hostname": "$(hostname)",
|
||||||
"ip": "$ip",
|
"ip": "$ip",
|
||||||
"engine": "$engine",
|
"engine": "$engine",
|
||||||
"mode": "$mode",
|
"mode": "$mode",
|
||||||
"port": $(config_get port 2>/dev/null || echo "443"),
|
"port": $(config_get port 2>/dev/null || echo "443"),
|
||||||
"domain": "$(config_get domain 2>/dev/null)"
|
"domain": "$(config_get domain 2>/dev/null)"
|
||||||
}
|
}
|
||||||
EOMETA
|
EOMETA
|
||||||
|
|
||||||
# Архивируем
|
# Архивируем
|
||||||
local tar_file="/tmp/${backup_name}.tar.gz"
|
local tar_file="/tmp/${backup_name}.tar.gz"
|
||||||
if ! tar czf "$tar_file" -C /tmp "$backup_name" 2>/dev/null; then
|
if ! tar czf "$tar_file" -C /tmp "$backup_name" 2>/dev/null; then
|
||||||
log_error "Ошибка создания архива"
|
log_error "Ошибка создания архива"
|
||||||
rm -rf "$tmp_dir"
|
rm -rf "$tmp_dir"
|
||||||
rm -f "$tar_file"
|
rm -f "$tar_file"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -f "$tar_file" ]; then
|
if [ ! -f "$tar_file" ]; then
|
||||||
log_error "Архив не создан"
|
log_error "Архив не создан"
|
||||||
rm -rf "$tmp_dir"
|
rm -rf "$tmp_dir"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Шифруем если задан пароль
|
# Шифруем если задан пароль
|
||||||
local final_file=""
|
local final_file=""
|
||||||
if [ -n "$password" ]; then
|
if [ -n "$password" ]; then
|
||||||
final_file="${output_dir}/${backup_name}.tar.gz.enc"
|
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
|
openssl enc -aes-256-cbc -salt -pbkdf2 -in "$tar_file" -out "$final_file" -pass "pass:${password}" 2>/dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
log_error "Ошибка шифрования"
|
log_error "Ошибка шифрования"
|
||||||
rm -f "$tar_file"
|
rm -f "$tar_file"
|
||||||
rm -rf "$tmp_dir"
|
rm -rf "$tmp_dir"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
rm -f "$tar_file"
|
rm -f "$tar_file"
|
||||||
log_success "Бекап зашифрован (AES-256-CBC)"
|
log_success "Бекап зашифрован (AES-256-CBC)"
|
||||||
else
|
else
|
||||||
final_file="${output_dir}/${backup_name}.tar.gz"
|
final_file="${output_dir}/${backup_name}.tar.gz"
|
||||||
mv "$tar_file" "$final_file"
|
mv "$tar_file" "$final_file"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# SHA256 подпись
|
# SHA256 подпись
|
||||||
sha256sum "$final_file" > "${final_file}.sha256" 2>/dev/null
|
sha256sum "$final_file" > "${final_file}.sha256" 2>/dev/null
|
||||||
|
|
||||||
# Очистка
|
# Очистка
|
||||||
rm -rf "$tmp_dir"
|
rm -rf "$tmp_dir"
|
||||||
|
|
||||||
local size
|
local size
|
||||||
size=$(du -h "$final_file" | cut -f1)
|
size=$(du -h "$final_file" | cut -f1)
|
||||||
log_success "Бекап создан: $final_file ($size)"
|
log_success "Бекап создан: $final_file ($size)"
|
||||||
echo "$final_file"
|
echo "$final_file"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Восстановление из бекапа ────────────────────────────────────────────────
|
# ── Восстановление из бекапа ────────────────────────────────────────────────
|
||||||
restore_backup() {
|
restore_backup() {
|
||||||
local backup_file="$1"
|
local backup_file="$1"
|
||||||
local password="$2"
|
local password="$2"
|
||||||
|
|
||||||
if [ ! -f "$backup_file" ]; then
|
if [ ! -f "$backup_file" ]; then
|
||||||
log_error "Файл не найден: $backup_file"
|
log_error "Файл не найден: $backup_file"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local tmp_dir="/tmp/gotelegram_restore_$$"
|
local tmp_dir="/tmp/gotelegram_restore_$$"
|
||||||
mkdir -p "$tmp_dir"
|
mkdir -p "$tmp_dir"
|
||||||
|
|
||||||
# Расшифровываем если нужно
|
# Расшифровываем если нужно
|
||||||
local tar_file=""
|
local tar_file=""
|
||||||
if echo "$backup_file" | grep -q '\.enc$'; then
|
if echo "$backup_file" | grep -q '\.enc$'; then
|
||||||
if [ -z "$password" ]; then
|
if [ -z "$password" ]; then
|
||||||
echo -ne " Введите пароль от бекапа: "
|
echo -ne " Введите пароль от бекапа: "
|
||||||
read -rs password
|
read -rs password
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
tar_file="/tmp/gotelegram_restore_$$.tar.gz"
|
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
|
openssl enc -aes-256-cbc -d -pbkdf2 -in "$backup_file" -out "$tar_file" -pass "pass:${password}" 2>/dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
log_error "Неверный пароль или повреждённый файл"
|
log_error "Неверный пароль или повреждённый файл"
|
||||||
rm -rf "$tmp_dir" "$tar_file"
|
rm -rf "$tmp_dir" "$tar_file"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
tar_file="$backup_file"
|
tar_file="$backup_file"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Распаковываем
|
# Распаковываем
|
||||||
tar xzf "$tar_file" -C "$tmp_dir" 2>/dev/null
|
tar xzf "$tar_file" -C "$tmp_dir" 2>/dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
log_error "Ошибка распаковки архива"
|
log_error "Ошибка распаковки архива"
|
||||||
rm -rf "$tmp_dir"
|
rm -rf "$tmp_dir"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Находим папку бекапа
|
# Находим папку бекапа
|
||||||
local backup_dir
|
local backup_dir
|
||||||
backup_dir=$(find "$tmp_dir" -maxdepth 1 -type d -name "gotelegram_backup_*" | head -1)
|
backup_dir=$(find "$tmp_dir" -maxdepth 1 -type d -name "gotelegram_backup_*" | head -1)
|
||||||
[ -z "$backup_dir" ] && backup_dir="$tmp_dir"
|
[ -z "$backup_dir" ] && backup_dir="$tmp_dir"
|
||||||
|
|
||||||
# Проверяем метаданные
|
# Проверяем метаданные
|
||||||
if [ -f "$backup_dir/metadata.json" ]; then
|
if [ -f "$backup_dir/metadata.json" ]; then
|
||||||
local bk_version bk_mode bk_ip
|
local bk_version bk_mode bk_ip
|
||||||
bk_version=$(jq -r '.gotelegram_version // "unknown"' "$backup_dir/metadata.json")
|
bk_version=$(jq -r '.gotelegram_version // "unknown"' "$backup_dir/metadata.json")
|
||||||
bk_mode=$(jq -r '.mode // "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")
|
bk_ip=$(jq -r '.ip // "unknown"' "$backup_dir/metadata.json")
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}📦 Бекап:${NC}"
|
echo -e " ${BOLD}${WHITE}📦 Бекап:${NC}"
|
||||||
echo -e " Версия: $bk_version | Режим: $bk_mode | IP: $bk_ip"
|
echo -e " Версия: $bk_version | Режим: $bk_mode | IP: $bk_ip"
|
||||||
echo -e " Дата: $(jq -r '.created_at' "$backup_dir/metadata.json")"
|
echo -e " Дата: $(jq -r '.created_at' "$backup_dir/metadata.json")"
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! confirm "Восстановить конфигурацию? Текущие настройки будут перезаписаны."; then
|
if ! confirm "Восстановить конфигурацию? Текущие настройки будут перезаписаны."; then
|
||||||
rm -rf "$tmp_dir"
|
rm -rf "$tmp_dir"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Останавливаем сервисы
|
# Останавливаем сервисы
|
||||||
stop_telemt 2>/dev/null
|
stop_telemt 2>/dev/null
|
||||||
systemctl stop nginx 2>/dev/null
|
systemctl stop nginx 2>/dev/null
|
||||||
|
|
||||||
# Восстанавливаем telemt конфиг
|
# Восстанавливаем telemt конфиг
|
||||||
if [ -f "$backup_dir/config.toml" ]; then
|
if [ -f "$backup_dir/config.toml" ]; then
|
||||||
mkdir -p /etc/telemt
|
mkdir -p /etc/telemt
|
||||||
cp "$backup_dir/config.toml" "$TELEMT_CONFIG"
|
cp "$backup_dir/config.toml" "$TELEMT_CONFIG"
|
||||||
chmod 600 "$TELEMT_CONFIG"
|
chmod 600 "$TELEMT_CONFIG"
|
||||||
log_success "telemt конфиг восстановлен"
|
log_success "telemt конфиг восстановлен"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Восстанавливаем GoTelegram конфиг
|
# Восстанавливаем GoTelegram конфиг
|
||||||
if [ -f "$backup_dir/gotelegram.json" ]; then
|
if [ -f "$backup_dir/gotelegram.json" ]; then
|
||||||
mkdir -p "$GOTELEGRAM_DIR"
|
mkdir -p "$GOTELEGRAM_DIR"
|
||||||
cp "$backup_dir/gotelegram.json" "$GOTELEGRAM_CONFIG"
|
cp "$backup_dir/gotelegram.json" "$GOTELEGRAM_CONFIG"
|
||||||
log_success "GoTelegram конфиг восстановлен"
|
log_success "GoTelegram конфиг восстановлен"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Восстанавливаем nginx конфиг
|
# Восстанавливаем nginx конфиг
|
||||||
if [ -f "$backup_dir/nginx.conf" ]; then
|
if [ -f "$backup_dir/nginx.conf" ]; then
|
||||||
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
||||||
cp "$backup_dir/nginx.conf" "$NGINX_SITE_CONF"
|
cp "$backup_dir/nginx.conf" "$NGINX_SITE_CONF"
|
||||||
ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
||||||
log_success "nginx конфиг восстановлен"
|
log_success "nginx конфиг восстановлен"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Восстанавливаем SSL
|
# Восстанавливаем SSL
|
||||||
if [ -d "$backup_dir/certs" ]; then
|
if [ -d "$backup_dir/certs" ]; then
|
||||||
local domain
|
local domain
|
||||||
domain=$(config_get domain 2>/dev/null)
|
domain=$(config_get domain 2>/dev/null)
|
||||||
if [ -n "$domain" ]; then
|
if [ -n "$domain" ]; then
|
||||||
local cert_dir="/etc/letsencrypt/live/$domain"
|
local cert_dir="/etc/letsencrypt/live/$domain"
|
||||||
mkdir -p "$cert_dir"
|
mkdir -p "$cert_dir"
|
||||||
cp "$backup_dir/certs/"* "$cert_dir/" 2>/dev/null
|
cp "$backup_dir/certs/"* "$cert_dir/" 2>/dev/null
|
||||||
log_success "SSL сертификаты восстановлены"
|
log_success "SSL сертификаты восстановлены"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Восстанавливаем шаблон сайта
|
# Восстанавливаем шаблон сайта
|
||||||
if [ -d "$backup_dir/site" ]; then
|
if [ -d "$backup_dir/site" ]; then
|
||||||
mkdir -p "$WEBSITE_ROOT"
|
mkdir -p "$WEBSITE_ROOT"
|
||||||
cp -r "$backup_dir/site"/* "$WEBSITE_ROOT/"
|
cp -r "$backup_dir/site"/* "$WEBSITE_ROOT/"
|
||||||
chown -R www-data:www-data "$WEBSITE_ROOT" 2>/dev/null
|
chown -R www-data:www-data "$WEBSITE_ROOT" 2>/dev/null
|
||||||
log_success "Шаблон сайта восстановлен"
|
log_success "Шаблон сайта восстановлен"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Запускаем сервисы
|
# Запускаем сервисы
|
||||||
if is_telemt_installed; then
|
if is_telemt_installed; then
|
||||||
start_telemt
|
start_telemt
|
||||||
fi
|
fi
|
||||||
systemctl start nginx 2>/dev/null
|
systemctl start nginx 2>/dev/null
|
||||||
|
|
||||||
# Очистка
|
# Очистка
|
||||||
rm -rf "$tmp_dir"
|
rm -rf "$tmp_dir"
|
||||||
[ "$tar_file" != "$backup_file" ] && rm -f "$tar_file"
|
[ "$tar_file" != "$backup_file" ] && rm -f "$tar_file"
|
||||||
|
|
||||||
log_success "Восстановление завершено!"
|
log_success "Восстановление завершено!"
|
||||||
show_proxy_info
|
show_proxy_info
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Список бекапов ───────────────────────────────────────────────────────────
|
# ── Список бекапов ───────────────────────────────────────────────────────────
|
||||||
list_backups() {
|
list_backups() {
|
||||||
if [ ! -d "$BACKUP_DIR" ] || [ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then
|
if [ ! -d "$BACKUP_DIR" ] || [ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then
|
||||||
log_info "Бекапов нет"
|
log_info "Бекапов нет"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}📦 Доступные бекапы:${NC}"
|
echo -e " ${BOLD}${WHITE}📦 Доступные бекапы:${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
||||||
|
|
||||||
local i=1
|
local i=1
|
||||||
for f in "$BACKUP_DIR"/gotelegram_backup_*.tar.gz*; do
|
for f in "$BACKUP_DIR"/gotelegram_backup_*.tar.gz*; do
|
||||||
[ -f "$f" ] || continue
|
[ -f "$f" ] || continue
|
||||||
[[ "$f" == *.sha256 ]] && continue
|
[[ "$f" == *.sha256 ]] && continue
|
||||||
local size date_str name
|
local size date_str name
|
||||||
size=$(du -h "$f" | cut -f1)
|
size=$(du -h "$f" | cut -f1)
|
||||||
name=$(basename "$f")
|
name=$(basename "$f")
|
||||||
date_str=$(echo "$name" | grep -oE '[0-9]{8}_[0-9]{6}' | head -1)
|
date_str=$(echo "$name" | grep -oE '[0-9]{8}_[0-9]{6}' | head -1)
|
||||||
local encrypted=""
|
local encrypted=""
|
||||||
[[ "$f" == *.enc ]] && encrypted=" 🔒"
|
[[ "$f" == *.enc ]] && encrypted=" 🔒"
|
||||||
echo -e " ${CYAN}${i})${NC} ${name} (${size})${encrypted}"
|
echo -e " ${CYAN}${i})${NC} ${name} (${size})${encrypted}"
|
||||||
((i++))
|
((i++))
|
||||||
done
|
done
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Очистка старых бекапов ───────────────────────────────────────────────────
|
# ── Очистка старых бекапов ───────────────────────────────────────────────────
|
||||||
cleanup_old_backups() {
|
cleanup_old_backups() {
|
||||||
local keep="${1:-5}"
|
local keep="${1:-5}"
|
||||||
local count
|
local count
|
||||||
count=$(find "$BACKUP_DIR" -name "gotelegram_backup_*.tar.gz*" ! -name "*.sha256" 2>/dev/null | wc -l)
|
count=$(find "$BACKUP_DIR" -name "gotelegram_backup_*.tar.gz*" ! -name "*.sha256" 2>/dev/null | wc -l)
|
||||||
|
|
||||||
if [ "$count" -gt "$keep" ]; then
|
if [ "$count" -gt "$keep" ]; then
|
||||||
local to_delete=$((count - keep))
|
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
|
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"
|
rm -f "$f" "${f}.sha256"
|
||||||
done
|
done
|
||||||
log_dim "Удалено $to_delete старых бекапов (оставлено $keep)"
|
log_dim "Удалено $to_delete старых бекапов (оставлено $keep)"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Интерактивный бекап ──────────────────────────────────────────────────────
|
# ── Интерактивный бекап ──────────────────────────────────────────────────────
|
||||||
interactive_backup() {
|
interactive_backup() {
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}💾 Создание бекапа${NC}"
|
echo -e " ${BOLD}${WHITE}💾 Создание бекапа${NC}"
|
||||||
echo -ne " Зашифровать бекап паролем? [Y/n]: "
|
echo -ne " Зашифровать бекап паролем? [Y/n]: "
|
||||||
read -r use_pass
|
read -r use_pass
|
||||||
|
|
||||||
local password=""
|
local password=""
|
||||||
if [[ ! "$use_pass" =~ ^[Nn] ]]; then
|
if [[ ! "$use_pass" =~ ^[Nn] ]]; then
|
||||||
echo -ne " Введите пароль: "
|
echo -ne " Введите пароль: "
|
||||||
read -rs password
|
read -rs password
|
||||||
echo ""
|
echo ""
|
||||||
echo -ne " Повторите пароль: "
|
echo -ne " Повторите пароль: "
|
||||||
read -rs password2
|
read -rs password2
|
||||||
echo ""
|
echo ""
|
||||||
if [ "$password" != "$password2" ]; then
|
if [ "$password" != "$password2" ]; then
|
||||||
log_error "Пароли не совпадают"
|
log_error "Пароли не совпадают"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if [ ${#password} -lt 6 ]; then
|
if [ ${#password} -lt 6 ]; then
|
||||||
log_error "Пароль слишком короткий (минимум 6 символов)"
|
log_error "Пароль слишком короткий (минимум 6 символов)"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
create_backup "$password"
|
create_backup "$password"
|
||||||
cleanup_old_backups
|
cleanup_old_backups
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Интерактивное восстановление ─────────────────────────────────────────────
|
# ── Интерактивное восстановление ─────────────────────────────────────────────
|
||||||
interactive_restore() {
|
interactive_restore() {
|
||||||
list_backups || return 1
|
list_backups || return 1
|
||||||
|
|
||||||
echo -ne " Номер бекапа (или путь к файлу): "
|
echo -ne " Номер бекапа (или путь к файлу): "
|
||||||
read -r choice
|
read -r choice
|
||||||
|
|
||||||
local backup_file=""
|
local backup_file=""
|
||||||
if [[ "$choice" =~ ^[0-9]+$ ]]; then
|
if [[ "$choice" =~ ^[0-9]+$ ]]; then
|
||||||
local i=1
|
local i=1
|
||||||
for f in "$BACKUP_DIR"/gotelegram_backup_*.tar.gz*; do
|
for f in "$BACKUP_DIR"/gotelegram_backup_*.tar.gz*; do
|
||||||
[ -f "$f" ] || continue
|
[ -f "$f" ] || continue
|
||||||
[[ "$f" == *.sha256 ]] && continue
|
[[ "$f" == *.sha256 ]] && continue
|
||||||
if [ "$i" -eq "$choice" ]; then
|
if [ "$i" -eq "$choice" ]; then
|
||||||
backup_file="$f"
|
backup_file="$f"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
((i++))
|
((i++))
|
||||||
done
|
done
|
||||||
elif [ -f "$choice" ]; then
|
elif [ -f "$choice" ]; then
|
||||||
backup_file="$choice"
|
backup_file="$choice"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$backup_file" ]; then
|
if [ -z "$backup_file" ]; then
|
||||||
log_error "Бекап не найден"
|
log_error "Бекап не найден"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
restore_backup "$backup_file"
|
restore_backup "$backup_file"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,282 +1,282 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# GoTelegram v2.2 — Генерация TOML конфигурации для telemt
|
# GoTelegram v2.2 — Генерация TOML конфигурации для telemt
|
||||||
|
|
||||||
# ── Популярные домены (не заблокированные в РФ) ──────────────────────────────
|
# ── Популярные домены (не заблокированные в РФ) ──────────────────────────────
|
||||||
QUICK_DOMAINS=(
|
QUICK_DOMAINS=(
|
||||||
"google.com"
|
"google.com"
|
||||||
"microsoft.com"
|
"microsoft.com"
|
||||||
"cloudflare.com"
|
"cloudflare.com"
|
||||||
"apple.com"
|
"apple.com"
|
||||||
"amazon.com"
|
"amazon.com"
|
||||||
"github.com"
|
"github.com"
|
||||||
"stackoverflow.com"
|
"stackoverflow.com"
|
||||||
"medium.com"
|
"medium.com"
|
||||||
"wikipedia.org"
|
"wikipedia.org"
|
||||||
"coursera.org"
|
"coursera.org"
|
||||||
"udemy.com"
|
"udemy.com"
|
||||||
"habr.com"
|
"habr.com"
|
||||||
"stepik.org"
|
"stepik.org"
|
||||||
"duolingo.com"
|
"duolingo.com"
|
||||||
"khanacademy.org"
|
"khanacademy.org"
|
||||||
"bbc.com"
|
"bbc.com"
|
||||||
"reuters.com"
|
"reuters.com"
|
||||||
"nytimes.com"
|
"nytimes.com"
|
||||||
"ted.com"
|
"ted.com"
|
||||||
"zoom.us"
|
"zoom.us"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── Генерация TOML конфига ───────────────────────────────────────────────────
|
# ── Генерация TOML конфига ───────────────────────────────────────────────────
|
||||||
generate_telemt_toml() {
|
generate_telemt_toml() {
|
||||||
local secret="$1"
|
local secret="$1"
|
||||||
local port="${2:-443}"
|
local port="${2:-443}"
|
||||||
local mask_mode="${3:-quick}" # quick | stealth
|
local mask_mode="${3:-quick}" # quick | stealth
|
||||||
local mask_host="${4:-google.com}"
|
local mask_host="${4:-google.com}"
|
||||||
local mask_port="${5:-443}"
|
local mask_port="${5:-443}"
|
||||||
local output="${6:-$TELEMT_CONFIG}"
|
local output="${6:-$TELEMT_CONFIG}"
|
||||||
|
|
||||||
mkdir -p "$(dirname "$output")"
|
mkdir -p "$(dirname "$output")"
|
||||||
|
|
||||||
cat > "$output" << EOTOML
|
cat > "$output" << EOTOML
|
||||||
# GoTelegram v${GOTELEGRAM_VERSION} — telemt configuration
|
# GoTelegram v${GOTELEGRAM_VERSION} — telemt configuration
|
||||||
# Сгенерировано: $(date -Iseconds)
|
# Сгенерировано: $(date -Iseconds)
|
||||||
# Режим: ${mask_mode}
|
# Режим: ${mask_mode}
|
||||||
|
|
||||||
# ── Основные настройки ───────────────────────────────────────────────────────
|
# ── Основные настройки ───────────────────────────────────────────────────────
|
||||||
[stats]
|
[stats]
|
||||||
statsd_address = ""
|
statsd_address = ""
|
||||||
|
|
||||||
# ── Секреты ──────────────────────────────────────────────────────────────────
|
# ── Секреты ──────────────────────────────────────────────────────────────────
|
||||||
[[users]]
|
[[users]]
|
||||||
name = "main"
|
name = "main"
|
||||||
secret = "${secret}"
|
secret = "${secret}"
|
||||||
|
|
||||||
# ── Привязка ─────────────────────────────────────────────────────────────────
|
# ── Привязка ─────────────────────────────────────────────────────────────────
|
||||||
[listen]
|
[listen]
|
||||||
bind_to = "0.0.0.0:${port}"
|
bind_to = "0.0.0.0:${port}"
|
||||||
|
|
||||||
# ── TLS маскировка ───────────────────────────────────────────────────────────
|
# ── TLS маскировка ───────────────────────────────────────────────────────────
|
||||||
[security]
|
[security]
|
||||||
# Маскировочный хост — куда перенаправлять неопознанные подключения
|
# Маскировочный хост — куда перенаправлять неопознанные подключения
|
||||||
# quick: внешний сайт | stealth: локальный nginx
|
# quick: внешний сайт | stealth: локальный nginx
|
||||||
host = "${mask_host}:${mask_port}"
|
host = "${mask_host}:${mask_port}"
|
||||||
|
|
||||||
EOTOML
|
EOTOML
|
||||||
|
|
||||||
chmod 600 "$output"
|
chmod 600 "$output"
|
||||||
log_success "Конфиг telemt записан: $output"
|
log_success "Конфиг telemt записан: $output"
|
||||||
log_dim "Режим: $mask_mode, маскировка: $mask_host:$mask_port"
|
log_dim "Режим: $mask_mode, маскировка: $mask_host:$mask_port"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Добавление дополнительного секрета ───────────────────────────────────────
|
# ── Добавление дополнительного секрета ───────────────────────────────────────
|
||||||
add_secret_to_config() {
|
add_secret_to_config() {
|
||||||
local name="$1"
|
local name="$1"
|
||||||
local secret="$2"
|
local secret="$2"
|
||||||
local config="${3:-$TELEMT_CONFIG}"
|
local config="${3:-$TELEMT_CONFIG}"
|
||||||
|
|
||||||
if [ ! -f "$config" ]; then
|
if [ ! -f "$config" ]; then
|
||||||
log_error "Конфиг не найден: $config"
|
log_error "Конфиг не найден: $config"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Добавляем новый блок [[users]]
|
# Добавляем новый блок [[users]]
|
||||||
cat >> "$config" << EOSECRET
|
cat >> "$config" << EOSECRET
|
||||||
|
|
||||||
[[users]]
|
[[users]]
|
||||||
name = "${name}"
|
name = "${name}"
|
||||||
secret = "${secret}"
|
secret = "${secret}"
|
||||||
EOSECRET
|
EOSECRET
|
||||||
|
|
||||||
log_success "Добавлен секрет: $name"
|
log_success "Добавлен секрет: $name"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Чтение текущего конфига ──────────────────────────────────────────────────
|
# ── Чтение текущего конфига ──────────────────────────────────────────────────
|
||||||
get_config_value() {
|
get_config_value() {
|
||||||
local key="$1"
|
local key="$1"
|
||||||
local config="${2:-$TELEMT_CONFIG}"
|
local config="${2:-$TELEMT_CONFIG}"
|
||||||
|
|
||||||
if [ ! -f "$config" ]; then return 1; fi
|
if [ ! -f "$config" ]; then return 1; fi
|
||||||
|
|
||||||
case "$key" in
|
case "$key" in
|
||||||
secret)
|
secret)
|
||||||
grep -m1 'secret\s*=' "$config" | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' '
|
grep -m1 'secret\s*=' "$config" | sed 's/.*=\s*"\(.*\)"/\1/' | tr -d ' '
|
||||||
;;
|
;;
|
||||||
port)
|
port)
|
||||||
grep 'bind_to\s*=' "$config" | sed 's/.*:\([0-9]*\)".*/\1/'
|
grep 'bind_to\s*=' "$config" | sed 's/.*:\([0-9]*\)".*/\1/'
|
||||||
;;
|
;;
|
||||||
mask_host)
|
mask_host)
|
||||||
grep -A10 '\[security\]' "$config" | grep 'host\s*=' | sed 's/.*=\s*"\(.*\)".*/\1/'
|
grep -A10 '\[security\]' "$config" | grep 'host\s*=' | sed 's/.*=\s*"\(.*\)".*/\1/'
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
grep "$key" "$config" | head -1 | sed 's/.*=\s*"\?\(.*\)"\?/\1/' | tr -d ' "'
|
grep "$key" "$config" | head -1 | sed 's/.*=\s*"\?\(.*\)"\?/\1/' | tr -d ' "'
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Валидация конфига ────────────────────────────────────────────────────────
|
# ── Валидация конфига ────────────────────────────────────────────────────────
|
||||||
validate_telemt_config() {
|
validate_telemt_config() {
|
||||||
local config="${1:-$TELEMT_CONFIG}"
|
local config="${1:-$TELEMT_CONFIG}"
|
||||||
|
|
||||||
if [ ! -f "$config" ]; then
|
if [ ! -f "$config" ]; then
|
||||||
log_error "Конфиг не найден: $config"
|
log_error "Конфиг не найден: $config"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Проверяем обязательные поля
|
# Проверяем обязательные поля
|
||||||
local secret port host
|
local secret port host
|
||||||
secret=$(get_config_value secret "$config")
|
secret=$(get_config_value secret "$config")
|
||||||
port=$(get_config_value port "$config")
|
port=$(get_config_value port "$config")
|
||||||
host=$(get_config_value mask_host "$config")
|
host=$(get_config_value mask_host "$config")
|
||||||
|
|
||||||
local errors=0
|
local errors=0
|
||||||
|
|
||||||
if [ -z "$secret" ]; then
|
if [ -z "$secret" ]; then
|
||||||
log_error "Не задан secret"
|
log_error "Не задан secret"
|
||||||
((errors++))
|
((errors++))
|
||||||
elif [ ${#secret} -lt 32 ]; then
|
elif [ ${#secret} -lt 32 ]; then
|
||||||
log_warning "Secret слишком короткий (${#secret} символов, рекомендуется 32+)"
|
log_warning "Secret слишком короткий (${#secret} символов, рекомендуется 32+)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$port" ]; then
|
if [ -z "$port" ]; then
|
||||||
log_error "Не задан порт (bind_to)"
|
log_error "Не задан порт (bind_to)"
|
||||||
((errors++))
|
((errors++))
|
||||||
elif [ "$port" -lt 1 ] || [ "$port" -gt 65535 ] 2>/dev/null; then
|
elif [ "$port" -lt 1 ] || [ "$port" -gt 65535 ] 2>/dev/null; then
|
||||||
log_error "Порт вне диапазона: $port"
|
log_error "Порт вне диапазона: $port"
|
||||||
((errors++))
|
((errors++))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$host" ]; then
|
if [ -z "$host" ]; then
|
||||||
log_error "Не задан маскировочный хост (security.host)"
|
log_error "Не задан маскировочный хост (security.host)"
|
||||||
((errors++))
|
((errors++))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ $errors -gt 0 ]; then
|
if [ $errors -gt 0 ]; then
|
||||||
log_error "Найдено ошибок: $errors"
|
log_error "Найдено ошибок: $errors"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_success "Конфиг валиден"
|
log_success "Конфиг валиден"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Выбор домена (интерактивный) ─────────────────────────────────────────────
|
# ── Выбор домена (интерактивный) ─────────────────────────────────────────────
|
||||||
select_quick_domain() {
|
select_quick_domain() {
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}🌐 Выберите домен для маскировки (Fake TLS):${NC}"
|
echo -e " ${BOLD}${WHITE}🌐 Выберите домен для маскировки (Fake TLS):${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
||||||
|
|
||||||
local i=1
|
local i=1
|
||||||
local row=""
|
local row=""
|
||||||
for d in "${QUICK_DOMAINS[@]}"; do
|
for d in "${QUICK_DOMAINS[@]}"; do
|
||||||
printf " ${CYAN}%2d)${NC} %-25s" "$i" "$d"
|
printf " ${CYAN}%2d)${NC} %-25s" "$i" "$d"
|
||||||
if (( i % 2 == 0 )); then
|
if (( i % 2 == 0 )); then
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
((i++))
|
((i++))
|
||||||
done
|
done
|
||||||
if (( (i-1) % 2 != 0 )); then echo ""; fi
|
if (( (i-1) % 2 != 0 )); then echo ""; fi
|
||||||
|
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
||||||
echo -ne " ${WHITE}Выбор (1-${#QUICK_DOMAINS[@]}):${NC} "
|
echo -ne " ${WHITE}Выбор (1-${#QUICK_DOMAINS[@]}):${NC} "
|
||||||
read -r choice
|
read -r choice
|
||||||
|
|
||||||
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#QUICK_DOMAINS[@]} ]; then
|
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#QUICK_DOMAINS[@]} ]; then
|
||||||
echo "${QUICK_DOMAINS[$((choice-1))]}"
|
echo "${QUICK_DOMAINS[$((choice-1))]}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_error "Неверный выбор"
|
log_error "Неверный выбор"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Выбор порта (интерактивный) ──────────────────────────────────────────────
|
# ── Выбор порта (интерактивный) ──────────────────────────────────────────────
|
||||||
select_port() {
|
select_port() {
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}🔌 Выберите порт:${NC}"
|
echo -e " ${BOLD}${WHITE}🔌 Выберите порт:${NC}"
|
||||||
|
|
||||||
# Проверяем стандартные порты
|
# Проверяем стандартные порты
|
||||||
local busy_443 busy_8443
|
local busy_443 busy_8443
|
||||||
busy_443=$(check_port 443)
|
busy_443=$(check_port 443)
|
||||||
busy_8443=$(check_port 8443)
|
busy_8443=$(check_port 8443)
|
||||||
|
|
||||||
local label_443="443 (рекомендуется)"
|
local label_443="443 (рекомендуется)"
|
||||||
local label_8443="8443"
|
local label_8443="8443"
|
||||||
[ -n "$busy_443" ] && label_443="443 ⚠️ занят"
|
[ -n "$busy_443" ] && label_443="443 ⚠️ занят"
|
||||||
[ -n "$busy_8443" ] && label_8443="8443 ⚠️ занят"
|
[ -n "$busy_8443" ] && label_8443="8443 ⚠️ занят"
|
||||||
|
|
||||||
echo -e " ${CYAN}1)${NC} $label_443"
|
echo -e " ${CYAN}1)${NC} $label_443"
|
||||||
echo -e " ${CYAN}2)${NC} $label_8443"
|
echo -e " ${CYAN}2)${NC} $label_8443"
|
||||||
echo -e " ${CYAN}3)${NC} Свой порт"
|
echo -e " ${CYAN}3)${NC} Свой порт"
|
||||||
|
|
||||||
if [ -n "$busy_443" ]; then
|
if [ -n "$busy_443" ]; then
|
||||||
echo -e " ${DIM} ⚠ Порт 443 занят: $(echo "$busy_443" | head -c 60)${NC}"
|
echo -e " ${DIM} ⚠ Порт 443 занят: $(echo "$busy_443" | head -c 60)${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -ne " ${WHITE}Выбор:${NC} "
|
echo -ne " ${WHITE}Выбор:${NC} "
|
||||||
read -r choice
|
read -r choice
|
||||||
|
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
1) echo "443" ;;
|
1) echo "443" ;;
|
||||||
2) echo "8443" ;;
|
2) echo "8443" ;;
|
||||||
3)
|
3)
|
||||||
echo -ne " Введите порт (1-65535): "
|
echo -ne " Введите порт (1-65535): "
|
||||||
read -r custom_port
|
read -r custom_port
|
||||||
if [[ "$custom_port" =~ ^[0-9]+$ ]] && [ "$custom_port" -ge 1 ] && [ "$custom_port" -le 65535 ]; then
|
if [[ "$custom_port" =~ ^[0-9]+$ ]] && [ "$custom_port" -ge 1 ] && [ "$custom_port" -le 65535 ]; then
|
||||||
echo "$custom_port"
|
echo "$custom_port"
|
||||||
else
|
else
|
||||||
log_error "Неверный порт"
|
log_error "Неверный порт"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
*) echo "443" ;;
|
*) echo "443" ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Генерация ссылки tg://proxy ──────────────────────────────────────────────
|
# ── Генерация ссылки tg://proxy ──────────────────────────────────────────────
|
||||||
generate_proxy_link() {
|
generate_proxy_link() {
|
||||||
local ip="${1:-$(get_server_ip)}"
|
local ip="${1:-$(get_server_ip)}"
|
||||||
local port="${2:-443}"
|
local port="${2:-443}"
|
||||||
local secret="$3"
|
local secret="$3"
|
||||||
echo "tg://proxy?server=${ip}&port=${port}&secret=${secret}"
|
echo "tg://proxy?server=${ip}&port=${port}&secret=${secret}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Вывод информации о прокси ────────────────────────────────────────────────
|
# ── Вывод информации о прокси ────────────────────────────────────────────────
|
||||||
show_proxy_info() {
|
show_proxy_info() {
|
||||||
local config="${1:-$TELEMT_CONFIG}"
|
local config="${1:-$TELEMT_CONFIG}"
|
||||||
local secret port mask_host ip link status
|
local secret port mask_host ip link status
|
||||||
|
|
||||||
secret=$(get_config_value secret "$config")
|
secret=$(get_config_value secret "$config")
|
||||||
port=$(get_config_value port "$config")
|
port=$(get_config_value port "$config")
|
||||||
mask_host=$(get_config_value mask_host "$config")
|
mask_host=$(get_config_value mask_host "$config")
|
||||||
ip=$(get_server_ip)
|
ip=$(get_server_ip)
|
||||||
link=$(generate_proxy_link "$ip" "$port" "$secret")
|
link=$(generate_proxy_link "$ip" "$port" "$secret")
|
||||||
status=$(telemt_status)
|
status=$(telemt_status)
|
||||||
|
|
||||||
local mode
|
local mode
|
||||||
mode=$(config_get mode 2>/dev/null || echo "quick")
|
mode=$(config_get mode 2>/dev/null || echo "quick")
|
||||||
|
|
||||||
local status_icon status_text
|
local status_icon status_text
|
||||||
case "$status" in
|
case "$status" in
|
||||||
running) status_icon="✅"; status_text="Работает" ;;
|
running) status_icon="✅"; status_text="Работает" ;;
|
||||||
stopped) status_icon="⏸️"; status_text="Остановлен" ;;
|
stopped) status_icon="⏸️"; status_text="Остановлен" ;;
|
||||||
*) status_icon="❌"; status_text="Не установлен" ;;
|
*) status_icon="❌"; status_text="Не установлен" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}${status_icon} Статус прокси: ${status_text}${NC}"
|
echo -e " ${BOLD}${WHITE}${status_icon} Статус прокси: ${status_text}${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
||||||
echo -e " ${WHITE}Ядро:${NC} telemt (Rust)"
|
echo -e " ${WHITE}Ядро:${NC} telemt (Rust)"
|
||||||
echo -e " ${WHITE}IP:${NC} ${CYAN}${ip}${NC}"
|
echo -e " ${WHITE}IP:${NC} ${CYAN}${ip}${NC}"
|
||||||
echo -e " ${WHITE}Порт:${NC} ${CYAN}${port}${NC}"
|
echo -e " ${WHITE}Порт:${NC} ${CYAN}${port}${NC}"
|
||||||
echo -e " ${WHITE}Режим:${NC} ${CYAN}${mode}${NC}"
|
echo -e " ${WHITE}Режим:${NC} ${CYAN}${mode}${NC}"
|
||||||
echo -e " ${WHITE}Маскировка:${NC} ${CYAN}${mask_host}${NC}"
|
echo -e " ${WHITE}Маскировка:${NC} ${CYAN}${mask_host}${NC}"
|
||||||
echo -e " ${WHITE}Secret:${NC} ${CYAN}${secret:0:16}...${NC}"
|
echo -e " ${WHITE}Secret:${NC} ${CYAN}${secret:0:16}...${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
|
||||||
echo -e " ${WHITE}Ссылка:${NC}"
|
echo -e " ${WHITE}Ссылка:${NC}"
|
||||||
echo -e " ${GREEN}${link}${NC}"
|
echo -e " ${GREEN}${link}${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# QR если доступен
|
# QR если доступен
|
||||||
if command -v qrencode &>/dev/null; then
|
if command -v qrencode &>/dev/null; then
|
||||||
qrencode -t UTF8 -m 2 "$link" 2>/dev/null
|
qrencode -t UTF8 -m 2 "$link" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,280 +1,280 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# GoTelegram v2.2 — Каталог шаблонов сайтов
|
# GoTelegram v2.2 — Каталог шаблонов сайтов
|
||||||
# Выбор из ~200 шаблонов, превью-ссылки, скачивание через git sparse-checkout
|
# Выбор из ~200 шаблонов, превью-ссылки, скачивание через git sparse-checkout
|
||||||
|
|
||||||
CATALOG_FILE="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")/templates_catalog.json"
|
CATALOG_FILE="$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")/templates_catalog.json"
|
||||||
TEMPLATES_CACHE="/tmp/gotelegram_templates"
|
TEMPLATES_CACHE="/tmp/gotelegram_templates"
|
||||||
|
|
||||||
# ── Загрузка каталога ────────────────────────────────────────────────────────
|
# ── Загрузка каталога ────────────────────────────────────────────────────────
|
||||||
load_catalog() {
|
load_catalog() {
|
||||||
if [ ! -f "$CATALOG_FILE" ]; then
|
if [ ! -f "$CATALOG_FILE" ]; then
|
||||||
log_error "Каталог шаблонов не найден: $CATALOG_FILE"
|
log_error "Каталог шаблонов не найден: $CATALOG_FILE"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Категории ────────────────────────────────────────────────────────────────
|
# ── Категории ────────────────────────────────────────────────────────────────
|
||||||
get_categories() {
|
get_categories() {
|
||||||
jq -r '.categories[] | "\(.id)|\(.name)|\(.icon)|\(.templates | length)"' "$CATALOG_FILE" 2>/dev/null
|
jq -r '.categories[] | "\(.id)|\(.name)|\(.icon)|\(.templates | length)"' "$CATALOG_FILE" 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
get_category_name() {
|
get_category_name() {
|
||||||
local cat_id="$1"
|
local cat_id="$1"
|
||||||
jq -r ".categories[] | select(.id == \"$cat_id\") | .name" "$CATALOG_FILE" 2>/dev/null
|
jq -r ".categories[] | select(.id == \"$cat_id\") | .name" "$CATALOG_FILE" 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Шаблоны по категории ────────────────────────────────────────────────────
|
# ── Шаблоны по категории ────────────────────────────────────────────────────
|
||||||
get_templates_by_category() {
|
get_templates_by_category() {
|
||||||
local cat_id="$1"
|
local cat_id="$1"
|
||||||
jq -r ".categories[] | select(.id == \"$cat_id\") | .templates[] | \"\(.id)|\(.name)|\(.source)|\(.preview_url)\"" "$CATALOG_FILE" 2>/dev/null
|
jq -r ".categories[] | select(.id == \"$cat_id\") | .templates[] | \"\(.id)|\(.name)|\(.source)|\(.preview_url)\"" "$CATALOG_FILE" 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Информация о шаблоне ────────────────────────────────────────────────────
|
# ── Информация о шаблоне ────────────────────────────────────────────────────
|
||||||
get_template_info() {
|
get_template_info() {
|
||||||
local tpl_id="$1"
|
local tpl_id="$1"
|
||||||
jq ".categories[].templates[] | select(.id == \"$tpl_id\")" "$CATALOG_FILE" 2>/dev/null
|
jq ".categories[].templates[] | select(.id == \"$tpl_id\")" "$CATALOG_FILE" 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
get_template_field() {
|
get_template_field() {
|
||||||
local tpl_id="$1"
|
local tpl_id="$1"
|
||||||
local field="$2"
|
local field="$2"
|
||||||
jq -r ".categories[].templates[] | select(.id == \"$tpl_id\") | .$field" "$CATALOG_FILE" 2>/dev/null
|
jq -r ".categories[].templates[] | select(.id == \"$tpl_id\") | .$field" "$CATALOG_FILE" 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Интерактивный выбор категории ────────────────────────────────────────────
|
# ── Интерактивный выбор категории ────────────────────────────────────────────
|
||||||
select_category() {
|
select_category() {
|
||||||
load_catalog || return 1
|
load_catalog || return 1
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}📂 Категории шаблонов сайтов:${NC}"
|
echo -e " ${BOLD}${WHITE}📂 Категории шаблонов сайтов:${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
|
|
||||||
local cats=()
|
local cats=()
|
||||||
local i=1
|
local i=1
|
||||||
while IFS='|' read -r id name icon count; do
|
while IFS='|' read -r id name icon count; do
|
||||||
[ "$count" -eq 0 ] && continue
|
[ "$count" -eq 0 ] && continue
|
||||||
local emoji
|
local emoji
|
||||||
case "$icon" in
|
case "$icon" in
|
||||||
briefcase) emoji="🏢" ;;
|
briefcase) emoji="🏢" ;;
|
||||||
shopping-cart) emoji="🛒" ;;
|
shopping-cart) emoji="🛒" ;;
|
||||||
heart) emoji="🏥" ;;
|
heart) emoji="🏥" ;;
|
||||||
book) emoji="🎓" ;;
|
book) emoji="🎓" ;;
|
||||||
palette) emoji="📸" ;;
|
palette) emoji="📸" ;;
|
||||||
home) emoji="🏠" ;;
|
home) emoji="🏠" ;;
|
||||||
utensils) emoji="🍕" ;;
|
utensils) emoji="🍕" ;;
|
||||||
rocket) emoji="🎨" ;;
|
rocket) emoji="🎨" ;;
|
||||||
chart-bar) emoji="🔧" ;;
|
chart-bar) emoji="🔧" ;;
|
||||||
*) emoji="📄" ;;
|
*) emoji="📄" ;;
|
||||||
esac
|
esac
|
||||||
printf " ${CYAN}%2d)${NC} ${emoji} %-30s ${DIM}(%d шаблонов)${NC}\n" "$i" "$name" "$count"
|
printf " ${CYAN}%2d)${NC} ${emoji} %-30s ${DIM}(%d шаблонов)${NC}\n" "$i" "$name" "$count"
|
||||||
cats+=("$id")
|
cats+=("$id")
|
||||||
((i++))
|
((i++))
|
||||||
done < <(get_categories)
|
done < <(get_categories)
|
||||||
|
|
||||||
printf " ${CYAN}%2d)${NC} 🎲 Случайный шаблон\n" "$i"
|
printf " ${CYAN}%2d)${NC} 🎲 Случайный шаблон\n" "$i"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
echo -ne " ${WHITE}Выбор:${NC} "
|
echo -ne " ${WHITE}Выбор:${NC} "
|
||||||
read -r choice
|
read -r choice
|
||||||
|
|
||||||
# Случайный
|
# Случайный
|
||||||
if [ "$choice" -eq "$i" ] 2>/dev/null; then
|
if [ "$choice" -eq "$i" ] 2>/dev/null; then
|
||||||
local random_cat="${cats[$((RANDOM % ${#cats[@]}))]}"
|
local random_cat="${cats[$((RANDOM % ${#cats[@]}))]}"
|
||||||
echo "$random_cat"
|
echo "$random_cat"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then
|
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then
|
||||||
echo "${cats[$((choice-1))]}"
|
echo "${cats[$((choice-1))]}"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_error "Неверный выбор"
|
log_error "Неверный выбор"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Интерактивный выбор шаблона ──────────────────────────────────────────────
|
# ── Интерактивный выбор шаблона ──────────────────────────────────────────────
|
||||||
select_template() {
|
select_template() {
|
||||||
local cat_id="$1"
|
local cat_id="$1"
|
||||||
local cat_name
|
local cat_name
|
||||||
cat_name=$(get_category_name "$cat_id")
|
cat_name=$(get_category_name "$cat_id")
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}📋 $cat_name — доступные шаблоны:${NC}"
|
echo -e " ${BOLD}${WHITE}📋 $cat_name — доступные шаблоны:${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
||||||
|
|
||||||
local tpls=()
|
local tpls=()
|
||||||
local i=1
|
local i=1
|
||||||
while IFS='|' read -r id name source preview; do
|
while IFS='|' read -r id name source preview; do
|
||||||
printf " ${CYAN}%2d)${NC} %-30s ${DIM}[%s]${NC}\n" "$i" "$name" "$source"
|
printf " ${CYAN}%2d)${NC} %-30s ${DIM}[%s]${NC}\n" "$i" "$name" "$source"
|
||||||
tpls+=("$id")
|
tpls+=("$id")
|
||||||
((i++))
|
((i++))
|
||||||
done < <(get_templates_by_category "$cat_id")
|
done < <(get_templates_by_category "$cat_id")
|
||||||
|
|
||||||
if [ ${#tpls[@]} -eq 0 ]; then
|
if [ ${#tpls[@]} -eq 0 ]; then
|
||||||
log_info "В этой категории нет шаблонов"
|
log_info "В этой категории нет шаблонов"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
||||||
echo -ne " ${WHITE}Выбор (1-$((i-1))):${NC} "
|
echo -ne " ${WHITE}Выбор (1-$((i-1))):${NC} "
|
||||||
read -r choice
|
read -r choice
|
||||||
|
|
||||||
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then
|
if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -lt "$i" ]; then
|
||||||
local selected_id="${tpls[$((choice-1))]}"
|
local selected_id="${tpls[$((choice-1))]}"
|
||||||
|
|
||||||
# Показываем превью
|
# Показываем превью
|
||||||
show_template_preview "$selected_id"
|
show_template_preview "$selected_id"
|
||||||
|
|
||||||
echo "$selected_id"
|
echo "$selected_id"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_error "Неверный выбор"
|
log_error "Неверный выбор"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Показ превью шаблона ────────────────────────────────────────────────────
|
# ── Показ превью шаблона ────────────────────────────────────────────────────
|
||||||
show_template_preview() {
|
show_template_preview() {
|
||||||
local tpl_id="$1"
|
local tpl_id="$1"
|
||||||
local info
|
local info
|
||||||
info=$(get_template_info "$tpl_id")
|
info=$(get_template_info "$tpl_id")
|
||||||
|
|
||||||
local name source preview_url repo_url description
|
local name source preview_url repo_url description
|
||||||
name=$(echo "$info" | jq -r '.name')
|
name=$(echo "$info" | jq -r '.name')
|
||||||
source=$(echo "$info" | jq -r '.source')
|
source=$(echo "$info" | jq -r '.source')
|
||||||
preview_url=$(echo "$info" | jq -r '.preview_url // empty')
|
preview_url=$(echo "$info" | jq -r '.preview_url // empty')
|
||||||
repo_url=$(echo "$info" | jq -r '.repo_url // empty')
|
repo_url=$(echo "$info" | jq -r '.repo_url // empty')
|
||||||
description=$(echo "$info" | jq -r '.description // "—"')
|
description=$(echo "$info" | jq -r '.description // "—"')
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${BOLD}${WHITE}🔍 Превью шаблона:${NC}"
|
echo -e " ${BOLD}${WHITE}🔍 Превью шаблона:${NC}"
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
echo -e " ${WHITE}Название:${NC} $name"
|
echo -e " ${WHITE}Название:${NC} $name"
|
||||||
echo -e " ${WHITE}Источник:${NC} $source"
|
echo -e " ${WHITE}Источник:${NC} $source"
|
||||||
echo -e " ${WHITE}Описание:${NC} $description"
|
echo -e " ${WHITE}Описание:${NC} $description"
|
||||||
|
|
||||||
if [ -n "$preview_url" ]; then
|
if [ -n "$preview_url" ]; then
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${GREEN}👁 Превью:${NC} ${CYAN}${preview_url}${NC}"
|
echo -e " ${GREEN}👁 Превью:${NC} ${CYAN}${preview_url}${NC}"
|
||||||
echo -e " ${DIM}Откройте ссылку в браузере для просмотра шаблона${NC}"
|
echo -e " ${DIM}Откройте ссылку в браузере для просмотра шаблона${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$repo_url" ]; then
|
if [ -n "$repo_url" ]; then
|
||||||
echo -e " ${DIM}📦 Репо: ${repo_url}${NC}"
|
echo -e " ${DIM}📦 Репо: ${repo_url}${NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Благодарность автору
|
# Благодарность автору
|
||||||
echo ""
|
echo ""
|
||||||
echo -e " ${MAGENTA}💜 Спасибо авторам ${source} за открытый код!${NC}"
|
echo -e " ${MAGENTA}💜 Спасибо авторам ${source} за открытый код!${NC}"
|
||||||
|
|
||||||
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
echo -e " ${DIM}$(printf '─%.0s' {1..55})${NC}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if ! confirm "Установить этот шаблон?"; then
|
if ! confirm "Установить этот шаблон?"; then
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Скачивание шаблона ───────────────────────────────────────────────────────
|
# ── Скачивание шаблона ───────────────────────────────────────────────────────
|
||||||
download_template() {
|
download_template() {
|
||||||
local tpl_id="$1"
|
local tpl_id="$1"
|
||||||
local output_dir="${2:-$TEMPLATES_CACHE}"
|
local output_dir="${2:-$TEMPLATES_CACHE}"
|
||||||
local info
|
local info
|
||||||
info=$(get_template_info "$tpl_id")
|
info=$(get_template_info "$tpl_id")
|
||||||
|
|
||||||
local repo_url sparse_path source name
|
local repo_url sparse_path source name
|
||||||
repo_url=$(echo "$info" | jq -r '.repo_url')
|
repo_url=$(echo "$info" | jq -r '.repo_url')
|
||||||
sparse_path=$(echo "$info" | jq -r '.sparse_path')
|
sparse_path=$(echo "$info" | jq -r '.sparse_path')
|
||||||
source=$(echo "$info" | jq -r '.source')
|
source=$(echo "$info" | jq -r '.source')
|
||||||
name=$(echo "$info" | jq -r '.name')
|
name=$(echo "$info" | jq -r '.name')
|
||||||
|
|
||||||
local clone_dir="$output_dir/${tpl_id}"
|
local clone_dir="$output_dir/${tpl_id}"
|
||||||
rm -rf "$clone_dir"
|
rm -rf "$clone_dir"
|
||||||
mkdir -p "$clone_dir"
|
mkdir -p "$clone_dir"
|
||||||
|
|
||||||
log_info "Скачивание шаблона \"$name\"..."
|
log_info "Скачивание шаблона \"$name\"..."
|
||||||
|
|
||||||
# Для HTML5 UP — отдельный репо с папками
|
# Для HTML5 UP — отдельный репо с папками
|
||||||
if [ "$source" = "html5up" ]; then
|
if [ "$source" = "html5up" ]; then
|
||||||
local tmp_clone="/tmp/html5up_clone_$$"
|
local tmp_clone="/tmp/html5up_clone_$$"
|
||||||
rm -rf "$tmp_clone"
|
rm -rf "$tmp_clone"
|
||||||
|
|
||||||
# Sparse checkout
|
# Sparse checkout
|
||||||
git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null
|
git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
# Fallback: полный clone
|
# Fallback: полный clone
|
||||||
git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null
|
git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -d "$tmp_clone" ]; then
|
if [ -d "$tmp_clone" ]; then
|
||||||
cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null
|
cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null
|
||||||
if [ -d "$tmp_clone/$sparse_path" ]; then
|
if [ -d "$tmp_clone/$sparse_path" ]; then
|
||||||
cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/"
|
cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/"
|
||||||
fi
|
fi
|
||||||
cd - >/dev/null
|
cd - >/dev/null
|
||||||
fi
|
fi
|
||||||
rm -rf "$tmp_clone"
|
rm -rf "$tmp_clone"
|
||||||
|
|
||||||
# Для learning-zone — один большой репо
|
# Для learning-zone — один большой репо
|
||||||
elif [ "$source" = "learning-zone" ]; then
|
elif [ "$source" = "learning-zone" ]; then
|
||||||
local tmp_clone="/tmp/lz_clone_$$"
|
local tmp_clone="/tmp/lz_clone_$$"
|
||||||
rm -rf "$tmp_clone"
|
rm -rf "$tmp_clone"
|
||||||
|
|
||||||
git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null
|
git clone --depth 1 --filter=blob:none --sparse "$repo_url" "$tmp_clone" 2>/dev/null
|
||||||
if [ $? -ne 0 ]; then
|
if [ $? -ne 0 ]; then
|
||||||
git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null
|
git clone --depth 1 "$repo_url" "$tmp_clone" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -d "$tmp_clone" ]; then
|
if [ -d "$tmp_clone" ]; then
|
||||||
cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null
|
cd "$tmp_clone" && git sparse-checkout set "$sparse_path" 2>/dev/null
|
||||||
if [ -d "$tmp_clone/$sparse_path" ]; then
|
if [ -d "$tmp_clone/$sparse_path" ]; then
|
||||||
cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/"
|
cp -r "$tmp_clone/$sparse_path"/* "$clone_dir/"
|
||||||
fi
|
fi
|
||||||
cd - >/dev/null
|
cd - >/dev/null
|
||||||
fi
|
fi
|
||||||
rm -rf "$tmp_clone"
|
rm -rf "$tmp_clone"
|
||||||
|
|
||||||
# Для StartBootstrap — каждый шаблон в своём репо
|
# Для StartBootstrap — каждый шаблон в своём репо
|
||||||
elif [ "$source" = "startbootstrap" ]; then
|
elif [ "$source" = "startbootstrap" ]; then
|
||||||
git clone --depth 1 "$repo_url" "$clone_dir" 2>/dev/null
|
git clone --depth 1 "$repo_url" "$clone_dir" 2>/dev/null
|
||||||
# Убираем .git
|
# Убираем .git
|
||||||
rm -rf "$clone_dir/.git"
|
rm -rf "$clone_dir/.git"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Проверяем результат
|
# Проверяем результат
|
||||||
if [ -f "$clone_dir/index.html" ]; then
|
if [ -f "$clone_dir/index.html" ]; then
|
||||||
log_success "Шаблон \"$name\" скачан"
|
log_success "Шаблон \"$name\" скачан"
|
||||||
echo "$clone_dir"
|
echo "$clone_dir"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
log_error "Шаблон не содержит index.html"
|
log_error "Шаблон не содержит index.html"
|
||||||
log_dim "Путь: $clone_dir"
|
log_dim "Путь: $clone_dir"
|
||||||
ls -la "$clone_dir" 2>/dev/null
|
ls -la "$clone_dir" 2>/dev/null
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Полный интерактивный процесс выбора ──────────────────────────────────────
|
# ── Полный интерактивный процесс выбора ──────────────────────────────────────
|
||||||
interactive_template_selection() {
|
interactive_template_selection() {
|
||||||
load_catalog || return 1
|
load_catalog || return 1
|
||||||
|
|
||||||
# Выбор категории
|
# Выбор категории
|
||||||
local cat_id
|
local cat_id
|
||||||
cat_id=$(select_category)
|
cat_id=$(select_category)
|
||||||
[ $? -ne 0 ] && return 1
|
[ $? -ne 0 ] && return 1
|
||||||
|
|
||||||
# Выбор шаблона
|
# Выбор шаблона
|
||||||
local tpl_id
|
local tpl_id
|
||||||
tpl_id=$(select_template "$cat_id")
|
tpl_id=$(select_template "$cat_id")
|
||||||
[ $? -ne 0 ] && return 1
|
[ $? -ne 0 ] && return 1
|
||||||
|
|
||||||
# Скачивание
|
# Скачивание
|
||||||
local template_dir
|
local template_dir
|
||||||
template_dir=$(download_template "$tpl_id")
|
template_dir=$(download_template "$tpl_id")
|
||||||
[ $? -ne 0 ] && return 1
|
[ $? -ne 0 ] && return 1
|
||||||
|
|
||||||
echo "$template_dir"
|
echo "$template_dir"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
680
lib/website.sh
680
lib/website.sh
@@ -1,340 +1,340 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# GoTelegram v2.2 — Управление сайтом (nginx + certbot + шаблоны)
|
# GoTelegram v2.2 — Управление сайтом (nginx + certbot + шаблоны)
|
||||||
|
|
||||||
# ── Установка nginx ──────────────────────────────────────────────────────────
|
# ── Установка nginx ──────────────────────────────────────────────────────────
|
||||||
install_nginx() {
|
install_nginx() {
|
||||||
if command -v nginx &>/dev/null; then
|
if command -v nginx &>/dev/null; then
|
||||||
log_dim "nginx уже установлен"
|
log_dim "nginx уже установлен"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
log_info "Установка nginx..."
|
log_info "Установка nginx..."
|
||||||
case "$(get_pkg_manager)" in
|
case "$(get_pkg_manager)" in
|
||||||
apt) apt-get update -qq && apt-get install -y -qq nginx ;;
|
apt) apt-get update -qq && apt-get install -y -qq nginx ;;
|
||||||
dnf) dnf install -y -q nginx ;;
|
dnf) dnf install -y -q nginx ;;
|
||||||
yum) yum install -y -q nginx ;;
|
yum) yum install -y -q nginx ;;
|
||||||
esac
|
esac
|
||||||
systemctl enable nginx 2>/dev/null
|
systemctl enable nginx 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Установка certbot ────────────────────────────────────────────────────────
|
# ── Установка certbot ────────────────────────────────────────────────────────
|
||||||
install_certbot() {
|
install_certbot() {
|
||||||
if command -v certbot &>/dev/null; then
|
if command -v certbot &>/dev/null; then
|
||||||
log_dim "certbot уже установлен"
|
log_dim "certbot уже установлен"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
log_info "Установка certbot..."
|
log_info "Установка certbot..."
|
||||||
case "$(get_pkg_manager)" in
|
case "$(get_pkg_manager)" in
|
||||||
apt) apt-get install -y -qq certbot python3-certbot-nginx ;;
|
apt) apt-get install -y -qq certbot python3-certbot-nginx ;;
|
||||||
dnf) dnf install -y -q certbot python3-certbot-nginx ;;
|
dnf) dnf install -y -q certbot python3-certbot-nginx ;;
|
||||||
yum) yum install -y -q certbot python3-certbot-nginx ;;
|
yum) yum install -y -q certbot python3-certbot-nginx ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Генерация nginx конфига ──────────────────────────────────────────────────
|
# ── Генерация nginx конфига ──────────────────────────────────────────────────
|
||||||
generate_nginx_config() {
|
generate_nginx_config() {
|
||||||
local domain="$1"
|
local domain="$1"
|
||||||
local proxy_port="${2:-443}"
|
local proxy_port="${2:-443}"
|
||||||
local use_ssl="${3:-true}"
|
local use_ssl="${3:-true}"
|
||||||
|
|
||||||
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
||||||
|
|
||||||
cat > "$NGINX_SITE_CONF" << 'EONGINX'
|
cat > "$NGINX_SITE_CONF" << 'EONGINX'
|
||||||
# GoTelegram v2.2 — nginx config
|
# GoTelegram v2.2 — nginx config
|
||||||
# Обслуживает сайт-маскировку для telemt stealth mode
|
# Обслуживает сайт-маскировку для telemt stealth mode
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
listen [::]:80;
|
listen [::]:80;
|
||||||
server_name DOMAIN_PLACEHOLDER;
|
server_name DOMAIN_PLACEHOLDER;
|
||||||
|
|
||||||
# Let's Encrypt ACME challenge
|
# Let's Encrypt ACME challenge
|
||||||
location /.well-known/acme-challenge/ {
|
location /.well-known/acme-challenge/ {
|
||||||
root /var/www/certbot;
|
root /var/www/certbot;
|
||||||
allow all;
|
allow all;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Редирект на HTTPS
|
# Редирект на HTTPS
|
||||||
location / {
|
location / {
|
||||||
return 301 https://$server_name$request_uri;
|
return 301 https://$server_name$request_uri;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
server {
|
server {
|
||||||
listen SSL_PORT_PLACEHOLDER ssl http2;
|
listen SSL_PORT_PLACEHOLDER ssl http2;
|
||||||
listen [::]:SSL_PORT_PLACEHOLDER ssl http2;
|
listen [::]:SSL_PORT_PLACEHOLDER ssl http2;
|
||||||
server_name DOMAIN_PLACEHOLDER;
|
server_name DOMAIN_PLACEHOLDER;
|
||||||
|
|
||||||
# SSL сертификаты
|
# SSL сертификаты
|
||||||
ssl_certificate /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/fullchain.pem;
|
ssl_certificate /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/fullchain.pem;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/privkey.pem;
|
||||||
|
|
||||||
# Современные TLS настройки
|
# Современные TLS настройки
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
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_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_prefer_server_ciphers off;
|
||||||
ssl_session_cache shared:SSL:10m;
|
ssl_session_cache shared:SSL:10m;
|
||||||
ssl_session_timeout 1d;
|
ssl_session_timeout 1d;
|
||||||
ssl_session_tickets off;
|
ssl_session_tickets off;
|
||||||
|
|
||||||
# OCSP stapling
|
# OCSP stapling
|
||||||
ssl_stapling on;
|
ssl_stapling on;
|
||||||
ssl_stapling_verify on;
|
ssl_stapling_verify on;
|
||||||
|
|
||||||
# Security headers
|
# Security headers
|
||||||
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||||
|
|
||||||
# Корень сайта
|
# Корень сайта
|
||||||
root /var/www/gotelegram-site;
|
root /var/www/gotelegram-site;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files $uri $uri/ =404;
|
try_files $uri $uri/ =404;
|
||||||
expires 30d;
|
expires 30d;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Кеширование статики
|
# Кеширование статики
|
||||||
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
|
||||||
expires 1y;
|
expires 1y;
|
||||||
add_header Cache-Control "public, immutable";
|
add_header Cache-Control "public, immutable";
|
||||||
}
|
}
|
||||||
|
|
||||||
# Скрываем служебные файлы
|
# Скрываем служебные файлы
|
||||||
location ~ /\. { deny all; }
|
location ~ /\. { deny all; }
|
||||||
location = /robots.txt { allow all; log_not_found off; access_log off; }
|
location = /robots.txt { allow all; log_not_found off; access_log off; }
|
||||||
location = /favicon.ico { log_not_found off; access_log off; }
|
location = /favicon.ico { log_not_found off; access_log off; }
|
||||||
}
|
}
|
||||||
EONGINX
|
EONGINX
|
||||||
|
|
||||||
# Подставляем значения (используем | как разделитель, чтобы / в домене не ломал sed)
|
# Подставляем значения (используем | как разделитель, чтобы / в домене не ломал sed)
|
||||||
local escaped_domain
|
local escaped_domain
|
||||||
escaped_domain=$(printf '%s\n' "$domain" | sed 's/[&/\]/\\&/g')
|
escaped_domain=$(printf '%s\n' "$domain" | sed 's/[&/\]/\\&/g')
|
||||||
sed -i "s|DOMAIN_PLACEHOLDER|${escaped_domain}|g" "$NGINX_SITE_CONF"
|
sed -i "s|DOMAIN_PLACEHOLDER|${escaped_domain}|g" "$NGINX_SITE_CONF"
|
||||||
sed -i "s|SSL_PORT_PLACEHOLDER|443|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
|
rm -f /etc/nginx/sites-enabled/default 2>/dev/null
|
||||||
ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
||||||
|
|
||||||
log_success "nginx конфиг создан для $domain"
|
log_success "nginx конфиг создан для $domain"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Временный конфиг (до получения SSL) ──────────────────────────────────────
|
# ── Временный конфиг (до получения SSL) ──────────────────────────────────────
|
||||||
generate_nginx_temp_config() {
|
generate_nginx_temp_config() {
|
||||||
local domain="$1"
|
local domain="$1"
|
||||||
|
|
||||||
cat > "$NGINX_SITE_CONF" << EONGINX_TEMP
|
cat > "$NGINX_SITE_CONF" << EONGINX_TEMP
|
||||||
# GoTelegram — временный конфиг (до получения SSL)
|
# GoTelegram — временный конфиг (до получения SSL)
|
||||||
server {
|
server {
|
||||||
listen 80;
|
listen 80;
|
||||||
listen [::]:80;
|
listen [::]:80;
|
||||||
server_name ${domain};
|
server_name ${domain};
|
||||||
|
|
||||||
location /.well-known/acme-challenge/ {
|
location /.well-known/acme-challenge/ {
|
||||||
root /var/www/certbot;
|
root /var/www/certbot;
|
||||||
allow all;
|
allow all;
|
||||||
}
|
}
|
||||||
|
|
||||||
root /var/www/gotelegram-site;
|
root /var/www/gotelegram-site;
|
||||||
index index.html;
|
index index.html;
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
try_files \$uri \$uri/ =404;
|
try_files \$uri \$uri/ =404;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EONGINX_TEMP
|
EONGINX_TEMP
|
||||||
|
|
||||||
rm -f /etc/nginx/sites-enabled/default 2>/dev/null
|
rm -f /etc/nginx/sites-enabled/default 2>/dev/null
|
||||||
ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
||||||
mkdir -p /var/www/certbot
|
mkdir -p /var/www/certbot
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Получение SSL сертификата ────────────────────────────────────────────────
|
# ── Получение SSL сертификата ────────────────────────────────────────────────
|
||||||
obtain_ssl_certificate() {
|
obtain_ssl_certificate() {
|
||||||
local domain="$1"
|
local domain="$1"
|
||||||
local email="${2:-}"
|
local email="${2:-}"
|
||||||
|
|
||||||
if [ ! -d "/etc/letsencrypt/live/$domain" ]; then
|
if [ ! -d "/etc/letsencrypt/live/$domain" ]; then
|
||||||
log_info "Получение SSL сертификата для $domain..."
|
log_info "Получение SSL сертификата для $domain..."
|
||||||
|
|
||||||
# Временный конфиг для ACME challenge
|
# Временный конфиг для ACME challenge
|
||||||
generate_nginx_temp_config "$domain"
|
generate_nginx_temp_config "$domain"
|
||||||
systemctl restart nginx 2>/dev/null
|
systemctl restart nginx 2>/dev/null
|
||||||
|
|
||||||
local certbot_args=(
|
local certbot_args=(
|
||||||
certonly
|
certonly
|
||||||
--webroot
|
--webroot
|
||||||
-w /var/www/certbot
|
-w /var/www/certbot
|
||||||
-d "$domain"
|
-d "$domain"
|
||||||
--non-interactive
|
--non-interactive
|
||||||
--agree-tos
|
--agree-tos
|
||||||
)
|
)
|
||||||
|
|
||||||
if [ -n "$email" ]; then
|
if [ -n "$email" ]; then
|
||||||
certbot_args+=(--email "$email")
|
certbot_args+=(--email "$email")
|
||||||
else
|
else
|
||||||
certbot_args+=(--register-unsafely-without-email)
|
certbot_args+=(--register-unsafely-without-email)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if certbot "${certbot_args[@]}" 2>/dev/null; then
|
if certbot "${certbot_args[@]}" 2>/dev/null; then
|
||||||
log_success "SSL сертификат получен для $domain"
|
log_success "SSL сертификат получен для $domain"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
log_error "Не удалось получить SSL сертификат"
|
log_error "Не удалось получить SSL сертификат"
|
||||||
log_dim "Убедитесь что домен $domain направлен на IP этого сервера"
|
log_dim "Убедитесь что домен $domain направлен на IP этого сервера"
|
||||||
log_dim "и порт 80 открыт в файрволе."
|
log_dim "и порт 80 открыт в файрволе."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
log_dim "SSL сертификат уже существует для $domain"
|
log_dim "SSL сертификат уже существует для $domain"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Авто-обновление сертификата ──────────────────────────────────────────────
|
# ── Авто-обновление сертификата ──────────────────────────────────────────────
|
||||||
setup_ssl_auto_renewal() {
|
setup_ssl_auto_renewal() {
|
||||||
# Certbot systemd timer (предпочтительно)
|
# Certbot systemd timer (предпочтительно)
|
||||||
if [ -f /etc/systemd/system/certbot.timer ] || [ -f /lib/systemd/system/certbot.timer ]; then
|
if [ -f /etc/systemd/system/certbot.timer ] || [ -f /lib/systemd/system/certbot.timer ]; then
|
||||||
systemctl enable certbot.timer 2>/dev/null
|
systemctl enable certbot.timer 2>/dev/null
|
||||||
systemctl start certbot.timer 2>/dev/null
|
systemctl start certbot.timer 2>/dev/null
|
||||||
log_success "Авто-обновление SSL через systemd timer"
|
log_success "Авто-обновление SSL через systemd timer"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Fallback: cron
|
# Fallback: cron
|
||||||
if ! crontab -l 2>/dev/null | grep -q "certbot renew"; then
|
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 -
|
(crontab -l 2>/dev/null; echo "0 3 * * * certbot renew --quiet --post-hook 'systemctl reload nginx'") | crontab -
|
||||||
log_success "Авто-обновление SSL через cron (3:00 ежедневно)"
|
log_success "Авто-обновление SSL через cron (3:00 ежедневно)"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Обновление сертификата вручную ───────────────────────────────────────────
|
# ── Обновление сертификата вручную ───────────────────────────────────────────
|
||||||
renew_ssl_certificate() {
|
renew_ssl_certificate() {
|
||||||
log_info "Обновление SSL сертификата..."
|
log_info "Обновление SSL сертификата..."
|
||||||
if certbot renew --quiet --post-hook "systemctl reload nginx" 2>/dev/null; then
|
if certbot renew --quiet --post-hook "systemctl reload nginx" 2>/dev/null; then
|
||||||
log_success "Сертификат обновлён"
|
log_success "Сертификат обновлён"
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
log_error "Ошибка обновления сертификата"
|
log_error "Ошибка обновления сертификата"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Дата истечения SSL ───────────────────────────────────────────────────────
|
# ── Дата истечения SSL ───────────────────────────────────────────────────────
|
||||||
get_ssl_expiry() {
|
get_ssl_expiry() {
|
||||||
local domain="$1"
|
local domain="$1"
|
||||||
local cert="/etc/letsencrypt/live/$domain/fullchain.pem"
|
local cert="/etc/letsencrypt/live/$domain/fullchain.pem"
|
||||||
if [ -f "$cert" ]; then
|
if [ -f "$cert" ]; then
|
||||||
openssl x509 -enddate -noout -in "$cert" 2>/dev/null | sed 's/notAfter=//'
|
openssl x509 -enddate -noout -in "$cert" 2>/dev/null | sed 's/notAfter=//'
|
||||||
else
|
else
|
||||||
echo "N/A"
|
echo "N/A"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Деплой шаблона сайта ─────────────────────────────────────────────────────
|
# ── Деплой шаблона сайта ─────────────────────────────────────────────────────
|
||||||
deploy_template_to_nginx() {
|
deploy_template_to_nginx() {
|
||||||
local template_dir="$1"
|
local template_dir="$1"
|
||||||
|
|
||||||
if [ ! -d "$template_dir" ] || [ ! -f "$template_dir/index.html" ]; then
|
if [ ! -d "$template_dir" ] || [ ! -f "$template_dir/index.html" ]; then
|
||||||
log_error "Шаблон не содержит index.html: $template_dir"
|
log_error "Шаблон не содержит index.html: $template_dir"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Бекапим старый сайт
|
# Бекапим старый сайт
|
||||||
if [ -d "$WEBSITE_ROOT" ] && [ "$(ls -A "$WEBSITE_ROOT" 2>/dev/null)" ]; then
|
if [ -d "$WEBSITE_ROOT" ] && [ "$(ls -A "$WEBSITE_ROOT" 2>/dev/null)" ]; then
|
||||||
local backup_name="site_backup_$(date +%Y%m%d_%H%M%S)"
|
local backup_name="site_backup_$(date +%Y%m%d_%H%M%S)"
|
||||||
mv "$WEBSITE_ROOT" "/tmp/$backup_name" 2>/dev/null
|
mv "$WEBSITE_ROOT" "/tmp/$backup_name" 2>/dev/null
|
||||||
log_dim "Старый сайт сохранён в /tmp/$backup_name"
|
log_dim "Старый сайт сохранён в /tmp/$backup_name"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
mkdir -p "$WEBSITE_ROOT"
|
mkdir -p "$WEBSITE_ROOT"
|
||||||
cp -r "$template_dir"/* "$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
|
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"
|
chmod -R 755 "$WEBSITE_ROOT"
|
||||||
|
|
||||||
log_success "Шаблон развёрнут в $WEBSITE_ROOT"
|
log_success "Шаблон развёрнут в $WEBSITE_ROOT"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Полная установка stealth-режима ──────────────────────────────────────────
|
# ── Полная установка stealth-режима ──────────────────────────────────────────
|
||||||
setup_stealth_mode() {
|
setup_stealth_mode() {
|
||||||
local domain="$1"
|
local domain="$1"
|
||||||
local template_dir="$2"
|
local template_dir="$2"
|
||||||
local proxy_port="${3:-443}"
|
local proxy_port="${3:-443}"
|
||||||
local email="${4:-}"
|
local email="${4:-}"
|
||||||
|
|
||||||
log_step "Настройка stealth-режима"
|
log_step "Настройка stealth-режима"
|
||||||
|
|
||||||
# 1. Устанавливаем nginx
|
# 1. Устанавливаем nginx
|
||||||
run_with_spinner "Установка nginx" install_nginx || return 1
|
run_with_spinner "Установка nginx" install_nginx || return 1
|
||||||
|
|
||||||
# 2. Устанавливаем certbot
|
# 2. Устанавливаем certbot
|
||||||
run_with_spinner "Установка certbot" install_certbot || return 1
|
run_with_spinner "Установка certbot" install_certbot || return 1
|
||||||
|
|
||||||
# 3. Деплоим шаблон сайта
|
# 3. Деплоим шаблон сайта
|
||||||
deploy_template_to_nginx "$template_dir" || return 1
|
deploy_template_to_nginx "$template_dir" || return 1
|
||||||
|
|
||||||
# 4. Получаем SSL
|
# 4. Получаем SSL
|
||||||
obtain_ssl_certificate "$domain" "$email" || return 1
|
obtain_ssl_certificate "$domain" "$email" || return 1
|
||||||
|
|
||||||
# 5. Генерируем полный nginx конфиг с SSL
|
# 5. Генерируем полный nginx конфиг с SSL
|
||||||
generate_nginx_config "$domain" "$proxy_port"
|
generate_nginx_config "$domain" "$proxy_port"
|
||||||
|
|
||||||
# 6. Тестируем и перезапускаем nginx
|
# 6. Тестируем и перезапускаем nginx
|
||||||
if nginx -t 2>/dev/null; then
|
if nginx -t 2>/dev/null; then
|
||||||
systemctl restart nginx
|
systemctl restart nginx
|
||||||
log_success "nginx запущен с SSL"
|
log_success "nginx запущен с SSL"
|
||||||
else
|
else
|
||||||
log_error "Ошибка в конфигурации nginx"
|
log_error "Ошибка в конфигурации nginx"
|
||||||
nginx -t
|
nginx -t
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 7. Настраиваем авто-обновление SSL
|
# 7. Настраиваем авто-обновление SSL
|
||||||
setup_ssl_auto_renewal
|
setup_ssl_auto_renewal
|
||||||
|
|
||||||
# 8. Показываем благодарности авторам шаблонов
|
# 8. Показываем благодарности авторам шаблонов
|
||||||
show_credits
|
show_credits
|
||||||
|
|
||||||
log_success "Stealth-режим настроен: https://${domain}"
|
log_success "Stealth-режим настроен: https://${domain}"
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Управление nginx ────────────────────────────────────────────────────────
|
# ── Управление nginx ────────────────────────────────────────────────────────
|
||||||
nginx_status() {
|
nginx_status() {
|
||||||
if systemctl is-active --quiet nginx 2>/dev/null; then
|
if systemctl is-active --quiet nginx 2>/dev/null; then
|
||||||
echo "running"
|
echo "running"
|
||||||
else
|
else
|
||||||
echo "stopped"
|
echo "stopped"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
restart_nginx() {
|
restart_nginx() {
|
||||||
if nginx -t 2>/dev/null; then
|
if nginx -t 2>/dev/null; then
|
||||||
systemctl restart nginx 2>/dev/null
|
systemctl restart nginx 2>/dev/null
|
||||||
log_success "nginx перезапущен"
|
log_success "nginx перезапущен"
|
||||||
else
|
else
|
||||||
log_error "Ошибка конфигурации nginx"
|
log_error "Ошибка конфигурации nginx"
|
||||||
nginx -t
|
nginx -t
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Удаление stealth-режима ──────────────────────────────────────────────────
|
# ── Удаление stealth-режима ──────────────────────────────────────────────────
|
||||||
remove_stealth_mode() {
|
remove_stealth_mode() {
|
||||||
log_info "Удаление stealth-режима..."
|
log_info "Удаление stealth-режима..."
|
||||||
rm -f "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
rm -f "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
||||||
rm -rf "$WEBSITE_ROOT"
|
rm -rf "$WEBSITE_ROOT"
|
||||||
systemctl restart nginx 2>/dev/null
|
systemctl restart nginx 2>/dev/null
|
||||||
log_success "Stealth-режим удалён (nginx оставлен)"
|
log_success "Stealth-режим удалён (nginx оставлен)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Смена шаблона ────────────────────────────────────────────────────────────
|
# ── Смена шаблона ────────────────────────────────────────────────────────────
|
||||||
switch_template() {
|
switch_template() {
|
||||||
local new_template_dir="$1"
|
local new_template_dir="$1"
|
||||||
deploy_template_to_nginx "$new_template_dir"
|
deploy_template_to_nginx "$new_template_dir"
|
||||||
# nginx не требует перезапуска — статика обновилась на месте
|
# nginx не требует перезапуска — статика обновилась на месте
|
||||||
log_success "Шаблон сайта обновлён"
|
log_success "Шаблон сайта обновлён"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user