ui: show backend traffic audit in dialog log

This commit is contained in:
beckline
2026-02-16 01:36:42 +03:00
parent 5eb292f17a
commit dd367728f6
3 changed files with 55 additions and 0 deletions

View File

@@ -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 {})

View File

@@ -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') в удобную модель

View File

@@ -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: