diff --git a/install.sh b/install.sh
index 42de8590..96e28caa 100644
--- a/install.sh
+++ b/install.sh
@@ -871,21 +871,60 @@ config_after_install() {
${xui_folder}/x-ui migrate
}
+get_releases() {
+ local releases_json
+ releases_json=$(curl -Ls "https://api.github.com/repos/Sora39831/3x-ui/releases")
+ if [[ -z "$releases_json" ]]; then
+ echo -e "${yellow}正在尝试通过 IPv4 获取版本...${plain}"
+ releases_json=$(curl -4 -Ls "https://api.github.com/repos/Sora39831/3x-ui/releases")
+ if [[ -z "$releases_json" ]]; then
+ echo -e "${red}获取 x-ui 版本失败,可能是 GitHub API 限制,请稍后重试${plain}"
+ exit 1
+ fi
+ fi
+
+ # Parse first non-prerelease tag_name
+ latest_stable=$(echo "$releases_json" | tr '{' '\n' | grep '"prerelease":false' | head -1 | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+
+ # Parse first prerelease tag_name
+ latest_prerelease=$(echo "$releases_json" | tr '{' '\n' | grep '"prerelease":true' | head -1 | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+
+ if [[ -z "$latest_stable" && -z "$latest_prerelease" ]]; then
+ echo -e "${red}获取 x-ui 版本失败${plain}"
+ exit 1
+ fi
+}
+
+select_version() {
+ if [[ -n "$latest_stable" && -n "$latest_prerelease" ]]; then
+ echo ""
+ echo -e "${green}请选择要安装的版本:${plain}"
+ echo -e "${green}1)${plain} 最新稳定版: ${latest_stable}"
+ echo -e "${green}2)${plain} 最新预发布版: ${latest_prerelease}"
+ read -rp "请输入选择 [1-2]: " version_choice
+ while [[ "$version_choice" != "1" && "$version_choice" != "2" ]]; do
+ read -rp "无效输入,请重新输入 [1-2]: " version_choice
+ done
+ if [[ "$version_choice" == "1" ]]; then
+ tag_version="$latest_stable"
+ else
+ tag_version="$latest_prerelease"
+ fi
+ elif [[ -n "$latest_stable" ]]; then
+ tag_version="$latest_stable"
+ else
+ tag_version="$latest_prerelease"
+ fi
+}
+
install_x-ui() {
cd ${xui_folder%/x-ui}/
# 下载资源
if [ $# == 0 ]; then
- tag_version=$(curl -Ls "https://api.github.com/repos/Sora39831/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
- if [[ ! -n "$tag_version" ]]; then
- echo -e "${yellow}正在尝试通过 IPv4 获取版本...${plain}"
- tag_version=$(curl -4 -Ls "https://api.github.com/repos/Sora39831/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
- if [[ ! -n "$tag_version" ]]; then
- echo -e "${red}获取 x-ui 版本失败,可能是 GitHub API 限制,请稍后重试${plain}"
- exit 1
- fi
- fi
- echo -e "获取到 x-ui 最新版本:${tag_version},开始安装..."
+ get_releases
+ select_version
+ echo -e "获取到 x-ui 版本:${tag_version},开始安装..."
curl -4fLRo ${xui_folder}-linux-$(arch).tar.gz https://github.com/Sora39831/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz
if [[ $? -ne 0 ]]; then
echo -e "${red}下载 x-ui 失败,请确保服务器可以访问 GitHub${plain}"
diff --git a/update.sh b/update.sh
index 9d890e60..eac108f7 100755
--- a/update.sh
+++ b/update.sh
@@ -745,6 +745,47 @@ config_after_update() {
fi
}
+get_releases() {
+ local releases_json
+ releases_json=$(${curl_bin} -Ls "https://api.github.com/repos/Sora39831/3x-ui/releases" 2>/dev/null)
+ if [[ -z "$releases_json" ]]; then
+ echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
+ releases_json=$(${curl_bin} -4 -Ls "https://api.github.com/repos/Sora39831/3x-ui/releases" 2>/dev/null)
+ if [[ -z "$releases_json" ]]; then
+ _fail "ERROR: Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later"
+ fi
+ fi
+
+ latest_stable=$(echo "$releases_json" | tr '{' '\n' | grep '"prerelease":false' | head -1 | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+ latest_prerelease=$(echo "$releases_json" | tr '{' '\n' | grep '"prerelease":true' | head -1 | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
+
+ if [[ -z "$latest_stable" && -z "$latest_prerelease" ]]; then
+ _fail "ERROR: Failed to fetch x-ui version"
+ fi
+}
+
+select_version() {
+ if [[ -n "$latest_stable" && -n "$latest_prerelease" ]]; then
+ echo ""
+ echo -e "${green}Which version do you want to update to?${plain}"
+ echo -e "${green}1)${plain} Latest Stable: ${latest_stable}"
+ echo -e "${green}2)${plain} Latest Pre-release: ${latest_prerelease}"
+ read -rp "Please enter your choice [1-2]: " version_choice
+ while [[ "$version_choice" != "1" && "$version_choice" != "2" ]]; do
+ read -rp "Invalid input, please re-enter [1-2]: " version_choice
+ done
+ if [[ "$version_choice" == "1" ]]; then
+ tag_version="$latest_stable"
+ else
+ tag_version="$latest_prerelease"
+ fi
+ elif [[ -n "$latest_stable" ]]; then
+ tag_version="$latest_stable"
+ else
+ tag_version="$latest_prerelease"
+ fi
+}
+
update_x-ui() {
cd ${xui_folder%/x-ui}/
@@ -757,15 +798,9 @@ update_x-ui() {
echo -e "${green}Downloading new x-ui version...${plain}"
- tag_version=$(${curl_bin} -Ls "https://api.github.com/repos/Sora39831/3x-ui/releases/latest" 2>/dev/null | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
- if [[ ! -n "$tag_version" ]]; then
- echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
- tag_version=$(${curl_bin} -4 -Ls "https://api.github.com/repos/Sora39831/3x-ui/releases/latest" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/')
- if [[ ! -n "$tag_version" ]]; then
- _fail "ERROR: Failed to fetch x-ui version, it may be due to GitHub API restrictions, please try it later"
- fi
- fi
- echo -e "Got x-ui latest version: ${tag_version}, beginning the installation..."
+ get_releases
+ select_version
+ echo -e "Got x-ui version: ${tag_version}, beginning the installation..."
${curl_bin} -fLRo ${xui_folder}-linux-$(arch).tar.gz https://github.com/Sora39831/3x-ui/releases/download/${tag_version}/x-ui-linux-$(arch).tar.gz 2>/dev/null
if [[ $? -ne 0 ]]; then
echo -e "${yellow}Trying to fetch version with IPv4...${plain}"
diff --git a/web/controller/index.go b/web/controller/index.go
index dd58e5e5..be289a68 100644
--- a/web/controller/index.go
+++ b/web/controller/index.go
@@ -44,6 +44,7 @@ func (a *IndexController) initRouter(g *gin.RouterGroup) {
g.POST("/login", a.login)
g.POST("/getTwoFactorEnable", a.getTwoFactorEnable)
+ g.POST("/getTurnstileSiteKey", a.getTurnstileSiteKey)
}
// index handles the root route, redirecting logged-in users to the panel or showing the login page.
@@ -131,3 +132,11 @@ func (a *IndexController) getTwoFactorEnable(c *gin.Context) {
jsonObj(c, status, nil)
}
}
+
+// getTurnstileSiteKey returns the Cloudflare Turnstile site key for the registration form.
+func (a *IndexController) getTurnstileSiteKey(c *gin.Context) {
+ siteKey, err := a.settingService.GetTurnstileSiteKey()
+ if err == nil {
+ jsonObj(c, siteKey, nil)
+ }
+}
diff --git a/web/entity/entity.go b/web/entity/entity.go
index 40294925..90710f88 100644
--- a/web/entity/entity.go
+++ b/web/entity/entity.go
@@ -103,7 +103,9 @@ type AllSetting struct {
LdapDefaultTotalGB int `json:"ldapDefaultTotalGB" form:"ldapDefaultTotalGB"`
LdapDefaultExpiryDays int `json:"ldapDefaultExpiryDays" form:"ldapDefaultExpiryDays"`
LdapDefaultLimitIP int `json:"ldapDefaultLimitIP" form:"ldapDefaultLimitIP"`
- // JSON subscription routing rules
+
+ // Registration settings
+ TurnstileSiteKey string `json:"turnstileSiteKey" form:"turnstileSiteKey"`
}
// CheckValid validates all settings in the AllSetting struct, checking IP addresses, ports, SSL certificates, and other configuration values.
diff --git a/web/html/login.html b/web/html/login.html
index 6d98e6b1..69bc2142 100644
--- a/web/html/login.html
+++ b/web/html/login.html
@@ -59,39 +59,83 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- [[ loadingStates.spinning ? '' : '{{ i18n "login" }}' ]]
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [[ loadingStates.spinning ? '' : '{{ i18n "login" }}' ]]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ [[ loadingStates.registerSpinning ? '' : '{{ i18n "pages.login.registerTab" }}' ]]
+
+
+
+
+
+
+
+
@@ -103,23 +147,39 @@
{{template "page/body_scripts" .}}
{{template "component/aThemeSwitch" .}}