mirror of
https://github.com/anten-ka/gotelegram_pro.git
synced 2026-05-19 14:26:02 +00:00
590 lines
24 KiB
Bash
Executable File
590 lines
24 KiB
Bash
Executable File
#!/bin/bash
|
|
# goTelegram Pro v2.5.0 — backup and restore (i18n-aware)
|
|
|
|
# ── Создание бекапа ──────────────────────────────────────────────────────────
|
|
create_backup() {
|
|
local password="$1"
|
|
local output_dir="${2:-$BACKUP_DIR}"
|
|
local timestamp
|
|
timestamp=$(date +%Y%m%d_%H%M%S)
|
|
local backup_name tmp_dir suffix=0
|
|
|
|
mkdir -p "$output_dir"
|
|
while true; do
|
|
if [ "$suffix" -eq 0 ]; then
|
|
backup_name="gotelegram_backup_${timestamp}"
|
|
else
|
|
backup_name="gotelegram_backup_${timestamp}_${suffix}"
|
|
fi
|
|
tmp_dir="/tmp/${backup_name}"
|
|
if [ ! -e "$tmp_dir" ] && \
|
|
[ ! -e "${output_dir}/${backup_name}.tar.gz" ] && \
|
|
[ ! -e "${output_dir}/${backup_name}.tar.gz.enc" ]; then
|
|
break
|
|
fi
|
|
suffix=$((suffix + 1))
|
|
done
|
|
mkdir -p "$tmp_dir"
|
|
|
|
# Собираем файлы
|
|
log_info "$(_t_or backup_collecting 'Собираю конфигурацию...')"
|
|
|
|
# telemt конфиг
|
|
if [ -f "$TELEMT_CONFIG" ]; then
|
|
cp "$TELEMT_CONFIG" "$tmp_dir/config.toml"
|
|
fi
|
|
|
|
# goTelegram Pro конфиг
|
|
if [ -f "$GOTELEGRAM_CONFIG" ]; then
|
|
cp "$GOTELEGRAM_CONFIG" "$tmp_dir/gotelegram.json"
|
|
fi
|
|
if [ -f "$GOTELEGRAM_DIR/disabled_users.json" ]; then
|
|
cp "$GOTELEGRAM_DIR/disabled_users.json" "$tmp_dir/disabled_users.json" 2>/dev/null
|
|
fi
|
|
if [ -f "$GOTELEGRAM_DIR/backup_schedule.json" ]; then
|
|
cp "$GOTELEGRAM_DIR/backup_schedule.json" "$tmp_dir/backup_schedule.json" 2>/dev/null
|
|
fi
|
|
|
|
# Language marker (i18n)
|
|
if [ -f "$GOTELEGRAM_DIR/.language" ]; then
|
|
cp "$GOTELEGRAM_DIR/.language" "$tmp_dir/.language"
|
|
fi
|
|
|
|
# nginx конфиг (stealth mode)
|
|
if [ -f "$NGINX_SITE_CONF" ]; then
|
|
cp "$NGINX_SITE_CONF" "$tmp_dir/nginx.conf"
|
|
fi
|
|
|
|
# SSL сертификаты и renewal metadata для переносов между VPS
|
|
local domain
|
|
domain=$(config_get domain 2>/dev/null)
|
|
if [ -n "$domain" ] && [ -d "/etc/letsencrypt/live/$domain" ]; then
|
|
mkdir -p "$tmp_dir/letsencrypt/live" "$tmp_dir/letsencrypt/archive" "$tmp_dir/letsencrypt/renewal"
|
|
cp -a "/etc/letsencrypt/live/$domain" "$tmp_dir/letsencrypt/live/" 2>/dev/null
|
|
[ -d "/etc/letsencrypt/archive/$domain" ] && \
|
|
cp -a "/etc/letsencrypt/archive/$domain" "$tmp_dir/letsencrypt/archive/" 2>/dev/null
|
|
[ -f "/etc/letsencrypt/renewal/$domain.conf" ] && \
|
|
cp -a "/etc/letsencrypt/renewal/$domain.conf" "$tmp_dir/letsencrypt/renewal/" 2>/dev/null
|
|
log_dim "SSL сертификаты включены"
|
|
fi
|
|
|
|
# Шаблон сайта (если есть)
|
|
if [ -d "$WEBSITE_ROOT" ] && [ -f "$WEBSITE_ROOT/index.html" ]; then
|
|
mkdir -p "$tmp_dir/site"
|
|
cp -a "$WEBSITE_ROOT/." "$tmp_dir/site/"
|
|
log_dim "$(_t_or backup_site_included 'Шаблон сайта включён')"
|
|
fi
|
|
|
|
# Custom templates and catalog
|
|
if [ -d "$GOTELEGRAM_DIR/custom_templates" ]; then
|
|
mkdir -p "$tmp_dir/custom_templates"
|
|
cp -a "$GOTELEGRAM_DIR/custom_templates/." "$tmp_dir/custom_templates/" 2>/dev/null
|
|
fi
|
|
if [ -f "$GOTELEGRAM_DIR/templates_catalog.json" ]; then
|
|
cp "$GOTELEGRAM_DIR/templates_catalog.json" "$tmp_dir/templates_catalog.json" 2>/dev/null
|
|
fi
|
|
|
|
# Bot state (.env has BotFather token, so encrypted backups are strongly recommended)
|
|
if [ -d "$BOT_DIR" ]; then
|
|
mkdir -p "$tmp_dir/bot"
|
|
[ -f "$BOT_DIR/.env" ] && cp "$BOT_DIR/.env" "$tmp_dir/bot/.env" 2>/dev/null
|
|
[ -f "$BOT_DIR/i18n.py" ] && cp "$BOT_DIR/i18n.py" "$tmp_dir/bot/i18n.py" 2>/dev/null
|
|
[ -d "$BOT_DIR/lang" ] && cp -a "$BOT_DIR/lang" "$tmp_dir/bot/" 2>/dev/null
|
|
fi
|
|
|
|
# Local web admin state
|
|
if [ -d "$ADMIN_WEB_DIR" ]; then
|
|
mkdir -p "$tmp_dir/admin_web"
|
|
[ -f "$ADMIN_WEB_DIR/server.py" ] && cp "$ADMIN_WEB_DIR/server.py" "$tmp_dir/admin_web/server.py" 2>/dev/null
|
|
[ -d "$ADMIN_WEB_DIR/static" ] && cp -a "$ADMIN_WEB_DIR/static" "$tmp_dir/admin_web/" 2>/dev/null
|
|
fi
|
|
|
|
# Traffic history
|
|
if [ -f "$GOTELEGRAM_DIR/stats_history.csv" ]; then
|
|
cp "$GOTELEGRAM_DIR/stats_history.csv" "$tmp_dir/stats_history.csv" 2>/dev/null
|
|
fi
|
|
if [ -f "$GOTELEGRAM_DIR/user_stats_history.csv" ]; then
|
|
cp "$GOTELEGRAM_DIR/user_stats_history.csv" "$tmp_dir/user_stats_history.csv" 2>/dev/null
|
|
fi
|
|
if [ -f "$GOTELEGRAM_DIR/shared-443.json" ]; then
|
|
cp "$GOTELEGRAM_DIR/shared-443.json" "$tmp_dir/shared-443.json" 2>/dev/null
|
|
fi
|
|
|
|
# Метаданные
|
|
local ip mode engine lang port domain
|
|
ip=$(get_server_ip)
|
|
mode=$(config_get mode 2>/dev/null || echo "unknown")
|
|
engine=$(config_get engine 2>/dev/null || echo "telemt")
|
|
lang=$(type get_language &>/dev/null && get_language 2>/dev/null || echo "en")
|
|
port=$(config_get port 2>/dev/null || echo "443")
|
|
# Ensure port is numeric; fall back to 443 if garbage
|
|
[[ "$port" =~ ^[0-9]+$ ]] || port=443
|
|
domain=$(config_get domain 2>/dev/null || echo "")
|
|
|
|
cat > "$tmp_dir/metadata.json" << EOMETA
|
|
{
|
|
"backup_version": "1.6",
|
|
"gotelegram_version": "$GOTELEGRAM_VERSION",
|
|
"created_at": "$(date -Iseconds)",
|
|
"hostname": "$(hostname)",
|
|
"ip": "$ip",
|
|
"engine": "$engine",
|
|
"mode": "$mode",
|
|
"language": "$lang",
|
|
"port": $port,
|
|
"domain": "$domain"
|
|
}
|
|
EOMETA
|
|
|
|
# Архивируем
|
|
local tar_file="/tmp/${backup_name}.tar.gz"
|
|
if ! tar czf "$tar_file" -C /tmp "$backup_name" 2>/dev/null; then
|
|
log_error "$(_t_or backup_archive_err 'Ошибка создания архива')"
|
|
rm -rf "$tmp_dir"
|
|
rm -f "$tar_file"
|
|
return 1
|
|
fi
|
|
|
|
if [ ! -f "$tar_file" ]; then
|
|
log_error "$(_t_or backup_archive_missing 'Архив не создан')"
|
|
rm -rf "$tmp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# Шифруем если задан пароль
|
|
local final_file=""
|
|
if [ -n "$password" ]; then
|
|
final_file="${output_dir}/${backup_name}.tar.gz.enc"
|
|
openssl enc -aes-256-cbc -salt -pbkdf2 -in "$tar_file" -out "$final_file" -pass "pass:${password}" 2>/dev/null
|
|
if [ $? -ne 0 ]; then
|
|
log_error "$(_t_or backup_encrypt_err 'Ошибка шифрования')"
|
|
rm -f "$tar_file"
|
|
rm -rf "$tmp_dir"
|
|
return 1
|
|
fi
|
|
rm -f "$tar_file"
|
|
log_success "$(_t_or backup_encrypted 'Бекап зашифрован (AES-256-CBC)')"
|
|
else
|
|
final_file="${output_dir}/${backup_name}.tar.gz"
|
|
mv "$tar_file" "$final_file"
|
|
fi
|
|
|
|
# SHA256 подпись
|
|
sha256sum "$final_file" > "${final_file}.sha256" 2>/dev/null
|
|
|
|
# Очистка
|
|
rm -rf "$tmp_dir"
|
|
|
|
local size
|
|
size=$(du -h "$final_file" | cut -f1)
|
|
if type tf &>/dev/null; then
|
|
log_success "$(tf backup_created_fmt "$final_file" "$size")"
|
|
else
|
|
log_success "Бекап создан: $final_file ($size)"
|
|
fi
|
|
echo "$final_file"
|
|
return 0
|
|
}
|
|
|
|
# ── Восстановление из бекапа ────────────────────────────────────────────────
|
|
restore_backup() {
|
|
local backup_file="$1"
|
|
local password="$2"
|
|
local assume_yes="$3"
|
|
|
|
if [ ! -f "$backup_file" ]; then
|
|
if type tf &>/dev/null; then
|
|
log_error "$(tf backup_file_not_found_fmt "$backup_file")"
|
|
else
|
|
log_error "Файл не найден: $backup_file"
|
|
fi
|
|
return 1
|
|
fi
|
|
|
|
local tmp_dir="/tmp/gotelegram_restore_$$"
|
|
mkdir -p "$tmp_dir"
|
|
|
|
# Расшифровываем если нужно
|
|
local tar_file=""
|
|
if echo "$backup_file" | grep -q '\.enc$'; then
|
|
if [ -z "$password" ]; then
|
|
echo -ne " $(_t_or backup_enter_pass 'Введите пароль от бекапа'): "
|
|
read -rs password
|
|
echo ""
|
|
fi
|
|
tar_file="/tmp/gotelegram_restore_$$.tar.gz"
|
|
openssl enc -aes-256-cbc -d -pbkdf2 -in "$backup_file" -out "$tar_file" -pass "pass:${password}" 2>/dev/null
|
|
if [ $? -ne 0 ]; then
|
|
log_error "$(_t_or backup_bad_pass 'Неверный пароль или повреждённый файл')"
|
|
rm -rf "$tmp_dir" "$tar_file"
|
|
return 1
|
|
fi
|
|
else
|
|
tar_file="$backup_file"
|
|
fi
|
|
|
|
# Распаковываем
|
|
tar xzf "$tar_file" -C "$tmp_dir" 2>/dev/null
|
|
if [ $? -ne 0 ]; then
|
|
log_error "$(_t_or backup_extract_err 'Ошибка распаковки архива')"
|
|
rm -rf "$tmp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# Находим папку бекапа
|
|
local backup_dir
|
|
backup_dir=$(find "$tmp_dir" -maxdepth 1 -type d -name "gotelegram_backup_*" | head -1)
|
|
[ -z "$backup_dir" ] && backup_dir="$tmp_dir"
|
|
|
|
# Legacy bot backups before v2.5.0 stored absolute paths directly in tar:
|
|
# opt/gotelegram/config.json and etc/telemt/config.toml.
|
|
if [ ! -f "$backup_dir/config.toml" ] && [ -f "$tmp_dir/etc/telemt/config.toml" ]; then
|
|
cp "$tmp_dir/etc/telemt/config.toml" "$backup_dir/config.toml" 2>/dev/null || true
|
|
fi
|
|
if [ ! -f "$backup_dir/gotelegram.json" ] && [ -f "$tmp_dir/opt/gotelegram/config.json" ]; then
|
|
cp "$tmp_dir/opt/gotelegram/config.json" "$backup_dir/gotelegram.json" 2>/dev/null || true
|
|
fi
|
|
if [ ! -f "$backup_dir/disabled_users.json" ] && [ -f "$tmp_dir/opt/gotelegram/disabled_users.json" ]; then
|
|
cp "$tmp_dir/opt/gotelegram/disabled_users.json" "$backup_dir/disabled_users.json" 2>/dev/null || true
|
|
fi
|
|
|
|
# Проверяем метаданные
|
|
if [ -f "$backup_dir/metadata.json" ]; then
|
|
local bk_version bk_mode bk_ip bk_lang bk_date
|
|
bk_version=$(jq -r '.gotelegram_version // "unknown"' "$backup_dir/metadata.json")
|
|
bk_mode=$(jq -r '.mode // "unknown"' "$backup_dir/metadata.json")
|
|
bk_ip=$(jq -r '.ip // "unknown"' "$backup_dir/metadata.json")
|
|
bk_lang=$(jq -r '.language // "-"' "$backup_dir/metadata.json")
|
|
bk_date=$(jq -r '.created_at // "-"' "$backup_dir/metadata.json")
|
|
echo ""
|
|
echo -e " ${BOLD}${WHITE}📦 $(_t_or backup_label 'Бекап'):${NC}"
|
|
echo -e " $(_t_or backup_version_label 'Версия'): $bk_version | $(_t_or backup_mode_label 'Режим'): $bk_mode | IP: $bk_ip | $(_t_or backup_lang_label 'Язык'): $bk_lang"
|
|
echo -e " $(_t_or backup_date_label 'Дата'): $bk_date"
|
|
echo ""
|
|
fi
|
|
|
|
if [ "$assume_yes" != "yes" ] && ! confirm "$(_t_or backup_confirm_restore 'Восстановить конфигурацию? Текущие настройки будут перезаписаны.')"; then
|
|
rm -rf "$tmp_dir"
|
|
return 0
|
|
fi
|
|
|
|
# Останавливаем сервисы
|
|
stop_telemt 2>/dev/null
|
|
systemctl stop nginx 2>/dev/null
|
|
|
|
# Восстанавливаем telemt конфиг
|
|
if [ -f "$backup_dir/config.toml" ]; then
|
|
mkdir -p /etc/telemt
|
|
cp "$backup_dir/config.toml" "$TELEMT_CONFIG"
|
|
chmod 600 "$TELEMT_CONFIG"
|
|
log_success "$(_t_or backup_restored_telemt 'telemt конфиг восстановлен')"
|
|
fi
|
|
|
|
# Восстанавливаем goTelegram Pro конфиг
|
|
if [ -f "$backup_dir/gotelegram.json" ]; then
|
|
mkdir -p "$GOTELEGRAM_DIR"
|
|
cp "$backup_dir/gotelegram.json" "$GOTELEGRAM_CONFIG"
|
|
log_success "$(_t_or backup_restored_gotelegram 'GoTelegram конфиг восстановлен')"
|
|
fi
|
|
if [ -f "$backup_dir/disabled_users.json" ]; then
|
|
mkdir -p "$GOTELEGRAM_DIR"
|
|
cp "$backup_dir/disabled_users.json" "$GOTELEGRAM_DIR/disabled_users.json"
|
|
chmod 600 "$GOTELEGRAM_DIR/disabled_users.json" 2>/dev/null || true
|
|
fi
|
|
if [ -f "$backup_dir/backup_schedule.json" ]; then
|
|
mkdir -p "$GOTELEGRAM_DIR"
|
|
cp "$backup_dir/backup_schedule.json" "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null
|
|
chmod 600 "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || true
|
|
if command -v jq >/dev/null 2>&1; then
|
|
local restored_schedule
|
|
restored_schedule=$(jq -r '.frequency // "off"' "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || echo "off")
|
|
case "$restored_schedule" in
|
|
off|daily|weekly|monthly) set_backup_schedule "$restored_schedule" >/dev/null 2>&1 || true ;;
|
|
esac
|
|
fi
|
|
fi
|
|
|
|
# Восстанавливаем language marker (i18n)
|
|
if [ -f "$backup_dir/.language" ]; then
|
|
mkdir -p "$GOTELEGRAM_DIR"
|
|
cp "$backup_dir/.language" "$GOTELEGRAM_DIR/.language"
|
|
log_success "$(_t_or backup_restored_lang 'Язык интерфейса восстановлен')"
|
|
fi
|
|
|
|
# Восстанавливаем nginx конфиг
|
|
if [ -f "$backup_dir/nginx.conf" ]; then
|
|
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
|
|
cp "$backup_dir/nginx.conf" "$NGINX_SITE_CONF"
|
|
ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
|
|
log_success "$(_t_or backup_restored_nginx 'nginx конфиг восстановлен')"
|
|
fi
|
|
|
|
# Восстанавливаем SSL / Let's Encrypt structure
|
|
if [ -d "$backup_dir/letsencrypt" ]; then
|
|
mkdir -p /etc/letsencrypt/live /etc/letsencrypt/archive /etc/letsencrypt/renewal
|
|
[ -d "$backup_dir/letsencrypt/live" ] && cp -a "$backup_dir/letsencrypt/live/." /etc/letsencrypt/live/ 2>/dev/null
|
|
[ -d "$backup_dir/letsencrypt/archive" ] && cp -a "$backup_dir/letsencrypt/archive/." /etc/letsencrypt/archive/ 2>/dev/null
|
|
[ -d "$backup_dir/letsencrypt/renewal" ] && cp -a "$backup_dir/letsencrypt/renewal/." /etc/letsencrypt/renewal/ 2>/dev/null
|
|
log_success "$(_t_or backup_restored_ssl 'SSL сертификаты восстановлены')"
|
|
elif [ -d "$backup_dir/certs" ]; then
|
|
local domain
|
|
domain=$(config_get domain 2>/dev/null)
|
|
if [ -n "$domain" ]; then
|
|
local cert_dir="/etc/letsencrypt/live/$domain"
|
|
mkdir -p "$cert_dir"
|
|
cp "$backup_dir/certs/"* "$cert_dir/" 2>/dev/null
|
|
log_success "$(_t_or backup_restored_ssl 'SSL сертификаты восстановлены')"
|
|
fi
|
|
fi
|
|
|
|
# Восстанавливаем шаблон сайта
|
|
if [ -d "$backup_dir/site" ]; then
|
|
mkdir -p "$WEBSITE_ROOT"
|
|
cp -a "$backup_dir/site/." "$WEBSITE_ROOT/"
|
|
chown -R www-data:www-data "$WEBSITE_ROOT" 2>/dev/null
|
|
log_success "$(_t_or backup_restored_site 'Шаблон сайта восстановлен')"
|
|
fi
|
|
|
|
# Восстанавливаем custom templates/catalog/statistics
|
|
if [ -d "$backup_dir/custom_templates" ]; then
|
|
mkdir -p "$GOTELEGRAM_DIR/custom_templates"
|
|
cp -a "$backup_dir/custom_templates/." "$GOTELEGRAM_DIR/custom_templates/" 2>/dev/null
|
|
log_success "Пользовательские шаблоны восстановлены"
|
|
fi
|
|
if [ -f "$backup_dir/templates_catalog.json" ]; then
|
|
cp "$backup_dir/templates_catalog.json" "$GOTELEGRAM_DIR/templates_catalog.json" 2>/dev/null
|
|
fi
|
|
if [ -f "$backup_dir/stats_history.csv" ]; then
|
|
cp "$backup_dir/stats_history.csv" "$GOTELEGRAM_DIR/stats_history.csv" 2>/dev/null
|
|
log_success "История статистики восстановлена"
|
|
fi
|
|
if [ -f "$backup_dir/user_stats_history.csv" ]; then
|
|
cp "$backup_dir/user_stats_history.csv" "$GOTELEGRAM_DIR/user_stats_history.csv" 2>/dev/null
|
|
log_success "История статистики пользователей восстановлена"
|
|
fi
|
|
if [ -f "$backup_dir/shared-443.json" ]; then
|
|
cp "$backup_dir/shared-443.json" "$GOTELEGRAM_DIR/shared-443.json" 2>/dev/null
|
|
fi
|
|
|
|
# Восстанавливаем состояние бота
|
|
if [ -d "$backup_dir/bot" ]; then
|
|
mkdir -p "$BOT_DIR"
|
|
[ -f "$backup_dir/bot/.env" ] && cp "$backup_dir/bot/.env" "$BOT_DIR/.env" 2>/dev/null && chmod 600 "$BOT_DIR/.env"
|
|
[ -d "$backup_dir/bot/lang" ] && cp -a "$backup_dir/bot/lang" "$BOT_DIR/" 2>/dev/null
|
|
[ -f "$backup_dir/bot/i18n.py" ] && cp "$backup_dir/bot/i18n.py" "$BOT_DIR/i18n.py" 2>/dev/null
|
|
log_success "Конфигурация Telegram-бота восстановлена"
|
|
fi
|
|
|
|
# Восстанавливаем состояние локальной web-админки
|
|
if [ -d "$backup_dir/admin_web" ]; then
|
|
mkdir -p "$ADMIN_WEB_DIR"
|
|
[ -f "$backup_dir/admin_web/server.py" ] && cp "$backup_dir/admin_web/server.py" "$ADMIN_WEB_DIR/server.py" 2>/dev/null
|
|
[ -d "$backup_dir/admin_web/static" ] && cp -a "$backup_dir/admin_web/static" "$ADMIN_WEB_DIR/" 2>/dev/null
|
|
rm -f "$ADMIN_WEB_DIR/token" 2>/dev/null || true
|
|
log_success "Конфигурация web-админки восстановлена"
|
|
fi
|
|
|
|
# Запускаем сервисы
|
|
if is_telemt_installed && [ ! -f "/etc/systemd/system/${TELEMT_SERVICE}.service" ]; then
|
|
install_telemt_service
|
|
fi
|
|
if is_telemt_installed; then
|
|
start_telemt
|
|
fi
|
|
command -v nginx &>/dev/null && systemctl start nginx 2>/dev/null
|
|
systemctl restart gotelegram-bot 2>/dev/null || true
|
|
systemctl restart gotelegram-admin 2>/dev/null || true
|
|
|
|
# Очистка
|
|
rm -rf "$tmp_dir"
|
|
[ "$tar_file" != "$backup_file" ] && rm -f "$tar_file"
|
|
|
|
log_success "$(_t_or backup_restore_done 'Восстановление завершено!')"
|
|
show_proxy_info
|
|
return 0
|
|
}
|
|
|
|
# ── Список бекапов ───────────────────────────────────────────────────────────
|
|
list_backups() {
|
|
if [ ! -d "$BACKUP_DIR" ] || [ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then
|
|
log_info "$(_t_or backup_none 'Бекапов нет')"
|
|
return 1
|
|
fi
|
|
|
|
echo ""
|
|
echo -e " ${BOLD}${WHITE}📦 $(_t_or backup_list_title 'Доступные бекапы'):${NC}"
|
|
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
|
|
|
local i=1
|
|
for f in "$BACKUP_DIR"/*.tar.gz*; do
|
|
[ -f "$f" ] || continue
|
|
[[ "$f" == *.sha256 ]] && continue
|
|
local size date_str name
|
|
size=$(du -h "$f" | cut -f1)
|
|
name=$(basename "$f")
|
|
date_str=$(echo "$name" | grep -oE '[0-9]{8}_[0-9]{6}' | head -1)
|
|
local encrypted=""
|
|
[[ "$f" == *.enc ]] && encrypted=" 🔒"
|
|
echo -e " ${CYAN}${i})${NC} ${name} (${size})${encrypted}"
|
|
((i++))
|
|
done
|
|
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
|
|
}
|
|
|
|
# ── Очистка старых бекапов ───────────────────────────────────────────────────
|
|
cleanup_old_backups() {
|
|
local keep="${1:-5}"
|
|
local count
|
|
count=$(find "$BACKUP_DIR" -name "*.tar.gz*" ! -name "*.sha256" 2>/dev/null | wc -l)
|
|
|
|
if [ "$count" -gt "$keep" ]; then
|
|
local to_delete=$((count - keep))
|
|
find "$BACKUP_DIR" -name "*.tar.gz*" ! -name "*.sha256" 2>/dev/null | sort | head -n "$to_delete" | while read -r f; do
|
|
rm -f "$f" "${f}.sha256"
|
|
done
|
|
if type tf &>/dev/null; then
|
|
log_dim "$(tf backup_cleanup_fmt "$to_delete" "$keep")"
|
|
else
|
|
log_dim "Удалено $to_delete старых бекапов (оставлено $keep)"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# ── Расписание бекапов ───────────────────────────────────────────────────────
|
|
backup_schedule_calendar() {
|
|
case "${1:-off}" in
|
|
off) echo "" ;;
|
|
daily) echo "*-*-* 03:20:00" ;;
|
|
weekly) echo "Sun 03:20:00" ;;
|
|
monthly) echo "*-*-01 03:20:00" ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
set_backup_schedule() {
|
|
local frequency="${1:-off}"
|
|
local calendar
|
|
if ! calendar=$(backup_schedule_calendar "$frequency"); then
|
|
log_error "Unsupported backup schedule: $frequency"
|
|
return 1
|
|
fi
|
|
|
|
mkdir -p "$GOTELEGRAM_DIR" "$BACKUP_DIR"
|
|
|
|
if [ "$frequency" = "off" ]; then
|
|
systemctl disable --now gotelegram-backup.timer >/dev/null 2>&1 || true
|
|
rm -f /etc/systemd/system/gotelegram-backup.timer /etc/systemd/system/gotelegram-backup.service
|
|
systemctl daemon-reload >/dev/null 2>&1 || true
|
|
else
|
|
cat > /etc/systemd/system/gotelegram-backup.service << 'EOSVC'
|
|
[Unit]
|
|
Description=goTelegram Pro backup
|
|
Wants=network-online.target
|
|
After=network-online.target
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
Environment=GOTELEGRAM_BACKUP_KEEP=30
|
|
ExecStart=/bin/bash -lc 'source /opt/gotelegram/lib/common.sh; source /opt/gotelegram/lib/i18n.sh; source /opt/gotelegram/lib/telemt.sh; source /opt/gotelegram/lib/website.sh; source /opt/gotelegram/lib/backup.sh; load_language "$(detect_language 2>/dev/null || echo en)"; create_backup ""; cleanup_old_backups "${GOTELEGRAM_BACKUP_KEEP:-30}"'
|
|
EOSVC
|
|
|
|
cat > /etc/systemd/system/gotelegram-backup.timer << EOTIMER
|
|
[Unit]
|
|
Description=goTelegram Pro scheduled backup
|
|
|
|
[Timer]
|
|
OnCalendar=$calendar
|
|
Persistent=true
|
|
RandomizedDelaySec=15m
|
|
Unit=gotelegram-backup.service
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
EOTIMER
|
|
systemctl daemon-reload >/dev/null 2>&1 || return 1
|
|
systemctl enable --now gotelegram-backup.timer >/dev/null 2>&1 || return 1
|
|
fi
|
|
|
|
cat > "$GOTELEGRAM_DIR/backup_schedule.json" << EOSCHEDULE
|
|
{
|
|
"frequency": "$frequency",
|
|
"calendar": "$calendar",
|
|
"keep": 30,
|
|
"updated_at": "$(date -Iseconds)"
|
|
}
|
|
EOSCHEDULE
|
|
chmod 600 "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || true
|
|
log_success "Backup schedule: $frequency"
|
|
echo "$frequency"
|
|
}
|
|
|
|
backup_schedule_status() {
|
|
local frequency="off" calendar=""
|
|
if [ -f "$GOTELEGRAM_DIR/backup_schedule.json" ] && command -v jq >/dev/null 2>&1; then
|
|
frequency=$(jq -r '.frequency // "off"' "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || echo "off")
|
|
calendar=$(jq -r '.calendar // ""' "$GOTELEGRAM_DIR/backup_schedule.json" 2>/dev/null || echo "")
|
|
fi
|
|
echo "frequency=$frequency calendar=$calendar"
|
|
systemctl list-timers gotelegram-backup.timer --no-pager 2>/dev/null || true
|
|
}
|
|
|
|
# ── Интерактивный бекап ──────────────────────────────────────────────────────
|
|
interactive_backup() {
|
|
echo ""
|
|
echo -e " ${BOLD}${WHITE}💾 $(_t_or backup_create_title 'Создание бекапа')${NC}"
|
|
echo -ne " $(_t_or backup_encrypt_prompt 'Зашифровать бекап паролем?') [Y/n]: "
|
|
read -r use_pass
|
|
|
|
local password=""
|
|
if [[ ! "$use_pass" =~ ^[Nn] ]]; then
|
|
echo -ne " $(_t_or backup_enter_pass 'Введите пароль'): "
|
|
read -rs password
|
|
echo ""
|
|
echo -ne " $(_t_or backup_repeat_pass 'Повторите пароль'): "
|
|
read -rs password2
|
|
echo ""
|
|
if [ "$password" != "$password2" ]; then
|
|
log_error "$(_t_or backup_pass_mismatch 'Пароли не совпадают')"
|
|
return 1
|
|
fi
|
|
if [ ${#password} -lt 6 ]; then
|
|
log_error "$(_t_or backup_pass_short 'Пароль слишком короткий (минимум 6 символов)')"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
create_backup "$password"
|
|
cleanup_old_backups
|
|
}
|
|
|
|
# ── Интерактивное восстановление ─────────────────────────────────────────────
|
|
interactive_restore() {
|
|
list_backups || return 1
|
|
|
|
echo -ne " $(_t_or backup_pick_prompt 'Номер бекапа (или путь к файлу)'): "
|
|
read -r choice
|
|
|
|
local backup_file=""
|
|
if [[ "$choice" =~ ^[0-9]+$ ]]; then
|
|
local i=1
|
|
for f in "$BACKUP_DIR"/*.tar.gz*; do
|
|
[ -f "$f" ] || continue
|
|
[[ "$f" == *.sha256 ]] && continue
|
|
if [ "$i" -eq "$choice" ]; then
|
|
backup_file="$f"
|
|
break
|
|
fi
|
|
((i++))
|
|
done
|
|
elif [ -f "$choice" ]; then
|
|
backup_file="$choice"
|
|
fi
|
|
|
|
if [ -z "$backup_file" ]; then
|
|
log_error "$(_t_or backup_not_found 'Бекап не найден')"
|
|
return 1
|
|
fi
|
|
|
|
restore_backup "$backup_file"
|
|
}
|