#!/bin/bash
# Envoy gRPC 极致性能优化脚本
# 基于 Envoy v1.37.0 官方文档标准
# 特性：长连接、高并发、BBR跑满带宽、智能熔断(Success Rate)、DNS无损更新、TLS会话复用、双向心跳、负载再平衡
# 修复：恢复完整菜单、路由路径修正、监听地址逻辑修正、热更新逻辑优化

# ===== 全局配置 =====
DOWNLOAD_SERVER="666ATM666.x9y.club"
ENVOY_BIN="/usr/sbin/envoy"
ENVOY_CONFIG="/etc/envoy/envoy.yaml"
SYSCTL_CONFIG="/etc/sysctl.d/99-envoy-optimized.conf"
TOOL_PATH="/usr/local/bin/envoy_tool"

# 默认值
DEFAULT_BW_MBPS=1000
DEFAULT_RTT_MS=200 
# 最小值 16MB，最大值 2GB (INT32_MAX)
MIN_WINDOW_SIZE=$((16 * 1024 * 1024))
MAX_WINDOW_SIZE=$((2 * 1024 * 1024 * 1024 - 1))

# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'

# ===== 辅助函数 =====
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_err() { echo -e "${RED}[ERROR]${NC} $1" >&2; }

check_root() {
    if [ "$EUID" -ne 0 ]; then
        log_err "请使用 root 运行"
        exit 1
    fi
}

download_file() {
    local url="$1"
    local dest="$2"
    log_info "正在下载 $url -> $dest"
    if command -v curl >/dev/null 2>&1; then
        curl -fsSL "$url" -o "$dest"
    elif command -v wget >/dev/null 2>&1; then
        wget -qO "$dest" "$url"
    else
        log_err "未找到 curl 或 wget"
        return 1
    fi
    if [ ! -s "$dest" ]; then
        log_err "下载失败或文件为空: $dest"
        rm -f "$dest"
        return 1
    fi
}

check_install_cron() {
    # 检查并安装依赖 (cron)
    local install_cmd=""
    local pkg_manager=""
    
    if command -v apt-get >/dev/null 2>&1; then
        pkg_manager="apt-get"
    elif command -v yum >/dev/null 2>&1; then
        pkg_manager="yum"
    fi
    
    if ! command -v crontab >/dev/null 2>&1; then
        if [ "$pkg_manager" = "apt-get" ]; then
            install_cmd="$install_cmd cron"
        elif [ "$pkg_manager" = "yum" ]; then
            install_cmd="$install_cmd crontabs"
        fi
    fi
    
    if [ -n "$install_cmd" ]; then
        log_info "安装依赖: $install_cmd"
        if [ "$pkg_manager" = "apt-get" ]; then
            apt-get update && apt-get install -y $install_cmd
        elif [ "$pkg_manager" = "yum" ]; then
            yum install -y $install_cmd
        fi
        
        # 启动 cron 服务 (兼容不同服务名)
        if command -v systemctl >/dev/null 2>&1; then
            systemctl enable cron 2>/dev/null || systemctl enable crond 2>/dev/null
            systemctl start cron 2>/dev/null || systemctl start crond 2>/dev/null
        fi
    fi
}

# 智能更新函数：仅当配置内容(忽略版本号)变更时才覆盖文件，实现精准热加载
safe_update_config() {
    local new_file="$1"
    local target_file="$2"
    local resource_name="$3"
    
    if [ ! -f "$target_file" ]; then
        mv -f "$new_file" "$target_file"
        log_info "${resource_name} 初始化完成"
        return
    fi

    # 忽略第一行 (version_info) 进行内容比对
    # 如果内容一致，则不更新文件，避免触发 Envoy 不必要的重载
    if cmp -s <(tail -n +2 "$new_file") <(tail -n +2 "$target_file"); then
        rm -f "$new_file"
        # log_info "${resource_name} 无变更，跳过重载"
    else
        mv -f "$new_file" "$target_file"
        log_info "${resource_name} 已更新，触发热加载"
    fi
}

# ===== 核心配置应用逻辑 (复用) =====
_apply_envoy_config() {
    local USER_BW="$1"
    local tunnel_num="$2"
    local config_type="$3"
    local pass_real_ip="$4"

    local mode="" envoy_txt_url="" crt_url="" key_url=""
    case $config_type in
        1) mode="in"
           envoy_txt_url="https://${DOWNLOAD_SERVER}/tunnel/tunnel${tunnel_num}/in/envoy.txt"
           crt_url="https://${DOWNLOAD_SERVER}/tunnel/ljfxz/server.crt"
           key_url="https://${DOWNLOAD_SERVER}/tunnel/ljfxz/server.key" ;;
        2) mode="out"
           envoy_txt_url="https://${DOWNLOAD_SERVER}/tunnel/tunnel${tunnel_num}/out/envoy.txt"
           crt_url="https://${DOWNLOAD_SERVER}/tunnel/ljfxz/server.crt"
           key_url="https://${DOWNLOAD_SERVER}/tunnel/ljfxz/server.key" ;;
        3) mode="single"
           envoy_txt_url="https://${DOWNLOAD_SERVER}/tunnel/tunnel${tunnel_num}/out/envoy.txt"
           crt_url="https://${DOWNLOAD_SERVER}/tunnel/ljfxz/server.crt"
           key_url="https://${DOWNLOAD_SERVER}/tunnel/ljfxz/server.key" ;;
        *) log_err "无效的配置类型"; return 1 ;;
    esac

    # 0. 环境自检与依赖修复
    # 确保管理脚本是最新的 (用于热更新回调)
    if [ -f "$0" ]; then
        if [ ! -f "$TOOL_PATH" ] || ! cmp -s "$0" "$TOOL_PATH"; then
            cp -f "$0" "$TOOL_PATH"
            chmod +x "$TOOL_PATH"
        fi
    fi
    # 确保 cron 服务已安装 (用于热更新定时任务)
    check_install_cron

    # 下载配置文件
    download_file "$envoy_txt_url" "/etc/envoy/envoy.txt" || return 1
    # 转换格式 (DOS to Unix)
    sed -i 's/\r$//' /etc/envoy/envoy.txt 2>/dev/null
    
    # 证书下载
    mkdir -p /etc/envoy/ssl
    download_file "$crt_url" "/etc/envoy/ssl/server.crt" || return 1
    download_file "$key_url" "/etc/envoy/ssl/server.key" || return 1
    
    # Session Ticket
    if [ ! -f /etc/envoy/ssl/ticket.key ]; then
        log_info "生成 TLS 会话复用密钥..."
        dd if=/dev/urandom of=/etc/envoy/ssl/ticket.key bs=80 count=1 2>/dev/null
        chmod 600 /etc/envoy/ssl/ticket.key
    fi
    
    calculate_params "$USER_BW"
    
    # 生成配置 (File-based XDS 模式)
    generate_envoy_yaml "$mode" "/etc/envoy/envoy.txt" "$pass_real_ip"
    
    # 智能启动/重载
    if command -v systemctl >/dev/null 2>&1; then
        if systemctl is-active --quiet envoy; then
            log_info "Envoy 服务正在运行，配置已通过 File-based XDS 自动热加载。"
        else
            log_info "Envoy 服务未运行，正在启动..."
            systemctl start envoy
            sleep 2
            systemctl is-active --quiet envoy && log_info "Envoy 启动成功！"
        fi
    fi
}

