"""Cloudflare 适配器(REST API v4,Bearer Token) API 文档:https://developers.cloudflare.com/api/ 所需配置:{"api_token": "..."}(建议用 API Token,权限含 Zone:Read / Account:Read) Cloudflare 无传统 VPS,主要同步托管域名(zones);Workers/R2/Tunnel 子资产留待后续阶段。 """ import httpx from app.adapters.base import BaseAdapter, NormalizedDomain from app.adapters.registry import register @register("cloudflare-api") class CloudflareAdapter(BaseAdapter): required_config = ["api_token"] BASE = "https://api.cloudflare.com/client/v4" def _headers(self) -> dict: return {"Authorization": f"Bearer {self.config.get('api_token', '')}"} def _get(self, path: str) -> dict: with httpx.Client(timeout=30) as client: resp = client.get(self.BASE + path, headers=self._headers()) resp.raise_for_status() data = resp.json() if not data.get("success", True): errors = data.get("errors", []) raise RuntimeError(errors[0].get("message") if errors else "Cloudflare API 返回失败") return data def test_connection(self) -> dict: try: data = self._get("/user/tokens/verify") status = data.get("result", {}).get("status") if status == "active": return {"ok": True, "message": "API Token 有效"} return {"ok": False, "message": f"Token 状态异常:{status}"} except httpx.HTTPStatusError as e: return {"ok": False, "message": f"HTTP {e.response.status_code}:Token 无效或权限不足"} except Exception as e: # noqa: BLE001 return {"ok": False, "message": str(e)} def list_domains(self) -> list: result = [] page = 1 while True: data = self._get(f"/zones?per_page=50&page={page}") for z in data.get("result", []): result.append( NormalizedDomain( external_id=z.get("id"), domain_name=z.get("name"), registrar="cloudflare", status="active" if z.get("status") == "active" else (z.get("status") or "unknown"), raw=z, ) ) info = data.get("result_info", {}) if page >= info.get("total_pages", 1): break page += 1 return result