|
1 | 1 | #!/system/bin/sh |
2 | 2 | # sing-box 运行时配置辅助函数 |
3 | 3 |
|
| 4 | +CUR_OUTBOUND_CONFIG="" |
| 5 | +CUR_OUTBOUND_DIR="" |
| 6 | +CUR_OUTBOUND_MODE="" |
| 7 | +CUR_SELECTOR_MODE="" |
| 8 | +CUR_CURRENT_TAG="" |
| 9 | + |
| 10 | +RUNTIME_OUTBOUNDS_FILE="" |
| 11 | +RUNTIME_NODE_PATHS="" |
| 12 | +RUNTIME_NODE_TAGS_JSON="" |
| 13 | +RUNTIME_NODE_COUNT=0 |
| 14 | +RUNTIME_SKIPPED_COUNT=0 |
| 15 | + |
4 | 16 | ####################################### |
5 | | -# 获取当前节点所在目录 |
| 17 | +# 初始化运行时上下文 |
6 | 18 | ####################################### |
7 | | -get_current_outbounds_dir() { |
8 | | - local current_config="$1" |
9 | | - local current_dir |
| 19 | +initialize_runtime_context() { |
| 20 | + require_file "${MODULE_CONF:-}" "模块配置文件不存在: ${MODULE_CONF:-未定义}" |
| 21 | + require_dir "${SINGBOX_DIR:-}" "sing-box 配置目录不存在: ${SINGBOX_DIR:-未定义}" |
| 22 | + require_dir "${CONFDIR:-}" "通用配置目录不存在: ${CONFDIR:-未定义}" |
| 23 | + require_dir "${RUNTIME_DIR:-}" "运行时目录不存在: ${RUNTIME_DIR:-未定义}" |
10 | 24 |
|
11 | | - current_dir="${current_config%/*}" |
12 | | - [ "$current_dir" != "$current_config" ] || die "无法解析当前节点目录: $current_config" |
13 | | - [ -d "$current_dir" ] || die "当前节点目录不存在: $current_dir" |
| 25 | + CUR_OUTBOUND_CONFIG="$(read_conf "$MODULE_CONF" "CURRENT_CONFIG" "")" |
| 26 | + CUR_OUTBOUND_MODE="$(read_conf "$MODULE_CONF" "OUTBOUND_MODE" "rule")" |
| 27 | + CUR_SELECTOR_MODE="$(read_conf "$MODULE_CONF" "SELECTOR_MODE" "urltest")" |
14 | 28 |
|
15 | | - echo "$current_dir" |
16 | | -} |
| 29 | + [ -n "$CUR_OUTBOUND_CONFIG" ] || die "CURRENT_CONFIG 未定义,请先选择节点" |
| 30 | + require_file "$CUR_OUTBOUND_CONFIG" "当前节点配置文件不存在: $CUR_OUTBOUND_CONFIG" |
17 | 31 |
|
18 | | -####################################### |
19 | | -# 判断是否为节点配置文件 |
20 | | -####################################### |
21 | | -is_node_config_file() { |
22 | | - local file="$1" |
| 32 | + CUR_OUTBOUND_DIR="${CUR_OUTBOUND_CONFIG%/*}" |
| 33 | + [ "$CUR_OUTBOUND_DIR" != "$CUR_OUTBOUND_CONFIG" ] || die "无法解析当前节点目录: $CUR_OUTBOUND_CONFIG" |
| 34 | + require_dir "$CUR_OUTBOUND_DIR" "当前节点目录不存在: $CUR_OUTBOUND_DIR" |
| 35 | + |
| 36 | + CUR_CURRENT_TAG="$(detect_outbound_tag "$CUR_OUTBOUND_CONFIG" || true)" |
| 37 | + [ -n "$CUR_CURRENT_TAG" ] || die "无法读取当前节点标签: $CUR_OUTBOUND_CONFIG" |
23 | 38 |
|
24 | | - [ -f "$file" ] || return 1 |
25 | | - [ "${file##*/}" != "_meta.json" ] || return 1 |
| 39 | + RUNTIME_OUTBOUNDS_FILE="$RUNTIME_DIR/outbounds.json" |
26 | 40 | } |
27 | 41 |
|
28 | 42 | ####################################### |
29 | | -# 转义 JSON 字符串 |
| 43 | +# 清空运行时节点缓存 |
30 | 44 | ####################################### |
31 | | -json_escape() { |
32 | | - printf "%s" "$1" | sed 's/\\/\\\\/g; s/"/\\"/g' |
| 45 | +reset_runtime_nodes() { |
| 46 | + RUNTIME_NODE_PATHS="" |
| 47 | + RUNTIME_NODE_TAGS_JSON="" |
| 48 | + RUNTIME_NODE_COUNT=0 |
| 49 | + RUNTIME_SKIPPED_COUNT=0 |
33 | 50 | } |
34 | 51 |
|
35 | 52 | ####################################### |
36 | | -# 追加出站标签到 JSON 数组片段 |
| 53 | +# 追加运行时节点缓存 |
37 | 54 | ####################################### |
38 | | -append_selector_tag() { |
39 | | - local tags="$1" |
| 55 | +append_runtime_node() { |
| 56 | + local file="$1" |
40 | 57 | local tag="$2" |
41 | | - local escaped |
| 58 | + local escaped_tag |
42 | 59 |
|
43 | | - escaped="$(json_escape "$tag")" |
44 | | - if [ -n "$tags" ]; then |
45 | | - printf "%s, \"%s\"" "$tags" "$escaped" |
| 60 | + if [ -n "$RUNTIME_NODE_PATHS" ]; then |
| 61 | + RUNTIME_NODE_PATHS="${RUNTIME_NODE_PATHS} |
| 62 | +$file" |
46 | 63 | else |
47 | | - printf "\"%s\"" "$escaped" |
| 64 | + RUNTIME_NODE_PATHS="$file" |
48 | 65 | fi |
49 | | -} |
50 | 66 |
|
51 | | -# 运行时上下文(由 initialize_runtime_context 填充) |
52 | | -CUR_OUTBOUND_CONFIG="" |
53 | | -CUR_OUTBOUND_DIR="" |
54 | | -CUR_OUTBOUND_MODE="" |
55 | | -CUR_SELECTOR_MODE="" |
| 67 | + if ! is_reserved_outbound_tag "$tag"; then |
| 68 | + escaped_tag="$(json_escape "$tag")" |
| 69 | + if [ -n "$RUNTIME_NODE_TAGS_JSON" ]; then |
| 70 | + RUNTIME_NODE_TAGS_JSON="$RUNTIME_NODE_TAGS_JSON, \"$escaped_tag\"" |
| 71 | + else |
| 72 | + RUNTIME_NODE_TAGS_JSON="\"$escaped_tag\"" |
| 73 | + fi |
| 74 | + fi |
56 | 75 |
|
57 | | -# 节点扫描结果(由 write_runtime_outbounds 填充) |
58 | | -SCAN_NODE_ARGS="" |
59 | | -SCAN_NODE_COUNT=0 |
60 | | -SCAN_SKIPPED_COUNT=0 |
| 76 | + RUNTIME_NODE_COUNT=$((RUNTIME_NODE_COUNT + 1)) |
| 77 | +} |
61 | 78 |
|
62 | 79 | ####################################### |
63 | | -# 初始化启动环境与基础配置 |
| 80 | +# 扫描当前节点目录 |
64 | 81 | ####################################### |
65 | | -initialize_runtime_context() { |
66 | | - # 基础环境检查 |
67 | | - [ -x "${SING_BOX_BIN:-}" ] || die "sing-box 二进制不存在或不可执行" |
68 | | - [ -f "${MODULE_CONF:-}" ] || die "模块配置文件不存在" |
69 | | - [ -f "${TPROXY_CONF_DIR:-}/tproxy.conf" ] || die "透明代理配置文件不存在" |
| 82 | +scan_runtime_nodes() { |
| 83 | + local current_dir="${1:-$CUR_OUTBOUND_DIR}" |
| 84 | + local file tag |
70 | 85 |
|
71 | | - # 加载模块与透明代理配置 |
72 | | - . "$MODULE_CONF" |
73 | | - . "$TPROXY_CONF_DIR/tproxy.conf" |
| 86 | + require_dir "$current_dir" "节点目录不存在: $current_dir" |
| 87 | + reset_runtime_nodes |
74 | 88 |
|
75 | | - # 提取并验证当前节点路径 |
76 | | - CUR_OUTBOUND_CONFIG="$(strip_quotes "${CURRENT_CONFIG:-}")" |
77 | | - [ -n "$CUR_OUTBOUND_CONFIG" ] || die "CURRENT_CONFIG 未定义,请先选择节点" |
78 | | - [ -f "$CUR_OUTBOUND_CONFIG" ] || die "指定的节点配置文件不存在: $CUR_OUTBOUND_CONFIG" |
79 | | - |
80 | | - # 确定运行模式与选择器模式 |
81 | | - CUR_OUTBOUND_MODE="${OUTBOUND_MODE:-rule}" |
82 | | - CUR_SELECTOR_MODE="${SELECTOR_MODE:-urltest}" |
83 | | - |
84 | | - # 获取当前节点目录 |
85 | | - CUR_OUTBOUND_DIR="$(get_current_outbounds_dir "$CUR_OUTBOUND_CONFIG")" || return 1 |
| 89 | + for file in "$current_dir"/*.json; do |
| 90 | + is_node_config_file "$file" || continue |
| 91 | + tag="$(detect_outbound_tag "$file" || true)" |
| 92 | + |
| 93 | + if [ -z "$tag" ]; then |
| 94 | + RUNTIME_SKIPPED_COUNT=$((RUNTIME_SKIPPED_COUNT + 1)) |
| 95 | + continue |
| 96 | + fi |
| 97 | + |
| 98 | + append_runtime_node "$file" "$tag" |
| 99 | + done |
86 | 100 | } |
87 | 101 |
|
88 | 102 | ####################################### |
89 | | -# 扫描节点并生成运行时出站配置 |
| 103 | +# 生成运行时出站配置 |
90 | 104 | ####################################### |
91 | 105 | write_runtime_outbounds() { |
92 | | - local output="${RUNTIME_DIR:?RUNTIME_DIR 未定义}/outbounds.json" |
93 | 106 | local current_config="${1:-$CUR_OUTBOUND_CONFIG}" |
94 | | - local current_dir="${CUR_OUTBOUND_DIR:-$(get_current_outbounds_dir "$current_config")}" |
95 | 107 | local selector_mode="${2:-$CUR_SELECTOR_MODE}" |
96 | | - local current_tag current_tag_json tags="" f tag |
97 | | - |
98 | | - SCAN_NODE_ARGS="" |
99 | | - SCAN_NODE_COUNT=0 |
100 | | - SCAN_SKIPPED_COUNT=0 |
| 108 | + local tags="$RUNTIME_NODE_TAGS_JSON" |
101 | 109 |
|
102 | | - current_tag="$(detect_outbound_tag "$current_config")" |
103 | | - [ -n "$current_tag" ] || die "无法从当前出站配置读取标签: $current_config" |
104 | | - current_tag_json="$(json_escape "$current_tag")" |
| 110 | + [ -n "$current_config" ] || die "当前节点配置未初始化" |
| 111 | + [ -n "$selector_mode" ] || selector_mode="urltest" |
105 | 112 |
|
106 | | - mkdir -p "$RUNTIME_DIR" || die "无法创建运行时配置目录: $RUNTIME_DIR" |
107 | | - |
108 | | - log "INFO" "正在扫描节点目录: $current_dir" |
109 | | - |
110 | | - # 扫描当前节点目录 |
111 | | - for f in "$current_dir"/*.json; do |
112 | | - is_node_config_file "$f" || continue |
113 | | - tag="$(detect_outbound_tag "$f")" |
114 | | - |
115 | | - if [ -z "$tag" ]; then |
116 | | - SCAN_SKIPPED_COUNT=$((SCAN_SKIPPED_COUNT + 1)) |
117 | | - continue |
118 | | - fi |
119 | | - |
120 | | - # 收集启动参数(所有节点都加载) |
121 | | - SCAN_NODE_ARGS="$SCAN_NODE_ARGS -c \"$f\"" |
122 | | - SCAN_NODE_COUNT=$((SCAN_NODE_COUNT + 1)) |
| 113 | + if [ "$RUNTIME_NODE_COUNT" -eq 0 ] && [ -z "$RUNTIME_NODE_PATHS" ]; then |
| 114 | + scan_runtime_nodes "$CUR_OUTBOUND_DIR" |
| 115 | + tags="$RUNTIME_NODE_TAGS_JSON" |
| 116 | + fi |
123 | 117 |
|
124 | | - # 收集可切换标签(过滤掉 default 以防止抢占测速组) |
125 | | - if [ "$tag" != "default" ]; then |
126 | | - tags="$(append_selector_tag "$tags" "$tag")" |
127 | | - fi |
128 | | - done |
| 118 | + if [ -z "$tags" ] && ! is_reserved_outbound_tag "$CUR_CURRENT_TAG"; then |
| 119 | + tags="\"$(json_escape "$CUR_CURRENT_TAG")\"" |
| 120 | + fi |
129 | 121 |
|
130 | | - # 未发现可切换节点时,至少保留当前节点供测速/选择 |
131 | | - [ -n "$tags" ] || tags="$(append_selector_tag "" "$current_tag")" |
| 122 | + [ -n "$tags" ] || die "当前节点目录没有可用的出站标签: $CUR_OUTBOUND_DIR" |
132 | 123 |
|
133 | 124 | case "$selector_mode" in |
134 | 125 | urltest | auto | 动态测速) |
135 | | - cat > "$output" << EOF |
| 126 | + cat > "$RUNTIME_OUTBOUNDS_FILE" << EOF |
136 | 127 | { |
137 | 128 | "outbounds": [ |
138 | 129 | { |
@@ -169,7 +160,7 @@ write_runtime_outbounds() { |
169 | 160 | EOF |
170 | 161 | ;; |
171 | 162 | manual | selector | 手动选择 | 手动) |
172 | | - cat > "$output" << EOF |
| 163 | + cat > "$RUNTIME_OUTBOUNDS_FILE" << EOF |
173 | 164 | { |
174 | 165 | "outbounds": [ |
175 | 166 | { |
|
187 | 178 | "direct", |
188 | 179 | $tags |
189 | 180 | ], |
190 | | - "default": "$current_tag_json", |
| 181 | + "default": "$(json_escape "$CUR_CURRENT_TAG")", |
191 | 182 | "interrupt_exist_connections": true |
192 | 183 | } |
193 | 184 | ] |
|
199 | 190 | ;; |
200 | 191 | esac |
201 | 192 |
|
202 | | - echo "$output" |
| 193 | + printf "%s\n" "$RUNTIME_OUTBOUNDS_FILE" |
203 | 194 | } |
0 commit comments