small-packages/luci-app-bandix/root/usr/libexec/rpcd/luci.bandix
2026-05-10 09:48:30 +08:00

1799 lines
58 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
# Copyright 2025 timsaya
. /usr/share/libubox/jshn.sh
# 从UCI配置读取端口号如果读取失败则使用默认端口8686
BANDIX_PORT=$(uci get bandix.general.port 2>/dev/null || echo "8686")
readonly BANDIX_API_BASE="http://127.0.0.1:$BANDIX_PORT"
readonly BANDIX_DEVICES_API="$BANDIX_API_BASE/api/traffic/devices"
readonly BANDIX_LIMITS_API="$BANDIX_API_BASE/api/traffic/limits"
readonly BANDIX_METRICS_API="$BANDIX_API_BASE/api/traffic/metrics"
readonly BANDIX_CONNECTION_API="$BANDIX_API_BASE/api/connection/devices"
readonly BANDIX_CONNECTION_FLOWS_API="$BANDIX_API_BASE/api/connection/flows"
readonly BANDIX_DNS_QUERIES_API="$BANDIX_API_BASE/api/dns/queries"
readonly BANDIX_DNS_STATS_API="$BANDIX_API_BASE/api/dns/stats"
readonly BANDIX_SCHEDULE_LIMITS_API="$BANDIX_API_BASE/api/traffic/limits/schedule"
readonly BANDIX_TRAFFIC_USAGE_RANKING_API="$BANDIX_API_BASE/api/traffic/usage/ranking"
readonly BANDIX_TRAFFIC_USAGE_INCREMENTS_API="$BANDIX_API_BASE/api/traffic/usage/increments"
readonly BANDIX_RATE_LIMIT_WHITELIST_API="$BANDIX_API_BASE/api/traffic/rate_limit/whitelist"
readonly BANDIX_RATE_LIMIT_WHITELIST_ENABLED_API="$BANDIX_API_BASE/api/traffic/rate_limit/whitelist/enabled"
readonly BANDIX_RATE_LIMIT_DEFAULT_API="$BANDIX_API_BASE/api/traffic/rate_limit/default"
# 通用函数创建简单的JSON响应
make_value() {
json_init
json_add_string "$1" "$2"
json_dump
json_cleanup
}
# 通用函数:创建错误响应
make_error() {
json_init
json_add_boolean "success" 0
json_add_string "error" "$1"
json_dump
json_cleanup
}
# 通用函数:创建成功响应
make_success() {
json_init
json_add_boolean "success" 1
[ -n "$1" ] && json_add_string "message" "$1"
json_dump
json_cleanup
}
# 转义JSON字符串
escape_json_string() {
echo "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\r/\\r/g; s/\t/\\t/g'
}
get_rate_limit_whitelist() {
local response=$(curl -s --connect-timeout 2 --max-time 5 -X GET "$BANDIX_RATE_LIMIT_WHITELIST_API" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
echo "$response"
}
set_rate_limit_whitelist_enabled() {
local enabled_raw="$1"
local enabled="false"
case "$enabled_raw" in
1|true|TRUE|True)
enabled="true"
;;
esac
local request_data="{\"enabled\": $enabled}"
local response=$(curl -s --connect-timeout 3 --max-time 10 -X POST -H "Content-Type: application/json" -d "$request_data" "$BANDIX_RATE_LIMIT_WHITELIST_ENABLED_API" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
echo "$response"
}
add_rate_limit_whitelist_mac() {
local mac="$1"
if [ -z "$mac" ]; then
make_error "MAC address is required"
return
fi
local mac_escaped=$(escape_json_string "$mac")
local request_data="{\"mac\": \"$mac_escaped\"}"
local response=$(curl -s --connect-timeout 3 --max-time 10 -X POST -H "Content-Type: application/json" -d "$request_data" "$BANDIX_RATE_LIMIT_WHITELIST_API" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
echo "$response"
}
delete_rate_limit_whitelist_mac() {
local mac="$1"
if [ -z "$mac" ]; then
make_error "MAC address is required"
return
fi
local mac_escaped=$(escape_json_string "$mac")
local request_data="{\"mac\": \"$mac_escaped\"}"
local response=$(curl -s --connect-timeout 3 --max-time 10 -X DELETE -H "Content-Type: application/json" -d "$request_data" "$BANDIX_RATE_LIMIT_WHITELIST_API" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
echo "$response"
}
set_default_rate_limit() {
local wan_rx_rate_limit="$1"
local wan_tx_rate_limit="$2"
if [ -z "$wan_rx_rate_limit" ] || [ -z "$wan_tx_rate_limit" ]; then
make_error "Missing parameters"
return
fi
local request_data="{
\"wan_rx_rate_limit\": $wan_rx_rate_limit,
\"wan_tx_rate_limit\": $wan_tx_rate_limit
}"
local response=$(curl -s --connect-timeout 3 --max-time 10 -X POST -H "Content-Type: application/json" -d "$request_data" "$BANDIX_RATE_LIMIT_DEFAULT_API" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
echo "$response"
}
# 删除设备
delete_device() {
local mac="$1"
if [ -z "$mac" ]; then
make_error "MAC address is required"
return
fi
local mac_escaped=$(escape_json_string "$mac")
local request_data="{\"mac\": \"$mac_escaped\"}"
local response=$(curl -s --connect-timeout 3 --max-time 10 -X DELETE -H "Content-Type: application/json" -d "$request_data" "$BANDIX_DEVICES_API" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
echo "$response"
}
# 获取设备状态
# GET /api/traffic/devices -> $.data
# {
# d: [{
# mac, # MAC 地址
# ip4, # IPv4 地址
# ip6, # IPv6 地址列表
# host, # 主机名
# conn, # 接入类型 (wifi/wired/router)
# uplink, # 接入接口名 (eth1/lan2/phy0-ap0),无则空串
# w_ch, # WiFi 频道号 (1/36/149),非 WiFi 为 0
# last, # 最后在线时间戳(ms)
# t_rx_b, t_tx_b, # 总下/上行字节
# t_rx_r, t_tx_r, # 总下/上行速率
# w_rx_b, w_tx_b, # WAN 下/上行字节
# w_rx_r, w_tx_r, # WAN 下/上行速率
# w_rx_l, w_tx_l, # WAN 下/上行限速
# l_rx_b, l_tx_b, # LAN 下/上行字节
# l_rx_r, l_tx_r # LAN 下/上行速率
# }]
# }
get_device_status() {
local url="$BANDIX_DEVICES_API"
local start_ms="$1"
local end_ms="$2"
local query_params=""
if [ -n "$start_ms" ] && [ "$start_ms" != "null" ] && [ "$start_ms" != "undefined" ]; then
query_params="${query_params}start_ms=$start_ms&"
fi
if [ -n "$end_ms" ] && [ "$end_ms" != "null" ] && [ "$end_ms" != "undefined" ]; then
query_params="${query_params}end_ms=$end_ms&"
fi
query_params=$(echo "$query_params" | sed 's/&$//')
[ -n "$query_params" ] && url="${url}?${query_params}"
local api_result=$(curl -s --connect-timeout 2 --max-time 5 "$url" 2>/dev/null)
# 检查API调用是否成功
if [ $? -ne 0 ] || [ -z "$api_result" ]; then
echo '{"d":[]}'
return
fi
# 提取 data 部分并返回
local data_part=$(echo "$api_result" | jsonfilter -e '$.data' 2>/dev/null)
if [ -n "$data_part" ]; then
echo "$data_part"
else
echo '{"d":[]}'
fi
}
# 获取历史指标可选MAC
get_metrics() {
local mac="$1"
local url="$BANDIX_METRICS_API"
if [ -n "$mac" ]; then
# 转义MAC
local mac_escaped=$(echo "$mac" | sed 's/\//\\\//g')
url="$url?mac=$mac_escaped"
fi
# logger "luci.bandix.metrics: entering mac=$mac url=$url"
local api_result=$(curl -s --connect-timeout 1 --max-time 3 "$url" 2>/dev/null)
local curl_exit_code=$?
# logger "luci.bandix.metrics: curl finished with exit code: $curl_exit_code"
if [ $curl_exit_code -ne 0 ] || [ -z "$api_result" ]; then
# logger "luci.bandix.metrics: curl failed or empty response, returning empty metrics"
# 返回空结果(原始格式)
echo '{"retention_seconds":600,"mac":"","metrics":[]}'
return
fi
# 使用 jsonfilter 提取 data 部分根据文档这是处理大JSON的推荐方式
local data_part=$(echo "$api_result" | jsonfilter -e '$.data' 2>/dev/null)
if [ -n "$data_part" ]; then
local data_size=${#data_part}
# logger "luci.bandix.metrics: extracted data part using jsonfilter, size: $data_size bytes"
echo "$data_part"
return
else
# logger "luci.bandix.metrics: jsonfilter failed to extract data part"
echo '{"retention_seconds":600,"mac":"","metrics":[]}'
fi
}
# 设置设备主机名绑定
set_device_hostname() {
local mac="$1"
local hostname="$2"
# logger "luci.bandix: set_device_hostname 参数: mac=$mac hostname=$hostname"
# 验证参数
if [ -z "$mac" ]; then
make_error "MAC address is required"
return
fi
# hostname可以为空表示删除绑定
if [ -z "$hostname" ]; then
hostname=""
fi
# 转义特殊字符
local mac_escaped=$(escape_json_string "$mac")
local hostname_escaped=$(escape_json_string "$hostname")
# 构建请求数据
local request_data="{
\"mac\": \"$mac\",
\"hostname\": \"$hostname\"
}"
# 记录请求数据(用于调试)
# logger "luci.bandix: hostname request_data=$request_data"
# 发送请求到bandix API
local bindings_api="$BANDIX_API_BASE/api/traffic/bindings"
local response=$(curl -s --connect-timeout 3 --max-time 10 -X POST -H "Content-Type: application/json" -d "$request_data" "$bindings_api" 2>/dev/null)
# logger "luci.bandix: hostname API response=$response"
if [ $? -eq 0 ] && [ -n "$response" ]; then
# 检查API响应是否包含success状态
if echo "$response" | grep -q '"status":\s*"success"'; then
make_success "Hostname binding set successfully"
else
# API返回了响应但不是success状态
json_init
json_add_boolean "success" 0
json_add_string "error" "API returned error response"
json_add_string "response" "$response"
json_dump
json_cleanup
fi
else
make_error "Failed to set hostname binding"
fi
}
# 获取连接监控数据
# GET /api/connection/devices -> raw response (no $.data extraction)
# {
# status, data: {
# cnt, # 设备数量
# last, # 最后更新时间戳(ms)
# g: { # 全局连接统计
# total, # 总连接数
# tcp, udp, # TCP/UDP 连接数
# tcp_est, tcp_tw, tcp_cw, # ESTABLISHED/TIME_WAIT/CLOSE_WAIT
# last # 最后更新时间戳(ms)
# },
# d: [{ # 各设备连接统计
# mac, ip4, host, # 设备标识
# total, tcp, udp, # 总/TCP/UDP 连接数
# tcp_est, tcp_tw, tcp_cw, # ESTABLISHED/TIME_WAIT/CLOSE_WAIT
# last # 最后更新时间戳(ms)
# }]
# }
# }
get_connection_devices() {
# 检查连接监控是否启用
local connection_enabled=$(uci get bandix.connections.enabled 2>/dev/null)
if [ "$connection_enabled" != "1" ]; then
make_error "Connection monitoring is not enabled"
return
fi
# 从 Bandix API 获取连接数据
local response=$(curl -s --connect-timeout 2 --max-time 5 -X GET "$BANDIX_CONNECTION_API" 2>/dev/null)
# 检查API调用是否成功
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
# 直接返回API结果
echo "$response"
}
get_connection_flows() {
local connection_enabled=$(uci get bandix.connections.enabled 2>/dev/null)
if [ "$connection_enabled" != "1" ]; then
make_error "Connection monitoring is not enabled"
return
fi
local ip="$1"
local protocol="$2"
local state="$3"
local page="$4"
local page_size="$5"
local query_params=""
[ -n "$ip" ] && query_params="${query_params}ip=$(printf '%s' "$ip" | sed 's/ /%20/g')&"
[ -n "$protocol" ] && query_params="${query_params}protocol=$(printf '%s' "$protocol" | sed 's/ /%20/g')&"
[ -n "$state" ] && query_params="${query_params}state=$(printf '%s' "$state" | sed 's/ /%20/g')&"
[ -n "$page" ] && query_params="${query_params}page=$page&"
[ -n "$page_size" ] && query_params="${query_params}page_size=$page_size&"
query_params=$(echo "$query_params" | sed 's/&$//')
local url="$BANDIX_CONNECTION_FLOWS_API"
[ -n "$query_params" ] && url="${url}?${query_params}"
local response=$(curl -s --connect-timeout 2 --max-time 5 -X GET "$url" 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
echo "$response"
}
# 获取 DNS 查询记录
get_dns_queries() {
# 检查 DNS 监控是否启用
local dns_enabled=$(uci get bandix.dns.enabled 2>/dev/null)
if [ "$dns_enabled" != "1" ]; then
make_error "DNS monitoring is not enabled"
return
fi
# 构建查询参数
local query_params=""
local domain="$1"
local device="$2"
local is_query="$3"
local dns_server="$4"
local query_type="$5"
local page="$6"
local page_size="$7"
[ -n "$domain" ] && query_params="${query_params}domain=$(printf '%s' "$domain" | sed 's/ /%20/g')&"
[ -n "$device" ] && query_params="${query_params}device=$(printf '%s' "$device" | sed 's/ /%20/g')&"
[ -n "$is_query" ] && query_params="${query_params}is_query=$is_query&"
[ -n "$dns_server" ] && query_params="${query_params}dns_server=$(printf '%s' "$dns_server" | sed 's/ /%20/g')&"
[ -n "$query_type" ] && query_params="${query_params}query_type=$(printf '%s' "$query_type" | sed 's/ /%20/g')&"
[ -n "$page" ] && query_params="${query_params}page=$page&"
[ -n "$page_size" ] && query_params="${query_params}page_size=$page_size&"
# 移除末尾的 &
query_params=$(echo "$query_params" | sed 's/&$//')
local url="$BANDIX_DNS_QUERIES_API"
[ -n "$query_params" ] && url="${url}?${query_params}"
# 从 Bandix API 获取 DNS 查询数据
local response=$(curl -s --connect-timeout 2 --max-time 10 -X GET "$url" 2>/dev/null)
# 检查API调用是否成功
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
# 直接返回API结果
echo "$response"
}
# 获取 DNS 统计信息
get_dns_stats() {
# 检查 DNS 监控是否启用
local dns_enabled=$(uci get bandix.dns.enabled 2>/dev/null)
if [ "$dns_enabled" != "1" ]; then
make_error "DNS monitoring is not enabled"
return
fi
# 从 Bandix API 获取 DNS 统计数据
local response=$(curl -s --connect-timeout 2 --max-time 10 -X GET "$BANDIX_DNS_STATS_API" 2>/dev/null)
# 检查API调用是否成功
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
# 直接返回API结果
echo "$response"
}
# 获取设备用量排行
# GET /api/traffic/usage/ranking -> $.data
# {
# start, end, # 统计时间范围(ms)
# net, # 网络类型 (wan/lan/all)
# cnt, # 设备数量
# t_b, t_rx_b, t_tx_b, # 总/下/上行字节
# r: [{
# mac, ip4, host, # 设备标识
# r, # 排名
# pct, # 占比(%)
# t_b, rx_b, tx_b # 总/下/上行字节
# }]
# }
get_traffic_usage_ranking() {
local start_ms="$1"
local end_ms="$2"
local network_type="$3"
# 构建查询参数(只添加非空且有效的参数)
local query_params=""
# 检查 start_ms 是否有效(非空且不是 "null" 或 "undefined"
if [ -n "$start_ms" ] && [ "$start_ms" != "null" ] && [ "$start_ms" != "undefined" ]; then
query_params="${query_params}start_ms=$start_ms&"
fi
# 检查 end_ms 是否有效
if [ -n "$end_ms" ] && [ "$end_ms" != "null" ] && [ "$end_ms" != "undefined" ]; then
query_params="${query_params}end_ms=$end_ms&"
fi
# 检查 network_type 是否有效
if [ -n "$network_type" ] && [ "$network_type" != "null" ] && [ "$network_type" != "undefined" ]; then
query_params="${query_params}network_type=$network_type&"
fi
# 移除末尾的 &
query_params=$(echo "$query_params" | sed 's/&$//')
local url="$BANDIX_TRAFFIC_USAGE_RANKING_API"
[ -n "$query_params" ] && url="${url}?${query_params}"
# 从 Bandix API 获取设备用量排行数据
local response=$(curl -s --connect-timeout 2 --max-time 10 -X GET "$url" 2>/dev/null)
# 检查API调用是否成功
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
# 提取 data 部分并返回
local data_part=$(echo "$response" | jsonfilter -e '$.data' 2>/dev/null)
if [ -n "$data_part" ]; then
echo "$data_part"
else
# 如果提取失败,返回空结果
echo '{"start":0,"end":0,"t_rx_b":0,"t_tx_b":0,"t_b":0,"cnt":0,"r":[]}'
fi
}
# 获取时间序列增量数据
# GET /api/traffic/usage/increments -> $.data
# {
# start, end, # 统计时间范围(ms)
# agg, # 聚合粒度 (1m/5m/1h/1d)
# net, # 网络类型 (wan/lan/all)
# mac, # 指定设备 MAC空=全部)
# t_b, t_rx_b, t_tx_b, # 总/下/上行字节
# inc: [{
# start, end, # 分段时间范围(ms)
# w_rx_avg/max/min/p90/p95/p99, # WAN 下行速率统计
# w_tx_avg/max/min/p90/p95/p99, # WAN 上行速率统计
# l_rx_avg/max/min/p90/p95/p99, # LAN 下行速率统计
# l_tx_avg/max/min/p90/p95/p99, # LAN 上行速率统计
# w_rx_b, w_tx_b, # WAN 下/上行字节增量
# l_rx_b, l_tx_b # LAN 下/上行字节增量
# }]
# }
get_traffic_usage_increments() {
local start_ms="$1"
local end_ms="$2"
local aggregation="$3"
local mac="$4"
local network_type="$5"
# 构建查询参数(只添加非空且有效的参数)
local query_params=""
# 检查 start_ms 是否有效(非空且不是 "null" 或 "undefined"
if [ -n "$start_ms" ] && [ "$start_ms" != "null" ] && [ "$start_ms" != "undefined" ]; then
query_params="${query_params}start_ms=$start_ms&"
fi
# 检查 end_ms 是否有效
if [ -n "$end_ms" ] && [ "$end_ms" != "null" ] && [ "$end_ms" != "undefined" ]; then
query_params="${query_params}end_ms=$end_ms&"
fi
# 检查 aggregation 是否有效
if [ -n "$aggregation" ] && [ "$aggregation" != "null" ] && [ "$aggregation" != "undefined" ]; then
query_params="${query_params}aggregation=$aggregation&"
fi
# 检查 mac 是否有效
if [ -n "$mac" ] && [ "$mac" != "null" ] && [ "$mac" != "undefined" ]; then
query_params="${query_params}mac=$(printf '%s' "$mac" | sed 's/ /%20/g')&"
fi
# 检查 network_type 是否有效
if [ -n "$network_type" ] && [ "$network_type" != "null" ] && [ "$network_type" != "undefined" ]; then
query_params="${query_params}network_type=$network_type&"
fi
# 移除末尾的 &
query_params=$(echo "$query_params" | sed 's/&$//')
local url="$BANDIX_TRAFFIC_USAGE_INCREMENTS_API"
[ -n "$query_params" ] && url="${url}?${query_params}"
# 从 Bandix API 获取时间序列增量数据
local response=$(curl -s --connect-timeout 2 --max-time 10 -X GET "$url" 2>/dev/null)
# 检查API调用是否成功
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
# 提取 data 部分并返回
local data_part=$(echo "$response" | jsonfilter -e '$.data' 2>/dev/null)
if [ -n "$data_part" ]; then
echo "$data_part"
else
# 如果提取失败,返回空结果
echo '{"start":0,"end":0,"agg":"hourly","mac":"all","inc":[],"t_rx_b":0,"t_tx_b":0,"t_b":0}'
fi
}
# 获取定时限速规则
get_schedule_limits() {
# 从 Bandix API 获取定时限速规则
local response=$(curl -s --connect-timeout 2 --max-time 5 -X GET "$BANDIX_SCHEDULE_LIMITS_API" 2>/dev/null)
# 检查API调用是否成功
if [ $? -ne 0 ] || [ -z "$response" ]; then
make_error "Failed to connect to Bandix service"
return
fi
# 直接返回API结果
echo "$response"
}
# 设置定时限速规则
set_schedule_limit() {
local mac="$1"
local start_time="$2"
local end_time="$3"
local days="$4"
local wan_tx_rate_limit="$5"
local wan_rx_rate_limit="$6"
# 验证参数
if [ -z "$mac" ]; then
make_error "MAC address is required"
return
fi
if [ -z "$start_time" ] || [ -z "$end_time" ] || [ -z "$days" ]; then
make_error "Time slot parameters are required"
return
fi
# 转义MAC地址中的特殊字符
local mac_escaped=$(escape_json_string "$mac")
# 构建请求数据
local request_data="{
\"mac\": \"$mac\",
\"time_slot\": {
\"start\": \"$start_time\",
\"end\": \"$end_time\",
\"days\": $days
},
\"wan_tx_rate_limit\": $wan_tx_rate_limit,
\"wan_rx_rate_limit\": $wan_rx_rate_limit
}"
# 发送请求到bandix API
local response=$(curl -s --connect-timeout 3 --max-time 10 -X POST -H "Content-Type: application/json" -d "$request_data" "$BANDIX_SCHEDULE_LIMITS_API" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$response" ]; then
# 检查API响应是否包含success状态
if echo "$response" | grep -q '"status":\s*"success"'; then
make_success "Schedule limit set successfully"
else
# API返回了响应但不是success状态
json_init
json_add_boolean "success" 0
json_add_string "error" "API returned error response"
json_add_string "response" "$response"
json_dump
json_cleanup
fi
else
make_error "Failed to set schedule limit"
fi
}
# 编辑定时限速规则
update_schedule_limit() {
local id="$1"
local mac="$2"
local start_time="$3"
local end_time="$4"
local days="$5"
local wan_rx_rate_limit="$6"
local wan_tx_rate_limit="$7"
if [ -z "$id" ]; then
make_error "ID is required"
return
fi
if [ -z "$mac" ]; then
make_error "MAC address is required"
return
fi
if [ -z "$start_time" ] || [ -z "$end_time" ] || [ -z "$days" ]; then
make_error "Time slot parameters are required"
return
fi
local id_escaped=$(escape_json_string "$id")
local mac_escaped=$(escape_json_string "$mac")
local request_data="{
\"id\": \"$id\",
\"mac\": \"$mac\",
\"time_slot\": {
\"start\": \"$start_time\",
\"end\": \"$end_time\",
\"days\": $days
},
\"wan_rx_rate_limit\": $wan_rx_rate_limit,
\"wan_tx_rate_limit\": $wan_tx_rate_limit
}"
local response=$(curl -s --connect-timeout 3 --max-time 10 -X PUT -H "Content-Type: application/json" -d "$request_data" "$BANDIX_SCHEDULE_LIMITS_API" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$response" ]; then
if echo "$response" | grep -q '"status":\s*"success"'; then
make_success "Schedule limit updated successfully"
else
json_init
json_add_boolean "success" 0
json_add_string "error" "API returned error response"
json_add_string "response" "$response"
json_dump
json_cleanup
fi
else
make_error "Failed to update schedule limit"
fi
}
# 删除定时限速规则
delete_schedule_limit() {
local id="$1"
if [ -z "$id" ]; then
make_error "ID is required"
return
fi
local id_escaped=$(escape_json_string "$id")
local request_data="{\"id\": \"$id\"}"
local response=$(curl -s --connect-timeout 3 --max-time 10 -X DELETE -H "Content-Type: application/json" -d "$request_data" "$BANDIX_SCHEDULE_LIMITS_API" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$response" ]; then
if echo "$response" | grep -q '"status":\s*"success"'; then
make_success "Schedule limit deleted successfully"
else
json_init
json_add_boolean "success" 0
json_add_string "error" "API returned error response"
json_add_string "response" "$response"
json_dump
json_cleanup
fi
else
make_error "Failed to delete schedule limit"
fi
}
# 清空数据
clear_data() {
local data_dir=$(uci get bandix.general.data_dir 2>/dev/null || echo "/usr/share/bandix")
local metrics_dir="$data_dir/metrics"
# 检查目录是否存在
if [ ! -d "$metrics_dir" ]; then
make_error "Metrics directory does not exist"
return
fi
# 删除 metrics 目录下的所有文件
rm -rf "$metrics_dir"/* 2>/dev/null
if [ $? -eq 0 ]; then
make_success "Data cleared successfully"
else
make_error "Failed to clear data"
fi
}
# 重启服务
restart_service() {
# 执行服务重启
/etc/init.d/bandix restart >/dev/null 2>&1
if [ $? -eq 0 ]; then
make_success "Service restarted successfully"
else
make_error "Failed to restart service"
fi
}
# 获取版本信息
get_version() {
json_init
# 检测包管理器类型
local pkg_manager=$(get_package_manager)
# 获取 luci-app-bandix 版本
local luci_version=""
if [ "$pkg_manager" = "opkg" ]; then
# opkg 方式
if [ -f "/usr/lib/opkg/info/luci-app-bandix.control" ]; then
luci_version=$(grep "^Version:" /usr/lib/opkg/info/luci-app-bandix.control 2>/dev/null | cut -d' ' -f2)
fi
# 如果无法从 control 文件获取,尝试从 opkg 命令获取
if [ -z "$luci_version" ]; then
luci_version=$(opkg list-installed luci-app-bandix 2>/dev/null | awk '{print $NF}')
fi
elif [ "$pkg_manager" = "apk" ]; then
# apk 方式
# apk 的 control 文件路径可能不同,先尝试常见路径
if [ -f "/lib/apk/db/installed" ]; then
luci_version=$(grep "^luci-app-bandix-" /usr/lib/apk/db/installed 2>/dev/null | head -n 1 | sed -n 's/^luci-app-bandix-\(.*\)$/\1/p')
fi
# 如果无法从 installed 文件获取,尝试从 apk info 命令获取
if [ -z "$luci_version" ]; then
luci_version=$(apk list --installed --manifest luci-app-bandix 2>/dev/null | awk '{print $NF}')
fi
fi
# 如果还是无法获取,使用默认值
if [ -z "$luci_version" ]; then
luci_version="Unknown"
fi
# 获取 bandix 版本
local bandix_version=""
if [ "$pkg_manager" = "opkg" ]; then
# opkg 方式
if [ -f "/usr/lib/opkg/info/bandix.control" ]; then
bandix_version=$(grep "^Version:" /usr/lib/opkg/info/bandix.control 2>/dev/null | cut -d' ' -f2)
fi
# 如果无法从 control 文件获取,尝试从 opkg 命令获取
if [ -z "$bandix_version" ]; then
bandix_version=$(opkg list-installed bandix 2>/dev/null | awk '{print $NF}')
fi
elif [ "$pkg_manager" = "apk" ]; then
# apk 方式
# apk 的 control 文件路径可能不同,先尝试常见路径
if [ -f "/usr/lib/apk/db/installed" ]; then
bandix_version=$(grep "^bandix-" /usr/lib/apk/db/installed 2>/dev/null | head -n 1 | sed -n 's/^bandix-\(.*\)$/\1/p')
fi
# 如果无法从 installed 文件获取,尝试从 apk info 命令获取
if [ -z "$bandix_version" ]; then
bandix_version=$(apk list --installed --manifest bandix 2>/dev/null | awk '{print $NF}')
fi
fi
# 如果还是无法获取,尝试从 bandix API 获取(如果服务运行中)
if [ -z "$bandix_version" ]; then
local api_result=$(curl -s --connect-timeout 2 --max-time 5 "$BANDIX_API_BASE/api/version" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$api_result" ]; then
bandix_version=$(echo "$api_result" | jsonfilter -e '$.version' 2>/dev/null)
[ -z "$bandix_version" ] && bandix_version=$(echo "$api_result" | jsonfilter -e '$.data.version' 2>/dev/null)
fi
fi
# 如果还是无法获取,使用默认值
if [ -z "$bandix_version" ]; then
bandix_version="Unknown"
fi
json_add_string "luci_app_version" "$luci_version"
json_add_string "bandix_version" "$bandix_version"
json_dump
json_cleanup
}
compare_versions() {
local ver1="$1"
local ver2="$2"
if [ "$ver1" = "$ver2" ]; then
return 1
fi
local sorted=$(printf '%s\n%s\n' "$ver1" "$ver2" | sort -V | head -n1)
if [ "$sorted" = "$ver1" ]; then
return 0
else
return 1
fi
}
# 检测包管理器类型
get_package_manager() {
command -v apk >/dev/null && echo "apk" || echo "opkg"
}
# 获取系统架构
get_system_arch() {
# 优先从 /etc/os-release 获取 OPENWRT_ARCH
if [ -f "/etc/os-release" ]; then
local arch=$(grep "^OPENWRT_ARCH=" /etc/os-release 2>/dev/null | cut -d'=' -f2 | tr -d '"')
if [ -n "$arch" ]; then
echo "$arch"
return
fi
fi
# 最后尝试从 uname 获取
uname -m 2>/dev/null
}
# 检查更新
check_update() {
json_init
# 检测包管理器类型
local pkg_manager=$(get_package_manager)
# 获取当前版本
local current_luci_version=""
if [ "$pkg_manager" = "opkg" ]; then
if [ -f "/usr/lib/opkg/info/luci-app-bandix.control" ]; then
current_luci_version=$(grep "^Version:" /usr/lib/opkg/info/luci-app-bandix.control 2>/dev/null | cut -d' ' -f2)
fi
if [ -z "$current_luci_version" ]; then
current_luci_version=$(opkg list-installed luci-app-bandix 2>/dev/null | awk '{print $NF}')
fi
elif [ "$pkg_manager" = "apk" ]; then
if [ -f "/lib/apk/db/installed" ]; then
current_luci_version=$(grep "^luci-app-bandix-" /lib/apk/db/installed 2>/dev/null | head -n 1 | sed -n 's/^luci-app-bandix-\(.*\)$/\1/p')
fi
if [ -z "$current_luci_version" ]; then
current_luci_version=$(apk info luci-app-bandix 2>/dev/null | grep "^luci-app-bandix-" | head -n 1 | sed -n 's/^luci-app-bandix-\(.*\) .*/\1/p')
fi
fi
local current_bandix_version=""
if [ "$pkg_manager" = "opkg" ]; then
if [ -f "/usr/lib/opkg/info/bandix.control" ]; then
current_bandix_version=$(grep "^Version:" /usr/lib/opkg/info/bandix.control 2>/dev/null | cut -d' ' -f2)
fi
if [ -z "$current_bandix_version" ]; then
current_bandix_version=$(opkg list-installed bandix 2>/dev/null | awk '{print $NF}')
fi
elif [ "$pkg_manager" = "apk" ]; then
if [ -f "/lib/apk/db/installed" ]; then
current_bandix_version=$(grep "^bandix-" /lib/apk/db/installed 2>/dev/null | grep -v "^luci-app-bandix-" | head -n 1 | sed -n 's/^bandix-\(.*\)$/\1/p')
fi
if [ -z "$current_bandix_version" ]; then
current_bandix_version=$(apk info bandix 2>/dev/null | grep "^bandix-" | head -n 1 | sed -n 's/^bandix-\(.*\) .*/\1/p')
fi
fi
# 检查 luci-app-bandix 更新
local luci_latest_version=""
local luci_has_update="0"
local luci_update_url=""
local luci_release_body=""
local luci_download_url=""
local luci_api_result=$(curl -s --connect-timeout 10 --max-time 30 "https://api.github.com/repos/timsaya/luci-app-bandix/releases/latest" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$luci_api_result" ]; then
luci_latest_version=$(echo "$luci_api_result" | jsonfilter -e '$.tag_name' 2>/dev/null)
# 去掉 v 前缀
luci_latest_version=${luci_latest_version#v}
luci_update_url=$(echo "$luci_api_result" | jsonfilter -e '$.html_url' 2>/dev/null)
luci_release_body=$(echo "$luci_api_result" | jsonfilter -e '$.body' 2>/dev/null)
# 获取架构信息和包管理器类型
local arch=$(get_system_arch)
local pkg_manager=$(get_package_manager)
# 根据包管理器类型确定优先的文件扩展名
local preferred_ext=""
if [ "$pkg_manager" = "opkg" ]; then
preferred_ext="ipk"
elif [ "$pkg_manager" = "apk" ]; then
preferred_ext="apk"
fi
# 查找对应架构的 ipk/apk 文件
# 只查找匹配包管理器格式的包,找不到就报错
# 使用循环遍历 assets 数组,直到找不到元素为止
local i=0
local asset_count=0
# 先统计 assets 数量
while true; do
local asset_name=$(echo "$luci_api_result" | jsonfilter -e "$.assets[$i].name" 2>/dev/null)
if [ -z "$asset_name" ]; then
break
fi
asset_count=$((asset_count + 1))
i=$((i + 1))
done
# 查找匹配包管理器格式的 _all 包
if [ -n "$preferred_ext" ] && [ "$asset_count" -gt 0 ]; then
i=0
while true; do
local asset_name=$(echo "$luci_api_result" | jsonfilter -e "$.assets[$i].name" 2>/dev/null)
if [ -z "$asset_name" ]; then
break
fi
# 匹配 _all 格式,且必须是优先格式
if echo "$asset_name" | grep -qE "(luci-app-bandix.*_all|luci-app-bandix-.*-all)\.${preferred_ext}$"; then
luci_download_url=$(echo "$luci_api_result" | jsonfilter -e "$.assets[$i].browser_download_url" 2>/dev/null)
break
fi
i=$((i + 1))
done
fi
# 如果没找到 _all尝试找包含当前架构的只匹配包管理器格式
# 例如luci-app-bandix_0.10.0-r1_aarch64_cortex-a53.ipk
if [ -z "$luci_download_url" ] && [ -n "$arch" ] && [ -n "$preferred_ext" ] && [ "$asset_count" -gt 0 ]; then
i=0
while true; do
local asset_name=$(echo "$luci_api_result" | jsonfilter -e "$.assets[$i].name" 2>/dev/null)
if [ -z "$asset_name" ]; then
break
fi
# 匹配格式:*_架构.优先扩展名
# 例如:文件名包含 "_aarch64_cortex-a53." 且以 .ipk 或 .apk 结尾
# 同时排除 i18n 包(只匹配主包)
if echo "$asset_name" | grep -qE "^luci-app-bandix[^-]" && \
echo "$asset_name" | grep -qE "\.${preferred_ext}$" && \
echo "$asset_name" | grep -q "_${arch}\."; then
luci_download_url=$(echo "$luci_api_result" | jsonfilter -e "$.assets[$i].browser_download_url" 2>/dev/null)
break
fi
i=$((i + 1))
done
fi
if [ -n "$current_luci_version" ] && [ -n "$luci_latest_version" ]; then
if compare_versions "$current_luci_version" "$luci_latest_version"; then
luci_has_update="1"
fi
fi
fi
# 检查 bandix 更新
local bandix_latest_version=""
local bandix_has_update="0"
local bandix_update_url=""
local bandix_release_body=""
local bandix_download_url=""
local bandix_api_result=$(curl -s --connect-timeout 10 --max-time 30 "https://api.github.com/repos/timsaya/openwrt-bandix/releases/latest" 2>/dev/null)
if [ $? -eq 0 ] && [ -n "$bandix_api_result" ]; then
bandix_latest_version=$(echo "$bandix_api_result" | jsonfilter -e '$.tag_name' 2>/dev/null)
# 去掉 v 前缀
bandix_latest_version=${bandix_latest_version#v}
bandix_update_url=$(echo "$bandix_api_result" | jsonfilter -e '$.html_url' 2>/dev/null)
bandix_release_body=$(echo "$bandix_api_result" | jsonfilter -e '$.body' 2>/dev/null)
# 获取架构信息和包管理器类型
local arch=$(get_system_arch)
local pkg_manager=$(get_package_manager)
# 根据包管理器类型确定优先的文件扩展名
local preferred_ext=""
if [ "$pkg_manager" = "opkg" ]; then
preferred_ext="ipk"
elif [ "$pkg_manager" = "apk" ]; then
preferred_ext="apk"
fi
# 查找对应架构的 ipk/apk 文件
# bandix 包格式bandix-版本-r修订号_架构.apk 或 bandix-版本-r修订号_架构.ipk
# 或者bandix_版本-r修订号_架构.apk 或 bandix_版本-r修订号_架构.ipk
# 例如bandix-0.10.2-r1_aarch64_cortex-a53.apk 或 bandix_0.10.2-r1_aarch64_cortex-a53.apk
local bandix_asset_count=0
# 先统计 assets 数量
if [ -n "$arch" ]; then
local i=0
while true; do
local asset_name=$(echo "$bandix_api_result" | jsonfilter -e "$.assets[$i].name" 2>/dev/null)
if [ -z "$asset_name" ]; then
break
fi
bandix_asset_count=$((bandix_asset_count + 1))
i=$((i + 1))
done
# 只查找匹配包管理器格式的架构特定包
# 如果找不到匹配格式的包,就不设置 download_url后续会报错
if [ -n "$preferred_ext" ] && [ "$bandix_asset_count" -gt 0 ]; then
i=0
while true; do
local asset_name=$(echo "$bandix_api_result" | jsonfilter -e "$.assets[$i].name" 2>/dev/null)
if [ -z "$asset_name" ]; then
break
fi
# 匹配格式bandix-*_架构.优先扩展名 或 bandix_*_架构.优先扩展名
# 例如bandix-0.10.2-r1_aarch64_cortex-a53.ipk 或 bandix_0.10.2-r1_aarch64_cortex-a53.ipk
if echo "$asset_name" | grep -qE "^(bandix-|bandix_)" && \
echo "$asset_name" | grep -qE "\.${preferred_ext}$" && \
echo "$asset_name" | grep -q "_${arch}\."; then
bandix_download_url=$(echo "$bandix_api_result" | jsonfilter -e "$.assets[$i].browser_download_url" 2>/dev/null)
break
fi
i=$((i + 1))
done
fi
fi
if [ -n "$current_bandix_version" ] && [ -n "$bandix_latest_version" ]; then
if compare_versions "$current_bandix_version" "$bandix_latest_version"; then
bandix_has_update="1"
fi
fi
fi
json_add_string "current_luci_version" "$current_luci_version"
json_add_string "current_bandix_version" "$current_bandix_version"
json_add_string "latest_luci_version" "$luci_latest_version"
json_add_string "latest_bandix_version" "$bandix_latest_version"
# 使用字符串 "1" 或 "0" 表示布尔值,在 JavaScript 中可以转换为布尔值
json_add_string "luci_has_update" "$luci_has_update"
json_add_string "bandix_has_update" "$bandix_has_update"
json_add_string "luci_update_url" "$luci_update_url"
json_add_string "bandix_update_url" "$bandix_update_url"
json_add_string "luci_release_body" "$luci_release_body"
json_add_string "bandix_release_body" "$bandix_release_body"
# 检查是否找到了下载链接,如果没找到则添加错误信息
local luci_error=""
local bandix_error=""
if [ -z "$luci_download_url" ] && [ "$luci_has_update" = "1" ]; then
local pkg_manager=$(get_package_manager)
local preferred_ext=""
if [ "$pkg_manager" = "opkg" ]; then
preferred_ext="ipk"
elif [ "$pkg_manager" = "apk" ]; then
preferred_ext="apk"
fi
if [ -n "$preferred_ext" ]; then
luci_error="No ${preferred_ext} package found for luci-app-bandix"
else
luci_error="Unknown package manager, cannot determine package format"
fi
fi
if [ -z "$bandix_download_url" ] && [ "$bandix_has_update" = "1" ]; then
local pkg_manager=$(get_package_manager)
local preferred_ext=""
if [ "$pkg_manager" = "opkg" ]; then
preferred_ext="ipk"
elif [ "$pkg_manager" = "apk" ]; then
preferred_ext="apk"
fi
if [ -n "$preferred_ext" ]; then
bandix_error="No ${preferred_ext} package found for bandix"
else
bandix_error="Unknown package manager, cannot determine package format"
fi
fi
json_add_string "luci_download_url" "$luci_download_url"
json_add_string "bandix_download_url" "$bandix_download_url"
json_add_string "luci_error" "$luci_error"
json_add_string "bandix_error" "$bandix_error"
# 添加调试信息:架构、包管理器和资产数量
local detected_arch=$(get_system_arch)
local detected_pkg_manager=$(get_package_manager)
json_add_string "detected_arch" "$detected_arch"
json_add_string "detected_pkg_manager" "$detected_pkg_manager"
json_add_int "luci_asset_count" "$asset_count"
json_add_int "bandix_asset_count" "$bandix_asset_count"
json_dump
json_cleanup
}
# 下载并安装更新
install_update() {
local package_type="$1" # "luci" 或 "bandix"
local download_url="$2"
if [ -z "$package_type" ] || [ -z "$download_url" ]; then
make_error "Missing parameters"
return
fi
json_init
# 步骤1: 下载文件到 /tmp
local filename=$(basename "$download_url")
local filepath="/tmp/$filename"
json_add_string "step" "downloading"
json_add_string "filepath" "$filepath"
# 使用 wget 下载文件
wget -q --timeout=10 -O "$filepath" "$download_url" 2>&1
local download_exit_code=$?
if [ $download_exit_code -ne 0 ] || [ ! -f "$filepath" ]; then
rm -f "$filepath"
json_add_boolean "success" 0
json_add_string "error" "Failed to download package"
json_add_string "step" "download_failed"
json_dump
json_cleanup
return
fi
# 步骤2: 更新软件仓库
json_add_string "step" "updating_repo"
if command -v opkg >/dev/null 2>&1; then
opkg update 2>/dev/null || true
elif command -v apk >/dev/null 2>&1; then
apk update 2>/dev/null || true
fi
# 步骤3: 安装包
json_add_string "step" "installing"
local install_result=""
local install_exit_code=1
if command -v opkg >/dev/null 2>&1; then
install_result=$(opkg install "$filepath" 2>&1)
install_exit_code=$?
elif command -v apk >/dev/null 2>&1; then
install_result=$(apk add --allow-untrusted "$filepath" 2>&1)
install_exit_code=$?
else
rm -f "$filepath"
json_add_boolean "success" 0
json_add_string "error" "No package manager found (opkg or apk)"
json_add_string "step" "install_failed"
json_dump
json_cleanup
return
fi
# 清理下载的文件
rm -f "$filepath"
if [ $install_exit_code -ne 0 ]; then
json_add_boolean "success" 0
json_add_string "error" "Failed to install package"
json_add_string "output" "$install_result"
json_add_string "step" "install_failed"
json_dump
json_cleanup
return
fi
# 返回成功结果
json_add_boolean "success" 1
json_add_string "message" "Package installed successfully"
json_add_string "output" "$install_result"
json_add_string "step" "completed"
json_dump
json_cleanup
}
case "$1" in
list)
json_init
json_add_object "getStatus"
json_add_int "start_ms"
json_add_int "end_ms"
json_close_object
json_add_object "getMetrics"
json_add_string "mac"
json_close_object
json_add_object "setHostname"
json_add_string "mac"
json_add_string "hostname"
json_close_object
json_add_object "deleteDevice"
json_add_string "mac"
json_close_object
json_add_object "getConnection"
json_close_object
json_add_object "getConnectionFlows"
json_add_string "ip"
json_add_string "protocol"
json_add_string "state"
json_add_int "page"
json_add_int "page_size"
json_close_object
json_add_object "getDnsQueries"
json_add_string "domain"
json_add_string "device"
json_add_string "is_query"
json_add_string "dns_server"
json_add_string "query_type"
json_add_int "page"
json_add_int "page_size"
json_close_object
json_add_object "getDnsStats"
json_close_object
json_add_object "getTrafficUsageRanking"
json_add_int "start_ms"
json_add_int "end_ms"
json_add_string "network_type"
json_close_object
json_add_object "getTrafficUsageIncrements"
json_add_int "start_ms"
json_add_int "end_ms"
json_add_string "aggregation"
json_add_string "mac"
json_add_string "network_type"
json_close_object
json_add_object "getScheduleLimits"
json_close_object
json_add_object "setScheduleLimit"
json_add_string "mac"
json_add_string "start_time"
json_add_string "end_time"
json_add_string "days"
json_add_int "wan_rx_rate_limit"
json_add_int "wan_tx_rate_limit"
json_close_object
json_add_object "updateScheduleLimit"
json_add_string "id"
json_add_string "mac"
json_add_string "start_time"
json_add_string "end_time"
json_add_string "days"
json_add_int "wan_rx_rate_limit"
json_add_int "wan_tx_rate_limit"
json_close_object
json_add_object "deleteScheduleLimit"
json_add_string "id"
json_close_object
json_add_object "clearData"
json_close_object
json_add_object "restartService"
json_close_object
json_add_object "getVersion"
json_close_object
json_add_object "getSystemArch"
json_close_object
json_add_object "checkUpdate"
json_close_object
json_add_object "installUpdate"
json_add_string "package_type"
json_add_string "download_url"
json_close_object
json_add_object "getRateLimitWhitelist"
json_close_object
json_add_object "setRateLimitWhitelistEnabled"
json_add_string "enabled"
json_close_object
json_add_object "addRateLimitWhitelist"
json_add_string "mac"
json_close_object
json_add_object "deleteRateLimitWhitelist"
json_add_string "mac"
json_close_object
json_add_object "setDefaultRateLimit"
json_add_int "wan_rx_rate_limit"
json_add_int "wan_tx_rate_limit"
json_close_object
json_dump
json_cleanup
;;
call)
case "$2" in
getStatus)
start_ms=""
end_ms=""
input=""
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
start_ms="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$start_ms" ] && start_ms="$(echo "$input" | jsonfilter -e '$.start_ms' 2>/dev/null)"
end_ms="$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)"
[ -z "$end_ms" ] && end_ms="$(echo "$input" | jsonfilter -e '$.end_ms' 2>/dev/null)"
else
[ -n "$3" ] && start_ms="$3"
[ -n "$4" ] && end_ms="$4"
fi
get_device_status "$start_ms" "$end_ms"
;;
getMetrics)
# logger "luci.bandix: getMetrics called"
# 读取参数:优先 STDIN带超时避免阻塞其次位置参数$3
# 很奇怪收到的是 received input: {"mac":"70:e2:84:2b:9f:42","ubus_rpc_session":"4b0c5dcd9baf9963e4f13647fa71228a"}
mac=""
input=""
# 尝试读取一行 STDIN1 秒超时以避免在无输入场景下阻塞
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
# logger "luci.bandix: received input: $input"
# 支持数组或对象两种风格
mac="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$mac" ] && mac="$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)"
else
# 某些环境不会通过 STDIN 传参,改为读取位置参数
[ -n "$3" ] && mac="$3"
# logger "luci.bandix: received argv mac: $mac"
fi
get_metrics "$mac"
;;
setHostname)
# logger "luci.bandix: setHostname called"
# 从 stdin 读取 JSON 参数
read input
# logger "luci.bandix: received input: $input"
if [ -n "$input" ]; then
# 尝试解析数组格式的 JSON 参数 (LuCI RPC 通常使用数组格式)
# 参数格式: ["mac", "hostname"]
mac=$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)
hostname=$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)
# 如果数组格式解析失败,尝试对象格式
if [ -z "$mac" ]; then
mac=$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)
hostname=$(echo "$input" | jsonfilter -e '$.hostname' 2>/dev/null)
fi
# logger "luci.bandix: parsed - mac=$mac hostname=$hostname"
# 验证参数hostname可以为空
if [ -n "$mac" ]; then
# 调用主机名设置函数
set_device_hostname "$mac" "$hostname"
else
# logger "luci.bandix: setHostname MAC参数缺失"
make_error "Missing MAC address parameter"
fi
else
# logger "luci.bandix: setHostname 没有接收到输入"
make_error "No input received"
fi
;;
deleteDevice)
mac=""
input=""
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
mac="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$mac" ] && mac="$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)"
else
[ -n "$3" ] && mac="$3"
fi
delete_device "$mac"
;;
getConnection)
# logger "luci.bandix: getConnection called"
get_connection_devices
;;
getConnectionFlows)
ip=""
protocol=""
state=""
page=""
page_size=""
input=""
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
ip="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$ip" ] && ip="$(echo "$input" | jsonfilter -e '$.ip' 2>/dev/null)"
protocol="$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)"
[ -z "$protocol" ] && protocol="$(echo "$input" | jsonfilter -e '$.protocol' 2>/dev/null)"
state="$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null)"
[ -z "$state" ] && state="$(echo "$input" | jsonfilter -e '$.state' 2>/dev/null)"
page="$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null)"
[ -z "$page" ] && page="$(echo "$input" | jsonfilter -e '$.page' 2>/dev/null)"
page_size="$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)"
[ -z "$page_size" ] && page_size="$(echo "$input" | jsonfilter -e '$.page_size' 2>/dev/null)"
else
[ -n "$3" ] && ip="$3"
[ -n "$4" ] && protocol="$4"
[ -n "$5" ] && state="$5"
[ -n "$6" ] && page="$6"
[ -n "$7" ] && page_size="$7"
fi
get_connection_flows "$ip" "$protocol" "$state" "$page" "$page_size"
;;
getDnsQueries)
# logger "luci.bandix: getDnsQueries called"
# 从 stdin 读取 JSON 参数
read input
if [ -n "$input" ]; then
# 解析参数
domain=$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)
[ -z "$domain" ] && domain=$(echo "$input" | jsonfilter -e '$.domain' 2>/dev/null)
device=$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)
[ -z "$device" ] && device=$(echo "$input" | jsonfilter -e '$.device' 2>/dev/null)
is_query=$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null)
[ -z "$is_query" ] && is_query=$(echo "$input" | jsonfilter -e '$.is_query' 2>/dev/null)
dns_server=$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null)
[ -z "$dns_server" ] && dns_server=$(echo "$input" | jsonfilter -e '$.dns_server' 2>/dev/null)
query_type=$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)
[ -z "$query_type" ] && query_type=$(echo "$input" | jsonfilter -e '$.query_type' 2>/dev/null)
page=$(echo "$input" | jsonfilter -e '$[5]' 2>/dev/null)
[ -z "$page" ] && page=$(echo "$input" | jsonfilter -e '$.page' 2>/dev/null)
page_size=$(echo "$input" | jsonfilter -e '$[6]' 2>/dev/null)
[ -z "$page_size" ] && page_size=$(echo "$input" | jsonfilter -e '$.page_size' 2>/dev/null)
get_dns_queries "$domain" "$device" "$is_query" "$dns_server" "$query_type" "$page" "$page_size"
else
get_dns_queries "" "" "" "" "" "" ""
fi
;;
getDnsStats)
# logger "luci.bandix: getDnsStats called"
get_dns_stats
;;
getTrafficUsageRanking)
start_ms=""
end_ms=""
network_type=""
input=""
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
start_ms="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$start_ms" ] && start_ms="$(echo "$input" | jsonfilter -e '$.start_ms' 2>/dev/null)"
end_ms="$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)"
[ -z "$end_ms" ] && end_ms="$(echo "$input" | jsonfilter -e '$.end_ms' 2>/dev/null)"
network_type="$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null)"
[ -z "$network_type" ] && network_type="$(echo "$input" | jsonfilter -e '$.network_type' 2>/dev/null)"
else
[ -n "$3" ] && start_ms="$3"
[ -n "$4" ] && end_ms="$4"
[ -n "$5" ] && network_type="$5"
fi
get_traffic_usage_ranking "$start_ms" "$end_ms" "$network_type"
;;
getTrafficUsageIncrements)
start_ms=""
end_ms=""
aggregation=""
mac=""
network_type=""
input=""
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
start_ms="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$start_ms" ] && start_ms="$(echo "$input" | jsonfilter -e '$.start_ms' 2>/dev/null)"
end_ms="$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)"
[ -z "$end_ms" ] && end_ms="$(echo "$input" | jsonfilter -e '$.end_ms' 2>/dev/null)"
aggregation="$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null)"
[ -z "$aggregation" ] && aggregation="$(echo "$input" | jsonfilter -e '$.aggregation' 2>/dev/null)"
mac="$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null)"
[ -z "$mac" ] && mac="$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)"
network_type="$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)"
[ -z "$network_type" ] && network_type="$(echo "$input" | jsonfilter -e '$.network_type' 2>/dev/null)"
else
[ -n "$3" ] && start_ms="$3"
[ -n "$4" ] && end_ms="$4"
[ -n "$5" ] && aggregation="$5"
[ -n "$6" ] && mac="$6"
[ -n "$7" ] && network_type="$7"
fi
get_traffic_usage_increments "$start_ms" "$end_ms" "$aggregation" "$mac" "$network_type"
;;
getScheduleLimits)
# logger "luci.bandix: getScheduleLimits called"
get_schedule_limits
;;
setScheduleLimit)
# logger "luci.bandix: setScheduleLimit called"
# 从 stdin 读取 JSON 参数
read input
# logger "luci.bandix: received input: $input"
if [ -n "$input" ]; then
# 尝试解析数组格式的 JSON 参数 (LuCI RPC 通常使用数组格式)
# 参数格式: ["mac", "start_time", "end_time", "days", wan_tx_rate_limit, wan_rx_rate_limit]
mac=$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)
start_time=$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)
end_time=$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null)
# days 可能是 JSON 数组字符串,需要特殊处理
days=$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null)
# 如果提取失败,尝试作为字符串提取
if [ -z "$days" ]; then
days=$(echo "$input" | jsonfilter -e '$[3]' -s 2>/dev/null)
fi
wan_tx_rate_limit=$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)
wan_rx_rate_limit=$(echo "$input" | jsonfilter -e '$[5]' 2>/dev/null)
# 如果数组格式解析失败,尝试对象格式
if [ -z "$mac" ]; then
mac=$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)
start_time=$(echo "$input" | jsonfilter -e '$.start_time' 2>/dev/null)
end_time=$(echo "$input" | jsonfilter -e '$.end_time' 2>/dev/null)
# days 可能是 JSON 数组字符串
days=$(echo "$input" | jsonfilter -e '$.days' 2>/dev/null)
# 如果提取失败,尝试提取为 JSON 数组
if [ -z "$days" ]; then
days=$(echo "$input" | jsonfilter -e '$.days' -s 2>/dev/null)
fi
wan_tx_rate_limit=$(echo "$input" | jsonfilter -e '$.wan_tx_rate_limit' 2>/dev/null)
wan_rx_rate_limit=$(echo "$input" | jsonfilter -e '$.wan_rx_rate_limit' 2>/dev/null)
fi
# 如果 days 还不是有效的 JSON 数组格式,尝试从原始输入中提取
if [ -z "$days" ] || ! echo "$days" | grep -q '^\['; then
# 尝试直接提取 days 字段的值
days=$(echo "$input" | sed -n 's/.*"days"[[:space:]]*:[[:space:]]*\(\[.*\]\).*/\1/p' 2>/dev/null)
fi
# logger "luci.bandix: parsed - mac=$mac start=$start_time end=$end_time days=$days tx=$wan_tx_rate_limit rx=$wan_rx_rate_limit"
# 验证参数
if [ -n "$mac" ] && [ -n "$start_time" ] && [ -n "$end_time" ] && [ -n "$days" ] && [ -n "$wan_tx_rate_limit" ] && [ -n "$wan_rx_rate_limit" ]; then
# 调用定时限速设置函数
set_schedule_limit "$mac" "$start_time" "$end_time" "$days" "$wan_tx_rate_limit" "$wan_rx_rate_limit"
else
# logger "luci.bandix: setScheduleLimit 参数缺失或无效"
make_error "Missing or invalid parameters"
fi
else
# logger "luci.bandix: setScheduleLimit 没有接收到输入"
make_error "No input received"
fi
;;
updateScheduleLimit)
read input
if [ -n "$input" ]; then
id=$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)
mac=$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)
start_time=$(echo "$input" | jsonfilter -e '$[2]' 2>/dev/null)
end_time=$(echo "$input" | jsonfilter -e '$[3]' 2>/dev/null)
days=$(echo "$input" | jsonfilter -e '$[4]' 2>/dev/null)
if [ -z "$days" ]; then
days=$(echo "$input" | jsonfilter -e '$[4]' -s 2>/dev/null)
fi
wan_rx_rate_limit=$(echo "$input" | jsonfilter -e '$[5]' 2>/dev/null)
wan_tx_rate_limit=$(echo "$input" | jsonfilter -e '$[6]' 2>/dev/null)
if [ -z "$id" ]; then
id=$(echo "$input" | jsonfilter -e '$.id' 2>/dev/null)
mac=$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)
start_time=$(echo "$input" | jsonfilter -e '$.start_time' 2>/dev/null)
end_time=$(echo "$input" | jsonfilter -e '$.end_time' 2>/dev/null)
days=$(echo "$input" | jsonfilter -e '$.days' 2>/dev/null)
if [ -z "$days" ]; then
days=$(echo "$input" | jsonfilter -e '$.days' -s 2>/dev/null)
fi
wan_rx_rate_limit=$(echo "$input" | jsonfilter -e '$.wan_rx_rate_limit' 2>/dev/null)
wan_tx_rate_limit=$(echo "$input" | jsonfilter -e '$.wan_tx_rate_limit' 2>/dev/null)
fi
if [ -z "$days" ] || ! echo "$days" | grep -q '^\['; then
days=$(echo "$input" | sed -n 's/.*"days"[[:space:]]*:[[:space:]]*\(\[.*\]\).*/\1/p' 2>/dev/null)
fi
if [ -n "$id" ] && [ -n "$mac" ] && [ -n "$start_time" ] && [ -n "$end_time" ] && [ -n "$days" ] && [ -n "$wan_rx_rate_limit" ] && [ -n "$wan_tx_rate_limit" ]; then
update_schedule_limit "$id" "$mac" "$start_time" "$end_time" "$days" "$wan_rx_rate_limit" "$wan_tx_rate_limit"
else
make_error "Missing or invalid parameters"
fi
else
make_error "No input received"
fi
;;
deleteScheduleLimit)
read input
if [ -n "$input" ]; then
id=$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)
[ -z "$id" ] && id=$(echo "$input" | jsonfilter -e '$.id' 2>/dev/null)
if [ -n "$id" ]; then
delete_schedule_limit "$id"
else
make_error "Missing id parameter"
fi
else
make_error "No input received"
fi
;;
clearData)
# logger "luci.bandix: clearData called"
clear_data
;;
restartService)
# logger "luci.bandix: restartService called"
restart_service
;;
getVersion)
# logger "luci.bandix: getVersion called"
get_version
;;
getSystemArch)
# logger "luci.bandix: getSystemArch called"
local arch=$(get_system_arch)
json_init
json_add_string "arch" "$arch"
json_dump
json_cleanup
;;
checkUpdate)
# logger "luci.bandix: checkUpdate called"
check_update
;;
installUpdate)
# logger "luci.bandix: installUpdate called"
# 从 stdin 读取 JSON 参数
read input
if [ -n "$input" ]; then
# 解析参数
package_type=$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)
[ -z "$package_type" ] && package_type=$(echo "$input" | jsonfilter -e '$.package_type' 2>/dev/null)
download_url=$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)
[ -z "$download_url" ] && download_url=$(echo "$input" | jsonfilter -e '$.download_url' 2>/dev/null)
if [ -n "$package_type" ] && [ -n "$download_url" ]; then
install_update "$package_type" "$download_url"
else
make_error "Missing package_type or download_url parameter"
fi
else
make_error "No input received"
fi
;;
getRateLimitWhitelist)
get_rate_limit_whitelist
;;
setRateLimitWhitelistEnabled)
enabled=""
input=""
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
enabled="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$enabled" ] && enabled="$(echo "$input" | jsonfilter -e '$.enabled' 2>/dev/null)"
else
[ -n "$3" ] && enabled="$3"
fi
set_rate_limit_whitelist_enabled "$enabled"
;;
addRateLimitWhitelist)
mac=""
input=""
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
mac="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$mac" ] && mac="$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)"
else
[ -n "$3" ] && mac="$3"
fi
add_rate_limit_whitelist_mac "$mac"
;;
deleteRateLimitWhitelist)
mac=""
input=""
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
mac="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$mac" ] && mac="$(echo "$input" | jsonfilter -e '$.mac' 2>/dev/null)"
else
[ -n "$3" ] && mac="$3"
fi
delete_rate_limit_whitelist_mac "$mac"
;;
setDefaultRateLimit)
wan_rx_rate_limit=""
wan_tx_rate_limit=""
input=""
if read -t 1 -r input; then
:
fi
if [ -n "$input" ]; then
wan_rx_rate_limit="$(echo "$input" | jsonfilter -e '$[0]' 2>/dev/null)"
[ -z "$wan_rx_rate_limit" ] && wan_rx_rate_limit="$(echo "$input" | jsonfilter -e '$.wan_rx_rate_limit' 2>/dev/null)"
wan_tx_rate_limit="$(echo "$input" | jsonfilter -e '$[1]' 2>/dev/null)"
[ -z "$wan_tx_rate_limit" ] && wan_tx_rate_limit="$(echo "$input" | jsonfilter -e '$.wan_tx_rate_limit' 2>/dev/null)"
else
[ -n "$3" ] && wan_rx_rate_limit="$3"
[ -n "$4" ] && wan_tx_rate_limit="$4"
fi
set_default_rate_limit "$wan_rx_rate_limit" "$wan_tx_rate_limit"
;;
esac
;;
esac