""" Keys tab — SSH key management: view, generate, install. """ import os import threading import customtkinter as ctk from core.ssh_client import SSHClientWrapper 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 ctk.CTkLabel(self, text="SSH Key", font=ctk.CTkFont(size=16, weight="bold"), anchor="w").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="Generate Key", command=self._generate) self.gen_btn.pack(side="left", padx=(0, 10)) self.install_btn = ctk.CTkButton(btn_frame, text="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="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=f"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="Key exists") else: self.pub_key_box.insert("1.0", "No key found. Click 'Generate Key' to create one.") self.gen_btn.configure(state="normal", text="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("[!] No server selected") return server = self.store.get_server(self._current_alias) if not server: return self.install_btn.configure(state="disabled", text="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="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("Public key copied to clipboard") else: self._log("[!] No public key to copy")