Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"mode": "apps_script",
"google_ip": "216.239.38.120",
"front_domain": "www.google.com",
"script_id": "YOUR_APPS_SCRIPT_DEPLOYMENT_ID",
"script_ids": ["YOUR_APPS_SCRIPT_DEPLOYMENT_ID"],
"auth_key": "CHANGE_ME_TO_A_STRONG_SECRET",
"listen_host": "127.0.0.1",
"socks5_enabled": true,
Expand Down Expand Up @@ -85,5 +85,9 @@
"safebrowsing.google.com"
],
"youtube_via_relay": false,
"hosts": {}
"hosts": {},
"upstream_forwarder_url": "",
"dashboard_enabled": true,
"dashboard_host": "127.0.0.1",
"dashboard_port": 7878
}
57 changes: 37 additions & 20 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@

from cert_installer import install_ca, uninstall_ca, is_ca_trusted
from constants import __version__
from dashboard import Dashboard
from lan_utils import log_lan_access
from google_ip_scanner import scan_sync
from logging_utils import configure as configure_logging, print_banner
from mitm import CA_CERT_FILE
from proxy_server import ProxyServer
from supervisor import Supervisor


def setup_logging(level_name: str):
Expand Down Expand Up @@ -166,11 +167,14 @@ def main():
print(f"Invalid JSON in config: {e}")
sys.exit(1)

# Environment variable overrides
legacy_sid = config.pop("script_id", None)
if legacy_sid and not config.get("script_ids"):
config["script_ids"] = [legacy_sid] if isinstance(legacy_sid, str) else list(legacy_sid)

if os.environ.get("DFT_AUTH_KEY"):
config["auth_key"] = os.environ["DFT_AUTH_KEY"]
if os.environ.get("DFT_SCRIPT_ID"):
config["script_id"] = os.environ["DFT_SCRIPT_ID"]
config["script_ids"] = [os.environ["DFT_SCRIPT_ID"]]

# CLI argument overrides
if args.port is not None:
Expand Down Expand Up @@ -209,12 +213,11 @@ def main():
)
sys.exit(1)

# Always Apps Script mode — force-set for backward-compat configs.
config["mode"] = "apps_script"
sid = config.get("script_ids") or config.get("script_id")
if not sid or (isinstance(sid, str) and sid == "YOUR_APPS_SCRIPT_DEPLOYMENT_ID"):
print("Missing 'script_id' in config.")
print("Deploy the Apps Script from Code.gs and paste the Deployment ID.")
sids = config.get("script_ids") or []
if not isinstance(sids, list) or not sids or sids == ["YOUR_APPS_SCRIPT_DEPLOYMENT_ID"]:
print("Missing 'script_ids' in config.")
print("Deploy the Apps Script from Code.gs and paste the Deployment ID(s).")
sys.exit(1)

# ── Google IP Scanner ──────────────────────────────────────────────────
Expand All @@ -234,13 +237,10 @@ def main():

log.info("Apps Script relay : SNI=%s → script.google.com",
config.get("front_domain", "www.google.com"))
script_ids = config.get("script_ids") or config.get("script_id")
if isinstance(script_ids, list):
log.info("Script IDs : %d scripts (sticky per-host)", len(script_ids))
for i, sid in enumerate(script_ids):
log.info(" [%d] %s", i + 1, sid)
else:
log.info("Script ID : %s", script_ids)
script_ids = config.get("script_ids") or []
log.info("Script IDs : %d scripts (sticky per-host)", len(script_ids))
for i, sid in enumerate(script_ids):
log.info(" [%d] %s", i + 1, sid)

# Ensure CA file exists before checking / installing it.
# MITMCertManager generates ca/ca.crt on first instantiation.
Expand Down Expand Up @@ -281,7 +281,7 @@ def main():
log_lan_access(config.get("listen_port", 8080), socks_port)

try:
asyncio.run(_run(config))
asyncio.run(_run(config, config_path))
except KeyboardInterrupt:
log.info("Stopped")

Expand All @@ -305,15 +305,32 @@ def handler(loop, context):
return handler


async def _run(config):
async def _run(config, config_path):
loop = asyncio.get_running_loop()
_log = logging.getLogger("asyncio")
loop.set_exception_handler(_make_exception_handler(_log))
server = ProxyServer(config)

supervisor = Supervisor(config, config_path)

dashboard = None
if config.get("dashboard_enabled", True):
dashboard_host = config.get("dashboard_host", "127.0.0.1")
try:
dashboard_port = int(config.get("dashboard_port", 7878))
except (TypeError, ValueError):
dashboard_port = 7878
dashboard = Dashboard(supervisor, dashboard_host, dashboard_port)

await supervisor.start()
if dashboard is not None:
await dashboard.start()

try:
await server.start()
await supervisor.wait()
finally:
await server.stop()
if dashboard is not None:
await dashboard.stop()
await supervisor.stop()


if __name__ == "__main__":
Expand Down
Empty file modified run.sh
100644 → 100755
Empty file.
12 changes: 6 additions & 6 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,8 @@ def configure_apps_script(cfg: dict) -> dict:
default=None,
)
ids = [x.strip() for x in ids_raw.split(",") if x.strip()]
if len(ids) == 1:
cfg["script_id"] = ids[0]
cfg.pop("script_ids", None)
else:
cfg["script_ids"] = ids
cfg.pop("script_id", None)
cfg["script_ids"] = ids
cfg.pop("script_id", None)
return cfg


Expand Down Expand Up @@ -193,6 +189,10 @@ def main() -> int:
print()
print(bold("Next step:"))
print(f" python main.py")
if cfg.get("dashboard_enabled", True):
host = cfg.get("dashboard_host", "127.0.0.1")
port = cfg.get("dashboard_port", 7878)
print(dim(f" Dashboard will be at http://{host}:{port}/"))
print()
print(yellow("Reminder: the AUTH_KEY inside apps_script/Code.gs must match the auth_key"))
print(yellow("you just entered - otherwise the relay will return 'unauthorized'."))
Expand Down
Loading