luci-app-passwall2: sync upstream

last commit: 489893bc86
This commit is contained in:
gitea-action 2025-04-01 02:31:12 +08:00
parent eb5b203669
commit b0f5377491
19 changed files with 206 additions and 129 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.3.2 PKG_VERSION:=25.4.1
PKG_RELEASE:=1 PKG_RELEASE:=1
PKG_CONFIG_DEPENDS:= \ PKG_CONFIG_DEPENDS:= \

View File

@ -176,7 +176,7 @@ function get_redir_log()
local name = luci.http.formvalue("name") local name = luci.http.formvalue("name")
local file_path = "/tmp/etc/passwall2/acl/" .. id .. "/" .. name .. ".log" local file_path = "/tmp/etc/passwall2/acl/" .. id .. "/" .. name .. ".log"
if nixio.fs.access(file_path) then if nixio.fs.access(file_path) then
local content = luci.sys.exec("cat '" .. file_path .. "'") local content = luci.sys.exec("tail -n 19999 '" .. file_path .. "'")
content = content:gsub("\n", "<br />") content = content:gsub("\n", "<br />")
luci.http.write(content) luci.http.write(content)
else else
@ -188,7 +188,7 @@ function get_socks_log()
local name = luci.http.formvalue("name") local name = luci.http.formvalue("name")
local path = "/tmp/etc/passwall2/SOCKS_" .. name .. ".log" local path = "/tmp/etc/passwall2/SOCKS_" .. name .. ".log"
if nixio.fs.access(path) then if nixio.fs.access(path) then
local content = luci.sys.exec("cat ".. path) local content = luci.sys.exec("tail -n 5000 ".. path)
content = content:gsub("\n", "<br />") content = content:gsub("\n", "<br />")
luci.http.write(content) luci.http.write(content)
else else

View File

@ -1,7 +1,7 @@
local api = require "luci.passwall2.api" local api = require "luci.passwall2.api"
local appname = api.appname local appname = api.appname
local datatypes = api.datatypes local datatypes = api.datatypes
local has_singbox = api.finded_com("singbox") local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray") local has_xray = api.finded_com("xray")
m = Map(appname) m = Map(appname)

View File

@ -3,7 +3,7 @@ local appname = api.appname
local uci = api.uci local uci = api.uci
local has_ss = api.is_finded("ss-redir") local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal") local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("singbox") local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray") local has_xray = api.finded_com("xray")
local has_hysteria2 = api.finded_com("hysteria") local has_hysteria2 = api.finded_com("hysteria")
local ss_type = {} local ss_type = {}

View File

@ -11,7 +11,7 @@ end
local has_ss = api.is_finded("ss-redir") local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal") local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("singbox") local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray") local has_xray = api.finded_com("xray")
local has_hysteria2 = api.finded_com("hysteria") local has_hysteria2 = api.finded_com("hysteria")
local ss_type = {} local ss_type = {}

View File

@ -1,7 +1,7 @@
local api = require "luci.passwall2.api" local api = require "luci.passwall2.api"
local appname = api.appname local appname = api.appname
local fs = api.fs local fs = api.fs
local has_singbox = api.finded_com("singbox") local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray") local has_xray = api.finded_com("xray")
local has_fw3 = api.is_finded("fw3") local has_fw3 = api.is_finded("fw3")
local has_fw4 = api.is_finded("fw4") local has_fw4 = api.is_finded("fw4")

View File

@ -8,7 +8,7 @@ if not arg[1] or not m:get(arg[1]) then
luci.http.redirect(api.url()) luci.http.redirect(api.url())
end end
local has_singbox = api.finded_com("singbox") local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray") local has_xray = api.finded_com("xray")
local nodes_table = {} local nodes_table = {}

View File

