|
|
6 天之前 | |
|---|---|---|
| .claude | 2 周之前 | |
| configs | 6 天之前 | |
| docs | 6 天之前 | |
| scripts | 2 周之前 | |
| substore | 3 周之前 | |
| tests | 2 周之前 | |
| .gitignore | 2 周之前 | |
| AGENTS.md | 2 月之前 | |
| CLAUDE.md | 2 周之前 | |
| README.md | 2 周之前 | |
| build_router_package.sh | 3 周之前 |
🚀 Cloudflare 优选 IP / 域名全自动轮换系统 支持多源选优评分、双频自适应调度、Git 孤儿分支状态总线与路由器嵌入式闪存防擦写设计。
本项目针对不同运行环境提供三大解耦运行模式:
| 运行模式 | 数据源 | 配置文件 | 运行时落盘路径 | 状态同步与发布 | 典型适用环境 |
|---|---|---|---|---|---|
| 服务器模式 | 远程监控 API | configs/config.server.json |
runtime/ |
自动提交并推送至runtime-state 孤儿分支 |
Linux VPS / 云服务器 (systemd) |
| 本地 Python 模式 | 本机cfst 测速 |
configs/config.local.json |
runtime/ |
纯本地文件落盘,供本地工具/脚本直接读取 | 本地开发机 / 具备 Python 环境的 PC |
| BusyBox 路由器模式 | 路由器cfst 测速 |
configs/config_router.conf |
cfip_runtime/ 或 /tmp |
启动微型 HTTP 服务供局域网直接拉取 | 华硕梅林 / OpenWrt / 嵌入式路由器 |
vmess-domain-rotator/
├── configs/ # 统一配置仓 (SSOT)
│ ├── config.server.json # 服务器 API 模式配置
│ ├── config.local.json # 本地 Python cfst 模式配置
│ └── config_router.conf # 路由器 BusyBox shell 模式配置
├── scripts/
│ ├── domain_updater.py # 顶层外观门面 (Facade CLI)
│ ├── rotator/ # 核心领域解耦子包 (Python)
│ │ ├── config.py / filter.py / sources.py / scoring.py / storage.py / notify.py / main.py
│ ├── server/ # Linux 服务端运维脚本
│ │ ├── run_update_and_commit.sh # 调度器入口 + Git 提交与修剪
│ │ ├── install_debian.sh # 一键部署与 systemd 注册
│ │ └── uninstall_debian.sh # 一键安全卸载
│ └── router/ # 路由器 BusyBox 运行时
│ ├── router_local_update.sh # 测速调度与原子落盘
│ └── router_local_http.sh # 微型 HTTP 服务
├── substore/
│ └── operator_template.js # Sub-Store 脚本操作符
├── build_router_package.sh # 路由器一键打包脚本
└── tests/ # 32 项自动化测试套件
所有运行模式均产生统一结构的标准输出,保障下游消费一致性:
| 产物文件名 | 文件说明 | 典型消费场景 |
|---|---|---|
current_domain.txt / current_ip.txt |
纯文本格式的当前优选值 | 极简脚本、命令行直接读取 |
current_domain.json / current_ip.json |
完整 JSON 状态报告与候选池列表 | 详细分析面板、具备完整解析能力的客户端 |
substore_vars.json |
轻量键值对(AUTO_DOMAIN 或 AUTO_CFIP、STATUS、UPDATED_AT) |
Sub-Store 算子专用(免去完整 JSON 解析开销) |
state.json |
持久化状态沉淀(记录last_good_domain / last_good_ip) |
双轨容灾 Fallback(异常或断网时平滑回退) |
[!NOTE] 候选信息稀疏语义:JSON 输出中的
top_candidates仅在字段非空时输出真实键值(如avg_latency、loss_rate、download_speed、region),彻底消除空字符串/空数组/null占位污染。
# 1. 仅执行 Python 选优并落盘(不执行 Git 提交)
python3 scripts/domain_updater.py --config configs/config.server.json
# 2. 执行完整调度(自适应时间检查 -> 选优落盘 -> 差异对比 -> Git 提交与推送)
bash scripts/server/run_update_and_commit.sh configs/config.server.json
# 3. 强制提交(忽略自适应时间拦截与内容未变防抖,强制生成 Commit 并推送)
bash scripts/server/run_update_and_commit.sh --force-commit configs/config.server.json
在 Debian / Ubuntu 等发行版上,通过 scripts/server/install_debian.sh 一键安装为 systemd 服务与定时器:
sudo bash scripts/server/install_debian.sh [options]
| 参数选项 | 默认值 | 参数说明 |
|---|---|---|
--config <path> |
configs/config.server.json |
配置文件路径(支持相对于仓库根目录的相对路径) |
--interval <value> |
30min |
定时任务基础轮询间隔(--offpeak-interval 别名) |
--peak-start <hour> |
19 |
晚高峰起始小时(0-23,如 19 代表 19:00) |
--peak-end <hour> |
24 |
晚高峰截止小时(1-24,如 24 代表 24:00) |
--peak-tz <timezone> |
Asia/Shanghai |
高峰期时钟判定的参考时区 |
--peak-interval <val> |
10min |
高峰期域名更新间隔(如5min、10m) |
--offpeak-interval <v> |
30min |
非高峰期域名更新间隔(如30min、1h) |
--user <name> |
当前sudo 用户 |
运行 systemd 服务的非 root 系统用户 |
--group <name> |
当前sudo 用户组 |
运行 systemd 服务的系统用户组 |
--git-push <0\|1> |
1 |
选出新域名后是否自动推送到远端 Git 仓库 |
--git-push-remote <n> |
origin |
推送的 Git 远程分支仓库名 |
--git-http-username <u> |
git |
HTTPS 认证用户名 |
--git-http-token <t> |
空 | HTTPS 访问令牌(用于非交互式免密推送) |
--git-http-token-file <f> |
空 | 从安全文件中读取访问令牌(权限限定600,推荐) |
--git-use-credential-store <0\|1> |
1 |
是否使用git credential.helper store 持久化凭据 |
--git-credentials-file <f> |
空 | 自定义凭据存储文件路径(隔离多项目凭据) |
--no-install-deps |
否 | 跳过apt-get 依赖检测与自动安装 |
-h, --help |
- | 显示帮助信息 |
# 场景 1:最简安装(默认晚高峰 10min、非高峰 30min,自动推送)
sudo bash scripts/server/install_debian.sh
# 场景 2:安全凭据安装(从指定文件读取 Token,自定义高峰时段)
sudo bash scripts/server/install_debian.sh \
--peak-start 18 --peak-end 23 \
--git-http-username aurora \
--git-http-token-file /home/aurora/.git_token
# 场景 3:纯本地运行(禁用 Git 远程推送)
sudo bash scripts/server/install_debian.sh --git-push 0
# 彻底卸载(移除服务与定时器,清理凭据与配置文件,保留项目代码)
sudo bash scripts/server/uninstall_debian.sh
# 卸载服务但保留认证凭据(适合临时调整后重装)
sudo bash scripts/server/uninstall_debian.sh --keep-auth-files
在服务器模式下,run_update_and_commit.sh 会在完成选优落盘后,通过独立 Worktree 检出 runtime-state 孤儿分支,提交变更并自动推送至远端。
flowchart TD
Push["run_update_and_commit.sh 执行 push"]
CheckRemote{"Remote 协议类型"}
ModeDisabled["模式 5: 禁用推送<br/>纯本地维护 runtime-state"]
Push --> CheckRemote
Push -. "GIT_PUSH_ENABLED=0" .-> ModeDisabled
CheckRemote -->|"HTTPS 协议"| Mode1["方式 1: 动态 Header 注入<br/>GIT_CONFIG_* 环境变量"]
CheckRemote -->|"HTTPS 协议"| Mode2["方式 2: Git 凭据助手<br/>credential.helper store"]
CheckRemote -->|"SSH 协议"| Mode3["方式 3: SSH Deploy Key<br/>~/.ssh 私钥免密"]
CheckRemote -->|"URL 内联"| Mode4["方式 4: Remote URL 内嵌 Token<br/>https://user:token@host"]
GIT_CONFIG_COUNT=1、GIT_CONFIG_KEY_0=http.extraHeader、GIT_CONFIG_VALUE_0="Authorization: Basic <base64>" 注入,ps aux 与命令行参数中完全不含 Token。GIT_HTTP_TOKEN_FILE=/path/to/token(文件权限设为 chmod 600)。GIT_HTTP_TOKEN=ghp_xxx 与 GIT_HTTP_USERNAME=git(CI/CD 流水线适用)。方式 2:Git 凭据助手持久化存储(credential.helper store)
git credential approve 将凭据写入存储文件。免交互配置命令(终端实操):
# 开启 store 助手
git config --global credential.helper store
# 零交互录入凭据
printf 'url=%s\nusername=%s\npassword=%s\n\n' \
"$(git remote get-url origin)" "你的用户名" "你的Token" | git credential approve
# 只读验证(零提交测试,不提示输入密码即代表成功)
git ls-remote origin
方式 3:SSH 部署公钥免密认证(SSH Deploy Key)
git@git.dewofaurora.de:user/repo.git),在服务用户的 ~/.ssh/ 下配置具备写权限的 Deploy Key 即可。方式 4:Remote URL 内嵌凭据(兼容支持)
https://user:token@host/repo.git(仅作兼容,Token 会留存在本地 .git/config,安全性较低)。模式 5:禁用远程推送模式(Local-Only)
GIT_PUSH_ENABLED=0,仅在本地维护 runtime-state 分支并通过局域网暴露 HTTP 服务,完全不需要配置外网推送凭据。Q1:配置凭据前需要先在项目中产生提交吗?
完全不需要。 Git 凭据存储是网络传输层(HTTP/SSH)的配置,与本地是否存在未提交的修改或历史 Commit 完全解耦。只要仓库具备有效 remote 且能连通网络,随时可以录入凭据。
Q2:如果之前已经在分支上产生过历史提交,会有影响吗?
完全不影响,且这正是脚本的标准工作模式。
- 代码分支隔离:开发人员在
main上的提交与runtime-state分支物理隔离,互不干扰。- 智能状态防抖:脚本每次运行都会比对
runtime-state最新 HEAD。若优选值未变且状态为ok,自动触发静默跳过,杜绝无效提交刷屏。- 模式 A 历史滚动修剪:当提交累积达到阈值(默认 50 次)时,脚本会自动通过
commit-tree创建 Baseline Snapshot 并执行 Squashed Rebase,始终将孤儿分支历史锁定在轻量窗口。
为了平衡晚高峰的网络实时性与低谷期的系统资源开销,系统内置了双频自适应调度引擎:
install_debian.sh 提取高峰与非高峰周期的最小值作为底层的唤醒基频(通常为 10 分钟):$$ \text{TIMER_INTERVAL} = \min(\text{Peak Interval}, \text{Off-Peak Interval}) $$
TZ="Asia/Shanghai" date 强制指定时区,机房位于 UTC 时区也能精准捕捉北京时间晚高峰(默认 19:00 - 24:00,支持跨午夜配置)。$$ \text{Tolerance} = \frac{\text{Peak Interval} + 1}{2} $$
$$ \text{Remainder} = \text{Current Minute} \pmod{\text{Off-Peak Interval}} $$
off-peak run skipped 并安全退出 0,零多余开销。record_mapping):将第三方 API 字段标准映射为 domain、created_at、yd_latency、yd_pkg_lost_rate、yd_score 等标准键。record_filter):在解析前剔除黑名单语义(如排除 host_provider == "CF优选IP")。domain_filter):通过正则表达式剔除纯 IPv4 记录,仅保留物理域名。当前配置针对中国移动线路加权:yd_latency(权重 1.0,越小越优)+ yd_pkg_lost_rate(权重 10.0,越小越优)。参与字段、权重及字段级 prefer_lower / order 可按业务需求调整。
yd_score(以及 dx_score、lt_score、avg_score)是上游 API 对应 *Score 的映射,含义、单位和计算公式由上游定义。本项目不重新计算这些字段,也不将其固定解释为健康分或成本分;配置排序方向前,应核对所用数据源的指标定义。它们与本项目根据配置生成的 score_value 是不同字段。vps789 API 数据源,接口返回的 createdTime 实际为「域名纳入监控池的建档时间」而非当次拨测时间。在服务器模式下默认设置 within_hours: 0(禁用时效过滤),防止误杀老牌稳定域名。tie_breakers):当前配置在 score_value 相同时,依次以 yd_score(升序 asc,数值较小者优先) $\to$ avg_latency(升序 asc) $\to$ created_at(降序 desc) $\to$ domain(升序 asc)决胜。yd_score 当前不参与加权;是否使用及排序方向均由配置决定。error_use_last_good)或所有候选均被过滤(fallback_last_good)。state.json 中读取 last_good_domain,并对其再次执行黑名单与合法性复检(杜绝故障节点复活);提交信息自动标注 chore(fallback):,且在值未变时抑制重复提交。所有服务端脚本统一输出标准日志格式:
[YYYY-MM-DD HH:MM:SS] [vmess-domain-rotator] <message>
[!TIP] 手动在终端执行
run_update_and_commit.sh时,脚本内置的journald tee技术会同时向终端(stdout/stderr)以及系统守护进程(systemd-cat)输出双流,确保手动调试信息与定时任务日志完整融合在同一系统时间线中。
# 1. 查看定时器运行状态与下一次触发时间
sudo systemctl status vmess-domain-rotator.timer
# 2. 手动立即触发一次更新(无需等待定时器)
sudo systemctl start vmess-domain-rotator.service
# 3. 实时追踪查看运行日志(包含定时任务与手动调试日志)
sudo journalctl -t vmess-domain-rotator -f --no-pager
# 4. 查看当前配置解析出的绝对输出路径(不执行更新)
python3 scripts/domain_updater.py --config configs/config.server.json --print-output-settings
# 5. 手动强制提交当前域名(强制产生 commit 并推送到 remote)
sudo -u aurora bash scripts/server/run_update_and_commit.sh --force-commit configs/config.server.json
适用于有完整 Python 3 环境、并在本地机器上直接运行 cfst 测速程序的场景。
# 单次执行更新并查看落盘结果
python3 scripts/domain_updater.py --config configs/config.local.json
# 维护与产物检查
cat runtime/current_ip.txt
cat runtime/current_ip.json
cat runtime/substore_vars.json
cat runtime/state.json
configs/config.local.json):
cfst_local.work_dir:测速工作目录(如 ./cfst_darwin_arm64)cfst_local.binary:测速程序路径(如 ./cfst)cfst_local.run_args:cfst 参数;测速地址统一为 -url https://speed.cloudflare.com/__down?bytes=50000000,避免依赖默认地址cfst_local.skip_run:设为 true 时仅解析现有 result.csv,设为 false 时每次调用二进制执行测速。download_speed == 0.00 的失效死节点;当在 run_args 中显式指定 -dd(禁用下载测速)时,自动转为按平均延迟排序。适用于运行在嵌入式路由器(如华硕梅林固件、OpenWrt)上,设备无 Python 环境,通过纯 POSIX Shell + BusyBox 驱动的场景。
推荐在路由器物理持久分区(如 /jffs/vmess/)建立以下布局:
/jffs/vmess/
├── busybox_armv7l # 具备完整 httpd applet 的 BusyBox 二进制
├── config_router.conf # 路由器端专用配置文件
├── scripts/
│ ├── router_local_update.sh # 本地更新脚本
│ └── router_local_http.sh # 微型 HTTP 守护进程
└── cfst/
├── cfst / ip.txt / result.csv # 测速二进制与网段文件
测速地址由 config_router.conf 的 CFST_URL 指定,默认 https://speed.cloudflare.com/__down?bytes=50000000,与本地 Python 模式保持一致。
[!WARNING] 闪存寿命保护警示 路由器的
/jffs分区位于 NAND/NOR 物理 Flash 闪存介质上。如果将高频定时生成的测速结果result.csv和运行时临时文件直接写入闪存,长期高频擦写将严重缩短路由器硬件寿命!
本项目全面支持动静分离与绝对路径重定向,通过将高频临时读写重定向至 RAM 内存文件系统(/tmp),实现硬件零磨损:
# === config_router.conf 推荐优化配置 ===
# 1. 测速工作区与输入文件(只读性质,放在 Flash 物理存储上)
CFST_WORK_DIR="/jffs/vmess/cfst"
CFST_BIN="./cfst"
CFST_IP_FILE="ip.txt"
# 2. 【核心优化】测速输出 CSV 重定向到 RAM 内存系统(/tmp),彻底杜绝闪存磨损!
CFST_RESULT_FILE="/tmp/vmess_result.csv"
# 3. 【核心优化】高频运行期数据全部落盘在 RAM 内存,极速读写且零磨损
RUNTIME_DIR="/tmp/vmess_runtime"
VALUE_TEXT_FILE="current_ip.txt"
VALUE_JSON_FILE="current_ip.json"
EXPORT_VARS_FILE="substore_vars.json"
# 4. 【容灾持久化】仅将极低频写入的备用状态 state.json 保留在物理闪存中(断电不丢失)
STATE_FILE="/jffs/vmess/state.json"
httpd 直接挂载 /tmp/vmess_runtime,内存直读提供极速响应。state.json 依然完好,开机即能从 Fallback 状态迅速恢复。在路由器的开机自启脚本(如 /jffs/scripts/services-start)中加入守护与保活:
VMESS_DIR="/jffs/vmess"
CONFIG="./config_router.conf"
UPDATE_LOG="/tmp/router_local_update.log"
HTTP_LOG="/tmp/router_http.log"
HTTP_PROCESS_PATTERN='[r]outer_local_http.sh'
start_vmess_http() {
if ps | grep -q "$HTTP_PROCESS_PATTERN"; then
return 0
fi
cd "$VMESS_DIR" || exit 1
nohup sh scripts/router_local_http.sh "$CONFIG" >> "$HTTP_LOG" 2>&1 &
}
cd "$VMESS_DIR" || exit 1
# 开机防抖:等待网络就绪后执行初次测速
sleep 60
i=1
while [ "$i" -le 5 ]; do
if sh scripts/router_local_update.sh "$CONFIG" >> "$UPDATE_LOG" 2>&1; then
break
fi
sleep 20
i=$((i + 1))
done
# 注册 Cron 定时更新(每 12 小时一次)
cru d vmess_rotate
cru a vmess_rotate "0 */12 * * * cd $VMESS_DIR && sh scripts/router_local_update.sh $CONFIG >> $UPDATE_LOG 2>&1"
# 启动微型 HTTP 服务并设置看门狗(每 5 分钟保活)
sleep 10
start_vmess_http
cru d vmess_watchdog
cru a vmess_watchdog "*/5 * * * * if ! ps | grep -q '$HTTP_PROCESS_PATTERN'; then cd $VMESS_DIR && nohup sh scripts/router_local_http.sh $CONFIG >> $HTTP_LOG 2>&1 & fi"
本地直接执行打包工具,一键生成开箱即用的部署压缩包:
# 默认打包当前配置与组件 -> 输出 vmess.tar.gz
./build_router_package.sh
# 支持通过 SSOT 从 JSON 配置自动同步测速参数至路由器配置文件
./build_router_package.sh --sync-from-config configs/config.local.json
substore/operator_template.js 提供了高度灵活的动态节点注入能力:
VALUE_SOURCE_MODE = "default":路由器优先;当路由器实测下载速度低于阈值(ROUTER_MIN_SPEED_MB_PER_S = 5)时,自动无缝切换至服务器模式。VALUE_SOURCE_MODE = "server_only":仅从服务器远端分支拉取。VALUE_SOURCE_MODE = "router_only":仅从局域网路由器 HTTP 拉取。在 Sub-Store 订阅链接参数中传入参数即可实现强行注入(拥有最高优先级,绕过测速与模式判断):
domain=my.preferred.com:强制覆盖所有节点的地址为此域名或 IP。domain_override=false:临时关闭覆盖,恢复自动选优逻辑。仓库配备了完善的零依赖自动化测试套件(执行时间 < 6 秒):
# 1. 运行全套 32 项自动化测试
python3 -m unittest discover tests -v
# 2. 全链路静态语法校验
bash -n scripts/server/*.sh scripts/router/*.sh build_router_package.sh
python3 -m py_compile scripts/domain_updater.py scripts/rotator/*.py tests/*.py
node -c substore/operator_template.js
| 测试套件文件 | 核心覆盖断言与安全边界 |
|---|---|
tests/test_server_mode_lifecycle.py |
Server 模式全生命周期(API 选优、孤儿分支 Worktree 提交、自适应时间拦截、静默抑制、模式 A 历史滚动修剪) |
tests/test_cfst_local_lifecycle.py |
本地cfst 模式全生命周期(CSV 解析、零速节点过滤、-dd 延迟免测排序、Fallback 容灾与路径防漂移契约) |
tests/test_domain_updater.py |
Python 选优核心算法(当前配置的yd_score 升序平局决胜、加权计算、迟滞防抖 Hysteresis、黑名单复检) |
tests/test_shell_lifecycle.py |
Shell 脚本生命周期安全(uninstall_debian.sh 防空服务名误删库、Token 动态环境零泄露、ls-remote 退出码精细判定) |
tests/test_router_scripts.py |
路由器脚本原子性(.tmp.$$ 替换写入、测速过滤、SSOT 打包配置同步) |
tests/test_substore_operator.py |
Sub-Store 脚本算子(XSS/非法 HTML 过滤、数据源超时降级、类型安全断言) |