diff --git a/easytier/Makefile b/easytier/Makefile
index 4eef117f4..cc2073a7f 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.5.0)
-PKG_RELEASE:=1
+PKG_VERSION:=$(or $(EASYTIER_VERSION),2.6.0)
+PKG_RELEASE:=2
ifeq ($(ARCH),mipsel)
APP_ARCH:=mipsel
@@ -54,15 +54,19 @@ define Package/$(PKG_NAME)/config
config EASYTIER_INCLUDE_WEBCONSOLE
bool "Include Web Console (easytier-web)"
depends on PACKAGE_$(PKG_NAME)
- depends on !@(mips||mipsel)
+ depends on !(mips || mipsel)
default y
help
Install the easytier-web web console.
endef
define Build/Prepare
- [ ! -f $(PKG_BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION).zip ] && wget https://github.com/EasyTier/EasyTier/releases/download/v$(PKG_VERSION)/$(PKG_NAME)-linux-$(APP_ARCH)-v$(PKG_VERSION).zip -O $(PKG_NAME)-$(PKG_VERSION).zip
- unzip -j $(PKG_NAME)-$(PKG_VERSION).zip -d $(PKG_BUILD_DIR)
+ mkdir -p $(PKG_BUILD_DIR)
+ if [ ! -f $(PKG_BUILD_DIR)/easytier-core ]; then \
+ wget https://github.com/EasyTier/EasyTier/releases/download/v$(PKG_VERSION)/$(PKG_NAME)-linux-$(APP_ARCH)-v$(PKG_VERSION).zip -O $(PKG_BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION).zip; \
+ unzip -o -j $(PKG_BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION).zip -d $(PKG_BUILD_DIR); \
+ rm -f $(PKG_BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION).zip; \
+ fi
endef
define Build/Compile
diff --git a/hickory-dns/Makefile b/hickory-dns/Makefile
index 337ad1968..6a5a0fa59 100644
--- a/hickory-dns/Makefile
+++ b/hickory-dns/Makefile
@@ -1,8 +1,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=hickory-dns
-PKG_VERSION:=0.26.0-beta.4
-PKG_RELEASE:=14
+PKG_VERSION:=0.26.0-beta.2
+PKG_RELEASE:=15
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/hickory-dns/hickory-dns/tar.gz/v$(PKG_VERSION)?
diff --git a/luci-app-easytier/Makefile b/luci-app-easytier/Makefile
index 514e9acf5..f80881fd7 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.5.0)
-PKG_RELEASE:=1
+PKG_VERSION:=$(or $(EASYTIER_VERSION),2.6.0)
+PKG_RELEASE:=2
LUCI_TITLE:=LuCI support for EasyTier
LUCI_DEPENDS:=+kmod-tun +easytier +luci-compat
@@ -54,9 +54,6 @@ if [ -d /tmp/et_config_backup ] ; then
fi
endef
-$(eval $(call Build/Template,Package/$(PKG_NAME)/prerm))
-$(eval $(call Build/Template,Package/$(PKG_NAME)/postinst))
-
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
diff --git a/luci-app-easytier/luasrc/controller/easytier.lua b/luci-app-easytier/luasrc/controller/easytier.lua
index 29aca2d8f..8d12aa550 100644
--- a/luci-app-easytier/luasrc/controller/easytier.lua
+++ b/luci-app-easytier/luasrc/controller/easytier.lua
@@ -1,5 +1,6 @@
module("luci.controller.easytier", package.seeall)
+local i18n = require "luci.i18n"
-- 安全执行命令并返回结果
local function safe_exec(cmd)
@@ -45,17 +46,34 @@ function index()
if not nixio.fs.access("/etc/config/easytier") then
return
end
-
- entry({"admin", "vpn", "easytier"}, alias("admin", "vpn", "easytier", "easytier"),_("EasyTier"), 46).dependent = true
- entry({"admin", "vpn", "easytier", "easytier"}, cbi("easytier"),_("EasyTier"), 47).leaf = true
- entry({"admin", "vpn", "easytier", "easytier_log"}, form("easytier_log"),_("core log"), 48).leaf = true
+
+ entry({"admin", "vpn"}, firstchild(), "VPN", 45).dependent = false
+ entry({"admin", "vpn", "easytier"}, firstchild(),_("EasyTier"), 46).dependent = true
+ entry({"admin", "vpn", "easytier", "status"}, cbi("easytier_status"),_("Status"), 1).leaf = true
+ entry({"admin", "vpn", "easytier", "config"}, cbi("easytier"),_("EasyTier Core"), 2).leaf = true
+ entry({"admin", "vpn", "easytier", "webconsole"}, template("easytier/easytier_web"),_("EasyTier Web"), 3).leaf = true
+ entry({"admin", "vpn", "easytier", "log"}, template("easytier/easytier_log"),_("Logs"), 4).leaf = true
+ entry({"admin", "vpn", "easytier", "upload"}, template("easytier/easytier_upload"),_("Upload Program"), 5).leaf = true
+ entry({"admin", "vpn", "easytier", "upload_binary"}, call("upload_binary")).leaf = true
+ entry({"admin", "vpn", "easytier", "get_upload_config"}, call("get_upload_config")).leaf = true
+ entry({"admin", "vpn", "easytier", "save_upload_config"}, call("save_upload_config")).leaf = true
+ entry({"admin", "vpn", "easytier", "get_disk_space"}, call("get_disk_space")).leaf = true
+ entry({"admin", "vpn", "easytier", "get_tun_info"}, call("get_tun_info")).leaf = true
entry({"admin", "vpn", "easytier", "get_log"}, call("get_log")).leaf = true
+ entry({"admin", "vpn", "easytier", "get_log_size"}, call("get_log_size")).leaf = true
entry({"admin", "vpn", "easytier", "clear_log"}, call("clear_log")).leaf = true
- entry({"admin", "vpn", "easytier", "easytierweb_log"}, form("easytierweb_log"),_("web log"), 49).leaf = true
entry({"admin", "vpn", "easytier", "get_wlog"}, call("get_wlog")).leaf = true
+ entry({"admin", "vpn", "easytier", "get_wlog_size"}, call("get_wlog_size")).leaf = true
entry({"admin", "vpn", "easytier", "clear_wlog"}, call("clear_wlog")).leaf = true
- entry({"admin", "vpn", "easytier", "status"}, call("act_status")).leaf = true
- entry({"admin", "vpn", "easytier", "conninfo"}, call("act_conninfo")).leaf = true
+ entry({"admin", "vpn", "easytier", "clear_version_cache"}, call("clear_version_cache")).leaf = true
+ entry({"admin", "vpn", "easytier", "get_web_config"}, call("get_web_config")).leaf = true
+ entry({"admin", "vpn", "easytier", "save_web_config"}, call("save_web_config")).leaf = true
+ entry({"admin", "vpn", "easytier", "check_web_status"}, call("check_web_status")).leaf = true
+ entry({"admin", "vpn", "easytier", "api_status"}, call("act_status")).leaf = true
+ entry({"admin", "vpn", "easytier", "api_conninfo"}, call("act_conninfo")).leaf = true
+ entry({"admin", "vpn", "easytier", "restart_service"}, call("restart_service")).leaf = true
+ entry({"admin", "vpn", "easytier", "toggle_core"}, call("toggle_core")).leaf = true
+ entry({"admin", "vpn", "easytier", "toggle_web"}, call("toggle_web")).leaf = true
end
function act_status()
@@ -66,6 +84,8 @@ function act_status()
e.crunning = luci.sys.call("pgrep easytier-core >/dev/null") == 0
e.wrunning = luci.sys.call("pgrep easytier-web >/dev/null") == 0
e.port = (port or 0)
+ e.cenabled = uci:get_first("easytier", "easytier", "enabled") == "1"
+ e.wenabled = uci:get_first("easytier", "easytier", "web_enabled") == "1"
-- 使用 Lua 原生计算运行时长
e.etsta = calc_uptime("/tmp/easytier_time")
@@ -105,16 +125,229 @@ function act_status()
e.ettag = cached_tag:gsub("[\r\n]+", "")
else
local easytierbin = uci:get_first("easytier", "easytier", "easytierbin") or "/usr/bin/easytier-core"
- e.ettag = safe_exec(easytierbin .. " -V | sed 's/^[^0-9]*//'")
- if e.ettag == "" then e.ettag = "?" end
+ e.ettag = safe_exec(easytierbin .. " -V 2>/dev/null | sed 's/^[^0-9]*//'")
+ if e.ettag == "" or e.ettag == nil then e.ettag = "unknown" end
local f = io.open("/tmp/easytier.tag", "w")
if f then f:write(e.ettag); f:close() end
end
+
+ local cached_webtag = safe_read_file("/tmp/easytierweb.tag")
+ if cached_webtag and cached_webtag ~= "" then
+ e.etwebtag = cached_webtag:gsub("[\r\n]+", "")
+ else
+ local easytierwebbin = uci:get_first("easytier", "easytier", "easytierwebbin") or "/usr/bin/easytier-web"
+ e.etwebtag = safe_exec(easytierwebbin .. " -V 2>/dev/null | sed 's/^[^0-9]*//'")
+ if e.etwebtag == "" or e.etwebtag == nil then e.etwebtag = "unknown" end
+ local f = io.open("/tmp/easytierweb.tag", "w")
+ if f then f:write(e.etwebtag); f:close() end
+ end
+
+ e.no_tun = uci:get_first("easytier", "easytier", "no_tun") == "1"
+ e.dev_name = uci:get_first("easytier", "easytier", "dev_name") or "tun0"
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
+function get_upload_config()
+ local uci = require "luci.model.uci".cursor()
+ local http = require "luci.http"
+
+ http.prepare_content("application/json")
+
+ local config = {
+ easytierbin = uci:get_first("easytier", "easytier", "easytierbin") or "/usr/bin/easytier-core",
+ webbin = uci:get_first("easytier", "easytier", "easytierwebbin") or "/usr/bin/easytier-web",
+ github_proxys = {},
+ fallback_version = uci:get_first("easytier", "easytier", "fallback_version") or "v2.6.0"
+ }
+
+ -- 读取代理列表(list 类型)
+ uci:foreach("easytier", "easytier", function(s)
+ local proxys = s.github_proxys
+ if proxys then
+ if type(proxys) == "table" then
+ config.github_proxys = proxys
+ else
+ table.insert(config.github_proxys, proxys)
+ end
+ end
+ end)
+
+ http.write_json(config)
+end
+
+function save_upload_config()
+ local uci = require "luci.model.uci".cursor()
+ local http = require "luci.http"
+ local json = require "luci.jsonc"
+
+ http.prepare_content("application/json")
+
+ -- 读取 POST 数据
+ local content = http.content()
+ local data = json.parse(content)
+
+ if not data then
+ http.write_json({success = false, message = "Invalid JSON data"})
+ return
+ end
+
+ -- 保存配置
+ uci:set("easytier", "@easytier[0]", "easytierbin", data.easytierbin or "/usr/bin/easytier-core")
+ uci:set("easytier", "@easytier[0]", "easytierwebbin", data.webbin or "/usr/bin/easytier-web")
+ uci:set("easytier", "@easytier[0]", "fallback_version", data.fallback_version or "v2.6.0")
+
+ -- 删除旧的代理列表
+ uci:delete("easytier", "@easytier[0]", "github_proxys")
+
+ -- 保存代理列表(list 类型)
+ if data.github_proxys and type(data.github_proxys) == "table" then
+ for _, proxy in ipairs(data.github_proxys) do
+ if proxy and proxy ~= "" then
+ local current = uci:get("easytier", "@easytier[0]", "github_proxys") or {}
+ if type(current) ~= "table" then
+ current = {current}
+ end
+ table.insert(current, proxy)
+ uci:set("easytier", "@easytier[0]", "github_proxys", current)
+ end
+ end
+ end
+
+ uci:commit("easytier")
+
+ http.write_json({success = true, message = "Configuration saved successfully"})
+end
+
+function get_disk_space()
+ local http = require "luci.http"
+ local json = require "luci.jsonc"
+ local nixio = require "nixio"
+
+ http.prepare_content("application/json")
+
+ local content = http.content()
+ local data = json.parse(content)
+
+ if not data or not data.path then
+ http.write_json({success = false, message = "Invalid path"})
+ return
+ end
+
+ local path = data.path
+ local full_path = data.full_path
+ local input_type = data.type
+
+ local df_output = luci.sys.exec("df -h '" .. path .. "' 2>/dev/null | tail -n 1")
+ local df_kb = luci.sys.exec("df -k '" .. path .. "' 2>/dev/null | tail -n 1")
+
+ if df_output and df_output ~= "" and df_kb and df_kb ~= "" then
+ local available = df_output:match("%S+%s+%S+%s+%S+%s+(%S+)")
+ local available_kb = df_kb:match("%S+%s+%S+%s+%S+%s+(%d+)")
+ local available_mb = available_kb and math.floor(tonumber(available_kb) / 1024) or 0
+
+ if available and available_mb then
+ local result = {
+ success = true,
+ available = available,
+ available_mb = available_mb
+ }
+
+ if input_type == "easytierbin" and full_path then
+ if nixio.fs.access(full_path) then
+ local size = luci.sys.exec("ls -lh '" .. full_path .. "' 2>/dev/null | awk '{print $5}'")
+ if size and size ~= "" then
+ result.core_size = size:gsub("[\r\n]+$", "")
+ end
+ end
+
+ local dir = full_path:match("(.*/)")
+ local cli_path = (dir or "/usr/bin/") .. "easytier-cli"
+ if nixio.fs.access(cli_path) then
+ local size = luci.sys.exec("ls -lh '" .. cli_path .. "' 2>/dev/null | awk '{print $5}'")
+ if size and size ~= "" then
+ result.cli_size = size:gsub("[\r\n]+$", "")
+ end
+ end
+ elseif input_type == "webbin" and full_path then
+ if nixio.fs.access(full_path) then
+ local size = luci.sys.exec("ls -lh '" .. full_path .. "' 2>/dev/null | awk '{print $5}'")
+ if size and size ~= "" then
+ result.web_size = size:gsub("[\r\n]+$", "")
+ end
+ end
+ end
+
+ http.write_json(result)
+ return
+ end
+ end
+
+ http.write_json({success = false, message = "Unable to query disk space"})
+end
+
+function get_tun_info()
+ luci.http.prepare_content("application/json")
+
+ local ifname = luci.http.formvalue("ifname") or "tun0"
+
+ local exists = luci.sys.exec("ip link show " .. ifname .. " >/dev/null 2>&1 && echo 1 || echo 0")
+ if not exists:match("1") then
+ luci.http.write('{"success":false,"exists":false}')
+ return
+ end
+
+ local ifconfig_out = luci.sys.exec("ifconfig " .. ifname .. " 2>/dev/null")
+
+ local ip = ifconfig_out:match("inet addr:([%d%.]+)") or ifconfig_out:match("inet ([%d%.]+)")
+
+ local netmask = ""
+ local netmask_full = ifconfig_out:match("Mask:([%d%.]+)") or ifconfig_out:match("netmask ([%d%.]+)")
+
+ if not netmask_full or netmask_full == "" then
+ local ip_output = luci.sys.exec("ip -4 addr show " .. ifname .. " 2>/dev/null | grep 'inet ' | head -n1 | awk '{print $2}'")
+ local cidr = ip_output:match("/(%d+)")
+ if cidr then
+ cidr = tonumber(cidr)
+ local mask = 0xFFFFFFFF - (2 ^ (32 - cidr) - 1)
+ netmask = string.format("%d.%d.%d.%d",
+ math.floor(mask / 16777216) % 256,
+ math.floor(mask / 65536) % 256,
+ math.floor(mask / 256) % 256,
+ mask % 256)
+ end
+ else
+ netmask = netmask_full
+ end
+
+ local ipv6_cmd = luci.sys.exec("ip -6 addr show " .. ifname .. " 2>/dev/null | grep 'inet6' | head -n1 | awk '{print $2}'")
+ local ipv6 = ipv6_cmd:gsub("%s", "")
+ if ipv6 == "" then ipv6 = nil end
+
+ local mtu = ifconfig_out:match("MTU:(%d+)") or luci.sys.exec("ip link show " .. ifname .. " 2>/dev/null | head -n1 | sed -n 's/.*mtu \\([0-9]*\\).*/\\1/p'"):gsub("%s", "")
+
+ local state = "UNKNOWN"
+ if ifconfig_out:match("UP") then
+ state = "UP"
+ elseif ifconfig_out:match("DOWN") then
+ state = "DOWN"
+ end
+
+ local rx = luci.sys.exec("cat /sys/class/net/" .. ifname .. "/statistics/rx_bytes 2>/dev/null || echo 0"):gsub("%s", "")
+ local tx = luci.sys.exec("cat /sys/class/net/" .. ifname .. "/statistics/tx_bytes 2>/dev/null || echo 0"):gsub("%s", "")
+
+ local response = string.format('{"success":true,"exists":true,"ip":"%s","netmask":"%s","mtu":"%s","state":"%s","rx_bytes":%s,"tx_bytes":%s',
+ ip or "", netmask, mtu, state, rx, tx)
+
+ if ipv6 then
+ response = response .. ',"ipv6":"' .. ipv6 .. '"'
+ end
+
+ response = response .. '}'
+
+ luci.http.write(response)
+end
function get_log()
local log = ""
@@ -127,10 +360,212 @@ function get_log()
luci.http.write(log)
end
+function get_log_size()
+ local size = luci.sys.exec("[ -f '/tmp/easytier.log' ] && stat -c%s /tmp/easytier.log 2>/dev/null || echo 0")
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({size = tonumber(size) or 0})
+end
+
+function get_wlog_size()
+ local size = luci.sys.exec("[ -f '/tmp/easytierweb.log' ] && stat -c%s /tmp/easytierweb.log 2>/dev/null || echo 0")
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({size = tonumber(size) or 0})
+end
+
function clear_log()
luci.sys.call("echo '' >/tmp/easytier.log")
end
+local function test_binary(path)
+ local handle = io.popen(path .. " -h 2>&1")
+ local output = handle:read("*a")
+ handle:close()
+
+ local line_count = 0
+ for _ in output:gmatch("[^\r\n]+") do
+ line_count = line_count + 1
+ end
+
+ return line_count >= 3 and output:lower():match("easytier")
+end
+
+local function cleanup_files(...)
+ for _, file in ipairs({...}) do
+ nixio.fs.remove(file)
+ end
+end
+
+function upload_binary()
+ local http = require "luci.http"
+ local uci = require "luci.model.uci".cursor()
+ local nixio = require "nixio"
+ local translate = i18n.translate
+
+ local fp
+ local filename = ""
+ local tmp_file = ""
+
+ http.setfilehandler(
+ function(meta, chunk, eof)
+ if meta and meta.file then
+ filename = meta.file
+ tmp_file = "/tmp/upload_" .. filename
+ end
+ if not fp and tmp_file ~= "" then
+ fp = io.open(tmp_file, "w")
+ end
+ if chunk and fp then
+ fp:write(chunk)
+ end
+ if eof and fp then
+ fp:close()
+ end
+ end
+ )
+
+ http.prepare_content("application/json")
+
+ if not http.formvalue("file") or tmp_file == "" then
+ http.write_json({success = false, message = translate("No file uploaded")})
+ return
+ end
+
+ local is_archive = filename:match("%.zip$") or filename:match("%.tar%.gz$") or filename:match("%.tgz$") or filename:match("%.tar$")
+
+ if is_archive then
+ local extract_dir = "/tmp/easytier_extract"
+ os.execute("rm -rf " .. extract_dir .. " && mkdir -p " .. extract_dir)
+
+ if filename:match("%.zip$") then
+ if os.execute("which unzip >/dev/null 2>&1") ~= 0 then
+ cleanup_files(tmp_file)
+ http.write_json({success = false, message = translate("System lacks unzip package, cannot extract zip archive")})
+ return
+ end
+ os.execute("unzip -o -q " .. tmp_file .. " -d " .. extract_dir)
+ else
+ os.execute("tar -xzf " .. tmp_file .. " -C " .. extract_dir .. " 2>/dev/null || tar -xf " .. tmp_file .. " -C " .. extract_dir)
+ end
+
+ os.execute("find " .. extract_dir .. "/easytier-linux-* -maxdepth 1 -type f -exec mv {} " .. extract_dir .. "/ \\; 2>/dev/null")
+ cleanup_files(tmp_file)
+
+ if nixio.fs.access(extract_dir .. "/easytier-web") then
+ nixio.fs.remove(extract_dir .. "/easytier-web")
+ end
+
+ local binaries = {"easytier-core", "easytier-cli", "easytier-web-embed"}
+ local valid_bins = {}
+
+ for _, bin in ipairs(binaries) do
+ local src = extract_dir .. "/" .. bin
+ if nixio.fs.access(src) then
+ nixio.fs.chmod(src, "755")
+ if test_binary(src) then
+ table.insert(valid_bins, {name = bin, path = src})
+ else
+ cleanup_files(src)
+ end
+ end
+ end
+
+ if #valid_bins == 0 then
+ os.execute("rm -rf " .. extract_dir)
+ http.write_json({success = false, message = translate("Not a valid EasyTier program or architecture mismatch")})
+ return
+ end
+
+ local core_path = uci:get_first("easytier", "easytier", "easytierbin") or "/usr/bin/easytier-core"
+ local web_path = uci:get_first("easytier", "easytier", "easytierwebbin") or "/usr/bin/easytier-web"
+
+ for _, bin in ipairs(valid_bins) do
+ local final_path
+ if bin.name == "easytier-web-embed" then
+ final_path = web_path
+ elseif bin.name == "easytier-core" then
+ final_path = core_path
+ elseif bin.name == "easytier-cli" then
+ local core_dir = core_path:match("(.*/)")
+ final_path = (core_dir or "/usr/bin/") .. "easytier-cli"
+ end
+
+ os.execute("rm -f " .. final_path)
+
+ if os.execute("mv " .. bin.path .. " " .. final_path) ~= 0 then
+ os.execute("rm -rf " .. extract_dir)
+ http.write_json({success = false, message = translate("Failed to move file. Insufficient space or permission denied")})
+ return
+ end
+
+ os.execute("chmod 755 " .. final_path)
+ if not test_binary(final_path) then
+ os.execute("rm -f " .. final_path)
+ os.execute("rm -rf " .. extract_dir)
+ http.write_json({success = false, message = translate("Not a valid EasyTier program or architecture mismatch")})
+ return
+ end
+ end
+
+ os.execute("rm -rf " .. extract_dir)
+ nixio.fs.remove("/tmp/easytier.tag")
+ nixio.fs.remove("/tmp/easytierweb.tag")
+ http.write_json({success = true, message = translate("Successfully installed") .. " " .. #valid_bins .. " " .. translate("binary file(s)")})
+ else
+ local valid_names = {["easytier-core"] = true, ["easytier-cli"] = true, ["easytier-web-embed"] = true, ["easytier-web"] = true}
+
+ if not valid_names[filename] then
+ cleanup_files(tmp_file)
+ http.write_json({success = false, message = translate("Not a valid EasyTier program")})
+ return
+ end
+
+ if filename == "easytier-web" then
+ cleanup_files(tmp_file)
+ http.write_json({success = false, message = translate("easytier-web is not used by this plugin, please upload easytier-web-embed")})
+ return
+ end
+
+ nixio.fs.chmod(tmp_file, "755")
+ if not test_binary(tmp_file) then
+ cleanup_files(tmp_file)
+ http.write_json({success = false, message = translate("Not a valid EasyTier program or architecture mismatch")})
+ return
+ end
+
+ local core_path = uci:get_first("easytier", "easytier", "easytierbin") or "/usr/bin/easytier-core"
+ local web_path = uci:get_first("easytier", "easytier", "easytierwebbin") or "/usr/bin/easytier-web"
+
+ local final_path
+ if filename == "easytier-web-embed" then
+ final_path = web_path
+ elseif filename == "easytier-core" then
+ final_path = core_path
+ elseif filename == "easytier-cli" then
+ local core_dir = core_path:match("(.*/)")
+ final_path = (core_dir or "/usr/bin/") .. "easytier-cli"
+ end
+
+ os.execute("rm -f " .. final_path)
+
+ if os.execute("mv " .. tmp_file .. " " .. final_path) ~= 0 then
+ cleanup_files(tmp_file)
+ http.write_json({success = false, message = translate("Failed to move file. Insufficient space or permission denied")})
+ return
+ end
+
+ os.execute("chmod 755 " .. final_path)
+ if not test_binary(final_path) then
+ os.execute("rm -f " .. final_path)
+ http.write_json({success = false, message = translate("Not a valid EasyTier program or architecture mismatch")})
+ return
+ end
+
+ nixio.fs.remove("/tmp/easytier.tag")
+ nixio.fs.remove("/tmp/easytierweb.tag")
+ http.write_json({success = true, message = translate("Binary uploaded successfully to") .. " " .. final_path})
+ end
+end
+
function get_wlog()
local log = ""
local files = {"/tmp/easytierweb.log"}
@@ -146,6 +581,16 @@ function clear_wlog()
luci.sys.call("echo '' >/tmp/easytierweb.log")
end
+function clear_version_cache()
+ local type = luci.http.formvalue("type")
+ if type == "core" then
+ luci.sys.call("rm -f /tmp/easytiernew.tag /tmp/easytier.tag")
+ elseif type == "web" then
+ luci.sys.call("rm -f /tmp/easytiernew.tag /tmp/easytierweb.tag")
+ end
+ luci.http.write("OK")
+end
+
function act_conninfo()
local e = {}
local uci = require "luci.model.uci".cursor()
@@ -186,8 +631,15 @@ function act_conninfo()
else
e.cmdline = ""
end
+
+ -- 检查是否使用配置文件启动
+ if e.cmdline:match("%-%-config%-file") or e.cmdline:match("%-c%s+/") then
+ e.config_file = safe_read_file("/etc/easytier/config.toml") or ""
+ else
+ e.config_file = ""
+ end
else
- local errMsg = "错误:程序未运行!请启动程序后刷新"
+ local errMsg = i18n.translate("Error: Program not running! Please start the program and refresh")
e.node = errMsg
e.peer = errMsg
e.connector = errMsg
@@ -205,3 +657,95 @@ function act_conninfo()
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
+
+
+function get_web_config()
+ local uci = require "luci.model.uci".cursor()
+ local config = {}
+
+ config.web_enabled = uci:get_first("easytier", "easytier", "web_enabled") or "0"
+ config.web_db_path = uci:get_first("easytier", "easytier", "web_db_path") or "/etc/easytier/et.db"
+ config.web_protocol = uci:get_first("easytier", "easytier", "web_protocol") or "udp"
+ config.web_port = uci:get_first("easytier", "easytier", "web_port") or "22020"
+ config.web_fw_web = uci:get_first("easytier", "easytier", "web_fw_web") or "0"
+ config.web_api_port = uci:get_first("easytier", "easytier", "web_api_port") or "11211"
+ config.web_html_port = uci:get_first("easytier", "easytier", "web_html_port") or "11211"
+ config.web_fw_api = uci:get_first("easytier", "easytier", "web_fw_api") or "0"
+ config.web_api_host = uci:get_first("easytier", "easytier", "web_api_host") or ""
+ config.web_geoip_db = uci:get_first("easytier", "easytier", "web_geoip_db") or ""
+ config.web_weblog = uci:get_first("easytier", "easytier", "web_weblog") or "off"
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json(config)
+end
+
+function save_web_config()
+ local uci = require "luci.model.uci".cursor()
+
+ uci:set("easytier", "@easytier[0]", "web_enabled", luci.http.formvalue("web_enabled") or "0")
+ uci:set("easytier", "@easytier[0]", "web_db_path", luci.http.formvalue("web_db_path") or "/etc/easytier/et.db")
+ uci:set("easytier", "@easytier[0]", "web_protocol", luci.http.formvalue("web_protocol") or "udp")
+ uci:set("easytier", "@easytier[0]", "web_port", luci.http.formvalue("web_port") or "22020")
+ uci:set("easytier", "@easytier[0]", "web_fw_web", luci.http.formvalue("web_fw_web") or "0")
+ uci:set("easytier", "@easytier[0]", "web_api_port", luci.http.formvalue("web_api_port") or "11211")
+ uci:set("easytier", "@easytier[0]", "web_html_port", luci.http.formvalue("web_html_port") or "11211")
+ uci:set("easytier", "@easytier[0]", "web_fw_api", luci.http.formvalue("web_fw_api") or "0")
+ uci:set("easytier", "@easytier[0]", "web_api_host", luci.http.formvalue("web_api_host") or "")
+ uci:set("easytier", "@easytier[0]", "web_geoip_db", luci.http.formvalue("web_geoip_db") or "")
+ uci:set("easytier", "@easytier[0]", "web_weblog", luci.http.formvalue("web_weblog") or "off")
+
+ uci:commit("easytier")
+
+ luci.sys.exec("/etc/init.d/easytier restart >/dev/null 2>&1 &")
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({success = true})
+end
+
+function check_web_status()
+ local running = luci.sys.call("pgrep easytier-web >/dev/null") == 0
+ local uci = require "luci.model.uci".cursor()
+ local port = uci:get_first("easytier", "easytier", "web_html_port") or "11211"
+
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({
+ running = running,
+ port = port
+ })
+end
+
+function restart_service()
+ luci.http.prepare_content("application/json")
+ luci.sys.exec("/etc/init.d/easytier restart >/dev/null 2>&1 &")
+ luci.http.write_json({success = true})
+end
+
+function toggle_core()
+ local enabled = luci.http.formvalue("enabled")
+ local uci = require "luci.model.uci".cursor()
+ uci:set("easytier", uci:get_first("easytier", "easytier"), "enabled", enabled)
+ uci:commit("easytier")
+
+ if enabled == "1" then
+ luci.sys.exec("/etc/init.d/easytier start >/dev/null 2>&1 &")
+ else
+ luci.sys.exec("/etc/init.d/easytier restart >/dev/null 2>&1 &")
+ end
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({success = true})
+end
+
+function toggle_web()
+ local enabled = luci.http.formvalue("enabled")
+ local uci = require "luci.model.uci".cursor()
+ uci:set("easytier", uci:get_first("easytier", "easytier"), "web_enabled", enabled)
+ uci:commit("easytier")
+
+ if enabled == "1" then
+ luci.sys.exec("/etc/init.d/easytier start >/dev/null 2>&1 &")
+ else
+ luci.sys.exec("/etc/init.d/easytier restart >/dev/null 2>&1 &")
+ end
+ luci.http.prepare_content("application/json")
+ luci.http.write_json({success = true})
+end
diff --git a/luci-app-easytier/luasrc/model/cbi/easytier.lua b/luci-app-easytier/luasrc/model/cbi/easytier.lua
index b2b83da4b..ec83091c5 100644
--- a/luci-app-easytier/luasrc/model/cbi/easytier.lua
+++ b/luci-app-easytier/luasrc/model/cbi/easytier.lua
@@ -2,36 +2,17 @@ local http = luci.http
local nixio = require "nixio"
m = Map("easytier")
-m.description = translate("A simple, secure, decentralized VPN solution for intranet penetration, implemented in Rust using the Tokio framework. "
- .. "Project URL: github.com/EasyTier/EasyTier "
- .. "Official Documentation "
- .. "QQ Group ")
-
-m:section(SimpleSection).template = "easytier/easytier_status"
-- easytier-core
-s=m:section(TypedSection, "easytier", translate("EasyTier Configuration"))
+s=m:section(TypedSection, "easytier")
s.addremove=false
s.anonymous=true
s:tab("general", translate("General Settings"))
s:tab("privacy", translate("Advanced Settings"))
-s:tab("webconsole", translate("Self-hosted Web Server"))
-s:tab("infos", translate("Connection Info"))
-s:tab("upload", translate("Upload Program"))
switch = s:taboption("general",Flag, "enabled", translate("Enable"))
switch.rmempty = false
-btncq = s:taboption("general", Button, "btncq", translate("Restart"))
-btncq.inputtitle = translate("Restart")
-btncq.description = translate("Quickly restart once without modifying any parameters")
-btncq.inputstyle = "apply"
-btncq:depends("enabled", "1")
-btncq.write = function()
- luci.sys.call("rm -rf /tmp/easytier*.tag /tmp/easytier*.newtag >/dev/null 2>&1 &") -- 执行删除版本号信息
- luci.sys.call("/etc/init.d/easytier restart >/dev/null 2>&1 &") -- 执行重启命令
-end
-
etcmd = s:taboption("general", ListValue, "etcmd", translate("Startup Method"),
translate("Official Web Console: https://easytier.cn/web
"
.. "Official Configuration File Generator: "
@@ -113,8 +94,6 @@ peeradd = s:taboption("general", DynamicList, "peeradd", translate("Peer Nodes")
translate("Initial connected peer nodes (-p parameter)
"
.. "Public server status check: "
.. "Click here to check"))
-peeradd.placeholder = "tcp://public.easytier.top:11010"
-peeradd:value("tcp://public.easytier.top:11010", translate("Official Server - tcp://public.easytier.top:11010"))
peeradd:depends("etcmd", "etcmd")
--[=[
@@ -471,8 +450,9 @@ et_forward:value("etfwlan", translate("Allow traffic from EasyTier virtual netwo
et_forward:value("etfwwan", translate("Allow traffic from EasyTier virtual network to WAN"))
et_forward:value("lanfwet", translate("Allow traffic from LAN to EasyTier virtual network"))
et_forward:value("wanfwet", translate("Allow traffic from WAN to EasyTier virtual network"))
-et_forward.default = "etfwlan etfwwan lanfwet"
et_forward.rmempty = true
+et_forward.optional = true
+et_forward:depends("auto_config_firewall", "1")
check = s:taboption("privacy", Flag, "check", translate("Connectivity Check"),
translate("Enable connectivity check to specify remote device IPs; if all specified IPs fail to ping, "
@@ -492,193 +472,4 @@ for s = 1, 60 do
end
checktime:depends("check", "1")
-local process_status = luci.sys.exec("ps | grep easytier-core| grep -v grep")
-
--- 连接信息 tab - 使用 HTM 模板展示
-conninfo = s:taboption("infos", DummyValue, "_conninfo")
-conninfo.template = "easytier/easytier_conninfo"
-conninfo.rawhtml = true
-
-btnrm = s:taboption("infos", Button, "btnrm")
-btnrm.inputtitle = translate("Check for Updates")
-btnrm.description = translate("Click the button to start checking for updates and refresh the version display in the status bar above")
-btnrm.inputstyle = "apply"
-btnrm.write = function()
- os.execute("rm -rf /tmp/easytier*.tag /tmp/easytier*.newtag /tmp/easytier-core_*")
-end
-
-
-easytierbin = s:taboption("upload", Value, "easytierbin", translate("easytier-core Binary Path"),
- translate("Customize the storage path for easytier-core. Make sure to provide the full path and filename. "
- .. "If the specified path has insufficient space, it will automatically move to /tmp/easytier-core"))
-easytierbin.placeholder = "/usr/bin/easytier-core"
-easytierbin.default = "/usr/bin/easytier-core"
-
-webbin = s:taboption("upload", Value, "webbin", translate("easytier-web Binary Path"),
- translate("Customize the storage path for easytier-web. Make sure to provide the full path and filename, "
- .. "then upload the installer"))
-webbin.placeholder = "/usr/bin/easytier-web"
-webbin.default = "/usr/bin/easytier-web"
-
-github_proxys = s:taboption("upload", Value, "github_proxys", translate("GitHub Proxy URLs"),
- translate("Space-separated list of GitHub proxy URLs for downloading binaries. "
- .. "Leave empty to use default proxies."))
-github_proxys.placeholder = "https://ghproxy.net/ https://gh-proxy.com/"
-
-fallback_version = s:taboption("upload", Value, "fallback_version", translate("Fallback Version"),
- translate("Fallback version to use when unable to fetch the latest version from GitHub."))
-fallback_version.placeholder = "v2.5.0"
-fallback_version.default = "v2.5.0"
-
-local upload = s:taboption("upload", FileUpload, "upload_file")
-upload.optional = true
-upload.default = ""
-upload.template = "easytier/other_upload"
-upload.description = translate("You can directly upload the binary programs easytier-core and easytier-cli, or a compressed .zip archive. "
- .. "Uploading a new version will automatically overwrite the old one. Download link: "
- .. "github.com/EasyTier/EasyTier
"
- .. "The uploaded files will be saved in the /tmp folder. If a custom program path is specified, "
- .. "the program will be automatically moved to that path when started.
")
-local um = s:taboption("upload",DummyValue, "", nil)
-um.template = "easytier/other_dvalue"
-
-local dir, fd, chunk
-dir = "/tmp/"
-nixio.fs.mkdir(dir)
-http.setfilehandler(
- function(meta, chunk, eof)
- if not fd then
- if not meta then return end
-
- if meta and chunk then fd = nixio.open(dir .. meta.file, "w") end
-
- if not fd then
- um.value = translate("Error: Upload failed!")
- return
- end
- end
-
- if chunk and fd then
- fd:write(chunk)
- end
-
- if eof and fd then
- fd:close()
- fd = nil
- um.value = translate("File has been uploaded to") .. ' "/tmp/' .. meta.file .. '"'
-
- if string.sub(meta.file, -4) == ".zip" then
- local file_path = dir .. meta.file
- os.execute("unzip -q " .. file_path .. " -d " .. dir)
- local extracted_dir = "/tmp/easytier-linux-*/"
- os.execute("mv " .. extracted_dir .. "easytier-cli /tmp/easytier-cli")
- os.execute("mv " .. extracted_dir .. "easytier-core /tmp/easytier-core")
- os.execute("mv " .. extracted_dir .. "easytier-web-embed /tmp/easytier-web-embed")
- if nixio.fs.access("/tmp/easytier-cli") then
- um.value = um.value .. "\n" .. translate("- Program /tmp/easytier-cli uploaded successfully, restart the plugin to take effect")
- end
- if nixio.fs.access("/tmp/easytier-core") then
- um.value = um.value .. "\n" .. translate("- Program /tmp/easytier-core uploaded successfully, restart the plugin to take effect")
- end
- if nixio.fs.access("/tmp/easytier-web-embed") then
- um.value = um.value .. "\n" .. translate("- Program /tmp/easytier-web uploaded successfully, restart the plugin to take effect")
- end
- end
-
- if string.sub(meta.file, -7) == ".tar.gz" then
- local file_path = dir .. meta.file
- os.execute("tar -xzf " .. file_path .. " -C " .. dir)
- local extracted_dir = "/tmp/easytier-linux-*/"
- os.execute("mv " .. extracted_dir .. "easytier-cli /tmp/easytier-cli")
- os.execute("mv " .. extracted_dir .. "easytier-core /tmp/easytier-core")
- os.execute("mv " .. extracted_dir .. "easytier-web-embed /tmp/easytier-web-embed")
- if nixio.fs.access("/tmp/easytier-cli") then
- um.value = um.value .. "\n" .. translate("- Program /tmp/easytier-cli uploaded successfully, restart the plugin to take effect")
- end
- if nixio.fs.access("/tmp/easytier-core") then
- um.value = um.value .. "\n" .. translate("- Program /tmp/easytier-core uploaded successfully, restart the plugin to take effect")
- end
- if nixio.fs.access("/tmp/easytier-web-embed") then
- um.value = um.value .. "\n" .. translate("- Program /tmp/easytier-web uploaded successfully, restart the plugin to take effect")
- end
- end
-
- os.execute("chmod +x /tmp/easytier-core")
- os.execute("chmod +x /tmp/easytier-cli")
- os.execute("chmod +x /tmp/easytier-web-embed")
-
- end
- end
-)
-if luci.http.formvalue("upload") then
- local f = luci.http.formvalue("ulfile")
-end
-
--- Self-hosted Web Console tab options
-
-web_enabled = s:taboption("webconsole", Flag, "web_enabled", translate("Enable"))
-web_enabled.rmempty = false
-
-web_btncq = s:taboption("webconsole", Button, "web_btncq", translate("Restart"))
-web_btncq.inputtitle = translate("Restart")
-web_btncq.description = translate("Quickly restart once without modifying any parameters")
-web_btncq.inputstyle = "apply"
-web_btncq.write = function()
- luci.sys.call("/etc/init.d/easytier restart >/dev/null 2>&1 &")
-end
-
-web_db_path = s:taboption("webconsole", Value, "web_db_path", translate("Database File Path"),
- translate("Path to the sqlite3 database file used to store all data. (-d parameter)"))
-web_db_path.default = "/etc/easytier/et.db"
-
-web_protocol = s:taboption("webconsole", ListValue, "web_protocol", translate("Listening Protocol"),
- translate("Configure the server's listening protocol for easytier-core to connect. (-p parameter)"))
-web_protocol.default = "udp"
-web_protocol:value("udp",translate("UDP"))
-web_protocol:value("tcp",translate("TCP"))
-web_protocol:value("ws",translate("WS"))
-
-web_port = s:taboption("webconsole", Value, "web_port", translate("Server Port"),
- translate("Configure the server's listening port for easytier-core to connect. (-c parameter)"))
-web_port.datatype = "range(1,65535)"
-web_port.placeholder = "22020"
-web_port.default = "22020"
-
-web_fw_web = s:taboption("webconsole", Flag, "web_fw_web", translate("WAN access to WEB"),
- translate("Automatically add firewall rules to allow WAN access to this WEB console"))
-
-web_api_port = s:taboption("webconsole", Value, "web_api_port", translate("API Port"),
- translate("Listening port of the RESTful server, used as ApiHost by the web frontend. (-a parameter)"))
-web_api_port.datatype = "range(1,65535)"
-web_api_port.placeholder = "11211"
-web_api_port.default = "11211"
-
-web_html_port = s:taboption("webconsole", Value, "web_html_port", translate("Web Interface Port"),
- translate("Frontend listening port for the web dashboard server. Leave empty to disable. (-l parameter)"))
-web_html_port.datatype = "range(1,65535)"
-web_html_port.default = "11211"
-
-web_fw_api = s:taboption("webconsole", Flag, "web_fw_api", translate("WAN access to API"),
- translate("Automatically add firewall rules to allow WAN access to the API control page"))
-
-web_api_host = s:taboption("webconsole", Value, "web_api_host", translate("Default API Server URL"),
- translate("The URL of the API server, used for connecting the web frontend. (--api-host parameter)
"
- .. "Example: http://[current device IP or resolved domain name]:[API port]"))
-
-web_geoip_db = s:taboption("webconsole", Value, "web_geoip_db", translate("GEOIP_DB Path"),
- translate("GeoIP2 database file path used to locate the client. Defaults to an embedded file (country-level information only)."
- .. "
Recommended: https://github.com/P3TERX/GeoLite.mmdb (--geoip-db parameter)"))
-web_geoip_db.placeholder = "/etc/easytier/GeoLite.mmdb"
-
-web_weblog = s:taboption("webconsole", ListValue, "web_weblog", translate("Program Log"),
- translate("Runtime log located at /tmp/easytierweb.log, viewable in the log section above.
"
- .. "Levels: Error < Warning < Info < Debug < Trace"))
-web_weblog.default = "off"
-web_weblog:value("off", translate("Off"))
-web_weblog:value("error", translate("Error"))
-web_weblog:value("warn", translate("Warning"))
-web_weblog:value("info", translate("Info"))
-web_weblog:value("debug", translate("Debug"))
-web_weblog:value("trace", translate("Trace"))
-
return m
diff --git a/luci-app-easytier/luasrc/model/cbi/easytier_log.lua b/luci-app-easytier/luasrc/model/cbi/easytier_log.lua
deleted file mode 100644
index 70b85233a..000000000
--- a/luci-app-easytier/luasrc/model/cbi/easytier_log.lua
+++ /dev/null
@@ -1,5 +0,0 @@
-f = SimpleForm("easytier")
-f.reset = false
-f.submit = false
-f:append(Template("easytier/easytier_log"))
-return f
diff --git a/luci-app-easytier/luasrc/model/cbi/easytier_status.lua b/luci-app-easytier/luasrc/model/cbi/easytier_status.lua
new file mode 100644
index 000000000..31f225798
--- /dev/null
+++ b/luci-app-easytier/luasrc/model/cbi/easytier_status.lua
@@ -0,0 +1,16 @@
+local http = luci.http
+
+m = Map("easytier", translate("EasyTier Status"))
+m.description = translate("A simple, secure, decentralized VPN solution for intranet penetration, implemented in Rust using the Tokio framework. "
+ .. "Project URL: github.com/EasyTier/EasyTier "
+ .. "Official Documentation "
+ .. "QQ Group ")
+m.pageaction = false
+
+-- 状态卡片
+m:section(SimpleSection).template = "easytier/easytier_status"
+
+-- 连接信息卡片
+m:section(SimpleSection).template = "easytier/easytier_cli"
+
+return m
diff --git a/luci-app-easytier/luasrc/model/cbi/easytierweb_log.lua b/luci-app-easytier/luasrc/model/cbi/easytierweb_log.lua
deleted file mode 100644
index ebd559f5c..000000000
--- a/luci-app-easytier/luasrc/model/cbi/easytierweb_log.lua
+++ /dev/null
@@ -1,5 +0,0 @@
-f = SimpleForm("easytier")
-f.reset = false
-f.submit = false
-f:append(Template("easytier/easytierweb_log"))
-return f
diff --git a/luci-app-easytier/luasrc/view/easytier/easytier_cli.htm b/luci-app-easytier/luasrc/view/easytier/easytier_cli.htm
new file mode 100644
index 000000000..2436db1a2
--- /dev/null
+++ b/luci-app-easytier/luasrc/view/easytier/easytier_cli.htm
@@ -0,0 +1,1166 @@
+<%
+local uci = require 'luci.model.uci'.cursor()
+-%>
+
+
+
+