🔥 Sync 2026-04-18 20:14:20

This commit is contained in:
github-actions[bot] 2026-04-18 20:14:20 +08:00
parent 5eb416f3e2
commit 4f4f9882d8
12 changed files with 840 additions and 94 deletions

View File

@ -5,8 +5,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=ddns-go
PKG_VERSION:=6.16.8
PKG_RELEASE:=7
PKG_VERSION:=6.16.9
PKG_RELEASE:=8
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/jeessy2/ddns-go/tar.gz/v$(PKG_VERSION)?

View File

@ -6,8 +6,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=gowebdav
PKG_VERSION:=5.11.5
PKG_RELEASE:=5
PKG_VERSION:=5.11.6
PKG_RELEASE:=6
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/hacdias/webdav/tar.gz/v$(PKG_VERSION)?

View File

@ -16,8 +16,8 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-amlogic
PKG_VERSION:=3.1.288
PKG_RELEASE:=3
PKG_VERSION:=3.1.289
PKG_RELEASE:=4
PKG_LICENSE:=GPL-2.0 License
PKG_MAINTAINER:=ophub <https://github.com/ophub/luci-app-amlogic>

View File

@ -4,7 +4,7 @@ config amlogic 'config'
option amlogic_firmware_repo 'https://github.com/breakingbadboy/OpenWrt'
option amlogic_firmware_tag 'ARMv8'
option amlogic_firmware_suffix '.img.gz'
option amlogic_kernel_path 'https://github.com/breakingbadboy/OpenWrt'
option amlogic_kernel_path 'https://github.com/ophub/kernel'
option amlogic_kernel_tags ''
option amlogic_kernel_branch ''
option amlogic_firmware_config '1'

View File

@ -133,6 +133,8 @@ else
kernel_tag="rk3588"
elif [[ "${kernel_uname}" =~ -rk35xx ]]; then
kernel_tag="rk35xx"
elif [[ "${kernel_uname}" =~ -flippy ]]; then
kernel_tag="flippy"
elif [[ "${kernel_uname}" =~ -h6|-zicai ]]; then
kernel_tag="h6"
else

View File

@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
-include $(dir $(lastword $(MAKEFILE_LIST)))../version.mk
PKG_VERSION:=$(or $(EASYTIER_VERSION),2.6.0)
PKG_RELEASE:=5
PKG_RELEASE:=6
LUCI_TITLE:=LuCI support for EasyTier
LUCI_DEPENDS:=+kmod-tun +easytier +luci-compat

View File

