luci-app-passwall2: sync upstream

This commit is contained in:
actions 2024-01-29 03:30:11 +08:00
parent 29a9a84edc
commit e5ca4ee15d
15 changed files with 207 additions and 212 deletions

View File

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

View File

@ -226,16 +226,15 @@ function ping_node()
local index = luci.http.formvalue("index")
local address = luci.http.formvalue("address")
local port = luci.http.formvalue("port")
local type = luci.http.formvalue("type") or "icmp"
local e = {}
e.index = index
local nodes_ping = ucic:get(appname, "@global_other[0]", "nodes_ping") or ""
if nodes_ping:find("tcping") and luci.sys.exec("echo -n $(command -v tcping)") ~= "" then
if type == "tcping" and luci.sys.exec("echo -n $(command -v tcping)") ~= "" then
if api.is_ipv6(address) then
address = api.get_ipv6_only(address)
end
e.ping = luci.sys.exec(string.format("echo -n $(tcping -q -c 1 -i 1 -t 2 -p %s %s 2>&1 | grep -o 'time=[0-9]*' | awk -F '=' '{print $2}') 2>/dev/null", port, address))
end
if e.ping == nil or tonumber(e.ping) == 0 then
else
e.ping = luci.sys.exec("echo -n $(ping -c 1 -W 1 %q 2>&1 | grep -o 'time=[0-9]*' | awk -F '=' '{print $2}') 2>/dev/null" % address)
end
luci.http.prepare_content("application/json")

View File

@ -2,6 +2,10 @@ local api = require "luci.passwall2.api"
local appname = api.appname
local sys = api.sys
local port_validate = function(self, value, t)
return value:gsub("-", ":")
end
m = Map(appname)
api.set_apply_on_parse(m)
@ -157,6 +161,7 @@ o.default = "default"
o:value("disable", translate("No patterns are used"))
o:value("default", translate("Default"))
o:value("1:65535", translate("All"))
o.validate = port_validate
---- UDP No Redir Ports
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
@ -167,6 +172,7 @@ o.default = "default"
o:value("disable", translate("No patterns are used"))
o:value("default", translate("Default"))
o:value("1:65535", translate("All"))
o.validate = port_validate
---- TCP Redir Ports
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"))
@ -175,12 +181,14 @@ o:value("default", translate("Default"))
o:value("1:65535", translate("All"))
o:value("22,25,53,143,465,587,853,993,995,80,443", translate("Common Use"))
o:value("80,443", "80,443")
o.validate = port_validate
---- UDP Redir Ports
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"))
o.default = "default"
o:value("default", translate("Default"))
o:value("1:65535", translate("All"))
o.validate = port_validate
node = s:option(ListValue, "node", "<a style='color: red'>" .. translate("Node") .. "</a>")
node.default = "default"

View File

@ -50,7 +50,7 @@ o:depends("balancing_enable", true)
o = s:option(ListValue, "health_check_type", translate("Health Check Type"))
o.default = "passwall_logic"
o:value("tcp", "TCP")
o:value("passwall_logic", translate("Availability test") .. string.format("(passwall %s)", translate("Inner implement")))
o:value("passwall_logic", translate("URL Test") .. string.format("(passwall %s)", translate("Inner implement")))
o:depends("balancing_enable", true)
---- Health Check Inter
@ -61,7 +61,7 @@ o:depends("balancing_enable", true)
o = s:option(DummyValue, "health_check_tips", " ")
o.rawhtml = true
o.cfgvalue = function(t, n)
return string.format('<span style="color: red">%s</span>', translate("When the availability test is used, the load balancing node will be converted into a Socks node. when node list set customizing, must be a Socks node, otherwise the health check will be invalid."))
return string.format('<span style="color: red">%s</span>', translate("When the URL test is used, the load balancing node will be converted into a Socks node. when node list set customizing, must be a Socks node, otherwise the health check will be invalid."))
end
o:depends("health_check_type", "passwall_logic")

View File

