Full implementation of multi-type server management across GUI and CLI: New clients: SQLClient (MariaDB/MSSQL/PostgreSQL), RedisClient, GrafanaClient, PrometheusClient, TelnetSession, WinRMClient, RemoteDesktopLauncher. New GUI tabs: QueryTab (SQL editor + Treeview), RedisTab (console + history), GrafanaTab (dashboards + alerts), PrometheusTab (PromQL + targets), PowershellTab (PS/CMD), LaunchTab (RDP/VNC external client). Infrastructure: TAB_REGISTRY for conditional tabs per server type, adaptive server_dialog fields, colored type badges in sidebar, status checker for all types (SSH/TCP/SQL/Redis/HTTP), 100+ i18n keys. CLI: ssh.py extended with --sql, --redis, --grafana-*, --prom-*, --ps, --cmd. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
3.6 KiB
Python
111 lines
3.6 KiB
Python
"""
|
|
Launch tab — connect button for RDP/VNC remote desktop sessions.
|
|
"""
|
|
|
|
import threading
|
|
import customtkinter as ctk
|
|
from core.remote_desktop import RemoteDesktopLauncher
|
|
from core.i18n import t
|
|
|
|
|
|
class LaunchTab(ctk.CTkFrame):
|
|
"""Minimal tab: server info + big Connect button for RDP/VNC."""
|
|
|
|
def __init__(self, master, store):
|
|
super().__init__(master, fg_color="transparent")
|
|
self.store = store
|
|
self._current_alias: str | None = None
|
|
self._server_type: str | None = None # "rdp" or "vnc"
|
|
|
|
self._build_ui()
|
|
|
|
def _build_ui(self):
|
|
# Server info label
|
|
self._info_label = ctk.CTkLabel(
|
|
self, text=t("no_server_selected_info"),
|
|
font=ctk.CTkFont(size=16), wraplength=400,
|
|
)
|
|
self._info_label.pack(padx=20, pady=(40, 20))
|
|
|
|
# Big connect button
|
|
self._connect_btn = ctk.CTkButton(
|
|
self, text=t("launch_connect"),
|
|
font=ctk.CTkFont(size=18, weight="bold"),
|
|
width=220, height=50,
|
|
command=self._on_connect,
|
|
)
|
|
self._connect_btn.pack(pady=20)
|
|
self._connect_btn.configure(state="disabled")
|
|
|
|
# Status / result label
|
|
self._status_label = ctk.CTkLabel(
|
|
self, text="", font=ctk.CTkFont(size=13),
|
|
text_color="#888888", wraplength=400,
|
|
)
|
|
self._status_label.pack(padx=20, pady=(10, 0))
|
|
|
|
def set_server(self, alias: str | None):
|
|
self._current_alias = alias
|
|
self._status_label.configure(text="", text_color="#888888")
|
|
|
|
if alias is None:
|
|
self._info_label.configure(text=t("no_server_selected_info"))
|
|
self._connect_btn.configure(state="disabled")
|
|
self._server_type = None
|
|
return
|
|
|
|
server = self.store.get_server(alias)
|
|
if not server:
|
|
self._info_label.configure(text=t("server_not_found").format(alias=alias))
|
|
self._connect_btn.configure(state="disabled")
|
|
self._server_type = None
|
|
return
|
|
|
|
stype = server.get("type", "").lower()
|
|
self._server_type = stype
|
|
|
|
if stype == "rdp":
|
|
info_text = t("launch_rdp_info").format(alias=alias)
|
|
elif stype == "vnc":
|
|
info_text = t("launch_vnc_info").format(alias=alias)
|
|
else:
|
|
info_text = f"{alias} ({stype.upper()})"
|
|
|
|
self._info_label.configure(text=info_text)
|
|
self._connect_btn.configure(state="normal")
|
|
|
|
def _on_connect(self):
|
|
if not self._current_alias or not self._server_type:
|
|
return
|
|
|
|
server = self.store.get_server(self._current_alias)
|
|
if not server:
|
|
return
|
|
|
|
self._connect_btn.configure(state="disabled")
|
|
self._status_label.configure(
|
|
text=t("launch_starting"), text_color="#ccaa00",
|
|
)
|
|
|
|
stype = self._server_type
|
|
|
|
def _do():
|
|
try:
|
|
if stype == "rdp":
|
|
RemoteDesktopLauncher.launch_rdp(server)
|
|
elif stype == "vnc":
|
|
RemoteDesktopLauncher.launch_vnc(server)
|
|
|
|
self.after(0, lambda: self._status_label.configure(
|
|
text=t("launch_started"), text_color="#44cc44",
|
|
))
|
|
except Exception as exc:
|
|
self.after(0, lambda: self._status_label.configure(
|
|
text=t("launch_error").format(error=str(exc)),
|
|
text_color="#ff4444",
|
|
))
|
|
finally:
|
|
self.after(0, lambda: self._connect_btn.configure(state="normal"))
|
|
|
|
threading.Thread(target=_do, daemon=True).start()
|