fix(ui): rendre la saisie du code d'accès atteignable à tout moment (v0.8.2)
La fenêtre de saisie ne s'ouvrait qu'une fois, juste après une connexion manuelle réussie. Un refus, une fenêtre fermée, une reconnexion automatique — qui n'ouvre volontairement aucune modale — ou l'expiration de l'autorisation au bout de 12 h laissaient un tunnel monté, un réseau muet et aucun élément d'interface pour ressaisir un code. Un bouton sous l'action de connexion et une entrée de systray apparaissent désormais dès qu'une saisie a un sens : identifiant VPN configuré, tunnel monté, accès non ouvert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+63
-3
@@ -238,6 +238,23 @@ class MainWindow(QMainWindow):
|
||||
self._btn_connect.clicked.connect(self._on_connect)
|
||||
s.addWidget(self._btn_connect)
|
||||
|
||||
# Saisie du code d'accès, atteignable à tout moment. Le tunnel monte
|
||||
# en quarantaine et l'autorisation obtenue expire d'elle-même : sans
|
||||
# ce bouton, un code refusé, une reconnexion automatique ou une
|
||||
# autorisation échue ne laissaient d'autre issue que se déconnecter
|
||||
# puis se reconnecter. Masqué tant qu'il n'y a rien à saisir, pour ne
|
||||
# rien ajouter aux installations sans authentification serveur.
|
||||
self._btn_access = QPushButton("🔐 Saisir le code d'accès")
|
||||
self._btn_access.setFixedHeight(32)
|
||||
self._btn_access.setStyleSheet(
|
||||
f"QPushButton {{ background: {theme.WARN_SOLID}; color: {theme.TEXT};"
|
||||
" font-size: 12px; font-weight: bold; border-radius: 6px; border: none; }"
|
||||
"QPushButton:hover { background: #d68910; }"
|
||||
)
|
||||
self._btn_access.clicked.connect(self.request_access_code)
|
||||
self._btn_access.setVisible(False)
|
||||
s.addWidget(self._btn_access)
|
||||
|
||||
# Repli/dépli du détail (transfert, config, bande passante, journal) :
|
||||
# rattaché à l'action qu'il détaille, pas à l'en-tête.
|
||||
self._btn_toggle_details = QPushButton("▾ Afficher le détail")
|
||||
@@ -599,6 +616,16 @@ class MainWindow(QMainWindow):
|
||||
self._access_open = False
|
||||
self._info_labels["access"].setText(self._access_state())
|
||||
|
||||
# Le bouton n'apparaît que lorsqu'il y a réellement un code à saisir ;
|
||||
# sa hauteur entre et sort du calcul de la fenêtre, d'où le
|
||||
# réajustement. `isHidden()` et non `isVisible()` : cette dernière est
|
||||
# fausse dès que la fenêtre est repliée dans le systray, ce qui
|
||||
# relancerait le calcul de hauteur à chaque cycle de rafraîchissement.
|
||||
needed = self.access_code_needed()
|
||||
if needed == self._btn_access.isHidden():
|
||||
self._btn_access.setVisible(needed)
|
||||
self._sync_window_height()
|
||||
|
||||
if connected:
|
||||
if self._connected_since is None:
|
||||
self._connected_since = QDateTime.currentDateTime()
|
||||
@@ -691,6 +718,38 @@ class MainWindow(QMainWindow):
|
||||
self._worker.finished.connect(self._worker.deleteLater)
|
||||
self._worker.start()
|
||||
|
||||
def access_code_needed(self) -> bool:
|
||||
"""Vrai quand une saisie de code aurait un sens ici et maintenant.
|
||||
|
||||
Trois conditions : un identifiant VPN configuré (sans quoi le serveur
|
||||
ne gère pas ce poste), un tunnel monté (l'API n'est joignable que par
|
||||
l'intérieur) et aucune autorisation en cours.
|
||||
"""
|
||||
return (bool((self._cfg.wg.get("vpn_username") or "").strip())
|
||||
and self._is_connected and not self._access_open)
|
||||
|
||||
def request_access_code(self):
|
||||
"""Ouvre la saisie du code, à la demande de l'utilisateur.
|
||||
|
||||
Distincte de l'enchaînement automatique après connexion : le tunnel
|
||||
monte en quarantaine et l'autorisation expire d'elle-même, il faut
|
||||
donc pouvoir revenir saisir un code à tout moment — après un refus,
|
||||
après une reconnexion automatique, ou une fois l'autorisation échue.
|
||||
"""
|
||||
if not self._is_connected:
|
||||
QMessageBox.information(
|
||||
self, "Tunnel inactif",
|
||||
"Le serveur d'authentification n'est joignable que depuis "
|
||||
"l'intérieur du tunnel.\nConnectez-vous d'abord.")
|
||||
return
|
||||
if not (self._cfg.wg.get("vpn_username") or "").strip():
|
||||
QMessageBox.information(
|
||||
self, "Aucun identifiant VPN",
|
||||
"Renseignez l'identifiant VPN connu du serveur dans le "
|
||||
"panneau Administrateur.")
|
||||
return
|
||||
self._authenticate_with_server(silent=False)
|
||||
|
||||
def _authenticate_with_server(self, silent: bool):
|
||||
"""Demande le code à 6 chiffres et le fait valider par le serveur.
|
||||
|
||||
@@ -703,9 +762,10 @@ class MainWindow(QMainWindow):
|
||||
return
|
||||
if silent:
|
||||
# Reconnexion automatique : ouvrir une fenêtre modale sans
|
||||
# personne devant l'écran laisserait l'application figée.
|
||||
self._add_log("Code d'accès requis — reconnectez-vous manuellement",
|
||||
"warning")
|
||||
# personne devant l'écran laisserait l'application figée. Le
|
||||
# bouton « Saisir le code d'accès » prend le relais.
|
||||
self._add_log("Code d'accès requis — bouton « Saisir le code "
|
||||
"d'accès »", "warning")
|
||||
return
|
||||
|
||||
dlg = AccessCodeDialog(self._cfg, self)
|
||||
|
||||
@@ -44,6 +44,15 @@ class SystemTray(QSystemTrayIcon):
|
||||
self._action_toggle.triggered.connect(self._toggle_connection)
|
||||
menu.addAction(self._action_toggle)
|
||||
|
||||
# Saisie du code d'accès : le tunnel monte en quarantaine et
|
||||
# l'autorisation expire d'elle-même, il faut donc pouvoir y revenir
|
||||
# sans passer par une déconnexion. Masquée quand elle n'a pas lieu
|
||||
# d'être — pas d'identifiant VPN, tunnel à l'arrêt, accès déjà ouvert.
|
||||
self._action_access = QAction("🔐 Saisir le code d'accès", self)
|
||||
self._action_access.triggered.connect(self._win.request_access_code)
|
||||
self._action_access.setVisible(False)
|
||||
menu.addAction(self._action_access)
|
||||
|
||||
menu.addSeparator()
|
||||
|
||||
# Panneau de configuration
|
||||
@@ -76,6 +85,11 @@ class SystemTray(QSystemTrayIcon):
|
||||
except Exception:
|
||||
self._connected = False
|
||||
|
||||
# La fenêtre principale est seule à savoir si le serveur a ouvert
|
||||
# l'accès : elle tient cet état, qu'aucune commande WireGuard ne
|
||||
# rapporte — le tunnel est identique, ouvert ou en quarantaine.
|
||||
self._action_access.setVisible(self._win.access_code_needed())
|
||||
|
||||
if self._connected:
|
||||
self.setIcon(icons.icon_connected())
|
||||
self.setToolTip("WGSecure — Connecté")
|
||||
|
||||
Reference in New Issue
Block a user