mirror of
https://github.com/hotyue/IP-Sentinel.git
synced 2026-08-07 21:53:27 +08:00
feat(master): V3.1.3 面板重构,新增大区多级折叠目录与双列排版,平滑升级 DB 协议
This commit is contained in:
@@ -54,6 +54,11 @@ generate_signed_url() {
|
|||||||
}
|
}
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
|
|
||||||
|
# ================== [v3.1.3 核心: 数据库结构无损热升级] ==================
|
||||||
|
# 自动探测并增加 region 字段,屏蔽已存在的报错,保护老节点数据
|
||||||
|
db_exec "ALTER TABLE nodes ADD COLUMN region TEXT DEFAULT 'UNKNOWN';" 2>/dev/null
|
||||||
|
# ========================================================================
|
||||||
|
|
||||||
# --- 核心轮询循环 ---
|
# --- 核心轮询循环 ---
|
||||||
while true; do
|
while true; do
|
||||||
OFFSET=$(cat $OFFSET_FILE)
|
OFFSET=$(cat $OFFSET_FILE)
|
||||||
@@ -79,43 +84,56 @@ while true; do
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# ==========================================
|
# ==========================================
|
||||||
# 1. 节点注册通道 (v3.0.1 终极防注入补丁)
|
# 1. 节点注册通道 (V3.1.3 大区拓扑升级版)
|
||||||
# ==========================================
|
# ==========================================
|
||||||
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
|
if [[ "$TEXT" == *"#REGISTER#"* ]]; then
|
||||||
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
|
REG_LINE=$(echo "$TEXT" | grep "#REGISTER#" | head -n 1 | tr -d '\` ')
|
||||||
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
|
||||||
|
|
||||||
# 🛡️ 强制字符白名单过滤:物理抹杀所有 SQL 注入特殊字符
|
# V3.1.3 兼容性拆包: 判断是新版协议 (5个字段) 还是老版协议 (4个字段)
|
||||||
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-') # ChatID 只能是数字和负号
|
FIELD_COUNT=$(echo "$REG_LINE" | awk -F'|' '{print NF}')
|
||||||
NODE_NAME=$(echo "$RAW_NODE" | tr -cd 'a-zA-Z0-9_.-' | cut -c 1-30) # 节点名限字母数字横杠下划线
|
if [ "$FIELD_COUNT" -ge 5 ]; then
|
||||||
AGENT_IP=$(echo "$RAW_IP" | tr -cd 'a-zA-Z0-9.:\[\]-' | cut -c 1-50) # IP 格式限制
|
IFS='|' read -r MAGIC RAW_REGION RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
||||||
AGENT_PORT=$(echo "$RAW_PORT" | tr -cd '0-9' | cut -c 1-5) # 端口只能是数字
|
else
|
||||||
|
IFS='|' read -r MAGIC RAW_NODE RAW_IP RAW_PORT <<< "$REG_LINE"
|
||||||
|
RAW_REGION="UNKNOWN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 🛡️ 强制字符白名单过滤:保留历史特征不变
|
||||||
|
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||||
|
AGENT_REGION=$(echo "$RAW_REGION" | tr -cd 'a-zA-Z0-9' | cut -c 1-10) # 提取国家大区
|
||||||
|
NODE_NAME=$(echo "$RAW_NODE" | tr -cd 'a-zA-Z0-9_.-' | cut -c 1-30)
|
||||||
|
AGENT_IP=$(echo "$RAW_IP" | tr -cd 'a-zA-Z0-9.:\[\]-' | cut -c 1-50)
|
||||||
|
AGENT_PORT=$(echo "$RAW_PORT" | tr -cd '0-9' | cut -c 1-5)
|
||||||
|
|
||||||
# ================== [v3.0.2 紧急加固: SSRF 内网隔离墙] ==================
|
|
||||||
# 拒绝 127.x, 10.x, 192.168.x, 172.16~31.x 以及 IPv6 回环地址的注册
|
|
||||||
if [[ "$AGENT_IP" =~ ^127\.|^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^::1$|^localhost$ ]]; then
|
if [[ "$AGENT_IP" =~ ^127\.|^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^::1$|^localhost$ ]]; then
|
||||||
send_msg "$CHAT_ID" "⛔ **安全拦截**:禁止注册内网或回环 IP,防止 SSRF 攻击渗透。"
|
send_msg "$CHAT_ID" "⛔ **安全拦截**:禁止注册内网或回环 IP,防止 SSRF 攻击渗透。"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 异常拦截:如果核心字段被过滤成了空值,说明是恶意请求,直接抛弃
|
|
||||||
if [ -z "$NODE_NAME" ] || [ -z "$AGENT_IP" ] || [ -z "$AGENT_PORT" ] || [ -z "$CHAT_ID" ]; then
|
if [ -z "$NODE_NAME" ] || [ -z "$AGENT_IP" ] || [ -z "$AGENT_PORT" ] || [ -z "$CHAT_ID" ]; then
|
||||||
send_msg "$CHAT_ID" "⛔ **安全拦截**:检测到非法注册载荷,请求已拒绝。"
|
send_msg "$CHAT_ID" "⛔ **安全拦截**:检测到非法注册载荷,请求已拒绝。"
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP) ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP;"
|
# 入库时追加 region 字段
|
||||||
|
db_exec "INSERT INTO nodes (chat_id, node_name, agent_ip, agent_port, last_seen, region) VALUES ('$CHAT_ID', '$NODE_NAME', '$AGENT_IP', '$AGENT_PORT', CURRENT_TIMESTAMP, '$AGENT_REGION') ON CONFLICT(chat_id, node_name) DO UPDATE SET agent_ip='$AGENT_IP', agent_port='$AGENT_PORT', last_seen=CURRENT_TIMESTAMP, region='$AGENT_REGION';"
|
||||||
send_msg "$CHAT_ID" "✅ 司令部已确认!节点接入成功: \`$NODE_NAME\` ($AGENT_IP:$AGENT_PORT)"
|
send_msg "$CHAT_ID" "✅ 司令部已确认!节点接入成功: \`$NODE_NAME\` ($AGENT_IP:$AGENT_PORT)"
|
||||||
|
|
||||||
# ================== [v3.1.2 丝滑连招: 注册完毕直接呼出面板] ==================
|
# ================== [v3.1.3 丝滑连招: 直接呼出全球大区雷达] ==================
|
||||||
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
|
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
|
||||||
if [ -n "$NODE_LIST" ]; then
|
if [ -n "$REGION_DATA" ]; then
|
||||||
BTNS="["
|
BTNS="["
|
||||||
for N in $NODE_LIST; do
|
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||||
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
|
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||||
done
|
FLAG="🌐"
|
||||||
|
case "$REGION_NAME" in
|
||||||
|
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||||
|
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||||
|
esac
|
||||||
|
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||||
|
done <<< "$REGION_DATA"
|
||||||
BTNS="${BTNS%,}]"
|
BTNS="${BTNS%,}]"
|
||||||
send_ui "$CHAT_ID" "🔍 您名下的活跃节点:" "$BTNS"
|
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n请选择要检阅的战区:" "$BTNS"
|
||||||
fi
|
fi
|
||||||
# ========================================================================
|
# ========================================================================
|
||||||
|
|
||||||
@@ -162,16 +180,56 @@ while true; do
|
|||||||
# ====================================================================
|
# ====================================================================
|
||||||
|
|
||||||
"list_nodes")
|
"list_nodes")
|
||||||
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
|
# 【V3.1.3】一级菜单:大区聚合并列出数量
|
||||||
if [ -z "$NODE_LIST" ]; then
|
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
|
||||||
|
if [ -z "$REGION_DATA" ]; then
|
||||||
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点,请先在边缘机执行部署。"
|
send_msg "$CHAT_ID" "⚠️ 您名下暂无在线节点,请先在边缘机执行部署。"
|
||||||
else
|
else
|
||||||
BTNS="["
|
BTNS="["
|
||||||
for N in $NODE_LIST; do
|
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||||
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
|
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||||
done
|
FLAG="🌐"
|
||||||
|
case "$REGION_NAME" in
|
||||||
|
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||||
|
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||||
|
esac
|
||||||
|
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||||
|
done <<< "$REGION_DATA"
|
||||||
BTNS="${BTNS%,}]"
|
BTNS="${BTNS%,}]"
|
||||||
send_ui "$CHAT_ID" "🔍 您名下的活跃节点:" "$BTNS"
|
send_ui "$CHAT_ID" "🌍 **全视界战略雷达**\n请选择要检阅的战区:" "$BTNS"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
|
||||||
|
region:*)
|
||||||
|
# 【V3.1.3】二级菜单:目标大区下的节点双列排版
|
||||||
|
TARGET_REGION=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9')
|
||||||
|
CHAT_ID=$(echo "$CHAT_ID" | tr -cd '0-9-')
|
||||||
|
|
||||||
|
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID' AND region='$TARGET_REGION';")
|
||||||
|
if [ -z "$NODE_LIST" ]; then
|
||||||
|
send_msg "$CHAT_ID" "⚠️ 该战区下暂无可用节点。"
|
||||||
|
else
|
||||||
|
BTNS="["
|
||||||
|
COL=0
|
||||||
|
ROW_STR="["
|
||||||
|
for N in $NODE_LIST; do
|
||||||
|
ROW_STR="$ROW_STR{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"},"
|
||||||
|
COL=$((COL+1))
|
||||||
|
if [ $COL -eq 2 ]; then
|
||||||
|
ROW_STR="${ROW_STR%,}]"
|
||||||
|
BTNS="$BTNS$ROW_STR,"
|
||||||
|
COL=0
|
||||||
|
ROW_STR="["
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# 如果是奇数,补齐最后的尾巴
|
||||||
|
if [ $COL -eq 1 ]; then
|
||||||
|
ROW_STR="${ROW_STR%,}]"
|
||||||
|
BTNS="$BTNS$ROW_STR,"
|
||||||
|
fi
|
||||||
|
# 添加返回上级大区雷达的按钮
|
||||||
|
BTNS="$BTNS[{\"text\":\"⬅️ 返回全球战区分布\",\"callback_data\":\"list_nodes\"}]]"
|
||||||
|
send_ui "$CHAT_ID" "📍 **[$TARGET_REGION] 战区哨兵矩阵**\n请下达控制指令:" "$BTNS"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@@ -179,7 +237,7 @@ while true; do
|
|||||||
# 🛡️ 强制过滤节点名,防止面板渲染时发生 XSS 或注入
|
# 🛡️ 强制过滤节点名,防止面板渲染时发生 XSS 或注入
|
||||||
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
TARGET_NODE=$(echo "${TEXT#*:}" | tr -cd 'a-zA-Z0-9_.-')
|
||||||
# 【核心升级】拆分下发按钮,精准对应 Google 与 Trust 两个模块,并排版为 3 行 2 列
|
# 【核心升级】拆分下发按钮,精准对应 Google 与 Trust 两个模块,并排版为 3 行 2 列
|
||||||
BTNS="[[{\"text\":\"📍 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"📜 实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 统计战报\",\"callback_data\":\"report:$TARGET_NODE\"}], [{\"text\":\"🗑️ 剔除失联节点\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回主列表\",\"callback_data\":\"list_nodes\"}]]"
|
BTNS="[[{\"text\":\"📍 Google 纠偏\",\"callback_data\":\"google:$TARGET_NODE\"}, {\"text\":\"🛡️ 信用净化\",\"callback_data\":\"trust:$TARGET_NODE\"}], [{\"text\":\"📜 实时日志\",\"callback_data\":\"log:$TARGET_NODE\"}, {\"text\":\"📊 统计战报\",\"callback_data\":\"report:$TARGET_NODE\"}], [{\"text\":\"🗑️ 剔除失联节点\",\"callback_data\":\"del:$TARGET_NODE\"}, {\"text\":\"⬅️ 返回大区目录\",\"callback_data\":\"list_nodes\"}]]"
|
||||||
send_ui "$CHAT_ID" "⚙️ **目标锁定**: \`$TARGET_NODE\`\n请选择战术动作:" "$BTNS"
|
send_ui "$CHAT_ID" "⚙️ **目标锁定**: \`$TARGET_NODE\`\n请选择战术动作:" "$BTNS"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@@ -191,16 +249,23 @@ while true; do
|
|||||||
db_exec "DELETE FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
db_exec "DELETE FROM nodes WHERE chat_id='$CHAT_ID' AND node_name='$TARGET_NODE';"
|
||||||
send_msg "$CHAT_ID" "🗑️ 节点 \`$TARGET_NODE\` 的档案已从司令部彻底销毁!"
|
send_msg "$CHAT_ID" "🗑️ 节点 \`$TARGET_NODE\` 的档案已从司令部彻底销毁!"
|
||||||
|
|
||||||
NODE_LIST=$(db_exec "SELECT node_name FROM nodes WHERE chat_id='$CHAT_ID';")
|
# 剔除后直接返回上级一级雷达菜单
|
||||||
if [ -z "$NODE_LIST" ]; then
|
REGION_DATA=$(db_exec "SELECT region, COUNT(*) FROM nodes WHERE chat_id='$CHAT_ID' GROUP BY region;")
|
||||||
|
if [ -z "$REGION_DATA" ]; then
|
||||||
send_msg "$CHAT_ID" "⚠️ 当前司令部已无任何节点挂载。"
|
send_msg "$CHAT_ID" "⚠️ 当前司令部已无任何节点挂载。"
|
||||||
else
|
else
|
||||||
BTNS="["
|
BTNS="["
|
||||||
for N in $NODE_LIST; do
|
while IFS='|' read -r REGION_NAME NODE_COUNT; do
|
||||||
BTNS="$BTNS[{\"text\":\"🖥️ $N\",\"callback_data\":\"manage:$N\"}],"
|
[ -z "$REGION_NAME" ] && REGION_NAME="UNKNOWN"
|
||||||
done
|
FLAG="🌐"
|
||||||
|
case "$REGION_NAME" in
|
||||||
|
"US") FLAG="🇺🇸" ;; "JP") FLAG="🇯🇵" ;; "HK") FLAG="🇭🇰" ;;
|
||||||
|
"SG") FLAG="🇸🇬" ;; "UK"|"GB") FLAG="🇬🇧" ;; "DE") FLAG="🇩🇪" ;; "FR") FLAG="🇫🇷" ;;
|
||||||
|
esac
|
||||||
|
BTNS="$BTNS[{\"text\":\"$FLAG $REGION_NAME ($NODE_COUNT 台)\",\"callback_data\":\"region:$REGION_NAME\"}],"
|
||||||
|
done <<< "$REGION_DATA"
|
||||||
BTNS="${BTNS%,}]"
|
BTNS="${BTNS%,}]"
|
||||||
send_ui "$CHAT_ID" "🔍 刷新后的节点列表:" "$BTNS"
|
send_ui "$CHAT_ID" "🌍 刷新后的全视界雷达:" "$BTNS"
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user