refactor: decouper main_window.py en controllers/services/ui
main_window.py concentrait la fenetre principale, l'arbre des sessions, les 4 classes d'onglets, la detection de commandes ssh et le multi-send : 983 lignes et une seule classe qui pilotait toute l'application. - app/services/ssh_target.py : detection d'une cible ssh dans un terminal (pur, sans dependance Qt) - app/ui/tabs.py : BaseTerminalTab / SessionTabWidget / SFTPTabWidget / LocalTerminalTabWidget (deplaces tels quels) - app/controllers/session_controller.py : panneau lateral (arbre sessions + terminaux locaux) et CRUD des sessions - app/controllers/connection_controller.py : cycle de vie des onglets (ouverture, connexion rapide, renommage, fermeture) - app/controllers/broadcast_manager.py : bouton et logique du mode Multi-Send main_window.py ne fait plus que construire ces composants et les relier par signaux (983 -> ~200 lignes). Comportement inchange ; MainWindow.tabs et detect_ssh_target restent importables depuis app.main_window pour la compatibilite avec les tests existants. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
55
app/services/ssh_target.py
Normal file
55
app/services/ssh_target.py
Normal file
@@ -0,0 +1,55 @@
|
||||
"""Detection d'une cible ssh (user/host/port) tapee a la main dans un terminal.
|
||||
|
||||
Sert a proposer le SFTP sur l'hote d'une commande `ssh ...` sans avoir a creer
|
||||
un favori au prealable.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
# Repere une commande ssh tapee a la main dans un terminal.
|
||||
SSH_COMMAND_RE = re.compile(r"\bssh\s+(?P<args>[^\r\n|;&]+)")
|
||||
# options ssh qui consomment la valeur suivante (a ne pas confondre avec l'hote)
|
||||
SSH_VALUE_OPTIONS = {
|
||||
"-p", "-i", "-l", "-o", "-F", "-J", "-b", "-c", "-D", "-L", "-R", "-W", "-e", "-m", "-Q",
|
||||
}
|
||||
HOST_RE = re.compile(r"^[A-Za-z0-9][\w.\-]*$")
|
||||
|
||||
|
||||
def _parse_ssh_args(args: str) -> dict | None:
|
||||
tokens = args.split()
|
||||
user, host, port = "", "", 22
|
||||
index = 0
|
||||
while index < len(tokens):
|
||||
token = tokens[index]
|
||||
if token.startswith("-"):
|
||||
if token in SSH_VALUE_OPTIONS:
|
||||
value = tokens[index + 1] if index + 1 < len(tokens) else ""
|
||||
if token == "-p" and value.isdigit():
|
||||
port = int(value)
|
||||
if token == "-l" and value:
|
||||
user = value
|
||||
index += 2
|
||||
continue
|
||||
if token.startswith("-p") and token[2:].isdigit(): # forme collee : -p2222
|
||||
port = int(token[2:])
|
||||
index += 1
|
||||
continue
|
||||
if not host:
|
||||
candidate = token
|
||||
if "@" in candidate:
|
||||
user, _, candidate = candidate.partition("@")
|
||||
if HOST_RE.match(candidate):
|
||||
host = candidate
|
||||
index += 1
|
||||
return {"user": user, "host": host, "port": port} if host else None
|
||||
|
||||
|
||||
def detect_ssh_target(text: str) -> dict | None:
|
||||
"""Renvoie la derniere cible ssh trouvee dans le texte d'un terminal."""
|
||||
result = None
|
||||
for match in SSH_COMMAND_RE.finditer(text):
|
||||
parsed = _parse_ssh_args(match.group("args"))
|
||||
if parsed:
|
||||
result = parsed
|
||||
return result
|
||||
Reference in New Issue
Block a user