v1.2.0 + v1.3.0: Localization, About dialog, TOTP/2FA, stability improvements

v1.2.0:
- GUI localization (EN/RU/ZH) with language switcher and persistent selection
- About dialog (ⓘ) with app info, features, quick start guide
- core/i18n.py — internationalization module with t() function
- All GUI components translated via t() keys

v1.3.0:
- TOTP/2FA tab — Google Authenticator compatible codes with live 30s countdown,
  one-click copy, per-server secret management
- core/totp.py — TOTP module (pyotp, RFC 6238)
- core/logger.py — rotating file logger (5MB, 3 backups)
- Stronger Fernet encryption key with automatic migration from old key
- Thread-safe server store with locks, atomic writes, auto-restore on corruption
- Parallel status checks via ThreadPoolExecutor (up to 10 concurrent)
- SSH client: explicit channel cleanup, Unix key permissions
- Server dialog: port validation (1-65535), TOTP secret field
- Language change preserves active tab and server selection
- pyotp dependency added

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chrome-storm-c442
2026-02-23 11:07:51 -05:00
parent f86d6a7214
commit bf39fd7b67
26 changed files with 2029 additions and 246 deletions

590
core/i18n.py Normal file
View File

@@ -0,0 +1,590 @@
"""
Internationalization module — translations for EN/RU/ZH.
"""
LANGUAGES = {"en": "English", "ru": "Русский", "zh": "中文"}
_current_lang = "en"
def get_language() -> str:
return _current_lang
def set_language(lang: str):
global _current_lang
if lang in LANGUAGES:
_current_lang = lang
def t(key: str) -> str:
"""Return translated string for key. Falls back to English."""
text = _TRANSLATIONS.get(_current_lang, {}).get(key)
if text is None:
text = _TRANSLATIONS["en"].get(key, key)
return text
_EN = {
# Sidebar
"servers": "Servers",
"search": "Search...",
"add": "+ Add",
"edit": "Edit",
"delete": "Delete",
# Tabs
"terminal": "Terminal",
"files": "Files",
"info": "Info",
"keys": "Keys",
"setup": "Setup",
# About
"about": "",
"about_title": "ServerManager",
"about_desc": (
"Desktop application for managing remote servers.\n"
"SSH terminal, SFTP file transfer, key management,\n"
"encrypted credentials, and Claude Code integration."
),
"about_features_title": "⚡ Features",
"about_features": (
"• SSH terminal with auto-sudo\n"
"• SFTP file transfer with progress\n"
"• SSH key management\n"
"• TOTP / 2FA (Google Authenticator)\n"
"• Encrypted credentials (Fernet)\n"
"• Automatic backups\n"
"• Claude Code integration"
),
"about_howto_title": "🚀 Quick Start",
"about_howto": (
"1. Click \"+ Add\" to add a server\n"
"2. Select server → Terminal / Files\n"
"3. Setup tab → Claude Code integration"
),
"version": "Version",
"author": "Author",
"close": "Close",
# Language
"language": "Language",
# Delete confirmation
"delete_server": "Delete Server",
"delete_confirm": "Remove '{alias}'?",
# Server dialog
"add_server": "Add Server",
"edit_server": "Edit Server",
"alias": "Alias",
"ip": "IP / Hostname",
"type": "Type",
"port": "Port",
"username": "Username",
"password": "Password",
"notes": "Notes",
"save": "Save",
"cancel": "Cancel",
"show": "Show",
"hide": "Hide",
"alias_required": "Alias is required",
"ip_required": "IP is required",
"port_must_be_number": "Port must be a number",
"error_prefix": "Error: {msg}",
"placeholder_alias": "my-server",
"placeholder_ip": "1.2.3.4",
"placeholder_port": "22",
"placeholder_user": "root",
"placeholder_password": "password",
"placeholder_notes": "optional description",
# Terminal
"sudo": "sudo",
"enter_command": "Enter command...",
"run": "Run",
"clear": "Clear",
"no_server_selected": "[!] No server selected",
"server_not_found": "[!] Server '{alias}' not found",
# Files
"upload": "Upload",
"download": "Download",
"local": "Local:",
"remote": "Remote:",
"browse": "Browse",
"both_paths_required": "[!] Both paths required",
"file_not_found": "[!] File not found: {path}",
"upload_ok": "OK: {local} -> {alias}:{remote}",
"download_ok": "OK: {alias}:{remote} -> {local}",
"placeholder_local_file": "/path/to/local/file",
"placeholder_remote_file": "/remote/path/file",
"placeholder_save_path": "/path/to/save",
# Info
"no_server_selected_info": "No server selected",
"info_alias": "Alias:",
"info_ip": "IP:",
"info_port": "Port:",
"info_user": "User:",
"info_type": "Type:",
"info_notes": "Notes:",
"info_status": "Status:",
"edit_server_btn": "Edit Server",
# Keys
"ssh_key": "SSH Key",
"key_path": "Path: {path}",
"generate_key": "Generate Key",
"key_exists": "Key exists",
"no_key_found": "No key found. Click 'Generate Key' to create one.",
"install_on_server": "Install on Server",
"installing": "Installing...",
"copy_public_key": "Copy Public Key",
"key_copied": "Public key copied to clipboard",
"no_public_key": "[!] No public key to copy",
# Setup
"claude_integration": "Claude Code Integration",
"claude_desc": (
"Setup everything so Claude Code can manage your servers via /ssh skill.\n"
"Both GUI and Claude Code share the same servers.json — add a server here,\n"
"Claude sees it immediately."
),
"status": "Status",
"status_shared_dir": "Shared config dir (~/.server-connections)",
"status_servers_json": "servers.json",
"status_ssh_script": "ssh.py (CLI tool)",
"status_encryption": "Encryption module",
"status_skill": "/ssh skill for Claude Code",
"status_ssh_key": "SSH key (ed25519)",
"install_everything": "Install Everything",
"installing_all": "Installing...",
"install_ssh_py": "ssh.py",
"install_skill": "/ssh skill",
"install_ssh_key": "SSH key",
"refresh": "Refresh",
"configuration": "Configuration",
"config_label": "Config:",
"change_path": "Change Path",
"backup_now": "Backup Now",
"select_backup": "Select backup...",
"no_backups": "No backups",
"restore": "Restore",
"install_done": "Done! Claude Code can now use /ssh to manage your servers.",
"config_changed": "Config path changed: {path}",
"backup_created": "Backup created: {name}",
"backup_failed": "Backup failed: {e}",
"no_backup_selected": "No backup selected.",
"restore_backup_title": "Restore Backup",
"restore_confirm": "Restore from '{name}'?\nCurrent data will be overwritten.",
"restored": "Restored from: {name}",
"restore_failed": "Restore failed: {e}",
"select_servers_json": "Select servers.json",
# TOTP / 2FA
"totp": "2FA",
"totp_title": "Two-Factor Authentication (TOTP)",
"totp_desc": (
"Google Authenticator compatible 2FA codes.\n"
"Add a TOTP secret to any server — the code refreshes every 30 seconds.\n"
"Click the code to copy it to clipboard."
),
"totp_copy": "Copy Code",
"totp_secret_label": "TOTP Secret (Base32)",
"totp_secret_placeholder": "JBSWY3DPEHPK3PXP...",
"totp_save_secret": "Save",
"totp_remove_secret": "Remove",
"totp_generate_secret": "Generate Random Secret",
"totp_no_secret": "No TOTP secret configured",
"totp_remaining": "{sec}s remaining",
"totp_copied": "Code copied to clipboard",
"totp_no_code": "No code to copy",
"totp_secret_empty": "Secret cannot be empty",
"totp_secret_invalid": "Invalid TOTP secret (must be Base32)",
"totp_secret_saved": "TOTP secret saved",
"totp_secret_removed": "TOTP secret removed",
"totp_secret_generated": "Random secret generated (click Save to store)",
"totp_secret_dialog": "TOTP Secret",
"placeholder_totp_secret": "Base32 secret (optional)",
"port_out_of_range": "Port must be 1-65535",
}
_RU = {
# Sidebar
"servers": "Серверы",
"search": "Поиск...",
"add": "+ Добавить",
"edit": "Изменить",
"delete": "Удалить",
# Tabs
"terminal": "Терминал",
"files": "Файлы",
"info": "Инфо",
"keys": "Ключи",
"setup": "Настройка",
# About
"about": "",
"about_title": "ServerManager",
"about_desc": (
"Настольное приложение для управления удалёнными серверами.\n"
"SSH-терминал, SFTP-передача файлов, управление ключами,\n"
"шифрование паролей и интеграция с Claude Code."
),
"about_features_title": "⚡ Возможности",
"about_features": (
"• SSH-терминал с авто-sudo\n"
"• SFTP-передача файлов с прогрессом\n"
"• Управление SSH-ключами\n"
"• TOTP / 2FA (Google Authenticator)\n"
"• Шифрование паролей (Fernet)\n"
"• Автоматические бэкапы\n"
"• Интеграция с Claude Code"
),
"about_howto_title": "🚀 Быстрый старт",
"about_howto": (
"1. Нажмите \"+ Добавить\" для добавления сервера\n"
"2. Выберите сервер → Терминал / Файлы\n"
"3. Вкладка Настройка → интеграция Claude Code"
),
"version": "Версия",
"author": "Автор",
"close": "Закрыть",
# Language
"language": "Язык",
# Delete confirmation
"delete_server": "Удалить сервер",
"delete_confirm": "Удалить '{alias}'?",
# Server dialog
"add_server": "Добавить сервер",
"edit_server": "Изменить сервер",
"alias": "Алиас",
"ip": "IP / Хост",
"type": "Тип",
"port": "Порт",
"username": "Пользователь",
"password": "Пароль",
"notes": "Заметки",
"save": "Сохранить",
"cancel": "Отмена",
"show": "Показать",
"hide": "Скрыть",
"alias_required": "Алиас обязателен",
"ip_required": "IP обязателен",
"port_must_be_number": "Порт должен быть числом",
"error_prefix": "Ошибка: {msg}",
"placeholder_alias": "мой-сервер",
"placeholder_ip": "1.2.3.4",
"placeholder_port": "22",
"placeholder_user": "root",
"placeholder_password": "пароль",
"placeholder_notes": "описание (необязательно)",
# Terminal
"sudo": "sudo",
"enter_command": "Введите команду...",
"run": "Выполнить",
"clear": "Очистить",
"no_server_selected": "[!] Сервер не выбран",
"server_not_found": "[!] Сервер '{alias}' не найден",
# Files
"upload": "Загрузить",
"download": "Скачать",
"local": "Локальный:",
"remote": "Удалённый:",
"browse": "Обзор",
"both_paths_required": "[!] Оба пути обязательны",
"file_not_found": "[!] Файл не найден: {path}",
"upload_ok": "OK: {local} -> {alias}:{remote}",
"download_ok": "OK: {alias}:{remote} -> {local}",
"placeholder_local_file": "/путь/к/локальному/файлу",
"placeholder_remote_file": "/удалённый/путь/файл",
"placeholder_save_path": "/путь/для/сохранения",
# Info
"no_server_selected_info": "Сервер не выбран",
"info_alias": "Алиас:",
"info_ip": "IP:",
"info_port": "Порт:",
"info_user": "Пользователь:",
"info_type": "Тип:",
"info_notes": "Заметки:",
"info_status": "Статус:",
"edit_server_btn": "Изменить сервер",
# Keys
"ssh_key": "SSH-ключ",
"key_path": "Путь: {path}",
"generate_key": "Создать ключ",
"key_exists": "Ключ существует",
"no_key_found": "Ключ не найден. Нажмите 'Создать ключ'.",
"install_on_server": "Установить на сервер",
"installing": "Установка...",
"copy_public_key": "Копировать ключ",
"key_copied": "Публичный ключ скопирован",
"no_public_key": "[!] Нет публичного ключа",
# Setup
"claude_integration": "Интеграция с Claude Code",
"claude_desc": (
"Настройте всё, чтобы Claude Code мог управлять серверами через скилл /ssh.\n"
"GUI и Claude Code используют один servers.json — добавьте сервер здесь,\n"
"Claude увидит его сразу."
),
"status": "Статус",
"status_shared_dir": "Общий каталог (~/.server-connections)",
"status_servers_json": "servers.json",
"status_ssh_script": "ssh.py (CLI-утилита)",
"status_encryption": "Модуль шифрования",
"status_skill": "Скилл /ssh для Claude Code",
"status_ssh_key": "SSH-ключ (ed25519)",
"install_everything": "Установить всё",
"installing_all": "Установка...",
"install_ssh_py": "ssh.py",
"install_skill": "Скилл /ssh",
"install_ssh_key": "SSH-ключ",
"refresh": "Обновить",
"configuration": "Конфигурация",
"config_label": "Конфиг:",
"change_path": "Изменить путь",
"backup_now": "Бэкап сейчас",
"select_backup": "Выберите бэкап...",
"no_backups": "Нет бэкапов",
"restore": "Восстановить",
"install_done": "Готово! Claude Code теперь может использовать /ssh для управления серверами.",
"config_changed": "Путь конфига изменён: {path}",
"backup_created": "Бэкап создан: {name}",
"backup_failed": "Ошибка бэкапа: {e}",
"no_backup_selected": "Бэкап не выбран.",
"restore_backup_title": "Восстановление бэкапа",
"restore_confirm": "Восстановить из '{name}'?\nТекущие данные будут перезаписаны.",
"restored": "Восстановлено из: {name}",
"restore_failed": "Ошибка восстановления: {e}",
"select_servers_json": "Выберите servers.json",
# TOTP / 2FA
"totp": "2FA",
"totp_title": "Двухфакторная аутентификация (TOTP)",
"totp_desc": (
"Коды 2FA, совместимые с Google Authenticator.\n"
"Добавьте TOTP-секрет к серверу — код обновляется каждые 30 секунд.\n"
"Нажмите на код, чтобы скопировать в буфер обмена."
),
"totp_copy": "Копировать код",
"totp_secret_label": "TOTP-секрет (Base32)",
"totp_secret_placeholder": "JBSWY3DPEHPK3PXP...",
"totp_save_secret": "Сохранить",
"totp_remove_secret": "Удалить",
"totp_generate_secret": "Сгенерировать секрет",
"totp_no_secret": "TOTP-секрет не настроен",
"totp_remaining": "Осталось {sec}с",
"totp_copied": "Код скопирован в буфер обмена",
"totp_no_code": "Нет кода для копирования",
"totp_secret_empty": "Секрет не может быть пустым",
"totp_secret_invalid": "Недопустимый TOTP-секрет (должен быть Base32)",
"totp_secret_saved": "TOTP-секрет сохранён",
"totp_secret_removed": "TOTP-секрет удалён",
"totp_secret_generated": "Случайный секрет создан (нажмите Сохранить)",
"totp_secret_dialog": "TOTP-секрет",
"placeholder_totp_secret": "Base32 секрет (необязательно)",
"port_out_of_range": "Порт должен быть от 1 до 65535",
}
_ZH = {
# Sidebar
"servers": "服务器",
"search": "搜索...",
"add": "+ 添加",
"edit": "编辑",
"delete": "删除",
# Tabs
"terminal": "终端",
"files": "文件",
"info": "信息",
"keys": "密钥",
"setup": "设置",
# About
"about": "",
"about_title": "ServerManager",
"about_desc": (
"用于管理远程服务器的桌面应用程序。\n"
"SSH终端、SFTP文件传输、密钥管理、\n"
"凭据加密以及Claude Code集成。"
),
"about_features_title": "⚡ 功能特点",
"about_features": (
"• SSH终端自动sudo\n"
"• SFTP文件传输含进度条\n"
"• SSH密钥管理\n"
"• TOTP / 2FAGoogle Authenticator\n"
"• 凭据加密Fernet\n"
"• 自动备份\n"
"• Claude Code集成"
),
"about_howto_title": "🚀 快速开始",
"about_howto": (
"1. 点击\"+ 添加\"来添加服务器\n"
"2. 选择服务器 → 终端 / 文件\n"
"3. 设置标签 → Claude Code集成"
),
"version": "版本",
"author": "作者",
"close": "关闭",
# Language
"language": "语言",
# Delete confirmation
"delete_server": "删除服务器",
"delete_confirm": "删除 '{alias}'",
# Server dialog
"add_server": "添加服务器",
"edit_server": "编辑服务器",
"alias": "别名",
"ip": "IP / 主机名",
"type": "类型",
"port": "端口",
"username": "用户名",
"password": "密码",
"notes": "备注",
"save": "保存",
"cancel": "取消",
"show": "显示",
"hide": "隐藏",
"alias_required": "别名不能为空",
"ip_required": "IP不能为空",
"port_must_be_number": "端口必须是数字",
"error_prefix": "错误:{msg}",
"placeholder_alias": "我的服务器",
"placeholder_ip": "1.2.3.4",
"placeholder_port": "22",
"placeholder_user": "root",
"placeholder_password": "密码",
"placeholder_notes": "可选描述",
# Terminal
"sudo": "sudo",
"enter_command": "输入命令...",
"run": "执行",
"clear": "清除",
"no_server_selected": "[!] 未选择服务器",
"server_not_found": "[!] 未找到服务器 '{alias}'",
# Files
"upload": "上传",
"download": "下载",
"local": "本地:",
"remote": "远程:",
"browse": "浏览",
"both_paths_required": "[!] 两个路径都必须填写",
"file_not_found": "[!] 文件未找到:{path}",
"upload_ok": "OK: {local} -> {alias}:{remote}",
"download_ok": "OK: {alias}:{remote} -> {local}",
"placeholder_local_file": "/本地/文件/路径",
"placeholder_remote_file": "/远程/路径/文件",
"placeholder_save_path": "/保存/路径",
# Info
"no_server_selected_info": "未选择服务器",
"info_alias": "别名:",
"info_ip": "IP",
"info_port": "端口:",
"info_user": "用户:",
"info_type": "类型:",
"info_notes": "备注:",
"info_status": "状态:",
"edit_server_btn": "编辑服务器",
# Keys
"ssh_key": "SSH密钥",
"key_path": "路径:{path}",
"generate_key": "生成密钥",
"key_exists": "密钥已存在",
"no_key_found": "未找到密钥。点击'生成密钥'来创建。",
"install_on_server": "安装到服务器",
"installing": "安装中...",
"copy_public_key": "复制公钥",
"key_copied": "公钥已复制到剪贴板",
"no_public_key": "[!] 没有公钥可复制",
# Setup
"claude_integration": "Claude Code集成",
"claude_desc": (
"设置一切以便Claude Code通过/ssh技能管理您的服务器。\n"
"GUI和Claude Code共享同一个servers.json — 在此添加服务器,\n"
"Claude会立即看到。"
),
"status": "状态",
"status_shared_dir": "共享配置目录 (~/.server-connections)",
"status_servers_json": "servers.json",
"status_ssh_script": "ssh.pyCLI工具",
"status_encryption": "加密模块",
"status_skill": "Claude Code的/ssh技能",
"status_ssh_key": "SSH密钥ed25519",
"install_everything": "全部安装",
"installing_all": "安装中...",
"install_ssh_py": "ssh.py",
"install_skill": "/ssh技能",
"install_ssh_key": "SSH密钥",
"refresh": "刷新",
"configuration": "配置",
"config_label": "配置:",
"change_path": "更改路径",
"backup_now": "立即备份",
"select_backup": "选择备份...",
"no_backups": "无备份",
"restore": "恢复",
"install_done": "完成Claude Code现在可以使用/ssh来管理您的服务器。",
"config_changed": "配置路径已更改:{path}",
"backup_created": "备份已创建:{name}",
"backup_failed": "备份失败:{e}",
"no_backup_selected": "未选择备份。",
"restore_backup_title": "恢复备份",
"restore_confirm": "'{name}' 恢复?\n当前数据将被覆盖。",
"restored": "已从 {name} 恢复",
"restore_failed": "恢复失败:{e}",
"select_servers_json": "选择servers.json",
# TOTP / 2FA
"totp": "2FA",
"totp_title": "双因素认证TOTP",
"totp_desc": (
"兼容Google Authenticator的2FA验证码。\n"
"为服务器添加TOTP密钥 — 验证码每30秒自动刷新。\n"
"点击验证码即可复制到剪贴板。"
),
"totp_copy": "复制验证码",
"totp_secret_label": "TOTP密钥Base32",
"totp_secret_placeholder": "JBSWY3DPEHPK3PXP...",
"totp_save_secret": "保存",
"totp_remove_secret": "删除",
"totp_generate_secret": "生成随机密钥",
"totp_no_secret": "未配置TOTP密钥",
"totp_remaining": "剩余 {sec}",
"totp_copied": "验证码已复制到剪贴板",
"totp_no_code": "没有可复制的验证码",
"totp_secret_empty": "密钥不能为空",
"totp_secret_invalid": "无效的TOTP密钥必须是Base32格式",
"totp_secret_saved": "TOTP密钥已保存",
"totp_secret_removed": "TOTP密钥已删除",
"totp_secret_generated": "已生成随机密钥(点击保存以存储)",
"totp_secret_dialog": "TOTP密钥",
"placeholder_totp_secret": "Base32密钥可选",
"port_out_of_range": "端口必须在1-65535之间",
}
_TRANSLATIONS = {
"en": _EN,
"ru": _RU,
"zh": _ZH,
}