From f9e33e2847a26f61810fb948b9956a66e1e40e38 Mon Sep 17 00:00:00 2001 From: ReLoia Date: Mon, 13 Apr 2026 00:24:20 +0200 Subject: [PATCH] refactor(proxy): move proxy logic to services.mitmproxy and update Docker service model - Rename DockerService to DockerContainer and add folder attribute - Move proxy-related modules (models, validator) to services.mitmproxy - Update imports and references to new module locations - Refactor Docker utilities to use DockerContainer and improve service discovery - Add manager.py with create_proxy utility stub - Initialize discovered Docker services at app startup and warn if none found --- app/api/proxy/router.py | 2 +- app/integrations/docker/__init__.py | 6 ++--- app/integrations/docker/compose.py | 22 +++++++-------- app/integrations/docker/models.py | 4 ++- app/main.py | 15 +++++++++++ app/repositories/proxy.py | 2 +- app/services/{proxy => mitmproxy}/__init__.py | 0 app/services/mitmproxy/manager.py | 27 +++++++++++++++++++ app/services/{proxy => mitmproxy}/models.py | 0 .../{proxy => mitmproxy}/validator.py | 0 app/services/proxy/manager.py | 10 ------- tests/api/test_proxy.py | 2 +- 12 files changed, 61 insertions(+), 29 deletions(-) rename app/services/{proxy => mitmproxy}/__init__.py (100%) create mode 100644 app/services/mitmproxy/manager.py rename app/services/{proxy => mitmproxy}/models.py (100%) rename app/services/{proxy => mitmproxy}/validator.py (100%) delete mode 100644 app/services/proxy/manager.py diff --git a/app/api/proxy/router.py b/app/api/proxy/router.py index 29007ca..78b1965 100644 --- a/app/api/proxy/router.py +++ b/app/api/proxy/router.py @@ -11,7 +11,7 @@ from repositories.proxy import ( from repositories.proxy import ( list_proxies as list_proxy_documents, ) -from services.proxy.validator import ProxyCodeValidationError, validate_proxy_code +from services.mitmproxy.validator import ProxyCodeValidationError, validate_proxy_code router = APIRouter( prefix="/proxy", diff --git a/app/integrations/docker/__init__.py b/app/integrations/docker/__init__.py index 2483ddd..92297f2 100644 --- a/app/integrations/docker/__init__.py +++ b/app/integrations/docker/__init__.py @@ -2,15 +2,13 @@ from integrations.docker.compose import ( find_docker_compose, get_all_services, get_services, - get_visible_folders, ) from integrations.docker.editor import change_port -from integrations.docker.models import DockerService +from integrations.docker.models import DockerContainer __all__ = [ - "DockerService", + "DockerContainer", "find_docker_compose", - "get_visible_folders", "get_services", "get_all_services", "change_port", diff --git a/app/integrations/docker/compose.py b/app/integrations/docker/compose.py index c77aa5c..e58e613 100644 --- a/app/integrations/docker/compose.py +++ b/app/integrations/docker/compose.py @@ -2,7 +2,7 @@ from pathlib import Path import yaml -from integrations.docker.models import DockerService +from integrations.docker.models import DockerContainer from integrations.docker.ports import _get_service_port COMPOSE_NAMES = ( @@ -13,7 +13,7 @@ COMPOSE_NAMES = ( ) -def get_visible_folders(directory: Path) -> list[Path]: +def _get_visible_folders(directory: Path) -> list[Path]: return [ p for p in Path(directory).iterdir() if p.is_dir() and not p.name.startswith(".") ] @@ -32,8 +32,8 @@ def find_docker_compose(path: Path) -> tuple[dict, str] | None: return None -def get_services(folder: Path) -> list[DockerService] | None: - services: list[DockerService] = [] +def get_services(folder: Path) -> list[DockerContainer] | None: + services: list[DockerContainer] = [] compose_data = find_docker_compose(folder) if compose_data is None: return None @@ -56,20 +56,20 @@ def get_services(folder: Path) -> list[DockerService] | None: if not ports: continue - services.append({"name": service_name, "ports": ports}) + services.append({"name": service_name, "ports": ports, "folder": folder}) - return services or None + return services -def get_all_services(directory: str | Path) -> list[DockerService] | None: +def get_all_services(directory: str | Path) -> list[DockerContainer]: path = Path(directory) if not path.is_dir(): - return None + return [] - services: list[DockerService] = [] - for sub_folder in get_visible_folders(path): + services: list[DockerContainer] = [] + for sub_folder in _get_visible_folders(path): sub_services = get_services(sub_folder) if sub_services: services.extend(sub_services) - return services or None + return services diff --git a/app/integrations/docker/models.py b/app/integrations/docker/models.py index 8644ac5..1c75380 100644 --- a/app/integrations/docker/models.py +++ b/app/integrations/docker/models.py @@ -1,6 +1,8 @@ +from pathlib import Path from typing import TypedDict -class DockerService(TypedDict): +class DockerContainer(TypedDict): name: str ports: list[int] + folder: Path diff --git a/app/main.py b/app/main.py index 671a4b6..0494a77 100644 --- a/app/main.py +++ b/app/main.py @@ -1,4 +1,6 @@ +import sys from contextlib import asynccontextmanager +from pathlib import Path from fastapi import FastAPI @@ -8,6 +10,7 @@ from core.config import settings from core.mongodb import close_mongodb, init_mongodb from core.redis import close_redis, init_redis from core.security import hash_password +from integrations.docker import get_all_services from repositories.auth import ensure_auth_indexes, ensure_single_account from repositories.proxy import ensure_proxy_indexes @@ -22,6 +25,18 @@ async def lifespan(_: FastAPI): password_hash=hash_password(settings.AUTH_PASSWORD), ) await ensure_proxy_indexes() + + home_dir = str(Path.home()) + app.state.services = get_all_services(home_dir) or [] + if not app.state.services: + warning = ( + "\n" + "!" * 60 + "\n" + " WARNING: No services found in home directory!\n" + f" Scanned: {home_dir}\n" + + "!" * 60 + "\n" + ) + print(warning, file=sys.stderr) + try: yield finally: diff --git a/app/repositories/proxy.py b/app/repositories/proxy.py index 74aedf4..8895374 100644 --- a/app/repositories/proxy.py +++ b/app/repositories/proxy.py @@ -8,7 +8,7 @@ from pymongo import ReturnDocument from core.mongodb import get_database from models.proxy import ProxyDocument, ProxyUpdateInput from models.service import ServiceEndpointDocument -from services.proxy.models import ServiceConfig +from services.mitmproxy.models import ServiceConfig COLLECTION_NAME = "proxies" diff --git a/app/services/proxy/__init__.py b/app/services/mitmproxy/__init__.py similarity index 100% rename from app/services/proxy/__init__.py rename to app/services/mitmproxy/__init__.py diff --git a/app/services/mitmproxy/manager.py b/app/services/mitmproxy/manager.py new file mode 100644 index 0000000..3393c5f --- /dev/null +++ b/app/services/mitmproxy/manager.py @@ -0,0 +1,27 @@ +import uuid +from pathlib import Path + +from integrations.docker import DockerContainer +from services.mitmproxy.models import ServiceConfig + + +async def create_proxy( + port: int, + name: str, + service: ServiceConfig, + code: str, + services: list[DockerContainer] +) -> int: + """ + Utility function that will: + - run ``integrations/docker`` utilities to change the old port of original service to a random one + - write ``code`` into ``/tmp/theplug/{random}.py`` + - start a subprocess via asyncio + - return the proxy infos (pid, ) + """ + path = Path("/tmp/theplug/{random}.py".format(random=str(uuid.uuid4()))) + path.parent.mkdir(parents=True, exist_ok=True) + + path.write_text(code, encoding="utf-8") + + return 0 diff --git a/app/services/proxy/models.py b/app/services/mitmproxy/models.py similarity index 100% rename from app/services/proxy/models.py rename to app/services/mitmproxy/models.py diff --git a/app/services/proxy/validator.py b/app/services/mitmproxy/validator.py similarity index 100% rename from app/services/proxy/validator.py rename to app/services/mitmproxy/validator.py diff --git a/app/services/proxy/manager.py b/app/services/proxy/manager.py deleted file mode 100644 index e9e564c..0000000 --- a/app/services/proxy/manager.py +++ /dev/null @@ -1,10 +0,0 @@ -from services.proxy.models import ServiceConfig - - -async def create_proxy( - port: int, - name: str, - service: ServiceConfig, - code: str -): - return \ No newline at end of file diff --git a/tests/api/test_proxy.py b/tests/api/test_proxy.py index b6783b1..523862e 100644 --- a/tests/api/test_proxy.py +++ b/tests/api/test_proxy.py @@ -3,7 +3,7 @@ import importlib from core.security import get_auth_context, require_authenticated_user from models.proxy import ProxyDocument from models.service import ServiceEndpointDocument -from services.proxy.validator import ProxyCodeValidationError +from services.mitmproxy.validator import ProxyCodeValidationError proxy_router_module = importlib.import_module("api.proxy.router")