From 9a8249c408c29cd22d4eb1d1f7db3d1eabfe5a09 Mon Sep 17 00:00:00 2001 From: action Date: Sat, 23 May 2026 14:30:42 +0800 Subject: [PATCH] update 2026-05-23 14:30:42 --- .../luasrc/controller/passwall.lua | 18 +++++ .../model/cbi/passwall/client/node_config.lua | 2 + .../cbi/passwall/server/type/hysteria2.lua | 15 ++-- .../model/cbi/passwall/server/type/ray.lua | 19 ++--- .../cbi/passwall/server/type/sing-box.lua | 24 ++++--- .../model/cbi/passwall/server/type/socks.lua | 9 ++- .../cbi/passwall/server/type/ss-rust.lua | 14 ++-- .../model/cbi/passwall/server/type/ssr.lua | 14 ++-- .../luasrc/model/cbi/passwall/server/user.lua | 28 ++++++-- luci-app-passwall/luasrc/passwall/api.lua | 9 +++ .../view/passwall/node_config/footer.htm | 4 +- .../view/passwall/node_config/header.htm | 2 +- .../view/passwall/server/config_footer.htm | 27 +++++++ .../view/passwall/server/config_header.htm | 56 +++++++++++++++ .../luasrc/controller/shadowsocksr.lua | 10 +-- .../root/etc/init.d/shadowsocksr | 33 +++++++-- .../root/usr/share/shadowsocksr/gfw2ipset.sh | 70 ++++++++++++++++--- 17 files changed, 283 insertions(+), 71 deletions(-) create mode 100644 luci-app-passwall/luasrc/view/passwall/server/config_footer.htm create mode 100644 luci-app-passwall/luasrc/view/passwall/server/config_header.htm diff --git a/luci-app-passwall/luasrc/controller/passwall.lua b/luci-app-passwall/luasrc/controller/passwall.lua index cc6c63ca..da4b8a40 100644 --- a/luci-app-passwall/luasrc/controller/passwall.lua +++ b/luci-app-passwall/luasrc/controller/passwall.lua @@ -59,6 +59,7 @@ function index() entry({"admin", "services", appname, "server_user"}, cbi(appname .. "/server/user")).leaf = true --[[ API ]] + entry({"admin", "services", appname, "server_user_update"}, call("server_user_update")).leaf = true entry({"admin", "services", appname, "server_user_status"}, call("server_user_status")).leaf = true entry({"admin", "services", appname, "server_user_log"}, call("server_user_log")).leaf = true entry({"admin", "services", appname, "server_get_log"}, call("server_get_log")).leaf = true @@ -730,6 +731,23 @@ function rollback_rules() http_write_json_ok() end +function server_user_update() + local id = http.formvalue("id") -- Node id + local data = http.formvalue("data") -- json new Data + if id and data then + local data_t = jsonParse(data) or {} + if next(data_t) then + for k, v in pairs(data_t) do + uci:set(appname .. "_server", id, k, v) + end + api.uci_save(uci, appname .. "_server") + http_write_json_ok() + return + end + end + http_write_json_error() +end + function server_user_status() local e = {} e.index = http.formvalue("index") diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua index 6e39b1b3..9cbb3a9e 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua @@ -14,6 +14,7 @@ formvalue_key = "cbid." .. appname .. "." .. arg[1] .. "." local header = Template(appname .. "/node_config/header") header.api = api +header.config = m.config header.section = arg[1] m:append(header) @@ -117,6 +118,7 @@ end local footer = Template(appname .. "/node_config/footer") footer.api = api +footer.config = m.config footer.section = arg[1] m:append(footer) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria2.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria2.lua index d015a8af..954ce7d3 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria2.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/hysteria2.lua @@ -1,7 +1,5 @@ local m, s = ... -local api = require "luci.passwall.api" - if not api.finded_com("hysteria") then return end @@ -10,21 +8,24 @@ local fs = api.fs local type_name = "Hysteria2" +-- [[ Hysteria2 ]] + +s.fields["type"]:value(type_name, "Hysteria2") + +if s.val["type"] and s.val["type"] ~= type_name then + return +end + local option_prefix = "hysteria2_" local function _n(name) return option_prefix .. name end --- [[ Hysteria2 ]] - -s.fields["type"]:value(type_name, "Hysteria2") - o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(Value, _n("port"), translate("Listen Port")) o.datatype = "port" -o.rmempty = false o:depends({ [_n("custom")] = false }) o = s:option(Flag, _n("realms"), translate("Realms")) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua index f2545104..437f11c7 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ray.lua @@ -1,15 +1,22 @@ local m, s = ... -local api = require "luci.passwall.api" - if not api.finded_com("xray") then return end -local fs = api.fs - local type_name = "Xray" +-- [[ Xray ]] + +s.fields["type"]:value(type_name, "Xray") +if not s.fields["type"].default then + s.fields["type"].default = type_name +end + +if s.val["type"] and s.val["type"] ~= type_name then + return +end + local option_prefix = "xray_" local function _n(name) @@ -24,10 +31,6 @@ local header_type_list = { "none", "srtp", "utp", "wechat-video", "dtls", "wireguard", "dns" } --- [[ Xray ]] - -s.fields["type"]:value(type_name, "Xray") - o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(ListValue, _n("protocol"), translate("Protocol")) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua index ce5766fc..c7ecb2ec 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/sing-box.lua @@ -1,34 +1,37 @@ local m, s = ... -local api = require "luci.passwall.api" - local singbox_bin = api.finded_com("sing-box") if not singbox_bin then return end -local fs = api.fs - -local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'") - local type_name = "sing-box" +-- [[ Sing-Box ]] + +s.fields["type"]:value(type_name, "Sing-Box") +if not s.fields["type"].default then + s.fields["type"].default = type_name +end + +if s.val["type"] and s.val["type"] ~= type_name then + return +end + local option_prefix = "singbox_" local function _n(name) return option_prefix .. name end +local singbox_tags = luci.sys.exec(singbox_bin .. " version | grep 'Tags:' | awk '{print $2}'") + local ss_method_list = { "none", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" } --- [[ Sing-Box ]] - -s.fields["type"]:value(type_name, "Sing-Box") - o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(ListValue, _n("protocol"), translate("Protocol")) @@ -55,7 +58,6 @@ o:depends({ [_n("custom")] = false }) o = s:option(Value, _n("port"), translate("Listen Port")) o.datatype = "port" -o.rmempty = false o:depends({ [_n("custom")] = false }) o = s:option(Flag, _n("auth"), translate("Auth")) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/socks.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/socks.lua index 82cd0524..bfb00061 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/socks.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/socks.lua @@ -1,7 +1,5 @@ local m, s = ... -local api = require "luci.passwall.api" - if not api.is_finded("microsocks") then return end @@ -17,6 +15,13 @@ end -- [[ microsocks ]] s.fields["type"]:value(type_name, "Socks") +if not s.fields["type"].default then + s.fields["type"].default = type_name +end + +if s.val["type"] and s.val["type"] ~= type_name then + return +end o = s:option(Value, _n("port"), translate("Listen Port")) o.datatype = "port" diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss-rust.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss-rust.lua index e3ea0652..ed2a2863 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss-rust.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ss-rust.lua @@ -1,13 +1,19 @@ local m, s = ... -local api = require "luci.passwall.api" - if not api.is_finded("ssserver") then return end local type_name = "SS-Rust" +-- [[ Shadowsocks Rust ]] + +s.fields["type"]:value(type_name, translate("Shadowsocks Rust")) + +if s.val["type"] and s.val["type"] ~= type_name then + return +end + local option_prefix = "ssrust_" local function _n(name) @@ -20,10 +26,6 @@ local ssrust_encrypt_method_list = { "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" } --- [[ Shadowsocks Rust ]] - -s.fields["type"]:value(type_name, translate("Shadowsocks Rust")) - o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(Value, _n("port"), translate("Listen Port")) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ssr.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ssr.lua index 45fb79d2..02a94f61 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ssr.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/type/ssr.lua @@ -1,13 +1,19 @@ local m, s = ... -local api = require "luci.passwall.api" - if not api.is_finded("ssr-server") then return end local type_name = "SSR" +-- [[ ShadowsocksR ]] + +s.fields["type"]:value(type_name, translate("ShadowsocksR")) + +if s.val["type"] and s.val["type"] ~= type_name then + return +end + local option_prefix = "ssr_" local function _n(name) @@ -33,10 +39,6 @@ local ssr_obfs_list = { "tls1.0_session_auth", "tls1.2_ticket_auth" } --- [[ ShadowsocksR ]] - -s.fields["type"]:value(type_name, translate("ShadowsocksR")) - o = s:option(Flag, _n("custom"), translate("Use Custom Config")) o = s:option(Value, _n("port"), translate("Listen Port")) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua b/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua index 2d5a9bba..35312f53 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/server/user.lua @@ -1,7 +1,6 @@ -local api = require "luci.passwall.api" -local appname = api.appname -local fs = api.fs -local types_dir = "/usr/lib/lua/luci/model/cbi/" .. appname .. "/server/type/" +api = require "luci.passwall.api" +appname = api.appname +fs = api.fs m = Map("passwall_server", translate("Server Config")) m.redirect = api.url("server") @@ -11,12 +10,21 @@ if not arg[1] or not m:get(arg[1]) then luci.http.redirect(m.redirect) end +local header = Template(appname .. "/server/config_header") +header.api = api +header.config = m.config +header.section = arg[1] +m:append(header) + m:append(Template(appname .. "/cbi/nodes_listvalue_com")) s = m:section(NamedSection, arg[1], "user", "") s.addremove = false s.dynamic = false +local types_dir = "/usr/lib/lua/luci/model/cbi/" .. appname .. "/server/type/" +s.val = m:get(arg[1]) or {} + o = s:option(Flag, "enable", translate("Enable")) o.default = "1" o.rmempty = false @@ -32,9 +40,9 @@ for filename in fs.dir(types_dir) do table.insert(type_table, filename) end table.sort(type_table, function(a, b) - if a == "socks.lua" then return true end - if b == "socks.lua" then return false end - return a < b + if a == "socks.lua" then return true end + if b == "socks.lua" then return false end + return a < b end) for index, value in ipairs(type_table) do @@ -42,4 +50,10 @@ for index, value in ipairs(type_table) do setfenv(p_func, getfenv(1))(m, s) end +local footer = Template(appname .. "/server/config_footer") +footer.api = api +footer.config = m.config +footer.section = arg[1] +m:append(footer) + return m diff --git a/luci-app-passwall/luasrc/passwall/api.lua b/luci-app-passwall/luasrc/passwall/api.lua index ebde80e4..fb54b982 100644 --- a/luci-app-passwall/luasrc/passwall/api.lua +++ b/luci-app-passwall/luasrc/passwall/api.lua @@ -1293,6 +1293,11 @@ function to_check_self() end function luci_types(id, m, s, type_name, option_prefix) + local fv_type + local field_type = s.fields["type"] + if field_type then + fv_type = field_type:formvalue(id) + end local rewrite_option_table = {} for key, value in pairs(s.fields) do if key:find(option_prefix) == 1 then @@ -1361,6 +1366,10 @@ function luci_types(id, m, s, type_name, option_prefix) else s.fields[key]:depends({ type = type_name }) end + + if fv_type and fv_type ~= type_name then + s.fields[key].rmempty = true + end end end end diff --git a/luci-app-passwall/luasrc/view/passwall/node_config/footer.htm b/luci-app-passwall/luasrc/view/passwall/node_config/footer.htm index 4cb26731..7a16884f 100644 --- a/luci-app-passwall/luasrc/view/passwall/node_config/footer.htm +++ b/luci-app-passwall/luasrc/view/passwall/node_config/footer.htm @@ -4,7 +4,7 @@ local api = self.api diff --git a/luci-app-passwall/luasrc/view/passwall/server/config_header.htm b/luci-app-passwall/luasrc/view/passwall/server/config_header.htm new file mode 100644 index 00000000..1957dec4 --- /dev/null +++ b/luci-app-passwall/luasrc/view/passwall/server/config_header.htm @@ -0,0 +1,56 @@ +<% +local api = self.api +-%> + diff --git a/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua b/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua index 79430d37..7db25438 100644 --- a/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua +++ b/luci-app-ssr-plus/luasrc/controller/shadowsocksr.lua @@ -1322,7 +1322,7 @@ function act_ping() if not e.ping then local icmp_cmd = string.format("ping -c 1 -W 1 %s 2>/dev/null | grep -o 'time=[0-9.]*' | cut -d= -f2", domain) - e.ping = tonumber(luci.sys.exec(icmp_cmd)) + e.ping = normalize_ping_ms(tonumber(luci.sys.exec(icmp_cmd))) end if not e.ping then e.ping = 0 @@ -1332,7 +1332,7 @@ function act_ping() local result = "" local success = false local icmp_cmd = string.format("ping -c 1 -W 1 %s 2>/dev/null | grep -o 'time=[0-9.]*' | cut -d= -f2", domain) - e.ping = tonumber(luci.sys.exec(icmp_cmd)) + e.ping = normalize_ping_ms(tonumber(luci.sys.exec(icmp_cmd))) -- WebSocket 探测 (适用于域名,或带 SNI 的 IP) if not is_ip or probe_host ~= domain then local resolve_arg = "" @@ -1403,7 +1403,7 @@ function act_ping() end if not e.ping then local icmp_cmd = string.format("ping -c 1 -W 1 %s 2>/dev/null | grep -o 'time=[0-9.]*' | cut -d= -f2", domain) - e.ping = tonumber(luci.sys.exec(icmp_cmd)) + e.ping = normalize_ping_ms(tonumber(luci.sys.exec(icmp_cmd))) end if not e.ping then @@ -1453,10 +1453,6 @@ function act_ping() }) end - if e.ping then - e.ping = math.floor(e.ping + 0.5) - end - luci.http.prepare_content("application/json") luci.http.write_json(e) end diff --git a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr index b4cafdcc..1970fa94 100755 --- a/luci-app-ssr-plus/root/etc/init.d/shadowsocksr +++ b/luci-app-ssr-plus/root/etc/init.d/shadowsocksr @@ -2271,16 +2271,41 @@ start() { echolog "禁止连接的域名加载完毕。" if [ "$(uci_get_by_type global adblock 0)" == "1" ]; then echolog "未启动主节点,广告过滤正在加载。" - cp -f /etc/ssrplus/ad.conf $TMP_DNSMASQ_PATH/ + cp -f /etc/ssrplus/ad.conf "$TMP_DNSMASQ_PATH/" if [ -f "$TMP_DNSMASQ_PATH/ad.conf" ]; then # Optimize: Batch filter using grep instead of looping sed for list_file in /etc/ssrplus/black.list /etc/ssrplus/white.list /etc/ssrplus/deny.list; do if [ -s "$list_file" ]; then # Clean list file (remove comments and empty lines) - grep -vE '^\s*#|^\s*$' "$list_file" > "${list_file}.clean" + grep -vE '^\s*#|^\s*$' "$list_file" | sed 's/\r//g' > "${list_file}.clean" if [ -s "${list_file}.clean" ]; then - grep -v -F -f "${list_file}.clean" "$TMP_DNSMASQ_PATH/ad.conf" > "$TMP_DNSMASQ_PATH/ad.conf.tmp" - mv "$TMP_DNSMASQ_PATH/ad.conf.tmp" "$TMP_DNSMASQ_PATH/ad.conf" + tmp_file="$TMP_DNSMASQ_PATH/ad.conf.tmp" + awk -v list="${list_file}.clean" ' + BEGIN { + while ((getline line < list) > 0) { + gsub(/\r/, "", line) + if (line != "") { + domain[line] = 1 + # 支持泛域名 + domain["*." line] = 1 + } + } + close(list) + } + { + keep = 1 + # 匹配 server=/domain/ + if (match($0, /^server=\/([^\/]+)\//, m)) { + if (m[1] in domain) keep = 0 + } + # 匹配 ipset=/domain/ + if (match($0, /^ipset=\/([^\/]+)\//, m)) { + if (m[1] in domain) keep = 0 + } + if (keep) print + } + ' "$TMP_DNSMASQ_PATH/ad.conf" > "$tmp_file" + mv "$tmp_file" "$TMP_DNSMASQ_PATH/ad.conf" fi rm -f "${list_file}.clean" fi diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gfw2ipset.sh b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gfw2ipset.sh index e3a405d7..fa168826 100755 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gfw2ipset.sh +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gfw2ipset.sh @@ -45,13 +45,37 @@ done # Optimize: Batch filter using grep for list_file in /etc/ssrplus/black.list /etc/ssrplus/white.list /etc/ssrplus/deny.list; do if [ -s "$list_file" ]; then - grep -vE '^\s*#|^\s*$' "$list_file" > "${list_file}.clean" + # 清理注释和空行 + grep -vE '^\s*#|^\s*$' "$list_file" | sed 's/\r//g' > "${list_file}.clean" if [ -s "${list_file}.clean" ]; then for target_file in "$TMP_DNSMASQ_PATH/gfw_list.conf" "$TMP_DNSMASQ_PATH/gfw_base.conf"; do - if [ -f "$target_file" ]; then - grep -v -F -f "${list_file}.clean" "$target_file" > "${target_file}.tmp" - mv "${target_file}.tmp" "$target_file" - fi + [ -f "$target_file" ] || continue + tmp_file="${target_file}.tmp" + awk -v list="${list_file}.clean" ' + BEGIN { + while ((getline line < list) > 0) { + gsub(/\r/, "", line) + if (line != "") { + domain[line] = 1 + # 同时支持 *.domain + domain["*." line] = 1 + } + } + close(list) + } + { + # 提取 server=/domain/xxx + if (match($0, /^server=\/([^\/]+)\//, m)) { + if (m[1] in domain) next + } + # 提取 ipset=/domain/xxx + if (match($0, /^ipset=\/([^\/]+)\//, m)) { + if (m[1] in domain) next + } + print + } + ' "$target_file" > "$tmp_file" + mv "$tmp_file" "$target_file" done fi rm -f "${list_file}.clean" @@ -69,19 +93,45 @@ fi cat /etc/ssrplus/deny.list | sed '/^$/d' | sed '/#/d' | sed "/.*/s/.*/address=\/&\//" >$TMP_DNSMASQ_PATH/denylist.conf if [ "$(uci_get_by_type global adblock 0)" == "1" ]; then - cp -f /etc/ssrplus/ad.conf $TMP_DNSMASQ_PATH/ + cp -f /etc/ssrplus/ad.conf "$TMP_DNSMASQ_PATH/" if [ -f "$TMP_DNSMASQ_PATH/ad.conf" ]; then for list_file in /etc/ssrplus/black.list /etc/ssrplus/white.list /etc/ssrplus/deny.list; do if [ -s "$list_file" ]; then - grep -vE '^\s*#|^\s*$' "$list_file" > "${list_file}.clean" + # 清理注释 & 空行 + grep -vE '^\s*#|^\s*$' "$list_file" | sed 's/\r//g' > "${list_file}.clean" if [ -s "${list_file}.clean" ]; then - grep -v -F -f "${list_file}.clean" "$TMP_DNSMASQ_PATH/ad.conf" > "$TMP_DNSMASQ_PATH/ad.conf.tmp" - mv "$TMP_DNSMASQ_PATH/ad.conf.tmp" "$TMP_DNSMASQ_PATH/ad.conf" + tmp_file="$TMP_DNSMASQ_PATH/ad.conf.tmp" + awk -v list="${list_file}.clean" ' + BEGIN { + while ((getline line < list) > 0) { + gsub(/\r/, "", line) + if (line != "") { + domain[line] = 1 + # 支持泛域名 + domain["*." line] = 1 + } + } + close(list) + } + { + keep = 1 + # 精确匹配 server=/domain/ + if (match($0, /^server=\/([^\/]+)\//, m)) { + if (m[1] in domain) keep = 0 + } + # 精确匹配 ipset=/domain/ + if (match($0, /^ipset=\/([^\/]+)\//, m)) { + if (m[1] in domain) keep = 0 + } + if (keep) print + } + ' "$TMP_DNSMASQ_PATH/ad.conf" > "$tmp_file" + mv "$tmp_file" "$TMP_DNSMASQ_PATH/ad.conf" fi rm -f "${list_file}.clean" fi done fi else - rm -f $TMP_DNSMASQ_PATH/ad.conf + rm -f "$TMP_DNSMASQ_PATH/ad.conf" fi