mirror of
https://github.com/Horizontal42/netsleuth.git
synced 2026-11-11 01:02:10 +03:00
refactor: extract CLI utilities to cli_commands package
This commit is contained in:
parent
e392793834
commit
224b5f395a
2 changed files with 123 additions and 0 deletions
4
src/netsleuth/cli_commands/__init__.py
Normal file
4
src/netsleuth/cli_commands/__init__.py
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
"""CLI command modules for netsleuth.
|
||||
|
||||
This package splits the main CLI into smaller, focused modules.
|
||||
"""
|
||||
119
src/netsleuth/cli_commands/utils.py
Normal file
119
src/netsleuth/cli_commands/utils.py
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
"""Shared CLI utilities and helpers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import platform
|
||||
import socket
|
||||
import sys
|
||||
import uuid
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any, List, Optional, Tuple
|
||||
|
||||
import httpx
|
||||
import psutil
|
||||
import typer
|
||||
from rich.console import Console
|
||||
|
||||
from netsleuth import __version__
|
||||
from netsleuth.config import FORMAT_EXTENSIONS, Settings, load_settings
|
||||
from netsleuth.models import BindTarget, Finding
|
||||
|
||||
|
||||
@dataclass
|
||||
class Options:
|
||||
"""CLI options container."""
|
||||
|
||||
mode: str = "auto"
|
||||
target_kind: str | None = None
|
||||
target_value: str | None = None
|
||||
quick: bool = False
|
||||
full: bool = False
|
||||
extra_host: str | None = None
|
||||
speedtest_server: str | None = None
|
||||
dnsbl: bool = False
|
||||
ndt7: bool = False
|
||||
tcp_trace: bool = False
|
||||
tls: bool = False
|
||||
dns_advanced: bool = False
|
||||
path_diversity: bool = False
|
||||
prefix_bench: bool = False
|
||||
dpi_target: str | None = None
|
||||
formats: frozenset[str] = frozenset({"md"})
|
||||
interface: str | None = None
|
||||
bind: BindTarget | None = None
|
||||
|
||||
|
||||
def parse_formats(
|
||||
values: list[str], ru: bool, json_flag: bool, default: frozenset[str]
|
||||
) -> frozenset[str]:
|
||||
"""Parse output format options from CLI arguments."""
|
||||
tokens = [t.strip().lower() for raw in values for t in raw.split(",") if t.strip()]
|
||||
if ru:
|
||||
tokens.append("ru-md")
|
||||
if json_flag:
|
||||
tokens.append("json")
|
||||
if not tokens:
|
||||
return default
|
||||
if "none" in tokens:
|
||||
if len(set(tokens)) > 1:
|
||||
raise typer.BadParameter("'none' cannot be combined with other formats.")
|
||||
return frozenset()
|
||||
if "all" in tokens:
|
||||
return frozenset(FORMAT_EXTENSIONS)
|
||||
unknown = sorted(set(tokens) - set(FORMAT_EXTENSIONS))
|
||||
if unknown:
|
||||
raise typer.BadParameter(
|
||||
f"unknown format(s) {unknown}; expected one of {sorted(FORMAT_EXTENSIONS)}, 'all' or 'none'"
|
||||
)
|
||||
return frozenset(tokens)
|
||||
|
||||
|
||||
def format_siblings(values: tuple[str, ...]) -> tuple[str, ...]:
|
||||
"""Normalize format sibling options."""
|
||||
return tuple(v.strip() for v in values if v.strip())
|
||||
|
||||
|
||||
def parse_target(value: str) -> tuple[str, str]:
|
||||
"""Parse target specification (e.g., 'ip:8.8.8.8' or 'host:example.com')."""
|
||||
if ":" not in value:
|
||||
raise typer.BadParameter(f"invalid target {value!r}; expected format '<kind>:<value>'")
|
||||
kind, _, rest = value.partition(":")
|
||||
if not kind or not rest:
|
||||
raise typer.BadParameter(f"invalid target {value!r}; expected format '<kind>:<value>'")
|
||||
if kind not in ("ip", "host", "prefix"):
|
||||
raise typer.BadParameter(f"unknown target kind {kind!r}; expected 'ip', 'host', or 'prefix'")
|
||||
return kind, rest
|
||||
|
||||
|
||||
def _os_timezone() -> str | None:
|
||||
"""Get the OS timezone name."""
|
||||
try:
|
||||
if sys.platform == "win32":
|
||||
import winreg
|
||||
|
||||
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation")
|
||||
tz_name, _ = winreg.QueryValueEx(key, "TimeZoneKeyName")
|
||||
return tz_name
|
||||
else:
|
||||
timezone = os.readlink("/etc/localtime")
|
||||
if "zoneinfo" in timezone:
|
||||
return timezone.split("zoneinfo/")[-1]
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _dedupe(findings: list[Finding]) -> list[Finding]:
|
||||
"""Deduplicate findings by id, keeping the first occurrence."""
|
||||
seen: dict[str, Finding] = {}
|
||||
for f in findings:
|
||||
if f.id not in seen:
|
||||
seen[f.id] = f
|
||||
return list(seen.values())
|
||||
|
||||
|
||||
# Re-export commonly used objects
|
||||
console = Console()
|
||||
app = typer.Typer(add_completion=False, help="Deep network diagnostics.")
|
||||
Loading…
Add table
Reference in a new issue