# ===== 1. 智能 BDP 算法 (带宽延迟积) =====
calculate_params() {
    local bw_mbps="$1"
    local rtt_ms="${2:-$DEFAULT_RTT_MS}"
    
    # BDP = 带宽(bps) * RTT(s) / 8
    # 丢包补偿系数: 2.5 (应对公网波动和丢包)
    # 使用 awk 进行浮点运算并四舍五入取整
    local bdp_bytes=$(echo "$bw_mbps $rtt_ms" | awk '{printf "%.0f", $1 * 1000000 * $2 / 1000 / 8 * 2.5}')
    
    # 边界限制
    if [ "$bdp_bytes" -lt "$MIN_WINDOW_SIZE" ]; then bdp_bytes=$MIN_WINDOW_SIZE; fi
    if [ "$bdp_bytes" -gt "$MAX_WINDOW_SIZE" ]; then bdp_bytes=$MAX_WINDOW_SIZE; fi
    
    # HTTP/2 窗口设置
    ENV_STREAM_WINDOW="$bdp_bytes"
    ENV_CONN_WINDOW=$(echo "$bdp_bytes" | awk '{printf "%.0f", $1 * 2}')
    
    if [ "$ENV_CONN_WINDOW" -gt "$MAX_WINDOW_SIZE" ]; then ENV_CONN_WINDOW=$MAX_WINDOW_SIZE; fi
    
    # 内核缓冲区限制: 必须大于等于 BDP
    ENV_PER_CONN_BUFFER="$ENV_CONN_WINDOW"

    log_info "极致优化参数 (带宽: ${bw_mbps}Mbps, RTT: ${rtt_ms}ms, 补偿系数: 2.5):"
    log_info "  - Stream Window: $((ENV_STREAM_WINDOW / 1024 / 1024)) MB"
    log_info "  - Conn Window:   $((ENV_CONN_WINDOW / 1024 / 1024)) MB"
    log_info "  - Buffer Limit:  $((ENV_PER_CONN_BUFFER / 1024 / 1024)) MB"
}

# ===== 2. 配置生成器 (Envoy v1.37.0 标准) =====

get_socket_options() {
    cat <<EOF
  socket_options:
  - level: 6 # IPPROTO_TCP
    name: 1  # TCP_NODELAY
    int_value: 1
    state: STATE_PREBIND
  - level: 1 # SOL_SOCKET
    name: 9  # SO_KEEPALIVE
    int_value: 1
    state: STATE_PREBIND
  - level: 6 # IPPROTO_TCP
    name: 4  # TCP_KEEPIDLE
    int_value: 60
    state: STATE_PREBIND
  - level: 6 # IPPROTO_TCP
    name: 5  # TCP_KEEPINTVL
    int_value: 10
    state: STATE_PREBIND
  - level: 6 # IPPROTO_TCP
    name: 6  # TCP_KEEPCNT
    int_value: 5
    state: STATE_PREBIND
  - level: 6 # IPPROTO_TCP
    name: 23 # TCP_FASTOPEN
    int_value: 4096
    state: STATE_PREBIND
  - level: 6 # IPPROTO_TCP
    name: 18 # TCP_USER_TIMEOUT
    int_value: 30000 # 30s
    state: STATE_PREBIND
EOF
}

get_downstream_tls() {
    local alpn="${1:-h2,http/1.1}"
    # 使用 SDS (Secret Discovery Service) 配置
    # 引用名为 "server_cert" 的 Secret，而不是直接指定文件路径
    cat <<EOF
    transport_socket:
      name: envoy.transport_sockets.tls
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
        common_tls_context:
          tls_certificate_sds_secret_configs:
          - name: "server_cert"
            sds_config:
              path_config_source:
                path: "/etc/envoy/sds.yaml"
          alpn_protocols: [${alpn}]
          tls_params:
            tls_minimum_protocol_version: TLSv1_2
            tls_maximum_protocol_version: TLSv1_3
        session_ticket_keys:
          keys:
          - filename: "/etc/envoy/ssl/ticket.key"
EOF
}

get_upstream_tls() {
    local sni="$1"
    cat <<EOF
  transport_socket:
    name: envoy.transport_sockets.tls
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
      sni: "${sni}"
      common_tls_context:
        alpn_protocols: ["h2"]
        tls_params:
            tls_minimum_protocol_version: TLSv1_2
            tls_maximum_protocol_version: TLSv1_3
EOF
}

