v2.2.1 hotfix: fix telemt download grep pattern (grep -iE), add QR cleanup, bump version to 2.2.1

This commit is contained in:
anten-ka
2026-04-06 21:30:30 +03:00
parent 55134f4864
commit 13cca51f5f
3 changed files with 2163 additions and 2158 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,456 +1,456 @@
#!/bin/bash #!/bin/bash
# GoTelegram v2.2 — Общие утилиты # GoTelegram v2.2 — Общие утилиты
# Цвета, логирование, спиннер, системные функции, совместимость с v1 # Цвета, логирование, спиннер, системные функции, совместимость с v1
# ── Версия ──────────────────────────────────────────────────────────────────── # ── Версия ────────────────────────────────────────────────────────────────────
GOTELEGRAM_VERSION="2.2.0" GOTELEGRAM_VERSION="2.2.1"
GOTELEGRAM_NAME="GoTelegram" GOTELEGRAM_NAME="GoTelegram"
# ── Пути ────────────────────────────────────────────────────────────────────── # ── Пути ──────────────────────────────────────────────────────────────────────
GOTELEGRAM_DIR="/opt/gotelegram" GOTELEGRAM_DIR="/opt/gotelegram"
GOTELEGRAM_CONFIG="$GOTELEGRAM_DIR/config.json" GOTELEGRAM_CONFIG="$GOTELEGRAM_DIR/config.json"
TELEMT_CONFIG="/etc/telemt/config.toml" TELEMT_CONFIG="/etc/telemt/config.toml"
TELEMT_BIN="/usr/local/bin/telemt" TELEMT_BIN="/usr/local/bin/telemt"
TELEMT_SERVICE="telemt" TELEMT_SERVICE="telemt"
NGINX_SITE_CONF="/etc/nginx/sites-available/gotelegram" NGINX_SITE_CONF="/etc/nginx/sites-available/gotelegram"
NGINX_SITE_LINK="/etc/nginx/sites-enabled/gotelegram" NGINX_SITE_LINK="/etc/nginx/sites-enabled/gotelegram"
WEBSITE_ROOT="/var/www/gotelegram-site" WEBSITE_ROOT="/var/www/gotelegram-site"
BACKUP_DIR="$GOTELEGRAM_DIR/backups" BACKUP_DIR="$GOTELEGRAM_DIR/backups"
LOG_FILE="/var/log/gotelegram.log" LOG_FILE="/var/log/gotelegram.log"
BOT_DIR="/opt/gotelegram-bot" BOT_DIR="/opt/gotelegram-bot"
# ── V1 совместимость ───────────────────────────────────────────────────────── # ── V1 совместимость ─────────────────────────────────────────────────────────
V1_CONTAINER_NAME="mtproto-proxy" V1_CONTAINER_NAME="mtproto-proxy"
V1_CONFIG_FILE="/opt/gotelegram-bot/proxy.json" V1_CONFIG_FILE="/opt/gotelegram-bot/proxy.json"
V1_SERVICE_NAME="gotelegram-bot" V1_SERVICE_NAME="gotelegram-bot"
# ── Цвета ──────────────────────────────────────────────────────────────────── # ── Цвета ────────────────────────────────────────────────────────────────────
RED='\033[0;31m' RED='\033[0;31m'
GREEN='\033[0;32m' GREEN='\033[0;32m'
CYAN='\033[0;36m' CYAN='\033[0;36m'
YELLOW='\033[1;33m' YELLOW='\033[1;33m'
MAGENTA='\033[0;35m' MAGENTA='\033[0;35m'
BLUE='\033[0;34m' BLUE='\033[0;34m'
WHITE='\033[1;37m' WHITE='\033[1;37m'
BOLD='\033[1m' BOLD='\033[1m'
DIM='\033[2m' DIM='\033[2m'
NC='\033[0m' NC='\033[0m'
# ── Логирование ────────────────────────────────────────────────────────────── # ── Логирование ──────────────────────────────────────────────────────────────
log_info() { echo -e " ${CYAN}${NC} $*"; } log_info() { echo -e " ${CYAN}${NC} $*"; }
log_success() { echo -e " ${GREEN}${NC} $*"; } log_success() { echo -e " ${GREEN}${NC} $*"; }
log_warning() { echo -e " ${YELLOW}${NC} $*"; } log_warning() { echo -e " ${YELLOW}${NC} $*"; }
log_error() { echo -e " ${RED}${NC} $*"; } log_error() { echo -e " ${RED}${NC} $*"; }
log_step() { echo -e "\n${BOLD}${WHITE} $*${NC}"; } log_step() { echo -e "\n${BOLD}${WHITE} $*${NC}"; }
log_dim() { echo -e " ${DIM}$*${NC}"; } log_dim() { echo -e " ${DIM}$*${NC}"; }
log_to_file() { log_to_file() {
local ts; ts=$(date '+%Y-%m-%d %H:%M:%S') local ts; ts=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$ts] $*" >> "$LOG_FILE" 2>/dev/null echo "[$ts] $*" >> "$LOG_FILE" 2>/dev/null
} }
# ── Спиннер ────────────────────────────────────────────────────────────────── # ── Спиннер ──────────────────────────────────────────────────────────────────
_spin_pid="" _spin_pid=""
spinner_start() { spinner_start() {
local msg="${1:-Подождите...}" local msg="${1:-Подождите...}"
( (
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏') local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
local i=0 local i=0
while true; do while true; do
printf "\r ${CYAN}${frames[$i]}${NC} ${msg}" >&2 printf "\r ${CYAN}${frames[$i]}${NC} ${msg}" >&2
i=$(( (i+1) % ${#frames[@]} )) i=$(( (i+1) % ${#frames[@]} ))
sleep 0.1 sleep 0.1
done done
) & ) &
_spin_pid=$! _spin_pid=$!
} }
spinner_stop() { spinner_stop() {
[ -n "$_spin_pid" ] && kill "$_spin_pid" 2>/dev/null && wait "$_spin_pid" 2>/dev/null [ -n "$_spin_pid" ] && kill "$_spin_pid" 2>/dev/null && wait "$_spin_pid" 2>/dev/null
_spin_pid="" _spin_pid=""
printf "\r\033[K" >&2 printf "\r\033[K" >&2
} }
# ── Прогресс-бар ───────────────────────────────────────────────────────────── # ── Прогресс-бар ─────────────────────────────────────────────────────────────
progress_bar() { progress_bar() {
local current="$1" total="$2" label="${3:-}" local current="$1" total="$2" label="${3:-}"
local pct=$(( current * 100 / total )) local pct=$(( current * 100 / total ))
local filled=$(( pct / 2 )) local filled=$(( pct / 2 ))
local empty=$(( 50 - filled )) local empty=$(( 50 - filled ))
local bar="" local bar=""
for ((i=0; i<filled; i++)); do bar+="█"; done for ((i=0; i<filled; i++)); do bar+="█"; done
for ((i=0; i<empty; i++)); do bar+="░"; done for ((i=0; i<empty; i++)); do bar+="░"; done
printf "\r ${GREEN}[${bar}]${NC} ${pct}%% ${label}" >&2 printf "\r ${GREEN}[${bar}]${NC} ${pct}%% ${label}" >&2
[ "$current" -eq "$total" ] && echo "" >&2 [ "$current" -eq "$total" ] && echo "" >&2
} }
# ── Выполнение с индикатором ───────────────────────────────────────────────── # ── Выполнение с индикатором ─────────────────────────────────────────────────
run_with_spinner() { run_with_spinner() {
local label="$1"; shift local label="$1"; shift
local err_file="/tmp/.gotelegram_spinner_err_$$" local err_file="/tmp/.gotelegram_spinner_err_$$"
spinner_start "$label" spinner_start "$label"
"$@" >/dev/null 2>"$err_file" "$@" >/dev/null 2>"$err_file"
local rc=$? local rc=$?
spinner_stop spinner_stop
if [ $rc -eq 0 ]; then if [ $rc -eq 0 ]; then
log_success "$label" log_success "$label"
else else
log_error "$label ${RED}(ошибка, код: $rc)${NC}" log_error "$label ${RED}(ошибка, код: $rc)${NC}"
if [ -s "$err_file" ]; then if [ -s "$err_file" ]; then
log_dim " $(head -3 "$err_file")" log_dim " $(head -3 "$err_file")"
fi fi
fi fi
rm -f "$err_file" rm -f "$err_file"
return $rc return $rc
} }
# ── Баннер ─────────────────────────────────────────────────────────────────── # ── Баннер ───────────────────────────────────────────────────────────────────
show_banner() { show_banner() {
echo "" echo ""
echo -e "${CYAN}╔══════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}╔══════════════════════════════════════════════════════════╗${NC}"
echo -e "${CYAN}${NC} ${BOLD}${WHITE}🚀 GoTelegram v${GOTELEGRAM_VERSION}${NC} ${CYAN}${NC}" echo -e "${CYAN}${NC} ${BOLD}${WHITE}🚀 GoTelegram v${GOTELEGRAM_VERSION}${NC} ${CYAN}${NC}"
echo -e "${CYAN}${NC} ${DIM}MTProxy на ядре telemt (Rust + Tokio)${NC} ${CYAN}${NC}" echo -e "${CYAN}${NC} ${DIM}MTProxy на ядре telemt (Rust + Tokio)${NC} ${CYAN}${NC}"
echo -e "${CYAN}${NC} ${DIM}Anti-DPI • Fake TLS • TCP Splice • JA3/JA4${NC} ${CYAN}${NC}" echo -e "${CYAN}${NC} ${DIM}Anti-DPI • Fake TLS • TCP Splice • JA3/JA4${NC} ${CYAN}${NC}"
echo -e "${CYAN}╚══════════════════════════════════════════════════════════╝${NC}" echo -e "${CYAN}╚══════════════════════════════════════════════════════════╝${NC}"
echo "" echo ""
} }
# ── Благодарности ──────────────────────────────────────────────────────────── # ── Благодарности ────────────────────────────────────────────────────────────
show_credits() { show_credits() {
echo "" echo ""
echo -e "${MAGENTA}╔══════════════════════════════════════════════════════════╗${NC}" echo -e "${MAGENTA}╔══════════════════════════════════════════════════════════╗${NC}"
echo -e "${MAGENTA}${NC} ${BOLD}Благодарности / Credits${NC} ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${BOLD}Благодарности / Credits${NC} ${MAGENTA}${NC}"
echo -e "${MAGENTA}╟──────────────────────────────────────────────────────────╢${NC}" echo -e "${MAGENTA}╟──────────────────────────────────────────────────────────╢${NC}"
echo -e "${MAGENTA}${NC} ${WHITE}telemt${NC} — MTProxy engine (Rust) ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${WHITE}telemt${NC} — MTProxy engine (Rust) ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${DIM}github.com/telemt/telemt${NC} ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${DIM}github.com/telemt/telemt${NC} ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${WHITE}HTML5 UP${NC} — адаптивные HTML/CSS шаблоны ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${WHITE}HTML5 UP${NC} — адаптивные HTML/CSS шаблоны ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${DIM}html5up.net • CC BY 3.0 • @ajlkn${NC} ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${DIM}html5up.net • CC BY 3.0 • @ajlkn${NC} ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${WHITE}learning-zone${NC} — 150+ HTML5 шаблонов ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${WHITE}learning-zone${NC} — 150+ HTML5 шаблонов ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${DIM}github.com/learning-zone/website-templates${NC} ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${DIM}github.com/learning-zone/website-templates${NC} ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${WHITE}Start Bootstrap${NC} — MIT лицензия ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${WHITE}Start Bootstrap${NC} — MIT лицензия ${MAGENTA}${NC}"
echo -e "${MAGENTA}${NC} ${DIM}startbootstrap.com${NC} ${MAGENTA}${NC}" echo -e "${MAGENTA}${NC} ${DIM}startbootstrap.com${NC} ${MAGENTA}${NC}"
echo -e "${MAGENTA}╚══════════════════════════════════════════════════════════╝${NC}" echo -e "${MAGENTA}╚══════════════════════════════════════════════════════════╝${NC}"
echo "" echo ""
} }
# ── Системные утилиты ──────────────────────────────────────────────────────── # ── Системные утилиты ────────────────────────────────────────────────────────
_valid_ip() { _valid_ip() {
# Validate that each octet is 0-255 # Validate that each octet is 0-255
local ip="$1" local ip="$1"
local IFS='.' local IFS='.'
read -ra octets <<< "$ip" read -ra octets <<< "$ip"
[ ${#octets[@]} -ne 4 ] && return 1 [ ${#octets[@]} -ne 4 ] && return 1
for octet in "${octets[@]}"; do for octet in "${octets[@]}"; do
[[ "$octet" =~ ^[0-9]+$ ]] || return 1 [[ "$octet" =~ ^[0-9]+$ ]] || return 1
[ "$octet" -gt 255 ] && return 1 [ "$octet" -gt 255 ] && return 1
done done
return 0 return 0
} }
get_server_ip() { get_server_ip() {
local ip raw local ip raw
for url in "https://api.ipify.org" "https://icanhazip.com" "https://ifconfig.me"; do for url in "https://api.ipify.org" "https://icanhazip.com" "https://ifconfig.me"; do
raw=$(curl -s -4 --max-time 5 "$url" 2>/dev/null) raw=$(curl -s -4 --max-time 5 "$url" 2>/dev/null)
ip=$(echo "$raw" | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1) ip=$(echo "$raw" | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' | head -1)
if [ -n "$ip" ] && _valid_ip "$ip"; then if [ -n "$ip" ] && _valid_ip "$ip"; then
echo "$ip" echo "$ip"
return 0 return 0
fi fi
done done
echo "0.0.0.0" echo "0.0.0.0"
return 1 return 1
} }
check_root() { check_root() {
if [ "$EUID" -ne 0 ]; then if [ "$EUID" -ne 0 ]; then
log_error "Запустите скрипт с sudo / от root" log_error "Запустите скрипт с sudo / от root"
exit 1 exit 1
fi fi
} }
check_os() { check_os() {
if [ ! -f /etc/os-release ]; then if [ ! -f /etc/os-release ]; then
log_error "Не удалось определить ОС. Требуется Linux." log_error "Не удалось определить ОС. Требуется Linux."
return 1 return 1
fi fi
# Validate os-release before sourcing (reject command injection: ;, backticks, $()) # Validate os-release before sourcing (reject command injection: ;, backticks, $())
if grep -qE '(;|`|\$\(|\$\{)' /etc/os-release 2>/dev/null; then if grep -qE '(;|`|\$\(|\$\{)' /etc/os-release 2>/dev/null; then
log_warning "/etc/os-release содержит подозрительные строки, пропускаем" log_warning "/etc/os-release содержит подозрительные строки, пропускаем"
return 0 return 0
fi fi
. /etc/os-release . /etc/os-release
case "$ID" in case "$ID" in
ubuntu|debian|centos|rocky|almalinux|fedora|rhel) ubuntu|debian|centos|rocky|almalinux|fedora|rhel)
log_dim "ОС: $PRETTY_NAME" log_dim "ОС: $PRETTY_NAME"
return 0 return 0
;; ;;
*) *)
log_warning "ОС $ID может быть несовместима. Поддерживаются: Ubuntu, Debian, CentOS, Rocky." log_warning "ОС $ID может быть несовместима. Поддерживаются: Ubuntu, Debian, CentOS, Rocky."
return 0 return 0
;; ;;
esac esac
} }
get_arch() { get_arch() {
local arch local arch
arch=$(uname -m) arch=$(uname -m)
case "$arch" in case "$arch" in
x86_64|amd64) echo "amd64" ;; x86_64|amd64) echo "amd64" ;;
aarch64|arm64) echo "arm64" ;; aarch64|arm64) echo "arm64" ;;
armv7*|armhf) echo "armv7" ;; armv7*|armhf) echo "armv7" ;;
*) echo "$arch" ;; *) echo "$arch" ;;
esac esac
} }
get_pkg_manager() { get_pkg_manager() {
if command -v apt-get &>/dev/null; then echo "apt" if command -v apt-get &>/dev/null; then echo "apt"
elif command -v dnf &>/dev/null; then echo "dnf" elif command -v dnf &>/dev/null; then echo "dnf"
elif command -v yum &>/dev/null; then echo "yum" elif command -v yum &>/dev/null; then echo "yum"
else echo "unknown" else echo "unknown"
fi fi
} }
install_pkg() { install_pkg() {
local pkg="$1" local pkg="$1"
case "$(get_pkg_manager)" in case "$(get_pkg_manager)" in
apt) apt-get install -y -qq "$pkg" ;; apt) apt-get install -y -qq "$pkg" ;;
dnf) dnf install -y -q "$pkg" ;; dnf) dnf install -y -q "$pkg" ;;
yum) yum install -y -q "$pkg" ;; yum) yum install -y -q "$pkg" ;;
*) log_error "Неизвестный пакетный менеджер"; return 1 ;; *) log_error "Неизвестный пакетный менеджер"; return 1 ;;
esac esac
} }
ensure_deps() { ensure_deps() {
local missing=() local missing=()
for cmd in curl jq openssl git; do for cmd in curl jq openssl git; do
if ! command -v "$cmd" &>/dev/null; then if ! command -v "$cmd" &>/dev/null; then
missing+=("$cmd") missing+=("$cmd")
fi fi
done done
if [ ${#missing[@]} -gt 0 ]; then if [ ${#missing[@]} -gt 0 ]; then
log_step "Установка зависимостей: ${missing[*]}" log_step "Установка зависимостей: ${missing[*]}"
case "$(get_pkg_manager)" in case "$(get_pkg_manager)" in
apt) apt-get update -qq && apt-get install -y -qq "${missing[@]}" ;; apt) apt-get update -qq && apt-get install -y -qq "${missing[@]}" ;;
dnf) dnf install -y -q "${missing[@]}" ;; dnf) dnf install -y -q "${missing[@]}" ;;
yum) yum install -y -q "${missing[@]}" ;; yum) yum install -y -q "${missing[@]}" ;;
esac esac
fi fi
} }
check_port() { check_port() {
local port="$1" local port="$1"
local line local line
line=$(ss -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1) line=$(ss -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1)
[ -z "$line" ] && line=$(netstat -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1) [ -z "$line" ] && line=$(netstat -tlnp 2>/dev/null | grep -E ":${port}\b" | head -1)
if [ -n "$line" ]; then if [ -n "$line" ]; then
echo "$line" echo "$line"
return 0 # порт занят return 0 # порт занят
fi fi
return 1 # свободен return 1 # свободен
} }
check_disk_space() { check_disk_space() {
local min_mb="${1:-500}" local min_mb="${1:-500}"
local avail_mb local avail_mb
avail_mb=$(df -m / | awk 'NR==2 {print $4}') avail_mb=$(df -m / | awk 'NR==2 {print $4}')
if [ "$avail_mb" -lt "$min_mb" ]; then if [ "$avail_mb" -lt "$min_mb" ]; then
log_error "Мало места на диске: ${avail_mb}MB (нужно ${min_mb}MB+)" log_error "Мало места на диске: ${avail_mb}MB (нужно ${min_mb}MB+)"
return 1 return 1
fi fi
return 0 return 0
} }
# ── Конфигурация GoTelegram (JSON) ────────────────────────────────────────── # ── Конфигурация GoTelegram (JSON) ──────────────────────────────────────────
save_gotelegram_config() { save_gotelegram_config() {
mkdir -p "$(dirname "$GOTELEGRAM_CONFIG")" mkdir -p "$(dirname "$GOTELEGRAM_CONFIG")"
cat > "$GOTELEGRAM_CONFIG" << EOJSON cat > "$GOTELEGRAM_CONFIG" << EOJSON
{ {
"version": "$GOTELEGRAM_VERSION", "version": "$GOTELEGRAM_VERSION",
"engine": "${1:-telemt}", "engine": "${1:-telemt}",
"mode": "${2:-quick}", "mode": "${2:-quick}",
"port": ${3:-443}, "port": ${3:-443},
"secret": "${4:-}", "secret": "${4:-}",
"mask_host": "${5:-google.com}", "mask_host": "${5:-google.com}",
"domain": "${6:-}", "domain": "${6:-}",
"template_id": "${7:-}", "template_id": "${7:-}",
"installed_at": "$(date -Iseconds)", "installed_at": "$(date -Iseconds)",
"updated_at": "$(date -Iseconds)" "updated_at": "$(date -Iseconds)"
} }
EOJSON EOJSON
chmod 600 "$GOTELEGRAM_CONFIG" chmod 600 "$GOTELEGRAM_CONFIG"
} }
load_gotelegram_config() { load_gotelegram_config() {
if [ -f "$GOTELEGRAM_CONFIG" ]; then if [ -f "$GOTELEGRAM_CONFIG" ]; then
cat "$GOTELEGRAM_CONFIG" cat "$GOTELEGRAM_CONFIG"
return 0 return 0
fi fi
echo "{}" echo "{}"
return 1 return 1
} }
config_get() { config_get() {
local key="$1" local key="$1"
if [ ! -f "$GOTELEGRAM_CONFIG" ]; then if [ ! -f "$GOTELEGRAM_CONFIG" ]; then
log_dim "Конфиг не найден: $GOTELEGRAM_CONFIG" >&2 log_dim "Конфиг не найден: $GOTELEGRAM_CONFIG" >&2
return 2 # file missing return 2 # file missing
fi fi
local val local val
val=$(jq -r ".$key // empty" "$GOTELEGRAM_CONFIG" 2>/dev/null) val=$(jq -r ".$key // empty" "$GOTELEGRAM_CONFIG" 2>/dev/null)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
log_dim "Ошибка чтения JSON: $GOTELEGRAM_CONFIG" >&2 log_dim "Ошибка чтения JSON: $GOTELEGRAM_CONFIG" >&2
return 3 # invalid JSON return 3 # invalid JSON
fi fi
if [ -z "$val" ]; then if [ -z "$val" ]; then
return 1 # key missing or empty return 1 # key missing or empty
fi fi
echo "$val" echo "$val"
return 0 return 0
} }
# ── V1 совместимость ───────────────────────────────────────────────────────── # ── V1 совместимость ─────────────────────────────────────────────────────────
detect_v1_installation() { detect_v1_installation() {
# Проверяем наличие mtg Docker контейнера (v1) # Проверяем наличие mtg Docker контейнера (v1)
if command -v docker &>/dev/null; then if command -v docker &>/dev/null; then
if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^${V1_CONTAINER_NAME}$"; then if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -q "^${V1_CONTAINER_NAME}$"; then
return 0 # v1 обнаружена return 0 # v1 обнаружена
fi fi
fi fi
# Проверяем наличие конфига v1 # Проверяем наличие конфига v1
if [ -f "$V1_CONFIG_FILE" ]; then if [ -f "$V1_CONFIG_FILE" ]; then
return 0 return 0
fi fi
return 1 return 1
} }
get_v1_config() { get_v1_config() {
# Извлекаем данные из работающего v1 контейнера # Извлекаем данные из работающего v1 контейнера
if ! command -v docker &>/dev/null; then if ! command -v docker &>/dev/null; then
echo "{}" echo "{}"
return 1 return 1
fi fi
local running local running
running=$(docker ps --format '{{.Names}}' 2>/dev/null | grep "^${V1_CONTAINER_NAME}$") running=$(docker ps --format '{{.Names}}' 2>/dev/null | grep "^${V1_CONTAINER_NAME}$")
if [ -z "$running" ]; then if [ -z "$running" ]; then
# Пробуем из сохранённого конфига # Пробуем из сохранённого конфига
if [ -f "$V1_CONFIG_FILE" ]; then if [ -f "$V1_CONFIG_FILE" ]; then
cat "$V1_CONFIG_FILE" cat "$V1_CONFIG_FILE"
return 0 return 0
fi fi
echo "{}" echo "{}"
return 1 return 1
fi fi
# Достаём из Docker # Достаём из Docker
local cmd_str port secret ip local cmd_str port secret ip
cmd_str=$(docker inspect "$V1_CONTAINER_NAME" --format='{{range .Config.Cmd}}{{.}} {{end}}' 2>/dev/null) cmd_str=$(docker inspect "$V1_CONTAINER_NAME" --format='{{range .Config.Cmd}}{{.}} {{end}}' 2>/dev/null)
secret=$(echo "$cmd_str" | awk '{print $NF}') secret=$(echo "$cmd_str" | awk '{print $NF}')
port=$(docker inspect "$V1_CONTAINER_NAME" --format='{{range $p,$c := .HostConfig.PortBindings}}{{(index $c 0).HostPort}}{{end}}' 2>/dev/null) port=$(docker inspect "$V1_CONTAINER_NAME" --format='{{range $p,$c := .HostConfig.PortBindings}}{{(index $c 0).HostPort}}{{end}}' 2>/dev/null)
ip=$(get_server_ip) ip=$(get_server_ip)
jq -n \ jq -n \
--arg secret "$secret" \ --arg secret "$secret" \
--arg port "${port:-443}" \ --arg port "${port:-443}" \
--arg ip "$ip" \ --arg ip "$ip" \
'{secret: $secret, port: ($port | tonumber), ip: $ip, engine: "mtg"}' '{secret: $secret, port: ($port | tonumber), ip: $ip, engine: "mtg"}'
} }
migrate_v1_to_v2() { migrate_v1_to_v2() {
log_step "Миграция с v1 (mtg) на v2 (telemt)" log_step "Миграция с v1 (mtg) на v2 (telemt)"
local v1_config local v1_config
v1_config=$(get_v1_config) v1_config=$(get_v1_config)
local old_port old_secret local old_port old_secret
old_port=$(echo "$v1_config" | jq -r '.port // 443') old_port=$(echo "$v1_config" | jq -r '.port // 443')
old_secret=$(echo "$v1_config" | jq -r '.secret // empty') old_secret=$(echo "$v1_config" | jq -r '.secret // empty')
if [ -z "$old_secret" ]; then if [ -z "$old_secret" ]; then
log_warning "Не удалось извлечь secret из v1. Будет создан новый." log_warning "Не удалось извлечь secret из v1. Будет создан новый."
return 1 return 1
fi fi
echo "" echo ""
echo -e " ${WHITE}Найдена установка v1 (mtg):${NC}" echo -e " ${WHITE}Найдена установка v1 (mtg):${NC}"
echo -e " Порт: ${CYAN}${old_port}${NC}" echo -e " Порт: ${CYAN}${old_port}${NC}"
echo -e " Secret: ${CYAN}${old_secret:0:16}...${NC}" echo -e " Secret: ${CYAN}${old_secret:0:16}...${NC}"
echo "" echo ""
echo -e " ${YELLOW}Внимание:${NC} секрет mtg НЕ совместим с telemt напрямую." echo -e " ${YELLOW}Внимание:${NC} секрет mtg НЕ совместим с telemt напрямую."
echo -e " Клиентам потребуется новая ссылка." echo -e " Клиентам потребуется новая ссылка."
echo "" echo ""
echo -ne " Остановить v1 контейнер и перейти на v2? [Y/n]: " echo -ne " Остановить v1 контейнер и перейти на v2? [Y/n]: "
read -r ans read -r ans
if [[ "$ans" =~ ^[Nn] ]]; then if [[ "$ans" =~ ^[Nn] ]]; then
log_info "Миграция отменена. v1 оставлен без изменений." log_info "Миграция отменена. v1 оставлен без изменений."
return 1 return 1
fi fi
# Останавливаем v1 # Останавливаем v1
log_info "Остановка v1 контейнера..." log_info "Остановка v1 контейнера..."
docker stop "$V1_CONTAINER_NAME" 2>/dev/null docker stop "$V1_CONTAINER_NAME" 2>/dev/null
docker rm "$V1_CONTAINER_NAME" 2>/dev/null docker rm "$V1_CONTAINER_NAME" 2>/dev/null
# Бекапим v1 конфиг # Бекапим v1 конфиг
if [ -f "$V1_CONFIG_FILE" ]; then if [ -f "$V1_CONFIG_FILE" ]; then
mkdir -p "$GOTELEGRAM_DIR" mkdir -p "$GOTELEGRAM_DIR"
cp "$V1_CONFIG_FILE" "$GOTELEGRAM_DIR/v1_backup_proxy.json" 2>/dev/null cp "$V1_CONFIG_FILE" "$GOTELEGRAM_DIR/v1_backup_proxy.json" 2>/dev/null
log_success "Конфиг v1 сохранён в $GOTELEGRAM_DIR/v1_backup_proxy.json" log_success "Конфиг v1 сохранён в $GOTELEGRAM_DIR/v1_backup_proxy.json"
fi fi
log_success "v1 остановлен. Порт $old_port освобождён." log_success "v1 остановлен. Порт $old_port освобождён."
return 0 return 0
} }
# ── Подтверждение ──────────────────────────────────────────────────────────── # ── Подтверждение ────────────────────────────────────────────────────────────
confirm() { confirm() {
local msg="${1:-Продолжить?}" local msg="${1:-Продолжить?}"
echo -ne " ${msg} [Y/n]: " echo -ne " ${msg} [Y/n]: "
read -r ans read -r ans
[[ ! "$ans" =~ ^[Nn] ]] [[ ! "$ans" =~ ^[Nn] ]]
} }
# ── Выбор из списка ────────────────────────────────────────────────────────── # ── Выбор из списка ──────────────────────────────────────────────────────────
select_option() { select_option() {
local title="$1" local title="$1"
shift shift
local options=("$@") local options=("$@")
echo "" echo ""
echo -e " ${BOLD}${WHITE}${title}${NC}" echo -e " ${BOLD}${WHITE}${title}${NC}"
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
local i=1 local i=1
for opt in "${options[@]}"; do for opt in "${options[@]}"; do
echo -e " ${CYAN}${i})${NC} ${opt}" echo -e " ${CYAN}${i})${NC} ${opt}"
((i++)) ((i++))
done done
echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}" echo -e " ${DIM}$(printf '─%.0s' {1..50})${NC}"
echo -ne " ${WHITE}Выбор:${NC} " echo -ne " ${WHITE}Выбор:${NC} "
read -r choice read -r choice
echo "$choice" echo "$choice"
} }
# ── Генерация случайного hex ───────────────────────────────────────────────── # ── Генерация случайного hex ─────────────────────────────────────────────────
generate_hex() { generate_hex() {
local len="${1:-32}" local len="${1:-32}"
openssl rand -hex "$((len/2))" 2>/dev/null || head -c "$((len/2))" /dev/urandom | xxd -p | tr -d '\n' openssl rand -hex "$((len/2))" 2>/dev/null || head -c "$((len/2))" /dev/urandom | xxd -p | tr -d '\n'
} }
# ── Проверка домена ────────────────────────────────────────────────────────── # ── Проверка домена ──────────────────────────────────────────────────────────
validate_domain() { validate_domain() {
local domain="$1" local domain="$1"
if echo "$domain" | grep -qE '^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'; then if echo "$domain" | grep -qE '^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'; then
return 0 return 0
fi fi
return 1 return 1
} }
# ── Init: создание директорий ──────────────────────────────────────────────── # ── Init: создание директорий ────────────────────────────────────────────────
init_dirs() { init_dirs() {
mkdir -p "$GOTELEGRAM_DIR" "$BACKUP_DIR" /etc/telemt 2>/dev/null mkdir -p "$GOTELEGRAM_DIR" "$BACKUP_DIR" /etc/telemt 2>/dev/null
touch "$LOG_FILE" 2>/dev/null touch "$LOG_FILE" 2>/dev/null
} }

