v0.4.0: Cloudflare Pages deploy tooling, items search in URL, spirit bear patch lookup

This commit is contained in:
voson
2026-07-27 16:28:31 +08:00
parent 1d0428b4a3
commit adb8f303c2
13 changed files with 674 additions and 27 deletions
+85
View File
@@ -0,0 +1,85 @@
"""Read-only Cloudflare Pages status probe (diagnostic helper).
Prints account id, whether the target project exists, its latest deployment,
and bound custom domains. Credentials come from env (keyzoo inject).
Usage via keyzoo: python _cf_status.py [--project-name NAME]
"""
from __future__ import annotations
import argparse
import json
import os
import urllib.error
import urllib.request
DEFAULT_PROJECT = "climperor-relations"
def cf(method: str, path: str, email: str, key: str, body: dict | None = None) -> dict:
url = "https://api.cloudflare.com/client/v4" + path
headers = {"X-Auth-Email": email, "X-Auth-Key": key, "Content-Type": "application/json"}
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(url, data=data, method=method, headers=headers)
try:
with urllib.request.urlopen(req) as r:
return json.loads(r.read().decode())
except urllib.error.HTTPError as e:
try:
return json.loads(e.read().decode())
except Exception:
return {"success": False, "errors": [{"code": e.code, "message": str(e)}]}
except urllib.error.URLError as e:
return {"success": False, "errors": [{"code": -1, "message": str(e)}]}
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--project-name", default=DEFAULT_PROJECT)
args = ap.parse_args()
email = os.environ.get("CLOUDFLARE_EMAIL") or os.environ.get("KEYZOO_ASSET_META_USERNAME")
key = os.environ.get("CLOUDFLARE_API_KEY") or os.environ.get("KEYZOO_ASSET_SECRET_GLOBAL_API_KEY")
if not email or not key:
raise SystemExit("missing credentials in env")
d = cf("GET", "/accounts", email, key)
if not d.get("success"):
print("GET /accounts FAILED:", d.get("errors"))
return
accts = d.get("result") or []
print(f"accounts: {[ (a['id'], a.get('name')) for a in accts ]}")
if not accts:
return
aid = accts[0]["id"]
d = cf("GET", f"/accounts/{aid}/pages/projects/{args.project_name}", email, key)
if not d.get("success"):
print(f"project {args.project_name}: NOT created -> {d.get('errors')}")
return
print(f"project: {args.project_name} (created)")
d = cf("GET", f"/accounts/{aid}/pages/projects/{args.project_name}/deployments", email, key)
deps = d.get("result") or []
print(f"deployments: {len(deps)}")
for dep in deps[:3]:
print(f" - {dep.get('latest_stage', {}).get('name')} / {dep.get('latest_stage', {}).get('status')} | env={dep.get('environment')} | created={dep.get('created_on')} | url={dep.get('url')}")
d = cf("GET", f"/accounts/{aid}/pages/projects/{args.project_name}/domains", email, key)
doms = d.get("result") or []
print("custom domains:")
for x in doms:
print(f" - {x.get('name')} status={x.get('status')}")
# Confirm the CNAME was auto-added on the refining.dev zone.
zd = cf("GET", "/zones?name=refining.dev", email, key)
zr = zd.get("result") or []
if zr:
zid = zr[0]["id"]
recs = cf("GET", f"/zones/{zid}/dns_records?name=dota2.refining.dev", email, key)
for rec in (recs.get("result") or []):
print(f" dns: {rec.get('type')} {rec.get('name')} -> {rec.get('content')} (proxied={rec.get('proxied')})")
if __name__ == "__main__":
main()