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

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

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

9.6 KiB
Raw Permalink Blame History

name, description
name description
update-db 经管理 API 查询或维护 LA 生产环境的节点、策略组、规则、订阅 token 与 Surge 设置,写入前在线备份。用于线上数据盘点、导入预览与数据变更;代码部署使用 deploy。

变更 proxy-station 生产数据

线上数据(节点、策略组、规则、token、设置)住在 la 机的 SQLite(/opt/proxy-station/data/proxy-station.db),由常驻的 proxy-station 服务直连。本 Skill 的做法是:走服务自己的管理 API 做变更,变更前用 sqlite3 在线备份做快照,不直接写库、不重启服务。

本技能由 Codex 与 Claude Code 共用,命令在仓库根目录运行;.agents/skills/update-db 链接到本目录。先区分查询、导入预览与实际变更;纯查询/commit:false 预览无需备份,也不能借此执行写入。用户已明确授权的具体变更不重复确认,范围不清时先查询现状并明确目标。

为什么必须走 API,不能直写 SQLite

API 不是简单转发,直写会绕过它的全部防护:

  • zod 校验拦住坏数据(VMess 必有 UUID、WS 必有 path、SS 必有 method)
  • 节点名唯一冲突返回 409;规则的 policy 必须指向存在的组或内置策略,否则规则整条失效
  • 策略组改名会级联更新引用它的组成员与规则;members 是「整组替换」语义,API 帮你做对
  • 订阅 token 由 crypto 随机生成、常量时间比对——手工 INSERT 塞进 null 或坏格式,/sub 全部当场 500

生成器每次请求实时读库,API 改完订阅即刻生效,不需要重启服务。直接动 SQLite 文件仅限两种只读场景:api.sh counts 的行数盘点,以及从备份快照里查旧值。

用法

bash .claude/skills/update-db/scripts/api.sh api GET  /api/nodes
bash .claude/skills/update-db/scripts/api.sh api POST /api/nodes '{"name":"…","protocol":"…",…}'
bash .claude/skills/update-db/scripts/api.sh api PUT /api/settings --body-file /private/tmp/<private-body>.json
bash .claude/skills/update-db/scripts/api.sh api GET  /sub/<token>/clash      # 拉订阅做校验(公开端点)
bash .claude/skills/update-db/scripts/api.sh backup <context>                # context 用小写连字符 slug,如 add-jp-hy2
bash .claude/skills/update-db/scripts/api.sh counts
bash .claude/skills/update-db/scripts/api.sh health
  • api 经 ssh 在远端本机回环调接口:ADMIN_TOKEN 只在远端 /etc/proxy-station/env 里展开,不经本地 shell、不落对话记录;JSON 走 stdin,无需转义
  • 含凭据、私有 Host 或复杂 JSON 时使用权限 600 的临时文件及 --body-file,不把正文放到 shell 字符串或进程参数;响应也保存到私有文件并只汇报脱敏摘要
  • 响应末尾带 __HTTP_STATUS:200__ 一行。非 2xx 时先读响应体里的错误信息再决定下一步,不要盲目重试;要管道给 jq 时先剥掉这行:| sed '$d' | jq …
  • 连不上 la(解析出 198.18.x.x)是本地 fake-IP 劫持了 ssh,与远端无关,等网络恢复再试
  • 环境变量可覆盖 DEPLOY_HOST/PS_DATA_DIR/PS_ENV_FILE,默认即生产值

标准流程

  1. 摸现状(只读):api GET /api/groups、/api/rules、/api/nodes、/api/tokens,把要动的对象当前值拿到手。改组成员、调规则顺序这类「全量替换」操作,不先拿全量必出错。 涉及下发行为先读 Surge 变更验证。检查业务例外与广告/服务集合的顺序、Host 的 DNS 例外、FINAL 和客户端节点范围;新防护依赖代码修复时先完成并验证代码部署。
  2. 备份:api.sh backup <context>。一个变更主题一个备份,同主题内的多条变更共用一份。备份落在远端 data/backups/(root 属主),不要清理。
  3. 执行:逐条调 API,每条核对 HTTP_STATUS 与返回体。某条失败就停下排查,不要跳过继续。
  4. 校验:
    • counts 与变更前对比,行数增减要能对上意图
    • api GET 变更对象,确认落库的是新值
    • 涉及节点/组/规则/token 范围/Surge 设置:从实际 HTTPS 入口拉 Surge 订阅到私有临时文件,核对 self URL,运行 python3 scripts/check-surge.py <文件> --fetch-rulesets;审阅遮蔽提示。回环 API 拉取的内网 self URL 不能替代 HTTPS 验收,CLI 不等于节点连接或泄漏测试
    • 影响 Clash 时拉其产物并在独立临时目录运行 mihomo -t -d <临时目录> -f <文件>;不启动代理或覆盖活动配置
    • excluded/skippedRules 只存在于生成器内部、不在 HTTP 响应里;要核对静默排除时,用 /api/nodes 列表对比 yaml 里的 proxies
  5. 汇报:向用户复述三件事——变更清单、备份文件名、校验结果。三者齐了才算完成。

