feat: add proxy template origin field and implement socket utilities for log streaming

This commit is contained in:
gabdevele
2026-05-07 10:54:57 +02:00 Unverified
parent 53dcdafdb1
commit 354ec150bc
32 changed files with 1681 additions and 1214 deletions
+24 -44
View File
@@ -4,12 +4,23 @@ import shutil
import signal
from pathlib import Path
import yaml
from app.integrations.docker import change_port
from app.integrations.docker.ports import get_random_port
from app.models.proxy import ProxyDocument
from app.services.mitmproxy.options import MitmOptionError, validate_options
from app.services.mitmproxy.runtime_files import (
conf_dir_for,
config_path_for,
ensure_log_path,
log_path_for,
read_cmdline,
read_log_tail,
remove_dir,
remove_file,
script_path_for,
write_config,
write_script,
)
from app.services.mitmproxy.runtime_validation import (
ReservedProxyPortError,
ensure_proxy_port_not_reserved,
@@ -300,23 +311,13 @@ class MitmdumpManager:
return candidate
async def write_script(self, proxy: ProxyDocument) -> Path:
path = self.script_path_for(proxy)
await asyncio.to_thread(path.parent.mkdir, parents=True, exist_ok=True)
await asyncio.to_thread(path.write_text, proxy.code, encoding="utf-8")
return path
return await write_script(self.work_dir, proxy)
async def write_config(self, proxy: ProxyDocument) -> Path:
path = self.config_path_for(proxy)
await asyncio.to_thread(path.parent.mkdir, parents=True, exist_ok=True)
options = {"block_global": False, **proxy.mitm_options}
config_text = yaml.safe_dump(options, sort_keys=True)
await asyncio.to_thread(path.write_text, config_text, encoding="utf-8")
return path
return await write_config(self.work_dir, proxy)
async def log_path(self, proxy: ProxyDocument) -> Path:
path = self.log_path_for(proxy)
await asyncio.to_thread(path.parent.mkdir, parents=True, exist_ok=True)
return path
return await ensure_log_path(self.work_dir, proxy)
async def spawn(self, command: list[str], log_path: Path):
with log_path.open("ab") as log_file:
@@ -408,49 +409,28 @@ class MitmdumpManager:
return expected_script in cmdline and expected_confdir in cmdline
def read_cmdline(self, pid: int) -> list[str]:
path = Path(f"/proc/{pid}/cmdline")
try:
raw = path.read_bytes()
except OSError:
return []
decoded = raw.decode("utf-8", errors="replace")
return [part for part in decoded.split("\x00") if part]
return read_cmdline(pid)
def script_path_for(self, proxy: ProxyDocument) -> Path:
return self.work_dir / "addons" / f"{proxy.proxy_port}.py"
return script_path_for(self.work_dir, proxy)
def conf_dir_for(self, proxy: ProxyDocument) -> Path:
return self.work_dir / "conf" / str(proxy.proxy_port)
return conf_dir_for(self.work_dir, proxy)
def config_path_for(self, proxy: ProxyDocument) -> Path:
return self.conf_dir_for(proxy) / "config.yaml"
return config_path_for(self.work_dir, proxy)
def log_path_for(self, proxy: ProxyDocument) -> Path:
return self.work_dir / "logs" / f"{proxy.proxy_port}.log"
return log_path_for(self.work_dir, proxy)
async def read_log_tail(self, path: Path, limit: int = 4000) -> str:
if not await asyncio.to_thread(path.exists):
return ""
content = await asyncio.to_thread(
path.read_text,
encoding="utf-8",
errors="replace",
)
return content[-limit:].strip()
return await read_log_tail(path, limit=limit)
def remove_file(self, path: Path) -> None:
try:
path.unlink(missing_ok=True)
except OSError:
return
remove_file(path)
def remove_dir(self, path: Path) -> None:
try:
shutil.rmtree(path)
except FileNotFoundError:
return
except OSError:
return
remove_dir(path)
def lock_for(self, proxy_port: int) -> asyncio.Lock:
lock = self.locks.get(proxy_port)