Explorar o código

Merge pull request #376 from 0xJacky/docker/s6-overlay

Enhance nginx ui docker image
Jacky hai 1 ano
pai
achega
1ac1d150c0
Modificáronse 86 ficheiros con 2812 adicións e 1622 borrados
  1. 1 0
      .gitignore
  2. 21 10
      Dockerfile
  3. 1 1
      README-es.md
  4. 1 1
      README-vi_VN.md
  5. 1 1
      README-zh_CN.md
  6. 1 1
      README-zh_TW.md
  7. 1 1
      README.md
  8. 37 85
      api/cluster/environment.go
  9. 1 0
      api/cluster/router.go
  10. 19 3
      api/nginx/control.go
  11. 43 57
      api/nginx/nginx.go
  12. 2 2
      api/system/install.go
  13. 1 0
      api/system/router.go
  14. 10 20
      api/system/settings.go
  15. 30 22
      app.example.ini
  16. 13 13
      app/package.json
  17. 612 281
      app/pnpm-lock.yaml
  18. 3 0
      app/src/App.vue
  19. 13 1
      app/src/api/environment.ts
  20. 1 1
      app/src/api/ngx.ts
  21. 4 0
      app/src/api/settings.ts
  22. 19 0
      app/src/api/translations.ts
  23. 13 8
      app/src/components/ChatGPT/ChatGPT.vue
  24. 3 1
      app/src/components/EnvIndicator/EnvIndicator.vue
  25. 13 10
      app/src/components/NginxControl/NginxControl.vue
  26. 27 7
      app/src/components/NodeSelector/NodeSelector.vue
  27. 11 17
      app/src/components/SetLanguage/SetLanguage.vue
  28. 1 1
      app/src/language/LINGUAS
  29. 107 85
      app/src/language/en/app.po
  30. 112 85
      app/src/language/es/app.po
  31. 107 85
      app/src/language/fr_FR/app.po
  32. 112 85
      app/src/language/ko_KR/app.po
  33. 103 79
      app/src/language/messages.pot
  34. 112 85
      app/src/language/ru_RU/app.po
  35. 107 85
      app/src/language/vi_VN/app.po
  36. BIN=BIN
      app/src/language/zh_CN/app.mo
  37. 111 85
      app/src/language/zh_CN/app.po
  38. 112 85
      app/src/language/zh_TW/app.po
  39. 9 4
      app/src/layouts/BaseLayout.vue
  40. 2 2
      app/src/layouts/SideBar.vue
  41. 1 0
      app/src/pinia/moudule/settings.ts
  42. 1 1
      app/src/version.json
  43. 10 5
      app/src/views/certificate/ACMEUserSelector.vue
  44. 7 1
      app/src/views/dashboard/DashBoard.vue
  45. 14 3
      app/src/views/dashboard/Environments.vue
  46. 49 8
      app/src/views/environment/Environment.vue
  47. 0 2
      app/src/views/other/Error.vue
  48. 11 2
      app/src/views/preference/BasicSettings.vue
  49. 9 3
      app/src/views/preference/OpenAISettings.vue
  50. 7 0
      app/src/views/preference/Preference.vue
  51. 1 0
      app/src/views/preference/typedef.ts
  52. 1 1
      app/version.json
  53. 12 6
      demo.Dockerfile
  54. 2 1
      docs/.vitepress/config/en.ts
  55. 2 1
      docs/.vitepress/config/zh_CN.ts
  56. 2 1
      docs/.vitepress/config/zh_TW.ts
  57. 24 0
      docs/guide/config-cluster.md
  58. 0 30
      docs/guide/config-nginx.md
  59. 25 0
      docs/guide/config-server.md
  60. 1 1
      docs/zh_CN/guide/build.md
  61. 24 0
      docs/zh_CN/guide/config-cluster.md
  62. 0 33
      docs/zh_CN/guide/config-nginx.md
  63. 24 0
      docs/zh_CN/guide/config-server.md
  64. 1 1
      docs/zh_TW/guide/build.md
  65. 26 0
      docs/zh_TW/guide/config-cluster.md
  66. 0 33
      docs/zh_TW/guide/config-nginx.md
  67. 24 0
      docs/zh_TW/guide/config-server.md
  68. 31 30
      go.mod
  69. 62 0
      go.sum
  70. 78 70
      internal/analytic/node.go
  71. 71 0
      internal/cluster/cluster.go
  72. 47 0
      internal/cluster/cluster_test.go
  73. 0 23
      internal/environment/environment.go
  74. 8 1
      internal/kernal/boot.go
  75. 74 0
      internal/kernal/skip_install.go
  76. 45 22
      internal/nginx/nginx.go
  77. 1 0
      model/environment.go
  78. 4 4
      resources/demo/app.ini
  79. 3 0
      resources/docker/nginx-ui.run
  80. 3 2
      resources/docker/nginx.conf
  81. 0 10
      resources/docker/start.sh
  82. 19 0
      settings/cluster.go
  83. 15 0
      settings/cluster_test.go
  84. 2 0
      settings/server.go
  85. 71 18
      settings/settings.go
  86. 103 0
      settings/settings_test.go

+ 1 - 0
.gitignore

@@ -2,6 +2,7 @@
 database.db
 tmp
 node_modules
+.pnpm-store
 app.ini
 dist
 *.exe

+ 21 - 10
Dockerfile

@@ -2,17 +2,28 @@ FROM --platform=$TARGETPLATFORM uozi/nginx-ui-base:latest
 ARG TARGETOS
 ARG TARGETARCH
 ARG TARGETVARIANT
-WORKDIR /app
 EXPOSE 80 443
 
-COPY resources/docker/start.sh /app/start.sh
-COPY resources/docker/nginx.conf /usr/etc/nginx/nginx.conf
-COPY resources/docker/nginx-ui.conf /usr/etc/nginx/conf.d/nginx-ui.conf
-COPY resources/docker/nginx-ui.conf /etc/nginx/conf.d/nginx-ui.conf
-COPY nginx-ui-$TARGETOS-$TARGETARCH$TARGETVARIANT/nginx-ui /app/nginx-ui
+ENV NGINX_UI_OFFICIAL_DOCKER=true
 
-RUN cd /app && chmod a+x /app/start.sh  \
-    && rm -f /etc/nginx/conf.d/default.conf  \
-    && rm -f /usr/etc/nginx/conf.d/default.conf
+# register nginx-ui service
+COPY resources/docker/nginx-ui.run /etc/s6-overlay/s6-rc.d/nginx-ui/run
+RUN echo 'longrun' > /etc/s6-overlay/s6-rc.d/nginx-ui/type && \
+    touch /etc/s6-overlay/s6-rc.d/user/contents.d/nginx-ui
 
-ENTRYPOINT ["./start.sh"]
+# copy nginx config
+COPY resources/docker/nginx.conf /usr/local/etc/nginx/nginx.conf
+COPY resources/docker/nginx-ui.conf /usr/local/etc/nginx/conf.d/nginx-ui.conf
+
+# copy nginx-ui executable binary
+COPY nginx-ui-$TARGETOS-$TARGETARCH$TARGETVARIANT/nginx-ui /usr/local/bin/nginx-ui
+
+# remove default nginx config
+RUN rm -f /etc/nginx/conf.d/default.conf  \
+    && rm -f /usr/local/etc/nginx/conf.d/default.conf
+
+# recreate access.log and error.log
+RUN rm -f /var/log/nginx/access.log && \
+    touch /var/log/nginx/access.log && \
+    rm -f /var/log/nginx/error.log && \
+    touch /var/log/nginx/error.log

+ 1 - 1
README-es.md

@@ -235,7 +235,7 @@ pnpm build
 Primero compile la interfaz y luego ejecute el siguiente comando en el directorio raíz del proyecto.
 
 ```shell
-go build -o nginx-ui -v main.go
+go build -tags=jsoniter -ldflags "$LD_FLAGS -X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'" -o nginx-ui -v main.go
 ```
 
 ## Script para Linux

+ 1 - 1
README-vi_VN.md

@@ -298,7 +298,7 @@ pnpm build
 Vui lòng build Frontend trước, sau đó thực hiện lệnh sau trong thư mục gốc của dự án.
 
 ```shell
-go build -o nginx-ui -v main.go
+go build -tags=jsoniter -ldflags "$LD_FLAGS -X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'" -o nginx-ui -v main.go
 ```
 
 ## Tập lệnh cho Linux

+ 1 - 1
README-zh_CN.md

@@ -229,7 +229,7 @@ pnpm build
 请先完成前端编译,再回到项目的根目录执行以下命令。
 
 ```shell
-go build -o nginx-ui -v main.go
+go build -tags=jsoniter -ldflags "$LD_FLAGS -X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'" -o nginx-ui -v main.go
 ```
 
 ## Linux 安装脚本

+ 1 - 1
README-zh_TW.md

@@ -234,7 +234,7 @@ pnpm build
 請先完成前端編譯,再回到專案的根目錄執行以下命令。
 
 ```shell
-go build -o nginx-ui -v main.go
+go build -tags=jsoniter -ldflags "$LD_FLAGS -X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'" -o nginx-ui -v main.go
 ```
 
 ## Linux 安裝指令

+ 1 - 1
README.md

@@ -295,7 +295,7 @@ pnpm build
 Please build the app first, and then execute the following command in the project root directory.
 
 ```shell
-go build -o nginx-ui -v main.go
+go build -tags=jsoniter -ldflags "$LD_FLAGS -X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'" -o nginx-ui -v main.go
 ```
 
 ## Script for Linux

+ 37 - 85
api/cluster/environment.go

@@ -3,13 +3,14 @@ package cluster
 import (
 	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/internal/analytic"
-	"github.com/0xJacky/Nginx-UI/internal/environment"
+	"github.com/0xJacky/Nginx-UI/internal/cluster"
+	"github.com/0xJacky/Nginx-UI/internal/cosy"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
 	"github.com/spf13/cast"
 	"net/http"
-	"regexp"
 )
 
 func GetEnvironment(c *gin.Context) {
@@ -27,75 +28,38 @@ func GetEnvironment(c *gin.Context) {
 }
 
 func GetEnvironmentList(c *gin.Context) {
-	data, err := environment.RetrieveEnvironmentList()
-	if err != nil {
-		api.ErrHandler(c, err)
-		return
-	}
-	c.JSON(http.StatusOK, gin.H{
-		"data": data,
-	})
-}
-
-type EnvironmentManageJson struct {
-	Name          string `json:"name" binding:"required"`
-	URL           string `json:"url" binding:"required"`
-	Token         string `json:"token"  binding:"required"`
-	OperationSync bool   `json:"operation_sync"`
-	SyncApiRegex  string `json:"sync_api_regex"`
-}
-
-func validateRegex(data EnvironmentManageJson) error {
-	if data.OperationSync {
-		_, err := regexp.Compile(data.SyncApiRegex)
-		return err
-	}
-	return nil
+	cosy.Core[model.Environment](c).
+		SetFussy("name").
+		SetEqual("enabled").
+		SetTransformer(func(m *model.Environment) any {
+			return analytic.GetNode(m)
+		}).PagingList()
 }
 
 func AddEnvironment(c *gin.Context) {
-	var json EnvironmentManageJson
-	if !api.BindAndValid(c, &json) {
-		return
-	}
-	if err := validateRegex(json); err != nil {
-		api.ErrHandler(c, err)
-		return
-	}
-
-	env := model.Environment{
-		Name:          json.Name,
-		URL:           json.URL,
-		Token:         json.Token,
-		OperationSync: json.OperationSync,
-		SyncApiRegex:  json.SyncApiRegex,
-	}
-
-	envQuery := query.Environment
-
-	err := envQuery.Create(&env)
-	if err != nil {
-		api.ErrHandler(c, err)
-		return
-	}
-
-	go analytic.RestartRetrieveNodesStatus()
-
-	c.JSON(http.StatusOK, env)
+	cosy.Core[model.Environment](c).SetValidRules(gin.H{
+		"name":    "required",
+		"url":     "required,url",
+		"token":   "required",
+		"enabled": "omitempty,boolean",
+	}).ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
+		go analytic.RestartRetrieveNodesStatus()
+	}).Create()
 }
 
 func EditEnvironment(c *gin.Context) {
-	id := cast.ToInt(c.Param("id"))
-
-	var json EnvironmentManageJson
-	if !api.BindAndValid(c, &json) {
-		return
-	}
-	if err := validateRegex(json); err != nil {
-		api.ErrHandler(c, err)
-		return
-	}
+	cosy.Core[model.Environment](c).SetValidRules(gin.H{
+		"name":    "required",
+		"url":     "required,url",
+		"token":   "required",
+		"enabled": "omitempty,boolean",
+	}).ExecutedHook(func(c *cosy.Ctx[model.Environment]) {
+		go analytic.RestartRetrieveNodesStatus()
+	}).Modify()
+}
 
