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

View File

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