From 06044551c71e56cba7c3e98952f7c93b789c622a Mon Sep 17 00:00:00 2001 From: chaos Date: Sat, 11 Jul 2026 18:06:17 +0800 Subject: [PATCH] Allow CPA_AUTO_IMPORT_REMOTE from .env Enable remote CPA import via env flag with priority over config.json, and load project .env when resolving remote settings. --- .env.example | 10 ++++---- README.md | 7 ++++-- config.example.json | 2 +- cpa_export.py | 56 +++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 66 insertions(+), 9 deletions(-) diff --git a/.env.example b/.env.example index 4c80bc4..73dd85e 100644 --- a/.env.example +++ b/.env.example @@ -29,7 +29,9 @@ GROK2API_APP_KEY= # ===== CPA 远程自动导入(Management API)===== # 写出 xai-*.json 后 POST 到 {CPA_REMOTE_BASE}/v0/management/auth-files -# 需同时在 config.json 设 cpa_auto_import_remote=true,或仅用环境变量时也要打开该开关 -# CPA_REMOTE_BASE=http://127.0.0.1:8317 -# CPA_REMOTE_PASSWORD= -# 兼容别名:CPA_MANAGEMENT_PASSWORD / MANAGEMENT_PASSWORD +# 1/true/yes/on 开启;0/false/no/off 关闭(优先于 config.json 的 cpa_auto_import_remote) +CPA_AUTO_IMPORT_REMOTE=0 +# 地址只填根,如 http://127.0.0.1:8317 +CPA_REMOTE_BASE= +CPA_REMOTE_PASSWORD= +# 兼容别名:CPA_MANAGEMENT_PASSWORD / MANAGEMENT_PASSWORD / CPA_AUTO_IMPORT diff --git a/README.md b/README.md index b40489d..13f5dc8 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,8 @@ cp config.example.json config.json | `GROK2API_APP_KEY` | 远端 grok2api Admin | | `API_REVERSE_TOOLS` | 可选:外部 `cpa_xai` 父目录 | | `CPA_EXPORT` | `auto_register` 是否导出 CPA(`0/false` 关) | -| `CPA_REMOTE_BASE` | 远程 CPA 根地址(配合 `cpa_auto_import_remote`) | +| `CPA_AUTO_IMPORT_REMOTE` | 是否自动导入远程 CPA(`1/true` 开,`0/false` 关;优先于 config) | +| `CPA_REMOTE_BASE` | 远程 CPA 根地址 | | `CPA_REMOTE_PASSWORD` | 远程 CPA 管理密码 | | `https_proxy` / `http_proxy` | 代理回退 | @@ -243,15 +244,17 @@ cpa_proxy > proxy > 环境变量 https_proxy/http_proxy } ``` -或写在 `.env`: +或全部写在 `.env`(开关也可用环境变量): ```bash +CPA_AUTO_IMPORT_REMOTE=1 CPA_REMOTE_BASE=http://127.0.0.1:8317 CPA_REMOTE_PASSWORD=你的管理密码 ``` 说明: +- `CPA_AUTO_IMPORT_REMOTE=1/true/yes` 开启,`0/false/no` 关闭;**env 优先于** `config.json` - **只填根地址**(到端口即可);程序会请求 `POST {base}/v0/management/auth-files?name=xai-email.json` - 认证头:`Authorization: Bearer <管理密码>`(同时附带 `X-Management-Key`) diff --git a/config.example.json b/config.example.json index f10313e..974b13f 100644 --- a/config.example.json +++ b/config.example.json @@ -131,7 +131,7 @@ "cpa_copy_to_hotload": false, "// cpa_hotload_dir": "CPA 容器/进程挂载的 auth-dir(copy_to_hotload=true 时生效)", "cpa_hotload_dir": "", - "// cpa_auto_import_remote": "写出 xai-*.json 后是否自动 POST 到 CPA Management API 导入", + "// cpa_auto_import_remote": "写出 xai-*.json 后是否自动 POST 到 CPA Management API 导入。也可在 .env 用 CPA_AUTO_IMPORT_REMOTE=1/0(env 优先)", "cpa_auto_import_remote": false, "// cpa_remote_base": "CPA 根地址,只填到端口即可,如 http://127.0.0.1:8317(程序自动拼 /v0/management/auth-files)", "cpa_remote_base": "", diff --git a/cpa_export.py b/cpa_export.py index 588532e..561dcde 100644 --- a/cpa_export.py +++ b/cpa_export.py @@ -22,6 +22,31 @@ from urllib.parse import quote, urlparse, urlunparse _REG_DIR = Path(__file__).resolve().parent _DEFAULT_OUT = _REG_DIR / "cpa_auths" _DEFAULT_CPA = Path("") # empty = do not assume a machine-local CPA path +_DOTENV_LOADED = False + + +def _load_dotenv_once() -> None: + """Load project .env into os.environ once (does not override existing env).""" + global _DOTENV_LOADED + if _DOTENV_LOADED: + return + _DOTENV_LOADED = True + env_path = _REG_DIR / ".env" + if not env_path.is_file(): + return + try: + for raw in env_path.read_text(encoding="utf-8").splitlines(): + line = raw.strip() + if not line or line.startswith("#") or "=" not in line: + continue + key, _, val = line.partition("=") + key = key.strip() + val = val.strip().strip('"').strip("'") + if key and key not in os.environ: + os.environ[key] = val + except Exception: + pass + def _ensure_cpa_xai_on_path(tools_dir: str | Path | None = None) -> Path: @@ -90,10 +115,37 @@ def _normalize_cpa_remote_base(base: str) -> str: return urlunparse((parsed.scheme, parsed.netloc, "", "", "", "")).rstrip("/") +def _env_flag(name: str) -> bool | None: + """Parse env truthy/falsey flag. None if unset/empty.""" + raw = (os.environ.get(name) or "").strip().lower() + if not raw: + return None + if raw in ("1", "true", "yes", "on", "y"): + return True + if raw in ("0", "false", "no", "off", "n"): + return False + return None + + def resolve_cpa_remote_settings(cfg: dict | None = None) -> dict[str, Any]: - """Resolve remote CPA import settings from config + env.""" + """Resolve remote CPA import settings from config + env. + + Enable flag priority: + 1) env CPA_AUTO_IMPORT_REMOTE / CPA_AUTO_IMPORT (if set) + 2) config cpa_auto_import_remote + 3) default false + + Base/password: config first, then env fallbacks. + """ + _load_dotenv_once() cfg = cfg or {} - enabled = bool(cfg.get("cpa_auto_import_remote", False)) + env_enabled = _env_flag("CPA_AUTO_IMPORT_REMOTE") + if env_enabled is None: + env_enabled = _env_flag("CPA_AUTO_IMPORT") + if env_enabled is not None: + enabled = env_enabled + else: + enabled = bool(cfg.get("cpa_auto_import_remote", False)) base = ( (cfg.get("cpa_remote_base") or "").strip() or (os.environ.get("CPA_REMOTE_BASE") or "").strip()