From 4c53474ffe0e196057243baf863af9c0201dbca5 Mon Sep 17 00:00:00 2001 From: v6ole Date: Fri, 19 Jun 2026 00:41:24 +0800 Subject: [PATCH] Add FastLink_Lite custom rules enhancement - Add custom rules editor to LUCI web interface - Support 32 rule types (DOMAIN, IP-CIDR, GEOSITE, etc.) - Rule CRUD, sorting, batch import, templates - netflow-rules script for mihomo config injection - Standalone IPK repackaging support --- .gitignore | 28 + AGENTS.md | 36 + scripts/build_luci.py | 51 + scripts/deploy.py | 29 + scripts/repack_netflow_ipk.py | 107 ++ scripts/router_main_lf.htm | 2764 +++++++++++++++++++++++++++++++ src/bin/netflow-rules | 105 ++ src/init.d/netflow | 29 + src/luci/controller/netflow.lua | 324 ++++ src/luci/view/netflow/main.htm | 2079 +++++++++++++++++++++++ 10 files changed, 5552 insertions(+) create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 scripts/build_luci.py create mode 100644 scripts/deploy.py create mode 100644 scripts/repack_netflow_ipk.py create mode 100644 scripts/router_main_lf.htm create mode 100644 src/bin/netflow-rules create mode 100644 src/init.d/netflow create mode 100644 src/luci/controller/netflow.lua create mode 100644 src/luci/view/netflow/main.htm diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ee49e7c --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +# Large binary files +*.ipk +*.tar.gz +*.dat +*.mmdb +*.metadb +*.exe +*.dll + +# Temp / build dirs +_tmp_extract/ +_clean/ +_repack_verify/ +__pycache__/ +*.pyc + +# External projects +clash-verge-rev-dev/ +zashboard-main/ + +# Personal files +*.backup.json +reorganize*.ps1 +reorganize*.py + +# OS files +.DS_Store +Thumbs.db diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..0109d02 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,36 @@ +# AGENTS.md + +## 项目概述 + +本项目为**个人电脑助手**,协助完成软件编译构建、软件安装配置、系统故障排查、环境调试等日常开发运维任务。 + +## 运行环境 + +- **操作系统**:Windows(PowerShell 7+) +- **包管理**:scoop / winget / choco 视系统已有工具选用 +- **Shell**:pwsh(PowerShell 7+),避免 cmd / bash 语法 + +## 网络代理 + +遇到 GitHub、npm、cargo、pip、curl 等网络连接问题(如超时、443 错误、resolve 失败)时,通过以下代理访问: + +``` +http://10.10.10.2:7890 +``` + +使用方式示例: +- Git: `git config --global http.proxy http://10.10.10.2:7890` +- 环境变量: `$env:HTTP_PROXY='http://10.10.10.2:7890'; $env:HTTPS_PROXY='http://10.10.10.2:7890'` +- curl/wget: `--proxy http://10.10.10.2:7890` +- npm: `npm config set proxy http://10.10.10.2:7890` + +无需代理时记得取消设置。 + +## 核心原则 + +1. **安全优先**:不执行危险命令(rm -rf 等)不确认用户意图之前,下载/安装软件前说明来源 +2. **解释操作**:运行非琐碎命令时,简要说明该命令做什么 +3. **诊断先行**:排查问题先收集信息(日志、配置、版本),再给方案 +4. **最小改动**:修改文件优先用编辑工具而非整体重写 +5. **遇到权限问题**:提示用户使用管理员权限运行,或提供提权命令 +6. **中文优先**:思考过程与回复均使用中文,技术术语可保留英文原名 diff --git a/scripts/build_luci.py b/scripts/build_luci.py new file mode 100644 index 0000000..ca5780e --- /dev/null +++ b/scripts/build_luci.py @@ -0,0 +1,51 @@ +"""Build LuCI template version from working file.""" +with open(r'E:\AI Project\WangAgent\_tmp_extract\router_live.htm', 'r', encoding='utf-8') as f: + c = f.read() + +# Remove standalone HTML wrapper (everything before ... +# LuCI template needs: <%+header%>\n\n...\n\n<%+footer%> + +# Find the actual content +style_start = c.find(' + +
+
+

FastLink_Lite

+