View File

@@ -1,305 +1,305 @@
#!/bin/bash #!/bin/bash
# GoTelegram v2.2 — Управление telemt binary # GoTelegram v2.2 — Управление telemt binary
# Скачивание, обновление, запуск, остановка через systemd # Скачивание, обновление, запуск, остановка через systemd
TELEMT_GITHUB="telemt/telemt" TELEMT_GITHUB="telemt/telemt"
TELEMT_RELEASE_API="https://api.github.com/repos/${TELEMT_GITHUB}/releases/latest" TELEMT_RELEASE_API="https://api.github.com/repos/${TELEMT_GITHUB}/releases/latest"
TELEMT_USER="telemt" TELEMT_USER="telemt"
TELEMT_GROUP="telemt" TELEMT_GROUP="telemt"
# ── Получение последней версии ─────────────────────────────────────────────── # ── Получение последней версии ───────────────────────────────────────────────
get_latest_telemt_version() { get_latest_telemt_version() {
local resp local resp
resp=$(curl -s --max-time 10 "$TELEMT_RELEASE_API" 2>/dev/null) resp=$(curl -s --max-time 10 "$TELEMT_RELEASE_API" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$resp" ]; then if [ $? -ne 0 ] || [ -z "$resp" ]; then
log_error "Не удалось получить информацию о релизах telemt" log_error "Не удалось получить информацию о релизах telemt"
return 1 return 1
fi fi
echo "$resp" | jq -r '.tag_name // empty' echo "$resp" | jq -r '.tag_name // empty'
} }
get_telemt_download_url() { get_telemt_download_url() {
local arch local arch
arch=$(get_arch) arch=$(get_arch)
local resp local resp
resp=$(curl -s --max-time 10 "$TELEMT_RELEASE_API" 2>/dev/null) resp=$(curl -s --max-time 10 "$TELEMT_RELEASE_API" 2>/dev/null)
if [ -z "$resp" ]; then return 1; fi if [ -z "$resp" ]; then return 1; fi
local pattern local pattern
case "$arch" in case "$arch" in
amd64) pattern="linux.*amd64\|linux.*x86_64" ;; amd64) pattern="linux.*(amd64|x86_64)" ;;
arm64) pattern="linux.*arm64\|linux.*aarch64" ;; arm64) pattern="linux.*(arm64|aarch64)" ;;
armv7) pattern="linux.*armv7\|linux.*arm" ;; armv7) pattern="linux.*(armv7|arm)" ;;
*) pattern="linux.*${arch}" ;; *) pattern="linux.*${arch}" ;;
esac esac
echo "$resp" | jq -r ".assets[].browser_download_url" 2>/dev/null | grep -i "$pattern" | head -1 echo "$resp" | jq -r ".assets[].browser_download_url" 2>/dev/null | grep -iE "$pattern" | head -1
} }
# ── Установленная версия ───────────────────────────────────────────────────── # ── Установленная версия ─────────────────────────────────────────────────────
get_installed_telemt_version() { get_installed_telemt_version() {
if [ -x "$TELEMT_BIN" ]; then if [ -x "$TELEMT_BIN" ]; then
"$TELEMT_BIN" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 "$TELEMT_BIN" --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1
else else
echo "" echo ""
fi fi
} }
is_telemt_installed() { is_telemt_installed() {
[ -x "$TELEMT_BIN" ] [ -x "$TELEMT_BIN" ]
} }
# ── Скачивание и установка ─────────────────────────────────────────────────── # ── Скачивание и установка ───────────────────────────────────────────────────
download_telemt() { download_telemt() {
local url local url
url=$(get_telemt_download_url) url=$(get_telemt_download_url)
if [ -z "$url" ]; then if [ -z "$url" ]; then
log_error "Не найден бинарник telemt для архитектуры $(get_arch)" log_error "Не найден бинарник telemt для архитектуры $(get_arch)"
return 1 return 1
fi fi
local tmp_file="/tmp/telemt_download_$$" local tmp_file="/tmp/telemt_download_$$"
log_info "Скачивание: $url" log_info "Скачивание: $url"
if ! curl -L -s --max-time 120 -o "$tmp_file" "$url"; then if ! curl -L -s --max-time 120 -o "$tmp_file" "$url"; then
log_error "Ошибка скачивания telemt" log_error "Ошибка скачивания telemt"
rm -f "$tmp_file" rm -f "$tmp_file"
return 1 return 1
fi fi
# Определяем тип файла и распаковываем # Определяем тип файла и распаковываем
local mime local mime
mime=$(file -b --mime-type "$tmp_file" 2>/dev/null) mime=$(file -b --mime-type "$tmp_file" 2>/dev/null)
case "$mime" in case "$mime" in
application/gzip|application/x-gzip) application/gzip|application/x-gzip)
tar xzf "$tmp_file" -C /tmp/ 2>/dev/null tar xzf "$tmp_file" -C /tmp/ 2>/dev/null
local extracted local extracted
extracted=$(find /tmp -maxdepth 2 -name "telemt" -type f -newer "$tmp_file" 2>/dev/null | head -1) extracted=$(find /tmp -maxdepth 2 -name "telemt" -type f -newer "$tmp_file" 2>/dev/null | head -1)
if [ -z "$extracted" ]; then if [ -z "$extracted" ]; then
# Может быть просто gzip без tar # Может быть просто gzip без tar
gunzip -c "$tmp_file" > /tmp/telemt_bin_$$ 2>/dev/null gunzip -c "$tmp_file" > /tmp/telemt_bin_$$ 2>/dev/null
extracted="/tmp/telemt_bin_$$" extracted="/tmp/telemt_bin_$$"
fi fi
;; ;;
application/x-tar) application/x-tar)
tar xf "$tmp_file" -C /tmp/ 2>/dev/null tar xf "$tmp_file" -C /tmp/ 2>/dev/null
extracted=$(find /tmp -maxdepth 2 -name "telemt" -type f -newer "$tmp_file" 2>/dev/null | head -1) extracted=$(find /tmp -maxdepth 2 -name "telemt" -type f -newer "$tmp_file" 2>/dev/null | head -1)
;; ;;
application/zip) application/zip)
unzip -o "$tmp_file" -d /tmp/telemt_extract_$$ 2>/dev/null unzip -o "$tmp_file" -d /tmp/telemt_extract_$$ 2>/dev/null
extracted=$(find /tmp/telemt_extract_$$ -name "telemt" -type f 2>/dev/null | head -1) extracted=$(find /tmp/telemt_extract_$$ -name "telemt" -type f 2>/dev/null | head -1)
;; ;;
application/octet-stream|application/x-executable) application/octet-stream|application/x-executable)
# Уже бинарник # Уже бинарник
extracted="$tmp_file" extracted="$tmp_file"
;; ;;
*) *)
# Пробуем как бинарник # Пробуем как бинарник
extracted="$tmp_file" extracted="$tmp_file"
;; ;;
esac esac
if [ -z "$extracted" ] || [ ! -f "$extracted" ]; then if [ -z "$extracted" ] || [ ! -f "$extracted" ]; then
log_error "Не удалось извлечь бинарник telemt" log_error "Не удалось извлечь бинарник telemt"
rm -f "$tmp_file" rm -f "$tmp_file"
return 1 return 1
fi fi
# Устанавливаем # Устанавливаем
cp "$extracted" "$TELEMT_BIN" cp "$extracted" "$TELEMT_BIN"
chmod 755 "$TELEMT_BIN" chmod 755 "$TELEMT_BIN"
rm -f "$tmp_file" rm -f "$tmp_file"
rm -rf /tmp/telemt_extract_$$ /tmp/telemt_bin_$$ rm -rf /tmp/telemt_extract_$$ /tmp/telemt_bin_$$
# Проверяем # Проверяем
if "$TELEMT_BIN" --version &>/dev/null; then if "$TELEMT_BIN" --version &>/dev/null; then
log_success "telemt $(get_installed_telemt_version) установлен в $TELEMT_BIN" log_success "telemt $(get_installed_telemt_version) установлен в $TELEMT_BIN"
return 0 return 0
else else
log_error "Бинарник telemt не запускается" log_error "Бинарник telemt не запускается"
return 1 return 1
fi fi
} }
# ── Системный пользователь ─────────────────────────────────────────────────── # ── Системный пользователь ───────────────────────────────────────────────────
create_telemt_user() { create_telemt_user() {
if ! id "$TELEMT_USER" &>/dev/null; then if ! id "$TELEMT_USER" &>/dev/null; then
useradd -r -s /usr/sbin/nologin -d /etc/telemt "$TELEMT_USER" 2>/dev/null useradd -r -s /usr/sbin/nologin -d /etc/telemt "$TELEMT_USER" 2>/dev/null
log_dim "Создан системный пользователь: $TELEMT_USER" log_dim "Создан системный пользователь: $TELEMT_USER"
fi fi
} }
# ── Systemd сервис ─────────────────────────────────────────────────────────── # ── Systemd сервис ───────────────────────────────────────────────────────────
install_telemt_service() { install_telemt_service() {
local config_path="${1:-$TELEMT_CONFIG}" local config_path="${1:-$TELEMT_CONFIG}"
cat > "/etc/systemd/system/${TELEMT_SERVICE}.service" << EOF cat > "/etc/systemd/system/${TELEMT_SERVICE}.service" << EOF
[Unit] [Unit]
Description=GoTelegram MTProxy (telemt engine) Description=GoTelegram MTProxy (telemt engine)
Documentation=https://github.com/telemt/telemt Documentation=https://github.com/telemt/telemt
After=network-online.target After=network-online.target
Wants=network-online.target Wants=network-online.target
[Service] [Service]
Type=simple Type=simple
User=root User=root
ExecStart=$TELEMT_BIN run $config_path ExecStart=$TELEMT_BIN run $config_path
Restart=always Restart=always
RestartSec=5 RestartSec=5
LimitNOFILE=65535 LimitNOFILE=65535
# Безопасность # Безопасность
NoNewPrivileges=true NoNewPrivileges=true
ProtectSystem=strict ProtectSystem=strict
ProtectHome=true ProtectHome=true
ReadWritePaths=/etc/telemt /var/log ReadWritePaths=/etc/telemt /var/log
PrivateTmp=true PrivateTmp=true
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
EOF EOF
systemctl daemon-reload systemctl daemon-reload
log_success "Systemd сервис $TELEMT_SERVICE создан" log_success "Systemd сервис $TELEMT_SERVICE создан"
} }
# ── Управление сервисом ────────────────────────────────────────────────────── # ── Управление сервисом ──────────────────────────────────────────────────────
start_telemt() { start_telemt() {
systemctl start "$TELEMT_SERVICE" 2>/dev/null systemctl start "$TELEMT_SERVICE" 2>/dev/null
sleep 2 sleep 2
if systemctl is-active --quiet "$TELEMT_SERVICE"; then if systemctl is-active --quiet "$TELEMT_SERVICE"; then
log_success "telemt запущен" log_success "telemt запущен"
return 0 return 0
else else
log_error "telemt не запустился" log_error "telemt не запустился"
journalctl -u "$TELEMT_SERVICE" --no-pager -n 10 2>/dev/null journalctl -u "$TELEMT_SERVICE" --no-pager -n 10 2>/dev/null
return 1 return 1
fi fi
} }
stop_telemt() { stop_telemt() {
if systemctl is-active --quiet "$TELEMT_SERVICE" 2>/dev/null; then if systemctl is-active --quiet "$TELEMT_SERVICE" 2>/dev/null; then
systemctl stop "$TELEMT_SERVICE" systemctl stop "$TELEMT_SERVICE"
log_success "telemt остановлен" log_success "telemt остановлен"
else else
log_dim "telemt уже остановлен" log_dim "telemt уже остановлен"
fi fi
} }
restart_telemt() { restart_telemt() {
systemctl restart "$TELEMT_SERVICE" 2>/dev/null systemctl restart "$TELEMT_SERVICE" 2>/dev/null
sleep 2 sleep 2
if systemctl is-active --quiet "$TELEMT_SERVICE"; then if systemctl is-active --quiet "$TELEMT_SERVICE"; then
log_success "telemt перезапущен" log_success "telemt перезапущен"
return 0 return 0
else else
log_error "telemt не перезапустился" log_error "telemt не перезапустился"
return 1 return 1
fi fi
} }
enable_telemt() { enable_telemt() {
systemctl enable "$TELEMT_SERVICE" 2>/dev/null systemctl enable "$TELEMT_SERVICE" 2>/dev/null
} }
telemt_status() { telemt_status() {
if ! is_telemt_installed; then if ! is_telemt_installed; then
echo "not_installed" echo "not_installed"
return return
fi fi
if systemctl is-active --quiet "$TELEMT_SERVICE" 2>/dev/null; then if systemctl is-active --quiet "$TELEMT_SERVICE" 2>/dev/null; then
echo "running" echo "running"
elif systemctl is-enabled --quiet "$TELEMT_SERVICE" 2>/dev/null; then elif systemctl is-enabled --quiet "$TELEMT_SERVICE" 2>/dev/null; then
echo "stopped" echo "stopped"
else else
echo "disabled" echo "disabled"
fi fi
} }
telemt_logs() { telemt_logs() {
local lines="${1:-40}" local lines="${1:-40}"
journalctl -u "$TELEMT_SERVICE" --no-pager -n "$lines" 2>/dev/null journalctl -u "$TELEMT_SERVICE" --no-pager -n "$lines" 2>/dev/null
} }
telemt_uptime() { telemt_uptime() {
local started local started
started=$(systemctl show "$TELEMT_SERVICE" --property=ActiveEnterTimestamp --value 2>/dev/null) started=$(systemctl show "$TELEMT_SERVICE" --property=ActiveEnterTimestamp --value 2>/dev/null)
if [ -n "$started" ] && [ "$started" != "" ]; then if [ -n "$started" ] && [ "$started" != "" ]; then
echo "$started" echo "$started"
else else
echo "N/A" echo "N/A"
fi fi
} }
# ── Обновление ─────────────────────────────────────────────────────────────── # ── Обновление ───────────────────────────────────────────────────────────────
check_telemt_update() { check_telemt_update() {
local current latest local current latest
current=$(get_installed_telemt_version) current=$(get_installed_telemt_version)
latest=$(get_latest_telemt_version) latest=$(get_latest_telemt_version)
if [ -z "$current" ] || [ -z "$latest" ]; then if [ -z "$current" ] || [ -z "$latest" ]; then
return 1 return 1
fi fi
if [ "$current" != "$latest" ]; then if [ "$current" != "$latest" ]; then
echo "$latest" echo "$latest"
return 0 # есть обновление return 0 # есть обновление
fi fi
return 1 # актуально return 1 # актуально
} }
update_telemt() { update_telemt() {
local latest local latest
latest=$(check_telemt_update) latest=$(check_telemt_update)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
log_info "telemt уже последней версии ($(get_installed_telemt_version))" log_info "telemt уже последней версии ($(get_installed_telemt_version))"
return 0 return 0
fi fi
log_info "Доступно обновление: $(get_installed_telemt_version)$latest" log_info "Доступно обновление: $(get_installed_telemt_version)$latest"
if ! confirm "Обновить telemt?"; then if ! confirm "Обновить telemt?"; then
return 0 return 0
fi fi
stop_telemt stop_telemt
if download_telemt; then if download_telemt; then
start_telemt start_telemt
log_success "telemt обновлён до $latest" log_success "telemt обновлён до $latest"
else else
start_telemt # запускаем старую версию обратно start_telemt # запускаем старую версию обратно
log_error "Обновление не удалось" log_error "Обновление не удалось"
return 1 return 1
fi fi
} }
# ── Полная установка telemt ────────────────────────────────────────────────── # ── Полная установка telemt ──────────────────────────────────────────────────
install_telemt_full() { install_telemt_full() {
log_step "Установка telemt" log_step "Установка telemt"
# Создаём директории # Создаём директории
mkdir -p /etc/telemt mkdir -p /etc/telemt
# Скачиваем бинарник # Скачиваем бинарник
run_with_spinner "Скачивание telemt" download_telemt || return 1 run_with_spinner "Скачивание telemt" download_telemt || return 1
# Устанавливаем systemd сервис # Устанавливаем systemd сервис
install_telemt_service install_telemt_service
# Включаем автозапуск # Включаем автозапуск
enable_telemt enable_telemt
log_success "telemt готов к работе" log_success "telemt готов к работе"
return 0 return 0
} }
# ── Удаление telemt ────────────────────────────────────────────────────────── # ── Удаление telemt ──────────────────────────────────────────────────────────
remove_telemt() { remove_telemt() {
stop_telemt stop_telemt
systemctl disable "$TELEMT_SERVICE" 2>/dev/null systemctl disable "$TELEMT_SERVICE" 2>/dev/null
rm -f "/etc/systemd/system/${TELEMT_SERVICE}.service" rm -f "/etc/systemd/system/${TELEMT_SERVICE}.service"
systemctl daemon-reload systemctl daemon-reload
rm -f "$TELEMT_BIN" rm -f "$TELEMT_BIN"
rm -rf /etc/telemt rm -rf /etc/telemt
log_success "telemt полностью удалён" log_success "telemt полностью удалён"
} }