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:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user