diff --git a/luci-app-bandix/Makefile b/luci-app-bandix/Makefile index 55a203e..54f8fcb 100644 --- a/luci-app-bandix/Makefile +++ b/luci-app-bandix/Makefile @@ -10,7 +10,7 @@ LUCI_DEPENDS:=+luci-base +luci-lib-jsonc +curl +bandix PKG_MAINTAINER:=timsaya -PKG_VERSION:=0.8.0 +PKG_VERSION:=0.8.1 PKG_RELEASE:=1 include $(TOPDIR)/feeds/luci/luci.mk diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/dns.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/dns.js index 1b81dfc..8ef7423 100644 --- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/dns.js +++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/dns.js @@ -570,11 +570,11 @@ function formatDnsServer(query) { function formatResponseResult(query) { if (!query) return { display: [], full: [] }; - // 显示响应IP(response_ips),它是一个字符串数组 - if (query.response_ips && Array.isArray(query.response_ips) && query.response_ips.length > 0) { + // 显示响应记录(response_records),它是一个字符串数组 + if (query.response_records && Array.isArray(query.response_records) && query.response_records.length > 0) { var maxDisplay = 5; // 最多显示5条 - var displayRecords = query.response_ips.slice(0, maxDisplay); - var fullRecords = query.response_ips; + var displayRecords = query.response_records.slice(0, maxDisplay); + var fullRecords = query.response_records; return { display: displayRecords, @@ -1113,20 +1113,25 @@ return view.extend({ ]), E('div', { 'class': 'stats-card' }, [ E('div', { 'class': 'stats-card-title' }, getTranslation('响应时间', language)), - E('div', { 'class': 'stats-card-value', 'id': 'stat-avg-response-time', 'style': 'margin-bottom: 12px;' }, [ - E('span', {}, (stats.avg_response_time_ms || 0).toFixed(1)), + E('div', { 'class': 'stats-card-value', 'id': 'stat-latest-response-time', 'style': 'margin-bottom: 12px;' }, [ + E('span', {}, (stats.latest_response_time_ms || 0).toFixed(1)), E('span', { 'class': 'stats-card-unit' }, ' ' + getTranslation('毫秒', language)) ]), E('div', { 'class': 'stats-card-details', 'id': 'stat-response-time-details' }, [ + E('div', { 'class': 'stats-detail-row' }, [ + E('span', { 'class': 'stats-detail-label' }, getTranslation('平均响应时间', language) + ':'), + E('span', { 'class': 'stats-detail-value', 'id': 'stat-avg-response-time' }, + (stats.avg_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language)) + ]), E('div', { 'class': 'stats-detail-row' }, [ E('span', { 'class': 'stats-detail-label' }, getTranslation('最快响应时间', language) + ':'), E('span', { 'class': 'stats-detail-value', 'id': 'stat-min-response-time' }, - (stats.min_response_time_ms || 0) + ' ' + getTranslation('毫秒', language)) + (stats.min_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language)) ]), E('div', { 'class': 'stats-detail-row' }, [ E('span', { 'class': 'stats-detail-label' }, getTranslation('最慢响应时间', language) + ':'), E('span', { 'class': 'stats-detail-value', 'id': 'stat-max-response-time' }, - (stats.max_response_time_ms || 0) + ' ' + getTranslation('毫秒', language)) + (stats.max_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language)) ]) ]) ]), @@ -1157,19 +1162,24 @@ return view.extend({ totalQueriesEl.textContent = stats.total_queries || 0; } + var latestResponseTimeEl = document.getElementById('stat-latest-response-time'); + if (latestResponseTimeEl && latestResponseTimeEl.firstChild) { + latestResponseTimeEl.firstChild.textContent = (stats.latest_response_time_ms || 0).toFixed(1); + } + var avgResponseTimeEl = document.getElementById('stat-avg-response-time'); - if (avgResponseTimeEl && avgResponseTimeEl.firstChild) { - avgResponseTimeEl.firstChild.textContent = (stats.avg_response_time_ms || 0).toFixed(1); + if (avgResponseTimeEl) { + avgResponseTimeEl.textContent = (stats.avg_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language); } var minResponseTimeEl = document.getElementById('stat-min-response-time'); if (minResponseTimeEl) { - minResponseTimeEl.textContent = (stats.min_response_time_ms || 0) + ' ' + getTranslation('毫秒', language); + minResponseTimeEl.textContent = (stats.min_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language); } var maxResponseTimeEl = document.getElementById('stat-max-response-time'); if (maxResponseTimeEl) { - maxResponseTimeEl.textContent = (stats.max_response_time_ms || 0) + ' ' + getTranslation('毫秒', language); + maxResponseTimeEl.textContent = (stats.max_response_time_ms || 0).toFixed(1) + ' ' + getTranslation('毫秒', language); } var successRateEl = document.getElementById('stat-success-rate'); diff --git a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js index 7e931f5..fbc24da 100644 --- a/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js +++ b/luci-app-bandix/htdocs/luci-static/resources/view/bandix/settings.js @@ -41,6 +41,8 @@ const translations = { '明亮模式': '明亮模式', '暗黑模式': '暗黑模式', '意见反馈': '意见反馈', + '日志级别': '日志级别', + '设置 Bandix 服务的日志级别': '设置 Bandix 服务的日志级别', '离线超时时间': '离线超时时间', '设置设备离线判断的超时时间(秒)': '设置设备离线判断的超时时间(秒)。超过此时间未活动的设备将被标记为离线', '历史流量周期': '历史流量周期', @@ -100,6 +102,8 @@ const translations = { '明亮模式': '明亮模式', '暗黑模式': '暗黑模式', '意见反馈': '意見反饋', + '日志级别': '日誌級別', + '设置 Bandix 服务的日志级别': '設置 Bandix 服務的日誌級別', '离线超时时间': '離線超時時間', '设置设备离线判断的超时时间(秒)': '設定設備離線判斷的超時時間(秒)。超過此時間未活動的設備將被標記為離線', '历史流量周期': '歷史流量週期', @@ -160,6 +164,8 @@ const translations = { '明亮模式': 'Light Mode', '暗黑模式': 'Dark Mode', '意见反馈': 'Feedback', + '日志级别': 'Log Level', + '设置 Bandix 服务的日志级别': 'Set the log level for Bandix service', '离线超时时间': 'Offline Timeout', '设置设备离线判断的超时时间(秒)': 'Set the timeout for device offline detection (seconds). Devices inactive for longer than this time will be marked as offline', '历史流量周期': 'Traffic History Period', @@ -220,6 +226,8 @@ const translations = { '明亮模式': 'Mode Clair', '暗黑模式': 'Mode Sombre', '意见反馈': 'Commentaires', + '日志级别': 'Niveau de Journal', + '设置 Bandix 服务的日志级别': 'Définir le niveau de journal pour le service Bandix', '离线超时时间': 'Délai d\'expiration hors ligne', '设置设备离线判断的超时时间(秒)': 'Définir le délai d\'expiration pour la détection hors ligne des appareils (secondes). Les appareils inactifs plus longtemps que cette durée seront marqués comme hors ligne', '历史流量周期': 'Période d\'Historique du Trafic', @@ -280,6 +288,8 @@ const translations = { '明亮模式': 'ライトモード', '暗黑模式': 'ダークモード', '意见反馈': 'フィードバック', + '日志级别': 'ログレベル', + '设置 Bandix 服务的日志级别': 'Bandix サービスのログレベルを設定', '离线超时时间': 'オフラインタイムアウト', '设置设备离线判断的超时时间(秒)': 'デバイスのオフライン検出のタイムアウト時間(秒)を設定。この時間を超えて非アクティブなデバイスはオフラインとしてマークされます', '历史流量周期': 'トラフィック履歴期間', @@ -340,6 +350,8 @@ const translations = { '明亮模式': 'Светлый Режим', '暗黑模式': 'Темный Режим', '意见反馈': 'Обратная связь', + '日志级别': 'Уровень Журналирования', + '设置 Bandix 服务的日志级别': 'Установить уровень журналирования для службы Bandix', '离线超时时间': 'Таймаут отключения', '设置设备离线判断的超时时间(秒)': 'Установить таймаут для обнаружения отключения устройств (секунды). Устройства, неактивные дольше этого времени, будут помечены как отключенные', '历史流量周期': 'Период Истории Трафика', @@ -591,13 +603,14 @@ return view.extend({ s.description = getTranslation('配置 Bandix 服务的基本参数', language); s.addremove = false; - // 添加端口设置选项 - o = s.option(form.Value, 'port', getTranslation('端口', language), - getTranslation('Bandix 服务监听的端口', language)); - o.default = '8686'; - o.datatype = 'port'; - o.placeholder = '8686'; - o.rmempty = false; + // 添加端口设置选项 + o = s.option(form.Value, 'port', getTranslation('端口', language), + getTranslation('Bandix 服务监听的端口', language)); + o.default = '8686'; + o.datatype = 'port'; + o.placeholder = '8686'; + o.rmempty = false; + // 添加网卡选择下拉菜单 o = s.option(form.ListValue, 'iface', getTranslation('监控网卡', language), @@ -631,6 +644,18 @@ return view.extend({ o.default = 'auto'; o.rmempty = false; + + // 添加日志级别选择选项 + o = s.option(form.ListValue, 'log_level', getTranslation('日志级别', language), + getTranslation('设置 Bandix 服务的日志级别', language)); + o.value('trace', 'Trace'); + o.value('debug', 'Debug'); + o.value('info', 'Info'); + o.value('warn', 'Warn'); + o.value('error', 'Error'); + o.default = 'info'; + o.rmempty = false; + // 添加数据目录设置(只读) o = s.option(form.DummyValue, 'data_dir', getTranslation('数据目录', language)); o.default = '/usr/share/bandix'; diff --git a/luci-app-passwall/Makefile b/luci-app-passwall/Makefile index e9aecde..8a64bb4 100644 --- a/luci-app-passwall/Makefile +++ b/luci-app-passwall/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall -PKG_VERSION:=25.11.1 +PKG_VERSION:=25.11.14 PKG_RELEASE:=1 PKG_PO_VERSION:=$(PKG_VERSION) diff --git a/luci-app-passwall/root/usr/share/passwall/rule_update.lua b/luci-app-passwall/root/usr/share/passwall/rule_update.lua index f447280..705b8fc 100755 --- a/luci-app-passwall/root/usr/share/passwall/rule_update.lua +++ b/luci-app-passwall/root/usr/share/passwall/rule_update.lua @@ -109,9 +109,11 @@ end --check excluded domain local function check_excluded_domain(value) - for k,v in ipairs(excluded_domain) do - if value:find(v) then - return true + value = value and value:lower() or "" + for _, domain in ipairs(excluded_domain) do + local pattern = "^[a-z0-9-]+%.(" .. domain .. ")$" + if value:match(pattern) then + return true end end end diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile index be6b72a..733c0f9 100644 --- a/luci-app-passwall2/Makefile +++ b/luci-app-passwall2/Makefile @@ -5,7 +5,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall2 -PKG_VERSION:=25.11.2 +PKG_VERSION:=25.11.14 PKG_RELEASE:=1 PKG_PO_VERSION:=$(PKG_VERSION) diff --git a/luci-app-passwall2/luasrc/controller/passwall2.lua b/luci-app-passwall2/luasrc/controller/passwall2.lua index 7f3fd81..7ed0d91 100644 --- a/luci-app-passwall2/luasrc/controller/passwall2.lua +++ b/luci-app-passwall2/luasrc/controller/passwall2.lua @@ -129,7 +129,7 @@ function hide_menu() end function link_add_node() - -- 分片接收以突破uhttpd的限制 + -- Fragmented reception to overcome uhttpd limitations local tmp_file = "/tmp/links.conf" local chunk = http.formvalue("chunk") local chunk_index = tonumber(http.formvalue("chunk_index")) @@ -137,7 +137,7 @@ function link_add_node() local group = http.formvalue("group") or "default" if chunk and chunk_index ~= nil and total_chunks ~= nil then - -- 按顺序拼接到文件 + -- Assemble the files in order local mode = "a" if chunk_index == 0 then mode = "w" @@ -147,7 +147,7 @@ function link_add_node() f:write(chunk) f:close() end - -- 如果是最后一片,才执行 + -- If it's the last piece, then it will be executed. if chunk_index + 1 == total_chunks then luci.sys.call("lua /usr/share/passwall2/subscribe.lua add " .. group) end @@ -624,7 +624,7 @@ function restore_backup() fp:close() if chunk_index + 1 == total_chunks then api.sys.call("echo '' > /tmp/log/passwall2.log") - api.log(" * PassWall2 配置文件上传成功…") + api.log(string.format(" * PassWall2 %s", i18n.translate("Configuration file uploaded successfully…"))) local temp_dir = '/tmp/passwall2_bak' api.sys.call("mkdir -p " .. temp_dir) if api.sys.call("tar -xzf " .. file_path .. " -C " .. temp_dir) == 0 then @@ -634,13 +634,13 @@ function restore_backup() api.sys.call("cp -f " .. temp_file .. " " .. backup_file) end end - api.log(" * PassWall2 配置还原成功…") - api.log(" * 重启 PassWall2 服务中…\n") + api.log(string.format(" * PassWall2 %s", i18n.translate("Configuration restored successfully…"))) + api.log(string.format(" * PassWall2 %s", i18n.translate("Service restarting…"))) luci.sys.call('/etc/init.d/passwall2 restart > /dev/null 2>&1 &') luci.sys.call('/etc/init.d/passwall2_server restart > /dev/null 2>&1 &') result = { status = "success", message = "Upload completed", path = file_path } else - api.log(" * PassWall2 配置文件解压失败,请重试!") + api.log(string.format(" * PassWall2 %s", i18n.translate("Configuration file decompression failed, please try again!"))) result = { status = "error", message = "Decompression failed" } end api.sys.call("rm -rf " .. temp_dir) @@ -746,7 +746,7 @@ function subscribe_manual_all() end local section_list = util.split(sections, ",") local url_list = util.split(urls, ",") - -- 检查是否存在未保存配置 + -- Check if there are any unsaved configurations. for i, section in ipairs(section_list) do local uci_url = api.sh_uci_get(appname, section, "url") if not uci_url or uci_url == "" then @@ -754,7 +754,7 @@ function subscribe_manual_all() return end end - -- 保存有变动的url + -- Save URLs that have changed. for i, section in ipairs(section_list) do local current_url = url_list[i] or "" local uci_url = api.sh_uci_get(appname, section, "url") diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua index 1de06d8..d3e54bc 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua @@ -40,7 +40,7 @@ m.uci:foreach(appname, "socks", function(s) if s.enabled == "1" and s.node then socks_list[#socks_list + 1] = { id = "Socks_" .. s[".name"], - remark = translate("Socks Config") .. " [" .. s.port .. "端口]" + remark = translate("Socks Config") .. " [" .. s.port .. translate("Port") .. "]" } end end) @@ -84,7 +84,7 @@ o.rmempty = false o = s:taboption("Main", ListValue, "node", "" .. translate("Node") .. "") o:value("", translate("Close")) --- 分流 +-- Shunt if (has_singbox or has_xray) and #nodes_table > 0 then local function get_cfgvalue(shunt_node_id, option) return function(self, section) @@ -152,7 +152,7 @@ if (has_singbox or has_xray) and #nodes_table > 0 then if (has_singbox and has_xray) or (v.type == "sing-box" and not has_singbox) or (v.type == "Xray" and not has_xray) then type:depends("node", v.id) else - type:depends({ __hide = true }) --不存在的依赖,即始终隐藏 + type:depends({ __hide = true }) -- Always hidden. end m.uci:foreach(appname, "shunt_rules", function(e) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua index 010498c..fd128e1 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua @@ -36,6 +36,21 @@ end) for k, v in pairs(groups) do o:value(k) end +o.write = function(self, section, value) + value = api.trim(value) + local lower = value:lower() + + if lower == "" or lower == "default" then + return m:del(section, self.option) + end + + for _, v in ipairs(self.keylist or {}) do + if v:lower() == lower then + return m:set(section, self.option, v) + end + end + m:set(section, self.option, value) +end local fs = require "nixio.fs" local types_dir = "/usr/lib/lua/luci/model/cbi/passwall2/client/type/" diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua index fd65d0b..e89a67d 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua @@ -168,19 +168,33 @@ end o = s:option(Value, "remark", translate("Remarks")) o.width = "auto" o.rmempty = false -o.validate = function(self, value, t) - if value then - local count = 0 - m.uci:foreach(appname, "subscribe_list", function(e) - if e[".name"] ~= t and e["remark"] == value then - count = count + 1 +o.validate = function(self, value, section) + value = api.trim(value) + if value == "" then + return nil, translate("Remark cannot be empty.") + end + local duplicate = false + m.uci:foreach(appname, "subscribe_list", function(e) + if e[".name"] ~= section and e["remark"] and e["remark"]:lower() == value:lower() then + duplicate = true + return false + end + end) + if duplicate or value:lower() == "default" then + return nil, translate("This remark already exists, please change a new remark.") + end + return value +end +o.write = function(self, section, value) + local old = m:get(section, self.option) or "" + if old:lower() ~= value:lower() then + m.uci:foreach(appname, "nodes", function(e) + if e["group"] and e["group"]:lower() == old:lower() then + m.uci:set(appname, e[".name"], "group", value) end end) - if count > 0 then - return nil, translate("This remark already exists, please change a new remark.") - end - return value end + return Value.write(self, section, value) end o = s:option(DummyValue, "_node_count", translate("Subscribe Info")) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua index df392e3..214f824 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua @@ -74,13 +74,41 @@ end o = s:option(Value, "remark", translate("Subscribe Remark")) o.rmempty = false +o.validate = function(self, value, section) + value = api.trim(value) + if value == "" then + return nil, translate("Remark cannot be empty.") + end + local duplicate = false + m.uci:foreach(appname, "subscribe_list", function(e) + if e[".name"] ~= section and e["remark"] and e["remark"]:lower() == value:lower() then + duplicate = true + return false + end + end) + if duplicate or value:lower() == "default" then + return nil, translate("This remark already exists, please change a new remark.") + end + return value +end +o.write = function(self, section, value) + local old = m:get(section, self.option) or "" + if old:lower() ~= value:lower() then + m.uci:foreach(appname, "nodes", function(e) + if e["group"] and e["group"]:lower() == old:lower() then + m.uci:set(appname, e[".name"], "group", value) + end + end) + end + return Value.write(self, section, value) +end o = s:option(TextValue, "url", translate("Subscribe URL")) o.rows = 5 o.rmempty = false o.validate = function(self, value) if not value or value == "" then - return nil, translate("URL cannot be empty") + return nil, translate("URL cannot be empty.") end return value:gsub("%s+", ""):gsub("%z", "") end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua index 092f8af..53cf903 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/other.lua @@ -119,7 +119,7 @@ if (os.execute("lsmod | grep -i REDIRECT >/dev/null") == 0 and os.execute("lsmod o:value("tproxy", "TPROXY") o:depends("ipv6_tproxy", false) o.remove = function(self, section) - -- 禁止在隐藏时删除 + -- Do not delete while hidden end o = s:option(ListValue, "_tcp_proxy_way", translate("TCP Proxy Way")) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua index d6237bb..b021a1b 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua @@ -27,8 +27,8 @@ div[id^="cbid.passwall2."] .cbi-checkbox { ]] function clean_text(text) - local nbsp = string.char(0xC2, 0xA0) -- 不间断空格(U+00A0) - local fullwidth_space = string.char(0xE3, 0x80, 0x80) -- 全角空格(U+3000) + local nbsp = string.char(0xC2, 0xA0) -- Non-breaking space (U+00A0) + local fullwidth_space = string.char(0xE3, 0x80, 0x80) -- Full-width space (U+3000) return text :gsub("\t", " ") :gsub(nbsp, " ") diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua index 9f26218..fbd6aab 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua @@ -95,12 +95,12 @@ m.uci:foreach(appname, "socks", function(s) if s.enabled == "1" and s.node then socks_list[#socks_list + 1] = { id = "Socks_" .. s[".name"], - remark = translate("Socks Config") .. " [" .. s.port .. "端口]" + remark = translate("Socks Config") .. " [" .. s.port .. translate("Port") .. "]" } end end) --- 负载均衡列表 +-- Load balancing node list o = s:option(DynamicList, _n("balancing_node"), translate("Load balancing node list"), translate("Load balancing node list, document")) o:depends({ [_n("protocol")] = "_balancing" }) local valid_ids = {} @@ -108,7 +108,7 @@ for k, v in pairs(nodes_table) do o:value(v.id, v.remark) valid_ids[v.id] = true end --- 去重并禁止自定义非法输入 +-- Deduplication and disabling of custom and illegal input function o.custom_write(self, section, value) local result = {} if type(value) == "table" then @@ -145,14 +145,13 @@ local function check_fallback_chain(fb) end end end --- 检查fallback链,去掉会形成闭环的balancer节点 +-- Check the fallback chain and remove the balancer node that would form a closed loop. if is_balancer then check_fallback_chain(arg[1]) end for k, v in pairs(fallback_table) do o:value(v.id, v.remark) end for k, v in pairs(nodes_table) do o:value(v.id, v.remark) end --- 探测地址 o = s:option(Flag, _n("useCustomProbeUrl"), translate("Use Custom Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL.")) o:depends({ [_n("protocol")] = "_balancing" }) @@ -167,7 +166,6 @@ o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud o.default = "https://www.google.com/generate_204" o.description = translate("The URL used to detect the connection status.") --- 探测间隔 o = s:option(Value, _n("probeInterval"), translate("Probe Interval")) o:depends({ [_n("protocol")] = "_balancing" }) o.default = "1m" @@ -184,7 +182,7 @@ o.placeholder = "2" o.description = translate("The load balancer selects the optimal number of nodes, and traffic is randomly distributed among them.") --- [[ 分流模块 ]] +-- [[ Shunt Start ]] if #nodes_table > 0 then o = s:option(Flag, _n("preproxy_enabled"), translate("Preproxy")) o:depends({ [_n("protocol")] = "_shunt" }) @@ -285,7 +283,7 @@ o:value("hybrid") o:value("linear") o:depends({ [_n("protocol")] = "_shunt" }) --- [[ 分流模块 End ]] +-- [[ Shunt End ]] o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) @@ -412,7 +410,7 @@ o:value("half") o:value("full") o:depends({ [_n("ech")] = true }) --- [[ REALITY部分 ]] -- +-- [[ REALITY ]] -- o = s:option(Value, _n("reality_publicKey"), translate("Public Key")) o:depends({ [_n("tls")] = true, [_n("reality")] = true }) @@ -493,24 +491,21 @@ o = s:option(Value, _n("wireguard_keepAlive"), translate("Keep Alive")) o.default = "0" o:depends({ [_n("protocol")] = "wireguard" }) --- [[ RAW部分 ]]-- +-- [[ RAW ]]-- --- TCP伪装 o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type")) o:value("none", "none") o:value("http", "http") o:depends({ [_n("transport")] = "raw" }) --- HTTP域名 o = s:option(DynamicList, _n("tcp_guise_http_host"), translate("HTTP Host")) o:depends({ [_n("tcp_guise")] = "http" }) --- HTTP路径 o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path")) o.placeholder = "/" o:depends({ [_n("tcp_guise")] = "http" }) --- [[ mKCP部分 ]]-- +-- [[ mKCP ]]-- o = s:option(ListValue, _n("mkcp_guise"), translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)
dns: Disguising traffic as DNS requests.')) for a, t in ipairs(header_type_list) do o:value(t) end @@ -549,7 +544,7 @@ o:depends({ [_n("transport")] = "mkcp" }) o = s:option(Value, _n("mkcp_seed"), translate("KCP Seed")) o:depends({ [_n("transport")] = "mkcp" }) --- [[ WebSocket部分 ]]-- +-- [[ WebSocket ]]-- o = s:option(Value, _n("ws_host"), translate("WebSocket Host")) o:depends({ [_n("transport")] = "ws" }) @@ -561,7 +556,7 @@ o = s:option(Value, _n("ws_heartbeatPeriod"), translate("HeartbeatPeriod(second) o.datatype = "integer" o:depends({ [_n("transport")] = "ws" }) --- [[ gRPC部分 ]]-- +-- [[ gRPC ]]-- o = s:option(Value, _n("grpc_serviceName"), "ServiceName") o:depends({ [_n("transport")] = "grpc" }) @@ -589,7 +584,7 @@ o = s:option(Value, _n("grpc_initial_windows_size"), translate("Initial Windows o.default = "0" o:depends({ [_n("transport")] = "grpc" }) --- [[ HttpUpgrade部分 ]]-- +-- [[ HttpUpgrade ]]-- o = s:option(Value, _n("httpupgrade_host"), translate("HttpUpgrade Host")) o:depends({ [_n("transport")] = "httpupgrade" }) @@ -597,7 +592,7 @@ o = s:option(Value, _n("httpupgrade_path"), translate("HttpUpgrade Path")) o.placeholder = "/" o:depends({ [_n("transport")] = "httpupgrade" }) --- [[ XHTTP部分 ]]-- +-- [[ XHTTP ]]-- o = s:option(ListValue, _n("xhttp_mode"), "XHTTP " .. translate("Mode")) o:depends({ [_n("transport")] = "xhttp" }) o.default = "auto" diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua index 518cbf9..6f66673 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua @@ -105,7 +105,7 @@ m.uci:foreach(appname, "socks", function(s) if s.enabled == "1" and s.node then socks_list[#socks_list + 1] = { id = "Socks_" .. s[".name"], - remark = translate("Socks Config") .. " [" .. s.port .. "端口]" + remark = translate("Socks Config") .. " [" .. s.port .. translate("Port") .. "]" } end end) @@ -118,7 +118,7 @@ for k, v in pairs(nodes_table) do o:value(v.id, v.remark) valid_ids[v.id] = true end --- 去重并禁止自定义非法输入 +-- Deduplication and disabling of custom and illegal input function o.custom_write(self, section, value) local result = {} if type(value) == "table" then @@ -174,7 +174,7 @@ o:depends({ [_n("protocol")] = "_urltest" }) o.default = "0" o.description = translate("Interrupt existing connections when the selected outbound has changed.") --- [[ 分流模块 ]] +-- [[ Shunt Start ]] if #nodes_table > 0 then o = s:option(Flag, _n("preproxy_enabled"), translate("Preproxy")) o:depends({ [_n("protocol")] = "_shunt" }) @@ -259,7 +259,7 @@ if #nodes_table > 0 then end end --- [[ 分流模块 End ]] +-- [[ Shunt End ]] o = s:option(Value, _n("address"), translate("Address (Support Domain Name)")) @@ -587,7 +587,7 @@ if singbox_tags:find("with_utls") then o.default = "chrome" o:depends({ [_n("utls")] = true }) - -- [[ REALITY部分 ]] -- + -- [[ REALITY ]] -- o = s:option(Flag, _n("reality"), translate("REALITY")) o.default = 0 o:depends({ [_n("protocol")] = "vless", [_n("tls")] = true }) @@ -650,7 +650,7 @@ if singbox_tags:find("with_wireguard") then o:depends({ [_n("protocol")] = "wireguard" }) end --- [[ TCP部分(模拟) ]]-- +-- [[ TCP ]]-- o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type")) o:value("none", "none") o:value("http", "http") @@ -663,7 +663,7 @@ o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path")) o.placeholder = "/" o:depends({ [_n("tcp_guise")] = "http" }) --- [[ HTTP部分 ]]-- +-- [[ HTTP ]]-- o = s:option(DynamicList, _n("http_host"), translate("HTTP Host")) o:depends({ [_n("transport")] = "http" }) @@ -682,7 +682,7 @@ o = s:option(Value, _n("http_h2_health_check_timeout"), translate("Health check o.default = "15" o:depends({ [_n("tls")] = true, [_n("transport")] = "http", [_n("http_h2_health_check")] = true }) --- [[ WebSocket部分 ]]-- +-- [[ WebSocket ]]-- o = s:option(Value, _n("ws_host"), translate("WebSocket Host")) o:depends({ [_n("transport")] = "ws" }) @@ -700,7 +700,7 @@ o:depends({ [_n("ws_enableEarlyData")] = true }) o = s:option(Value, _n("ws_earlyDataHeaderName"), translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol")) o:depends({ [_n("ws_enableEarlyData")] = true }) --- [[ HTTPUpgrade部分 ]]-- +-- [[ HTTPUpgrade ]]-- o = s:option(Value, _n("httpupgrade_host"), translate("HTTPUpgrade Host")) o:depends({ [_n("transport")] = "httpupgrade" }) @@ -708,7 +708,7 @@ o = s:option(Value, _n("httpupgrade_path"), translate("HTTPUpgrade Path")) o.placeholder = "/" o:depends({ [_n("transport")] = "httpupgrade" }) --- [[ gRPC部分 ]]-- +-- [[ gRPC ]]-- o = s:option(Value, _n("grpc_serviceName"), "ServiceName") o:depends({ [_n("transport")] = "grpc" }) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua index f411caa..9c08ee4 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua @@ -147,7 +147,7 @@ o:depends({ [_n("protocol")] = "socks" }) o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ [_n("protocol")] = "trojan" }) --- [[ REALITY部分 ]] -- +-- [[ REALITY ]] -- o = s:option(Flag, _n("reality"), translate("REALITY")) o.default = 0 o:depends({ [_n("tls")] = true }) @@ -211,7 +211,7 @@ end -- o:value("1.3") --o:depends({ [_n("tls")] = true }) --- [[ TLS部分 ]] -- +-- [[ TLS ]] -- o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem" if o and o:formvalue(arg[1]) then o.default = o:formvalue(arg[1]) end @@ -268,14 +268,14 @@ o:depends({ [_n("protocol")] = "socks" }) o:depends({ [_n("protocol")] = "shadowsocks" }) o:depends({ [_n("protocol")] = "trojan" }) --- [[ WebSocket部分 ]]-- +-- [[ WebSocket ]]-- o = s:option(Value, _n("ws_host"), translate("WebSocket Host")) o:depends({ [_n("transport")] = "ws" }) o = s:option(Value, _n("ws_path"), translate("WebSocket Path")) o:depends({ [_n("transport")] = "ws" }) --- [[ HttpUpgrade部分 ]]-- +-- [[ HttpUpgrade ]]-- o = s:option(Value, _n("httpupgrade_host"), translate("HttpUpgrade Host")) o:depends({ [_n("transport")] = "httpupgrade" }) @@ -283,7 +283,7 @@ o = s:option(Value, _n("httpupgrade_path"), translate("HttpUpgrade Path")) o.placeholder = "/" o:depends({ [_n("transport")] = "httpupgrade" }) --- [[ XHTTP部分 ]]-- +-- [[ XHTTP ]]-- o = s:option(Value, _n("xhttp_host"), translate("XHTTP Host")) o:depends({ [_n("transport")] = "xhttp" }) @@ -307,23 +307,20 @@ o = s:option(Value, _n("splithttp_maxconcurrentuploads"), translate("maxConcurre o.default = "10" o:depends({ [_n("transport")] = "splithttp" }) --- [[ TCP部分 ]]-- +-- [[ TCP ]]-- --- TCP伪装 o = s:option(ListValue, _n("tcp_guise"), translate("Camouflage Type")) o:value("none", "none") o:value("http", "http") o:depends({ [_n("transport")] = "raw" }) --- HTTP域名 o = s:option(DynamicList, _n("tcp_guise_http_host"), translate("HTTP Host")) o:depends({ [_n("tcp_guise")] = "http" }) --- HTTP路径 o = s:option(DynamicList, _n("tcp_guise_http_path"), translate("HTTP Path")) o:depends({ [_n("tcp_guise")] = "http" }) --- [[ mKCP部分 ]]-- +-- [[ mKCP ]]-- o = s:option(ListValue, _n("mkcp_guise"), translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)
dns: Disguising traffic as DNS requests.')) for a, t in ipairs(header_type_list) do o:value(t) end @@ -362,7 +359,7 @@ o:depends({ [_n("transport")] = "mkcp" }) o = s:option(Value, _n("mkcp_seed"), translate("KCP Seed")) o:depends({ [_n("transport")] = "mkcp" }) --- [[ gRPC部分 ]]-- +-- [[ gRPC ]]-- o = s:option(Value, _n("grpc_serviceName"), "ServiceName") o:depends({ [_n("transport")] = "grpc" }) @@ -370,7 +367,7 @@ o = s:option(Flag, _n("acceptProxyProtocol"), translate("acceptProxyProtocol"), o.default = "0" o:depends({ [_n("custom")] = false }) --- [[ Fallback部分 ]]-- +-- [[ Fallback ]]-- o = s:option(Flag, _n("fallback"), translate("Fallback")) o:depends({ [_n("protocol")] = "vless", [_n("transport")] = "raw" }) o:depends({ [_n("protocol")] = "trojan", [_n("transport")] = "raw" }) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua index f69e011..b86dc3c 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/sing-box.lua @@ -239,7 +239,7 @@ o:depends({ [_n("protocol")] = "anytls" }) -- https://github.com/SagerNet/sing-box/commit/d2a04c4e41e6cef0937331cb6d10211f431caaab if singbox_tags:find("with_utls") then - -- [[ REALITY部分 ]] -- + -- [[ REALITY ]] -- o = s:option(Flag, _n("reality"), translate("REALITY")) o.default = 0 o:depends({ [_n("protocol")] = "http", [_n("tls")] = true }) @@ -264,7 +264,7 @@ if singbox_tags:find("with_utls") then o:depends({ [_n("reality")] = true }) end --- [[ TLS部分 ]] -- +-- [[ TLS ]] -- o = s:option(FileUpload, _n("tls_certificateFile"), translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem" @@ -346,7 +346,7 @@ o:depends({ [_n("protocol")] = "vmess" }) o:depends({ [_n("protocol")] = "vless" }) o:depends({ [_n("protocol")] = "trojan" }) --- [[ HTTP部分 ]]-- +-- [[ HTTP ]]-- o = s:option(DynamicList, _n("http_host"), translate("HTTP Host")) o:depends({ [_n("transport")] = "http" }) @@ -354,7 +354,7 @@ o:depends({ [_n("transport")] = "http" }) o = s:option(Value, _n("http_path"), translate("HTTP Path")) o:depends({ [_n("transport")] = "http" }) --- [[ WebSocket部分 ]]-- +-- [[ WebSocket ]]-- o = s:option(Value, _n("ws_host"), translate("WebSocket Host")) o:depends({ [_n("transport")] = "ws" }) @@ -362,7 +362,7 @@ o:depends({ [_n("transport")] = "ws" }) o = s:option(Value, _n("ws_path"), translate("WebSocket Path")) o:depends({ [_n("transport")] = "ws" }) --- [[ HTTPUpgrade部分 ]]-- +-- [[ HTTPUpgrade ]]-- o = s:option(Value, _n("httpupgrade_host"), translate("HTTPUpgrade Host")) o:depends({ [_n("transport")] = "httpupgrade" }) @@ -370,7 +370,7 @@ o:depends({ [_n("transport")] = "httpupgrade" }) o = s:option(Value, _n("httpupgrade_path"), translate("HTTPUpgrade Path")) o:depends({ [_n("transport")] = "httpupgrade" }) --- [[ gRPC部分 ]]-- +-- [[ gRPC ]]-- o = s:option(Value, _n("grpc_serviceName"), "ServiceName") o:depends({ [_n("transport")] = "grpc" }) diff --git a/luci-app-passwall2/luasrc/passwall2/api.lua b/luci-app-passwall2/luasrc/passwall2/api.lua index bb3254b..f3670c9 100644 --- a/luci-app-passwall2/luasrc/passwall2/api.lua +++ b/luci-app-passwall2/luasrc/passwall2/api.lua @@ -8,6 +8,11 @@ util = require "luci.util" datatypes = require "luci.cbi.datatypes" jsonc = require "luci.jsonc" i18n = require "luci.i18n" +local lang = uci:get("luci", "main", "lang") or "auto" +if lang == "auto" then + lang = i18n.default +end +i18n.setlanguage(lang) appname = "passwall2" curl_args = { "-skfL", "--connect-timeout 3", "--retry 3" } @@ -128,7 +133,7 @@ function base64Encode(text) return result end ---提取URL中的域名和端口(no ip) +-- Extract the domain name and port from the URL (no IP address). function get_domain_port_from_url(url) local scheme, domain, port = string.match(url, "^(https?)://([%w%.%-]+):?(%d*)") if not domain then @@ -141,7 +146,7 @@ function get_domain_port_from_url(url) return domain, port end ---解析域名 +-- Domain resolution function domainToIPv4(domain, dns) local Dns = dns or "223.5.5.5" local IPs = luci.sys.exec('nslookup %s %s | awk \'/^Name:/{getline; if ($1 == "Address:") print $2}\'' % { domain, Dns }) @@ -161,7 +166,7 @@ function curl_base(url, file, args) end function curl_proxy(url, file, args) - --使用代理 + -- Use the proxy local socks_server = get_cache_var("GLOBAL_SOCKS_server") if socks_server and socks_server ~= "" then if not args then args = {} end @@ -181,7 +186,7 @@ function curl_logic(url, file, args) end function curl_direct(url, file, args) - --直连访问 + -- Direct access if not args then args = {} end local tmp_args = clone(args) local domain, port = get_domain_port_from_url(url) @@ -223,16 +228,15 @@ function trim(text) return text:match("^%s*(.-)%s*$") end --- 分割字符串 function split(full, sep) if full then - full = full:gsub("%z", "") -- 这里不是很清楚 有时候结尾带个\0 + full = full:gsub("%z", "") -- This is not very clear; sometimes it ends with a `\0`. local off, result = 1, {} while true do local nStart, nEnd = full:find(sep, off) if not nEnd then local res = string.sub(full, off, string.len(full)) - if #res > 0 then -- 过滤掉 \0 + if #res > 0 then -- Filter out `\0` table.insert(result, res) end break @@ -1145,7 +1149,7 @@ end function to_check_self() local url = "https://raw.githubusercontent.com/xiaorouji/openwrt-passwall2/main/luci-app-passwall2/Makefile" local tmp_file = "/tmp/passwall2_makefile" - local return_code, result = curl_logic(url, tmp_file, curl_args) + local return_code, result = curl_auto(url, tmp_file, curl_args) result = return_code == 0 if not result then exec("/bin/rm", {"-f", tmp_file}) @@ -1155,8 +1159,8 @@ function to_check_self() } end local local_version = get_version() - local remote_version = sys.exec("echo -n $(grep 'PKG_VERSION' /tmp/passwall2_makefile|awk -F '=' '{print $2}')") - .. "-" .. sys.exec("echo -n $(grep 'PKG_RELEASE' /tmp/passwall2_makefile|awk -F '=' '{print $2}')") + local remote_version = sys.exec("echo -n $(grep '^PKG_VERSION' /tmp/passwall2_makefile | head -n 1 | awk -F '=' '{print $2}')") + exec("/bin/rm", {"-f", tmp_file}) local has_update = compare_versions(local_version, "<", remote_version) if not has_update then @@ -1224,7 +1228,7 @@ function set_apply_on_parse(map) end end map.render = function(self, ...) - getmetatable(self).__index.render(self, ...) -- 保持原渲染流程 + getmetatable(self).__index.render(self, ...) -- Maintain the original rendering process optimize_cbi_ui() end end @@ -1243,7 +1247,7 @@ function luci_types(id, m, s, type_name, option_prefix) end s.fields[key].cfgvalue = function(self, section) - -- 添加自定义 custom_cfgvalue 属性,如果有自定义的 custom_cfgvalue 函数,则使用自定义的 cfgvalue 逻辑 + -- Add a custom `custom_cfgvalue` attribute. If a custom `custom_cfgvalue` function exists, the custom `cfgvalue` logic will be used. if self.custom_cfgvalue then return self:custom_cfgvalue(section) else @@ -1258,7 +1262,7 @@ function luci_types(id, m, s, type_name, option_prefix) end s.fields[key].write = function(self, section, value) if s.fields["type"]:formvalue(id) == type_name then - -- 添加自定义 custom_write 属性,如果有自定义的 custom_write 函数,则使用自定义的 write 逻辑 + -- Add a custom `custom_write` attribute; if a custom `custom_write` function exists, then use the custom write logic. if self.custom_write then self:custom_write(section, value) else @@ -1274,7 +1278,7 @@ function luci_types(id, m, s, type_name, option_prefix) end s.fields[key].remove = function(self, section) if s.fields["type"]:formvalue(id) == type_name then - -- 添加自定义 custom_remove 属性,如果有自定义的 custom_remove 函数,则使用自定义的 remove 逻辑 + -- Add a custom `custom_remove` attribute; if a custom `custom_remove` function exists, use the custom remove logic. if self.custom_remove then self:custom_remove(section) else @@ -1334,14 +1338,14 @@ end function optimize_cbi_ui() luci.http.write([[