| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- """Clean expired registration tokens from backend auth storage."""
- from __future__ import annotations
- import argparse
- import time
- from datetime import datetime, timezone
- from pathlib import Path
- from curl_cffi import requests
- from .common import CpaClient, DEFAULT_MANAGEMENT_BASE_URL, DEFAULT_POOL_DIR, now
- def is_expired(data: dict) -> bool:
- expired_str = str(data.get("expired") or "").strip()
- if not expired_str:
- return False
- try:
- expired_at = datetime.fromisoformat(expired_str.replace("Z", "+00:00"))
- except ValueError:
- return False
- return expired_at < datetime.now(timezone.utc)
- def try_refresh(refresh_token: str, proxy: str | None) -> bool:
- try:
- proxies = {"http": proxy, "https": proxy} if proxy else None
- response = requests.post(
- "https://auth0.openai.com/oauth/token",
- json={
- "redirect_uri": "com.openai.chat://auth0.openai.com/ios/com.openai.chat/callback",
- "grant_type": "refresh_token",
- "client_id": "app_EMoamEEZ73f0CkXaXp7hrann",
- "refresh_token": refresh_token,
- },
- proxies=proxies,
- impersonate="chrome",
- timeout=15,
- )
- return response.status_code == 200 and bool(response.json().get("access_token"))
- except Exception:
- return False
- def _hard_delete_pool_file(pool_dir: Path, name: str, reason: str) -> None:
- pool_file = pool_dir / name
- if not pool_file.exists():
- return
- pool_file.unlink(missing_ok=True)
- print(f"[{now()}] [清理] ❌ pool {name} deleted ({reason})")
- def cleanup_once(
- proxy: str | None = None,
- pool_dir: Path = DEFAULT_POOL_DIR,
- *,
- client: object | None = None,
- management_base_url: str = DEFAULT_MANAGEMENT_BASE_URL,
- management_key: str | None = None,
- ) -> tuple[int, int, int]:
- backend_client = client or CpaClient(management_base_url, management_key=management_key)
- if not getattr(backend_client, "health_check")():
- return 0, 0, 0
- reg_files = sorted(
- str(entry.get("name") or "").strip()
- for entry in getattr(backend_client, "list_auth_files")()
- if "@" in str(entry.get("name") or "").strip()
- )
- checked = 0
- deleted = 0
- refreshed = 0
- for name in reg_files:
- checked += 1
- data = getattr(backend_client, "get_auth_file")(name)
- if not isinstance(data, dict):
- continue
- refresh_token = str(data.get("refresh_token") or "").strip()
- if not refresh_token:
- print(f"[{now()}] [清理] ⚠️ {name} 无 refresh_token, 删除")
- if getattr(backend_client, "delete_auth_file")(name):
- deleted += 1
- _hard_delete_pool_file(pool_dir, name, "no_refresh_token")
- continue
- if not is_expired(data):
- continue
- if try_refresh(refresh_token, proxy):
- refreshed += 1
- print(f"[{now()}] [清理] 🔄 {name} 已过期但刷新成功, 保留")
- continue
- print(f"[{now()}] [清理] ❌ {name} 已过期且刷新失败, 删除")
- if getattr(backend_client, "delete_auth_file")(name):
- deleted += 1
- _hard_delete_pool_file(pool_dir, name, "expired_refresh_failed")
- return checked, deleted, refreshed
- def main() -> None:
- from core.settings import AppSettings
- env_settings = AppSettings.from_env()
- parser = argparse.ArgumentParser(description="清理 zhuce6 backend 中失效 token")
- parser.add_argument("--interval", type=int, default=300, help="清理间隔秒数")
- parser.add_argument("--proxy", default=None, help="可选代理地址")
- parser.add_argument("--once", action="store_true", help="只执行一轮")
- parser.add_argument("--management-base-url", default=env_settings.cpa_management_base_url or DEFAULT_MANAGEMENT_BASE_URL, help="CPA management base url")
- parser.add_argument("--management-key", default=env_settings.cpa_management_key, help="可选 CPA management key")
- parser.add_argument("--pool-dir", default=str(env_settings.pool_dir or DEFAULT_POOL_DIR), help="本地 pool 目录")
- args = parser.parse_args()
- interval = max(1, args.interval)
- proxy = str(args.proxy or "").strip() or None
- pool_dir = Path(args.pool_dir).expanduser().resolve()
- print(
- "[清理] 启动"
- f" | management_base_url: {args.management_base_url}"
- f" | 间隔: {interval}s"
- f" | proxy: {proxy or 'none'}"
- )
- while True:
- cycle_started_at = time.time()
- try:
- checked, deleted, refreshed = cleanup_once(
- proxy,
- pool_dir,
- management_base_url=args.management_base_url,
- management_key=str(args.management_key or "").strip() or None,
- )
- elapsed = time.time() - cycle_started_at
- if checked > 0 or deleted > 0 or refreshed > 0:
- print(f"[{now()}] [清理] 本轮: 检查 {checked}, 删除 {deleted}, 刷新验证 {refreshed}")
- print(f"[{now()}] [清理] 本轮耗时: {elapsed:.2f}s")
- except Exception as exc:
- print(f"[{now()}] [错误] 清理异常: {exc}")
- elapsed = time.time() - cycle_started_at
- if args.once:
- break
- time.sleep(max(0, interval - elapsed))
- if __name__ == "__main__":
- main()
|