From 3abe250372f66546d9ee71c62225762a8391489f Mon Sep 17 00:00:00 2001 From: actions-user Date: Fri, 7 Nov 2025 00:12:45 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=8D=89=20Sync=202025-11-07=2000:12:45?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../luasrc/controller/passwall.lua | 61 ++- .../model/cbi/passwall/client/node_config.lua | 15 + .../model/cbi/passwall/client/node_list.lua | 215 --------- .../cbi/passwall/client/node_subscribe.lua | 2 +- .../view/passwall/node_list/link_add_node.htm | 9 +- .../view/passwall/node_list/node_list.htm | 408 ++++++++++++++---- luci-app-passwall/po/zh-cn/passwall.po | 6 + .../root/etc/uci-defaults/luci-passwall | 5 +- .../root/usr/share/passwall/app.sh | 2 +- .../root/usr/share/passwall/iptables.sh | 29 +- .../root/usr/share/passwall/nftables.sh | 17 +- .../root/usr/share/passwall/subscribe.lua | 32 +- luci-theme-kucat/Makefile | 4 +- .../htdocs/luci-static/kucat/css/style.css | 224 ++++++---- 14 files changed, 575 insertions(+), 454 deletions(-) diff --git a/luci-app-passwall/luasrc/controller/passwall.lua b/luci-app-passwall/luasrc/controller/passwall.lua index 831941c..91b8c1a 100644 --- a/luci-app-passwall/luasrc/controller/passwall.lua +++ b/luci-app-passwall/luasrc/controller/passwall.lua @@ -77,10 +77,12 @@ function index() 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, "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, "copy_node"}, call("copy_node")).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, "get_node"}, call("get_node")).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_all"}, call("subscribe_del_all")).leaf = true @@ -397,6 +399,21 @@ function urltest_node() http_write_json(e) 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() local protocol = http.formvalue("protocol") local section = http.formvalue("section") @@ -420,7 +437,7 @@ function copy_node() end) end end - uci:delete(appname, uuid, "add_from") + uci:delete(appname, uuid, "group") uci:set(appname, uuid, "add_mode", 1) api.uci_save(uci, appname) http.redirect(api.url("node_config", uuid)) @@ -458,6 +475,7 @@ end function delete_select_nodes() local ids = http.formvalue("ids") + local redirect = http.formvalue("redirect") string.gsub(ids, '[^' .. "," .. ']+', function(w) if (uci:get(appname, "@global[0]", "tcp_node") or "") == w then uci:delete(appname, '@global[0]', "tcp_node") @@ -532,10 +550,10 @@ function delete_select_nodes() end end) if (uci:get(appname, w, "add_mode") or "0") == "2" then - local add_from = uci:get(appname, w, "add_from") or "" - if add_from ~= "" then + local group = uci:get(appname, w, "group") or "" + if group ~= "" then uci:foreach(appname, "subscribe_list", function(t) - if t["remark"] == add_from then + if t["remark"] == group then uci:delete(appname, t[".name"], "md5") end end) @@ -543,7 +561,40 @@ function delete_select_nodes() end uci:delete(appname, w) 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 function update_rules() diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua index b6a3511..4d713e6 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_config.lua @@ -21,6 +21,21 @@ o = s:option(Value, "remarks", translate("Node Remarks")) o.default = translate("Remarks") 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")) if api.is_finded("ipt2socks") then diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua index 229cd30..8311476 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_list.lua @@ -20,221 +20,6 @@ o.default = "0" -- [[ Add the node via the link ]]-- 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("", 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("", appname, n, address) - str = str .. string.format("", 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('%s', n, translate("Test")) - else - result = string.format('---', 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('%s', n, translate("Test")) - else - result = string.format('---', 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('%s', n, translate("Test")) -end - m:append(Template(appname .. "/node_list/node_list")) return m diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe.lua index c194d78..a2d3694 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe.lua @@ -190,7 +190,7 @@ o.cfgvalue = function(t, n) str = str ~= "" and "
" .. str or "" local num = 0 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 end end) diff --git a/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm b/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm index 5acf53f..1df9c78 100644 --- a/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm +++ b/luci-app-passwall/luasrc/view/passwall/node_list/link_add_node.htm @@ -79,6 +79,10 @@ local api = require "luci.passwall.api" } } + function add_new_node() { + window.location.href = '<%=api.url("add_node")%>?redirect=1'; + } + //]]> @@ -99,15 +103,14 @@ local api = require "luci.passwall.api"
- + - + -
diff --git a/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm b/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm index 6ab4cd6..b1bd04a 100644 --- a/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm +++ b/luci-app-passwall/luasrc/view/passwall/node_list/node_list.htm @@ -44,12 +44,54 @@ table td, .table .td { } + +<% if api.is_js_luci() then -%> + +<%- end %> + - var edit_btn = document.getElementById("cbi-passwall-nodes").getElementsByClassName("cbi-button cbi-button-edit"); - for (var i = 0; i < edit_btn.length; i++) { - try { - var onclick_str = edit_btn[i].getAttribute("onclick"); - var id = onclick_str.substring(onclick_str.lastIndexOf('/') + 1, onclick_str.length - 1); - var td = edit_btn[i].parentNode; - var new_div = ""; - //添加"勾选"框 - new_div += '  '; - //添加"置顶"按钮 - new_div += '  '; - //添加"应用"按钮 - new_div += '  '; - //添加"复制"按钮 - new_div += '  '; - td.innerHTML = new_div + td.innerHTML; + - var obj = {}; - obj.id = id; - obj.type = document.getElementById("cbid.passwall." + id + ".type").value; - var address_dom = document.getElementById("cbid.passwall." + id + ".address"); - var port_dom = document.getElementById("cbid.passwall." + id + ".port"); - if (address_dom && port_dom) { - obj.address = address_dom.value; - obj.port = port_dom.value; + + +
+
+ +