generate_envoy_yaml() {
    local mode="$1" # in, out, single
    local listeners_file="$2"
    local pass_real_ip="${3:-y}"
    
    log_info "正在生成 Envoy 配置 (Mode: $mode, RealIP: $pass_real_ip)..."

    # 生成主配置文件 envoy.yaml (只需生成一次)
    local MAIN_CONFIG="/etc/envoy/envoy.yaml"
    cat > "$MAIN_CONFIG" <<EOF
node:
  cluster: envoy_cluster
  id: envoy_node_1

overload_manager:
  resource_monitors:
  - name: "envoy.resource_monitors.global_downstream_max_connections"
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.resource_monitors.downstream_connections.v3.DownstreamConnectionsConfig
      max_active_downstream_connections: 2147483647

dynamic_resources:
  lds_config:
    resource_api_version: V3
    initial_fetch_timeout: 0s
    path_config_source:
      path: "/etc/envoy/lds.yaml"
  cds_config:
    resource_api_version: V3
    initial_fetch_timeout: 0s
    path_config_source:
      path: "/etc/envoy/cds.yaml"
EOF

    # 生成 SDS 配置文件 (sds.yaml) - 实现官方标准的证书热加载
    # 机制: 使用 inline_string 将证书内容直接嵌入 YAML
    # 优势: 绕过文件系统监听的复杂性，内容变更即触发热更新
    local SDS_CONFIG="/etc/envoy/sds.yaml"
    local timestamp=$(date +%s)
    # 确保证书文件存在，避免 sed 报错
    touch /etc/envoy/ssl/server.crt /etc/envoy/ssl/server.key
    local cert_content=$(sed 's/^/        /' /etc/envoy/ssl/server.crt)
    local key_content=$(sed 's/^/        /' /etc/envoy/ssl/server.key)

    cat > "$SDS_CONFIG" <<EOF
version_info: "${timestamp}"
resources:
- "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
  name: "server_cert"
  tls_certificate:
    certificate_chain:
      inline_string: |
${cert_content}
    private_key:
      inline_string: |
${key_content}
EOF

    # 生成 LDS 配置文件 (lds.yaml)
    local LDS_CONFIG="/etc/envoy/lds.yaml"
    local LDS_TMP="/etc/envoy/lds.yaml.tmp"
    local timestamp=$(date +%s)
    
    cat > "$LDS_TMP" <<EOF
version_info: "${timestamp}"
resources:
EOF

    while read -r l_ip l_port t_host t_port; do
        [[ -z "$l_ip" || "$l_ip" =~ ^# ]] && continue
        
        # 监听地址逻辑
        local current_listen_ip="${l_ip}"
        local ipv4_compat=""
        
        # 特殊标记 "ljfxz" 表示双栈监听 (v4+v6)
        # 其他情况保持原样 (v4 Only 或 v6 Only)
        if [ "$current_listen_ip" == "ljfxz" ]; then
            current_listen_ip="::"
            ipv4_compat="ipv4_compat: true"
        fi

        local cluster_name="cluster_${l_port}"
        local use_remote_address="false"
        local xff_hops="0"
        local request_headers_to_add=""
        
    if [ "$mode" == "in" ] || [ "$mode" == "single" ]; then
        # 入口/单层: 使用远端地址，不信任 XFF
        use_remote_address="true"
        xff_hops="0"
        
        # 添加入口特定的请求头 (传递真实 IP 给后端)
        # 仅在入口模式，或单层且用户确认传递时添加
        if [ "$mode" == "in" ] || { [ "$mode" == "single" ] && [ "$pass_real_ip" == "y" ]; }; then
            # 注意：这里的缩进必须严格匹配 YAML 结构 (14个空格，与下方的 route 对齐)
            request_headers_to_add=$(cat <<EOF
              request_headers_to_add:
              - header:
                  key: "X-Real-IP"
                  value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
                append_action: OVERWRITE_IF_EXISTS_OR_ADD
              - header:
                  key: "X-Forwarded-For"
                  value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
                append_action: OVERWRITE_IF_EXISTS_OR_ADD
EOF
)
        fi
    elif [ "$mode" == "out" ]; then
        # 出口: 必须开启 use_remote_address=true 才能正确解析上一跳的 XFF
        # 并设置 trusted_hops=1 来信任入口发来的 XFF
        use_remote_address="true"
        xff_hops="1"
        request_headers_to_add=$(cat <<EOF
              request_headers_to_add:
              - header:
                  key: "X-Real-IP"
                  value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
                append_action: OVERWRITE_IF_EXISTS_OR_ADD
              - header:
                  key: "X-Forwarded-For"
                  value: "%DOWNSTREAM_REMOTE_ADDRESS_WITHOUT_PORT%"
                append_action: OVERWRITE_IF_EXISTS_OR_ADD
EOF
)
    fi

        cat >> "$LDS_TMP" <<EOF
- "@type": type.googleapis.com/envoy.config.listener.v3.Listener
  name: listener_${l_port}
  address:
    socket_address:
      address: "${current_listen_ip}"
      port_value: ${l_port}
      ${ipv4_compat}
  per_connection_buffer_limit_bytes: ${ENV_PER_CONN_BUFFER}
  enable_reuse_port: true
$(get_socket_options)
    
  listener_filters:
  - name: envoy.filters.listener.tls_inspector
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector

  filter_chains:
  - filters:
    - name: envoy.filters.network.http_connection_manager
      typed_config:
        "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
        stat_prefix: "ingress_http_${l_port}"
        # codec_type: AUTO (自动检测 HTTP/1.1 或 HTTP/2)
        # v1.37.0 推荐使用 AUTO 以获得更好的兼容性 (支持 ALPN)
        codec_type: AUTO
        use_remote_address: ${use_remote_address}
        xff_num_trusted_hops: ${xff_hops}
          
        # 流超时设置 (0s = 无限，防止断流)
        # stream_idle_timeout: 流空闲超时。如果流已建立但没有数据传输超过此时长，流会被关闭。
        # 设置为 0s 禁用此超时，确保 gRPC 长连接心跳或低频数据流不被切断。
        stream_idle_timeout: 0s
          
        # request_timeout: 整个请求的超时时间。对于 gRPC 流式传输，这必须是 0s (无限)，否则流会在固定时间后强制中断。
        request_timeout: 0s
          
        common_http_protocol_options:
          idle_timeout: 3600s # 连接空闲超时 (Connection Idle Timeout)
                              # 当连接上没有任何活跃流时，超过此时间连接将被关闭。
                              # 设置为 1小时 (3600s) 是为了回收僵尸连接，不影响活跃连接。
          max_headers_count: 200
          max_connection_duration: 86400s # 24小时 (下游连接最大持续时间，用于负载再平衡)

        http2_protocol_options:
          initial_stream_window_size: ${ENV_STREAM_WINDOW}
          initial_connection_window_size: ${ENV_CONN_WINDOW}
          max_concurrent_streams: 2147483647
          override_stream_error_on_invalid_http_message: true
          connection_keepalive:
            interval: 30s
            timeout: 10s
            connection_idle_interval: 15s
            
        route_config:
          name: local_route
          virtual_hosts:
          - name: local_service
            retry_policy:
              retry_on: "connect-failure,refused-stream,gateway-error,deadline-exceeded,stream-idle-timeout,unavailable,reset"
              num_retries: 3
              retry_back_off:
                base_interval: 0.1s
                max_interval: 1s
            domains: ["*"]
            routes:
            - match: { prefix: "/ljfxz" }
${request_headers_to_add}
              route:
                cluster: ${cluster_name}
                timeout: 0s
                max_stream_duration:
                  max_stream_duration: 0s
                  grpc_timeout_header_max: 0s
            - match: { prefix: "/" }
              direct_response:
                status: 200
                body: { inline_string: '{"status":"ok"}' }
                  
        http_filters:
        - name: envoy.filters.http.router
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
            suppress_envoy_headers: true
              
EOF
        # 所有模式强制 TLS 监听
        get_downstream_tls "h2" >> "$LDS_TMP"
        
    done < "$listeners_file"
    


    # 生成 CDS 配置文件 (cds.yaml)
    local CDS_CONFIG="/etc/envoy/cds.yaml"
    local CDS_TMP="/etc/envoy/cds.yaml.tmp"
    
    cat > "$CDS_TMP" <<EOF
version_info: "${timestamp}"
resources:
EOF

    while read -r l_ip l_port t_host t_port; do
        [[ -z "$l_ip" || "$l_ip" =~ ^# ]] && continue
        local cluster_name="cluster_${l_port}"
        
        cat >> "$CDS_TMP" <<EOF
- "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster
  name: ${cluster_name}
  connect_timeout: 20s
  
  # v1.37.0 Standard: Use cluster_type with DnsCluster extension
  # Replaces deprecated type: STRICT_DNS and related fields
  cluster_type:
    name: envoy.clusters.dns
    typed_config:
      "@type": type.googleapis.com/envoy.extensions.clusters.dns.v3.DnsCluster
      dns_lookup_family: ALL
      respect_dns_ttl: true
      dns_refresh_rate: 5s
      
  per_connection_buffer_limit_bytes: ${ENV_PER_CONN_BUFFER}
  lb_policy: LEAST_REQUEST
    
  common_lb_config:
    # 设为 false 以避免 DNS 列表变动时强制断开旧连接 (实现优雅排空)
    # 对于多 IP 负载均衡域名，这能防止因 DNS 抖动导致的连接中断
    close_connections_on_host_set_change: false
      
  health_checks:
    - timeout: 5s
      interval: 10s
      unhealthy_threshold: 2
      healthy_threshold: 1
      tcp_health_check: {}
    
  outlier_detection:
    # 硬伤检测：连续 5 次 TCP 连接失败/超时就踢出 (Local Origin Failure)
    consecutive_local_origin_failure: 5
    split_external_local_origin_errors: true
      
    # 软伤检测 (智能熔断)：基于 Success Rate 的动态统计
    # 只有当节点的成功率显著低于集群平均值 (偏离 1.9 个标准差) 时才踢出
    # 注意：此功能需要至少 2 个节点才能生效 (success_rate_minimum_hosts: 2)
    # 对于单 IP 域名，此项会自动禁用，仅保留硬伤检测，完美兼容单/多 IP 场景
    success_rate_minimum_hosts: 2
    success_rate_request_volume: 50
    success_rate_stdev_factor: 1900 # 1900 = 1.9 stdev
      
    # 基础保护：防止雪崩
    interval: 10s
    base_ejection_time: 30s
    max_ejection_percent: 50 # 最多踢出 50%，保留一半算力(Panic Mode)
        
  upstream_connection_options:
    tcp_keepalive:
      keepalive_probes: 5
      keepalive_time: 60
      keepalive_interval: 10
    
  typed_extension_protocol_options:
    envoy.extensions.upstreams.http.v3.HttpProtocolOptions:
      "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions
      explicit_http_config:
        http2_protocol_options:
          initial_stream_window_size: ${ENV_STREAM_WINDOW}
          initial_connection_window_size: ${ENV_CONN_WINDOW}
          max_concurrent_streams: 2147483647
          connection_keepalive:
            interval: 30s
            timeout: 10s
            connection_idle_interval: 15s
      common_http_protocol_options:
        idle_timeout: 3600s
        max_connection_duration: 86400s # 24小时
EOF
    if [ "$mode" == "in" ]; then
         get_upstream_tls "$t_host" >> "$CDS_TMP"
    fi
    
    cat >> "$CDS_TMP" <<EOF
  circuit_breakers:
    thresholds:
    - priority: DEFAULT
      max_connections: 4294967295
      max_connection_pools: 4294967295
      max_pending_requests: 4294967295
      max_requests: 4294967295
      max_retries: 4294967295
      track_remaining: false
    - priority: HIGH
      max_connections: 4294967295
      max_connection_pools: 4294967295
      max_pending_requests: 4294967295
      max_requests: 4294967295
      max_retries: 4294967295
      track_remaining: false
  load_assignment:
    cluster_name: ${cluster_name}
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: ${t_host}
              port_value: ${t_port}
EOF
    done < "$listeners_file"
    
    # 智能更新 CDS (仅当 Clusters 实际变更时重载)
    # 关键优化: 必须先更新 CDS，再更新 LDS。
    # 因为 LDS 引用了 Cluster，如果先更新 LDS 而 CDS 还没更新，
    # Envoy 可能会报错 "unknown cluster" 并拒绝 LDS 更新。
    safe_update_config "$CDS_TMP" "$CDS_CONFIG" "CDS(集群)"
    safe_update_config "$LDS_TMP" "$LDS_CONFIG" "LDS(监听器)"

    # 生成热更新脚本 (仅当变量存在时，避免递归调用时覆盖)
    if [ -n "$envoy_txt_url" ]; then
        cat > "/usr/local/bin/update_envoy_config.sh" <<EOF
#!/bin/bash
# Envoy 自动热更新脚本 (File-based Dynamic Configuration)

ENVOY_TXT_URL="$envoy_txt_url"
CRT_URL="$crt_url"
KEY_URL="$key_url"
PASS_REAL_IP="$pass_real_ip"
MODE="$mode"
USER_BW="$USER_BW"

# 下载新配置到临时文件
if ! curl -fsSL "\$ENVOY_TXT_URL" -o /tmp/envoy_new.txt; then
    exit 1
fi
sed -i 's/\r$//' /tmp/envoy_new.txt

# 标记配置是否变更
CONFIG_CHANGED=0
if ! cmp -s /tmp/envoy_new.txt /etc/envoy/envoy.txt; then
    CONFIG_CHANGED=1
fi

# 1. 始终检查证书更新 (防止证书过期但配置未变的情况)
# 下载新证书到临时文件
if ! curl -fsSL "\$CRT_URL" -o /tmp/server.crt.new || ! curl -fsSL "\$KEY_URL" -o /tmp/server.key.new; then
    # 如果证书下载失败但配置变了，依然继续更新配置，不强制退出
    :
else
    # 对比证书差异 (如果新证书与旧证书不同，则更新)
    if ! cmp -s /tmp/server.crt.new /etc/envoy/ssl/server.crt || ! cmp -s /tmp/server.key.new /etc/envoy/ssl/server.key; then
        mv /tmp/server.crt.new /etc/envoy/ssl/server.crt
        mv /tmp/server.key.new /etc/envoy/ssl/server.key
        chmod 644 /etc/envoy/ssl/server.crt
        chmod 600 /etc/envoy/ssl/server.key
        
        # 证书更新后，即使配置没变，也需要触发 Envoy 热加载
        # SDS 模式：将新证书内容写入 sds.yaml (inline_string) 以触发热更新
        # 注意：使用 inline_string 确保内容变更能被 Envoy 捕捉
        CERT_CONTENT=\$(sed 's/^/        /' /etc/envoy/ssl/server.crt)
        KEY_CONTENT=\$(sed 's/^/        /' /etc/envoy/ssl/server.key)
        
        # 1. 写入临时文件，避免写入过程中被 Envoy 读取导致配置不完整
        TIMESTAMP=\$(date +%s)
        cat > /etc/envoy/sds.yaml.tmp <<INNEREOF
version_info: "\${TIMESTAMP}"
resources:
- "@type": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret"
  name: "server_cert"
  tls_certificate:
    certificate_chain:
      inline_string: |
\${CERT_CONTENT}
    private_key:
      inline_string: |
\${KEY_CONTENT}
INNEREOF
        # 2. 设置权限 (与私钥保持一致，因含敏感信息)
        chmod 600 /etc/envoy/sds.yaml.tmp
        # 3. 原子移动 (Atomic Move) 触发热加载
        mv -f /etc/envoy/sds.yaml.tmp /etc/envoy/sds.yaml
        
        # SDS 机制下不需要重载主配置，CONFIG_CHANGED=0 即可
        # 但为了日志记录或其他依赖，我们可以记录一下
        echo "证书已更新并触发 SDS 热加载"
    else
        rm -f /tmp/server.crt.new /tmp/server.key.new
    fi
fi

# 如果配置没变，则退出 (证书更新由 SDS 处理，无需重载 LDS)
if [ "\$CONFIG_CHANGED" -eq 0 ]; then
    rm -f /tmp/envoy_new.txt
    exit 0
fi

# 2. 更新 envoy.txt (如果有变)
mv -f /tmp/envoy_new.txt /etc/envoy/envoy.txt

# 3. 重新生成 LDS/CDS 配置文件 (原子替换触发热重载)
# 调用主脚本中的生成函数 (需要主脚本支持 export 或在此处内联逻辑，为简化直接调用主脚本的 generate 功能)

$TOOL_PATH generate_yaml "\$MODE" "/etc/envoy/envoy.txt" "\$PASS_REAL_IP" "\$USER_BW" >/dev/null 2>&1

EOF
        chmod +x "/usr/local/bin/update_envoy_config.sh"

        if command -v systemctl >/dev/null 2>&1; then
            systemctl enable cron 2>/dev/null || systemctl enable crond 2>/dev/null
            systemctl start cron 2>/dev/null || systemctl start crond 2>/dev/null
            
            # 自动添加定时任务 (每分钟执行一次)
            local cron_job="* * * * * /usr/local/bin/update_envoy_config.sh >/dev/null 2>&1"
            if ! crontab -l 2>/dev/null | grep -q "update_envoy_config.sh"; then
                (crontab -l 2>/dev/null; echo "$cron_job") | crontab -
                log_info "已添加证书自动热更新定时任务"
            fi
        fi
    fi
}

# 新增一个隐藏的 CLI 命令，用于脚本内部调用生成 YAML
# 在 main 函数中处理



# ===== 功能菜单函数 =====

envoy_install() {
    log_info "安装 Envoy..."
    
    # 安装前先停止服务，防止文件占用或冲突
    systemctl stop envoy 2>/dev/null

    # 强制下载最新版 Envoy (移除条件判断)
    mkdir -p /etc/envoy/ssl
    download_file "https://${DOWNLOAD_SERVER}/tools/envoy" "$ENVOY_BIN"
    chmod +x "$ENVOY_BIN"
    
    # 远程下载 service 文件
    download_file "https://${DOWNLOAD_SERVER}/tools/envoy.service" "/usr/lib/systemd/system/envoy.service" || return 1
    systemctl daemon-reload
    systemctl enable envoy
    
    # 安装时自动执行系统优化
    system_optimize
    
    # 安装管理脚本
    if [ -f "$0" ]; then
        cp "$0" "$TOOL_PATH"
        chmod +x "$TOOL_PATH"
        log_info "管理脚本已安装到 $TOOL_PATH"
    fi
    
    log_info "Envoy 安装完成"
}

envoy_manage() {
    echo "1.启动 2.停止 3.重启 4.状态"
    read -p "选择: " c
    case $c in
        1) systemctl start envoy ;;
        2) systemctl stop envoy ;;
        3) systemctl restart envoy ;;
        4) systemctl status envoy --no-pager ;;
    esac
}

