ui: show backend traffic audit in dialog log
This commit is contained in:
@@ -173,6 +173,15 @@ class TrafficAppProfileSaveResult:
|
|||||||
profile: Optional[TrafficAppProfile] = None
|
profile: Optional[TrafficAppProfile] = None
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class TrafficAudit:
|
||||||
|
ok: bool
|
||||||
|
message: str
|
||||||
|
now: str
|
||||||
|
pretty: str
|
||||||
|
issues: List[str]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class TrafficCandidateSubnet:
|
class TrafficCandidateSubnet:
|
||||||
@@ -1041,6 +1050,22 @@ class ApiClient:
|
|||||||
stderr="",
|
stderr="",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def traffic_audit_get(self) -> TrafficAudit:
|
||||||
|
data = cast(
|
||||||
|
Dict[str, Any],
|
||||||
|
self._json(self._request("GET", "/api/v1/traffic/audit")) or {},
|
||||||
|
)
|
||||||
|
raw_issues = data.get("issues") or []
|
||||||
|
if not isinstance(raw_issues, list):
|
||||||
|
raw_issues = []
|
||||||
|
return TrafficAudit(
|
||||||
|
ok=bool(data.get("ok", False)),
|
||||||
|
message=strip_ansi(str(data.get("message") or "").strip()),
|
||||||
|
now=str(data.get("now") or "").strip(),
|
||||||
|
pretty=strip_ansi(str(data.get("pretty") or "").strip()),
|
||||||
|
issues=[strip_ansi(str(x)).strip() for x in raw_issues if str(x).strip()],
|
||||||
|
)
|
||||||
|
|
||||||
# DNS / SmartDNS
|
# DNS / SmartDNS
|
||||||
def dns_upstreams_get(self) -> DnsUpstreams:
|
def dns_upstreams_get(self) -> DnsUpstreams:
|
||||||
data = cast(Dict[str, Any], self._json(self._request("GET", "/api/v1/dns-upstreams")) or {})
|
data = cast(Dict[str, Any], self._json(self._request("GET", "/api/v1/dns-upstreams")) or {})
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ from api_client import (
|
|||||||
TrafficAppMarkItem,
|
TrafficAppMarkItem,
|
||||||
TrafficAppProfile,
|
TrafficAppProfile,
|
||||||
TrafficAppProfileSaveResult,
|
TrafficAppProfileSaveResult,
|
||||||
|
TrafficAudit,
|
||||||
TrafficInterfaces,
|
TrafficInterfaces,
|
||||||
TrafficModeStatus,
|
TrafficModeStatus,
|
||||||
TraceDump,
|
TraceDump,
|
||||||
@@ -767,6 +768,9 @@ class DashboardController:
|
|||||||
def traffic_app_profile_delete(self, id: str) -> CmdResult:
|
def traffic_app_profile_delete(self, id: str) -> CmdResult:
|
||||||
return self.client.traffic_app_profile_delete(id)
|
return self.client.traffic_app_profile_delete(id)
|
||||||
|
|
||||||
|
def traffic_audit(self) -> TrafficAudit:
|
||||||
|
return self.client.traffic_audit_get()
|
||||||
|
|
||||||
def routes_nft_progress_from_event(self, ev: Event) -> RoutesNftProgressView:
|
def routes_nft_progress_from_event(self, ev: Event) -> RoutesNftProgressView:
|
||||||
"""
|
"""
|
||||||
Превращает Event(kind='routes_nft_progress') в удобную модель
|
Превращает Event(kind='routes_nft_progress') в удобную модель
|
||||||
|
|||||||
@@ -580,6 +580,16 @@ RU: Восстанавливает маршруты/nft из последнег
|
|||||||
self.txt_app.setReadOnly(True)
|
self.txt_app.setReadOnly(True)
|
||||||
tab_log = QWidget()
|
tab_log = QWidget()
|
||||||
tab_log_layout = QVBoxLayout(tab_log)
|
tab_log_layout = QVBoxLayout(tab_log)
|
||||||
|
row_log = QHBoxLayout()
|
||||||
|
self.btn_app_audit = QPushButton("Run audit")
|
||||||
|
self.btn_app_audit.setToolTip(
|
||||||
|
"EN: Runs backend traffic audit (duplicates + nft consistency) and prints it here.\n"
|
||||||
|
"RU: Запускает backend-аудит трафика (дубли + nft консистентность) и печатает сюда."
|
||||||
|
)
|
||||||
|
self.btn_app_audit.clicked.connect(self.on_app_audit)
|
||||||
|
row_log.addWidget(self.btn_app_audit)
|
||||||
|
row_log.addStretch(1)
|
||||||
|
tab_log_layout.addLayout(row_log)
|
||||||
tab_log_layout.addWidget(self.txt_app, stretch=1)
|
tab_log_layout.addWidget(self.txt_app, stretch=1)
|
||||||
self.apps_tabs.addTab(tab_log, "Log")
|
self.apps_tabs.addTab(tab_log, "Log")
|
||||||
|
|
||||||
@@ -1742,6 +1752,22 @@ RU: Применяет policy-rules и проверяет health. При оши
|
|||||||
|
|
||||||
self._safe(work, title="App picker error")
|
self._safe(work, title="App picker error")
|
||||||
|
|
||||||
|
def on_app_audit(self) -> None:
|
||||||
|
def work() -> None:
|
||||||
|
audit = self.ctrl.traffic_audit()
|
||||||
|
pretty = (getattr(audit, "pretty", "") or "").strip()
|
||||||
|
if not pretty:
|
||||||
|
pretty = f"ok={getattr(audit, 'ok', False)} message={getattr(audit, 'message', '')}"
|
||||||
|
self._append_app_log("[audit]\n" + pretty)
|
||||||
|
issues = list(getattr(audit, "issues", []) or [])
|
||||||
|
ok = bool(getattr(audit, "ok", False)) and len(issues) == 0
|
||||||
|
if issues:
|
||||||
|
self._set_action_status(f"Audit: issues={len(issues)}", ok=False)
|
||||||
|
else:
|
||||||
|
self._set_action_status("Audit: OK", ok=True)
|
||||||
|
|
||||||
|
self._safe(work, title="Traffic audit error")
|
||||||
|
|
||||||
def _run_systemd_unit(self, cmdline: str, *, unit: str) -> tuple[str, str]:
|
def _run_systemd_unit(self, cmdline: str, *, unit: str) -> tuple[str, str]:
|
||||||
args = shlex.split(cmdline or "")
|
args = shlex.split(cmdline or "")
|
||||||
if not args:
|
if not args:
|
||||||
|
|||||||
Reference in New Issue
Block a user