@ -68,6 +68,7 @@ function index()
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", "reset_database"}, call("reset_database")).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
@ -663,46 +664,178 @@ 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")
local uci = require "luci.model.uci".cursor()
local config = {
web_enabled = uci:get_first("easytier", "easytier", "web_enabled") or "0",
web_db_path = uci:get_first("easytier", "easytier", "web_db_path") or "/etc/easytier/et.db",
web_protocol = uci:get_first("easytier", "easytier", "web_protocol") or "udp",
web_port = uci:get_first("easytier", "easytier", "web_port") or "22020",
web_fw_web = uci:get_first("easytier", "easytier", "web_fw_web") or "0",
web_api_port = uci:get_first("easytier", "easytier", "web_api_port") or "11211",
web_html_port = uci:get_first("easytier", "easytier", "web_html_port") or "11211",
web_api_addr = uci:get_first("easytier", "easytier", "web_api_addr") or "0.0.0.0",
web_html_addr = uci:get_first("easytier", "easytier", "web_html_addr") or "0.0.0.0",
web_fw_api = uci:get_first("easytier", "easytier", "web_fw_api") or "0",
web_api_host = uci:get_first("easytier", "easytier", "web_api_host") or "",
web_geoip_db = uci:get_first("easytier", "easytier", "web_geoip_db") or "",
web_disable_registration = uci:get_first("easytier", "easytier", "web_disable_registration") or "0",
web_allow_auto_create_user = uci:get_first("easytier", "easytier", "web_allow_auto_create_user") or "0",
web_weblog = uci:get_first("easytier", "easytier", "web_weblog") or "off",
-- OIDC配置
web_oidc_enabled = uci:get_first("easytier", "easytier", "web_oidc_enabled") or "0",
web_oidc_issuer_url = uci:get_first("easytier", "easytier", "web_oidc_issuer_url") or "",
web_oidc_client_id = uci:get_first("easytier", "easytier", "web_oidc_client_id") or "",
web_oidc_client_secret = uci:get_first("easytier", "easytier", "web_oidc_client_secret") or "",
web_oidc_redirect_url = uci:get_first("easytier", "easytier", "web_oidc_redirect_url") or "",
web_oidc_username_claim = uci:get_first("easytier", "easytier", "web_oidc_username_claim") or "preferred_username",
web_oidc_scopes = uci:get_first("easytier", "easytier", "web_oidc_scopes") or "openid profile",
web_oidc_frontend_base_url = uci:get_first("easytier", "easytier", "web_oidc_frontend_base_url") or "",
web_oidc_disable_pkce = uci:get_first("easytier", "easytier", "web_oidc_disable_pkce") or "0",
-- Webhook配置
web_webhook_enabled = uci:get_first("easytier", "easytier", "web_webhook_enabled") or "0",
web_webhook_url = uci:get_first("easytier", "easytier", "web_webhook_url") or "",
web_webhook_secret = uci:get_first("easytier", "easytier", "web_webhook_secret") or "",
web_internal_auth_token = uci:get_first("easytier", "easytier", "web_internal_auth_token") or "",
web_web_instance_id = uci:get_first("easytier", "easytier", "web_web_instance_id") or "",
web_web_instance_api_base_url = uci:get_first("easytier", "easytier", "web_web_instance_api_base_url") or ""
}
luci.http.write_json(config)
end
function save_web_config()
local uci = require "luci.model.uci".cursor()
local http = require "luci.http"
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")
local web_enabled = http.formvalue("web_enabled") or "0"
local web_db_path = http.formvalue("web_db_path") or "/etc/easytier/et.db"
local web_protocol = http.formvalue("web_protocol") or "udp"
local web_port = http.formvalue("web_port") or "22020"
local web_fw_web = http.formvalue("web_fw_web") or "0"
local web_api_port = http.formvalue("web_api_port") or "11211"
local web_html_port = http.formvalue("web_html_port") or "11211"
local web_api_addr = http.formvalue("web_api_addr") or "0.0.0.0"
local web_html_addr = http.formvalue("web_html_addr") or "0.0.0.0"
local web_fw_api = http.formvalue("web_fw_api") or "0"
local web_api_host = http.formvalue("web_api_host") or ""
local web_geoip_db = http.formvalue("web_geoip_db") or ""
local web_disable_registration = http.formvalue("web_disable_registration") or "0"
local web_allow_auto_create_user = http.formvalue("web_allow_auto_create_user") or "0"
local web_weblog = http.formvalue("web_weblog") or "off"
-- OIDC
local web_oidc_enabled = http.formvalue("web_oidc_enabled") or "0"
local web_oidc_issuer_url = http.formvalue("web_oidc_issuer_url") or ""
local web_oidc_client_id = http.formvalue("web_oidc_client_id") or ""
local web_oidc_client_secret = http.formvalue("web_oidc_client_secret") or ""
local web_oidc_redirect_url = http.formvalue("web_oidc_redirect_url") or ""
local web_oidc_username_claim = http.formvalue("web_oidc_username_claim") or "preferred_username"
local web_oidc_scopes = http.formvalue("web_oidc_scopes") or "openid profile"
local web_oidc_frontend_base_url = http.formvalue("web_oidc_frontend_base_url") or ""
local web_oidc_disable_pkce = http.formvalue("web_oidc_disable_pkce") or "0"
-- Webhook
local web_webhook_enabled = http.formvalue("web_webhook_enabled") or "0"
local web_webhook_url = http.formvalue("web_webhook_url") or ""
local web_webhook_secret = http.formvalue("web_webhook_secret") or ""
local web_internal_auth_token = http.formvalue("web_internal_auth_token") or ""
local web_web_instance_id = http.formvalue("web_web_instance_id") or ""
local web_web_instance_api_base_url = http.formvalue("web_web_instance_api_base_url") or ""
uci:set("easytier", "@easytier[0]", "web_enabled", web_enabled)
uci:set("easytier", "@easytier[0]", "web_db_path", web_db_path)
uci:set("easytier", "@easytier[0]", "web_protocol", web_protocol)
uci:set("easytier", "@easytier[0]", "web_port", web_port)
uci:set("easytier", "@easytier[0]", "web_fw_web", web_fw_web)
uci:set("easytier", "@easytier[0]", "web_api_port", web_api_port)
uci:set("easytier", "@easytier[0]", "web_html_port", web_html_port)
uci:set("easytier", "@easytier[0]", "web_api_addr", web_api_addr)
uci:set("easytier", "@easytier[0]", "web_html_addr", web_html_addr)
uci:set("easytier", "@easytier[0]", "web_fw_api", web_fw_api)
uci:set("easytier", "@easytier[0]", "web_api_host", web_api_host)
uci:set("easytier", "@easytier[0]", "web_geoip_db", web_geoip_db)
uci:set("easytier", "@easytier[0]", "web_disable_registration", web_disable_registration)
uci:set("easytier", "@easytier[0]", "web_allow_auto_create_user", web_allow_auto_create_user)
uci:set("easytier", "@easytier[0]", "web_weblog", web_weblog)
-- OIDC
uci:set("easytier", "@easytier[0]", "web_oidc_enabled", web_oidc_enabled)
uci:set("easytier", "@easytier[0]", "web_oidc_issuer_url", web_oidc_issuer_url)
uci:set("easytier", "@easytier[0]", "web_oidc_client_id", web_oidc_client_id)
uci:set("easytier", "@easytier[0]", "web_oidc_client_secret", web_oidc_client_secret)
uci:set("easytier", "@easytier[0]", "web_oidc_redirect_url", web_oidc_redirect_url)
uci:set("easytier", "@easytier[0]", "web_oidc_username_claim", web_oidc_username_claim)
uci:set("easytier", "@easytier[0]", "web_oidc_scopes", web_oidc_scopes)
uci:set("easytier", "@easytier[0]", "web_oidc_frontend_base_url", web_oidc_frontend_base_url)
uci:set("easytier", "@easytier[0]", "web_oidc_disable_pkce", web_oidc_disable_pkce)
-- Webhook
uci:set("easytier", "@easytier[0]", "web_webhook_enabled", web_webhook_enabled)
uci:set("easytier", "@easytier[0]", "web_webhook_url", web_webhook_url)
uci:set("easytier", "@easytier[0]", "web_webhook_secret", web_webhook_secret)
uci:set("easytier", "@easytier[0]", "web_internal_auth_token", web_internal_auth_token)
uci:set("easytier", "@easytier[0]", "web_web_instance_id", web_web_instance_id)
uci:set("easytier", "@easytier[0]", "web_web_instance_api_base_url", web_web_instance_api_base_url)
uci:commit("easytier")
luci.sys.exec("/etc/init.d/easytier restart >/dev/null 2>&1 &")
http.prepare_content("application/json")
http.write_json({success = true})
end
function reset_database()
luci.http.prepare_content("application/json")
luci.http.write_json({success = true})
local json = require "luci.jsonc"
local nixio = require "nixio"
-- 从请求中获取数据库路径
local req_data = json.parse(luci.http.content())
if not req_data or not req_data.db_path then
luci.http.write_json({success = false, message = "Missing db_path parameter"})
return
end
local db_path = req_data.db_path
-- 安全检查:路径必须是绝对路径且不能是根目录或系统关键目录
if not db_path:match("^/") or db_path == "/" or
db_path:match("^/bin") or db_path:match("^/sbin") or
db_path:match("^/usr/bin") or db_path:match("^/usr/sbin") or
db_path:match("^/lib") or db_path:match("^/boot") then
luci.http.write_json({success = false, message = "Invalid database path"})
return
end
-- 构建要删除的文件列表
local files_to_delete = {
db_path,
db_path .. "-wal",
db_path .. "-shm"
}
local deleted_files = {}
-- 删除文件
for _, file in ipairs(files_to_delete) do
if nixio.fs.access(file) then
local result = os.remove(file)
if result then
table.insert(deleted_files, file)
end
end
end
if #deleted_files > 0 then
luci.http.write_json({
success = true,
deleted_files = deleted_files,
message = "Database reset successfully"
})
else
luci.http.write_json({
success = false,
message = "No database files found or deletion failed"
})
end
end
function check_web_status()

View File

@ -5,7 +5,7 @@ m.description = translate("A simple, secure, decentralized VPN solution for intr
.. "Project URL: <a href=\"https://github.com/EasyTier/EasyTier\" target=\"_blank\">github.com/EasyTier/EasyTier</a>&nbsp;&nbsp;"
.. "<a href=\"http://easytier.cn\" target=\"_blank\">Official Documentation</a>&nbsp;&nbsp;"
.. "<a href=\"http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=jhP2Z4UsEZ8wvfGPLrs0VwLKn_uz0Q_p&authKey=OGKSQLfg61YPCpVQuvx%2BxE7hUKBVBEVi9PljrDKbHlle6xqOXx8sOwPPTncMambK&noverify=0&group_code=949700262\" target=\"_blank\">QQ Group</a>&nbsp;&nbsp;")
.. "<a href=\"https://github.com/EasyTier/EasyTier/releases\" target=\"_blank\"><img alt=\"\" src=\"https://img.shields.io/github/v/tag/EasyTier/EasyTier?include_prereleases&logo=github&label=pre-releases&link=https%3A%2F%2Fgithub.com%2FEasyTier%2FEasyTier%2Freleases\" style=\"vertical-align:middle;\"></a>&nbsp;"
.. "<a href=\"https://github.com/EasyTier/EasyTier/releases\" target=\"_blank\"><img alt=\"\" src=\"https://img.shields.io/github/v/tag/EasyTier/EasyTier?include_prereleases&logo=github&label=tag&link=https%3A%2F%2Fgithub.com%2FEasyTier%2FEasyTier%2Freleases\" style=\"vertical-align:middle;\"></a>&nbsp;"
.. "<a href=\"https://github.com/EasyTier/EasyTier/releases\" target=\"_blank\"><img alt=\"\" src=\"https://img.shields.io/github/v/release/EasyTier/EasyTier?logo=github&link=https%3A%2F%2Fgithub.com%2FEasyTier%2FEasyTier%2Freleases\" style=\"vertical-align:middle;\"></a>&nbsp;"
.. "<a href=\"https://github.com/EasyTier/luci-app-easytier/releases\" target=\"_blank\"><img alt=\"\" src=\"https://img.shields.io/github/v/release/EasyTier/luci-app-easytier?logo=openwrt&label=luci&link=https%3A%2F%2Fgithub.com%2FEasyTier%2Fluci-app-easytier%2Freleases\" style=\"vertical-align:middle;\"></a>"
m.pageaction = false

