🎄 Sync 2025-11-05 10:53:12

This commit is contained in:
actions-user
2025-11-05 10:53:12 +08:00
parent e112520fb4
commit 9c732ba677
12 changed files with 325 additions and 300 deletions

View File

@@ -5,10 +5,10 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=daed PKG_NAME:=daed
PKG_VERSION:=2025.09.23 PKG_VERSION:=2025.11.03
DAED_VERSION:=daed-c3588a9 DAED_VERSION:=daed-c3588a9
WING_VERSION:=wing-6df3da2 WING_VERSION:=wing-6df3da2
CORE_VERSION:=core-5abd651 CORE_VERSION:=core-7e67e31
WING_HASH_SHORT:=$(shell echo $(WING_VERSION) | cut -d- -f2) WING_HASH_SHORT:=$(shell echo $(WING_VERSION) | cut -d- -f2)
CORE_HASH_SHORT:=$(shell echo $(CORE_VERSION) | cut -d- -f2) CORE_HASH_SHORT:=$(shell echo $(CORE_VERSION) | cut -d- -f2)
PKG_RELEASE:=1 PKG_RELEASE:=1

View File

@@ -74,10 +74,12 @@ function index()
entry({"admin", "services", appname, "connect_status"}, call("connect_status")).leaf = true entry({"admin", "services", appname, "connect_status"}, call("connect_status")).leaf = true
entry({"admin", "services", appname, "ping_node"}, call("ping_node")).leaf = true entry({"admin", "services", appname, "ping_node"}, call("ping_node")).leaf = true
entry({"admin", "services", appname, "urltest_node"}, call("urltest_node")).leaf = true entry({"admin", "services", appname, "urltest_node"}, call("urltest_node")).leaf = true
entry({"admin", "services", appname, "add_node"}, call("add_node")).leaf = true
entry({"admin", "services", appname, "set_node"}, call("set_node")).leaf = true entry({"admin", "services", appname, "set_node"}, call("set_node")).leaf = true
entry({"admin", "services", appname, "copy_node"}, call("copy_node")).leaf = true entry({"admin", "services", appname, "copy_node"}, call("copy_node")).leaf = true
entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true
entry({"admin", "services", appname, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true entry({"admin", "services", appname, "delete_select_nodes"}, call("delete_select_nodes")).leaf = true
entry({"admin", "services", appname, "get_node"}, call("get_node")).leaf = true
entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true entry({"admin", "services", appname, "update_rules"}, call("update_rules")).leaf = true
entry({"admin", "services", appname, "subscribe_del_node"}, call("subscribe_del_node")).leaf = true entry({"admin", "services", appname, "subscribe_del_node"}, call("subscribe_del_node")).leaf = true
entry({"admin", "services", appname, "subscribe_del_all"}, call("subscribe_del_all")).leaf = true entry({"admin", "services", appname, "subscribe_del_all"}, call("subscribe_del_all")).leaf = true
@@ -326,6 +328,21 @@ function urltest_node()
http_write_json(e) http_write_json(e)
end end
function add_node()
local redirect = http.formvalue("redirect")
local uuid = api.gen_short_uuid()
uci:section(appname, "nodes", uuid)
if redirect == "1" then
api.uci_save(uci, appname)
http.redirect(api.url("node_config", uuid))
else
api.uci_save(uci, appname, true, true)
http_write_json({result = uuid})
end
end
function set_node() function set_node()
local type = http.formvalue("type") local type = http.formvalue("type")
local config = http.formvalue("config") local config = http.formvalue("config")
@@ -350,7 +367,7 @@ function copy_node()
end) end)
end end
end end
uci:delete(appname, uuid, "add_from") uci:delete(appname, uuid, "group")
uci:set(appname, uuid, "add_mode", 1) uci:set(appname, uuid, "add_mode", 1)
api.uci_save(uci, appname) api.uci_save(uci, appname)
http.redirect(api.url("node_config", uuid)) http.redirect(api.url("node_config", uuid))
@@ -386,6 +403,7 @@ end
function delete_select_nodes() function delete_select_nodes()
local ids = http.formvalue("ids") local ids = http.formvalue("ids")
local redirect = http.formvalue("redirect")
string.gsub(ids, '[^' .. "," .. ']+', function(w) string.gsub(ids, '[^' .. "," .. ']+', function(w)
if (uci:get(appname, "@global[0]", "node") or "") == w then if (uci:get(appname, "@global[0]", "node") or "") == w then
uci:delete(appname, '@global[0]', "node") uci:delete(appname, '@global[0]', "node")
@@ -454,10 +472,10 @@ function delete_select_nodes()
end end
end) end)
if (uci:get(appname, w, "add_mode") or "0") == "2" then if (uci:get(appname, w, "add_mode") or "0") == "2" then
local add_from = uci:get(appname, w, "add_from") or "" local group = uci:get(appname, w, "group") or ""
if add_from ~= "" then if group ~= "" then
uci:foreach(appname, "subscribe_list", function(t) uci:foreach(appname, "subscribe_list", function(t)
if t["remark"] == add_from then if t["remark"] == group then
uci:delete(appname, t[".name"], "md5") uci:delete(appname, t[".name"], "md5")
end end
end) end)
@@ -465,7 +483,25 @@ function delete_select_nodes()
end end
uci:delete(appname, w) uci:delete(appname, w)
end) end)
api.uci_save(uci, appname, true, true) if redirect == "1" then
api.uci_save(uci, appname)
http.redirect(api.url("node_list"))
else
api.uci_save(uci, appname, true, true)
end
end
function get_node()
local id = http.formvalue("id")
local result = {}
if id then
result = uci:get_all(appname, id)
else
uci:foreach(appname, "nodes", function(t)
result[#result + 1] = t
end)
end
http_write_json(result)
end end
function update_rules() function update_rules()

View File

@@ -22,6 +22,19 @@ o = s:option(Value, "remarks", translate("Node Remarks"))
o.default = translate("Remarks") o.default = translate("Remarks")
o.rmempty = false o.rmempty = false
o = s:option(Value, "group", translate("Group Name"))
local groups = {}
m.uci:foreach(appname, "nodes", function(s)
if s[".name"] ~= arg[1] then
if s.group then
groups[s.group] = true
end
end
end)
for k, v in pairs(groups) do
o:value(k)
end
local fs = require "nixio.fs" local fs = require "nixio.fs"
local types_dir = "/usr/lib/lua/luci/model/cbi/passwall2/client/type/" local types_dir = "/usr/lib/lua/luci/model/cbi/passwall2/client/type/"

View File

@@ -21,214 +21,6 @@ o.default = "0"
-- [[ Add the node via the link ]]-- -- [[ Add the node via the link ]]--
s:append(Template(appname .. "/node_list/link_add_node")) s:append(Template(appname .. "/node_list/link_add_node"))
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")
s.anonymous = true
s.addremove = true
s.template = "cbi/tblsection"
s.extedit = api.url("node_config", "%s")
function s.create(e, t)
local uuid = api.gen_short_uuid()
t = uuid
TypedSection.create(e, t)
luci.http.redirect(e.extedit:format(t))
end
function s.remove(e, t)
m.uci:foreach(appname, "socks", function(s)
if s["node"] == t then
m:del(s[".name"])
end
for k, v in ipairs(m:get(s[".name"], "autoswitch_backup_node") or {}) do
if v and v == t then
sys.call(string.format("uci -q del_list %s.%s.autoswitch_backup_node='%s'", appname, s[".name"], v))
end
end
end)
m.uci:foreach(appname, "acl_rule", function(s)
if s["node"] and s["node"] == t then
m:set(s[".name"], "node", "default")
end
end)
m.uci:foreach(appname, "nodes", function(s)
if s["preproxy_node"] == t then
m:del(s[".name"], "preproxy_node")
m:del(s[".name"], "chain_proxy")
end
if s["to_node"] == t then
m:del(s[".name"], "to_node")
m:del(s[".name"], "chain_proxy")
end
local list_name = s["urltest_node"] and "urltest_node" or (s["balancing_node"] and "balancing_node")
if list_name then
local nodes = m.uci:get_list(appname, s[".name"], list_name)
if nodes then
local changed = false
local new_nodes = {}
for _, node in ipairs(nodes) do
if node ~= t then
table.insert(new_nodes, node)
else
changed = true
end
end
if changed then
m.uci:set_list(appname, s[".name"], list_name, new_nodes)
end
end
end
if s["fallback_node"] == t then
m:del(s[".name"], "fallback_node")
end
end)
m.uci:foreach(appname, "subscribe_list", function(s)
if s["preproxy_node"] == t then
m:del(s[".name"], "preproxy_node")
m:del(s[".name"], "chain_proxy")
end
if s["to_node"] == t then
m:del(s[".name"], "to_node")
m:del(s[".name"], "chain_proxy")
end
end)
if (m:get(t, "add_mode") or "0") == "2" then
local add_from = m:get(t, "add_from") or ""
if add_from ~= "" then
m.uci:foreach(appname, "subscribe_list", function(s)
if s["remark"] == add_from then
m:del(s[".name"], "md5")
end
end)
end
end
TypedSection.remove(e, t)
local new_node
local node0 = m:get("@nodes[0]") or nil
if node0 then
new_node = node0[".name"]
end
if (m:get("@global[0]", "node") or "") == t then
if new_node then
m:set('@global[0]', "node", new_node)
else
m:del('@global[0]', "node")
end
end
end
s.sortable = true
-- 简洁模式
o = s:option(DummyValue, "add_from", "")
o.cfgvalue = function(t, n)
local v = Value.cfgvalue(t, n)
if v and v ~= '' then
local group = m:get(n, "group") or ""
if group ~= "" then
v = v .. " " .. group
end
return v
else
return ''
end
end
o = s:option(DummyValue, "remarks", translate("Remarks"))
o.rawhtml = true
o.cfgvalue = function(t, n)
local str = ""
local is_sub = m:get(n, "is_sub") or ""
local group = m:get(n, "group") or ""
local remarks = m:get(n, "remarks") or ""
local type = m:get(n, "type") or ""
str = str .. string.format("<input type='hidden' id='cbid.%s.%s.type' value='%s'/>", appname, n, type)
if type == "sing-box" or type == "Xray" then
local protocol = m:get(n, "protocol")
if protocol == "_balancing" then
protocol = translate("Balancing")
elseif protocol == "_urltest" then
protocol = "URLTest"
elseif protocol == "_shunt" then
protocol = translate("Shunt")
elseif protocol == "vmess" then
protocol = "VMess"
elseif protocol == "vless" then
protocol = "VLESS"
elseif protocol == "shadowsocks" then
protocol = "SS"
elseif protocol == "shadowsocksr" then
protocol = "SSR"
elseif protocol == "wireguard" then
protocol = "WG"
elseif protocol == "hysteria" then
protocol = "HY"
elseif protocol == "hysteria2" then
protocol = "HY2"
elseif protocol == "anytls" then
protocol = "AnyTLS"
elseif protocol == "ssh" then
protocol = "SSH"
else
protocol = protocol:gsub("^%l",string.upper)
end
if type == "sing-box" then type = "Sing-Box" end
type = type .. " " .. protocol
end
local address = m:get(n, "address") or ""
local port = m:get(n, "port") or ""
local port_s = (port ~= "") and port or m:get(n, "hysteria_hop") or m:get(n, "hysteria2_hop") or ""
str = str .. translate(type) .. "" .. remarks
if address ~= "" and port_s ~= "" then
port_s = port_s:gsub(":", "-")
if show_node_info == "1" then
if datatypes.ip6addr(address) then
str = str .. string.format("[%s]:%s", address, port_s)
else
str = str .. string.format("%s:%s", address, port_s)
end
end
end
str = str .. string.format("<input type='hidden' id='cbid.%s.%s.address' value='%s'/>", appname, n, address)
str = str .. string.format("<input type='hidden' id='cbid.%s.%s.port' value='%s'/>", appname, n, port)
return str
end
---- Ping
o = s:option(DummyValue, "ping", "Ping")
o.width = "8%"
o.rawhtml = true
o.cfgvalue = function(t, n)
local result = "---"
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
---- TCP Ping
o = s:option(DummyValue, "tcping", "TCPing")
o.width = "8%"
o.rawhtml = true
o.cfgvalue = function(t, 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")) m:append(Template(appname .. "/node_list/node_list"))
return m return m

View File

@@ -195,7 +195,7 @@ o.cfgvalue = function(t, n)
str = str ~= "" and "<br>" .. str or "" str = str ~= "" and "<br>" .. str or ""
local num = 0 local num = 0
m.uci:foreach(appname, "nodes", function(s) m.uci:foreach(appname, "nodes", function(s)
if s["add_from"] ~= "" and s["add_from"] == remark then if s["group"] ~= "" and s["group"] == remark then
num = num + 1 num = num + 1
end end
end) end)

View File

@@ -79,6 +79,10 @@ local api = require "luci.passwall2.api"
} }
} }
function add_new_node() {
window.location.href = '<%=api.url("add_node")%>?redirect=1';
}
//]]> //]]>
</script> </script>
@@ -99,7 +103,7 @@ local api = require "luci.passwall2.api"
<div class="cbi-value"> <div class="cbi-value">
<label class="cbi-value-title"></label> <label class="cbi-value-title"></label>
<div class="cbi-value-field"> <div class="cbi-value-field">
<input class="btn cbi-button cbi-button-add" type="submit" name="cbi.cts.<%=api.appname%>.nodes." value="<%:Add%>" /> <input class="btn cbi-button cbi-button-add" type="button" onclick="add_new_node()" value="<%:Add%>" />
<input class="btn cbi-button cbi-button-add" type="button" onclick="open_add_link_div()" value="<%:Add the node via the link%>" /> <input class="btn cbi-button cbi-button-add" type="button" onclick="open_add_link_div()" value="<%:Add the node via the link%>" />
<input class="btn cbi-button cbi-button-remove" type="button" onclick="clear_all_nodes()" value="<%:Clear all nodes%>" /> <input class="btn cbi-button cbi-button-remove" type="button" onclick="clear_all_nodes()" value="<%:Clear all nodes%>" />
<input class="btn cbi-button cbi-button-remove" type="button" onclick="delete_select_nodes()" value="<%:Delete select nodes%>" /> <input class="btn cbi-button cbi-button-remove" type="button" onclick="delete_select_nodes()" value="<%:Delete select nodes%>" />
@@ -107,7 +111,6 @@ local api = require "luci.passwall2.api"
<input class="btn cbi-button cbi-button-apply" type="submit" name="cbi.apply" value="<%:Save & Apply%>" /> <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-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%>'" /> <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>
</div> </div>

