mirror of
https://github.com/wnlen/clash-for-linux.git
synced 2026-10-10 04:03:04 +08:00
fix(select): block manual node picking for auto strategy groups
This commit is contained in:
+137
-4
@@ -6191,7 +6191,7 @@ cmd_select() {
|
||||
|
||||
if [ -z "${1:-}" ]; then
|
||||
print_select_context || return $?
|
||||
proxy_select_interactive
|
||||
proxy_select_interactive_guarded
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -6217,7 +6217,7 @@ cmd_proxy_groups() {
|
||||
[ -n "${group:-}" ] || continue
|
||||
found="true"
|
||||
type="$(proxy_group_type "$group" 2>/dev/null || echo "unknown")"
|
||||
current="$(proxy_group_current "$group" 2>/dev/null || echo "-")"
|
||||
current="$(proxy_group_current "$group" 2>/dev/null || true)"
|
||||
printf ' 📦 %-20s %-12s %s\n' "$group" "$type" "$current"
|
||||
done < <(proxy_group_list)
|
||||
|
||||
@@ -6333,14 +6333,14 @@ proxy_pick_index() {
|
||||
}
|
||||
|
||||
proxy_pick_group_interactive() {
|
||||
local idx count group current
|
||||
local idx count group current type
|
||||
local -a groups=()
|
||||
local -a ordered_groups=()
|
||||
|
||||
while IFS= read -r group; do
|
||||
[ -n "${group:-}" ] || continue
|
||||
groups+=("$group")
|
||||
done < <(proxy_group_manual_list)
|
||||
done < <(proxy_group_display_list)
|
||||
|
||||
for group in "节点选择" "自动选择"; do
|
||||
if printf '%s\n' "${groups[@]}" | grep -Fxq "$group"; then
|
||||
@@ -6543,6 +6543,139 @@ proxy_select_interactive() {
|
||||
print_select_feedback "$group"
|
||||
}
|
||||
|
||||
proxy_pick_group_for_select() {
|
||||
local idx count group current type
|
||||
local -a groups=()
|
||||
local -a ordered_groups=()
|
||||
|
||||
while IFS= read -r group; do
|
||||
[ -n "${group:-}" ] || continue
|
||||
groups+=("$group")
|
||||
done < <(proxy_group_display_list)
|
||||
|
||||
for group in "节点选择" "自动选择"; do
|
||||
if printf '%s\n' "${groups[@]}" | grep -Fxq "$group"; then
|
||||
ordered_groups+=("$group")
|
||||
fi
|
||||
done
|
||||
|
||||
for group in "${groups[@]}"; do
|
||||
case "$group" in
|
||||
节点选择|自动选择)
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
ordered_groups+=("$group")
|
||||
done
|
||||
|
||||
groups=("${ordered_groups[@]}")
|
||||
|
||||
count="${#groups[@]}"
|
||||
[ "$count" -gt 0 ] || die "📭 暂无可切换策略组"
|
||||
|
||||
echo "📦 请选择策略组:" >&2
|
||||
echo "💡 通常优先选择:节点选择" >&2
|
||||
idx=1
|
||||
for group in "${groups[@]}"; do
|
||||
current="$(proxy_group_current "$group" 2>/dev/null || true)"
|
||||
type="$(proxy_group_type_label "$group" 2>/dev/null || echo "unknown")"
|
||||
[ -n "${current:-}" ] || current="<unknown>"
|
||||
printf ' %s) 📦 %s [%s] -> 🚀 %s\n' "$idx" "$group" "$type" "$current" >&2
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
echo " q) 退出" >&2
|
||||
echo >&2
|
||||
|
||||
idx="$(proxy_pick_index "$count")" || return 1
|
||||
echo "${groups[$((idx - 1))]}"
|
||||
}
|
||||
|
||||
print_proxy_group_manual_pick_blocked() {
|
||||
local group="$1"
|
||||
local current type message
|
||||
|
||||
message="$(proxy_group_manual_pick_error_message "$group" 2>/dev/null || echo "该策略组不支持手动切换:$group")"
|
||||
current="$(proxy_group_current "$group" 2>/dev/null || true)"
|
||||
type="$(proxy_group_type_label "$group" 2>/dev/null || echo "unknown")"
|
||||
|
||||
ui_warn "$message"
|
||||
ui_kv "📦" "策略组" "$group"
|
||||
ui_kv "🧭" "策略组类型" "$type"
|
||||
if [ -n "${current:-}" ]; then
|
||||
ui_kv "🚀" "当前节点" "$current"
|
||||
else
|
||||
ui_kv "🚀" "当前节点" "<unknown>"
|
||||
fi
|
||||
ui_blank
|
||||
}
|
||||
|
||||
proxy_select_interactive_guarded() {
|
||||
local group="${1:-}"
|
||||
local current idx count total_count node selected_node
|
||||
local -a nodes=()
|
||||
|
||||
prepare
|
||||
|
||||
if ! status_is_running; then
|
||||
die_state "代理内核未运行" "clashon"
|
||||
fi
|
||||
|
||||
if ! proxy_controller_reachable 2>/dev/null; then
|
||||
die_state "控制器不可访问" "clashctl doctor"
|
||||
fi
|
||||
|
||||
if [ -z "${group:-}" ]; then
|
||||
ui_title "🚀 节点切换"
|
||||
group="$(proxy_pick_group_for_select)" || return 0
|
||||
fi
|
||||
|
||||
proxy_group_exists "$group" || die "策略组不存在:$group"
|
||||
|
||||
if ! proxy_group_supports_manual_pick "$group"; then
|
||||
print_proxy_group_manual_pick_blocked "$group"
|
||||
return 0
|
||||
fi
|
||||
|
||||
current="$(proxy_group_current "$group" 2>/dev/null || true)"
|
||||
|
||||
while IFS= read -r node; do
|
||||
[ -n "${node:-}" ] || continue
|
||||
nodes+=("$node")
|
||||
done < <(proxy_group_selectable_nodes "$group")
|
||||
|
||||
count="${#nodes[@]}"
|
||||
[ "$count" -gt 0 ] || die "📭 当前策略组没有候选节点:$group"
|
||||
total_count="$(select_total_node_count 2>/dev/null || true)"
|
||||
|
||||
echo
|
||||
echo "📦 当前策略组:$group"
|
||||
[ -n "${current:-}" ] && echo "🚀 当前节点:$current"
|
||||
echo "📦 当前策略组候选数:$count"
|
||||
[ -n "${total_count:-}" ] && echo "🔢 全部节点总数:$total_count"
|
||||
echo "ℹ️ 以下仅显示当前策略组可切换节点"
|
||||
echo
|
||||
echo "🚀 请选择节点:"
|
||||
|
||||
idx=1
|
||||
for node in "${nodes[@]}"; do
|
||||
if [ "$node" = "$current" ]; then
|
||||
printf ' %s) 🚀 %s [当前]\n' "$idx" "$node"
|
||||
else
|
||||
printf ' %s) 🚀 %s\n' "$idx" "$node"
|
||||
fi
|
||||
idx=$((idx + 1))
|
||||
done
|
||||
|
||||
echo " q) 退出"
|
||||
echo
|
||||
|
||||
idx="$(proxy_pick_index "$count")" || return 0
|
||||
selected_node="${nodes[$((idx - 1))]}"
|
||||
|
||||
proxy_group_select "$group" "$selected_node"
|
||||
print_select_feedback "$group"
|
||||
}
|
||||
|
||||
cmd_proxy() {
|
||||
echo "⚠ 当前版本不提供 proxy 子命令"
|
||||
echo "👉 使用 clashon / clashoff 控制代理"
|
||||
|
||||
+151
-17
@@ -278,13 +278,50 @@ proxy_group_type() {
|
||||
proxy_groups_json | "$(yq_bin)" -p=json eval ".proxies.\"$group\".type // \"\"" - 2>/dev/null
|
||||
}
|
||||
|
||||
proxy_group_is_selector() {
|
||||
proxy_group_type_key() {
|
||||
local type
|
||||
|
||||
type="$(proxy_group_type "$1")"
|
||||
type="$(proxy_group_type "$1" 2>/dev/null || true)"
|
||||
printf '%s' "${type:-}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]_-'
|
||||
}
|
||||
|
||||
case "$type" in
|
||||
Selector|URLTest|Fallback|LoadBalance)
|
||||
proxy_group_type_label() {
|
||||
local group="$1"
|
||||
local type_key raw_type
|
||||
|
||||
type_key="$(proxy_group_type_key "$group")"
|
||||
|
||||
case "$type_key" in
|
||||
selector)
|
||||
echo "select"
|
||||
;;
|
||||
urltest)
|
||||
echo "自动选择 / url-test"
|
||||
;;
|
||||
fallback)
|
||||
echo "故障转移 / fallback"
|
||||
;;
|
||||
loadbalance)
|
||||
echo "负载均衡 / load-balance"
|
||||
;;
|
||||
*)
|
||||
raw_type="$(proxy_group_type "$group" 2>/dev/null || true)"
|
||||
if [ -n "${raw_type:-}" ] && [ "$raw_type" != "null" ]; then
|
||||
echo "$raw_type"
|
||||
else
|
||||
echo "unknown"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
proxy_group_can_show_candidates() {
|
||||
local type_key
|
||||
|
||||
type_key="$(proxy_group_type_key "$1")"
|
||||
|
||||
case "$type_key" in
|
||||
selector|urltest|fallback|loadbalance)
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
@@ -293,22 +330,25 @@ proxy_group_is_selector() {
|
||||
esac
|
||||
}
|
||||
|
||||
proxy_group_is_selector() {
|
||||
proxy_group_can_show_candidates "$1"
|
||||
}
|
||||
|
||||
proxy_group_is_manual_selector() {
|
||||
proxy_group_supports_manual_pick "$1"
|
||||
}
|
||||
|
||||
proxy_group_is_auto_managed() {
|
||||
local group="$1"
|
||||
local type normalized_type
|
||||
local type_key
|
||||
|
||||
[ -n "${group:-}" ] || return 0
|
||||
proxy_group_exists "$group" || return 0
|
||||
|
||||
type="$(proxy_group_type "$group" 2>/dev/null || true)"
|
||||
normalized_type="$(printf '%s' "${type:-}" | tr '[:upper:]' '[:lower:]')"
|
||||
type_key="$(proxy_group_type_key "$group")"
|
||||
|
||||
case "$normalized_type" in
|
||||
urltest|url-test|fallback|loadbalance|load-balance)
|
||||
case "$type_key" in
|
||||
urltest|fallback|loadbalance)
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
@@ -389,14 +429,54 @@ proxy_group_selectable_nodes() {
|
||||
done < <(proxy_group_nodes "$group")
|
||||
}
|
||||
|
||||
proxy_group_supports_manual_pick() {
|
||||
proxy_group_has_selectable_candidates() {
|
||||
local group="$1"
|
||||
local node
|
||||
local has_now=""
|
||||
|
||||
[ -n "${group:-}" ] || return 1
|
||||
proxy_group_exists "$group" || return 1
|
||||
proxy_group_is_auto_managed "$group" && return 1
|
||||
proxy_group_can_show_candidates "$group" || return 1
|
||||
|
||||
while IFS= read -r node; do
|
||||
[ -n "${node:-}" ] || continue
|
||||
return 0
|
||||
done < <(proxy_group_selectable_nodes "$group")
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
proxy_group_manual_pick_error_message() {
|
||||
local group="$1"
|
||||
local type_key type_label
|
||||
|
||||
[ -n "${group:-}" ] || {
|
||||
echo "该策略组不支持手动切换"
|
||||
return 0
|
||||
}
|
||||
|
||||
type_key="$(proxy_group_type_key "$group")"
|
||||
type_label="$(proxy_group_type_label "$group")"
|
||||
|
||||
case "$type_key" in
|
||||
urltest|fallback|loadbalance)
|
||||
echo "该策略组为${type_label}类型,不支持手动切换:$group"
|
||||
;;
|
||||
*)
|
||||
echo "该策略组不支持手动切换:$group"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
proxy_group_supports_manual_pick() {
|
||||
local group="$1"
|
||||
local has_now=""
|
||||
local type_key
|
||||
|
||||
[ -n "${group:-}" ] || return 1
|
||||
proxy_group_exists "$group" || return 1
|
||||
|
||||
type_key="$(proxy_group_type_key "$group")"
|
||||
[ "$type_key" = "selector" ] || return 1
|
||||
|
||||
has_now="$(
|
||||
proxy_groups_json \
|
||||
@@ -405,12 +485,17 @@ proxy_group_supports_manual_pick() {
|
||||
)"
|
||||
[ "${has_now:-false}" = "true" ] || return 1
|
||||
|
||||
while IFS= read -r node; do
|
||||
[ -n "${node:-}" ] || continue
|
||||
return 0
|
||||
done < <(proxy_group_selectable_nodes "$group")
|
||||
proxy_group_has_selectable_candidates "$group"
|
||||
}
|
||||
|
||||
return 1
|
||||
proxy_group_display_list() {
|
||||
local group
|
||||
|
||||
while IFS= read -r group; do
|
||||
[ -n "${group:-}" ] || continue
|
||||
proxy_group_has_selectable_candidates "$group" || continue
|
||||
echo "$group"
|
||||
done < <(proxy_group_list)
|
||||
}
|
||||
|
||||
proxy_group_manual_list() {
|
||||
@@ -472,6 +557,55 @@ proxy_group_select() {
|
||||
rm -f "$response_file" 2>/dev/null || true
|
||||
}
|
||||
|
||||
proxy_group_select() {
|
||||
local group="$1"
|
||||
local node="$2"
|
||||
local base secret
|
||||
local code response_file response_body
|
||||
local available_node found
|
||||
|
||||
[ -n "${group:-}" ] || die "策略组名称不能为空"
|
||||
[ -n "${node:-}" ] || die "节点名称不能为空"
|
||||
|
||||
proxy_group_exists "$group" || die "策略组不存在:$group"
|
||||
proxy_group_supports_manual_pick "$group" || die "$(proxy_group_manual_pick_error_message "$group")"
|
||||
|
||||
found=false
|
||||
while IFS= read -r available_node; do
|
||||
[ -n "${available_node:-}" ] || continue
|
||||
if [ "$available_node" = "$node" ]; then
|
||||
found=true
|
||||
break
|
||||
fi
|
||||
done < <(proxy_group_selectable_nodes "$group")
|
||||
|
||||
if [ "$found" != "true" ]; then
|
||||
die "节点不存在于策略组中:$group -> $node"
|
||||
fi
|
||||
|
||||
base="$(controller_api_base)"
|
||||
secret="$(controller_secret)"
|
||||
response_file="$(mktemp)"
|
||||
code="$(
|
||||
curl -sS -o "$response_file" -w "%{http_code}" -X PUT \
|
||||
-H "Content-Type: application/json" \
|
||||
${secret:+-H "Authorization: Bearer $secret"} \
|
||||
--data "{\"name\":\"$node\"}" \
|
||||
"$base/proxies/$group"
|
||||
)"
|
||||
|
||||
if [ "${code:-000}" -lt 200 ] || [ "${code:-000}" -ge 300 ]; then
|
||||
response_body="$(cat "$response_file" 2>/dev/null || true)"
|
||||
rm -f "$response_file" 2>/dev/null || true
|
||||
if [ -n "${response_body:-}" ]; then
|
||||
die "节点切换失败:$response_body"
|
||||
fi
|
||||
die "节点切换失败:controller 返回 HTTP $code"
|
||||
fi
|
||||
|
||||
rm -f "$response_file" 2>/dev/null || true
|
||||
}
|
||||
|
||||
proxy_group_count() {
|
||||
proxy_group_list 2>/dev/null | awk 'NF{c++} END{print c+0}'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user