🍉 Sync 2025-11-07 00:12:45

This commit is contained in:
actions-user
2025-11-07 00:12:45 +08:00
parent dbd5a80522
commit 3abe250372
14 changed files with 575 additions and 454 deletions

View File

@@ -77,10 +77,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
@@ -397,6 +399,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 protocol = http.formvalue("protocol") local protocol = http.formvalue("protocol")
local section = http.formvalue("section") local section = http.formvalue("section")
@@ -420,7 +437,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))
@@ -458,6 +475,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]", "tcp_node") or "") == w then if (uci:get(appname, "@global[0]", "tcp_node") or "") == w then
uci:delete(appname, '@global[0]', "tcp_node") uci:delete(appname, '@global[0]', "tcp_node")
@@ -532,10 +550,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)
@@ -543,7 +561,40 @@ 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 = {}
local show_node_info = api.uci_get_type("@global_other[0]", "show_node_info", "0")
function add_is_ipv6_key(o)
if o and o.address and show_node_info == "1" then
local f = api.get_ipv6_full(o.address)
if f ~= "" then
o.ipv6 = true
o.full_address = f
end
end
end
if id then
result = uci:get_all(appname, id)
add_is_ipv6_key(result)
else
uci:foreach(appname, "nodes", function(t)
add_is_ipv6_key(t)
result[#result + 1] = t
end)
end
http_write_json(result)
end end
function update_rules() function update_rules()

View File

@@ -21,6 +21,21 @@ 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"))
o.default = ""
o:value("", translate("default"))
local groups = {}
m.uci:foreach(appname, "nodes", function(s)
if s[".name"] ~= arg[1] then
if s.group and s.group ~= "" then
groups[s.group] = true
end
end
end)
for k, v in pairs(groups) do
o:value(k)
end
o = s:option(ListValue, "type", translate("Type")) o = s:option(ListValue, "type", translate("Type"))
if api.is_finded("ipt2socks") then if api.is_finded("ipt2socks") then

View File

@@ -20,221 +20,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, "haproxy_config", function(s)
if s["lbss"] and s["lbss"] == t then
m:del(s[".name"])
end
end)
m.uci:foreach(appname, "acl_rule", function(s)
if s["tcp_node"] and s["tcp_node"] == t then
m:set(s[".name"], "tcp_node", "default")
end
if s["udp_node"] and s["udp_node"] == t then
m:set(s[".name"], "udp_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]", "tcp_node") or "") == t then
m:set('@global[0]', "tcp_node", new_node)
end
if (m:get("@global[0]", "udp_node") or "") == t then
m:set('@global[0]', "udp_node", new_node)
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

@@ -190,7 +190,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.passwall.api"
} }
} }
function add_new_node() {
window.location.href = '<%=api.url("add_node")%>?redirect=1';
}
//]]> //]]>
</script> </script>
@@ -99,15 +103,14 @@ local api = require "luci.passwall.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.passwall.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%>" />
<input class="btn cbi-button" type="button" onclick="checked_all_node(this)" value="<%:Select all%>" /> <input class="btn cbi-button" type="button" id="select_all_btn" 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-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