@ -10,15 +10,19 @@ api.set_apply_on_parse(m)
s = m:section(TypedSection, "global_other")
s.anonymous = true
o = s:option(MultiValue, "nodes_ping", " ")
o:value("auto_ping", translate("Auto Ping"), translate("This will automatically ping the node for latency"))
o:value("tcping", translate("Tcping"), translate("This will use tcping replace ping detection of node"))
o:value("info", translate("Show server address and port"), translate("Show server address and port"))
o = s:option(ListValue, "auto_detection_time", translate("Automatic detection delay"))
o:value("0", translate("Close"))
o:value("icmp", "Ping")
o:value("tcping", "TCP Ping")
o = s:option(Flag, "show_node_info", translate("Show server address and port"))
o.default = "0"
-- [[ Add the node via the link ]]--
s:append(Template(appname .. "/node_list/link_add_node"))
local nodes_ping = m:get("@global_other[0]", "nodes_ping") or ""
local auto_detection_time = m:get("@global_other[0]", "auto_detection_time") or "0"
local show_node_info = m:get("@global_other[0]", "show_node_info") or "0"
-- [[ Node List ]]--
s = m:section(TypedSection, "nodes")
@ -103,7 +107,7 @@ o.cfgvalue = function(t, n)
local port = m:get(n, "port") or ""
str = str .. translate(type) .. "" .. remarks
if address ~= "" and port ~= "" then
if nodes_ping:find("info") then
if show_node_info == "1" then
if datatypes.ip6addr(address) then
str = str .. string.format("[%s]:%s", address, port)
else
@ -117,23 +121,38 @@ o.cfgvalue = function(t, n)
end
---- Ping
o = s:option(DummyValue, "ping")
o = s:option(DummyValue, "ping", "Ping")
o.width = "8%"
o.rawhtml = true
o.cfgvalue = function(t, n)
local result = "---"
if not nodes_ping:find("auto_ping") then
result = string.format('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'%s\',this)">Ping</a></span>', n)
if auto_detection_time ~= "icmp" then
result = string.format('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'%s\', this, \'icmp\')">%s</a></span>', n, translate("Test"))
else
result = string.format('<span class="ping_value" cbiid="%s">---</span>', n)
end
return result
end
o = s:option(DummyValue, "_url_test")
---- TCP Ping
o = s:option(DummyValue, "tcping", "TCPing")
o.width = "8%"
o.rawhtml = true
o.cfgvalue = function(t, n)
return string.format('<input type="button" class="cbi-button" value="%s" onclick="javascript:urltest_node(\'%s\',this)"', translate("Availability test"), n)
local result = "---"
if auto_detection_time ~= "tcping" then
result = string.format('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'%s\', this, \'tcping\')">%s</a></span>', n, translate("Test"))
else
result = string.format('<span class="tcping_value" cbiid="%s">---</span>', n)
end
return result
end
o = s:option(DummyValue, "_url_test", translate("URL Test"))
o.width = "8%"
o.rawhtml = true
o.cfgvalue = function(t, n)
return string.format('<span class="ping"><a href="javascript:void(0)" onclick="javascript:urltest_node(\'%s\', this)">%s</a></span>', n, translate("Test"))
end
m:append(Template(appname .. "/node_list/node_list"))

View File

