""" Keys tab — SSH key management: view, generate, install. """ import os import threading import customtkinter as ctk from core.ssh_client import SSHClientWrapper from core.i18n import t from core.icons import icon_text class KeysTab(ctk.CTkFrame): def __init__(self, master, store): super().__init__(master, fg_color="transparent") self.store = store self._current_alias: str | None = None # Key info self.key_title = ctk.CTkLabel(self, text=t("ssh_key"), font=ctk.CTkFont(size=16, weight="bold"), anchor="w") self.key_title.pack(fill="x", padx=15, pady=(15, 5)) self.key_path_label = ctk.CTkLabel(self, text="", anchor="w", text_color="#9ca3af") self.key_path_label.pack(fill="x", padx=15) self.pub_key_box = ctk.CTkTextbox(self, height=80, font=ctk.CTkFont(family="Consolas", size=11), state="disabled") self.pub_key_box.pack(fill="x", padx=15, pady=(5, 10)) # Buttons btn_frame = ctk.CTkFrame(self, fg_color="transparent") btn_frame.pack(fill="x", padx=15, pady=5) self.gen_btn = ctk.CTkButton(btn_frame, text=icon_text("key", t("generate_key")), command=self._generate) self.gen_btn.pack(side="left", padx=(0, 10)) self.install_btn = ctk.CTkButton(btn_frame, text=icon_text("upload", t("install_on_server")), fg_color="#22c55e", hover_color="#16a34a", command=self._install) self.install_btn.pack(side="left") self.copy_btn = ctk.CTkButton(btn_frame, text=icon_text("copy", t("copy_public_key")), fg_color="#6b7280", command=self._copy_key) self.copy_btn.pack(side="right") # Status log self.status_log = ctk.CTkTextbox(self, height=120, font=ctk.CTkFont(family="Consolas", size=11), state="disabled") self.status_log.pack(fill="both", expand=True, padx=15, pady=(10, 15)) self._refresh_key_info() def set_server(self, alias: str | None): self._current_alias = alias def _refresh_key_info(self): key_path = self.store.get_ssh_key_path() pub_path = key_path + ".pub" self.key_path_label.configure(text=t("key_path").format(path=key_path)) self.pub_key_box.configure(state="normal") self.pub_key_box.delete("1.0", "end") if os.path.exists(pub_path): with open(pub_path, "r") as f: pub_key = f.read().strip() self.pub_key_box.insert("1.0", pub_key) self.gen_btn.configure(state="disabled", text=t("key_exists")) else: self.pub_key_box.insert("1.0", t("no_key_found")) self.gen_btn.configure(state="normal", text=t("generate_key")) self.pub_key_box.configure(state="disabled") def _log(self, text: str): self.status_log.configure(state="normal") self.status_log.insert("end", text + "\n") self.status_log.configure(state="disabled") self.status_log.see("end") def _generate(self): try: key_path = self.store.get_ssh_key_path() wrapper = SSHClientWrapper({"alias": "temp", "ip": "0", "user": "root"}, key_path) msg = wrapper.generate_key() self._log(msg) self._refresh_key_info() except Exception as e: self._log(f"[ERROR] {e}") def _install(self): if not self._current_alias: self._log(t("no_server_selected")) return server = self.store.get_server(self._current_alias) if not server: return self.install_btn.configure(state="disabled", text=t("installing")) def _do(): try: wrapper = SSHClientWrapper(server, self.store.get_ssh_key_path()) msg = wrapper.install_key() self.after(0, lambda: self._log(f"[{self._current_alias}] {msg}")) except Exception as e: self.after(0, lambda: self._log(f"[ERROR] {e}")) finally: self.after(0, lambda: self.install_btn.configure(state="normal", text=t("install_on_server"))) threading.Thread(target=_do, daemon=True).start() def _copy_key(self): key_path = self.store.get_ssh_key_path() pub_path = key_path + ".pub" if os.path.exists(pub_path): with open(pub_path, "r") as f: pub_key = f.read().strip() self.clipboard_clear() self.clipboard_append(pub_key) self._log(t("key_copied")) else: self._log(t("no_public_key"))