@ -95,7 +95,7 @@ m.uci:foreach(appname, "socks", function(s)
end) end)
-- 负载均衡列表 -- 负载均衡列表
local o = s:option(DynamicList, _n("balancing_node"), translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>")) local o = s:option(DynamicList, _n("balancing_node"), translate("Load balancing node list"), translate("Load balancing node list, <a target='_blank' href='https://xtls.github.io/config/routing.html#balancerobject'>document</a>"))
o:depends({ [_n("protocol")] = "_balancing" }) o:depends({ [_n("protocol")] = "_balancing" })
for k, v in pairs(nodes_table) do o:value(v.id, v.remark) end for k, v in pairs(nodes_table) do o:value(v.id, v.remark) end
@ -104,7 +104,8 @@ o:depends({ [_n("protocol")] = "_balancing" })
o:value("random") o:value("random")
o:value("roundRobin") o:value("roundRobin")
o:value("leastPing") o:value("leastPing")
o.default = "leastPing" o:value("leastLoad")
o.default = "leastLoad"
-- Fallback Node -- Fallback Node
if api.compare_versions(xray_version, ">=", "1.8.10") then if api.compare_versions(xray_version, ">=", "1.8.10") then
@ -133,6 +134,7 @@ end
-- 探测地址 -- 探测地址
local ucpu = s:option(Flag, _n("useCustomProbeUrl"), translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL.")) local ucpu = s:option(Flag, _n("useCustomProbeUrl"), translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL."))
ucpu:depends({ [_n("balancingStrategy")] = "leastPing" }) ucpu:depends({ [_n("balancingStrategy")] = "leastPing" })
ucpu:depends({ [_n("balancingStrategy")] = "leastLoad" })
local pu = s:option(Value, _n("probeUrl"), translate("Probe URL")) local pu = s:option(Value, _n("probeUrl"), translate("Probe URL"))
pu:depends({ [_n("useCustomProbeUrl")] = true }) pu:depends({ [_n("useCustomProbeUrl")] = true })
@ -148,8 +150,9 @@ pu.description = translate("The URL used to detect the connection status.")
-- 探测间隔 -- 探测间隔
local pi = s:option(Value, _n("probeInterval"), translate("Probe Interval")) local pi = s:option(Value, _n("probeInterval"), translate("Probe Interval"))
pi:depends({ [_n("balancingStrategy")] = "leastPing" }) pi:depends({ [_n("balancingStrategy")] = "leastPing" })
pi:depends({ [_n("balancingStrategy")] = "leastLoad" })
pi.default = "1m" pi.default = "1m"
pi.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.") pi.description = translate("The interval between initiating probes. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.")
if api.compare_versions(xray_version, ">=", "1.8.12") then if api.compare_versions(xray_version, ">=", "1.8.12") then
ucpu:depends({ [_n("protocol")] = "_balancing" }) ucpu:depends({ [_n("protocol")] = "_balancing" })
@ -159,6 +162,12 @@ else
pi:depends({ [_n("balancingStrategy")] = "leastPing" }) pi:depends({ [_n("balancingStrategy")] = "leastPing" })
end end
o = s:option(Value, _n("expected"), translate("Preferred Node Count"))
o:depends({ [_n("balancingStrategy")] = "leastLoad" })
o.datatype = "uinteger"
o.default = "2"
o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.")
-- [[ 分流模块 ]] -- [[ 分流模块 ]]
if #nodes_table > 0 then if #nodes_table > 0 then

View File

@ -2,7 +2,7 @@ local m, s = ...
local api = require "luci.passwall2.api" local api = require "luci.passwall2.api"
local singbox_bin = api.finded_com("singbox") local singbox_bin = api.finded_com("sing-box")
if not singbox_bin then if not singbox_bin then
return return
@ -401,6 +401,10 @@ if singbox_tags:find("with_quic") then
end end
if singbox_tags:find("with_quic") then if singbox_tags:find("with_quic") then
o = s:option(Value, _n("hysteria2_ports"), translate("Port hopping range"))
o.description = translate("Format as 1000:2000 Multiple groups are separated by commas (,).")
o:depends({ [_n("protocol")] = "hysteria2" })
o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps")) o = s:option(Value, _n("hysteria2_up_mbps"), translate("Max upload Mbps"))
o:depends({ [_n("protocol")] = "hysteria2" }) o:depends({ [_n("protocol")] = "hysteria2" })

View File

@ -2,7 +2,7 @@ local m, s = ...
local api = require "luci.passwall2.api" local api = require "luci.passwall2.api"
local singbox_bin = api.finded_com("singbox") local singbox_bin = api.finded_com("sing-box")
if not singbox_bin then if not singbox_bin then
return return

View File