@ -7,6 +7,10 @@ local has_xray = api.finded_com("xray")
local has_fw3 = api.is_finded("fw3")
local has_fw4 = api.is_finded("fw4")
local port_validate = function(self, value, t)
return value:gsub("-", ":")
end
m = Map(appname)
api.set_apply_on_parse(m)
@ -63,6 +67,7 @@ o = s:option(Value, "tcp_no_redir_ports", translate("TCP No Redir Ports"))
o.default = "disable"
o:value("disable", translate("No patterns are used"))
o:value("1:65535", translate("All"))
o.validate = port_validate
---- UDP No Redir Ports
o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
@ -72,6 +77,7 @@ o = s:option(Value, "udp_no_redir_ports", translate("UDP No Redir Ports"),
o.default = "disable"
o:value("disable", translate("No patterns are used"))
o:value("1:65535", translate("All"))
o.validate = port_validate
---- TCP Redir Ports
o = s:option(Value, "tcp_redir_ports", translate("TCP Redir Ports"))
@ -79,11 +85,13 @@ o.default = "22,25,53,143,465,587,853,993,995,80,443"
o:value("1:65535", translate("All"))
o:value("22,25,53,143,465,587,853,993,995,80,443", translate("Common Use"))
o:value("80,443", translate("Only Web"))
o.validate = port_validate
---- UDP Redir Ports
o = s:option(Value, "udp_redir_ports", translate("UDP Redir Ports"))
o.default = "1:65535"
o:value("1:65535", translate("All"))
o.validate = port_validate
---- Use nftables
o = s:option(ListValue, "use_nft", translate("Firewall tools"))

View File

@ -41,7 +41,7 @@ o:value("trojan", translate("Trojan"))
o:value("wireguard", translate("WireGuard"))
o:value("_balancing", translate("Balancing"))
o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
o:value("_iface", translate("Custom Interface"))
o = s:option(Value, option_name("iface"), translate("Interface"))
o.default = "eth1"

View File

@ -58,7 +58,7 @@ if singbox_tags:find("with_quic") then
o:value("hysteria2", "Hysteria2")
end
o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
o:value("_iface", translate("Custom Interface"))
o = s:option(Value, option_name("iface"), translate("Interface"))
o.default = "eth1"

View File

@ -345,7 +345,7 @@ o = s:option(ListValue, option_name("outbound_node"), translate("outbound node")
o:value("nil", translate("Close"))
o:value("_socks", translate("Custom Socks"))
o:value("_http", translate("Custom HTTP"))
o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)")
o:value("_iface", translate("Custom Interface"))
for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end
o.default = "nil"

View File

@ -292,7 +292,7 @@ function get_domain_from_url(url)
end
function get_valid_nodes()
local nodes_ping = uci_get_type("global_other", "nodes_ping") or ""
local show_node_info = uci_get_type("global_other", "show_node_info") or "0"
local nodes = {}
uci:foreach(appname, "nodes", function(e)
e.id = e[".name"]
@ -319,7 +319,7 @@ function get_valid_nodes()
end
if is_ipv6(address) then address = get_ipv6_full(address) end
e["remark"] = "%s[%s]" % {type, e.remarks}
if nodes_ping:find("info") then
if show_node_info == "1" then
e["remark"] = "%s[%s] %s:%s" % {type, e.remarks, address, e.port}
end
e.node_type = "normal"

View File

@ -103,6 +103,8 @@ local api = require "luci.passwall2.api"
<input class="btn cbi-button cbi-button-remove" type="button" onclick="delete_select_nodes()" value="<%:Delete select nodes%>" />
<input class="btn cbi-button" type="button" onclick="checked_all_node(this)" value="<%:Select all%>" />
<input class="btn cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" />
<input class="btn cbi-button cbi-button-save" type="submit" name="cbi.save" value="<%:Save%>" />
<input class="btn cbi-button cbi-button-reset" type="button" value="<%:Reset%>" onclick="location.href='<%=REQUEST_URI%>'" />
<div id="div_node_count"></div>
</div>
</div>

View File

@ -162,9 +162,10 @@ local api = require "luci.passwall2.api"
}
}
var url = null;
if (v_type === "SS") {
if (v_type === "SS" || v_type === "SS-Rust" || ((v_type === "sing-box" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "shadowsocks")) {
protocol = "ss"
var v_port = opt.get(dom_prefix + "port");
var v_method = opt.get(dom_prefix + "method");
var v_method = opt.get(dom_prefix + "method") || opt.get(dom_prefix + "ss_method");
var v_password = opt.get(dom_prefix + "password");
url = b64encsafe(v_method.value + ":" + v_password.value) + "@" +
@ -172,7 +173,9 @@ local api = require "luci.passwall2.api"
v_port.value + "/?";
var params = "";
var v_plugin = opt.get(dom_prefix + "plugin").value;
var v_plugin_dom = opt.get(dom_prefix + "plugin");
if (v_plugin_dom) {
var v_plugin = v_plugin_dom.value;
if (v_plugin && v_plugin != "none") {
if (v_plugin == "simple-obfs" || v_plugin == "obfs-local") {
v_plugin = "obfs-local";
@ -183,32 +186,6 @@ local api = require "luci.passwall2.api"
}
params += "&plugin=" + encodeURIComponent(v_plugin);
}
params += "&group="
params += "#" + encodeURIComponent(v_alias.value);
if (params[0] == "&") {
params = params.substring(1);
}
url += params;
} else if (v_type === "SS-Rust") {
var v_port = opt.get(dom_prefix + "port");
var v_method = opt.get(dom_prefix + "method");
var v_password = opt.get(dom_prefix + "password");
url = btoa(v_method.value + ":" + v_password.value) + "@" +
_address + ":" +
v_port.value + "/?";
var params = "";
var v_plugin = opt.get(dom_prefix + "plugin").value;
if (v_plugin && v_plugin != "none") {
if (v_plugin == "simple-obfs" || v_plugin == "obfs-local") {
v_plugin = "obfs-local";
}
var v_plugin_opts = opt.get(dom_prefix + "plugin_opts").value;
if (v_plugin_opts && v_plugin_opts != "") {
v_plugin += ";" + v_plugin_opts;
}
params += "&plugin=" + encodeURIComponent(v_plugin);
}
params += "&group="
params += "#" + encodeURIComponent(v_alias.value);
@ -600,12 +577,23 @@ local api = require "luci.passwall2.api"
method = userInfo.substr(0, userInfoSplitIndex);
password = userInfo.substr(userInfoSplitIndex + 1);
}
if (["2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"].includes(method)) {
opt.set('type', "SS-Rust");
dom_prefix = "ssrust_"
if (has_singbox) {
dom_prefix = "singbox_"
opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "shadowsocks");
} else if (has_xray) {
dom_prefix = "xray_"
opt.set('type', "Xray");
opt.set(dom_prefix + 'protocol', "shadowsocks");
} else {
if (["2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305"].includes(method)) {
dom_prefix = "ssrust_"
opt.set('type', "SS-Rust");
} else {
dom_prefix = "ss_"
opt.set('type', "SS");
}
}
opt.set(dom_prefix + 'address', server);
opt.set(dom_prefix + 'port', port);
opt.set(dom_prefix + 'password', password || "");
@ -620,9 +608,22 @@ local api = require "luci.passwall2.api"
} else {
var sstr = b64decsafe(url0);
var team = sstr.split('@');
opt.set('type', "SS");
var part1 = team[0].split(':');
var part2 = team[1].split(':');
if (has_singbox) {
dom_prefix = "singbox_"
opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "shadowsocks");
} else if (has_xray) {
dom_prefix = "xray_"
opt.set('type', "Xray");
opt.set(dom_prefix + 'protocol', "shadowsocks");
} else {
dom_prefix = "ss_"
opt.set('type', "SS");
}
opt.set(dom_prefix + 'address', part2[0]);
opt.set(dom_prefix + 'port', part2[1]);
opt.set(dom_prefix + 'password', part1[1]);
@ -653,17 +654,17 @@ local api = require "luci.passwall2.api"
}
}
if (has_singbox) {
opt.set('type', "sing-box");
dom_prefix = "singbox_"
opt.set('type', "sing-box");
} else if (has_xray) {
opt.set('type', "Xray");
dom_prefix = "xray_"
opt.set('type', "Xray");
}
opt.set(dom_prefix + 'protocol', "trojan");
opt.set(dom_prefix + 'address', m.hostname);
opt.set(dom_prefix + 'port', m.port || "443");
opt.set(dom_prefix + 'password', decodeURIComponent(password));
opt.set(dom_prefix + 'tls', true);
opt.set(dom_prefix + 'tls', queryParam.tls === "1");
opt.set(dom_prefix + 'tls_serverName', queryParam.peer || queryParam.sni || '');
opt.set(dom_prefix + 'tls_allowInsecure', queryParam.allowinsecure === '1');
opt.set(dom_prefix + 'mux', queryParam.mux === '1');
@ -675,12 +676,12 @@ local api = require "luci.passwall2.api"
var sstr = b64DecodeUnicode(ssu[1]);
var ploc = sstr.indexOf("/?");
if (has_singbox) {
opt.set('type', "sing-box");
dom_prefix = "singbox_"
opt.set('type', "sing-box");
}
if (has_xray) {
opt.set('type', "Xray");
dom_prefix = "xray_"
opt.set('type', "Xray");
}
opt.set(dom_prefix + 'protocol', "vmess");
var url0, param = "";
@ -728,12 +729,12 @@ local api = require "luci.passwall2.api"
}
if (ssu[0] === "vless") {
if (has_singbox) {
opt.set('type', "sing-box");
dom_prefix = "singbox_"
opt.set('type', "sing-box");
}
if (has_xray) {
opt.set('type', "Xray");
dom_prefix = "xray_"
opt.set('type', "Xray");
}
opt.set(dom_prefix + 'protocol', "vless");
var m = parseNodeUrl(ssrurl);
@ -897,8 +898,8 @@ local api = require "luci.passwall2.api"
}
}
if (has_singbox) {
opt.set('type', "sing-box");
dom_prefix = "singbox_"
opt.set('type', "sing-box");
opt.set(dom_prefix + 'protocol', "hysteria2");
opt.set(dom_prefix + 'hysteria2_auth_password', decodeURIComponent(password));
if (queryParam["obfs-password"]) {

View File

@ -53,6 +53,7 @@ table td, .table .td {
<script type="text/javascript">
//<![CDATA[
let auto_detection_time = "<%=api.uci_get_type("global_other", "auto_detection_time", "0")%>"
var node_list = {};
var node_count = 0;
@ -246,8 +247,8 @@ table td, .table .td {
function urltest_node(cbi_id, dom) {
if (cbi_id != null) {
dom.disabled = true;
dom.value = "<%:Check...%>";
dom.onclick = null
dom.innerText = "<%:Check...%>";
XHR.get('<%=api.url("urltest_node")%>', {
id: cbi_id
},
@ -258,6 +259,7 @@ table td, .table .td {
} else {
var color = "red";
var use_time = result.use_time;
use_time = parseInt(use_time) + 1;
if (use_time < 1000) {
color = "green";
} else if (use_time < 2000) {
@ -265,20 +267,25 @@ table td, .table .td {
} else {
color = "red";
}
dom.outerHTML = "<font style='color:" + color + "'>" + result.use_time + " ms" + "</font>";
dom.outerHTML = "<font style='color:" + color + "'>" + use_time + " ms" + "</font>";
}
} else {
dom.outerHTML = "<font style='color:red'><%:Error%></font>";
}
}
);
}
}
function ping_node(cbi_id, dom) {
function ping_node(cbi_id, dom, type) {
var full = get_address_full(cbi_id);
if (full != null) {
dom.onclick = null
dom.innerText = "<%:Check...%>";
XHR.get('<%=api.url("ping_node")%>', {
address: full.address,
port: full.port
port: full.port,
type: type
},
function(x, result) {
if(x && x.status == 200) {
@ -299,9 +306,12 @@ table td, .table .td {
}
}
get_now_use_node();
/* 自动Ping */
if (auto_detection_time == "icmp" || auto_detection_time == "tcping") {
var nodes = [];
const ping_value = document.getElementsByClassName('ping_value');
const ping_value = document.getElementsByClassName(auto_detection_time == "tcping" ? 'tcping_value' : 'ping_value');
for (var i = 0; i < ping_value.length; i++) {
var cbi_id = ping_value[i].getAttribute("cbiid");
var full = get_address_full(cbi_id);
@ -325,8 +335,6 @@ table td, .table .td {
}
}
get_now_use_node();
const _xhr = (index) => {
return new Promise((res) => {
const dom = nodes[index];
@ -334,7 +342,8 @@ table td, .table .td {
ajax.post('<%=api.url("ping_node")%>', {
index: dom.indexs,
address: dom.address,
port: dom.port
port: dom.port,
type: auto_detection_time
},
function(x, result) {
if (x && x.status == 200) {
@ -377,53 +386,7 @@ table td, .table .td {
for (let i = 0; i < 20; i++) {
thread()
}
/* 递归单请求方法
var index = 0;
function auto_ping() {
if (index >= nodes.length) {
return;
}
var indexs = nodes[index].indexs;
var address = nodes[index].address;
var port = nodes[index].port;
ajax.post('<%=api.url("ping_node")%>', {
index: indexs,
address: address,
port: port
},
function(x, result) {
if (x && x.status == 200) {
var strs = indexs.split(",");
for (var i = 0; i < strs.length; i++) {
if (result.ping == null || result.ping.trim() == "") {
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
} else {
var ping = parseInt(result.ping);
if (ping < 100)
ping_value[strs[i]].innerHTML = "<font style='color:green'>" + result.ping + " ms" + "</font>";
else if (ping < 200)
ping_value[strs[i]].innerHTML = "<font style='color:#fb9a05'>" + result.ping + " ms" + "</font>";
else if (ping >= 200)
ping_value[strs[i]].innerHTML = "<font style='color:red'>" + result.ping + " ms" + "</font>";
}
}
}
index++;
return auto_ping();
},
function(x) {
var strs = indexs.split(",");
for (var i = 0; i < strs.length; i++) {
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
}
index++;
return auto_ping();
},
);
}
auto_ping();
*/
var edit_btn = document.getElementById("cbi-passwall2-nodes").getElementsByClassName("cbi-button cbi-button-edit");
for (var i = 0; i < edit_btn.length; i++) {

View File

@ -502,23 +502,17 @@ msgstr "加密"
msgid "Latency"
msgstr "延迟"
msgid "Show Add Mode"
msgstr "显示添加方式"
msgid "Show Group"
msgstr "显示组"
msgid "Group"
msgstr "组"
msgid "Auto Ping"
msgstr "自动Ping"
msgid "Automatic detection delay"
msgstr "自动检测延迟"
msgid "Show server address and port"
msgstr "显示服务器地址和端口"
msgid "Availability test"
msgstr "可用性测试"
msgid "URL Test"
msgstr "URL 测试"
msgid "Test"
msgstr "测试"
msgid "Node num"
msgstr "节点数量"
@ -709,8 +703,8 @@ msgstr "内置实现"
msgid "Health Check Inter"
msgstr "健康检查节点间隔时间"
msgid "When the availability test is used, the load balancing node will be converted into a Socks node. when node list set customizing, must be a Socks node, otherwise the health check will be invalid."
msgstr "当使用可用性测试时负载均衡节点将转换成Socks节点。下面的节点列表自定义时必须为Socks节点否则健康检查将无效。"
msgid "When the URL test is used, the load balancing node will be converted into a Socks node. when node list set customizing, must be a Socks node, otherwise the health check will be invalid."
msgstr "当使用URL测试时负载均衡节点将转换成Socks节点。下面的节点列表自定义时必须为Socks节点否则健康检查将无效。"
msgid "Add a node, Export Of Multi WAN Only support Multi Wan. Load specific gravity range 1-256. Multiple primary servers can be load balanced, standby will only be enabled when the primary server is offline! Multiple groups can be set, Haproxy port same one for each group."
msgstr "添加节点指定出口功能是为多WAN用户准备的。负载比重范围1-256。多个主服务器可以负载均衡备用只有在主服务器离线时才会启用可以设置多个组负载均衡端口相同则为一组。"

View File

@ -39,7 +39,8 @@ config global_xray
option route_only '0'
config global_other
option nodes_ping 'auto_ping tcping'
option auto_detection_time 'tcping'
option show_node_info '0'
config global_rules
option auto_update '0'