From d2379ed6ccd07ee125aec5ca68c8388fe0e45370 Mon Sep 17 00:00:00 2001 From: Rixuan Shao <2023311022@bipt.edu.cn> Date: Tue, 25 Aug 2026 14:01:02 +0800 Subject: [PATCH] feat: make direct Douyin traffic the branch default --- .env.example | 4 +- CHANGELOG.md | 2 + DouYinSparkFlow/core/browser.py | 37 ++++++++++++++---- .../tests/test_deployment_contract.py | 22 ++++++++--- DouYinSparkFlow/tests/test_douyin_network.py | 38 +++++++++++++++++++ DouYinSparkFlow/utils/config.py | 2 + DouYinSparkFlow/webui/app.py | 3 ++ .../webui/templates/dashboard.html | 2 + README.md | 6 +-- docker-compose.yml | 26 +------------ docs/usage.md | 4 +- 11 files changed, 101 insertions(+), 45 deletions(-) create mode 100644 DouYinSparkFlow/tests/test_douyin_network.py diff --git a/.env.example b/.env.example index 5ad6d11..2b65176 100644 --- a/.env.example +++ b/.env.example @@ -15,8 +15,8 @@ LOGIN_DESKTOP_PIDS_LIMIT=256 LOGIN_DESKTOP_IDLE_TIMEOUT_SECONDS=1800 LOGIN_DESKTOP_STOP_AFTER_EXPORT_SECONDS=60 LOGIN_DESKTOP_STATUS_CACHE_SECONDS=15 -# Login browser uses direct access first; Mihomo is a fallback when direct access fails. -LOGIN_DESKTOP_PROXY_MODE=auto +# Login browser uses direct access by default; Mihomo is an advanced option. +LOGIN_DESKTOP_PROXY_MODE=direct LOGIN_DESKTOP_PROXY=http://proxy:7890 LOGIN_DESKTOP_PREFLIGHT_TIMEOUT_SECONDS=15 LOGIN_DESKTOP_NETWORK_CACHE_SECONDS=30 diff --git a/CHANGELOG.md b/CHANGELOG.md index e27438e..b373111 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## 2026-08-25 +- Made the experimental branch default to direct Douyin business traffic; Mihomo is now an explicit advanced option for browser traffic. + - Made login-browser networking direct-first with Mihomo fallback and explicit preflight errors. # Changelog diff --git a/DouYinSparkFlow/core/browser.py b/DouYinSparkFlow/core/browser.py index f501ccc..d36f171 100644 --- a/DouYinSparkFlow/core/browser.py +++ b/DouYinSparkFlow/core/browser.py @@ -8,7 +8,7 @@ from pathlib import Path from playwright.async_api import async_playwright from rich.console import Console -from utils.config import DEBUG, Environment, get_environment +from utils.config import DEBUG, Environment, get_app_settings, get_environment console = Console() @@ -52,6 +52,30 @@ def _browser_args(): ] +def _douyin_browser_proxy(): + """Return an explicit proxy for Douyin browser traffic, or None for direct.""" + settings = get_app_settings(force_reload=True) + mode = str( + os.getenv("SPARKFLOW_DOUYIN_NETWORK_MODE") + or settings.get("douyin_network_mode", "direct") + ).strip().lower() + if mode != "mihomo": + return None + return str( + os.getenv("SPARKFLOW_DOUYIN_PROXY_URL") + or settings.get("douyin_proxy_url", "http://proxy:7890") + ).strip() or None + + +def _browser_launch_options(GUI=False): + args = _browser_args() + proxy = _douyin_browser_proxy() + if proxy: + return {"headless": _headless_for(GUI), "args": args, "proxy": {"server": proxy}} + args.append("--no-proxy-server") + return {"headless": _headless_for(GUI), "args": args} + + def sanitize_profile_name(value): raw = str(value or "").strip() if not raw: @@ -83,10 +107,7 @@ async def get_browser(GUI=False): try: playwright = await async_playwright().start() - browser = await playwright.chromium.launch( - headless=_headless_for(GUI), - args=_browser_args(), - ) + browser = await playwright.chromium.launch(**_browser_launch_options(GUI)) return playwright, browser except Exception as exc: if "Executable doesn't exist" in str(exc) and get_environment() != Environment.GITHUBACTION: @@ -105,11 +126,11 @@ async def get_persistent_browser_context(profile_name, GUI=False, root=None): try: playwright = await async_playwright().start() + launch_options = _browser_launch_options(GUI) + launch_options["viewport"] = {"width": 1600, "height": 1000} context = await playwright.chromium.launch_persistent_context( str(profile_dir), - headless=_headless_for(GUI), - viewport={"width": 1600, "height": 1000}, - args=_browser_args(), + **launch_options, ) return playwright, context, profile_dir except Exception as exc: diff --git a/DouYinSparkFlow/tests/test_deployment_contract.py b/DouYinSparkFlow/tests/test_deployment_contract.py index 6a12647..4ba3006 100644 --- a/DouYinSparkFlow/tests/test_deployment_contract.py +++ b/DouYinSparkFlow/tests/test_deployment_contract.py @@ -72,9 +72,14 @@ class DeploymentContractTests(unittest.TestCase): compose = (REPO_ROOT / "docker-compose.yml").read_text(encoding="utf-8") self.assertIn("# Build proxies must never leak", dockerfile) self.assertIn("http_proxy=", dockerfile) - self.assertGreaterEqual(compose.count("http_proxy: http://proxy:7890"), 3) - self.assertGreaterEqual(compose.count("https_proxy: http://proxy:7890"), 3) - self.assertGreaterEqual(compose.count("no_proxy:"), 3) + self.assertIn("HTTP_PROXY: ${HTTP_PROXY_BUILD:-}", compose) + self.assertIn("HTTPS_PROXY: ${HTTPS_PROXY_BUILD:-}", compose) + self.assertIn("ALL_PROXY: ${ALL_PROXY_BUILD:-}", compose) + for service in ("web", "scheduler", "task"): + block = compose.split(f" {service}:", 1)[1] + block = block.split("\n ", 1)[0] + self.assertNotIn("HTTP_PROXY: http://proxy:7890", block) + self.assertNotIn("http_proxy: http://proxy:7890", block) def test_sensitive_ports_bind_to_loopback_by_default(self): text = (REPO_ROOT / "docker-compose.yml").read_text(encoding="utf-8") @@ -119,14 +124,21 @@ class DeploymentContractTests(unittest.TestCase): env_example = (REPO_ROOT / ".env.example").read_text(encoding="utf-8") server = (SOURCE_ROOT / "login_desktop_server.py").read_text(encoding="utf-8") login_block = compose.split(" login-desktop:", 1)[1].split(" scheduler:", 1)[0] - self.assertIn("LOGIN_DESKTOP_PROXY_MODE: ${LOGIN_DESKTOP_PROXY_MODE:-auto}", login_block) + self.assertIn("LOGIN_DESKTOP_PROXY_MODE: ${LOGIN_DESKTOP_PROXY_MODE:-direct}", login_block) self.assertIn("LOGIN_DESKTOP_PROXY: ${LOGIN_DESKTOP_PROXY:-http://proxy:7890}", login_block) self.assertNotIn("HTTP_PROXY: http://proxy:7890", login_block) - self.assertIn("LOGIN_DESKTOP_PROXY_MODE=auto", env_example) + self.assertIn("LOGIN_DESKTOP_PROXY_MODE=direct", env_example) self.assertIn('candidates.append(("direct", None))', server) self.assertIn('candidates.append(("proxy", LOGIN_PROXY_SERVER))', server) self.assertIn('"--no-proxy-server"', server) self.assertIn('"/preflight"', server) + self.assertIn('LOGIN_DESKTOP_PROXY_MODE: ${LOGIN_DESKTOP_PROXY_MODE:-direct}', login_block) + dashboard = (SOURCE_ROOT / "webui" / "templates" / "dashboard.html").read_text(encoding="utf-8") + self.assertIn('name="douyin_network_mode"', dashboard) + self.assertIn('name="douyin_proxy_url"', dashboard) + browser = (SOURCE_ROOT / "core" / "browser.py").read_text(encoding="utf-8") + self.assertIn("--no-proxy-server", browser) + self.assertIn("SPARKFLOW_DOUYIN_NETWORK_MODE", browser) def test_login_desktop_resource_controls_are_configured(self): compose = (REPO_ROOT / "docker-compose.yml").read_text(encoding="utf-8") diff --git a/DouYinSparkFlow/tests/test_douyin_network.py b/DouYinSparkFlow/tests/test_douyin_network.py new file mode 100644 index 0000000..4418b16 --- /dev/null +++ b/DouYinSparkFlow/tests/test_douyin_network.py @@ -0,0 +1,38 @@ +import unittest +from unittest.mock import patch + +from core import browser +from utils import config as config_module + + +class DouyinNetworkRouteTests(unittest.TestCase): + def test_default_route_is_direct(self): + with patch.object(browser, "get_app_settings", return_value={}): + with patch.dict(browser.os.environ, {}, clear=False): + browser.os.environ.pop("SPARKFLOW_DOUYIN_NETWORK_MODE", None) + browser.os.environ.pop("SPARKFLOW_DOUYIN_PROXY_URL", None) + options = browser._browser_launch_options(False) + self.assertNotIn("proxy", options) + self.assertIn("--no-proxy-server", options["args"]) + + def test_mihomo_route_is_explicit(self): + with patch.object(browser, "get_app_settings", return_value={}): + with patch.dict( + browser.os.environ, + { + "SPARKFLOW_DOUYIN_NETWORK_MODE": "mihomo", + "SPARKFLOW_DOUYIN_PROXY_URL": "http://127.0.0.1:7890", + }, + clear=False, + ): + options = browser._browser_launch_options(False) + self.assertEqual({"server": "http://127.0.0.1:7890"}, options["proxy"]) + self.assertNotIn("--no-proxy-server", options["args"]) + + def test_default_app_settings_keep_direct_route(self): + self.assertEqual("direct", config_module.DEFAULT_APP_SETTINGS["douyin_network_mode"]) + self.assertEqual("http://proxy:7890", config_module.DEFAULT_APP_SETTINGS["douyin_proxy_url"]) + + +if __name__ == "__main__": + unittest.main() diff --git a/DouYinSparkFlow/utils/config.py b/DouYinSparkFlow/utils/config.py index 1e7cf57..3d2354d 100644 --- a/DouYinSparkFlow/utils/config.py +++ b/DouYinSparkFlow/utils/config.py @@ -85,6 +85,8 @@ DEFAULT_APP_SETTINGS = { "proxy_refresh_script": "/opt/douyin-sparkflow/refresh_proxy.sh", "local_login_helper_url": "http://127.0.0.1:18765", "login_desktop_api_url": "http://127.0.0.1:18090", + "douyin_network_mode": "direct", + "douyin_proxy_url": "http://proxy:7890", "login_desktop_public_url": "", "login_desktop_public_scheme": "http", "login_desktop_public_port": 8788, diff --git a/DouYinSparkFlow/webui/app.py b/DouYinSparkFlow/webui/app.py index 098b31e..9e12318 100644 --- a/DouYinSparkFlow/webui/app.py +++ b/DouYinSparkFlow/webui/app.py @@ -1035,6 +1035,9 @@ def create_app(): settings["login_desktop_api_url"] = str( form.get("login_desktop_api_url", settings.get("login_desktop_api_url", "http://127.0.0.1:18090")) ).strip() + network_mode = str(form.get("douyin_network_mode", settings.get("douyin_network_mode", "direct"))).strip().lower() + settings["douyin_network_mode"] = network_mode if network_mode in {"direct", "mihomo"} else "direct" + settings["douyin_proxy_url"] = str(form.get("douyin_proxy_url", settings.get("douyin_proxy_url", "http://proxy:7890"))).strip() settings["ui_port"] = int(form.get("ui_port", settings.get("ui_port", 8787))) save_app_settings(settings) diff --git a/DouYinSparkFlow/webui/templates/dashboard.html b/DouYinSparkFlow/webui/templates/dashboard.html index 5bdd833..987304e 100644 --- a/DouYinSparkFlow/webui/templates/dashboard.html +++ b/DouYinSparkFlow/webui/templates/dashboard.html @@ -578,6 +578,8 @@ + +