Files
elmprodvpn/selective-vpn-gui/main_window/ui_tabs_other_mixin.py

306 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
from PySide6.QtWidgets import (
QCheckBox,
QFormLayout,
QGroupBox,
QHBoxLayout,
QLabel,
QLineEdit,
QListWidget,
QListWidgetItem,
QPlainTextEdit,
QPushButton,
QProgressBar,
QRadioButton,
QVBoxLayout,
QWidget,
)
class UITabsOtherMixin:
def _build_tab_routes(self) -> None:
tab = QWidget()
layout = QVBoxLayout(tab)
# --- Service actions ---
act_group = QGroupBox("Selective routes service")
act_layout = QHBoxLayout(act_group)
self.btn_routes_start = QPushButton("Start")
self.btn_routes_start.clicked.connect(
lambda: self.on_routes_action("start")
)
self.btn_routes_restart = QPushButton("Restart")
self.btn_routes_restart.clicked.connect(
lambda: self.on_routes_action("restart")
)
self.btn_routes_stop = QPushButton("Stop")
self.btn_routes_stop.clicked.connect(
lambda: self.on_routes_action("stop")
)
act_layout.addWidget(self.btn_routes_start)
act_layout.addWidget(self.btn_routes_restart)
act_layout.addWidget(self.btn_routes_stop)
act_layout.addStretch(1)
layout.addWidget(act_group)
# --- Timer / policy route ---
timer_group = QGroupBox("Timer")
timer_layout = QHBoxLayout(timer_group)
self.chk_timer = QCheckBox("Enable timer")
self.chk_timer.stateChanged.connect(self.on_toggle_timer)
timer_layout.addWidget(self.chk_timer)
self.btn_fix_policy = QPushButton("Fix policy route")
self.btn_fix_policy.clicked.connect(self.on_fix_policy_route)
timer_layout.addWidget(self.btn_fix_policy)
timer_layout.addStretch(1)
layout.addWidget(timer_group)
# --- Traffic mode relay ---
traffic_group = QGroupBox("Traffic mode relay")
traffic_layout = QVBoxLayout(traffic_group)
relay_row = QHBoxLayout()
self.btn_traffic_settings = QPushButton("Open traffic settings")
self.btn_traffic_settings.clicked.connect(self.on_open_traffic_settings)
relay_row.addWidget(self.btn_traffic_settings)
self.btn_traffic_test = QPushButton("Test mode")
self.btn_traffic_test.clicked.connect(self.on_test_traffic_mode)
relay_row.addWidget(self.btn_traffic_test)
self.btn_routes_prewarm = QPushButton("Prewarm wildcard now")
self.btn_routes_prewarm.setToolTip("""EN: Sends DNS queries for wildcard domains to prefill agvpn_dyn4 before traffic arrives.
RU: Делает DNS-запросы wildcard-доменов, чтобы заранее наполнить agvpn_dyn4.""")
self.btn_routes_prewarm.clicked.connect(self.on_smartdns_prewarm)
relay_row.addWidget(self.btn_routes_prewarm)
self.btn_routes_precheck_debug = QPushButton("Debug precheck now")
self.btn_routes_precheck_debug.setToolTip("""EN: Debug helper. Arms one-shot resolver precheck and requests routes restart now.
RU: Отладочный helper. Включает one-shot precheck резолвера и запрашивает restart routes.""")
self.btn_routes_precheck_debug.clicked.connect(self.on_routes_precheck_debug)
relay_row.addWidget(self.btn_routes_precheck_debug)
relay_row.addStretch(1)
traffic_layout.addLayout(relay_row)
self.chk_routes_prewarm_aggressive = QCheckBox("Aggressive prewarm (use subs)")
self.chk_routes_prewarm_aggressive.setToolTip("""EN: Aggressive mode also queries subs list. This can increase DNS load.
RU: Агрессивный режим дополнительно дергает subs список. Может увеличить нагрузку на DNS.""")
self.chk_routes_prewarm_aggressive.stateChanged.connect(self._on_prewarm_aggressive_changed)
traffic_layout.addWidget(self.chk_routes_prewarm_aggressive)
self.lbl_routes_prewarm_mode = QLabel("Prewarm mode: wildcard-only")
self.lbl_routes_prewarm_mode.setStyleSheet("color: gray;")
traffic_layout.addWidget(self.lbl_routes_prewarm_mode)
self._update_prewarm_mode_label()
self.lbl_traffic_mode_state = QLabel("Traffic mode: —")
self.lbl_traffic_mode_state.setStyleSheet("color: gray;")
traffic_layout.addWidget(self.lbl_traffic_mode_state)
self.lbl_traffic_mode_diag = QLabel("")
self.lbl_traffic_mode_diag.setStyleSheet("color: gray;")
traffic_layout.addWidget(self.lbl_traffic_mode_diag)
self.lbl_routes_resolve_summary = QLabel("Resolve summary: —")
self.lbl_routes_resolve_summary.setToolTip("""EN: Parsed from latest 'resolve summary' trace line.
RU: Берется из последней строки 'resolve summary' в trace.""")
self.lbl_routes_resolve_summary.setStyleSheet("color: gray;")
traffic_layout.addWidget(self.lbl_routes_resolve_summary)
self.lbl_routes_recheck_summary = QLabel("Timeout recheck: —")
self.lbl_routes_recheck_summary.setToolTip("""EN: Hidden timeout-recheck counters included in resolve summary.
RU: Счетчики скрытого timeout-recheck из итогового resolve summary.""")
self.lbl_routes_recheck_summary.setStyleSheet("color: gray;")
traffic_layout.addWidget(self.lbl_routes_recheck_summary)
layout.addWidget(traffic_group)
# --- NFT progress (agvpn4) ---
progress_row = QHBoxLayout()
self.routes_progress = QProgressBar()
self.routes_progress.setRange(0, 100)
self.routes_progress.setValue(0)
self.routes_progress.setFormat("") # текст выводим отдельным лейблом
self.routes_progress.setTextVisible(False)
self.routes_progress.setEnabled(False) # idle по умолчанию
self.lbl_routes_progress = QLabel("NFT: idle")
self.lbl_routes_progress.setStyleSheet("color: gray;")
progress_row.addWidget(self.routes_progress)
progress_row.addWidget(self.lbl_routes_progress)
layout.addLayout(progress_row)
# --- Log output ---
self.txt_routes = QPlainTextEdit()
self.txt_routes.setReadOnly(True)
layout.addWidget(self.txt_routes, stretch=1)
self.tabs.addTab(tab, "Routes")
# ---------------- DNS TAB ----------------
def _build_tab_dns(self) -> None:
tab = QWidget()
main_layout = QVBoxLayout(tab)
tip = QLabel("Tip: hover fields for help. Подсказка: наведи на элементы для описания.")
tip.setWordWrap(True)
tip.setStyleSheet("color: gray;")
main_layout.addWidget(tip)
resolver_group = QGroupBox("Resolver DNS")
resolver_group.setToolTip("""EN: Compact resolver DNS status. Open benchmark to test/apply upstreams.
RU: Компактный статус DNS резолвера. Открой benchmark для проверки/применения апстримов.""")
resolver_layout = QVBoxLayout(resolver_group)
row = QHBoxLayout()
self.btn_dns_benchmark = QPushButton("Open DNS benchmark")
self.btn_dns_benchmark.clicked.connect(self.on_open_dns_benchmark)
row.addWidget(self.btn_dns_benchmark)
row.addStretch(1)
resolver_layout.addLayout(row)
self.lbl_dns_resolver_upstreams = QLabel("Resolver upstreams: default[—, —] meta[—, —]")
self.lbl_dns_resolver_upstreams.setStyleSheet("color: gray;")
resolver_layout.addWidget(self.lbl_dns_resolver_upstreams)
self.lbl_dns_resolver_health = QLabel("Resolver health: —")
self.lbl_dns_resolver_health.setStyleSheet("color: gray;")
resolver_layout.addWidget(self.lbl_dns_resolver_health)
main_layout.addWidget(resolver_group)
smart_group = QGroupBox("SmartDNS")
smart_group.setToolTip("""EN: SmartDNS is used for wildcard domains in hybrid mode.
RU: SmartDNS используется для wildcard-доменов в hybrid режиме.""")
smart_layout = QVBoxLayout(smart_group)
smart_form = QFormLayout()
self.ent_smartdns_addr = QLineEdit()
self.ent_smartdns_addr.setToolTip("""EN: SmartDNS address in host#port format (example: 127.0.0.1#6053).
RU: Адрес SmartDNS в формате host#port (пример: 127.0.0.1#6053).""")
self.ent_smartdns_addr.setPlaceholderText("127.0.0.1#6053")
self.ent_smartdns_addr.textEdited.connect(self._schedule_dns_autosave)
smart_form.addRow("SmartDNS address", self.ent_smartdns_addr)
smart_layout.addLayout(smart_form)
self.chk_dns_via_smartdns = QCheckBox("Use SmartDNS for wildcard domains")
self.chk_dns_via_smartdns.setToolTip("""EN: Hybrid wildcard mode: wildcard domains resolve via SmartDNS, other lists resolve via direct upstreams.
RU: Hybrid wildcard режим: wildcard-домены резолвятся через SmartDNS, остальные списки через direct апстримы.""")
self.chk_dns_via_smartdns.stateChanged.connect(self.on_dns_mode_toggle)
smart_layout.addWidget(self.chk_dns_via_smartdns)
self.lbl_dns_mode_state = QLabel("Resolver mode: unknown")
self.lbl_dns_mode_state.setToolTip("""EN: Current resolver mode reported by API.
RU: Текущий режим резолвера по данным API.""")
smart_layout.addWidget(self.lbl_dns_mode_state)
self.chk_dns_unit_relay = QCheckBox("SmartDNS unit relay: OFF")
self.chk_dns_unit_relay.setToolTip("""EN: Starts/stops smartdns-local.service. Service state is independent from resolver mode.
RU: Запускает/останавливает smartdns-local.service. Состояние сервиса не равно режиму резолвера.""")
self.chk_dns_unit_relay.stateChanged.connect(self.on_smartdns_unit_toggle)
smart_layout.addWidget(self.chk_dns_unit_relay)
self.chk_dns_runtime_nftset = QCheckBox("SmartDNS runtime accelerator (nftset -> agvpn_dyn4): ON")
self.chk_dns_runtime_nftset.setToolTip("""EN: Optional accelerator: SmartDNS can add resolved IPs to agvpn_dyn4 in runtime (via nftset).
EN: Wildcard still works without it (resolver job + prewarm).
RU: Опциональный ускоритель: SmartDNS может добавлять IP в agvpn_dyn4 в runtime (через nftset).
RU: Wildcard работает и без него (resolver job + prewarm).""")
self.chk_dns_runtime_nftset.stateChanged.connect(self.on_smartdns_runtime_toggle)
smart_layout.addWidget(self.chk_dns_runtime_nftset)
self.lbl_dns_wildcard_source = QLabel("Wildcard source: resolver")
self.lbl_dns_wildcard_source.setToolTip("""EN: Where wildcard IPs come from: resolver job, SmartDNS runtime nftset, or both.
RU: Источник wildcard IP: резолвер, runtime nftset SmartDNS, или оба.""")
self.lbl_dns_wildcard_source.setStyleSheet("color: gray;")
smart_layout.addWidget(self.lbl_dns_wildcard_source)
main_layout.addWidget(smart_group)
main_layout.addStretch(1)
self.tabs.addTab(tab, "DNS")
# ---------------- DOMAINS TAB ----------------
def _build_tab_domains(self) -> None:
tab = QWidget()
main_layout = QHBoxLayout(tab)
left = QVBoxLayout()
main_layout.addLayout(left)
left.addWidget(QLabel("Files:"))
self.lst_files = QListWidget()
for name in (
"bases",
"meta-special",
"subs",
"static-ips",
"last-ips-map-direct",
"last-ips-map-wildcard",
"wildcard-observed-hosts",
"smartdns.conf",
):
QListWidgetItem(name, self.lst_files)
self.lst_files.setCurrentRow(0)
self.lst_files.itemSelectionChanged.connect(self.on_domains_load)
left.addWidget(self.lst_files)
self.btn_domains_save = QPushButton("Save file")
self.btn_domains_save.clicked.connect(self.on_domains_save)
left.addWidget(self.btn_domains_save)
left.addStretch(1)
right_layout = QVBoxLayout()
main_layout.addLayout(right_layout, stretch=1)
self.lbl_domains_info = QLabel("")
self.lbl_domains_info.setStyleSheet("color: gray;")
right_layout.addWidget(self.lbl_domains_info)
self.txt_domains = QPlainTextEdit()
right_layout.addWidget(self.txt_domains, stretch=1)
self.tabs.addTab(tab, "Domains")
# ---------------- TRACE TAB ----------------
def _build_tab_trace(self) -> None:
tab = QWidget()
layout = QVBoxLayout(tab)
top = QHBoxLayout()
layout.addLayout(top)
self.radio_trace_full = QRadioButton("Full")
self.radio_trace_full.setChecked(True)
self.radio_trace_full.toggled.connect(self.refresh_trace_tab)
top.addWidget(self.radio_trace_full)
self.radio_trace_gui = QRadioButton("Events")
self.radio_trace_gui.toggled.connect(self.refresh_trace_tab)
top.addWidget(self.radio_trace_gui)
self.radio_trace_smartdns = QRadioButton("SmartDNS")
self.radio_trace_smartdns.toggled.connect(self.refresh_trace_tab)
top.addWidget(self.radio_trace_smartdns)
btn_refresh = QPushButton("Refresh")
btn_refresh.clicked.connect(self.refresh_trace_tab)
top.addWidget(btn_refresh)
top.addStretch(1)
self.txt_trace = QPlainTextEdit()
self.txt_trace.setReadOnly(True)
layout.addWidget(self.txt_trace, stretch=1)
self.tabs.addTab(tab, "Trace")