chore: 清理调试脚本并收敛到 Ansible 流程
移除已废弃的调试/验证脚本与空目录,统一文档与脚本说明到 ansible-playbook 的部署方式,避免失效引用和误用路径。 Made-with: Cursor
This commit is contained in:
140
scripts/cloudflare-delete-acme-challenge-dns.sh
Normal file
140
scripts/cloudflare-delete-acme-challenge-dns.sh
Normal file
@@ -0,0 +1,140 @@
|
||||
#!/usr/bin/env bash
|
||||
# 批量删除 Cloudflare 中 _acme-challenge 相关的 DNS 记录
|
||||
# 用法:环境变量 或 脚本内配置 二选一,环境变量优先
|
||||
# CF_API_TOKEN=xxx ZONE_NAME=jackadam.top ./scripts/cloudflare-delete-acme-challenge-dns.sh [--dry-run]
|
||||
# 或在下方配置中填写,直接运行 ./scripts/cloudflare-delete-acme-challenge-dns.sh [--dry-run]
|
||||
|
||||
set -e
|
||||
|
||||
# ---------- 脚本内配置(环境变量未设置时生效)----------
|
||||
# Cloudflare API Token:需 Zone → DNS → Read + Edit
|
||||
# (勿将含真实 Token 的脚本提交到 Git)
|
||||
DEFAULT_CF_API_TOKEN=""
|
||||
# 区域:填 ZONE_NAME 或 ZONE_ID 其一
|
||||
DEFAULT_ZONE_NAME="jackadam.top"
|
||||
DEFAULT_ZONE_ID=""
|
||||
# ------------------------------------
|
||||
|
||||
# 环境变量优先于脚本配置
|
||||
CF_API_TOKEN="${CF_API_TOKEN:-$DEFAULT_CF_API_TOKEN}"
|
||||
ZONE_NAME="${ZONE_NAME:-$DEFAULT_ZONE_NAME}"
|
||||
ZONE_ID="${ZONE_ID:-$DEFAULT_ZONE_ID}"
|
||||
|
||||
API_BASE="https://api.cloudflare.com/client/v4"
|
||||
DRY_RUN=false
|
||||
BATCH_SIZE=200
|
||||
|
||||
usage() {
|
||||
echo "用法: $0 [--dry-run]"
|
||||
echo " 方式一:环境变量 CF_API_TOKEN=xxx ZONE_NAME=jackadam.top $0"
|
||||
echo " 方式二:脚本内配置 在 DEFAULT_* 变量中填写后直接运行"
|
||||
echo " --dry-run 仅列出待删除记录,不执行删除"
|
||||
exit 1
|
||||
}
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--dry-run) DRY_RUN=true ;;
|
||||
-h|--help) usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "${CF_API_TOKEN}" ]]; then
|
||||
echo "[ERROR] 请设置 CF_API_TOKEN(环境变量或脚本内 DEFAULT_CF_API_TOKEN)" >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
# 若未填 ZONE_ID,用 ZONE_NAME 查询
|
||||
if [[ -n "${ZONE_NAME}" && -z "${ZONE_ID}" ]]; then
|
||||
echo "[INFO] 查询区域 ${ZONE_NAME} 的 ZONE_ID..."
|
||||
resp=$(curl -s -X GET "${API_BASE}/zones?name=${ZONE_NAME}" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json")
|
||||
if ! echo "$resp" | jq -e '.success == true' >/dev/null 2>&1; then
|
||||
echo "[ERROR] 查询区域失败: $(echo "$resp" | jq -r '.errors[0].message // .')" >&2
|
||||
exit 1
|
||||
fi
|
||||
ZONE_ID=$(echo "$resp" | jq -r '.result[0].id // empty')
|
||||
if [[ -z "$ZONE_ID" ]]; then
|
||||
echo "[ERROR] 未找到区域: ${ZONE_NAME}" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[INFO] ZONE_ID=${ZONE_ID}"
|
||||
fi
|
||||
|
||||
if [[ -z "${ZONE_ID}" ]]; then
|
||||
echo "[ERROR] 请设置 ZONE_NAME 或 ZONE_ID(环境变量或脚本内 DEFAULT_ZONE_*)" >&2
|
||||
usage
|
||||
fi
|
||||
|
||||
# 分页获取所有 DNS 记录,筛选 _acme-challenge
|
||||
echo "[INFO] 获取 DNS 记录列表..."
|
||||
all_ids=()
|
||||
page=1
|
||||
per_page=100
|
||||
|
||||
while true; do
|
||||
resp=$(curl -s -X GET "${API_BASE}/zones/${ZONE_ID}/dns_records?per_page=${per_page}&page=${page}" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json")
|
||||
|
||||
if ! echo "$resp" | jq -e '.success == true' >/dev/null 2>&1; then
|
||||
echo "[ERROR] 获取记录失败: $(echo "$resp" | jq -r '.errors[0].message // .')" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 筛选 name 包含 _acme-challenge 的记录
|
||||
ids=$(echo "$resp" | jq -r '.result[] | select(.name | contains("_acme-challenge")) | .id')
|
||||
names=$(echo "$resp" | jq -r '.result[] | select(.name | contains("_acme-challenge")) | "\(.type) \(.name) -> \(.content)"')
|
||||
while IFS= read -r id; do
|
||||
[[ -n "$id" ]] && all_ids+=("$id")
|
||||
done <<< "$ids"
|
||||
|
||||
if [[ -n "$names" ]]; then
|
||||
echo "$names" | while read -r line; do
|
||||
[[ -n "$line" ]] && echo " - $line"
|
||||
done
|
||||
fi
|
||||
|
||||
fetched=$(echo "$resp" | jq -r '.result | length')
|
||||
[[ "$fetched" -lt "$per_page" ]] && break
|
||||
((page++)) || true
|
||||
done
|
||||
|
||||
count=${#all_ids[@]}
|
||||
echo "[INFO] 共找到 ${count} 条 _acme-challenge 相关记录"
|
||||
|
||||
if [[ $count -eq 0 ]]; then
|
||||
echo "[INFO] 无需删除"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "$DRY_RUN" == "true" ]]; then
|
||||
echo "[DRY-RUN] 未执行删除,以上 ${count} 条记录将在去掉 --dry-run 后被删除"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 分批删除(使用 jq 构建 JSON 避免转义问题)
|
||||
deleted=0
|
||||
for ((i=0; i<count; i+=BATCH_SIZE)); do
|
||||
batch=("${all_ids[@]:i:BATCH_SIZE}")
|
||||
body=$(printf '%s\n' "${batch[@]}" | jq -R -s 'split("\n") | map(select(length>0)) | {deletes: map({id: .})}')
|
||||
|
||||
echo "[INFO] 删除第 $((i/BATCH_SIZE + 1)) 批,共 ${#batch[@]} 条..."
|
||||
resp=$(curl -s -X POST "${API_BASE}/zones/${ZONE_ID}/dns_records/batch" \
|
||||
-H "Authorization: Bearer ${CF_API_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$body")
|
||||
|
||||
if ! echo "$resp" | jq -e '.success == true' >/dev/null 2>&1; then
|
||||
echo "[ERROR] 批量删除失败: $(echo "$resp" | jq -r '.errors[0].message // .')" >&2
|
||||
echo "$resp" | jq '.' >&2
|
||||
exit 1
|
||||
fi
|
||||
deleted=$((deleted + ${#batch[@]}))
|
||||
echo "[OK] 已删除 ${deleted}/${count} 条"
|
||||
# 避免 API 限流
|
||||
[[ $((i + BATCH_SIZE)) -lt $count ]] && sleep 1
|
||||
done
|
||||
|
||||
echo "[DONE] 共删除 ${deleted} 条 _acme-challenge 记录"
|
||||
Reference in New Issue
Block a user