Просмотр исходного кода

feat(env): allow setting the initial URI path for the web panel (#5149)

* feat(env): allow setting the initial URI path for the web panel

* fix(setting): normalize and guard XUI_INIT_WEB_BASE_PATH default

Address Copilot review on PR #5149: an env value that is empty, whitespace, or lacks slashes (e.g. `panel`) could produce an invalid webBasePath such as `/ /` and reach the frontend un-normalized.

getEnv now trims whitespace and falls back when the value is empty; the env-derived default is passed through the existing normalizeBasePath helper (reused from node.go) so it always carries a leading and trailing slash. GetBasePath reuses the same helper instead of duplicating the slash logic.

---------

Co-authored-by: Sanaei <[email protected]>
Vladimir Avtsenov 1 день назад
Родитель
Сommit
89b1137b00
11 измененных файлов с 27 добавлено и 9 удалено
  1. 2 1
      .env.example
  2. 2 0
      CONTRIBUTING.md
  3. 1 0
      README.ar_EG.md
  4. 1 0
      README.es_ES.md
  5. 1 0
      README.fa_IR.md
  6. 1 0
      README.md
  7. 1 0
      README.ru_RU.md
  8. 1 0
      README.tr_TR.md
  9. 1 0
      README.zh_CN.md
  10. 1 0
      docker-compose.yml
  11. 15 8
      internal/web/service/setting.go

+ 2 - 1
.env.example

@@ -1,4 +1,5 @@
 XUI_DEBUG=true
 XUI_DEBUG=true
 XUI_DB_FOLDER=x-ui
 XUI_DB_FOLDER=x-ui
 XUI_LOG_FOLDER=x-ui
 XUI_LOG_FOLDER=x-ui
-XUI_BIN_FOLDER=x-ui
+XUI_BIN_FOLDER=x-ui
+XUI_INIT_WEB_BASE_PATH=/

+ 2 - 0
CONTRIBUTING.md

@@ -72,6 +72,7 @@ XUI_DEBUG=true
 XUI_DB_FOLDER=x-ui
 XUI_DB_FOLDER=x-ui
 XUI_LOG_FOLDER=x-ui
 XUI_LOG_FOLDER=x-ui
 XUI_BIN_FOLDER=x-ui
 XUI_BIN_FOLDER=x-ui
+XUI_INIT_WEB_BASE_PATH=/
 ```
 ```
 
 
 Drop the xray binary (`xray-windows-amd64.exe` on Windows, `xray-linux-amd64` on Linux, etc.) plus the matching `geoip.dat` and `geosite.dat` files into `x-ui/`. The easiest source is a [released Xray-core build](https://github.com/XTLS/Xray-core/releases). On Windows, `wintun.dll` is also required for testing TUN inbounds.
 Drop the xray binary (`xray-windows-amd64.exe` on Windows, `xray-linux-amd64` on Linux, etc.) plus the matching `geoip.dat` and `geosite.dat` files into `x-ui/`. The easiest source is a [released Xray-core build](https://github.com/XTLS/Xray-core/releases). On Windows, `wintun.dll` is also required for testing TUN inbounds.
@@ -254,6 +255,7 @@ For deeper notes on the frontend toolchain see [`frontend/README.md`](frontend/R
 | `XUI_DB_FOLDER` | platform default | Where `x-ui.db` lives |
 | `XUI_DB_FOLDER` | platform default | Where `x-ui.db` lives |
 | `XUI_LOG_FOLDER` | platform default | Where `3xui.log` lives |
 | `XUI_LOG_FOLDER` | platform default | Where `3xui.log` lives |
 | `XUI_BIN_FOLDER` | `bin` | Where the xray binary, geo files, and xray `config.json` live |
 | `XUI_BIN_FOLDER` | `bin` | Where the xray binary, geo files, and xray `config.json` live |
+| `XUI_INIT_WEB_BASE_PATH` | `/` | The initial URI path for the web panel |
 | `XUI_DB_TYPE` | `sqlite` | Set to `postgres` to use PostgreSQL via `XUI_DB_DSN` |
 | `XUI_DB_TYPE` | `sqlite` | Set to `postgres` to use PostgreSQL via `XUI_DB_DSN` |
 | `XUI_DB_DSN` | — | PostgreSQL DSN when `XUI_DB_TYPE=postgres` |
 | `XUI_DB_DSN` | — | PostgreSQL DSN when `XUI_DB_TYPE=postgres` |
 
 

+ 1 - 0
README.ar_EG.md

@@ -130,6 +130,7 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
 | `XUI_DB_FOLDER` | مجلد ملف قاعدة بيانات SQLite | `/etc/x-ui` |
 | `XUI_DB_FOLDER` | مجلد ملف قاعدة بيانات SQLite | `/etc/x-ui` |
 | `XUI_DB_MAX_OPEN_CONNS` | الحد الأقصى للاتصالات المفتوحة (تجمّع PostgreSQL) | — |
 | `XUI_DB_MAX_OPEN_CONNS` | الحد الأقصى للاتصالات المفتوحة (تجمّع PostgreSQL) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | الحد الأقصى للاتصالات الخاملة (تجمّع PostgreSQL) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | الحد الأقصى للاتصالات الخاملة (تجمّع PostgreSQL) | — |
+| `XUI_INIT_WEB_BASE_PATH` | مسار URI الأولي للوحة الويب | `/` |
 | `XUI_ENABLE_FAIL2BAN` | تفعيل فرض حدود IP المعتمد على Fail2ban | `true` |
 | `XUI_ENABLE_FAIL2BAN` | تفعيل فرض حدود IP المعتمد على Fail2ban | `true` |
 | `XUI_LOG_LEVEL` | مستوى السجل (`debug`، `info`، `warning`، `error`) | `info` |
 | `XUI_LOG_LEVEL` | مستوى السجل (`debug`، `info`، `warning`، `error`) | `info` |
 | `XUI_DEBUG` | تفعيل وضع التصحيح | `false` |
 | `XUI_DEBUG` | تفعيل وضع التصحيح | `false` |

+ 1 - 0
README.es_ES.md

@@ -130,6 +130,7 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
 | `XUI_DB_FOLDER` | Directorio del archivo de base de datos SQLite | `/etc/x-ui` |
 | `XUI_DB_FOLDER` | Directorio del archivo de base de datos SQLite | `/etc/x-ui` |
 | `XUI_DB_MAX_OPEN_CONNS` | Máximo de conexiones abiertas (pool de PostgreSQL) | — |
 | `XUI_DB_MAX_OPEN_CONNS` | Máximo de conexiones abiertas (pool de PostgreSQL) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | Máximo de conexiones inactivas (pool de PostgreSQL) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | Máximo de conexiones inactivas (pool de PostgreSQL) | — |
+| `XUI_INIT_WEB_BASE_PATH` | La ruta URI inicial para el panel web | `/` |
 | `XUI_ENABLE_FAIL2BAN` | Habilitar la aplicación de límites de IP basada en Fail2ban | `true` |
 | `XUI_ENABLE_FAIL2BAN` | Habilitar la aplicación de límites de IP basada en Fail2ban | `true` |
 | `XUI_LOG_LEVEL` | Nivel de registro (`debug`, `info`, `warning`, `error`) | `info` |
 | `XUI_LOG_LEVEL` | Nivel de registro (`debug`, `info`, `warning`, `error`) | `info` |
 | `XUI_DEBUG` | Habilitar el modo de depuración | `false` |
 | `XUI_DEBUG` | Habilitar el modo de depuración | `false` |

+ 1 - 0
README.fa_IR.md

@@ -130,6 +130,7 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
 | `XUI_DB_FOLDER` | پوشه‌ی فایل پایگاه‌داده‌ی SQLite | `/etc/x-ui` |
 | `XUI_DB_FOLDER` | پوشه‌ی فایل پایگاه‌داده‌ی SQLite | `/etc/x-ui` |
 | `XUI_DB_MAX_OPEN_CONNS` | حداکثر اتصالات باز (استخر PostgreSQL) | — |
 | `XUI_DB_MAX_OPEN_CONNS` | حداکثر اتصالات باز (استخر PostgreSQL) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | حداکثر اتصالات بی‌کار (استخر PostgreSQL) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | حداکثر اتصالات بی‌کار (استخر PostgreSQL) | — |
+| `XUI_INIT_WEB_BASE_PATH` | مسیر URI اولیه برای پنل وب | `/` |
 | `XUI_ENABLE_FAIL2BAN` | فعال‌سازی اعمال محدودیت IP مبتنی بر Fail2ban | `true` |
 | `XUI_ENABLE_FAIL2BAN` | فعال‌سازی اعمال محدودیت IP مبتنی بر Fail2ban | `true` |
 | `XUI_LOG_LEVEL` | سطح گزارش‌گیری (`debug`، `info`، `warning`، `error`) | `info` |
 | `XUI_LOG_LEVEL` | سطح گزارش‌گیری (`debug`، `info`، `warning`، `error`) | `info` |
 | `XUI_DEBUG` | فعال‌سازی حالت دیباگ | `false` |
 | `XUI_DEBUG` | فعال‌سازی حالت دیباگ | `false` |

+ 1 - 0
README.md

@@ -130,6 +130,7 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
 | `XUI_DB_FOLDER` | Directory for the SQLite database file | `/etc/x-ui` |
 | `XUI_DB_FOLDER` | Directory for the SQLite database file | `/etc/x-ui` |
 | `XUI_DB_MAX_OPEN_CONNS` | Maximum open connections (PostgreSQL pool) | — |
 | `XUI_DB_MAX_OPEN_CONNS` | Maximum open connections (PostgreSQL pool) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | Maximum idle connections (PostgreSQL pool) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | Maximum idle connections (PostgreSQL pool) | — |
+| `XUI_INIT_WEB_BASE_PATH` | The initial URI path for the web panel | `/` |
 | `XUI_ENABLE_FAIL2BAN` | Enable Fail2ban-based IP-limit enforcement | `true` |
 | `XUI_ENABLE_FAIL2BAN` | Enable Fail2ban-based IP-limit enforcement | `true` |
 | `XUI_LOG_LEVEL` | Log verbosity (`debug`, `info`, `warning`, `error`) | `info` |
 | `XUI_LOG_LEVEL` | Log verbosity (`debug`, `info`, `warning`, `error`) | `info` |
 | `XUI_DEBUG` | Enable debug mode | `false` |
 | `XUI_DEBUG` | Enable debug mode | `false` |

+ 1 - 0
README.ru_RU.md

@@ -130,6 +130,7 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
 | `XUI_DB_FOLDER` | Каталог для файла базы данных SQLite | `/etc/x-ui` |
 | `XUI_DB_FOLDER` | Каталог для файла базы данных SQLite | `/etc/x-ui` |
 | `XUI_DB_MAX_OPEN_CONNS` | Максимум открытых соединений (пул PostgreSQL) | — |
 | `XUI_DB_MAX_OPEN_CONNS` | Максимум открытых соединений (пул PostgreSQL) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | Максимум простаивающих соединений (пул PostgreSQL) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | Максимум простаивающих соединений (пул PostgreSQL) | — |
+| `XUI_INIT_WEB_BASE_PATH` | Начальный URI-путь для веб-панели | `/` |
 | `XUI_ENABLE_FAIL2BAN` | Включить применение лимитов IP на основе Fail2ban | `true` |
 | `XUI_ENABLE_FAIL2BAN` | Включить применение лимитов IP на основе Fail2ban | `true` |
 | `XUI_LOG_LEVEL` | Уровень логирования (`debug`, `info`, `warning`, `error`) | `info` |
 | `XUI_LOG_LEVEL` | Уровень логирования (`debug`, `info`, `warning`, `error`) | `info` |
 | `XUI_DEBUG` | Включить режим отладки | `false` |
 | `XUI_DEBUG` | Включить режим отладки | `false` |

+ 1 - 0
README.tr_TR.md

@@ -130,6 +130,7 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
 | `XUI_DB_FOLDER` | SQLite veritabanı dizini | `/etc/x-ui` |
 | `XUI_DB_FOLDER` | SQLite veritabanı dizini | `/etc/x-ui` |
 | `XUI_DB_MAX_OPEN_CONNS` | Maksimum açık bağlantı sayısı (PostgreSQL havuzu) | — |
 | `XUI_DB_MAX_OPEN_CONNS` | Maksimum açık bağlantı sayısı (PostgreSQL havuzu) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | Maksimum boşta bekleme bağlantısı (PostgreSQL havuzu) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | Maksimum boşta bekleme bağlantısı (PostgreSQL havuzu) | — |
+| `XUI_INIT_WEB_BASE_PATH` | Web paneli için başlangıç URI yolu | `/` |
 | `XUI_ENABLE_FAIL2BAN` | Fail2ban tabanlı IP limit uygulamasını etkinleştir | `true` |
 | `XUI_ENABLE_FAIL2BAN` | Fail2ban tabanlı IP limit uygulamasını etkinleştir | `true` |
 | `XUI_LOG_LEVEL` | Günlük (Log) ayrıntı seviyesi (`debug`, `info`, `warning`, `error`) | `info` |
 | `XUI_LOG_LEVEL` | Günlük (Log) ayrıntı seviyesi (`debug`, `info`, `warning`, `error`) | `info` |
 | `XUI_DEBUG` | Hata ayıklama (debug) modunu etkinleştir | `false` |
 | `XUI_DEBUG` | Hata ayıklama (debug) modunu etkinleştir | `false` |

+ 1 - 0
README.zh_CN.md

@@ -130,6 +130,7 @@ docker run -d --cap-add=NET_ADMIN --cap-add=NET_RAW ... ghcr.io/mhsanaei/3x-ui
 | `XUI_DB_FOLDER` | SQLite 数据库文件所在目录 | `/etc/x-ui` |
 | `XUI_DB_FOLDER` | SQLite 数据库文件所在目录 | `/etc/x-ui` |
 | `XUI_DB_MAX_OPEN_CONNS` | 最大打开连接数(PostgreSQL 连接池) | — |
 | `XUI_DB_MAX_OPEN_CONNS` | 最大打开连接数(PostgreSQL 连接池) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | 最大空闲连接数(PostgreSQL 连接池) | — |
 | `XUI_DB_MAX_IDLE_CONNS` | 最大空闲连接数(PostgreSQL 连接池) | — |
+| `XUI_INIT_WEB_BASE_PATH` | Web 面板的初始 URI 路径 | `/` |
 | `XUI_ENABLE_FAIL2BAN` | 启用基于 Fail2ban 的 IP 限制 | `true` |
 | `XUI_ENABLE_FAIL2BAN` | 启用基于 Fail2ban 的 IP 限制 | `true` |
 | `XUI_LOG_LEVEL` | 日志级别(`debug`、`info`、`warning`、`error`) | `info` |
 | `XUI_LOG_LEVEL` | 日志级别(`debug`、`info`、`warning`、`error`) | `info` |
 | `XUI_DEBUG` | 启用调试模式 | `false` |
 | `XUI_DEBUG` | 启用调试模式 | `false` |

+ 1 - 0
docker-compose.yml

@@ -18,6 +18,7 @@ services:
     environment:
     environment:
       XRAY_VMESS_AEAD_FORCED: "false"
       XRAY_VMESS_AEAD_FORCED: "false"
       XUI_ENABLE_FAIL2BAN: "true"
       XUI_ENABLE_FAIL2BAN: "true"
+      # XUI_INIT_WEB_BASE_PATH: "/"
       # To use PostgreSQL instead of the default SQLite, run:
       # To use PostgreSQL instead of the default SQLite, run:
       #   docker compose --profile postgres up -d
       #   docker compose --profile postgres up -d
       # and uncomment the two lines below.
       # and uncomment the two lines below.

+ 15 - 8
internal/web/service/setting.go

@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"fmt"
 	"net"
 	"net"
 	"net/http"
 	"net/http"
+	"os"
 	"reflect"
 	"reflect"
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
@@ -37,7 +38,7 @@ var defaultValueMap = map[string]string{
 	"secret":                      random.Seq(32),
 	"secret":                      random.Seq(32),
 	"panelGuid":                   uuid.NewString(),
 	"panelGuid":                   uuid.NewString(),
 	"apiToken":                    "",
 	"apiToken":                    "",
-	"webBasePath":                 "/",
+	"webBasePath":                 normalizeBasePath(getEnv("XUI_INIT_WEB_BASE_PATH", "/")),
 	"sessionMaxAge":               "360",
 	"sessionMaxAge":               "360",
 	"trustedProxyCIDRs":           "127.0.0.1/32,::1/128",
 	"trustedProxyCIDRs":           "127.0.0.1/32,::1/128",
 	"pageSize":                    "25",
 	"pageSize":                    "25",
@@ -237,6 +238,18 @@ func mustString(value string, _ error) string {
 	return value
 	return value
 }
 }
 
 
+func getEnv(key, fallback string) string {
+	val, ok := os.LookupEnv(key)
+	if !ok {
+		return fallback
+	}
+	val = strings.TrimSpace(val)
+	if val == "" {
+		return fallback
+	}
+	return val
+}
+
 func (s *SettingService) ResetSettings() error {
 func (s *SettingService) ResetSettings() error {
 	db := database.GetDB()
 	db := database.GetDB()
 	err := db.Where("1 = 1").Delete(model.Setting{}).Error
 	err := db.Where("1 = 1").Delete(model.Setting{}).Error
@@ -587,13 +600,7 @@ func (s *SettingService) GetBasePath() (string, error) {
 	if err != nil {
 	if err != nil {
 		return "", err
 		return "", err
 	}
 	}
-	if !strings.HasPrefix(basePath, "/") {
-		basePath = "/" + basePath
-	}
-	if !strings.HasSuffix(basePath, "/") {
-		basePath += "/"
-	}
-	return basePath, nil
+	return normalizeBasePath(basePath), nil
 }
 }
 
 
 func (s *SettingService) GetTimeLocation() (*time.Location, error) {
 func (s *SettingService) GetTimeLocation() (*time.Location, error) {