@ -23,7 +23,7 @@ _M.hysteria = {
} }
} }
_M.singbox = { _M["sing-box"] = {
name = "Sing-Box", name = "Sing-Box",
repo = "SagerNet/sing-box", repo = "SagerNet/sing-box",
get_url = gh_release_url, get_url = gh_release_url,

View File

@ -149,7 +149,7 @@ local function start()
bin = ln_run(api.get_app_path("xray"), "xray", "run -c " .. config_file, log_path) bin = ln_run(api.get_app_path("xray"), "xray", "run -c " .. config_file, log_path)
elseif type == "sing-box" then elseif type == "sing-box" then
config = require(require_dir .. "util_sing-box").gen_config_server(user) config = require(require_dir .. "util_sing-box").gen_config_server(user)
bin = ln_run(api.get_app_path("singbox"), "sing-box", "run -c " .. config_file, log_path) bin = ln_run(api.get_app_path("sing-box"), "sing-box", "run -c " .. config_file, log_path)
elseif type == "Hysteria2" then elseif type == "Hysteria2" then
config = require(require_dir .. "util_hysteria2").gen_config_server(user) config = require(require_dir .. "util_hysteria2").gen_config_server(user)
bin = ln_run(api.get_app_path("hysteria"), "hysteria", "-c " .. config_file .. " server", log_path) bin = ln_run(api.get_app_path("hysteria"), "hysteria", "-c " .. config_file .. " server", log_path)

View File

@ -8,7 +8,7 @@ local fs = api.fs
local CACHE_PATH = api.CACHE_PATH local CACHE_PATH = api.CACHE_PATH
local split = api.split local split = api.split
local local_version = api.get_app_version("singbox") local local_version = api.get_app_version("sing-box")
local version_ge_1_11_0 = api.compare_versions(local_version:match("[^v]+"), ">=", "1.11.0") local version_ge_1_11_0 = api.compare_versions(local_version:match("[^v]+"), ">=", "1.11.0")
local new_port local new_port
@ -349,7 +349,17 @@ function gen_outbound(flag, node, tag, proxy_table)
end end
if node.protocol == "hysteria2" then if node.protocol == "hysteria2" then
local server_ports = {}
if node.hysteria2_ports then
for range in node.hysteria2_ports:gmatch("([^,]+)") do
if range:match("^%d+:%d+$") then
table.insert(server_ports, range)
end
end
end
protocol_table = { protocol_table = {
server_ports = next(server_ports) and server_ports or nil,
hop_interval = next(server_ports) and "30s" or nil,
up_mbps = (node.hysteria2_up_mbps and tonumber(node.hysteria2_up_mbps)) and tonumber(node.hysteria2_up_mbps) or nil, up_mbps = (node.hysteria2_up_mbps and tonumber(node.hysteria2_up_mbps)) and tonumber(node.hysteria2_up_mbps) or nil,
down_mbps = (node.hysteria2_down_mbps and tonumber(node.hysteria2_down_mbps)) and tonumber(node.hysteria2_down_mbps) or nil, down_mbps = (node.hysteria2_down_mbps and tonumber(node.hysteria2_down_mbps)) and tonumber(node.hysteria2_down_mbps) or nil,
obfs = { obfs = {

View File

@ -584,7 +584,8 @@ function gen_config(var)
local inbounds = {} local inbounds = {}
local outbounds = {} local outbounds = {}
local routing = nil local routing = nil
local observatory = nil local burstObservatory = nil
local strategy = nil
local COMMON = {} local COMMON = {}
local CACHE_TEXT_FILE = CACHE_PATH .. "/cache_" .. flag .. ".txt" local CACHE_TEXT_FILE = CACHE_PATH .. "/cache_" .. flag .. ".txt"
@ -758,19 +759,33 @@ function gen_config(var)
end end
end end
end end
if _node.balancingStrategy == "leastLoad" then
strategy = {
type = _node.balancingStrategy,
settings = {
expected = _node.expected and tonumber(_node.expected) and tonumber(_node.expected) or 2,
maxRTT = "1s"
}
}
else
strategy = { type = _node.balancingStrategy or "random" }
end
table.insert(balancers, { table.insert(balancers, {
tag = balancer_tag, tag = balancer_tag,
selector = valid_nodes, selector = valid_nodes,
fallbackTag = fallback_node_tag, fallbackTag = fallback_node_tag,
strategy = { type = _node.balancingStrategy or "random" } strategy = strategy
}) })
if _node.balancingStrategy == "leastPing" or fallback_node_tag then if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" or fallback_node_tag then
if not observatory then if not burstObservatory then
observatory = { burstObservatory = {
subjectSelector = { "blc-" }, subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil, pingConfig = {
probeInterval = _node.probeInterval or "1m", destination = _node.useCustomProbeUrl and _node.probeUrl or nil,
enableConcurrency = true interval = _node.probeInterval or "1m",
sampling = 3,
timeout = "5s"
}
} }
end end
end end
@ -1483,7 +1498,7 @@ function gen_config(var)
-- 传出连接 -- 传出连接
outbounds = outbounds, outbounds = outbounds,
-- 连接观测 -- 连接观测
observatory = observatory, burstObservatory = burstObservatory,
-- 路由 -- 路由
routing = routing, routing = routing,
-- 本地策略 -- 本地策略

View File

@ -10,7 +10,7 @@ local hysteria2_type = map:get("@global_subscribe[0]", "hysteria2_type") or "sin
-%> -%>
<script src="<%=resource%>/qrcode.min.js"></script> <script src="<%=resource%>/qrcode.min.js"></script>
<script type="text/javascript">//<![CDATA[ <script type="text/javascript">//<![CDATA[
let has_singbox = "<%=api.finded_com("singbox")%>" let has_singbox = "<%=api.finded_com("sing-box")%>"
let has_xray = "<%=api.finded_com("xray")%>" let has_xray = "<%=api.finded_com("xray")%>"
let has_hysteria2 = "<%=api.finded_com("hysteria")%>" let has_hysteria2 = "<%=api.finded_com("hysteria")%>"
let ss_type = "<%=ss_type%>" let ss_type = "<%=ss_type%>"

View File

@ -313,82 +313,84 @@ table td, .table .td {
} }
/* 自动Ping */ /* 自动Ping */
if (auto_detection_time == "icmp" || auto_detection_time == "tcping") { function pingAllNodes() {
var nodes = []; if (auto_detection_time == "icmp" || auto_detection_time == "tcping") {
const ping_value = document.getElementsByClassName(auto_detection_time == "tcping" ? 'tcping_value' : 'ping_value'); var nodes = [];
for (var i = 0; i < ping_value.length; i++) { const ping_value = document.getElementsByClassName(auto_detection_time == "tcping" ? 'tcping_value' : 'ping_value');
var cbi_id = ping_value[i].getAttribute("cbiid"); for (var i = 0; i < ping_value.length; i++) {
var full = get_address_full(cbi_id); var cbi_id = ping_value[i].getAttribute("cbiid");
if (full != null) { var full = get_address_full(cbi_id);
var flag = false; if (full != null) {
//当有多个相同地址和端口时合在一起 var flag = false;
for (var j = 0; j < nodes.length; j++) { //当有多个相同地址和端口时合在一起
if (nodes[j].address == full.address && nodes[j].port == full.port) { for (var j = 0; j < nodes.length; j++) {
nodes[j].indexs = nodes[j].indexs + "," + i; if (nodes[j].address == full.address && nodes[j].port == full.port) {
flag = true; nodes[j].indexs = nodes[j].indexs + "," + i;
break; flag = true;
break;
}
} }
if (flag)
continue;
nodes.push({
indexs: i + "",
address: full.address,
port: full.port
});
} }
if (flag)
continue;
nodes.push({
indexs: i + "",
address: full.address,
port: full.port
});
} }
}
const _xhr = (index) => { const _xhr = (index) => {
return new Promise((res) => { return new Promise((res) => {
const dom = nodes[index]; const dom = nodes[index];
if (!dom) res() if (!dom) res()
ajax.post('<%=api.url("ping_node")%>', { ajax.post('<%=api.url("ping_node")%>', {
index: dom.indexs, index: dom.indexs,
address: dom.address, address: dom.address,
port: dom.port, port: dom.port,
type: auto_detection_time type: auto_detection_time
}, },
function(x, result) { function(x, result) {
if (x && x.status == 200) { if (x && x.status == 200) {
var strs = dom.indexs.split(","); var strs = dom.indexs.split(",");
for (var i = 0; i < strs.length; i++) { for (var i = 0; i < strs.length; i++) {
if (result.ping == null || result.ping.trim() == "") { if (result.ping == null || result.ping.trim() == "") {
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>"; ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
} else { } else {
var ping = parseInt(result.ping); var ping = parseInt(result.ping);
if (ping < 100) if (ping < 100)
ping_value[strs[i]].innerHTML = "<font style='color:green'>" + result.ping + " ms" + "</font>"; ping_value[strs[i]].innerHTML = "<font style='color:green'>" + result.ping + " ms" + "</font>";
else if (ping < 200) else if (ping < 200)
ping_value[strs[i]].innerHTML = "<font style='color:#fb9a05'>" + result.ping + " ms" + "</font>"; ping_value[strs[i]].innerHTML = "<font style='color:#fb9a05'>" + result.ping + " ms" + "</font>";
else if (ping >= 200) else if (ping >= 200)
ping_value[strs[i]].innerHTML = "<font style='color:red'>" + result.ping + " ms" + "</font>"; ping_value[strs[i]].innerHTML = "<font style='color:red'>" + result.ping + " ms" + "</font>";
}
} }
} }
res();
},
5000,
function(x) {
var strs = dom.indexs.split(",");
for (var i = 0; i < strs.length; i++) {
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
}
res();
} }
res(); );
}, })
5000, }
function(x) {
var strs = dom.indexs.split(","); let task = -1;
for (var i = 0; i < strs.length; i++) { const thread = () => {
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>"; task = task + 1
} if (nodes[task]) {
res(); _xhr(task).then(thread);
} }
); }
}) for (let i = 0; i < 20; i++) {
} thread()
let task = -1;
const thread = () => {
task = task + 1
if (nodes[task]) {
_xhr(task).then(thread);
} }
}
for (let i = 0; i < 20; i++) {
thread()
} }
} }
@ -445,6 +447,14 @@ table td, .table .td {
} }
document.getElementById("div_node_count").innerHTML = "<div style='margin-top:5px'>" + str + "</div>"; document.getElementById("div_node_count").innerHTML = "<div style='margin-top:5px'>" + str + "</div>";
} }
//UI渲染完成后再自动Ping
window.onload = function () {
setTimeout(function () {
pingAllNodes();
}, 800);
};
//]]> //]]>
</script> </script>

View File

@ -355,8 +355,14 @@ msgstr "用于检测连接状态的网址。"
msgid "Probe Interval" msgid "Probe Interval"
msgstr "探测间隔" msgstr "探测间隔"
msgid "The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively." msgid "The interval between initiating probes. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are <code>ns</code>, <code>us</code>, <code>ms</code>, <code>s</code>, <code>m</code>, <code>h</code>, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively."
msgstr "发起探测的间隔。每经过这个时间,就会对一个服务器进行服务器状态检测。时间格式为数字+单位,比如<code>&quot;10s&quot;</code>, <code>&quot;2h45m&quot;</code>,支持的时间单位有 <code>ns</code><code>us</code><code>ms</code><code>s</code><code>m</code><code>h</code>,分别对应纳秒、微秒、毫秒、秒、分、时。" msgstr "发起探测的间隔。时间格式为数字+单位,比如<code>&quot;10s&quot;</code>, <code>&quot;2h45m&quot;</code>,支持的时间单位有 <code>ns</code><code>us</code><code>ms</code><code>s</code><code>m</code><code>h</code>,分别对应纳秒、微秒、毫秒、秒、分、时。"
msgid "Preferred Node Count"
msgstr "优选节点数量"
msgid "The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them."
msgstr "负载均衡器选出最优节点的个数,流量将在这几个节点中随机分配。"
msgid "Shunt" msgid "Shunt"
msgstr "分流" msgstr "分流"
@ -406,8 +412,8 @@ msgstr "IPOnDemand当匹配时碰到任何基于 IP 的规则,将域名立
msgid "Load balancing node list" msgid "Load balancing node list"
msgstr "负载均衡节点列表" msgstr "负载均衡节点列表"
msgid "Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>" msgid "Load balancing node list, <a target='_blank' href='https://xtls.github.io/config/routing.html#balancerobject'>document</a>"
msgstr "负载均衡节点列表,<a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>文档原理</a>" msgstr "负载均衡节点列表,<a target='_blank' href='https://xtls.github.io/config/routing.html#balancerobject'>文档原理</a>"
msgid "From Share URL" msgid "From Share URL"
msgstr "导入分享URL" msgstr "导入分享URL"
@ -1677,3 +1683,9 @@ msgstr "中断现有连接"
msgid "Interrupt existing connections when the selected outbound has changed." msgid "Interrupt existing connections when the selected outbound has changed."
msgstr "当选择的出站发生变化时中断现有连接。" msgstr "当选择的出站发生变化时中断现有连接。"
msgid "Port hopping range"
msgstr "端口跳跃范围"
msgid "Format as 1000:2000 Multiple groups are separated by commas (,)."
msgstr "格式为1000:2000 多组时用逗号(,)隔开。"

View File

@ -8,7 +8,7 @@ server_port=$4
probe_file="/tmp/etc/passwall2/haproxy/Probe_URL" probe_file="/tmp/etc/passwall2/haproxy/Probe_URL"
probeUrl="https://www.google.com/generate_204" probeUrl="https://www.google.com/generate_204"
if [ -f "$probe_file" ]; then if [ -f "$probe_file" ]; then
firstLine=$(head -n 1 "$probe_file" | tr -d ' \t') firstLine=$(head -n 1 "$probe_file" | tr -d ' \t\n')
[ -n "$firstLine" ] && probeUrl="$firstLine" [ -n "$firstLine" ] && probeUrl="$firstLine"
fi fi

View File

@ -23,7 +23,7 @@ uci:revert(appname)
local has_ss = api.is_finded("ss-redir") local has_ss = api.is_finded("ss-redir")
local has_ss_rust = api.is_finded("sslocal") local has_ss_rust = api.is_finded("sslocal")
local has_singbox = api.finded_com("singbox") local has_singbox = api.finded_com("sing-box")
local has_xray = api.finded_com("xray") local has_xray = api.finded_com("xray")
local has_hysteria2 = api.finded_com("hysteria") local has_hysteria2 = api.finded_com("hysteria")
local allowInsecure_default = true local allowInsecure_default = true
@ -182,6 +182,11 @@ do
if true then if true then
local i = 0 local i = 0
local option = "lbss" local option = "lbss"
local function is_ip_port(str)
if type(str) ~= "string" then return false end
local ip, port = str:match("^([%d%.]+):(%d+)$")
return ip and datatypes.ipaddr(ip) and tonumber(port) and tonumber(port) <= 65535
end
uci:foreach(appname, "haproxy_config", function(t) uci:foreach(appname, "haproxy_config", function(t)
i = i + 1 i = i + 1
local node_id = t[option] local node_id = t[option]
@ -191,11 +196,17 @@ do
remarks = "HAProxy负载均衡节点列表[" .. i .. "]", remarks = "HAProxy负载均衡节点列表[" .. i .. "]",
currentNode = node_id and uci:get_all(appname, node_id) or nil, currentNode = node_id and uci:get_all(appname, node_id) or nil,
set = function(o, server) set = function(o, server)
uci:set(appname, t[".name"], option, server) -- 如果当前 lbss 值不是 ip:port 格式,才进行修改
o.newNodeId = server if not is_ip_port(t[option]) then
uci:set(appname, t[".name"], option, server)
o.newNodeId = server
end
end, end,
delete = function(o) delete = function(o)
uci:delete(appname, t[".name"]) -- 如果当前 lbss 值不是 ip:port 格式,才进行删除
if not is_ip_port(t[option]) then
uci:delete(appname, t[".name"])
end
end end
} }
end) end)
@ -1294,19 +1305,21 @@ local function processData(szType, content, add_mode, add_from)
end end
local function curl(url, file, ua, mode) local function curl(url, file, ua, mode)
local curl_args = api.clone(api.curl_args) local curl_args = {
"-skL", "-w %{http_code}", "--retry 3", "--connect-timeout 3"
}
if ua and ua ~= "" and ua ~= "curl" then if ua and ua ~= "" and ua ~= "curl" then
table.insert(curl_args, '--user-agent "' .. ua .. '"') curl_args[#curl_args + 1] = '--user-agent "' .. ua .. '"'
end end
local return_code local return_code, result
if mode == "direct" then if mode == "direct" then
return_code = api.curl_direct(url, file, curl_args) return_code, result = api.curl_direct(url, file, curl_args)
elseif mode == "proxy" then elseif mode == "proxy" then
return_code = api.curl_proxy(url, file, curl_args) return_code, result = api.curl_proxy(url, file, curl_args)
else else
return_code = api.curl_auto(url, file, curl_args) return_code, result = api.curl_auto(url, file, curl_args)
end end
return return_code return tonumber(result)
end end
local function truncate_nodes(add_from) local function truncate_nodes(add_from)
@ -1630,7 +1643,7 @@ local function parse_link(raw, add_mode, add_from, cfgid)
log('成功解析【' .. add_from .. '】节点数量: ' .. #node_list) log('成功解析【' .. add_from .. '】节点数量: ' .. #node_list)
else else
if add_mode == "2" then if add_mode == "2" then
log('获取到的【' .. add_from .. '】订阅内容为空,可能是订阅地址失效,或是网络问题,请请检测。') log('获取到的【' .. add_from .. '】订阅内容为空,可能是订阅地址无效,或是网络问题,请诊断!')
end end
end end
end end
@ -1705,23 +1718,27 @@ local execute = function()
local result = (not access_mode) and "自动" or (access_mode == "direct" and "直连访问" or (access_mode == "proxy" and "通过代理" or "自动")) local result = (not access_mode) and "自动" or (access_mode == "direct" and "直连访问" or (access_mode == "proxy" and "通过代理" or "自动"))
log('正在订阅:【' .. remark .. '' .. url .. ' [' .. result .. ']') log('正在订阅:【' .. remark .. '' .. url .. ' [' .. result .. ']')
local tmp_file = "/tmp/" .. cfgid local tmp_file = "/tmp/" .. cfgid
local raw = curl(url, tmp_file, ua, access_mode) value.http_code = curl(url, tmp_file, ua, access_mode)
if raw == 0 then if value.http_code ~= 200 then
local f = io.open(tmp_file, "r")
local stdout = f:read("*all")
f:close()
raw = trim(stdout)
local old_md5 = value.md5 or ""
local new_md5 = luci.sys.exec("[ -f " .. tmp_file .. " ] && md5sum " .. tmp_file .. " | awk '{print $1}' || echo 0"):gsub("\n", "")
os.remove(tmp_file)
if old_md5 == new_md5 then
log('订阅:【' .. remark .. '】没有变化,无需更新。')
else
parse_link(raw, "2", remark, cfgid)
uci:set(appname, cfgid, "md5", new_md5)
end
else
fail_list[#fail_list + 1] = value fail_list[#fail_list + 1] = value
else
if luci.sys.call("[ -f " .. tmp_file .. " ] && sed -i -e '/^[ \t]*$/d' -e '/^[ \t]*\r$/d' " .. tmp_file) == 0 then
local f = io.open(tmp_file, "r")
local stdout = f:read("*all")
f:close()
local raw_data = trim(stdout)
local old_md5 = value.md5 or ""
local new_md5 = luci.sys.exec("md5sum " .. tmp_file .. " 2>/dev/null | awk '{print $1}'"):gsub("\n", "")
os.remove(tmp_file)
if old_md5 == new_md5 then
log('订阅:【' .. remark .. '】没有变化,无需更新。')
else
parse_link(raw_data, "2", remark, cfgid)
uci:set(appname, cfgid, "md5", new_md5)
end
else
fail_list[#fail_list + 1] = value
end
end end
allowInsecure_default = true allowInsecure_default = true
filter_keyword_mode_default = uci:get(appname, "@global_subscribe[0]", "filter_keyword_mode") or "0" filter_keyword_mode_default = uci:get(appname, "@global_subscribe[0]", "filter_keyword_mode") or "0"
@ -1736,7 +1753,7 @@ local execute = function()
if #fail_list > 0 then if #fail_list > 0 then
for index, value in ipairs(fail_list) do for index, value in ipairs(fail_list) do
log(string.format('【%s】订阅失败可能是订阅地址失效,或是网络问题,请诊断!', value.remark)) log(string.format('【%s】订阅失败可能是订阅地址无效,或是网络问题,请诊断![%s]', value.remark, tostring(value.http_code)))
end end
end end
update_node(0) update_node(0)