Proxy Client

+
+ +
+
+ 加载中... +
+ +
+
+
🔑 账户登录
+
+ + +
+
+ + +
+ +
+
+ +
+ +
+
+
上传
+
- -
+
+
+
下载
+
- -
+
+
+
上传总量
+
- -
+
+
+
下载总量
+
- -
+
+
+
活动连接
+
- -
+
+
+
内存占用
+
- -
+
+
+
CPU 占用
+
- -
+
+
+
系统负载
+
- -
+
+
+ +
+
+
+ 代理未运行 + +
+ +
+ + +
+ + +
+ 🌐 局域网代理 + + +
+
+
+
+
局域网代理
+
开启后局域网设备流量走代理
+
+
+ 局域网地址 ,端口 7890 +
+
+
+
+
+
+
IPv6 代理
+
开启后 IPv6 流量也走代理(需节点支持)
+
+
+
+
+
+
+ ⚡ 运行模式 +
+
增强
+
TUN
+
混合
+ +
+
+
+
+
+ +
+
+
+ ⚙ 核心管理 +
+ + + +
+
+
+
+
+
核心状态
+
检测中...
+
+
+
设备架构
+
-
+
+
+
当前版本
+
-
+
+
+
最新版本
+
-
+
+
+
+ + +
+
+ + + +
+
+
+
+
+
+
+
+ +
+
👤 账户信息
+ +
+
+ + + + + + +
+
+ + +
+
+ 🌐 DNS 配置 + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ +
+
+ 📝 自定义规则 +
+ + + + +
+
+ +
+ + +
+
+ + +
+
+ +
+ + + +
+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
暂无自定义规则
+
+
+ + +
+
+ 🎬 流媒体解锁 + +
+
+
点击"检测全部"开始
+
+
+ +
+
+ 📋 实时日志 +
+ + +
+
+
+
+
全部
+
INFO
+
WARN
+
ERROR
+
DEBUG
+
+ +
+
暂无日志
+
+
+ + +
+
+
+ +
+
+ 🔗 活跃连接 +
+ + +
+
+
+
+ + +
+
+
+
点击刷新获取连接数据
+
+
+
+ + + + +
+ + + +<%+footer%> \ No newline at end of file diff --git a/src/bin/netflow-rules b/src/bin/netflow-rules new file mode 100644 index 0000000..8cce0d0 --- /dev/null +++ b/src/bin/netflow-rules @@ -0,0 +1,105 @@ +#!/bin/sh +# netflow-rules — 从 UCI 读取自定义规则,注入 mihomo config.yaml +# 用法: netflow-rules → 只生成 /tmp/netflow_custom_rules.yaml +# netflow-rules inject → 生成并注入到 config.yaml 中 +# netflow-rules reload → 注入后通过 mihomo API 热重载 + +CONFIG="/etc/netflow/config.yaml" +CUSTOM_RULES="/tmp/netflow_custom_rules.yaml" +BAK="${CONFIG}.bak" + +API_PORT=$(uci -q get netflow.config.api_port 2>/dev/null || echo "9091") +API_SECRET=$(uci -q get netflow.config.api_secret 2>/dev/null || echo "netflow_secret") + +generate() { + >"$CUSTOM_RULES" + count=0 + for sec in $(uci -q show netflow 2>/dev/null | grep '^netflow\.@custom_rule\[' | sed 's/=.*//'); do + t=$(uci -q get "${sec}.type" 2>/dev/null) + v=$(uci -q get "${sec}.value" 2>/dev/null) + p=$(uci -q get "${sec}.policy" 2>/dev/null) + if [ -n "$t" ] && [ -n "$v" ] && [ -n "$p" ]; then + echo " - ${t},${v},${p}" >> "$CUSTOM_RULES" + count=$((count + 1)) + fi + done + return $count +} + +inject() { + generate + if [ $? -eq 0 ]; then + logger -t netflow-rules "no custom rules defined, skipping" + rm -f "$CUSTOM_RULES" + return 0 + fi + + if [ ! -f "$CONFIG" ] || [ ! -s "$CONFIG" ]; then + logger -t netflow-rules "config.yaml not found or empty, skip inject" + return 0 + fi + + MATCH_LINE=$(grep -n -m1 'MATCH,' "$CONFIG" 2>/dev/null | cut -d: -f1) + if [ -z "$MATCH_LINE" ]; then + logger -t netflow-rules "no MATCH rule found in config.yaml, skip inject" + return 0 + fi + + TMP_CONFIG="/tmp/netflow_config_patched.yaml" + cp "$CONFIG" "$BAK" + + head -n $((MATCH_LINE - 1)) "$CONFIG" > "$TMP_CONFIG" + cat "$CUSTOM_RULES" >> "$TMP_CONFIG" + tail -n +${MATCH_LINE} "$CONFIG" >> "$TMP_CONFIG" + + if [ -s "$TMP_CONFIG" ]; then + mv "$TMP_CONFIG" "$CONFIG" + logger -t netflow-rules "custom rules injected into config.yaml" + else + logger -t netflow-rules "ERROR: patched config is empty, restoring backup" + cp "$BAK" "$CONFIG" + fi + + rm -f "$CUSTOM_RULES" +} + +reload_mihomo() { + if command -v curl >/dev/null 2>&1; then + curl -s -X PUT \ + -H "Authorization: Bearer ${API_SECRET}" \ + -H "Content-Type: application/json" \ + -d "{\"path\":\"${CONFIG}\"}" \ + "http://127.0.0.1:${API_PORT}/configs" >/dev/null 2>&1 + ret=$? + elif command -v wget >/dev/null 2>&1; then + wget -qO- --method=PUT \ + --header="Authorization: Bearer ${API_SECRET}" \ + --header="Content-Type: application/json" \ + --body-data="{\"path\":\"${CONFIG}\"}" \ + "http://127.0.0.1:${API_PORT}/configs" >/dev/null 2>&1 + ret=$? + else + logger -t netflow-rules "curl/wget not found, cannot reload mihomo" + return 1 + fi + + if [ $ret -eq 0 ]; then + logger -t netflow-rules "mihomo config reloaded" + else + logger -t netflow-rules "mihomo reload failed (code=${ret})" + fi + return $ret +} + +case "${1:-}" in + inject) + inject + ;; + reload) + inject + reload_mihomo + ;; + *) + generate + ;; +esac diff --git a/src/init.d/netflow b/src/init.d/netflow new file mode 100644 index 0000000..5a39b3c --- /dev/null +++ b/src/init.d/netflow @@ -0,0 +1,29 @@ +#!/bin/sh /etc/rc.common + +START=99 +STOP=10 +USE_PROCD=1 + +start_service() { + if [ ! -x "/usr/bin/netflow" ]; then + logger -t netflow "binary not found: /usr/bin/netflow" + return 1 + fi + + procd_open_instance + procd_set_param command /usr/bin/netflow + procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5} + procd_set_param stdout 1 + procd_set_param stderr 1 + procd_close_instance + + logger -t netflow "service started" +} + +stop_service() { + logger -t netflow "service stopping" +} + +service_triggers() { + procd_add_reload_trigger "netflow" +} diff --git a/src/luci/controller/netflow.lua b/src/luci/controller/netflow.lua new file mode 100644 index 0000000..9011ae3 --- /dev/null +++ b/src/luci/controller/netflow.lua @@ -0,0 +1,324 @@ +module("luci.controller.netflow", package.seeall) + +local sys = require "luci.sys" +local http = require "luci.http" +local uci = require "luci.model.uci".cursor() + +--- 调用本机 netflow 后端:优先 curl(与 Makefile 依赖一致),失败时用 wget 兜底(部分魔改固件无 curl)。 +local function call_backend_post(action, json_file_path) + local port = uci:get("netflow", "config", "backend_port") or "9190" + if not port:match("^%d+$") then port = "9190" end + local url = string.format("http://127.0.0.1:%s/api/%s", port, action) + local curl = string.format( + "curl -sS --connect-timeout 8 --max-time 45 -X POST '%s' -H 'Content-Type: application/json' -d @%s 2>/dev/null", + url, json_file_path + ) + local out = sys.exec(curl) + if out and out ~= "" then + return out + end + local wget = string.format( + "wget -qO- --timeout=50 --header='Content-Type: application/json' --post-file=%s '%s' 2>/dev/null", + json_file_path, url + ) + return sys.exec(wget) or "" +end + +function index() + -- OpenWrt 25+ / LuCI(js)按 rpcd ACL 过滤菜单;键名须与 /usr/share/rpcd/acl.d/*.json 顶层键一致(CI 在 pkg_tag≠netflow 时改为 luci-app-) + entry({"admin", "services", "netflow"}, template("netflow/main"), _("FastLink_Lite"), 80).acl_depends = { "luci-app-flclient" } + entry({"admin", "services", "netflow", "api"}, call("action_api"), nil).acl_depends = { "luci-app-flclient" } + entry({"admin", "services", "netflow", "upload_core"}, call("action_upload_core"), nil).acl_depends = { "luci-app-flclient" } +end + +function action_api() + local action = http.formvalue("action") or "" + + if not action:match("^[%w_]+$") then + http.prepare_content("application/json") + http.write('{"status":"error","message":"invalid action"}') + return + end + + -- 自定义规则 API(本地 UCI 操作,不经过 Go 后端) + if action == "rule_list" then + action_rule_list_local() + return + elseif action == "rule_add" then + action_rule_add_local() + return + elseif action == "rule_delete" then + action_rule_delete_local() + return + elseif action == "rule_update" then + action_rule_update_local() + return + elseif action == "rule_sort" then + action_rule_sort_local() + return + elseif action == "rule_import" then + action_rule_import_local() + return + elseif action == "rule_templates" then + action_rule_templates_local() + return + elseif action == "rule_apply" then + action_rule_apply_local() + return + end + + local function urldecode(s) + if not s then return s end + s = s:gsub('+', ' ') + s = s:gsub('%%(%x%x)', function(h) return string.char(tonumber(h, 16)) end) + return s + end + + local params = {} + local known_keys = { + "email", "password", "group", "node", "mode", "state", "run_mode", "source" + } + for _, key in ipairs(known_keys) do + local val = http.formvalue(key) + if val and val ~= "" then + params[key] = urldecode(val) + end + end + + local jsonc = require "luci.jsonc" + local json_body = jsonc.stringify(params) or "{}" + + local tmp = os.tmpname() + local f = io.open(tmp, "w") + if f then + f:write(json_body) + f:close() + end + + local result = call_backend_post(action, tmp) + os.remove(tmp) + + if not result or result == "" then + result = '{"status":"error","message":"后端未响应,请检查服务是否运行(或安装 curl/wget)"}' + end + + http.prepare_content("application/json") + http.write(result) +end + +-- ===== 自定义规则 API ===== + +function action_rule_list_local() + local rules = {} + uci:foreach("netflow", "custom_rule", function(s) + if s[".name"] then + table.insert(rules, {id = s[".name"], type = s.type or "", value = s.value or "", policy = s.policy or ""}) + end + end) + local jsonc = require "luci.jsonc" + http.prepare_content("application/json") + http.write(jsonc.stringify({status = "success", rules = rules})) +end + +function action_rule_add_local() + local t = http.formvalue("type") or "" + local v = http.formvalue("value") or "" + local p = http.formvalue("policy") or "" + if t == "" or v == "" or p == "" then + http.prepare_content("application/json") + http.write('{"status":"error","message":"参数不完整"}') + return + end + local section = uci:add("netflow", "custom_rule") + uci:set("netflow", section, "type", t) + uci:set("netflow", section, "value", v) + uci:set("netflow", section, "policy", p) + uci:commit("netflow") + local jsonc = require "luci.jsonc" + http.prepare_content("application/json") + http.write(jsonc.stringify({status = "success", message = "规则已添加", id = section})) +end + +function action_rule_delete_local() + local id = http.formvalue("id") or "" + if id == "" then + http.prepare_content("application/json") + http.write('{"status":"error","message":"缺少规则ID"}') + return + end + uci:delete("netflow", id) + uci:commit("netflow") + http.prepare_content("application/json") + http.write('{"status":"success","message":"规则已删除"}') +end + +function action_rule_update_local() + local id = http.formvalue("id") or "" + local t = http.formvalue("type") or "" + local v = http.formvalue("value") or "" + local p = http.formvalue("policy") or "" + if id == "" or t == "" or v == "" or p == "" then + http.prepare_content("application/json") + http.write('{"status":"error","message":"参数不完整"}') + return + end + uci:set("netflow", id, "type", t) + uci:set("netflow", id, "value", v) + uci:set("netflow", id, "policy", p) + uci:commit("netflow") + local jsonc = require "luci.jsonc" + http.prepare_content("application/json") + http.write(jsonc.stringify({status = "success", message = "规则已更新"})) +end + +function action_rule_sort_local() + local id = http.formvalue("id") or "" + local dir = http.formvalue("dir") or "" + if id == "" or (dir ~= "up" and dir ~= "down") then + http.prepare_content("application/json") + http.write('{"status":"error","message":"参数错误"}') + return + end + local rules = {} + uci:foreach("netflow", "custom_rule", function(s) + if s[".name"] then + table.insert(rules, {id = s[".name"], type = s.type or "", value = s.value or "", policy = s.policy or ""}) + end + end) + local idx = nil + for i, r in ipairs(rules) do if r.id == id then idx = i; break end end + if not idx then + http.prepare_content("application/json") + http.write('{"status":"error","message":"规则不存在"}') + return + end + local swap = nil + if dir == "up" and idx > 1 then swap = idx - 1 + elseif dir == "down" and idx < #rules then swap = idx + 1 end + if not swap then + http.prepare_content("application/json") + http.write('{"status":"error","message":"已在边界"}') + return + end + rules[idx], rules[swap] = rules[swap], rules[idx] + uci:foreach("netflow", "custom_rule", function(s) + if s[".name"] then uci:delete("netflow", s[".name"]) end + end) + for _, r in ipairs(rules) do + local section = uci:add("netflow", "custom_rule") + uci:set("netflow", section, "type", r.type) + uci:set("netflow", section, "value", r.value) + uci:set("netflow", section, "policy", r.policy) + end + uci:commit("netflow") + http.prepare_content("application/json") + http.write('{"status":"success","message":"排序已更新"}') +end + +function action_rule_import_local() + local text = http.formvalue("text") or "" + if text == "" then + http.prepare_content("application/json") + http.write('{"status":"error","message":"内容为空"}') + return + end + local count = 0 + for line in text:gmatch("[^\r\n]+") do + line = line:match("^%s*(.-)%s*$") + if line ~= "" and line:sub(1,1) ~= "#" then + local parts = {} + for p in line:gmatch("[^,]+") do table.insert(parts, p:match("^%s*(.-)%s*$")) end + local tp, val, pol + if #parts >= 3 then tp, val, pol = parts[1], parts[2], parts[3] + elseif #parts == 2 then tp, val, pol = "DOMAIN-SUFFIX", parts[1], parts[2] end + if tp and val and pol then + local section = uci:add("netflow", "custom_rule") + uci:set("netflow", section, "type", tp) + uci:set("netflow", section, "value", val) + uci:set("netflow", section, "policy", pol) + count = count + 1 + end + end + end + uci:commit("netflow") + local jsonc = require "luci.jsonc" + http.prepare_content("application/json") + http.write(jsonc.stringify({status = "success", message = "已导入 " .. count .. " 条规则", count = count})) +end + +function action_rule_templates_local() + local templates = { + {name = "屏蔽常见广告域名", rules = { + {type = "DOMAIN-SUFFIX", value = "doubleclick.net", policy = "REJECT"}, + {type = "DOMAIN-SUFFIX", value = "googlesyndication.com", policy = "REJECT"}, + {type = "DOMAIN-SUFFIX", value = "googleadservices.com", policy = "REJECT"}, + {type = "DOMAIN-SUFFIX", value = "adservice.google.com", policy = "REJECT"}, + {type = "DOMAIN-SUFFIX", value = "googletagmanager.com", policy = "REJECT"}, + }}, + {name = "屏蔽 iOS 更新", rules = { + {type = "DOMAIN-SUFFIX", value = "mesu.apple.com", policy = "REJECT"}, + {type = "DOMAIN-SUFFIX", value = "appldnld.apple.com", policy = "REJECT"}, + {type = "DOMAIN-SUFFIX", value = "gdmf.apple.com", policy = "REJECT"}, + }}, + {name = "国内直连", rules = { + {type = "DOMAIN-SUFFIX", value = "cn", policy = "DIRECT"}, + {type = "DOMAIN-KEYWORD", value = "baidu", policy = "DIRECT"}, + {type = "DOMAIN-KEYWORD", value = "taobao", policy = "DIRECT"}, + {type = "DOMAIN-KEYWORD", value = "alibaba", policy = "DIRECT"}, + {type = "DOMAIN-SUFFIX", value = "189.cn", policy = "DIRECT"}, + }}, + {name = "AI 工具走代理", rules = { + {type = "DOMAIN-SUFFIX", value = "openai.com", policy = "Proxy"}, + {type = "DOMAIN-SUFFIX", value = "claude.ai", policy = "Proxy"}, + {type = "DOMAIN-SUFFIX", value = "chatgpt.com", policy = "Proxy"}, + {type = "DOMAIN-SUFFIX", value = "anthropic.com", policy = "Proxy"}, + {type = "DOMAIN-SUFFIX", value = "gemini.google.com", policy = "Proxy"}, + {type = "DOMAIN-SUFFIX", value = "perplexity.ai", policy = "Proxy"}, + }}, + } + local jsonc = require "luci.jsonc" + http.prepare_content("application/json") + http.write(jsonc.stringify({status = "success", templates = templates})) +end + +function action_rule_apply_local() + sys.call("/usr/bin/netflow-rules reload") + http.prepare_content("application/json") + http.write('{"status":"success","message":"规则已应用并热重载"}') +end + +function action_upload_core() + local fp + local upload_path = "/tmp/mihomo_upload" + + http.setfilehandler(function(meta, chunk, eof) + if not fp and meta and meta.name == "corefile" then + fp = io.open(upload_path, "w") + end + if fp and chunk then + fp:write(chunk) + end + if fp and eof then + fp:close() + end + end) + + http.formvalue("corefile") + + local empty_json = os.tmpname() + local ef = io.open(empty_json, "w") + if ef then + ef:write("{}") + ef:close() + end + + local result = call_backend_post("core_install_upload", empty_json) + os.remove(empty_json) + + if not result or result == "" then + result = '{"status":"error","message":"后端未响应,请检查服务是否运行(或安装 curl/wget)"}' + end + + http.prepare_content("application/json") + http.write(result) +end diff --git a/src/luci/view/netflow/main.htm b/src/luci/view/netflow/main.htm new file mode 100644 index 0000000..3a10f70 --- /dev/null +++ b/src/luci/view/netflow/main.htm @@ -0,0 +1,2079 @@ +<%+header%> + + + +
+
+

