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([[