firewall_remove() {
    systemctl stop firewalld 2>/dev/null
    systemctl disable firewalld 2>/dev/null
    ufw disable 2>/dev/null
    log_info "防火墙已禁用"
}

envoy_monitor() {
    check_install_cron
    download_file "https://${DOWNLOAD_SERVER}/tools/checkenvoy.sh" "/usr/local/bin/checkenvoy.sh" || return 1
    chmod +x /usr/local/bin/checkenvoy.sh
    (crontab -l 2>/dev/null | grep -v checkenvoy; echo "* * * * * /usr/local/bin/checkenvoy.sh >/dev/null 2>&1") | crontab -
    log_info "定时检测已设置"
}

soga_setup() {
    local node_id="$1"
    local soga_key="$2"
    local webapi_domain="$3"
    local webapi_key="$4"

    # 如果没有传参，进入交互模式
    if [ -z "$node_id" ]; then
        read -p "节点ID: " node_id
        read -p "授权码: " soga_key
        read -p "域名: " webapi_domain
        read -p "webapi_key: " webapi_key
    fi

    bash <(curl -Ls https://raw.githubusercontent.com/vaxilu/soga/master/install.sh) || return 1
    cat > /etc/soga/soga.conf <<EOF
type=xboard
server_type=vless
node_id=${node_id}
soga_key=${soga_key}
api=webapi
webapi_url=https://${webapi_domain}
webapi_key=${webapi_key}
proxy_protocol=false
user_conn_limit=0
user_speed_limit=0
user_tcp_limit=0
node_speed_limit=0
check_interval=60
submit_interval=60
forbidden_bit_torrent=true
log_level=info
auto_update=true
force_close_ssl=true
EOF
    soga restart && log_info "soga 配置完成并重启" || log_err "soga 重启失败"
}

setup_ddns() {
    local cf_key="$1"
    local cf_user="$2"
    local cf_zone="$3"
    local cf_record="$4"
    local record_type="$5"

    log_info "正在配置 Cloudflare DDNS..."
    
    # 检查并安装 cron 依赖
    check_install_cron
    
    # 检查并安装 curl (ddns 还需要 curl)
    if ! command -v curl >/dev/null 2>&1; then
        log_info "安装依赖: curl"
        if command -v apt-get >/dev/null 2>&1; then
            apt-get update && apt-get install -y curl
        elif command -v yum >/dev/null 2>&1; then
            yum install -y curl
        fi
    fi

    # 如果没有传参，进入交互模式
    if [ -z "$cf_key" ]; then
        read -p "请输入 Cloudflare API Key (Global Key): " cf_key
        read -p "请输入 Cloudflare 邮箱: " cf_user
        read -p "请输入主域名 (例如 example.com): " cf_zone
        read -p "请输入解析记录名 (例如 ddns): " cf_record
        echo "解析类型: 1)IPv4(A) 2)IPv6(AAAA)"
        read -p "选择 [1-2]: " record_type_choice
        record_type="A"
        [ "$record_type_choice" = "2" ] && record_type="AAAA"
    else
        # CLI 模式下的类型转换
        if [ "$record_type" = "1" ]; then
            record_type="A"
        elif [ "$record_type" = "2" ]; then
            record_type="AAAA"
        fi
        # 如果用户直接传了 A 或 AAAA，则保持原样
    fi

    # 下载并配置 DDNS 脚本
    local ddns_script="/usr/local/bin/cf-ddns.sh"
    download_file "https://raw.githubusercontent.com/yulewang/cloudflare-api-v4-ddns/master/cf-v4-ddns.sh" "$ddns_script" || return 1
    chmod +x "$ddns_script"

    # 修复环境变量缺失问题 (如 HOME)
    # 在脚本顶部添加 export HOME=/root 如果未定义
    if ! grep -q "export HOME=" "$ddns_script"; then
        sed -i '2i export HOME=${HOME:-/root}' "$ddns_script"
    fi

    # 创建定时任务
    local cron_cmd="$ddns_script -k $cf_key -u $cf_user -h $cf_record -z $cf_zone -t $record_type >/dev/null 2>&1"
    (crontab -l 2>/dev/null | grep -v "cf-ddns.sh"; echo "* * * * * $cron_cmd") | crontab -
    
    # 立即执行一次
    log_info "正在执行第一次同步..."
    # 手动设置 HOME 环境变量以防万一
    export HOME=${HOME:-/root}
    $ddns_script -k "$cf_key" -u "$cf_user" -h "$cf_record" -z "$cf_zone" -t "$record_type" -f true >/dev/null 2>&1
    
    if [ $? -eq 0 ]; then
        log_info "DDNS 配置成功！每分钟自动更新。"
    else
        log_err "DDNS 同步失败，请检查 API Key 和网络。"
    fi
}

system_optimize() {
    log_info "正在应用极致系统优化 (BBR + Kernel)..."
    
    # 直接写入专用配置文件
    cat > "$SYSCTL_CONFIG" <<EOF
# --- 系统级限制 ---
fs.file-max = 2097152              # 系统最大文件打开数 (200w)
net.core.somaxconn = 65535         # 监听队列长度 (全连接队列)
net.ipv4.tcp_max_syn_backlog = 819200 # SYN 队列长度 (半连接队列)

# --- TCP 缓冲区 (适配大带宽) ---
net.core.rmem_max = 134217728      # 接收缓冲区最大值 (128MB)
net.core.wmem_max = 134217728      # 发送缓冲区最大值 (128MB)
net.ipv4.tcp_rmem = 4096 87380 134217728 # 自动调整范围
net.ipv4.tcp_wmem = 4096 65536 134217728 # 自动调整范围

# --- TCP 连接管理 ---
net.ipv4.tcp_max_tw_buckets = 2000000 # 最大 TIME_WAIT 数量 (防报错)
net.ipv4.tcp_tw_reuse = 1             # 允许重用 TIME_WAIT socket
net.ipv4.tcp_fin_timeout = 15         # 缩短 FIN_WAIT_2 时间 (加快回收)
net.ipv4.tcp_slow_start_after_idle = 0 # 禁用空闲后慢启动 (防降速)

# --- Keepalive (防死连接) ---
net.ipv4.tcp_keepalive_time = 60      # 60秒无数据开始探测
net.ipv4.tcp_keepalive_intvl = 10     # 探测间隔 10秒
net.ipv4.tcp_keepalive_probes = 6     # 探测 6次无果即断开

# --- 协议栈优化 ---
net.ipv4.tcp_fastopen = 3             # 开启 TFO (Client+Server)
net.ipv4.tcp_mtu_probing = 1          # 开启 MTU 探测 (防黑洞)
net.ipv4.tcp_window_scaling = 1       # 开启窗口扩大因子
net.ipv4.ip_local_port_range = 1024 65535 # 扩大本地端口范围

# --- BBR 核心 ---
net.core.default_qdisc = fq           # 必须使用 fq 队列
net.ipv4.tcp_congestion_control = bbr # 开启 BBR 拥塞控制

# === 增强优化参数 (From tcp.sh Option 22) ===
# --- 网卡队列与调度 ---
net.core.netdev_max_backlog = 100000  # 网卡接收队列 (防丢包)
net.core.netdev_budget = 50000        # 软中断一次处理包数量
net.core.netdev_budget_usecs = 5000   # 软中断处理时间限制
net.core.rmem_default = 67108864      # 默认接收缓冲
net.core.wmem_default = 67108864      # 默认发送缓冲
net.core.optmem_max = 65536           # Socket 选项内存限制

# --- 安全与 ICMP 设置 ---
net.ipv4.icmp_echo_ignore_all = 0     # 允许 Ping
net.ipv4.icmp_echo_ignore_broadcasts = 1 # 禁止广播 Ping (防风暴)
net.ipv4.icmp_ignore_bogus_error_responses = 1 # 忽略错误 ICMP
net.ipv4.conf.all.accept_redirects = 0 # 禁用 ICMP 重定向 (安全)
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.default.rp_filter = 0   # 宽松的反向路径过滤
net.ipv4.conf.all.rp_filter = 0

# --- IPv6 设置 ---
net.ipv6.conf.all.disable_ipv6 = 0    # 开启 IPv6
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.lo.disable_ipv6 = 0
net.ipv6.conf.all.accept_ra = 2       # 接受路由广播
net.ipv6.conf.default.accept_ra = 2
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0

# --- 邻居表优化 (抗丢包关键) ---
net.ipv4.neigh.default.gc_thresh1 = 2048
net.ipv4.neigh.default.gc_thresh2 = 4096
net.ipv4.neigh.default.gc_thresh3 = 8192
net.ipv6.neigh.default.gc_thresh1 = 2048
net.ipv6.neigh.default.gc_thresh2 = 4096
net.ipv6.neigh.default.gc_thresh3 = 8192
net.ipv4.neigh.default.unres_qlen = 10000  # 未解析队列长度 (防突发流量丢包)
net.ipv6.neigh.default.unres_qlen = 10000

# --- 其他 TCP 细节 ---
net.ipv4.tcp_synack_retries = 1       # SYN-ACK 重试次数 (快速失败)
net.ipv4.tcp_syncookies = 1           # 开启 SYN Cookies (防 SYN Flood)
net.ipv4.tcp_rfc1337 = 0              # 禁用 TIME-WAIT Assassination 保护
net.ipv4.tcp_timestamps = 1           # 开启时间戳 (BBR 需要)
net.ipv4.udp_rmem_min = 8192          # UDP 缓冲区下限
net.ipv4.udp_wmem_min = 8192
net.ipv4.tcp_autocorking = 0          # 禁用自动粘包 (降低延迟)
net.ipv4.tcp_max_orphans = 262144     # 最大孤儿连接数 (防DDoS)
net.ipv4.tcp_orphan_retries = 0       # 加快回收孤儿连接 (防断流)
net.ipv4.tcp_retries2 = 5             # 已建立连接的重试次数
net.ipv4.tcp_early_retrans = 3        # 开启早期重传
net.ipv4.tcp_no_metrics_save = 0      # 缓存 TCP 指标
net.ipv4.tcp_ecn = 1                  # 开启 ECN (显式拥塞通知)
net.ipv4.tcp_ecn_fallback = 1
net.ipv4.tcp_frto = 0                 # 禁用 F-RTO

# --- 内存管理 ---
vm.swappiness = 1                     # 尽量不使用 Swap
vm.overcommit_memory = 1              # 允许内存超卖
kernel.pid_max = 64000                # 最大进程 ID
net.netfilter.nf_conntrack_max = 262144 # 连接追踪表大小
EOF

    sysctl --system
    echo always >/sys/kernel/mm/transparent_hugepage/enabled

    cat >'/etc/systemd/system.conf' <<EOF
[Manager]
#DefaultTimeoutStartSec=90s
DefaultTimeoutStopSec=30s
#DefaultRestartSec=100ms
DefaultLimitCORE=infinity
DefaultLimitNOFILE=infinity
DefaultLimitNPROC=infinity
DefaultTasksMax=infinity
EOF

    cat >'/etc/security/limits.conf' <<EOF
root     soft   nofile    1000000
root     hard   nofile    1000000
root     soft   nproc     unlimited
root     hard   nproc     unlimited
root     soft   core      unlimited
root     hard   core      unlimited
root     hard   memlock   unlimited
root     soft   memlock   unlimited
*     soft   nofile    1000000
*     hard   nofile    1000000
*     soft   nproc     unlimited
*     hard   nproc     unlimited
*     soft   core      unlimited
*     hard   core      unlimited
*     hard   memlock   unlimited
*     soft   memlock   unlimited
EOF

    sed -i '/ulimit -SHn/d' /etc/profile
    sed -i '/ulimit -SHu/d' /etc/profile
    echo "ulimit -SHn 1000000" >>/etc/profile

    if grep -q "pam_limits.so" /etc/pam.d/common-session; then
        :
    else
        sed -i '/required pam_limits.so/d' /etc/pam.d/common-session
        echo "session required pam_limits.so" >>/etc/pam.d/common-session
    fi
    systemctl daemon-reload
    log_info "系统优化和 BBR 配置完成！"
}

envoy_uninstall() {
    local force="$1"
    if [ "$force" != "-y" ] && [ "$force" != "force" ]; then
        read -p "确定要卸载 Envoy 吗? [y/N]: " confirm
        [[ "$confirm" != "y" && "$confirm" != "Y" ]] && return
    fi

    log_info "正在停止 Envoy 服务..."
    systemctl stop envoy 2>/dev/null
    systemctl disable envoy 2>/dev/null
    
    log_info "正在删除文件..."
    rm -f "$ENVOY_BIN"
    rm -f "/usr/lib/systemd/system/envoy.service"
    rm -rf "/etc/envoy"
    rm -f "$SYSCTL_CONFIG"
    rm -f "/etc/security/limits.d/envoy.conf"
    rm -f "/usr/local/bin/update_envoy_config.sh"
    rm -f "/usr/local/bin/checkenvoy.sh"
    rm -f "$TOOL_PATH"
    crontab -l 2>/dev/null | grep -v "update_envoy_config.sh" | grep -v "checkenvoy.sh" | crontab -
    
    systemctl daemon-reload
    log_info "Envoy 已卸载完成"
}

# ===== 核心配置流程 =====
envoy_tcp_config() {
    read -p "请输入带宽 (Mbps) [${DEFAULT_BW_MBPS}]: " USER_BW
    USER_BW=${USER_BW:-$DEFAULT_BW_MBPS}
    
    read -p "隧道编号: " tunnel_num
    if [[ ! "$tunnel_num" =~ ^[0-9]+$ ]]; then
        log_err "必须是数字"
        return 1
    fi
    
    echo "配置类型: 1)入口 2)出口 3)单层"
    read -p "选择 [1-3]: " config_type
    
    local pass_real_ip="y"
    if [ "$config_type" == "3" ]; then
        read -p "是否传递真实IP给后端? [Y/n]: " user_pass_ip
        [[ "$user_pass_ip" == "n" || "$user_pass_ip" == "N" ]] && pass_real_ip="n"
    fi

    # 调用核心函数 (无需 IP 版本参数)
    _apply_envoy_config "$USER_BW" "$tunnel_num" "$config_type" "$pass_real_ip"
}

show_menu() {
    clear
    echo "====================================="
    echo "  Envoy 极致性能代理 v3.0"
    echo "  大带宽|不断流|长连接|BBR"
    echo "====================================="
    echo "1. 安装 Envoy"
    echo "2. 生成配置"
    echo "3. 管理服务"
    echo "4. 删除防火墙"
    echo "5. 定时检测"
    echo "6. 对接 soga"
    echo "7. 设置 DDNS"
    echo "8. 系统优化 (BBR + 内核)"
    echo "9. 卸载 Envoy"
    echo "0. 退出"
    echo "====================================="
}


# ===== 主逻辑 =====
main() {
    check_root
    
    # 支持命令行参数直接调用
    if [ $# -gt 0 ]; then
        case "$1" in
            install)
                envoy_install
                exit $?
                ;;
            config)
                # 参数: config <带宽MB> <隧道号> <类型1-3> [是否传IP y/n]
                # 参数校验逻辑: 至少需要 3 个参数 (config, bandwidth, tunnel_num, type)
                if [ $# -lt 4 ]; then
                    echo "用法: $0 config <带宽MB> <隧道号> <配置类型1-3> [是否传IP y/n]"
                    echo "配置类型: 1:入口, 2:出口, 3:单层"
                    exit 1
                fi
                # 获取参数
                local user_bw="$2"
                local tunnel_num="$3"
                local config_type="$4"
                local pass_real_ip="${5:-y}" # 默认为 y
                
                # 调用核心配置函数
                _apply_envoy_config "$user_bw" "$tunnel_num" "$config_type" "$pass_real_ip"
                exit $?
                ;;
            ddns)
                # 参数: ddns <Key> <Email> <Zone> <Record> <Type>
                # 如果只有 "./envoy.sh ddns" (参数个数=1)，进入交互模式
                if [ $# -eq 1 ]; then
                    setup_ddns
                else
                    # 否则将参数传给函数
                    setup_ddns "$2" "$3" "$4" "$5" "$6"
                fi
                exit $?
                ;;
            soga)
                # 参数: soga <NodeID> <SogaKey> <Domain> <WebApiKey>
                if [ $# -eq 1 ]; then
                    soga_setup
                else
                    soga_setup "$2" "$3" "$4" "$5"
                fi
                exit $?
                ;;
            optimize)
                system_optimize
                exit $?
                ;;
            generate_yaml)
                # 隐藏命令，供热更新脚本内部调用
                # 参数: generate_yaml <mode> <txt_file> <pass_real_ip> <user_bw>
                # 参数校验: 需要至少 4 个参数 (generate_yaml, mode, txt_file, pass_real_ip)
                if [ $# -lt 4 ]; then
                     echo "Usage: $0 generate_yaml <mode> <txt_file> <pass_real_ip> <user_bw>"
                     exit 1
                fi
                local mode="$2"
                local listeners_file="$3"
                local pass_real_ip="$4"
                local user_bw="${5:-1000}"
                
                # 重新计算 BDP 参数
                calculate_params "$user_bw"
                # 生成配置
                generate_envoy_yaml "$mode" "$listeners_file" "$pass_real_ip"
                exit 0
                ;;
            uninstall)
                envoy_uninstall "$2"
                exit $?
                ;;
            *)
                echo "未知命令: $1"
                echo "可用命令: install, config, ddns, soga, optimize, uninstall"
                exit 1
                ;;
        esac
    fi

    # 无参数则进入交互式菜单
    while true; do
        show_menu
        read -p "选择 [0-9]: " choice
        case $choice in
            1) envoy_install ;;
            2) envoy_tcp_config ;;
            3) envoy_manage ;;
            4) firewall_remove ;;
            5) envoy_monitor ;;
            6) soga_setup ;;
            7) setup_ddns ;;
            8) system_optimize ;;
            9) envoy_uninstall ;;
            0) exit 0 ;;
            *) echo "无效选择" ;;
        esac
        read -p "按回车继续..."
    done
}

main "$@"