View File

@ -372,7 +372,7 @@ local translate = i18n.translate
<label style="display:flex;align-items:center;font-size:13px;font-weight:500;color:var(--text-primary);margin-bottom:4px;">
<span><%=translate("Fallback Version")%></span>
<a href="https://github.com/EasyTier/EasyTier/releases" target="_blank" style="margin-left:8px;line-height:1;">
<img alt="" src="https://img.shields.io/github/v/tag/EasyTier/EasyTier?include_prereleases&logo=github&label=pre-releases&link=https%3A%2F%2Fgithub.com%2FEasyTier%2FEasyTier%2Freleases" style="height:13px;vertical-align:middle;">
<img alt="" src="https://img.shields.io/github/v/tag/EasyTier/EasyTier?include_prereleases&logo=github&label=tag&link=https%3A%2F%2Fgithub.com%2FEasyTier%2FEasyTier%2Freleases" style="height:13px;vertical-align:middle;">
</a>
<a href="https://github.com/EasyTier/EasyTier/releases" target="_blank" style="margin-left:4px;line-height:1;">
<img alt="" src="https://img.shields.io/github/v/release/EasyTier/EasyTier?logo=github&link=https%3A%2F%2Fgithub.com%2FEasyTier%2FEasyTier%2Freleases" style="height:13px;vertical-align:middle;">
@ -1378,7 +1378,7 @@ function cancelDownload() {
xhr.open('POST', '<%=url("admin", "vpn", "easytier", "cancel_download")%>', true);
xhr.onload = function() {
closeDownloadModal();
showAlert('info', '<%=translate("Download cancelled")%>');
showAlert('info', '<%=translate("Cancelling download, may take up to 5 minutes to stop the current task")%>');
};
xhr.send();
}

View File

