diff --git a/luci-app-fchomo/root/usr/libexec/fchomo/update_resources.sh b/luci-app-fchomo/root/usr/libexec/fchomo/update_resources.sh index 68f83aef..a55b11af 100755 --- a/luci-app-fchomo/root/usr/libexec/fchomo/update_resources.sh +++ b/luci-app-fchomo/root/usr/libexec/fchomo/update_resources.sh @@ -17,7 +17,7 @@ log() { } to_upper() { - echo -e "$1" | tr "[a-z]" "[A-Z]" + echo -e "$1" | tr "a-z" "A-Z" } get_local_ver() { diff --git a/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc b/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc index 3d2f18db..c0c56bd5 100644 --- a/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc +++ b/luci-app-fchomo/root/usr/share/fchomo/generate_client.uc @@ -82,7 +82,7 @@ const listen_interfaces = uci.get(uciconf, uciroute, 'listen_interfaces') || nul lan_proxy_mac_addrs = uci.get(uciconf, uciroute, 'lan_proxy_mac_addrs') || null, proxy_router = (uci.get(uciconf, uciroute, 'proxy_router') === '0') ? null : true, top_upstream = (uci.get(uciconf, uciroute, 'top_upstream') === '1') || null, - client_enabled = uci.get(uciconf, uciroute, 'client_enabled' === '1') || null, + client_enabled = (uci.get(uciconf, uciroute, 'client_enabled') === '1') || null, routing_tcpport = uci.get(uciconf, uciroute, 'routing_tcpport') || [], routing_udpport = uci.get(uciconf, uciroute, 'routing_udpport') || [], routing_mode = uci.get(uciconf, uciroute, 'routing_mode') || null, diff --git a/luci-app-fchomo/root/usr/share/rpcd/ucode/luci.fchomo b/luci-app-fchomo/root/usr/share/rpcd/ucode/luci.fchomo index a399543d..a258d915 100644 --- a/luci-app-fchomo/root/usr/share/rpcd/ucode/luci.fchomo +++ b/luci-app-fchomo/root/usr/share/rpcd/ucode/luci.fchomo @@ -50,7 +50,7 @@ const methods = { const type = req.args?.type; let result = {}; - const fd = popen('/usr/bin/mihomo generate ' + type + ` ${req.args?.params ?? ''}`); + const fd = popen('/usr/bin/mihomo generate ' + type + (req.args?.params ? ' ' + shellQuote(req.args.params) : '')); if (fd) { for (let line = fd.read('line'); length(line); line = fd.read('line')) { if (type === 'uuid') diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua index 444c40bf..431a60dc 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/other.lua @@ -184,11 +184,16 @@ if has_xray then o.default = "100-200" o:depends("fragment", true) - o = s_xray:option(Value, "fragment_interval", translate("Fragment Interval"), translate("Fragmentation interval (ms)")) + o = s_xray:option(Value, "fragment_delay", translate("Fragment Delay"), translate("Fragmentation interval (ms)")) o.datatype = "or(uinteger,portrange)" o.default = "10-20" o:depends("fragment", true) + o = s_xray:option(Value, "fragment_maxSplit", translate("Max Split"), translate("Limit the maximum number of splits.")) + o.datatype = "or(uinteger,portrange)" + o.default = "100-200" + o:depends("fragment", true) + o = s_xray:option(Flag, "noise", translate("Noise"), translate("UDP noise, Under some circumstances it can bypass some UDP based protocol restrictions.")) o.default = 0 @@ -231,11 +236,12 @@ if has_xray then o = s_xray_noise:option(ListValue, "type", translate("Type")) o:value("rand", "rand") + o:value("array", "array") o:value("str", "str") o:value("hex", "hex") o:value("base64", "base64") - o = s_xray_noise:option(Value, "packet", translate("Packet")) + o = s_xray_noise:option(Value, "packet", translate("Packet | Rand Length")) o.datatype = "minlength(1)" o.rmempty = false diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria2.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria2.lua index c677aadc..e06eb72d 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria2.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/hysteria2.lua @@ -67,8 +67,10 @@ o.rewrite_option = o.option o = s:option(Value, _n("recv_window_conn"), translate("QUIC connection receive window")) o.rewrite_option = o.option +o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)") +o.rewrite_option = o.option -o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s-120s)") +o = s:option(Value, _n("keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)") o.rewrite_option = o.option o = s:option(Flag, _n("disable_mtu_discovery"), translate("Disable MTU detection")) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua index 6c0cd877..24f295d6 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/type/ray.lua @@ -328,7 +328,10 @@ o = s:option(Value, _n("hysteria2_auth_password"), translate("Auth Password")) o.password = true o:depends({ [_n("protocol")] = "hysteria2"}) -o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s-120s)") +o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)") +o:depends({ [_n("protocol")] = "hysteria2"}) + +o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)") o:depends({ [_n("protocol")] = "hysteria2"}) o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection")) @@ -406,7 +409,7 @@ o.validate = function(self, value) end o = s:option(ListValue, _n("ech_ForceQuery"), translate("ECH Query Policy"), translate("Controls the policy used when performing DNS queries for ECH configuration.")) -o.default = "none" +o.default = "full" o:value("none") o:value("half") o:value("full") @@ -694,10 +697,12 @@ o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ [_n("protocol")] = "wireguard" }) o:depends({ [_n("protocol")] = "hysteria2" }) -o = s:option(TextValue, _n("finalmask"), " ", translate("An FinalMaskObject in JSON format, used for sharing.")) +o = s:option(TextValue, _n("finalmask"), " ") o:depends({ [_n("use_finalmask")] = true }) o.rows = 10 o.wrap = "off" +o.description = translate("An FinalMaskObject in JSON format, used for sharing.") .. "
" .. + translate("Custom finalmask overrides mkcp, hysteria2, fragment, noise, and related settings.") o.custom_cfgvalue = function(self, section, value) local raw = m:get(section, "finalmask") if raw then diff --git a/luci-app-passwall/luasrc/passwall/util_hysteria2.lua b/luci-app-passwall/luasrc/passwall/util_hysteria2.lua index 4053bc91..f69c7164 100644 --- a/luci-app-passwall/luasrc/passwall/util_hysteria2.lua +++ b/luci-app-passwall/luasrc/passwall/util_hysteria2.lua @@ -90,14 +90,14 @@ function gen_config(var) quic = { initStreamReceiveWindow = (node.hysteria2_recv_window) and tonumber(node.hysteria2_recv_window) or nil, initConnReceiveWindow = (node.hysteria2_recv_window_conn) and tonumber(node.hysteria2_recv_window_conn) or nil, - maxIdleTimeout = (function() - local timeoutStr = tostring(node.hysteria2_idle_timeout or "") - local timeout = tonumber(timeoutStr:match("^%d+")) - if timeout and timeout >= 4 and timeout <= 120 then - return tostring(timeout) .. "s" - end - return nil - end)(), + maxIdleTimeout = (function(t) + t = tonumber(tostring(t or "30"):match("^%d+")) + return (t and t >= 4 and t <= 120) and t .. "s" or "30s" + end)(node.hysteria2_idle_timeout), + keepAlivePeriod = (function(t) + t = tonumber(tostring(t or "0"):match("^%d+")) + return (t and t >= 2 and t <= 60) and t .. "s" or nil + end)(node.hysteria2_keep_alive_period), disablePathMTUDiscovery = (node.hysteria2_disable_mtu_discovery) and true or false, }, bandwidth = (node.hysteria2_up_mbps or node.hysteria2_down_mbps) and { diff --git a/luci-app-passwall/luasrc/passwall/util_xray.lua b/luci-app-passwall/luasrc/passwall/util_xray.lua index f340bb64..f00ae406 100644 --- a/luci-app-passwall/luasrc/passwall/util_xray.lua +++ b/luci-app-passwall/luasrc/passwall/util_xray.lua @@ -8,20 +8,6 @@ local fs = api.fs local xray_version = api.get_app_version("xray") -local function get_noise_packets() - local noises = {} - uci:foreach(appname, "xray_noise_packets", function(n) - local noise = (n.enabled == "1") and { - type = n.type, - packet = n.packet, - delay = string.find(n.delay, "-") and n.delay or tonumber(n.delay) - } or nil - table.insert(noises, noise) - end) - if #noises == 0 then noises = nil end - return noises -end - local function get_domain_excluded() local path = string.format("/usr/share/%s/rules/domains_excluded", appname) local content = fs.readfile(path) @@ -149,8 +135,7 @@ function gen_outbound(flag, node, tag, proxy_table) sockopt = { mark = 255, tcpFastOpen = (node.tcp_fast_open == "1") and true or nil, - tcpMptcp = (node.tcpMptcp == "1") and true or nil, - dialerProxy = (fragment or noise) and "dialerproxy" or nil + tcpMptcp = (node.tcpMptcp == "1") and true or nil }, network = node.transport, security = node.stream_security, @@ -172,7 +157,7 @@ function gen_outbound(flag, node, tag, proxy_table) return node.tls_CertByName end)(), echConfigList = (node.ech == "1") and node.ech_config or nil, - echForceQuery = (node.ech == "1") and (node.ech_ForceQuery or "none") or nil + echForceQuery = (node.ech == "1") and (node.ech_ForceQuery or "full") or nil } or nil, realitySettings = (node.stream_security == "reality") and { serverName = node.tls_serverName, @@ -263,7 +248,8 @@ function gen_outbound(flag, node, tag, proxy_table) } or nil, finalmask = (function() local finalmask = {} - if node.transport == "mkcp" then + local TP = node.transport + if TP == "mkcp" then local map = {none = "none", srtp = "header-srtp", utp = "header-utp", ["wechat-video"] = "header-wechat", dtls = "header-dtls", wireguard = "header-wireguard", dns = "header-dns"} local udp = {} @@ -280,7 +266,7 @@ function gen_outbound(flag, node, tag, proxy_table) end udp[#udp+1] = c finalmask.udp = udp - elseif node.transport == "hysteria" then + elseif TP == "hysteria" then if node.hysteria2_obfs_type and node.hysteria2_obfs_type ~= "" then finalmask.udp = {{ type = node.hysteria2_obfs_type, @@ -306,9 +292,20 @@ function gen_outbound(flag, node, tag, proxy_table) t = tonumber(tostring(t or "30"):match("^%d+")) return (t and t >= 4 and t <= 120) and t or 30 end)(node.hysteria2_idle_timeout), + keepAlivePeriod = (function(t) + t = tonumber(tostring(t or "0"):match("^%d+")) + return (t and t >= 2 and t <= 60) and t or nil + end)(node.hysteria2_keep_alive_period), disablePathMTUDiscovery = tonumber(node.hysteria2_disable_mtu_discovery) == 1 } end + if fragment and fragment_table and ({raw=1, ws=1, httpupgrade=1, grpc=1, xhttp=1})[TP] then + finalmask.tcp = finalmask.tcp or {} + finalmask.tcp[#finalmask.tcp+1] = api.clone(fragment_table) + end + if noise and noise_table and (TP == "mkcp" or (TP == "xhttp" and node.alpn == "h3")) then + finalmask.udp[#finalmask.udp+1] = api.clone(noise_table) + end if node.finalmask and node.finalmask ~= "" then local ok, fm = pcall(jsonc.parse, api.base64Decode(node.finalmask)) if ok and type(fm) == "table" then @@ -755,6 +752,38 @@ function gen_config(var) local xray_settings = uci:get_all(appname, "@global_xray[0]") or {} + if xray_settings.fragment == "1" then + local delay = xray_settings.fragment_delay + fragment_table = { + type = "fragment", + settings = { + packets = xray_settings.fragment_packets, + length = xray_settings.fragment_length, + delay = string.find(delay, "-") and delay or tonumber(delay), + maxSplit = xray_settings.fragment_maxSplit + } + } + end + + if xray_settings.noise == "1" then + local noises = {} + uci:foreach(appname, "xray_noise_packets", function(n) + if n.enabled == "1" then + local noise = { + rand = (n.type == "rand") and (string.find(n.packet, "-") and n.packet or tonumber(n.packet)) or nil, + type = (n.type ~= "rand") and n.type or nil, + packet = (n.type ~= "rand") and n.packet or nil, + delay = string.find(n.delay, "-") and n.delay or tonumber(n.delay) + } + table.insert(noises, noise) + end + end) + noise_table = #noises > 0 and { + type = "noise", + settings = { reset = 0, noise = noises } + } or nil + end + if node_id then local node = uci:get_all(appname, node_id) local balancers = {} @@ -1663,27 +1692,6 @@ function gen_config(var) } } - if xray_settings.fragment == "1" or xray_settings.noise == "1" then - table.insert(outbounds, { - protocol = "freedom", - tag = "dialerproxy", - settings = { - domainStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP", - fragment = (xray_settings.fragment == "1") and { - packets = (xray_settings.fragment_packets and xray_settings.fragment_packets ~= "") and xray_settings.fragment_packets, - length = (xray_settings.fragment_length and xray_settings.fragment_length ~= "") and xray_settings.fragment_length, - interval = (xray_settings.fragment_interval and xray_settings.fragment_interval ~= "") and xray_settings.fragment_interval - } or nil, - noises = (xray_settings.noise == "1") and get_noise_packets() or nil - }, - streamSettings = { - sockopt = { - mark = 255 - } - } - }) - end - local direct_outbound = { protocol = "freedom", tag = "direct", diff --git a/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm b/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm index e179bc47..83165135 100644 --- a/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm +++ b/luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm @@ -1312,12 +1312,7 @@ local current_node = map:get(section) queryParam.security = queryParam.security || "tls"; opt.set(dom_prefix + 'tls', queryParam.security === "tls"); if (queryParam.security === "tls") { - var tls_serverName = queryParam.peer; - if (queryParam.sni) { - tls_serverName = queryParam.sni - } - tls_serverName = tls_serverName || ""; - opt.set(dom_prefix + 'tls_serverName', tls_serverName); + opt.set(dom_prefix + 'tls_serverName', queryParam.peer || queryParam.sni || ""); opt.set(dom_prefix + 'alpn', queryParam.alpn || 'default'); opt.set(dom_prefix + 'tls_CertSha', queryParam.pcs || ''); opt.set(dom_prefix + 'tls_CertByName', queryParam.vcn || ''); diff --git a/luci-app-passwall/po/zh-cn/passwall.po b/luci-app-passwall/po/zh-cn/passwall.po index 3bd1ebc2..fd697d9c 100644 --- a/luci-app-passwall/po/zh-cn/passwall.po +++ b/luci-app-passwall/po/zh-cn/passwall.po @@ -706,6 +706,9 @@ msgstr "QUIC 连接接收窗口" msgid "QUIC concurrent bidirectional streams" msgstr "QUIC 并发双向流的最大数量" +msgid "QUIC KeepAlive interval" +msgstr "QUIC KeepAlive 间隔" + msgid "Disable MTU detection" msgstr "禁用 MTU 检测" @@ -1855,6 +1858,9 @@ msgstr "启用 Multipath TCP,需在服务端和客户端配置中同时启用 msgid "An FinalMaskObject in JSON format, used for sharing." msgstr "JSON 格式的 FinalMaskObject,用来实现分享。" +msgid "Custom finalmask overrides mkcp, hysteria2, fragment, noise, and related settings." +msgstr "自定义 finalmask 将覆盖 mkcp、hysteria2、fragment、noise 等相关配置。" + msgid "Fragment" msgstr "分片" @@ -1873,12 +1879,18 @@ msgstr "分片包长" msgid "Fragmented packet length (byte)" msgstr "分片包长 (byte)" -msgid "Fragment Interval" -msgstr "分片间隔" +msgid "Fragment Delay" +msgstr "分片延迟" msgid "Fragmentation interval (ms)" msgstr "分片间隔(ms)" +msgid "Max Split" +msgstr "最大分片数" + +msgid "Limit the maximum number of splits." +msgstr "限制分片的最大数量。" + msgid "Split handshake data into multiple TLS records for better censorship evasion. Low overhead. Recommended to enable first." msgstr "将握手数据拆分为多个 TLS 记录,提升抗封锁能力,几乎不增加延迟,建议优先启用。" @@ -1897,8 +1909,8 @@ msgstr "在 Xray 设置中勾选 “噪声” 以发送噪声包。" msgid "Xray Noise Packets" msgstr "Xray 噪声数据包" -msgid "Packet" -msgstr "数据包" +msgid "Packet | Rand Length" +msgstr "数据包 | Rand 长度" msgid "Delay (ms)" msgstr "延迟(ms)" diff --git a/luci-app-passwall/root/usr/share/passwall/subscribe.lua b/luci-app-passwall/root/usr/share/passwall/subscribe.lua index 938aa945..636a9740 100755 --- a/luci-app-passwall/root/usr/share/passwall/subscribe.lua +++ b/luci-app-passwall/root/usr/share/passwall/subscribe.lua @@ -997,14 +997,10 @@ local function processData(szType, content, add_mode, group) else result.address = host_port end - - local peer, sni = nil, "" - if params.peer then peer = params.peer end - sni = params.sni and params.sni or "" result.port = port result.tls = '1' - result.tls_serverName = peer and peer or sni + result.tls_serverName = params.peer or params.sni or "" result.tls_CertSha = params.pcs result.tls_CertByName = params.vcn @@ -1346,7 +1342,7 @@ local function processData(szType, content, add_mode, group) result.hysteria_obfs = params.obfsParam result.hysteria_auth_type = "string" result.hysteria_auth_password = params.auth - result.tls_serverName = params.peer + result.tls_serverName = params.peer or params.sni or "" params.allowinsecure = params.allowinsecure or params.insecure if params.allowinsecure and (params.allowinsecure == "1" or params.allowinsecure == "0") then result.tls_allowInsecure = params.allowinsecure diff --git a/luci-app-ssr-plus/Makefile b/luci-app-ssr-plus/Makefile index 27eeefa4..9cbb8305 100644 --- a/luci-app-ssr-plus/Makefile +++ b/luci-app-ssr-plus/Makefile @@ -4,7 +4,7 @@ LUCI_TITLE:=luci-app-ssr-plus LUCI_PKGARCH:=all PKG_NAME:=luci-app-ssr-plus PKG_VERSION:=190 -PKG_RELEASE:=3 +PKG_RELEASE:=4 PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \ diff --git a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua index d89fa115..b2950b93 100644 --- a/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua +++ b/luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client-config.lua @@ -1323,7 +1323,7 @@ if is_finded("xray") then o = s:option(ListValue, "ech_ForceQuery", translate("ECH Query Policy")) o.description = translate("Controls the policy used when performing DNS queries for ECH configuration.") - o.default = "none" + o.default = "full" o:value("none") o:value("half") o:value("full") diff --git a/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_list.htm b/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_list.htm index 21f21326..0d0eb3c2 100644 --- a/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_list.htm +++ b/luci-app-ssr-plus/luasrc/view/shadowsocksr/server_list.htm @@ -40,9 +40,13 @@ table td, .table .td { .cbi-section-table th:nth-child(2) { width: 180px; max-width: 180px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + cursor: help; +} + +.cbi-section-table td:nth-child(2) div { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .node-wrapper { @@ -115,9 +119,9 @@ table td, .table .td { }*/ .socket-connected { - flex: 0 0 40px; - white-space: nowrap; - min-width: 0; + flex: 0 0 40px; + white-space: nowrap; + min-width: 0; } @media screen and (max-width: 1152px) { @@ -160,6 +164,14 @@ table td, .table .td { color: #555; } + /* 手机端完整显示节点名称 */ + .cbi-section-table td:nth-child(2) div { + overflow: visible; + white-space: normal; + text-overflow: clip; + word-break: break-word; + } + .cbi-section-table td.cbi-section-actions { display: block !important; width: 100% !important; @@ -697,6 +709,15 @@ table td, .table .td { // 初始化排序功能 initSortable(); + // 为节点名称列添加 title 属性 + document.querySelectorAll("#cbi-shadowsocksr-servers table tbody tr").forEach(function(row) { + var nameCell = row.querySelector("td:nth-child(2)"); + if (nameCell) { + var fullName = nameCell.innerText.trim(); + if (fullName) nameCell.setAttribute("title", fullName); + } + }); + // 绑定保存按钮点击事件 var saveBtn = document.getElementById("save_order_btn"); if (saveBtn) { diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua index 8f43b978..87700815 100755 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/gen_config.lua @@ -337,7 +337,7 @@ end return server.tls_CertByName end)(), echConfigList = (server.enable_ech == "1") and server.ech_config or nil, - echForceQuery = (server.enable_ech == "1") and (server.ech_ForceQuery or "none") or nil + echForceQuery = (server.enable_ech == "1") and (server.ech_ForceQuery or "full") or nil } or nil, xtlsSettings = (server.xtls == '1') and server.tls_host and { -- xtls diff --git a/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua b/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua index af1bc987..ce196877 100755 --- a/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua +++ b/luci-app-ssr-plus/root/usr/share/shadowsocksr/subscribe.lua @@ -217,7 +217,7 @@ local function checkTabValue(tab) end -- JSON完整性检查 local function isCompleteJSON(str) - -- 检查JSON格式 + -- 检查JSON格式 if type(str) ~= "string" or str:match("^%s*$") then return false end @@ -269,7 +269,9 @@ local function processData(szType, content, cfgid) result.v2ray_protocol = has_xray_hy2_type end - result.alias = url.fragment and UrlDecode(url.fragment) or nil + local raw_alias = url.fragment and UrlDecode(url.fragment) or nil + result.raw_alias = raw_alias -- 新增 + result.alias = raw_alias -- 临时赋值(后面会被覆盖) result.type = hy2_type result.server = url.host result.server_port = url.port or 443 @@ -360,10 +362,11 @@ local function processData(szType, content, cfgid) -- 拼接 alias local alias = "" if group ~= "" then - alias = "[" .. group .. "] " + raw_alias = "[" .. group .. "] " end - alias = alias .. remarks - result.alias = alias + raw_alias = raw_alias .. remarks + result.raw_alias = raw_alias -- 新增 + result.alias = raw_alias -- 临时赋值(后面会被覆盖) elseif szType == "vmess" then -- 去掉前后空白和注释 local link = trim(content:gsub("#.*$", "")) @@ -387,7 +390,8 @@ local function processData(szType, content, cfgid) result.server_port = info.port result.alter_id = info.aid result.vmess_id = info.id - result.alias = info.ps + result.raw_alias = info.ps -- 新增 + result.alias = info.ps -- 临时赋值(后面会被覆盖) -- 调整传输协议 if info.net == "tcp" then @@ -515,7 +519,9 @@ local function processData(szType, content, cfgid) alias = content:sub(idx_sp + 1, -1) content = content:sub(0, idx_sp - 1):gsub("/%?", "?") end - result.alias = UrlDecode(alias) + local raw_alias = UrlDecode(alias) + result.raw_alias = raw_alias -- 新增 + result.alias = raw_alias -- 临时赋值(后面会被覆盖) -- 拆 base64 主体和 ? 参数部分 local info = content @@ -582,13 +588,13 @@ local function processData(szType, content, cfgid) if not pwd:find("%%[0-9A-Fa-f][0-9A-Fa-f]") then return false end - local ok, decoded = pcall(UrlDecode, pwd) - return ok and urlEncode(decoded) == pwd + local ok, decoded = pcall(UrlDecode, pwd) + return ok and urlEncode(decoded) == pwd end local decoded = UrlDecode(password) - if isURLEncodedPassword(password) and decoded then - password = decoded + if isURLEncodedPassword(password) and decoded then + password = decoded end -- 解析服务器地址和端口(兼容 IPv6) @@ -642,18 +648,16 @@ local function processData(szType, content, cfgid) if decoded_tls then local ok, st = pcall(jsonParse, decoded_tls) if ok and st then - result.plugin = "shadow-tls" result.enable_plugin = 1 - local version_flag = "" if st.version and tonumber(st.version) then - version_flag = string.format("v%s=1;", st.version) + version_flag = string.format("v%s=1;", st.version) end -- 合成 plugin_opts 格式:v%s=1;host=xxx;password=xxx result.plugin_opts = string.format("%shost=%s;passwd=%s", - version_flag, + version_flag, st.host or "", st.password or "") else @@ -694,14 +698,14 @@ local function processData(szType, content, cfgid) -- 新格式:method:password result.encrypt_method_ss, result.password = is_base64:match("^(.-):(.*)$") else - -- 旧格式:UUID 直接作为密码 - result.password = url.user - result.encrypt_method_ss = params.encryption or "none" + -- 旧格式:UUID 直接作为密码 + result.password = url.user + result.encrypt_method_ss = params.encryption or "none" end if params.udp then - -- 处理 udp 参数 - result.uot = params.udp + -- 处理 udp 参数 + result.uot = params.udp end result.transport = params.type or "raw" @@ -806,7 +810,7 @@ local function processData(szType, content, cfgid) result.grpc_mode = params.mode or "gun" elseif result.transport == "tcp" or result.transport == "raw" then result.tcp_guise = params.headerType or "none" - if result.tcp_guise == "http" then + if result.tcp_guise == "http" then result.tcp_host = params.host and UrlDecode(params.host) or nil result.tcp_path = params.path and UrlDecode(params.path) or nil end @@ -826,7 +830,8 @@ local function processData(szType, content, cfgid) result.encrypt_method_ss = content.method result.plugin = content.plugin result.plugin_opts = content.plugin_opts - result.alias = content.remarks + result.raw_alias = content.remarks -- 新增 + result.alias = content.remarks -- 临时赋值(后面会被覆盖) if not checkTabValue(encrypt_methods_ss)[content.method] then result.server = nil end @@ -843,7 +848,9 @@ local function processData(szType, content, cfgid) result.password = content.password result.encrypt_method_ss = content.method result.plugin_opts = content.plugin_options - result.alias = "[" .. content.airport .. "] " .. content.remarks + local raw_alias = "[" .. content.airport .. "] " .. content.remarks + result.raw_alias = raw_alias -- 新增 + result.alias = raw_alias -- 临时赋值(后面会被覆盖) if content.plugin == "simple-obfs" then result.plugin = "obfs-local" else @@ -860,11 +867,13 @@ local function processData(szType, content, cfgid) alias = content:sub(idx_sp + 1, -1) content = content:sub(0, idx_sp - 1) end - result.alias = UrlDecode(alias) + local raw_alias = UrlDecode(alias) + result.raw_alias = raw_alias -- 新增 + result.alias = raw_alias -- 临时赋值(后面会被覆盖) -- 分离和提取 password local Info = content - local params = {} + local params = {} if Info:find("@") then local contents = split(Info, "@") result.password = UrlDecode(contents[1]) @@ -984,7 +993,7 @@ local function processData(szType, content, cfgid) if success and type(Data) == "table" then local address = (Data.extra and Data.extra.downloadSettings and Data.extra.downloadSettings.address) or (Data.downloadSettings and Data.downloadSettings.address) - result.download_address = (address and address ~= "") and address:gsub("^%[", ""):gsub("%]$", "") + result.download_address = (address and address ~= "") and address:gsub("^%[", ""):gsub("%]$", "") else -- 如果解析失败,清空下载地址 result.download_address = nil @@ -1027,7 +1036,9 @@ local function processData(szType, content, cfgid) local url = URL.parse("http://" .. content) local params = url.query - result.alias = url.fragment and UrlDecode(url.fragment) or nil + local raw_alias = url.fragment and UrlDecode(url.fragment) or nil + result.raw_alias = raw_alias -- 新增 + result.alias = raw_alias -- 临时赋值(后面会被覆盖) result.type = "v2ray" result.v2ray_protocol = "vless" result.server = url.host @@ -1113,11 +1124,9 @@ local function processData(szType, content, cfgid) if result.transport == "ws" then result.ws_host = (result.tls ~= "1" and result.reality ~= "1") and (params.host and UrlDecode(params.host)) or nil result.ws_path = params.path and UrlDecode(params.path) or "/" - elseif result.transport == "httpupgrade" then result.httpupgrade_host = (result.tls ~= "1" and result.reality ~= "1") and (params.host and UrlDecode(params.host)) or nil result.httpupgrade_path = params.path and UrlDecode(params.path) or "/" - elseif result.transport == "xhttp" then result.xhttp_mode = params.mode or "auto" result.xhttp_host = params.host and UrlDecode(params.host) or nil @@ -1134,7 +1143,7 @@ local function processData(szType, content, cfgid) if success and type(Data) == "table" then local address = (Data.extra and Data.extra.downloadSettings and Data.extra.downloadSettings.address) or (Data.downloadSettings and Data.downloadSettings.address) - result.download_address = (address and address ~= "") and address:gsub("^%[", ""):gsub("%]$", "") + result.download_address = (address and address ~= "") and address:gsub("^%[", ""):gsub("%]$", "") else result.download_address = nil end @@ -1180,7 +1189,9 @@ local function processData(szType, content, cfgid) alias = content:sub(idx_sp + 1, -1) content = content:sub(0, idx_sp - 1) end - result.alias = UrlDecode(alias) + local raw_alias = UrlDecode(alias) + result.raw_alias = raw_alias -- 新增 + result.alias = raw_alias -- 临时赋值(后面会被覆盖) -- 分离和提取 uuid 和 password local Info = content @@ -1242,7 +1253,7 @@ local function processData(szType, content, cfgid) if params.disable_sni then if params.disable_sni == "1" or params.disable_sni == "0" then result.disable_sni = params.disable_sni - else + else result.disable_sni = string.lower(params.disable_sni) == "true" and "1" or "0" end end @@ -1251,7 +1262,7 @@ local function processData(szType, content, cfgid) if params.zero_rtt_handshake then if params.zero_rtt_handshake == "1" or params.zero_rtt_handshake == "0" then result.zero_rtt_handshake = params.zero_rtt_handshake - else + else result.zero_rtt_handshake = string.lower(params.zero_rtt_handshake) == "true" and "1" or "0" end end @@ -1260,7 +1271,7 @@ local function processData(szType, content, cfgid) if params.dual_stack then if params.dual_stack == "1" or params.dual_stack == "0" then result.dual_stack = params.dual_stack - else + else result.dual_stack = string.lower(params.dual_stack) == "true" and "1" or "0" end -- 处理 ipstack_prefer 参数 @@ -1277,12 +1288,14 @@ local function processData(szType, content, cfgid) end end end + if not result.alias then if result.server and result.server_port then result.alias = result.server .. ':' .. result.server_port else result.alias = "NULL" end + result.raw_alias = result.alias end -- alias 不参与 hashkey 计算 local alias = result.alias @@ -1352,44 +1365,42 @@ local function curl(url, user_agent) end local function check_filer(result) - do - -- 过滤的关键词列表 - local filter_word = split(filter_words, "/") - -- 保留的关键词列表 - local check_save = false - if save_words ~= nil and save_words ~= "" and save_words ~= "NULL" then - check_save = true + -- 过滤的关键词列表 + local filter_word = split(filter_words, "/") + -- 保留的关键词列表 + local check_save = false + if save_words ~= nil and save_words ~= "" and save_words ~= "NULL" then + check_save = true + end + local save_word = split(save_words, "/") + + -- 检查结果 + local filter_result = false + local save_result = true + + -- 检查是否存在过滤关键词 + for i, v in pairs(filter_word) do + if tostring(result.alias):find(v, nil, true) then + filter_result = true end - local save_word = split(save_words, "/") + end - -- 检查结果 - local filter_result = false - local save_result = true - - -- 检查是否存在过滤关键词 - for i, v in pairs(filter_word) do + -- 检查是否打开了保留关键词检查,并且进行过滤 + if check_save == true then + for i, v in pairs(save_word) do if tostring(result.alias):find(v, nil, true) then - filter_result = true + save_result = false end end + else + save_result = false + end - -- 检查是否打开了保留关键词检查,并且进行过滤 - if check_save == true then - for i, v in pairs(save_word) do - if tostring(result.alias):find(v, nil, true) then - save_result = false - end - end - else - save_result = false - end - - -- 不等时返回 - if filter_result == true or save_result == true then - return true - else - return false - end + -- 不等时返回 + if filter_result == true or save_result == true then + return true + else + return false end end @@ -1410,131 +1421,154 @@ local function loadOldNodes(groupHash) end local execute = function() - -- exec - do - --local updated = false - local service_stopped = false - for k, url in ipairs(subscribe_url) do - local raw, new_md5 = curl(url) - log("raw 长度: "..#raw) - local groupHash = md5(url) - local old_md5 = read_old_md5(groupHash) + local updated = false + local service_stopped = false + for k, url in ipairs(subscribe_url) do + local raw, new_md5 = curl(url) + log("raw 长度: "..#raw) + local groupHash = md5(url) + local old_md5 = read_old_md5(groupHash) - log("处理订阅: " .. url) - log("groupHash: " .. groupHash) - log("old_md5: " .. tostring(old_md5)) - log("new_md5: " .. tostring(new_md5)) + log("处理订阅: " .. url) + log("groupHash: " .. groupHash) + log("old_md5: " .. tostring(old_md5)) + log("new_md5: " .. tostring(new_md5)) - if #raw > 0 then - if old_md5 and new_md5 == old_md5 then - log("订阅未变化, 跳过无需更新的订阅: " .. url) - -- 防止 diff 阶段误删未更新订阅节点 - loadOldNodes(groupHash) - --ucic:foreach(name, uciType, function(s) - -- if s.grouphashkey == groupHash and s.hashkey then - -- cache[groupHash][s.hashkey] = s - -- tinsert(nodeResult[index], s) - -- end - --end) - else - updated = true - -- 保存更新后的 MD5 值到以 groupHash 为标识的临时文件中,用于下次订阅更新时进行对比 - write_new_md5(groupHash, new_md5) - - -- 暂停服务(仅当 MD5 有变化时才执行) - if proxy == '0' and not service_stopped then - log('服务正在暂停') - luci.sys.init.stop(name) - service_stopped = true - end - - cache[groupHash] = {} - tinsert(nodeResult, {}) - local index = #nodeResult - local nodes, szType - - -- SSD 似乎是这种格式 ssd:// 开头的 - if raw:find('ssd://') then - szType = 'ssd' - local nEnd = select(2, raw:find('ssd://')) - nodes = base64Decode(raw:sub(nEnd + 1, #raw)) - nodes = jsonParse(nodes) - local extra = { - airport = nodes.airport, - port = nodes.port, - encryption = nodes.encryption, - password = nodes.password - } - local servers = {} - -- SS里面包着 干脆直接这样 - for _, server in ipairs(nodes.servers or {}) do - tinsert(servers, setmetatable(server, {__index = extra})) - end - nodes = servers - -- SS SIP008 直接使用 Json 格式 - elseif jsonParse(raw) then - nodes = jsonParse(raw).servers or jsonParse(raw) - if nodes[1] and nodes[1].server and nodes[1].method then - szType = 'sip008' - end - -- 其他 base64 格式 - else - -- ssd 外的格式 - nodes = split(base64Decode(raw):gsub("\r\n", "\n"), "\n") - end - for _, v in ipairs(nodes) do - if v and not string.match(v, "^%s*$") then - xpcall(function() - local result - if szType then - result = processData(szType, v) - elseif not szType then - local node = trim(v) - -- 一些奇葩的链接用"&"、"<"当做"&","#"前后带空格 - local link = node:gsub("&[a-zA-Z]+;", "&"):gsub("%s*#%s*", "#") - local dat = split(link, "://") - if dat and dat[1] and dat[2] then - local dat3 = "" - if dat[3] then - dat3 = "://" .. dat[3] - end - if dat[1] == 'ss' or dat[1] == 'trojan' or dat[1] == 'tuic' then - result = processData(dat[1], dat[2] .. dat3) - else - result = processData(dat[1], base64Decode(dat[2])) - end - end - else - log('跳过未知类型: ' .. szType) - end - -- log(result) - if result then - -- 中文做地址的 也没有人拿中文域名搞,就算中文域也有Puny Code SB 机场 - if not result.server or not result.server_port - or result.server == "127.0.0.1" - or result.alias == "NULL" - or check_filer(result) - or result.server:match("[^0-9a-zA-Z%-_%.%s]") - or cache[groupHash][result.hashkey] - then - log('丢弃无效节点: ' .. result.alias) - else - -- log('成功解析: ' .. result.type ..' 节点, ' .. result.alias) - result.grouphashkey = groupHash - tinsert(nodeResult[index], result) - cache[groupHash][result.hashkey] = nodeResult[index][#nodeResult[index]] - end - end - end, function(err) - log(string.format("解析节点出错: %s\n原始数据: %s", tostring(err), tostring(v))) - end) - end - end - log('成功解析节点数量: ' .. #nodes) - end + if #raw > 0 then + if old_md5 and new_md5 == old_md5 then + log("订阅未变化, 跳过无需更新的订阅: " .. url) + -- 防止 diff 阶段误删未更新订阅节点 + loadOldNodes(groupHash) + --ucic:foreach(name, uciType, function(s) + -- if s.grouphashkey == groupHash and s.hashkey then + -- cache[groupHash][s.hashkey] = s + -- tinsert(nodeResult[index], s) + -- end + --end) else - log(url .. ': 获取内容为空') + updated = true + -- 保存更新后的 MD5 值到以 groupHash 为标识的临时文件中,用于下次订阅更新时进行对比 + write_new_md5(groupHash, new_md5) + + -- 暂停服务(仅当 MD5 有变化时才执行) + if proxy == '0' and not service_stopped then + log('服务正在暂停') + luci.sys.init.stop(name) + service_stopped = true + end + + cache[groupHash] = {} + tinsert(nodeResult, {}) + local index = #nodeResult + local nodes, szType + + -- SSD 似乎是这种格式 ssd:// 开头的 + if raw:find('ssd://') then + szType = 'ssd' + local nEnd = select(2, raw:find('ssd://')) + nodes = base64Decode(raw:sub(nEnd + 1, #raw)) + nodes = jsonParse(nodes) + local extra = { + airport = nodes.airport, + port = nodes.port, + encryption = nodes.encryption, + password = nodes.password + } + local servers = {} + -- SS里面包着 干脆直接这样 + for _, server in ipairs(nodes.servers or {}) do + tinsert(servers, setmetatable(server, {__index = extra})) + end + nodes = servers + -- SS SIP008 直接使用 Json 格式 + elseif jsonParse(raw) then + nodes = jsonParse(raw).servers or jsonParse(raw) + if nodes[1] and nodes[1].server and nodes[1].method then + szType = 'sip008' + end + -- 其他 base64 格式 + else + -- ssd 外的格式 + nodes = split(base64Decode(raw):gsub("\r\n", "\n"), "\n") + end + + -- 临时存储该订阅解析出的节点(带原始别名) + local groupRawNodes = {} + + for _, v in ipairs(nodes) do + if v and not string.match(v, "^%s*$") then + xpcall(function() + local result + if szType then + result = processData(szType, v) + elseif not szType then + local node = trim(v) + -- 一些奇葩的链接用"&"、"<"当做"&","#"前后带空格 + local link = node:gsub("&[a-zA-Z]+;", "&"):gsub("%s*#%s*", "#") + local dat = split(link, "://") + if dat and dat[1] and dat[2] then + local dat3 = "" + if dat[3] then + dat3 = "://" .. dat[3] + end + if dat[1] == 'ss' or dat[1] == 'trojan' or dat[1] == 'tuic' then + result = processData(dat[1], dat[2] .. dat3) + else + result = processData(dat[1], base64Decode(dat[2])) + end + end + else + log('跳过未知类型: ' .. szType) + end + -- log(result) + if result then + -- 中文做地址的 也没有人拿中文域名搞,就算中文域也有Puny Code SB 机场 + if not result.server or not result.server_port + or result.server == "127.0.0.1" + or result.alias == "NULL" + or check_filer(result) + or result.server:match("[^0-9a-zA-Z%-_%.%s]") + or cache[groupHash][result.hashkey] then + log('丢弃无效节点: ' .. result.alias) + else + -- 暂存节点 + table.insert(groupRawNodes, result) + end + end + end, function(err) + log(string.format("解析节点出错: %s\n原始数据: %s", tostring(err), tostring(v))) + end) + end + end + + -- 对该组节点进行别名编号:重复节点加后缀,唯一节点不加 + local freq = {} + for _, node in ipairs(groupRawNodes) do + local raw = node.raw_alias or "" + freq[raw] = (freq[raw] or 0) + 1 + end + local aliasCount = {} + for _, node in ipairs(groupRawNodes) do + local raw = node.raw_alias or "" + if freq[raw] > 1 then + local count = (aliasCount[raw] or 0) + 1 + aliasCount[raw] = count + node.alias = raw .. "_" .. count + else + node.alias = raw + end + -- 清理临时字段 + node.raw_alias = nil + -- 存入 nodeResult + node.grouphashkey = groupHash + table.insert(nodeResult[index], node) + cache[groupHash][node.hashkey] = node + end + + log('成功解析节点数量: ' .. #groupRawNodes) end + else + log(url .. ': 获取内容为空') end end -- 输出日志并判断是否需要进行 diff @@ -1543,106 +1577,105 @@ local execute = function() log('保留手动添加的节点。') return end - -- diff - do - if next(nodeResult) == nil then - log("更新失败,没有可用的节点信息") - if proxy == '0' then - luci.sys.init.start(name) - log('订阅失败, 恢复服务') - end - return - end - local add, del = 0, 0 - ucic:foreach(name, uciType, function(old) - if old.grouphashkey or old.hashkey then -- 没有 hash 的不参与删除 - if not nodeResult[old.grouphashkey] or not nodeResult[old.grouphashkey][old.hashkey] then - ucic:delete(name, old['.name']) - del = del + 1 - else - local dat = nodeResult[old.grouphashkey][old.hashkey] - ucic:tset(name, old['.name'], dat) - -- 标记一下 - setmetatable(nodeResult[old.grouphashkey][old.hashkey], {__index = {_ignore = true}}) - end - else - if not old.alias then - if old.server or old.server_port then - old.alias = old.server .. ':' .. old.server_port - log('忽略手动添加的节点: ' .. old.alias) - else - ucic:delete(name, old['.name']) - end - else - log('忽略手动添加的节点: ' .. old.alias) - end - end - end) - -- 1583-1620 行为生成 sid - -- 记录已使用编号 - local used_sid = {} - local next_sid = 1 - -- 扫描已有 section - ucic:foreach(name, uciType, function(s) - local num = s[".name"]:match("^cfg(%x%x)") -- 提取两位十六进制序号 - if num then - local n = tonumber(num, 16) - used_sid[n] = true - end - end) - -- 获取下一个可用编号(O(1)) - local function get_next_sid() - while used_sid[next_sid] do - next_sid = next_sid + 1 - end - used_sid[next_sid] = true - return next_sid - end - for _, v in ipairs(nodeResult) do - for _, vv in ipairs(v) do - if not vv._ignore then - local sid = ucic:add(name, uciType) - if sid then - local suffix = sid:sub(-4) - ucic:delete(name, sid) - local id = get_next_sid() - local cfgid = string.format("cfg%02x%s", id, suffix) - local section = ucic:section(name, uciType, cfgid) - if section then - ucic:tset(name, section, vv) - ucic:set(name, section, "switch_enable", switch) - add = add + 1 - end - end - end - end + -- diff 阶段 + if next(nodeResult) == nil then + log("更新失败,没有可用的节点信息") + if proxy == '0' then + luci.sys.init.start(name) + log('订阅失败, 恢复服务') end - ucic:commit(name) - -- 如果原有服务器节点已经不见了就尝试换为第一个节点 - local globalServer = ucic:get_first(name, 'global', 'global_server', '') - if globalServer ~= "nil" then - local firstServer = ucic:get_first(name, uciType) - if firstServer then - if not ucic:get(name, globalServer) then - luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &") - ucic:commit(name) - ucic:set(name, ucic:get_first(name, 'global'), 'global_server', firstServer) - ucic:commit(name) - log('当前主服务器节点已被删除,正在自动更换为第一个节点。') - luci.sys.call("/etc/init.d/" .. name .. " start > /dev/null 2>&1 &") + return + end + local add, del = 0, 0 + ucic:foreach(name, uciType, function(old) + if old.grouphashkey or old.hashkey then -- 没有 hash 的不参与删除 + if not nodeResult[old.grouphashkey] or not nodeResult[old.grouphashkey][old.hashkey] then + ucic:delete(name, old['.name']) + del = del + 1 + else + local dat = nodeResult[old.grouphashkey][old.hashkey] + ucic:tset(name, old['.name'], dat) + -- 标记一下 + setmetatable(nodeResult[old.grouphashkey][old.hashkey], {__index = {_ignore = true}}) + end + else + if not old.alias then + if old.server or old.server_port then + old.alias = old.server .. ':' .. old.server_port + log('忽略手动添加的节点: ' .. old.alias) else - log('维持当前主服务器节点。') - luci.sys.call("/etc/init.d/" .. name .. " restart > /dev/null 2>&1 &") + ucic:delete(name, old['.name']) end else - log('没有服务器节点了,停止服务') - luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &") + log('忽略手动添加的节点: ' .. old.alias) end end - log('新增节点数量: ' .. add .. ', 删除节点数量: ' .. del) - log('订阅更新成功') + end) + -- 1615-1653 行为生成 sid + -- 记录已使用编号 + local used_sid = {} + local next_sid = 1 + -- 扫描已有 section + ucic:foreach(name, uciType, function(s) + local num = s[".name"]:match("^cfg(%x%x)") -- 提取两位十六进制序号 + if num then + local n = tonumber(num, 16) + used_sid[n] = true + end + end) + -- 获取下一个可用编号(O(1)) + local function get_next_sid() + while used_sid[next_sid] do + next_sid = next_sid + 1 + end + used_sid[next_sid] = true + return next_sid end + + for _, v in ipairs(nodeResult) do + for _, vv in ipairs(v) do + if not vv._ignore then + local sid = ucic:add(name, uciType) + if sid then + local suffix = sid:sub(-4) + ucic:delete(name, sid) + local id = get_next_sid() + local cfgid = string.format("cfg%02x%s", id, suffix) + local section = ucic:section(name, uciType, cfgid) + if section then + ucic:tset(name, section, vv) + ucic:set(name, section, "switch_enable", switch) + add = add + 1 + end + end + end + end + end + ucic:commit(name) + -- 如果原有服务器节点已经不见了就尝试换为第一个节点 + local globalServer = ucic:get_first(name, 'global', 'global_server', '') + if globalServer ~= "nil" then + local firstServer = ucic:get_first(name, uciType) + if firstServer then + if not ucic:get(name, globalServer) then + luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &") + ucic:commit(name) + ucic:set(name, ucic:get_first(name, 'global'), 'global_server', firstServer) + ucic:commit(name) + log('当前主服务器节点已被删除,正在自动更换为第一个节点。') + luci.sys.call("/etc/init.d/" .. name .. " start > /dev/null 2>&1 &") + else + log('维持当前主服务器节点。') + luci.sys.call("/etc/init.d/" .. name .. " restart > /dev/null 2>&1 &") + end + else + log('没有服务器节点了,停止服务') + luci.sys.call("/etc/init.d/" .. name .. " stop > /dev/null 2>&1 &") + end + end + log('新增节点数量: ' .. add .. ', 删除节点数量: ' .. del) + log('订阅更新成功') end if subscribe_url and #subscribe_url > 0 then diff --git a/luci-theme-alpha/Makefile b/luci-theme-alpha/Makefile index 4076f5c9..5ccc03cc 100644 --- a/luci-theme-alpha/Makefile +++ b/luci-theme-alpha/Makefile @@ -11,7 +11,7 @@ THEME_TITLE:=Alpha PKG_NAME:=luci-theme-$(THEME_NAME) PKG_VERSION:=3.9.7_beta -PKG_RELEASE:=3 +PKG_RELEASE:=4 include $(INCLUDE_DIR)/package.mk @@ -19,7 +19,7 @@ define Package/luci-theme-$(THEME_NAME) SECTION:=luci CATEGORY:=LuCI SUBMENU:=9. Themes - DEPENDS:=+libc + DEPENDS:=+libc +luci-app-alpha-config TITLE:=LuCi Theme For OpenWrt And Alpha OS ONLY - $(THEME_TITLE) URL:=http://facebook.com/derisamedia/ PKGARCH:=all diff --git a/luci-theme-alpha/template/footer.ut b/luci-theme-alpha/template/footer.ut index f6740eb1..efaa4da0 100644 --- a/luci-theme-alpha/template/footer.ut +++ b/luci-theme-alpha/template/footer.ut @@ -54,7 +54,7 @@