This commit is contained in:
2026-06-01 23:22:18 +02:00
parent f061da654d
commit d23d0c15be
14 changed files with 1858 additions and 202 deletions
+398 -117
View File
@@ -1,26 +1,52 @@
from __future__ import annotations
from PyQt6.QtWidgets import (
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QFrame, QMessageBox, QInputDialog,
QLineEdit,
QLineEdit, QListWidget, QListWidgetItem, QApplication,
QComboBox, QSizePolicy,
)
from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QFont, QCloseEvent, QPalette, QColor
from PyQt6.QtCore import Qt, QTimer, QDateTime
from PyQt6.QtGui import QFont, QCloseEvent, QColor
from app.core.config import Config
from app.core import wireguard as wg_core
from app.core import mfa as mfa_core
from app.core import log as conn_log
from app.ui.mfa_dialog import MFADialog
from app.ui.admin_window import AdminWindow
from app.ui.bw_graph import BandwidthGraph
from app.ui import icons
_DARK = "#1c2833"
_DARK2 = "#17202a"
_CARD = "#212f3d"
_LOG_COLORS = {
"success": "#a9dfbf",
"error": "#f1948a",
"warning": "#f9e79f",
"info": "#aed6f1",
}
_PING_CSS = {
"good": "background:#1e8449; color:white;",
"medium": "background:#d4ac0d; color:black;",
"bad": "background:#922b21; color:white;",
"offline": "background:#2e4057; color:#5d6d7e;",
}
class MainWindow(QMainWindow):
def __init__(self, config: Config, parent=None):
super().__init__(parent)
self._cfg = config
self._connecting = False
self._connected_since: QDateTime | None = None
self._prev_rx: int | None = None
self._prev_tx: int | None = None
self._auto_reconnect_active = False
self.setWindowTitle("WGSecure")
self.setFixedSize(400, 520)
self.setFixedSize(430, 680)
self.setWindowFlags(
Qt.WindowType.Window
| Qt.WindowType.WindowTitleHint
@@ -29,147 +55,364 @@ class MainWindow(QMainWindow):
)
self.setWindowIcon(icons.icon_app())
self._build_ui()
# Timers
self._status_timer = QTimer(self)
self._status_timer.timeout.connect(self._refresh_status)
self._status_timer.start(3000)
self._clock_timer = QTimer(self)
self._clock_timer.timeout.connect(self._tick_duration)
self._clock_timer.start(1000)
self._bw_timer = QTimer(self)
self._bw_timer.timeout.connect(self._update_bw)
self._bw_timer.start(3000)
self._ping_timer = QTimer(self)
self._ping_timer.timeout.connect(self._update_ping)
self._ping_timer.start(10000)
self._reconnect_timer = QTimer(self)
self._reconnect_timer.timeout.connect(self._check_reconnect)
self._refresh_status()
self._reload_log()
self._reload_profiles()
# ------------------------------------------------------------------ #
# Construction UI
# ------------------------------------------------------------------ #
def _build_ui(self):
central = QWidget()
central.setStyleSheet(f"background: {_DARK};")
self.setCentralWidget(central)
layout = QVBoxLayout(central)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
root = QVBoxLayout(central)
root.setContentsMargins(0, 0, 0, 0)
root.setSpacing(0)
# -- En-tête --------------------------------------------------
# ── En-tête ──────────────────────────────────────────────────
header = QWidget()
header.setFixedHeight(72)
header.setStyleSheet("background: #2c3e50;")
h_layout = QHBoxLayout(header)
h_layout.setContentsMargins(16, 10, 16, 10)
header.setStyleSheet(f"background: {_DARK2};")
h = QHBoxLayout(header)
h.setContentsMargins(14, 8, 14, 8)
self._icon_label = QLabel()
self._icon_label.setFixedSize(44, 44)
self._icon_label.setFixedSize(40, 40)
self._icon_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
h_layout.addWidget(self._icon_label)
h.addWidget(self._icon_label)
title_col = QVBoxLayout()
title_col.setSpacing(2)
app_title = QLabel("WGSecure")
app_title.setStyleSheet("color: white; font-size: 17px; font-weight: bold; background: transparent;")
version_label = QLabel("v0.1.0 — WireGuard + MFA")
version_label.setStyleSheet("color: #95a5a6; font-size: 10px; background: transparent;")
title_col.addWidget(app_title)
title_col.addWidget(version_label)
h_layout.addLayout(title_col)
h_layout.addStretch()
layout.addWidget(header)
title_col.setSpacing(1)
title_lbl = QLabel("WGSecure")
title_lbl.setStyleSheet(
"color: white; font-size: 16px; font-weight: bold; background: transparent;"
)
self._profile_combo = QComboBox()
self._profile_combo.setFixedHeight(22)
self._profile_combo.setStyleSheet(
"QComboBox { background: #2e4057; color: #aed6f1; border: none;"
" border-radius: 3px; font-size: 10px; padding: 0 6px; }"
"QComboBox::drop-down { border: none; }"
"QComboBox QAbstractItemView { background: #2e4057; color: white; }"
)
self._profile_combo.currentTextChanged.connect(self._on_profile_changed)
title_col.addWidget(title_lbl)
title_col.addWidget(self._profile_combo)
h.addLayout(title_col)
h.addStretch()
# -- Zone statut ----------------------------------------------
# Ping badge
self._ping_badge = QLabel("— ms")
self._ping_badge.setFixedSize(62, 22)
self._ping_badge.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._ping_badge.setStyleSheet(
f"border-radius: 11px; font-size: 10px; font-weight: bold; {_PING_CSS['offline']}"
)
h.addWidget(self._ping_badge)
# Copier clé publique
self._btn_copy_key = QPushButton("📋 Clé pub.")
self._btn_copy_key.setFixedHeight(26)
self._btn_copy_key.setStyleSheet(
"QPushButton { background: #2471a3; color: white; border-radius: 5px;"
" font-size: 10px; padding: 0 8px; border: none; }"
"QPushButton:hover { background: #1a5276; }"
"QPushButton:disabled { background: #2e4057; color: #5d6d7e; }"
)
self._btn_copy_key.clicked.connect(self._copy_public_key)
h.addWidget(self._btn_copy_key)
root.addWidget(header)
# ── Statut ───────────────────────────────────────────────────
status_area = QWidget()
status_area.setFixedHeight(90)
status_area.setStyleSheet("background: #ecf0f1;")
s_layout = QVBoxLayout(status_area)
s_layout.setContentsMargins(10, 10, 10, 10)
s_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
status_area.setFixedHeight(80)
status_area.setStyleSheet(f"background: {_CARD};")
s = QVBoxLayout(status_area)
s.setContentsMargins(10, 6, 10, 6)
s.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._status_badge = QLabel(" Déconnecté")
self._status_badge = QLabel("● Déconnecté")
self._status_badge.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._status_badge.setStyleSheet("color: #e74c3c; font-size: 16px; font-weight: bold; background: transparent;")
s_layout.addWidget(self._status_badge)
self._status_badge.setStyleSheet(
"color: #e74c3c; font-size: 15px; font-weight: bold; background: transparent;"
)
s.addWidget(self._status_badge)
self._status_sub = QLabel("")
self._status_sub.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._status_sub.setStyleSheet("color: #7f8c8d; font-size: 11px; background: transparent;")
s_layout.addWidget(self._status_sub)
layout.addWidget(status_area)
self._duration_label = QLabel("")
self._duration_label.setAlignment(Qt.AlignmentFlag.AlignCenter)
self._duration_label.setStyleSheet(
"color: #5d6d7e; font-size: 11px; background: transparent;"
)
s.addWidget(self._duration_label)
root.addWidget(status_area)
# -- Séparateur -----------------------------------------------
sep = QFrame()
sep.setFrameShape(QFrame.Shape.HLine)
sep.setStyleSheet("color: #bdc3c7;")
layout.addWidget(sep)
root.addWidget(self._hsep())
# -- Section infos --------------------------------------------
# ── Infos ─────────────────────────────────────────────────────
info_frame = QFrame()
info_frame.setFrameShape(QFrame.Shape.NoFrame)
info_frame.setStyleSheet("background-color: #ffffff;")
info_frame.setStyleSheet(f"background: {_DARK};")
info_frame.setAutoFillBackground(True)
i_layout = QVBoxLayout(info_frame)
i_layout.setContentsMargins(16, 12, 16, 12)
i_layout.setSpacing(8)
il = QVBoxLayout(info_frame)
il.setContentsMargins(14, 8, 14, 8)
il.setSpacing(5)
self._info_labels: dict[str, QLabel] = {}
rows = [
("server", "Serveur"),
("iface", "Interface"),
("addr", "Adresse IP"),
("mfa", "MFA"),
("rx_tx", "Transfert"),
("handshake", "Dernier handshake"),
]
for key, label in rows:
row_w = QWidget()
row_w.setStyleSheet("background: transparent;")
row_layout = QHBoxLayout(row_w)
row_layout.setContentsMargins(0, 0, 0, 0)
row_layout.setSpacing(8)
for key, icon_txt, label in [
("server", "🌐", "Serveur"),
("iface", "🔌", "Interface"),
("addr", "📍", "Adresse IP"),
("mfa", "🔐", "MFA"),
("rx_tx", "↕️ ", "Transfert"),
("handshake", "🤝", "Handshake"),
]:
rw = QWidget()
rw.setStyleSheet("background: transparent;")
rl = QHBoxLayout(rw)
rl.setContentsMargins(0, 0, 0, 0)
rl.setSpacing(5)
rl.addWidget(self._lbl(icon_txt, fixed=20, color="#5d6d7e", size=11))
rl.addWidget(self._lbl(f"{label} :", fixed=100, color="#5d6d7e", size=12))
val = self._lbl("", color="#aed6f1", size=12)
val.setWordWrap(True)
self._info_labels[key] = val
rl.addWidget(val, 1)
il.addWidget(rw)
lbl_key = QLabel(f"{label} :")
lbl_key.setFixedWidth(130)
lbl_key.setStyleSheet("color: #7f8c8d; font-size: 12px; background: transparent;")
root.addWidget(info_frame)
root.addWidget(self._hsep())
lbl_val = QLabel("")
lbl_val.setStyleSheet("color: #2c3e50; font-size: 12px; background: transparent;")
lbl_val.setWordWrap(True)
self._info_labels[key] = lbl_val
# ── Graphique bande passante ───────────────────────────────────
bw_header = self._section_header("📊 Bande passante")
root.addWidget(bw_header)
row_layout.addWidget(lbl_key)
row_layout.addWidget(lbl_val, 1)
i_layout.addWidget(row_w)
self._bw_graph = BandwidthGraph()
self._bw_graph.setFixedHeight(75)
root.addWidget(self._bw_graph)
root.addWidget(self._hsep())
layout.addWidget(info_frame, 1) # stretch factor 1 — prend l'espace disponible
# ── Journal ───────────────────────────────────────────────────
log_hdr = self._section_header("📋 Journal", btn_text="Effacer",
btn_slot=self._clear_log)
root.addWidget(log_hdr)
# -- Séparateur bas -------------------------------------------
sep2 = QFrame()
sep2.setFrameShape(QFrame.Shape.HLine)
sep2.setStyleSheet("color: #bdc3c7;")
layout.addWidget(sep2)
self._log_list = QListWidget()
self._log_list.setFixedHeight(78)
self._log_list.setStyleSheet(
f"QListWidget {{ background: {_DARK2}; color: #aed6f1;"
" border: none; font-size: 10px; font-family: Courier; }}"
"QListWidget::item { padding: 1px 6px; }"
)
self._log_list.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
root.addWidget(self._log_list)
root.addWidget(self._hsep())
# -- Boutons --------------------------------------------------
# ── Boutons ───────────────────────────────────────────────────
btn_area = QWidget()
btn_area.setStyleSheet("background: #f8f9fa;")
b_layout = QVBoxLayout(btn_area)
b_layout.setContentsMargins(16, 12, 16, 12)
b_layout.setSpacing(8)
btn_area.setStyleSheet(f"background: {_DARK2};")
bl = QVBoxLayout(btn_area)
bl.setContentsMargins(14, 10, 14, 12)
bl.setSpacing(7)
self._btn_connect = QPushButton("Se connecter")
self._btn_connect.setFixedHeight(42)
self._btn_connect.setStyleSheet(
"QPushButton { background: #27ae60; color: white; font-size: 14px; "
"font-weight: bold; border-radius: 6px; border: none; }"
"QPushButton:hover { background: #219a52; }"
"QPushButton:disabled { background: #bdc3c7; color: #ecf0f1; }"
"QPushButton { background: #1e8449; color: white; font-size: 14px;"
" font-weight: bold; border-radius: 6px; border: none; }"
"QPushButton:hover { background: #27ae60; }"
"QPushButton:disabled { background: #2e4057; color: #5d6d7e; }"
)
self._btn_connect.clicked.connect(self._on_connect)
b_layout.addWidget(self._btn_connect)
bl.addWidget(self._btn_connect)
btn_admin = QPushButton("Panneau Administrateur")
btn_admin.setFixedHeight(34)
btn_admin = QPushButton("⚙️ Panneau Administrateur")
btn_admin.setFixedHeight(30)
btn_admin.setStyleSheet(
"QPushButton { background: #dde1e7; color: #2c3e50; border-radius: 6px; "
"font-size: 12px; border: none; }"
"QPushButton:hover { background: #bdc3c7; color: #2c3e50; }"
"QPushButton { background: #2e4057; color: white; border-radius: 6px;"
" font-size: 11px; border: none; }"
"QPushButton:hover { background: #3d5166; }"
)
btn_admin.clicked.connect(self._open_admin)
b_layout.addWidget(btn_admin)
layout.addWidget(btn_area)
bl.addWidget(btn_admin)
root.addWidget(btn_area)
# ── Helpers UI ───────────────────────────────────────────────────────
def _lbl(self, text: str, fixed: int = 0, color: str = "white",
size: int = 12) -> QLabel:
lbl = QLabel(text)
lbl.setStyleSheet(
f"color: {color}; font-size: {size}px; background: transparent;"
)
if fixed:
lbl.setFixedWidth(fixed)
return lbl
def _hsep(self) -> QFrame:
f = QFrame()
f.setFrameShape(QFrame.Shape.HLine)
f.setFixedHeight(1)
f.setStyleSheet(f"background: {_DARK2};")
return f
def _section_header(self, title: str, btn_text: str = "",
btn_slot=None) -> QWidget:
w = QWidget()
w.setFixedHeight(24)
w.setStyleSheet(f"background: {_DARK2};")
hl = QHBoxLayout(w)
hl.setContentsMargins(12, 0, 12, 0)
t = QLabel(title)
t.setStyleSheet("color: #5d6d7e; font-size: 10px; font-weight: bold;"
" background: transparent;")
hl.addWidget(t)
hl.addStretch()
if btn_text and btn_slot:
b = QPushButton(btn_text)
b.setFixedHeight(18)
b.setStyleSheet(
"QPushButton { background: transparent; color: #5d6d7e;"
" font-size: 9px; border: none; padding: 0 4px; }"
"QPushButton:hover { color: white; }"
)
b.clicked.connect(btn_slot)
hl.addWidget(b)
return w
# ------------------------------------------------------------------ #
# Rafraîchissement du statut
# Profils
# ------------------------------------------------------------------ #
def _reload_profiles(self):
self._profile_combo.blockSignals(True)
self._profile_combo.clear()
self._profile_combo.addItem("⭐ Défaut (actif)")
for name in self._cfg.list_profiles():
marker = "" if name == self._cfg.active_profile else " "
self._profile_combo.addItem(f"{marker}{name}")
self._profile_combo.blockSignals(False)
def _on_profile_changed(self, text: str):
if "Défaut" in text:
return
name = text.lstrip("").strip()
if not name or name == self._cfg.active_profile:
return
if self._cfg.load_profile(name):
self._add_log(f"Profil chargé : {name}", "info")
self._reload_profiles()
self._refresh_status()
# ------------------------------------------------------------------ #
# Journal
# ------------------------------------------------------------------ #
def _reload_log(self):
self._log_list.clear()
for ev in conn_log.get_events(30):
self._add_log_item(ev["ts"], ev["msg"], ev["kind"])
self._log_list.scrollToBottom()
def _clear_log(self):
conn_log.clear()
self._log_list.clear()
def _add_log(self, msg: str, kind: str = "info"):
conn_log.log_event(msg, kind)
ts = QDateTime.currentDateTime().toString("yyyy-MM-dd hh:mm:ss")
self._add_log_item(ts, msg, kind)
def _add_log_item(self, ts: str, msg: str, kind: str):
color = _LOG_COLORS.get(kind, "#aed6f1")
item = QListWidgetItem(f"[{ts}] {msg}")
item.setForeground(QColor(color))
self._log_list.addItem(item)
self._log_list.scrollToBottom()
# ------------------------------------------------------------------ #
# Timers
# ------------------------------------------------------------------ #
def _tick_duration(self):
if self._connected_since is None:
return
secs = self._connected_since.secsTo(QDateTime.currentDateTime())
h, rem = divmod(secs, 3600)
m, s = divmod(rem, 60)
self._duration_label.setText(f"Connecté depuis {h:02d}h {m:02d}m {s:02d}s")
def _update_bw(self):
iface = self._cfg.wg.get("interface_name", "wgs0")
result = wg_core.get_interface_bytes(iface)
if result is None or self._prev_rx is None:
self._prev_rx, self._prev_tx = (result or (0, 0))
return
rx_now, tx_now = result
dt = 3.0
rx_bps = max(0, rx_now - self._prev_rx) / dt
tx_bps = max(0, tx_now - self._prev_tx) / dt
self._prev_rx, self._prev_tx = rx_now, tx_now
if wg_core.is_connected(self._cfg):
self._bw_graph.push(rx_bps, tx_bps)
else:
self._bw_graph.push(0, 0)
def _update_ping(self):
host = self._cfg.wg.get("server_endpoint", "")
if not host or not wg_core.is_connected(self._cfg):
self._ping_badge.setText("— ms")
self._ping_badge.setStyleSheet(
f"border-radius: 11px; font-size: 10px; font-weight: bold; {_PING_CSS['offline']}"
)
return
ms = wg_core.ping_server(host)
if ms is None:
css = _PING_CSS["bad"]
txt = "hors ligne"
elif ms < 50:
css = _PING_CSS["good"]
txt = f"{ms} ms"
elif ms < 200:
css = _PING_CSS["medium"]
txt = f"{ms} ms"
else:
css = _PING_CSS["bad"]
txt = f"{ms} ms"
self._ping_badge.setText(txt)
self._ping_badge.setStyleSheet(
f"border-radius: 11px; font-size: 10px; font-weight: bold; {css}"
)
def _check_reconnect(self):
if not self._cfg.get("ui", "auto_reconnect"):
return
try:
connected = wg_core.is_connected(self._cfg)
except Exception:
connected = False
if not connected and self._cfg.configured:
self._add_log("Auto-reconnexion…", "warning")
self._on_connect()
# ------------------------------------------------------------------ #
# Statut WireGuard
# ------------------------------------------------------------------ #
def _refresh_status(self):
wg = self._cfg.wg
@@ -181,6 +424,15 @@ class MainWindow(QMainWindow):
self._info_labels["mfa"].setText(
"Activé ✓" if self._cfg.mfa_enabled else "Désactivé"
)
self._btn_copy_key.setEnabled(bool(wg.get("client_public_key", "")))
# Auto-reconnect timer
if self._cfg.get("ui", "auto_reconnect"):
interval = int(self._cfg.get("ui", "reconnect_interval") or 30) * 1000
if not self._reconnect_timer.isActive():
self._reconnect_timer.start(interval)
else:
self._reconnect_timer.stop()
try:
info = wg_core.get_status_info(self._cfg)
@@ -190,36 +442,47 @@ class MainWindow(QMainWindow):
info = {}
if connected:
self._status_badge.setText("● Connecté")
if self._connected_since is None:
self._connected_since = QDateTime.currentDateTime()
self._status_badge.setText("● Connecté")
self._status_badge.setStyleSheet(
"color: #27ae60; font-size: 16px; font-weight: bold; background: transparent;"
"color: #27ae60; font-size: 15px; font-weight: bold; background: transparent;"
)
self._btn_connect.setText("Se déconnecter")
self._btn_connect.setStyleSheet(
"QPushButton { background: #e74c3c; color: white; font-size: 14px; "
"font-weight: bold; border-radius: 6px; border: none; }"
"QPushButton { background: #922b21; color: white; font-size: 14px;"
" font-weight: bold; border-radius: 6px; border: none; }"
"QPushButton:hover { background: #c0392b; }"
)
self._info_labels["rx_tx"].setText(
f"{info.get('rx_bytes','')}{info.get('tx_bytes','')}"
)
self._info_labels["handshake"].setText(info.get("last_handshake", ""))
self._status_sub.setText(f"Interface active : {info.get('interface','')}")
self._duration_label.setStyleSheet(
"color: #a9dfbf; font-size: 11px; background: transparent;"
)
self.setWindowIcon(icons.icon_connected())
else:
self._status_badge.setText("● Déconnecté")
if self._connected_since is not None:
self._connected_since = None
self._duration_label.setText("")
self._bw_graph.reset()
self._prev_rx = self._prev_tx = None
self._status_badge.setText("● Déconnecté")
self._status_badge.setStyleSheet(
"color: #e74c3c; font-size: 16px; font-weight: bold; background: transparent;"
"color: #e74c3c; font-size: 15px; font-weight: bold; background: transparent;"
)
self._btn_connect.setText("Se connecter")
self._btn_connect.setStyleSheet(
"QPushButton { background: #27ae60; color: white; font-size: 14px; "
"font-weight: bold; border-radius: 6px; border: none; }"
"QPushButton:hover { background: #219a52; }"
"QPushButton { background: #1e8449; color: white; font-size: 14px;"
" font-weight: bold; border-radius: 6px; border: none; }"
"QPushButton:hover { background: #27ae60; }"
)
self._info_labels["rx_tx"].setText("")
self._info_labels["handshake"].setText("")
self._status_sub.setText("")
self._duration_label.setStyleSheet(
"color: #5d6d7e; font-size: 11px; background: transparent;"
)
self.setWindowIcon(icons.icon_disconnected())
pix = (icons.icon_connected() if connected else icons.icon_disconnected()).pixmap(36, 36)
@@ -228,6 +491,12 @@ class MainWindow(QMainWindow):
# ------------------------------------------------------------------ #
# Actions
# ------------------------------------------------------------------ #
def _copy_public_key(self):
pub = self._cfg.wg.get("client_public_key", "")
if pub:
QApplication.clipboard().setText(pub)
self._add_log("Clé publique copiée dans le presse-papiers", "info")
def _on_connect(self):
if self._connecting:
return
@@ -238,37 +507,48 @@ class MainWindow(QMainWindow):
if connected:
ok, msg = wg_core.disconnect(self._cfg)
if not ok:
QMessageBox.warning(self, "Erreur", f"Déconnexion échouée :\n{msg}")
if ok:
self._add_log("Tunnel WireGuard désactivé", "warning")
else:
self._add_log(f"Erreur déconnexion : {msg}", "error")
QMessageBox.warning(self, "Erreur", msg)
else:
if not self._cfg.configured:
QMessageBox.information(
self, "Configuration manquante",
"WireGuard n'est pas encore configuré.\n"
"Ouvrez le panneau Administrateur pour paramétrer la connexion."
"Ouvrez le panneau Administrateur."
)
return
if self._cfg.mfa_enabled:
if not self._cfg.mfa_secret:
QMessageBox.warning(
self, "MFA non configuré",
"Le MFA est activé mais aucun secret n'est configuré."
)
QMessageBox.warning(self, "MFA non configuré",
"MFA activé mais aucun secret défini.")
return
dlg = MFADialog(self._cfg.mfa_secret, self)
if dlg.exec() != MFADialog.DialogCode.Accepted or not dlg.is_verified():
self._add_log("MFA annulé ou échoué", "warning")
return
self._add_log("Authentification MFA réussie ✓", "success")
self._btn_connect.setEnabled(False)
self._btn_connect.setText("Connexion…")
self._connecting = True
from PyQt6.QtWidgets import QApplication
QApplication.processEvents()
ok, msg = wg_core.connect(self._cfg)
self._connecting = False
self._btn_connect.setEnabled(True)
if not ok:
if ok:
self._connected_since = QDateTime.currentDateTime()
self._add_log(
f"Connexion établie → {self._cfg.wg.get('server_endpoint','')}", "success"
)
QTimer.singleShot(5000, self._update_ping)
else:
self._add_log(f"Erreur : {msg}", "error")
QMessageBox.warning(self, "Erreur de connexion", msg)
self._refresh_status()
@@ -287,6 +567,7 @@ class MainWindow(QMainWindow):
return
dlg = AdminWindow(self._cfg, self)
dlg.exec()
self._reload_profiles()
self._refresh_status()
def closeEvent(self, event: QCloseEvent):