@ -388,6 +388,76 @@ input:disabled + .slider {
color: white;
border: none;
}
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.5);
z-index: 9999;
align-items: center;
justify-content: center;
}
.modal.show {
display: flex;
}
.modal-content {
background: var(--card-bg);
border-radius: 12px;
padding: 24px;
max-width: 500px;
width: 90%;
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.modal-title {
font-size: 18px;
font-weight: 600;
color: var(--text-primary);
margin-bottom: 16px;
}
.modal-message {
font-size: 14px;
color: var(--text-secondary);
margin-bottom: 24px;
line-height: 1.6;
}
.modal-buttons {
display: flex;
gap: 12px;
justify-content: flex-end;
}
.modal-btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s;
}
.modal-btn:hover {
opacity: 0.9;
}
.modal-btn-primary {
background: var(--info);
color: white;
}
.modal-btn-secondary {
background: var(--border-color);
color: var(--text-primary);
}
</style>
<div class="web-console-container">
@ -407,7 +477,12 @@ input:disabled + .slider {
<div class="form-group">
<label for="web_db_path"><%:Database File Path%></label>
<input type="text" class="form-control" id="web_db_path" name="web_db_path" placeholder="/etc/easytier/et.db">
<div style="display:flex;gap:8px;align-items:flex-start;flex-wrap:wrap;">
<input type="text" class="form-control" id="web_db_path" name="web_db_path" placeholder="/etc/easytier/et.db" style="flex:1;min-width:200px;">
<button type="button" class="btn" onclick="showResetDbModal()" style="background:var(--error);color:white;padding:12px 16px;white-space:nowrap;">
<%:Reset Database%>
</button>
</div>
<span class="description"><%:Path to the sqlite3 database file used to store all data. (-d parameter)%></span>
</div>
@ -427,7 +502,7 @@ input:disabled + .slider {
<input type="number" class="form-control" id="web_port" name="web_port" placeholder="22020" min="1" max="65535">
<span class="description"><%:Configure the server's listening port for easytier-core to connect. (-c parameter)%></span>
</div>
<div class="form-group">
<div class="checkbox-wrapper">
<label for="web_fw_web"><%:WAN access to WEB%></label>
@ -444,12 +519,26 @@ input:disabled + .slider {
<input type="number" class="form-control" id="web_api_port" name="web_api_port" placeholder="11211" min="1" max="65535">
<span class="description"><%:Listening port of the RESTful server, used as ApiHost by the web frontend. (-a parameter)%></span>
</div>
<div class="form-group">
<label for="web_api_addr"><%:API Listen Address%></label>
<input type="text" class="form-control" id="web_api_addr" name="web_api_addr" placeholder="0.0.0.0">
<span class="description"><%:Listen address for the RESTful server%>. (--api-server-addr <%:parameter%>)<br>
<%:Example%>: 0.0.0.0 (<%:all interfaces%>), 127.0.0.1 (<%:localhost only%>), :: (<%:IPv6 all%>)</span>
</div>
<div class="form-group">
<label for="web_html_port"><%:Web Interface Port%></label>
<input type="number" class="form-control" id="web_html_port" name="web_html_port" placeholder="11211" min="1" max="65535">
<span class="description"><%:Frontend listening port for the web dashboard server. Leave empty to disable. (-l parameter)%></span>
</div>
<div class="form-group">
<label for="web_html_addr"><%:Web Listen Address%></label>
<input type="text" class="form-control" id="web_html_addr" name="web_html_addr" placeholder="0.0.0.0">
<span class="description"><%:Listen address for the web dashboard server%>. (--web-server-addr <%:parameter%>)<br>
<%:Only effective when web port differs from API port or addresses differ%></span>
</div>
<div class="form-group">
<div class="checkbox-wrapper">
@ -475,6 +564,28 @@ input:disabled + .slider {
<span class="description"><%:GeoIP2 database file path used to locate the client%>. <%:Defaults to an embedded file (country-level information only)%>.<br>
<%:Recommended%>: <a href="https://github.com/P3TERX/GeoLite.mmdb" target="_blank">https://github.com/P3TERX/GeoLite.mmdb</a> (--geoip-db <%:parameter%>)</span>
</div>
<div class="form-group">
<div class="checkbox-wrapper">
<label for="web_disable_registration"><%:Disable User Registration%></label>
<label class="switch">
<input type="checkbox" id="web_disable_registration" name="web_disable_registration">
<span class="slider"></span>
</label>
</div>
<span class="description"><%:Prevent new users from registering accounts%>. (--disable-registration <%:parameter%>)</span>
</div>
<div class="form-group">
<div class="checkbox-wrapper">
<label for="web_allow_auto_create_user"><%:Allow Auto-create User%></label>
<label class="switch">
<input type="checkbox" id="web_allow_auto_create_user" name="web_allow_auto_create_user">
<span class="slider"></span>
</label>
</div>
<span class="description"><%:Automatically create local user when easytier-core connects with an unknown username%>. (--allow-auto-create-user <%:parameter%>)</span>
</div>
<div class="form-group">
<label for="web_weblog"><%:Log Level%></label>
@ -489,7 +600,130 @@ input:disabled + .slider {
<span class="description"><%:Runtime log located at%> /tmp/easytierweb.log, <%:viewable in the log section%>.<br>
<%:Log levels%>: <%:Error%> < <%:Warning%> < <%:Info%> < <%:Debug%> < <%:Trace%></span>
</div>
<!-- OIDC单点登录配置 -->
<div style="margin-top:32px;padding-top:24px;border-top:2px dashed var(--border-color);">
<div class="form-group">
<div class="checkbox-wrapper">
<label for="web_oidc_enabled"><%:Enable OIDC Single Sign-On%></label>
<label class="switch">
<input type="checkbox" id="web_oidc_enabled" name="web_oidc_enabled" onchange="toggleOIDCFields()">
<span class="slider"></span>
</label>
</div>
<span class="description"><%:Enable OIDC (OpenID Connect) for single sign-on authentication%></span>
</div>
<div id="oidc_fields" style="display:none;">
<div class="form-group">
<label for="web_oidc_issuer_url"><%:OIDC Issuer URL%> *</label>
<input type="text" class="form-control" id="web_oidc_issuer_url" name="web_oidc_issuer_url" placeholder="https://auth.example.com">
<span class="description"><%:The OIDC issuer URL for single sign-on authentication%>. (--oidc-issuer-url <%:parameter%>)</span>
</div>
<div class="form-group">
<label for="web_oidc_client_id"><%:OIDC Client ID%> *</label>
<input type="text" class="form-control" id="web_oidc_client_id" name="web_oidc_client_id" placeholder="easytier-client">
<span class="description"><%:The OIDC client ID%>. (--oidc-client-id <%:parameter%>)</span>
</div>
<div class="form-group">
<label for="web_oidc_client_secret"><%:OIDC Client Secret%> *</label>
<input type="password" class="form-control" id="web_oidc_client_secret" name="web_oidc_client_secret" placeholder="your-client-secret">
<span class="description"><%:The OIDC client secret%>. (--oidc-client-secret <%:parameter%>)<br>
<%:Can also be set via OIDC_CLIENT_SECRET environment variable%></span>
</div>
<div class="form-group">
<label for="web_oidc_redirect_url"><%:OIDC Redirect URL%> *</label>
<input type="text" class="form-control" id="web_oidc_redirect_url" name="web_oidc_redirect_url" placeholder="http://your-domain.com:11211/api/v1/auth/oidc/callback">
<span class="description"><%:The OIDC redirect URL (callback URL), must match exactly what is registered with your Identity Provider%>. (--oidc-redirect-url <%:parameter%>)<br>
<%:Example%>: http://your-domain.com:11211/api/v1/auth/oidc/callback</span>
</div>
<div class="form-group">
<label for="web_oidc_username_claim"><%:OIDC Username Claim%></label>
<input type="text" class="form-control" id="web_oidc_username_claim" name="web_oidc_username_claim" placeholder="preferred_username">
<span class="description"><%:The OIDC claim to use as the local username%>. (--oidc-username-claim <%:parameter%>)<br>
<%:Default%>: preferred_username</span>
</div>
<div class="form-group">
<label for="web_oidc_scopes"><%:OIDC Scopes%></label>
<input type="text" class="form-control" id="web_oidc_scopes" name="web_oidc_scopes" placeholder="openid profile">
<span class="description"><%:OIDC scopes to request during login%>. (--oidc-scopes <%:parameter%>)<br>
<%:Supports comma-separated values%>. <%:Default%>: openid profile</span>
</div>
<div class="form-group">
<label for="web_oidc_frontend_base_url"><%:OIDC Frontend Base URL%></label>
<input type="text" class="form-control" id="web_oidc_frontend_base_url" name="web_oidc_frontend_base_url" placeholder="http://your-domain.com:11211">
<span class="description"><%:Frontend base URL to redirect to after successful OIDC callback%>. (--oidc-frontend-base-url <%:parameter%>)<br>
<%:Required when frontend and API are deployed separately%></span>
</div>
<div class="form-group">
<div class="checkbox-wrapper">
<label for="web_oidc_disable_pkce"><%:Disable PKCE%></label>
<label class="switch">
<input type="checkbox" id="web_oidc_disable_pkce" name="web_oidc_disable_pkce">
<span class="slider"></span>
</label>
</div>
<span class="description"><%:Disable PKCE (Proof Key for Code Exchange) for OIDC authentication%>. (--oidc-disable-pkce <%:parameter%>)</span>
</div>
</div>
</div>
<!-- Webhook高级配置 -->
<div style="margin-top:32px;padding-top:24px;border-top:2px dashed var(--border-color);">
<div class="form-group">
<div class="checkbox-wrapper">
<label for="web_webhook_enabled"><%:Enable Webhook Integration%></label>
<label class="switch">
<input type="checkbox" id="web_webhook_enabled" name="web_webhook_enabled" onchange="toggleWebhookFields()">
<span class="slider"></span>
</label>
</div>
<span class="description"><%:Enable webhook for token validation and event delivery%></span>
</div>
<div id="webhook_fields" style="display:none;">
<div class="form-group">
<label for="web_webhook_url"><%:Webhook URL%></label>
<input type="text" class="form-control" id="web_webhook_url" name="web_webhook_url" placeholder="https://webhook.example.com/easytier">
<span class="description"><%:Base URL of the webhook endpoint for token validation and event delivery%>. (--webhook-url <%:parameter%>)<br>
<%:When set, incoming tokens are validated via this webhook before local fallback%></span>
</div>
<div class="form-group">
<label for="web_webhook_secret"><%:Webhook Secret%></label>
<input type="password" class="form-control" id="web_webhook_secret" name="web_webhook_secret" placeholder="your-webhook-secret">
<span class="description"><%:Shared secret used to authenticate outbound webhook calls%>. (--webhook-secret <%:parameter%>)</span>
</div>
<div class="form-group">
<label for="web_internal_auth_token"><%:Internal Auth Token%></label>
<input type="password" class="form-control" id="web_internal_auth_token" name="web_internal_auth_token" placeholder="your-internal-token">
<span class="description"><%:Token for X-Internal-Auth header%>. (--internal-auth-token <%:parameter%>)<br>
<%:When set, API requests with this header bypass session authentication%></span>
</div>
<div class="form-group">
<label for="web_web_instance_id"><%:Web Instance ID%></label>
<input type="text" class="form-control" id="web_web_instance_id" name="web_web_instance_id" placeholder="instance-1">
<span class="description"><%:Stable identifier for this easytier-web instance when routing webhook callbacks%>. (--web-instance-id <%:parameter%>)</span>
</div>
<div class="form-group">
<label for="web_web_instance_api_base_url"><%:Web Instance API Base URL%></label>
<input type="text" class="form-control" id="web_web_instance_api_base_url" name="web_web_instance_api_base_url" placeholder="http://192.168.1.1:11211">
<span class="description"><%:Reachable base URL for this easytier-web instance's internal REST API%>. (--web-instance-api-base-url <%:parameter%>)</span>
</div>
</div>
</div>
<div class="btn-group">
<button type="button" class="btn btn-primary" onclick="saveConfig()"><%:Save Configuration%></button>
</div>
@ -506,6 +740,40 @@ input:disabled + .slider {
<!-- 提示框 -->
<div id="alert_box" class="alert"></div>
<!-- 重置数据库模态窗口 -->
<div class="modal" id="reset_db_modal">
<div class="modal-content">
<div class="modal-title" style="color:var(--error);">⚠️ <%:Warning%></div>
<div class="modal-message">
<div style="color:var(--error);font-weight:600;margin-bottom:12px;">
<%:Are you sure you want to reset the database?%>
</div>
<div style="font-size:13px;line-height:1.6;color:var(--text-secondary);">
<%:This will delete all database files and cannot be recovered%>:<br>
<code id="reset_db_files" style="display:block;margin-top:8px;padding:8px;background:var(--hover-bg);border-radius:4px;word-break:break-all;font-size:12px;"></code>
</div>
</div>
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closeResetDbModal()"><%:Cancel%></button>
<button class="modal-btn" onclick="confirmResetDb()" style="background:var(--error);color:white;"><%:Delete%></button>
</div>
</div>
</div>
<!-- 重启服务模态窗口 -->
<div class="modal" id="restart_web_modal">
<div class="modal-content">
<div class="modal-title"><%:Database Reset Successful%></div>
<div class="modal-message">
<pre id="restart_web_message" style="white-space:pre-wrap;word-break:break-all;font-size:13px;line-height:1.6;"></pre>
</div>
<div class="modal-buttons">
<button class="modal-btn modal-btn-secondary" onclick="closeRestartModal()"><%:Later%></button>
<button class="modal-btn modal-btn-primary" onclick="restartWebService()"><%:Restart Now%></button>
</div>
</div>
</div>
<script type="text/javascript">//<![CDATA[
var originalConfig = {};
@ -539,12 +807,37 @@ function loadConfig() {
document.getElementById('web_port').value = data.web_port || '22020';
document.getElementById('web_fw_web').checked = data.web_fw_web === '1';
document.getElementById('web_api_port').value = data.web_api_port || '11211';
document.getElementById('web_api_addr').value = data.web_api_addr || '0.0.0.0';
document.getElementById('web_html_port').value = data.web_html_port || '11211';
document.getElementById('web_html_addr').value = data.web_html_addr || '0.0.0.0';
document.getElementById('web_fw_api').checked = data.web_fw_api === '1';
document.getElementById('web_api_host').value = data.web_api_host || '';
document.getElementById('web_geoip_db').value = data.web_geoip_db || '';
document.getElementById('web_disable_registration').checked = data.web_disable_registration === '1';
document.getElementById('web_allow_auto_create_user').checked = data.web_allow_auto_create_user === '1';
document.getElementById('web_weblog').value = data.web_weblog || 'off';
// OIDC配置
document.getElementById('web_oidc_enabled').checked = data.web_oidc_enabled === '1';
document.getElementById('web_oidc_issuer_url').value = data.web_oidc_issuer_url || '';
document.getElementById('web_oidc_client_id').value = data.web_oidc_client_id || '';
document.getElementById('web_oidc_client_secret').value = data.web_oidc_client_secret || '';
document.getElementById('web_oidc_redirect_url').value = data.web_oidc_redirect_url || '';
document.getElementById('web_oidc_username_claim').value = data.web_oidc_username_claim || 'preferred_username';
document.getElementById('web_oidc_scopes').value = data.web_oidc_scopes || 'openid profile';
document.getElementById('web_oidc_frontend_base_url').value = data.web_oidc_frontend_base_url || '';
document.getElementById('web_oidc_disable_pkce').checked = data.web_oidc_disable_pkce === '1';
toggleOIDCFields();
// Webhook配置
document.getElementById('web_webhook_enabled').checked = data.web_webhook_enabled === '1';
document.getElementById('web_webhook_url').value = data.web_webhook_url || '';
document.getElementById('web_webhook_secret').value = data.web_webhook_secret || '';
document.getElementById('web_internal_auth_token').value = data.web_internal_auth_token || '';
document.getElementById('web_web_instance_id').value = data.web_web_instance_id || '';
document.getElementById('web_web_instance_api_base_url').value = data.web_web_instance_api_base_url || '';
toggleWebhookFields();
// 保存原始配置
originalConfig = JSON.parse(JSON.stringify(data));
@ -562,11 +855,32 @@ function getCurrentConfig() {
web_port: document.getElementById('web_port').value,
web_fw_web: document.getElementById('web_fw_web').checked ? '1' : '0',
web_api_port: document.getElementById('web_api_port').value,
web_api_addr: document.getElementById('web_api_addr').value,
web_html_port: document.getElementById('web_html_port').value,
web_html_addr: document.getElementById('web_html_addr').value,
web_fw_api: document.getElementById('web_fw_api').checked ? '1' : '0',
web_api_host: document.getElementById('web_api_host').value,
web_geoip_db: document.getElementById('web_geoip_db').value,
web_weblog: document.getElementById('web_weblog').value
web_disable_registration: document.getElementById('web_disable_registration').checked ? '1' : '0',
web_allow_auto_create_user: document.getElementById('web_allow_auto_create_user').checked ? '1' : '0',
web_weblog: document.getElementById('web_weblog').value,
// OIDC
web_oidc_enabled: document.getElementById('web_oidc_enabled').checked ? '1' : '0',
web_oidc_issuer_url: document.getElementById('web_oidc_issuer_url').value,
web_oidc_client_id: document.getElementById('web_oidc_client_id').value,
web_oidc_client_secret: document.getElementById('web_oidc_client_secret').value,
web_oidc_redirect_url: document.getElementById('web_oidc_redirect_url').value,
web_oidc_username_claim: document.getElementById('web_oidc_username_claim').value,
web_oidc_scopes: document.getElementById('web_oidc_scopes').value,
web_oidc_frontend_base_url: document.getElementById('web_oidc_frontend_base_url').value,
web_oidc_disable_pkce: document.getElementById('web_oidc_disable_pkce').checked ? '1' : '0',
// Webhook
web_webhook_enabled: document.getElementById('web_webhook_enabled').checked ? '1' : '0',
web_webhook_url: document.getElementById('web_webhook_url').value,
web_webhook_secret: document.getElementById('web_webhook_secret').value,
web_internal_auth_token: document.getElementById('web_internal_auth_token').value,
web_web_instance_id: document.getElementById('web_web_instance_id').value,
web_web_instance_api_base_url: document.getElementById('web_web_instance_api_base_url').value
};
}
@ -608,6 +922,22 @@ function saveConfig() {
xhr.send(formData);
}
function toggleOIDCFields() {
var enabled = document.getElementById('web_oidc_enabled').checked;
var fields = document.getElementById('oidc_fields');
if (fields) {
fields.style.display = enabled ? 'block' : 'none';
}
}
function toggleWebhookFields() {
var enabled = document.getElementById('web_webhook_enabled').checked;
var fields = document.getElementById('webhook_fields');
if (fields) {
fields.style.display = enabled ? 'block' : 'none';
}
}
function checkWebStatus() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '<%=url("admin", "vpn", "easytier", "check_web_status")%>', true);
@ -635,6 +965,119 @@ function checkWebStatus() {
xhr.send();
}
function showResetDbModal() {
var dbPath = document.getElementById('web_db_path').value || '/etc/easytier/et.db';
var files = [
dbPath,
dbPath + '-wal',
dbPath + '-shm'
];
document.getElementById('reset_db_files').textContent = files.join('\n');
document.getElementById('reset_db_modal').classList.add('show');
}
function closeResetDbModal() {
document.getElementById('reset_db_modal').classList.remove('show');
}
function confirmResetDb() {
var dbPath = document.getElementById('web_db_path').value || '/etc/easytier/et.db';
var xhr = new XMLHttpRequest();
xhr.open('POST', '<%=url("admin", "vpn", "easytier", "reset_database")%>', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
closeResetDbModal();
if (xhr.status === 200) {
try {
var response = JSON.parse(xhr.responseText);
if (response.success) {
// 检查web服务是否运行
checkWebStatusForRestart(response.deleted_files);
} else {
showAlert('error', response.message || '<%:Reset failed%>');
}
} catch (e) {
showAlert('error', '<%:Reset failed%>');
}
} else {
showAlert('error', '<%:Reset failed%>');
}
};
xhr.onerror = function() {
closeResetDbModal();
showAlert('error', '<%:Network error%>');
};
xhr.send(JSON.stringify({db_path: dbPath}));
}
function checkWebStatusForRestart(deletedFiles) {
var xhr = new XMLHttpRequest();
xhr.open('GET', '<%=url("admin", "vpn", "easytier", "check_web_status")%>', true);
xhr.onload = function() {
if (xhr.status === 200) {
try {
var data = JSON.parse(xhr.responseText);
if (data.running) {
// Web服务正在运行弹出重启确认
showRestartModal(deletedFiles);
} else {
// Web服务未运行只显示成功提示
var message = '<%:Database reset successfully%>\n' + deletedFiles.join('\n');
showAlert('success', message);
}
} catch (e) {
// 检查失败,只显示成功提示
var message = '<%:Database reset successfully%>\n' + deletedFiles.join('\n');
showAlert('success', message);
}
} else {
// 检查失败,只显示成功提示
var message = '<%:Database reset successfully%>\n' + deletedFiles.join('\n');
showAlert('success', message);
}
};
xhr.onerror = function() {
// 检查失败,只显示成功提示
var message = '<%:Database reset successfully%>\n' + deletedFiles.join('\n');
showAlert('success', message);
};
xhr.send();
}
function showRestartModal(deletedFiles) {
var modal = document.getElementById('restart_web_modal');
var message = document.getElementById('restart_web_message');
message.textContent = '<%:Database reset successfully%>\n' + deletedFiles.join('\n') + '\n\n<%:Restart service to apply changes?%>';
modal.classList.add('show');
}
function closeRestartModal() {
document.getElementById('restart_web_modal').classList.remove('show');
}
function restartWebService() {
closeRestartModal();
showAlert('info', '<%:Restarting service...%>');
var xhr = new XMLHttpRequest();
xhr.open('GET', '<%=url("admin", "vpn", "easytier", "restart_service")%>', true);
xhr.onload = function() {
if (xhr.status === 200) {
setTimeout(function() {
showAlert('success', '<%:Service restarted successfully%>');
}, 2000);
} else {
showAlert('error', '<%:Restart failed%>');
}
};
xhr.onerror = function() {
showAlert('error', '<%:Restart failed%>');
};
xhr.send();
}
window.addEventListener('load', function() {
loadConfig();
checkWebStatus();

View File

@ -94,14 +94,6 @@ msgstr "检测IP"
msgid "Check for Updates"
msgstr "检测更新"
#: applications/luci-app-easytier/luasrc/view/easytier/easytier_log.htm:31
#: applications/luci-app-easytier/luasrc/view/easytier/easytierweb_log.htm:31
msgid "Clear Logs"
msgstr "清除日志"
msgid "Download Log"
msgstr "下载日志"
#: applications/luci-app-easytier/luasrc/view/easytier/easytier_status.htm:35
msgid "Collecting data..."
msgstr "正在获取中..."
@ -478,12 +470,6 @@ msgstr "监听端口"
msgid "Listening Protocol"
msgstr "监听协议"
#: applications/luci-app-easytier/luasrc/model/cbi/easytier.lua:710
msgid ""
"Listening port of the RESTful server, used as ApiHost by the web frontend. (-"
"a parameter)"
msgstr "restful 服务器的监听端口,作为 ApiHost 并被 web 前端使用。( -a 参数)"
#: applications/luci-app-easytier/luasrc/model/cbi/easytier.lua:564
msgid "Local Startup Parameters"
msgstr "本机启动参数"
@ -747,10 +733,6 @@ msgid ""
"console, for issuing configuration files"
msgstr "连接web控制台时识别此设备的唯一标识用于下发配置文件"
#: applications/luci-app-easytier/luasrc/view/easytier/other_upload.htm:4
msgid "Upload"
msgstr "上传"
#: applications/luci-app-easytier/luasrc/model/cbi/easytier.lua:20
msgid "Upload Program"
msgstr "上传程序"
@ -826,10 +808,6 @@ msgstr "警告"
msgid "Web Configuration"
msgstr "Web配置"
#: applications/luci-app-easytier/luasrc/view/easytier/easytier_status.htm:25
msgid "Web Console"
msgstr "Web 控制台"
#: applications/luci-app-easytier/luasrc/model/cbi/easytier.lua:726
msgid "Web Interface Port"
msgstr "web界面端口"
@ -1037,9 +1015,6 @@ msgstr "自建控制台"
msgid "All Levels"
msgstr "全部级别"
msgid "Auto Refresh"
msgstr "自动刷新"
msgid "Stop Auto Refresh"
msgstr "停止自动刷新"
@ -1097,9 +1072,6 @@ msgstr "Web 控制台主页"
msgid "Service not running"
msgstr "服务未运行"
msgid "Collecting data..."
msgstr "收集数据..."
msgid "New"
msgstr "发现新版本"
@ -1121,9 +1093,6 @@ msgstr "请稍候,操作进行中..."
msgid "Restarting service..."
msgstr "正在重启服务..."
msgid "Upload Program"
msgstr "上传程序"
msgid "Upload EasyTier Binary"
msgstr "上传 EasyTier 程序"
@ -1178,9 +1147,6 @@ msgstr "仅允许上传 easytier 相关程序文件或压缩包(.zip、.tar.gz
msgid "Upload failed"
msgstr "上传失败"
msgid "Network error"
msgstr "网络错误"
msgid "Service restarted successfully"
msgstr "服务重启成功"
@ -1193,9 +1159,6 @@ msgstr "没有需要保存的更改"
msgid "Configuration"
msgstr "配置"
msgid "Save Configuration"
msgstr "保存并应用"
msgid "Configuration saved successfully"
msgstr "配置保存成功"
@ -1223,9 +1186,6 @@ msgstr "包含EasyTier相关程序的压缩包"
msgid "Drag *.tar.gz / *.zip / easytier binaries here or click to select file"
msgstr "支持拖放 *.tar.gz / *.zip 压缩包和 easytier 二进制程序到此处,或点击选择文件"
msgid "Used when unable to fetch the latest version during online download"
msgstr "在线下载时无法获取最新版本时使用的回退版本"
msgid "Error: Program not running! Please start the program and refresh"
msgstr "错误:程序未运行!请先启动程序后刷新"
@ -1277,9 +1237,6 @@ msgstr "GeoIP 数据库路径"
msgid "Connection Information"
msgstr "连接信息"
msgid "Node Info"
msgstr "本机信息"
msgid "Peer List"
msgstr "节点列表"
@ -1445,9 +1402,6 @@ msgstr "值"
msgid "Labels"
msgstr "标签"
msgid "Listener"
msgstr "监听器"
msgid "Client Config Start"
msgstr "客户端配置开始"
@ -1688,9 +1642,6 @@ msgstr "无"
msgid "Domain"
msgstr "域名"
msgid "Failed to connect to RPC server. Please check if the RPC port is set to 15888 in the configuration."
msgstr "无法连接到 RPC 服务器,请检查配置中的 RPC 端口是否设置为 15888"
msgid "Configuration File Content"
msgstr "配置文件内容"
@ -1719,7 +1670,7 @@ msgid "Fallback Version"
msgstr "备用版本"
msgid "Used when unable to fetch the latest version during online download"
msgstr "用于无法获取GitHub最新版本时在线下载使用的版本"
msgstr "用于启动脚本里无法获取GitHub最新版本时在线下载回退使用的版本"
msgid "Save Configuration"
msgstr "保存并应用"
@ -1965,6 +1916,9 @@ msgstr "取消下载"
msgid "Download cancelled"
msgstr "已取消下载"
msgid "Cancelling download, may take up to 5 minutes to stop the current task"
msgstr "正在取消下载当前任务可能需要最多5分钟才能停止"
msgid "All download attempts failed"
msgstr "所有下载地址均下载失败请确认github加速镜像地址有效或使用手动上传"
@ -1979,3 +1933,169 @@ msgstr "重试"
msgid "Close"
msgstr "关闭"
msgid "API Listen Address"
msgstr "API监听地址"
msgid "Listen address for the RESTful server"
msgstr "RESTful服务器的监听地址"
msgid "all interfaces"
msgstr "所有接口"
msgid "localhost only"
msgstr "仅本地"
msgid "IPv6 all"
msgstr "IPv6所有接口"
msgid "Web Listen Address"
msgstr "Web监听地址"
msgid "Listen address for the web dashboard server"
msgstr "Web控制台服务器的监听地址"
msgid "Only effective when web port differs from API port or addresses differ"
msgstr "仅在Web端口与API端口不同或地址不同时生效"
msgid "Disable User Registration"
msgstr "禁用用户注册"
msgid "Prevent new users from registering accounts"
msgstr "阻止新用户注册账户"
msgid "Allow Auto-create User"
msgstr "允许自动创建用户"
msgid "Automatically create local user when easytier-core connects with an unknown username"
msgstr "当easytier-core使用未知用户名连接时自动创建本地用户"
msgid "Enable OIDC Single Sign-On"
msgstr "启用OIDC单点登录"
msgid "Enable OIDC (OpenID Connect) for single sign-on authentication"
msgstr "启用OIDCOpenID Connect单点登录认证"
msgid "OIDC Issuer URL"
msgstr "OIDC发行者URL"
msgid "The OIDC issuer URL for single sign-on authentication"
msgstr "用于单点登录认证的OIDC发行者URL"
msgid "OIDC Client ID"
msgstr "OIDC客户端ID"
msgid "The OIDC client ID"
msgstr "OIDC客户端ID"
msgid "OIDC Client Secret"
msgstr "OIDC客户端密钥"
msgid "The OIDC client secret"
msgstr "OIDC客户端密钥"
msgid "Can also be set via OIDC_CLIENT_SECRET environment variable"
msgstr "也可以通过OIDC_CLIENT_SECRET环境变量设置"
msgid "OIDC Redirect URL"
msgstr "OIDC重定向URL"
msgid "The OIDC redirect URL (callback URL), must match exactly what is registered with your Identity Provider"
msgstr "OIDC重定向URL回调URL必须与身份提供商注册的完全匹配"
msgid "OIDC Username Claim"
msgstr "OIDC用户名声明"
msgid "The OIDC claim to use as the local username"
msgstr "用作本地用户名的OIDC声明"
msgid "OIDC Scopes"
msgstr "OIDC作用域"
msgid "OIDC scopes to request during login"
msgstr "登录时请求的OIDC作用域"
msgid "Supports comma-separated values"
msgstr "支持逗号分隔的值"
msgid "OIDC Frontend Base URL"
msgstr "OIDC前端基础URL"
msgid "Frontend base URL to redirect to after successful OIDC callback"
msgstr "OIDC回调成功后重定向到的前端基础URL"
msgid "Required when frontend and API are deployed separately"
msgstr "当前端和API分开部署时需要"
msgid "Disable PKCE"
msgstr "禁用PKCE"
msgid "Disable PKCE (Proof Key for Code Exchange) for OIDC authentication"
msgstr "禁用OIDC认证的PKCE代码交换证明密钥"
msgid "Enable Webhook Integration"
msgstr "启用Webhook集成"
msgid "Enable webhook for token validation and event delivery"
msgstr "启用webhook进行令牌验证和事件传递"
msgid "Webhook URL"
msgstr "Webhook URL"
msgid "Base URL of the webhook endpoint for token validation and event delivery"
msgstr "用于令牌验证和事件传递的webhook端点基础URL"
msgid "When set, incoming tokens are validated via this webhook before local fallback"
msgstr "设置后传入的令牌将通过此webhook验证然后再进行本地回退"
msgid "Webhook Secret"
msgstr "Webhook密钥"
msgid "Shared secret used to authenticate outbound webhook calls"
msgstr "用于验证出站webhook调用的共享密钥"
msgid "Internal Auth Token"
msgstr "内部认证令牌"
msgid "Token for X-Internal-Auth header"
msgstr "X-Internal-Auth头的令牌"
msgid "When set, API requests with this header bypass session authentication"
msgstr "设置后带有此头的API请求将绕过会话认证"
msgid "Web Instance ID"
msgstr "Web实例ID"
msgid "Stable identifier for this easytier-web instance when routing webhook callbacks"
msgstr "路由webhook回调时此easytier-web实例的稳定标识符"
msgid "Web Instance API Base URL"
msgstr "Web实例API基础URL"
msgid "Reachable base URL for this easytier-web instance's internal REST API"
msgstr "此easytier-web实例内部REST API的可访问基础URL"
msgid "Reset Database"
msgstr "重置数据库"
msgid "Are you sure you want to reset the database?"
msgstr "确定要重置数据库吗?"
msgid "This will delete all database files and cannot be recovered"
msgstr "这将删除所有数据库文件,不可恢复"
msgid "Database reset successfully"
msgstr "数据库重置成功"
msgid "Reset failed"
msgstr "重置失败"
msgid "No database files found or deletion failed"
msgstr "未找到数据库文件或删除失败"
msgid "Database Reset Successful"
msgstr "数据库重置成功"
msgid "Restart service to apply changes?"
msgstr "重启服务以生效?"

View File

@ -658,11 +658,34 @@ start_web() {
api_port="$(uci -q get easytier.@easytier[0].web_api_port)"
html_port="$(uci -q get easytier.@easytier[0].web_html_port)"
weblog="$(uci -q get easytier.@easytier[0].web_weblog)"
weblog="${weblog:-off}"
weblog="${weblog:-off}"
api_host="$(uci -q get easytier.@easytier[0].web_api_host)"
geoip_db="$(uci -q get easytier.@easytier[0].web_geoip_db)"
fw_web="$(uci -q get easytier.@easytier[0].web_fw_web || echo 0)"
fw_api="$(uci -q get easytier.@easytier[0].web_fw_api || echo 0)"
api_addr="$(uci -q get easytier.@easytier[0].web_api_addr)"
html_addr="$(uci -q get easytier.@easytier[0].web_html_addr)"
disable_registration="$(uci -q get easytier.@easytier[0].web_disable_registration || echo 0)"
allow_auto_create_user="$(uci -q get easytier.@easytier[0].web_allow_auto_create_user || echo 0)"
# OIDC配置
oidc_enabled="$(uci -q get easytier.@easytier[0].web_oidc_enabled || echo 0)"
oidc_issuer_url="$(uci -q get easytier.@easytier[0].web_oidc_issuer_url)"
oidc_client_id="$(uci -q get easytier.@easytier[0].web_oidc_client_id)"
oidc_client_secret="$(uci -q get easytier.@easytier[0].web_oidc_client_secret)"
oidc_redirect_url="$(uci -q get easytier.@easytier[0].web_oidc_redirect_url)"
oidc_username_claim="$(uci -q get easytier.@easytier[0].web_oidc_username_claim)"
oidc_scopes="$(uci -q get easytier.@easytier[0].web_oidc_scopes)"
oidc_frontend_base_url="$(uci -q get easytier.@easytier[0].web_oidc_frontend_base_url)"
oidc_disable_pkce="$(uci -q get easytier.@easytier[0].web_oidc_disable_pkce || echo 0)"
# Webhook配置
webhook_enabled="$(uci -q get easytier.@easytier[0].web_webhook_enabled || echo 0)"
webhook_url="$(uci -q get easytier.@easytier[0].web_webhook_url)"
webhook_secret="$(uci -q get easytier.@easytier[0].web_webhook_secret)"
internal_auth_token="$(uci -q get easytier.@easytier[0].web_internal_auth_token)"
web_instance_id="$(uci -q get easytier.@easytier[0].web_web_instance_id)"
web_instance_api_base_url="$(uci -q get easytier.@easytier[0].web_web_instance_api_base_url)"
procd_open_instance "easytier_web"
get_tz
[ -z "$SET_TZ" ] || procd_set_param env TZ="$SET_TZ"
@ -678,6 +701,31 @@ start_web() {
else
web_cmd="${web_cmd} -l $html_port"
fi
# 基础配置参数
[ -n "$api_addr" ] && [ "$api_addr" != "0.0.0.0" ] && web_cmd="${web_cmd} --api-server-addr $api_addr"
[ -n "$html_addr" ] && [ "$html_addr" != "0.0.0.0" ] && web_cmd="${web_cmd} --web-server-addr $html_addr"
[ "$disable_registration" = "1" ] && web_cmd="${web_cmd} --disable-registration"
[ "$allow_auto_create_user" = "1" ] && web_cmd="${web_cmd} --allow-auto-create-user"
# OIDC配置参数
if [ "$oidc_enabled" = "1" ] ; then
[ -z "$oidc_issuer_url" ] || web_cmd="${web_cmd} --oidc-issuer-url $oidc_issuer_url"
[ -z "$oidc_client_id" ] || web_cmd="${web_cmd} --oidc-client-id $oidc_client_id"
[ -z "$oidc_client_secret" ] || web_cmd="${web_cmd} --oidc-client-secret $oidc_client_secret"
[ -z "$oidc_redirect_url" ] || web_cmd="${web_cmd} --oidc-redirect-url $oidc_redirect_url"
[ -z "$oidc_username_claim" ] || web_cmd="${web_cmd} --oidc-username-claim $oidc_username_claim"
[ -z "$oidc_scopes" ] || web_cmd="${web_cmd} --oidc-scopes \"$oidc_scopes\""
[ -z "$oidc_frontend_base_url" ] || web_cmd="${web_cmd} --oidc-frontend-base-url $oidc_frontend_base_url"
[ "$oidc_disable_pkce" = "1" ] && web_cmd="${web_cmd} --oidc-disable-pkce"
fi
# Webhook配置参数
if [ "$webhook_enabled" = "1" ] ; then
[ -z "$webhook_url" ] || web_cmd="${web_cmd} --webhook-url $webhook_url"
[ -z "$webhook_secret" ] || web_cmd="${web_cmd} --webhook-secret $webhook_secret"
[ -z "$internal_auth_token" ] || web_cmd="${web_cmd} --internal-auth-token $internal_auth_token"
[ -z "$web_instance_id" ] || web_cmd="${web_cmd} --web-instance-id $web_instance_id"
[ -z "$web_instance_api_base_url" ] || web_cmd="${web_cmd} --web-instance-api-base-url $web_instance_api_base_url"
fi
if [ "$weblog" != "off" ] ; then
web_cmd="${web_cmd} --console-log-level $weblog"
fi