🎉 Sync 2026-10-13 02:34:41

This commit is contained in:
github-actions[bot] committed 2026-10-13 02:34:41 +08:00
1 parent b537a62e59
commit 72c44a6325
38 files changed
+3510 -1734

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=26.10.4
PKG_RELEASE:=331
PKG_RELEASE:=332
PKG_PO_VERSION:=$(PKG_VERSION)
PKG_CONFIG_DEPENDS:= \
@@ -133,10 +133,10 @@ sources.validate = function(self, value, t)
flag = true
end
end
if flag == false and datatypes.macaddr(v) then
if flag == false and api.datatypes.macaddr(v) then
flag = true
end
if flag == false and datatypes.ip4addr(v) then
if flag == false and api.datatypes.ip4addr(v) then
flag = true
end
if flag == false and api.iprange(v) then
@@ -3,6 +3,7 @@ datatypes = api.datatypes
local fs = api.fs
has_singbox = api.finded_com("sing-box")
has_xray = api.finded_com("xray")
local has_smartdns = api.is_finded("smartdns")
local has_gfwlist = fs.access("/usr/share/passwall/rules/gfwlist")
local has_chnlist = fs.access("/usr/share/passwall/rules/chnlist")
local has_chnroute = fs.access("/usr/share/passwall/rules/chnroute")
@@ -180,7 +181,7 @@ o.write = function(self, section, value)
end
return ListValue.write(self, section, value)
end
if api.is_finded("smartdns") then
if has_smartdns then
o:value("smartdns", "SmartDNS")
o = s:taboption("DNS", Value, "group_domestic", translate("Domestic group name"))
@@ -284,7 +285,7 @@ o.remove = function(self, section)
end
---- SmartDNS Forward Mode
if api.is_finded("smartdns") then
if has_smartdns then
o = s:taboption("DNS", ListValue, "smartdns_dns_mode", translate("Filter Mode"))
o:value("socks", "Socks")
if has_singbox then
@@ -487,14 +488,14 @@ o:value("remote", translate("Remote DNS"))
o:value("direct", translate("Direct DNS"))
o.description = desc .. "</ul>"
o:depends({dns_shunt = "dnsmasq", tcp_proxy_mode = "proxy", chn_list = "direct"})
if api.is_finded("smartdns") then
if has_smartdns then
o:depends({dns_shunt = "smartdns", tcp_proxy_mode = "proxy", chn_list = "direct"})
end
o = s:taboption("DNS", Flag, "force_https_soa", translate("Force HTTPS SOA"), translate("Force queries with qtype 65 to respond with an SOA record."))
o.default = "0"
o:depends({dns_shunt = "chinadns-ng"})
if api.is_finded("smartdns") then
if has_smartdns then
o:depends({dns_shunt = "smartdns"})
end
+31 -14
View File
@@ -871,19 +871,26 @@ function clone(org)
end
function get_bin_version_cache(file, cmd)
sys.call("mkdir -p " .. CACHE_PATH)
if fs.access(file) then
if file and file ~= "" and fs.access(file) then
chmod_755(file)
local md5 = md5_file(file)
if fs.access(CACHE_PATH .. "/" .. md5) then
return sys.exec("echo -n $(cat %s)" % { CACHE_PATH .. "/" .. md5 })
else
local version = sys.exec(string.format("echo -n $(%s %s)", file, cmd))
if version and version ~= "" then
sys.call("echo '%s' > %s" % { version, CACHE_PATH .. "/" .. md5})
return version
local stat = fs.stat(file)
local cache_file
if stat and stat.dev and stat.ino and stat.size and stat.mtime and stat.ctime then
-- Use metadata to avoid hashing the entire binary on every lookup.
cache_file = CACHE_PATH .. "/binver_" .. table.concat({stat.dev, stat.ino, stat.size, stat.mtime, stat.ctime}, "_")
local cached = fs.readfile(cache_file)
if cached and trim(cached) ~= "" then
return trim(cached)
end
end
local version = sys.exec(string.format("echo -n $(%s %s)", util.shellquote(file), cmd))
if version and version ~= "" then
if cache_file then
fs.mkdirr(CACHE_PATH)
fs.writefile(cache_file, version)
end
return version
end
end
return ""
end
@@ -904,7 +911,7 @@ end
local function is_file(path)
if path and #path > 1 then
if sys.exec('[ -f "%s" ] && echo -n 1' % path) == "1" then
if fs.stat(path, "type") == "reg" then
return true
end
end
@@ -913,7 +920,7 @@ end
local function is_dir(path)
if path and #path > 1 then
if sys.exec('[ -d "%s" ] && echo -n 1' % path) == "1" then
if fs.stat(path, "type") == "dir" then
return true
end
end
@@ -1554,7 +1561,7 @@ function to_install_self(id, force)
remove(dir)
return {code = 1}
end
fs.chmod(script, 755)
chmod_755(script)
fs.writefile(status, '{"installing":true}')
sys.call("nohup " .. script .. " " .. dir .. " " .. status .. " " .. manager .. " " ..
(force == "1" and "1" or "0") .. " >/dev/null 2>&1 </dev/null &")
@@ -1562,7 +1569,7 @@ function to_install_self(id, force)
end
function is_js_luci()
return sys.call('[ -f "/www/luci-static/resources/uci.js" ]') == 0
return fs.stat("/www/luci-static/resources/uci.js", "type") == "reg"
end
function set_default_cbi()
@@ -1614,6 +1621,16 @@ function set_default_cbi()
return obj
end
end
if true then
--Table
local Table = cbi.Table
local default_init = Table.__init__
function Table.__init__(self, form, ...)
default_init(self, form, ...)
-- Table 的 map 是独立数据源,需要从所属表单传递 API
self.map.api = form.api
end
end
if true then
--TextValue
local TextValue = cbi.TextValue
@@ -7,6 +7,7 @@
-- Description: Passwall(2) UI template
local json = require "luci.jsonc"
local api = self.map.api
-- 读取值(keylist/vallist/group)
local values = {}
@@ -48,6 +49,30 @@ for _, item in ipairs(values) do
end
end
-- 仅共享组名、节点 key/label 及顺序完全相同的分组,保留各控件的候选限制
local group_cache = self.map._lv_group_cache
if not group_cache then
group_cache = {}
self.map._lv_group_cache = group_cache
end
local group_refs = {}
local new_groups = {}
for i, g in ipairs(group_order) do
-- 使用有序数组生成完整签名,不依赖 JSON 对象的字段顺序或哈希值
local signature = {g}
for _, item in ipairs(groups[g]) do
signature[#signature + 1] = {item.key, item.label}
end
local cache_key = json.stringify(signature)
local ref = group_cache[cache_key]
if not ref then
ref = cbid .. ".group." .. i
group_cache[cache_key] = ref
new_groups[ref] = groups[g]
end
group_refs[i] = ref
end
-- 如果没有配置值,默认取第一个(优先无组第一项,否则第一组第一项)
if not current_key then
if #ungrouped > 0 then
@@ -73,19 +98,28 @@ local function find_label_by_key(k)
end
local current_label = find_label_by_key(current_key) or ""
-- 打包数据给 JS
-- 控件只携带分组引用,重复的候选数据在页面中仅输出一次
local dropdown_data = {
appname = api.appname,
current_key = current_key,
current_label = current_label,
ungrouped = ungrouped,
groups = groups,
group_refs = group_refs,
group_order = group_order,
cbid = cbid,
}
%>
<script>
window.lv_dropdown_data = window.lv_dropdown_data || {};
window.lv_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
(function() {
const appname = <%=json.stringify(api.appname)%>;
window.lv_groups_pool = window.lv_groups_pool || Object.create(null);
const pool = window.lv_groups_pool[appname] || (window.lv_groups_pool[appname] = Object.create(null));
<% if next(new_groups) then %>
Object.assign(pool, <%=json.stringify(new_groups)%>);
<% end %>
window.lv_dropdown_data = window.lv_dropdown_data || {};
window.lv_dropdown_data[<%=json.stringify(cbid)%>] = <%=json.stringify(dropdown_data)%>;
})();
</script>
<div id="<%=cbid%>.main" class="lv-dropdown-container">
@@ -577,8 +577,10 @@
}
// 分组项
data.group_order.forEach(gname => {
const items = data.groups[gname];
const groupPool = window.lv_groups_pool && window.lv_groups_pool[data.appname];
data.group_order.forEach((gname, index) => {
const items = data.groups ? data.groups[gname] : groupPool && groupPool[data.group_refs[index]];
if (!Array.isArray(items)) return;
html += `
<div class="lv-group" data-group="${lv_idSafe(gname)}" style="margin-bottom:8px;">
@@ -6,6 +6,7 @@
-- Description: Passwall(2) UI template
local json = require "luci.jsonc"
local api = self.map.api
-- 读取值(keylist/vallist/group)
local values = {{key = "", label = translate("-- Please choose --")}}
@@ -56,6 +57,30 @@ if not current_key_added and current_key and current_key ~= "" then
table.insert(ungrouped, {key = current_key, label = current_key})
end
-- 仅共享候选分组,各控件的自定义地址仍保留在自己的 ungrouped 中
local group_cache = self.map._v_group_cache
if not group_cache then
group_cache = {}
self.map._v_group_cache = group_cache
end
local group_refs = {}
local new_groups = {}
for i, g in ipairs(group_order) do
-- 组名、节点 key/label 及顺序完全相同才复用,不与 ListValue 缓存混用
local signature = {g}
for _, item in ipairs(groups[g]) do
signature[#signature + 1] = {item.key, item.label}
end
local cache_key = json.stringify(signature)
local ref = group_cache[cache_key]
if not ref then
ref = cbid .. ".v-group." .. i
group_cache[cache_key] = ref
new_groups[ref] = groups[g]
end
group_refs[i] = ref
end
-- 如果没有配置值,默认取第一个(优先无组第一项,否则第一组第一项)
if not current_key then
if #ungrouped > 0 then
@@ -81,19 +106,28 @@ local function find_label_by_key(k)
end
local current_label = find_label_by_key(current_key) or ""
-- 打包数据给 JS
-- 控件只携带分组引用,重复的候选数据在页面中仅输出一次
local dropdown_data = {
appname = api.appname,
current_key = current_key,
current_label = current_label,
ungrouped = ungrouped,
groups = groups,
group_refs = group_refs,
group_order = group_order,
cbid = cbid,
}
%>
<script>
window.v_dropdown_data = window.v_dropdown_data || {};
window.v_dropdown_data["<%=cbid%>"] = <%=json.stringify(dropdown_data)%>;
(function() {
const appname = <%=json.stringify(api.appname)%>;
window.v_groups_pool = window.v_groups_pool || Object.create(null);
const pool = window.v_groups_pool[appname] || (window.v_groups_pool[appname] = Object.create(null));
<% if next(new_groups) then %>
Object.assign(pool, <%=json.stringify(new_groups)%>);
<% end %>
window.v_dropdown_data = window.v_dropdown_data || {};
window.v_dropdown_data[<%=json.stringify(cbid)%>] = <%=json.stringify(dropdown_data)%>;
})();
</script>
<div id="<%=cbid%>.v-main" class="v-dropdown-container">
@@ -603,8 +603,10 @@
}
// 分组项
data.group_order.forEach(gname => {
const items = data.groups[gname];
const groupPool = window.v_groups_pool && window.v_groups_pool[data.appname];
data.group_order.forEach((gname, index) => {
const items = data.groups ? data.groups[gname] : groupPool && groupPool[data.group_refs[index]];
if (!Array.isArray(items)) return;
html += `
<div class="v-group" data-group="${v_idSafe(gname)}" style="margin-bottom:8px;">
@@ -139,7 +139,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('aliyun', 'https://www.aliyun.com')" title="<%:HTTP request latency, for reference only%>">
<div class="pure-u-1-4 check" onclick="check_connect('aliyun', 'https://www.aliyun.com/favicon.ico')" title="<%:HTTP request latency, for reference only%>">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">
@@ -163,7 +163,7 @@ https://github.com/pure-css/pure/blob/master/LICENSE.md
</div>
</div>
</div>
<div class="pure-u-1-4 check" onclick="check_connect('github', 'https://github.com')" title="<%:HTTP request latency, for reference only%>">
<div class="pure-u-1-4 check" onclick="check_connect('github', 'https://github.com/favicon.ico')" title="<%:HTTP request latency, for reference only%>">
<div class="block pure-g">
<div class="pure-u-1-3">
<div class="img-con">