luci-app-passwall2: sync upstream

last commit: 9b09b0d001
This commit is contained in:
gitea-action 2025-03-02 19:00:25 +08:00
parent 80f3bada16
commit 1da39105c3
10 changed files with 199 additions and 15 deletions

View File

@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2 PKG_NAME:=luci-app-passwall2
PKG_VERSION:=25.1.27 PKG_VERSION:=25.3.2
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_CONFIG_DEPENDS:= \ PKG_CONFIG_DEPENDS:= \

View File

@ -14,6 +14,7 @@ end
local normal_list = {} local normal_list = {}
local balancing_list = {} local balancing_list = {}
local urltest_list = {}
local shunt_list = {} local shunt_list = {}
local iface_list = {} local iface_list = {}
for k, v in pairs(nodes_table) do for k, v in pairs(nodes_table) do
@ -23,6 +24,9 @@ for k, v in pairs(nodes_table) do
if v.protocol and v.protocol == "_balancing" then if v.protocol and v.protocol == "_balancing" then
balancing_list[#balancing_list + 1] = v balancing_list[#balancing_list + 1] = v
end end
if v.protocol and v.protocol == "_urltest" then
urltest_list[#urltest_list + 1] = v
end
if v.protocol and v.protocol == "_shunt" then if v.protocol and v.protocol == "_shunt" then
shunt_list[#shunt_list + 1] = v shunt_list[#shunt_list + 1] = v
end end
@ -130,6 +134,9 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
for k1, v1 in pairs(balancing_list) do for k1, v1 in pairs(balancing_list) do
o:value(v1.id, v1.remark) o:value(v1.id, v1.remark)
end end
for k1, v1 in pairs(urltest_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(iface_list) do for k1, v1 in pairs(iface_list) do
o:value(v1.id, v1.remark) o:value(v1.id, v1.remark)
end end
@ -174,6 +181,9 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
for k1, v1 in pairs(balancing_list) do for k1, v1 in pairs(balancing_list) do
o:value(v1.id, v1.remark) o:value(v1.id, v1.remark)
end end
for k1, v1 in pairs(urltest_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(iface_list) do for k1, v1 in pairs(iface_list) do
o:value(v1.id, v1.remark) o:value(v1.id, v1.remark)
end end
@ -199,6 +209,9 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
for k1, v1 in pairs(balancing_list) do for k1, v1 in pairs(balancing_list) do
o:value(v1.id, v1.remark) o:value(v1.id, v1.remark)
end end
for k1, v1 in pairs(urltest_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(iface_list) do for k1, v1 in pairs(iface_list) do
o:value(v1.id, v1.remark) o:value(v1.id, v1.remark)
end end
@ -214,7 +227,7 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
o:value("", translate("Close")) o:value("", translate("Close"))
o:value("main", translate("Preproxy Node")) o:value("main", translate("Preproxy Node"))
for k1, v1 in pairs(normal_list) do for k1, v1 in pairs(normal_list) do
if v1.protocol ~= "_balancing" then if v1.protocol ~= "_balancing" and v1.protocol ~= "_urltest" then
o:depends({ [vid .. "-default_node"] = v1.id, [vid .. "-preproxy_enabled"] = "1" }) o:depends({ [vid .. "-default_node"] = v1.id, [vid .. "-preproxy_enabled"] = "1" })
end end
end end

View File

@ -96,6 +96,8 @@ o.cfgvalue = function(t, n)
local protocol = m:get(n, "protocol") local protocol = m:get(n, "protocol")
if protocol == "_balancing" then if protocol == "_balancing" then
protocol = translate("Balancing") protocol = translate("Balancing")
elseif protocol == "_urltest" then
protocol = "URLTest"
elseif protocol == "_shunt" then elseif protocol == "_shunt" then
protocol = translate("Shunt") protocol = translate("Shunt")
elseif protocol == "vmess" then elseif protocol == "vmess" then

View File

@ -56,6 +56,7 @@ end
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o:value("hysteria2", "Hysteria2") o:value("hysteria2", "Hysteria2")
end end
o:value("_urltest", translate("URLTest"))
o:value("_shunt", translate("Shunt")) o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface")) o:value("_iface", translate("Custom Interface"))
@ -65,6 +66,7 @@ o:depends({ [_n("protocol")] = "_iface" })
local nodes_table = {} local nodes_table = {}
local iface_table = {} local iface_table = {}
local urltest_table = {}
for k, e in ipairs(api.get_valid_nodes()) do for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then if e.node_type == "normal" then
nodes_table[#nodes_table + 1] = { nodes_table[#nodes_table + 1] = {
@ -79,6 +81,12 @@ for k, e in ipairs(api.get_valid_nodes()) do
remark = e["remark"] remark = e["remark"]
} }
end end
if e.protocol == "_urltest" then
urltest_table[#urltest_table + 1] = {
id = e[".name"],
remark = e["remark"]
}
end
end end
local socks_list = {} local socks_list = {}
@ -91,6 +99,44 @@ m.uci:foreach(appname, "socks", function(s)
end end
end) end)
--[[ URLTest ]]
o = s:option(DynamicList, _n("urltest_node"), translate("URLTest node list"), translate("List of nodes to test, <a target='_blank' href='https://sing-box.sagernet.org/configuration/outbound/urltest'>document</a>"))
o:depends({ [_n("protocol")] = "_urltest" })
for k, v in pairs(nodes_table) do o:value(v.id, v.remark) end
o = s:option(Value, _n("urltest_url"), translate("Probe URL"))
o:depends({ [_n("protocol")] = "_urltest" })
o:value("https://cp.cloudflare.com/", "Cloudflare")
o:value("https://www.gstatic.com/generate_204", "Gstatic")
o:value("https://www.google.com/generate_204", "Google")
o:value("https://www.youtube.com/generate_204", "YouTube")
o:value("https://connect.rom.miui.com/generate_204", "MIUI (CN)")
o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud (CN)")
o.default = "https://www.gstatic.com/generate_204"
o.description = translate("The URL used to detect the connection status.")
o = s:option(Value, _n("urltest_interval"), translate("Test interval"))
o:depends({ [_n("protocol")] = "_urltest" })
o.datatype = "uinteger"
o.default = "180"
o.description = translate("The test interval in seconds.") .. "<br />" ..
translate("Test interval must be less or equal than idle timeout.")
o = s:option(Value, _n("urltest_tolerance"), translate("Test tolerance"), translate("The test tolerance in milliseconds."))
o:depends({ [_n("protocol")] = "_urltest" })
o.datatype = "uinteger"
o.default = "50"
o = s:option(Value, _n("urltest_idle_timeout"), translate("Idle timeout"), translate("The idle timeout in seconds."))
o:depends({ [_n("protocol")] = "_urltest" })
o.datatype = "uinteger"
o.default = "1800"
o = s:option(Flag, _n("urltest_interrupt_exist_connections"), translate("Interrupt existing connections"))
o:depends({ [_n("protocol")] = "_urltest" })
o.default = "0"
o.description = translate("Interrupt existing connections when the selected outbound has changed.")
-- [[ 分流模块 ]] -- [[ 分流模块 ]]
if #nodes_table > 0 then if #nodes_table > 0 then
o = s:option(Flag, _n("preproxy_enabled"), translate("Preproxy")) o = s:option(Flag, _n("preproxy_enabled"), translate("Preproxy"))
@ -101,6 +147,9 @@ if #nodes_table > 0 then
for k, v in pairs(socks_list) do for k, v in pairs(socks_list) do
o:value(v.id, v.remark) o:value(v.id, v.remark)
end end
for k, v in pairs(urltest_table) do
o:value(v.id, v.remark)
end
for k, v in pairs(iface_table) do for k, v in pairs(iface_table) do
o:value(v.id, v.remark) o:value(v.id, v.remark)
end end
@ -124,6 +173,9 @@ m.uci:foreach(appname, "shunt_rules", function(e)
for k, v in pairs(socks_list) do for k, v in pairs(socks_list) do
o:value(v.id, v.remark) o:value(v.id, v.remark)
end end
for k, v in pairs(urltest_table) do
o:value(v.id, v.remark)
end
for k, v in pairs(iface_table) do for k, v in pairs(iface_table) do
o:value(v.id, v.remark) o:value(v.id, v.remark)
end end
@ -155,6 +207,9 @@ if #nodes_table > 0 then
for k, v in pairs(socks_list) do for k, v in pairs(socks_list) do
o:value(v.id, v.remark) o:value(v.id, v.remark)
end end
for k, v in pairs(urltest_table) do
o:value(v.id, v.remark)
end
for k, v in pairs(iface_table) do for k, v in pairs(iface_table) do
o:value(v.id, v.remark) o:value(v.id, v.remark)
end end

View File

@ -314,7 +314,7 @@ function strToTable(str)
end end
function is_normal_node(e) function is_normal_node(e)
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface") then if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface" or e.protocol == "_urltest") then
return false return false
end end
return true return true
@ -444,7 +444,7 @@ function get_valid_nodes()
uci:foreach(appname, "nodes", function(e) uci:foreach(appname, "nodes", function(e)
e.id = e[".name"] e.id = e[".name"]
if e.type and e.remarks then if e.type and e.remarks then
if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface") then if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface" or e.protocol == "_urltest") then
local type = e.type local type = e.type
if type == "sing-box" then type = "Sing-Box" end if type == "sing-box" then type = "Sing-Box" end
e["remark"] = "%s[%s] " % {type .. " " .. i18n.translatef(e.protocol), e.remarks} e["remark"] = "%s[%s] " % {type .. " " .. i18n.translatef(e.protocol), e.remarks}
@ -494,7 +494,7 @@ end
function get_node_remarks(n) function get_node_remarks(n)
local remarks = "" local remarks = ""
if n then if n then
if n.protocol and (n.protocol == "_balancing" or n.protocol == "_shunt" or n.protocol == "_iface") then if n.protocol and (n.protocol == "_balancing" or n.protocol == "_shunt" or n.protocol == "_iface" or n.protocol == "_urltest") then
remarks = "%s[%s] " % {n.type .. " " .. i18n.translatef(n.protocol), n.remarks} remarks = "%s[%s] " % {n.type .. " " .. i18n.translatef(n.protocol), n.remarks}
else else
local type2 = n.type local type2 = n.type
@ -1114,7 +1114,7 @@ end
function get_version() function get_version()
local version = sys.exec("opkg list-installed luci-app-passwall2 2>/dev/null | awk '{print $3}'") local version = sys.exec("opkg list-installed luci-app-passwall2 2>/dev/null | awk '{print $3}'")
if not version or #version == 0 then if not version or #version == 0 then
version = sys.exec("apk info -L luci-app-passwall2 2>/dev/null | awk 'NR == 1 {print $1}' | cut -d'-' -f4-") version = sys.exec("apk list luci-app-passwall2 2>/dev/null | awk '/installed/ {print $1}' | cut -d'-' -f4-")
end end
return version or "" return version or ""
end end

View File

@ -892,6 +892,54 @@ function gen_config(var)
node.port = server_port node.port = server_port
end end
local function gen_urltest(_node)
local urltest_id = _node[".name"]
local urltest_tag = "urltest-" .. urltest_id
-- existing urltest
for _, v in ipairs(outbounds) do
if v.tag == urltest_tag then
return urltest_tag
end
end
-- new urltest
local ut_nodes = _node.urltest_node
local valid_nodes = {}
for i = 1, #ut_nodes do
local ut_node_id = ut_nodes[i]
local ut_node_tag = "ut-" .. ut_node_id
local is_new_ut_node = true
for _, outbound in ipairs(outbounds) do
if string.sub(outbound.tag, 1, #ut_node_tag) == ut_node_tag then
is_new_ut_node = false
valid_nodes[#valid_nodes + 1] = outbound.tag
break
end
end
if is_new_ut_node then
local ut_node = uci:get_all(appname, ut_node_id)
local outbound = gen_outbound(flag, ut_node, ut_node_tag)
if outbound then
outbound.tag = outbound.tag .. ":" .. ut_node.remarks
table.insert(outbounds, outbound)
valid_nodes[#valid_nodes + 1] = outbound.tag
end
end
end
if #valid_nodes == 0 then return nil end
local outbound = {
type = "urltest",
tag = urltest_tag,
outbounds = valid_nodes,
url = _node.urltest_url or "https://www.gstatic.com/generate_204",
interval = _node.urltest_interval and tonumber(_node.urltest_interval) and string.format("%dm", tonumber(_node.urltest_interval) / 60) or "3m",
tolerance = _node.urltest_tolerance and tonumber(_node.urltest_tolerance) and tonumber(_node.urltest_tolerance) or 50,
idle_timeout = _node.urltest_idle_timeout and tonumber(_node.urltest_idle_timeout) and string.format("%dm", tonumber(_node.urltest_idle_timeout) / 60) or "30m",
interrupt_exist_connections = (_node.urltest_interrupt_exist_connections == "true" or _node.urltest_interrupt_exist_connections == "1") and true or false
}
table.insert(outbounds, outbound)
return urltest_tag
end
local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name) local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name)
if not node or not outbound or not outbounds_table then return nil end if not node or not outbound or not outbounds_table then return nil end
local default_outTag = outbound.tag local default_outTag = outbound.tag
@ -1046,6 +1094,8 @@ function gen_config(var)
end end
end end
end end
elseif _node.protocol == "_urltest" then
rule_outboundTag = gen_urltest(_node)
elseif _node.protocol == "_iface" then elseif _node.protocol == "_iface" then
if _node.iface then if _node.iface then
local _outbound = { local _outbound = {
@ -1220,6 +1270,10 @@ function gen_config(var)
for index, value in ipairs(rules) do for index, value in ipairs(rules) do
table.insert(route.rules, rules[index]) table.insert(route.rules, rules[index])
end end
elseif node.protocol == "_urltest" then
if node.urltest_node then
COMMON.default_outbound_tag = gen_urltest(node)
end
elseif node.protocol == "_iface" then elseif node.protocol == "_iface" then
if node.iface then if node.iface then
local outbound = { local outbound = {

View File

@ -1423,9 +1423,6 @@ msgstr "最大并发连接数"
msgid "XUDP Mux concurrency" msgid "XUDP Mux concurrency"
msgstr "XUDP 最大并发连接数" msgstr "XUDP 最大并发连接数"
msgid "Mux idle timeout"
msgstr "最大闲置时间"
msgid "Padding" msgid "Padding"
msgstr "填充" msgstr "填充"
@ -1444,9 +1441,6 @@ msgstr "推荐值Sec-WebSocket-Protocol"
msgid "Health check" msgid "Health check"
msgstr "健康检查" msgstr "健康检查"
msgid "Idle timeout"
msgstr "闲置时间"
msgid "Health check timeout" msgid "Health check timeout"
msgstr "检查超时时间" msgstr "检查超时时间"
@ -1647,3 +1641,39 @@ msgstr "是否要恢复客户端默认配置?"
msgid "Are you sure you want to restore the client to default settings?" msgid "Are you sure you want to restore the client to default settings?"
msgstr "是否真的要恢复客户端默认配置?" msgstr "是否真的要恢复客户端默认配置?"
msgid "_urltest"
msgstr "URLTest"
msgid "URLTest node list"
msgstr "URLTest 节点列表"
msgid "List of nodes to test, <a target='_blank' href='https://sing-box.sagernet.org/configuration/outbound/urltest'>document</a>"
msgstr "要测试的节点列表,<a target='_blank' href='https://sing-box.sagernet.org/zh/configuration/outbound/urltest'>文档原理</a>"
msgid "Test interval"
msgstr "测试间隔"
msgid "The test interval in seconds."
msgstr "测试间隔时间(单位:秒)。"
msgid "Test interval must be less or equal than idle timeout."
msgstr "测试间隔时间必须小于或等于空闲超时时间。"
msgid "Test tolerance"
msgstr "测试容差"
msgid "The test tolerance in milliseconds."
msgstr "测试容差时间(单位:毫秒)。"
msgid "Idle timeout"
msgstr "空闲超时"
msgid "The idle timeout in seconds."
msgstr "空闲超时时间(单位:秒)。"
msgid "Interrupt existing connections"
msgstr "中断现有连接"
msgid "Interrupt existing connections when the selected outbound has changed."
msgstr "当选择的出站发生变化时中断现有连接。"

View File

@ -589,7 +589,7 @@ run_socks() {
if [ "$type" == "sing-box" ] || [ "$type" == "xray" ]; then if [ "$type" == "sing-box" ] || [ "$type" == "xray" ]; then
local protocol=$(config_n_get $node protocol) local protocol=$(config_n_get $node protocol)
if [ "$protocol" == "_balancing" ] || [ "$protocol" == "_shunt" ] || [ "$protocol" == "_iface" ]; then if [ "$protocol" == "_balancing" ] || [ "$protocol" == "_shunt" ] || [ "$protocol" == "_iface" ] || [ "$protocol" == "_urltest" ]; then
unset error_msg unset error_msg
fi fi
fi fi

View File

@ -303,6 +303,36 @@ do
end end
} }
end end
elseif node.protocol and node.protocol == '_urltest' then
local flag = "Sing-Box URLTest节点[" .. node_id .. "]列表"
local currentNodes = {}
local newNodes = {}
if node.urltest_node then
for k, node in pairs(node.urltest_node) do
currentNodes[#currentNodes + 1] = {
log = false,
node = node,
currentNode = node and uci:get_all(appname, node) or nil,
remarks = node,
set = function(o, server)
if o and server and server ~= "nil" then
table.insert(o.newNodes, server)
end
end
}
end
end
CONFIG[#CONFIG + 1] = {
remarks = flag,
currentNodes = currentNodes,
newNodes = newNodes,
set = function(o, newNodes)
if o then
if not newNodes then newNodes = o.newNodes end
uci:set_list(appname, node_id, "urltest_node", newNodes or {})
end
end
}
else else
--前置代理节点 --前置代理节点
local currentNode = uci:get_all(appname, node_id) or nil local currentNode = uci:get_all(appname, node_id) or nil
@ -1682,7 +1712,7 @@ local execute = function()
f:close() f:close()
raw = trim(stdout) raw = trim(stdout)
local old_md5 = value.md5 or "" local old_md5 = value.md5 or ""
local new_md5 = luci.sys.exec("[ -f " .. tmp_file .. " ] && md5sum " .. tmp_file .. " | awk '{print $1}' || echo 0") local new_md5 = luci.sys.exec("[ -f " .. tmp_file .. " ] && md5sum " .. tmp_file .. " | awk '{print $1}' || echo 0"):gsub("\n", "")
os.remove(tmp_file) os.remove(tmp_file)
if old_md5 == new_md5 then if old_md5 == new_md5 then
log('订阅:【' .. remark .. '】没有变化,无需更新。') log('订阅:【' .. remark .. '】没有变化,无需更新。')

View File

@ -71,7 +71,7 @@ url_test_node() {
sleep 1s sleep 1s
result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "https://www.google.com/generate_204") result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "https://www.google.com/generate_204")
pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1 pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf "/tmp/etc/${CONFIG}/url_test_${node_id}.json" rm -rf "/tmp/etc/${CONFIG}/url_test_${node_id}"*.json
} }
echo $result echo $result
} }