@@ -44,12 +44,54 @@ 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 btn = document.getElementById("select_all_btn");
if (btn) {
dechecked_all_node(btn);
}
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[0]", "auto_detection_time", "0")%>" let auto_detection_time = "<%=api.uci_get_type("@global_other[0]", "auto_detection_time", "0")%>"
let show_node_info = "<%=api.uci_get_type("@global_other[0]", "show_node_info", "0")%>"
var node_list = {}; var node_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) {
@@ -106,6 +148,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;
@@ -120,6 +166,7 @@ table td, .table .td {
} }
function _cbi_row_top(id) { function _cbi_row_top(id) {
//此函数已经损坏,等待修复或其他解决方案。
var dom = document.getElementById("cbi-passwall-" + id); var dom = document.getElementById("cbi-passwall-" + id);
if (dom) { if (dom) {
var trs = document.getElementById("cbi-passwall-nodes").getElementsByClassName("cbi-section-table-row"); var trs = document.getElementById("cbi-passwall-nodes").getElementsByClassName("cbi-section-table-row");
@@ -135,7 +182,9 @@ table td, .table .td {
} }
function checked_all_node(btn) { function checked_all_node(btn) {
var doms = document.getElementById("cbi-passwall-nodes").getElementsByClassName("nodes_select"); var visibleContainer = document.querySelector('#cbi-passwall-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall-nodes > .cbi-tabcontainer[style*="display: block"]');
if (!visibleContainer) return;
var doms = visibleContainer.getElementsByClassName("nodes_select");
if (doms && doms.length > 0) { if (doms && doms.length > 0) {
for (var i = 0 ; i < doms.length; i++) { for (var i = 0 ; i < doms.length; i++) {
doms[i].checked = true; doms[i].checked = true;
@@ -146,7 +195,9 @@ table td, .table .td {
} }
function dechecked_all_node(btn) { function dechecked_all_node(btn) {
var doms = document.getElementById("cbi-passwall-nodes").getElementsByClassName("nodes_select"); var visibleContainer = document.querySelector('#cbi-passwall-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall-nodes > .cbi-tabcontainer[style*="display: block"]');
if (!visibleContainer) return;
var doms = visibleContainer.getElementsByClassName("nodes_select");
if (doms && doms.length > 0) { if (doms && doms.length > 0) {
for (var i = 0 ; i < doms.length; i++) { for (var i = 0 ; i < doms.length; i++) {
doms[i].checked = false; doms[i].checked = false;
@@ -158,7 +209,9 @@ table td, .table .td {
function delete_select_nodes() { function delete_select_nodes() {
var ids = []; var ids = [];
var doms = document.getElementById("cbi-passwall-nodes").getElementsByClassName("nodes_select"); var visibleContainer = document.querySelector('#cbi-passwall-nodes > .cbi-tabcontainer[style*="display:block"], #cbi-passwall-nodes > .cbi-tabcontainer[style*="display: block"]');
if (!visibleContainer) return;
var doms = visibleContainer.getElementsByClassName("nodes_select");
if (doms && doms.length > 0) { if (doms && doms.length > 0) {
for (var i = 0 ; i < doms.length; i++) { for (var i = 0 ; i < doms.length; i++) {
if (doms[i].checked) { if (doms[i].checked) {
@@ -215,7 +268,7 @@ table td, .table .td {
if (id) { if (id) {
var dom = document.getElementById("cbi-passwall-" + id); var dom = document.getElementById("cbi-passwall-" + id);
if (dom) { if (dom) {
dom.title = "当前使用的 TCP 节点"; dom.title = '<%=api.i18n.translatef("Currently using %s node", "TCP")%>';
dom.classList.add("_now_use_bg"); dom.classList.add("_now_use_bg");
//var v = "<a style='color: red'>当前TCP节点</a>" + document.getElementById("cbid.passwall." + id + ".remarks").value; //var v = "<a style='color: red'>当前TCP节点</a>" + document.getElementById("cbid.passwall." + id + ".remarks").value;
//document.getElementById("cbi-passwall-" + id + "-remarks").innerHTML = v; //document.getElementById("cbi-passwall-" + id + "-remarks").innerHTML = v;
@@ -230,9 +283,9 @@ table td, .table .td {
var dom = document.getElementById("cbi-passwall-" + id); var dom = document.getElementById("cbi-passwall-" + id);
if (dom) { if (dom) {
if (result["TCP"] == result["UDP"]) { if (result["TCP"] == result["UDP"]) {
dom.title = "当前使用的 TCP/UDP 节点"; dom.title = '<%=api.i18n.translatef("Currently using %s node", "TCP/UDP")%>';
} else { } else {
dom.title = "当前使用的 UDP 节点"; dom.title = '<%=api.i18n.translatef("Currently using %s node", "UDP")%>';
} }
dom.classList.add("_now_use_bg"); dom.classList.add("_now_use_bg");
var dom_remarks = document.getElementById("cbi-passwall-" + id + "-remarks"); var dom_remarks = document.getElementById("cbi-passwall-" + id + "-remarks");
@@ -309,6 +362,7 @@ table td, .table .td {
/* 自动Ping */ /* 自动Ping */
function pingAllNodes() { function pingAllNodes() {
if (auto_detection_time == "icmp" || auto_detection_time == "tcping") { if (auto_detection_time == "icmp" || auto_detection_time == "tcping") {
const now = Date.now();
var nodes = []; var nodes = [];
const ping_value = document.getElementsByClassName(auto_detection_time == "tcping" ? 'tcping_value' : 'ping_value'); const ping_value = document.getElementsByClassName(auto_detection_time == "tcping" ? 'tcping_value' : 'ping_value');
for (var i = 0; i < ping_value.length; i++) { for (var i = 0; i < ping_value.length; i++) {
@@ -316,7 +370,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;
@@ -326,11 +380,23 @@ table td, .table .td {
} }
if (flag) if (flag)
continue; continue;
nodes.push({
indexs: i + "", const cacheData = JSON.parse(localStorage.getItem(auto_detection_time + ":" + full.address + ":" + full.port));
address: full.address, if (cacheData && cacheData.savetime && (now - cacheData.timestamp) < cacheData.savetime) {
port: full.port if (cacheData.value < 100)
}); ping_value[i].innerHTML = "<font style='color:green'>" + cacheData.value + " ms" + "</font>";
else if (cacheData.value < 200)
ping_value[i].innerHTML = "<font style='color:#fb9a05'>" + cacheData.value + " ms" + "</font>";
else if (cacheData.value >= 200)
ping_value[i].innerHTML = "<font style='color:red'>" + cacheData.value + " ms" + "</font>";
} else {
localStorage.removeItem(auto_detection_time + ":" + full.address + ":" + full.port);
nodes.push({
indexs: i + "",
address: full.address,
port: full.port
});
}
} }
} }
@@ -339,39 +405,47 @@ table td, .table .td {
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(",");
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>";
}
}
}
res();
},
5000,
function(x) {
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++) {
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>"; if (result.ping == null || result.ping.trim() == "") {
ping_value[strs[i]].innerHTML = "<font style='color:red'><%:Timeout%></font>";
} else {
var ping = parseInt(result.ping);
//save to cache
const cache_data = {
dom_id: strs[i],
timestamp: Date.now(),
savetime: 60 * 1000,
value: ping
};
localStorage.setItem(auto_detection_time + ":" + dom.address + ":" + dom.port, JSON.stringify(cache_data));
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>";
}
} }
res();
} }
); 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();
}
);
}) })
} }
@@ -387,66 +461,212 @@ table td, .table .td {
} }
} }
} }
</script>
var edit_btn = document.getElementById("cbi-passwall-nodes").getElementsByClassName("cbi-button cbi-button-edit"); <script type="text/template" id="nodes-table-template">
for (var i = 0; i < edit_btn.length; i++) { <fieldset class="cbi-section cbi-tblsection" id="cbi-passwall-nodes-{{group}}-fieldset">
try { <table class="table cbi-section-table" id="cbi-passwall-nodes-{{group}}-table" style="">
var onclick_str = edit_btn[i].getAttribute("onclick"); <tr class="tr cbi-section-table-titles anonymous">
var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1); <th class="th cbi-section-table-cell" style="width:40%"><%:Remarks%></th>
var td = edit_btn[i].parentNode; <th class="th cbi-section-table-cell" style="width:8%">Ping</th>
var new_div = ""; <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="cbi-input-checkbox nodes_select" type="checkbox" cbid="' + id + '" />&nbsp;&nbsp;'; <th class="th cbi-section-table-cell cbi-section-actions"></th>
//添加"置顶"按钮 </tr>
new_div += '<input class="btn cbi-button" type="button" value="<%:To Top%>" onclick="_cbi_row_top(\'' + id + '\')"/>&nbsp;&nbsp;'; {{node-tr}}
//添加"应用"按钮 </table>
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;'; <div class="cbi-section-create cbi-tblsection-create">
//添加"复制"按钮 <input class="cbi-button cbi-button-add" type="button" value="<%:Add%>" onclick="location.href='<%=api.url("add_node")%>?redirect=1'">
new_div += '<input class="btn cbi-button cbi-button-add" type="button" value="<%:Copy%>" onclick="copy_node(\'' + id + '\')"/>&nbsp;&nbsp;'; </div>
td.innerHTML = new_div + td.innerHTML; </fieldset>
</script>
var obj = {}; <script type="text/template" id="node-tr-template">
obj.id = id; <tr class="tr cbi-section-table-row" id="cbi-passwall-{{id}}">
obj.type = document.getElementById("cbid.passwall." + id + ".type").value; <input class="hidden" id="cbid.passwall.{{id}}.remarks" value="{{remarks_val}}"/>
var address_dom = document.getElementById("cbid.passwall." + id + ".address"); <input class="hidden" id="cbid.passwall.{{id}}.address" value="{{address_val}}"/>
var port_dom = document.getElementById("cbid.passwall." + id + ".port"); <input class="hidden" id="cbid.passwall.{{id}}.port" value="{{port_val}}"/>
if (address_dom && port_dom) { <td class="td cbi-value-field">{{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="<%:Delete%>" onclick="del_node('{{id}}')" alt="<%:Delete%>" title="<%:Delete%>">
</div>
</td>
</tr>
</script>
<fieldset class="cbi-section" id="node_list">
</fieldset>
<script type="text/javascript">
function get_remarks_name(o) {
let str = "";
let remarks = o["remarks"] || "";
let type = o["type"] || "";
str += "<input type='hidden' id='cbid.passwall." + 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.passwall." + 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;
return str;
} }
get_now_use_node(); XHR.get('<%=api.url("get_node")%>', null,
function(x, result) {
var node_list = result
if (true) { var group_nodes = {}
var str = ""; for (let i = 0; i < node_list.length; i++) {
for (var add_from in node_list) { let _node = node_list[i]
var num = node_list[add_from].length + 1; if (!_node.group || _node.group === "") {
if (add_from == "") { _node.group = "<%:default%>"
add_from = "<%:Self add%>"; }
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";
}
document.getElementById("div_node_count").innerHTML = "<div style='margin-top:5px'>" + str + "</div>";
}
//UI渲染完成后再自动Ping 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-passwall-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" && o["address"] && o["port"]) {
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" && o["address"] && o["port"]) {
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"] || "");
let node_remarks = get_remarks_name(o);
if (show_node_info == "1") {
if (o["address"] && o["port"]) {
let _address = o["address"]
if (o["full_address"])
_address = o["full_address"]
node_remarks += "<br>" + _address + ":" + o["port"]
}
}
innerHTML = innerHTML.split("{{remarks}}").join(node_remarks);
innerHTML = innerHTML.split("{{remarks_val}}").join(o["remarks"]);
innerHTML = innerHTML.split("{{address_val}}").join(o["address"] || "");
innerHTML = innerHTML.split("{{port_val}}").join(o["port"] || "");
node_tr_html += innerHTML
}
_html = _html.split("{{node-tr}}").join(node_tr_html);
table_html = _html;
}
tab_ul_li_html +=
'<li id="tab.passwall.nodes.' + group + '" class="cbi-tab">' +
'<a onclick="this.blur(); return cbi_t_switch(\'passwall.nodes\', \'' + group + '\')" href="<%=REQUEST_URI%>?tab.passwall.nodes=' + group + '">' + group + " | " + "<font style='color: red'>" + group_nodes[group].length + '</font></a>' +
'</li>'
tab_content_html +=
'<div class="cbi-tabcontainer" id="container.passwall.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("passwall.nodes", group)
}
if (default_group) {
cbi_t_switch("passwall.nodes", default_group)
}
get_now_use_node();
pingAllNodes();
}
);
window.onload = function () { window.onload = function () {
setTimeout(function () { setTimeout(function () {
pingAllNodes(); pingAllNodes();
}, 800); }, 1000);
}; };
//]]> //]]>

View File

@@ -1974,3 +1974,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 "Currently using %s node"
msgstr "当前使用的 %s 节点"

View File

@@ -35,14 +35,13 @@ EOF
chmod +x /usr/share/passwall/*.sh chmod +x /usr/share/passwall/*.sh
## 4.77-5 below upgrade to 4.77-6 above
[ -e "/etc/config/passwall_show" ] && rm -rf /etc/config/passwall_show
[ "$(uci -q get passwall.@global_xray[0].sniffing)" == "1" ] && [ "$(uci -q get passwall.@global_xray[0].route_only)" != "1" ] && uci -q set passwall.@global_xray[0].sniffing_override_dest=1 [ "$(uci -q get passwall.@global_xray[0].sniffing)" == "1" ] && [ "$(uci -q get passwall.@global_xray[0].route_only)" != "1" ] && uci -q set passwall.@global_xray[0].sniffing_override_dest=1
uci -q delete passwall.@global_xray[0].sniffing uci -q delete passwall.@global_xray[0].sniffing
uci -q delete passwall.@global_xray[0].route_only uci -q delete passwall.@global_xray[0].route_only
uci -q commit passwall uci -q commit passwall
sed -i "s#add_from#group#g" /etc/config/passwall 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

@@ -2114,7 +2114,7 @@ stop() {
# 结束 SS 插件进程 # 结束 SS 插件进程
# kill_all xray-plugin v2ray-plugin obfs-local shadow-tls # kill_all xray-plugin v2ray-plugin obfs-local shadow-tls
local pid_file pid local pid_file pid
find "$TMP_PATH" -type f -name '*_plugin.pid' | while read -r pid_file; do find "$TMP_PATH" -type f -name '*_plugin.pid' 2>/dev/null | while read -r pid_file; do
read -r pid < "$pid_file" read -r pid < "$pid_file"
if [ -n "$pid" ]; then if [ -n "$pid" ]; then
kill -9 "$pid" >/dev/null 2>&1 kill -9 "$pid" >/dev/null 2>&1

View File

@@ -781,7 +781,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
@@ -980,20 +980,17 @@ add_firewall_rule() {
$ipt_n -I PREROUTING 1 -j PSW_DNS $ipt_n -I PREROUTING 1 -j PSW_DNS
fi fi
$ipt_m -N PSW_DIVERT
$ipt_m -A PSW_DIVERT -j MARK --set-mark 1
$ipt_m -A PSW_DIVERT -j ACCEPT
$ipt_m -N PSW_RULE $ipt_m -N PSW_RULE
$ipt_m -A PSW_RULE -j CONNMARK --restore-mark $ipt_m -A PSW_RULE -j CONNMARK --restore-mark
$ipt_m -A PSW_RULE -m mark --mark 1 -j RETURN $ipt_m -A PSW_RULE -m mark --mark 1 -j RETURN
$ipt_m -A PSW_RULE -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j MARK --set-xmark 1 $ipt_m -A PSW_RULE -p tcp -m tcp --syn -j MARK --set-xmark 1
$ipt_m -A PSW_RULE -p udp -m conntrack --ctstate NEW -j MARK --set-xmark 1 $ipt_m -A PSW_RULE -p udp -m conntrack --ctstate NEW,RELATED -j MARK --set-xmark 1
$ipt_m -A PSW_RULE -j CONNMARK --save-mark $ipt_m -A PSW_RULE -j CONNMARK --save-mark
$ipt_m -N PSW $ipt_m -N PSW
$ipt_m -A PSW $(dst $IPSET_LAN) -j RETURN $ipt_m -A PSW $(dst $IPSET_LAN) -j RETURN
$ipt_m -A PSW $(dst $IPSET_VPS) -j RETURN $ipt_m -A PSW $(dst $IPSET_VPS) -j RETURN
$ipt_m -A PSW -m conntrack --ctdir REPLY -j RETURN
[ ! -z "${WAN_IP}" ] && { [ ! -z "${WAN_IP}" ] && {
$ipt_m -A PSW $(comment "WAN_IP_RETURN") -d "${WAN_IP}" -j RETURN $ipt_m -A PSW $(comment "WAN_IP_RETURN") -d "${WAN_IP}" -j RETURN
@@ -1002,11 +999,11 @@ add_firewall_rule() {
unset WAN_IP unset WAN_IP
insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW" insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW"
insert_rule_before "$ipt_m" "PREROUTING" "PSW" "-p tcp -m socket -j PSW_DIVERT"
$ipt_m -N PSW_OUTPUT $ipt_m -N PSW_OUTPUT
$ipt_m -A PSW_OUTPUT $(dst $IPSET_LAN) -j RETURN $ipt_m -A PSW_OUTPUT $(dst $IPSET_LAN) -j RETURN
$ipt_m -A PSW_OUTPUT $(dst $IPSET_VPS) -j RETURN $ipt_m -A PSW_OUTPUT $(dst $IPSET_VPS) -j RETURN
$ipt_m -A PSW_OUTPUT -m conntrack --ctdir REPLY -j RETURN
[ -n "$IPT_APPEND_DNS" ] && { [ -n "$IPT_APPEND_DNS" ] && {
local local_dns dns_address dns_port local local_dns dns_address dns_port
@@ -1053,20 +1050,17 @@ add_firewall_rule() {
$ip6t_n -I PREROUTING 1 -j PSW_DNS $ip6t_n -I PREROUTING 1 -j PSW_DNS
fi fi
$ip6t_m -N PSW_DIVERT
$ip6t_m -A PSW_DIVERT -j MARK --set-mark 1
$ip6t_m -A PSW_DIVERT -j ACCEPT
$ip6t_m -N PSW_RULE $ip6t_m -N PSW_RULE
$ip6t_m -A PSW_RULE -j CONNMARK --restore-mark $ip6t_m -A PSW_RULE -j CONNMARK --restore-mark
$ip6t_m -A PSW_RULE -m mark --mark 1 -j RETURN $ip6t_m -A PSW_RULE -m mark --mark 1 -j RETURN
$ip6t_m -A PSW_RULE -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j MARK --set-xmark 1 $ip6t_m -A PSW_RULE -p tcp -m tcp --syn -j MARK --set-xmark 1
$ip6t_m -A PSW_RULE -p udp -m conntrack --ctstate NEW -j MARK --set-xmark 1 $ip6t_m -A PSW_RULE -p udp -m conntrack --ctstate NEW,RELATED -j MARK --set-xmark 1
$ip6t_m -A PSW_RULE -j CONNMARK --save-mark $ip6t_m -A PSW_RULE -j CONNMARK --save-mark
$ip6t_m -N PSW $ip6t_m -N PSW
$ip6t_m -A PSW $(dst $IPSET_LAN6) -j RETURN $ip6t_m -A PSW $(dst $IPSET_LAN6) -j RETURN
$ip6t_m -A PSW $(dst $IPSET_VPS6) -j RETURN $ip6t_m -A PSW $(dst $IPSET_VPS6) -j RETURN
$ip6t_m -A PSW -m conntrack --ctdir REPLY -j RETURN
WAN6_IP=$(get_wan6_ip) WAN6_IP=$(get_wan6_ip)
[ ! -z "${WAN6_IP}" ] && $ip6t_m -A PSW $(comment "WAN6_IP_RETURN") -d ${WAN6_IP} -j RETURN [ ! -z "${WAN6_IP}" ] && $ip6t_m -A PSW $(comment "WAN6_IP_RETURN") -d ${WAN6_IP} -j RETURN
@@ -1079,6 +1073,7 @@ add_firewall_rule() {
$ip6t_m -A PSW_OUTPUT -m mark --mark 0xff -j RETURN $ip6t_m -A PSW_OUTPUT -m mark --mark 0xff -j RETURN
$ip6t_m -A PSW_OUTPUT $(dst $IPSET_LAN6) -j RETURN $ip6t_m -A PSW_OUTPUT $(dst $IPSET_LAN6) -j RETURN
$ip6t_m -A PSW_OUTPUT $(dst $IPSET_VPS6) -j RETURN $ip6t_m -A PSW_OUTPUT $(dst $IPSET_VPS6) -j RETURN
$ip6t_m -A PSW_OUTPUT -m conntrack --ctdir REPLY -j RETURN
[ "${USE_BLOCK_LIST}" = "1" ] && $ip6t_m -A PSW_OUTPUT $(dst $IPSET_BLOCK6) -j DROP [ "${USE_BLOCK_LIST}" = "1" ] && $ip6t_m -A PSW_OUTPUT $(dst $IPSET_BLOCK6) -j DROP
[ "${USE_DIRECT_LIST}" = "1" ] && $ip6t_m -A PSW_OUTPUT $(dst $IPSET_WHITE6) -j RETURN [ "${USE_DIRECT_LIST}" = "1" ] && $ip6t_m -A PSW_OUTPUT $(dst $IPSET_WHITE6) -j RETURN
@@ -1315,7 +1310,7 @@ del_firewall_rule() {
$ipt -D $chain $index 2>/dev/null $ipt -D $chain $index 2>/dev/null
done done
done done
for chain in "PSW" "PSW_OUTPUT" "PSW_DIVERT" "PSW_DNS" "PSW_RULE"; do for chain in "PSW" "PSW_OUTPUT" "PSW_DNS" "PSW_RULE"; do
$ipt -F $chain 2>/dev/null $ipt -F $chain 2>/dev/null
$ipt -X $chain 2>/dev/null $ipt -X $chain 2>/dev/null
done done
@@ -1369,7 +1364,7 @@ gen_include() {
[ -z "${_ipt}" ] && return [ -z "${_ipt}" ] && return
echo "*$2" echo "*$2"
${_ipt}-save -t $2 | grep "PSW" | grep -v "\-j PSW$" | grep -v "mangle\-OUTPUT\-PSW" | grep -v "socket \-j PSW_DIVERT$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/" ${_ipt}-save -t $2 | grep "PSW" | grep -v "\-j PSW$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/"
echo 'COMMIT' echo 'COMMIT'
} }
local __ipt="" local __ipt=""
@@ -1390,7 +1385,6 @@ gen_include() {
[ -z "${is_tproxy}" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p tcp -j PSW") [ -z "${is_tproxy}" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p tcp -j PSW")
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW") \$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW")
\$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "PSW" "-p tcp -m socket -j PSW_DIVERT")
WAN_IP=\$(${MY_PATH} get_wan_ip) WAN_IP=\$(${MY_PATH} get_wan_ip)
@@ -1423,7 +1417,6 @@ gen_include() {
[ "$accept_icmpv6" = "1" ] && $ip6t_n -A PREROUTING -p ipv6-icmp -j PSW [ "$accept_icmpv6" = "1" ] && $ip6t_n -A PREROUTING -p ipv6-icmp -j PSW
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW") \$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW")
\$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "PSW" "-p tcp -m socket -j PSW_DIVERT")
PR_INDEX=\$(${MY_PATH} RULE_LAST_INDEX "$ip6t_m" PSW WAN6_IP_RETURN -1) PR_INDEX=\$(${MY_PATH} RULE_LAST_INDEX "$ip6t_m" PSW WAN6_IP_RETURN -1)
if [ \$PR_INDEX -ge 0 ]; then if [ \$PR_INDEX -ge 0 ]; then

View File

@@ -815,7 +815,7 @@ filter_direct_node_list() {
} }
add_firewall_rule() { add_firewall_rule() {
echolog "开始加载防火墙规则..." echolog "开始加载 nftables 防火墙规则..."
gen_nft_tables gen_nft_tables
gen_nftset $NFTSET_VPS ipv4_addr 0 0 gen_nftset $NFTSET_VPS ipv4_addr 0 0
gen_nftset $NFTSET_GFW ipv4_addr "2d" 0 gen_nftset $NFTSET_GFW ipv4_addr "2d" 0
@@ -986,10 +986,6 @@ add_firewall_rule() {
nft_output_chain="PSW_OUTPUT_MANGLE" nft_output_chain="PSW_OUTPUT_MANGLE"
fi fi
nft "add chain $NFTABLE_NAME PSW_DIVERT"
nft "flush chain $NFTABLE_NAME PSW_DIVERT"
nft "add rule $NFTABLE_NAME PSW_DIVERT meta l4proto tcp socket transparent 1 mark set 1 counter accept"
nft "add chain $NFTABLE_NAME PSW_DNS" nft "add chain $NFTABLE_NAME PSW_DNS"
nft "flush chain $NFTABLE_NAME PSW_DNS" nft "flush chain $NFTABLE_NAME PSW_DNS"
if [ $(config_t_get global dns_redirect "1") = "0" ]; then if [ $(config_t_get global dns_redirect "1") = "0" ]; then
@@ -1005,8 +1001,8 @@ add_firewall_rule() {
nft "flush chain $NFTABLE_NAME PSW_RULE" nft "flush chain $NFTABLE_NAME PSW_RULE"
nft "add rule $NFTABLE_NAME PSW_RULE meta mark set ct mark counter" nft "add rule $NFTABLE_NAME PSW_RULE meta mark set ct mark counter"
nft "add rule $NFTABLE_NAME PSW_RULE meta mark 1 counter return" nft "add rule $NFTABLE_NAME PSW_RULE meta mark 1 counter return"
nft "add rule $NFTABLE_NAME PSW_RULE tcp flags &(fin|syn|rst|ack) == syn meta mark set mark and 0x0 xor 0x1 counter" nft "add rule $NFTABLE_NAME PSW_RULE tcp flags syn meta mark set mark and 0x0 xor 0x1 counter"
nft "add rule $NFTABLE_NAME PSW_RULE meta l4proto udp ct state new meta mark set mark and 0x0 xor 0x1 counter" nft "add rule $NFTABLE_NAME PSW_RULE meta l4proto udp ct state new,related meta mark set mark and 0x0 xor 0x1 counter"
nft "add rule $NFTABLE_NAME PSW_RULE ct mark set mark counter" nft "add rule $NFTABLE_NAME PSW_RULE ct mark set mark counter"
#ipv4 tproxy mode and udp #ipv4 tproxy mode and udp
@@ -1014,11 +1010,13 @@ add_firewall_rule() {
nft "flush chain $NFTABLE_NAME PSW_MANGLE" nft "flush chain $NFTABLE_NAME PSW_MANGLE"
nft "add rule $NFTABLE_NAME PSW_MANGLE ip daddr @$NFTSET_LAN counter return" nft "add rule $NFTABLE_NAME PSW_MANGLE ip daddr @$NFTSET_LAN counter return"
nft "add rule $NFTABLE_NAME PSW_MANGLE ip daddr @$NFTSET_VPS counter return" nft "add rule $NFTABLE_NAME PSW_MANGLE ip daddr @$NFTSET_VPS counter return"
nft "add rule $NFTABLE_NAME PSW_MANGLE ct direction reply counter return"
nft "add chain $NFTABLE_NAME PSW_OUTPUT_MANGLE" nft "add chain $NFTABLE_NAME PSW_OUTPUT_MANGLE"
nft "flush chain $NFTABLE_NAME PSW_OUTPUT_MANGLE" nft "flush chain $NFTABLE_NAME PSW_OUTPUT_MANGLE"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_LAN counter return" nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_LAN counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_VPS counter return" nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_VPS counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ct direction reply counter return"
[ "${USE_BLOCK_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_BLOCK counter drop" [ "${USE_BLOCK_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_BLOCK counter drop"
[ "${USE_DIRECT_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_WHITE counter return" [ "${USE_DIRECT_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE ip daddr @$NFTSET_WHITE counter return"
@@ -1027,7 +1025,6 @@ add_firewall_rule() {
# jump chains # jump chains
nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol udp counter jump PSW_MANGLE" nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol udp counter jump PSW_MANGLE"
[ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol tcp counter jump PSW_MANGLE" [ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol tcp counter jump PSW_MANGLE"
insert_rule_before "$NFTABLE_NAME" "mangle_prerouting" "PSW_MANGLE" "counter jump PSW_DIVERT"
#ipv4 tcp redirect mode #ipv4 tcp redirect mode
[ -z "${is_tproxy}" ] && { [ -z "${is_tproxy}" ] && {
@@ -1078,11 +1075,13 @@ add_firewall_rule() {
nft "flush chain $NFTABLE_NAME PSW_MANGLE_V6" nft "flush chain $NFTABLE_NAME PSW_MANGLE_V6"
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ip6 daddr @$NFTSET_LAN6 counter return" nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ip6 daddr @$NFTSET_LAN6 counter return"
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return" nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return"
nft "add rule $NFTABLE_NAME PSW_MANGLE_V6 ct direction reply counter return"
nft "add chain $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6" nft "add chain $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6"
nft "flush chain $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6" nft "flush chain $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_LAN6 counter return" nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_LAN6 counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return" nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ct direction reply counter return"
[ "${USE_BLOCK_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_BLOCK6 counter drop" [ "${USE_BLOCK_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_BLOCK6 counter drop"
[ "${USE_DIRECT_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_WHITE6 counter return" [ "${USE_DIRECT_LIST}" = "1" ] && nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_WHITE6 counter return"
nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta mark 0xff counter return" nft "add rule $NFTABLE_NAME PSW_OUTPUT_MANGLE_V6 meta mark 0xff counter return"
@@ -1398,7 +1397,7 @@ gen_include() {
local __nft=" " local __nft=" "
__nft=$(cat <<- EOF __nft=$(cat <<- EOF
[ -z "\$(nft list chain $NFTABLE_NAME mangle_prerouting | grep PSW_DIVERT)" ] && nft -f ${nft_chain_file} [ -z "\$(nft list chain $NFTABLE_NAME mangle_prerouting | grep PSW)" ] && nft -f ${nft_chain_file}
[ -z "${is_tproxy}" ] && { [ -z "${is_tproxy}" ] && {
PR_INDEX=\$(sh ${MY_PATH} RULE_LAST_INDEX "$NFTABLE_NAME" PSW_NAT WAN_IP_RETURN -1) PR_INDEX=\$(sh ${MY_PATH} RULE_LAST_INDEX "$NFTABLE_NAME" PSW_NAT WAN_IP_RETURN -1)
if [ \$PR_INDEX -ge 0 ]; then if [ \$PR_INDEX -ge 0 ]; then

View File

@@ -450,12 +450,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
@@ -1539,14 +1539,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
@@ -1561,7 +1561,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
@@ -1573,13 +1573,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)
@@ -1720,7 +1720,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)
@@ -1797,7 +1797,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 = {}
@@ -1833,17 +1833,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
@@ -1874,14 +1874,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

View File

@@ -10,8 +10,8 @@ THEME_TITLE:=Kucat Theme
PKG_NAME:=luci-theme-$(THEME_NAME) PKG_NAME:=luci-theme-$(THEME_NAME)
LUCI_TITLE:=Kucat Theme by sirpdboy LUCI_TITLE:=Kucat Theme by sirpdboy
LUCI_DEPENDS:= LUCI_DEPENDS:=
PKG_VERSION:=2.7.3 PKG_VERSION:=2.7.4
PKG_RELEASE:=20251028 PKG_RELEASE:=20251106
define Package/luci-theme-$(THEME_NAME)/conffiles define Package/luci-theme-$(THEME_NAME)/conffiles
/www/luci-static/resources/background/ /www/luci-static/resources/background/

View File

@@ -869,7 +869,7 @@ small {
.main-right>#maincontent { .main-right>#maincontent {
position: relative; position: relative;
background-color: rgba(var(--primary-rgbbody),var(--primary-rgbm-ts)); background-color: rgba(var(--primary-rgbbody),var(--primary-rgbm-ts));
padding: 0.2rem 0.2rem 3rem 0.2rem; padding: 0.2rem 0.2rem 3rem 0.5rem;
z-index: 50 z-index: 50
} }
@@ -960,7 +960,51 @@ header .fill .status span {
flex-wrap: nowrap flex-wrap: nowrap
} }
span[data-indicator="uci-changes"],span[data-indicator="poll-status"] { span[data-indicator="poll-status"] {
font-size: 0 !important;
cursor: pointer;
background-repeat: no-repeat;
background-position: center;
color: transparent !important;
-webkit-appearance: none;
display: flex;
background-color: rgba(255, 255, 255, 0);
-moz-appearance: none;
padding: 1rem;
transition: all .3s
}
span[data-indicator="poll-status"]:not([data-style="inactive"]):before {
font-size: 1.5rem !important;
font-family: 'kucat' !important;
content: "\e20b";
color: var(--body-color);
text-decoration: none
}
span[data-indicator="poll-status"]:not([data-style="active"]):before {
font-size: 1.5rem !important;
font-family: 'kucat' !important;
content: "\e20a";
color: var(--body-color);
text-decoration: none
}
#indicators span[data-indicator="poll-status"],{
line-height: 1;
padding: 1rem;
cursor: pointer !important;
font-weight: normal !important;
margin: 0;
display: inline-block
}
#indicators span[data-indicator="poll-status"]:hover {
border-radius: var(--radius1);
text-decoration: none
}
span[data-indicator="uci-changes"],#indicators span[data-indicator="poll-status"] {
font-size: 0 !important; font-size: 0 !important;
cursor: pointer; cursor: pointer;
background-repeat: no-repeat; background-repeat: no-repeat;
@@ -979,7 +1023,7 @@ span[data-indicator="uci-changes"]:before {
text-decoration: none text-decoration: none
} }
span[data-indicator="poll-status"]:not([data-style="inactive"]):before { #indicators span[data-indicator="poll-status"]:not([data-style="inactive"]):before {
font-size: 1.5rem !important; font-size: 1.5rem !important;
font-family: 'kucat' !important; font-family: 'kucat' !important;
content: "\e936"; content: "\e936";
@@ -987,7 +1031,7 @@ span[data-indicator="poll-status"]:not([data-style="inactive"]):before {
text-decoration: none text-decoration: none
} }
span[data-indicator="poll-status"]:not([data-style="active"]):before { #indicators span[data-indicator="poll-status"]:not([data-style="active"]):before {
font-family: 'kucat' !important; font-family: 'kucat' !important;
font-size: 1.5rem !important; font-size: 1.5rem !important;
content: "\e932"; content: "\e932";
@@ -995,7 +1039,7 @@ span[data-indicator="poll-status"]:not([data-style="active"]):before {
text-decoration: none text-decoration: none
} }
.pdboy-dark:hover,.pdboy-light:hover, .pdboy-dark:hover,.pdboy-light:hover,
span[data-indicator="uci-changes"]:hover,span[data-indicator="poll-status"]:hover,.pdboy-qlogout:hover,.showSide:hover { span[data-indicator="uci-changes"]:hover,#indicators span[data-indicator="poll-status"]:hover,.pdboy-qlogout:hover,.showSide:hover {
background-color: var(--menu-hover-barbgcolor) !important; background-color: var(--menu-hover-barbgcolor) !important;
color: var(--menu-hover-color); color: var(--menu-hover-color);
border-radius: var(--radius1); border-radius: var(--radius1);
@@ -1003,7 +1047,7 @@ span[data-indicator="uci-changes"]:hover,span[data-indicator="poll-status"]:hove
} }
.pdboy-dark,.pdboy-light, .pdboy-dark,.pdboy-light,
span[data-indicator="uci-changes"],span[data-indicator="poll-status"],.pdboy-qlogout { span[data-indicator="uci-changes"],#indicators span[data-indicator="poll-status"],.pdboy-qlogout {
line-height: 1; line-height: 1;
padding: 1rem; padding: 1rem;
cursor: pointer !important; cursor: pointer !important;
@@ -1089,63 +1133,6 @@ span[data-indicator="uci-changes"],span[data-indicator="poll-status"],.pdboy-qlo
-moz-osx-font-smoothing: grayscale -moz-osx-font-smoothing: grayscale
} }
.danger {
background-color: #d9534f !important;
color: #eee
}
.warning {
background-color: #b98413 !important;
margin: 0 0 0.5rem 0;
color: #eee
}
.success {
background-color: #1a8361 !important;
color: #eee;
width: 14rem !important
}
#log_textarea {
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16),0 0 1px 0 var(--input-boxcolor)
}
.error {
color: #f00
}
.alert,.alert-message {
padding: 1rem;
border: 0;
font-weight: normal;
font-style: normal;
line-height: 1.6em;
font-family: inherit;
min-width: inherit;
overflow: unset;
border-radius: var(--radius1);
background-color: rgba(var(--primary-rgbbody),var(--primary-rgbm-ts));
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16),0 0 2px 0 var(--input-boxcolor)
}
.alert-message>* {
margin: 0;
white-space: normal
}
.alert-message>h4 {
margin: 0.5rem;
color: red;
padding: 0.5rem 1rem;
font-weight: bold
}
.errorbox {
color: #f8f8f8;
background-color: #f0ad4e;
border-color: #eea236
}
.container .alert,.container .alert-message { .container .alert,.container .alert-message {
margin-top: 1rem margin-top: 1rem
} }
@@ -1420,7 +1407,7 @@ h2 {
h3 { h3 {
font-size: var(--font-d); font-size: var(--font-d);
display: block; display: block;
margin: 0.5rem 0; margin: 0.5rem 0 0 1rem;
color: var(--primary-title-color); color: var(--primary-title-color);
font-weight: bold; font-weight: bold;
letter-spacing: 0.1rem; letter-spacing: 0.1rem;
@@ -1433,7 +1420,7 @@ h4 {
padding: 0.75rem 1.25rem; padding: 0.75rem 1.25rem;
font-weight: 600; font-weight: 600;
font-size: var(--font-z); font-size: var(--font-z);
color: var(--primary-title-color) !important; color: var(--primary-title-color) ;
padding-bottom: 10px padding-bottom: 10px
} }
@@ -2529,11 +2516,68 @@ select,input {
min-width: 270px; min-width: 270px;
max-width: 900px; max-width: 900px;
min-height: 32px; min-height: 32px;
border-radius: var(--radius2) !important;
background-color: rgba(var(--primary-rgbbody), 1)!important;
color: var(--primary-title-color)!important;
box-shadow: 0 2px 10px 0px rgba(255, 255, 255, .16), 0 0 10px 0 rgba(255, 255, 255, .12);
margin: 5em auto; margin: 5em auto;
padding: 1rem; padding: 1rem;
border-radius: var(--radius2) !important; }
background-color: rgba(var(--primary-rgbbody),1)!important;
box-shadow: 0 2px 10px 0px rgba(255,255,255,.16),0 0 10px 0 rgba(255,255,255,.12) .danger {
background-color: #d9534f ;
color: #eee
}
#log_textarea {
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16),0 0 1px 0 var(--input-boxcolor)
}
.error {
color: #f00
}
.alert,.alert-message {
padding: 1rem;
border: 0;
font-weight: normal;
font-style: normal;
line-height: 1.6em;
font-family: inherit;
min-width: inherit;
overflow: unset;
border-radius: var(--radius1);
background-color: rgba(var(--primary-rgbbody),var(--primary-rgbm-ts));
box-shadow: 0 2px 2px 0 rgba(0,0,0,0.16),0 0 2px 0 var(--input-boxcolor)
}
.alert-message>* {
margin: 0;
white-space: normal
}
.alert-message>h4 {
margin: 0.5rem;
color: red;
padding: 0.5rem 1rem;
font-weight: bold
}
.warning {
background-color: #b98413 !important;
color: #eee!important;
}
.success {
background-color: #1a8361 !important;
color: #eee!important;
width: 14rem !important
}
.errorbox {
color: #f8f8f8!important;
background-color: #f0ad4e!important;
border-color: #eea236
} }
#modal_overlay .cbi-section,.modal .cbi-section { #modal_overlay .cbi-section,.modal .cbi-section {
@@ -2599,10 +2643,9 @@ select,input {
} }
.notice { .notice {
background-color: #b98413 !important; color: var(--primary-title-color);
background-color: rgba(var(--primary-rgbm),1) !important; background-color: rgba(var(--primary-rgbbody), 1);
padding: 2rem 1rem; padding: 2rem 1rem;
color: #eee
} }
.modal>p { .modal>p {
font-size: var(--font-x); font-size: var(--font-x);
@@ -2718,9 +2761,8 @@ body.modal-overlay-active #modal_overlay {
bottom: 0; bottom: 0;
left: .2em; left: .2em;
width: 32px; width: 32px;
color: var(--body-color);
content: ""; content: "";
color: var(--body-color);
background: url(../../resources/icons/loading.svg) no-repeat center, background: url(../../resources/icons/loading.svg) no-repeat center,
url(../../resources/icons/loading.gif) no-repeat center; url(../../resources/icons/loading.gif) no-repeat center;
background-size: 20px; background-size: 20px;
@@ -4028,6 +4070,7 @@ pre.command-output {
--bg-light: rgba(255, 255, 255, 0); --bg-light: rgba(255, 255, 255, 0);
--bg-gray: rgba(var(--primary-rgbm), 0.02); --bg-gray: rgba(var(--primary-rgbm), 0.02);
--text-title: var(--primary-title-color); --text-title: var(--primary-title-color);
--text-secondary: var(--body-color);
--border-color: rgba(255, 255, 255, 0); --border-color: rgba(255, 255, 255, 0);
--border-light: rgba(255, 255, 255, 0); --border-light: rgba(255, 255, 255, 0);
--text-primary: var(--inputtext-color); --text-primary: var(--inputtext-color);
@@ -4189,12 +4232,11 @@ pre.command-output {
.bandix-card , .bandix-card ,
.stats-card .stats-card
{ {
background-color: rgba(var(--primary-rgbm), 0.1)!important; background-color: rgba(var(--primary-rgbm), 0.05)!important;
border-radius: 0 !important; box-shadow: 0 1px 3px 0 var(--input-boxcolor) !important;
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0) !important;
margin-bottom: 0 !important;
border: 0px solid #333!important; border: 0px solid #333!important;
border-bottom: 0px solid var(--input-boxcolor)!important; border-bottom: 0px solid var(--input-boxcolor)!important;
margin-bottom: 0 !important;
overflow: auto!important; overflow: auto!important;
} }
@@ -4283,6 +4325,9 @@ border-bottom: 0px solid #f1f5f9!important;
[data-page="admin-system-package-manager"] .modal>textarea { [data-page="admin-system-package-manager"] .modal>textarea {
white-space: nowrap white-space: nowrap
} }
[data-page="admin-status-overview"] #view>div {
padding: 0rem 0rem 1rem 0.5rem;
}
.main>.main-left,.cbi-section,[data-tab-title],[data-page^="admin-system-admin"]:not(.node-main-login) .cbi-map:not(#cbi-dropbear),#maincontent>.container>form,#maincontent>.container>form>div,.tabs,.cbi-tabmenu,.cbi-tooltip,#view>p,#view>div,#view>table { .main>.main-left,.cbi-section,[data-tab-title],[data-page^="admin-system-admin"]:not(.node-main-login) .cbi-map:not(#cbi-dropbear),#maincontent>.container>form,#maincontent>.container>form>div,.tabs,.cbi-tabmenu,.cbi-tooltip,#view>p,#view>div,#view>table {
backdrop-filter: var(--ufilter); backdrop-filter: var(--ufilter);
@@ -4392,24 +4437,29 @@ div#add_link_div {
padding: 0.5rem padding: 0.5rem
} }
div#file-manager-container { #file-manager-container {
margin-left: 0.5rem margin-left: 0.5rem
} }
div#content-filemanager>div#file-list-container { #content-filemanager>#file-list-container {
margin-top: 10px !important margin-top: 10px !important;
min-width: 800px !important;
max-width: 100% !important;
width: auto !important;
} }
div#file-manager-container #file-table tr>th { #file-manager-container #file-table tr>th {
background-color: rgba(var(--primary-rgbm),1); background-color: rgba(var(--primary-rgbm),1);
color: var(--menu-color) color: var(--menu-color)
} }
#file-list-container table>tbody>tr>td span {
div#file-manager-container #status-bar { padding: 0.5rem;
}
#file-manager-container #status-bar {
background-color: rgba(var(--primary-rgbm),0.3) background-color: rgba(var(--primary-rgbm),0.3)
} }
div#file-manager-container #status-bar #status-info { #file-manager-container #status-bar #status-info {
color: var(--title-color) color: var(--title-color)
} }
@@ -4417,6 +4467,9 @@ div#file-manager-container #status-bar #status-info {
background-color: rgba(var(--primary-rgbm),0.4) !important background-color: rgba(var(--primary-rgbm),0.4) !important
} }
#file-list-container table>tbody>tr>td {
padding: 0.2rem
}
.cbi-tabcontainer-content #editor-container { .cbi-tabcontainer-content #editor-container {
width: 100%; width: 100%;
border: 1px solid var(--inputborder-color) border: 1px solid var(--inputborder-color)
@@ -4441,9 +4494,6 @@ div#file-manager-container #status-bar #status-info {
--clr-header: var(--title-color) --clr-header: var(--title-color)
} }
#file-list-container table>tbody>tr>td {
padding: 0.2rem
}
.tr.cbi-section-table-row[id*="wolplus"],.tr.cbi-section-table-row[id*="firewall"] { .tr.cbi-section-table-row[id*="wolplus"],.tr.cbi-section-table-row[id*="firewall"] {
flex-wrap: wrap; flex-wrap: wrap;