FastLink_Lite

+

Proxy Client

+
+ +
+
+ 加载中... +
+ +
+
+
🔑 账户登录
+
+ + +
+
+ + +
+ +
+
+ +
+
+
+
+ 代理未运行 + +
+ +
+ + +
+ +
+
+
+
+
局域网代理
+
开启后局域网设备流量走代理
+
+
+ 局域网地址 ,端口 7890 +
+
+
+
+
+
+
+
IPv6 代理
+
开启后 IPv6 流量也走代理(需节点支持)
+
+
+
+
+
+
+ ⚡ 运行模式 +
+
增强
+
TUN
+
混合
+ +
+
+
+
+
+ +
+
+ + + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+ ⚙ 核心管理 + +
+
+
+
+
核心状态
+
检测中...
+
+
+
设备架构
+
-
+
+
+
当前版本
+
-
+
+
+
最新版本
+
-
+
+
+
+ + +
+
+ + + +
+
+
+
+
+
+
+
+ +
+
👤 账户信息
+ +
+
+ + + + +
+
+ +
+
+ 📝 自定义规则 +
+ + + +
+
+
+ + + + + + + +
+
+ +
+ + +
+
+
+
+
暂无自定义规则
+
+
+ +
+
+ 📋 实时日志 + +
+
暂无日志
+
+
+ +
+
+
上传
+
- -
+
+
+
下载
+
- -
+
+
+
上传总量
+
- -
+
+
+
下载总量
+
- -
+
+
+
活动连接
+
- -
+
+
+
内存占用
+
- -
+
+
+
CPU 占用
+
- -
+
+
+
系统负载
+
- -
+
+
+
+ +
+
+ + + +<%+footer%>