View File

@@ -61,11 +61,45 @@ table td, .table .td {
} }
</style> </style>
<% if api.is_js_luci() then -%>
<script type="text/javascript">
var cbi_t = [];
function cbi_t_add(section, tab) {
var t = document.getElementById('tab.' + section + '.' + tab);
var c = document.getElementById('container.' + section + '.' + tab);
if( t && c ) {
cbi_t[section] = (cbi_t[section] || [ ]);
cbi_t[section][tab] = { 'tab': t, 'container': c, 'cid': c.id };
}
}
function cbi_t_switch(section, tab) {
if( cbi_t[section] && cbi_t[section][tab] ) {
var o = cbi_t[section][tab];
var h = document.getElementById('tab.' + section);
for( var tid in cbi_t[section] ) {
var o2 = cbi_t[section][tid];
if( o.tab.id != o2.tab.id ) {
o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab( |$)/, " cbi-tab-disabled ");
o2.container.style.display = 'none';
}
else {
if(h) h.value = tab;
o2.tab.className = o2.tab.className.replace(/(^| )cbi-tab-disabled( |$)/, " cbi-tab ");
o2.container.style.display = 'block';
}
}
}
return false
}
</script>
<%- end %>
<script type="text/javascript"> <script type="text/javascript">
//<![CDATA[ //<![CDATA[
let auto_detection_time = "<%=api.uci_get_type("global_other", "auto_detection_time", "0")%>" let auto_detection_time = "<%=api.uci_get_type("global_other", "auto_detection_time", "0")%>"
var node_list = {}; var nodes_list = [];
var node_count = 0;
var ajax = { var ajax = {
post: function(url, data, fn_success, timeout, fn_timeout) { post: function(url, data, fn_success, timeout, fn_timeout) {
@@ -122,6 +156,10 @@ table td, .table .td {
window.location.href = '<%=api.url("copy_node")%>' + "?section=" + cbi_id; window.location.href = '<%=api.url("copy_node")%>' + "?section=" + cbi_id;
} }
function del_node(cbi_id) {
window.location.href = '<%=api.url("delete_select_nodes")%>' + "?redirect=1&ids=" + cbi_id;
}
var section = ""; var section = "";
function open_set_node_div(cbi_id) { function open_set_node_div(cbi_id) {
section = cbi_id; section = cbi_id;
@@ -136,6 +174,7 @@ table td, .table .td {
} }
function _cbi_row_top(id) { function _cbi_row_top(id) {
//It has been damaged and awaits repair or other solutions.
var dom = document.getElementById("cbi-passwall2-" + id); var dom = document.getElementById("cbi-passwall2-" + id);
if (dom) { if (dom) {
var trs = document.getElementById("cbi-passwall2-nodes").getElementsByClassName("cbi-section-table-row"); var trs = document.getElementById("cbi-passwall2-nodes").getElementsByClassName("cbi-section-table-row");
@@ -217,13 +256,12 @@ table td, .table .td {
function get_address_full(id) { function get_address_full(id) {
var address = (document.getElementById("cbid.passwall2." + id + ".address") || {}).value || ""; var address = (document.getElementById("cbid.passwall2." + id + ".address") || {}).value || "";
var port = (document.getElementById("cbid.passwall2." + id + ".port") || {}).value || ""; var port = (document.getElementById("cbid.passwall2." + id + ".port") || {}).value || "";
//判断是否含有汉字 //Has Chinese characters?
var reg = /[\u4E00-\u9FFF]+/; var reg = /[\u4E00-\u9FFF]+/;
address = !reg.test(address) ? address : ""; address = !reg.test(address) ? address : "";
return { address: address, port: port }; return { address: address, port: port };
} }
//获取当前使用的节点
function get_now_use_node() { function get_now_use_node() {
XHR.get('<%=api.url("get_now_use_node")%>', null, XHR.get('<%=api.url("get_now_use_node")%>', null,
function(x, result) { function(x, result) {
@@ -231,9 +269,9 @@ table td, .table .td {
if (id) { if (id) {
var dom = document.getElementById("cbi-passwall2-" + id); var dom = document.getElementById("cbi-passwall2-" + id);
if (dom) { if (dom) {
dom.title = "当前使用的节点"; dom.title = "<%:Using...%>";
dom.classList.add("_now_use_bg"); dom.classList.add("_now_use_bg");
//var v = "<a style='color: red'>当前节点</a>" + document.getElementById("cbid.passwall2." + id + ".remarks").value; //var v = "<a style='color: red'><%:Using...%></a>" + document.getElementById("cbid.passwall2." + id + ".remarks").value;
//document.getElementById("cbi-passwall2-" + id + "-remarks").innerHTML = v; //document.getElementById("cbi-passwall2-" + id + "-remarks").innerHTML = v;
var dom_remarks = document.getElementById("cbi-passwall2-" + id + "-remarks"); var dom_remarks = document.getElementById("cbi-passwall2-" + id + "-remarks");
if (dom_remarks) { if (dom_remarks) {
@@ -306,7 +344,7 @@ table td, .table .td {
} }
} }
/* 自动Ping */ /* Auto Ping */
function pingAllNodes() { function pingAllNodes() {
if (auto_detection_time == "icmp" || auto_detection_time == "tcping") { if (auto_detection_time == "icmp" || auto_detection_time == "tcping") {
var nodes = []; var nodes = [];
@@ -316,7 +354,7 @@ table td, .table .td {
var full = get_address_full(cbi_id); var full = get_address_full(cbi_id);
if ((auto_detection_time == "icmp" && full.address != "" ) || (auto_detection_time == "tcping" && full.address != "" && full.port != "")) { if ((auto_detection_time == "icmp" && full.address != "" ) || (auto_detection_time == "tcping" && full.address != "" && full.port != "")) {
var flag = false; var flag = false;
//当有多个相同地址和端口时合在一起 // Merge duplicates
for (var j = 0; j < nodes.length; j++) { for (var j = 0; j < nodes.length; j++) {
if (nodes[j].address == full.address && nodes[j].port == full.port) { if (nodes[j].address == full.address && nodes[j].port == full.port) {
nodes[j].indexs = nodes[j].indexs + "," + i; nodes[j].indexs = nodes[j].indexs + "," + i;
@@ -387,66 +425,203 @@ table td, .table .td {
} }
} }
} }
</script>
var edit_btn = document.getElementById("cbi-passwall2-nodes").getElementsByClassName("cbi-button cbi-button-edit");
for (var i = 0; i < edit_btn.length; i++) {
try { <script type="text/template" id="nodes-table-template">
var onclick_str = edit_btn[i].getAttribute("onclick"); <fieldset class="cbi-section cbi-tblsection" id="cbi-passwall2-nodes-{{group}}-fieldset">
var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1); <table class="table cbi-section-table" id="cbi-passwall2-nodes-{{group}}-table" style="">
var td = edit_btn[i].parentNode; <tr class="tr cbi-section-table-titles anonymous">
var new_div = ""; <th class="th cbi-section-table-cell" style="width:40%"><%:Remarks%></th>
//添加"勾选"框 <th class="th cbi-section-table-cell" style="width:8%">Ping</th>
new_div += '<input class="cbi-input-checkbox nodes_select" type="checkbox" cbid="' + id + '" />&nbsp;&nbsp;'; <th class="th cbi-section-table-cell" style="width:8%">TCPing</th>
//添加"置顶"按钮 <th class="th cbi-section-table-cell" style="width:8%"><%:URL Test%></th>
new_div += '<input class="btn cbi-button" type="button" value="<%:To Top%>" onclick="_cbi_row_top(\'' + id + '\')"/>&nbsp;&nbsp;'; <th class="th cbi-section-table-cell cbi-section-actions"></th>
//添加"应用"按钮 </tr>
new_div += '<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Use%>" id="apply_' + id + '" onclick="open_set_node_div(\'' + id + '\')"/>&nbsp;&nbsp;'; {{node-tr}}
//添加"复制"按钮 </table>
new_div += '<input class="btn cbi-button cbi-button-add" type="button" value="<%:Copy%>" onclick="copy_node(\'' + id + '\')"/>&nbsp;&nbsp;'; <div class="cbi-section-create cbi-tblsection-create">
td.innerHTML = new_div + td.innerHTML; <input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="location.href='<%=api.url("add_node")%>?redirect=1'">
</div>
var obj = {}; </fieldset>
obj.id = id; </script>
obj.type = document.getElementById("cbid.passwall2." + id + ".type").value;
var address_dom = document.getElementById("cbid.passwall2." + id + ".address"); <script type="text/template" id="node-tr-template">
var port_dom = document.getElementById("cbid.passwall2." + id + ".port"); <tr class="tr cbi-section-table-row" id="cbi-passwall2-{{id}}">
if (address_dom && port_dom) { <td class="td cbi-value-field">{{remarks}}<input class="hidden" id="cbid.passwall2.{{id}}.remarks" value="{{remarks}}"/></td>
obj.address = address_dom.value; <td class="td cbi-value-field">{{ping}}</td>
obj.port = port_dom.value; <td class="td cbi-value-field">{{tcping}}</td>
<td class="td cbi-value-field">{{url_test}}</td>
<td class="td cbi-section-table-cell nowrap cbi-section-actions">
<div>
<!--It has been damaged and awaits repair or other solutions.-->
<!--<input class="btn cbi-button" type="button" value="<%:To Top%>" onclick="_cbi_row_top('{{id}}')"/>-->
<input class="cbi-input-checkbox nodes_select" type="checkbox" cbid="{{id}}" />
<input class="btn cbi-button cbi-button-apply" type="button" value="<%:Use%>" id="apply_{{id}}" onclick="open_set_node_div('{{id}}')"/>
<input class="btn cbi-button cbi-button-add" type="button" value="<%:Copy%>" onclick="copy_node('{{id}}')"/>
<input class="btn cbi-button cbi-button-edit" type="button" value="<%:Edit%>" onclick="location.href='<%=api.url("node_config")%>/{{id}}'" alt="<%:Edit%>" title="<%:Edit%>">
<input class="btn cbi-button cbi-button-remove" type="button" value="<%:Del%>" onclick="del_node('{{id}}')" alt="<%:Del%>" title="<%:Del%>">
</div>
</td>
</tr>
</script>
<fieldset class="cbi-section" id="node_list">
</fieldset>
<script type="text/javascript">
function get_remarks_name(o) {
let show_node_info = "<%=api.uci_get_type("global_other", "show_node_info", "0")%>"
let str = "";
let remarks = o["remarks"] || "";
let type = o["type"] || "";
str += "<input type='hidden' id='cbid.passwall2." + o[".name"] + ".type' value='" + type + "'/>";
if (type == "sing-box" || type == "Xray") {
let protocol = o["protocol"]
let p = "";
if (protocol == "_balancing") {
p = "<%:Balancing%>";
} else if (protocol == "_urltest") {
p = "URLTest";
} else if (protocol == "_shunt") {
p = "<%:Shunt%>";
} else if (protocol == "vmess") {
p = "VMess";
} else if (protocol == "vless") {
p = "VLESS";
} else if (protocol == "shadowsocks") {
p = "SS";
} else if (protocol == "shadowsocksr") {
p = "SSR";
} else if (protocol == "wireguard") {
p = "WG";
} else if (protocol == "hysteria") {
p = "HY";
} else if (protocol == "hysteria2") {
p = "HY2";
} else if (protocol == "anytls") {
p = "AnyTLS";
} else if (protocol == "ssh") {
p = "SSH";
} else {
p = protocol.charAt(0).toUpperCase() + protocol.slice(1);
} }
if (type == "sing-box") {
node_count++; type = "Sing-Box";
var add_from = document.getElementById("cbid.passwall2." + id + ".add_from").value; }
if (node_list[add_from]) type += " " + p;
node_list[add_from].push(obj);
else
node_list[add_from] = [];
} }
catch(err) { let address = o["address"] || "";
console.error(err); let port = o["port"] || "";
let port_s = "";
if (port != "") {
port_s = port;
} else {
port_s = o["hysteria_hop"] || o["hysteria2_hop"];
} }
str += type + "" + remarks;
if (address != "" && port_s != "") {
port_s = port_s.split(":").join("-");
if (show_node_info == "1") {
//TODO
}
}
str += "<input type='hidden' id='cbid.passwall2." + o[".name"] + ".address' value='" + address + "'/>";
str += "<input type='hidden' id='cbid.passwall2." + o[".name"] + ".port' value='" + port + "'/>";
return str;
} }
get_now_use_node(); XHR.get('<%=api.url("get_node")%>', null,
function(x, result) {
if (true) { var node_list = result
var str = "";
for (var add_from in node_list) { var group_nodes = {}
var num = node_list[add_from].length + 1; for (let i = 0; i < node_list.length; i++) {
if (add_from == "") { let _node = node_list[i]
add_from = "<%:Self add%>"; if (!_node.group) {
_node.group = "default"
}
if (!group_nodes[_node.group]) {
group_nodes[_node.group] = []
}
group_nodes[_node.group].push(_node)
} }
str += add_from + " " + "<%:Node num%>: <a style='color: red'>" + num + "</a>&nbsp&nbsp&nbsp";
var tab_ul_html = '<ul class="cbi-tabmenu">'
var tab_ul_li_html = ''
var tab_content_html = '<fieldset class="cbi-section-node cbi-section-node-tabbed" id="cbi-passwall2-nodes">'
var nodes_table_template = document.getElementById("nodes-table-template");
var node_template = document.getElementById("node-tr-template");
var default_group = null
for (let group in group_nodes) {
if (default_group == null)
default_group = group
var table_html = "";
if (true) {
//Node List
var new_nodes_table_dom = nodes_table_template.cloneNode(true);
var _html = new_nodes_table_dom.innerHTML;
_html = _html.split("{{group}}").join(group);
var node_tr_html = "";
for (var i = 0; i < group_nodes[group].length; i++) {
let o = group_nodes[group][i]
var newDom = node_template.cloneNode(true);
newDom.classList.add("cbi-rowstyle-" + (i % 2 + 1));
var innerHTML = newDom.innerHTML;
if (auto_detection_time != "icmp") {
innerHTML = innerHTML.split("{{ping}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'{{id}}\', this, \'icmp\')"><%:Test%></a></span>');
} else {
innerHTML = innerHTML.split("{{ping}}").join('<span class="ping_value" cbiid="{{id}}">---</span>');
}
if (auto_detection_time != "tcping") {
innerHTML = innerHTML.split("{{tcping}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:ping_node(\'{{id}}\', this, \'tcping\')"><%:Test%></a></span>');
} else {
innerHTML = innerHTML.split("{{tcping}}").join('<span class="tcping_value" cbiid="{{id}}">---</span>');
}
innerHTML = innerHTML.split("{{url_test}}").join('<span class="ping"><a href="javascript:void(0)" onclick="javascript:urltest_node(\'{{id}}\', this)"><%:Test%></a></span>');
innerHTML = innerHTML.split("{{id}}").join(o[".name"]);
innerHTML = innerHTML.split("{{group}}").join(o["group"] || "");
innerHTML = innerHTML.split("{{remarks}}").join(get_remarks_name(o));
node_tr_html += innerHTML
}
_html = _html.split("{{node-tr}}").join(node_tr_html);
table_html = _html;
}
tab_ul_li_html +=
'<li id="tab.passwall2.nodes.' + group + '" class="cbi-tab">' +
'<a onclick="this.blur(); return cbi_t_switch(\'passwall2.nodes\', \'' + group + '\')" href="<%=REQUEST_URI%>?tab.passwall2.nodes=' + group + '">' + group + " | " + "<font style='color: red'>" + group_nodes[group].length + '</font></a>' +
'</li>'
tab_content_html +=
'<div class="cbi-tabcontainer" id="container.passwall2.nodes.' + group + '" style="display: none;">' +
'' + table_html +
'</div>'
}
tab_ul_html += tab_ul_li_html + '</ul>'
tab_content_html += tab_content_html + '</fieldset>'
var tab_html = tab_ul_html + tab_content_html
document.getElementById("node_list").innerHTML = tab_html
for (let group in group_nodes) {
cbi_t_add("passwall2.nodes", group)
}
if (default_group) {
cbi_t_switch("passwall2.nodes", default_group)
}
get_now_use_node();
} }
document.getElementById("div_node_count").innerHTML = "<div style='margin-top:5px'>" + str + "</div>"; );
}
//UI渲染完成后再自动Ping
window.onload = function () { window.onload = function () {
setTimeout(function () { setTimeout(function () {
pingAllNodes(); pingAllNodes();
}, 800); }, 1000);
}; };
//]]> //]]>

View File

@@ -1836,3 +1836,9 @@ msgstr "如留空,则使用随机版本。"
msgid "The configured type also applies to the core specified when manually importing nodes." msgid "The configured type also applies to the core specified when manually importing nodes."
msgstr "配置的类型同样适用于手动导入节点时所指定的核心程序。" msgstr "配置的类型同样适用于手动导入节点时所指定的核心程序。"
msgid "Group Name"
msgstr "分组名"
msgid "Using..."
msgstr "使用中。"

View File

@@ -35,13 +35,13 @@ EOF
chmod +x /usr/share/passwall2/*.sh chmod +x /usr/share/passwall2/*.sh
[ -e "/etc/config/passwall2_show" ] && rm -rf /etc/config/passwall2_show
[ "$(uci -q get passwall2.@global_xray[0].sniffing)" == "1" ] && [ "$(uci -q get passwall2.@global_xray[0].route_only)" != "1" ] && uci -q set passwall2.@global_xray[0].sniffing_override_dest=1 [ "$(uci -q get passwall2.@global_xray[0].sniffing)" == "1" ] && [ "$(uci -q get passwall2.@global_xray[0].route_only)" != "1" ] && uci -q set passwall2.@global_xray[0].sniffing_override_dest=1
uci -q delete passwall2.@global_xray[0].sniffing uci -q delete passwall2.@global_xray[0].sniffing
uci -q delete passwall2.@global_xray[0].route_only uci -q delete passwall2.@global_xray[0].route_only
uci -q commit passwall2 uci -q commit passwall2
sed -i "s#add_from#group#g" /etc/config/passwall2 2>/dev/null
rm -f /tmp/luci-indexcache rm -f /tmp/luci-indexcache
rm -rf /tmp/luci-modulecache/ rm -rf /tmp/luci-modulecache/
killall -HUP rpcd 2>/dev/null killall -HUP rpcd 2>/dev/null

View File

@@ -358,7 +358,7 @@ load_acl() {
ipset -! create $ipset_white6 nethash family inet6 maxelem 1048576 ipset -! create $ipset_white6 nethash family inet6 maxelem 1048576
#分流规则的IP列表(使用分流节点时导入) #分流规则的IP列表(使用分流节点时导入)
gen_shunt_list ${node} shunt_list4 shunt_list6 ${write_ipset_direct} ${ipset_white} ${ipset_white6} gen_shunt_list "${node}" shunt_list4 shunt_list6 ${write_ipset_direct} ${ipset_white} ${ipset_white6}
fi fi
} }
@@ -671,7 +671,7 @@ filter_direct_node_list() {
} }
add_firewall_rule() { add_firewall_rule() {
echolog "开始加载防火墙规则..." echolog "开始加载 iptables 防火墙规则..."
ipset -! create $IPSET_LOCAL nethash maxelem 1048576 ipset -! create $IPSET_LOCAL nethash maxelem 1048576
ipset -! create $IPSET_LAN nethash maxelem 1048576 ipset -! create $IPSET_LAN nethash maxelem 1048576
ipset -! create $IPSET_VPS nethash maxelem 1048576 ipset -! create $IPSET_VPS nethash maxelem 1048576
@@ -735,7 +735,7 @@ add_firewall_rule() {
ipset -! create $ipset_global_white6 nethash family inet6 maxelem 1048576 timeout 259200 ipset -! create $ipset_global_white6 nethash family inet6 maxelem 1048576 timeout 259200
#分流规则的IP列表(使用分流节点时导入) #分流规则的IP列表(使用分流节点时导入)
gen_shunt_list ${NODE} SHUNT_LIST4 SHUNT_LIST6 ${WRITE_IPSET_DIRECT} ${ipset_global_white} ${ipset_global_white6} gen_shunt_list "${NODE}" SHUNT_LIST4 SHUNT_LIST6 ${WRITE_IPSET_DIRECT} ${ipset_global_white} ${ipset_global_white6}
# 过滤所有节点IP # 过滤所有节点IP
filter_vpsip > /dev/null 2>&1 & filter_vpsip > /dev/null 2>&1 &

View File

@@ -382,7 +382,7 @@ load_acl() {
gen_nftset $nftset_white6 ipv6_addr 3d 3d gen_nftset $nftset_white6 ipv6_addr 3d 3d
#分流规则的IP列表(使用分流节点时导入) #分流规则的IP列表(使用分流节点时导入)
gen_shunt_list ${node} shunt_list4 shunt_list6 ${write_ipset_direct} ${nftset_white} ${nftset_white6} gen_shunt_list "${node}" shunt_list4 shunt_list6 ${write_ipset_direct} ${nftset_white} ${nftset_white6}
fi fi
} }
@@ -701,7 +701,7 @@ filter_direct_node_list() {
} }
add_firewall_rule() { add_firewall_rule() {
echolog "开始加载防火墙规则..." echolog "开始加载 nftables 防火墙规则..."
gen_nft_tables gen_nft_tables
gen_nftset $NFTSET_LOCAL ipv4_addr 0 "-1" gen_nftset $NFTSET_LOCAL ipv4_addr 0 "-1"
gen_nftset $NFTSET_LAN ipv4_addr 0 "-1" $(gen_lanlist) gen_nftset $NFTSET_LAN ipv4_addr 0 "-1" $(gen_lanlist)
@@ -749,7 +749,7 @@ add_firewall_rule() {
gen_nftset $nftset_global_white6 ipv6_addr 0 0 gen_nftset $nftset_global_white6 ipv6_addr 0 0
#分流规则的IP列表(使用分流节点时导入) #分流规则的IP列表(使用分流节点时导入)
gen_shunt_list ${NODE} SHUNT_LIST4 SHUNT_LIST6 ${WRITE_IPSET_DIRECT} ${nftset_global_white} ${nftset_global_white6} gen_shunt_list "${NODE}" SHUNT_LIST4 SHUNT_LIST6 ${WRITE_IPSET_DIRECT} ${nftset_global_white} ${nftset_global_white6}
# 过滤所有节点IP # 过滤所有节点IP
filter_vpsip > /dev/null 2>&1 & filter_vpsip > /dev/null 2>&1 &

View File

@@ -444,12 +444,12 @@ local function get_subscribe_info(cfgid, value)
end end
-- 处理数据 -- 处理数据
local function processData(szType, content, add_mode, add_from) local function processData(szType, content, add_mode, group)
--log(content, add_mode, add_from) --log(content, add_mode, group)
local result = { local result = {
timeout = 60, timeout = 60,
add_mode = add_mode, --0为手动配置,1为导入,2为订阅 add_mode = add_mode, --0为手动配置,1为导入,2为订阅
add_from = add_from group = group
} }
--ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0) --ssr://base64(host:port:protocol:method:obfs:base64pass/?obfsparam=base64param&protoparam=base64param&remarks=base64remarks&group=base64group&udpport=0&uot=0)
if szType == 'ssr' then if szType == 'ssr' then
@@ -1544,14 +1544,14 @@ local function curl(url, file, ua, mode)
return tonumber(result) return tonumber(result)
end end
local function truncate_nodes(add_from) local function truncate_nodes(group)
for _, config in pairs(CONFIG) do for _, config in pairs(CONFIG) do
if config.currentNodes and #config.currentNodes > 0 then if config.currentNodes and #config.currentNodes > 0 then
local newNodes = {} local newNodes = {}
local removeNodesSet = {} local removeNodesSet = {}
for k, v in pairs(config.currentNodes) do for k, v in pairs(config.currentNodes) do
if v.currentNode and v.currentNode.add_mode == "2" then if v.currentNode and v.currentNode.add_mode == "2" then
if (not add_from) or (add_from and add_from == v.currentNode.add_from) then if (not group) or (group and group == v.currentNode.group) then
removeNodesSet[v.currentNode[".name"]] = true removeNodesSet[v.currentNode[".name"]] = true
end end
end end
@@ -1566,7 +1566,7 @@ local function truncate_nodes(add_from)
end end
else else
if config.currentNode and config.currentNode.add_mode == "2" then if config.currentNode and config.currentNode.add_mode == "2" then
if (not add_from) or (add_from and add_from == config.currentNode.add_from) then if (not group) or (group and group == config.currentNode.group) then
if config.delete then if config.delete then
config.delete(config) config.delete(config)
elseif config.set then elseif config.set then
@@ -1578,13 +1578,13 @@ local function truncate_nodes(add_from)
end end
uci:foreach(appname, "nodes", function(node) uci:foreach(appname, "nodes", function(node)
if node.add_mode == "2" then if node.add_mode == "2" then
if (not add_from) or (add_from and add_from == node.add_from) then if (not group) or (group and group == node.group) then
uci:delete(appname, node['.name']) uci:delete(appname, node['.name'])
end end
end end
end) end)
uci:foreach(appname, "subscribe_list", function(o) uci:foreach(appname, "subscribe_list", function(o)
if (not add_from) or add_from == o.remark then if (not group) or group == o.remark then
uci:delete(appname, o['.name'], "md5") uci:delete(appname, o['.name'], "md5")
end end
end) end)
@@ -1725,7 +1725,7 @@ local function update_node(manual)
if manual == 0 and next(group) then if manual == 0 and next(group) then
uci:foreach(appname, "nodes", function(node) uci:foreach(appname, "nodes", function(node)
-- 如果未发现新节点或手动导入的节点就不要删除了... -- 如果未发现新节点或手动导入的节点就不要删除了...
if node.add_mode == "2" and (node.add_from and group[node.add_from] == true) then if node.add_mode == "2" and (node.group and group[node.group] == true) then
uci:delete(appname, node['.name']) uci:delete(appname, node['.name'])
end end
end) end)
@@ -1802,7 +1802,7 @@ local function update_node(manual)
luci.sys.call("/etc/init.d/" .. appname .. " restart > /dev/null 2>&1 &") luci.sys.call("/etc/init.d/" .. appname .. " restart > /dev/null 2>&1 &")
end end
local function parse_link(raw, add_mode, add_from, cfgid) local function parse_link(raw, add_mode, group, cfgid)
if raw and #raw > 0 then if raw and #raw > 0 then
local nodes, szType local nodes, szType
local node_list = {} local node_list = {}
@@ -1838,17 +1838,17 @@ local function parse_link(raw, add_mode, add_from, cfgid)
xpcall(function () xpcall(function ()
local result local result
if szType == 'ssd' then if szType == 'ssd' then
result = processData(szType, v, add_mode, add_from) result = processData(szType, v, add_mode, group)
elseif not szType then elseif not szType then
local node = api.trim(v) local node = api.trim(v)
local dat = split(node, "://") local dat = split(node, "://")
if dat and dat[1] and dat[2] then if dat and dat[1] and dat[2] then
if dat[1] == 'vmess' or dat[1] == 'ssr' then if dat[1] == 'vmess' or dat[1] == 'ssr' then
local link = api.trim(dat[2]:gsub("#.*$", "")) local link = api.trim(dat[2]:gsub("#.*$", ""))
result = processData(dat[1], base64Decode(link), add_mode, add_from) result = processData(dat[1], base64Decode(link), add_mode, group)
else else
local link = dat[2]:gsub("&amp;", "&"):gsub("%s*#%s*", "#") -- 一些奇葩的链接用"&amp;"当做"&""#"前后带空格 local link = dat[2]:gsub("&amp;", "&"):gsub("%s*#%s*", "#") -- 一些奇葩的链接用"&amp;"当做"&""#"前后带空格
result = processData(dat[1], link, add_mode, add_from) result = processData(dat[1], link, add_mode, group)
end end
end end
else else
@@ -1879,14 +1879,14 @@ local function parse_link(raw, add_mode, add_from, cfgid)
end end
if #node_list > 0 then if #node_list > 0 then
nodeResult[#nodeResult + 1] = { nodeResult[#nodeResult + 1] = {
remark = add_from, remark = group,
list = node_list list = node_list
} }
end end
log('成功解析【' .. add_from .. '】节点数量: ' .. #node_list) log('成功解析【' .. group .. '】节点数量: ' .. #node_list)
else else
if add_mode == "2" then if add_mode == "2" then
log('获取到的【' .. add_from .. '】订阅内容为空,可能是订阅地址无效,或是网络问题,请诊断!') log('获取到的【' .. group .. '】订阅内容为空,可能是订阅地址无效,或是网络问题,请诊断!')
end end
end end
end end