From af7ed5e1ec9ac8ee2eb39e1a82196ae09f75a25c Mon Sep 17 00:00:00 2001 From: sindricn Date: Fri, 26 Sep 2025 15:05:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/outbound-manager.sh | 168 ++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/scripts/outbound-manager.sh b/scripts/outbound-manager.sh index b5c62e3..f1d0618 100644 --- a/scripts/outbound-manager.sh +++ b/scripts/outbound-manager.sh @@ -191,6 +191,8 @@ add_outbound_rule() { echo -e "${BLUE}=== 添加出站规则 ===${NC}" echo "" + echo -e "${YELLOW}注意: 每种类型只能有一个出站规则,添加同类型规则将覆盖现有规则${NC}" + echo "" echo "选择出站类型:" echo "1. Direct (直连)" echo "2. SOCKS5 代理" @@ -442,6 +444,138 @@ apply_outbound_config() { fi } +# 检查现有同类型出站规则 +check_existing_outbound_type() { + local target_type="$1" + local config_file="${2:-$HYSTERIA_CONFIG}" + + if [[ ! -f "$config_file" ]]; then + return 1 # 文件不存在,没有冲突 + fi + + # 查找同类型的规则 + local in_outbounds=false + local current_rule_type="" + local current_rule_name="" + + while IFS= read -r line; do + # 检测outbounds节点 + if [[ "$line" =~ ^[[:space:]]*outbounds: ]]; then + in_outbounds=true + continue + fi + + # 离开outbounds节点 + if [[ "$in_outbounds" == true ]] && [[ "$line" =~ ^[[:space:]]*[a-zA-Z]+:[[:space:]]*$ ]] && [[ ! "$line" =~ ^[[:space:]]*- ]]; then + in_outbounds=false + fi + + # 在outbounds节点中 + if [[ "$in_outbounds" == true ]]; then + # 检测规则名 + if [[ "$line" =~ ^[[:space:]]*-[[:space:]]*name:[[:space:]]*(.+)$ ]]; then + current_rule_name="${BASH_REMATCH[1]}" + current_rule_name=$(echo "$current_rule_name" | xargs) # 去除前后空格 + fi + + # 检测规则类型 + if [[ "$line" =~ ^[[:space:]]*type:[[:space:]]*(.+)$ ]]; then + current_rule_type="${BASH_REMATCH[1]}" + current_rule_type=$(echo "$current_rule_type" | xargs) # 去除前后空格 + + # 检查是否与目标类型匹配 + if [[ "$current_rule_type" == "$target_type" ]]; then + echo "$current_rule_name" # 返回现有同类型规则的名称 + return 0 + fi + fi + fi + done < "$config_file" + + return 1 # 未找到同类型规则 +} + +# 静默删除指定规则(用于类型覆盖,无用户确认) +delete_existing_rule_silent() { + local rule_name="$1" + + echo -e "${BLUE}[INFO]${NC} 正在删除现有规则: $rule_name" + + # 创建临时文件 + local temp_config="/tmp/hysteria_delete_temp_$(date +%s).yaml" + + # 智能删除逻辑:完整删除outbound规则和相关ACL条目 + local in_outbound_rule=false + local in_acl_section=false + local acl_base_indent="" + + while IFS= read -r line || [[ -n "$line" ]]; do + local should_keep=true + + # 1. 删除包含规则名的注释 + if [[ "$line" =~ ^[[:space:]]*#.*${rule_name} ]]; then + should_keep=false + fi + + # 2. 检测outbound规则块 + if [[ "$line" =~ ^[[:space:]]*-[[:space:]]*name:[[:space:]]*${rule_name}[[:space:]]*$ ]]; then + in_outbound_rule=true + should_keep=false + elif [[ "$in_outbound_rule" == true ]]; then + # 在outbound规则块中,检查是否结束 + if [[ "$line" =~ ^[[:space:]]*-[[:space:]]*name: ]] || [[ "$line" =~ ^[[:space:]]*[a-zA-Z]+:[[:space:]]*$ ]] && [[ ! "$line" =~ ^[[:space:]]*(type|direct|socks5|http|addr|url|mode|username|password|insecure): ]]; then + in_outbound_rule=false + should_keep=true + else + should_keep=false # 删除outbound规则块内的所有行 + fi + fi + + # 3. 检测ACL节点 + if [[ "$line" =~ ^[[:space:]]*acl: ]]; then + in_acl_section=true + acl_base_indent=$(echo "$line" | sed 's/acl:.*//') + should_keep=true + elif [[ "$in_acl_section" == true ]]; then + # 检查是否离开ACL节点 + if [[ "$line" =~ ^[[:space:]]*[a-zA-Z]+:[[:space:]]*$ ]] && [[ ! "$line" =~ ^[[:space:]]*(inline|file): ]]; then + local line_indent=$(echo "$line" | sed 's/[a-zA-Z].*//') + if [[ ${#line_indent} -le ${#acl_base_indent} ]]; then + in_acl_section=false + should_keep=true + fi + fi + + # 在ACL节点中处理 - 删除包含目标规则名的行 + if [[ "$in_acl_section" == true ]] && [[ "$line" =~ ${rule_name} ]]; then + should_keep=false # 删除ACL中包含目标规则名的条目 + fi + fi + + # 写入保留的行 + if [[ "$should_keep" == true ]]; then + echo "$line" >> "$temp_config" + fi + done < "$HYSTERIA_CONFIG" + + # 检查删除是否成功 + if grep -q "name: *$rule_name" "$temp_config" 2>/dev/null; then + echo -e "${RED}[ERROR]${NC} 删除失败,规则仍存在" + rm -f "$temp_config" + return 1 + fi + + # 应用修改 + if mv "$temp_config" "$HYSTERIA_CONFIG" 2>/dev/null; then + echo -e "${GREEN}[SUCCESS]${NC} 现有规则 '$rule_name' 已删除" + return 0 + else + echo -e "${RED}[ERROR]${NC} 删除失败,文件操作错误" + rm -f "$temp_config" + return 1 + fi +} + # 极简稳定的配置应用函数 apply_outbound_simple() { local name="$1" type="$2" @@ -454,6 +588,40 @@ apply_outbound_simple() { return 1 fi + # 检查同类型规则冲突 + local existing_rule + if existing_rule=$(check_existing_outbound_type "$type"); then + echo "" + echo -e "${YELLOW}⚠️ 冲突检测 ⚠️${NC}" + echo -e "${YELLOW}检测到现有的 $type 类型规则: ${CYAN}$existing_rule${NC}" + echo -e "${YELLOW}根据系统设计原则,每种类型只能有一个出站规则${NC}" + echo "" + echo -e "${BLUE}可选操作:${NC}" + echo -e "${GREEN}1.${NC} 覆盖现有规则 ${CYAN}$existing_rule${NC} (推荐)" + echo -e "${RED}2.${NC} 取消本次添加操作" + echo "" + read -p "请选择操作 [1-2]: " choice + + case $choice in + 1) + echo -e "${BLUE}[INFO]${NC} 将覆盖现有的 $type 规则: $existing_rule" + # 先删除现有同类型规则 (静默删除,不需要用户确认) + if ! delete_existing_rule_silent "$existing_rule"; then + echo -e "${RED}[ERROR]${NC} 删除现有规则失败" + return 1 + fi + ;; + 2) + echo -e "${BLUE}[INFO]${NC} 取消添加操作" + return 0 + ;; + *) + echo -e "${RED}[ERROR]${NC} 无效选择,取消添加" + return 1 + ;; + esac + fi + # 直接操作,不创建不必要的备份 # 创建临时文件