Multi-interface management, topology view, performance tuning, precompiled CSS
- Interface model with per-interface subnet/port/keys; import/adopt existing wg-quick configs (key-less imported peers, optional key rotation), cascade delete - Split wireguard.py into a package (keys via cryptography X25519, status, addressing, conf parse/render, sync, host tuning) - ECharts horizontal topology view (interface -> peers -> site subnets) - Advanced options: MTU, MSS clamping, FwMark/Table, custom PostUp/PostDown, per-peer keepalive override - Runtime settings (sample interval/retention, online threshold, UI refresh) with traffic sample pruning; host tuning (UDP buffers, backlog, GRO forwarding) - Precompiled Tailwind CSS replacing Play CDN runtime (fixes FOUC); stable table layout and diffed polling renders - Host network mode in compose; NAT/isolation iptables moved into app sync Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
parent
192960ad3e
commit
1b227c2470
@@ -0,0 +1,81 @@
|
||||
import subprocess
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from .runner import run
|
||||
|
||||
DEFAULT_ONLINE_THRESHOLD_SECONDS = 180
|
||||
|
||||
# Adjustable at runtime from the settings page.
|
||||
online_threshold_seconds = DEFAULT_ONLINE_THRESHOLD_SECONDS
|
||||
|
||||
|
||||
@dataclass
|
||||
class PeerStatus:
|
||||
public_key: str
|
||||
endpoint: str | None = None
|
||||
latest_handshake: datetime | None = None
|
||||
rx_bytes: int = 0
|
||||
tx_bytes: int = 0
|
||||
|
||||
@property
|
||||
def online(self) -> bool:
|
||||
if self.latest_handshake is None:
|
||||
return False
|
||||
delta = datetime.now(timezone.utc) - self.latest_handshake
|
||||
return delta.total_seconds() < online_threshold_seconds
|
||||
|
||||
|
||||
@dataclass
|
||||
class InterfaceStatus:
|
||||
name: str
|
||||
public_key: str | None = None
|
||||
listen_port: int | None = None
|
||||
up: bool = False
|
||||
peers: dict[str, PeerStatus] = field(default_factory=dict)
|
||||
|
||||
@property
|
||||
def total_rx(self) -> int:
|
||||
return sum(p.rx_bytes for p in self.peers.values())
|
||||
|
||||
@property
|
||||
def total_tx(self) -> int:
|
||||
return sum(p.tx_bytes for p in self.peers.values())
|
||||
|
||||
|
||||
def get_status(name: str) -> InterfaceStatus:
|
||||
status = InterfaceStatus(name=name)
|
||||
try:
|
||||
output = run(["wg", "show", name, "dump"])
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return status
|
||||
|
||||
status.up = True
|
||||
lines = output.splitlines()
|
||||
if lines:
|
||||
fields = lines[0].split("\t")
|
||||
if len(fields) >= 3:
|
||||
status.public_key = fields[1]
|
||||
status.listen_port = int(fields[2])
|
||||
for line in lines[1:]:
|
||||
fields = line.split("\t")
|
||||
if len(fields) < 8:
|
||||
continue
|
||||
peer = PeerStatus(public_key=fields[0])
|
||||
if fields[2] != "(none)":
|
||||
peer.endpoint = fields[2]
|
||||
handshake = int(fields[4])
|
||||
if handshake:
|
||||
peer.latest_handshake = datetime.fromtimestamp(handshake, tz=timezone.utc)
|
||||
peer.rx_bytes = int(fields[5])
|
||||
peer.tx_bytes = int(fields[6])
|
||||
status.peers[peer.public_key] = peer
|
||||
return status
|
||||
|
||||
|
||||
def system_interfaces() -> list[str]:
|
||||
try:
|
||||
output = run(["wg", "show", "interfaces"])
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return []
|
||||
return output.split()
|
||||
Reference in New Issue
Block a user