Detect Cloudflare 403 on mint and retry alternate proxies.

- Clear CF block errors with egress label and config hint
- Log mint exit (direct/proxy); probe local proxy ports on 403
- Retry mint_proxy/proxy/pool candidates without browser mint
This commit is contained in:
chaos committed 2026-07-14 14:41:52 +08:00
1 parent cc0aa6a433
commit 7d6d52ac5d
2 files changed
+186 -17

No files matched your search

+138 -17
View File
@@ -62,6 +62,72 @@ def _resolve_proxy(cfg: dict) -> str | None:
return resolved or None
def _proxy_label(proxy: str | None) -> str:
try:
from oidc_mint.proxyutil import proxy_log_label
return proxy_log_label(proxy or "") or "(direct)"
except Exception:
return proxy or "(direct)"
def _port_open(host: str, port: int, timeout: float = 0.25) -> bool:
import socket
try:
with socket.create_connection((host, port), timeout=timeout):
return True
except OSError:
return False
def _local_proxy_candidates(cfg: dict) -> list[str]:
"""直连被 CF 拦时尝试的本机/配置代理列表(去重)。"""
cands: list[str] = []
seen: set[str] = set()
def _add(raw: str | None) -> None:
p = (raw or "").strip()
if not p or p in seen:
return
seen.add(p)
cands.append(p)
for key in ("mint_proxy", "proxy"):
_add(str(cfg.get(key) or ""))
raw_list = cfg.get("proxy_pool") or []
if isinstance(raw_list, str):
for line in raw_list.replace(",", "\n").splitlines():
_add(line)
elif isinstance(raw_list, (list, tuple)):
for x in raw_list:
_add(str(x))
try:
import proxy_pool
for p in proxy_pool.pool_snapshot()[:8]:
_add(p)
except Exception:
pass
# 仅探测本机已监听的常见代理端口,避免空转超时
for port in (7890, 7897, 10809, 10808, 7891, 20171, 6152, 1080):
if _port_open("127.0.0.1", port):
_add(f"http://127.0.0.1:{port}")
return cands
def _is_cf_mint_error(exc: BaseException | str) -> bool:
try:
from oidc_mint.oauth_code import is_cloudflare_block
return is_cloudflare_block(exc=exc)
except Exception:
text = str(exc or "").lower()
return "403" in text and (
"cloudflare" in text or "<!doctype" in text or "oldie" in text
)
def _mint_tokens(
*,
email: str,
@@ -73,32 +139,87 @@ def _mint_tokens(
proxy: str | None,
) -> dict[str, Any]:
"""优先 SSO 授权码;可选回退设备码。"""
from oidc_mint import set_runtime_proxy
from oidc_mint.oauth_code import OAuthCodeError, mint_from_sso, normalize_sso_cookie
sso_token = normalize_sso_cookie(sso or "")
prefer_sso = bool(cfg.get("cpa_prefer_sso_oauth", True))
allow_device = bool(cfg.get("cpa_allow_device_fallback", False))
timeout = float(cfg.get("mint_timeout_sec", 300) or 300)
max_proxy_tries = int(cfg.get("mint_proxy_retries", 4) or 4)
if prefer_sso and sso_token:
log("[cpa] 使用 SSO→OAuth(PKCE, referrer=grok-build)")
try:
return mint_from_sso(
sso_token,
proxy=proxy,
log=lambda m: log(f"[Debug] {m}"),
require_referrer=bool(cfg.get("cpa_require_referrer", True)),
)
except OAuthCodeError as exc:
log(f"[!] SSO→OAuth 失败: {exc}")
if not allow_device:
raise
log("[cpa] 回退设备码铸造(可能缺 referrer)")
except Exception as exc: # noqa: BLE001
log(f"[!] SSO→OAuth 异常: {exc}")
if not allow_device:
raise
log("[cpa] 回退设备码铸造(可能缺 referrer)")
tried: set[str] = set()
proxies_to_try: list[str | None] = [proxy]
last_exc: Exception | None = None
def _expand_proxy_candidates() -> None:
for p in _local_proxy_candidates(cfg):
if p and p not in tried and p not in {
x for x in proxies_to_try if x
}:
proxies_to_try.append(p)
# 直连时先挂上本机已开端口,减少首次 403 后的空等
if not proxy:
_expand_proxy_candidates()
idx = 0
while idx < len(proxies_to_try) and idx < max(1, max_proxy_tries):
use_proxy = proxies_to_try[idx]
label = _proxy_label(use_proxy)
if idx == 0:
log(f"[cpa] mint 出口={label}")
else:
log(f"[cpa] CF/403 换出口重试 ({idx + 1}/{max_proxy_tries}): {label}")
set_runtime_proxy(use_proxy)
tried.add(use_proxy or "")
try:
return mint_from_sso(
sso_token,
proxy=use_proxy,
log=lambda m: log(f"[Debug] {m}"),
require_referrer=bool(cfg.get("cpa_require_referrer", True)),
)
except OAuthCodeError as exc:
last_exc = exc
log(f"[!] SSO→OAuth 失败: {exc}")
if _is_cf_mint_error(exc):
if use_proxy:
try:
import proxy_pool
proxy_pool.report_failure(
use_proxy, reason=f"mint CF: {str(exc)[:120]}"
)
except Exception:
pass
_expand_proxy_candidates()
idx += 1
continue
if not allow_device:
raise
log("[cpa] 回退设备码铸造(可能缺 referrer)")
break
except Exception as exc: # noqa: BLE001
last_exc = exc
log(f"[!] SSO→OAuth 异常: {exc}")
if _is_cf_mint_error(exc):
_expand_proxy_candidates()
idx += 1
continue
if not allow_device:
raise
log("[cpa] 回退设备码铸造(可能缺 referrer)")
break
# 成功已 return;失败已 continue/break
idx += 1 # pragma: no cover
else:
if last_exc is not None and not allow_device:
raise last_exc
if last_exc is not None and not allow_device:
raise last_exc
if not allow_device and not sso_token:
raise RuntimeError("无 sso cookie,且已禁用设备码回退;无法铸造带 referrer 的 token")