Files
proxy-station/.claude/skills/edge-node-multi-protocol/scripts/02-cf-dns.py
YANG JIANKUAN 5326c572a2 feat: 合并边缘节点运维并完善 Surge 配置安全
迁入通用节点部署工具、离线交付页生成器和共用技能,隔离真实配置与交付产物。统一 Codex 与 Claude Code 开发约定,补充节点迁移与运维文档。

完善 FINAL、策略组引用、输入校验、Surge 字段编码与 AI DNS 例外保护,增加隔离测试和订阅验证工具。

验证:全量类型检查、44 项单测、前端构建、脚本语法、技能入口与凭据隔离检查均通过。
2026-10-03 23:19:59 +08:00

73 lines
2.4 KiB
Python
Executable File

#!/usr/bin/env python3
"""Upsert <DOMAIN_ROOT> + *.<DOMAIN_ROOT> A/AAAA records in Cloudflare (DNS-only).
Run wherever the CF token lives (your laptop / a bastion) — the token never touches the VPS.
Reads config.env from the same dir. No real domains/IPs are baked in.
"""
import json, os, re, sys, urllib.request
HERE = os.path.dirname(os.path.abspath(__file__))
def load_env():
env = {}
path = os.path.join(HERE, "config.env")
if not os.path.exists(path):
sys.exit("ERROR: copy config.env.example -> config.env")
for line in open(path):
m = re.match(r'\s*([A-Z0-9_]+)\s*=\s*"?([^"#\n]*)"?', line)
if m:
env[m.group(1)] = m.group(2).strip()
return env
E = load_env()
API = "https://api.cloudflare.com/client/v4"
TOKEN = E["CF_API_TOKEN"]
ROOT = E["DOMAIN_ROOT"]
IPV4 = E.get("SERVER_IP", "")
IPV6 = E.get("SERVER_IPV6", "")
NAMES = [ROOT, "*." + ROOT]
def req(method, path, body=None):
data = json.dumps(body).encode() if body is not None else None
r = urllib.request.Request(API + path, data=data, method=method)
r.add_header("Authorization", "Bearer " + TOKEN)
r.add_header("Content-Type", "application/json")
with urllib.request.urlopen(r, timeout=20) as f:
return json.load(f)
def zone_id():
zid = E.get("CF_ZONE_ID", "")
if zid:
return zid
parent = E["PARENT_ZONE"]
res = req("GET", "/zones?name=%s" % parent)
if not res.get("result"):
sys.exit("ERROR: zone %s not found for this token" % parent)
return res["result"][0]["id"]
ZONE = zone_id()
def upsert(rtype, content):
if not content:
return
existing = req("GET", "/zones/%s/dns_records?per_page=200&type=%s" % (ZONE, rtype))["result"]
by_name = {r["name"]: r for r in existing}
for name in NAMES:
payload = {"type": rtype, "name": name, "content": content, "ttl": 300, "proxied": False}
if name in by_name:
res = req("PUT", "/zones/%s/dns_records/%s" % (ZONE, by_name[name]["id"]), payload)
print("UPDATE %-4s %-28s -> %s %s" % (rtype, name, content, "OK" if res.get("success") else res.get("errors")))
else:
res = req("POST", "/zones/%s/dns_records" % ZONE, payload)
print("CREATE %-4s %-28s -> %s %s" % (rtype, name, content, "OK" if res.get("success") else res.get("errors")))
upsert("A", IPV4)
upsert("AAAA", IPV6)
print("DNS done for *.%s (DNS-only / grey cloud)" % ROOT)