feat: import/export for config and backups

- Add export_config/import_config/export_backup/import_backup to ServerStore
- Add 4 buttons row in Setup tab UI with file dialogs
- Add i18n keys for EN/RU/ZH (16 keys each)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chrome-storm-c442
2026-02-23 15:37:53 -05:00
parent b0b7d263fb
commit 9baafc9a36
3 changed files with 220 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ Includes configuration path management and backup/restore.
import os
import threading
from datetime import datetime
from tkinter import filedialog, messagebox
import customtkinter as ctk
from core.claude_setup import check_status, install_all, install_ssh_script, install_skill, generate_ssh_key
@@ -171,6 +172,34 @@ class SetupTab(ctk.CTkFrame):
)
self.restore_btn.pack(side="left")
# Import/Export row
ie_row = ctk.CTkFrame(config_frame, fg_color="transparent")
ie_row.pack(fill="x", padx=15, pady=(0, 10))
self.export_config_btn = ctk.CTkButton(
ie_row, text=t("export_config"), width=120, fg_color="#6b7280",
command=self._export_config
)
self.export_config_btn.pack(side="left", padx=(0, 5))
self.import_config_btn = ctk.CTkButton(
ie_row, text=t("import_config"), width=120, fg_color="#6b7280",
command=self._import_config
)
self.import_config_btn.pack(side="left", padx=5)
self.export_backup_btn = ctk.CTkButton(
ie_row, text=t("export_backup"), width=120, fg_color="#6b7280",
command=self._export_backup
)
self.export_backup_btn.pack(side="left", padx=5)
self.import_backup_btn = ctk.CTkButton(
ie_row, text=t("import_backup"), width=120, fg_color="#6b7280",
command=self._import_backup
)
self.import_backup_btn.pack(side="left", padx=5)
# Log
self.log = ctk.CTkTextbox(self, height=150, font=ctk.CTkFont(family="Consolas", size=11), state="disabled")
self.log.pack(fill="both", expand=True, padx=20, pady=(5, 20))
@@ -279,3 +308,69 @@ class SetupTab(ctk.CTkFrame):
else:
self._backup_menu.configure(values=[t("no_backups")])
self._backup_var.set(t("no_backups"))
# ── Import / Export handlers ─────────────────────
def _export_config(self):
default_name = f"servers_export_{datetime.now().strftime('%Y-%m-%d')}.json"
path = filedialog.asksaveasfilename(
title=t("export_config_title"),
defaultextension=".json",
initialfile=default_name,
filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
)
if not path:
return
try:
self.store.export_config(path)
self._log(t("export_config_ok").format(path=path))
except Exception as e:
self._log(t("export_config_failed").format(e=e))
def _import_config(self):
path = filedialog.askopenfilename(
title=t("import_config_title"),
filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
)
if not path:
return
if not messagebox.askyesno(t("import_config_title"), t("import_config_confirm")):
return
try:
self.store.import_config(path)
self._log(t("import_config_ok").format(path=path))
except Exception as e:
self._log(t("import_config_failed").format(e=e))
def _export_backup(self):
selected = self._backup_var.get()
if not selected or selected in (t("select_backup"), t("no_backups")):
self._log(t("no_backup_selected"))
return
path = filedialog.asksaveasfilename(
title=t("export_backup_title"),
defaultextension=".json",
initialfile=selected,
filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
)
if not path:
return
try:
self.store.export_backup(selected, path)
self._log(t("export_backup_ok").format(path=path))
except Exception as e:
self._log(t("export_backup_failed").format(e=e))
def _import_backup(self):
path = filedialog.askopenfilename(
title=t("import_backup_title"),
filetypes=[("JSON files", "*.json"), ("All files", "*.*")],
)
if not path:
return
try:
name = self.store.import_backup(path)
self._refresh_backups()
self._log(t("import_backup_ok").format(name=name))
except Exception as e:
self._log(t("import_backup_failed").format(e=e))