+func DeleteEnvironment(c *gin.Context) {
+	id := cast.ToInt(c.Param("id"))
 	envQuery := query.Environment
 
 	env, err := envQuery.FirstByID(id)
@@ -103,15 +67,7 @@ func EditEnvironment(c *gin.Context) {
 		api.ErrHandler(c, err)
 		return
 	}
-
-	_, err = envQuery.Where(envQuery.ID.Eq(env.ID)).Updates(&model.Environment{
-		Name:          json.Name,
-		URL:           json.URL,
-		Token:         json.Token,
-		OperationSync: json.OperationSync,
-		SyncApiRegex:  json.SyncApiRegex,
-	})
-
+	err = envQuery.DeleteByID(env.ID)
 	if err != nil {
 		api.ErrHandler(c, err)
 		return
@@ -119,25 +75,21 @@ func EditEnvironment(c *gin.Context) {
 
 	go analytic.RestartRetrieveNodesStatus()
 
-	GetEnvironment(c)
+	c.JSON(http.StatusNoContent, nil)
 }
 
-func DeleteEnvironment(c *gin.Context) {
-	id := cast.ToInt(c.Param("id"))
-	envQuery := query.Environment
-
-	env, err := envQuery.FirstByID(id)
-	if err != nil {
-		api.ErrHandler(c, err)
-		return
-	}
-	err = envQuery.DeleteByID(env.ID)
+func LoadEnvironmentFromSettings(c *gin.Context) {
+	err := settings.ReloadCluster()
 	if err != nil {
 		api.ErrHandler(c, err)
 		return
 	}
 
+	cluster.RegisterPredefinedNodes()
+
 	go analytic.RestartRetrieveNodesStatus()
 
-	c.JSON(http.StatusNoContent, nil)
+	c.JSON(http.StatusOK, gin.H{
+		"message": "ok",
+	})
 }

+ 1 - 0
api/cluster/router.go

@@ -5,6 +5,7 @@ import "github.com/gin-gonic/gin"
 func InitRouter(r *gin.RouterGroup) {
 	// Environment
 	r.GET("environments", GetEnvironmentList)
+	r.POST("environments/load_from_settings", LoadEnvironmentFromSettings)
 	envGroup := r.Group("environment")
 	{
 		envGroup.GET("/:id", GetEnvironment)

+ 19 - 3
api/nginx/control.go

@@ -4,6 +4,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/gin-gonic/gin"
 	"net/http"
+	"os"
 )
 
 func Reload(c *gin.Context) {
@@ -23,9 +24,24 @@ func Test(c *gin.Context) {
 }
 
 func Restart(c *gin.Context) {
-	output := nginx.Restart()
 	c.JSON(http.StatusOK, gin.H{
-		"message": output,
-		"level":   nginx.GetLogLevel(output),
+		"message": "ok",
+	})
+	go nginx.Restart()
+}
+
+func Status(c *gin.Context) {
+	pidPath := nginx.GetPIDPath()
+	lastOutput := nginx.GetLastOutput()
+
+	running := true
+	if fileInfo, err := os.Stat(pidPath); err != nil || fileInfo.Size() == 0 { // fileInfo.Size() == 0 no process id
+		running = false
+	}
+
+	c.JSON(http.StatusOK, gin.H{
+		"running": running,
+		"message": lastOutput,
+		"level":   nginx.GetLogLevel(lastOutput),
 	})
 }

+ 43 - 57
api/nginx/nginx.go

@@ -1,73 +1,59 @@
 package nginx
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
-	"github.com/0xJacky/Nginx-UI/internal/nginx"
-	"github.com/gin-gonic/gin"
-	"net/http"
-	"os"
+    "github.com/0xJacky/Nginx-UI/api"
+    "github.com/0xJacky/Nginx-UI/internal/nginx"
+    "github.com/gin-gonic/gin"
+    "net/http"
 )
 
 func BuildNginxConfig(c *gin.Context) {
-	var ngxConf nginx.NgxConfig
-	if !api.BindAndValid(c, &ngxConf) {
-		return
-	}
-	content, err := ngxConf.BuildConfig()
-	if err != nil {
-		api.ErrHandler(c, err)
-		return
-	}
-	c.JSON(http.StatusOK, gin.H{
-		"content": content,
-	})
+    var ngxConf nginx.NgxConfig
+    if !api.BindAndValid(c, &ngxConf) {
+        return
+    }
+    content, err := ngxConf.BuildConfig()
+    if err != nil {
+        api.ErrHandler(c, err)
+        return
+    }
+    c.JSON(http.StatusOK, gin.H{
+        "content": content,
+    })
 }
 
 func TokenizeNginxConfig(c *gin.Context) {
-	var json struct {
-		Content string `json:"content" binding:"required"`
-	}
+    var json struct {
+        Content string `json:"content" binding:"required"`
+    }
 
-	if !api.BindAndValid(c, &json) {
-		return
-	}
+    if !api.BindAndValid(c, &json) {
+        return
+    }
 
-	ngxConfig, err := nginx.ParseNgxConfigByContent(json.Content)
-	if err != nil {
-		api.ErrHandler(c, err)
-		return
-	}
-	c.JSON(http.StatusOK, ngxConfig)
+    ngxConfig, err := nginx.ParseNgxConfigByContent(json.Content)
+    if err != nil {
+        api.ErrHandler(c, err)
+        return
+    }
+    c.JSON(http.StatusOK, ngxConfig)
 
 }
 
 func FormatNginxConfig(c *gin.Context) {
-	var json struct {
-		Content string `json:"content" binding:"required"`
-	}
-
-	if !api.BindAndValid(c, &json) {
-		return
-	}
-	content, err := nginx.FmtCode(json.Content)
-	if err != nil {
-		api.ErrHandler(c, err)
-		return
-	}
-	c.JSON(http.StatusOK, gin.H{
-		"content": content,
-	})
-}
-
-func Status(c *gin.Context) {
-	pidPath := nginx.GetPIDPath()
-
-	running := true
-	if fileInfo, err := os.Stat(pidPath); err != nil || fileInfo.Size() == 0 { // fileInfo.Size() == 0 no process id
-		running = false
-	}
-
-	c.JSON(http.StatusOK, gin.H{
-		"running": running,
-	})
+    var json struct {
+        Content string `json:"content" binding:"required"`
+    }
+
+    if !api.BindAndValid(c, &json) {
+        return
+    }
+    content, err := nginx.FmtCode(json.Content)
+    if err != nil {
+        api.ErrHandler(c, err)
+        return
+    }
+    c.JSON(http.StatusOK, gin.H{
+        "content": content,
+    })
 }

+ 2 - 2
api/system/install.go

@@ -13,7 +13,7 @@ import (
 )
 
 func installLockStatus() bool {
-	return "" != settings.ServerSettings.JwtSecret
+	return settings.ServerSettings.SkipInstallation || "" != settings.ServerSettings.JwtSecret
 }
 
 func InstallLockCheck(c *gin.Context) {
@@ -49,7 +49,6 @@ func InstallNginxUI(c *gin.Context) {
 	if "" != json.Database {
 		settings.ServerSettings.Database = json.Database
 	}
-	settings.ReflectFrom()
 
 	err := settings.Save()
 	if err != nil {
@@ -72,6 +71,7 @@ func InstallNginxUI(c *gin.Context) {
 		api.ErrHandler(c, err)
 		return
 	}
+
 	c.JSON(http.StatusOK, gin.H{
 		"message": "ok",
 	})

+ 1 - 0
api/system/router.go

@@ -11,6 +11,7 @@ func InitPublicRouter(r *gin.RouterGroup) {
 }
 
 func InitPrivateRouter(r *gin.RouterGroup) {
+    r.GET("settings/server/name", GetServerName)
     r.GET("settings", GetSettings)
     r.POST("settings", SaveSettings)
 

+ 10 - 20
api/system/settings.go

@@ -6,9 +6,14 @@ import (
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
 	"net/http"
-	"reflect"
 )
 
+func GetServerName(c *gin.Context) {
+	c.JSON(http.StatusOK, gin.H{
+		"name": settings.ServerSettings.Name,
+	})
+}
+
 func GetSettings(c *gin.Context) {
 	c.JSON(http.StatusOK, gin.H{
 		"server":    settings.ServerSettings,
@@ -35,12 +40,10 @@ func SaveSettings(c *gin.Context) {
 		go cron.RestartLogrotate()
 	}
 
-	fillSettings(&settings.ServerSettings, &json.Server)
-	fillSettings(&settings.NginxSettings, &json.Nginx)
-	fillSettings(&settings.OpenAISettings, &json.Openai)
-	fillSettings(&settings.LogrotateSettings, &json.Logrotate)
-
-	settings.ReflectFrom()
+	settings.ProtectedFill(&settings.ServerSettings, &json.Server)
+	settings.ProtectedFill(&settings.NginxSettings, &json.Nginx)
+	settings.ProtectedFill(&settings.OpenAISettings, &json.Openai)
+	settings.ProtectedFill(&settings.LogrotateSettings, &json.Logrotate)
 
 	err := settings.Save()
 	if err != nil {
@@ -50,16 +53,3 @@ func SaveSettings(c *gin.Context) {
 
 	GetSettings(c)
 }
-
-func fillSettings(targetSettings interface{}, newSettings interface{}) {
-	s := reflect.TypeOf(targetSettings).Elem()
-	vt := reflect.ValueOf(targetSettings).Elem()
-	vn := reflect.ValueOf(newSettings).Elem()
-
-	// copy the values from new to target settings if it is not protected
-	for i := 0; i < s.NumField(); i++ {
-		if s.Field(i).Tag.Get("protected") != "true" {
-			vt.Field(i).Set(vn.Field(i))
-		}
-	}
-}

+ 30 - 22
app.example.ini

@@ -1,45 +1,53 @@
+; suppress inspection "DuplicateKeyInSection" for whole file
 [server]
 HttpPort             = 9000
 RunMode              = debug
-JwtSecret            =
-Email                =
+JwtSecret            = 
+Email                = 
 HTTPChallengePort    = 9180
 StartCmd             = bash
 Database             = database
-CADir                =
-GithubProxy          =
-NodeSecret           =
+CADir                = 
+GithubProxy          = 
+NodeSecret           = 
 Demo                 = false
 PageSize             = 10
 HttpHost             = 0.0.0.0
 CertRenewalInterval  = 7
-RecursiveNameservers =
+RecursiveNameservers = 
+SkipInstallation     = false
+Name                 = 
 
 [nginx]
 AccessLogPath = /var/log/nginx/access.log
 ErrorLogPath  = /var/log/nginx/error.log
-ConfigDir    =
-PIDPath       =
-TestConfigCmd =
-ReloadCmd     =
-RestartCmd    =
+ConfigDir     = 
+PIDPath       = 
+TestConfigCmd = 
+ReloadCmd     = 
+RestartCmd    = 
 
 [openai]
-Model =
-BaseUrl =
-Proxy =
-Token =
+Model   = 
+BaseUrl = 
+Proxy   = 
+Token   = 
 
 [casdoor]
-Endpoint =
-ClientId =
-ClientSecret =
-Certificate =
-Organization =
-Application =
-RedirectUri =
+Endpoint     = 
+ClientId     = 
+ClientSecret = 
+Certificate  = 
+Organization = 
+Application  = 
+RedirectUri  = 
 
 [logrotate]
 Enabled  = false
 CMD      = logrotate /etc/logrotate.d/nginx
 Interval = 1440
+
+[cluster]
+Node = http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true
+Node = http://10.0.0.2:9000?name=node2&node_secret=my-node-secret&enabled=true
+Node = http://10.0.0.3?name=node3&node_secret=my-node-secret&enabled=true

+ 13 - 13
app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "nginx-ui-app-next",
-  "version": "2.0.0-beta.22",
+  "version": "2.0.0-beta.23",
   "type": "module",
   "scripts": {
     "dev": "vite",
@@ -13,13 +13,16 @@
   "dependencies": {
     "@ant-design/icons-vue": "^7.0.1",
     "@formkit/auto-animate": "^0.8.2",
-    "@vue/reactivity": "^3.4.25",
-    "@vue/shared": "^3.4.25",
+    "@vue/reactivity": "^3.4.27",
+    "@vue/shared": "^3.4.27",
     "@vueuse/core": "^10.9.0",
+    "@xterm/addon-attach": "^0.11.0",
+    "@xterm/addon-fit": "^0.10.0",
+    "@xterm/xterm": "^5.5.0",
     "ant-design-vue": "^4.2.1",
     "apexcharts": "^3.49.0",
     "axios": "^1.6.8",
-    "dayjs": "^1.11.10",
+    "dayjs": "^1.11.11",
     "highlight.js": "^11.9.0",
     "lodash": "^4.17.21",
     "marked": "^10.0.0",
@@ -29,27 +32,24 @@
     "reconnecting-websocket": "^4.4.0",
     "sortablejs": "^1.15.2",
     "vite-plugin-build-id": "^0.2.8",
-    "vue": "^3.4.26",
+    "vue": "^3.4.27",
     "vue-github-button": "github:0xJacky/vue-github-button",
     "vue-router": "^4.3.2",
     "vue3-ace-editor": "2.2.4",
     "vue3-apexcharts": "1.4.4",
     "vue3-gettext": "3.0.0-beta.4",
-    "vuedraggable": "^4.1.0",
-    "@xterm/xterm": "^5.5.0",
-    "@xterm/addon-attach": "^0.11.0",
-    "@xterm/addon-fit": "^0.10.0"
+    "vuedraggable": "^4.1.0"
   },
   "devDependencies": {
     "@antfu/eslint-config-vue": "^0.43.1",
-    "@types/lodash": "^4.17.0",
+    "@types/lodash": "^4.17.1",
     "@types/nprogress": "^0.2.3",
     "@types/sortablejs": "^1.15.8",
     "@typescript-eslint/eslint-plugin": "^6.21.0",
     "@typescript-eslint/parser": "^6.21.0",
     "@vitejs/plugin-vue": "^5.0.4",
     "@vitejs/plugin-vue-jsx": "^3.1.0",
-    "@vue/compiler-sfc": "^3.4.25",
+    "@vue/compiler-sfc": "^3.4.27",
     "@vue/tsconfig": "^0.5.1",
     "ace-builds": "^1.33.1",
     "autoprefixer": "^10.4.19",
@@ -66,8 +66,8 @@
     "typescript": "5.3.3",
     "unplugin-auto-import": "^0.17.5",
     "unplugin-vue-components": "^0.26.0",
-    "unplugin-vue-define-options": "^1.4.3",
-    "vite": "^5.2.10",
+    "unplugin-vue-define-options": "^1.4.4",
+    "vite": "^5.2.11",
     "vite-svg-loader": "^5.1.0",
     "vue-tsc": "^1.8.27"
   },

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 612 - 281
app/pnpm-lock.yaml


+ 3 - 0
app/src/App.vue

@@ -10,6 +10,7 @@ import en_US from 'ant-design-vue/es/locale/en_US'
 
 import { useSettingsStore } from '@/pinia'
 import gettext from '@/gettext'
+import loadTranslations from '@/api/translations'
 
 const media = window.matchMedia('(prefers-color-scheme: dark)')
 
@@ -49,6 +50,8 @@ const lang = computed(() => {
 
 const settings = useSettingsStore()
 const is_theme_dark = computed(() => settings.theme === 'dark')
+
+loadTranslations()
 </script>
 
 <template>

+ 13 - 1
app/src/api/environment.ts

@@ -1,3 +1,4 @@
+import http from '@/lib/http'
 import type { ModelBase } from '@/api/curd'
 import Curd from '@/api/curd'
 
@@ -14,6 +15,17 @@ export interface Node {
   token: string
   response_at?: Date
 }
-const environment: Curd<Environment> = new Curd('/environment')
+
+class EnvironmentCurd extends Curd<Environment> {
+  constructor() {
+    super('/environment')
+  }
+
+  load_from_settings() {
+    return http.post(`${this.plural}/load_from_settings`)
+  }
+}
+
+const environment: EnvironmentCurd = new EnvironmentCurd()
 
 export default environment

+ 1 - 1
app/src/api/ngx.ts

@@ -46,7 +46,7 @@ const ngx = {
     return http.post('/ngx/format_code', { content })
   },
 
-  status(): Promise<{ running: boolean }> {
+  status(): Promise<{ running: boolean; message: string; level: number }> {
     return http.get('/nginx/status')
   },
 

+ 4 - 0
app/src/api/settings.ts

@@ -8,6 +8,10 @@ const settings = {
   save(data: any) {
     return http.post('/settings', data)
   },
+
+  get_server_name() {
+    return http.get('/settings/server/name')
+  },
 }
 
 export default settings

+ 19 - 0
app/src/api/translations.ts

@@ -0,0 +1,19 @@
+import gettext from '@/gettext'
+
+export default async function loadTranslations() {
+  const route = useRoute()
+
+  if (gettext.current !== 'en') {
+    await fetch(`${import.meta.env.VITE_API_ROOT}/translation/${gettext.current}`).then(async r => {
+      gettext.translations[gettext.current] = await r.json()
+    })
+
+    if (route?.meta?.name)
+      document.title = `${route.meta.name?.()} | Nginx UI`
+  }
+
+  watch(route, () => {
+    if (route?.meta?.name)
+      document.title = `${route.meta.name?.()} | Nginx UI`
+  })
+}

+ 13 - 8
app/src/components/ChatGPT/ChatGPT.vue

@@ -66,16 +66,21 @@ async function request() {
   let hasCodeBlockIndicator = false
 
   while (true) {
-    const { done, value } = await reader.read()
-    if (done) {
-      setTimeout(() => {
-        scrollToBottom()
-      }, 500)
-      loading.value = false
-      store_record()
+    try {
+      const { done, value } = await reader.read()
+      if (done) {
+        setTimeout(() => {
+          scrollToBottom()
+        }, 500)
+        loading.value = false
+        store_record()
+        break
+      }
+      apply(value!)
+    }
+    catch (e) {
       break
     }
-    apply(value!)
   }
 
   function apply(input: Uint8Array) {

+ 3 - 1
app/src/components/EnvIndicator/EnvIndicator.vue

@@ -25,6 +25,8 @@ watch(node_id, async () => {
   await router.push('/dashboard')
   location.reload()
 })
+
+const { server_name } = storeToRefs(useSettingsStore())
 </script>
 
 <template>
@@ -35,7 +37,7 @@ watch(node_id, async () => {
         v-if="is_local"
         class="env-name"
       >
-        {{ $gettext('Local') }}
+        {{ server_name || $gettext('Local') }}
       </span>
       <span
         v-else

+ 13 - 10
app/src/components/NginxControl/NginxControl.vue

@@ -6,13 +6,14 @@ import { logLevel } from '@/views/config/constants'
 import { NginxStatus } from '@/constants'
 
 const status = ref(0)
-function get_status() {
-  ngx.status().then(r => {
-    if (r?.running === true)
-      status.value = NginxStatus.Running
-    else
-      status.value = NginxStatus.Stopped
-  })
+async function get_status() {
+  const r = await ngx.status()
+  if (r?.running === true)
+    status.value = NginxStatus.Running
+  else
+    status.value = NginxStatus.Stopped
+
+  return r
 }
 
 function reload_nginx() {
@@ -29,9 +30,11 @@ function reload_nginx() {
   }).finally(() => get_status())
 }
 
-function restart_nginx() {
+async function restart_nginx() {
   status.value = NginxStatus.Restarting
-  ngx.restart().then(r => {
+  await ngx.restart()
+
+  get_status().then(r => {
     if (r.level < logLevel.Warn)
       message.success($gettext('Nginx restarted successfully'))
     else if (r.level === logLevel.Warn)
@@ -40,7 +43,7 @@ function restart_nginx() {
       message.error(r.message)
   }).catch(e => {
     message.error(`${$gettext('Server error')} ${e?.message}`)
-  }).finally(() => get_status())
+  })
 }
 
 const visible = ref(false)

+ 27 - 7
app/src/components/NodeSelector/NodeSelector.vue

@@ -14,11 +14,21 @@ const emit = defineEmits(['update:target', 'update:map'])
 const data = ref([]) as Ref<Environment[]>
 const data_map = ref({}) as Ref<Record<number, Environment>>
 
-environment.get_list().then(r => {
-  data.value = r.data
-  r.data?.forEach(node => {
-    data_map.value[node.id] = node
-  })
+onMounted(async () => {
+  let hasMore = true
+  let page = 1
+  while (hasMore) {
+    await environment.get_list({ page, enabled: true }).then(r => {
+      data.value.push(...r.data)
+      r.data?.forEach(node => {
+        data_map.value[node.id] = node
+      })
+      hasMore = r.data.length === r.pagination.per_page
+      page++
+    }).catch(() => {
+      hasMore = false
+    })
+  }
 })
 
 const value = computed({
@@ -35,14 +45,24 @@ const value = computed({
     emit('update:target', v)
   },
 })
+
+const noData = computed(() => {
+  return props.hiddenLocal && !data?.value?.length
+})
 </script>
 
 <template>
   <ACheckboxGroup
     v-model:value="value"
     style="width: 100%"
+    :class="{
+      'justify-center': noData,
+    }"
   >
-    <ARow :gutter="[16, 16]">
+    <ARow
+      v-if="!noData"
+      :gutter="[16, 16]"
+    >
       <ACol
         v-if="!hiddenLocal"
         :span="8"
@@ -76,7 +96,7 @@ const value = computed({
         </ATag>
       </ACol>
     </ARow>
-    <AEmpty v-if="hiddenLocal && data?.length === 0" />
+    <AEmpty v-else />
   </ACheckboxGroup>
 </template>
 

+ 11 - 17
app/src/components/SetLanguage/SetLanguage.vue

@@ -1,32 +1,27 @@
 <script setup lang="ts">
-import { ref, watch } from 'vue'
+import { watch } from 'vue'
 
 import { useSettingsStore } from '@/pinia'
-import http from '@/lib/http'
 import gettext from '@/gettext'
+import loadTranslations from '@/api/translations'
 
 const settings = useSettingsStore()
 
 const route = useRoute()
 
-const current = ref(gettext.current)
+const current = computed({
+  get() {
+    return gettext.current
+  },
+  set(v) {
+    gettext.current = v
+  },
+})
 
 const languageAvailable = gettext.available
 
-async function init() {
-  if (current.value !== 'en') {
-    await http.get(`/translation/${current.value}`).then(r => {
-      gettext.translations[current.value] = r
-    })
-
-    document.title = `${route.meta.name?.()} | Nginx UI`
-  }
-}
-
-init()
-
 watch(current, v => {
-  init()
+  loadTranslations()
   settings.set_language(v)
   gettext.current = v
 
@@ -34,7 +29,6 @@ watch(current, v => {
 
   document.title = `${name()} | Nginx UI`
 })
-
 </script>
 
 <template>

+ 1 - 1
app/src/language/LINGUAS

@@ -1 +1 @@
-es fr_FR ko_KR ru_RU vi_VN zh_CN zh_TW
+en zh_CN zh_TW fr_FR es ru_RU vi_VN ko_KR

+ 107 - 85
app/src/language/en/app.po

@@ -18,7 +18,7 @@ msgid "Access Logs"
 msgstr ""
 
 #: src/routes/index.ts:128 src/views/certificate/ACMEUser.vue:76
-#: src/views/certificate/ACMEUserSelector.vue:79
+#: src/views/certificate/ACMEUserSelector.vue:84
 #, fuzzy
 msgid "ACME User"
 msgstr "Username"
@@ -26,7 +26,7 @@ msgstr "Username"
 #: src/views/certificate/ACMEUser.vue:59
 #: src/views/certificate/Certificate.vue:108
 #: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
-#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:102
+#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:35
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
@@ -74,7 +74,7 @@ msgstr "Add Location"
 msgid "Advance Mode"
 msgstr "Advance Mode"
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:39
 msgid "API Base Url"
 msgstr ""
 
@@ -82,11 +82,11 @@ msgstr ""
 msgid "API Document"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:45
+#: src/views/preference/OpenAISettings.vue:51
 msgid "API Proxy"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:63
 msgid "API Token"
 msgstr ""
 
@@ -100,7 +100,7 @@ msgstr ""
 msgid "Are you sure you want to clear all notifications?"
 msgstr "Are you sure you want to remove this directive?"
 
-#: src/components/ChatGPT/ChatGPT.vue:267
+#: src/components/ChatGPT/ChatGPT.vue:272
 #, fuzzy
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Are you sure you want to remove this directive?"
@@ -134,11 +134,11 @@ msgstr "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to remove this location?"
 msgstr "Are you sure you want to remove this directive?"
 
-#: src/components/ChatGPT/ChatGPT.vue:211
+#: src/components/ChatGPT/ChatGPT.vue:216
 msgid "Ask ChatGPT for Help"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "Assistant"
 msgstr ""
 
@@ -147,7 +147,7 @@ msgstr ""
 msgid "Author"
 msgstr ""
 
-#: src/views/domain/cert/ChangeCert.vue:34
+#: src/views/domain/cert/ChangeCert.vue:33
 msgid "Auto Cert"
 msgstr ""
 
@@ -181,7 +181,7 @@ msgstr "Base information"
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:94
+#: src/views/preference/Preference.vue:101
 #: src/views/stream/components/RightSettings.vue:74
 #, fuzzy
 msgid "Basic"
@@ -209,10 +209,10 @@ msgstr ""
 msgid "CADir"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:248
+#: src/components/ChatGPT/ChatGPT.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:263
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:102
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:153
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:21
 #: src/views/domain/components/RightSettings.vue:51
@@ -256,8 +256,8 @@ msgstr "Certificate is valid"
 msgid "Challenge Method"
 msgstr ""
 
-#: src/views/domain/cert/ChangeCert.vue:88
-#: src/views/domain/cert/ChangeCert.vue:92
+#: src/views/domain/cert/ChangeCert.vue:95
+#: src/views/domain/cert/ChangeCert.vue:99
 #, fuzzy
 msgid "Change Certificate"
 msgstr "Certificate is valid"
@@ -274,7 +274,7 @@ msgstr ""
 msgid "Cleaning environment variables"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:271
+#: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
 #: src/views/notification/Notification.vue:75
 msgid "Clear"
@@ -319,7 +319,7 @@ msgstr "Configurations"
 msgid "Configure SSL"
 msgstr "Configure SSL"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Connected"
 msgstr ""
 
@@ -333,7 +333,7 @@ msgstr "Content"
 msgid "Core Upgrade"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:293
+#: src/views/dashboard/ServerAnalytic.vue:296
 msgid "CPU Status"
 msgstr "CPU Status"
 
@@ -375,6 +375,12 @@ msgstr ""
 msgid "Custom"
 msgstr ""
 
+#: src/views/preference/BasicSettings.vue:121
+msgid ""
+"Customize the name of local server to be displayed in the environment "
+"indicator."
+msgstr ""
+
 #: src/routes/index.ts:39
 msgid "Dashboard"
 msgstr "Dashboard"
@@ -466,9 +472,9 @@ msgstr "Disabled"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "Disable auto-renewal failed for %{name}"
 
-#: src/views/domain/cert/ChangeCert.vue:45 src/views/domain/DomainEdit.vue:185
-#: src/views/domain/DomainList.vue:33 src/views/stream/StreamEdit.vue:177
-#: src/views/stream/StreamList.vue:33
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
+#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Disabled"
 
@@ -479,7 +485,7 @@ msgstr "Disabled"
 msgid "Disabled successfully"
 msgstr "Disabled successfully"
 
-#: src/views/dashboard/ServerAnalytic.vue:358
+#: src/views/dashboard/ServerAnalytic.vue:361
 msgid "Disk IO"
 msgstr "Disk IO"
 
@@ -660,9 +666,10 @@ msgstr "Enabled successfully"
 msgid "Enable TLS"
 msgstr "Enable TLS"
 
-#: src/views/domain/cert/ChangeCert.vue:41
+#: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
@@ -682,11 +689,11 @@ msgstr "Enabled successfully"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Encrypt website with Let's Encrypt"
 
-#: src/routes/index.ts:212 src/views/environment/Environment.vue:110
+#: src/routes/index.ts:212 src/views/environment/Environment.vue:137
 msgid "Environment"
 msgstr ""
 
-#: src/views/dashboard/Environments.vue:71
+#: src/views/dashboard/Environments.vue:82
 #, fuzzy
 msgid "Environments"
 msgstr "Comments"
@@ -913,7 +920,7 @@ msgstr ""
 msgid "Leave blank for no change"
 msgstr "Leave blank for no change"
 
-#: src/views/preference/OpenAISettings.vue:41
+#: src/views/preference/OpenAISettings.vue:47
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr ""
 
@@ -928,7 +935,7 @@ msgstr "Leave blank for no change"
 msgid "License"
 msgstr "License"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Link Start"
 msgstr ""
 
@@ -942,8 +949,8 @@ msgstr ""
 msgid "Load Average:"
 msgstr "Load Averages:"
 
-#: src/components/EnvIndicator/EnvIndicator.vue:38
-#: src/components/NodeSelector/NodeSelector.vue:51
+#: src/components/EnvIndicator/EnvIndicator.vue:40
+#: src/components/NodeSelector/NodeSelector.vue:71
 #, fuzzy
 msgid "Local"
 msgstr "Location"
@@ -973,7 +980,7 @@ msgstr "Login successful"
 msgid "Logout successful"
 msgstr "Logout successful"
 
-#: src/views/preference/Preference.vue:112
+#: src/views/preference/Preference.vue:119
 msgid "Logrotate"
 msgstr ""
 
@@ -1019,6 +1026,7 @@ msgid "Managed Certificate"
 msgstr "Certificate is valid"
 
 #: src/views/dashboard/ServerAnalytic.vue:217
+#: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgstr "Memory"
 
@@ -1030,12 +1038,12 @@ msgstr "Memory and Storage"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:26
+#: src/views/preference/OpenAISettings.vue:27
 #, fuzzy
 msgid "Model"
 msgstr "Advance Mode"
 
-#: src/components/ChatGPT/ChatGPT.vue:244
+#: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #, fuzzy
@@ -1065,7 +1073,7 @@ msgstr "Single Directive"
 #: src/views/certificate/Certificate.vue:20
 #: src/views/certificate/CertificateEditor.vue:146
 #: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
-#: src/views/domain/cert/ChangeCert.vue:18
+#: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
 #: src/views/domain/DomainList.vue:13
@@ -1077,19 +1085,19 @@ msgstr "Single Directive"
 msgid "Name"
 msgstr "Name"
 
-#: src/views/dashboard/ServerAnalytic.vue:319
+#: src/views/dashboard/ServerAnalytic.vue:322
 msgid "Network"
 msgstr "Network"
 
-#: src/views/dashboard/ServerAnalytic.vue:261
+#: src/views/dashboard/ServerAnalytic.vue:264
 msgid "Network Statistics"
 msgstr "Network Statistics"
 
-#: src/views/dashboard/ServerAnalytic.vue:268
+#: src/views/dashboard/ServerAnalytic.vue:271
 msgid "Network Total Receive"
 msgstr "Network Total Receive"
 
-#: src/views/dashboard/ServerAnalytic.vue:274
+#: src/views/dashboard/ServerAnalytic.vue:277
 msgid "Network Total Send"
 msgstr "Network Total Send"
 
@@ -1103,7 +1111,7 @@ msgstr ""
 msgid "Next"
 msgstr "Next"
 
-#: src/views/preference/Preference.vue:100
+#: src/views/preference/Preference.vue:107
 msgid "Nginx"
 msgstr ""
 
@@ -1116,7 +1124,7 @@ msgstr ""
 msgid "Nginx Configuration Parse Error"
 msgstr "Configuration Name"
 
-#: src/components/NginxControl/NginxControl.vue:62
+#: src/components/NginxControl/NginxControl.vue:65
 msgid "Nginx Control"
 msgstr ""
 
@@ -1128,17 +1136,17 @@ msgstr ""
 msgid "Nginx Log"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:22
+#: src/components/NginxControl/NginxControl.vue:23
 #, fuzzy
 msgid "Nginx reloaded successfully"
 msgstr "Saved successfully"
 
-#: src/components/NginxControl/NginxControl.vue:36
+#: src/components/NginxControl/NginxControl.vue:39
 #, fuzzy
 msgid "Nginx restarted successfully"
 msgstr "Saved successfully"
 
-#: src/components/ChatGPT/ChatGPT.vue:265
+#: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
@@ -1191,22 +1199,22 @@ msgstr "Certificate is valid"
 msgid "Obtaining certificate"
 msgstr ""
 
-#: src/components/NodeSelector/NodeSelector.vue:75
-#: src/views/dashboard/Environments.vue:95
-#: src/views/environment/Environment.vue:86
+#: src/components/NodeSelector/NodeSelector.vue:95
+#: src/views/dashboard/Environments.vue:106
+#: src/views/environment/Environment.vue:88
 msgid "Offline"
 msgstr ""
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:264
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:154
 msgid "Ok"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:266
+#: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1225,14 +1233,14 @@ msgstr ""
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
-#: src/components/NodeSelector/NodeSelector.vue:54
-#: src/components/NodeSelector/NodeSelector.vue:69
-#: src/views/dashboard/Environments.vue:88
-#: src/views/environment/Environment.vue:82
+#: src/components/NodeSelector/NodeSelector.vue:74
+#: src/components/NodeSelector/NodeSelector.vue:89
+#: src/views/dashboard/Environments.vue:99
+#: src/views/environment/Environment.vue:84
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:106
+#: src/views/preference/Preference.vue:113
 msgid "OpenAI"
 msgstr ""
 
@@ -1326,7 +1334,7 @@ msgstr ""
 msgid "Pre-release"
 msgstr ""
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
 msgid "Preference"
 msgstr ""
 
@@ -1352,12 +1360,12 @@ msgid "Provider"
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:28
-#: src/views/dashboard/ServerAnalytic.vue:375
+#: src/views/dashboard/ServerAnalytic.vue:378
 msgid "Reads"
 msgstr "Reads"
 
 #: src/views/dashboard/ServerAnalytic.vue:24
-#: src/views/dashboard/ServerAnalytic.vue:326
+#: src/views/dashboard/ServerAnalytic.vue:329
 msgid "Receive"
 msgstr "Receive"
 
@@ -1374,7 +1382,7 @@ msgstr "Saved successfully"
 msgid "Recursive Nameservers"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/ChatGPT/ChatGPT.vue:283
 msgid "Regenerate response"
 msgstr ""
 
@@ -1409,12 +1417,12 @@ msgstr "Install"
 msgid "Release Note"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:254
-#: src/components/NginxControl/NginxControl.vue:97
+#: src/components/ChatGPT/ChatGPT.vue:259
+#: src/components/NginxControl/NginxControl.vue:100
 msgid "Reload"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:71
+#: src/components/NginxControl/NginxControl.vue:74
 msgid "Reloading"
 msgstr ""
 
@@ -1462,11 +1470,11 @@ msgstr ""
 msgid "Reset"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:90
+#: src/components/NginxControl/NginxControl.vue:93
 msgid "Restart"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:76
+#: src/components/NginxControl/NginxControl.vue:79
 msgid "Restarting"
 msgstr ""
 
@@ -1475,15 +1483,15 @@ msgstr ""
 msgid "Run Mode"
 msgstr "Advance Mode"
 
-#: src/components/NginxControl/NginxControl.vue:66
+#: src/components/NginxControl/NginxControl.vue:69
 msgid "Running"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:247
+#: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
 msgid "Save"
 msgstr "Save"
 
@@ -1499,7 +1507,7 @@ msgstr "Save error %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:59
+#: src/views/preference/Preference.vue:66
 #, fuzzy
 msgid "Save successfully"
 msgstr "Saved successfully"
@@ -1515,24 +1523,24 @@ msgstr "Saved successfully"
 msgid "SDK"
 msgstr ""
 
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:25
-#: src/views/dashboard/ServerAnalytic.vue:336
+#: src/views/dashboard/ServerAnalytic.vue:339
 msgid "Send"
 msgstr "Send"
 
-#: src/components/NginxControl/NginxControl.vue:28
-#: src/components/NginxControl/NginxControl.vue:42
+#: src/components/NginxControl/NginxControl.vue:29
+#: src/components/NginxControl/NginxControl.vue:45
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:70
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"
@@ -1542,6 +1550,11 @@ msgstr "Server error"
 msgid "Server Info"
 msgstr "Server Info"
 
+#: src/views/preference/BasicSettings.vue:117
+#, fuzzy
+msgid "Server Name"
+msgstr "Server Info"
+
 #: src/views/domain/cert/components/ObtainCert.vue:102
 msgid "server_name not found in directives"
 msgstr "server_name not found in directives"
@@ -1616,15 +1629,16 @@ msgstr "Enabled"
 
 #: src/views/certificate/ACMEUser.vue:42
 #: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
-#: src/views/environment/Environment.vue:75 src/views/stream/StreamList.vue:22
+#: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Status"
 
-#: src/components/NginxControl/NginxControl.vue:81
+#: src/components/NginxControl/NginxControl.vue:84
 msgid "Stopped"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:243
+#: src/views/dashboard/ServerAnalytic.vue:245
+#: src/views/dashboard/ServerAnalytic.vue:246
 msgid "Storage"
 msgstr "Storage"
 
@@ -1637,7 +1651,8 @@ msgstr "Subject Name: %{name}"
 msgid "Success"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:230
+#: src/views/dashboard/ServerAnalytic.vue:231
+#: src/views/dashboard/ServerAnalytic.vue:232
 msgid "Swap"
 msgstr "Swap"
 
@@ -1688,6 +1703,10 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr "Certificate Status"
 
+#: src/views/preference/OpenAISettings.vue:30
+msgid "The model name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/certificate/CertificateEditor.vue:165
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
@@ -1697,6 +1716,10 @@ msgstr "Certificate Status"
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
+#: src/views/preference/BasicSettings.vue:120
+msgid "The server name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/domain/cert/components/AutoCertStepOne.vue:50
 #, fuzzy
 msgid ""
@@ -1708,9 +1731,12 @@ msgstr ""
 
 #: src/views/preference/BasicSettings.vue:38
 #: src/views/preference/BasicSettings.vue:50
-#: src/views/preference/OpenAISettings.vue:36
-#: src/views/preference/OpenAISettings.vue:48
-msgid "The url is not valid"
+msgid "The url is invalid"
+msgstr ""
+
+#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:54
+msgid "The url is invalid."
 msgstr ""
 
 #: src/language/constants.ts:2
@@ -1751,7 +1777,7 @@ msgid ""
 "continue?"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:60
+#: src/views/preference/OpenAISettings.vue:66
 msgid "Token is not valid"
 msgstr ""
 
@@ -1768,7 +1794,7 @@ msgstr ""
 #: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
-#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:95
+#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
 #: src/views/stream/components/RightSettings.vue:85
 #: src/views/stream/StreamList.vue:41 src/views/user/User.vue:37
 msgid "Updated at"
@@ -1801,11 +1827,11 @@ msgstr ""
 msgid "Uptime:"
 msgstr "Uptime:"
 
-#: src/views/environment/Environment.vue:21
+#: src/views/environment/Environment.vue:22
 msgid "URL"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 #, fuzzy
 msgid "User"
 msgstr "Username"
@@ -1861,7 +1887,7 @@ msgid ""
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:27
-#: src/views/dashboard/ServerAnalytic.vue:365
+#: src/views/dashboard/ServerAnalytic.vue:368
 msgid "Writes"
 msgstr "Writes"
 
@@ -1895,10 +1921,6 @@ msgstr ""
 #~ msgid "Table"
 #~ msgstr "Enabled"
 
-#, fuzzy
-#~ msgid "Server"
-#~ msgstr "Server Info"
-
 #, fuzzy
 #~ msgid "Leave blank will not change anything."
 #~ msgstr "Leave blank for no change"

+ 112 - 85
app/src/language/es/app.po

@@ -23,7 +23,7 @@ msgid "Access Logs"
 msgstr "Registros de acceso"
 
 #: src/routes/index.ts:128 src/views/certificate/ACMEUser.vue:76
-#: src/views/certificate/ACMEUserSelector.vue:79
+#: src/views/certificate/ACMEUserSelector.vue:84
 #, fuzzy
 msgid "ACME User"
 msgstr "Usuario"
@@ -31,7 +31,7 @@ msgstr "Usuario"
 #: src/views/certificate/ACMEUser.vue:59
 #: src/views/certificate/Certificate.vue:108
 #: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
-#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:102
+#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:35
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
@@ -76,7 +76,7 @@ msgstr "Adicional"
 msgid "Advance Mode"
 msgstr "Modo avanzado"
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:39
 msgid "API Base Url"
 msgstr "URL Base de la API"
 
@@ -85,11 +85,11 @@ msgstr "URL Base de la API"
 msgid "API Document"
 msgstr "Token de la API"
 
-#: src/views/preference/OpenAISettings.vue:45
+#: src/views/preference/OpenAISettings.vue:51
 msgid "API Proxy"
 msgstr "Proxy de la API"
 
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:63
 msgid "API Token"
 msgstr "Token de la API"
 
@@ -102,7 +102,7 @@ msgstr "Arquitectura"
 msgid "Are you sure you want to clear all notifications?"
 msgstr "¿Está seguro de que desea borrar todas las notificaciones?"
 
-#: src/components/ChatGPT/ChatGPT.vue:267
+#: src/components/ChatGPT/ChatGPT.vue:272
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "¿Está seguro de que desea borrar el registro del chat?"
 
@@ -133,11 +133,11 @@ msgstr "¿Está seguro de que quiere borrar esta directiva?"
 msgid "Are you sure you want to remove this location?"
 msgstr "¿Está seguro de que quiere borrar esta ubicación?"
 
-#: src/components/ChatGPT/ChatGPT.vue:211
+#: src/components/ChatGPT/ChatGPT.vue:216
 msgid "Ask ChatGPT for Help"
 msgstr "Preguntar por ayuda a ChatGPT"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "Assistant"
 msgstr "Asistente"
 
@@ -146,7 +146,7 @@ msgstr "Asistente"
 msgid "Author"
 msgstr "Autor"
 
-#: src/views/domain/cert/ChangeCert.vue:34
+#: src/views/domain/cert/ChangeCert.vue:33
 msgid "Auto Cert"
 msgstr "Certificado automático"
 
@@ -179,7 +179,7 @@ msgstr "Información general"
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:94
+#: src/views/preference/Preference.vue:101
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgstr "Básico"
@@ -205,10 +205,10 @@ msgstr ""
 msgid "CADir"
 msgstr "Directorio CA"
 
-#: src/components/ChatGPT/ChatGPT.vue:248
+#: src/components/ChatGPT/ChatGPT.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:263
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:102
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:153
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:21
 #: src/views/domain/components/RightSettings.vue:51
@@ -250,8 +250,8 @@ msgstr "Lista de Certificados"
 msgid "Challenge Method"
 msgstr "Método de desafío"
 
-#: src/views/domain/cert/ChangeCert.vue:88
-#: src/views/domain/cert/ChangeCert.vue:92
+#: src/views/domain/cert/ChangeCert.vue:95
+#: src/views/domain/cert/ChangeCert.vue:99
 msgid "Change Certificate"
 msgstr "Cambiar Certificado"
 
@@ -267,7 +267,7 @@ msgstr "Intentar nuevamente"
 msgid "Cleaning environment variables"
 msgstr "Borrar las variables de entorno"
 
-#: src/components/ChatGPT/ChatGPT.vue:271
+#: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
 #: src/views/notification/Notification.vue:75
 msgid "Clear"
@@ -310,7 +310,7 @@ msgstr "Configuraciones"
 msgid "Configure SSL"
 msgstr "Configurar SSL"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Connected"
 msgstr "Conectado"
 
@@ -324,7 +324,7 @@ msgstr "Contenido"
 msgid "Core Upgrade"
 msgstr "Actualización del kernel"
 
-#: src/views/dashboard/ServerAnalytic.vue:293
+#: src/views/dashboard/ServerAnalytic.vue:296
 msgid "CPU Status"
 msgstr "Estado del CPU"
 
@@ -365,6 +365,12 @@ msgstr "Versión actual"
 msgid "Custom"
 msgstr "Personalizado"
 
+#: src/views/preference/BasicSettings.vue:121
+msgid ""
+"Customize the name of local server to be displayed in the environment "
+"indicator."
+msgstr ""
+
 #: src/routes/index.ts:39
 msgid "Dashboard"
 msgstr "Panel"
@@ -452,9 +458,9 @@ msgstr "Desactivar"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "No se pudo desactivar la renovación automática por %{name}"
 
-#: src/views/domain/cert/ChangeCert.vue:45 src/views/domain/DomainEdit.vue:185
-#: src/views/domain/DomainList.vue:33 src/views/stream/StreamEdit.vue:177
-#: src/views/stream/StreamList.vue:33
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
+#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Desactivado"
 
@@ -465,7 +471,7 @@ msgstr "Desactivado"
 msgid "Disabled successfully"
 msgstr "Desactivado con éxito"
 
-#: src/views/dashboard/ServerAnalytic.vue:358
+#: src/views/dashboard/ServerAnalytic.vue:361
 msgid "Disk IO"
 msgstr "I/O del disco"
 
@@ -633,9 +639,10 @@ msgstr "Habilitado con Éxito"
 msgid "Enable TLS"
 msgstr "Habilitar TLS"
 
-#: src/views/domain/cert/ChangeCert.vue:41
+#: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
@@ -655,11 +662,11 @@ msgstr "Habilitado con éxito"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Encriptar sitio web con Let's Encrypt"
 
-#: src/routes/index.ts:212 src/views/environment/Environment.vue:110
+#: src/routes/index.ts:212 src/views/environment/Environment.vue:137
 msgid "Environment"
 msgstr "Entorno"
 
-#: src/views/dashboard/Environments.vue:71
+#: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr "Entornos"
 
@@ -876,7 +883,7 @@ msgstr "Comprobado por última vez el"
 msgid "Leave blank for no change"
 msgstr "Para no modificar dejar en blanco"
 
-#: src/views/preference/OpenAISettings.vue:41
+#: src/views/preference/OpenAISettings.vue:47
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Dejar en blanco para el valor predeterminado: https://api.openai.com/"
 
@@ -889,7 +896,7 @@ msgstr "Dejarlo en blanco no cambiará nada"
 msgid "License"
 msgstr "Licencia"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Link Start"
 msgstr "Iniciar conexión"
 
@@ -902,8 +909,8 @@ msgstr ""
 msgid "Load Average:"
 msgstr "Promedios de carga:"
 
-#: src/components/EnvIndicator/EnvIndicator.vue:38
-#: src/components/NodeSelector/NodeSelector.vue:51
+#: src/components/EnvIndicator/EnvIndicator.vue:40
+#: src/components/NodeSelector/NodeSelector.vue:71
 msgid "Local"
 msgstr "Local"
 
@@ -931,7 +938,7 @@ msgstr "Acceso exitoso"
 msgid "Logout successful"
 msgstr "Cierre de sesión exitoso"
 
-#: src/views/preference/Preference.vue:112
+#: src/views/preference/Preference.vue:119
 msgid "Logrotate"
 msgstr ""
 
@@ -974,6 +981,7 @@ msgid "Managed Certificate"
 msgstr "Certificado Administrado"
 
 #: src/views/dashboard/ServerAnalytic.vue:217
+#: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgstr "Memoria"
 
@@ -985,12 +993,12 @@ msgstr "Memoria y almacenamiento"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:26
+#: src/views/preference/OpenAISettings.vue:27
 #, fuzzy
 msgid "Model"
 msgstr "Modo de ejecución"
 
-#: src/components/ChatGPT/ChatGPT.vue:244
+#: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 msgid "Modify"
@@ -1017,7 +1025,7 @@ msgstr "Directiva multilínea"
 #: src/views/certificate/Certificate.vue:20
 #: src/views/certificate/CertificateEditor.vue:146
 #: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
-#: src/views/domain/cert/ChangeCert.vue:18
+#: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
 #: src/views/domain/DomainList.vue:13
@@ -1029,19 +1037,19 @@ msgstr "Directiva multilínea"
 msgid "Name"
 msgstr "Nombre"
 
-#: src/views/dashboard/ServerAnalytic.vue:319
+#: src/views/dashboard/ServerAnalytic.vue:322
 msgid "Network"
 msgstr "Red"
 
-#: src/views/dashboard/ServerAnalytic.vue:261
+#: src/views/dashboard/ServerAnalytic.vue:264
 msgid "Network Statistics"
 msgstr "Estadísticas de red"
 
-#: src/views/dashboard/ServerAnalytic.vue:268
+#: src/views/dashboard/ServerAnalytic.vue:271
 msgid "Network Total Receive"
 msgstr "Total recibido por la red"
 
-#: src/views/dashboard/ServerAnalytic.vue:274
+#: src/views/dashboard/ServerAnalytic.vue:277
 msgid "Network Total Send"
 msgstr "Total enviado por la red"
 
@@ -1055,7 +1063,7 @@ msgstr "Se liberó una nueva versión"
 msgid "Next"
 msgstr "Siguiente"
 
-#: src/views/preference/Preference.vue:100
+#: src/views/preference/Preference.vue:107
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1067,7 +1075,7 @@ msgstr "Ruta de registro de acceso de Nginx"
 msgid "Nginx Configuration Parse Error"
 msgstr "Error de análisis de configuración de Nginx"
 
-#: src/components/NginxControl/NginxControl.vue:62
+#: src/components/NginxControl/NginxControl.vue:65
 msgid "Nginx Control"
 msgstr "Control de Nginx"
 
@@ -1079,15 +1087,15 @@ msgstr "Ruta de registro de errores de Nginx"
 msgid "Nginx Log"
 msgstr "Registro Nginx"
 
-#: src/components/NginxControl/NginxControl.vue:22
+#: src/components/NginxControl/NginxControl.vue:23
 msgid "Nginx reloaded successfully"
 msgstr "Nginx recargado con éxito"
 
-#: src/components/NginxControl/NginxControl.vue:36
+#: src/components/NginxControl/NginxControl.vue:39
 msgid "Nginx restarted successfully"
 msgstr "Nginx reiniciado con éxito"
 
-#: src/components/ChatGPT/ChatGPT.vue:265
+#: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
@@ -1137,22 +1145,22 @@ msgstr "Obtener certificado"
 msgid "Obtaining certificate"
 msgstr "Obteniendo certificado"
 
-#: src/components/NodeSelector/NodeSelector.vue:75
-#: src/views/dashboard/Environments.vue:95
-#: src/views/environment/Environment.vue:86
+#: src/components/NodeSelector/NodeSelector.vue:95
+#: src/views/dashboard/Environments.vue:106
+#: src/views/environment/Environment.vue:88
 msgid "Offline"
 msgstr "Desconectado"
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:264
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:154
 msgid "Ok"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:266
+#: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1171,14 +1179,14 @@ msgstr "OK"
 msgid "Once the verification is complete, the records will be removed."
 msgstr "Una vez que se complete la verificación, los registros se eliminarán."
 
-#: src/components/NodeSelector/NodeSelector.vue:54
-#: src/components/NodeSelector/NodeSelector.vue:69
-#: src/views/dashboard/Environments.vue:88
-#: src/views/environment/Environment.vue:82
+#: src/components/NodeSelector/NodeSelector.vue:74
+#: src/components/NodeSelector/NodeSelector.vue:89
+#: src/views/dashboard/Environments.vue:99
+#: src/views/environment/Environment.vue:84
 msgid "Online"
 msgstr "En línea"
 
-#: src/views/preference/Preference.vue:106
+#: src/views/preference/Preference.vue:113
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1280,7 +1288,7 @@ msgstr "¡Seleccione al menos un nodo!"
 msgid "Pre-release"
 msgstr "Prelanzamiento"
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
 msgid "Preference"
 msgstr "Configuración"
 
@@ -1305,12 +1313,12 @@ msgid "Provider"
 msgstr "Proveedor"
 
 #: src/views/dashboard/ServerAnalytic.vue:28
-#: src/views/dashboard/ServerAnalytic.vue:375
+#: src/views/dashboard/ServerAnalytic.vue:378
 msgid "Reads"
 msgstr "Lecturas"
 
 #: src/views/dashboard/ServerAnalytic.vue:24
-#: src/views/dashboard/ServerAnalytic.vue:326
+#: src/views/dashboard/ServerAnalytic.vue:329
 msgid "Receive"
 msgstr "Recibido"
 
@@ -1327,7 +1335,7 @@ msgstr "Eliminado con éxito"
 msgid "Recursive Nameservers"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/ChatGPT/ChatGPT.vue:283
 msgid "Regenerate response"
 msgstr "Regenerar respuesta"
 
@@ -1363,12 +1371,12 @@ msgstr "Reinstalar"
 msgid "Release Note"
 msgstr "Nota de versión"
 
-#: src/components/ChatGPT/ChatGPT.vue:254
-#: src/components/NginxControl/NginxControl.vue:97
+#: src/components/ChatGPT/ChatGPT.vue:259
+#: src/components/NginxControl/NginxControl.vue:100
 msgid "Reload"
 msgstr "Recargar"
 
-#: src/components/NginxControl/NginxControl.vue:71
+#: src/components/NginxControl/NginxControl.vue:74
 msgid "Reloading"
 msgstr "Recargando"
 
@@ -1410,11 +1418,11 @@ msgstr "Pedido con parámetros incorrectos"
 msgid "Reset"
 msgstr "Limpiar"
 
-#: src/components/NginxControl/NginxControl.vue:90
+#: src/components/NginxControl/NginxControl.vue:93
 msgid "Restart"
 msgstr "Reiniciar"
 
-#: src/components/NginxControl/NginxControl.vue:76
+#: src/components/NginxControl/NginxControl.vue:79
 msgid "Restarting"
 msgstr "Reiniciando"
 
@@ -1422,15 +1430,15 @@ msgstr "Reiniciando"
 msgid "Run Mode"
 msgstr "Modo de ejecución"
 
-#: src/components/NginxControl/NginxControl.vue:66
+#: src/components/NginxControl/NginxControl.vue:69
 msgid "Running"
 msgstr "Corriendo"
 
-#: src/components/ChatGPT/ChatGPT.vue:247
+#: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
 msgid "Save"
 msgstr "Guardar"
 
@@ -1446,7 +1454,7 @@ msgstr "Error al guardar %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:59
+#: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr "Guardado con éxito"
 
@@ -1461,24 +1469,24 @@ msgstr "Guardado con éxito"
 msgid "SDK"
 msgstr ""
 
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgstr "Selector"
 
 #: src/views/dashboard/ServerAnalytic.vue:25
-#: src/views/dashboard/ServerAnalytic.vue:336
+#: src/views/dashboard/ServerAnalytic.vue:339
 msgid "Send"
 msgstr "Enviado"
 
-#: src/components/NginxControl/NginxControl.vue:28
-#: src/components/NginxControl/NginxControl.vue:42
+#: src/components/NginxControl/NginxControl.vue:29
+#: src/components/NginxControl/NginxControl.vue:45
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:70
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"
@@ -1488,6 +1496,11 @@ msgstr "Error del servidor"
 msgid "Server Info"
 msgstr "Información del servidor"
 
+#: src/views/preference/BasicSettings.vue:117
+#, fuzzy
+msgid "Server Name"
+msgstr "Información del servidor"
+
 #: src/views/domain/cert/components/ObtainCert.vue:102
 msgid "server_name not found in directives"
 msgstr "No se encuentra server_name en las directivas"
@@ -1555,15 +1568,16 @@ msgstr "Estable"
 
 #: src/views/certificate/ACMEUser.vue:42
 #: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
-#: src/views/environment/Environment.vue:75 src/views/stream/StreamList.vue:22
+#: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Estado"
 
-#: src/components/NginxControl/NginxControl.vue:81
+#: src/components/NginxControl/NginxControl.vue:84
 msgid "Stopped"
 msgstr "Detenido"
 
-#: src/views/dashboard/ServerAnalytic.vue:243
+#: src/views/dashboard/ServerAnalytic.vue:245
+#: src/views/dashboard/ServerAnalytic.vue:246
 msgid "Storage"
 msgstr "Almacenamiento"
 
@@ -1575,7 +1589,8 @@ msgstr "Nombre del asunto: %{subject}"
 msgid "Success"
 msgstr "Éxito"
 
-#: src/views/dashboard/ServerAnalytic.vue:230
+#: src/views/dashboard/ServerAnalytic.vue:231
+#: src/views/dashboard/ServerAnalytic.vue:232
 msgid "Swap"
 msgstr "Swap"
 
@@ -1625,6 +1640,10 @@ msgstr "La entrada no es un Certificado SSL"
 msgid "The input is not a SSL Certificate Key"
 msgstr "La entrada no es una clave de certificado SSL"
 
+#: src/views/preference/OpenAISettings.vue:30
+msgid "The model name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/certificate/CertificateEditor.vue:165
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
@@ -1634,6 +1653,10 @@ msgstr "La ruta existe, pero el archivo no es una clave privada"
 msgid "The path exists, but the file is not a private key"
 msgstr "La ruta existe, pero el archivo no es una clave privada"
 
+#: src/views/preference/BasicSettings.vue:120
+msgid "The server name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/domain/cert/components/AutoCertStepOne.vue:50
 msgid ""
 "The server_name in the current configuration must be the domain name you "
@@ -1644,9 +1667,14 @@ msgstr ""
 
 #: src/views/preference/BasicSettings.vue:38
 #: src/views/preference/BasicSettings.vue:50
-#: src/views/preference/OpenAISettings.vue:36
-#: src/views/preference/OpenAISettings.vue:48
-msgid "The url is not valid"
+#, fuzzy
+msgid "The url is invalid"
+msgstr "La URL no es válida"
+
+#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:54
+#, fuzzy
+msgid "The url is invalid."
 msgstr "La URL no es válida"
 
 #: src/language/constants.ts:2
@@ -1691,7 +1719,7 @@ msgstr ""
 "de la autoridad al backend, y debemos guardar este archivo y volver a cargar "
 "Nginx. ¿Estás seguro de que quieres continuar?"
 
-#: src/views/preference/OpenAISettings.vue:60
+#: src/views/preference/OpenAISettings.vue:66
 msgid "Token is not valid"
 msgstr "El token no es válido"
 
@@ -1708,7 +1736,7 @@ msgstr "Tipo"
 #: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
-#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:95
+#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
 #: src/views/stream/components/RightSettings.vue:85
 #: src/views/stream/StreamList.vue:41 src/views/user/User.vue:37
 msgid "Updated at"
@@ -1739,11 +1767,11 @@ msgstr "Nombre de la Transmisión"
 msgid "Uptime:"
 msgstr "Tiempo encendido:"
 
-#: src/views/environment/Environment.vue:21
+#: src/views/environment/Environment.vue:22
 msgid "URL"
 msgstr "URL"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "User"
 msgstr "Usuario"
 
@@ -1802,7 +1830,7 @@ msgstr ""
 "recargaremos Nginx. ¿Estás seguro de que quieres continuar?"
 
 #: src/views/dashboard/ServerAnalytic.vue:27
-#: src/views/dashboard/ServerAnalytic.vue:365
+#: src/views/dashboard/ServerAnalytic.vue:368
 msgid "Writes"
 msgstr "Escrituras"
 
@@ -1828,6 +1856,9 @@ msgstr "Estás usando la última versión"
 msgid "You can check Nginx UI upgrade at this page."
 msgstr "Puede consultar la actualización de Nginx UI en esta página."
 
+#~ msgid "The url is not valid"
+#~ msgstr "La URL no es válida"
+
 #~ msgid "ChatGPT Model"
 #~ msgstr "Modelo de ChatGPT"
 
@@ -1852,10 +1883,6 @@ msgstr "Puede consultar la actualización de Nginx UI en esta página."
 #~ msgid "The path exists, but the file is not a public key"
 #~ msgstr "La ruta existe, pero el archivo no es una clave pública"
 
-#, fuzzy
-#~ msgid "Server"
-#~ msgstr "Información del servidor"
-
 #, fuzzy
 #~ msgid "Leave blank will not change anything."
 #~ msgstr "Para no modificar dejar en blanco"

+ 107 - 85
app/src/language/fr_FR/app.po

@@ -20,7 +20,7 @@ msgid "Access Logs"
 msgstr "Journaux d'accès"
 
 #: src/routes/index.ts:128 src/views/certificate/ACMEUser.vue:76
-#: src/views/certificate/ACMEUserSelector.vue:79
+#: src/views/certificate/ACMEUserSelector.vue:84
 #, fuzzy
 msgid "ACME User"
 msgstr "Nom d'utilisateur"
@@ -28,7 +28,7 @@ msgstr "Nom d'utilisateur"
 #: src/views/certificate/ACMEUser.vue:59
 #: src/views/certificate/Certificate.vue:108
 #: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
-#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:102
+#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:35
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
@@ -76,7 +76,7 @@ msgstr "Supplémentaire"
 msgid "Advance Mode"
 msgstr "Mode avancé"
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:39
 msgid "API Base Url"
 msgstr "URL de base de l'API"
 
@@ -85,11 +85,11 @@ msgstr "URL de base de l'API"
 msgid "API Document"
 msgstr "Jeton d'API"
 
-#: src/views/preference/OpenAISettings.vue:45
+#: src/views/preference/OpenAISettings.vue:51
 msgid "API Proxy"
 msgstr "Proxy d'API"
 
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:63
 msgid "API Token"
 msgstr "Jeton d'API"
 
@@ -104,7 +104,7 @@ msgstr "Arch"
 msgid "Are you sure you want to clear all notifications?"
 msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
 
-#: src/components/ChatGPT/ChatGPT.vue:267
+#: src/components/ChatGPT/ChatGPT.vue:272
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
 
@@ -135,12 +135,12 @@ msgstr "Voulez-vous vraiment supprimer cette directive ?"
 msgid "Are you sure you want to remove this location?"
 msgstr "Voulez-vous vraiment supprimer cette localisation ?"
 
-#: src/components/ChatGPT/ChatGPT.vue:211
+#: src/components/ChatGPT/ChatGPT.vue:216
 #, fuzzy
 msgid "Ask ChatGPT for Help"
 msgstr "Modèle ChatGPT"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "Assistant"
 msgstr ""
 
@@ -149,7 +149,7 @@ msgstr ""
 msgid "Author"
 msgstr "Autheur"
 
-#: src/views/domain/cert/ChangeCert.vue:34
+#: src/views/domain/cert/ChangeCert.vue:33
 msgid "Auto Cert"
 msgstr "Auto Cert"
 
@@ -182,7 +182,7 @@ msgstr "Information générale"
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:94
+#: src/views/preference/Preference.vue:101
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgstr "Basique"
@@ -209,10 +209,10 @@ msgstr ""
 msgid "CADir"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:248
+#: src/components/ChatGPT/ChatGPT.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:263
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:102
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:153
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:21
 #: src/views/domain/components/RightSettings.vue:51
@@ -256,8 +256,8 @@ msgstr "Liste des certifications"
 msgid "Challenge Method"
 msgstr "Méthode de challenge"
 
-#: src/views/domain/cert/ChangeCert.vue:88
-#: src/views/domain/cert/ChangeCert.vue:92
+#: src/views/domain/cert/ChangeCert.vue:95
+#: src/views/domain/cert/ChangeCert.vue:99
 msgid "Change Certificate"
 msgstr "Changer de certificat"
 
@@ -273,7 +273,7 @@ msgstr "Revérifier"
 msgid "Cleaning environment variables"
 msgstr "Nettoyage des variables d'environnement"
 
-#: src/components/ChatGPT/ChatGPT.vue:271
+#: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
 #: src/views/notification/Notification.vue:75
 msgid "Clear"
@@ -317,7 +317,7 @@ msgstr "Configurations"
 msgid "Configure SSL"
 msgstr "Configurer SSL"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Connected"
 msgstr ""
 
@@ -331,7 +331,7 @@ msgstr "Contenu"
 msgid "Core Upgrade"
 msgstr "Mise à jour du core"
 
-#: src/views/dashboard/ServerAnalytic.vue:293
+#: src/views/dashboard/ServerAnalytic.vue:296
 msgid "CPU Status"
 msgstr "État du processeur"
 
@@ -373,6 +373,12 @@ msgstr "Version actuelle"
 msgid "Custom"
 msgstr "Custom"
 
+#: src/views/preference/BasicSettings.vue:121
+msgid ""
+"Customize the name of local server to be displayed in the environment "
+"indicator."
+msgstr ""
+
 #: src/routes/index.ts:39
 msgid "Dashboard"
 msgstr "Dashboard"
@@ -465,9 +471,9 @@ msgstr "Désactivé"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "La désactivation du renouvellement automatique a échoué pour %{name}"
 
-#: src/views/domain/cert/ChangeCert.vue:45 src/views/domain/DomainEdit.vue:185
-#: src/views/domain/DomainList.vue:33 src/views/stream/StreamEdit.vue:177
-#: src/views/stream/StreamList.vue:33
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
+#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Désactivé"
 
@@ -478,7 +484,7 @@ msgstr "Désactivé"
 msgid "Disabled successfully"
 msgstr "Désactivé avec succès"
 
-#: src/views/dashboard/ServerAnalytic.vue:358
+#: src/views/dashboard/ServerAnalytic.vue:361
 msgid "Disk IO"
 msgstr "E/S disque"
 
@@ -658,9 +664,10 @@ msgstr "Activé avec succès"
 msgid "Enable TLS"
 msgstr "Activer TLS"
 
-#: src/views/domain/cert/ChangeCert.vue:41
+#: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
@@ -680,11 +687,11 @@ msgstr "Activé avec succès"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Crypter le site Web avec Let's Encrypt"
 
-#: src/routes/index.ts:212 src/views/environment/Environment.vue:110
+#: src/routes/index.ts:212 src/views/environment/Environment.vue:137
 msgid "Environment"
 msgstr ""
 
-#: src/views/dashboard/Environments.vue:71
+#: src/views/dashboard/Environments.vue:82
 #, fuzzy
 msgid "Environments"
 msgstr "Commentaires"
@@ -910,7 +917,7 @@ msgstr "Dernière vérification le"
 msgid "Leave blank for no change"
 msgstr "Laisser vide pour aucun changement"
 
-#: src/views/preference/OpenAISettings.vue:41
+#: src/views/preference/OpenAISettings.vue:47
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Laissez vide pour la valeur par défaut : https://api.openai.com/"
 
@@ -925,7 +932,7 @@ msgstr "Laisser vide pour aucun changement"
 msgid "License"
 msgstr "Licence"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Link Start"
 msgstr ""
 
@@ -939,8 +946,8 @@ msgstr ""
 msgid "Load Average:"
 msgstr "Charges moyennes :"
 
-#: src/components/EnvIndicator/EnvIndicator.vue:38
-#: src/components/NodeSelector/NodeSelector.vue:51
+#: src/components/EnvIndicator/EnvIndicator.vue:40
+#: src/components/NodeSelector/NodeSelector.vue:71
 #, fuzzy
 msgid "Local"
 msgstr "Localisation"
@@ -972,7 +979,7 @@ msgstr "Connexion réussie"
 msgid "Logout successful"
 msgstr "Déconnexion réussie"
 
-#: src/views/preference/Preference.vue:112
+#: src/views/preference/Preference.vue:119
 msgid "Logrotate"
 msgstr ""
 
@@ -1018,6 +1025,7 @@ msgid "Managed Certificate"
 msgstr "Changer de certificat"
 
 #: src/views/dashboard/ServerAnalytic.vue:217
+#: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgstr "Mémoire"
 
@@ -1029,12 +1037,12 @@ msgstr "Mémoire et stockage"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:26
+#: src/views/preference/OpenAISettings.vue:27
 #, fuzzy
 msgid "Model"
 msgstr "Mode d'exécution"
 
-#: src/components/ChatGPT/ChatGPT.vue:244
+#: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 msgid "Modify"
@@ -1062,7 +1070,7 @@ msgstr "Directive multiligne"
 #: src/views/certificate/Certificate.vue:20
 #: src/views/certificate/CertificateEditor.vue:146
 #: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
-#: src/views/domain/cert/ChangeCert.vue:18
+#: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
 #: src/views/domain/DomainList.vue:13
@@ -1074,19 +1082,19 @@ msgstr "Directive multiligne"
 msgid "Name"
 msgstr "Nom"
 
-#: src/views/dashboard/ServerAnalytic.vue:319
+#: src/views/dashboard/ServerAnalytic.vue:322
 msgid "Network"
 msgstr "Réseau"
 
-#: src/views/dashboard/ServerAnalytic.vue:261
+#: src/views/dashboard/ServerAnalytic.vue:264
 msgid "Network Statistics"
 msgstr "Statistiques du réseau"
 
-#: src/views/dashboard/ServerAnalytic.vue:268
+#: src/views/dashboard/ServerAnalytic.vue:271
 msgid "Network Total Receive"
 msgstr "Réception totale du réseau"
 
-#: src/views/dashboard/ServerAnalytic.vue:274
+#: src/views/dashboard/ServerAnalytic.vue:277
 msgid "Network Total Send"
 msgstr "Envoi total réseau"
 
@@ -1100,7 +1108,7 @@ msgstr "Nouvelle version publiée"
 msgid "Next"
 msgstr "Suivant"
 
-#: src/views/preference/Preference.vue:100
+#: src/views/preference/Preference.vue:107
 #, fuzzy
 msgid "Nginx"
 msgstr "Journal Nginx"
@@ -1113,7 +1121,7 @@ msgstr "Chemin du journal d'accès Nginx"
 msgid "Nginx Configuration Parse Error"
 msgstr "Erreur d'analyse de configuration Nginx"
 
-#: src/components/NginxControl/NginxControl.vue:62
+#: src/components/NginxControl/NginxControl.vue:65
 msgid "Nginx Control"
 msgstr "Contrôle Nginx"
 
@@ -1125,15 +1133,15 @@ msgstr "Chemin du journal des erreurs Nginx"
 msgid "Nginx Log"
 msgstr "Journal Nginx"
 
-#: src/components/NginxControl/NginxControl.vue:22
+#: src/components/NginxControl/NginxControl.vue:23
 msgid "Nginx reloaded successfully"
 msgstr "Nginx a été rechargé avec succès"
 
-#: src/components/NginxControl/NginxControl.vue:36
+#: src/components/NginxControl/NginxControl.vue:39
 msgid "Nginx restarted successfully"
 msgstr "Nginx a redémarré avec succès"
 
-#: src/components/ChatGPT/ChatGPT.vue:265
+#: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
@@ -1186,22 +1194,22 @@ msgstr "Obtenir un certificat"
 msgid "Obtaining certificate"
 msgstr "Obtention du certificat"
 
-#: src/components/NodeSelector/NodeSelector.vue:75
-#: src/views/dashboard/Environments.vue:95
-#: src/views/environment/Environment.vue:86
+#: src/components/NodeSelector/NodeSelector.vue:95
+#: src/views/dashboard/Environments.vue:106
+#: src/views/environment/Environment.vue:88
 msgid "Offline"
 msgstr ""
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:264
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:154
 msgid "Ok"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:266
+#: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1220,14 +1228,14 @@ msgstr "OK"
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
-#: src/components/NodeSelector/NodeSelector.vue:54
-#: src/components/NodeSelector/NodeSelector.vue:69
-#: src/views/dashboard/Environments.vue:88
-#: src/views/environment/Environment.vue:82
+#: src/components/NodeSelector/NodeSelector.vue:74
+#: src/components/NodeSelector/NodeSelector.vue:89
+#: src/views/dashboard/Environments.vue:99
+#: src/views/environment/Environment.vue:84
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:106
+#: src/views/preference/Preference.vue:113
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1327,7 +1335,7 @@ msgstr ""
 msgid "Pre-release"
 msgstr ""
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
 msgid "Preference"
 msgstr "Préférence"
 
@@ -1353,13 +1361,13 @@ msgid "Provider"
 msgstr "Fournisseur"
 
 #: src/views/dashboard/ServerAnalytic.vue:28
-#: src/views/dashboard/ServerAnalytic.vue:375
+#: src/views/dashboard/ServerAnalytic.vue:378
 #, fuzzy
 msgid "Reads"
 msgstr "Lectures"
 
 #: src/views/dashboard/ServerAnalytic.vue:24
-#: src/views/dashboard/ServerAnalytic.vue:326
+#: src/views/dashboard/ServerAnalytic.vue:329
 #, fuzzy
 msgid "Receive"
 msgstr "Recevoir"
@@ -1377,7 +1385,7 @@ msgstr "Enregistré avec succès"
 msgid "Recursive Nameservers"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/ChatGPT/ChatGPT.vue:283
 msgid "Regenerate response"
 msgstr "Régénérer la réponse"
 
@@ -1413,12 +1421,12 @@ msgstr "Réinstaller"
 msgid "Release Note"
 msgstr "Note de version"
 
-#: src/components/ChatGPT/ChatGPT.vue:254
-#: src/components/NginxControl/NginxControl.vue:97
+#: src/components/ChatGPT/ChatGPT.vue:259
+#: src/components/NginxControl/NginxControl.vue:100
 msgid "Reload"
 msgstr "Recharger"
 
-#: src/components/NginxControl/NginxControl.vue:71
+#: src/components/NginxControl/NginxControl.vue:74
 msgid "Reloading"
 msgstr "Rechargement"
 
@@ -1466,11 +1474,11 @@ msgstr ""
 msgid "Reset"
 msgstr "Réinitialiser"
 
-#: src/components/NginxControl/NginxControl.vue:90
+#: src/components/NginxControl/NginxControl.vue:93
 msgid "Restart"
 msgstr "Redémarrer"
 
-#: src/components/NginxControl/NginxControl.vue:76
+#: src/components/NginxControl/NginxControl.vue:79
 msgid "Restarting"
 msgstr "Redémarrage"
 
@@ -1478,15 +1486,15 @@ msgstr "Redémarrage"
 msgid "Run Mode"
 msgstr "Mode d'exécution"
 
-#: src/components/NginxControl/NginxControl.vue:66
+#: src/components/NginxControl/NginxControl.vue:69
 msgid "Running"
 msgstr "En cours d'éxécution"
 
-#: src/components/ChatGPT/ChatGPT.vue:247
+#: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
 msgid "Save"
 msgstr "Enregistrer"
 
@@ -1502,7 +1510,7 @@ msgstr "Enregistrer l'erreur %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:59
+#: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr "Sauvegarde réussie"
 
@@ -1517,24 +1525,24 @@ msgstr "Enregistré avec succès"
 msgid "SDK"
 msgstr ""
 
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgstr "Sélecteur"
 
 #: src/views/dashboard/ServerAnalytic.vue:25
-#: src/views/dashboard/ServerAnalytic.vue:336
+#: src/views/dashboard/ServerAnalytic.vue:339
 msgid "Send"
 msgstr "Envoyer"
 
-#: src/components/NginxControl/NginxControl.vue:28
-#: src/components/NginxControl/NginxControl.vue:42
+#: src/components/NginxControl/NginxControl.vue:29
+#: src/components/NginxControl/NginxControl.vue:45
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:70
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"
@@ -1544,6 +1552,11 @@ msgstr "Erreur du serveur"
 msgid "Server Info"
 msgstr "Informations sur le serveur"
 
+#: src/views/preference/BasicSettings.vue:117
+#, fuzzy
+msgid "Server Name"
+msgstr "Informations sur le serveur"
+
 #: src/views/domain/cert/components/ObtainCert.vue:102
 msgid "server_name not found in directives"
 msgstr "server_name introuvable dans les directives"
@@ -1617,15 +1630,16 @@ msgstr "Tableau"
 
 #: src/views/certificate/ACMEUser.vue:42
 #: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
-#: src/views/environment/Environment.vue:75 src/views/stream/StreamList.vue:22
+#: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Statut"
 
-#: src/components/NginxControl/NginxControl.vue:81
+#: src/components/NginxControl/NginxControl.vue:84
 msgid "Stopped"
 msgstr "Arrêté"
 
-#: src/views/dashboard/ServerAnalytic.vue:243
+#: src/views/dashboard/ServerAnalytic.vue:245
+#: src/views/dashboard/ServerAnalytic.vue:246
 msgid "Storage"
 msgstr "Stockage"
 
@@ -1638,7 +1652,8 @@ msgstr "Nom du sujet : %{name}"
 msgid "Success"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:230
+#: src/views/dashboard/ServerAnalytic.vue:231
+#: src/views/dashboard/ServerAnalytic.vue:232
 #, fuzzy
 msgid "Swap"
 msgstr "Échanger"
@@ -1690,6 +1705,10 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr "Chemin de la clé du certificat SSL"
 
+#: src/views/preference/OpenAISettings.vue:30
+msgid "The model name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/certificate/CertificateEditor.vue:165
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
@@ -1699,6 +1718,10 @@ msgstr "Chemin de la clé du certificat SSL"
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
+#: src/views/preference/BasicSettings.vue:120
+msgid "The server name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/domain/cert/components/AutoCertStepOne.vue:50
 #, fuzzy
 msgid ""
@@ -1711,9 +1734,12 @@ msgstr ""
 
 #: src/views/preference/BasicSettings.vue:38
 #: src/views/preference/BasicSettings.vue:50
-#: src/views/preference/OpenAISettings.vue:36
-#: src/views/preference/OpenAISettings.vue:48
-msgid "The url is not valid"
+msgid "The url is invalid"
+msgstr ""
+
+#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:54
+msgid "The url is invalid."
 msgstr ""
 
 #: src/language/constants.ts:2
@@ -1761,7 +1787,7 @@ msgstr ""
 "transmettre la demande de l'autorité au backend, et nous devons enregistrer "
 "ce fichier et recharger le Nginx. Êtes-vous sûr de vouloir continuer?"
 
-#: src/views/preference/OpenAISettings.vue:60
+#: src/views/preference/OpenAISettings.vue:66
 msgid "Token is not valid"
 msgstr ""
 
@@ -1778,7 +1804,7 @@ msgstr "Type"
 #: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
-#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:95
+#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
 #: src/views/stream/components/RightSettings.vue:85
 #: src/views/stream/StreamList.vue:41 src/views/user/User.vue:37
 msgid "Updated at"
@@ -1809,11 +1835,11 @@ msgstr ""
 msgid "Uptime:"
 msgstr "Disponibilité :"
 
-#: src/views/environment/Environment.vue:21
+#: src/views/environment/Environment.vue:22
 msgid "URL"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 #, fuzzy
 msgid "User"
 msgstr "Nom d'utilisateur"
@@ -1871,7 +1897,7 @@ msgstr ""
 "recharger le Nginx. Êtes-vous sûr de vouloir continuer?"
 
 #: src/views/dashboard/ServerAnalytic.vue:27
-#: src/views/dashboard/ServerAnalytic.vue:365
+#: src/views/dashboard/ServerAnalytic.vue:368
 msgid "Writes"
 msgstr "Écritures"
 
@@ -1919,10 +1945,6 @@ msgstr "Vous pouvez vérifier la mise à niveau de Nginx UI sur cette page."
 #~ msgid "Table"
 #~ msgstr "Tableau"
 
-#, fuzzy
-#~ msgid "Server"
-#~ msgstr "Informations sur le serveur"
-
 #, fuzzy
 #~ msgid "Leave blank will not change anything."
 #~ msgstr "Laisser vide pour aucun changement"

+ 112 - 85
app/src/language/ko_KR/app.po

@@ -22,7 +22,7 @@ msgid "Access Logs"
 msgstr "접근 로그"
 
 #: src/routes/index.ts:128 src/views/certificate/ACMEUser.vue:76
-#: src/views/certificate/ACMEUserSelector.vue:79
+#: src/views/certificate/ACMEUserSelector.vue:84
 #, fuzzy
 msgid "ACME User"
 msgstr "사용자 이름"
@@ -30,7 +30,7 @@ msgstr "사용자 이름"
 #: src/views/certificate/ACMEUser.vue:59
 #: src/views/certificate/Certificate.vue:108
 #: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
-#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:102
+#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:35
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
@@ -75,7 +75,7 @@ msgstr "추가적인"
 msgid "Advance Mode"
 msgstr "고급 모드"
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:39
 msgid "API Base Url"
 msgstr "API 기본 URL"
 
@@ -84,11 +84,11 @@ msgstr "API 기본 URL"
 msgid "API Document"
 msgstr "API 토큰"
 
-#: src/views/preference/OpenAISettings.vue:45
+#: src/views/preference/OpenAISettings.vue:51
 msgid "API Proxy"
 msgstr "API 프록시"
 
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:63
 msgid "API Token"
 msgstr "API 토큰"
 
@@ -101,7 +101,7 @@ msgstr "아키텍처"
 msgid "Are you sure you want to clear all notifications?"
 msgstr "모든 알림을 지우시겠습니까?"
 
-#: src/components/ChatGPT/ChatGPT.vue:267
+#: src/components/ChatGPT/ChatGPT.vue:272
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "기록을 지우시겠습니까?"
 
@@ -132,11 +132,11 @@ msgstr "이 지시문을 정말로 제거하시겠습니까?"
 msgid "Are you sure you want to remove this location?"
 msgstr "이 위치를 제거하시겠습니까?"
 
-#: src/components/ChatGPT/ChatGPT.vue:211
+#: src/components/ChatGPT/ChatGPT.vue:216
 msgid "Ask ChatGPT for Help"
 msgstr "ChatGPT에게 도움 요청"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "Assistant"
 msgstr "조수"
 
@@ -145,7 +145,7 @@ msgstr "조수"
 msgid "Author"
 msgstr "저자"
 
-#: src/views/domain/cert/ChangeCert.vue:34
+#: src/views/domain/cert/ChangeCert.vue:33
 msgid "Auto Cert"
 msgstr "자동 인증"
 
@@ -178,7 +178,7 @@ msgstr "기본 정보"
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:94
+#: src/views/preference/Preference.vue:101
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgstr "기본"
@@ -204,10 +204,10 @@ msgstr ""
 msgid "CADir"
 msgstr "CA 디렉토리"
 
-#: src/components/ChatGPT/ChatGPT.vue:248
+#: src/components/ChatGPT/ChatGPT.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:263
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:102
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:153
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:21
 #: src/views/domain/components/RightSettings.vue:51
@@ -249,8 +249,8 @@ msgstr "인증서 목록"
 msgid "Challenge Method"
 msgstr "인증 방법"
 
-#: src/views/domain/cert/ChangeCert.vue:88
-#: src/views/domain/cert/ChangeCert.vue:92
+#: src/views/domain/cert/ChangeCert.vue:95
+#: src/views/domain/cert/ChangeCert.vue:99
 msgid "Change Certificate"
 msgstr "인증서 변경"
 
@@ -266,7 +266,7 @@ msgstr "다시 확인"
 msgid "Cleaning environment variables"
 msgstr "환경 변수 정리"
 
-#: src/components/ChatGPT/ChatGPT.vue:271
+#: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
 #: src/views/notification/Notification.vue:75
 msgid "Clear"
@@ -308,7 +308,7 @@ msgstr "구성들"
 msgid "Configure SSL"
 msgstr "SSL 구성하기"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Connected"
 msgstr "연결됨"
 
@@ -322,7 +322,7 @@ msgstr "내용"
 msgid "Core Upgrade"
 msgstr "코어 업그레이드"
 
-#: src/views/dashboard/ServerAnalytic.vue:293
+#: src/views/dashboard/ServerAnalytic.vue:296
 msgid "CPU Status"
 msgstr "CPU 상태"
 
@@ -363,6 +363,12 @@ msgstr "현재 버전"
 msgid "Custom"
 msgstr "사용자 정의"
 
+#: src/views/preference/BasicSettings.vue:121
+msgid ""
+"Customize the name of local server to be displayed in the environment "
+"indicator."
+msgstr ""
+
 #: src/routes/index.ts:39
 msgid "Dashboard"
 msgstr "대시보드"
@@ -450,9 +456,9 @@ msgstr "비활성화"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "%{name}의 자동 갱신 비활성화 실패"
 
-#: src/views/domain/cert/ChangeCert.vue:45 src/views/domain/DomainEdit.vue:185
-#: src/views/domain/DomainList.vue:33 src/views/stream/StreamEdit.vue:177
-#: src/views/stream/StreamList.vue:33
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
+#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "비활성화됨"
 
@@ -463,7 +469,7 @@ msgstr "비활성화됨"
 msgid "Disabled successfully"
 msgstr "성공적으로 비활성화됨"
 
-#: src/views/dashboard/ServerAnalytic.vue:358
+#: src/views/dashboard/ServerAnalytic.vue:361
 msgid "Disk IO"
 msgstr "디스크 IO"
 
@@ -631,9 +637,10 @@ msgstr "성공적으로 활성화"
 msgid "Enable TLS"
 msgstr "TLS 활성화"
 
-#: src/views/domain/cert/ChangeCert.vue:41
+#: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
@@ -653,11 +660,11 @@ msgstr "성공적으로 활성화됨"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Let's Encrypt로 웹사이트 암호화"
 
-#: src/routes/index.ts:212 src/views/environment/Environment.vue:110
+#: src/routes/index.ts:212 src/views/environment/Environment.vue:137
 msgid "Environment"
 msgstr "환경"
 
-#: src/views/dashboard/Environments.vue:71
+#: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr "환경"
 
@@ -883,7 +890,7 @@ msgstr "마지막 확인 시간"
 msgid "Leave blank for no change"
 msgstr "변경사항이 없으면 비워두세요"
 
-#: src/views/preference/OpenAISettings.vue:41
+#: src/views/preference/OpenAISettings.vue:47
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "기본값을 사용하려면 비워 두세요: https://api.openai.com/"
 
@@ -898,7 +905,7 @@ msgstr "변경사항이 없으면 비워두세요"
 msgid "License"
 msgstr "라이센스"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Link Start"
 msgstr "링크 시작"
 
@@ -912,8 +919,8 @@ msgstr ""
 msgid "Load Average:"
 msgstr "부하 평균:"
 
-#: src/components/EnvIndicator/EnvIndicator.vue:38
-#: src/components/NodeSelector/NodeSelector.vue:51
+#: src/components/EnvIndicator/EnvIndicator.vue:40
+#: src/components/NodeSelector/NodeSelector.vue:71
 #, fuzzy
 msgid "Local"
 msgstr "지역"
@@ -943,7 +950,7 @@ msgstr "로그인 성공"
 msgid "Logout successful"
 msgstr "로그아웃 성공"
 
-#: src/views/preference/Preference.vue:112
+#: src/views/preference/Preference.vue:119
 msgid "Logrotate"
 msgstr "로그관리"
 
@@ -994,6 +1001,7 @@ msgid "Managed Certificate"
 msgstr "인증서 유효"
 
 #: src/views/dashboard/ServerAnalytic.vue:217
+#: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgstr "메모리"
 
@@ -1005,12 +1013,12 @@ msgstr "메모리 및 저장소"
 msgid "Minutes"
 msgstr "분"
 
-#: src/views/preference/OpenAISettings.vue:26
+#: src/views/preference/OpenAISettings.vue:27
 #, fuzzy
 msgid "Model"
 msgstr "실행 모드"
 
-#: src/components/ChatGPT/ChatGPT.vue:244
+#: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #, fuzzy
@@ -1040,7 +1048,7 @@ msgstr "단일 지시문"
 #: src/views/certificate/Certificate.vue:20
 #: src/views/certificate/CertificateEditor.vue:146
 #: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
-#: src/views/domain/cert/ChangeCert.vue:18
+#: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
 #: src/views/domain/DomainList.vue:13
@@ -1052,19 +1060,19 @@ msgstr "단일 지시문"
 msgid "Name"
 msgstr "이름"
 
-#: src/views/dashboard/ServerAnalytic.vue:319
+#: src/views/dashboard/ServerAnalytic.vue:322
 msgid "Network"
 msgstr "네트워크"
 
-#: src/views/dashboard/ServerAnalytic.vue:261
+#: src/views/dashboard/ServerAnalytic.vue:264
 msgid "Network Statistics"
 msgstr "네트워크 통계"
 
-#: src/views/dashboard/ServerAnalytic.vue:268
+#: src/views/dashboard/ServerAnalytic.vue:271
 msgid "Network Total Receive"
 msgstr "네트워크 총 수신"
 
-#: src/views/dashboard/ServerAnalytic.vue:274
+#: src/views/dashboard/ServerAnalytic.vue:277
 msgid "Network Total Send"
 msgstr "네트워크 총 송신"
 
@@ -1078,7 +1086,7 @@ msgstr "새 버전 출시"
 msgid "Next"
 msgstr "다음"
 
-#: src/views/preference/Preference.vue:100
+#: src/views/preference/Preference.vue:107
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1091,7 +1099,7 @@ msgstr "Nginx 접근 로그 경로"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 구성 오류름"
 
-#: src/components/NginxControl/NginxControl.vue:62
+#: src/components/NginxControl/NginxControl.vue:65
 msgid "Nginx Control"
 msgstr "Nginx 제어"
 
@@ -1103,17 +1111,17 @@ msgstr "Nginx 오류 로그 경로"
 msgid "Nginx Log"
 msgstr "Nginx 로그"
 
-#: src/components/NginxControl/NginxControl.vue:22
+#: src/components/NginxControl/NginxControl.vue:23
 #, fuzzy
 msgid "Nginx reloaded successfully"
 msgstr "Nginx가 성공적으로 리로드됨"
 
-#: src/components/NginxControl/NginxControl.vue:36
+#: src/components/NginxControl/NginxControl.vue:39
 #, fuzzy
 msgid "Nginx restarted successfully"
 msgstr "Nginx가 성공적으로 재시작됨"
 
-#: src/components/ChatGPT/ChatGPT.vue:265
+#: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
@@ -1166,22 +1174,22 @@ msgstr "인증서 획득"
 msgid "Obtaining certificate"
 msgstr "인증서 획득 중"
 
-#: src/components/NodeSelector/NodeSelector.vue:75
-#: src/views/dashboard/Environments.vue:95
-#: src/views/environment/Environment.vue:86
+#: src/components/NodeSelector/NodeSelector.vue:95
+#: src/views/dashboard/Environments.vue:106
+#: src/views/environment/Environment.vue:88
 msgid "Offline"
 msgstr "오프라인"
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:264
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:154
 msgid "Ok"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:266
+#: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1200,14 +1208,14 @@ msgstr "확인"
 msgid "Once the verification is complete, the records will be removed."
 msgstr "검증이 완료되면, 레코드는 제거됩니다."
 
-#: src/components/NodeSelector/NodeSelector.vue:54
-#: src/components/NodeSelector/NodeSelector.vue:69
-#: src/views/dashboard/Environments.vue:88
-#: src/views/environment/Environment.vue:82
+#: src/components/NodeSelector/NodeSelector.vue:74
+#: src/components/NodeSelector/NodeSelector.vue:89
+#: src/views/dashboard/Environments.vue:99
+#: src/views/environment/Environment.vue:84
 msgid "Online"
 msgstr "온라인"
 
-#: src/views/preference/Preference.vue:106
+#: src/views/preference/Preference.vue:113
 msgid "OpenAI"
 msgstr "오픈AI"
 
@@ -1303,7 +1311,7 @@ msgstr "적어도 하나의 노드를 선택해주세요!"
 msgid "Pre-release"
 msgstr "사전 출시"
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
 msgid "Preference"
 msgstr "환경설정"
 
@@ -1329,12 +1337,12 @@ msgid "Provider"
 msgstr "제공자"
 
 #: src/views/dashboard/ServerAnalytic.vue:28
-#: src/views/dashboard/ServerAnalytic.vue:375
+#: src/views/dashboard/ServerAnalytic.vue:378
 msgid "Reads"
 msgstr "읽기"
 
 #: src/views/dashboard/ServerAnalytic.vue:24
-#: src/views/dashboard/ServerAnalytic.vue:326
+#: src/views/dashboard/ServerAnalytic.vue:329
 msgid "Receive"
 msgstr "수신"
 
@@ -1351,7 +1359,7 @@ msgstr "성공적으로 제거됨"
 msgid "Recursive Nameservers"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/ChatGPT/ChatGPT.vue:283
 msgid "Regenerate response"
 msgstr "응답 재생성"
 
@@ -1388,12 +1396,12 @@ msgstr "재설치"
 msgid "Release Note"
 msgstr "릴리스 노트"
 
-#: src/components/ChatGPT/ChatGPT.vue:254
-#: src/components/NginxControl/NginxControl.vue:97
+#: src/components/ChatGPT/ChatGPT.vue:259
+#: src/components/NginxControl/NginxControl.vue:100
 msgid "Reload"
 msgstr "리로드"
 
-#: src/components/NginxControl/NginxControl.vue:71
+#: src/components/NginxControl/NginxControl.vue:74
 msgid "Reloading"
 msgstr "리로딩 중"
 
@@ -1441,11 +1449,11 @@ msgstr "잘못된 매개변수로 요청됨"
 msgid "Reset"
 msgstr "재설정"
 
-#: src/components/NginxControl/NginxControl.vue:90
+#: src/components/NginxControl/NginxControl.vue:93
 msgid "Restart"
 msgstr "재시작"
 
-#: src/components/NginxControl/NginxControl.vue:76
+#: src/components/NginxControl/NginxControl.vue:79
 msgid "Restarting"
 msgstr "재시작 중"
 
@@ -1454,15 +1462,15 @@ msgstr "재시작 중"
 msgid "Run Mode"
 msgstr "실행 모드"
 
-#: src/components/NginxControl/NginxControl.vue:66
+#: src/components/NginxControl/NginxControl.vue:69
 msgid "Running"
 msgstr "실행 중"
 
-#: src/components/ChatGPT/ChatGPT.vue:247
+#: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
 msgid "Save"
 msgstr "저장"
 
@@ -1478,7 +1486,7 @@ msgstr "저장 오류 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:59
+#: src/views/preference/Preference.vue:66
 #, fuzzy
 msgid "Save successfully"
 msgstr "성공적으로 저장됨"
@@ -1494,24 +1502,24 @@ msgstr "성공적으로 저장됨"
 msgid "SDK"
 msgstr ""
 
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgstr "선택"
 
 #: src/views/dashboard/ServerAnalytic.vue:25
-#: src/views/dashboard/ServerAnalytic.vue:336
+#: src/views/dashboard/ServerAnalytic.vue:339
 msgid "Send"
 msgstr "보내기"
 
-#: src/components/NginxControl/NginxControl.vue:28
-#: src/components/NginxControl/NginxControl.vue:42
+#: src/components/NginxControl/NginxControl.vue:29
+#: src/components/NginxControl/NginxControl.vue:45
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:70
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"
@@ -1521,6 +1529,11 @@ msgstr "서버 오류"
 msgid "Server Info"
 msgstr "서버 정보"
 
+#: src/views/preference/BasicSettings.vue:117
+#, fuzzy
+msgid "Server Name"
+msgstr "Server Info"
+
 #: src/views/domain/cert/components/ObtainCert.vue:102
 msgid "server_name not found in directives"
 msgstr "directives에서 server_name을 찾을 수 없습니다"
@@ -1594,15 +1607,16 @@ msgstr "활성화됨"
 
 #: src/views/certificate/ACMEUser.vue:42
 #: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
-#: src/views/environment/Environment.vue:75 src/views/stream/StreamList.vue:22
+#: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "상태"
 
-#: src/components/NginxControl/NginxControl.vue:81
+#: src/components/NginxControl/NginxControl.vue:84
 msgid "Stopped"
 msgstr "정지됨"
 
-#: src/views/dashboard/ServerAnalytic.vue:243
+#: src/views/dashboard/ServerAnalytic.vue:245
+#: src/views/dashboard/ServerAnalytic.vue:246
 msgid "Storage"
 msgstr "저장소"
 
@@ -1615,7 +1629,8 @@ msgstr "제목 이름: %{name}"
 msgid "Success"
 msgstr "성공"
 
-#: src/views/dashboard/ServerAnalytic.vue:230
+#: src/views/dashboard/ServerAnalytic.vue:231
+#: src/views/dashboard/ServerAnalytic.vue:232
 msgid "Swap"
 msgstr "스왑"
 
@@ -1666,6 +1681,10 @@ msgstr "입력이 SSL 인증서가 아닙니다"
 msgid "The input is not a SSL Certificate Key"
 msgstr "Certificate Status"
 
+#: src/views/preference/OpenAISettings.vue:30
+msgid "The model name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/certificate/CertificateEditor.vue:165
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
@@ -1675,6 +1694,10 @@ msgstr "Certificate Status"
 msgid "The path exists, but the file is not a private key"
 msgstr "경로는 존재하지만 파일은 개인 키가 아닙니다"
 
+#: src/views/preference/BasicSettings.vue:120
+msgid "The server name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/domain/cert/components/AutoCertStepOne.vue:50
 #, fuzzy
 msgid ""
@@ -1686,9 +1709,14 @@ msgstr ""
 
 #: src/views/preference/BasicSettings.vue:38
 #: src/views/preference/BasicSettings.vue:50
-#: src/views/preference/OpenAISettings.vue:36
-#: src/views/preference/OpenAISettings.vue:48
-msgid "The url is not valid"
+#, fuzzy
+msgid "The url is invalid"
+msgstr "유효한 URL이 아닙니다"
+
+#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:54
+#, fuzzy
+msgid "The url is invalid."
 msgstr "유효한 URL이 아닙니다"
 
 #: src/language/constants.ts:2
@@ -1732,7 +1760,7 @@ msgstr ""
 "시할 수 있는 위치를 추가해야 하며,이 파일을 저장하고 Nginx를 다시로드해야 합"
 "니다.계속하시겠습니까?"
 
-#: src/views/preference/OpenAISettings.vue:60
+#: src/views/preference/OpenAISettings.vue:66
 msgid "Token is not valid"
 msgstr "토큰이 유효하지 않습니다"
 
@@ -1749,7 +1777,7 @@ msgstr "유형"
 #: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
-#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:95
+#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
 #: src/views/stream/components/RightSettings.vue:85
 #: src/views/stream/StreamList.vue:41 src/views/user/User.vue:37
 msgid "Updated at"
@@ -1782,11 +1810,11 @@ msgstr "업스트림 이름"
 msgid "Uptime:"
 msgstr "가동 시간:"
 
-#: src/views/environment/Environment.vue:21
+#: src/views/environment/Environment.vue:22
 msgid "URL"
 msgstr "URL"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 #, fuzzy
 msgid "User"
 msgstr "사용자 이름"
@@ -1847,7 +1875,7 @@ msgstr ""
 "속하시겠습니까?"
 
 #: src/views/dashboard/ServerAnalytic.vue:27
-#: src/views/dashboard/ServerAnalytic.vue:365
+#: src/views/dashboard/ServerAnalytic.vue:368
 msgid "Writes"
 msgstr "쓰기"
 
@@ -1873,6 +1901,9 @@ msgstr "최신 버전을 사용하고 있습니다"
 msgid "You can check Nginx UI upgrade at this page."
 msgstr "이 페이지에서 Nginx UI 업그레이드를 확인할 수 있습니다."
 
+#~ msgid "The url is not valid"
+#~ msgstr "유효한 URL이 아닙니다"
+
 #~ msgid "ChatGPT Model"
 #~ msgstr "ChatGPT 모델"
 
@@ -1896,10 +1927,6 @@ msgstr "이 페이지에서 Nginx UI 업그레이드를 확인할 수 있습니
 #~ msgid "Table"
 #~ msgstr "활성화됨"
 
-#, fuzzy
-#~ msgid "Server"
-#~ msgstr "Server Info"
-
 #, fuzzy
 #~ msgid "Leave blank will not change anything."
 #~ msgstr "Leave blank for no change"

+ 103 - 79
app/src/language/messages.pot

@@ -13,7 +13,7 @@ msgstr ""
 
 #: src/routes/index.ts:128
 #: src/views/certificate/ACMEUser.vue:76
-#: src/views/certificate/ACMEUserSelector.vue:79
+#: src/views/certificate/ACMEUserSelector.vue:84
 msgid "ACME User"
 msgstr ""
 
@@ -22,7 +22,7 @@ msgstr ""
 #: src/views/certificate/DNSCredential.vue:29
 #: src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47
-#: src/views/environment/Environment.vue:102
+#: src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:35
 #: src/views/stream/StreamList.vue:47
 #: src/views/user/User.vue:43
@@ -70,7 +70,7 @@ msgstr ""
 msgid "Advance Mode"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:39
 msgid "API Base Url"
 msgstr ""
 
@@ -78,11 +78,11 @@ msgstr ""
 msgid "API Document"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:45
+#: src/views/preference/OpenAISettings.vue:51
 msgid "API Proxy"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:63
 msgid "API Token"
 msgstr ""
 
@@ -95,7 +95,7 @@ msgstr ""
 msgid "Are you sure you want to clear all notifications?"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:267
+#: src/components/ChatGPT/ChatGPT.vue:272
 msgid "Are you sure you want to clear the record of chat?"
 msgstr ""
 
@@ -124,11 +124,11 @@ msgstr ""
 msgid "Are you sure you want to remove this location?"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:211
+#: src/components/ChatGPT/ChatGPT.vue:216
 msgid "Ask ChatGPT for Help"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "Assistant"
 msgstr ""
 
@@ -137,7 +137,7 @@ msgstr ""
 msgid "Author"
 msgstr ""
 
-#: src/views/domain/cert/ChangeCert.vue:34
+#: src/views/domain/cert/ChangeCert.vue:33
 msgid "Auto Cert"
 msgstr ""
 
@@ -172,7 +172,7 @@ msgstr ""
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:94
+#: src/views/preference/Preference.vue:101
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgstr ""
@@ -199,10 +199,10 @@ msgstr ""
 msgid "CADir"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:248
+#: src/components/ChatGPT/ChatGPT.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:263
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:102
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:153
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:21
 #: src/views/domain/components/RightSettings.vue:51
@@ -244,8 +244,8 @@ msgstr ""
 msgid "Challenge Method"
 msgstr ""
 
-#: src/views/domain/cert/ChangeCert.vue:88
-#: src/views/domain/cert/ChangeCert.vue:92
+#: src/views/domain/cert/ChangeCert.vue:95
+#: src/views/domain/cert/ChangeCert.vue:99
 msgid "Change Certificate"
 msgstr ""
 
@@ -261,7 +261,7 @@ msgstr ""
 msgid "Cleaning environment variables"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:271
+#: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
 #: src/views/notification/Notification.vue:75
 msgid "Clear"
@@ -303,7 +303,7 @@ msgstr ""
 msgid "Configure SSL"
 msgstr ""
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Connected"
 msgstr ""
 
@@ -317,7 +317,7 @@ msgstr ""
 msgid "Core Upgrade"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:293
+#: src/views/dashboard/ServerAnalytic.vue:296
 msgid "CPU Status"
 msgstr ""
 
@@ -359,6 +359,10 @@ msgstr ""
 msgid "Custom"
 msgstr ""
 
+#: src/views/preference/BasicSettings.vue:121
+msgid "Customize the name of local server to be displayed in the environment indicator."
+msgstr ""
+
 #: src/routes/index.ts:39
 msgid "Dashboard"
 msgstr ""
@@ -447,9 +451,10 @@ msgstr ""
 msgid "Disable auto-renewal failed for %{name}"
 msgstr ""
 
-#: src/views/domain/cert/ChangeCert.vue:45
+#: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/DomainEdit.vue:185
 #: src/views/domain/DomainList.vue:33
+#: src/views/environment/Environment.vue:93
 #: src/views/stream/StreamEdit.vue:177
 #: src/views/stream/StreamList.vue:33
 msgid "Disabled"
@@ -462,7 +467,7 @@ msgstr ""
 msgid "Disabled successfully"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:358
+#: src/views/dashboard/ServerAnalytic.vue:361
 msgid "Disk IO"
 msgstr ""
 
@@ -630,10 +635,11 @@ msgstr ""
 msgid "Enable TLS"
 msgstr ""
 
-#: src/views/domain/cert/ChangeCert.vue:41
+#: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/DomainEdit.vue:179
 #: src/views/domain/DomainList.vue:29
+#: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/StreamEdit.vue:171
@@ -656,11 +662,11 @@ msgid "Encrypt website with Let's Encrypt"
 msgstr ""
 
 #: src/routes/index.ts:212
-#: src/views/environment/Environment.vue:110
+#: src/views/environment/Environment.vue:137
 msgid "Environment"
 msgstr ""
 
-#: src/views/dashboard/Environments.vue:71
+#: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr ""
 
@@ -879,7 +885,7 @@ msgstr ""
 msgid "Leave blank for no change"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:41
+#: src/views/preference/OpenAISettings.vue:47
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr ""
 
@@ -892,7 +898,7 @@ msgstr ""
 msgid "License"
 msgstr ""
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Link Start"
 msgstr ""
 
@@ -905,8 +911,8 @@ msgstr ""
 msgid "Load Average:"
 msgstr ""
 
-#: src/components/EnvIndicator/EnvIndicator.vue:38
-#: src/components/NodeSelector/NodeSelector.vue:51
+#: src/components/EnvIndicator/EnvIndicator.vue:40
+#: src/components/NodeSelector/NodeSelector.vue:71
 msgid "Local"
 msgstr ""
 
@@ -936,7 +942,7 @@ msgstr ""
 msgid "Logout successful"
 msgstr ""
 
-#: src/views/preference/Preference.vue:112
+#: src/views/preference/Preference.vue:119
 msgid "Logrotate"
 msgstr ""
 
@@ -972,6 +978,7 @@ msgid "Managed Certificate"
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:217
+#: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgstr ""
 
@@ -983,11 +990,11 @@ msgstr ""
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:26
+#: src/views/preference/OpenAISettings.vue:27
 msgid "Model"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:244
+#: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 msgid "Modify"
@@ -1015,7 +1022,7 @@ msgstr ""
 #: src/views/certificate/CertificateEditor.vue:146
 #: src/views/certificate/DNSCredential.vue:10
 #: src/views/config/config.ts:7
-#: src/views/domain/cert/ChangeCert.vue:18
+#: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
 #: src/views/domain/DomainList.vue:13
@@ -1028,19 +1035,19 @@ msgstr ""
 msgid "Name"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:319
+#: src/views/dashboard/ServerAnalytic.vue:322
 msgid "Network"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:261
+#: src/views/dashboard/ServerAnalytic.vue:264
 msgid "Network Statistics"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:268
+#: src/views/dashboard/ServerAnalytic.vue:271
 msgid "Network Total Receive"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:274
+#: src/views/dashboard/ServerAnalytic.vue:277
 msgid "Network Total Send"
 msgstr ""
 
@@ -1054,7 +1061,7 @@ msgstr ""
 msgid "Next"
 msgstr ""
 
-#: src/views/preference/Preference.vue:100
+#: src/views/preference/Preference.vue:107
 msgid "Nginx"
 msgstr ""
 
@@ -1067,7 +1074,7 @@ msgstr ""
 msgid "Nginx Configuration Parse Error"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:62
+#: src/components/NginxControl/NginxControl.vue:65
 msgid "Nginx Control"
 msgstr ""
 
@@ -1080,15 +1087,15 @@ msgstr ""
 msgid "Nginx Log"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:22
+#: src/components/NginxControl/NginxControl.vue:23
 msgid "Nginx reloaded successfully"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:36
+#: src/components/NginxControl/NginxControl.vue:39
 msgid "Nginx restarted successfully"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:265
+#: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
@@ -1139,22 +1146,22 @@ msgstr ""
 msgid "Obtaining certificate"
 msgstr ""
 
-#: src/components/NodeSelector/NodeSelector.vue:75
-#: src/views/dashboard/Environments.vue:95
-#: src/views/environment/Environment.vue:86
+#: src/components/NodeSelector/NodeSelector.vue:95
+#: src/views/dashboard/Environments.vue:106
+#: src/views/environment/Environment.vue:88
 msgid "Offline"
 msgstr ""
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:264
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:154
 msgid "Ok"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:266
+#: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1173,14 +1180,14 @@ msgstr ""
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
-#: src/components/NodeSelector/NodeSelector.vue:54
-#: src/components/NodeSelector/NodeSelector.vue:69
-#: src/views/dashboard/Environments.vue:88
-#: src/views/environment/Environment.vue:82
+#: src/components/NodeSelector/NodeSelector.vue:74
+#: src/components/NodeSelector/NodeSelector.vue:89
+#: src/views/dashboard/Environments.vue:99
+#: src/views/environment/Environment.vue:84
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:106
+#: src/views/preference/Preference.vue:113
 msgid "OpenAI"
 msgstr ""
 
@@ -1271,7 +1278,7 @@ msgid "Pre-release"
 msgstr ""
 
 #: src/routes/index.ts:239
-#: src/views/preference/Preference.vue:89
+#: src/views/preference/Preference.vue:96
 msgid "Preference"
 msgstr ""
 
@@ -1296,12 +1303,12 @@ msgid "Provider"
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:28
-#: src/views/dashboard/ServerAnalytic.vue:375
+#: src/views/dashboard/ServerAnalytic.vue:378
 msgid "Reads"
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:24
-#: src/views/dashboard/ServerAnalytic.vue:326
+#: src/views/dashboard/ServerAnalytic.vue:329
 msgid "Receive"
 msgstr ""
 
@@ -1317,7 +1324,7 @@ msgstr ""
 msgid "Recursive Nameservers"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/ChatGPT/ChatGPT.vue:283
 msgid "Regenerate response"
 msgstr ""
 
@@ -1349,12 +1356,12 @@ msgstr ""
 msgid "Release Note"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:254
-#: src/components/NginxControl/NginxControl.vue:97
+#: src/components/ChatGPT/ChatGPT.vue:259
+#: src/components/NginxControl/NginxControl.vue:100
 msgid "Reload"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:71
+#: src/components/NginxControl/NginxControl.vue:74
 msgid "Reloading"
 msgstr ""
 
@@ -1396,11 +1403,11 @@ msgstr ""
 msgid "Reset"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:90
+#: src/components/NginxControl/NginxControl.vue:93
 msgid "Restart"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:76
+#: src/components/NginxControl/NginxControl.vue:79
 msgid "Restarting"
 msgstr ""
 
@@ -1408,16 +1415,16 @@ msgstr ""
 msgid "Run Mode"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:66
+#: src/components/NginxControl/NginxControl.vue:69
 msgid "Running"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:247
+#: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:96
 #: src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:123
+#: src/views/preference/Preference.vue:130
 #: src/views/stream/StreamEdit.vue:254
 msgid "Save"
 msgstr ""
@@ -1435,7 +1442,7 @@ msgstr ""
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:59
+#: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr ""
 
@@ -1451,17 +1458,17 @@ msgstr ""
 msgid "SDK"
 msgstr ""
 
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:25
-#: src/views/dashboard/ServerAnalytic.vue:336
+#: src/views/dashboard/ServerAnalytic.vue:339
 msgid "Send"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:28
-#: src/components/NginxControl/NginxControl.vue:42
+#: src/components/NginxControl/NginxControl.vue:29
+#: src/components/NginxControl/NginxControl.vue:45
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
@@ -1470,7 +1477,7 @@ msgstr ""
 #: src/views/config/ConfigEdit.vue:40
 #: src/views/domain/DomainList.vue:81
 #: src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:63
+#: src/views/preference/Preference.vue:70
 #: src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
@@ -1481,6 +1488,10 @@ msgstr ""
 msgid "Server Info"
 msgstr ""
 
+#: src/views/preference/BasicSettings.vue:117
+msgid "Server Name"
+msgstr ""
+
 #: src/views/domain/cert/components/ObtainCert.vue:102
 msgid "server_name not found in directives"
 msgstr ""
@@ -1548,16 +1559,17 @@ msgstr ""
 #: src/views/certificate/ACMEUser.vue:42
 #: src/views/certificate/Certificate.vue:83
 #: src/views/domain/DomainList.vue:22
-#: src/views/environment/Environment.vue:75
+#: src/views/environment/Environment.vue:76
 #: src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:81
+#: src/components/NginxControl/NginxControl.vue:84
 msgid "Stopped"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:243
+#: src/views/dashboard/ServerAnalytic.vue:245
+#: src/views/dashboard/ServerAnalytic.vue:246
 msgid "Storage"
 msgstr ""
 
@@ -1569,7 +1581,8 @@ msgstr ""
 msgid "Success"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:230
+#: src/views/dashboard/ServerAnalytic.vue:231
+#: src/views/dashboard/ServerAnalytic.vue:232
 msgid "Swap"
 msgstr ""
 
@@ -1615,6 +1628,10 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr ""
 
+#: src/views/preference/OpenAISettings.vue:30
+msgid "The model name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/certificate/CertificateEditor.vue:165
 msgid "The path exists, but the file is not a certificate"
 msgstr ""
@@ -1623,15 +1640,22 @@ msgstr ""
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
+#: src/views/preference/BasicSettings.vue:120
+msgid "The server name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/domain/cert/components/AutoCertStepOne.vue:50
 msgid "The server_name in the current configuration must be the domain name you need to get the certificate, supportmultiple domains."
 msgstr ""
 
 #: src/views/preference/BasicSettings.vue:38
 #: src/views/preference/BasicSettings.vue:50
-#: src/views/preference/OpenAISettings.vue:36
-#: src/views/preference/OpenAISettings.vue:48
-msgid "The url is not valid"
+msgid "The url is invalid"
+msgstr ""
+
+#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:54
+msgid "The url is invalid."
 msgstr ""
 
 #: src/language/constants.ts:2
@@ -1668,7 +1692,7 @@ msgstr ""
 msgid "To make sure the certification auto-renewal can work normally, we need to add a location which can proxy the request from authority to backend, and we need to save this file and reload the Nginx. Are you sure you want to continue?"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:60
+#: src/views/preference/OpenAISettings.vue:66
 msgid "Token is not valid"
 msgstr ""
 
@@ -1688,7 +1712,7 @@ msgstr ""
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
 #: src/views/domain/DomainList.vue:41
-#: src/views/environment/Environment.vue:95
+#: src/views/environment/Environment.vue:122
 #: src/views/stream/components/RightSettings.vue:85
 #: src/views/stream/StreamList.vue:41
 #: src/views/user/User.vue:37
@@ -1721,11 +1745,11 @@ msgstr ""
 msgid "Uptime:"
 msgstr ""
 
-#: src/views/environment/Environment.vue:21
+#: src/views/environment/Environment.vue:22
 msgid "URL"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "User"
 msgstr ""
 
@@ -1776,7 +1800,7 @@ msgid "We will remove the HTTPChallenge configuration from this file and reload
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:27
-#: src/views/dashboard/ServerAnalytic.vue:365
+#: src/views/dashboard/ServerAnalytic.vue:368
 msgid "Writes"
 msgstr ""
 

+ 112 - 85
app/src/language/ru_RU/app.po

@@ -18,7 +18,7 @@ msgid "Access Logs"
 msgstr "Журнал доступа"
 
 #: src/routes/index.ts:128 src/views/certificate/ACMEUser.vue:76
-#: src/views/certificate/ACMEUserSelector.vue:79
+#: src/views/certificate/ACMEUserSelector.vue:84
 #, fuzzy
 msgid "ACME User"
 msgstr "Пользователь"
@@ -26,7 +26,7 @@ msgstr "Пользователь"
 #: src/views/certificate/ACMEUser.vue:59
 #: src/views/certificate/Certificate.vue:108
 #: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
-#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:102
+#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:35
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
@@ -74,7 +74,7 @@ msgstr "Дополнительно"
 msgid "Advance Mode"
 msgstr "Расширенный режим"
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:39
 msgid "API Base Url"
 msgstr ""
 
@@ -82,11 +82,11 @@ msgstr ""
 msgid "API Document"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:45
+#: src/views/preference/OpenAISettings.vue:51
 msgid "API Proxy"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:63
 msgid "API Token"
 msgstr ""
 
@@ -100,7 +100,7 @@ msgstr ""
 msgid "Are you sure you want to clear all notifications?"
 msgstr "Вы уверены, что хотите удалить все уведомления?"
 
-#: src/components/ChatGPT/ChatGPT.vue:267
+#: src/components/ChatGPT/ChatGPT.vue:272
 #, fuzzy
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Вы уверены, что хотите очистить сообщения чата?"
@@ -134,11 +134,11 @@ msgstr "Вы уверены, что хотите удалить эту дире
 msgid "Are you sure you want to remove this location?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 
-#: src/components/ChatGPT/ChatGPT.vue:211
+#: src/components/ChatGPT/ChatGPT.vue:216
 msgid "Ask ChatGPT for Help"
 msgstr "Обратитесь за помощью к ChatGPT"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "Assistant"
 msgstr "Ассистент"
 
@@ -147,7 +147,7 @@ msgstr "Ассистент"
 msgid "Author"
 msgstr "Автор"
 
-#: src/views/domain/cert/ChangeCert.vue:34
+#: src/views/domain/cert/ChangeCert.vue:33
 msgid "Auto Cert"
 msgstr "Авто Сертификат"
 
@@ -181,7 +181,7 @@ msgstr "Основная информация"
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:94
+#: src/views/preference/Preference.vue:101
 #: src/views/stream/components/RightSettings.vue:74
 #, fuzzy
 msgid "Basic"
@@ -209,10 +209,10 @@ msgstr ""
 msgid "CADir"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:248
+#: src/components/ChatGPT/ChatGPT.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:263
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:102
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:153
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:21
 #: src/views/domain/components/RightSettings.vue:51
@@ -256,8 +256,8 @@ msgstr "Список"
 msgid "Challenge Method"
 msgstr "Метод Challenge"
 
-#: src/views/domain/cert/ChangeCert.vue:88
-#: src/views/domain/cert/ChangeCert.vue:92
+#: src/views/domain/cert/ChangeCert.vue:95
+#: src/views/domain/cert/ChangeCert.vue:99
 #, fuzzy
 msgid "Change Certificate"
 msgstr "Сертификат действителен"
@@ -274,7 +274,7 @@ msgstr "Проверить повторно"
 msgid "Cleaning environment variables"
 msgstr "Очистка переменных среды"
 
-#: src/components/ChatGPT/ChatGPT.vue:271
+#: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
 #: src/views/notification/Notification.vue:75
 msgid "Clear"
@@ -319,7 +319,7 @@ msgstr "Конфигурации"
 msgid "Configure SSL"
 msgstr "Настроить SSL"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Connected"
 msgstr "Подключено"
 
@@ -333,7 +333,7 @@ msgstr "Содержание"
 msgid "Core Upgrade"
 msgstr "Обновление ядра"
 
-#: src/views/dashboard/ServerAnalytic.vue:293
+#: src/views/dashboard/ServerAnalytic.vue:296
 msgid "CPU Status"
 msgstr "Нагрузка CPU"
 
@@ -375,6 +375,12 @@ msgstr "Текущяя версия"
 msgid "Custom"
 msgstr "Пользовательский"
 
+#: src/views/preference/BasicSettings.vue:121
+msgid ""
+"Customize the name of local server to be displayed in the environment "
+"indicator."
+msgstr ""
+
 #: src/routes/index.ts:39
 msgid "Dashboard"
 msgstr "Доска"
@@ -466,9 +472,9 @@ msgstr "Отключить"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "Не удалось отключить автоматическое продление для %{name}"
 
-#: src/views/domain/cert/ChangeCert.vue:45 src/views/domain/DomainEdit.vue:185
-#: src/views/domain/DomainList.vue:33 src/views/stream/StreamEdit.vue:177
-#: src/views/stream/StreamList.vue:33
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
+#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Отключено"
 
@@ -479,7 +485,7 @@ msgstr "Отключено"
 msgid "Disabled successfully"
 msgstr "Отключено успешно"
 
-#: src/views/dashboard/ServerAnalytic.vue:358
+#: src/views/dashboard/ServerAnalytic.vue:361
 msgid "Disk IO"
 msgstr "Нагрузка на Диск IO"
 
@@ -662,9 +668,10 @@ msgstr "Активировано успешно"
 msgid "Enable TLS"
 msgstr "Включить TLS"
 
-#: src/views/domain/cert/ChangeCert.vue:41
+#: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
@@ -684,11 +691,11 @@ msgstr "Активировано успешно"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Использовать для сайта Let's Encrypt"
 
-#: src/routes/index.ts:212 src/views/environment/Environment.vue:110
+#: src/routes/index.ts:212 src/views/environment/Environment.vue:137
 msgid "Environment"
 msgstr "Окружение"
 
-#: src/views/dashboard/Environments.vue:71
+#: src/views/dashboard/Environments.vue:82
 #, fuzzy
 msgid "Environments"
 msgstr "Комментарии"
@@ -917,7 +924,7 @@ msgstr "Последняя проверка в"
 msgid "Leave blank for no change"
 msgstr "Оставьте пустым без изменений"
 
-#: src/views/preference/OpenAISettings.vue:41
+#: src/views/preference/OpenAISettings.vue:47
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Оставьте пустым для значения по умолчанию: https://api.openai.com/"
 
@@ -932,7 +939,7 @@ msgstr "Оставьте пустым без изменений"
 msgid "License"
 msgstr "Лицензия"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Link Start"
 msgstr ""
 
@@ -946,8 +953,8 @@ msgstr ""
 msgid "Load Average:"
 msgstr "Средняя нагрузка:"
 
-#: src/components/EnvIndicator/EnvIndicator.vue:38
-#: src/components/NodeSelector/NodeSelector.vue:51
+#: src/components/EnvIndicator/EnvIndicator.vue:40
+#: src/components/NodeSelector/NodeSelector.vue:71
 #, fuzzy
 msgid "Local"
 msgstr "Location"
@@ -977,7 +984,7 @@ msgstr "Авторизация успешна"
 msgid "Logout successful"
 msgstr "Выход выполнен успешно"
 
-#: src/views/preference/Preference.vue:112
+#: src/views/preference/Preference.vue:119
 msgid "Logrotate"
 msgstr ""
 
@@ -1023,6 +1030,7 @@ msgid "Managed Certificate"
 msgstr "Управление сертификатами"
 
 #: src/views/dashboard/ServerAnalytic.vue:217
+#: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgstr "Память"
 
@@ -1034,12 +1042,12 @@ msgstr "Память и хранилище"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:26
+#: src/views/preference/OpenAISettings.vue:27
 #, fuzzy
 msgid "Model"
 msgstr "Расширенный режим"
 
-#: src/components/ChatGPT/ChatGPT.vue:244
+#: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #, fuzzy
@@ -1069,7 +1077,7 @@ msgstr "Одиночная директива"
 #: src/views/certificate/Certificate.vue:20
 #: src/views/certificate/CertificateEditor.vue:146
 #: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
-#: src/views/domain/cert/ChangeCert.vue:18
+#: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
 #: src/views/domain/DomainList.vue:13
@@ -1081,19 +1089,19 @@ msgstr "Одиночная директива"
 msgid "Name"
 msgstr "Имя"
 
-#: src/views/dashboard/ServerAnalytic.vue:319
+#: src/views/dashboard/ServerAnalytic.vue:322
 msgid "Network"
 msgstr "Сеть"
 
-#: src/views/dashboard/ServerAnalytic.vue:261
+#: src/views/dashboard/ServerAnalytic.vue:264
 msgid "Network Statistics"
 msgstr "Статистика сети"
 
-#: src/views/dashboard/ServerAnalytic.vue:268
+#: src/views/dashboard/ServerAnalytic.vue:271
 msgid "Network Total Receive"
 msgstr "Всего получено"
 
-#: src/views/dashboard/ServerAnalytic.vue:274
+#: src/views/dashboard/ServerAnalytic.vue:277
 msgid "Network Total Send"
 msgstr "Всего отправлено"
 
@@ -1107,7 +1115,7 @@ msgstr "Вышла новая версия"
 msgid "Next"
 msgstr "Дальше"
 
-#: src/views/preference/Preference.vue:100
+#: src/views/preference/Preference.vue:107
 #, fuzzy
 msgid "Nginx"
 msgstr "Журнал"
@@ -1121,7 +1129,7 @@ msgstr "Путь для Nginx Access Log"
 msgid "Nginx Configuration Parse Error"
 msgstr "Ошибка синтаксического анализа конфигурации Nginx"
 
-#: src/components/NginxControl/NginxControl.vue:62
+#: src/components/NginxControl/NginxControl.vue:65
 msgid "Nginx Control"
 msgstr "Управление Nginx"
 
@@ -1133,17 +1141,17 @@ msgstr "Путь для Nginx Error Log"
 msgid "Nginx Log"
 msgstr "Журнал"
 
-#: src/components/NginxControl/NginxControl.vue:22
+#: src/components/NginxControl/NginxControl.vue:23
 #, fuzzy
 msgid "Nginx reloaded successfully"
 msgstr "Nginx перезагружен успешно"
 
-#: src/components/NginxControl/NginxControl.vue:36
+#: src/components/NginxControl/NginxControl.vue:39
 #, fuzzy
 msgid "Nginx restarted successfully"
 msgstr "Nginx успешно перезапущен"
 
-#: src/components/ChatGPT/ChatGPT.vue:265
+#: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
@@ -1196,22 +1204,22 @@ msgstr "Получить сертификат"
 msgid "Obtaining certificate"
 msgstr "Получение сертификата"
 
-#: src/components/NodeSelector/NodeSelector.vue:75
-#: src/views/dashboard/Environments.vue:95
-#: src/views/environment/Environment.vue:86
+#: src/components/NodeSelector/NodeSelector.vue:95
+#: src/views/dashboard/Environments.vue:106
+#: src/views/environment/Environment.vue:88
 msgid "Offline"
 msgstr ""
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:264
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:154
 msgid "Ok"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:266
+#: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1230,14 +1238,14 @@ msgstr ""
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
-#: src/components/NodeSelector/NodeSelector.vue:54
-#: src/components/NodeSelector/NodeSelector.vue:69
-#: src/views/dashboard/Environments.vue:88
-#: src/views/environment/Environment.vue:82
+#: src/components/NodeSelector/NodeSelector.vue:74
+#: src/components/NodeSelector/NodeSelector.vue:89
+#: src/views/dashboard/Environments.vue:99
+#: src/views/environment/Environment.vue:84
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:106
+#: src/views/preference/Preference.vue:113
 msgid "OpenAI"
 msgstr ""
 
@@ -1333,7 +1341,7 @@ msgstr ""
 msgid "Pre-release"
 msgstr ""
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
 msgid "Preference"
 msgstr "Настройки"
 
@@ -1359,12 +1367,12 @@ msgid "Provider"
 msgstr "Провайдер"
 
 #: src/views/dashboard/ServerAnalytic.vue:28
-#: src/views/dashboard/ServerAnalytic.vue:375
+#: src/views/dashboard/ServerAnalytic.vue:378
 msgid "Reads"
 msgstr "Чтение"
 
 #: src/views/dashboard/ServerAnalytic.vue:24
-#: src/views/dashboard/ServerAnalytic.vue:326
+#: src/views/dashboard/ServerAnalytic.vue:329
 msgid "Receive"
 msgstr "Принято"
 
@@ -1381,7 +1389,7 @@ msgstr "Успешно сохранено"
 msgid "Recursive Nameservers"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/ChatGPT/ChatGPT.vue:283
 msgid "Regenerate response"
 msgstr "Восстановить ответ"
 
@@ -1418,12 +1426,12 @@ msgstr "Переустановить"
 msgid "Release Note"
 msgstr "Что нового"
 
-#: src/components/ChatGPT/ChatGPT.vue:254
-#: src/components/NginxControl/NginxControl.vue:97
+#: src/components/ChatGPT/ChatGPT.vue:259
+#: src/components/NginxControl/NginxControl.vue:100
 msgid "Reload"
 msgstr "Перегрузить"
 
-#: src/components/NginxControl/NginxControl.vue:71
+#: src/components/NginxControl/NginxControl.vue:74
 msgid "Reloading"
 msgstr "Перезагружается"
 
@@ -1471,11 +1479,11 @@ msgstr "Запрос с неправильными параметрами"
 msgid "Reset"
 msgstr "Сброс"
 
-#: src/components/NginxControl/NginxControl.vue:90
+#: src/components/NginxControl/NginxControl.vue:93
 msgid "Restart"
 msgstr "Перезапуск"
 
-#: src/components/NginxControl/NginxControl.vue:76
+#: src/components/NginxControl/NginxControl.vue:79
 msgid "Restarting"
 msgstr "Перезапускается"
 
@@ -1484,15 +1492,15 @@ msgstr "Перезапускается"
 msgid "Run Mode"
 msgstr "Расширенный режим"
 
-#: src/components/NginxControl/NginxControl.vue:66
+#: src/components/NginxControl/NginxControl.vue:69
 msgid "Running"
 msgstr "Выполняется"
 
-#: src/components/ChatGPT/ChatGPT.vue:247
+#: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
 msgid "Save"
 msgstr "Сохранить"
 
@@ -1508,7 +1516,7 @@ msgstr "Ошибка сохранения %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:59
+#: src/views/preference/Preference.vue:66
 #, fuzzy
 msgid "Save successfully"
 msgstr "Успешно сохранено"
@@ -1524,24 +1532,24 @@ msgstr "Успешно сохранено"
 msgid "SDK"
 msgstr ""
 
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgstr "Выбор"
 
 #: src/views/dashboard/ServerAnalytic.vue:25
-#: src/views/dashboard/ServerAnalytic.vue:336
+#: src/views/dashboard/ServerAnalytic.vue:339
 msgid "Send"
 msgstr "Отправлено"
 
-#: src/components/NginxControl/NginxControl.vue:28
-#: src/components/NginxControl/NginxControl.vue:42
+#: src/components/NginxControl/NginxControl.vue:29
+#: src/components/NginxControl/NginxControl.vue:45
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:70
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"
@@ -1551,6 +1559,11 @@ msgstr "Ошибка сервера"
 msgid "Server Info"
 msgstr "Информация о сервере"
 
+#: src/views/preference/BasicSettings.vue:117
+#, fuzzy
+msgid "Server Name"
+msgstr "Информация о сервере"
+
 #: src/views/domain/cert/components/ObtainCert.vue:102
 msgid "server_name not found in directives"
 msgstr "server_name не нашел в директивах"
@@ -1625,15 +1638,16 @@ msgstr "Таблица"
 
 #: src/views/certificate/ACMEUser.vue:42
 #: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
-#: src/views/environment/Environment.vue:75 src/views/stream/StreamList.vue:22
+#: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Статус"
 
-#: src/components/NginxControl/NginxControl.vue:81
+#: src/components/NginxControl/NginxControl.vue:84
 msgid "Stopped"
 msgstr "Остановлен"
 
-#: src/views/dashboard/ServerAnalytic.vue:243
+#: src/views/dashboard/ServerAnalytic.vue:245
+#: src/views/dashboard/ServerAnalytic.vue:246
 msgid "Storage"
 msgstr "Хранилище"
 
@@ -1646,7 +1660,8 @@ msgstr "Название темы: %{name}"
 msgid "Success"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:230
+#: src/views/dashboard/ServerAnalytic.vue:231
+#: src/views/dashboard/ServerAnalytic.vue:232
 msgid "Swap"
 msgstr "Своп"
 
@@ -1697,6 +1712,10 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr "Путь к ключу сертификата SSL"
 
+#: src/views/preference/OpenAISettings.vue:30
+msgid "The model name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/certificate/CertificateEditor.vue:165
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
@@ -1706,6 +1725,10 @@ msgstr "Путь к ключу сертификата SSL"
 msgid "The path exists, but the file is not a private key"
 msgstr "Путь существует, но файл не является приватным ключом"
 
+#: src/views/preference/BasicSettings.vue:120
+msgid "The server name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/domain/cert/components/AutoCertStepOne.vue:50
 #, fuzzy
 msgid ""
@@ -1717,9 +1740,14 @@ msgstr ""
 
 #: src/views/preference/BasicSettings.vue:38
 #: src/views/preference/BasicSettings.vue:50
-#: src/views/preference/OpenAISettings.vue:36
-#: src/views/preference/OpenAISettings.vue:48
-msgid "The url is not valid"
+#, fuzzy
+msgid "The url is invalid"
+msgstr "URL-адрес неверный"
+
+#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:54
+#, fuzzy
+msgid "The url is invalid."
 msgstr "URL-адрес неверный"
 
 #: src/language/constants.ts:2
@@ -1761,7 +1789,7 @@ msgid ""
 "continue?"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:60
+#: src/views/preference/OpenAISettings.vue:66
 msgid "Token is not valid"
 msgstr ""
 
@@ -1778,7 +1806,7 @@ msgstr "Тип"
 #: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
-#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:95
+#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
 #: src/views/stream/components/RightSettings.vue:85
 #: src/views/stream/StreamList.vue:41 src/views/user/User.vue:37
 msgid "Updated at"
@@ -1811,11 +1839,11 @@ msgstr ""
 msgid "Uptime:"
 msgstr "Аптайм:"
 
-#: src/views/environment/Environment.vue:21
+#: src/views/environment/Environment.vue:22
 msgid "URL"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 #, fuzzy
 msgid "User"
 msgstr "Пользователь"
@@ -1873,7 +1901,7 @@ msgid ""
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:27
-#: src/views/dashboard/ServerAnalytic.vue:365
+#: src/views/dashboard/ServerAnalytic.vue:368
 msgid "Writes"
 msgstr "Запись"
 
@@ -1899,6 +1927,9 @@ msgstr "Вы используете последнюю версию"
 msgid "You can check Nginx UI upgrade at this page."
 msgstr "Вы можете проверить обновление Nginx UI на этой странице."
 
+#~ msgid "The url is not valid"
+#~ msgstr "URL-адрес неверный"
+
 #, fuzzy
 #~ msgid "Save Successfully"
 #~ msgstr "Успешно сохранено"
@@ -1907,10 +1938,6 @@ msgstr "Вы можете проверить обновление Nginx UI на
 #~ msgid "Table"
 #~ msgstr "Таблица"
 
-#, fuzzy
-#~ msgid "Server"
-#~ msgstr "Информация о сервере"
-
 #, fuzzy
 #~ msgid "Leave blank will not change anything."
 #~ msgstr "Оставьте пустым без изменений"

+ 107 - 85
app/src/language/vi_VN/app.po

@@ -18,7 +18,7 @@ msgid "Access Logs"
 msgstr "Log truy cập"
 
 #: src/routes/index.ts:128 src/views/certificate/ACMEUser.vue:76
-#: src/views/certificate/ACMEUserSelector.vue:79
+#: src/views/certificate/ACMEUserSelector.vue:84
 #, fuzzy
 msgid "ACME User"
 msgstr "Người dùng"
@@ -26,7 +26,7 @@ msgstr "Người dùng"
 #: src/views/certificate/ACMEUser.vue:59
 #: src/views/certificate/Certificate.vue:108
 #: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
-#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:102
+#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:35
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
@@ -74,7 +74,7 @@ msgstr "Tùy chọn bổ sung"
 msgid "Advance Mode"
 msgstr "Nâng cao"
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:39
 msgid "API Base Url"
 msgstr ""
 
@@ -82,11 +82,11 @@ msgstr ""
 msgid "API Document"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:45
+#: src/views/preference/OpenAISettings.vue:51
 msgid "API Proxy"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:63
 msgid "API Token"
 msgstr ""
 
@@ -100,7 +100,7 @@ msgstr ""
 msgid "Are you sure you want to clear all notifications?"
 msgstr "Bạn có chắc chắn muốn xóa tất cả thông báo không ?"
 
-#: src/components/ChatGPT/ChatGPT.vue:267
+#: src/components/ChatGPT/ChatGPT.vue:272
 #, fuzzy
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Bạn có chắc chắn muốn xóa lịch sử trò chuyện không ?"
@@ -134,11 +134,11 @@ msgstr "Bạn chắc chắn muốn xoá directive này ?"
 msgid "Are you sure you want to remove this location?"
 msgstr "Bạn chắc chắn muốn xoá location này ?"
 
-#: src/components/ChatGPT/ChatGPT.vue:211
+#: src/components/ChatGPT/ChatGPT.vue:216
 msgid "Ask ChatGPT for Help"
 msgstr "Hỏi ChatGPT"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "Assistant"
 msgstr "Trợ lý"
 
@@ -147,7 +147,7 @@ msgstr "Trợ lý"
 msgid "Author"
 msgstr "Tác giả"
 
-#: src/views/domain/cert/ChangeCert.vue:34
+#: src/views/domain/cert/ChangeCert.vue:33
 msgid "Auto Cert"
 msgstr "Tự động ký chứng chỉ SSL"
 
@@ -181,7 +181,7 @@ msgstr "Thông tin"
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:94
+#: src/views/preference/Preference.vue:101
 #: src/views/stream/components/RightSettings.vue:74
 #, fuzzy
 msgid "Basic"
@@ -209,10 +209,10 @@ msgstr ""
 msgid "CADir"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:248
+#: src/components/ChatGPT/ChatGPT.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:263
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:102
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:153
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:21
 #: src/views/domain/components/RightSettings.vue:51
@@ -256,8 +256,8 @@ msgstr "Danh sách chứng chỉ"
 msgid "Challenge Method"
 msgstr "Phương pháp xác thực"
 
-#: src/views/domain/cert/ChangeCert.vue:88
-#: src/views/domain/cert/ChangeCert.vue:92
+#: src/views/domain/cert/ChangeCert.vue:95
+#: src/views/domain/cert/ChangeCert.vue:99
 #, fuzzy
 msgid "Change Certificate"
 msgstr "Thay đổi chứng chỉ"
@@ -274,7 +274,7 @@ msgstr "Kiểm tra lại"
 msgid "Cleaning environment variables"
 msgstr "Xoá các biến môi trường"
 
-#: src/components/ChatGPT/ChatGPT.vue:271
+#: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
 #: src/views/notification/Notification.vue:75
 msgid "Clear"
@@ -319,7 +319,7 @@ msgstr "Cấu hình"
 msgid "Configure SSL"
 msgstr "Cấu hình SSL"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Connected"
 msgstr "Đã kết nối"
 
@@ -333,7 +333,7 @@ msgstr "Nội dung"
 msgid "Core Upgrade"
 msgstr "Cập nhật core"
 
-#: src/views/dashboard/ServerAnalytic.vue:293
+#: src/views/dashboard/ServerAnalytic.vue:296
 msgid "CPU Status"
 msgstr "Trạng thái CPU"
 
@@ -375,6 +375,12 @@ msgstr "Phiên bản hiện tại"
 msgid "Custom"
 msgstr "Tuỳ chỉnh"
 
+#: src/views/preference/BasicSettings.vue:121
+msgid ""
+"Customize the name of local server to be displayed in the environment "
+"indicator."
+msgstr ""
+
 #: src/routes/index.ts:39
 msgid "Dashboard"
 msgstr "Bảng điều khiển"
@@ -467,9 +473,9 @@ msgstr "Tắt"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "Tắt tự động gia hạn SSL cho %{name} thất bại"
 
-#: src/views/domain/cert/ChangeCert.vue:45 src/views/domain/DomainEdit.vue:185
-#: src/views/domain/DomainList.vue:33 src/views/stream/StreamEdit.vue:177
-#: src/views/stream/StreamList.vue:33
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
+#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Đã tắt"
 
@@ -480,7 +486,7 @@ msgstr "Đã tắt"
 msgid "Disabled successfully"
 msgstr "Đã tắt thành công"
 
-#: src/views/dashboard/ServerAnalytic.vue:358
+#: src/views/dashboard/ServerAnalytic.vue:361
 msgid "Disk IO"
 msgstr "Disk IO"
 
@@ -663,9 +669,10 @@ msgstr "Đã bật"
 msgid "Enable TLS"
 msgstr "Bật TLS"
 
-#: src/views/domain/cert/ChangeCert.vue:41
+#: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
@@ -685,11 +692,11 @@ msgstr "Đã bật"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Bảo mật trang web với Let's Encrypt"
 
-#: src/routes/index.ts:212 src/views/environment/Environment.vue:110
+#: src/routes/index.ts:212 src/views/environment/Environment.vue:137
 msgid "Environment"
 msgstr "Environment"
 
-#: src/views/dashboard/Environments.vue:71
+#: src/views/dashboard/Environments.vue:82
 #, fuzzy
 msgid "Environments"
 msgstr "Environments"
@@ -919,7 +926,7 @@ msgstr "Kiểm tra lần cuối lúc"
 msgid "Leave blank for no change"
 msgstr "Bỏ trống nếu không thay đổi"
 
-#: src/views/preference/OpenAISettings.vue:41
+#: src/views/preference/OpenAISettings.vue:47
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Bỏ trống để sử dụng địa chỉ mặc định: https://api.openai.com/"
 
@@ -934,7 +941,7 @@ msgstr "Bỏ trống nếu không thay đổi"
 msgid "License"
 msgstr "Giấy phép"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Link Start"
 msgstr "Liên kết bắt đầu"
 
@@ -948,8 +955,8 @@ msgstr ""
 msgid "Load Average:"
 msgstr "Tải trung bình:"
 
-#: src/components/EnvIndicator/EnvIndicator.vue:38
-#: src/components/NodeSelector/NodeSelector.vue:51
+#: src/components/EnvIndicator/EnvIndicator.vue:40
+#: src/components/NodeSelector/NodeSelector.vue:71
 #, fuzzy
 msgid "Local"
 msgstr "Location"
@@ -979,7 +986,7 @@ msgstr "Đăng nhập thành công"
 msgid "Logout successful"
 msgstr "Đã đăng xuất"
 
-#: src/views/preference/Preference.vue:112
+#: src/views/preference/Preference.vue:119
 msgid "Logrotate"
 msgstr ""
 
@@ -1024,6 +1031,7 @@ msgid "Managed Certificate"
 msgstr ""
 
 #: src/views/dashboard/ServerAnalytic.vue:217
+#: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgstr "Memory"
 
@@ -1035,12 +1043,12 @@ msgstr "Memory và Storage"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:26
+#: src/views/preference/OpenAISettings.vue:27
 #, fuzzy
 msgid "Model"
 msgstr "Run Mode"
 
-#: src/components/ChatGPT/ChatGPT.vue:244
+#: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #, fuzzy
@@ -1070,7 +1078,7 @@ msgstr "Single Directive"
 #: src/views/certificate/Certificate.vue:20
 #: src/views/certificate/CertificateEditor.vue:146
 #: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
-#: src/views/domain/cert/ChangeCert.vue:18
+#: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
 #: src/views/domain/DomainList.vue:13
@@ -1082,19 +1090,19 @@ msgstr "Single Directive"
 msgid "Name"
 msgstr "Tên"
 
-#: src/views/dashboard/ServerAnalytic.vue:319
+#: src/views/dashboard/ServerAnalytic.vue:322
 msgid "Network"
 msgstr "Mạng"
 
-#: src/views/dashboard/ServerAnalytic.vue:261
+#: src/views/dashboard/ServerAnalytic.vue:264
 msgid "Network Statistics"
 msgstr "Thống kê mạng"
 
-#: src/views/dashboard/ServerAnalytic.vue:268
+#: src/views/dashboard/ServerAnalytic.vue:271
 msgid "Network Total Receive"
 msgstr "Tổng lưu lượng mạng đã nhận"
 
-#: src/views/dashboard/ServerAnalytic.vue:274
+#: src/views/dashboard/ServerAnalytic.vue:277
 msgid "Network Total Send"
 msgstr "Tổng lưu lượng mạng đã gửi"
 
@@ -1108,7 +1116,7 @@ msgstr "Đã có phiên bản mới"
 msgid "Next"
 msgstr "Tiếp theo"
 
-#: src/views/preference/Preference.vue:100
+#: src/views/preference/Preference.vue:107
 msgid "Nginx"
 msgstr ""
 
@@ -1121,7 +1129,7 @@ msgstr "Vị trí lưu log truy cập (Access log) của Nginx"
 msgid "Nginx Configuration Parse Error"
 msgstr "Lỗi phân tích cú pháp cấu hình Nginx"
 
-#: src/components/NginxControl/NginxControl.vue:62
+#: src/components/NginxControl/NginxControl.vue:65
 msgid "Nginx Control"
 msgstr ""
 
@@ -1133,17 +1141,17 @@ msgstr "Vị trí lưu log lỗi (Error log) của Nginx"
 msgid "Nginx Log"
 msgstr ""
 
-#: src/components/NginxControl/NginxControl.vue:22
+#: src/components/NginxControl/NginxControl.vue:23
 #, fuzzy
 msgid "Nginx reloaded successfully"
 msgstr "Reload Nginx thành công"
 
-#: src/components/NginxControl/NginxControl.vue:36
+#: src/components/NginxControl/NginxControl.vue:39
 #, fuzzy
 msgid "Nginx restarted successfully"
 msgstr "Restart Nginx thành công"
 
-#: src/components/ChatGPT/ChatGPT.vue:265
+#: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
@@ -1196,22 +1204,22 @@ msgstr "Nhận chứng chỉ"
 msgid "Obtaining certificate"
 msgstr "Đang nhận chứng chỉ"
 
-#: src/components/NodeSelector/NodeSelector.vue:75
-#: src/views/dashboard/Environments.vue:95
-#: src/views/environment/Environment.vue:86
+#: src/components/NodeSelector/NodeSelector.vue:95
+#: src/views/dashboard/Environments.vue:106
+#: src/views/environment/Environment.vue:88
 msgid "Offline"
 msgstr "Ngoại tuyến"
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:264
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:154
 msgid "Ok"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:266
+#: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1230,14 +1238,14 @@ msgstr ""
 msgid "Once the verification is complete, the records will be removed."
 msgstr "Sau khi quá trình xác minh hoàn tất, bản ghi sẽ bị xóa."
 
-#: src/components/NodeSelector/NodeSelector.vue:54
-#: src/components/NodeSelector/NodeSelector.vue:69
-#: src/views/dashboard/Environments.vue:88
-#: src/views/environment/Environment.vue:82
+#: src/components/NodeSelector/NodeSelector.vue:74
+#: src/components/NodeSelector/NodeSelector.vue:89
+#: src/views/dashboard/Environments.vue:99
+#: src/views/environment/Environment.vue:84
 msgid "Online"
 msgstr "Trực tuyến"
 
-#: src/views/preference/Preference.vue:106
+#: src/views/preference/Preference.vue:113
 msgid "OpenAI"
 msgstr ""
 
@@ -1335,7 +1343,7 @@ msgstr ""
 msgid "Pre-release"
 msgstr ""
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
 msgid "Preference"
 msgstr "Cài đặt"
 
@@ -1361,12 +1369,12 @@ msgid "Provider"
 msgstr "Nhà cung cấp"
 
 #: src/views/dashboard/ServerAnalytic.vue:28
-#: src/views/dashboard/ServerAnalytic.vue:375
+#: src/views/dashboard/ServerAnalytic.vue:378
 msgid "Reads"
 msgstr "Đọc"
 
 #: src/views/dashboard/ServerAnalytic.vue:24
-#: src/views/dashboard/ServerAnalytic.vue:326
+#: src/views/dashboard/ServerAnalytic.vue:329
 msgid "Receive"
 msgstr "Nhận"
 
@@ -1383,7 +1391,7 @@ msgstr "Xoá thành công"
 msgid "Recursive Nameservers"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/ChatGPT/ChatGPT.vue:283
 msgid "Regenerate response"
 msgstr "Tạo lại câu trả lời"
 
@@ -1420,12 +1428,12 @@ msgstr "Cài lại"
 msgid "Release Note"
 msgstr "Ghi chú phát hành"
 
-#: src/components/ChatGPT/ChatGPT.vue:254
-#: src/components/NginxControl/NginxControl.vue:97
+#: src/components/ChatGPT/ChatGPT.vue:259
+#: src/components/NginxControl/NginxControl.vue:100
 msgid "Reload"
 msgstr "Tải lại"
 
-#: src/components/NginxControl/NginxControl.vue:71
+#: src/components/NginxControl/NginxControl.vue:74
 msgid "Reloading"
 msgstr "Đang tải lại"
 
@@ -1473,11 +1481,11 @@ msgstr "Yêu cầu có chứa tham số sai"
 msgid "Reset"
 msgstr "Đặt lại"
 
-#: src/components/NginxControl/NginxControl.vue:90
+#: src/components/NginxControl/NginxControl.vue:93
 msgid "Restart"
 msgstr "Khởi động lại"
 
-#: src/components/NginxControl/NginxControl.vue:76
+#: src/components/NginxControl/NginxControl.vue:79
 msgid "Restarting"
 msgstr "Đang khởi động lại"
 
@@ -1486,15 +1494,15 @@ msgstr "Đang khởi động lại"
 msgid "Run Mode"
 msgstr "Run Mode"
 
-#: src/components/NginxControl/NginxControl.vue:66
+#: src/components/NginxControl/NginxControl.vue:69
 msgid "Running"
 msgstr "Running"
 
-#: src/components/ChatGPT/ChatGPT.vue:247
+#: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
 msgid "Save"
 msgstr "Lưu"
 
@@ -1510,7 +1518,7 @@ msgstr "Đã xảy ra lỗi khi lưu %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:59
+#: src/views/preference/Preference.vue:66
 #, fuzzy
 msgid "Save successfully"
 msgstr "Lưu thành công"
@@ -1526,24 +1534,24 @@ msgstr "Lưu thành công"
 msgid "SDK"
 msgstr ""
 
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgstr "Bộ chọn"
 
 #: src/views/dashboard/ServerAnalytic.vue:25
-#: src/views/dashboard/ServerAnalytic.vue:336
+#: src/views/dashboard/ServerAnalytic.vue:339
 msgid "Send"
 msgstr "Gửi"
 
-#: src/components/NginxControl/NginxControl.vue:28
-#: src/components/NginxControl/NginxControl.vue:42
+#: src/components/NginxControl/NginxControl.vue:29
+#: src/components/NginxControl/NginxControl.vue:45
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:70
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"
@@ -1553,6 +1561,11 @@ msgstr "Lỗi máy chủ"
 msgid "Server Info"
 msgstr "Thông tin máy chủ"
 
+#: src/views/preference/BasicSettings.vue:117
+#, fuzzy
+msgid "Server Name"
+msgstr "Thông tin máy chủ"
+
 #: src/views/domain/cert/components/ObtainCert.vue:102
 msgid "server_name not found in directives"
 msgstr "không tìm thấy server_name trong directives"
@@ -1623,15 +1636,16 @@ msgstr "Ổn định"
 
 #: src/views/certificate/ACMEUser.vue:42
 #: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
-#: src/views/environment/Environment.vue:75 src/views/stream/StreamList.vue:22
+#: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Trạng thái"
 
-#: src/components/NginxControl/NginxControl.vue:81
+#: src/components/NginxControl/NginxControl.vue:84
 msgid "Stopped"
 msgstr "Đã dừng"
 
-#: src/views/dashboard/ServerAnalytic.vue:243
+#: src/views/dashboard/ServerAnalytic.vue:245
+#: src/views/dashboard/ServerAnalytic.vue:246
 msgid "Storage"
 msgstr "Storage"
 
@@ -1644,7 +1658,8 @@ msgstr "Tên chủ đề: %{name}"
 msgid "Success"
 msgstr "Thành công"
 
-#: src/views/dashboard/ServerAnalytic.vue:230
+#: src/views/dashboard/ServerAnalytic.vue:231
+#: src/views/dashboard/ServerAnalytic.vue:232
 msgid "Swap"
 msgstr "Swap"
 
@@ -1694,6 +1709,10 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr ""
 
+#: src/views/preference/OpenAISettings.vue:30
+msgid "The model name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/certificate/CertificateEditor.vue:165
 msgid "The path exists, but the file is not a certificate"
 msgstr ""
@@ -1702,6 +1721,10 @@ msgstr ""
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
+#: src/views/preference/BasicSettings.vue:120
+msgid "The server name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/domain/cert/components/AutoCertStepOne.vue:50
 #, fuzzy
 msgid ""
@@ -1712,9 +1735,12 @@ msgstr ""
 
 #: src/views/preference/BasicSettings.vue:38
 #: src/views/preference/BasicSettings.vue:50
-#: src/views/preference/OpenAISettings.vue:36
-#: src/views/preference/OpenAISettings.vue:48
-msgid "The url is not valid"
+msgid "The url is invalid"
+msgstr ""
+
+#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:54
+msgid "The url is invalid."
 msgstr ""
 
 #: src/language/constants.ts:2
@@ -1759,7 +1785,7 @@ msgstr ""
 "quyền đến chương trình phụ trợ và chúng tôi cần lưu tệp này và tải lại "
 "Nginx. Bạn có chắc chắn muốn Tiếp tục?"
 
-#: src/views/preference/OpenAISettings.vue:60
+#: src/views/preference/OpenAISettings.vue:66
 msgid "Token is not valid"
 msgstr ""
 
@@ -1776,7 +1802,7 @@ msgstr "Loại"
 #: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
-#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:95
+#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
 #: src/views/stream/components/RightSettings.vue:85
 #: src/views/stream/StreamList.vue:41 src/views/user/User.vue:37
 msgid "Updated at"
@@ -1809,11 +1835,11 @@ msgstr ""
 msgid "Uptime:"
 msgstr "Thời gian hoạt động:"
 
-#: src/views/environment/Environment.vue:21
+#: src/views/environment/Environment.vue:22
 msgid "URL"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 #, fuzzy
 msgid "User"
 msgstr "Người dùng"
@@ -1874,7 +1900,7 @@ msgstr ""
 "có muốn tiếp tục không?"
 
 #: src/views/dashboard/ServerAnalytic.vue:27
-#: src/views/dashboard/ServerAnalytic.vue:365
+#: src/views/dashboard/ServerAnalytic.vue:368
 msgid "Writes"
 msgstr "Ghi"
 
@@ -1908,10 +1934,6 @@ msgstr "Bạn có thể kiểm tra nâng cấp Nginx UI tại trang này"
 #~ msgid "Table"
 #~ msgstr "Mục lục"
 
-#, fuzzy
-#~ msgid "Server"
-#~ msgstr "Thông tin máy chủ"
-
 #, fuzzy
 #~ msgid "Leave blank will not change anything."
 #~ msgstr "Bỏ trống nếu không thay đổi"

BIN=BIN
app/src/language/zh_CN/app.mo


+ 111 - 85
app/src/language/zh_CN/app.po

@@ -22,14 +22,14 @@ msgid "Access Logs"
 msgstr "访问日志"
 
 #: src/routes/index.ts:128 src/views/certificate/ACMEUser.vue:76
-#: src/views/certificate/ACMEUserSelector.vue:79
+#: src/views/certificate/ACMEUserSelector.vue:84
 msgid "ACME User"
 msgstr "ACME 用户"
 
 #: src/views/certificate/ACMEUser.vue:59
 #: src/views/certificate/Certificate.vue:108
 #: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
-#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:102
+#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:35
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
@@ -74,7 +74,7 @@ msgstr "额外选项"
 msgid "Advance Mode"
 msgstr "高级模式"
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:39
 msgid "API Base Url"
 msgstr "API 地址"
 
@@ -82,11 +82,11 @@ msgstr "API 地址"
 msgid "API Document"
 msgstr "API 文档"
 
-#: src/views/preference/OpenAISettings.vue:45
+#: src/views/preference/OpenAISettings.vue:51
 msgid "API Proxy"
 msgstr "API 代理"
 
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:63
 msgid "API Token"
 msgstr "API Token"
 
@@ -99,7 +99,7 @@ msgstr "架构"
 msgid "Are you sure you want to clear all notifications?"
 msgstr "您确定要清除所有通知吗?"
 
-#: src/components/ChatGPT/ChatGPT.vue:267
+#: src/components/ChatGPT/ChatGPT.vue:272
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "你确定你要清除聊天记录吗?"
 
@@ -127,11 +127,11 @@ msgstr "您确定要删除这个项目吗?"
 msgid "Are you sure you want to remove this location?"
 msgstr "您确定要删除这个 Location?"
 
-#: src/components/ChatGPT/ChatGPT.vue:211
+#: src/components/ChatGPT/ChatGPT.vue:216
 msgid "Ask ChatGPT for Help"
 msgstr "与ChatGPT聊天"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "Assistant"
 msgstr "助手"
 
@@ -140,7 +140,7 @@ msgstr "助手"
 msgid "Author"
 msgstr "作者"
 
-#: src/views/domain/cert/ChangeCert.vue:34
+#: src/views/domain/cert/ChangeCert.vue:33
 msgid "Auto Cert"
 msgstr "自动更新"
 
@@ -173,7 +173,7 @@ msgstr "基本信息"
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:94
+#: src/views/preference/Preference.vue:101
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgstr "基本"
@@ -199,10 +199,10 @@ msgstr "CA Dir"
 msgid "CADir"
 msgstr "CADir"
 
-#: src/components/ChatGPT/ChatGPT.vue:248
+#: src/components/ChatGPT/ChatGPT.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:263
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:102
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:153
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:21
 #: src/views/domain/components/RightSettings.vue:51
@@ -243,8 +243,8 @@ msgstr "证书列表"
 msgid "Challenge Method"
 msgstr "挑战方法"
 
-#: src/views/domain/cert/ChangeCert.vue:88
-#: src/views/domain/cert/ChangeCert.vue:92
+#: src/views/domain/cert/ChangeCert.vue:95
+#: src/views/domain/cert/ChangeCert.vue:99
 msgid "Change Certificate"
 msgstr "更改证书"
 
@@ -260,7 +260,7 @@ msgstr "重新检查"
 msgid "Cleaning environment variables"
 msgstr "正在清理环境变量"
 
-#: src/components/ChatGPT/ChatGPT.vue:271
+#: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
 #: src/views/notification/Notification.vue:75
 msgid "Clear"
@@ -302,7 +302,7 @@ msgstr "配置"
 msgid "Configure SSL"
 msgstr "配置 SSL"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Connected"
 msgstr "已连接"
 
@@ -316,7 +316,7 @@ msgstr "内容"
 msgid "Core Upgrade"
 msgstr "核心升级"
 
-#: src/views/dashboard/ServerAnalytic.vue:293
+#: src/views/dashboard/ServerAnalytic.vue:296
 msgid "CPU Status"
 msgstr "CPU 状态"
 
@@ -357,6 +357,12 @@ msgstr "当前版本"
 msgid "Custom"
 msgstr "自定义"
 
+#: src/views/preference/BasicSettings.vue:121
+msgid ""
+"Customize the name of local server to be displayed in the environment "
+"indicator."
+msgstr "自定义显示在环境指示器中的本地服务器名称。"
+
 #: src/routes/index.ts:39
 msgid "Dashboard"
 msgstr "仪表盘"
@@ -444,9 +450,9 @@ msgstr "禁用"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "关闭 %{name} 自动续签失败"
 
-#: src/views/domain/cert/ChangeCert.vue:45 src/views/domain/DomainEdit.vue:185
-#: src/views/domain/DomainList.vue:33 src/views/stream/StreamEdit.vue:177
-#: src/views/stream/StreamList.vue:33
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
+#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "禁用"
 
@@ -457,7 +463,7 @@ msgstr "禁用"
 msgid "Disabled successfully"
 msgstr "禁用成功"
 
-#: src/views/dashboard/ServerAnalytic.vue:358
+#: src/views/dashboard/ServerAnalytic.vue:361
 msgid "Disk IO"
 msgstr "磁盘 IO"
 
@@ -621,9 +627,10 @@ msgstr "启用成功"
 msgid "Enable TLS"
 msgstr "启用 TLS"
 
-#: src/views/domain/cert/ChangeCert.vue:41
+#: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
@@ -643,11 +650,11 @@ msgstr "启用成功"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "用 Let's Encrypt 对网站进行加密"
 
-#: src/routes/index.ts:212 src/views/environment/Environment.vue:110
+#: src/routes/index.ts:212 src/views/environment/Environment.vue:137
 msgid "Environment"
 msgstr "环境"
 
-#: src/views/dashboard/Environments.vue:71
+#: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr "环境"
 
@@ -860,7 +867,7 @@ msgstr "最后检查时间"
 msgid "Leave blank for no change"
 msgstr "留空表示不修改"
 
-#: src/views/preference/OpenAISettings.vue:41
+#: src/views/preference/OpenAISettings.vue:47
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "留空为默认:https://api.openai.com/"
 
@@ -873,7 +880,7 @@ msgstr "留空不做任何更改"
 msgid "License"
 msgstr "开源许可"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Link Start"
 msgstr "链接"
 
@@ -886,8 +893,8 @@ msgstr "列表"
 msgid "Load Average:"
 msgstr "系统负载:"
 
-#: src/components/EnvIndicator/EnvIndicator.vue:38
-#: src/components/NodeSelector/NodeSelector.vue:51
+#: src/components/EnvIndicator/EnvIndicator.vue:40
+#: src/components/NodeSelector/NodeSelector.vue:71
 msgid "Local"
 msgstr "本地"
 
@@ -915,7 +922,7 @@ msgstr "登录成功"
 msgid "Logout successful"
 msgstr "登出成功"
 
-#: src/views/preference/Preference.vue:112
+#: src/views/preference/Preference.vue:119
 msgid "Logrotate"
 msgstr "Logrotate"
 
@@ -962,6 +969,7 @@ msgid "Managed Certificate"
 msgstr "托管证书"
 
 #: src/views/dashboard/ServerAnalytic.vue:217
+#: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgstr "内存"
 
@@ -973,11 +981,11 @@ msgstr "内存与存储"
 msgid "Minutes"
 msgstr "分钟"
 
-#: src/views/preference/OpenAISettings.vue:26
+#: src/views/preference/OpenAISettings.vue:27
 msgid "Model"
 msgstr "模型"
 
-#: src/components/ChatGPT/ChatGPT.vue:244
+#: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 msgid "Modify"
@@ -1003,7 +1011,7 @@ msgstr "多行指令"
 #: src/views/certificate/Certificate.vue:20
 #: src/views/certificate/CertificateEditor.vue:146
 #: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
-#: src/views/domain/cert/ChangeCert.vue:18
+#: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
 #: src/views/domain/DomainList.vue:13
@@ -1015,19 +1023,19 @@ msgstr "多行指令"
 msgid "Name"
 msgstr "名称"
 
-#: src/views/dashboard/ServerAnalytic.vue:319
+#: src/views/dashboard/ServerAnalytic.vue:322
 msgid "Network"
 msgstr "网络"
 
-#: src/views/dashboard/ServerAnalytic.vue:261
+#: src/views/dashboard/ServerAnalytic.vue:264
 msgid "Network Statistics"
 msgstr "流量统计"
 
-#: src/views/dashboard/ServerAnalytic.vue:268
+#: src/views/dashboard/ServerAnalytic.vue:271
 msgid "Network Total Receive"
 msgstr "下载流量"
 
-#: src/views/dashboard/ServerAnalytic.vue:274
+#: src/views/dashboard/ServerAnalytic.vue:277
 msgid "Network Total Send"
 msgstr "上传流量"
 
@@ -1041,7 +1049,7 @@ msgstr "新版本发布"
 msgid "Next"
 msgstr "下一步"
 
-#: src/views/preference/Preference.vue:100
+#: src/views/preference/Preference.vue:107
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1053,7 +1061,7 @@ msgstr "Nginx 访问日志路径"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 配置解析错误"
 
-#: src/components/NginxControl/NginxControl.vue:62
+#: src/components/NginxControl/NginxControl.vue:65
 msgid "Nginx Control"
 msgstr "控制 Nginx"
 
@@ -1065,15 +1073,15 @@ msgstr "Nginx 错误日志路径"
 msgid "Nginx Log"
 msgstr "Nginx 日志"
 
-#: src/components/NginxControl/NginxControl.vue:22
+#: src/components/NginxControl/NginxControl.vue:23
 msgid "Nginx reloaded successfully"
 msgstr "Nginx 重载成功"
 
-#: src/components/NginxControl/NginxControl.vue:36
+#: src/components/NginxControl/NginxControl.vue:39
 msgid "Nginx restarted successfully"
 msgstr "Nginx 重启成功"
 
-#: src/components/ChatGPT/ChatGPT.vue:265
+#: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
@@ -1123,22 +1131,22 @@ msgstr "获取证书"
 msgid "Obtaining certificate"
 msgstr "正在获取证书"
 
-#: src/components/NodeSelector/NodeSelector.vue:75
-#: src/views/dashboard/Environments.vue:95
-#: src/views/environment/Environment.vue:86
+#: src/components/NodeSelector/NodeSelector.vue:95
+#: src/views/dashboard/Environments.vue:106
+#: src/views/environment/Environment.vue:88
 msgid "Offline"
 msgstr "离线"
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:264
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:154
 msgid "Ok"
 msgstr "确定"
 
-#: src/components/ChatGPT/ChatGPT.vue:266
+#: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1157,14 +1165,14 @@ msgstr "确定"
 msgid "Once the verification is complete, the records will be removed."
 msgstr "一旦验证完成,这些记录将被删除。"
 
-#: src/components/NodeSelector/NodeSelector.vue:54
-#: src/components/NodeSelector/NodeSelector.vue:69
-#: src/views/dashboard/Environments.vue:88
-#: src/views/environment/Environment.vue:82
+#: src/components/NodeSelector/NodeSelector.vue:74
+#: src/components/NodeSelector/NodeSelector.vue:89
+#: src/views/dashboard/Environments.vue:99
+#: src/views/environment/Environment.vue:84
 msgid "Online"
 msgstr "在线"
 
-#: src/views/preference/Preference.vue:106
+#: src/views/preference/Preference.vue:113
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1259,7 +1267,7 @@ msgstr "请至少选择一个节点!"
 msgid "Pre-release"
 msgstr "预发布"
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
 msgid "Preference"
 msgstr "偏好设置"
 
@@ -1284,12 +1292,12 @@ msgid "Provider"
 msgstr "提供商"
 
 #: src/views/dashboard/ServerAnalytic.vue:28
-#: src/views/dashboard/ServerAnalytic.vue:375
+#: src/views/dashboard/ServerAnalytic.vue:378
 msgid "Reads"
 msgstr "读"
 
 #: src/views/dashboard/ServerAnalytic.vue:24
-#: src/views/dashboard/ServerAnalytic.vue:326
+#: src/views/dashboard/ServerAnalytic.vue:329
 msgid "Receive"
 msgstr "下载"
 
@@ -1305,7 +1313,7 @@ msgstr "恢复成功"
 msgid "Recursive Nameservers"
 msgstr "递归域名服务器"
 
-#: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/ChatGPT/ChatGPT.vue:283
 msgid "Regenerate response"
 msgstr "重新生成响应"
 
@@ -1337,12 +1345,12 @@ msgstr "重新安装"
 msgid "Release Note"
 msgstr "发行日志"
 
-#: src/components/ChatGPT/ChatGPT.vue:254
-#: src/components/NginxControl/NginxControl.vue:97
+#: src/components/ChatGPT/ChatGPT.vue:259
+#: src/components/NginxControl/NginxControl.vue:100
 msgid "Reload"
 msgstr "重载"
 
-#: src/components/NginxControl/NginxControl.vue:71
+#: src/components/NginxControl/NginxControl.vue:74
 msgid "Reloading"
 msgstr "重载中"
 
@@ -1384,11 +1392,11 @@ msgstr "请求参数错误"
 msgid "Reset"
 msgstr "重置"
 
-#: src/components/NginxControl/NginxControl.vue:90
+#: src/components/NginxControl/NginxControl.vue:93
 msgid "Restart"
 msgstr "重启"
 
-#: src/components/NginxControl/NginxControl.vue:76
+#: src/components/NginxControl/NginxControl.vue:79
 msgid "Restarting"
 msgstr "重启中"
 
@@ -1396,15 +1404,15 @@ msgstr "重启中"
 msgid "Run Mode"
 msgstr "运行模式"
 
-#: src/components/NginxControl/NginxControl.vue:66
+#: src/components/NginxControl/NginxControl.vue:69
 msgid "Running"
 msgstr "运行中"
 
-#: src/components/ChatGPT/ChatGPT.vue:247
+#: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
 msgid "Save"
 msgstr "保存"
 
@@ -1420,7 +1428,7 @@ msgstr "保存错误 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:59
+#: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr "保存成功"
 
@@ -1435,24 +1443,24 @@ msgstr "保存成功"
 msgid "SDK"
 msgstr "SDK"
 
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgstr "选择器"
 
 #: src/views/dashboard/ServerAnalytic.vue:25
-#: src/views/dashboard/ServerAnalytic.vue:336
+#: src/views/dashboard/ServerAnalytic.vue:339
 msgid "Send"
 msgstr "上传"
 
-#: src/components/NginxControl/NginxControl.vue:28
-#: src/components/NginxControl/NginxControl.vue:42
+#: src/components/NginxControl/NginxControl.vue:29
+#: src/components/NginxControl/NginxControl.vue:45
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:70
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"
@@ -1462,6 +1470,10 @@ msgstr "服务器错误"
 msgid "Server Info"
 msgstr "服务器信息"
 
+#: src/views/preference/BasicSettings.vue:117
+msgid "Server Name"
+msgstr "服务器名称"
+
 #: src/views/domain/cert/components/ObtainCert.vue:102
 msgid "server_name not found in directives"
 msgstr "未在指令集合中找到 server_name"
@@ -1529,15 +1541,16 @@ msgstr "稳定"
 
 #: src/views/certificate/ACMEUser.vue:42
 #: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
-#: src/views/environment/Environment.vue:75 src/views/stream/StreamList.vue:22
+#: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "状态"
 
-#: src/components/NginxControl/NginxControl.vue:81
+#: src/components/NginxControl/NginxControl.vue:84
 msgid "Stopped"
 msgstr "已停止"
 
-#: src/views/dashboard/ServerAnalytic.vue:243
+#: src/views/dashboard/ServerAnalytic.vue:245
+#: src/views/dashboard/ServerAnalytic.vue:246
 msgid "Storage"
 msgstr "存储"
 
@@ -1549,7 +1562,8 @@ msgstr "主体名称: %{subject}"
 msgid "Success"
 msgstr "成功"
 
-#: src/views/dashboard/ServerAnalytic.vue:230
+#: src/views/dashboard/ServerAnalytic.vue:231
+#: src/views/dashboard/ServerAnalytic.vue:232
 msgid "Swap"
 msgstr "Swap"
 
@@ -1598,6 +1612,10 @@ msgstr "输入的内容不是 SSL 证书"
 msgid "The input is not a SSL Certificate Key"
 msgstr "输入的内容不是 SSL 证书密钥"
 
+#: src/views/preference/OpenAISettings.vue:30
+msgid "The model name should only contain letters, numbers, dashes, and dots."
+msgstr "模型只能包含字母、数字、破折号和点。"
+
 #: src/views/certificate/CertificateEditor.vue:165
 msgid "The path exists, but the file is not a certificate"
 msgstr "路径存在,但文件不是证书"
@@ -1606,6 +1624,10 @@ msgstr "路径存在,但文件不是证书"
 msgid "The path exists, but the file is not a private key"
 msgstr "路径存在,但文件不是私钥"
 
+#: src/views/preference/BasicSettings.vue:120
+msgid "The server name should only contain letters, numbers, dashes, and dots."
+msgstr "服务器名称只能包含字母、数字、破折号和点。"
+
 #: src/views/domain/cert/components/AutoCertStepOne.vue:50
 msgid ""
 "The server_name in the current configuration must be the domain name you "
@@ -1614,10 +1636,14 @@ msgstr "当前配置中的 server_name 必须是获取证书所需的域名,
 
 #: src/views/preference/BasicSettings.vue:38
 #: src/views/preference/BasicSettings.vue:50
-#: src/views/preference/OpenAISettings.vue:36
-#: src/views/preference/OpenAISettings.vue:48
-msgid "The url is not valid"
-msgstr "URL 无效"
+#, fuzzy
+msgid "The url is invalid"
+msgstr "URL 无效."
+
+#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:54
+msgid "The url is invalid."
+msgstr "URL 无效."
 
 #: src/language/constants.ts:2
 msgid "The username or password is incorrect"
@@ -1659,7 +1685,7 @@ msgstr ""
 "为了确保认证自动更新能够正常工作,我们需要添加一个能够代理从权威机构到后端的"
 "请求的 Location,并且我们需要保存这个文件并重新加载Nginx。你确定要继续吗?"
 
-#: src/views/preference/OpenAISettings.vue:60
+#: src/views/preference/OpenAISettings.vue:66
 msgid "Token is not valid"
 msgstr "Token 无效"
 
@@ -1676,7 +1702,7 @@ msgstr "类型"
 #: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
-#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:95
+#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
 #: src/views/stream/components/RightSettings.vue:85
 #: src/views/stream/StreamList.vue:41 src/views/user/User.vue:37
 msgid "Updated at"
@@ -1707,11 +1733,11 @@ msgstr "Upstream 名称"
 msgid "Uptime:"
 msgstr "运行时间:"
 
-#: src/views/environment/Environment.vue:21
+#: src/views/environment/Environment.vue:22
 msgid "URL"
 msgstr "URL"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "User"
 msgstr "用户"
 
@@ -1765,7 +1791,7 @@ msgstr ""
 "我们将从这个文件中删除HTTPChallenge的配置,并重新加载Nginx。你确定要继续吗?"
 
 #: src/views/dashboard/ServerAnalytic.vue:27
-#: src/views/dashboard/ServerAnalytic.vue:365
+#: src/views/dashboard/ServerAnalytic.vue:368
 msgid "Writes"
 msgstr "写"
 
@@ -1791,6 +1817,9 @@ msgstr "您使用的是最新版本"
 msgid "You can check Nginx UI upgrade at this page."
 msgstr "你可以在这个页面检查Nginx UI的升级。"
 
+#~ msgid "The url is not valid"
+#~ msgstr "URL 无效"
+
 #~ msgid "Enable this option will significantly increase the token usage."
 #~ msgstr "启用该选项将显著增加 token 的使用量。"
 
@@ -1824,9 +1853,6 @@ msgstr "你可以在这个页面检查Nginx UI的升级。"
 #~ msgid "Rename Upstream"
 #~ msgstr "重新命名 Upstream"
 
-#~ msgid "Server"
-#~ msgstr "Server"
-
 #~ msgid "Leave blank will not change anything."
 #~ msgstr "留空不会有任何变化。"
 

+ 112 - 85
app/src/language/zh_TW/app.po

@@ -23,7 +23,7 @@ msgid "Access Logs"
 msgstr "存取日誌"
 
 #: src/routes/index.ts:128 src/views/certificate/ACMEUser.vue:76
-#: src/views/certificate/ACMEUserSelector.vue:79
+#: src/views/certificate/ACMEUserSelector.vue:84
 #, fuzzy
 msgid "ACME User"
 msgstr "使用者名稱"
@@ -31,7 +31,7 @@ msgstr "使用者名稱"
 #: src/views/certificate/ACMEUser.vue:59
 #: src/views/certificate/Certificate.vue:108
 #: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
-#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:102
+#: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:35
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
@@ -78,7 +78,7 @@ msgstr "其他設定"
 msgid "Advance Mode"
 msgstr "進階模式"
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:39
 msgid "API Base Url"
 msgstr "API 基礎網址"
 
@@ -87,11 +87,11 @@ msgstr "API 基礎網址"
 msgid "API Document"
 msgstr "API Token"
 
-#: src/views/preference/OpenAISettings.vue:45
+#: src/views/preference/OpenAISettings.vue:51
 msgid "API Proxy"
 msgstr "API 代理"
 
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:63
 msgid "API Token"
 msgstr "API Token"
 
@@ -105,7 +105,7 @@ msgstr "架構"
 msgid "Are you sure you want to clear all notifications?"
 msgstr "您確定要清除聊天記錄嗎?"
 
-#: src/components/ChatGPT/ChatGPT.vue:267
+#: src/components/ChatGPT/ChatGPT.vue:272
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "您確定要清除聊天記錄嗎?"
 
@@ -136,11 +136,11 @@ msgstr "您確定要刪除這條指令嗎?"
 msgid "Are you sure you want to remove this location?"
 msgstr "您確定要刪除此 Location 嗎?"
 
-#: src/components/ChatGPT/ChatGPT.vue:211
+#: src/components/ChatGPT/ChatGPT.vue:216
 msgid "Ask ChatGPT for Help"
 msgstr "向 ChatGPT 尋求幫助"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "Assistant"
 msgstr "助理"
 
@@ -149,7 +149,7 @@ msgstr "助理"
 msgid "Author"
 msgstr "作者"
 
-#: src/views/domain/cert/ChangeCert.vue:34
+#: src/views/domain/cert/ChangeCert.vue:33
 msgid "Auto Cert"
 msgstr "自動憑證"
 
@@ -182,7 +182,7 @@ msgstr "基本資訊"
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:94
+#: src/views/preference/Preference.vue:101
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgstr "基本"
@@ -208,10 +208,10 @@ msgstr ""
 msgid "CADir"
 msgstr "CADir"
 
-#: src/components/ChatGPT/ChatGPT.vue:248
+#: src/components/ChatGPT/ChatGPT.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:263
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:102
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:153
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:21
 #: src/views/domain/components/RightSettings.vue:51
@@ -255,8 +255,8 @@ msgstr "憑證清單"
 msgid "Challenge Method"
 msgstr "驗證方式"
 
-#: src/views/domain/cert/ChangeCert.vue:88
-#: src/views/domain/cert/ChangeCert.vue:92
+#: src/views/domain/cert/ChangeCert.vue:95
+#: src/views/domain/cert/ChangeCert.vue:99
 msgid "Change Certificate"
 msgstr "更換憑證"
 
@@ -272,7 +272,7 @@ msgstr "再次檢查"
 msgid "Cleaning environment variables"
 msgstr "清理環境變數"
 
-#: src/components/ChatGPT/ChatGPT.vue:271
+#: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
 #: src/views/notification/Notification.vue:75
 msgid "Clear"
@@ -316,7 +316,7 @@ msgstr "設定"
 msgid "Configure SSL"
 msgstr "設定 SSL"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Connected"
 msgstr "已連結"
 
@@ -330,7 +330,7 @@ msgstr "內容"
 msgid "Core Upgrade"
 msgstr "核心升級"
 
-#: src/views/dashboard/ServerAnalytic.vue:293
+#: src/views/dashboard/ServerAnalytic.vue:296
 msgid "CPU Status"
 msgstr "中央處理器狀態"
 
@@ -372,6 +372,12 @@ msgstr "目前版本"
 msgid "Custom"
 msgstr "自訂"
 
+#: src/views/preference/BasicSettings.vue:121
+msgid ""
+"Customize the name of local server to be displayed in the environment "
+"indicator."
+msgstr ""
+
 #: src/routes/index.ts:39
 msgid "Dashboard"
 msgstr "儀表板"
@@ -462,9 +468,9 @@ msgstr "停用"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "關閉 %{name} 自動續簽失敗"
 
-#: src/views/domain/cert/ChangeCert.vue:45 src/views/domain/DomainEdit.vue:185
-#: src/views/domain/DomainList.vue:33 src/views/stream/StreamEdit.vue:177
-#: src/views/stream/StreamList.vue:33
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
+#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "停用"
 
@@ -475,7 +481,7 @@ msgstr "停用"
 msgid "Disabled successfully"
 msgstr "成功停用"
 
-#: src/views/dashboard/ServerAnalytic.vue:358
+#: src/views/dashboard/ServerAnalytic.vue:361
 msgid "Disk IO"
 msgstr "磁碟 IO"
 
@@ -645,9 +651,10 @@ msgstr "啟用成功"
 msgid "Enable TLS"
 msgstr "啟用 TLS"
 
-#: src/views/domain/cert/ChangeCert.vue:41
+#: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
@@ -667,11 +674,11 @@ msgstr "成功啟用"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "用 Let's Encrypt 對網站進行加密"
 
-#: src/routes/index.ts:212 src/views/environment/Environment.vue:110
+#: src/routes/index.ts:212 src/views/environment/Environment.vue:137
 msgid "Environment"
 msgstr "環境"
 
-#: src/views/dashboard/Environments.vue:71
+#: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr "環境"
 
@@ -895,7 +902,7 @@ msgstr "上次檢查時間"
 msgid "Leave blank for no change"
 msgstr "留空表示不修改"
 
-#: src/views/preference/OpenAISettings.vue:41
+#: src/views/preference/OpenAISettings.vue:47
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "預設留空:https://api.openai.com/"
 
@@ -910,7 +917,7 @@ msgstr "留空表示不修改"
 msgid "License"
 msgstr "授權條款"
 
-#: src/views/dashboard/Environments.vue:128
+#: src/views/dashboard/Environments.vue:139
 msgid "Link Start"
 msgstr "連結開始"
 
@@ -924,8 +931,8 @@ msgstr ""
 msgid "Load Average:"
 msgstr "系統負載:"
 
-#: src/components/EnvIndicator/EnvIndicator.vue:38
-#: src/components/NodeSelector/NodeSelector.vue:51
+#: src/components/EnvIndicator/EnvIndicator.vue:40
+#: src/components/NodeSelector/NodeSelector.vue:71
 msgid "Local"
 msgstr "本機"
 
@@ -954,7 +961,7 @@ msgstr "登入成功"
 msgid "Logout successful"
 msgstr "登出成功"
 
-#: src/views/preference/Preference.vue:112
+#: src/views/preference/Preference.vue:119
 msgid "Logrotate"
 msgstr ""
 
@@ -998,6 +1005,7 @@ msgid "Managed Certificate"
 msgstr "更換憑證"
 
 #: src/views/dashboard/ServerAnalytic.vue:217
+#: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgstr "記憶體"
 
@@ -1009,12 +1017,12 @@ msgstr "記憶體與儲存"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:26
+#: src/views/preference/OpenAISettings.vue:27
 #, fuzzy
 msgid "Model"
 msgstr "執行模式"
 
-#: src/components/ChatGPT/ChatGPT.vue:244
+#: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 msgid "Modify"
@@ -1042,7 +1050,7 @@ msgstr "多行指令"
 #: src/views/certificate/Certificate.vue:20
 #: src/views/certificate/CertificateEditor.vue:146
 #: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
-#: src/views/domain/cert/ChangeCert.vue:18
+#: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
 #: src/views/domain/DomainList.vue:13
@@ -1054,19 +1062,19 @@ msgstr "多行指令"
 msgid "Name"
 msgstr "名稱"
 
-#: src/views/dashboard/ServerAnalytic.vue:319
+#: src/views/dashboard/ServerAnalytic.vue:322
 msgid "Network"
 msgstr "網路"
 
-#: src/views/dashboard/ServerAnalytic.vue:261
+#: src/views/dashboard/ServerAnalytic.vue:264
 msgid "Network Statistics"
 msgstr "網路統計"
 
-#: src/views/dashboard/ServerAnalytic.vue:268
+#: src/views/dashboard/ServerAnalytic.vue:271
 msgid "Network Total Receive"
 msgstr "下載流量"
 
-#: src/views/dashboard/ServerAnalytic.vue:274
+#: src/views/dashboard/ServerAnalytic.vue:277
 msgid "Network Total Send"
 msgstr "上傳流量"
 
@@ -1080,7 +1088,7 @@ msgstr "新版本發布"
 msgid "Next"
 msgstr "下一步"
 
-#: src/views/preference/Preference.vue:100
+#: src/views/preference/Preference.vue:107
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1092,7 +1100,7 @@ msgstr "Nginx 存取日誌路徑"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 設定解析錯誤"
 
-#: src/components/NginxControl/NginxControl.vue:62
+#: src/components/NginxControl/NginxControl.vue:65
 msgid "Nginx Control"
 msgstr "Nginx 控制元件"
 
@@ -1104,15 +1112,15 @@ msgstr "Nginx 錯誤日誌路徑"
 msgid "Nginx Log"
 msgstr "Nginx 日誌"
 
-#: src/components/NginxControl/NginxControl.vue:22
+#: src/components/NginxControl/NginxControl.vue:23
 msgid "Nginx reloaded successfully"
 msgstr "Nginx 重新載入成功"
 
-#: src/components/NginxControl/NginxControl.vue:36
+#: src/components/NginxControl/NginxControl.vue:39
 msgid "Nginx restarted successfully"
 msgstr "Nginx 重啟成功"
 
-#: src/components/ChatGPT/ChatGPT.vue:265
+#: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
@@ -1164,22 +1172,22 @@ msgstr "取得憑證"
 msgid "Obtaining certificate"
 msgstr "正在取得憑證"
 
-#: src/components/NodeSelector/NodeSelector.vue:75
-#: src/views/dashboard/Environments.vue:95
-#: src/views/environment/Environment.vue:86
+#: src/components/NodeSelector/NodeSelector.vue:95
+#: src/views/dashboard/Environments.vue:106
+#: src/views/environment/Environment.vue:88
 msgid "Offline"
 msgstr "離線"
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:264
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:154
 msgid "Ok"
 msgstr ""
 
-#: src/components/ChatGPT/ChatGPT.vue:266
+#: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1198,14 +1206,14 @@ msgstr "確定"
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
-#: src/components/NodeSelector/NodeSelector.vue:54
-#: src/components/NodeSelector/NodeSelector.vue:69
-#: src/views/dashboard/Environments.vue:88
-#: src/views/environment/Environment.vue:82
+#: src/components/NodeSelector/NodeSelector.vue:74
+#: src/components/NodeSelector/NodeSelector.vue:89
+#: src/views/dashboard/Environments.vue:99
+#: src/views/environment/Environment.vue:84
 msgid "Online"
 msgstr "線上"
 
-#: src/views/preference/Preference.vue:106
+#: src/views/preference/Preference.vue:113
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1301,7 +1309,7 @@ msgstr "請至少選擇一個節點!"
 msgid "Pre-release"
 msgstr "預先發布"
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:89
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
 msgid "Preference"
 msgstr "偏好設定"
 
@@ -1326,12 +1334,12 @@ msgid "Provider"
 msgstr "供應商"
 
 #: src/views/dashboard/ServerAnalytic.vue:28
-#: src/views/dashboard/ServerAnalytic.vue:375
+#: src/views/dashboard/ServerAnalytic.vue:378
 msgid "Reads"
 msgstr "讀取"
 
 #: src/views/dashboard/ServerAnalytic.vue:24
-#: src/views/dashboard/ServerAnalytic.vue:326
+#: src/views/dashboard/ServerAnalytic.vue:329
 msgid "Receive"
 msgstr "接收"
 
@@ -1349,7 +1357,7 @@ msgstr "儲存成功"
 msgid "Recursive Nameservers"
 msgstr "網站域名 (server_name)"
 
-#: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/ChatGPT/ChatGPT.vue:283
 msgid "Regenerate response"
 msgstr "重新產生回應"
 
@@ -1385,12 +1393,12 @@ msgstr "重新安裝"
 msgid "Release Note"
 msgstr "發行公告"
 
-#: src/components/ChatGPT/ChatGPT.vue:254
-#: src/components/NginxControl/NginxControl.vue:97
+#: src/components/ChatGPT/ChatGPT.vue:259
+#: src/components/NginxControl/NginxControl.vue:100
 msgid "Reload"
 msgstr "重新載入"
 
-#: src/components/NginxControl/NginxControl.vue:71
+#: src/components/NginxControl/NginxControl.vue:74
 msgid "Reloading"
 msgstr "重新載入中"
 
@@ -1438,11 +1446,11 @@ msgstr "請求參數錯誤"
 msgid "Reset"
 msgstr "重設"
 
-#: src/components/NginxControl/NginxControl.vue:90
+#: src/components/NginxControl/NginxControl.vue:93
 msgid "Restart"
 msgstr "重新啟動"
 
-#: src/components/NginxControl/NginxControl.vue:76
+#: src/components/NginxControl/NginxControl.vue:79
 msgid "Restarting"
 msgstr "正在重新啟動"
 
@@ -1450,15 +1458,15 @@ msgstr "正在重新啟動"
 msgid "Run Mode"
 msgstr "執行模式"
 
-#: src/components/NginxControl/NginxControl.vue:66
+#: src/components/NginxControl/NginxControl.vue:69
 msgid "Running"
 msgstr "執行中"
 
-#: src/components/ChatGPT/ChatGPT.vue:247
+#: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:123 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
 msgid "Save"
 msgstr "儲存"
 
@@ -1474,7 +1482,7 @@ msgstr "儲存錯誤 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:45
-#: src/views/preference/Preference.vue:59
+#: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr "儲存成功"
 
@@ -1489,24 +1497,24 @@ msgstr "儲存成功"
 msgid "SDK"
 msgstr ""
 
-#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
+#: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgstr "選擇器"
 
 #: src/views/dashboard/ServerAnalytic.vue:25
-#: src/views/dashboard/ServerAnalytic.vue:336
+#: src/views/dashboard/ServerAnalytic.vue:339
 msgid "Send"
 msgstr "傳送"
 
-#: src/components/NginxControl/NginxControl.vue:28
-#: src/components/NginxControl/NginxControl.vue:42
+#: src/components/NginxControl/NginxControl.vue:29
+#: src/components/NginxControl/NginxControl.vue:45
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:63
+#: src/views/other/Install.vue:70 src/views/preference/Preference.vue:70
 #: src/views/stream/StreamList.vue:113 src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 msgid "Server error"
@@ -1516,6 +1524,11 @@ msgstr "伺服器錯誤"
 msgid "Server Info"
 msgstr "伺服器資訊"
 
+#: src/views/preference/BasicSettings.vue:117
+#, fuzzy
+msgid "Server Name"
+msgstr "伺服器資訊"
+
 #: src/views/domain/cert/components/ObtainCert.vue:102
 msgid "server_name not found in directives"
 msgstr "在指令中未找到 server_name"
@@ -1588,15 +1601,16 @@ msgstr "穩定"
 
 #: src/views/certificate/ACMEUser.vue:42
 #: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
-#: src/views/environment/Environment.vue:75 src/views/stream/StreamList.vue:22
+#: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "狀態"
 
-#: src/components/NginxControl/NginxControl.vue:81
+#: src/components/NginxControl/NginxControl.vue:84
 msgid "Stopped"
 msgstr "已停止"
 
-#: src/views/dashboard/ServerAnalytic.vue:243
+#: src/views/dashboard/ServerAnalytic.vue:245
+#: src/views/dashboard/ServerAnalytic.vue:246
 msgid "Storage"
 msgstr "儲存空間"
 
@@ -1609,7 +1623,8 @@ msgstr "主體名稱: %{name}"
 msgid "Success"
 msgstr ""
 
-#: src/views/dashboard/ServerAnalytic.vue:230
+#: src/views/dashboard/ServerAnalytic.vue:231
+#: src/views/dashboard/ServerAnalytic.vue:232
 msgid "Swap"
 msgstr "交換空間"
 
@@ -1660,6 +1675,10 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr "SSL 憑證金鑰路徑"
 
+#: src/views/preference/OpenAISettings.vue:30
+msgid "The model name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/certificate/CertificateEditor.vue:165
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
@@ -1669,6 +1688,10 @@ msgstr "SSL 憑證金鑰路徑"
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
+#: src/views/preference/BasicSettings.vue:120
+msgid "The server name should only contain letters, numbers, dashes, and dots."
+msgstr ""
+
 #: src/views/domain/cert/components/AutoCertStepOne.vue:50
 #, fuzzy
 msgid ""
@@ -1678,10 +1701,14 @@ msgstr "注意:目前設定中的 server_name 必須為需要申請憑證的
 
 #: src/views/preference/BasicSettings.vue:38
 #: src/views/preference/BasicSettings.vue:50
-#: src/views/preference/OpenAISettings.vue:36
-#: src/views/preference/OpenAISettings.vue:48
 #, fuzzy
-msgid "The url is not valid"
+msgid "The url is invalid"
+msgstr "此功能在演示中不可用。"
+
+#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:54
+#, fuzzy
+msgid "The url is invalid."
 msgstr "此功能在演示中不可用。"
 
 #: src/language/constants.ts:2
@@ -1725,7 +1752,7 @@ msgstr ""
 "為了確保憑證自動續期能夠正常運作,我們需要新增一個 Location 來代理從授權後端"
 "的請求,我們需要儲存這個檔案並重新載入 Nginx。你確定你要繼續嗎?"
 
-#: src/views/preference/OpenAISettings.vue:60
+#: src/views/preference/OpenAISettings.vue:66
 msgid "Token is not valid"
 msgstr ""
 
@@ -1742,7 +1769,7 @@ msgstr "類型"
 #: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
-#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:95
+#: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
 #: src/views/stream/components/RightSettings.vue:85
 #: src/views/stream/StreamList.vue:41 src/views/user/User.vue:37
 msgid "Updated at"
@@ -1773,11 +1800,11 @@ msgstr ""
 msgid "Uptime:"
 msgstr "運作時間:"
 
-#: src/views/environment/Environment.vue:21
+#: src/views/environment/Environment.vue:22
 msgid "URL"
 msgstr "URL"
 
-#: src/components/ChatGPT/ChatGPT.vue:225
+#: src/components/ChatGPT/ChatGPT.vue:230
 msgid "User"
 msgstr "使用者名稱"
 
@@ -1834,7 +1861,7 @@ msgstr ""
 "繼續嗎?"
 
 #: src/views/dashboard/ServerAnalytic.vue:27
-#: src/views/dashboard/ServerAnalytic.vue:365
+#: src/views/dashboard/ServerAnalytic.vue:368
 msgid "Writes"
 msgstr "寫"
 
@@ -1860,6 +1887,10 @@ msgstr "您正在使用最新版本"
 msgid "You can check Nginx UI upgrade at this page."
 msgstr "您可以在此頁面檢查 Nginx UI 的升級。"
 
+#, fuzzy
+#~ msgid "The url is not valid"
+#~ msgstr "此功能在演示中不可用。"
+
 #~ msgid "ChatGPT Model"
 #~ msgstr "ChatGPT 模型"
 
@@ -1881,10 +1912,6 @@ msgstr "您可以在此頁面檢查 Nginx UI 的升級。"
 #~ msgid "Table"
 #~ msgstr "表格"
 
-#, fuzzy
-#~ msgid "Server"
-#~ msgstr "伺服器資訊"
-
 #, fuzzy
 #~ msgid "Leave blank will not change anything."
 #~ msgstr "留空表示不修改"

+ 9 - 4
app/src/layouts/BaseLayout.vue

@@ -1,9 +1,12 @@
 <script setup lang="ts">
 import _ from 'lodash'
+import { storeToRefs } from 'pinia'
 import FooterLayout from './FooterLayout.vue'
 import SideBar from './SideBar.vue'
 import HeaderLayout from './HeaderLayout.vue'
 import PageHeader from '@/components/PageHeader/PageHeader.vue'
+import { useSettingsStore } from '@/pinia'
+import settings from '@/api/settings'
 
 const drawer_visible = ref(false)
 const collapsed = ref(collapse())
@@ -19,6 +22,12 @@ function getClientWidth() {
 function collapse() {
   return getClientWidth() < 1280
 }
+
+const { server_name } = storeToRefs(useSettingsStore())
+
+settings.get_server_name().then(r => {
+  server_name.value = r.name
+})
 </script>
 
 <template>
@@ -150,10 +159,6 @@ body {
   font-size: 13px;
 }
 
-.ant-card-bordered {
-
-}
-
 .header-notice-wrapper .ant-tabs-content {
   max-height: 250px;
 }

+ 2 - 2
app/src/layouts/SideBar.vue

@@ -42,7 +42,7 @@ interface meta {
 
 interface sidebar {
   path: string
-  name: () => string
+  name: string
   meta: meta
   children: sidebar[]
 }
@@ -56,7 +56,7 @@ const visible: ComputedRef<sidebar[]> = computed(() => {
 
     const t: sidebar = {
       path: s.path,
-      name: s?.meta?.name ?? (() => ''),
+      name: s.name as string,
       meta: s.meta as unknown as meta,
       children: [],
     };

+ 1 - 0
app/src/pinia/moudule/settings.ts

@@ -9,6 +9,7 @@ export const useSettingsStore = defineStore('settings', {
       id: 0,
       name: 'Local',
     },
+    server_name: '',
   }),
   getters: {
     is_remote(): boolean {

+ 1 - 1
app/src/version.json

@@ -1 +1 @@
-{"version":"2.0.0-beta.22","build_id":131,"total_build":335}
+{"version":"2.0.0-beta.23","build_id":133,"total_build":337}

+ 10 - 5
app/src/views/certificate/ACMEUserSelector.vue

@@ -42,12 +42,17 @@ onMounted(async () => {
   users.value = []
   let page = 1
   while (true) {
-    const r = await acme_user.get_list({ page })
-
-    users.value.push(...r.data)
-    if (r?.data?.length < r?.pagination?.per_page)
+    try {
+      const r = await acme_user.get_list({ page })
+
+      users.value.push(...r.data)
+      if (r?.data?.length < r?.pagination?.per_page)
+        break
+      page++
+    }
+    catch (e) {
       break
-    page++
+    }
   }
 
   init()

+ 7 - 1
app/src/views/dashboard/DashBoard.vue

@@ -1,11 +1,17 @@
 <script setup lang="ts">
 import ServerAnalytic from '@/views/dashboard/ServerAnalytic.vue'
 import Environments from '@/views/dashboard/Environments.vue'
+
+const key = ref(0)
+
+setInterval(() => {
+  key.value++
+}, 5 * 60 * 1000)
 </script>
 
 <template>
   <div>
-    <ServerAnalytic />
+    <ServerAnalytic :key />
     <Environments />
   </div>
 </template>

+ 14 - 3
app/src/views/dashboard/Environments.vue

@@ -25,10 +25,21 @@ const node_map = computed(() => {
 
 let websocket: ReconnectingWebSocket | WebSocket
 
+onMounted(async () => {
+  let hasMore = true
+  let page = 1
+  while (hasMore) {
+    await environment.get_list({ page, enabled: true }).then(r => {
+      data.value.push(...r.data)
+      hasMore = r.data.length === r.pagination.per_page
+      page++
+    }).catch(() => {
+      hasMore = false
+    })
+  }
+})
+
 onMounted(() => {
-  environment.get_list().then(r => {
-    data.value = r.data
-  })
   websocket = analytic.nodes()
   websocket.onmessage = async m => {
     const nodes = JSON.parse(m.data)

+ 49 - 8
app/src/views/environment/Environment.vue

@@ -1,11 +1,11 @@
 <script setup lang="tsx">
 import { h } from 'vue'
-import { Badge } from 'ant-design-vue'
+import { Badge, Tag, message } from 'ant-design-vue'
 import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
 import { datetime } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
 import environment from '@/api/environment'
 import StdCurd from '@/components/StdDesign/StdDataDisplay/StdCurd.vue'
-import { input } from '@/components/StdDesign/StdDataEntry'
+import { input, switcher } from '@/components/StdDesign/StdDataEntry'
 import type { Column, JSXElements } from '@/components/StdDesign/types'
 
 const columns: Column[] = [{
@@ -16,6 +16,7 @@ const columns: Column[] = [{
   edit: {
     type: input,
   },
+  search: true,
 },
 {
   title: () => $gettext('URL'),
@@ -77,13 +78,19 @@ const columns: Column[] = [{
   customRender: (args: customRender) => {
     const template: JSXElements = []
     const { text } = args
-    if (text === true || text > 0) {
-      template.push(<Badge status="success"/>)
-      template.push($gettext('Online'))
+    if (args.record.enabled) {
+      if (text === true || text > 0) {
+        template.push(<Badge status="success"/>)
+        template.push($gettext('Online'))
+      }
+      else {
+        template.push(<Badge status="error"/>)
+        template.push($gettext('Offline'))
+      }
     }
     else {
-      template.push(<Badge status="error"/>)
-      template.push($gettext('Offline'))
+      template.push(<Badge status="default"/>)
+      template.push($gettext('Disabled'))
     }
 
     return h('div', template)
@@ -91,6 +98,26 @@ const columns: Column[] = [{
   sortable: true,
   pithy: true,
 },
+{
+  title: () => $gettext('Enabled'),
+  dataIndex: 'enabled',
+  customRender: (args: customRender) => {
+    const template: JSXElements = []
+    const { text } = args
+    if (text === true || text > 0)
+      template.push(<Tag color="green">{$gettext('Enabled')}</Tag>)
+
+    else
+      template.push(<Tag color="orange">{$gettext('Disabled')}</Tag>)
+
+    return h('div', template)
+  },
+  edit: {
+    type: switcher,
+  },
+  sortable: true,
+  pithy: true,
+},
 {
   title: () => $gettext('Updated at'),
   dataIndex: 'updated_at',
@@ -103,14 +130,28 @@ const columns: Column[] = [{
   dataIndex: 'action',
 }]
 
+const curd = ref()
+function load_from_settings() {
+  environment.load_from_settings().then(() => {
+    curd.value.get_list()
+    message.success($gettext('Load successfully'))
+  }).catch(e => {
+    message.error(`${$gettext('Server error')} ${e?.message}`)
+  })
+}
 </script>
 
 <template>
   <StdCurd
+    ref="curd"
     :title="$gettext('Environment')"
     :api="environment"
     :columns="columns"
-  />
+  >
+    <template #extra>
+      <a @click="load_from_settings">{{ $gettext('Load from settings') }}</a>
+    </template>
+  </StdCurd>
 </template>
 
 <style lang="less" scoped>

+ 0 - 2
app/src/views/other/Error.vue

@@ -5,8 +5,6 @@ const route = useRoute()
 const info = computed(() => {
   if (typeof route.meta.error === 'function')
     return route.meta.error()
-  else if (typeof route.meta.error === 'string')
-    return route.meta.error
   else
     return $gettext('File Not Found')
 })

+ 11 - 2
app/src/views/preference/BasicSettings.vue

@@ -35,7 +35,7 @@ const errors: Record<string, Record<string, string>> = inject('errors') as Recor
       :label="$gettext('Github Proxy')"
       :validate-status="errors?.server?.github_proxy ? 'error' : ''"
       :help="errors?.server?.github_proxy === 'url'
-        ? $gettext('The url is not valid')
+        ? $gettext('The url is invalid')
         : ''"
     >
       <AInput
@@ -47,7 +47,7 @@ const errors: Record<string, Record<string, string>> = inject('errors') as Recor
       :label="$gettext('CADir')"
       :validate-status="errors?.server?.ca_dir ? 'error' : ''"
       :help="errors?.server?.ca_dir === 'url'
-        ? $gettext('The url is not valid')
+        ? $gettext('The url is invalid')
         : ''"
     >
       <AInput v-model:value="data.server.ca_dir" />
@@ -113,6 +113,15 @@ const errors: Record<string, Record<string, string>> = inject('errors') as Recor
         </template>
       </Draggable>
     </AFormItem>
+    <AFormItem
+      :label="$gettext('Server Name')"
+      :validate-status="errors?.server?.name ? 'error' : ''"
+      :help="errors?.server?.name.includes('alpha_num_dash_dot')
+        ? $gettext('The server name should only contain letters, numbers, dashes, and dots.')
+        : $gettext('Customize the name of local server to be displayed in the environment indicator.')"
+    >
+      <AInput v-model:value="data.server.name" />
+    </AFormItem>
   </AForm>
 </template>
 

+ 9 - 3
app/src/views/preference/OpenAISettings.vue

@@ -23,7 +23,13 @@ const models = shallowRef([
 
 <template>
   <AForm layout="vertical">
-    <AFormItem :label="$gettext('Model')">
+    <AFormItem
+      :label="$gettext('Model')"
+      :validate-status="errors?.openai?.model ? 'error' : ''"
+      :help="errors?.openai?.model === 'alpha_num_dash_dot'
+        ? $gettext('The model name should only contain letters, numbers, dashes, and dots.')
+        : ''"
+    >
       <AAutoComplete
         v-model:value="data.openai.model"
         :options="models"
@@ -33,7 +39,7 @@ const models = shallowRef([
       :label="$gettext('API Base Url')"
       :validate-status="errors?.openai?.base_url ? 'error' : ''"
       :help="errors?.openai?.base_url === 'url'
-        ? $gettext('The url is not valid')
+        ? $gettext('The url is invalid.')
         : ''"
     >
       <AInput
@@ -45,7 +51,7 @@ const models = shallowRef([
       :label="$gettext('API Proxy')"
       :validate-status="errors?.openai?.proxy ? 'error' : ''"
       :help="errors?.openai?.proxy === 'url'
-        ? $gettext('The url is not valid')
+        ? $gettext('The url is invalid.')
         : ''"
     >
       <AInput

+ 7 - 0
app/src/views/preference/Preference.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import { message } from 'ant-design-vue'
 import type { Ref } from 'vue'
+import { storeToRefs } from 'pinia'
 import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
 import settings from '@/api/settings'
 import BasicSettings from '@/views/preference/BasicSettings.vue'
@@ -8,6 +9,7 @@ import OpenAISettings from '@/views/preference/OpenAISettings.vue'
 import NginxSettings from '@/views/preference/NginxSettings.vue'
 import type { Settings } from '@/views/preference/typedef'
 import LogrotateSettings from '@/views/preference/LogrotateSettings.vue'
+import { useSettingsStore } from '@/pinia'
 
 const data = ref<Settings>({
   server: {
@@ -23,6 +25,7 @@ const data = ref<Settings>({
     node_secret: '',
     cert_renewal_interval: 7,
     recursive_nameservers: [],
+    name: '',
   },
   nginx: {
     access_log_path: '',
@@ -49,12 +52,16 @@ settings.get().then(r => {
   data.value = r
 })
 
+const settingsStore = useSettingsStore()
+const { server_name } = storeToRefs(settingsStore)
 const errors = ref({}) as Ref<Record<string, Record<string, string>>>
 
 async function save() {
   // fix type
   data.value.server.http_challenge_port = data.value.server.http_challenge_port.toString()
   settings.save(data.value).then(r => {
+    if (!settingsStore.is_remote)
+      server_name.value = r?.server?.name ?? ''
     data.value = r
     message.success($gettext('Save successfully'))
     errors.value = {}

+ 1 - 0
app/src/views/preference/typedef.ts

@@ -12,6 +12,7 @@ export interface Settings {
     ca_dir: string
     cert_renewal_interval: number
     recursive_nameservers: string[]
+    name: string
   }
   nginx: {
     access_log_path: string

+ 1 - 1
app/version.json

@@ -1 +1 @@
-{"version":"2.0.0-beta.22","build_id":131,"total_build":335}
+{"version":"2.0.0-beta.23","build_id":133,"total_build":337}

+ 12 - 6
demo.Dockerfile

@@ -9,11 +9,17 @@ EXPOSE 80
 COPY resources/demo/ojbk.me /etc/nginx/sites-available/ojbk.me
 COPY resources/demo/app.ini /etc/nginx-ui/app.ini
 COPY resources/demo/demo.db /etc/nginx-ui/database.db
-COPY resources/docker/nginx.conf /etc/nginx/nginx.conf
-COPY resources/docker/nginx-ui.conf /etc/nginx/conf.d/nginx-ui.conf
-COPY resources/docker/start.sh /app/start.sh
-COPY nginx-ui-$TARGETOS-$TARGETARCH$TARGETVARIANT/nginx-ui /app/nginx-ui
 
-RUN cd /app && chmod a+x start.sh && rm -f /etc/nginx/conf.d/default.conf
+# register nginx-ui service
+COPY resources/docker/nginx-ui.run /etc/s6-overlay/s6-rc.d/nginx-ui/run
+RUN echo 'longrun' > /etc/s6-overlay/s6-rc.d/nginx-ui/type && \
+    touch /etc/s6-overlay/s6-rc.d/user/contents.d/nginx-ui
 
-ENTRYPOINT ["./start.sh"]
+# copy nginx config
+COPY resources/docker/nginx.conf /usr/local/etc/nginx/nginx.conf
+COPY resources/docker/nginx-ui.conf /usr/local/etc/nginx/conf.d/nginx-ui.conf
+
+# copy nginx-ui executable binary
+COPY nginx-ui-$TARGETOS-$TARGETARCH$TARGETVARIANT/nginx-ui /usr/local/bin/nginx-ui
+
+RUN rm -f /etc/nginx/conf.d/default.conf

+ 2 - 1
docs/.vitepress/config/en.ts

@@ -38,7 +38,8 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
             {text: 'Nginx', link: '/guide/config-nginx'},
             {text: 'Open AI', link: '/guide/config-openai'},
             {text: 'Casdoor', link: '/guide/config-casdoor'},
-            {text: 'Logrotate', link: '/guide/config-logrotate'}
+            {text: 'Logrotate', link: '/guide/config-logrotate'},
+            {text: 'Cluster', link: '/guide/config-cluster'}
           ]
         },
         {

+ 2 - 1
docs/.vitepress/config/zh_CN.ts

@@ -43,7 +43,8 @@ export const zhCNConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
             {text: 'Nginx', link: '/zh_CN/guide/config-nginx'},
             {text: 'Open AI', link: '/zh_CN/guide/config-openai'},
             {text: 'Casdoor', link: '/zh_CN/guide/config-casdoor'},
-            {text: 'Logrotate', link: '/zh_CN/guide/config-logrotate'}
+            {text: 'Logrotate', link: '/zh_CN/guide/config-logrotate'},
+            {text: '集群', link: '/zh_CN/guide/config-cluster'}
           ]
         },
         {

+ 2 - 1
docs/.vitepress/config/zh_TW.ts

@@ -42,7 +42,8 @@ export const zhTWConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
             {text: 'Nginx', link: '/zh_TW/guide/config-nginx'},
             {text: 'Open AI', link: '/zh_TW/guide/config-openai'},
             {text: 'Casdoor', link: '/zh_TW/guide/config-casdoor'},
-            {text: 'Logrotate', link: '/zh_CN/guide/config-logrotate'}
+            {text: 'Logrotate', link: '/zh_TW/guide/config-logrotate'},
+            {text: '集群', link: '/zh_TW/guide/config-cluster'}
           ]
         },
         {

+ 24 - 0
docs/guide/config-cluster.md

@@ -0,0 +1,24 @@
+# 集群
+From v2.0.0-beta.23, you can define multiple environments in the `cluster` section of the configuration file.
+
+## Node
+- Type: `string`
+- Structure:`Scheme://Host(:Port)?name=ENV_NAME&node_secret=NODE_SECRET&enabled=(true/false)`
+- Example: `http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true`
+
+If you have multiple environments to configure, please refer to the following configuration:
+```ini
+[cluster]
+Node = http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true
+Node = http://10.0.0.2:9000?name=node2&node_secret=my-node-secret&enabled=false
+Node = http://10.0.0.3?name=node3&node_secret=my-node-secret&enabled=true
+```
+
+By default, Nginx UI will create the predefined environments during the bootstrapping stage.
+You can also find the "Load from Config" button in the environment list in the WebUI to manually update the environments.
+
+In order to avoid conflicts with the environemnts that already exist in the database,
+Nginx UI will check if the `Scheme://Host(:Port)` part is unique.
+If it does not exist, it will be created according to the configuration, otherwise no action will be taken.
+
+Please note that if you delete a node from the configuration file, Nginx UI will not delete the record from the database.

+ 0 - 30
docs/guide/config-nginx.md

@@ -9,36 +9,6 @@ Starting from Nginx UI v2.0.0-beta.3, we have renamed the `nginx_log` configurat
 ## Logs
 Nginx logs are crucial for monitoring, troubleshooting, and maintaining your web server. They provide valuable insights into server performance, user behavior, and potential issues.
 
-In this section, we will discuss two main types of logs: access logs and error logs.
-
-For Nginx-UI Docker users upgrading from version v1.5.2 or earlier, you need to add separate `access_log` and `error_log` directives in `nginx.conf` before configuring `app.ini`.
-
-In the Nginx-UI container, `/var/log/nginx/access.log` is a symbolic link to `/dev/stdout`, and `/var/log/nginx/error.log` is a symbolic link to `/dev/stderr`. This setup allows you to view Nginx and Nginx-UI logs using the `docker logs nginx-ui` command. However, these devices do not support the `tail` command, so it is necessary to use additional log files to record Nginx logs.
-
-Example:
-
-```nginx
-error_log /var/log/nginx/error.log notice;
-error_log /var/log/nginx/error.local.log notice;
-
-http {
-...
-    access_log /var/log/nginx/access.log main;
-    access_log /var/log/nginx/access.local.log main;
-...
-}
-```
-
-Afterward, set the nginx access log and error log paths in `app.ini`, then restart nginx-ui.
-
-Example:
-
-```ini
-[nginx]
-AccessLogPath = /var/log/nginx/access.local.log
-ErrorLogPath = /var/log/nginx/error.local.log
-```
-
 ### AccessLogPath
 
 - Type: `string`

+ 25 - 0
docs/guide/config-server.md

@@ -106,6 +106,7 @@ allows them to set a proxy for github.com to improve accessibility.
 
 ## CertRenewalInterval
 
+- Version:`>= v2.0.0-beta.22`
 - Type: `int`
 - Default value: `7`
 
@@ -114,9 +115,33 @@ By default, Nginx UI will automatically renew the certificate every 7 days.
 
 ## RecursiveNameservers
 
+- Version:`>= v2.0.0-beta.22`
 - Type: `[]string`
 - Example: `8.8.8.8:53,1.1.1.1:53`
 
 This option is used to set the recursive nameservers used by
 Nginx UI in the DNS challenge step of applying for a certificate.
 If this option is not configured, Nginx UI will use the nameservers settings of the operating system.
+
+## SkipInstallation
+
+- Version:`>= v2.0.0-beta.23`
+- Type: `bool`
+- Default value: `false`
+
+You can skip the installation of the Nginx UI server by setting this option to `true`.
+This is useful when you want to deploy Nginx UI to multiple servers with
+a same configuration file or environment variables.
+
+By default, if you enabled the skip installation mode without setting the `JWTSecret` and `NodeSecret` options
+in the server section, Nginx UI will generate a random UUID value for these two options.
+
+Plus, if you don't set the `Email` option also in the server section,
+Nginx UI will not create a system initial acme user, this means you can't apply for an SSL certificate in this server.
+
+## Name
+
+- Version:`>= v2.0.0-beta.23`
+- Type: `string`
+
+Use this option to customize the name of local server to be displayed in the environment indicator.

+ 1 - 1
docs/zh_CN/guide/build.md

@@ -31,5 +31,5 @@ pnpm build
 请在项目的根目录执行以下命令。
 
 ```shell
-go build -tags=jsoniter -ldflags "$LD_FLAGS -X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'" -o dist/nginx-ui -v main.go
+go build -tags=jsoniter -ldflags "$LD_FLAGS -X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'" -o nginx-ui -v main.go
 ```

+ 24 - 0
docs/zh_CN/guide/config-cluster.md

@@ -0,0 +1,24 @@
+# 集群
+
+自 v2.0.0-beta.23 起,您可以在配置文件的 `cluster` 分区中定义多个环境。
+
+## Node
+- 类型: `string`
+- 结构:`Scheme://Host(:Port)?name=环境名称&node_secret=节点密钥&enabled=是否启用`
+- 示例: `http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true`
+
+
+如果您需要配置多个环境,请参考下面的配置:
+```ini
+[cluster]
+Node = http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true
+Node = http://10.0.0.2:9000?name=node2&node_secret=my-node-secret&enabled=false
+Node = http://10.0.0.3?name=node3&node_secret=my-node-secret&enabled=true
+```
+
+默认情况下,Nginx UI 将在启动阶段执行环境的创建操作,您也可以在 WebUI 中的环境列表中找到「从配置中加载」按钮,手动更新环境。
+
+为了避免与数据库内已经存在的环境冲突,Nginx UI 会检查 `Scheme://Host(:Port)` 部分是否应是否唯一,
+如果不存在,则按照配置进行创建,反之则不会进行任何操作。
+
+注意:如果您删除了配置文件中的某个节点,Nginx UI 不会删除数据库中的记录。

+ 0 - 33
docs/zh_CN/guide/config-nginx.md

@@ -10,39 +10,6 @@
 ## 日志
 Nginx 日志对于监控、排查问题和维护您的 Web 服务器至关重要。它们提供了有关服务器性能、用户行为和潜在问题的宝贵见解。
 
-在本节中,我们将讨论两种主要类型的日志:访问日志和错误日志。
-
-对于从 v1.5.2 或更早版本升级的 Nginx-UI Docker 用户,在配置 `app.ini` 之前,您需要在 `nginx.conf`
-中添加单独的 `access_log` 和 `error_log` 指令。
-
-在 Nginx-UI 容器中,`/var/log/nginx/access.log` 是一个指向 `/dev/stdout` 的符号链接,而 `/var/log/nginx/error.log`
-是一个指向 `/dev/stderr` 的符号链接。这种设置允许您使用 `docker logs nginx-ui` 命令查看 Nginx 和 Nginx-UI 日志。然而,这两个设备不支持
-`tail` 命令,因此有必要使用额外的日志文件来记录 Nginx 日志。
-
-示例:
-
-```nginx
-error_log /var/log/nginx/error.log notice;
-error_log /var/log/nginx/error.local.log notice;
-
-http {
-...
-    access_log /var/log/nginx/access.log main;
-    access_log /var/log/nginx/access.local.log main;
-...
-}
-```
-
-之后,请在 `app.ini` 中设置 nginx 访问日志和错误日志路径,然后重新启动 nginx-ui。
-
-示例:
-
-```ini
-[nginx]
-AccessLogPath = /var/log/nginx/access.local.log
-ErrorLogPath = /var/log/nginx/error.local.log
-```
-
 ### AccessLogPath
 
 - 类型:`string`

+ 24 - 0
docs/zh_CN/guide/config-server.md

@@ -97,6 +97,7 @@ JWT 是一种用于验证用户身份的标准,它可以在用户登录后生
 
 ## CertRenewalInterval
 
+- 版本:`>= v2.0.0-beta.22`
 - 类型:`int`
 - 默认值: `7`
 
@@ -104,7 +105,30 @@ JWT 是一种用于验证用户身份的标准,它可以在用户登录后生
 
 ## RecursiveNameservers
 
+- 版本:`>= v2.0.0-beta.22`
 - 类型: `[]string`
 - 示例: `8.8.8.8:53,1.1.1.1:53`
 
 此选项用于设置 Nginx UI 在申请证书的 DNS 挑战步骤所使用的递归域名服务器。在不配置此项目的情况下,Nginx UI 使用操作系统的域名服务器设置。
+
+## SkipInstallation
+
+- 版本:`>= v2.0.0-beta.23`
+- 类型:`bool`
+- 默认值:`false`
+
+通过将此选项设置为 `true`,您可以跳过 Nginx UI 服务器的安装。
+当您希望使用相同的配置文件或环境变量将 Nginx UI 部署到多个服务器时,这非常有用。
+
+默认情况下,如果您启用了跳过安装模式,而没有在服务器部分设置 `JWTSecret` 和 `NodeSecret` 选项,
+Nginx UI 将为这两个选项生成一个随机的 UUID 值。
+
+此外,如果您也没有在服务器部分设置 `Email` 选项,
+Nginx UI 将不会创建系统初始的 acme 用户,这意味着您无法在此服务器上申请 SSL 证书。
+
+## Name
+
+- 版本:`>= v2.0.0-beta.23`
+- 类型:`string`
+
+使用此选项自定义本地服务器的名称,以在环境指示器中显示。

+ 1 - 1
docs/zh_TW/guide/build.md

@@ -31,5 +31,5 @@ pnpm build
 請在專案的根資料夾執行以下命令。
 
 ```shell
-go build -tags=jsoniter -ldflags "$LD_FLAGS -X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'" -o dist/nginx-ui -v main.go
+go build -tags=jsoniter -ldflags "$LD_FLAGS -X 'github.com/0xJacky/Nginx-UI/settings.buildTime=$(date +%s)'" -o nginx-ui -v main.go
 ```

+ 26 - 0
docs/zh_TW/guide/config-cluster.md

@@ -0,0 +1,26 @@
+# 集群
+
+自 v2.0.0-beta.23 起,您可以在配置文件的 `cluster` 分區中定義多個環境。
+
+## Node
+
+- 版本:`>= v2.0.0-beta.23`
+- 類型: `string`
+- 結構:`Scheme://Host(:Port)?name=環境名稱&node_secret=節點密鑰&enabled=是否啟用`
+- 範例: `http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true`
+
+
+如果您需要配置多個環境,請參考下面的配置:
+```ini
+[cluster]
+Node = http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true
+Node = http://10.0.0.2:9000?name=node2&node_secret=my-node-secret&enabled=false
+Node = http://10.0.0.3?name=node3&node_secret=my-node-secret&enabled=true
+```
+
+預設情況下,Nginx UI 將在啟動階段執行環境的創建操作,您也可以在 WebUI 中的環境列表中找到「從配置中加載」按鈕,手動更新環境。
+
+為了避免與資料庫內已經存在的環境衝突,Nginx UI 會檢查 Scheme://Host(:Port) 部分是否應是否唯一,
+如果不存在,則按照配置進行創建,反之則不會進行任何操作。
+
+注意:如果您刪除了配置文件中的某個節點,Nginx UI 不會刪除資料庫中的記錄。

+ 0 - 33
docs/zh_TW/guide/config-nginx.md

@@ -9,39 +9,6 @@
 ## 日誌
 Nginx 日誌對於監控、排查問題和維護您的 Web 伺服器至關重要。它們提供了有關伺服器性能、用戶行為和潛在問題的寶貴見解。
 
-在本節中,我們將討論兩種主要類型的日誌:訪問日誌和錯誤日誌。
-
-對於從 v1.5.2 或更早版本升級的 Nginx-UI Docker 用戶,在配置 `app.ini` 之前,您需要在 `nginx.conf`
-中添加單獨的 `access_log` 和 `error_log` 指令。
-
-在 Nginx-UI 容器中,`/var/log/nginx/access.log` 是一個指向 `/dev/stdout` 的符號鏈接,而 `/var/log/nginx/error.log`
-是一個指向 `/dev/stderr` 的符號鏈接。這種設置允許您使用 `docker logs nginx-ui` 命令查看 Nginx 和 Nginx-UI 日誌。然而,這兩個設備不支持
-`tail` 命令,因此有必要使用額外的日誌文件來記錄 Nginx 日誌。
-
-範例:
-
-```nginx
-error_log /var/log/nginx/error.log notice;
-error_log /var/log/nginx/error.local.log notice;
-
-http {
-...
-    access_log /var/log/nginx/access.log main;
-    access_log /var/log/nginx/access.local.log main;
-...
-}
-```
-
-之後,請在 `app.ini` 中設置 nginx 訪問日誌和錯誤日誌路徑,然後重新啟動 nginx-ui。
-
-範例:
-
-```ini
-[nginx]
-AccessLogPath = /var/log/nginx/access.local.log
-ErrorLogPath = /var/log/nginx/error.local.log
-```
-
 ### AccessLogPath
 
 - 類型:`string`

+ 24 - 0
docs/zh_TW/guide/config-server.md

@@ -98,6 +98,7 @@ JWT 是一種用於驗證用戶身份的標準,它可以在用戶登錄後生
 
 ## CertRenewalInterval
 
+- 版本:`>= v2.0.0-beta.22`
 - 類型:`int`
 - 預設值: `7`
 
@@ -105,7 +106,30 @@ JWT 是一種用於驗證用戶身份的標準,它可以在用戶登錄後生
 
 ## RecursiveNameservers
 
+- 版本:`>= v2.0.0-beta.22`
 - 類型: `[]string`
 - 範例: `8.8.8.8:53,1.1.1.1:53`
 
 此選項用於設定 Nginx UI 在申請證書的 DNS 挑戰步驟所使用的遞迴域名伺服器。在不配置此項目的情況下,Nginx UI 使用作業系統的域名伺服器設定。
+
+## SkipInstallation
+
+- 版本:`>= v2.0.0-beta.23`
+- 類型:`bool`
+- 預設值:`false`
+
+透過將此選項設定為 `true`,您可以跳過 Nginx UI 伺服器的安裝。
+當您希望使用相同的配置文件或環境變數將 Nginx UI 部署到多個伺服器時,這非常有用。
+
+預設情況下,如果您啟用了跳過安裝模式,而沒有在伺服器部分設定 `JWTSecret` 和 `NodeSecret` 選項,
+Nginx UI 將為這兩個選項生成一個隨機的 UUID 值。
+
+此外,如果您也沒有在伺服器部分設定 `Email` 選項,
+Nginx UI 將不會創建系統初始的 acme 使用者,這意味著您無法在此伺服器上申請 SSL 證書。
+
+## Name
+
+- 版本:`>= v2.0.0-beta.23`
+- 類型:`string`
+
+使用此選項自定義本地伺服器的名稱,以在環境指示器中顯示。

+ 31 - 30
go.mod

@@ -5,15 +5,16 @@ go 1.22.0
 require (
 	github.com/0xJacky/pofile v0.2.1
 	github.com/BurntSushi/toml v1.3.2
-	github.com/casdoor/casdoor-go-sdk v0.43.0
+	github.com/caarlos0/env/v11 v11.0.0
+	github.com/casdoor/casdoor-go-sdk v0.45.0
 	github.com/creack/pty v1.1.21
 	github.com/dustin/go-humanize v1.0.1
 	github.com/fatih/color v1.16.0
-	github.com/gin-contrib/static v1.1.1
-	github.com/gin-gonic/gin v1.9.1
+	github.com/gin-contrib/static v1.1.2
+	github.com/gin-gonic/gin v1.10.0
 	github.com/go-acme/lego/v4 v4.16.1
 	github.com/go-co-op/gocron v1.37.0
-	github.com/go-playground/validator/v10 v10.19.0
+	github.com/go-playground/validator/v10 v10.20.0
 	github.com/golang-jwt/jwt v3.2.2+incompatible
 	github.com/google/uuid v1.6.0
 	github.com/gorilla/websocket v1.5.1
@@ -25,12 +26,12 @@ require (
 	github.com/pretty66/websocketproxy v0.0.0-20220507015215-930b3a686308
 	github.com/samber/lo v1.39.0
 	github.com/sashabaranov/go-openai v1.23.0
-	github.com/shirou/gopsutil/v3 v3.24.3
+	github.com/shirou/gopsutil/v3 v3.24.4
 	github.com/shopspring/decimal v1.4.0
 	github.com/spf13/cast v1.6.0
 	github.com/tufanbarisyildirim/gonginx v0.0.0-20240109151651-bb3e845a7a2a
 	go.uber.org/zap v1.27.0
-	golang.org/x/crypto v0.22.0
+	golang.org/x/crypto v0.23.0
 	gopkg.in/guregu/null.v4 v4.0.0
 	gopkg.in/ini.v1 v1.67.0
 	gorm.io/driver/sqlite v1.5.5
@@ -42,14 +43,14 @@ require (
 require (
 	cloud.google.com/go/auth v0.3.0 // indirect
 	cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
-	cloud.google.com/go/compute v1.25.1 // indirect
+	cloud.google.com/go/compute v1.26.0 // indirect
 	cloud.google.com/go/compute/metadata v0.3.0 // indirect
 	filippo.io/edwards25519 v1.1.0 // indirect
 	github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
 	github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 // indirect
-	github.com/Azure/azure-sdk-for-go/sdk/internal v1.6.0 // indirect
+	github.com/Azure/azure-sdk-for-go/sdk/internal v1.7.0 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
 	github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 // indirect
 	github.com/Azure/go-autorest v14.2.0+incompatible // indirect
@@ -69,7 +70,7 @@ require (
 	github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect
 	github.com/StackExchange/wmi v1.2.1 // indirect
 	github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
-	github.com/aliyun/alibaba-cloud-sdk-go v1.62.726 // indirect
+	github.com/aliyun/alibaba-cloud-sdk-go v1.62.731 // indirect
 	github.com/andybalholm/brotli v1.1.0 // indirect
 	github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
 	github.com/aws/aws-sdk-go-v2 v1.26.1 // indirect
@@ -136,7 +137,7 @@ require (
 	github.com/google/gofuzz v1.2.0 // indirect
 	github.com/google/s2a-go v0.1.7 // indirect
 	github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
-	github.com/googleapis/gax-go/v2 v2.12.3 // indirect
+	github.com/googleapis/gax-go/v2 v2.12.4 // indirect
 	github.com/gophercloud/gophercloud v1.11.0 // indirect
 	github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
 	github.com/gorilla/css v1.0.1 // indirect
@@ -171,7 +172,7 @@ require (
 	github.com/labstack/echo/v4 v4.12.0 // indirect
 	github.com/labstack/gommon v0.4.2 // indirect
 	github.com/leodido/go-urn v1.4.0 // indirect
-	github.com/linode/linodego v1.33.0 // indirect
+	github.com/linode/linodego v1.33.1 // indirect
 	github.com/liquidweb/liquidweb-cli v0.7.0 // indirect
 	github.com/liquidweb/liquidweb-go v1.6.4 // indirect
 	github.com/lufia/plan9stats v0.0.0-20240408141607-282e7b5d6b74 // indirect
@@ -202,7 +203,7 @@ require (
 	github.com/oracle/oci-go-sdk v24.3.0+incompatible // indirect
 	github.com/ovh/go-ovh v1.5.1 // indirect
 	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
-	github.com/pelletier/go-toml/v2 v2.2.1 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.2 // indirect
 	github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
@@ -224,22 +225,22 @@ require (
 	github.com/stretchr/testify v1.9.0 // indirect
 	github.com/tdewolff/minify/v2 v2.20.20 // indirect
 	github.com/tdewolff/parse/v2 v2.7.13 // indirect
-	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.910 // indirect
-	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.910 // indirect
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.914 // indirect
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.914 // indirect
 	github.com/tklauser/go-sysconf v0.3.14 // indirect
 	github.com/tklauser/numcpus v0.8.0 // indirect
 	github.com/transip/gotransip/v6 v6.23.0 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.12 // indirect
-	github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a // indirect
+	github.com/ultradns/ultradns-go-sdk v1.6.2-20240501171831-432d643 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
 	github.com/valyala/fasttemplate v1.2.2 // indirect
 	github.com/vinyldns/go-vinyldns v0.9.16 // indirect
 	github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
 	github.com/vultr/govultr/v2 v2.17.2 // indirect
-	github.com/yandex-cloud/go-genproto v0.0.0-20240425114406-68c9b49389a1 // indirect
-	github.com/yandex-cloud/go-sdk v0.0.0-20240425115054-85caccb84041 // indirect
+	github.com/yandex-cloud/go-genproto v0.0.0-20240502080826-5fa7aabf7673 // indirect
+	github.com/yandex-cloud/go-sdk v0.0.0-20240502081211-7639841896bb // indirect
 	github.com/yosssi/ace v0.0.5 // indirect
 	github.com/yusufpapurcu/wmi v1.2.4 // indirect
 	go.opencensus.io v0.24.0 // indirect
@@ -250,23 +251,23 @@ require (
 	go.uber.org/atomic v1.11.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/ratelimit v0.3.1 // indirect
-	golang.org/x/arch v0.7.0 // indirect
-	golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
+	golang.org/x/arch v0.8.0 // indirect
+	golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
 	golang.org/x/mod v0.17.0 // indirect
-	golang.org/x/net v0.24.0 // indirect
-	golang.org/x/oauth2 v0.19.0 // indirect
+	golang.org/x/net v0.25.0 // indirect
+	golang.org/x/oauth2 v0.20.0 // indirect
 	golang.org/x/sync v0.7.0 // indirect
-	golang.org/x/sys v0.19.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
+	golang.org/x/sys v0.20.0 // indirect
+	golang.org/x/text v0.15.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
-	golang.org/x/tools v0.20.0 // indirect
-	google.golang.org/api v0.176.1 // indirect
+	golang.org/x/tools v0.21.0 // indirect
+	google.golang.org/api v0.177.0 // indirect
 	google.golang.org/appengine v1.6.8 // indirect
-	google.golang.org/genproto v0.0.0-20240415180920-8c6c420018be // indirect
-	google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be // indirect
-	google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
+	google.golang.org/genproto v0.0.0-20240506185236-b8a5c65736ae // indirect
+	google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae // indirect
+	google.golang.org/genproto/googleapis/rpc v0.0.0-20240506185236-b8a5c65736ae // indirect
 	google.golang.org/grpc v1.63.2 // indirect
-	google.golang.org/protobuf v1.33.0 // indirect
+	google.golang.org/protobuf v1.34.1 // indirect
 	gopkg.in/fsnotify.v1 v1.4.7 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/ns1/ns1-go.v2 v2.7.13 // indirect
@@ -279,7 +280,7 @@ require (
 	k8s.io/api v0.30.0 // indirect
 	k8s.io/apimachinery v0.30.0 // indirect
 	k8s.io/klog/v2 v2.120.1 // indirect
-	k8s.io/utils v0.0.0-20240423183400-0849a56e8f22 // indirect
+	k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect
 	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
 )

+ 62 - 0
go.sum

@@ -181,6 +181,8 @@ cloud.google.com/go/compute v1.23.4 h1:EBT9Nw4q3zyE7G45Wvv3MzolIrCJEuHys5muLY0wv
 cloud.google.com/go/compute v1.23.4/go.mod h1:/EJMj55asU6kAFnuZET8zqgwgJ9FvXWXOkkfQZa4ioI=
 cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
 cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
+cloud.google.com/go/compute v1.26.0 h1:uHf0NN2nvxl1Gh4QO83yRCOdMK4zivtMS5gv0dEX0hg=
+cloud.google.com/go/compute v1.26.0/go.mod h1:T9RIRap4pVHCGUkVFRJ9hygT3KCXjip41X1GgWtBBII=
 cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU=
 cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
 cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
@@ -630,6 +632,8 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aM
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.6.0 h1:sUFnFjzDUie80h24I7mrKtwCKgLY9L8h5Tp2x9+TWqk=
 github.com/Azure/azure-sdk-for-go/sdk/internal v1.6.0/go.mod h1:52JbnQTp15qg5mRkMBHwp0j0ZFwHJ42Sx3zVV5RE9p0=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.7.0 h1:rTfKOCZGy5ViVrlA74ZPE99a+SgoEE2K/yg3RyW9dFA=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.7.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0=
 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v2 v2.0.0 h1:PTFGRSlMKCQelWwxUyYVEUqseBJVemLyqWJjvMyt0do=
@@ -709,6 +713,8 @@ github.com/aliyun/alibaba-cloud-sdk-go v1.62.676 h1:ChWMMr76tXrRh3ximWQyg83EROEf
 github.com/aliyun/alibaba-cloud-sdk-go v1.62.676/go.mod h1:CJJYa1ZMxjlN/NbXEwmejEnBkhi0DV+Yb3B2lxf+74o=
 github.com/aliyun/alibaba-cloud-sdk-go v1.62.726 h1:vCatyVjwoeS9PSyd6PnPBjQnxR1b8eyFhbJ0obHE6Ok=
 github.com/aliyun/alibaba-cloud-sdk-go v1.62.726/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.731 h1:Zo2TSHK/E5Q+uWPVnFyaQ26ODrY/NpJzsiQnQKfFIiY=
+github.com/aliyun/alibaba-cloud-sdk-go v1.62.731/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
 github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
 github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
 github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
@@ -811,10 +817,14 @@ github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4
 github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7FVlAwDAVw=
 github.com/c-bata/go-prompt v0.2.6/go.mod h1:/LMAke8wD2FsNu9EXNdHxNLbd9MedkPnCdfpU9wwHfY=
 github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
+github.com/caarlos0/env/v11 v11.0.0 h1:ZIlkOjuL3xoZS0kmUJlF74j2Qj8GMOq3CDLX/Viak8Q=
+github.com/caarlos0/env/v11 v11.0.0/go.mod h1:2RC3HQu8BQqtEK3V4iHPxj0jOdWdbPpWJ6pOueeU1xM=
 github.com/casdoor/casdoor-go-sdk v0.35.1 h1:vOGBjHQPw7U5BbnkJgX6/ZUf+PEix1wv4greuNzDCUY=
 github.com/casdoor/casdoor-go-sdk v0.35.1/go.mod h1:hVSgmSdwTCsBEJNt9r2K5aLVsoeMc37/N4Zzescy5SA=
 github.com/casdoor/casdoor-go-sdk v0.43.0 h1:iKDEJlHYwFNY70iWhdVrTTIKqdoqthv3Rxv4x1+rWko=
 github.com/casdoor/casdoor-go-sdk v0.43.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4=
+github.com/casdoor/casdoor-go-sdk v0.45.0 h1:pww5N1V686ehebo8tHaCczdUAAVX9inwUJCCjlZnPPA=
+github.com/casdoor/casdoor-go-sdk v0.45.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4=
 github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
 github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
@@ -946,9 +956,13 @@ github.com/gin-contrib/static v0.0.1 h1:JVxuvHPuUfkoul12N7dtQw7KRn/pSMq7Ue1Va9Sw
 github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs=
 github.com/gin-contrib/static v1.1.1 h1:XEvBd4DDLG1HBlyPBQU1XO8NlTpw6mgdqcPteetYA5k=
 github.com/gin-contrib/static v1.1.1/go.mod h1:yRGmar7+JYvbMLRPIi4H5TVVSBwULfT9vetnVD0IO74=
+github.com/gin-contrib/static v1.1.2 h1:c3kT4bFkUJn2aoRU3s6XnMjJT8J6nNWJkR0NglqmlZ4=
+github.com/gin-contrib/static v1.1.2/go.mod h1:Fw90ozjHCmZBWbgrsqrDvO28YbhKEKzKp8GixhR4yLw=
 github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
 github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
 github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
+github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
 github.com/go-acme/lego/v4 v4.15.0 h1:A7MHEU3b+TDFqhC/HmzMJnzPbyeaYvMZQBbqgvbThhU=
 github.com/go-acme/lego/v4 v4.15.0/go.mod h1:eeGhjW4zWT7Ccqa3sY7ayEqFLCAICx+mXgkMHKIkLxg=
 github.com/go-acme/lego/v4 v4.16.1 h1:JxZ93s4KG0jL27rZ30UsIgxap6VGzKuREsSkkyzeoCQ=
@@ -1012,6 +1026,8 @@ github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ
 github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
 github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
 github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
+github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
 github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8=
 github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
 github.com/go-resty/resty/v2 v2.12.0 h1:rsVL8P90LFvkUYq/V5BTVe203WfRIU4gvcf+yfzJzGA=
@@ -1186,6 +1202,8 @@ github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56
 github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU=
 github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
 github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
+github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg=
+github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI=
 github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
 github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
 github.com/gophercloud/gophercloud v1.3.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
@@ -1392,6 +1410,8 @@ github.com/linode/linodego v1.28.0 h1:lzxxJebsYg5cCWRNDLyL2StW3sfMyAwf/FYfxFjFrl
 github.com/linode/linodego v1.28.0/go.mod h1:5oAsx+uinHtVo6U77nXXXtox7MWzUW6aEkTOKXxA9uo=
 github.com/linode/linodego v1.33.0 h1:cX2FYry7r6CA1ujBMsdqiM4VhvIQtnWsOuVblzfBhCw=
 github.com/linode/linodego v1.33.0/go.mod h1:dSJJgIwqZCF5wnpuC6w5cyIbRtcexAm7uVvuJopGB40=
+github.com/linode/linodego v1.33.1 h1:GcI7ozlHHzZbfthD8edLNInhHjQ452iCwtphza+FJGc=
+github.com/linode/linodego v1.33.1/go.mod h1:rEjoJQACp1gKZn9LfxtCJPwS8ri/+h2B3ScJrgBPPdI=
 github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
 github.com/liquidweb/go-lwApi v0.0.5/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs=
 github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ=
@@ -1575,6 +1595,8 @@ github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOS
 github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
 github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg=
 github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
+github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
+github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
 github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
 github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
 github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
@@ -1680,6 +1702,8 @@ github.com/shirou/gopsutil/v3 v3.24.1 h1:R3t6ondCEvmARp3wxODhXMTLC/klMa87h2PHUw5
 github.com/shirou/gopsutil/v3 v3.24.1/go.mod h1:UU7a2MSBQa+kW1uuDq8DeEBS8kmrnQwsv2b5O513rwU=
 github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE=
 github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg=
+github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
+github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
 github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
 github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
 github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -1774,10 +1798,14 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.857 h1:6TxC
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.857/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.910 h1:u+rAnHhLixQaNYy8vtnuClj4kYWs77VAHEbi9jl8k/4=
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.910/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.914 h1:CByhsxUgtDkGg6cxzwdgfMAZ4yiBEn1RIXYyq62nwwI=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.914/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.857 h1:EYkAGRkx8q+e+X190mCc6e5z+sHkb50iw7ZHGgoc2c0=
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.857/go.mod h1:4nCgJQrj+kq8IW4bqilFqlG6b9hPb2F9Y2WpGMOazE0=
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.910 h1:dO8s0rMnCO6FkP9LocS2zzn0AmTnQsjFcBhH55e8mOM=
 github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.910/go.mod h1:SzZONdscLNC9REG/GV7prP42YNAQ3ft8ovurtGNWTec=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.914 h1:R13gTV0eWyYBhUnban4hQczfhKD5MT7IsMgiwOCMJaY=
+github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.914/go.mod h1:yV15O8egeJgEjab302lvaqDUS8poWajUZKf8DXgW244=
 github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
 github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
 github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
@@ -1804,6 +1832,8 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
 github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a h1:w4PK5/N9kq8PfNxBv8a5t1bqlYRrVT7XzT7iTPTtiPk=
 github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a/go.mod h1:Xwz7o+ExFtxR/i0aJDnTXuiccQJlOxDgNe6FsZC4TzQ=
+github.com/ultradns/ultradns-go-sdk v1.6.2-20240501171831-432d643 h1:Y2gOdFNdP0QrXN7HkhrT42686bxBmDPqq5Xu8RgeU2s=
+github.com/ultradns/ultradns-go-sdk v1.6.2-20240501171831-432d643/go.mod h1:mqka31zT/P4yfNKj1qbOXUqamham/YO05GgUc/dOrl8=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
 github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
@@ -1829,10 +1859,14 @@ github.com/yandex-cloud/go-genproto v0.0.0-20240205090910-007acb101be5 h1:HVYr9E
 github.com/yandex-cloud/go-genproto v0.0.0-20240205090910-007acb101be5/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
 github.com/yandex-cloud/go-genproto v0.0.0-20240425114406-68c9b49389a1 h1:VDGcTxVXpQ6N2sKdKVzSrt1Rp6xm4thrCH5TeqMoWtY=
 github.com/yandex-cloud/go-genproto v0.0.0-20240425114406-68c9b49389a1/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
+github.com/yandex-cloud/go-genproto v0.0.0-20240502080826-5fa7aabf7673 h1:N4xWIsknZx9pK0o7tJ8GGNj4JdHzGyN3y4g5fnpO9rw=
+github.com/yandex-cloud/go-genproto v0.0.0-20240502080826-5fa7aabf7673/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
 github.com/yandex-cloud/go-sdk v0.0.0-20240205112400-7fabb70436b0 h1:dfin4M6ZEpoBAbaxM8RJ0G0NsSE/ZUh8t4+sFRJcPAI=
 github.com/yandex-cloud/go-sdk v0.0.0-20240205112400-7fabb70436b0/go.mod h1:v0wy5uhpD/Ts0YfY6tg4CpWvv1+sse6ovKdF/KSgXV8=
 github.com/yandex-cloud/go-sdk v0.0.0-20240425115054-85caccb84041 h1:CJFVkjTl0Jxgx7ha9rNgG4y7YCcZqCl1lZDkk8Kw3ac=
 github.com/yandex-cloud/go-sdk v0.0.0-20240425115054-85caccb84041/go.mod h1:gf3YxmV6R09JmNxOQrfoeV8mRIXqr7EQ7Yh7sAG2UhA=
+github.com/yandex-cloud/go-sdk v0.0.0-20240502081211-7639841896bb h1:neUOrst9RECDTfeCpjaFRrDY93vNpFsrhvb4cbYUPsg=
+github.com/yandex-cloud/go-sdk v0.0.0-20240502081211-7639841896bb/go.mod h1:M54BPoNxIcDFSlTe0xHmDPCJVJmWxZp8MOIcjlINiL8=
 github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA=
 github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
 github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
@@ -1913,6 +1947,8 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
 golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
 golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
+golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
+golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -1940,6 +1976,8 @@ golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
 golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
 golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
 golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1959,6 +1997,8 @@ golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 h1:/RIbNt/Zr7rVhIkQhooTxCxFc
 golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
 golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
 golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
+golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@@ -2088,6 +2128,8 @@ golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
 golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
 golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -2121,6 +2163,8 @@ golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
 golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
 golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
 golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
+golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
+golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -2263,6 +2307,8 @@ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
 golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
 golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -2297,6 +2343,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2383,6 +2431,8 @@ golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
 golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
 golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
 golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
+golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw=
+golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -2461,6 +2511,8 @@ google.golang.org/api v0.162.0 h1:Vhs54HkaEpkMBdgGdOT2P6F0csGG/vxDS0hWHJzmmps=
 google.golang.org/api v0.162.0/go.mod h1:6SulDkfoBIg4NFmCuZ39XeeAgSHCPecfSUuDyYlAHs0=
 google.golang.org/api v0.176.1 h1:DJSXnV6An+NhJ1J+GWtoF2nHEuqB1VNoTfnIbjNvwD4=
 google.golang.org/api v0.176.1/go.mod h1:j2MaSDYcvYV1lkZ1+SMW4IeF90SrEyFA+tluDYWRrFg=
+google.golang.org/api v0.177.0 h1:8a0p/BbPa65GlqGWtUKxot4p0TV8OGOfyTjtmkXNXmk=
+google.golang.org/api v0.177.0/go.mod h1:srbhue4MLjkjbkux5p3dw/ocYOSZTaIEvf7bCOnFQDw=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2608,14 +2660,20 @@ google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 h1:g/4bk7P6TPMkAUb
 google.golang.org/genproto v0.0.0-20240205150955-31a09d347014/go.mod h1:xEgQu1e4stdSSsxPDK8Azkrk/ECl5HvdPf6nbZrTS5M=
 google.golang.org/genproto v0.0.0-20240415180920-8c6c420018be h1:g4aX8SUFA8V5F4LrSY5EclyGYw1OZN4HS1jTyjB9ZDc=
 google.golang.org/genproto v0.0.0-20240415180920-8c6c420018be/go.mod h1:FeSdT5fk+lkxatqJP38MsUicGqHax5cLtmy/6TAuxO4=
+google.golang.org/genproto v0.0.0-20240506185236-b8a5c65736ae h1:HjgkYCl6cWQEKSHkpUp4Q8VB74swzyBwTz1wtTzahm0=
+google.golang.org/genproto v0.0.0-20240506185236-b8a5c65736ae/go.mod h1:i4np6Wrjp8EujFAUn0CM0SH+iZhY1EbrfzEIJbFkHFM=
 google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014 h1:x9PwdEgd11LgK+orcck69WVRo7DezSO4VUMPI4xpc8A=
 google.golang.org/genproto/googleapis/api v0.0.0-20240205150955-31a09d347014/go.mod h1:rbHMSEDyoYX62nRVLOCc4Qt1HbsdytAYoVwgjiOhF3I=
 google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be h1:Zz7rLWqp0ApfsR/l7+zSHhY3PMiH2xqgxlfYfAfNpoU=
 google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be/go.mod h1:dvdCTIoAGbkWbcIKBniID56/7XHTt6WfxXNMxuziJ+w=
+google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae h1:AH34z6WAGVNkllnKs5raNq3yRq93VnjBG6rpfub/jYk=
+google.golang.org/genproto/googleapis/api v0.0.0-20240506185236-b8a5c65736ae/go.mod h1:FfiGhwUm6CJviekPrc0oJ+7h29e+DmWU6UtjX0ZvI7Y=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 h1:FSL3lRCkhaPFxqi0s9o+V4UI2WTzAVOvkgbd4kVV4Wg=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240506185236-b8a5c65736ae h1:c55+MER4zkBS14uJhSZMGGmya0yJx5iHV4x/fpOSNRk=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240506185236-b8a5c65736ae/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -2682,6 +2740,8 @@ google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
 google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
 google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -2793,6 +2853,8 @@ k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCf
 k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 k8s.io/utils v0.0.0-20240423183400-0849a56e8f22 h1:ao5hUqGhsqdm+bYbjH/pRkCs0unBGe9UyDahzs9zQzQ=
 k8s.io/utils v0.0.0-20240423183400-0849a56e8f22/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
+k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
 lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
 lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
 modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=

+ 78 - 70
internal/analytic/node.go

@@ -16,26 +16,26 @@ import (
 )
 
 type NodeInfo struct {
-    NodeRuntimeInfo upgrader.RuntimeInfo `json:"node_runtime_info"`
-    Version         string               `json:"version"`
-    CPUNum          int                  `json:"cpu_num"`
-    MemoryTotal     string               `json:"memory_total"`
-    DiskTotal       string               `json:"disk_total"`
+	NodeRuntimeInfo upgrader.RuntimeInfo `json:"node_runtime_info"`
+	Version         string               `json:"version"`
+	CPUNum          int                  `json:"cpu_num"`
+	MemoryTotal     string               `json:"memory_total"`
+	DiskTotal       string               `json:"disk_total"`
 }
 
 type NodeStat struct {
-    AvgLoad       *load.AvgStat      `json:"avg_load"`
-    CPUPercent    float64            `json:"cpu_percent"`
-    MemoryPercent float64            `json:"memory_percent"`
-    DiskPercent   float64            `json:"disk_percent"`
-    Network       net.IOCountersStat `json:"network"`
-    Status        bool               `json:"status"`
-    ResponseAt    time.Time          `json:"response_at"`
+	AvgLoad       *load.AvgStat      `json:"avg_load"`
+	CPUPercent    float64            `json:"cpu_percent"`
+	MemoryPercent float64            `json:"memory_percent"`
+	DiskPercent   float64            `json:"disk_percent"`
+	Network       net.IOCountersStat `json:"network"`
+	Status        bool               `json:"status"`
+	ResponseAt    time.Time          `json:"response_at"`
 }
 
 type Node struct {
-    EnvironmentID int `json:"environment_id,omitempty"`
-    *model.Environment
+	EnvironmentID int `json:"environment_id,omitempty"`
+	*model.Environment
 	NodeStat
 	NodeInfo
 }
@@ -47,66 +47,74 @@ type TNodeMap map[int]*Node
 var NodeMap TNodeMap
 
 func init() {
-    NodeMap = make(TNodeMap)
+	NodeMap = make(TNodeMap)
 }
 
 func GetNode(env *model.Environment) (n *Node) {
-    if env == nil {
-        logger.Error("env is nil")
-        return
-    }
-    n, ok := NodeMap[env.ID]
-    if !ok {
-        n = &Node{}
-    }
-    n.Environment = env
-    return n
+	if env == nil {
+		// this should never happen
+		logger.Error("env is nil")
+		return
+	}
+	if !env.Enabled {
+		return &Node{
+			Environment: env,
+		}
+	}
+	n, ok := NodeMap[env.ID]
+	if !ok {
+		n = &Node{}
+	}
+	n.Environment = env
+	return n
 }
 
 func InitNode(env *model.Environment) (n *Node) {
-    n = &Node{
-        Environment: env,
-    }
-
-    u, err := url.JoinPath(env.URL, "/api/node")
-
-    if err != nil {
-        logger.Error(err)
-        return
-    }
-
-    if err != nil {
-        logger.Error(err)
-        return
-    }
-    client := http.Client{
-        Transport: &http.Transport{
-            TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
-        },
-    }
-    req, err := http.NewRequest("GET", u, nil)
-    req.Header.Set("X-Node-Secret", env.Token)
-
-    resp, err := client.Do(req)
-
-    if err != nil {
-        logger.Error(err)
-        return
-    }
-
-    defer resp.Body.Close()
-    bytes, _ := io.ReadAll(resp.Body)
-
-    if resp.StatusCode != 200 {
-        logger.Error(string(bytes))
-        return
-    }
-
-    err = json.Unmarshal(bytes, &n.NodeInfo)
-    if err != nil {
-        logger.Error(err)
-        return
-    }
-
-    return
+	n = &Node{
+		Environment: env,
+	}
+
+	u, err := url.JoinPath(env.URL, "/api/node")
+
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+
+	client := http.Client{
+		Transport: &http.Transport{
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+		},
+	}
+
+	req, err := http.NewRequest("GET", u, nil)
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+
+	req.Header.Set("X-Node-Secret", env.Token)
+
+	resp, err := client.Do(req)
+
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+
+	defer resp.Body.Close()
+	bytes, _ := io.ReadAll(resp.Body)
+
+	if resp.StatusCode != http.StatusOK {
+		logger.Error(string(bytes))
+		return
+	}
+
+	err = json.Unmarshal(bytes, &n.NodeInfo)
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+
+	return
 }

+ 71 - 0
internal/cluster/cluster.go

@@ -0,0 +1,71 @@
+package cluster
+
+import (
+	"github.com/0xJacky/Nginx-UI/internal/logger"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"gorm.io/gen/field"
+	"net/url"
+	"strings"
+)
+
+func RegisterPredefinedNodes() {
+	if len(settings.ClusterSettings.Node) == 0 {
+		return
+	}
+
+	q := query.Environment
+	for _, nodeUrl := range settings.ClusterSettings.Node {
+		func() {
+			node, err := parseNodeUrl(nodeUrl)
+			if err != nil {
+				logger.Error(nodeUrl, err)
+				return
+			}
+
+			if node.Name == "" {
+				logger.Error(nodeUrl, "Node name is required")
+				return
+			}
+
+			if node.URL == "" {
+				logger.Error(nodeUrl, "Node URL is required")
+				return
+			}
+
+			if node.Token == "" {
+				logger.Error(nodeUrl, "Node Token is required")
+				return
+			}
+
+			_, err = q.Where(q.URL.Eq(node.URL)).
+				Attrs(field.Attrs(node)).
+				FirstOrCreate()
+			if err != nil {
+				logger.Error(node.URL, err)
+			}
+		}()
+	}
+}
+
+func parseNodeUrl(nodeUrl string) (env *model.Environment, err error) {
+	u, err := url.Parse(nodeUrl)
+	if err != nil {
+		return
+	}
+	var sb strings.Builder
+	sb.WriteString(u.Scheme)
+	sb.WriteString("://")
+	sb.WriteString(u.Host)
+	sb.WriteString(u.Path)
+
+	env = &model.Environment{
+		Name:    u.Query().Get("name"),
+		URL:     sb.String(),
+		Token:   u.Query().Get("node_secret"),
+		Enabled: u.Query().Get("enabled") == "true",
+	}
+
+	return
+}

+ 47 - 0
internal/cluster/cluster_test.go

@@ -0,0 +1,47 @@
+package cluster
+
+import (
+	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func Test_parseNodeUrl(t *testing.T) {
+	settings.Init("../../app.example.ini")
+	t.Log(settings.ClusterSettings.Node)
+	node := settings.ClusterSettings.Node[0]
+
+	env, err := parseNodeUrl(node)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assert.Equal(t, "node1", env.Name)
+	assert.Equal(t, "http://10.0.0.1:9000", env.URL)
+	assert.Equal(t, "my-node-secret", env.Token)
+	assert.Equal(t, true, env.Enabled)
+
+	node = settings.ClusterSettings.Node[1]
+
+	env, err = parseNodeUrl(node)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assert.Equal(t, "node2", env.Name)
+	assert.Equal(t, "http://10.0.0.2:9000", env.URL)
+	assert.Equal(t, "my-node-secret", env.Token)
+	assert.Equal(t, true, env.Enabled)
+
+	node = settings.ClusterSettings.Node[2]
+
+	env, err = parseNodeUrl(node)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assert.Equal(t, "node3", env.Name)
+	assert.Equal(t, "http://10.0.0.3", env.URL)
+	assert.Equal(t, "my-node-secret", env.Token)
+	assert.Equal(t, true, env.Enabled)
+}

+ 0 - 23
internal/environment/environment.go

@@ -1,23 +0,0 @@
-package environment
-
-import (
-	"github.com/0xJacky/Nginx-UI/internal/analytic"
-	"github.com/0xJacky/Nginx-UI/query"
-)
-
-func RetrieveEnvironmentList() (envs []*analytic.Node, err error) {
-	envQuery := query.Environment
-
-	data, err := envQuery.Find()
-	if err != nil {
-		return
-	}
-
-	for _, v := range data {
-		t := analytic.GetNode(v)
-
-		envs = append(envs, t)
-	}
-
-	return
-}

+ 8 - 1
internal/kernal/boot.go

@@ -3,6 +3,7 @@ package kernal
 import (
 	"github.com/0xJacky/Nginx-UI/internal/analytic"
 	"github.com/0xJacky/Nginx-UI/internal/cert"
+	"github.com/0xJacky/Nginx-UI/internal/cluster"
 	"github.com/0xJacky/Nginx-UI/internal/logger"
 	"github.com/0xJacky/Nginx-UI/internal/logrotate"
 	"github.com/0xJacky/Nginx-UI/internal/validation"
@@ -41,8 +42,10 @@ func Boot() {
 
 func InitAfterDatabase() {
 	syncs := []func(){
+		registerPredefinedUser,
 		cert.InitRegister,
 		InitCronJobs,
+		cluster.RegisterPredefinedNodes,
 		analytic.RetrieveNodesStatus,
 	}
 
@@ -60,6 +63,11 @@ func recovery() {
 }
 
 func InitDatabase() {
+	// Skip install
+	if settings.ServerSettings.SkipInstallation {
+		skipInstall()
+	}
+
 	if "" != settings.ServerSettings.JwtSecret {
 		db := model.Init()
 		query.Init(db)
@@ -72,7 +80,6 @@ func InitNodeSecret() {
 	if "" == settings.ServerSettings.NodeSecret {
 		logger.Warn("NodeSecret is empty, generating...")
 		settings.ServerSettings.NodeSecret = uuid.New().String()
-		settings.ReflectFrom()
 
 		err := settings.Save()
 		if err != nil {

+ 74 - 0
internal/kernal/skip_install.go

@@ -0,0 +1,74 @@
+package kernal
+
+import (
+	"github.com/0xJacky/Nginx-UI/internal/logger"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/caarlos0/env/v11"
+	"github.com/google/uuid"
+	"github.com/pkg/errors"
+	"golang.org/x/crypto/bcrypt"
+	"gorm.io/gorm"
+)
+
+type predefinedUser struct {
+	Name     string `json:"name"`
+	Password string `json:"password"`
+}
+
+func skipInstall() {
+	logger.Info("Skip installation mode enabled")
+
+	if settings.ServerSettings.JwtSecret == "" {
+		settings.ServerSettings.JwtSecret = uuid.New().String()
+	}
+
+	if settings.ServerSettings.NodeSecret == "" {
+		settings.ServerSettings.NodeSecret = uuid.New().String()
+		logger.Infof("NodeSecret: %s", settings.ServerSettings.NodeSecret)
+	}
+
+	err := settings.Save()
+	if err != nil {
+		logger.Fatal(err)
+	}
+}
+
+func registerPredefinedUser() {
+	// when skip installation mode is enabled, the predefined user will be created
+	if !settings.ServerSettings.SkipInstallation {
+		return
+	}
+	pUser := &predefinedUser{}
+
+	err := env.ParseWithOptions(pUser, env.Options{
+		Prefix:                "NGINX_UI_PREDEFINED_USER_",
+		UseFieldNameByDefault: true,
+	})
+
+	if err != nil {
+		logger.Fatal(err)
+	}
+
+	u := query.Auth
+
+	_, err = u.First()
+
+	// Only effect when there is no user in the database
+	if !errors.Is(err, gorm.ErrRecordNotFound) || pUser.Name == "" || pUser.Password == "" {
+		return
+	}
+
+	// Create a new user with the predefined name and password
+	pwd, _ := bcrypt.GenerateFromPassword([]byte(pUser.Password), bcrypt.DefaultCost)
+
+	err = u.Create(&model.Auth{
+		Name:     pUser.Name,
+		Password: string(pwd),
+	})
+
+	if err != nil {
+		logger.Error(err)
+	}
+}

+ 45 - 22
internal/nginx/nginx.go

@@ -3,27 +3,18 @@ package nginx
 import (
 	"github.com/0xJacky/Nginx-UI/settings"
 	"os/exec"
+	"sync"
+	"time"
 )
 
-func execShell(cmd string) (out string) {
-	bytes, err := exec.Command("/bin/sh", "-c", cmd).CombinedOutput()
-	out = string(bytes)
-	if err != nil {
-		out += " " + err.Error()
-	}
-	return
-}
-
-func execCommand(name string, cmd ...string) (out string) {
-	bytes, err := exec.Command(name, cmd...).CombinedOutput()
-	out = string(bytes)
-	if err != nil {
-		out += " " + err.Error()
-	}
-	return
-}
+var (
+	mutex      sync.Mutex
+	lastOutput string
+)
 
 func TestConf() (out string) {
+	mutex.Lock()
+	defer mutex.Unlock()
 	if settings.NginxSettings.TestConfigCmd != "" {
 		out = execShell(settings.NginxSettings.TestConfigCmd)
 
@@ -36,6 +27,8 @@ func TestConf() (out string) {
 }
 
 func Reload() (out string) {
+	mutex.Lock()
+	defer mutex.Unlock()
 	if settings.NginxSettings.ReloadCmd != "" {
 		out = execShell(settings.NginxSettings.ReloadCmd)
 		return
@@ -46,9 +39,15 @@ func Reload() (out string) {
 	return
 }
 
-func Restart() (out string) {
+func Restart() {
+	mutex.Lock()
+	defer mutex.Unlock()
+
+	// fix(docker): nginx restart always output network error
+	time.Sleep(500 * time.Millisecond)
+
 	if settings.NginxSettings.RestartCmd != "" {
-		out = execShell(settings.NginxSettings.RestartCmd)
+		lastOutput = execShell(settings.NginxSettings.RestartCmd)
 
 		return
 	}
@@ -56,15 +55,39 @@ func Restart() (out string) {
 	pidPath := GetPIDPath()
 	daemon := GetSbinPath()
 
-	out = execCommand("start-stop-daemon", "--stop", "--quiet", "--oknodo", "--retry=TERM/30/KILL/5", "--pidfile", pidPath)
+	lastOutput = execCommand("start-stop-daemon", "--stop", "--quiet", "--oknodo", "--retry=TERM/30/KILL/5", "--pidfile", pidPath)
 
 	if daemon == "" {
-		out += execCommand("nginx")
+		lastOutput += execCommand("nginx")
 
 		return
 	}
 
-	out += execCommand("start-stop-daemon", "--start", "--quiet", "--pidfile", pidPath, "--exec", daemon)
+	lastOutput += execCommand("start-stop-daemon", "--start", "--quiet", "--pidfile", pidPath, "--exec", daemon)
 
 	return
 }
+
+func GetLastOutput() string {
+	mutex.Lock()
+	defer mutex.Unlock()
+	return lastOutput
+}
+
+func execShell(cmd string) (out string) {
+	bytes, err := exec.Command("/bin/sh", "-c", cmd).CombinedOutput()
+	out = string(bytes)
+	if err != nil {
+		out += " " + err.Error()
+	}
+	return
+}
+
+func execCommand(name string, cmd ...string) (out string) {
+	bytes, err := exec.Command(name, cmd...).CombinedOutput()
+	out = string(bytes)
+	if err != nil {
+		out += " " + err.Error()
+	}
+	return
+}

+ 1 - 0
model/environment.go

@@ -10,6 +10,7 @@ type Environment struct {
 	Name          string `json:"name"`
 	URL           string `json:"url"`
 	Token         string `json:"token"`
+	Enabled       bool   `json:"enabled" gorm:"default:true"`
 	OperationSync bool   `json:"operation_sync"`
 	SyncApiRegex  string `json:"sync_api_regex"`
 }

+ 4 - 4
resources/demo/app.ini

@@ -1,16 +1,16 @@
 [server]
 HttpPort = 9000
 RunMode = release
-JwtSecret = 2E1CE615-BB15-44F5-B5BE-6B5DA3581D0F
+JwtSecret = 6EEB622E-5C73-4082-AF76-7BAC337772F2
 Email = test@jackyu.cn
 HTTPChallengePort = 9180
 StartCmd = bash
-NodeSecret = fdc7764f-92d2-454c-9640-6a09be121139
+NodeSecret = 57D079F2-CA8B-412A-B5C0-FDA291C13391
 Demo = true
 
 [nginx]
-AccessLogPath = /var/log/nginx/access.local.log
-ErrorLogPath = /var/log/nginx/error.local.log
+AccessLogPath =
+ErrorLogPath =
 
 [openai]
 Model   = gpt-3.5-turbo

+ 3 - 0
resources/docker/nginx-ui.run

@@ -0,0 +1,3 @@
+#!/command/with-contenv sh
+env
+nginx-ui --config /etc/nginx-ui/app.ini

+ 3 - 2
resources/docker/nginx.conf

@@ -5,11 +5,13 @@ error_log  /var/log/nginx/error.log notice;
 error_log  /var/log/nginx/error.local.log notice;
 pid        /var/run/nginx.pid;
 
-
 events {
     worker_connections  1024;
 }
 
+stream {
+    include /etc/nginx/streams-enabled/*.conf;
+}
 
 http {
     include       /etc/nginx/mime.types;
@@ -20,7 +22,6 @@ http {
                       '"$http_user_agent" "$http_x_forwarded_for"';
 
     access_log  /var/log/nginx/access.log  main;
-    access_log  /var/log/nginx/access.local.log  main;
 
     sendfile        on;
     #tcp_nopush     on;

+ 0 - 10
resources/docker/start.sh

@@ -1,10 +0,0 @@
-#!/bin/bash
-
-if [ "$(ls -A /etc/nginx)" = "" ]; then
-    echo "Initialing Nginx config dir"
-    cp -rp /usr/etc/nginx/* /etc/nginx/
-    echo "Initialed Nginx config dir"
-fi
-
-nginx &
-/app/nginx-ui --config /etc/nginx-ui/app.ini

+ 19 - 0
settings/cluster.go

@@ -0,0 +1,19 @@
+package settings
+
+type Cluster struct {
+	Node []string `ini:",,allowshadow"`
+}
+
+var ClusterSettings = Cluster{
+	Node: []string{},
+}
+
+func ReloadCluster() (err error) {
+	err = load()
+
+	if err != nil {
+		return err
+	}
+
+	return mapTo("cluster", &ClusterSettings)
+}

+ 15 - 0
settings/cluster_test.go

@@ -0,0 +1,15 @@
+package settings
+
+import (
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestCluster(t *testing.T) {
+	Init("../app.example.ini")
+
+	assert.Equal(t, []string{
+		"http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true",
+		"http://10.0.0.2:9000?name=node2&node_secret=my-node-secret&enabled=true",
+	}, ClusterSettings.Node)
+}

+ 2 - 0
settings/server.go

@@ -20,6 +20,8 @@ type Server struct {
 	GithubProxy          string   `json:"github_proxy" binding:"omitempty,url"`
 	CertRenewalInterval  int      `json:"cert_renewal_interval" binding:"min=7,max=21"`
 	RecursiveNameservers []string `json:"recursive_nameservers" binding:"omitempty,dive,hostname_port"`
+	SkipInstallation     bool     `json:"skip_installation" protected:"true"`
+	Name                 string   `json:"name" binding:"omitempty,alpha_num_dash_dot"`
 }
 
 func (s *Server) GetCADir() string {

+ 71 - 18
settings/settings.go

@@ -1,21 +1,24 @@
 package settings
 
 import (
+	"github.com/caarlos0/env/v11"
 	"github.com/spf13/cast"
 	"gopkg.in/ini.v1"
 	"log"
+	"os"
+	"reflect"
 	"strings"
 	"time"
 )
 
-var Conf *ini.File
-
 var (
 	buildTime    string
 	LastModified string
-)
 
-var ConfPath string
+	Conf      *ini.File
+	ConfPath  string
+	EnvPrefix = "NGINX_UI_"
+)
 
 var sections = map[string]interface{}{
 	"server":    &ServerSettings,
@@ -23,6 +26,7 @@ var sections = map[string]interface{}{
 	"openai":    &OpenAISettings,
 	"casdoor":   &CasdoorSettings,
 	"logrotate": &LogrotateSettings,
+	"cluster":   &ClusterSettings,
 }
 
 func init() {
@@ -35,34 +39,81 @@ func Init(confPath string) {
 	Setup()
 }
 
+func load() (err error) {
+	Conf, err = ini.LoadSources(ini.LoadOptions{
+		Loose:        true,
+		AllowShadows: true,
+	}, ConfPath)
+
+	return
+}
+
 func Setup() {
-	var err error
-	Conf, err = ini.LooseLoad(ConfPath)
+	err := load()
+
 	if err != nil {
-		log.Fatalf("setting.Setup: %v\n", err)
+		log.Fatalf("settings.Setup: %v\n", err)
 	}
+
 	MapTo()
+
+	parseEnv(&ServerSettings, "SERVER_")
+	parseEnv(&NginxSettings, "NGINX_")
+	parseEnv(&OpenAISettings, "OPENAI_")
+	parseEnv(&CasdoorSettings, "CASDOOR_")
+	parseEnv(&LogrotateSettings, "LOGROTATE_")
+
+	// if in official docker, set the restart cmd of nginx to "nginx -s stop",
+	// then the supervisor of s6-overlay will start the nginx again.
+	if cast.ToBool(os.Getenv("NGINX_UI_OFFICIAL_DOCKER")) {
+		NginxSettings.RestartCmd = "nginx -s stop"
+	}
+
+	err = Save()
+	if err != nil {
+		log.Fatalf("settings.Setup: %v\n", err)
+	}
 }
 
 func MapTo() {
 	for k, v := range sections {
-		mapTo(k, v)
+		err := mapTo(k, v)
+
+		if err != nil {
+			log.Fatalf("Cfg.MapTo %s err: %v", k, err)
+		}
 	}
 }
 
-func ReflectFrom() {
+func Save() (err error) {
 	for k, v := range sections {
 		reflectFrom(k, v)
 	}
-}
 
-func mapTo(section string, v interface{}) {
-	err := Conf.Section(section).MapTo(v)
+	err = Conf.SaveTo(ConfPath)
 	if err != nil {
-		log.Fatalf("Cfg.MapTo %s err: %v", section, err)
+		return
+	}
+	return
+}
+
+func ProtectedFill(targetSettings interface{}, newSettings interface{}) {
+	s := reflect.TypeOf(targetSettings).Elem()
+	vt := reflect.ValueOf(targetSettings).Elem()
+	vn := reflect.ValueOf(newSettings).Elem()
+
+	// copy the values from new to target settings if it is not protected
+	for i := 0; i < s.NumField(); i++ {
+		if s.Field(i).Tag.Get("protected") != "true" {
+			vt.Field(i).Set(vn.Field(i))
+		}
 	}
 }
 
+func mapTo(section string, v interface{}) error {
+	return Conf.Section(section).MapTo(v)
+}
+
 func reflectFrom(section string, v interface{}) {
 	err := Conf.Section(section).ReflectFrom(v)
 	if err != nil {
@@ -70,11 +121,13 @@ func reflectFrom(section string, v interface{}) {
 	}
 }
 
-func Save() (err error) {
-	err = Conf.SaveTo(ConfPath)
+func parseEnv(ptr interface{}, prefix string) {
+	err := env.ParseWithOptions(ptr, env.Options{
+		Prefix:                EnvPrefix + prefix,
+		UseFieldNameByDefault: true,
+	})
+
 	if err != nil {
-		return
+		log.Fatalf("settings.parseEnv: %v\n", err)
 	}
-	Setup()
-	return
 }

+ 103 - 0
settings/settings_test.go

@@ -0,0 +1,103 @@
+package settings
+
+import (
+	"github.com/stretchr/testify/assert"
+	"os"
+	"testing"
+)
+
+func TestSetup(t *testing.T) {
+	Init("../app.example.ini")
+
+	_ = os.Setenv("NGINX_UI_OFFICIAL_DOCKER", "true")
+
+	_ = os.Setenv("NGINX_UI_SERVER_HTTP_PORT", "8080")
+	_ = os.Setenv("NGINX_UI_SERVER_RUN_MODE", "test")
+	_ = os.Setenv("NGINX_UI_SERVER_JWT_SECRET", "newSecret123")
+	_ = os.Setenv("NGINX_UI_SERVER_HTTP_CHALLENGE_PORT", "9181")
+	_ = os.Setenv("NGINX_UI_SERVER_START_CMD", "start")
+	_ = os.Setenv("NGINX_UI_SERVER_DATABASE", "testDB")
+	_ = os.Setenv("NGINX_UI_SERVER_CA_DIR", "/test/ca")
+	_ = os.Setenv("NGINX_UI_SERVER_GITHUB_PROXY", "http://proxy.example.com")
+	_ = os.Setenv("NGINX_UI_SERVER_NODE_SECRET", "nodeSecret")
+	_ = os.Setenv("NGINX_UI_SERVER_DEMO", "true")
+	_ = os.Setenv("NGINX_UI_SERVER_PAGE_SIZE", "20")
+	_ = os.Setenv("NGINX_UI_SERVER_HTTP_HOST", "127.0.0.1")
+	_ = os.Setenv("NGINX_UI_SERVER_CERT_RENEWAL_INTERVAL", "14")
+	_ = os.Setenv("NGINX_UI_SERVER_RECURSIVE_NAMESERVERS", "8.8.8.8")
+	_ = os.Setenv("NGINX_UI_SERVER_SKIP_INSTALLATION", "true")
+	_ = os.Setenv("NGINX_UI_SERVER_NAME", "test")
+
+	_ = os.Setenv("NGINX_UI_NGINX_ACCESS_LOG_PATH", "/tmp/nginx/access.log")
+	_ = os.Setenv("NGINX_UI_NGINX_ERROR_LOG_PATH", "/tmp/nginx/error.log")
+	_ = os.Setenv("NGINX_UI_NGINX_CONFIG_DIR", "/etc/nginx/conf")
+	_ = os.Setenv("NGINX_UI_NGINX_PID_PATH", "/var/run/nginx.pid")
+	_ = os.Setenv("NGINX_UI_NGINX_TEST_CONFIG_CMD", "nginx -t")
+	_ = os.Setenv("NGINX_UI_NGINX_RELOAD_CMD", "nginx -s reload")
+	_ = os.Setenv("NGINX_UI_NGINX_RESTART_CMD", "nginx -s restart")
+
+	_ = os.Setenv("NGINX_UI_OPENAI_MODEL", "davinci")
+	_ = os.Setenv("NGINX_UI_OPENAI_BASE_URL", "https://api.openai.com")
+	_ = os.Setenv("NGINX_UI_OPENAI_PROXY", "https://proxy.openai.com")
+	_ = os.Setenv("NGINX_UI_OPENAI_TOKEN", "token123")
+
+	_ = os.Setenv("NGINX_UI_CASDOOR_ENDPOINT", "https://casdoor.example.com")
+	_ = os.Setenv("NGINX_UI_CASDOOR_CLIENT_ID", "clientId")
+	_ = os.Setenv("NGINX_UI_CASDOOR_CLIENT_SECRET", "clientSecret")
+	_ = os.Setenv("NGINX_UI_CASDOOR_CERTIFICATE", "cert.pem")
+	_ = os.Setenv("NGINX_UI_CASDOOR_ORGANIZATION", "org1")
+	_ = os.Setenv("NGINX_UI_CASDOOR_APPLICATION", "app1")
+	_ = os.Setenv("NGINX_UI_CASDOOR_REDIRECT_URI", "https://redirect.example.com")
+
+	_ = os.Setenv("NGINX_UI_LOGROTATE_ENABLED", "true")
+	_ = os.Setenv("NGINX_UI_LOGROTATE_CMD", "logrotate /custom/logrotate.conf")
+	_ = os.Setenv("NGINX_UI_LOGROTATE_INTERVAL", "60")
+
+	ConfPath = "app.testing.ini"
+	Setup()
+
+	assert.Equal(t, "8080", ServerSettings.HttpPort)
+	assert.Equal(t, "test", ServerSettings.RunMode)
+	assert.Equal(t, "newSecret123", ServerSettings.JwtSecret)
+	assert.Equal(t, "9181", ServerSettings.HTTPChallengePort)
+	assert.Equal(t, "start", ServerSettings.StartCmd)
+	assert.Equal(t, "testDB", ServerSettings.Database)
+	assert.Equal(t, "/test/ca", ServerSettings.CADir)
+	assert.Equal(t, "http://proxy.example.com", ServerSettings.GithubProxy)
+	assert.Equal(t, "nodeSecret", ServerSettings.NodeSecret)
+	assert.Equal(t, true, ServerSettings.Demo)
+	assert.Equal(t, 20, ServerSettings.PageSize)
+	assert.Equal(t, "127.0.0.1", ServerSettings.HttpHost)
+	assert.Equal(t, 14, ServerSettings.CertRenewalInterval)
+	assert.Equal(t, []string{"8.8.8.8"}, ServerSettings.RecursiveNameservers)
+	assert.Equal(t, true, ServerSettings.SkipInstallation)
+	assert.Equal(t, "test", ServerSettings.Name)
+
+	assert.Equal(t, "/tmp/nginx/access.log", NginxSettings.AccessLogPath)
+	assert.Equal(t, "/tmp/nginx/error.log", NginxSettings.ErrorLogPath)
+	assert.Equal(t, "/etc/nginx/conf", NginxSettings.ConfigDir)
+	assert.Equal(t, "/var/run/nginx.pid", NginxSettings.PIDPath)
+	assert.Equal(t, "nginx -t", NginxSettings.TestConfigCmd)
+	assert.Equal(t, "nginx -s reload", NginxSettings.ReloadCmd)
+	assert.Equal(t, "nginx -s stop", NginxSettings.RestartCmd)
+
+	assert.Equal(t, "davinci", OpenAISettings.Model)
+	assert.Equal(t, "https://api.openai.com", OpenAISettings.BaseUrl)
+	assert.Equal(t, "https://proxy.openai.com", OpenAISettings.Proxy)
+	assert.Equal(t, "token123", OpenAISettings.Token)
+
+	assert.Equal(t, "https://casdoor.example.com", CasdoorSettings.Endpoint)
+	assert.Equal(t, "clientId", CasdoorSettings.ClientId)
+	assert.Equal(t, "clientSecret", CasdoorSettings.ClientSecret)
+	assert.Equal(t, "cert.pem", CasdoorSettings.Certificate)
+	assert.Equal(t, "org1", CasdoorSettings.Organization)
+	assert.Equal(t, "app1", CasdoorSettings.Application)
+	assert.Equal(t, "https://redirect.example.com", CasdoorSettings.RedirectUri)
+
+	assert.Equal(t, true, LogrotateSettings.Enabled)
+	assert.Equal(t, "logrotate /custom/logrotate.conf", LogrotateSettings.CMD)
+	assert.Equal(t, 60, LogrotateSettings.Interval)
+
+	os.Clearenv()
+	_ = os.Remove("app.testing.ini")
+}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio