v2.4.0 — internationalization (EN/RU) + custom git templates

- i18n engine (lib/i18n.sh, lib/lang/en.sh, lib/lang/ru.sh)
- first-run language picker, persisted to .language + config.json
- install.sh, common.sh, backup.sh, templates_catalog.sh wired through t()/tf()
- backup.sh preserves .language marker and records language in metadata.json
- custom git template feature (first item in pro template picker)
  * validates HTTPS URLs, rejects shell metachars
  * 100MB size guard, 90s clone timeout
  * auto-detects index.html in dist/public/build/_site/site/docs/out/www
- bot v2.4.0: i18n.py + lang/{en,ru}.json, /lang command, language toggle button
- bot: custom git template via text input with waiter gating
This commit is contained in:
anten-ka
2026-04-10 11:26:02 +03:00
parent 9c084f37ec
commit 0d087831d8
13 changed files with 2489 additions and 1002 deletions

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# GoTelegram v2.2Бекап и восстановление конфигурации
# GoTelegram v2.4backup and restore (i18n-aware)
# ── Создание бекапа ──────────────────────────────────────────────────────────
create_backup() {
@@ -13,7 +13,7 @@ create_backup() {
mkdir -p "$tmp_dir" "$output_dir"
# Собираем файлы
log_info "Собираю конфигурацию..."
log_info "$(_t_or backup_collecting 'Собираю конфигурацию...')"
# telemt конфиг
if [ -f "$TELEMT_CONFIG" ]; then
@@ -25,6 +25,11 @@ create_backup() {
cp "$GOTELEGRAM_CONFIG" "$tmp_dir/gotelegram.json"
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"
@@ -44,40 +49,46 @@ create_backup() {
if [ -d "$WEBSITE_ROOT" ] && [ -f "$WEBSITE_ROOT/index.html" ]; then
mkdir -p "$tmp_dir/site"
cp -r "$WEBSITE_ROOT"/* "$tmp_dir/site/"
log_dim "Шаблон сайта включён"
log_dim "$(_t_or backup_site_included 'Шаблон сайта включён')"
fi
# Метаданные
local ip mode engine
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.0",
"backup_version": "1.1",
"gotelegram_version": "$GOTELEGRAM_VERSION",
"created_at": "$(date -Iseconds)",
"hostname": "$(hostname)",
"ip": "$ip",
"engine": "$engine",
"mode": "$mode",
"port": $(config_get port 2>/dev/null || echo "443"),
"domain": "$(config_get domain 2>/dev/null)"
"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 "Ошибка создания архива"
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 "Архив не создан"
log_error "$(_t_or backup_archive_missing 'Архив не создан')"
rm -rf "$tmp_dir"
return 1
fi
@@ -88,13 +99,13 @@ EOMETA
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 "Ошибка шифрования"
log_error "$(_t_or backup_encrypt_err 'Ошибка шифрования')"
rm -f "$tar_file"
rm -rf "$tmp_dir"
return 1
fi
rm -f "$tar_file"
log_success "Бекап зашифрован (AES-256-CBC)"
log_success "$(_t_or backup_encrypted 'Бекап зашифрован (AES-256-CBC)')"
else
final_file="${output_dir}/${backup_name}.tar.gz"
mv "$tar_file" "$final_file"
@@ -108,7 +119,11 @@ EOMETA
local size
size=$(du -h "$final_file" | cut -f1)
log_success "Бекап создан: $final_file ($size)"
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
}
@@ -119,7 +134,11 @@ restore_backup() {
local password="$2"
if [ ! -f "$backup_file" ]; then
log_error "Файл не найден: $backup_file"
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
@@ -130,14 +149,14 @@ restore_backup() {
local tar_file=""
if echo "$backup_file" | grep -q '\.enc$'; then
if [ -z "$password" ]; then
echo -ne " Введите пароль от бекапа: "
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 "Неверный пароль или повреждённый файл"
log_error "$(_t_or backup_bad_pass 'Неверный пароль или повреждённый файл')"
rm -rf "$tmp_dir" "$tar_file"
return 1
fi
@@ -148,7 +167,7 @@ restore_backup() {
# Распаковываем
tar xzf "$tar_file" -C "$tmp_dir" 2>/dev/null
if [ $? -ne 0 ]; then
log_error "Ошибка распаковки архива"
log_error "$(_t_or backup_extract_err 'Ошибка распаковки архива')"
rm -rf "$tmp_dir"
return 1
fi
@@ -160,18 +179,20 @@ restore_backup() {
# Проверяем метаданные
if [ -f "$backup_dir/metadata.json" ]; then
local bk_version bk_mode bk_ip
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}📦 Бекап:${NC}"
echo -e " Версия: $bk_version | Режим: $bk_mode | IP: $bk_ip"
echo -e " Дата: $(jq -r '.created_at' "$backup_dir/metadata.json")"
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 ! confirm "Восстановить конфигурацию? Текущие настройки будут перезаписаны."; then
if ! confirm "$(_t_or backup_confirm_restore 'Восстановить конфигурацию? Текущие настройки будут перезаписаны.')"; then
rm -rf "$tmp_dir"
return 0
fi
@@ -185,14 +206,21 @@ restore_backup() {
mkdir -p /etc/telemt
cp "$backup_dir/config.toml" "$TELEMT_CONFIG"
chmod 600 "$TELEMT_CONFIG"
log_success "telemt конфиг восстановлен"
log_success "$(_t_or backup_restored_telemt 'telemt конфиг восстановлен')"
fi
# Восстанавливаем GoTelegram конфиг
if [ -f "$backup_dir/gotelegram.json" ]; then
mkdir -p "$GOTELEGRAM_DIR"
cp "$backup_dir/gotelegram.json" "$GOTELEGRAM_CONFIG"
log_success "GoTelegram конфиг восстановлен"
log_success "$(_t_or backup_restored_gotelegram 'GoTelegram конфиг восстановлен')"
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 конфиг
@@ -200,7 +228,7 @@ restore_backup() {
mkdir -p /etc/nginx/sites-available /etc/nginx/sites-enabled
cp "$backup_dir/nginx.conf" "$NGINX_SITE_CONF"
ln -sf "$NGINX_SITE_CONF" "$NGINX_SITE_LINK"
log_success "nginx конфиг восстановлен"
log_success "$(_t_or backup_restored_nginx 'nginx конфиг восстановлен')"
fi
# Восстанавливаем SSL
@@ -211,7 +239,7 @@ restore_backup() {
local cert_dir="/etc/letsencrypt/live/$domain"
mkdir -p "$cert_dir"
cp "$backup_dir/certs/"* "$cert_dir/" 2>/dev/null
log_success "SSL сертификаты восстановлены"
log_success "$(_t_or backup_restored_ssl 'SSL сертификаты восстановлены')"
fi
fi
@@ -220,7 +248,7 @@ restore_backup() {
mkdir -p "$WEBSITE_ROOT"
cp -r "$backup_dir/site"/* "$WEBSITE_ROOT/"
chown -R www-data:www-data "$WEBSITE_ROOT" 2>/dev/null
log_success "Шаблон сайта восстановлен"
log_success "$(_t_or backup_restored_site 'Шаблон сайта восстановлен')"
fi
# Запускаем сервисы
@@ -233,7 +261,7 @@ restore_backup() {
rm -rf "$tmp_dir"
[ "$tar_file" != "$backup_file" ] && rm -f "$tar_file"
log_success "Восстановление завершено!"
log_success "$(_t_or backup_restore_done 'Восстановление завершено!')"
show_proxy_info
return 0
}
@@ -241,12 +269,12 @@ restore_backup() {
# ── Список бекапов ───────────────────────────────────────────────────────────
list_backups() {
if [ ! -d "$BACKUP_DIR" ] || [ -z "$(ls -A "$BACKUP_DIR" 2>/dev/null)" ]; then
log_info "Бекапов нет"
log_info "$(_t_or backup_none 'Бекапов нет')"
return 1
fi
echo ""
echo -e " ${BOLD}${WHITE}📦 Доступные бекапы:${NC}"
echo -e " ${BOLD}${WHITE}📦 $(_t_or backup_list_title 'Доступные бекапы'):${NC}"
echo -e " ${DIM}$(printf '─%.0s' {1..60})${NC}"
local i=1
@@ -276,31 +304,35 @@ cleanup_old_backups() {
find "$BACKUP_DIR" -name "gotelegram_backup_*.tar.gz*" ! -name "*.sha256" 2>/dev/null | sort | head -n "$to_delete" | while read -r f; do
rm -f "$f" "${f}.sha256"
done
log_dim "Удалено $to_delete старых бекапов (оставлено $keep)"
if type tf &>/dev/null; then
log_dim "$(tf backup_cleanup_fmt "$to_delete" "$keep")"
else
log_dim "Удалено $to_delete старых бекапов (оставлено $keep)"
fi
fi
}
# ── Интерактивный бекап ──────────────────────────────────────────────────────
interactive_backup() {
echo ""
echo -e " ${BOLD}${WHITE}💾 Создание бекапа${NC}"
echo -ne " Зашифровать бекап паролем? [Y/n]: "
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 " Введите пароль: "
echo -ne " $(_t_or backup_enter_pass 'Введите пароль'): "
read -rs password
echo ""
echo -ne " Повторите пароль: "
echo -ne " $(_t_or backup_repeat_pass 'Повторите пароль'): "
read -rs password2
echo ""
if [ "$password" != "$password2" ]; then
log_error "Пароли не совпадают"
log_error "$(_t_or backup_pass_mismatch 'Пароли не совпадают')"
return 1
fi
if [ ${#password} -lt 6 ]; then
log_error "Пароль слишком короткий (минимум 6 символов)"
log_error "$(_t_or backup_pass_short 'Пароль слишком короткий (минимум 6 символов)')"
return 1
fi
fi
@@ -313,7 +345,7 @@ interactive_backup() {
interactive_restore() {
list_backups || return 1
echo -ne " Номер бекапа (или путь к файлу): "
echo -ne " $(_t_or backup_pick_prompt 'Номер бекапа (или путь к файлу)'): "
read -r choice
local backup_file=""
@@ -333,7 +365,7 @@ interactive_restore() {
fi
if [ -z "$backup_file" ]; then
log_error "Бекап не найден"
log_error "$(_t_or backup_not_found 'Бекап не найден')"
return 1
fi