各实体的操作要点

节点 /api/nodes

  • 新节点来自分享链接:先 POST /api/nodes/import 带 {"text":"…","commit":false} 预览(返回每条的解析结果与自动去重后的名字);用户授权导入且结果符合目标时,备份后再 commit:true 入库。只要求预览时到预览为止
  • 节点名是分流的关键:必须保留出口词尾缀(客户端里没有标签,用户靠名字区分直连/WARP 线路);地区组按名称正则收编,涉及新地区先读 docs/ADDING-REGIONS.md
  • 名称不能含配置分隔符、引号、反斜线、控制字符或首尾空格,不能与组或内置策略重名;导入预览与 commit 使用同一字段验证。Surge 当前排除 SS over WS、非零 alterId 及不支持加密方式的 VMess,核对实际输出而非只看节点总数
  • 临时下线用 PATCH /:id/enabled {"enabled":false},不要删——IR 层会自动剔除停用节点,删了凭据就没了
  • 真要删除:先 GET 全量字段核对对象,向用户展示名称及影响(不展示密码/UUID);删除范围已有明确授权时按流程 DELETE,否则先确认

策略组 /api/groups

  • members 是整体替换:GET 全量 → 改数组 → PUT 回去,别只发改动项
  • 成员三态:{kind:"node", nodeId}/{kind:"group", refName}/{kind:"builtin", refName}
  • filterRegex 与 includeAllNodes 可共存;Surge 保留显式节点/内置成员,全部成员组成为动态来源,正则只筛动态来源。新增同地区节点按名称进入组
  • 成员必须存在且类型正确,图无环;禁用组在 IR 中被剔除,不要新写指向停用组的规则
  • 改名后端会级联引用,但已导入客户端的旧组名会失配——改名前先向用户点明
  • testUrl 用于 Clash/ShadowRocket;Surge 用 General 的 proxy-test-url,组级 url= 无效。fallback 不使用 tolerance,url-test 可显式设 0

规则 /api/rules

  • 顺序由 PUT /api/rules/reorder 的全量、无重复 ids 数组决定,启用的唯一 FINAL 必须末尾。自定义创建置顶,但有明确顺序意图时按完整列表调整;业务例外在覆盖它的集合之前,特定 UDP 拦截在服务域名规则之前
  • FINAL 不可停用、删除、改类型或重复创建;失效策略由 IR 降级 DIRECT。不要借防御性兜底制造无效数据库状态
  • 新规则的 policy 必须已存在(内置 DIRECT/REJECT… 或现有组名),后端会校验,报错就先建组
  • POST /api/rules/reset 事务内恢复模板并保留自定义规则;不是定向更新生产规则的方法,除非用户明确要求重置,否则不调用
  • 靠前 IP 集是否加 no-resolve 按意图决定;主动解析可能绕过后续业务规则,no-resolve 又会改变对未解析域名的覆盖。URL-REGEX 值只检查 HTTP URL,不能拿 SMTP 文字当邮件协议匹配

规则集 /api/rulesets(只读)

  • GET /api/rulesets 看清单,GET /:id/content 看自托管转换结果
  • ruleset_cache 是 24h 缓存(etag + 陈旧回落)自动管理,不要手动碰;「改规则集」实际是改引用它的规则

订阅 token /api/tokens

  • 输入 {name, nodeIds}:nodeIds 空数组 = 下发全部启用节点;非空 = 只下发这些节点(给不同设备发不同范围的入口就在这里配)
  • token 值由后端随机生成,创建响应里拿一次;lastAccessAt/accessCount 可判断是否仍在被使用
  • 删除 token 会即刻断供所有用它的订阅 URL——删除前必须向用户点明后果

设置 /api/settings

  • surgeSections 是 Surge 原文段落透传,改错直接毁掉 Surge 配置;动它之前先 GET 存档 + backup,改完拉 /sub/<token>/surge 全文核对
  • 保留手写 Host 的精确 DNS 例外,不将全局 DNS 或全部端口 53 送到 AI;设置变化也须实际 HTTPS 订阅与官方 CLI 验证
  • 共享订阅默认不启用控制接口,不使用公开模板口令;确需远程控制时使用设备独立凭据与访问限制。MITM 不关闭上游证书验证,启用效果另核对设备 CA 与开关

回滚

  • 单条改错:用 API 反向改回。旧值可以从备份快照只读查出:远端 sqlite3 data/backups/<file>.db 'SELECT …'
  • 整体回滚:api.sh restore data/backups/<file>.db --confirm——停服换库再拉起,订阅中断约 10–20 秒,只在用户明确要求时执行

边界

  • 不改代码、不做部署(那是 deploy Skill 的事);不动 nginx/证书/ADMIN_TOKEN/systemd 单元
  • 删除类操作与 reset:先核对并说明对象清单与影响;未明确授权时先确认,已有明确授权且对象一致时直接按流程执行
  • 节点凭据是真凭据:汇报里不要粘贴密码/UUID 全文,用节点名指代