diff --git a/easytier-noweb/Makefile b/easytier-noweb/Makefile index 3fcd525b..1a9fda95 100644 --- a/easytier-noweb/Makefile +++ b/easytier-noweb/Makefile @@ -2,8 +2,8 @@ include $(TOPDIR)/rules.mk -include $(dir $(lastword $(MAKEFILE_LIST)))../version.mk PKG_NAME:=easytier-noweb -PKG_VERSION:=$(or $(EASYTIER_VERSION),2.6.2) -PKG_RELEASE:=2 +PKG_VERSION:=$(or $(EASYTIER_VERSION),2.6.4) +PKG_RELEASE:=3 ifeq ($(ARCH),mipsel) APP_ARCH:=mipsel diff --git a/easytier/Makefile b/easytier/Makefile index 2a936340..8d843f97 100644 --- a/easytier/Makefile +++ b/easytier/Makefile @@ -2,8 +2,8 @@ include $(TOPDIR)/rules.mk -include $(dir $(lastword $(MAKEFILE_LIST)))../version.mk PKG_NAME:=easytier -PKG_VERSION:=$(or $(EASYTIER_VERSION),2.6.2) -PKG_RELEASE:=5 +PKG_VERSION:=$(or $(EASYTIER_VERSION),2.6.4) +PKG_RELEASE:=6 ifeq ($(ARCH),mipsel) APP_ARCH:=mipsel diff --git a/luci-app-alist/po/lo/alist.po b/luci-app-alist/po/lo/alist.po new file mode 100644 index 00000000..e69de29b diff --git a/luci-app-easytier/Makefile b/luci-app-easytier/Makefile index d51540bb..6c4ce31b 100644 --- a/luci-app-easytier/Makefile +++ b/luci-app-easytier/Makefile @@ -7,8 +7,8 @@ include $(TOPDIR)/rules.mk -include $(dir $(lastword $(MAKEFILE_LIST)))../version.mk -PKG_VERSION:=$(or $(EASYTIER_VERSION),2.6.2) -PKG_RELEASE:=8 +PKG_VERSION:=$(or $(EASYTIER_VERSION),2.6.4) +PKG_RELEASE:=9 LUCI_TITLE:=LuCI support for EasyTier LUCI_DEPENDS:=+kmod-tun +easytier +luci-compat diff --git a/luci-app-easytier/luasrc/controller/easytier.lua b/luci-app-easytier/luasrc/controller/easytier.lua index 96a6c164..395c67a4 100644 --- a/luci-app-easytier/luasrc/controller/easytier.lua +++ b/luci-app-easytier/luasrc/controller/easytier.lua @@ -163,7 +163,7 @@ function get_upload_config() easytierbin = uci:get_first("easytier", "easytier", "easytierbin") or "/usr/bin/easytier-core", webbin = uci:get_first("easytier", "easytier", "webbin") or "/usr/bin/easytier-web", github_proxys = {}, - fallback_version = uci:get_first("easytier", "easytier", "fallback_version") or "v2.6.2" + fallback_version = uci:get_first("easytier", "easytier", "fallback_version") or "v2.6.4" } -- 读取代理列表(list 类型) @@ -200,7 +200,7 @@ function save_upload_config() -- 保存配置 uci:set("easytier", "@easytier[0]", "easytierbin", data.easytierbin or "/usr/bin/easytier-core") uci:set("easytier", "@easytier[0]", "webbin", data.webbin or "/usr/bin/easytier-web") - uci:set("easytier", "@easytier[0]", "fallback_version", data.fallback_version or "v2.6.2") + uci:set("easytier", "@easytier[0]", "fallback_version", data.fallback_version or "v2.6.4") -- 删除旧的代理列表 uci:delete("easytier", "@easytier[0]", "github_proxys") diff --git a/luci-app-easytier/luasrc/model/cbi/easytier.lua b/luci-app-easytier/luasrc/model/cbi/easytier.lua index ec83091c..4476fb1d 100644 --- a/luci-app-easytier/luasrc/model/cbi/easytier.lua +++ b/luci-app-easytier/luasrc/model/cbi/easytier.lua @@ -56,12 +56,6 @@ network_name = s:taboption("general", Value, "network_name", translate("Network network_name.password = true network_name.placeholder = "easytier-name" network_name.maxlength = 64 -network_name.validate = function(self, value) - if value and value ~= "" and value:match("[^%w%-_]") then - return nil, translate("Only alphanumeric characters, hyphens and underscores allowed") - end - return value -end network_name:depends("etcmd", "etcmd") network_secret = s:taboption("general", Value, "network_secret", translate("Network Secret"), @@ -412,9 +406,28 @@ foreign_relay_bps_limit = s:taboption("privacy", Value, "foreign_relay_bps_limit .. "(--foreign-relay-bps-limit parameter)")) foreign_relay_bps_limit:depends("etcmd", "etcmd") -extra_args = s:taboption("privacy", Value, "extra_args", translate("Extra Parameters"), +extra_args = s:taboption("privacy", DynamicList, "extra_args", translate("Extra Parameters"), translate("Additional command-line arguments passed to the backend process")) -extra_args.placeholder = "--tcp-whitelist 80 --udp-whitelist 53" +extra_args.placeholder = "--tcp-whitelist=80" +extra_args.validate = function(self, value, section) + if type(value) == "table" then + local seen = {} + local i + for i = 1, #value do + local arg = value[i] + if arg then + arg = tostring(arg):match("^%s*(.-)%s*$") + if arg ~= "" then + if seen[arg] then + return nil, translate("Duplicate extra parameter") .. ": " .. arg + end + seen[arg] = true + end + end + end + end + return value +end extra_args:depends("etcmd", "etcmd") log = s:taboption("general", ListValue, "log", translate("Program Log"), diff --git a/luci-app-easytier/luasrc/view/easytier/easytier_cli.htm b/luci-app-easytier/luasrc/view/easytier/easytier_cli.htm index 0dc2dca5..38b71400 100644 --- a/luci-app-easytier/luasrc/view/easytier/easytier_cli.htm +++ b/luci-app-easytier/luasrc/view/easytier/easytier_cli.htm @@ -370,6 +370,8 @@ local uci = require 'luci.model.uci'.cursor() border-radius: 12px; white-space: pre-wrap; word-wrap: break-word; + overflow-wrap: anywhere; + word-break: break-word; font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace; font-size: 13px; color: var(--text-primary); @@ -377,6 +379,34 @@ local uci = require 'luci.model.uci'.cursor() line-height: 1.5; } +.et-command-panel { + position: relative; +} + +.et-command-toolbar { + display: flex; + justify-content: flex-end; + margin-bottom: 8px; +} + +.et-copy-btn { + display: inline-flex; + align-items: center; + padding: 5px 10px; + background: var(--tab-active); + color: #fff; + border: 1px solid var(--tab-active); + border-radius: 6px; + cursor: pointer; + font-size: 12px; + font-weight: 500; + line-height: 1.4; +} + +.et-copy-btn:hover { + opacity: 0.9; +} + @media (max-width: 768px) { .et-conninfo-header { flex-direction: column; @@ -893,6 +923,50 @@ function escapeHtml(text) { return div.innerHTML; } +function copyTextFromElement(elementId, button) { + var el = document.getElementById(elementId); + if (!el) return; + var text = el.textContent || el.innerText || ''; + + function markCopied() { + if (!button) return; + var oldText = button.textContent; + button.textContent = '<%:Copied%>'; + window.setTimeout(function() { + button.textContent = oldText; + }, 1500); + } + + if (navigator.clipboard && window.isSecureContext) { + navigator.clipboard.writeText(text).then(markCopied); + return; + } + + var textarea = document.createElement('textarea'); + textarea.value = text; + textarea.setAttribute('readonly', 'readonly'); + textarea.style.position = 'fixed'; + textarea.style.left = '-9999px'; + document.body.appendChild(textarea); + textarea.select(); + try { + document.execCommand('copy'); + markCopied(); + } finally { + document.body.removeChild(textarea); + } +} + +function renderCopyablePre(content, className) { + var id = 'et_copy_' + Math.random().toString(36).slice(2); + return '
' + + '
' + + '' + + '
' + + '
' + escapeHtml(content) + '
' + + '
'; +} + function parseTable(content, tabName) { if (!content || content.trim() === '') { return '
<%:No data available%>
'; @@ -916,6 +990,47 @@ function parseTable(content, tabName) { if (content.indexOf('Error: Invalid service name: "PeerCenterRpc"') !== -1) { return '
<%:Peer Center service not available. This feature requires multiple nodes in the network.%>
'; } + + // 启动参数可能包含管道符等特殊字符,必须先于表格解析处理。 + if (tabName === 'cmdline') { + var html = renderCopyablePre(content); + + // 检查是否使用配置文件启动 + if (content.indexOf('--config-file') !== -1 || content.match(/-c\s+\//)) { + XHR.get('<%=url("admin/vpn/easytier/api_conninfo")%>', null, function(x, data) { + if (data && data.config_file) { + var configDiv = document.createElement('div'); + configDiv.className = 'et-pre'; + configDiv.style.marginTop = '16px'; + configDiv.style.borderTop = '2px solid var(--border-color)'; + configDiv.style.paddingTop = '16px'; + + var title = document.createElement('div'); + title.style.fontWeight = '600'; + title.style.marginBottom = '8px'; + title.style.color = 'var(--primary-color)'; + title.textContent = tr('Configuration File Content') + ':'; + + var contentDiv = document.createElement('pre'); + contentDiv.style.margin = '0'; + contentDiv.style.whiteSpace = 'pre-wrap'; + contentDiv.style.overflowWrap = 'anywhere'; + contentDiv.style.wordBreak = 'break-word'; + contentDiv.textContent = data.config_file; + + configDiv.appendChild(title); + configDiv.appendChild(contentDiv); + + var container = document.getElementById('tab_cmdline'); + if (container) { + container.appendChild(configDiv); + } + } + }); + } + + return html; + } // 连接失败错误处理 if (content.indexOf('failed to get peer manager client') !== -1 || @@ -948,46 +1063,6 @@ function parseTable(content, tabName) { var lines = content.split(/\r?\n/).filter(function(l) { return l.trim() !== ''; }); if (lines.length === 0 || lines[0].indexOf('|') === -1) { - // 启动参数不翻译 - if (tabName === 'cmdline') { - var html = '
' + escapeHtml(content) + '
'; - - // 检查是否使用配置文件启动 - if (content.indexOf('--config-file') !== -1 || content.match(/-c\s+\//)) { - XHR.get('<%=url("admin/vpn/easytier/api_conninfo")%>', null, function(x, data) { - if (data && data.config_file) { - var configDiv = document.createElement('div'); - configDiv.className = 'et-pre'; - configDiv.style.marginTop = '16px'; - configDiv.style.borderTop = '2px solid var(--border-color)'; - configDiv.style.paddingTop = '16px'; - - var title = document.createElement('div'); - title.style.fontWeight = '600'; - title.style.marginBottom = '8px'; - title.style.color = 'var(--primary-color)'; - title.textContent = tr('Configuration File Content') + ':'; - - var contentDiv = document.createElement('pre'); - contentDiv.style.margin = '0'; - contentDiv.style.whiteSpace = 'pre-wrap'; - contentDiv.style.wordBreak = 'break-all'; - contentDiv.textContent = data.config_file; - - configDiv.appendChild(title); - configDiv.appendChild(contentDiv); - - var container = document.querySelector('.et-tab-content[data-tab="cmdline"]'); - if (container) { - container.appendChild(configDiv); - } - } - }); - } - - return html; - } - // 对非表格内容进行全面翻译处理 var translatedContent = content; diff --git a/luci-app-easytier/luasrc/view/easytier/easytier_upload.htm b/luci-app-easytier/luasrc/view/easytier/easytier_upload.htm index 209dd5ed..eb013744 100644 --- a/luci-app-easytier/luasrc/view/easytier/easytier_upload.htm +++ b/luci-app-easytier/luasrc/view/easytier/easytier_upload.htm @@ -383,7 +383,7 @@ local translate = i18n.translate - +