ss
This commit is contained in:
@@ -29,6 +29,7 @@ BASH_BIN = "/bin/bash" if Path("/bin/bash").is_file() else (shutil.which("bash")
|
||||
|
||||
_PRO_URL_CONFIG_INI = re.compile(r"^URL_config\.[a-zA-Z0-9_.-]+\.ini$")
|
||||
_PRO_YOUTUBE_INI = re.compile(r"^youtube\.[a-zA-Z0-9_.-]+\.ini$")
|
||||
_NETWORK_RATE_CACHE: dict[str, Any] = {"timestamp": 0.0, "interfaces": {}}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -497,12 +498,10 @@ def _env_int(env: dict[str, str], key: str, default: int) -> int:
|
||||
|
||||
|
||||
def _neko_http_ports(env: dict[str, str]) -> list[int]:
|
||||
count = max(1, min(3, _env_int(env, "NEKO_INSTANCE_COUNT", 2)))
|
||||
return [
|
||||
_env_int(env, "NEKO_HTTP_PORT_1", _env_int(env, "NEKO_HTTP_PORT", 9200)),
|
||||
_env_int(env, "NEKO_HTTP_PORT_2", 9201),
|
||||
_env_int(env, "NEKO_HTTP_PORT_3", 9202),
|
||||
][:count]
|
||||
max_count = max(2, min(64, _env_int(env, "NEKO_MAX_INSTANCE_COUNT", 64)))
|
||||
count = max(2, min(max_count, _env_int(env, "NEKO_INSTANCE_COUNT", 2)))
|
||||
base = _env_int(env, "NEKO_HTTP_PORT", 9200)
|
||||
return [_env_int(env, f"NEKO_HTTP_PORT_{idx}", base + idx - 1) for idx in range(1, count + 1)]
|
||||
|
||||
|
||||
def _neko_instances(mdns_host: str, env: dict[str, str]) -> list[dict[str, Any]]:
|
||||
@@ -790,16 +789,24 @@ def _iface_operstate(name: str) -> str:
|
||||
|
||||
|
||||
def network_iface_stats() -> dict[str, Any]:
|
||||
"""Linux /proc/net/dev cumulative counters plus current link bandwidth hints."""
|
||||
"""Linux /proc/net/dev counters with per-refresh bandwidth rates."""
|
||||
dev = Path("/proc/net/dev")
|
||||
interfaces: list[dict[str, Any]] = []
|
||||
if not dev.is_file():
|
||||
return {"interfaces": interfaces}
|
||||
return {"interfaces": interfaces, "total_rx_bytes": 0, "total_tx_bytes": 0, "total_rx_bps": 0.0, "total_tx_bps": 0.0}
|
||||
skip_prefixes = ("lo", "docker", "veth", "br-", "virbr", "tun", "tap")
|
||||
try:
|
||||
lines = dev.read_text(encoding="utf-8", errors="replace").splitlines()
|
||||
except OSError:
|
||||
return {"interfaces": interfaces}
|
||||
return {"interfaces": interfaces, "total_rx_bytes": 0, "total_tx_bytes": 0, "total_rx_bps": 0.0, "total_tx_bps": 0.0}
|
||||
now = time.monotonic()
|
||||
previous_ts = float(_NETWORK_RATE_CACHE.get("timestamp") or 0.0)
|
||||
previous = _NETWORK_RATE_CACHE.get("interfaces") or {}
|
||||
next_cache: dict[str, dict[str, int]] = {}
|
||||
total_rx = 0
|
||||
total_tx = 0
|
||||
total_rx_bps = 0.0
|
||||
total_tx_bps = 0.0
|
||||
for line in lines[2:]:
|
||||
if ":" not in line:
|
||||
continue
|
||||
@@ -811,20 +818,50 @@ def network_iface_stats() -> dict[str, Any]:
|
||||
if len(parts) < 16:
|
||||
continue
|
||||
try:
|
||||
rx_bytes = int(parts[0])
|
||||
rx_packets = int(parts[1])
|
||||
tx_bytes = int(parts[8])
|
||||
tx_packets = int(parts[9])
|
||||
total_rx += rx_bytes
|
||||
total_tx += tx_bytes
|
||||
prev = previous.get(name) if isinstance(previous, dict) else None
|
||||
elapsed = max(0.0, now - previous_ts) if previous_ts else 0.0
|
||||
rx_bps = 0.0
|
||||
tx_bps = 0.0
|
||||
if isinstance(prev, dict) and elapsed > 0:
|
||||
prev_rx = int(prev.get("rx_bytes", rx_bytes))
|
||||
prev_tx = int(prev.get("tx_bytes", tx_bytes))
|
||||
rx_bps = max(0.0, (rx_bytes - prev_rx) / elapsed)
|
||||
tx_bps = max(0.0, (tx_bytes - prev_tx) / elapsed)
|
||||
total_rx_bps += rx_bps
|
||||
total_tx_bps += tx_bps
|
||||
next_cache[name] = {"rx_bytes": rx_bytes, "tx_bytes": tx_bytes}
|
||||
interfaces.append(
|
||||
{
|
||||
"name": name,
|
||||
"rx_bytes": int(parts[0]),
|
||||
"rx_packets": int(parts[1]),
|
||||
"tx_bytes": int(parts[8]),
|
||||
"tx_packets": int(parts[9]),
|
||||
"rx_bytes": rx_bytes,
|
||||
"rx_packets": rx_packets,
|
||||
"tx_bytes": tx_bytes,
|
||||
"tx_packets": tx_packets,
|
||||
"rx_bps": round(rx_bps, 2),
|
||||
"tx_bps": round(tx_bps, 2),
|
||||
"sample_interval_seconds": round(elapsed, 3),
|
||||
"speed_mbps": _iface_speed_mbps(name),
|
||||
"operstate": _iface_operstate(name),
|
||||
}
|
||||
)
|
||||
except (ValueError, IndexError):
|
||||
continue
|
||||
return {"interfaces": interfaces}
|
||||
_NETWORK_RATE_CACHE["timestamp"] = now
|
||||
_NETWORK_RATE_CACHE["interfaces"] = next_cache
|
||||
return {
|
||||
"interfaces": interfaces,
|
||||
"total_rx_bytes": total_rx,
|
||||
"total_tx_bytes": total_tx,
|
||||
"total_rx_bps": round(total_rx_bps, 2),
|
||||
"total_tx_bps": round(total_tx_bps, 2),
|
||||
"sample_interval_seconds": round(max(0.0, now - previous_ts) if previous_ts else 0.0, 3),
|
||||
}
|
||||
|
||||
|
||||
def recent_pm2_log_lines(max_lines: int = 14) -> list[str]:
|
||||
@@ -878,7 +915,7 @@ async def stack_stream_probes() -> dict[str, Any]:
|
||||
http = _probe_http_get(f"http://127.0.0.1:{port}/", timeout=2.5)
|
||||
instances.append({"id": idx, "port": port, "tcp": tcp, "http": http})
|
||||
primary = instances[0] if instances else {"port": 0, "tcp": {}, "http": {}}
|
||||
required_count = max(1, min(3, _env_int(env, "NEKO_INSTANCE_COUNT", 2)))
|
||||
required_count = len(neko_ports)
|
||||
return {
|
||||
"port": primary["port"],
|
||||
"tcp": primary["tcp"],
|
||||
|
||||
Reference in New Issue
Block a user