feat: 模块化命令框架 + runbook 严格断言与查询结果回传
- core/module: 命令模板、门控(feature flag/confirm/dry-run)、ecode 错误标签、ResultFormatter - runbook executor: http.get/http.post/assert.json(expect_success 严格断言)、目标主机管理、任务落库、最终 stdout 直接回传 - cf 模块: /cf dns list 支持域名自动解析 zone_id;dns list/zones/workers list 格式化回传;API success:false 判真失败 - 全部 CF/CPA runbook 接入严格断言,杜绝假阳性 success - 新增 cpa/cfdav/mail/sub 模块与 runbooks;QQ 订阅到期提醒调度 - Web 后台: 模块设置(CF/CPA/cfdav/AI)、OPS 任务、渠道管理、审计页
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
version: 1
|
||||
name: cf_dns_set
|
||||
description: 修改 DNS 记录内容(按 record_id)
|
||||
steps:
|
||||
- id: get_dns
|
||||
action: ssh.exec
|
||||
on_fail: stop
|
||||
with:
|
||||
target: hwsg
|
||||
command: "CF_API_TOKEN=${env_cf_api_token} INPUT_RECORD_ID=${env.INPUT_RECORD_ID} python3 - <<'PY'\nimport os,requests\nrec=os.getenv('INPUT_RECORD_ID','')\ntoken=os.getenv('CF_API_TOKEN','')\nemail=os.getenv('CF_API_EMAIL','')\nheaders={'Authorization':'Bearer '+token,'Content-Type':'application/json'}\nurl=f'https://api.cloudflare.com/client/v4/zones?per_page=200'\nresp=requests.get(url, headers=headers, timeout=15)\nresp.raise_for_status()\n# find record across zones\nfor z in resp.json().get('result',[]):\n zone_id=z.get('id')\n rurl=f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{rec}'\n r=requests.get(rurl, headers=headers, timeout=15)\n if r.status_code==200:\n print(r.text)\n raise SystemExit(0)\nprint('{"success":false,"errors":["record_not_found"]}')\nPY"
|
||||
- id: assert_get
|
||||
action: assert.json
|
||||
on_fail: stop
|
||||
with:
|
||||
source_step: get_dns
|
||||
expect_success: true
|
||||
required_paths:
|
||||
- "success"
|
||||
- id: update_dns
|
||||
action: ssh.exec
|
||||
on_fail: stop
|
||||
with:
|
||||
target: hwsg
|
||||
command: "CF_API_TOKEN=${env_cf_api_token} INPUT_RECORD_ID=${env.INPUT_RECORD_ID} INPUT_CONTENT=${env.INPUT_CONTENT} INPUT_PROXIED=${env.INPUT_PROXIED} python3 - <<'PY'\nimport os,requests,json\nrec=os.getenv('INPUT_RECORD_ID','')\ncontent=os.getenv('INPUT_CONTENT','')\nproxied=os.getenv('INPUT_PROXIED','false').lower()=='true'\ntoken=os.getenv('CF_API_TOKEN','')\nemail=os.getenv('CF_API_EMAIL','')\nheaders={'Authorization':'Bearer '+token,'Content-Type':'application/json'}\n# find record and zone\nzones=requests.get('https://api.cloudflare.com/client/v4/zones?per_page=200', headers=headers, timeout=15).json().get('result',[])\nzone_id=None\nrecord=None\nfor z in zones:\n zid=z.get('id')\n r=requests.get(f'https://api.cloudflare.com/client/v4/zones/{zid}/dns_records/{rec}', headers=headers, timeout=15)\n if r.status_code==200:\n data=r.json()\n if data.get('success'):\n zone_id=zid\n record=data.get('result')\n break\nif not zone_id or not record:\n print(json.dumps({'success':False,'errors':['record_not_found']}))\n raise SystemExit(1)\npayload={\n 'type': record.get('type'),\n 'name': record.get('name'),\n 'content': content,\n 'proxied': proxied\n}\nresp=requests.put(f'https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records/{rec}', headers=headers, json=payload, timeout=15)\nprint(resp.text)\nPY"
|
||||
- id: assert_update
|
||||
action: assert.json
|
||||
on_fail: stop
|
||||
with:
|
||||
source_step: update_dns
|
||||
expect_success: true
|
||||
required_paths:
|
||||
- "success"
|
||||
Reference in New Issue
Block a user