Browse Source

feat: add login failed ban ip list

Jacky 11 months ago
parent
commit
cff843b82b
43 changed files with 2257 additions and 953 deletions
  1. 45 0
      api/settings/auth.go
  2. 14 0
      api/settings/router.go
  3. 4 1
      api/settings/settings.go
  4. 7 11
      api/system/router.go
  5. 29 1
      api/user/auth.go
  6. 2 2
      app/src/api/curd.ts
  7. 15 5
      app/src/api/settings.ts
  8. 1 1
      app/src/language/LINGUAS
  9. 163 98
      app/src/language/en/app.po
  10. 165 98
      app/src/language/es/app.po
  11. 165 98
      app/src/language/fr_FR/app.po
  12. 165 98
      app/src/language/ko_KR/app.po
  13. 164 103
      app/src/language/messages.pot
  14. 165 98
      app/src/language/ru_RU/app.po
  15. 165 98
      app/src/language/vi_VN/app.po
  16. BIN
      app/src/language/zh_CN/app.mo
  17. 163 98
      app/src/language/zh_CN/app.po
  18. 165 98
      app/src/language/zh_TW/app.po
  19. 0 4
      app/src/views/domain/DomainAdd.vue
  20. 0 2
      app/src/views/domain/DomainEdit.vue
  21. 1 4
      app/src/views/domain/components/Deploy.vue
  22. 2 6
      app/src/views/domain/components/SiteDuplicate.vue
  23. 1 2
      app/src/views/other/Install.vue
  24. 15 3
      app/src/views/other/Login.vue
  25. 114 0
      app/src/views/preference/AuthSettings.vue
  26. 16 1
      app/src/views/preference/Preference.vue
  27. 5 0
      app/src/views/preference/typedef.ts
  28. 0 2
      app/src/views/stream/StreamEdit.vue
  29. 1 4
      app/src/views/stream/components/Deploy.vue
  30. 2 6
      app/src/views/stream/components/StreamDuplicate.vue
  31. 86 0
      app/vite.config.ts.timestamp-1721542436572-91822f5b8889d.mjs
  32. 1 1
      docs/guide/config-auth.md
  33. 1 1
      docs/zh_CN/guide/config-auth.md
  34. 1 1
      docs/zh_TW/guide/config-auth.md
  35. 19 4
      internal/user/login.go
  36. 7 0
      model/ban_ip.go
  37. 1 0
      model/model.go
  38. 5 1
      query/auths.gen.go
  39. 358 0
      query/ban_ips.gen.go
  40. 8 0
      query/gen.go
  41. 3 1
      router/routers.go
  42. 5 2
      settings/auth.go
  43. 8 0
      settings/settings.go

+ 45 - 0
api/settings/auth.go

@@ -0,0 +1,45 @@
+package settings
+
+import (
+	"github.com/0xJacky/Nginx-UI/api"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/gin-gonic/gin"
+	"net/http"
+	"time"
+)
+
+func GetBanLoginIP(c *gin.Context) {
+	b := query.BanIP
+
+	// clear expired banned IPs
+	_, _ = b.Where(b.ExpiredAt.Lte(time.Now().Unix())).Delete()
+
+	banIps, err := b.Where(
+		b.ExpiredAt.Gte(time.Now().Unix()),
+		b.Attempts.Gte(settings.AuthSettings.MaxAttempts)).Find()
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, banIps)
+}
+
+func RemoveBannedIP(c *gin.Context) {
+	var json struct {
+		IP string `json:"ip"`
+	}
+	if !api.BindAndValid(c, &json) {
+		return
+	}
+
+	b := query.BanIP
+	_, err := b.Where(b.IP.Eq(json.IP)).Delete()
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	c.JSON(http.StatusNoContent, nil)
+}

+ 14 - 0
api/settings/router.go

@@ -0,0 +1,14 @@
+package settings
+
+import (
+	"github.com/gin-gonic/gin"
+)
+
+func InitRouter(r *gin.RouterGroup) {
+	r.GET("settings/server/name", GetServerName)
+	r.GET("settings", GetSettings)
+	r.POST("settings", SaveSettings)
+
+	r.GET("settings/auth/banned_ips", GetBanLoginIP)
+	r.DELETE("settings/auth/banned_ip", RemoveBannedIP)
+}

+ 4 - 1
api/system/settings.go → api/settings/settings.go

@@ -1,4 +1,4 @@
-package system
+package settings
 
 
 import (
 import (
 	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/api"
@@ -20,6 +20,7 @@ func GetSettings(c *gin.Context) {
 		"nginx":     settings.NginxSettings,
 		"nginx":     settings.NginxSettings,
 		"openai":    settings.OpenAISettings,
 		"openai":    settings.OpenAISettings,
 		"logrotate": settings.LogrotateSettings,
 		"logrotate": settings.LogrotateSettings,
+		"auth":      settings.AuthSettings,
 	})
 	})
 }
 }
 
 
@@ -29,6 +30,7 @@ func SaveSettings(c *gin.Context) {
 		Nginx     settings.Nginx     `json:"nginx"`
 		Nginx     settings.Nginx     `json:"nginx"`
 		Openai    settings.OpenAI    `json:"openai"`
 		Openai    settings.OpenAI    `json:"openai"`
 		Logrotate settings.Logrotate `json:"logrotate"`
 		Logrotate settings.Logrotate `json:"logrotate"`
+		Auth      settings.Auth      `json:"auth"`
 	}
 	}
 
 
 	if !api.BindAndValid(c, &json) {
 	if !api.BindAndValid(c, &json) {
@@ -44,6 +46,7 @@ func SaveSettings(c *gin.Context) {
 	settings.ProtectedFill(&settings.NginxSettings, &json.Nginx)
 	settings.ProtectedFill(&settings.NginxSettings, &json.Nginx)
 	settings.ProtectedFill(&settings.OpenAISettings, &json.Openai)
 	settings.ProtectedFill(&settings.OpenAISettings, &json.Openai)
 	settings.ProtectedFill(&settings.LogrotateSettings, &json.Logrotate)
 	settings.ProtectedFill(&settings.LogrotateSettings, &json.Logrotate)
+	settings.ProtectedFill(&settings.AuthSettings, &json.Auth)
 
 
 	err := settings.Save()
 	err := settings.Save()
 	if err != nil {
 	if err != nil {

+ 7 - 11
api/system/router.go

@@ -1,21 +1,17 @@
 package system
 package system
 
 
 import (
 import (
-    "github.com/gin-gonic/gin"
+	"github.com/gin-gonic/gin"
 )
 )
 
 
 func InitPublicRouter(r *gin.RouterGroup) {
 func InitPublicRouter(r *gin.RouterGroup) {
-    r.GET("install", InstallLockCheck)
-    r.POST("install", InstallNginxUI)
-    r.GET("translation/:code", GetTranslation)
+	r.GET("install", InstallLockCheck)
+	r.POST("install", InstallNginxUI)
+	r.GET("translation/:code", GetTranslation)
 }
 }
 
 
 func InitPrivateRouter(r *gin.RouterGroup) {
 func InitPrivateRouter(r *gin.RouterGroup) {
-    r.GET("settings/server/name", GetServerName)
-    r.GET("settings", GetSettings)
-    r.POST("settings", SaveSettings)
-
-    r.GET("upgrade/release", GetRelease)
-    r.GET("upgrade/current", GetCurrentVersion)
-    r.GET("upgrade/perform", PerformCoreUpgrade)
+	r.GET("upgrade/release", GetRelease)
+	r.GET("upgrade/current", GetCurrentVersion)
+	r.GET("upgrade/perform", PerformCoreUpgrade)
 }
 }

+ 29 - 1
api/user/auth.go

@@ -4,12 +4,17 @@ import (
 	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/internal/logger"
 	"github.com/0xJacky/Nginx-UI/internal/logger"
 	"github.com/0xJacky/Nginx-UI/internal/user"
 	"github.com/0xJacky/Nginx-UI/internal/user"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
 	"github.com/gin-gonic/gin"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
 	"net/http"
 	"net/http"
+	"sync"
 	"time"
 	"time"
 )
 )
 
 
+var mutex = &sync.Mutex{}
+
 type LoginUser struct {
 type LoginUser struct {
 	Name     string `json:"name" binding:"required,max=255"`
 	Name     string `json:"name" binding:"required,max=255"`
 	Password string `json:"password" binding:"required,max=255"`
 	Password string `json:"password" binding:"required,max=255"`
@@ -29,6 +34,25 @@ type LoginResponse struct {
 }
 }
 
 
 func Login(c *gin.Context) {
 func Login(c *gin.Context) {
+	// make sure that only one request is processed at a time
+	mutex.Lock()
+	defer mutex.Unlock()
+	// check if the ip is banned
+	clientIP := c.ClientIP()
+	b := query.BanIP
+	banIP, _ := b.Where(b.IP.Eq(clientIP),
+		b.ExpiredAt.Gte(time.Now().Unix()),
+		b.Attempts.Gte(settings.AuthSettings.MaxAttempts),
+	).Count()
+
+	if banIP > 0 {
+		c.JSON(http.StatusTooManyRequests, LoginResponse{
+			Message: "Max attempts",
+			Code:    ErrMaxAttempts,
+		})
+		return
+	}
+
 	var json LoginUser
 	var json LoginUser
 	ok := api.BindAndValid(c, &json)
 	ok := api.BindAndValid(c, &json)
 	if !ok {
 	if !ok {
@@ -37,7 +61,7 @@ func Login(c *gin.Context) {
 
 
 	u, err := user.Login(json.Name, json.Password)
 	u, err := user.Login(json.Name, json.Password)
 	if err != nil {
 	if err != nil {
-		time.Sleep(5 * time.Second)
+		// time.Sleep(5 * time.Second)
 		switch {
 		switch {
 		case errors.Is(err, user.ErrPasswordIncorrect):
 		case errors.Is(err, user.ErrPasswordIncorrect):
 			c.JSON(http.StatusForbidden, LoginResponse{
 			c.JSON(http.StatusForbidden, LoginResponse{
@@ -52,9 +76,13 @@ func Login(c *gin.Context) {
 		default:
 		default:
 			api.ErrHandler(c, err)
 			api.ErrHandler(c, err)
 		}
 		}
+		user.BanIP(clientIP)
 		return
 		return
 	}
 	}
 
 
+	// login success, clear banned record
+	_, _ = b.Where(b.IP.Eq(clientIP)).Delete()
+
 	logger.Info("[User Login]", u.Name)
 	logger.Info("[User Login]", u.Name)
 	token, err := user.GenerateJWT(u.Name)
 	token, err := user.GenerateJWT(u.Name)
 	if err != nil {
 	if err != nil {

+ 2 - 2
app/src/api/curd.ts

@@ -13,7 +13,7 @@ export interface Pagination {
   total_pages: number
   total_pages: number
 }
 }
 
 
-export interface IGetListResponse<T> {
+export interface GetListResponse<T> {
   data: T[]
   data: T[]
   pagination: Pagination
   pagination: Pagination
 }
 }
@@ -35,7 +35,7 @@ class Curd<T> {
   }
   }
 
 
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  _get_list(params: any = null): Promise<IGetListResponse<T>> {
+  _get_list(params: any = null): Promise<GetListResponse<T>> {
     return http.get(this.plural, { params })
     return http.get(this.plural, { params })
   }
   }
 
 

+ 15 - 5
app/src/api/settings.ts

@@ -1,17 +1,27 @@
 import http from '@/lib/http'
 import http from '@/lib/http'
 
 
+export interface BannedIP {
+  ip: string
+  attempts: number
+  expired_at: string
+}
+
 const settings = {
 const settings = {
-  get() {
+  get<T>(): Promise<T> {
     return http.get('/settings')
     return http.get('/settings')
   },
   },
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  save(data: any) {
+  save<T>(data: T) {
     return http.post('/settings', data)
     return http.post('/settings', data)
   },
   },
-
-  get_server_name() {
+  get_server_name(): Promise<{ name: string }> {
     return http.get('/settings/server/name')
     return http.get('/settings/server/name')
   },
   },
+  get_banned_ips(): Promise<BannedIP[]> {
+    return http.get('/settings/auth/banned_ips')
+  },
+  remove_banned_ip(ip: string) {
+    return http.delete('/settings/auth/banned_ip', { data: { ip } })
+  },
 }
 }
 
 
 export default settings
 export default settings

+ 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

+ 163 - 98
app/src/language/en/app.po

@@ -28,7 +28,8 @@ msgstr "Username"
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
 #: src/views/notification/Notification.vue:37
-#: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
+#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47
+#: src/views/user/User.vue:43
 msgid "Action"
 msgid "Action"
 msgstr "Action"
 msgstr "Action"
 
 
@@ -51,7 +52,7 @@ msgstr "Add Directive Below"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Add Location"
 msgstr "Add Location"
 
 
-#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:93
+#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Add Site"
 msgstr "Add Site"
 
 
@@ -70,11 +71,11 @@ msgstr "Saved successfully"
 msgid "Additional"
 msgid "Additional"
 msgstr "Add Location"
 msgstr "Add Location"
 
 
-#: src/views/domain/DomainEdit.vue:199 src/views/stream/StreamEdit.vue:191
+#: src/views/domain/DomainEdit.vue:197 src/views/stream/StreamEdit.vue:189
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Advance Mode"
 msgstr "Advance Mode"
 
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgid "API Base Url"
 msgstr ""
 msgstr ""
 
 
@@ -82,11 +83,11 @@ msgstr ""
 msgid "API Document"
 msgid "API Document"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgid "API Proxy"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgid "API Token"
 msgstr ""
 msgstr ""
 
 
@@ -94,7 +95,12 @@ msgstr ""
 msgid "Arch"
 msgid "Arch"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:84
+#: src/views/preference/AuthSettings.vue:94
+#, fuzzy
+msgid "Are you sure to delete this banned IP immediately?"
+msgstr "Are you sure you want to remove this directive?"
+
+#: src/components/Notification/Notification.vue:86
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgid "Are you sure you want to clear all notifications?"
@@ -147,6 +153,14 @@ msgstr ""
 msgid "Assistant"
 msgid "Assistant"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:17
+msgid "Attempts"
+msgstr ""
+
+#: src/views/preference/Preference.vue:116
+msgid "Auth"
+msgstr ""
+
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 msgid "Author"
 msgid "Author"
@@ -170,8 +184,8 @@ msgstr "Auto-renewal enabled for %{name}"
 
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
-#: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
-#: src/views/stream/StreamEdit.vue:247
+#: src/views/domain/DomainEdit.vue:254 src/views/nginx_log/NginxLog.vue:168
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgid "Back"
 msgstr "Back"
 msgstr "Back"
 
 
@@ -180,19 +194,31 @@ msgstr "Back"
 msgid "Back Home"
 msgid "Back Home"
 msgstr "Back"
 msgstr "Back"
 
 
-#: src/views/domain/DomainAdd.vue:99
+#: src/views/preference/AuthSettings.vue:68
+msgid "Ban Threshold Minutes"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:82
+msgid "Banned IPs"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:20
+msgid "Banned Until"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:95
 msgid "Base information"
 msgid "Base information"
 msgstr "Base information"
 msgstr "Base information"
 
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:101
+#: src/views/preference/Preference.vue:110
 #: src/views/stream/components/RightSettings.vue:74
 #: src/views/stream/components/RightSettings.vue:74
 #, fuzzy
 #, fuzzy
 msgid "Basic"
 msgid "Basic"
 msgstr "Basic Mode"
 msgstr "Basic Mode"
 
 
-#: src/views/domain/DomainEdit.vue:202 src/views/stream/StreamEdit.vue:194
+#: src/views/domain/DomainEdit.vue:200 src/views/stream/StreamEdit.vue:192
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Basic Mode"
 msgstr "Basic Mode"
 
 
@@ -280,12 +306,12 @@ msgid "Cleaning environment variables"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgid "Clear"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 #, fuzzy
 msgid "Cleared successfully"
 msgid "Cleared successfully"
@@ -312,7 +338,7 @@ msgstr "Configurations"
 msgid "Configuration file is test successful"
 msgid "Configuration file is test successful"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgid "Configuration Name"
 msgstr "Configuration Name"
 msgstr "Configuration Name"
 
 
@@ -320,7 +346,7 @@ msgstr "Configuration Name"
 msgid "Configurations"
 msgid "Configurations"
 msgstr "Configurations"
 msgstr "Configurations"
 
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgid "Configure SSL"
 msgstr "Configure SSL"
 msgstr "Configure SSL"
 
 
@@ -351,7 +377,7 @@ msgstr "CPU:"
 msgid "Create"
 msgid "Create"
 msgstr "Created at"
 msgstr "Created at"
 
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Create Another"
 msgstr "Create Another"
 
 
@@ -390,7 +416,7 @@ msgstr ""
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Dashboard"
 msgstr "Dashboard"
 
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "Database (Optional, default: database)"
 msgstr "Database (Optional, default: database)"
 
 
@@ -423,15 +449,15 @@ msgstr ""
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr "Disabled successfully"
 msgstr "Disabled successfully"
 
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: src/views/domain/components/RightSettings.vue:93
 #: src/views/domain/components/RightSettings.vue:93
-#: src/views/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 msgid "Deploy"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:63
-#: src/views/stream/components/Deploy.vue:63
+#: src/views/domain/components/Deploy.vue:60
+#: src/views/stream/components/Deploy.vue:60
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
@@ -481,9 +507,9 @@ msgstr "Disabled"
 msgid "Disable auto-renewal failed for %{name}"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "Disable auto-renewal failed for %{name}"
 msgstr "Disable auto-renewal failed for %{name}"
 
 
-#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:183
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Disabled"
 msgstr "Disabled"
 
 
@@ -562,7 +588,7 @@ msgstr "Are you sure you want to remove this directive?"
 msgid "Domain"
 msgid "Domain"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "Domain Config Created Successfully"
 msgstr "Domain Config Created Successfully"
 
 
@@ -582,38 +608,38 @@ msgstr ""
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: src/views/domain/DomainList.vue:140
 #: src/views/domain/DomainList.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:84
-#: src/views/stream/components/StreamDuplicate.vue:84
+#: src/views/domain/components/SiteDuplicate.vue:82
+#: src/views/stream/components/StreamDuplicate.vue:82
 #, fuzzy
 #, fuzzy
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:90
-#: src/views/stream/components/StreamDuplicate.vue:90
+#: src/views/domain/components/SiteDuplicate.vue:87
+#: src/views/stream/components/StreamDuplicate.vue:87
 #, fuzzy
 #, fuzzy
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Enable failed"
 msgstr "Enable failed"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:82
+#: src/views/domain/components/SiteDuplicate.vue:80
+#: src/views/stream/components/StreamDuplicate.vue:80
 #, fuzzy
 #, fuzzy
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:64
-#: src/views/stream/components/StreamDuplicate.vue:64
+#: src/views/domain/components/SiteDuplicate.vue:63
+#: src/views/stream/components/StreamDuplicate.vue:63
 #, fuzzy
 #, fuzzy
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/views/domain/DomainEdit.vue:174 src/views/stream/StreamEdit.vue:166
+#: src/views/domain/DomainEdit.vue:172 src/views/stream/StreamEdit.vue:164
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Edit %{n}"
 msgstr "Edit %{n}"
 
 
@@ -635,25 +661,25 @@ msgstr "Edit Site"
 msgid "Email"
 msgid "Email"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: src/views/domain/DomainList.vue:132
 #: src/views/domain/DomainList.vue:132
-#: src/views/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #: src/views/stream/StreamList.vue:153
 #, fuzzy
 #, fuzzy
 msgid "Enable"
 msgid "Enable"
 msgstr "Enabled"
 msgstr "Enabled"
 
 
-#: src/views/domain/components/Deploy.vue:52
-#: src/views/stream/components/Deploy.vue:52
+#: src/views/domain/components/Deploy.vue:50
+#: src/views/stream/components/Deploy.vue:50
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:46
-#: src/views/stream/components/Deploy.vue:46
+#: src/views/domain/components/Deploy.vue:45
+#: src/views/stream/components/Deploy.vue:45
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
@@ -661,12 +687,12 @@ msgstr ""
 msgid "Enable auto-renewal failed for %{name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "Enable auto-renewal failed for %{name}"
 msgstr "Enable auto-renewal failed for %{name}"
 
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 msgid "Enable failed"
 msgstr "Enable failed"
 msgstr "Enable failed"
 
 
-#: src/views/domain/components/Deploy.vue:44
-#: src/views/stream/components/Deploy.vue:44
+#: src/views/domain/components/Deploy.vue:43
+#: src/views/stream/components/Deploy.vue:43
 #, fuzzy
 #, fuzzy
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Enabled successfully"
 msgstr "Enabled successfully"
@@ -677,19 +703,19 @@ msgstr "Enable TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Enabled"
 msgstr "Enabled"
 
 
 #: src/views/domain/components/RightSettings.vue:29
 #: src/views/domain/components/RightSettings.vue:29
-#: src/views/domain/components/SiteDuplicate.vue:98
-#: src/views/domain/DomainAdd.vue:42 src/views/domain/DomainList.vue:57
+#: src/views/domain/components/SiteDuplicate.vue:94
+#: src/views/domain/DomainAdd.vue:40 src/views/domain/DomainList.vue:57
 #: src/views/stream/components/RightSettings.vue:29
 #: src/views/stream/components/RightSettings.vue:29
-#: src/views/stream/components/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Enabled successfully"
 msgstr "Enabled successfully"
@@ -759,7 +785,7 @@ msgstr "Failed to enable %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainEdit.vue:132 src/views/stream/StreamEdit.vue:124
+#: src/views/domain/DomainEdit.vue:130 src/views/stream/StreamEdit.vue:122
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr ""
 msgstr ""
 
 
@@ -785,7 +811,7 @@ msgstr "File Not Found"
 msgid "Filter"
 msgid "Filter"
 msgstr ""
 msgstr ""
 
 
-#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:101
+#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgid "Finished"
 msgstr "Finished"
 msgstr "Finished"
 
 
@@ -857,6 +883,12 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:60
+msgid ""
+"If the number of login failed attempts from a ip reach the max attempts in "
+"ban threshold minutes, the ip will be banned for a period of time."
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:136
 #: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgid "Import"
 msgstr ""
 msgstr ""
@@ -866,6 +898,10 @@ msgstr ""
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
+#: src/views/other/Login.vue:59
+msgid "Incorrect username or password"
+msgstr ""
+
 #: src/constants/index.ts:18
 #: src/constants/index.ts:18
 msgid "Info"
 msgid "Info"
 msgstr ""
 msgstr ""
@@ -878,7 +914,7 @@ msgstr ""
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:273 src/views/other/Install.vue:136
+#: src/routes/index.ts:273 src/views/other/Install.vue:135
 msgid "Install"
 msgid "Install"
 msgstr "Install"
 msgstr "Install"
 
 
@@ -900,6 +936,10 @@ msgstr ""
 msgid "Invalid"
 msgid "Invalid"
 msgstr "Invalid E-mail!"
 msgstr "Invalid E-mail!"
 
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgid "Issue wildcard certificate"
@@ -933,7 +973,7 @@ msgstr ""
 msgid "Leave blank for no change"
 msgid "Leave blank for no change"
 msgstr "Leave blank for no change"
 msgstr "Leave blank for no change"
 
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr ""
 msgstr ""
 
 
@@ -990,11 +1030,11 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "Login"
 msgstr "Login"
 
 
-#: src/routes/index.ts:279 src/views/other/Login.vue:147
+#: src/routes/index.ts:279 src/views/other/Login.vue:159
 msgid "Login"
 msgid "Login"
 msgstr "Login"
 msgstr "Login"
 
 
-#: src/views/other/Login.vue:51 src/views/other/Login.vue:97
+#: src/views/other/Login.vue:109 src/views/other/Login.vue:51
 msgid "Login successful"
 msgid "Login successful"
 msgstr "Login successful"
 msgstr "Login successful"
 
 
@@ -1002,7 +1042,7 @@ msgstr "Login successful"
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "Logout successful"
 msgstr "Logout successful"
 
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgid "Logrotate"
 msgstr ""
 msgstr ""
 
 
@@ -1047,6 +1087,10 @@ msgstr "Manage Users"
 msgid "Managed Certificate"
 msgid "Managed Certificate"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
 
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgid "Memory"
@@ -1060,7 +1104,7 @@ msgstr "Memory and Storage"
 msgid "Minutes"
 msgid "Minutes"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 #, fuzzy
 msgid "Model"
 msgid "Model"
 msgstr "Advance Mode"
 msgstr "Advance Mode"
@@ -1077,7 +1121,7 @@ msgstr "Modify Config"
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgid "Modify Config"
 msgstr "Modify Config"
 msgstr "Modify Config"
 
 
@@ -1097,12 +1141,12 @@ msgstr "Single Directive"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/RightSettings.vue:83
-#: src/views/domain/components/SiteDuplicate.vue:133
+#: src/views/domain/components/SiteDuplicate.vue:129
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: src/views/environment/Environment.vue:12
 #: src/views/stream/components/RightSettings.vue:82
 #: src/views/stream/components/RightSettings.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:133
+#: src/views/stream/components/StreamDuplicate.vue:129
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 msgid "Name"
 msgid "Name"
 msgstr "Name"
 msgstr "Name"
@@ -1129,11 +1173,11 @@ msgstr ""
 
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgid "Next"
 msgstr "Next"
 msgstr "Next"
 
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgid "Nginx"
 msgstr ""
 msgstr ""
 
 
@@ -1141,7 +1185,7 @@ msgstr ""
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainEdit.vue:217 src/views/stream/StreamEdit.vue:209
+#: src/views/domain/DomainEdit.vue:215 src/views/stream/StreamEdit.vue:207
 #, fuzzy
 #, fuzzy
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Configuration Name"
 msgstr "Configuration Name"
@@ -1169,7 +1213,7 @@ msgid "Nginx restarted successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/ChatGPT/ChatGPT.vue:270
-#: src/components/Notification/Notification.vue:82
+#: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
@@ -1177,6 +1221,7 @@ msgstr "Saved successfully"
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 #: src/views/stream/StreamList.vue:165
 msgid "No"
 msgid "No"
@@ -1208,7 +1253,7 @@ msgstr ""
 msgid "Notification"
 msgid "Notification"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
 
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
@@ -1234,7 +1279,7 @@ msgid "Ok"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/ChatGPT/ChatGPT.vue:271
-#: src/components/Notification/Notification.vue:83
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1264,7 +1309,7 @@ msgstr ""
 msgid "Online"
 msgid "Online"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgid "OpenAI"
 msgstr ""
 msgstr ""
 
 
@@ -1277,13 +1322,13 @@ msgstr "OS:"
 msgid "OS:"
 msgid "OS:"
 msgstr "OS:"
 msgstr "OS:"
 
 
-#: src/views/domain/components/Deploy.vue:90
-#: src/views/stream/components/Deploy.vue:90
+#: src/views/domain/components/Deploy.vue:87
+#: src/views/stream/components/Deploy.vue:87
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:94
-#: src/views/stream/components/Deploy.vue:94
+#: src/views/domain/components/Deploy.vue:91
+#: src/views/stream/components/Deploy.vue:91
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr ""
 msgstr ""
 
 
@@ -1291,11 +1336,11 @@ msgstr ""
 msgid "Params"
 msgid "Params"
 msgstr "Params"
 msgstr "Params"
 
 
-#: src/views/other/Login.vue:132 src/views/user/User.vue:18
+#: src/views/other/Login.vue:144 src/views/user/User.vue:18
 msgid "Password"
 msgid "Password"
 msgstr "Password"
 msgstr "Password"
 
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgid "Password (*)"
 msgstr "Password (*)"
 msgstr "Password (*)"
 
 
@@ -1358,7 +1403,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:105
 msgid "Preference"
 msgid "Preference"
 msgstr ""
 msgstr ""
 
 
@@ -1454,7 +1499,16 @@ msgstr ""
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:50
+#: src/views/preference/AuthSettings.vue:101
+msgid "Remove"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:47
+#, fuzzy
+msgid "Remove successfully"
+msgstr "Saved successfully"
+
+#: src/components/Notification/Notification.vue:52
 #, fuzzy
 #, fuzzy
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
@@ -1513,9 +1567,9 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/certificate/CertificateEditor.vue:249
-#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
+#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:261
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgid "Save"
 msgstr "Save"
 msgstr "Save"
 
 
@@ -1523,7 +1577,7 @@ msgstr "Save"
 msgid "Save Directive"
 msgid "Save Directive"
 msgstr "Save Directive"
 msgstr "Save Directive"
 
 
-#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:50
+#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:46
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Save error %{msg}"
 msgstr "Save error %{msg}"
@@ -1531,15 +1585,15 @@ msgstr "Save error %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 #, fuzzy
 #, fuzzy
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:38
-#: src/views/domain/DomainEdit.vue:148
+#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:37
+#: src/views/domain/DomainEdit.vue:146
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
-#: src/views/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
@@ -1564,8 +1618,9 @@ msgstr "Send"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
+#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:69
+#: src/views/preference/AuthSettings.vue:49
+#: src/views/preference/Preference.vue:78 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
 msgstr "Server error"
 msgstr "Server error"
@@ -1584,7 +1639,7 @@ msgid "server_name not found in directives"
 msgstr "server_name not found in directives"
 msgstr "server_name not found in directives"
 
 
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
-#: src/views/domain/DomainAdd.vue:121
+#: src/views/domain/DomainAdd.vue:117
 msgid "server_name parameter is required"
 msgid "server_name parameter is required"
 msgstr "server_name parameter is required"
 msgstr "server_name parameter is required"
 
 
@@ -1641,7 +1696,7 @@ msgstr "Certificate Status"
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 #, fuzzy
 msgid "SSO Login"
 msgid "SSO Login"
 msgstr "Login"
 msgstr "Login"
@@ -1728,8 +1783,8 @@ msgstr ""
 msgid "System"
 msgid "System"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:140
+#: src/views/domain/components/SiteDuplicate.vue:136
+#: src/views/stream/components/StreamDuplicate.vue:136
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
@@ -1763,7 +1818,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgid "The input is not a SSL Certificate Key"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid ""
 msgid ""
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "dashes, and dots."
 "dashes, and dots."
@@ -1798,8 +1853,8 @@ msgstr ""
 msgid "The url is invalid"
 msgid "The url is invalid"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:45
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:48
+#: src/views/preference/OpenAISettings.vue:60
 msgid "The url is invalid."
 msgid "The url is invalid."
 msgstr ""
 msgstr ""
 
 
@@ -1825,6 +1880,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgid "Tips"
 msgstr ""
 msgstr ""
@@ -1841,10 +1897,14 @@ msgid ""
 "continue?"
 "continue?"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 msgid "Token is not valid"
 msgstr ""
 msgstr ""
 
 
+#: src/views/other/Login.vue:62
+msgid "Too many login failed attempts, please try again later"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 msgid "Trash"
 msgid "Trash"
 msgstr ""
 msgstr ""
@@ -1900,11 +1960,15 @@ msgstr ""
 msgid "User"
 msgid "User"
 msgstr "Username"
 msgstr "Username"
 
 
-#: src/views/other/Login.vue:122 src/views/user/User.vue:9
+#: src/views/other/Login.vue:65
+msgid "User is banned"
+msgstr ""
+
+#: src/views/other/Login.vue:134 src/views/user/User.vue:9
 msgid "Username"
 msgid "Username"
 msgstr "Username"
 msgstr "Username"
 
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "Username (*)"
 msgstr "Username (*)"
 
 
@@ -1918,7 +1982,7 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 #, fuzzy
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
@@ -1934,7 +1998,7 @@ msgstr "Basic Mode"
 
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgid "Warning"
 msgstr "Warning"
 msgstr "Warning"
 
 
@@ -1965,6 +2029,7 @@ msgstr ""
 
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgid "Yes"
 msgstr "Yes"
 msgstr "Yes"

+ 165 - 98
app/src/language/es/app.po

@@ -33,7 +33,8 @@ msgstr "Usuario"
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
 #: src/views/notification/Notification.vue:37
-#: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
+#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47
+#: src/views/user/User.vue:43
 msgid "Action"
 msgid "Action"
 msgstr "Acción"
 msgstr "Acción"
 
 
@@ -56,7 +57,7 @@ msgstr "Añadir directiva a continuación"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Agregar Ubicación"
 msgstr "Agregar Ubicación"
 
 
-#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:93
+#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Agregar Sitio"
 msgstr "Agregar Sitio"
 
 
@@ -72,11 +73,11 @@ msgstr "Agregado exitoso"
 msgid "Additional"
 msgid "Additional"
 msgstr "Adicional"
 msgstr "Adicional"
 
 
-#: src/views/domain/DomainEdit.vue:199 src/views/stream/StreamEdit.vue:191
+#: src/views/domain/DomainEdit.vue:197 src/views/stream/StreamEdit.vue:189
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Modo avanzado"
 msgstr "Modo avanzado"
 
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgid "API Base Url"
 msgstr "URL Base de la API"
 msgstr "URL Base de la API"
 
 
@@ -85,11 +86,11 @@ msgstr "URL Base de la API"
 msgid "API Document"
 msgid "API Document"
 msgstr "Token de la API"
 msgstr "Token de la API"
 
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgid "API Proxy"
 msgstr "Proxy de la API"
 msgstr "Proxy de la API"
 
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgid "API Token"
 msgstr "Token de la API"
 msgstr "Token de la API"
 
 
@@ -97,7 +98,12 @@ msgstr "Token de la API"
 msgid "Arch"
 msgid "Arch"
 msgstr "Arquitectura"
 msgstr "Arquitectura"
 
 
-#: src/components/Notification/Notification.vue:84
+#: src/views/preference/AuthSettings.vue:94
+#, fuzzy
+msgid "Are you sure to delete this banned IP immediately?"
+msgstr "¿Está seguro de que quiere borrar?"
+
+#: src/components/Notification/Notification.vue:86
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
 msgid "Are you sure you want to clear all notifications?"
 msgid "Are you sure you want to clear all notifications?"
 msgstr "¿Está seguro de que desea borrar todas las notificaciones?"
 msgstr "¿Está seguro de que desea borrar todas las notificaciones?"
@@ -146,6 +152,15 @@ msgstr "Preguntar por ayuda a ChatGPT"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "Asistente"
 msgstr "Asistente"
 
 
+#: src/views/preference/AuthSettings.vue:17
+msgid "Attempts"
+msgstr ""
+
+#: src/views/preference/Preference.vue:116
+#, fuzzy
+msgid "Auth"
+msgstr "Autor"
+
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 msgid "Author"
 msgid "Author"
@@ -169,8 +184,8 @@ msgstr "Renovación automática habilitada por %{name}"
 
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
-#: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
-#: src/views/stream/StreamEdit.vue:247
+#: src/views/domain/DomainEdit.vue:254 src/views/nginx_log/NginxLog.vue:168
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgid "Back"
 msgstr "Volver"
 msgstr "Volver"
 
 
@@ -178,18 +193,30 @@ msgstr "Volver"
 msgid "Back Home"
 msgid "Back Home"
 msgstr "Volver al Inicio"
 msgstr "Volver al Inicio"
 
 
-#: src/views/domain/DomainAdd.vue:99
+#: src/views/preference/AuthSettings.vue:68
+msgid "Ban Threshold Minutes"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:82
+msgid "Banned IPs"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:20
+msgid "Banned Until"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:95
 msgid "Base information"
 msgid "Base information"
 msgstr "Información general"
 msgstr "Información general"
 
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:101
+#: src/views/preference/Preference.vue:110
 #: src/views/stream/components/RightSettings.vue:74
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgid "Basic"
 msgstr "Básico"
 msgstr "Básico"
 
 
-#: src/views/domain/DomainEdit.vue:202 src/views/stream/StreamEdit.vue:194
+#: src/views/domain/DomainEdit.vue:200 src/views/stream/StreamEdit.vue:192
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Modo Básico"
 msgstr "Modo Básico"
 
 
@@ -273,12 +300,12 @@ msgid "Cleaning environment variables"
 msgstr "Borrar las variables de entorno"
 msgstr "Borrar las variables de entorno"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgid "Clear"
 msgstr "Borrar"
 msgstr "Borrar"
 
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "Limpiado exitoso"
 msgstr "Limpiado exitoso"
@@ -303,7 +330,7 @@ msgstr "Plantillas de configuración"
 msgid "Configuration file is test successful"
 msgid "Configuration file is test successful"
 msgstr "El archivo de configuración se probó exitosamente"
 msgstr "El archivo de configuración se probó exitosamente"
 
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgid "Configuration Name"
 msgstr "Nombre de la configuración"
 msgstr "Nombre de la configuración"
 
 
@@ -311,7 +338,7 @@ msgstr "Nombre de la configuración"
 msgid "Configurations"
 msgid "Configurations"
 msgstr "Configuraciones"
 msgstr "Configuraciones"
 
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgid "Configure SSL"
 msgstr "Configurar SSL"
 msgstr "Configurar SSL"
 
 
@@ -341,7 +368,7 @@ msgstr "CPU:"
 msgid "Create"
 msgid "Create"
 msgstr "Crear"
 msgstr "Crear"
 
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Crear otro"
 msgstr "Crear otro"
 
 
@@ -380,7 +407,7 @@ msgstr ""
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Panel"
 msgstr "Panel"
 
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "Base de datos (Opcional, default: database)"
 msgstr "Base de datos (Opcional, default: database)"
 
 
@@ -412,15 +439,15 @@ msgstr "Eliminar stream: %{site_name}"
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr "Borrado exitoso"
 msgstr "Borrado exitoso"
 
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: src/views/domain/components/RightSettings.vue:93
 #: src/views/domain/components/RightSettings.vue:93
-#: src/views/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 msgid "Deploy"
 msgstr "Desplegar"
 msgstr "Desplegar"
 
 
-#: src/views/domain/components/Deploy.vue:63
-#: src/views/stream/components/Deploy.vue:63
+#: src/views/domain/components/Deploy.vue:60
+#: src/views/stream/components/Deploy.vue:60
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr "Falló el desplegado de %{conf_name} a %{node_name}"
 msgstr "Falló el desplegado de %{conf_name} a %{node_name}"
 
 
@@ -467,9 +494,9 @@ msgstr "Desactivar"
 msgid "Disable auto-renewal failed for %{name}"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "No se pudo desactivar la renovación automática por %{name}"
 msgstr "No se pudo desactivar la renovación automática por %{name}"
 
 
-#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:183
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Desactivado"
 msgstr "Desactivado"
 
 
@@ -540,7 +567,7 @@ msgstr "¿Quieres eliminar esta transmisión?"
 msgid "Domain"
 msgid "Domain"
 msgstr "Dominio"
 msgstr "Dominio"
 
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "Configuración de dominio creada con éxito"
 msgstr "Configuración de dominio creada con éxito"
 
 
@@ -562,34 +589,34 @@ msgstr "Descargando la última versión"
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Modo de ejecución de prueba habilitado"
 msgstr "Modo de ejecución de prueba habilitado"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: src/views/domain/DomainList.vue:140
 #: src/views/domain/DomainList.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Duplicar"
 msgstr "Duplicar"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:84
-#: src/views/stream/components/StreamDuplicate.vue:84
+#: src/views/domain/components/SiteDuplicate.vue:82
+#: src/views/stream/components/StreamDuplicate.vue:82
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Duplicado con éxito de %{conf_name} a %{node_name}"
 msgstr "Duplicado con éxito de %{conf_name} a %{node_name}"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:90
-#: src/views/stream/components/StreamDuplicate.vue:90
+#: src/views/domain/components/SiteDuplicate.vue:87
+#: src/views/stream/components/StreamDuplicate.vue:87
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Duplicado fallido"
 msgstr "Duplicado fallido"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:82
+#: src/views/domain/components/SiteDuplicate.vue:80
+#: src/views/stream/components/StreamDuplicate.vue:80
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Duplicado con éxito"
 msgstr "Duplicado con éxito"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:64
-#: src/views/stream/components/StreamDuplicate.vue:64
+#: src/views/domain/components/SiteDuplicate.vue:63
+#: src/views/stream/components/StreamDuplicate.vue:63
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Duplicado con éxito a local"
 msgstr "Duplicado con éxito a local"
 
 
-#: src/views/domain/DomainEdit.vue:174 src/views/stream/StreamEdit.vue:166
+#: src/views/domain/DomainEdit.vue:172 src/views/stream/StreamEdit.vue:164
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Editar %{n}"
 msgstr "Editar %{n}"
 
 
@@ -610,24 +637,24 @@ msgstr "Editar Transmisión"
 msgid "Email"
 msgid "Email"
 msgstr "Correo (*)"
 msgstr "Correo (*)"
 
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Correo (*)"
 msgstr "Correo (*)"
 
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: src/views/domain/DomainList.vue:132
 #: src/views/domain/DomainList.vue:132
-#: src/views/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #: src/views/stream/StreamList.vue:153
 msgid "Enable"
 msgid "Enable"
 msgstr "Habilitar"
 msgstr "Habilitar"
 
 
-#: src/views/domain/components/Deploy.vue:52
-#: src/views/stream/components/Deploy.vue:52
+#: src/views/domain/components/Deploy.vue:50
+#: src/views/stream/components/Deploy.vue:50
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "Falló el habilitado de %{conf_name} en %{node_name}"
 msgstr "Falló el habilitado de %{conf_name} en %{node_name}"
 
 
-#: src/views/domain/components/Deploy.vue:46
-#: src/views/stream/components/Deploy.vue:46
+#: src/views/domain/components/Deploy.vue:45
+#: src/views/stream/components/Deploy.vue:45
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "Habilitado exitoso de %{conf_name} en %{node_name}"
 msgstr "Habilitado exitoso de %{conf_name} en %{node_name}"
 
 
@@ -635,12 +662,12 @@ msgstr "Habilitado exitoso de %{conf_name} en %{node_name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "No se pudo activar la renovación automática por %{name}"
 msgstr "No se pudo activar la renovación automática por %{name}"
 
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 msgid "Enable failed"
 msgstr "Falló la habilitación"
 msgstr "Falló la habilitación"
 
 
-#: src/views/domain/components/Deploy.vue:44
-#: src/views/stream/components/Deploy.vue:44
+#: src/views/domain/components/Deploy.vue:43
+#: src/views/stream/components/Deploy.vue:43
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Habilitado con Éxito"
 msgstr "Habilitado con Éxito"
 
 
@@ -650,19 +677,19 @@ msgstr "Habilitar TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Habilitado"
 msgstr "Habilitado"
 
 
 #: src/views/domain/components/RightSettings.vue:29
 #: src/views/domain/components/RightSettings.vue:29
-#: src/views/domain/components/SiteDuplicate.vue:98
-#: src/views/domain/DomainAdd.vue:42 src/views/domain/DomainList.vue:57
+#: src/views/domain/components/SiteDuplicate.vue:94
+#: src/views/domain/DomainAdd.vue:40 src/views/domain/DomainList.vue:57
 #: src/views/stream/components/RightSettings.vue:29
 #: src/views/stream/components/RightSettings.vue:29
-#: src/views/stream/components/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Habilitado con éxito"
 msgstr "Habilitado con éxito"
@@ -731,7 +758,7 @@ msgstr "Error al habilitar %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "No se pudo obtener la información del certificado"
 msgstr "No se pudo obtener la información del certificado"
 
 
-#: src/views/domain/DomainEdit.vue:132 src/views/stream/StreamEdit.vue:124
+#: src/views/domain/DomainEdit.vue:130 src/views/stream/StreamEdit.vue:122
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr ""
 msgstr ""
 "No se pudo guardar, se detectó un error(es) de sintaxis en la configuración."
 "No se pudo guardar, se detectó un error(es) de sintaxis en la configuración."
@@ -757,7 +784,7 @@ msgstr "Archivo no encontrado"
 msgid "Filter"
 msgid "Filter"
 msgstr "Filtro"
 msgstr "Filtro"
 
 
-#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:101
+#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgid "Finished"
 msgstr "Terminado"
 msgstr "Terminado"
 
 
@@ -825,6 +852,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:60
+msgid ""
+"If the number of login failed attempts from a ip reach the max attempts in "
+"ban threshold minutes, the ip will be banned for a period of time."
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:136
 #: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgid "Import"
 msgstr "Importar"
 msgstr "Importar"
@@ -833,6 +866,11 @@ msgstr "Importar"
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "Importar Certificado"
 msgstr "Importar Certificado"
 
 
+#: src/views/other/Login.vue:59
+#, fuzzy
+msgid "Incorrect username or password"
+msgstr "El nombre de usuario o contraseña son incorrectos"
+
 #: src/constants/index.ts:18
 #: src/constants/index.ts:18
 msgid "Info"
 msgid "Info"
 msgstr "Información"
 msgstr "Información"
@@ -845,7 +883,7 @@ msgstr "Error de actualización de kernel inicial"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Inicializando la actualización del kernel"
 msgstr "Inicializando la actualización del kernel"
 
 
-#: src/routes/index.ts:273 src/views/other/Install.vue:136
+#: src/routes/index.ts:273 src/views/other/Install.vue:135
 msgid "Install"
 msgid "Install"
 msgstr "Instalar"
 msgstr "Instalar"
 
 
@@ -866,6 +904,10 @@ msgstr ""
 msgid "Invalid"
 msgid "Invalid"
 msgstr "Válido"
 msgstr "Válido"
 
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #: src/views/certificate/Certificate.vue:144
 msgid "Issue wildcard certificate"
 msgid "Issue wildcard certificate"
 msgstr "Obtener certificado comodín"
 msgstr "Obtener certificado comodín"
@@ -897,7 +939,7 @@ msgstr "Comprobado por última vez el"
 msgid "Leave blank for no change"
 msgid "Leave blank for no change"
 msgstr "Para no modificar dejar en blanco"
 msgstr "Para no modificar dejar en blanco"
 
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Dejar en blanco para el valor predeterminado: https://api.openai.com/"
 msgstr "Dejar en blanco para el valor predeterminado: https://api.openai.com/"
 
 
@@ -949,11 +991,11 @@ msgstr "Ubicaciones"
 msgid "Log"
 msgid "Log"
 msgstr "Registro"
 msgstr "Registro"
 
 
-#: src/routes/index.ts:279 src/views/other/Login.vue:147
+#: src/routes/index.ts:279 src/views/other/Login.vue:159
 msgid "Login"
 msgid "Login"
 msgstr "Acceso"
 msgstr "Acceso"
 
 
-#: src/views/other/Login.vue:51 src/views/other/Login.vue:97
+#: src/views/other/Login.vue:109 src/views/other/Login.vue:51
 msgid "Login successful"
 msgid "Login successful"
 msgstr "Acceso exitoso"
 msgstr "Acceso exitoso"
 
 
@@ -961,7 +1003,7 @@ msgstr "Acceso exitoso"
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "Cierre de sesión exitoso"
 msgstr "Cierre de sesión exitoso"
 
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgid "Logrotate"
 msgstr ""
 msgstr ""
 
 
@@ -1003,6 +1045,10 @@ msgstr "Administrar usuarios"
 msgid "Managed Certificate"
 msgid "Managed Certificate"
 msgstr "Certificado Administrado"
 msgstr "Certificado Administrado"
 
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgid "Memory"
@@ -1016,7 +1062,7 @@ msgstr "Memoria y almacenamiento"
 msgid "Minutes"
 msgid "Minutes"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 #, fuzzy
 msgid "Model"
 msgid "Model"
 msgstr "Modo de ejecución"
 msgstr "Modo de ejecución"
@@ -1031,7 +1077,7 @@ msgstr "Modificar"
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Modificar Certificado"
 msgstr "Modificar Certificado"
 
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgid "Modify Config"
 msgstr "Modificar configuración"
 msgstr "Modificar configuración"
 
 
@@ -1050,12 +1096,12 @@ msgstr "Directiva multilínea"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/RightSettings.vue:83
-#: src/views/domain/components/SiteDuplicate.vue:133
+#: src/views/domain/components/SiteDuplicate.vue:129
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: src/views/environment/Environment.vue:12
 #: src/views/stream/components/RightSettings.vue:82
 #: src/views/stream/components/RightSettings.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:133
+#: src/views/stream/components/StreamDuplicate.vue:129
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 msgid "Name"
 msgid "Name"
 msgstr "Nombre"
 msgstr "Nombre"
@@ -1082,11 +1128,11 @@ msgstr "Se liberó una nueva versión"
 
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgid "Next"
 msgstr "Siguiente"
 msgstr "Siguiente"
 
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgid "Nginx"
 msgstr "Nginx"
 msgstr "Nginx"
 
 
@@ -1094,7 +1140,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Ruta de registro de acceso de Nginx"
 msgstr "Ruta de registro de acceso de Nginx"
 
 
-#: src/views/domain/DomainEdit.vue:217 src/views/stream/StreamEdit.vue:209
+#: src/views/domain/DomainEdit.vue:215 src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Error de análisis de configuración de Nginx"
 msgstr "Error de análisis de configuración de Nginx"
 
 
@@ -1119,7 +1165,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx reiniciado con éxito"
 msgstr "Nginx reiniciado con éxito"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/ChatGPT/ChatGPT.vue:270
-#: src/components/Notification/Notification.vue:82
+#: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
@@ -1127,6 +1173,7 @@ msgstr "Nginx reiniciado con éxito"
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 #: src/views/stream/StreamList.vue:165
 msgid "No"
 msgid "No"
@@ -1157,7 +1204,7 @@ msgstr "Nota"
 msgid "Notification"
 msgid "Notification"
 msgstr "Notificación"
 msgstr "Notificación"
 
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Notificaciones"
 msgstr "Notificaciones"
 
 
@@ -1181,7 +1228,7 @@ msgid "Ok"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/ChatGPT/ChatGPT.vue:271
-#: src/components/Notification/Notification.vue:83
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1211,7 +1258,7 @@ msgstr "Una vez que se complete la verificación, los registros se eliminarán."
 msgid "Online"
 msgid "Online"
 msgstr "En línea"
 msgstr "En línea"
 
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgid "OpenAI"
 msgstr "OpenAI"
 msgstr "OpenAI"
 
 
@@ -1223,13 +1270,13 @@ msgstr "SO"
 msgid "OS:"
 msgid "OS:"
 msgstr "SO:"
 msgstr "SO:"
 
 
-#: src/views/domain/components/Deploy.vue:90
-#: src/views/stream/components/Deploy.vue:90
+#: src/views/domain/components/Deploy.vue:87
+#: src/views/stream/components/Deploy.vue:87
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr "Sobrescribir"
 msgstr "Sobrescribir"
 
 
-#: src/views/domain/components/Deploy.vue:94
-#: src/views/stream/components/Deploy.vue:94
+#: src/views/domain/components/Deploy.vue:91
+#: src/views/stream/components/Deploy.vue:91
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr "Sobrescribir archivo existente"
 msgstr "Sobrescribir archivo existente"
 
 
@@ -1237,11 +1284,11 @@ msgstr "Sobrescribir archivo existente"
 msgid "Params"
 msgid "Params"
 msgstr "Parámetros"
 msgstr "Parámetros"
 
 
-#: src/views/other/Login.vue:132 src/views/user/User.vue:18
+#: src/views/other/Login.vue:144 src/views/user/User.vue:18
 msgid "Password"
 msgid "Password"
 msgstr "Contraseña"
 msgstr "Contraseña"
 
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgid "Password (*)"
 msgstr "Contraseña (*)"
 msgstr "Contraseña (*)"
 
 
@@ -1313,7 +1360,7 @@ msgstr "¡Seleccione al menos un nodo!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Prelanzamiento"
 msgstr "Prelanzamiento"
 
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:105
 msgid "Preference"
 msgid "Preference"
 msgstr "Configuración"
 msgstr "Configuración"
 
 
@@ -1409,7 +1456,16 @@ msgstr "Recargando"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "Recargando Nginx"
 msgstr "Recargando Nginx"
 
 
-#: src/components/Notification/Notification.vue:50
+#: src/views/preference/AuthSettings.vue:101
+msgid "Remove"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:47
+#, fuzzy
+msgid "Remove successfully"
+msgstr "Eliminado con éxito"
+
+#: src/components/Notification/Notification.vue:52
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Eliminado con éxito"
 msgstr "Eliminado con éxito"
 
 
@@ -1461,9 +1517,9 @@ msgstr "Corriendo"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/certificate/CertificateEditor.vue:249
-#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
+#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:261
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgid "Save"
 msgstr "Guardar"
 msgstr "Guardar"
 
 
@@ -1471,7 +1527,7 @@ msgstr "Guardar"
 msgid "Save Directive"
 msgid "Save Directive"
 msgstr "Guardar Directiva"
 msgstr "Guardar Directiva"
 
 
-#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:50
+#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:46
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Error al guardar %{msg}"
 msgstr "Error al guardar %{msg}"
@@ -1479,14 +1535,14 @@ msgstr "Error al guardar %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "Guardado con éxito"
 msgstr "Guardado con éxito"
 
 
-#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:38
-#: src/views/domain/DomainEdit.vue:148
+#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:37
+#: src/views/domain/DomainEdit.vue:146
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
-#: src/views/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Guardado con éxito"
 msgstr "Guardado con éxito"
 
 
@@ -1511,8 +1567,9 @@ msgstr "Enviado"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
+#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:69
+#: src/views/preference/AuthSettings.vue:49
+#: src/views/preference/Preference.vue:78 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
 msgstr "Error del servidor"
 msgstr "Error del servidor"
@@ -1531,7 +1588,7 @@ msgid "server_name not found in directives"
 msgstr "No se encuentra server_name en las directivas"
 msgstr "No se encuentra server_name en las directivas"
 
 
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
-#: src/views/domain/DomainAdd.vue:121
+#: src/views/domain/DomainAdd.vue:117
 msgid "server_name parameter is required"
 msgid "server_name parameter is required"
 msgstr "Se requiere el parámetro server_name"
 msgstr "Se requiere el parámetro server_name"
 
 
@@ -1583,7 +1640,7 @@ msgstr "Ruta de la llave del certificado SSL"
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "Ruta del certificado SSL"
 msgstr "Ruta del certificado SSL"
 
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 msgid "SSO Login"
 msgid "SSO Login"
 msgstr "Acceso SSO"
 msgstr "Acceso SSO"
 
 
@@ -1667,8 +1724,8 @@ msgstr ""
 msgid "System"
 msgid "System"
 msgstr "Sistema"
 msgstr "Sistema"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:140
+#: src/views/domain/components/SiteDuplicate.vue:136
+#: src/views/stream/components/StreamDuplicate.vue:136
 msgid "Target"
 msgid "Target"
 msgstr "Objetivo"
 msgstr "Objetivo"
 
 
@@ -1701,7 +1758,7 @@ msgstr "La entrada no es un Certificado SSL"
 msgid "The input is not a SSL Certificate Key"
 msgid "The input is not a SSL Certificate Key"
 msgstr "La entrada no es una clave de certificado SSL"
 msgstr "La entrada no es una clave de certificado SSL"
 
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid ""
 msgid ""
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "dashes, and dots."
 "dashes, and dots."
@@ -1736,8 +1793,8 @@ msgstr ""
 msgid "The url is invalid"
 msgid "The url is invalid"
 msgstr "La URL no es válida"
 msgstr "La URL no es válida"
 
 
-#: src/views/preference/OpenAISettings.vue:45
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:48
+#: src/views/preference/OpenAISettings.vue:60
 #, fuzzy
 #, fuzzy
 msgid "The url is invalid."
 msgid "The url is invalid."
 msgstr "La URL no es válida"
 msgstr "La URL no es válida"
@@ -1764,6 +1821,7 @@ msgstr "Este campo es obligatorio"
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr "Este campo no debe estar vacío"
 msgstr "Este campo no debe estar vacío"
 
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgid "Tips"
 msgstr ""
 msgstr ""
@@ -1784,10 +1842,14 @@ msgstr ""
 "de la autoridad al backend, y debemos guardar este archivo y volver a cargar "
 "de la autoridad al backend, y debemos guardar este archivo y volver a cargar "
 "Nginx. ¿Estás seguro de que quieres continuar?"
 "Nginx. ¿Estás seguro de que quieres continuar?"
 
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 msgid "Token is not valid"
 msgstr "El token no es válido"
 msgstr "El token no es válido"
 
 
+#: src/views/other/Login.vue:62
+msgid "Too many login failed attempts, please try again later"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 msgid "Trash"
 msgid "Trash"
 msgstr ""
 msgstr ""
@@ -1840,11 +1902,15 @@ msgstr "URL"
 msgid "User"
 msgid "User"
 msgstr "Usuario"
 msgstr "Usuario"
 
 
-#: src/views/other/Login.vue:122 src/views/user/User.vue:9
+#: src/views/other/Login.vue:65
+msgid "User is banned"
+msgstr ""
+
+#: src/views/other/Login.vue:134 src/views/user/User.vue:9
 msgid "Username"
 msgid "Username"
 msgstr "Nombre de usuario"
 msgstr "Nombre de usuario"
 
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "Nombre de usuario (*)"
 msgstr "Nombre de usuario (*)"
 
 
@@ -1858,7 +1924,7 @@ msgstr "Válido"
 msgid "View"
 msgid "View"
 msgstr "Ver"
 msgstr "Ver"
 
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Ver todas las notificaciones"
 msgstr "Ver todas las notificaciones"
 
 
@@ -1874,7 +1940,7 @@ msgstr "Modo Básico"
 
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgid "Warning"
 msgstr "Advertencia"
 msgstr "Advertencia"
 
 
@@ -1909,6 +1975,7 @@ msgstr "Escribir certificado a disco"
 
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgid "Yes"
 msgstr "Si"
 msgstr "Si"

+ 165 - 98
app/src/language/fr_FR/app.po

@@ -30,7 +30,8 @@ msgstr "Nom d'utilisateur"
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
 #: src/views/notification/Notification.vue:37
-#: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
+#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47
+#: src/views/user/User.vue:43
 msgid "Action"
 msgid "Action"
 msgstr "Action"
 msgstr "Action"
 
 
@@ -53,7 +54,7 @@ msgstr "Ajouter une directive"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Ajouter une localisation"
 msgstr "Ajouter une localisation"
 
 
-#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:93
+#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Ajouter un site"
 msgstr "Ajouter un site"
 
 
@@ -72,11 +73,11 @@ msgstr "Mis à jour avec succés"
 msgid "Additional"
 msgid "Additional"
 msgstr "Supplémentaire"
 msgstr "Supplémentaire"
 
 
-#: src/views/domain/DomainEdit.vue:199 src/views/stream/StreamEdit.vue:191
+#: src/views/domain/DomainEdit.vue:197 src/views/stream/StreamEdit.vue:189
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Mode avancé"
 msgstr "Mode avancé"
 
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgid "API Base Url"
 msgstr "URL de base de l'API"
 msgstr "URL de base de l'API"
 
 
@@ -85,11 +86,11 @@ msgstr "URL de base de l'API"
 msgid "API Document"
 msgid "API Document"
 msgstr "Jeton d'API"
 msgstr "Jeton d'API"
 
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgid "API Proxy"
 msgstr "Proxy d'API"
 msgstr "Proxy d'API"
 
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgid "API Token"
 msgstr "Jeton d'API"
 msgstr "Jeton d'API"
 
 
@@ -98,7 +99,12 @@ msgstr "Jeton d'API"
 msgid "Arch"
 msgid "Arch"
 msgstr "Arch"
 msgstr "Arch"
 
 
-#: src/components/Notification/Notification.vue:84
+#: src/views/preference/AuthSettings.vue:94
+#, fuzzy
+msgid "Are you sure to delete this banned IP immediately?"
+msgstr "Etes-vous sûr que vous voulez supprimer ?"
+
+#: src/components/Notification/Notification.vue:86
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgid "Are you sure you want to clear all notifications?"
@@ -149,6 +155,15 @@ msgstr "Modèle ChatGPT"
 msgid "Assistant"
 msgid "Assistant"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:17
+msgid "Attempts"
+msgstr ""
+
+#: src/views/preference/Preference.vue:116
+#, fuzzy
+msgid "Auth"
+msgstr "Autheur"
+
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 msgid "Author"
 msgid "Author"
@@ -172,8 +187,8 @@ msgstr "Renouvellement automatique activé pour %{name}"
 
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
-#: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
-#: src/views/stream/StreamEdit.vue:247
+#: src/views/domain/DomainEdit.vue:254 src/views/nginx_log/NginxLog.vue:168
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgid "Back"
 msgstr "Retour"
 msgstr "Retour"
 
 
@@ -181,18 +196,30 @@ msgstr "Retour"
 msgid "Back Home"
 msgid "Back Home"
 msgstr "Retour au menu principal"
 msgstr "Retour au menu principal"
 
 
-#: src/views/domain/DomainAdd.vue:99
+#: src/views/preference/AuthSettings.vue:68
+msgid "Ban Threshold Minutes"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:82
+msgid "Banned IPs"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:20
+msgid "Banned Until"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:95
 msgid "Base information"
 msgid "Base information"
 msgstr "Information générale"
 msgstr "Information générale"
 
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:101
+#: src/views/preference/Preference.vue:110
 #: src/views/stream/components/RightSettings.vue:74
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgid "Basic"
 msgstr "Basique"
 msgstr "Basique"
 
 
-#: src/views/domain/DomainEdit.vue:202 src/views/stream/StreamEdit.vue:194
+#: src/views/domain/DomainEdit.vue:200 src/views/stream/StreamEdit.vue:192
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Mode simple"
 msgstr "Mode simple"
 
 
@@ -279,12 +306,12 @@ msgid "Cleaning environment variables"
 msgstr "Nettoyage des variables d'environnement"
 msgstr "Nettoyage des variables d'environnement"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgid "Clear"
 msgstr "Effacer"
 msgstr "Effacer"
 
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 #, fuzzy
 msgid "Cleared successfully"
 msgid "Cleared successfully"
@@ -310,7 +337,7 @@ msgstr "Modèles de configuration"
 msgid "Configuration file is test successful"
 msgid "Configuration file is test successful"
 msgstr "Le fichier de configuration est testé avec succès"
 msgstr "Le fichier de configuration est testé avec succès"
 
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgid "Configuration Name"
 msgstr "Nom de la configuration"
 msgstr "Nom de la configuration"
 
 
@@ -318,7 +345,7 @@ msgstr "Nom de la configuration"
 msgid "Configurations"
 msgid "Configurations"
 msgstr "Configurations"
 msgstr "Configurations"
 
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgid "Configure SSL"
 msgstr "Configurer SSL"
 msgstr "Configurer SSL"
 
 
@@ -349,7 +376,7 @@ msgstr "CPU :"
 msgid "Create"
 msgid "Create"
 msgstr "Créé le"
 msgstr "Créé le"
 
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Créer un autre"
 msgstr "Créer un autre"
 
 
@@ -388,7 +415,7 @@ msgstr ""
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Dashboard"
 msgstr "Dashboard"
 
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "Base de données (Facultatif, par défaut : database)"
 msgstr "Base de données (Facultatif, par défaut : database)"
 
 
@@ -422,15 +449,15 @@ msgstr "Supprimer le site : %{site_name}"
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr "Désactivé avec succès"
 msgstr "Désactivé avec succès"
 
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: src/views/domain/components/RightSettings.vue:93
 #: src/views/domain/components/RightSettings.vue:93
-#: src/views/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 msgid "Deploy"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:63
-#: src/views/stream/components/Deploy.vue:63
+#: src/views/domain/components/Deploy.vue:60
+#: src/views/stream/components/Deploy.vue:60
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
@@ -480,9 +507,9 @@ msgstr "Désactivé"
 msgid "Disable auto-renewal failed for %{name}"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "La désactivation du renouvellement automatique a échoué pour %{name}"
 msgstr "La désactivation du renouvellement automatique a échoué pour %{name}"
 
 
-#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:183
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Désactivé"
 msgstr "Désactivé"
 
 
@@ -557,7 +584,7 @@ msgstr "Voulez-vous supprimer ce serveur ?"
 msgid "Domain"
 msgid "Domain"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "La configuration du domaine a été créée avec succès"
 msgstr "La configuration du domaine a été créée avec succès"
 
 
@@ -580,38 +607,38 @@ msgstr "Téléchargement de la dernière version"
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: src/views/domain/DomainList.vue:140
 #: src/views/domain/DomainList.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Dupliquer"
 msgstr "Dupliquer"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:84
-#: src/views/stream/components/StreamDuplicate.vue:84
+#: src/views/domain/components/SiteDuplicate.vue:82
+#: src/views/stream/components/StreamDuplicate.vue:82
 #, fuzzy
 #, fuzzy
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Dupliqué avec succès"
 msgstr "Dupliqué avec succès"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:90
-#: src/views/stream/components/StreamDuplicate.vue:90
+#: src/views/domain/components/SiteDuplicate.vue:87
+#: src/views/stream/components/StreamDuplicate.vue:87
 #, fuzzy
 #, fuzzy
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Dupliquer"
 msgstr "Dupliquer"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:82
+#: src/views/domain/components/SiteDuplicate.vue:80
+#: src/views/stream/components/StreamDuplicate.vue:80
 #, fuzzy
 #, fuzzy
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Dupliqué avec succès"
 msgstr "Dupliqué avec succès"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:64
-#: src/views/stream/components/StreamDuplicate.vue:64
+#: src/views/domain/components/SiteDuplicate.vue:63
+#: src/views/stream/components/StreamDuplicate.vue:63
 #, fuzzy
 #, fuzzy
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Dupliqué avec succès"
 msgstr "Dupliqué avec succès"
 
 
-#: src/views/domain/DomainEdit.vue:174 src/views/stream/StreamEdit.vue:166
+#: src/views/domain/DomainEdit.vue:172 src/views/stream/StreamEdit.vue:164
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Modifier %{n}"
 msgstr "Modifier %{n}"
 
 
@@ -633,25 +660,25 @@ msgstr "Modifier le site"
 msgid "Email"
 msgid "Email"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: src/views/domain/DomainList.vue:132
 #: src/views/domain/DomainList.vue:132
-#: src/views/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #: src/views/stream/StreamList.vue:153
 #, fuzzy
 #, fuzzy
 msgid "Enable"
 msgid "Enable"
 msgstr "Activé"
 msgstr "Activé"
 
 
-#: src/views/domain/components/Deploy.vue:52
-#: src/views/stream/components/Deploy.vue:52
+#: src/views/domain/components/Deploy.vue:50
+#: src/views/stream/components/Deploy.vue:50
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:46
-#: src/views/stream/components/Deploy.vue:46
+#: src/views/domain/components/Deploy.vue:45
+#: src/views/stream/components/Deploy.vue:45
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
@@ -659,12 +686,12 @@ msgstr ""
 msgid "Enable auto-renewal failed for %{name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "Échec de l'activation du renouvellement automatique pour %{name}"
 msgstr "Échec de l'activation du renouvellement automatique pour %{name}"
 
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 msgid "Enable failed"
 msgstr "Échec de l'activation"
 msgstr "Échec de l'activation"
 
 
-#: src/views/domain/components/Deploy.vue:44
-#: src/views/stream/components/Deploy.vue:44
+#: src/views/domain/components/Deploy.vue:43
+#: src/views/stream/components/Deploy.vue:43
 #, fuzzy
 #, fuzzy
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Activé avec succès"
 msgstr "Activé avec succès"
@@ -675,19 +702,19 @@ msgstr "Activer TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Activé"
 msgstr "Activé"
 
 
 #: src/views/domain/components/RightSettings.vue:29
 #: src/views/domain/components/RightSettings.vue:29
-#: src/views/domain/components/SiteDuplicate.vue:98
-#: src/views/domain/DomainAdd.vue:42 src/views/domain/DomainList.vue:57
+#: src/views/domain/components/SiteDuplicate.vue:94
+#: src/views/domain/DomainAdd.vue:40 src/views/domain/DomainList.vue:57
 #: src/views/stream/components/RightSettings.vue:29
 #: src/views/stream/components/RightSettings.vue:29
-#: src/views/stream/components/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Activé avec succès"
 msgstr "Activé avec succès"
@@ -758,7 +785,7 @@ msgstr "Impossible d'activer %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "Échec de l'obtention des informations sur le certificat"
 msgstr "Échec de l'obtention des informations sur le certificat"
 
 
-#: src/views/domain/DomainEdit.vue:132 src/views/stream/StreamEdit.vue:124
+#: src/views/domain/DomainEdit.vue:130 src/views/stream/StreamEdit.vue:122
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr ""
 msgstr ""
 "Échec de l'enregistrement, une ou plusieurs erreurs de syntaxe ont été "
 "Échec de l'enregistrement, une ou plusieurs erreurs de syntaxe ont été "
@@ -786,7 +813,7 @@ msgstr "Fichier introuvable"
 msgid "Filter"
 msgid "Filter"
 msgstr "Filtrer"
 msgstr "Filtrer"
 
 
-#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:101
+#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgid "Finished"
 msgstr "Finie"
 msgstr "Finie"
 
 
@@ -856,6 +883,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:60
+msgid ""
+"If the number of login failed attempts from a ip reach the max attempts in "
+"ban threshold minutes, the ip will be banned for a period of time."
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:136
 #: src/views/certificate/Certificate.vue:136
 #, fuzzy
 #, fuzzy
 msgid "Import"
 msgid "Import"
@@ -866,6 +899,11 @@ msgstr "Exporter"
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "État du certificat"
 msgstr "État du certificat"
 
 
+#: src/views/other/Login.vue:59
+#, fuzzy
+msgid "Incorrect username or password"
+msgstr "Le pseudo ou mot de passe est incorect"
+
 #: src/constants/index.ts:18
 #: src/constants/index.ts:18
 msgid "Info"
 msgid "Info"
 msgstr ""
 msgstr ""
@@ -878,7 +916,7 @@ msgstr "Erreur du programme de mise à niveau initial du core"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Initialisation du programme de mise à niveau du core"
 msgstr "Initialisation du programme de mise à niveau du core"
 
 
-#: src/routes/index.ts:273 src/views/other/Install.vue:136
+#: src/routes/index.ts:273 src/views/other/Install.vue:135
 msgid "Install"
 msgid "Install"
 msgstr "Installer"
 msgstr "Installer"
 
 
@@ -898,6 +936,10 @@ msgstr ""
 msgid "Invalid"
 msgid "Invalid"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgid "Issue wildcard certificate"
@@ -931,7 +973,7 @@ msgstr "Dernière vérification le"
 msgid "Leave blank for no change"
 msgid "Leave blank for no change"
 msgstr "Laisser vide pour aucun changement"
 msgstr "Laisser vide pour aucun changement"
 
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Laissez vide pour la valeur par défaut : https://api.openai.com/"
 msgstr "Laissez vide pour la valeur par défaut : https://api.openai.com/"
 
 
@@ -990,11 +1032,11 @@ msgstr "Localisations"
 msgid "Log"
 msgid "Log"
 msgstr "Connexion"
 msgstr "Connexion"
 
 
-#: src/routes/index.ts:279 src/views/other/Login.vue:147
+#: src/routes/index.ts:279 src/views/other/Login.vue:159
 msgid "Login"
 msgid "Login"
 msgstr "Connexion"
 msgstr "Connexion"
 
 
-#: src/views/other/Login.vue:51 src/views/other/Login.vue:97
+#: src/views/other/Login.vue:109 src/views/other/Login.vue:51
 msgid "Login successful"
 msgid "Login successful"
 msgstr "Connexion réussie"
 msgstr "Connexion réussie"
 
 
@@ -1002,7 +1044,7 @@ msgstr "Connexion réussie"
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "Déconnexion réussie"
 msgstr "Déconnexion réussie"
 
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgid "Logrotate"
 msgstr ""
 msgstr ""
 
 
@@ -1047,6 +1089,10 @@ msgstr "Gérer les utilisateurs"
 msgid "Managed Certificate"
 msgid "Managed Certificate"
 msgstr "Changer de certificat"
 msgstr "Changer de certificat"
 
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgid "Memory"
@@ -1060,7 +1106,7 @@ msgstr "Mémoire et stockage"
 msgid "Minutes"
 msgid "Minutes"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 #, fuzzy
 msgid "Model"
 msgid "Model"
 msgstr "Mode d'exécution"
 msgstr "Mode d'exécution"
@@ -1076,7 +1122,7 @@ msgstr "Modifier"
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "État du certificat"
 msgstr "État du certificat"
 
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgid "Modify Config"
 msgstr "Modifier la configuration"
 msgstr "Modifier la configuration"
 
 
@@ -1095,12 +1141,12 @@ msgstr "Directive multiligne"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/RightSettings.vue:83
-#: src/views/domain/components/SiteDuplicate.vue:133
+#: src/views/domain/components/SiteDuplicate.vue:129
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: src/views/environment/Environment.vue:12
 #: src/views/stream/components/RightSettings.vue:82
 #: src/views/stream/components/RightSettings.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:133
+#: src/views/stream/components/StreamDuplicate.vue:129
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 msgid "Name"
 msgid "Name"
 msgstr "Nom"
 msgstr "Nom"
@@ -1127,11 +1173,11 @@ msgstr "Nouvelle version publiée"
 
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgid "Next"
 msgstr "Suivant"
 msgstr "Suivant"
 
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 #, fuzzy
 #, fuzzy
 msgid "Nginx"
 msgid "Nginx"
 msgstr "Journal Nginx"
 msgstr "Journal Nginx"
@@ -1140,7 +1186,7 @@ msgstr "Journal Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Chemin du journal d'accès Nginx"
 msgstr "Chemin du journal d'accès Nginx"
 
 
-#: src/views/domain/DomainEdit.vue:217 src/views/stream/StreamEdit.vue:209
+#: src/views/domain/DomainEdit.vue:215 src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Erreur d'analyse de configuration Nginx"
 msgstr "Erreur d'analyse de configuration Nginx"
 
 
@@ -1165,7 +1211,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx a redémarré avec succès"
 msgstr "Nginx a redémarré avec succès"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/ChatGPT/ChatGPT.vue:270
-#: src/components/Notification/Notification.vue:82
+#: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
@@ -1173,6 +1219,7 @@ msgstr "Nginx a redémarré avec succès"
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 #: src/views/stream/StreamList.vue:165
 msgid "No"
 msgid "No"
@@ -1205,7 +1252,7 @@ msgstr "Note"
 msgid "Notification"
 msgid "Notification"
 msgstr "Certification"
 msgstr "Certification"
 
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Certification"
 msgstr "Certification"
@@ -1230,7 +1277,7 @@ msgid "Ok"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/ChatGPT/ChatGPT.vue:271
-#: src/components/Notification/Notification.vue:83
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1260,7 +1307,7 @@ msgstr ""
 msgid "Online"
 msgid "Online"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgid "OpenAI"
 msgstr "OpenAI"
 msgstr "OpenAI"
 
 
@@ -1272,13 +1319,13 @@ msgstr "OS"
 msgid "OS:"
 msgid "OS:"
 msgstr "OS :"
 msgstr "OS :"
 
 
-#: src/views/domain/components/Deploy.vue:90
-#: src/views/stream/components/Deploy.vue:90
+#: src/views/domain/components/Deploy.vue:87
+#: src/views/stream/components/Deploy.vue:87
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:94
-#: src/views/stream/components/Deploy.vue:94
+#: src/views/domain/components/Deploy.vue:91
+#: src/views/stream/components/Deploy.vue:91
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr ""
 msgstr ""
 
 
@@ -1286,11 +1333,11 @@ msgstr ""
 msgid "Params"
 msgid "Params"
 msgstr "Paramètres"
 msgstr "Paramètres"
 
 
-#: src/views/other/Login.vue:132 src/views/user/User.vue:18
+#: src/views/other/Login.vue:144 src/views/user/User.vue:18
 msgid "Password"
 msgid "Password"
 msgstr "Mot de passe"
 msgstr "Mot de passe"
 
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgid "Password (*)"
 msgstr "Mot de passe (*)"
 msgstr "Mot de passe (*)"
 
 
@@ -1360,7 +1407,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:105
 msgid "Preference"
 msgid "Preference"
 msgstr "Préférence"
 msgstr "Préférence"
 
 
@@ -1459,7 +1506,16 @@ msgstr "Rechargement"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "Rechargement de nginx"
 msgstr "Rechargement de nginx"
 
 
-#: src/components/Notification/Notification.vue:50
+#: src/views/preference/AuthSettings.vue:101
+msgid "Remove"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:47
+#, fuzzy
+msgid "Remove successfully"
+msgstr "Enregistré avec succès"
+
+#: src/components/Notification/Notification.vue:52
 #, fuzzy
 #, fuzzy
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Enregistré avec succès"
 msgstr "Enregistré avec succès"
@@ -1517,9 +1573,9 @@ msgstr "En cours d'éxécution"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/certificate/CertificateEditor.vue:249
-#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
+#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:261
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgid "Save"
 msgstr "Enregistrer"
 msgstr "Enregistrer"
 
 
@@ -1527,7 +1583,7 @@ msgstr "Enregistrer"
 msgid "Save Directive"
 msgid "Save Directive"
 msgstr "Enregistrer la directive"
 msgstr "Enregistrer la directive"
 
 
-#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:50
+#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:46
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Enregistrer l'erreur %{msg}"
 msgstr "Enregistrer l'erreur %{msg}"
@@ -1535,14 +1591,14 @@ msgstr "Enregistrer l'erreur %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "Sauvegarde réussie"
 msgstr "Sauvegarde réussie"
 
 
-#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:38
-#: src/views/domain/DomainEdit.vue:148
+#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:37
+#: src/views/domain/DomainEdit.vue:146
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
-#: src/views/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Enregistré avec succès"
 msgstr "Enregistré avec succès"
 
 
@@ -1567,8 +1623,9 @@ msgstr "Envoyer"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
+#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:69
+#: src/views/preference/AuthSettings.vue:49
+#: src/views/preference/Preference.vue:78 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
 msgstr "Erreur du serveur"
 msgstr "Erreur du serveur"
@@ -1587,7 +1644,7 @@ msgid "server_name not found in directives"
 msgstr "server_name introuvable dans les directives"
 msgstr "server_name introuvable dans les directives"
 
 
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
-#: src/views/domain/DomainAdd.vue:121
+#: src/views/domain/DomainAdd.vue:117
 msgid "server_name parameter is required"
 msgid "server_name parameter is required"
 msgstr "Le paramètre server_name est obligatoire"
 msgstr "Le paramètre server_name est obligatoire"
 
 
@@ -1643,7 +1700,7 @@ msgstr "Chemin de la clé du certificat SSL"
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "Chemin du certificat SSL"
 msgstr "Chemin du certificat SSL"
 
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 #, fuzzy
 msgid "SSO Login"
 msgid "SSO Login"
 msgstr "Connexion"
 msgstr "Connexion"
@@ -1731,8 +1788,8 @@ msgstr ""
 msgid "System"
 msgid "System"
 msgstr "Système"
 msgstr "Système"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:140
+#: src/views/domain/components/SiteDuplicate.vue:136
+#: src/views/stream/components/StreamDuplicate.vue:136
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
@@ -1766,7 +1823,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgid "The input is not a SSL Certificate Key"
 msgstr "Chemin de la clé du certificat SSL"
 msgstr "Chemin de la clé du certificat SSL"
 
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid ""
 msgid ""
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "dashes, and dots."
 "dashes, and dots."
@@ -1802,8 +1859,8 @@ msgstr ""
 msgid "The url is invalid"
 msgid "The url is invalid"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:45
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:48
+#: src/views/preference/OpenAISettings.vue:60
 msgid "The url is invalid."
 msgid "The url is invalid."
 msgstr ""
 msgstr ""
 
 
@@ -1832,6 +1889,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgid "Tips"
 msgstr ""
 msgstr ""
@@ -1852,10 +1910,14 @@ msgstr ""
 "transmettre la demande de l'autorité au backend, et nous devons enregistrer "
 "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?"
 "ce fichier et recharger le Nginx. Êtes-vous sûr de vouloir continuer?"
 
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 msgid "Token is not valid"
 msgstr ""
 msgstr ""
 
 
+#: src/views/other/Login.vue:62
+msgid "Too many login failed attempts, please try again later"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 msgid "Trash"
 msgid "Trash"
 msgstr ""
 msgstr ""
@@ -1909,11 +1971,15 @@ msgstr ""
 msgid "User"
 msgid "User"
 msgstr "Nom d'utilisateur"
 msgstr "Nom d'utilisateur"
 
 
-#: src/views/other/Login.vue:122 src/views/user/User.vue:9
+#: src/views/other/Login.vue:65
+msgid "User is banned"
+msgstr ""
+
+#: src/views/other/Login.vue:134 src/views/user/User.vue:9
 msgid "Username"
 msgid "Username"
 msgstr "Nom d'utilisateur"
 msgstr "Nom d'utilisateur"
 
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "Nom d'utilisateur (*)"
 msgstr "Nom d'utilisateur (*)"
 
 
@@ -1927,7 +1993,7 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr "Voir"
 msgstr "Voir"
 
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 #, fuzzy
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Certification"
 msgstr "Certification"
@@ -1943,7 +2009,7 @@ msgstr "Mode simple"
 
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgid "Warning"
 msgstr "Avertissement"
 msgstr "Avertissement"
 
 
@@ -1976,6 +2042,7 @@ msgstr "Écriture du certificat sur le disque"
 
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgid "Yes"
 msgstr "Oui"
 msgstr "Oui"

+ 165 - 98
app/src/language/ko_KR/app.po

@@ -32,7 +32,8 @@ msgstr "사용자 이름"
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
 #: src/views/notification/Notification.vue:37
-#: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
+#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47
+#: src/views/user/User.vue:43
 msgid "Action"
 msgid "Action"
 msgstr "작업"
 msgstr "작업"
 
 
@@ -55,7 +56,7 @@ msgstr "아래에 지시문 추가"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "위치 추가"
 msgstr "위치 추가"
 
 
-#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:93
+#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgid "Add Site"
 msgstr "사이트 추가"
 msgstr "사이트 추가"
 
 
@@ -71,11 +72,11 @@ msgstr "성공적으로 추가됨"
 msgid "Additional"
 msgid "Additional"
 msgstr "추가적인"
 msgstr "추가적인"
 
 
-#: src/views/domain/DomainEdit.vue:199 src/views/stream/StreamEdit.vue:191
+#: src/views/domain/DomainEdit.vue:197 src/views/stream/StreamEdit.vue:189
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "고급 모드"
 msgstr "고급 모드"
 
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgid "API Base Url"
 msgstr "API 기본 URL"
 msgstr "API 기본 URL"
 
 
@@ -84,11 +85,11 @@ msgstr "API 기본 URL"
 msgid "API Document"
 msgid "API Document"
 msgstr "API 토큰"
 msgstr "API 토큰"
 
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgid "API Proxy"
 msgstr "API 프록시"
 msgstr "API 프록시"
 
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgid "API Token"
 msgstr "API 토큰"
 msgstr "API 토큰"
 
 
@@ -96,7 +97,12 @@ msgstr "API 토큰"
 msgid "Arch"
 msgid "Arch"
 msgstr "아키텍처"
 msgstr "아키텍처"
 
 
-#: src/components/Notification/Notification.vue:84
+#: src/views/preference/AuthSettings.vue:94
+#, fuzzy
+msgid "Are you sure to delete this banned IP immediately?"
+msgstr "정말 삭제하시겠습니까?"
+
+#: src/components/Notification/Notification.vue:86
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
 msgid "Are you sure you want to clear all notifications?"
 msgid "Are you sure you want to clear all notifications?"
 msgstr "모든 알림을 지우시겠습니까?"
 msgstr "모든 알림을 지우시겠습니까?"
@@ -145,6 +151,15 @@ msgstr "ChatGPT에게 도움 요청"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "조수"
 msgstr "조수"
 
 
+#: src/views/preference/AuthSettings.vue:17
+msgid "Attempts"
+msgstr ""
+
+#: src/views/preference/Preference.vue:116
+#, fuzzy
+msgid "Auth"
+msgstr "저자"
+
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 msgid "Author"
 msgid "Author"
@@ -168,8 +183,8 @@ msgstr "%{name}에 대한 자동 갱신 활성화됨"
 
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
-#: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
-#: src/views/stream/StreamEdit.vue:247
+#: src/views/domain/DomainEdit.vue:254 src/views/nginx_log/NginxLog.vue:168
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgid "Back"
 msgstr "뒤로"
 msgstr "뒤로"
 
 
@@ -177,18 +192,30 @@ msgstr "뒤로"
 msgid "Back Home"
 msgid "Back Home"
 msgstr "홈으로"
 msgstr "홈으로"
 
 
-#: src/views/domain/DomainAdd.vue:99
+#: src/views/preference/AuthSettings.vue:68
+msgid "Ban Threshold Minutes"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:82
+msgid "Banned IPs"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:20
+msgid "Banned Until"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:95
 msgid "Base information"
 msgid "Base information"
 msgstr "기본 정보"
 msgstr "기본 정보"
 
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:101
+#: src/views/preference/Preference.vue:110
 #: src/views/stream/components/RightSettings.vue:74
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgid "Basic"
 msgstr "기본"
 msgstr "기본"
 
 
-#: src/views/domain/DomainEdit.vue:202 src/views/stream/StreamEdit.vue:194
+#: src/views/domain/DomainEdit.vue:200 src/views/stream/StreamEdit.vue:192
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "기본 모드"
 msgstr "기본 모드"
 
 
@@ -272,12 +299,12 @@ msgid "Cleaning environment variables"
 msgstr "환경 변수 정리"
 msgstr "환경 변수 정리"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgid "Clear"
 msgstr "클리어"
 msgstr "클리어"
 
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "성공적으로 제거됨"
 msgstr "성공적으로 제거됨"
@@ -301,7 +328,7 @@ msgstr "구성 템플릿"
 msgid "Configuration file is test successful"
 msgid "Configuration file is test successful"
 msgstr "구성 파일 테스트 성공"
 msgstr "구성 파일 테스트 성공"
 
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgid "Configuration Name"
 msgstr "구성 이름"
 msgstr "구성 이름"
 
 
@@ -309,7 +336,7 @@ msgstr "구성 이름"
 msgid "Configurations"
 msgid "Configurations"
 msgstr "구성들"
 msgstr "구성들"
 
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgid "Configure SSL"
 msgstr "SSL 구성하기"
 msgstr "SSL 구성하기"
 
 
@@ -339,7 +366,7 @@ msgstr "CPU:"
 msgid "Create"
 msgid "Create"
 msgstr "생성"
 msgstr "생성"
 
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgid "Create Another"
 msgstr "다른 것 생성하기"
 msgstr "다른 것 생성하기"
 
 
@@ -378,7 +405,7 @@ msgstr ""
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "대시보드"
 msgstr "대시보드"
 
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "데이터베이스 (선택사항, 기본값: database)"
 msgstr "데이터베이스 (선택사항, 기본값: database)"
 
 
@@ -410,15 +437,15 @@ msgstr "스트림 삭제: %{stream_name}"
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr "성공적으로 삭제됨"
 msgstr "성공적으로 삭제됨"
 
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: src/views/domain/components/RightSettings.vue:93
 #: src/views/domain/components/RightSettings.vue:93
-#: src/views/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 msgid "Deploy"
 msgstr "배포"
 msgstr "배포"
 
 
-#: src/views/domain/components/Deploy.vue:63
-#: src/views/stream/components/Deploy.vue:63
+#: src/views/domain/components/Deploy.vue:60
+#: src/views/stream/components/Deploy.vue:60
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr "%{conf_name}을(를) %{node_name}(으)로 배포 실패"
 msgstr "%{conf_name}을(를) %{node_name}(으)로 배포 실패"
 
 
@@ -465,9 +492,9 @@ msgstr "비활성화"
 msgid "Disable auto-renewal failed for %{name}"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "%{name}의 자동 갱신 비활성화 실패"
 msgstr "%{name}의 자동 갱신 비활성화 실패"
 
 
-#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:183
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgid "Disabled"
 msgstr "비활성화됨"
 msgstr "비활성화됨"
 
 
@@ -538,7 +565,7 @@ msgstr "이 업스트림을 제거하시겠습니까?"
 msgid "Domain"
 msgid "Domain"
 msgstr "도메인"
 msgstr "도메인"
 
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "도메인 구성이 성공적으로 생성되었습니다"
 msgstr "도메인 구성이 성공적으로 생성되었습니다"
 
 
@@ -559,35 +586,35 @@ msgstr "최신 릴리스 다운로드 중"
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "드라이런 모드 활성화됨"
 msgstr "드라이런 모드 활성화됨"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: src/views/domain/DomainList.vue:140
 #: src/views/domain/DomainList.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "복제"
 msgstr "복제"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:84
-#: src/views/stream/components/StreamDuplicate.vue:84
+#: src/views/domain/components/SiteDuplicate.vue:82
+#: src/views/stream/components/StreamDuplicate.vue:82
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "%{conf_name}을(를) %{node_name}(으)로 성공적으로 복제함"
 msgstr "%{conf_name}을(를) %{node_name}(으)로 성공적으로 복제함"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:90
-#: src/views/stream/components/StreamDuplicate.vue:90
+#: src/views/domain/components/SiteDuplicate.vue:87
+#: src/views/stream/components/StreamDuplicate.vue:87
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "복제 실패"
 msgstr "복제 실패"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:82
+#: src/views/domain/components/SiteDuplicate.vue:80
+#: src/views/stream/components/StreamDuplicate.vue:80
 #, fuzzy
 #, fuzzy
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "성공적으로 복제됨"
 msgstr "성공적으로 복제됨"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:64
-#: src/views/stream/components/StreamDuplicate.vue:64
+#: src/views/domain/components/SiteDuplicate.vue:63
+#: src/views/stream/components/StreamDuplicate.vue:63
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "로컬로 성공적으로 복제됨"
 msgstr "로컬로 성공적으로 복제됨"
 
 
-#: src/views/domain/DomainEdit.vue:174 src/views/stream/StreamEdit.vue:166
+#: src/views/domain/DomainEdit.vue:172 src/views/stream/StreamEdit.vue:164
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "%{n} 편집"
 msgstr "%{n} 편집"
 
 
@@ -608,24 +635,24 @@ msgstr "스트림 편집"
 msgid "Email"
 msgid "Email"
 msgstr "이메일 (*)"
 msgstr "이메일 (*)"
 
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "이메일 (*)"
 msgstr "이메일 (*)"
 
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: src/views/domain/DomainList.vue:132
 #: src/views/domain/DomainList.vue:132
-#: src/views/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #: src/views/stream/StreamList.vue:153
 msgid "Enable"
 msgid "Enable"
 msgstr "활성화"
 msgstr "활성화"
 
 
-#: src/views/domain/components/Deploy.vue:52
-#: src/views/stream/components/Deploy.vue:52
+#: src/views/domain/components/Deploy.vue:50
+#: src/views/stream/components/Deploy.vue:50
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "%{node_name}에서 %{conf_name} 활성화 실패"
 msgstr "%{node_name}에서 %{conf_name} 활성화 실패"
 
 
-#: src/views/domain/components/Deploy.vue:46
-#: src/views/stream/components/Deploy.vue:46
+#: src/views/domain/components/Deploy.vue:45
+#: src/views/stream/components/Deploy.vue:45
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "%{node_name}에서 %{conf_name} 성공적으로 활성화됨"
 msgstr "%{node_name}에서 %{conf_name} 성공적으로 활성화됨"
 
 
@@ -633,12 +660,12 @@ msgstr "%{node_name}에서 %{conf_name} 성공적으로 활성화됨"
 msgid "Enable auto-renewal failed for %{name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "%{name}에 대한 자동 갱신 활성화 실패"
 msgstr "%{name}에 대한 자동 갱신 활성화 실패"
 
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 msgid "Enable failed"
 msgstr "활성화 실패"
 msgstr "활성화 실패"
 
 
-#: src/views/domain/components/Deploy.vue:44
-#: src/views/stream/components/Deploy.vue:44
+#: src/views/domain/components/Deploy.vue:43
+#: src/views/stream/components/Deploy.vue:43
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "성공적으로 활성화"
 msgstr "성공적으로 활성화"
 
 
@@ -648,19 +675,19 @@ msgstr "TLS 활성화"
 
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgid "Enabled"
 msgstr "활성화됨"
 msgstr "활성화됨"
 
 
 #: src/views/domain/components/RightSettings.vue:29
 #: src/views/domain/components/RightSettings.vue:29
-#: src/views/domain/components/SiteDuplicate.vue:98
-#: src/views/domain/DomainAdd.vue:42 src/views/domain/DomainList.vue:57
+#: src/views/domain/components/SiteDuplicate.vue:94
+#: src/views/domain/DomainAdd.vue:40 src/views/domain/DomainList.vue:57
 #: src/views/stream/components/RightSettings.vue:29
 #: src/views/stream/components/RightSettings.vue:29
-#: src/views/stream/components/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "성공적으로 활성화됨"
 msgstr "성공적으로 활성화됨"
@@ -730,7 +757,7 @@ msgstr "%{msg} 활성화 실패"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "인증서 정보 가져오기 실패"
 msgstr "인증서 정보 가져오기 실패"
 
 
-#: src/views/domain/DomainEdit.vue:132 src/views/stream/StreamEdit.vue:124
+#: src/views/domain/DomainEdit.vue:130 src/views/stream/StreamEdit.vue:122
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr "저장 실패, 구성에서 구문 오류가 감지되었습니다."
 msgstr "저장 실패, 구성에서 구문 오류가 감지되었습니다."
 
 
@@ -756,7 +783,7 @@ msgstr "파일을 찾을 수 없음"
 msgid "Filter"
 msgid "Filter"
 msgstr "필터"
 msgstr "필터"
 
 
-#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:101
+#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgid "Finished"
 msgstr "완료됨"
 msgstr "완료됨"
 
 
@@ -828,6 +855,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:60
+msgid ""
+"If the number of login failed attempts from a ip reach the max attempts in "
+"ban threshold minutes, the ip will be banned for a period of time."
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:136
 #: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgid "Import"
 msgstr "가져오기"
 msgstr "가져오기"
@@ -837,6 +870,11 @@ msgstr "가져오기"
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "인증서 상태"
 msgstr "인증서 상태"
 
 
+#: src/views/other/Login.vue:59
+#, fuzzy
+msgid "Incorrect username or password"
+msgstr "사용자 이름 또는 비밀번호가 올바르지 않습니다"
+
 #: src/constants/index.ts:18
 #: src/constants/index.ts:18
 msgid "Info"
 msgid "Info"
 msgstr "정보"
 msgstr "정보"
@@ -849,7 +887,7 @@ msgstr "초기 코어 업그레이더 오류"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "코어 업그레이더 초기화"
 msgstr "코어 업그레이더 초기화"
 
 
-#: src/routes/index.ts:273 src/views/other/Install.vue:136
+#: src/routes/index.ts:273 src/views/other/Install.vue:135
 msgid "Install"
 msgid "Install"
 msgstr "설치"
 msgstr "설치"
 
 
@@ -871,6 +909,10 @@ msgstr "간격"
 msgid "Invalid"
 msgid "Invalid"
 msgstr "유효함"
 msgstr "유효함"
 
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgid "Issue wildcard certificate"
@@ -904,7 +946,7 @@ msgstr "마지막 확인 시간"
 msgid "Leave blank for no change"
 msgid "Leave blank for no change"
 msgstr "변경사항이 없으면 비워두세요"
 msgstr "변경사항이 없으면 비워두세요"
 
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "기본값을 사용하려면 비워 두세요: https://api.openai.com/"
 msgstr "기본값을 사용하려면 비워 두세요: https://api.openai.com/"
 
 
@@ -961,11 +1003,11 @@ msgstr "위치들"
 msgid "Log"
 msgid "Log"
 msgstr "로그인"
 msgstr "로그인"
 
 
-#: src/routes/index.ts:279 src/views/other/Login.vue:147
+#: src/routes/index.ts:279 src/views/other/Login.vue:159
 msgid "Login"
 msgid "Login"
 msgstr "로그인"
 msgstr "로그인"
 
 
-#: src/views/other/Login.vue:51 src/views/other/Login.vue:97
+#: src/views/other/Login.vue:109 src/views/other/Login.vue:51
 msgid "Login successful"
 msgid "Login successful"
 msgstr "로그인 성공"
 msgstr "로그인 성공"
 
 
@@ -973,7 +1015,7 @@ msgstr "로그인 성공"
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "로그아웃 성공"
 msgstr "로그아웃 성공"
 
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgid "Logrotate"
 msgstr "로그관리"
 msgstr "로그관리"
 
 
@@ -1023,6 +1065,10 @@ msgstr "사용자 관리"
 msgid "Managed Certificate"
 msgid "Managed Certificate"
 msgstr "인증서 유효"
 msgstr "인증서 유효"
 
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgid "Memory"
@@ -1036,7 +1082,7 @@ msgstr "메모리 및 저장소"
 msgid "Minutes"
 msgid "Minutes"
 msgstr "분"
 msgstr "분"
 
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 #, fuzzy
 msgid "Model"
 msgid "Model"
 msgstr "실행 모드"
 msgstr "실행 모드"
@@ -1053,7 +1099,7 @@ msgstr "설정 수정"
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "인증서 상태"
 msgstr "인증서 상태"
 
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgid "Modify Config"
 msgstr "설정 수정"
 msgstr "설정 수정"
 
 
@@ -1073,12 +1119,12 @@ msgstr "단일 지시문"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/RightSettings.vue:83
-#: src/views/domain/components/SiteDuplicate.vue:133
+#: src/views/domain/components/SiteDuplicate.vue:129
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: src/views/environment/Environment.vue:12
 #: src/views/stream/components/RightSettings.vue:82
 #: src/views/stream/components/RightSettings.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:133
+#: src/views/stream/components/StreamDuplicate.vue:129
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 msgid "Name"
 msgid "Name"
 msgstr "이름"
 msgstr "이름"
@@ -1105,11 +1151,11 @@ msgstr "새 버전 출시"
 
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgid "Next"
 msgstr "다음"
 msgstr "다음"
 
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgid "Nginx"
 msgstr "Nginx"
 msgstr "Nginx"
 
 
@@ -1117,7 +1163,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Nginx 접근 로그 경로"
 msgstr "Nginx 접근 로그 경로"
 
 
-#: src/views/domain/DomainEdit.vue:217 src/views/stream/StreamEdit.vue:209
+#: src/views/domain/DomainEdit.vue:215 src/views/stream/StreamEdit.vue:207
 #, fuzzy
 #, fuzzy
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 구성 오류름"
 msgstr "Nginx 구성 오류름"
@@ -1145,7 +1191,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx가 성공적으로 재시작됨"
 msgstr "Nginx가 성공적으로 재시작됨"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/ChatGPT/ChatGPT.vue:270
-#: src/components/Notification/Notification.vue:82
+#: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
@@ -1153,6 +1199,7 @@ msgstr "Nginx가 성공적으로 재시작됨"
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 #: src/views/stream/StreamList.vue:165
 msgid "No"
 msgid "No"
@@ -1184,7 +1231,7 @@ msgstr "참고"
 msgid "Notification"
 msgid "Notification"
 msgstr "알림"
 msgstr "알림"
 
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "알림"
 msgstr "알림"
@@ -1210,7 +1257,7 @@ msgid "Ok"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/ChatGPT/ChatGPT.vue:271
-#: src/components/Notification/Notification.vue:83
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1240,7 +1287,7 @@ msgstr "검증이 완료되면, 레코드는 제거됩니다."
 msgid "Online"
 msgid "Online"
 msgstr "온라인"
 msgstr "온라인"
 
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgid "OpenAI"
 msgstr "오픈AI"
 msgstr "오픈AI"
 
 
@@ -1253,13 +1300,13 @@ msgstr "OS"
 msgid "OS:"
 msgid "OS:"
 msgstr "OS:"
 msgstr "OS:"
 
 
-#: src/views/domain/components/Deploy.vue:90
-#: src/views/stream/components/Deploy.vue:90
+#: src/views/domain/components/Deploy.vue:87
+#: src/views/stream/components/Deploy.vue:87
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr "덮어쓰기"
 msgstr "덮어쓰기"
 
 
-#: src/views/domain/components/Deploy.vue:94
-#: src/views/stream/components/Deploy.vue:94
+#: src/views/domain/components/Deploy.vue:91
+#: src/views/stream/components/Deploy.vue:91
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr "기존 파일 덮어쓰기"
 msgstr "기존 파일 덮어쓰기"
 
 
@@ -1267,11 +1314,11 @@ msgstr "기존 파일 덮어쓰기"
 msgid "Params"
 msgid "Params"
 msgstr "파라미터"
 msgstr "파라미터"
 
 
-#: src/views/other/Login.vue:132 src/views/user/User.vue:18
+#: src/views/other/Login.vue:144 src/views/user/User.vue:18
 msgid "Password"
 msgid "Password"
 msgstr "비밀번호"
 msgstr "비밀번호"
 
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgid "Password (*)"
 msgstr "비밀번호 (*)"
 msgstr "비밀번호 (*)"
 
 
@@ -1336,7 +1383,7 @@ msgstr "적어도 하나의 노드를 선택해주세요!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "사전 출시"
 msgstr "사전 출시"
 
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:105
 msgid "Preference"
 msgid "Preference"
 msgstr "환경설정"
 msgstr "환경설정"
 
 
@@ -1434,7 +1481,16 @@ msgstr "리로딩 중"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "Nginx 리로딩 중"
 msgstr "Nginx 리로딩 중"
 
 
-#: src/components/Notification/Notification.vue:50
+#: src/views/preference/AuthSettings.vue:101
+msgid "Remove"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:47
+#, fuzzy
+msgid "Remove successfully"
+msgstr "성공적으로 제거됨"
+
+#: src/components/Notification/Notification.vue:52
 #, fuzzy
 #, fuzzy
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "성공적으로 제거됨"
 msgstr "성공적으로 제거됨"
@@ -1493,9 +1549,9 @@ msgstr "실행 중"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/certificate/CertificateEditor.vue:249
-#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
+#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:261
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgid "Save"
 msgstr "저장"
 msgstr "저장"
 
 
@@ -1503,7 +1559,7 @@ msgstr "저장"
 msgid "Save Directive"
 msgid "Save Directive"
 msgstr "지시문 저장"
 msgstr "지시문 저장"
 
 
-#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:50
+#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:46
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "저장 오류 %{msg}"
 msgstr "저장 오류 %{msg}"
@@ -1511,15 +1567,15 @@ msgstr "저장 오류 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 #, fuzzy
 #, fuzzy
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "성공적으로 저장됨"
 msgstr "성공적으로 저장됨"
 
 
-#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:38
-#: src/views/domain/DomainEdit.vue:148
+#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:37
+#: src/views/domain/DomainEdit.vue:146
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
-#: src/views/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "성공적으로 저장됨"
 msgstr "성공적으로 저장됨"
 
 
@@ -1544,8 +1600,9 @@ msgstr "보내기"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
+#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:69
+#: src/views/preference/AuthSettings.vue:49
+#: src/views/preference/Preference.vue:78 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
 msgstr "서버 오류"
 msgstr "서버 오류"
@@ -1564,7 +1621,7 @@ msgid "server_name not found in directives"
 msgstr "directives에서 server_name을 찾을 수 없습니다"
 msgstr "directives에서 server_name을 찾을 수 없습니다"
 
 
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
-#: src/views/domain/DomainAdd.vue:121
+#: src/views/domain/DomainAdd.vue:117
 msgid "server_name parameter is required"
 msgid "server_name parameter is required"
 msgstr "server_name 매개변수가 필요합니다"
 msgstr "server_name 매개변수가 필요합니다"
 
 
@@ -1620,7 +1677,7 @@ msgstr "SSL 인증서 키 경로"
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "SSL 인증서 경로"
 msgstr "SSL 인증서 경로"
 
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 #, fuzzy
 msgid "SSO Login"
 msgid "SSO Login"
 msgstr "SSO 로그인"
 msgstr "SSO 로그인"
@@ -1707,8 +1764,8 @@ msgstr ""
 msgid "System"
 msgid "System"
 msgstr "시스템"
 msgstr "시스템"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:140
+#: src/views/domain/components/SiteDuplicate.vue:136
+#: src/views/stream/components/StreamDuplicate.vue:136
 msgid "Target"
 msgid "Target"
 msgstr "대상"
 msgstr "대상"
 
 
@@ -1742,7 +1799,7 @@ msgstr "입력이 SSL 인증서가 아닙니다"
 msgid "The input is not a SSL Certificate Key"
 msgid "The input is not a SSL Certificate Key"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid ""
 msgid ""
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "dashes, and dots."
 "dashes, and dots."
@@ -1778,8 +1835,8 @@ msgstr ""
 msgid "The url is invalid"
 msgid "The url is invalid"
 msgstr "유효한 URL이 아닙니다"
 msgstr "유효한 URL이 아닙니다"
 
 
-#: src/views/preference/OpenAISettings.vue:45
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:48
+#: src/views/preference/OpenAISettings.vue:60
 #, fuzzy
 #, fuzzy
 msgid "The url is invalid."
 msgid "The url is invalid."
 msgstr "유효한 URL이 아닙니다"
 msgstr "유효한 URL이 아닙니다"
@@ -1806,6 +1863,7 @@ msgstr "이 필드는 필수입니다"
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr "이 필드는 비워둘 수 없습니다"
 msgstr "이 필드는 비워둘 수 없습니다"
 
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgid "Tips"
 msgstr "팁"
 msgstr "팁"
@@ -1825,10 +1883,14 @@ msgstr ""
 "시할 수 있는 위치를 추가해야 하며,이 파일을 저장하고 Nginx를 다시로드해야 합"
 "시할 수 있는 위치를 추가해야 하며,이 파일을 저장하고 Nginx를 다시로드해야 합"
 "니다.계속하시겠습니까?"
 "니다.계속하시겠습니까?"
 
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 msgid "Token is not valid"
 msgstr "토큰이 유효하지 않습니다"
 msgstr "토큰이 유효하지 않습니다"
 
 
+#: src/views/other/Login.vue:62
+msgid "Too many login failed attempts, please try again later"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 msgid "Trash"
 msgid "Trash"
 msgstr ""
 msgstr ""
@@ -1884,11 +1946,15 @@ msgstr "URL"
 msgid "User"
 msgid "User"
 msgstr "사용자 이름"
 msgstr "사용자 이름"
 
 
-#: src/views/other/Login.vue:122 src/views/user/User.vue:9
+#: src/views/other/Login.vue:65
+msgid "User is banned"
+msgstr ""
+
+#: src/views/other/Login.vue:134 src/views/user/User.vue:9
 msgid "Username"
 msgid "Username"
 msgstr "사용자 이름"
 msgstr "사용자 이름"
 
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "사용자 이름 (*)"
 msgstr "사용자 이름 (*)"
 
 
@@ -1902,7 +1968,7 @@ msgstr "유효함"
 msgid "View"
 msgid "View"
 msgstr "보기"
 msgstr "보기"
 
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 #, fuzzy
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
@@ -1919,7 +1985,7 @@ msgstr "기본 모드"
 
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgid "Warning"
 msgstr "경고"
 msgstr "경고"
 
 
@@ -1954,6 +2020,7 @@ msgstr "인증서를 디스크에 쓰기"
 
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgid "Yes"
 msgstr "예"
 msgstr "예"

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

@@ -24,6 +24,7 @@ msgstr ""
 #: src/views/domain/DomainList.vue:47
 #: src/views/domain/DomainList.vue:47
 #: src/views/environment/Environment.vue:129
 #: src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
 #: src/views/notification/Notification.vue:37
+#: src/views/preference/AuthSettings.vue:26
 #: src/views/stream/StreamList.vue:47
 #: src/views/stream/StreamList.vue:47
 #: src/views/user/User.vue:43
 #: src/views/user/User.vue:43
 msgid "Action"
 msgid "Action"
@@ -49,7 +50,7 @@ msgid "Add Location"
 msgstr ""
 msgstr ""
 
 
 #: src/routes/index.ts:64
 #: src/routes/index.ts:64
-#: src/views/domain/DomainAdd.vue:93
+#: src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgid "Add Site"
 msgstr ""
 msgstr ""
 
 
@@ -65,12 +66,12 @@ msgstr ""
 msgid "Additional"
 msgid "Additional"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainEdit.vue:199
-#: src/views/stream/StreamEdit.vue:191
+#: src/views/domain/DomainEdit.vue:197
+#: src/views/stream/StreamEdit.vue:189
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgid "API Base Url"
 msgstr ""
 msgstr ""
 
 
@@ -78,11 +79,11 @@ msgstr ""
 msgid "API Document"
 msgid "API Document"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgid "API Proxy"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgid "API Token"
 msgstr ""
 msgstr ""
 
 
@@ -90,7 +91,11 @@ msgstr ""
 msgid "Arch"
 msgid "Arch"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:84
+#: src/views/preference/AuthSettings.vue:94
+msgid "Are you sure to delete this banned IP immediately?"
+msgstr ""
+
+#: src/components/Notification/Notification.vue:86
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
 msgid "Are you sure you want to clear all notifications?"
 msgid "Are you sure you want to clear all notifications?"
 msgstr ""
 msgstr ""
@@ -136,6 +141,14 @@ msgstr ""
 msgid "Assistant"
 msgid "Assistant"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:17
+msgid "Attempts"
+msgstr ""
+
+#: src/views/preference/Preference.vue:116
+msgid "Auth"
+msgstr ""
+
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 msgid "Author"
 msgid "Author"
@@ -160,9 +173,9 @@ msgstr ""
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73
 #: src/views/config/Config.vue:73
 #: src/views/config/ConfigEdit.vue:87
 #: src/views/config/ConfigEdit.vue:87
-#: src/views/domain/DomainEdit.vue:256
+#: src/views/domain/DomainEdit.vue:254
 #: src/views/nginx_log/NginxLog.vue:168
 #: src/views/nginx_log/NginxLog.vue:168
-#: src/views/stream/StreamEdit.vue:247
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgid "Back"
 msgstr ""
 msgstr ""
 
 
@@ -170,19 +183,31 @@ msgstr ""
 msgid "Back Home"
 msgid "Back Home"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:99
+#: src/views/preference/AuthSettings.vue:68
+msgid "Ban Threshold Minutes"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:82
+msgid "Banned IPs"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:20
+msgid "Banned Until"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:95
 msgid "Base information"
 msgid "Base information"
 msgstr ""
 msgstr ""
 
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:101
+#: src/views/preference/Preference.vue:110
 #: src/views/stream/components/RightSettings.vue:74
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgid "Basic"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainEdit.vue:202
-#: src/views/stream/StreamEdit.vue:194
+#: src/views/domain/DomainEdit.vue:200
+#: src/views/stream/StreamEdit.vue:192
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr ""
 msgstr ""
 
 
@@ -266,12 +291,12 @@ msgid "Cleaning environment variables"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgid "Clear"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr ""
 msgstr ""
@@ -295,7 +320,7 @@ msgstr ""
 msgid "Configuration file is test successful"
 msgid "Configuration file is test successful"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgid "Configuration Name"
 msgstr ""
 msgstr ""
 
 
@@ -303,7 +328,7 @@ msgstr ""
 msgid "Configurations"
 msgid "Configurations"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgid "Configure SSL"
 msgstr ""
 msgstr ""
 
 
@@ -333,7 +358,7 @@ msgstr ""
 msgid "Create"
 msgid "Create"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgid "Create Another"
 msgstr ""
 msgstr ""
 
 
@@ -371,7 +396,7 @@ msgstr ""
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr ""
 msgstr ""
 
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr ""
 msgstr ""
 
 
@@ -403,15 +428,15 @@ msgstr ""
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: src/views/domain/components/RightSettings.vue:93
 #: src/views/domain/components/RightSettings.vue:93
-#: src/views/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 msgid "Deploy"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:63
-#: src/views/stream/components/Deploy.vue:63
+#: src/views/domain/components/Deploy.vue:60
+#: src/views/stream/components/Deploy.vue:60
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
@@ -460,10 +485,10 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
-#: src/views/domain/DomainEdit.vue:185
+#: src/views/domain/DomainEdit.vue:183
 #: src/views/domain/DomainList.vue:33
 #: src/views/domain/DomainList.vue:33
 #: src/views/environment/Environment.vue:93
 #: src/views/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177
+#: src/views/stream/StreamEdit.vue:175
 #: src/views/stream/StreamList.vue:33
 #: src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgid "Disabled"
 msgstr ""
 msgstr ""
@@ -536,7 +561,7 @@ msgstr ""
 msgid "Domain"
 msgid "Domain"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr ""
 msgstr ""
 
 
@@ -556,35 +581,35 @@ msgstr ""
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: src/views/domain/DomainList.vue:140
 #: src/views/domain/DomainList.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:84
-#: src/views/stream/components/StreamDuplicate.vue:84
+#: src/views/domain/components/SiteDuplicate.vue:82
+#: src/views/stream/components/StreamDuplicate.vue:82
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:90
-#: src/views/stream/components/StreamDuplicate.vue:90
+#: src/views/domain/components/SiteDuplicate.vue:87
+#: src/views/stream/components/StreamDuplicate.vue:87
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:82
+#: src/views/domain/components/SiteDuplicate.vue:80
+#: src/views/stream/components/StreamDuplicate.vue:80
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:64
-#: src/views/stream/components/StreamDuplicate.vue:64
+#: src/views/domain/components/SiteDuplicate.vue:63
+#: src/views/stream/components/StreamDuplicate.vue:63
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainEdit.vue:174
-#: src/views/stream/StreamEdit.vue:166
+#: src/views/domain/DomainEdit.vue:172
+#: src/views/stream/StreamEdit.vue:164
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr ""
 msgstr ""
 
 
@@ -605,24 +630,24 @@ msgstr ""
 msgid "Email"
 msgid "Email"
 msgstr ""
 msgstr ""
 
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: src/views/domain/DomainList.vue:132
 #: src/views/domain/DomainList.vue:132
-#: src/views/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #: src/views/stream/StreamList.vue:153
 msgid "Enable"
 msgid "Enable"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:52
-#: src/views/stream/components/Deploy.vue:52
+#: src/views/domain/components/Deploy.vue:50
+#: src/views/stream/components/Deploy.vue:50
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:46
-#: src/views/stream/components/Deploy.vue:46
+#: src/views/domain/components/Deploy.vue:45
+#: src/views/stream/components/Deploy.vue:45
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
@@ -630,12 +655,12 @@ msgstr ""
 msgid "Enable auto-renewal failed for %{name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 msgid "Enable failed"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:44
-#: src/views/stream/components/Deploy.vue:44
+#: src/views/domain/components/Deploy.vue:43
+#: src/views/stream/components/Deploy.vue:43
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr ""
 msgstr ""
 
 
@@ -645,22 +670,22 @@ msgstr ""
 
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179
+#: src/views/domain/DomainEdit.vue:177
 #: src/views/domain/DomainList.vue:29
 #: src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171
+#: src/views/stream/StreamEdit.vue:169
 #: src/views/stream/StreamList.vue:29
 #: src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgid "Enabled"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/RightSettings.vue:29
 #: src/views/domain/components/RightSettings.vue:29
-#: src/views/domain/components/SiteDuplicate.vue:98
-#: src/views/domain/DomainAdd.vue:42
+#: src/views/domain/components/SiteDuplicate.vue:94
+#: src/views/domain/DomainAdd.vue:40
 #: src/views/domain/DomainList.vue:57
 #: src/views/domain/DomainList.vue:57
 #: src/views/stream/components/RightSettings.vue:29
 #: src/views/stream/components/RightSettings.vue:29
-#: src/views/stream/components/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr ""
 msgstr ""
@@ -730,8 +755,8 @@ msgstr ""
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainEdit.vue:132
-#: src/views/stream/StreamEdit.vue:124
+#: src/views/domain/DomainEdit.vue:130
+#: src/views/stream/StreamEdit.vue:122
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr ""
 msgstr ""
 
 
@@ -757,7 +782,7 @@ msgid "Filter"
 msgstr ""
 msgstr ""
 
 
 #: src/language/constants.ts:19
 #: src/language/constants.ts:19
-#: src/views/domain/DomainAdd.vue:101
+#: src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgid "Finished"
 msgstr ""
 msgstr ""
 
 
@@ -825,6 +850,10 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:60
+msgid "If the number of login failed attempts from a ip reach the max attempts in ban threshold minutes, the ip will be banned for a period of time."
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:136
 #: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgid "Import"
 msgstr ""
 msgstr ""
@@ -834,6 +863,10 @@ msgstr ""
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr ""
 msgstr ""
 
 
+#: src/views/other/Login.vue:59
+msgid "Incorrect username or password"
+msgstr ""
+
 #: src/constants/index.ts:18
 #: src/constants/index.ts:18
 msgid "Info"
 msgid "Info"
 msgstr ""
 msgstr ""
@@ -847,7 +880,7 @@ msgid "Initialing core upgrader"
 msgstr ""
 msgstr ""
 
 
 #: src/routes/index.ts:273
 #: src/routes/index.ts:273
-#: src/views/other/Install.vue:136
+#: src/views/other/Install.vue:135
 msgid "Install"
 msgid "Install"
 msgstr ""
 msgstr ""
 
 
@@ -867,6 +900,10 @@ msgstr ""
 msgid "Invalid"
 msgid "Invalid"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #: src/views/certificate/Certificate.vue:144
 msgid "Issue wildcard certificate"
 msgid "Issue wildcard certificate"
 msgstr ""
 msgstr ""
@@ -897,7 +934,7 @@ msgstr ""
 msgid "Leave blank for no change"
 msgid "Leave blank for no change"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr ""
 msgstr ""
 
 
@@ -949,12 +986,12 @@ msgid "Log"
 msgstr ""
 msgstr ""
 
 
 #: src/routes/index.ts:279
 #: src/routes/index.ts:279
-#: src/views/other/Login.vue:147
+#: src/views/other/Login.vue:159
 msgid "Login"
 msgid "Login"
 msgstr ""
 msgstr ""
 
 
+#: src/views/other/Login.vue:109
 #: src/views/other/Login.vue:51
 #: src/views/other/Login.vue:51
-#: src/views/other/Login.vue:97
 msgid "Login successful"
 msgid "Login successful"
 msgstr ""
 msgstr ""
 
 
@@ -962,7 +999,7 @@ msgstr ""
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgid "Logrotate"
 msgstr ""
 msgstr ""
 
 
@@ -997,6 +1034,10 @@ msgstr ""
 msgid "Managed Certificate"
 msgid "Managed Certificate"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgid "Memory"
@@ -1010,7 +1051,7 @@ msgstr ""
 msgid "Minutes"
 msgid "Minutes"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 msgid "Model"
 msgid "Model"
 msgstr ""
 msgstr ""
 
 
@@ -1025,7 +1066,7 @@ msgstr ""
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgid "Modify Config"
 msgstr ""
 msgstr ""
 
 
@@ -1044,12 +1085,12 @@ msgstr ""
 #: src/views/config/config.ts:7
 #: src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/RightSettings.vue:83
-#: src/views/domain/components/SiteDuplicate.vue:133
+#: src/views/domain/components/SiteDuplicate.vue:129
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: src/views/environment/Environment.vue:12
 #: src/views/stream/components/RightSettings.vue:82
 #: src/views/stream/components/RightSettings.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:133
+#: src/views/stream/components/StreamDuplicate.vue:129
 #: src/views/stream/StreamList.vue:13
 #: src/views/stream/StreamList.vue:13
 #: src/views/stream/StreamList.vue:187
 #: src/views/stream/StreamList.vue:187
 msgid "Name"
 msgid "Name"
@@ -1077,11 +1118,11 @@ msgstr ""
 
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgid "Next"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgid "Nginx"
 msgstr ""
 msgstr ""
 
 
@@ -1089,8 +1130,8 @@ msgstr ""
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainEdit.vue:217
-#: src/views/stream/StreamEdit.vue:209
+#: src/views/domain/DomainEdit.vue:215
+#: src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr ""
 msgstr ""
 
 
@@ -1116,7 +1157,7 @@ msgid "Nginx restarted successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/ChatGPT/ChatGPT.vue:270
-#: src/components/Notification/Notification.vue:82
+#: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
@@ -1124,6 +1165,7 @@ msgstr ""
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 #: src/views/stream/StreamList.vue:165
 msgid "No"
 msgid "No"
@@ -1154,7 +1196,7 @@ msgstr ""
 msgid "Notification"
 msgid "Notification"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:80
+#: src/components/Notification/Notification.vue:82
 #: src/routes/index.ts:221
 #: src/routes/index.ts:221
 msgid "Notifications"
 msgid "Notifications"
 msgstr ""
 msgstr ""
@@ -1179,7 +1221,7 @@ msgid "Ok"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/ChatGPT/ChatGPT.vue:271
-#: src/components/Notification/Notification.vue:83
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1209,7 +1251,7 @@ msgstr ""
 msgid "Online"
 msgid "Online"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgid "OpenAI"
 msgstr ""
 msgstr ""
 
 
@@ -1221,13 +1263,13 @@ msgstr ""
 msgid "OS:"
 msgid "OS:"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:90
-#: src/views/stream/components/Deploy.vue:90
+#: src/views/domain/components/Deploy.vue:87
+#: src/views/stream/components/Deploy.vue:87
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:94
-#: src/views/stream/components/Deploy.vue:94
+#: src/views/domain/components/Deploy.vue:91
+#: src/views/stream/components/Deploy.vue:91
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr ""
 msgstr ""
 
 
@@ -1235,12 +1277,12 @@ msgstr ""
 msgid "Params"
 msgid "Params"
 msgstr ""
 msgstr ""
 
 
-#: src/views/other/Login.vue:132
+#: src/views/other/Login.vue:144
 #: src/views/user/User.vue:18
 #: src/views/user/User.vue:18
 msgid "Password"
 msgid "Password"
 msgstr ""
 msgstr ""
 
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgid "Password (*)"
 msgstr ""
 msgstr ""
 
 
@@ -1300,7 +1342,7 @@ msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
 #: src/routes/index.ts:239
 #: src/routes/index.ts:239
-#: src/views/preference/Preference.vue:96
+#: src/views/preference/Preference.vue:105
 msgid "Preference"
 msgid "Preference"
 msgstr ""
 msgstr ""
 
 
@@ -1391,7 +1433,15 @@ msgstr ""
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:50
+#: src/views/preference/AuthSettings.vue:101
+msgid "Remove"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:47
+msgid "Remove successfully"
+msgstr ""
+
+#: src/components/Notification/Notification.vue:52
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr ""
 msgstr ""
 
 
@@ -1444,10 +1494,10 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96
 #: src/views/config/ConfigEdit.vue:96
-#: src/views/domain/DomainEdit.vue:263
+#: src/views/domain/DomainEdit.vue:261
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:130
-#: src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145
+#: src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgid "Save"
 msgstr ""
 msgstr ""
 
 
@@ -1456,7 +1506,7 @@ msgid "Save Directive"
 msgstr ""
 msgstr ""
 
 
 #: src/views/config/ConfigEdit.vue:57
 #: src/views/config/ConfigEdit.vue:57
-#: src/views/domain/DomainAdd.vue:50
+#: src/views/domain/DomainAdd.vue:46
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr ""
 msgstr ""
@@ -1464,15 +1514,15 @@ msgstr ""
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/views/config/ConfigEdit.vue:55
 #: src/views/config/ConfigEdit.vue:55
-#: src/views/domain/DomainAdd.vue:38
-#: src/views/domain/DomainEdit.vue:148
+#: src/views/domain/DomainAdd.vue:37
+#: src/views/domain/DomainEdit.vue:146
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
-#: src/views/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr ""
 msgstr ""
 
 
@@ -1499,8 +1549,9 @@ msgstr ""
 #: src/views/config/ConfigEdit.vue:40
 #: src/views/config/ConfigEdit.vue:40
 #: src/views/domain/DomainList.vue:81
 #: src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139
 #: src/views/environment/Environment.vue:139
-#: src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:70
+#: src/views/other/Install.vue:69
+#: src/views/preference/AuthSettings.vue:49
+#: src/views/preference/Preference.vue:78
 #: src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81
 #: src/views/stream/StreamList.vue:81
 #: src/views/system/Upgrade.vue:42
 #: src/views/system/Upgrade.vue:42
@@ -1520,7 +1571,7 @@ msgid "server_name not found in directives"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
-#: src/views/domain/DomainAdd.vue:121
+#: src/views/domain/DomainAdd.vue:117
 msgid "server_name parameter is required"
 msgid "server_name parameter is required"
 msgstr ""
 msgstr ""
 
 
@@ -1570,7 +1621,7 @@ msgstr ""
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr ""
 msgstr ""
 
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 msgid "SSO Login"
 msgid "SSO Login"
 msgstr ""
 msgstr ""
 
 
@@ -1649,8 +1700,8 @@ msgstr ""
 msgid "System"
 msgid "System"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/SiteDuplicate.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:140
+#: src/views/domain/components/SiteDuplicate.vue:136
+#: src/views/stream/components/StreamDuplicate.vue:136
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
@@ -1679,7 +1730,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgid "The input is not a SSL Certificate Key"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid "The model name should only contain letters, unicode, numbers, hyphens, dashes, and dots."
 msgid "The model name should only contain letters, unicode, numbers, hyphens, dashes, and dots."
 msgstr ""
 msgstr ""
 
 
@@ -1704,8 +1755,8 @@ msgstr ""
 msgid "The url is invalid"
 msgid "The url is invalid"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:45
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:48
+#: src/views/preference/OpenAISettings.vue:60
 msgid "The url is invalid."
 msgid "The url is invalid."
 msgstr ""
 msgstr ""
 
 
@@ -1731,6 +1782,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgid "Tips"
 msgstr ""
 msgstr ""
@@ -1743,10 +1795,14 @@ 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?"
 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 ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 msgid "Token is not valid"
 msgstr ""
 msgstr ""
 
 
+#: src/views/other/Login.vue:62
+msgid "Too many login failed attempts, please try again later"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 msgid "Trash"
 msgid "Trash"
 msgstr ""
 msgstr ""
@@ -1804,12 +1860,16 @@ msgstr ""
 msgid "User"
 msgid "User"
 msgstr ""
 msgstr ""
 
 
-#: src/views/other/Login.vue:122
+#: src/views/other/Login.vue:65
+msgid "User is banned"
+msgstr ""
+
+#: src/views/other/Login.vue:134
 #: src/views/user/User.vue:9
 #: src/views/user/User.vue:9
 msgid "Username"
 msgid "Username"
 msgstr ""
 msgstr ""
 
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr ""
 msgstr ""
 
 
@@ -1823,7 +1883,7 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr ""
 msgstr ""
 
 
@@ -1838,7 +1898,7 @@ msgstr ""
 #: src/constants/index.ts:17
 #: src/constants/index.ts:17
 #: src/views/config/InspectConfig.vue:33
 #: src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgid "Warning"
 msgstr ""
 msgstr ""
 
 
@@ -1865,6 +1925,7 @@ msgstr ""
 
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgid "Yes"
 msgstr ""
 msgstr ""

+ 165 - 98
app/src/language/ru_RU/app.po

@@ -28,7 +28,8 @@ msgstr "Пользователь"
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
 #: src/views/notification/Notification.vue:37
-#: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
+#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47
+#: src/views/user/User.vue:43
 msgid "Action"
 msgid "Action"
 msgstr "Действие"
 msgstr "Действие"
 
 
@@ -51,7 +52,7 @@ msgstr "Добавить директиву ниже"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Добавить Location"
 msgstr "Добавить Location"
 
 
-#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:93
+#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Добавть Сайт"
 msgstr "Добавть Сайт"
 
 
@@ -70,11 +71,11 @@ msgstr "Обновлено успешно"
 msgid "Additional"
 msgid "Additional"
 msgstr "Дополнительно"
 msgstr "Дополнительно"
 
 
-#: src/views/domain/DomainEdit.vue:199 src/views/stream/StreamEdit.vue:191
+#: src/views/domain/DomainEdit.vue:197 src/views/stream/StreamEdit.vue:189
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Расширенный режим"
 msgstr "Расширенный режим"
 
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgid "API Base Url"
 msgstr ""
 msgstr ""
 
 
@@ -82,11 +83,11 @@ msgstr ""
 msgid "API Document"
 msgid "API Document"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgid "API Proxy"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgid "API Token"
 msgstr ""
 msgstr ""
 
 
@@ -94,7 +95,12 @@ msgstr ""
 msgid "Arch"
 msgid "Arch"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:84
+#: src/views/preference/AuthSettings.vue:94
+#, fuzzy
+msgid "Are you sure to delete this banned IP immediately?"
+msgstr "Вы уверены, что хотите удалить?"
+
+#: src/components/Notification/Notification.vue:86
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgid "Are you sure you want to clear all notifications?"
@@ -147,6 +153,15 @@ msgstr "Обратитесь за помощью к ChatGPT"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "Ассистент"
 msgstr "Ассистент"
 
 
+#: src/views/preference/AuthSettings.vue:17
+msgid "Attempts"
+msgstr ""
+
+#: src/views/preference/Preference.vue:116
+#, fuzzy
+msgid "Auth"
+msgstr "Автор"
+
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 msgid "Author"
 msgid "Author"
@@ -170,8 +185,8 @@ msgstr "Автообновление включено для %{name}"
 
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
-#: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
-#: src/views/stream/StreamEdit.vue:247
+#: src/views/domain/DomainEdit.vue:254 src/views/nginx_log/NginxLog.vue:168
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgid "Back"
 msgstr "Назад"
 msgstr "Назад"
 
 
@@ -180,19 +195,31 @@ msgstr "Назад"
 msgid "Back Home"
 msgid "Back Home"
 msgstr "Вернутся"
 msgstr "Вернутся"
 
 
-#: src/views/domain/DomainAdd.vue:99
+#: src/views/preference/AuthSettings.vue:68
+msgid "Ban Threshold Minutes"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:82
+msgid "Banned IPs"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:20
+msgid "Banned Until"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:95
 msgid "Base information"
 msgid "Base information"
 msgstr "Основная информация"
 msgstr "Основная информация"
 
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:101
+#: src/views/preference/Preference.vue:110
 #: src/views/stream/components/RightSettings.vue:74
 #: src/views/stream/components/RightSettings.vue:74
 #, fuzzy
 #, fuzzy
 msgid "Basic"
 msgid "Basic"
 msgstr "Простой режим"
 msgstr "Простой режим"
 
 
-#: src/views/domain/DomainEdit.vue:202 src/views/stream/StreamEdit.vue:194
+#: src/views/domain/DomainEdit.vue:200 src/views/stream/StreamEdit.vue:192
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Простой режим"
 msgstr "Простой режим"
 
 
@@ -280,12 +307,12 @@ msgid "Cleaning environment variables"
 msgstr "Очистка переменных среды"
 msgstr "Очистка переменных среды"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgid "Clear"
 msgstr "Очистить"
 msgstr "Очистить"
 
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 #, fuzzy
 msgid "Cleared successfully"
 msgid "Cleared successfully"
@@ -312,7 +339,7 @@ msgstr "Шаблоны конфигураций"
 msgid "Configuration file is test successful"
 msgid "Configuration file is test successful"
 msgstr "Проверка конфигурации успешна"
 msgstr "Проверка конфигурации успешна"
 
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgid "Configuration Name"
 msgstr "Название конфигурации"
 msgstr "Название конфигурации"
 
 
@@ -320,7 +347,7 @@ msgstr "Название конфигурации"
 msgid "Configurations"
 msgid "Configurations"
 msgstr "Конфигурации"
 msgstr "Конфигурации"
 
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgid "Configure SSL"
 msgstr "Настроить SSL"
 msgstr "Настроить SSL"
 
 
@@ -351,7 +378,7 @@ msgstr "CPU:"
 msgid "Create"
 msgid "Create"
 msgstr "Создан в"
 msgstr "Создан в"
 
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Создать еще"
 msgstr "Создать еще"
 
 
@@ -390,7 +417,7 @@ msgstr ""
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Доска"
 msgstr "Доска"
 
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "База данных (Опционально, по умолчанию: database)"
 msgstr "База данных (Опционально, по умолчанию: database)"
 
 
@@ -423,15 +450,15 @@ msgstr ""
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr "Отключено успешно"
 msgstr "Отключено успешно"
 
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: src/views/domain/components/RightSettings.vue:93
 #: src/views/domain/components/RightSettings.vue:93
-#: src/views/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 msgid "Deploy"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:63
-#: src/views/stream/components/Deploy.vue:63
+#: src/views/domain/components/Deploy.vue:60
+#: src/views/stream/components/Deploy.vue:60
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
@@ -481,9 +508,9 @@ msgstr "Отключить"
 msgid "Disable auto-renewal failed for %{name}"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "Не удалось отключить автоматическое продление для %{name}"
 msgstr "Не удалось отключить автоматическое продление для %{name}"
 
 
-#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:183
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Отключено"
 msgstr "Отключено"
 
 
@@ -562,7 +589,7 @@ msgstr "Вы хотите удалить этот сервер?"
 msgid "Domain"
 msgid "Domain"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "Конфигурация домена успешно создана"
 msgstr "Конфигурация домена успешно создана"
 
 
@@ -584,38 +611,38 @@ msgstr "Загрузка последней версии"
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Включен пробный режим"
 msgstr "Включен пробный режим"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: src/views/domain/DomainList.vue:140
 #: src/views/domain/DomainList.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Дублировать"
 msgstr "Дублировать"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:84
-#: src/views/stream/components/StreamDuplicate.vue:84
+#: src/views/domain/components/SiteDuplicate.vue:82
+#: src/views/stream/components/StreamDuplicate.vue:82
 #, fuzzy
 #, fuzzy
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Продублированно %{conf_name} в %{node_name}"
 msgstr "Продублированно %{conf_name} в %{node_name}"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:90
-#: src/views/stream/components/StreamDuplicate.vue:90
+#: src/views/domain/components/SiteDuplicate.vue:87
+#: src/views/stream/components/StreamDuplicate.vue:87
 #, fuzzy
 #, fuzzy
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Дублировать не удалось"
 msgstr "Дублировать не удалось"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:82
+#: src/views/domain/components/SiteDuplicate.vue:80
+#: src/views/stream/components/StreamDuplicate.vue:80
 #, fuzzy
 #, fuzzy
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Продублированно"
 msgstr "Продублированно"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:64
-#: src/views/stream/components/StreamDuplicate.vue:64
+#: src/views/domain/components/SiteDuplicate.vue:63
+#: src/views/stream/components/StreamDuplicate.vue:63
 #, fuzzy
 #, fuzzy
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/views/domain/DomainEdit.vue:174 src/views/stream/StreamEdit.vue:166
+#: src/views/domain/DomainEdit.vue:172 src/views/stream/StreamEdit.vue:164
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Редактировать %{n}"
 msgstr "Редактировать %{n}"
 
 
@@ -637,25 +664,25 @@ msgstr "Редактировать Сайт"
 msgid "Email"
 msgid "Email"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: src/views/domain/DomainList.vue:132
 #: src/views/domain/DomainList.vue:132
-#: src/views/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #: src/views/stream/StreamList.vue:153
 #, fuzzy
 #, fuzzy
 msgid "Enable"
 msgid "Enable"
 msgstr "Включить"
 msgstr "Включить"
 
 
-#: src/views/domain/components/Deploy.vue:52
-#: src/views/stream/components/Deploy.vue:52
+#: src/views/domain/components/Deploy.vue:50
+#: src/views/stream/components/Deploy.vue:50
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "Включение %{conf_name} in %{node_name} нипалучилася"
 msgstr "Включение %{conf_name} in %{node_name} нипалучилася"
 
 
-#: src/views/domain/components/Deploy.vue:46
-#: src/views/stream/components/Deploy.vue:46
+#: src/views/domain/components/Deploy.vue:45
+#: src/views/stream/components/Deploy.vue:45
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "Включение %{conf_name} in %{node_name} успешно"
 msgstr "Включение %{conf_name} in %{node_name} успешно"
 
 
@@ -663,12 +690,12 @@ msgstr "Включение %{conf_name} in %{node_name} успешно"
 msgid "Enable auto-renewal failed for %{name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "Не удалось включить автоматическое продление для %{name}"
 msgstr "Не удалось включить автоматическое продление для %{name}"
 
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 msgid "Enable failed"
 msgstr "Включить не удалось"
 msgstr "Включить не удалось"
 
 
-#: src/views/domain/components/Deploy.vue:44
-#: src/views/stream/components/Deploy.vue:44
+#: src/views/domain/components/Deploy.vue:43
+#: src/views/stream/components/Deploy.vue:43
 #, fuzzy
 #, fuzzy
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Активировано успешно"
 msgstr "Активировано успешно"
@@ -679,19 +706,19 @@ msgstr "Включить TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Включено"
 msgstr "Включено"
 
 
 #: src/views/domain/components/RightSettings.vue:29
 #: src/views/domain/components/RightSettings.vue:29
-#: src/views/domain/components/SiteDuplicate.vue:98
-#: src/views/domain/DomainAdd.vue:42 src/views/domain/DomainList.vue:57
+#: src/views/domain/components/SiteDuplicate.vue:94
+#: src/views/domain/DomainAdd.vue:40 src/views/domain/DomainList.vue:57
 #: src/views/stream/components/RightSettings.vue:29
 #: src/views/stream/components/RightSettings.vue:29
-#: src/views/stream/components/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Активировано успешно"
 msgstr "Активировано успешно"
@@ -762,7 +789,7 @@ msgstr "Не удалось включить %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "Не удалось получить информацию о сертификате"
 msgstr "Не удалось получить информацию о сертификате"
 
 
-#: src/views/domain/DomainEdit.vue:132 src/views/stream/StreamEdit.vue:124
+#: src/views/domain/DomainEdit.vue:130 src/views/stream/StreamEdit.vue:122
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr "Не удалось сохранить, обнаружены синтаксические ошибки в конфигурации."
 msgstr "Не удалось сохранить, обнаружены синтаксические ошибки в конфигурации."
 
 
@@ -788,7 +815,7 @@ msgstr "Файл не найден"
 msgid "Filter"
 msgid "Filter"
 msgstr "Фильтр"
 msgstr "Фильтр"
 
 
-#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:101
+#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgid "Finished"
 msgstr "Готово"
 msgstr "Готово"
 
 
@@ -860,6 +887,12 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:60
+msgid ""
+"If the number of login failed attempts from a ip reach the max attempts in "
+"ban threshold minutes, the ip will be banned for a period of time."
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:136
 #: src/views/certificate/Certificate.vue:136
 #, fuzzy
 #, fuzzy
 msgid "Import"
 msgid "Import"
@@ -870,6 +903,11 @@ msgstr "Экспорт"
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
 
 
+#: src/views/other/Login.vue:59
+#, fuzzy
+msgid "Incorrect username or password"
+msgstr "Имя пользователя или пароль неверны"
+
 #: src/constants/index.ts:18
 #: src/constants/index.ts:18
 msgid "Info"
 msgid "Info"
 msgstr ""
 msgstr ""
@@ -882,7 +920,7 @@ msgstr "Ошибка первоначального обновления ядр
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Инициализация программы обновления ядра"
 msgstr "Инициализация программы обновления ядра"
 
 
-#: src/routes/index.ts:273 src/views/other/Install.vue:136
+#: src/routes/index.ts:273 src/views/other/Install.vue:135
 msgid "Install"
 msgid "Install"
 msgstr "Установить"
 msgstr "Установить"
 
 
@@ -904,6 +942,10 @@ msgstr ""
 msgid "Invalid"
 msgid "Invalid"
 msgstr "Действительный"
 msgstr "Действительный"
 
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgid "Issue wildcard certificate"
@@ -938,7 +980,7 @@ msgstr "Последняя проверка в"
 msgid "Leave blank for no change"
 msgid "Leave blank for no change"
 msgstr "Оставьте пустым без изменений"
 msgstr "Оставьте пустым без изменений"
 
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Оставьте пустым для значения по умолчанию: https://api.openai.com/"
 msgstr "Оставьте пустым для значения по умолчанию: https://api.openai.com/"
 
 
@@ -995,11 +1037,11 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "Логин"
 msgstr "Логин"
 
 
-#: src/routes/index.ts:279 src/views/other/Login.vue:147
+#: src/routes/index.ts:279 src/views/other/Login.vue:159
 msgid "Login"
 msgid "Login"
 msgstr "Логин"
 msgstr "Логин"
 
 
-#: src/views/other/Login.vue:51 src/views/other/Login.vue:97
+#: src/views/other/Login.vue:109 src/views/other/Login.vue:51
 msgid "Login successful"
 msgid "Login successful"
 msgstr "Авторизация успешна"
 msgstr "Авторизация успешна"
 
 
@@ -1007,7 +1049,7 @@ msgstr "Авторизация успешна"
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "Выход выполнен успешно"
 msgstr "Выход выполнен успешно"
 
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgid "Logrotate"
 msgstr ""
 msgstr ""
 
 
@@ -1052,6 +1094,10 @@ msgstr "Пользователи"
 msgid "Managed Certificate"
 msgid "Managed Certificate"
 msgstr "Управление сертификатами"
 msgstr "Управление сертификатами"
 
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgid "Memory"
@@ -1065,7 +1111,7 @@ msgstr "Память и хранилище"
 msgid "Minutes"
 msgid "Minutes"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 #, fuzzy
 msgid "Model"
 msgid "Model"
 msgstr "Расширенный режим"
 msgstr "Расширенный режим"
@@ -1082,7 +1128,7 @@ msgstr "Изменить"
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
 
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgid "Modify Config"
 msgstr "Изменить конфигурацию"
 msgstr "Изменить конфигурацию"
 
 
@@ -1102,12 +1148,12 @@ msgstr "Одиночная директива"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/RightSettings.vue:83
-#: src/views/domain/components/SiteDuplicate.vue:133
+#: src/views/domain/components/SiteDuplicate.vue:129
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: src/views/environment/Environment.vue:12
 #: src/views/stream/components/RightSettings.vue:82
 #: src/views/stream/components/RightSettings.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:133
+#: src/views/stream/components/StreamDuplicate.vue:129
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 msgid "Name"
 msgid "Name"
 msgstr "Имя"
 msgstr "Имя"
@@ -1134,11 +1180,11 @@ msgstr "Вышла новая версия"
 
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgid "Next"
 msgstr "Дальше"
 msgstr "Дальше"
 
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 #, fuzzy
 #, fuzzy
 msgid "Nginx"
 msgid "Nginx"
 msgstr "Журнал"
 msgstr "Журнал"
@@ -1147,7 +1193,7 @@ msgstr "Журнал"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Путь для Nginx Access Log"
 msgstr "Путь для Nginx Access Log"
 
 
-#: src/views/domain/DomainEdit.vue:217 src/views/stream/StreamEdit.vue:209
+#: src/views/domain/DomainEdit.vue:215 src/views/stream/StreamEdit.vue:207
 #, fuzzy
 #, fuzzy
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Ошибка синтаксического анализа конфигурации Nginx"
 msgstr "Ошибка синтаксического анализа конфигурации Nginx"
@@ -1175,7 +1221,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx успешно перезапущен"
 msgstr "Nginx успешно перезапущен"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/ChatGPT/ChatGPT.vue:270
-#: src/components/Notification/Notification.vue:82
+#: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
@@ -1183,6 +1229,7 @@ msgstr "Nginx успешно перезапущен"
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 #: src/views/stream/StreamList.vue:165
 msgid "No"
 msgid "No"
@@ -1214,7 +1261,7 @@ msgstr "Заметка"
 msgid "Notification"
 msgid "Notification"
 msgstr "Сертификат"
 msgstr "Сертификат"
 
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Уведомления"
 msgstr "Уведомления"
@@ -1240,7 +1287,7 @@ msgid "Ok"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/ChatGPT/ChatGPT.vue:271
-#: src/components/Notification/Notification.vue:83
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1270,7 +1317,7 @@ msgstr ""
 msgid "Online"
 msgid "Online"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgid "OpenAI"
 msgstr ""
 msgstr ""
 
 
@@ -1283,13 +1330,13 @@ msgstr "OS:"
 msgid "OS:"
 msgid "OS:"
 msgstr "OS:"
 msgstr "OS:"
 
 
-#: src/views/domain/components/Deploy.vue:90
-#: src/views/stream/components/Deploy.vue:90
+#: src/views/domain/components/Deploy.vue:87
+#: src/views/stream/components/Deploy.vue:87
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/components/Deploy.vue:94
-#: src/views/stream/components/Deploy.vue:94
+#: src/views/domain/components/Deploy.vue:91
+#: src/views/stream/components/Deploy.vue:91
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr ""
 msgstr ""
 
 
@@ -1297,11 +1344,11 @@ msgstr ""
 msgid "Params"
 msgid "Params"
 msgstr "Параметры"
 msgstr "Параметры"
 
 
-#: src/views/other/Login.vue:132 src/views/user/User.vue:18
+#: src/views/other/Login.vue:144 src/views/user/User.vue:18
 msgid "Password"
 msgid "Password"
 msgstr "Пароль"
 msgstr "Пароль"
 
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgid "Password (*)"
 msgstr "Пароль (*)"
 msgstr "Пароль (*)"
 
 
@@ -1366,7 +1413,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:105
 msgid "Preference"
 msgid "Preference"
 msgstr "Настройки"
 msgstr "Настройки"
 
 
@@ -1464,7 +1511,16 @@ msgstr "Перезагружается"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "Перезагружается nginx"
 msgstr "Перезагружается nginx"
 
 
-#: src/components/Notification/Notification.vue:50
+#: src/views/preference/AuthSettings.vue:101
+msgid "Remove"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:47
+#, fuzzy
+msgid "Remove successfully"
+msgstr "Успешно сохранено"
+
+#: src/components/Notification/Notification.vue:52
 #, fuzzy
 #, fuzzy
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Успешно сохранено"
 msgstr "Успешно сохранено"
@@ -1523,9 +1579,9 @@ msgstr "Выполняется"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/certificate/CertificateEditor.vue:249
-#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
+#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:261
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgid "Save"
 msgstr "Сохранить"
 msgstr "Сохранить"
 
 
@@ -1533,7 +1589,7 @@ msgstr "Сохранить"
 msgid "Save Directive"
 msgid "Save Directive"
 msgstr "Сохранить директиву"
 msgstr "Сохранить директиву"
 
 
-#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:50
+#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:46
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Ошибка сохранения %{msg}"
 msgstr "Ошибка сохранения %{msg}"
@@ -1541,15 +1597,15 @@ msgstr "Ошибка сохранения %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 #, fuzzy
 #, fuzzy
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "Успешно сохранено"
 msgstr "Успешно сохранено"
 
 
-#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:38
-#: src/views/domain/DomainEdit.vue:148
+#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:37
+#: src/views/domain/DomainEdit.vue:146
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
-#: src/views/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Успешно сохранено"
 msgstr "Успешно сохранено"
 
 
@@ -1574,8 +1630,9 @@ msgstr "Отправлено"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
+#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:69
+#: src/views/preference/AuthSettings.vue:49
+#: src/views/preference/Preference.vue:78 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
 msgstr "Ошибка сервера"
 msgstr "Ошибка сервера"
@@ -1594,7 +1651,7 @@ msgid "server_name not found in directives"
 msgstr "server_name не нашел в директивах"
 msgstr "server_name не нашел в директивах"
 
 
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
-#: src/views/domain/DomainAdd.vue:121
+#: src/views/domain/DomainAdd.vue:117
 msgid "server_name parameter is required"
 msgid "server_name parameter is required"
 msgstr "server_name параметр обязателен"
 msgstr "server_name параметр обязателен"
 
 
@@ -1651,7 +1708,7 @@ msgstr "Путь к ключу сертификата SSL"
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "Путь к сертификату SSL"
 msgstr "Путь к сертификату SSL"
 
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 #, fuzzy
 msgid "SSO Login"
 msgid "SSO Login"
 msgstr "Логин"
 msgstr "Логин"
@@ -1738,8 +1795,8 @@ msgstr ""
 msgid "System"
 msgid "System"
 msgstr "Система"
 msgstr "Система"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:140
+#: src/views/domain/components/SiteDuplicate.vue:136
+#: src/views/stream/components/StreamDuplicate.vue:136
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
@@ -1773,7 +1830,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgid "The input is not a SSL Certificate Key"
 msgstr "Путь к ключу сертификата SSL"
 msgstr "Путь к ключу сертификата SSL"
 
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid ""
 msgid ""
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "dashes, and dots."
 "dashes, and dots."
@@ -1809,8 +1866,8 @@ msgstr ""
 msgid "The url is invalid"
 msgid "The url is invalid"
 msgstr "URL-адрес неверный"
 msgstr "URL-адрес неверный"
 
 
-#: src/views/preference/OpenAISettings.vue:45
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:48
+#: src/views/preference/OpenAISettings.vue:60
 #, fuzzy
 #, fuzzy
 msgid "The url is invalid."
 msgid "The url is invalid."
 msgstr "URL-адрес неверный"
 msgstr "URL-адрес неверный"
@@ -1838,6 +1895,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr "Это поле обязательно к заполнению"
 msgstr "Это поле обязательно к заполнению"
 
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgid "Tips"
 msgstr ""
 msgstr ""
@@ -1854,10 +1912,14 @@ msgid ""
 "continue?"
 "continue?"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 msgid "Token is not valid"
 msgstr ""
 msgstr ""
 
 
+#: src/views/other/Login.vue:62
+msgid "Too many login failed attempts, please try again later"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 msgid "Trash"
 msgid "Trash"
 msgstr ""
 msgstr ""
@@ -1913,11 +1975,15 @@ msgstr ""
 msgid "User"
 msgid "User"
 msgstr "Пользователь"
 msgstr "Пользователь"
 
 
-#: src/views/other/Login.vue:122 src/views/user/User.vue:9
+#: src/views/other/Login.vue:65
+msgid "User is banned"
+msgstr ""
+
+#: src/views/other/Login.vue:134 src/views/user/User.vue:9
 msgid "Username"
 msgid "Username"
 msgstr "Имя пользователя"
 msgstr "Имя пользователя"
 
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "Имя пользователя (*)"
 msgstr "Имя пользователя (*)"
 
 
@@ -1931,7 +1997,7 @@ msgstr "Действительный"
 msgid "View"
 msgid "View"
 msgstr "Просмотр"
 msgstr "Просмотр"
 
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 #, fuzzy
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Просмотреть все уведомления"
 msgstr "Просмотреть все уведомления"
@@ -1947,7 +2013,7 @@ msgstr "Простой режим"
 
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgid "Warning"
 msgstr "Внимание"
 msgstr "Внимание"
 
 
@@ -1980,6 +2046,7 @@ msgstr "Запись сертификата на диск"
 
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgid "Yes"
 msgstr "Да"
 msgstr "Да"

+ 165 - 98
app/src/language/vi_VN/app.po

@@ -28,7 +28,8 @@ msgstr "Người dùng"
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
 #: src/views/notification/Notification.vue:37
-#: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
+#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47
+#: src/views/user/User.vue:43
 msgid "Action"
 msgid "Action"
 msgstr "Hành động"
 msgstr "Hành động"
 
 
@@ -51,7 +52,7 @@ msgstr "Thêm Directive"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Thêm Location"
 msgstr "Thêm Location"
 
 
-#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:93
+#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Thêm Website"
 msgstr "Thêm Website"
 
 
@@ -70,11 +71,11 @@ msgstr "Cập nhật thành công"
 msgid "Additional"
 msgid "Additional"
 msgstr "Tùy chọn bổ sung"
 msgstr "Tùy chọn bổ sung"
 
 
-#: src/views/domain/DomainEdit.vue:199 src/views/stream/StreamEdit.vue:191
+#: src/views/domain/DomainEdit.vue:197 src/views/stream/StreamEdit.vue:189
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Nâng cao"
 msgstr "Nâng cao"
 
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgid "API Base Url"
 msgstr ""
 msgstr ""
 
 
@@ -82,11 +83,11 @@ msgstr ""
 msgid "API Document"
 msgid "API Document"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgid "API Proxy"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgid "API Token"
 msgstr ""
 msgstr ""
 
 
@@ -94,7 +95,12 @@ msgstr ""
 msgid "Arch"
 msgid "Arch"
 msgstr ""
 msgstr ""
 
 
-#: src/components/Notification/Notification.vue:84
+#: src/views/preference/AuthSettings.vue:94
+#, fuzzy
+msgid "Are you sure to delete this banned IP immediately?"
+msgstr "Bạn chắc chắn muốn xóa nó "
+
+#: src/components/Notification/Notification.vue:86
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgid "Are you sure you want to clear all notifications?"
@@ -147,6 +153,15 @@ msgstr "Hỏi ChatGPT"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "Trợ lý"
 msgstr "Trợ lý"
 
 
+#: src/views/preference/AuthSettings.vue:17
+msgid "Attempts"
+msgstr ""
+
+#: src/views/preference/Preference.vue:116
+#, fuzzy
+msgid "Auth"
+msgstr "Tác giả"
+
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 msgid "Author"
 msgid "Author"
@@ -170,8 +185,8 @@ msgstr "Đã bật tự động gia hạn SSL cho %{name}"
 
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
-#: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
-#: src/views/stream/StreamEdit.vue:247
+#: src/views/domain/DomainEdit.vue:254 src/views/nginx_log/NginxLog.vue:168
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgid "Back"
 msgstr "Quay lại"
 msgstr "Quay lại"
 
 
@@ -180,19 +195,31 @@ msgstr "Quay lại"
 msgid "Back Home"
 msgid "Back Home"
 msgstr "Quay lại"
 msgstr "Quay lại"
 
 
-#: src/views/domain/DomainAdd.vue:99
+#: src/views/preference/AuthSettings.vue:68
+msgid "Ban Threshold Minutes"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:82
+msgid "Banned IPs"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:20
+msgid "Banned Until"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:95
 msgid "Base information"
 msgid "Base information"
 msgstr "Thông tin"
 msgstr "Thông tin"
 
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:101
+#: src/views/preference/Preference.vue:110
 #: src/views/stream/components/RightSettings.vue:74
 #: src/views/stream/components/RightSettings.vue:74
 #, fuzzy
 #, fuzzy
 msgid "Basic"
 msgid "Basic"
 msgstr "Cơ bản"
 msgstr "Cơ bản"
 
 
-#: src/views/domain/DomainEdit.vue:202 src/views/stream/StreamEdit.vue:194
+#: src/views/domain/DomainEdit.vue:200 src/views/stream/StreamEdit.vue:192
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Cơ bản"
 msgstr "Cơ bản"
 
 
@@ -280,12 +307,12 @@ msgid "Cleaning environment variables"
 msgstr "Xoá các biến môi trường"
 msgstr "Xoá các biến môi trường"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgid "Clear"
 msgstr "Xoá"
 msgstr "Xoá"
 
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 #, fuzzy
 msgid "Cleared successfully"
 msgid "Cleared successfully"
@@ -312,7 +339,7 @@ msgstr "Mẫu Cấu hình"
 msgid "Configuration file is test successful"
 msgid "Configuration file is test successful"
 msgstr "Tệp cấu hình được kiểm tra thành công"
 msgstr "Tệp cấu hình được kiểm tra thành công"
 
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgid "Configuration Name"
 msgstr "Tên cấu hình"
 msgstr "Tên cấu hình"
 
 
@@ -320,7 +347,7 @@ msgstr "Tên cấu hình"
 msgid "Configurations"
 msgid "Configurations"
 msgstr "Cấu hình"
 msgstr "Cấu hình"
 
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgid "Configure SSL"
 msgstr "Cấu hình SSL"
 msgstr "Cấu hình SSL"
 
 
@@ -351,7 +378,7 @@ msgstr "CPU:"
 msgid "Create"
 msgid "Create"
 msgstr "Ngày tạo"
 msgstr "Ngày tạo"
 
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Tạo thêm"
 msgstr "Tạo thêm"
 
 
@@ -390,7 +417,7 @@ msgstr ""
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Bảng điều khiển"
 msgstr "Bảng điều khiển"
 
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "Tên cơ sở dữ liệu (Tuỳ chọn, Mặc định là: database)"
 msgstr "Tên cơ sở dữ liệu (Tuỳ chọn, Mặc định là: database)"
 
 
@@ -424,15 +451,15 @@ msgstr "Xoá trang web: %{site_name}"
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr "Đã xoá thành công"
 msgstr "Đã xoá thành công"
 
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: src/views/domain/components/RightSettings.vue:93
 #: src/views/domain/components/RightSettings.vue:93
-#: src/views/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 msgid "Deploy"
 msgstr "Triển khai"
 msgstr "Triển khai"
 
 
-#: src/views/domain/components/Deploy.vue:63
-#: src/views/stream/components/Deploy.vue:63
+#: src/views/domain/components/Deploy.vue:60
+#: src/views/stream/components/Deploy.vue:60
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr "Triển khai %{conf_name} tới %{node_name} thất bại"
 msgstr "Triển khai %{conf_name} tới %{node_name} thất bại"
 
 
@@ -482,9 +509,9 @@ msgstr "Tắt"
 msgid "Disable auto-renewal failed for %{name}"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "Tắt tự động gia hạn SSL cho %{name} thất bại"
 msgstr "Tắt tự động gia hạn SSL cho %{name} thất bại"
 
 
-#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:183
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Đã tắt"
 msgstr "Đã tắt"
 
 
@@ -563,7 +590,7 @@ msgstr "Bạn muốn xóa máy chủ này ?"
 msgid "Domain"
 msgid "Domain"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "Tên miền đã được tạo"
 msgstr "Tên miền đã được tạo"
 
 
@@ -585,38 +612,38 @@ msgstr "Đang tải phiên bản mới nhất"
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Đã bật chế độ Dry run"
 msgstr "Đã bật chế độ Dry run"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: src/views/domain/DomainList.vue:140
 #: src/views/domain/DomainList.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Nhân bản"
 msgstr "Nhân bản"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:84
-#: src/views/stream/components/StreamDuplicate.vue:84
+#: src/views/domain/components/SiteDuplicate.vue:82
+#: src/views/stream/components/StreamDuplicate.vue:82
 #, fuzzy
 #, fuzzy
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
 msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:90
-#: src/views/stream/components/StreamDuplicate.vue:90
+#: src/views/domain/components/SiteDuplicate.vue:87
+#: src/views/stream/components/StreamDuplicate.vue:87
 #, fuzzy
 #, fuzzy
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Nhân bản thất bại"
 msgstr "Nhân bản thất bại"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:82
+#: src/views/domain/components/SiteDuplicate.vue:80
+#: src/views/stream/components/StreamDuplicate.vue:80
 #, fuzzy
 #, fuzzy
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Nhân bản thành công"
 msgstr "Nhân bản thành công"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:64
-#: src/views/stream/components/StreamDuplicate.vue:64
+#: src/views/domain/components/SiteDuplicate.vue:63
+#: src/views/stream/components/StreamDuplicate.vue:63
 #, fuzzy
 #, fuzzy
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Đã sao chép thành công vào máy cục bộ"
 msgstr "Đã sao chép thành công vào máy cục bộ"
 
 
-#: src/views/domain/DomainEdit.vue:174 src/views/stream/StreamEdit.vue:166
+#: src/views/domain/DomainEdit.vue:172 src/views/stream/StreamEdit.vue:164
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Sửa %{n}"
 msgstr "Sửa %{n}"
 
 
@@ -638,25 +665,25 @@ msgstr "Sửa trang web"
 msgid "Email"
 msgid "Email"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: src/views/domain/DomainList.vue:132
 #: src/views/domain/DomainList.vue:132
-#: src/views/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #: src/views/stream/StreamList.vue:153
 #, fuzzy
 #, fuzzy
 msgid "Enable"
 msgid "Enable"
 msgstr "Đã bật"
 msgstr "Đã bật"
 
 
-#: src/views/domain/components/Deploy.vue:52
-#: src/views/stream/components/Deploy.vue:52
+#: src/views/domain/components/Deploy.vue:50
+#: src/views/stream/components/Deploy.vue:50
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "Không thể bật %{conf_name} trên %{node_name}"
 msgstr "Không thể bật %{conf_name} trên %{node_name}"
 
 
-#: src/views/domain/components/Deploy.vue:46
-#: src/views/stream/components/Deploy.vue:46
+#: src/views/domain/components/Deploy.vue:45
+#: src/views/stream/components/Deploy.vue:45
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "Đã bật %{conf_name} trên %{node_name}"
 msgstr "Đã bật %{conf_name} trên %{node_name}"
 
 
@@ -664,12 +691,12 @@ msgstr "Đã bật %{conf_name} trên %{node_name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "Không thể bật tự động gia hạn SSL cho %{name}"
 msgstr "Không thể bật tự động gia hạn SSL cho %{name}"
 
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 msgid "Enable failed"
 msgstr "Bật không thành công"
 msgstr "Bật không thành công"
 
 
-#: src/views/domain/components/Deploy.vue:44
-#: src/views/stream/components/Deploy.vue:44
+#: src/views/domain/components/Deploy.vue:43
+#: src/views/stream/components/Deploy.vue:43
 #, fuzzy
 #, fuzzy
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Đã bật"
 msgstr "Đã bật"
@@ -680,19 +707,19 @@ msgstr "Bật TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Đã bật"
 msgstr "Đã bật"
 
 
 #: src/views/domain/components/RightSettings.vue:29
 #: src/views/domain/components/RightSettings.vue:29
-#: src/views/domain/components/SiteDuplicate.vue:98
-#: src/views/domain/DomainAdd.vue:42 src/views/domain/DomainList.vue:57
+#: src/views/domain/components/SiteDuplicate.vue:94
+#: src/views/domain/DomainAdd.vue:40 src/views/domain/DomainList.vue:57
 #: src/views/stream/components/RightSettings.vue:29
 #: src/views/stream/components/RightSettings.vue:29
-#: src/views/stream/components/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Đã bật"
 msgstr "Đã bật"
@@ -763,7 +790,7 @@ msgstr "Không thể bật %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "Không thể truy xuất thông tin chứng chỉ"
 msgstr "Không thể truy xuất thông tin chứng chỉ"
 
 
-#: src/views/domain/DomainEdit.vue:132 src/views/stream/StreamEdit.vue:124
+#: src/views/domain/DomainEdit.vue:130 src/views/stream/StreamEdit.vue:122
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr "Không lưu được, đã phát hiện thấy (các) lỗi cú pháp trong cấu hình."
 msgstr "Không lưu được, đã phát hiện thấy (các) lỗi cú pháp trong cấu hình."
 
 
@@ -789,7 +816,7 @@ msgstr "Không tìm thấy tệp tin"
 msgid "Filter"
 msgid "Filter"
 msgstr "Lọc"
 msgstr "Lọc"
 
 
-#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:101
+#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgid "Finished"
 msgstr "Đã hoàn thành"
 msgstr "Đã hoàn thành"
 
 
@@ -862,6 +889,12 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:60
+msgid ""
+"If the number of login failed attempts from a ip reach the max attempts in "
+"ban threshold minutes, the ip will be banned for a period of time."
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:136
 #: src/views/certificate/Certificate.vue:136
 #, fuzzy
 #, fuzzy
 msgid "Import"
 msgid "Import"
@@ -872,6 +905,11 @@ msgstr "Xuất"
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "Chứng chỉ"
 msgstr "Chứng chỉ"
 
 
+#: src/views/other/Login.vue:59
+#, fuzzy
+msgid "Incorrect username or password"
+msgstr "Tên người dùng hoặc mật khẩu không chính xác"
+
 #: src/constants/index.ts:18
 #: src/constants/index.ts:18
 msgid "Info"
 msgid "Info"
 msgstr "Thông tin"
 msgstr "Thông tin"
@@ -884,7 +922,7 @@ msgstr "Không thể khởi tạo trình nâng cấp"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Đang khởi tạo trình nâng cấp"
 msgstr "Đang khởi tạo trình nâng cấp"
 
 
-#: src/routes/index.ts:273 src/views/other/Install.vue:136
+#: src/routes/index.ts:273 src/views/other/Install.vue:135
 msgid "Install"
 msgid "Install"
 msgstr "Cài đặt"
 msgstr "Cài đặt"
 
 
@@ -906,6 +944,10 @@ msgstr ""
 msgid "Invalid"
 msgid "Invalid"
 msgstr "Hợp lệ"
 msgstr "Hợp lệ"
 
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgid "Issue wildcard certificate"
@@ -940,7 +982,7 @@ msgstr "Kiểm tra lần cuối lúc"
 msgid "Leave blank for no change"
 msgid "Leave blank for no change"
 msgstr "Bỏ trống nếu không thay đổi"
 msgstr "Bỏ trống nếu không thay đổi"
 
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 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/"
 msgstr "Bỏ trống để sử dụng địa chỉ mặc định: https://api.openai.com/"
 
 
@@ -997,11 +1039,11 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "Log"
 msgstr "Log"
 
 
-#: src/routes/index.ts:279 src/views/other/Login.vue:147
+#: src/routes/index.ts:279 src/views/other/Login.vue:159
 msgid "Login"
 msgid "Login"
 msgstr "Đăng nhập"
 msgstr "Đăng nhập"
 
 
-#: src/views/other/Login.vue:51 src/views/other/Login.vue:97
+#: src/views/other/Login.vue:109 src/views/other/Login.vue:51
 msgid "Login successful"
 msgid "Login successful"
 msgstr "Đăng nhập thành công"
 msgstr "Đăng nhập thành công"
 
 
@@ -1009,7 +1051,7 @@ msgstr "Đăng nhập thành công"
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "Đã đăng xuất"
 msgstr "Đã đăng xuất"
 
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgid "Logrotate"
 msgstr ""
 msgstr ""
 
 
@@ -1053,6 +1095,10 @@ msgstr "Người dùng"
 msgid "Managed Certificate"
 msgid "Managed Certificate"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgid "Memory"
@@ -1066,7 +1112,7 @@ msgstr "Memory và Storage"
 msgid "Minutes"
 msgid "Minutes"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 #, fuzzy
 msgid "Model"
 msgid "Model"
 msgstr "Run Mode"
 msgstr "Run Mode"
@@ -1083,7 +1129,7 @@ msgstr "Sửa"
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Sửa chứng chỉ"
 msgstr "Sửa chứng chỉ"
 
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgid "Modify Config"
 msgstr "Sửa cấu hình"
 msgstr "Sửa cấu hình"
 
 
@@ -1103,12 +1149,12 @@ msgstr "Single Directive"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/RightSettings.vue:83
-#: src/views/domain/components/SiteDuplicate.vue:133
+#: src/views/domain/components/SiteDuplicate.vue:129
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: src/views/environment/Environment.vue:12
 #: src/views/stream/components/RightSettings.vue:82
 #: src/views/stream/components/RightSettings.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:133
+#: src/views/stream/components/StreamDuplicate.vue:129
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 msgid "Name"
 msgid "Name"
 msgstr "Tên"
 msgstr "Tên"
@@ -1135,11 +1181,11 @@ msgstr "Đã có phiên bản mới"
 
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgid "Next"
 msgstr "Tiếp theo"
 msgstr "Tiếp theo"
 
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgid "Nginx"
 msgstr ""
 msgstr ""
 
 
@@ -1147,7 +1193,7 @@ msgstr ""
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Vị trí lưu log truy cập (Access log) của Nginx"
 msgstr "Vị trí lưu log truy cập (Access log) của Nginx"
 
 
-#: src/views/domain/DomainEdit.vue:217 src/views/stream/StreamEdit.vue:209
+#: src/views/domain/DomainEdit.vue:215 src/views/stream/StreamEdit.vue:207
 #, fuzzy
 #, fuzzy
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Lỗi phân tích cú pháp cấu hình Nginx"
 msgstr "Lỗi phân tích cú pháp cấu hình Nginx"
@@ -1175,7 +1221,7 @@ msgid "Nginx restarted successfully"
 msgstr "Restart Nginx thành công"
 msgstr "Restart Nginx thành công"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/ChatGPT/ChatGPT.vue:270
-#: src/components/Notification/Notification.vue:82
+#: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
@@ -1183,6 +1229,7 @@ msgstr "Restart Nginx thành công"
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 #: src/views/stream/StreamList.vue:165
 msgid "No"
 msgid "No"
@@ -1214,7 +1261,7 @@ msgstr "Ghi chú"
 msgid "Notification"
 msgid "Notification"
 msgstr "Thông báo"
 msgstr "Thông báo"
 
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Thông báo"
 msgstr "Thông báo"
@@ -1240,7 +1287,7 @@ msgid "Ok"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/ChatGPT/ChatGPT.vue:271
-#: src/components/Notification/Notification.vue:83
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1270,7 +1317,7 @@ msgstr "Sau khi quá trình xác minh hoàn tất, bản ghi sẽ bị xóa."
 msgid "Online"
 msgid "Online"
 msgstr "Trực tuyến"
 msgstr "Trực tuyến"
 
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgid "OpenAI"
 msgstr ""
 msgstr ""
 
 
@@ -1283,13 +1330,13 @@ msgstr "Hệ điều hành"
 msgid "OS:"
 msgid "OS:"
 msgstr "Hệ điều hành:"
 msgstr "Hệ điều hành:"
 
 
-#: src/views/domain/components/Deploy.vue:90
-#: src/views/stream/components/Deploy.vue:90
+#: src/views/domain/components/Deploy.vue:87
+#: src/views/stream/components/Deploy.vue:87
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr "Ghi đè"
 msgstr "Ghi đè"
 
 
-#: src/views/domain/components/Deploy.vue:94
-#: src/views/stream/components/Deploy.vue:94
+#: src/views/domain/components/Deploy.vue:91
+#: src/views/stream/components/Deploy.vue:91
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr "Ghi đè tập tin đã tồn tại"
 msgstr "Ghi đè tập tin đã tồn tại"
 
 
@@ -1297,11 +1344,11 @@ msgstr "Ghi đè tập tin đã tồn tại"
 msgid "Params"
 msgid "Params"
 msgstr "Tham số"
 msgstr "Tham số"
 
 
-#: src/views/other/Login.vue:132 src/views/user/User.vue:18
+#: src/views/other/Login.vue:144 src/views/user/User.vue:18
 msgid "Password"
 msgid "Password"
 msgstr "Mật khẩu"
 msgstr "Mật khẩu"
 
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgid "Password (*)"
 msgstr "Mật khẩu (*)"
 msgstr "Mật khẩu (*)"
 
 
@@ -1368,7 +1415,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:105
 msgid "Preference"
 msgid "Preference"
 msgstr "Cài đặt"
 msgstr "Cài đặt"
 
 
@@ -1466,7 +1513,16 @@ msgstr "Đang tải lại"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "Tải lại nginx"
 msgstr "Tải lại nginx"
 
 
-#: src/components/Notification/Notification.vue:50
+#: src/views/preference/AuthSettings.vue:101
+msgid "Remove"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:47
+#, fuzzy
+msgid "Remove successfully"
+msgstr "Xoá thành công"
+
+#: src/components/Notification/Notification.vue:52
 #, fuzzy
 #, fuzzy
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Xoá thành công"
 msgstr "Xoá thành công"
@@ -1525,9 +1581,9 @@ msgstr "Running"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/certificate/CertificateEditor.vue:249
-#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
+#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:261
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgid "Save"
 msgstr "Lưu"
 msgstr "Lưu"
 
 
@@ -1535,7 +1591,7 @@ msgstr "Lưu"
 msgid "Save Directive"
 msgid "Save Directive"
 msgstr "Lưu Directive"
 msgstr "Lưu Directive"
 
 
-#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:50
+#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:46
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Đã xảy ra lỗi khi lưu %{msg}"
 msgstr "Đã xảy ra lỗi khi lưu %{msg}"
@@ -1543,15 +1599,15 @@ msgstr "Đã xảy ra lỗi khi lưu %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 #, fuzzy
 #, fuzzy
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "Lưu thành công"
 msgstr "Lưu thành công"
 
 
-#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:38
-#: src/views/domain/DomainEdit.vue:148
+#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:37
+#: src/views/domain/DomainEdit.vue:146
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
-#: src/views/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Lưu thành công"
 msgstr "Lưu thành công"
 
 
@@ -1576,8 +1632,9 @@ msgstr "Gửi"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
+#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:69
+#: src/views/preference/AuthSettings.vue:49
+#: src/views/preference/Preference.vue:78 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
 msgstr "Lỗi máy chủ"
 msgstr "Lỗi máy chủ"
@@ -1596,7 +1653,7 @@ msgid "server_name not found in directives"
 msgstr "không tìm thấy server_name trong directives"
 msgstr "không tìm thấy server_name trong directives"
 
 
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
-#: src/views/domain/DomainAdd.vue:121
+#: src/views/domain/DomainAdd.vue:117
 msgid "server_name parameter is required"
 msgid "server_name parameter is required"
 msgstr "Tham số server_name là bắt buộc"
 msgstr "Tham số server_name là bắt buộc"
 
 
@@ -1650,7 +1707,7 @@ msgstr ""
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr ""
 msgstr ""
 
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 msgid "SSO Login"
 msgid "SSO Login"
 msgstr ""
 msgstr ""
 
 
@@ -1736,8 +1793,8 @@ msgstr ""
 msgid "System"
 msgid "System"
 msgstr "Thông tin"
 msgstr "Thông tin"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:140
+#: src/views/domain/components/SiteDuplicate.vue:136
+#: src/views/stream/components/StreamDuplicate.vue:136
 msgid "Target"
 msgid "Target"
 msgstr "Mục tiêu"
 msgstr "Mục tiêu"
 
 
@@ -1770,7 +1827,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgid "The input is not a SSL Certificate Key"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid ""
 msgid ""
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "dashes, and dots."
 "dashes, and dots."
@@ -1803,8 +1860,8 @@ msgstr ""
 msgid "The url is invalid"
 msgid "The url is invalid"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:45
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:48
+#: src/views/preference/OpenAISettings.vue:60
 msgid "The url is invalid."
 msgid "The url is invalid."
 msgstr ""
 msgstr ""
 
 
@@ -1830,6 +1887,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr "Trường này không được để trống"
 msgstr "Trường này không được để trống"
 
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgid "Tips"
 msgstr ""
 msgstr ""
@@ -1850,10 +1908,14 @@ 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 "
 "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?"
 "Nginx. Bạn có chắc chắn muốn Tiếp tục?"
 
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 msgid "Token is not valid"
 msgstr ""
 msgstr ""
 
 
+#: src/views/other/Login.vue:62
+msgid "Too many login failed attempts, please try again later"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 msgid "Trash"
 msgid "Trash"
 msgstr ""
 msgstr ""
@@ -1909,11 +1971,15 @@ msgstr ""
 msgid "User"
 msgid "User"
 msgstr "Người dùng"
 msgstr "Người dùng"
 
 
-#: src/views/other/Login.vue:122 src/views/user/User.vue:9
+#: src/views/other/Login.vue:65
+msgid "User is banned"
+msgstr ""
+
+#: src/views/other/Login.vue:134 src/views/user/User.vue:9
 msgid "Username"
 msgid "Username"
 msgstr "Username"
 msgstr "Username"
 
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "Username (*)"
 msgstr "Username (*)"
 
 
@@ -1927,7 +1993,7 @@ msgstr "Hợp lệ"
 msgid "View"
 msgid "View"
 msgstr "Xem"
 msgstr "Xem"
 
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 #, fuzzy
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Xem tất cả thông báo"
 msgstr "Xem tất cả thông báo"
@@ -1944,7 +2010,7 @@ msgstr "Cơ bản"
 
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgid "Warning"
 msgstr "Lưu ý"
 msgstr "Lưu ý"
 
 
@@ -1979,6 +2045,7 @@ msgstr "Ghi chứng chỉ vào disk"
 
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgid "Yes"
 msgstr "Có"
 msgstr "Có"

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


+ 163 - 98
app/src/language/zh_CN/app.po

@@ -31,7 +31,8 @@ msgstr "ACME 用户"
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
 #: src/views/notification/Notification.vue:37
-#: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
+#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47
+#: src/views/user/User.vue:43
 msgid "Action"
 msgid "Action"
 msgstr "操作"
 msgstr "操作"
 
 
@@ -54,7 +55,7 @@ msgstr "在下面添加指令"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "添加 Location"
 msgstr "添加 Location"
 
 
-#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:93
+#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgid "Add Site"
 msgstr "添加站点"
 msgstr "添加站点"
 
 
@@ -70,11 +71,11 @@ msgstr "添加成功"
 msgid "Additional"
 msgid "Additional"
 msgstr "额外选项"
 msgstr "额外选项"
 
 
-#: src/views/domain/DomainEdit.vue:199 src/views/stream/StreamEdit.vue:191
+#: src/views/domain/DomainEdit.vue:197 src/views/stream/StreamEdit.vue:189
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "高级模式"
 msgstr "高级模式"
 
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgid "API Base Url"
 msgstr "API 地址"
 msgstr "API 地址"
 
 
@@ -82,11 +83,11 @@ msgstr "API 地址"
 msgid "API Document"
 msgid "API Document"
 msgstr "API 文档"
 msgstr "API 文档"
 
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgid "API Proxy"
 msgstr "API 代理"
 msgstr "API 代理"
 
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgid "API Token"
 msgstr "API Token"
 msgstr "API Token"
 
 
@@ -94,7 +95,11 @@ msgstr "API Token"
 msgid "Arch"
 msgid "Arch"
 msgstr "架构"
 msgstr "架构"
 
 
-#: src/components/Notification/Notification.vue:84
+#: src/views/preference/AuthSettings.vue:94
+msgid "Are you sure to delete this banned IP immediately?"
+msgstr "您确定要立即删除这个被禁用的 IP 吗?"
+
+#: src/components/Notification/Notification.vue:86
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
 msgid "Are you sure you want to clear all notifications?"
 msgid "Are you sure you want to clear all notifications?"
 msgstr "您确定要清除所有通知吗?"
 msgstr "您确定要清除所有通知吗?"
@@ -139,6 +144,14 @@ msgstr "与ChatGPT聊天"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "助手"
 msgstr "助手"
 
 
+#: src/views/preference/AuthSettings.vue:17
+msgid "Attempts"
+msgstr "尝试次数"
+
+#: src/views/preference/Preference.vue:116
+msgid "Auth"
+msgstr "认证"
+
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 msgid "Author"
 msgid "Author"
@@ -162,8 +175,8 @@ msgstr "成功启用 %{name} 自动续签"
 
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
-#: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
-#: src/views/stream/StreamEdit.vue:247
+#: src/views/domain/DomainEdit.vue:254 src/views/nginx_log/NginxLog.vue:168
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgid "Back"
 msgstr "返回"
 msgstr "返回"
 
 
@@ -171,18 +184,30 @@ msgstr "返回"
 msgid "Back Home"
 msgid "Back Home"
 msgstr "返回首页"
 msgstr "返回首页"
 
 
-#: src/views/domain/DomainAdd.vue:99
+#: src/views/preference/AuthSettings.vue:68
+msgid "Ban Threshold Minutes"
+msgstr "禁止阈值(分钟)"
+
+#: src/views/preference/AuthSettings.vue:82
+msgid "Banned IPs"
+msgstr "禁止 IP 列表"
+
+#: src/views/preference/AuthSettings.vue:20
+msgid "Banned Until"
+msgstr "禁用至"
+
+#: src/views/domain/DomainAdd.vue:95
 msgid "Base information"
 msgid "Base information"
 msgstr "基本信息"
 msgstr "基本信息"
 
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:101
+#: src/views/preference/Preference.vue:110
 #: src/views/stream/components/RightSettings.vue:74
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgid "Basic"
 msgstr "基本"
 msgstr "基本"
 
 
-#: src/views/domain/DomainEdit.vue:202 src/views/stream/StreamEdit.vue:194
+#: src/views/domain/DomainEdit.vue:200 src/views/stream/StreamEdit.vue:192
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "基本模式"
 msgstr "基本模式"
 
 
@@ -265,12 +290,12 @@ msgid "Cleaning environment variables"
 msgstr "正在清理环境变量"
 msgstr "正在清理环境变量"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgid "Clear"
 msgstr "清空"
 msgstr "清空"
 
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "清除成功"
 msgstr "清除成功"
@@ -294,7 +319,7 @@ msgstr "配置"
 msgid "Configuration file is test successful"
 msgid "Configuration file is test successful"
 msgstr "配置文件测试成功"
 msgstr "配置文件测试成功"
 
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgid "Configuration Name"
 msgstr "配置名称"
 msgstr "配置名称"
 
 
@@ -302,7 +327,7 @@ msgstr "配置名称"
 msgid "Configurations"
 msgid "Configurations"
 msgstr "配置"
 msgstr "配置"
 
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgid "Configure SSL"
 msgstr "配置 SSL"
 msgstr "配置 SSL"
 
 
@@ -332,7 +357,7 @@ msgstr "CPU:"
 msgid "Create"
 msgid "Create"
 msgstr "创建"
 msgstr "创建"
 
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgid "Create Another"
 msgstr "再创建一个"
 msgstr "再创建一个"
 
 
@@ -371,7 +396,7 @@ msgstr "自定义显示在环境指示器中的本地服务器名称。"
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "仪表盘"
 msgstr "仪表盘"
 
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "数据库 (可选,默认: database)"
 msgstr "数据库 (可选,默认: database)"
 
 
@@ -403,15 +428,15 @@ msgstr "删除 Stream: %{stream_name}"
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr "删除成功"
 msgstr "删除成功"
 
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: src/views/domain/components/RightSettings.vue:93
 #: src/views/domain/components/RightSettings.vue:93
-#: src/views/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 msgid "Deploy"
 msgstr "部署"
 msgstr "部署"
 
 
-#: src/views/domain/components/Deploy.vue:63
-#: src/views/stream/components/Deploy.vue:63
+#: src/views/domain/components/Deploy.vue:60
+#: src/views/stream/components/Deploy.vue:60
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr "部署%{conf_name}到%{node_name}失败"
 msgstr "部署%{conf_name}到%{node_name}失败"
 
 
@@ -458,9 +483,9 @@ msgstr "禁用"
 msgid "Disable auto-renewal failed for %{name}"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "关闭 %{name} 自动续签失败"
 msgstr "关闭 %{name} 自动续签失败"
 
 
-#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:183
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgid "Disabled"
 msgstr "禁用"
 msgstr "禁用"
 
 
@@ -530,7 +555,7 @@ msgstr "你想删除这个 Upstream 吗?"
 msgid "Domain"
 msgid "Domain"
 msgstr "域名"
 msgstr "域名"
 
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "域名配置文件创建成功"
 msgstr "域名配置文件创建成功"
 
 
@@ -550,34 +575,34 @@ msgstr "下载最新版本"
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "试运行模式已启动"
 msgstr "试运行模式已启动"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: src/views/domain/DomainList.vue:140
 #: src/views/domain/DomainList.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "复制"
 msgstr "复制"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:84
-#: src/views/stream/components/StreamDuplicate.vue:84
+#: src/views/domain/components/SiteDuplicate.vue:82
+#: src/views/stream/components/StreamDuplicate.vue:82
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "成功地将%{conf_name}复制到%{node_name}"
 msgstr "成功地将%{conf_name}复制到%{node_name}"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:90
-#: src/views/stream/components/StreamDuplicate.vue:90
+#: src/views/domain/components/SiteDuplicate.vue:87
+#: src/views/stream/components/StreamDuplicate.vue:87
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "复制失败"
 msgstr "复制失败"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:82
+#: src/views/domain/components/SiteDuplicate.vue:80
+#: src/views/stream/components/StreamDuplicate.vue:80
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "复制成功"
 msgstr "复制成功"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:64
-#: src/views/stream/components/StreamDuplicate.vue:64
+#: src/views/domain/components/SiteDuplicate.vue:63
+#: src/views/stream/components/StreamDuplicate.vue:63
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "成功复制到本地"
 msgstr "成功复制到本地"
 
 
-#: src/views/domain/DomainEdit.vue:174 src/views/stream/StreamEdit.vue:166
+#: src/views/domain/DomainEdit.vue:172 src/views/stream/StreamEdit.vue:164
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "编辑 %{n}"
 msgstr "编辑 %{n}"
 
 
@@ -597,24 +622,24 @@ msgstr "编辑 Stream"
 msgid "Email"
 msgid "Email"
 msgstr "邮箱"
 msgstr "邮箱"
 
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "邮箱 (*)"
 msgstr "邮箱 (*)"
 
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: src/views/domain/DomainList.vue:132
 #: src/views/domain/DomainList.vue:132
-#: src/views/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #: src/views/stream/StreamList.vue:153
 msgid "Enable"
 msgid "Enable"
 msgstr "启用"
 msgstr "启用"
 
 
-#: src/views/domain/components/Deploy.vue:52
-#: src/views/stream/components/Deploy.vue:52
+#: src/views/domain/components/Deploy.vue:50
+#: src/views/stream/components/Deploy.vue:50
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "在%{node_name}中启用%{conf_name}失败"
 msgstr "在%{node_name}中启用%{conf_name}失败"
 
 
-#: src/views/domain/components/Deploy.vue:46
-#: src/views/stream/components/Deploy.vue:46
+#: src/views/domain/components/Deploy.vue:45
+#: src/views/stream/components/Deploy.vue:45
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "成功启用%{node_name}中的%{conf_name}"
 msgstr "成功启用%{node_name}中的%{conf_name}"
 
 
@@ -622,12 +647,12 @@ msgstr "成功启用%{node_name}中的%{conf_name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "启用 %{name} 自动续签失败"
 msgstr "启用 %{name} 自动续签失败"
 
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 msgid "Enable failed"
 msgstr "启用失败"
 msgstr "启用失败"
 
 
-#: src/views/domain/components/Deploy.vue:44
-#: src/views/stream/components/Deploy.vue:44
+#: src/views/domain/components/Deploy.vue:43
+#: src/views/stream/components/Deploy.vue:43
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "启用成功"
 msgstr "启用成功"
 
 
@@ -637,19 +662,19 @@ msgstr "启用 TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgid "Enabled"
 msgstr "启用"
 msgstr "启用"
 
 
 #: src/views/domain/components/RightSettings.vue:29
 #: src/views/domain/components/RightSettings.vue:29
-#: src/views/domain/components/SiteDuplicate.vue:98
-#: src/views/domain/DomainAdd.vue:42 src/views/domain/DomainList.vue:57
+#: src/views/domain/components/SiteDuplicate.vue:94
+#: src/views/domain/DomainAdd.vue:40 src/views/domain/DomainList.vue:57
 #: src/views/stream/components/RightSettings.vue:29
 #: src/views/stream/components/RightSettings.vue:29
-#: src/views/stream/components/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "启用成功"
 msgstr "启用成功"
@@ -716,7 +741,7 @@ msgstr "启用失败 %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "获取证书信息失败"
 msgstr "获取证书信息失败"
 
 
-#: src/views/domain/DomainEdit.vue:132 src/views/stream/StreamEdit.vue:124
+#: src/views/domain/DomainEdit.vue:130 src/views/stream/StreamEdit.vue:122
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr "保存失败,在配置中检测到语法错误。"
 msgstr "保存失败,在配置中检测到语法错误。"
 
 
@@ -741,7 +766,7 @@ msgstr "未找到文件"
 msgid "Filter"
 msgid "Filter"
 msgstr "过滤"
 msgstr "过滤"
 
 
-#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:101
+#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgid "Finished"
 msgstr "完成"
 msgstr "完成"
 
 
@@ -809,6 +834,14 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgid "If left blank, the default CA Dir will be used."
 msgstr "如果留空,则使用默认 CA Dir。"
 msgstr "如果留空,则使用默认 CA Dir。"
 
 
+#: src/views/preference/AuthSettings.vue:60
+msgid ""
+"If the number of login failed attempts from a ip reach the max attempts in "
+"ban threshold minutes, the ip will be banned for a period of time."
+msgstr ""
+"如果某个 IP 的登录失败次数达到禁用阈值分钟内的最大尝试次数,该 IP 将被禁止登"
+"录一段时间。"
+
 #: src/views/certificate/Certificate.vue:136
 #: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgid "Import"
 msgstr "导入"
 msgstr "导入"
@@ -817,6 +850,10 @@ msgstr "导入"
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "导入证书"
 msgstr "导入证书"
 
 
+#: src/views/other/Login.vue:59
+msgid "Incorrect username or password"
+msgstr "用户名或密码错误"
+
 #: src/constants/index.ts:18
 #: src/constants/index.ts:18
 msgid "Info"
 msgid "Info"
 msgstr "信息"
 msgstr "信息"
@@ -829,7 +866,7 @@ msgstr "初始化核心升级程序错误"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "初始化核心升级器"
 msgstr "初始化核心升级器"
 
 
-#: src/routes/index.ts:273 src/views/other/Install.vue:136
+#: src/routes/index.ts:273 src/views/other/Install.vue:135
 msgid "Install"
 msgid "Install"
 msgstr "安装"
 msgstr "安装"
 
 
@@ -849,6 +886,10 @@ msgstr "间隔"
 msgid "Invalid"
 msgid "Invalid"
 msgstr "无效的"
 msgstr "无效的"
 
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr "IP"
+
 #: src/views/certificate/Certificate.vue:144
 #: src/views/certificate/Certificate.vue:144
 msgid "Issue wildcard certificate"
 msgid "Issue wildcard certificate"
 msgstr "签发通配符证书"
 msgstr "签发通配符证书"
@@ -879,7 +920,7 @@ msgstr "最后检查时间"
 msgid "Leave blank for no change"
 msgid "Leave blank for no change"
 msgstr "留空表示不修改"
 msgstr "留空表示不修改"
 
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "留空为默认:https://api.openai.com/"
 msgstr "留空为默认:https://api.openai.com/"
 
 
@@ -930,11 +971,11 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "日志"
 msgstr "日志"
 
 
-#: src/routes/index.ts:279 src/views/other/Login.vue:147
+#: src/routes/index.ts:279 src/views/other/Login.vue:159
 msgid "Login"
 msgid "Login"
 msgstr "登录"
 msgstr "登录"
 
 
-#: src/views/other/Login.vue:51 src/views/other/Login.vue:97
+#: src/views/other/Login.vue:109 src/views/other/Login.vue:51
 msgid "Login successful"
 msgid "Login successful"
 msgstr "登录成功"
 msgstr "登录成功"
 
 
@@ -942,7 +983,7 @@ msgstr "登录成功"
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "登出成功"
 msgstr "登出成功"
 
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgid "Logrotate"
 msgstr "Logrotate"
 msgstr "Logrotate"
 
 
@@ -988,6 +1029,10 @@ msgstr "用户管理"
 msgid "Managed Certificate"
 msgid "Managed Certificate"
 msgstr "托管证书"
 msgstr "托管证书"
 
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr "最大尝试次数"
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgid "Memory"
@@ -1001,7 +1046,7 @@ msgstr "内存与存储"
 msgid "Minutes"
 msgid "Minutes"
 msgstr "分钟"
 msgstr "分钟"
 
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 msgid "Model"
 msgid "Model"
 msgstr "模型"
 msgstr "模型"
 
 
@@ -1015,7 +1060,7 @@ msgstr "修改"
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "修改证书"
 msgstr "修改证书"
 
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgid "Modify Config"
 msgstr "修改配置文件"
 msgstr "修改配置文件"
 
 
@@ -1033,12 +1078,12 @@ msgstr "多行指令"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/RightSettings.vue:83
-#: src/views/domain/components/SiteDuplicate.vue:133
+#: src/views/domain/components/SiteDuplicate.vue:129
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: src/views/environment/Environment.vue:12
 #: src/views/stream/components/RightSettings.vue:82
 #: src/views/stream/components/RightSettings.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:133
+#: src/views/stream/components/StreamDuplicate.vue:129
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 msgid "Name"
 msgid "Name"
 msgstr "名称"
 msgstr "名称"
@@ -1065,11 +1110,11 @@ msgstr "新版本发布"
 
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgid "Next"
 msgstr "下一步"
 msgstr "下一步"
 
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgid "Nginx"
 msgstr "Nginx"
 msgstr "Nginx"
 
 
@@ -1077,7 +1122,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Nginx 访问日志路径"
 msgstr "Nginx 访问日志路径"
 
 
-#: src/views/domain/DomainEdit.vue:217 src/views/stream/StreamEdit.vue:209
+#: src/views/domain/DomainEdit.vue:215 src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 配置解析错误"
 msgstr "Nginx 配置解析错误"
 
 
@@ -1102,7 +1147,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx 重启成功"
 msgstr "Nginx 重启成功"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/ChatGPT/ChatGPT.vue:270
-#: src/components/Notification/Notification.vue:82
+#: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
@@ -1110,6 +1155,7 @@ msgstr "Nginx 重启成功"
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 #: src/views/stream/StreamList.vue:165
 msgid "No"
 msgid "No"
@@ -1140,7 +1186,7 @@ msgstr "注意"
 msgid "Notification"
 msgid "Notification"
 msgstr "通知"
 msgstr "通知"
 
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 msgid "Notifications"
 msgid "Notifications"
 msgstr "通知"
 msgstr "通知"
 
 
@@ -1164,7 +1210,7 @@ msgid "Ok"
 msgstr "确定"
 msgstr "确定"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/ChatGPT/ChatGPT.vue:271
-#: src/components/Notification/Notification.vue:83
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1194,7 +1240,7 @@ msgstr "一旦验证完成,这些记录将被删除。"
 msgid "Online"
 msgid "Online"
 msgstr "在线"
 msgstr "在线"
 
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgid "OpenAI"
 msgstr "OpenAI"
 msgstr "OpenAI"
 
 
@@ -1206,13 +1252,13 @@ msgstr "OS"
 msgid "OS:"
 msgid "OS:"
 msgstr "OS:"
 msgstr "OS:"
 
 
-#: src/views/domain/components/Deploy.vue:90
-#: src/views/stream/components/Deploy.vue:90
+#: src/views/domain/components/Deploy.vue:87
+#: src/views/stream/components/Deploy.vue:87
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr "覆盖"
 msgstr "覆盖"
 
 
-#: src/views/domain/components/Deploy.vue:94
-#: src/views/stream/components/Deploy.vue:94
+#: src/views/domain/components/Deploy.vue:91
+#: src/views/stream/components/Deploy.vue:91
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr "覆盖现有文件"
 msgstr "覆盖现有文件"
 
 
@@ -1220,11 +1266,11 @@ msgstr "覆盖现有文件"
 msgid "Params"
 msgid "Params"
 msgstr "参数"
 msgstr "参数"
 
 
-#: src/views/other/Login.vue:132 src/views/user/User.vue:18
+#: src/views/other/Login.vue:144 src/views/user/User.vue:18
 msgid "Password"
 msgid "Password"
 msgstr "密码"
 msgstr "密码"
 
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgid "Password (*)"
 msgstr "密码 (*)"
 msgstr "密码 (*)"
 
 
@@ -1289,7 +1335,7 @@ msgstr "请至少选择一个节点!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "预发布"
 msgstr "预发布"
 
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:105
 msgid "Preference"
 msgid "Preference"
 msgstr "偏好设置"
 msgstr "偏好设置"
 
 
@@ -1380,7 +1426,15 @@ msgstr "重载中"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "正在重载 Nginx"
 msgstr "正在重载 Nginx"
 
 
-#: src/components/Notification/Notification.vue:50
+#: src/views/preference/AuthSettings.vue:101
+msgid "Remove"
+msgstr "删除"
+
+#: src/views/preference/AuthSettings.vue:47
+msgid "Remove successfully"
+msgstr "移除成功"
+
+#: src/components/Notification/Notification.vue:52
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "删除成功"
 msgstr "删除成功"
 
 
@@ -1432,9 +1486,9 @@ msgstr "运行中"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/certificate/CertificateEditor.vue:249
-#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
+#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:261
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgid "Save"
 msgstr "保存"
 msgstr "保存"
 
 
@@ -1442,7 +1496,7 @@ msgstr "保存"
 msgid "Save Directive"
 msgid "Save Directive"
 msgstr "保存指令"
 msgstr "保存指令"
 
 
-#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:50
+#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:46
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "保存错误 %{msg}"
 msgstr "保存错误 %{msg}"
@@ -1450,14 +1504,14 @@ msgstr "保存错误 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "保存成功"
 msgstr "保存成功"
 
 
-#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:38
-#: src/views/domain/DomainEdit.vue:148
+#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:37
+#: src/views/domain/DomainEdit.vue:146
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
-#: src/views/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "保存成功"
 msgstr "保存成功"
 
 
@@ -1482,8 +1536,9 @@ msgstr "上传"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
+#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:69
+#: src/views/preference/AuthSettings.vue:49
+#: src/views/preference/Preference.vue:78 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
 msgstr "服务器错误"
 msgstr "服务器错误"
@@ -1501,7 +1556,7 @@ msgid "server_name not found in directives"
 msgstr "未在指令集合中找到 server_name"
 msgstr "未在指令集合中找到 server_name"
 
 
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
-#: src/views/domain/DomainAdd.vue:121
+#: src/views/domain/DomainAdd.vue:117
 msgid "server_name parameter is required"
 msgid "server_name parameter is required"
 msgstr "必须为 server_name 指令指明参数"
 msgstr "必须为 server_name 指令指明参数"
 
 
@@ -1553,7 +1608,7 @@ msgstr "SSL证书密钥路径"
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "SSL证书路径"
 msgstr "SSL证书路径"
 
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 msgid "SSO Login"
 msgid "SSO Login"
 msgstr "SSO 登录"
 msgstr "SSO 登录"
 
 
@@ -1633,8 +1688,8 @@ msgstr "同步到"
 msgid "System"
 msgid "System"
 msgstr "系统"
 msgstr "系统"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:140
+#: src/views/domain/components/SiteDuplicate.vue:136
+#: src/views/stream/components/StreamDuplicate.vue:136
 msgid "Target"
 msgid "Target"
 msgstr "目标"
 msgstr "目标"
 
 
@@ -1666,7 +1721,7 @@ msgstr "输入的内容不是 SSL 证书"
 msgid "The input is not a SSL Certificate Key"
 msgid "The input is not a SSL Certificate Key"
 msgstr "输入的内容不是 SSL 证书密钥"
 msgstr "输入的内容不是 SSL 证书密钥"
 
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid ""
 msgid ""
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "dashes, and dots."
 "dashes, and dots."
@@ -1697,8 +1752,8 @@ msgstr "当前配置中的 server_name 必须是获取证书所需的域名,
 msgid "The url is invalid"
 msgid "The url is invalid"
 msgstr "URL无效"
 msgstr "URL无效"
 
 
-#: src/views/preference/OpenAISettings.vue:45
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:48
+#: src/views/preference/OpenAISettings.vue:60
 msgid "The url is invalid."
 msgid "The url is invalid."
 msgstr "URL 无效."
 msgstr "URL 无效."
 
 
@@ -1724,6 +1779,7 @@ msgstr "此字段必填"
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr "该字段不能为空"
 msgstr "该字段不能为空"
 
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgid "Tips"
 msgstr "提示"
 msgstr "提示"
@@ -1742,10 +1798,14 @@ msgstr ""
 "为了确保认证自动更新能够正常工作,我们需要添加一个能够代理从权威机构到后端的"
 "为了确保认证自动更新能够正常工作,我们需要添加一个能够代理从权威机构到后端的"
 "请求的 Location,并且我们需要保存这个文件并重新加载Nginx。你确定要继续吗?"
 "请求的 Location,并且我们需要保存这个文件并重新加载Nginx。你确定要继续吗?"
 
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 msgid "Token is not valid"
 msgstr "Token 无效"
 msgstr "Token 无效"
 
 
+#: src/views/other/Login.vue:62
+msgid "Too many login failed attempts, please try again later"
+msgstr "登录失败次数过多,请稍后再试"
+
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 msgid "Trash"
 msgid "Trash"
 msgstr "回收站"
 msgstr "回收站"
@@ -1798,11 +1858,15 @@ msgstr "URL"
 msgid "User"
 msgid "User"
 msgstr "用户"
 msgstr "用户"
 
 
-#: src/views/other/Login.vue:122 src/views/user/User.vue:9
+#: src/views/other/Login.vue:65
+msgid "User is banned"
+msgstr "用户被禁止"
+
+#: src/views/other/Login.vue:134 src/views/user/User.vue:9
 msgid "Username"
 msgid "Username"
 msgstr "用户名"
 msgstr "用户名"
 
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "用户名 (*)"
 msgstr "用户名 (*)"
 
 
@@ -1816,7 +1880,7 @@ msgstr "有效的"
 msgid "View"
 msgid "View"
 msgstr "查看"
 msgstr "查看"
 
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "查看全部通知"
 msgstr "查看全部通知"
 
 
@@ -1830,7 +1894,7 @@ msgstr "预览模式"
 
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgid "Warning"
 msgstr "警告"
 msgstr "警告"
 
 
@@ -1862,6 +1926,7 @@ msgstr "正在将证书写入磁盘"
 
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgid "Yes"
 msgstr "是的"
 msgstr "是的"

+ 165 - 98
app/src/language/zh_TW/app.po

@@ -33,7 +33,8 @@ msgstr "使用者名稱"
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
 #: src/views/notification/Notification.vue:37
-#: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
+#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47
+#: src/views/user/User.vue:43
 msgid "Action"
 msgid "Action"
 msgstr "操作"
 msgstr "操作"
 
 
@@ -56,7 +57,7 @@ msgstr "在下方新增指令"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "新增 Location"
 msgstr "新增 Location"
 
 
-#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:93
+#: src/routes/index.ts:64 src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgid "Add Site"
 msgstr "新增網站"
 msgstr "新增網站"
 
 
@@ -74,11 +75,11 @@ msgstr "更新成功"
 msgid "Additional"
 msgid "Additional"
 msgstr "其他設定"
 msgstr "其他設定"
 
 
-#: src/views/domain/DomainEdit.vue:199 src/views/stream/StreamEdit.vue:191
+#: src/views/domain/DomainEdit.vue:197 src/views/stream/StreamEdit.vue:189
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "進階模式"
 msgstr "進階模式"
 
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgid "API Base Url"
 msgstr "API 基礎網址"
 msgstr "API 基礎網址"
 
 
@@ -87,11 +88,11 @@ msgstr "API 基礎網址"
 msgid "API Document"
 msgid "API Document"
 msgstr "API Token"
 msgstr "API Token"
 
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgid "API Proxy"
 msgstr "API 代理"
 msgstr "API 代理"
 
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgid "API Token"
 msgstr "API Token"
 msgstr "API Token"
 
 
@@ -99,7 +100,12 @@ msgstr "API Token"
 msgid "Arch"
 msgid "Arch"
 msgstr "架構"
 msgstr "架構"
 
 
-#: src/components/Notification/Notification.vue:84
+#: src/views/preference/AuthSettings.vue:94
+#, fuzzy
+msgid "Are you sure to delete this banned IP immediately?"
+msgstr "您確定要刪除嗎?"
+
+#: src/components/Notification/Notification.vue:86
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgid "Are you sure you want to clear all notifications?"
@@ -149,6 +155,15 @@ msgstr "向 ChatGPT 尋求幫助"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "助理"
 msgstr "助理"
 
 
+#: src/views/preference/AuthSettings.vue:17
+msgid "Attempts"
+msgstr ""
+
+#: src/views/preference/Preference.vue:116
+#, fuzzy
+msgid "Auth"
+msgstr "作者"
+
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:106
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:120
 msgid "Author"
 msgid "Author"
@@ -172,8 +187,8 @@ msgstr "已啟用 %{name} 的自動續簽"
 
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
-#: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
-#: src/views/stream/StreamEdit.vue:247
+#: src/views/domain/DomainEdit.vue:254 src/views/nginx_log/NginxLog.vue:168
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgid "Back"
 msgstr "返回"
 msgstr "返回"
 
 
@@ -181,18 +196,30 @@ msgstr "返回"
 msgid "Back Home"
 msgid "Back Home"
 msgstr "返回首頁"
 msgstr "返回首頁"
 
 
-#: src/views/domain/DomainAdd.vue:99
+#: src/views/preference/AuthSettings.vue:68
+msgid "Ban Threshold Minutes"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:82
+msgid "Banned IPs"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:20
+msgid "Banned Until"
+msgstr ""
+
+#: src/views/domain/DomainAdd.vue:95
 msgid "Base information"
 msgid "Base information"
 msgstr "基本資訊"
 msgstr "基本資訊"
 
 
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/config/ConfigEdit.vue:115
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/preference/Preference.vue:101
+#: src/views/preference/Preference.vue:110
 #: src/views/stream/components/RightSettings.vue:74
 #: src/views/stream/components/RightSettings.vue:74
 msgid "Basic"
 msgid "Basic"
 msgstr "基本"
 msgstr "基本"
 
 
-#: src/views/domain/DomainEdit.vue:202 src/views/stream/StreamEdit.vue:194
+#: src/views/domain/DomainEdit.vue:200 src/views/stream/StreamEdit.vue:192
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "基本模式"
 msgstr "基本模式"
 
 
@@ -278,12 +305,12 @@ msgid "Cleaning environment variables"
 msgstr "清理環境變數"
 msgstr "清理環境變數"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgid "Clear"
 msgstr "清除"
 msgstr "清除"
 
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 #, fuzzy
 msgid "Cleared successfully"
 msgid "Cleared successfully"
@@ -309,7 +336,7 @@ msgstr "設定模板"
 msgid "Configuration file is test successful"
 msgid "Configuration file is test successful"
 msgstr "設定檔案測試成功"
 msgstr "設定檔案測試成功"
 
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgid "Configuration Name"
 msgstr "設定名稱"
 msgstr "設定名稱"
 
 
@@ -317,7 +344,7 @@ msgstr "設定名稱"
 msgid "Configurations"
 msgid "Configurations"
 msgstr "設定"
 msgstr "設定"
 
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgid "Configure SSL"
 msgstr "設定 SSL"
 msgstr "設定 SSL"
 
 
@@ -348,7 +375,7 @@ msgstr "中央處理器:"
 msgid "Create"
 msgid "Create"
 msgstr "建立時間"
 msgstr "建立時間"
 
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgid "Create Another"
 msgstr "再建立一個"
 msgstr "再建立一個"
 
 
@@ -387,7 +414,7 @@ msgstr ""
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "儀表板"
 msgstr "儀表板"
 
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "資料庫 (可選,預設: database)"
 msgstr "資料庫 (可選,預設: database)"
 
 
@@ -421,15 +448,15 @@ msgstr "刪除網站:%{site_name}"
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr "成功停用"
 msgstr "成功停用"
 
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: src/views/domain/components/RightSettings.vue:93
 #: src/views/domain/components/RightSettings.vue:93
-#: src/views/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 msgid "Deploy"
 msgstr "部署"
 msgstr "部署"
 
 
-#: src/views/domain/components/Deploy.vue:63
-#: src/views/stream/components/Deploy.vue:63
+#: src/views/domain/components/Deploy.vue:60
+#: src/views/stream/components/Deploy.vue:60
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr "部署 %{conf_name} 至 %{node_name} 失敗"
 msgstr "部署 %{conf_name} 至 %{node_name} 失敗"
 
 
@@ -477,9 +504,9 @@ msgstr "停用"
 msgid "Disable auto-renewal failed for %{name}"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "關閉 %{name} 自動續簽失敗"
 msgstr "關閉 %{name} 自動續簽失敗"
 
 
-#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:185
+#: src/views/domain/cert/ChangeCert.vue:44 src/views/domain/DomainEdit.vue:183
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
 #: src/views/domain/DomainList.vue:33 src/views/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgid "Disabled"
 msgstr "停用"
 msgstr "停用"
 
 
@@ -552,7 +579,7 @@ msgstr "您要移除此伺服器嗎?"
 msgid "Domain"
 msgid "Domain"
 msgstr "網域"
 msgstr "網域"
 
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "網域設定檔成功建立"
 msgstr "網域設定檔成功建立"
 
 
@@ -573,34 +600,34 @@ msgstr "正在下載最新版本"
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "試運轉模式已啟用"
 msgstr "試運轉模式已啟用"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: src/views/domain/DomainList.vue:140
 #: src/views/domain/DomainList.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "複製"
 msgstr "複製"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:84
-#: src/views/stream/components/StreamDuplicate.vue:84
+#: src/views/domain/components/SiteDuplicate.vue:82
+#: src/views/stream/components/StreamDuplicate.vue:82
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "成功複製 %{conf_name} 到 %{node_name}"
 msgstr "成功複製 %{conf_name} 到 %{node_name}"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:90
-#: src/views/stream/components/StreamDuplicate.vue:90
+#: src/views/domain/components/SiteDuplicate.vue:87
+#: src/views/stream/components/StreamDuplicate.vue:87
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "複製失敗"
 msgstr "複製失敗"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:82
+#: src/views/domain/components/SiteDuplicate.vue:80
+#: src/views/stream/components/StreamDuplicate.vue:80
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "複製成功"
 msgstr "複製成功"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:64
-#: src/views/stream/components/StreamDuplicate.vue:64
+#: src/views/domain/components/SiteDuplicate.vue:63
+#: src/views/stream/components/StreamDuplicate.vue:63
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "成功複製至本機"
 msgstr "成功複製至本機"
 
 
-#: src/views/domain/DomainEdit.vue:174 src/views/stream/StreamEdit.vue:166
+#: src/views/domain/DomainEdit.vue:172 src/views/stream/StreamEdit.vue:164
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "編輯 %{n}"
 msgstr "編輯 %{n}"
 
 
@@ -622,24 +649,24 @@ msgstr "編輯網站"
 msgid "Email"
 msgid "Email"
 msgstr "電子郵件 (*)"
 msgstr "電子郵件 (*)"
 
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "電子郵件 (*)"
 msgstr "電子郵件 (*)"
 
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: src/views/domain/DomainList.vue:132
 #: src/views/domain/DomainList.vue:132
-#: src/views/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #: src/views/stream/StreamList.vue:153
 msgid "Enable"
 msgid "Enable"
 msgstr "啟用"
 msgstr "啟用"
 
 
-#: src/views/domain/components/Deploy.vue:52
-#: src/views/stream/components/Deploy.vue:52
+#: src/views/domain/components/Deploy.vue:50
+#: src/views/stream/components/Deploy.vue:50
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "在 %{node_name} 啟用 %{conf_name} 失敗"
 msgstr "在 %{node_name} 啟用 %{conf_name} 失敗"
 
 
-#: src/views/domain/components/Deploy.vue:46
-#: src/views/stream/components/Deploy.vue:46
+#: src/views/domain/components/Deploy.vue:45
+#: src/views/stream/components/Deploy.vue:45
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "成功在 %{node_name} 啟用 %{conf_name}"
 msgstr "成功在 %{node_name} 啟用 %{conf_name}"
 
 
@@ -647,12 +674,12 @@ msgstr "成功在 %{node_name} 啟用 %{conf_name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "啟用 %{name} 自動續簽失敗"
 msgstr "啟用 %{name} 自動續簽失敗"
 
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 msgid "Enable failed"
 msgstr "啟用失敗"
 msgstr "啟用失敗"
 
 
-#: src/views/domain/components/Deploy.vue:44
-#: src/views/stream/components/Deploy.vue:44
+#: src/views/domain/components/Deploy.vue:43
+#: src/views/stream/components/Deploy.vue:43
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "啟用成功"
 msgstr "啟用成功"
 
 
@@ -662,19 +689,19 @@ msgstr "啟用 TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgid "Enabled"
 msgstr "已啟用"
 msgstr "已啟用"
 
 
 #: src/views/domain/components/RightSettings.vue:29
 #: src/views/domain/components/RightSettings.vue:29
-#: src/views/domain/components/SiteDuplicate.vue:98
-#: src/views/domain/DomainAdd.vue:42 src/views/domain/DomainList.vue:57
+#: src/views/domain/components/SiteDuplicate.vue:94
+#: src/views/domain/DomainAdd.vue:40 src/views/domain/DomainList.vue:57
 #: src/views/stream/components/RightSettings.vue:29
 #: src/views/stream/components/RightSettings.vue:29
-#: src/views/stream/components/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "成功啟用"
 msgstr "成功啟用"
@@ -744,7 +771,7 @@ msgstr "啟用 %{msg} 失敗"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "取得憑證資訊失敗"
 msgstr "取得憑證資訊失敗"
 
 
-#: src/views/domain/DomainEdit.vue:132 src/views/stream/StreamEdit.vue:124
+#: src/views/domain/DomainEdit.vue:130 src/views/stream/StreamEdit.vue:122
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr "儲存失敗,在設定中檢測到語法錯誤。"
 msgstr "儲存失敗,在設定中檢測到語法錯誤。"
 
 
@@ -770,7 +797,7 @@ msgstr "找不到檔案"
 msgid "Filter"
 msgid "Filter"
 msgstr "篩選"
 msgstr "篩選"
 
 
-#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:101
+#: src/language/constants.ts:19 src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgid "Finished"
 msgstr "完成"
 msgstr "完成"
 
 
@@ -840,6 +867,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/AuthSettings.vue:60
+msgid ""
+"If the number of login failed attempts from a ip reach the max attempts in "
+"ban threshold minutes, the ip will be banned for a period of time."
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:136
 #: src/views/certificate/Certificate.vue:136
 #, fuzzy
 #, fuzzy
 msgid "Import"
 msgid "Import"
@@ -850,6 +883,11 @@ msgstr "匯出"
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
 
 
+#: src/views/other/Login.vue:59
+#, fuzzy
+msgid "Incorrect username or password"
+msgstr "使用者名稱或密碼不正確"
+
 #: src/constants/index.ts:18
 #: src/constants/index.ts:18
 msgid "Info"
 msgid "Info"
 msgstr ""
 msgstr ""
@@ -862,7 +900,7 @@ msgstr "初始化核心升級程式錯誤"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "正在初始化核心升級程式"
 msgstr "正在初始化核心升級程式"
 
 
-#: src/routes/index.ts:273 src/views/other/Install.vue:136
+#: src/routes/index.ts:273 src/views/other/Install.vue:135
 msgid "Install"
 msgid "Install"
 msgstr "安裝"
 msgstr "安裝"
 
 
@@ -883,6 +921,10 @@ msgstr ""
 msgid "Invalid"
 msgid "Invalid"
 msgstr "無效的郵箱!"
 msgstr "無效的郵箱!"
 
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgid "Issue wildcard certificate"
@@ -916,7 +958,7 @@ msgstr "上次檢查時間"
 msgid "Leave blank for no change"
 msgid "Leave blank for no change"
 msgstr "留空表示不修改"
 msgstr "留空表示不修改"
 
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "預設留空:https://api.openai.com/"
 msgstr "預設留空:https://api.openai.com/"
 
 
@@ -972,11 +1014,11 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "登入"
 msgstr "登入"
 
 
-#: src/routes/index.ts:279 src/views/other/Login.vue:147
+#: src/routes/index.ts:279 src/views/other/Login.vue:159
 msgid "Login"
 msgid "Login"
 msgstr "登入"
 msgstr "登入"
 
 
-#: src/views/other/Login.vue:51 src/views/other/Login.vue:97
+#: src/views/other/Login.vue:109 src/views/other/Login.vue:51
 msgid "Login successful"
 msgid "Login successful"
 msgstr "登入成功"
 msgstr "登入成功"
 
 
@@ -984,7 +1026,7 @@ msgstr "登入成功"
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "登出成功"
 msgstr "登出成功"
 
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgid "Logrotate"
 msgstr ""
 msgstr ""
 
 
@@ -1027,6 +1069,10 @@ msgstr "管理使用者"
 msgid "Managed Certificate"
 msgid "Managed Certificate"
 msgstr "更換憑證"
 msgstr "更換憑證"
 
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
 msgid "Memory"
@@ -1040,7 +1086,7 @@ msgstr "記憶體與儲存"
 msgid "Minutes"
 msgid "Minutes"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 #, fuzzy
 msgid "Model"
 msgid "Model"
 msgstr "執行模式"
 msgstr "執行模式"
@@ -1056,7 +1102,7 @@ msgstr "修改"
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
 
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgid "Modify Config"
 msgstr "修改設定"
 msgstr "修改設定"
 
 
@@ -1075,12 +1121,12 @@ msgstr "多行指令"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/RightSettings.vue:83
-#: src/views/domain/components/SiteDuplicate.vue:133
+#: src/views/domain/components/SiteDuplicate.vue:129
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/DomainList.vue:13
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/domain/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: src/views/environment/Environment.vue:12
 #: src/views/stream/components/RightSettings.vue:82
 #: src/views/stream/components/RightSettings.vue:82
-#: src/views/stream/components/StreamDuplicate.vue:133
+#: src/views/stream/components/StreamDuplicate.vue:129
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 #: src/views/stream/StreamList.vue:13 src/views/stream/StreamList.vue:187
 msgid "Name"
 msgid "Name"
 msgstr "名稱"
 msgstr "名稱"
@@ -1107,11 +1153,11 @@ msgstr "新版本發布"
 
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgid "Next"
 msgstr "下一步"
 msgstr "下一步"
 
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgid "Nginx"
 msgstr "Nginx"
 msgstr "Nginx"
 
 
@@ -1119,7 +1165,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Nginx 存取日誌路徑"
 msgstr "Nginx 存取日誌路徑"
 
 
-#: src/views/domain/DomainEdit.vue:217 src/views/stream/StreamEdit.vue:209
+#: src/views/domain/DomainEdit.vue:215 src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 設定解析錯誤"
 msgstr "Nginx 設定解析錯誤"
 
 
@@ -1144,7 +1190,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx 重啟成功"
 msgstr "Nginx 重啟成功"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/ChatGPT/ChatGPT.vue:270
-#: src/components/Notification/Notification.vue:82
+#: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
@@ -1152,6 +1198,7 @@ msgstr "Nginx 重啟成功"
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 #: src/views/stream/StreamList.vue:165
 msgid "No"
 msgid "No"
@@ -1183,7 +1230,7 @@ msgstr "備註"
 msgid "Notification"
 msgid "Notification"
 msgstr "憑證"
 msgstr "憑證"
 
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "憑證"
 msgstr "憑證"
@@ -1208,7 +1255,7 @@ msgid "Ok"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/ChatGPT/ChatGPT.vue:271
-#: src/components/Notification/Notification.vue:83
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1238,7 +1285,7 @@ msgstr ""
 msgid "Online"
 msgid "Online"
 msgstr "線上"
 msgstr "線上"
 
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgid "OpenAI"
 msgstr "OpenAI"
 msgstr "OpenAI"
 
 
@@ -1250,13 +1297,13 @@ msgstr "作業系統"
 msgid "OS:"
 msgid "OS:"
 msgstr "作業系統:"
 msgstr "作業系統:"
 
 
-#: src/views/domain/components/Deploy.vue:90
-#: src/views/stream/components/Deploy.vue:90
+#: src/views/domain/components/Deploy.vue:87
+#: src/views/stream/components/Deploy.vue:87
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr "覆蓋"
 msgstr "覆蓋"
 
 
-#: src/views/domain/components/Deploy.vue:94
-#: src/views/stream/components/Deploy.vue:94
+#: src/views/domain/components/Deploy.vue:91
+#: src/views/stream/components/Deploy.vue:91
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr "覆蓋現有檔案"
 msgstr "覆蓋現有檔案"
 
 
@@ -1264,11 +1311,11 @@ msgstr "覆蓋現有檔案"
 msgid "Params"
 msgid "Params"
 msgstr "參數"
 msgstr "參數"
 
 
-#: src/views/other/Login.vue:132 src/views/user/User.vue:18
+#: src/views/other/Login.vue:144 src/views/user/User.vue:18
 msgid "Password"
 msgid "Password"
 msgstr "密碼"
 msgstr "密碼"
 
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgid "Password (*)"
 msgstr "密碼 (*)"
 msgstr "密碼 (*)"
 
 
@@ -1334,7 +1381,7 @@ msgstr "請至少選擇一個節點!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "預先發布"
 msgstr "預先發布"
 
 
-#: src/routes/index.ts:239 src/views/preference/Preference.vue:96
+#: src/routes/index.ts:239 src/views/preference/Preference.vue:105
 msgid "Preference"
 msgid "Preference"
 msgstr "偏好設定"
 msgstr "偏好設定"
 
 
@@ -1431,7 +1478,16 @@ msgstr "重新載入中"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "正在重新載入 Nginx"
 msgstr "正在重新載入 Nginx"
 
 
-#: src/components/Notification/Notification.vue:50
+#: src/views/preference/AuthSettings.vue:101
+msgid "Remove"
+msgstr ""
+
+#: src/views/preference/AuthSettings.vue:47
+#, fuzzy
+msgid "Remove successfully"
+msgstr "儲存成功"
+
+#: src/components/Notification/Notification.vue:52
 #, fuzzy
 #, fuzzy
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "儲存成功"
 msgstr "儲存成功"
@@ -1489,9 +1545,9 @@ msgstr "執行中"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: src/views/certificate/CertificateEditor.vue:249
-#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
+#: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:261
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
-#: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgid "Save"
 msgstr "儲存"
 msgstr "儲存"
 
 
@@ -1499,7 +1555,7 @@ msgstr "儲存"
 msgid "Save Directive"
 msgid "Save Directive"
 msgstr "儲存指令"
 msgstr "儲存指令"
 
 
-#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:50
+#: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:46
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "儲存錯誤 %{msg}"
 msgstr "儲存錯誤 %{msg}"
@@ -1507,14 +1563,14 @@ msgstr "儲存錯誤 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "儲存成功"
 msgstr "儲存成功"
 
 
-#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:38
-#: src/views/domain/DomainEdit.vue:148
+#: src/views/config/ConfigEdit.vue:55 src/views/domain/DomainAdd.vue:37
+#: src/views/domain/DomainEdit.vue:146
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
-#: src/views/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "儲存成功"
 msgstr "儲存成功"
 
 
@@ -1539,8 +1595,9 @@ msgstr "傳送"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
-#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
-#: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
+#: src/views/environment/Environment.vue:139 src/views/other/Install.vue:69
+#: src/views/preference/AuthSettings.vue:49
+#: src/views/preference/Preference.vue:78 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
 msgstr "伺服器錯誤"
 msgstr "伺服器錯誤"
@@ -1559,7 +1616,7 @@ msgid "server_name not found in directives"
 msgstr "在指令中未找到 server_name"
 msgstr "在指令中未找到 server_name"
 
 
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
 #: src/views/domain/cert/components/AutoCertStepOne.vue:34
-#: src/views/domain/DomainAdd.vue:121
+#: src/views/domain/DomainAdd.vue:117
 msgid "server_name parameter is required"
 msgid "server_name parameter is required"
 msgstr "必須提供 server_name 參數"
 msgstr "必須提供 server_name 參數"
 
 
@@ -1615,7 +1672,7 @@ msgstr "SSL 憑證金鑰路徑"
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "SSL 憑證路徑"
 msgstr "SSL 憑證路徑"
 
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 #, fuzzy
 msgid "SSO Login"
 msgid "SSO Login"
 msgstr "登入"
 msgstr "登入"
@@ -1701,8 +1758,8 @@ msgstr ""
 msgid "System"
 msgid "System"
 msgstr "系統"
 msgstr "系統"
 
 
-#: src/views/domain/components/SiteDuplicate.vue:140
-#: src/views/stream/components/StreamDuplicate.vue:140
+#: src/views/domain/components/SiteDuplicate.vue:136
+#: src/views/stream/components/StreamDuplicate.vue:136
 msgid "Target"
 msgid "Target"
 msgstr "目標"
 msgstr "目標"
 
 
@@ -1736,7 +1793,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgid "The input is not a SSL Certificate Key"
 msgstr "SSL 憑證金鑰路徑"
 msgstr "SSL 憑證金鑰路徑"
 
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid ""
 msgid ""
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "dashes, and dots."
 "dashes, and dots."
@@ -1770,8 +1827,8 @@ msgstr "注意:目前設定中的 server_name 必須為需要申請憑證的
 msgid "The url is invalid"
 msgid "The url is invalid"
 msgstr "此功能在演示中不可用。"
 msgstr "此功能在演示中不可用。"
 
 
-#: src/views/preference/OpenAISettings.vue:45
-#: src/views/preference/OpenAISettings.vue:57
+#: src/views/preference/OpenAISettings.vue:48
+#: src/views/preference/OpenAISettings.vue:60
 #, fuzzy
 #, fuzzy
 msgid "The url is invalid."
 msgid "The url is invalid."
 msgstr "此功能在演示中不可用。"
 msgstr "此功能在演示中不可用。"
@@ -1799,6 +1856,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr "此欄位不應為空"
 msgstr "此欄位不應為空"
 
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgid "Tips"
 msgstr ""
 msgstr ""
@@ -1817,10 +1875,14 @@ msgstr ""
 "為了確保憑證自動續期能夠正常運作,我們需要新增一個 Location 來代理從授權後端"
 "為了確保憑證自動續期能夠正常運作,我們需要新增一個 Location 來代理從授權後端"
 "的請求,我們需要儲存這個檔案並重新載入 Nginx。你確定你要繼續嗎?"
 "的請求,我們需要儲存這個檔案並重新載入 Nginx。你確定你要繼續嗎?"
 
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 msgid "Token is not valid"
 msgstr ""
 msgstr ""
 
 
+#: src/views/other/Login.vue:62
+msgid "Too many login failed attempts, please try again later"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:221
 msgid "Trash"
 msgid "Trash"
 msgstr ""
 msgstr ""
@@ -1873,11 +1935,15 @@ msgstr "URL"
 msgid "User"
 msgid "User"
 msgstr "使用者名稱"
 msgstr "使用者名稱"
 
 
-#: src/views/other/Login.vue:122 src/views/user/User.vue:9
+#: src/views/other/Login.vue:65
+msgid "User is banned"
+msgstr ""
+
+#: src/views/other/Login.vue:134 src/views/user/User.vue:9
 msgid "Username"
 msgid "Username"
 msgstr "使用者名稱"
 msgstr "使用者名稱"
 
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "使用者名稱 (*)"
 msgstr "使用者名稱 (*)"
 
 
@@ -1891,7 +1957,7 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr "檢視"
 msgstr "檢視"
 
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 #, fuzzy
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "憑證"
 msgstr "憑證"
@@ -1907,7 +1973,7 @@ msgstr "基本模式"
 
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgid "Warning"
 msgstr "警告"
 msgstr "警告"
 
 
@@ -1940,6 +2006,7 @@ msgstr "將憑證寫入磁碟"
 
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgid "Yes"
 msgstr "是的"
 msgstr "是的"

+ 0 - 4
app/src/views/domain/DomainAdd.vue

@@ -33,19 +33,15 @@ function init() {
 
 
 async function save() {
 async function save() {
   return ngx.build_config(ngx_config).then(r => {
   return ngx.build_config(ngx_config).then(r => {
-    // eslint-disable-next-line promise/no-nesting
     domain.save(ngx_config.name, { name: ngx_config.name, content: r.content, overwrite: true }).then(() => {
     domain.save(ngx_config.name, { name: ngx_config.name, content: r.content, overwrite: true }).then(() => {
       message.success($gettext('Saved successfully'))
       message.success($gettext('Saved successfully'))
 
 
-      // eslint-disable-next-line promise/no-nesting
       domain.enable(ngx_config.name).then(() => {
       domain.enable(ngx_config.name).then(() => {
         message.success($gettext('Enabled successfully'))
         message.success($gettext('Enabled successfully'))
         window.scroll({ top: 0, left: 0, behavior: 'smooth' })
         window.scroll({ top: 0, left: 0, behavior: 'smooth' })
-        // eslint-disable-next-line promise/no-nesting
       }).catch(e => {
       }).catch(e => {
         message.error(e.message ?? $gettext('Enable failed'), 5)
         message.error(e.message ?? $gettext('Enable failed'), 5)
       })
       })
-      // eslint-disable-next-line promise/no-nesting
     }).catch(e => {
     }).catch(e => {
       message.error($gettext('Save error %{msg}', { msg: $gettext(e.message) ?? '' }), 5)
       message.error($gettext('Save error %{msg}', { msg: $gettext(e.message) ?? '' }), 5)
     })
     })

+ 0 - 2
app/src/views/domain/DomainEdit.vue

@@ -105,10 +105,8 @@ function on_mode_change(advanced: CheckedType) {
       build_config()
       build_config()
     }
     }
     else {
     else {
-      // eslint-disable-next-line promise/no-nesting
       return ngx.tokenize_config(configText.value).then(r => {
       return ngx.tokenize_config(configText.value).then(r => {
         Object.assign(ngx_config, r)
         Object.assign(ngx_config, r)
-        // eslint-disable-next-line promise/no-nesting
       }).catch(handle_parse_error)
       }).catch(handle_parse_error)
     }
     }
   })
   })

+ 1 - 4
app/src/views/domain/components/Deploy.vue

@@ -29,7 +29,7 @@ function deploy() {
             name: name.value,
             name: name.value,
             content: r.config,
             content: r.config,
             overwrite: overwrite.value,
             overwrite: overwrite.value,
-            // eslint-disable-next-line promise/no-nesting
+
           }, { headers: { 'X-Node-ID': id } }).then(async () => {
           }, { headers: { 'X-Node-ID': id } }).then(async () => {
             notification.success({
             notification.success({
               message: $gettext('Deploy successfully'),
               message: $gettext('Deploy successfully'),
@@ -38,7 +38,6 @@ function deploy() {
                   { conf_name: name.value, node_name }),
                   { conf_name: name.value, node_name }),
             })
             })
             if (enabled.value) {
             if (enabled.value) {
-              // eslint-disable-next-line promise/no-nesting
               domain.enable(name.value).then(() => {
               domain.enable(name.value).then(() => {
                 notification.success({
                 notification.success({
                   message: $gettext('Enable successfully'),
                   message: $gettext('Enable successfully'),
@@ -46,7 +45,6 @@ function deploy() {
                     $gettext('Enable %{conf_name} in %{node_name} successfully',
                     $gettext('Enable %{conf_name} in %{node_name} successfully',
                       { conf_name: name.value, node_name }),
                       { conf_name: name.value, node_name }),
                 })
                 })
-                // eslint-disable-next-line promise/no-nesting
               }).catch(e => {
               }).catch(e => {
                 notification.error({
                 notification.error({
                   message: $gettext('Enable %{conf_name} in %{node_name} failed', {
                   message: $gettext('Enable %{conf_name} in %{node_name} failed', {
@@ -57,7 +55,6 @@ function deploy() {
                 })
                 })
               })
               })
             }
             }
-            // eslint-disable-next-line promise/no-nesting
           }).catch(e => {
           }).catch(e => {
             notification.error({
             notification.error({
               message: $gettext('Deploy %{conf_name} to %{node_name} failed', {
               message: $gettext('Deploy %{conf_name} to %{node_name} failed', {

+ 2 - 6
app/src/views/domain/components/SiteDuplicate.vue

@@ -59,24 +59,22 @@ function onSubmit() {
 
 
     modelRef.target.forEach(id => {
     modelRef.target.forEach(id => {
       if (id === 0) {
       if (id === 0) {
-        // eslint-disable-next-line promise/no-nesting
         domain.duplicate(props.name, { name: modelRef.name }).then(() => {
         domain.duplicate(props.name, { name: modelRef.name }).then(() => {
           message.success($gettext('Duplicate to local successfully'))
           message.success($gettext('Duplicate to local successfully'))
           show.value = false
           show.value = false
           emit('duplicated')
           emit('duplicated')
-          // eslint-disable-next-line promise/no-nesting
         }).catch(e => {
         }).catch(e => {
           message.error($gettext(e?.message ?? 'Server error'))
           message.error($gettext(e?.message ?? 'Server error'))
         })
         })
       }
       }
       else {
       else {
         // get source content
         // get source content
-        // eslint-disable-next-line promise/no-nesting
+
         domain.get(props.name).then(r => {
         domain.get(props.name).then(r => {
           domain.save(modelRef.name, {
           domain.save(modelRef.name, {
             name: modelRef.name,
             name: modelRef.name,
             content: r.config,
             content: r.config,
-            // eslint-disable-next-line promise/no-nesting
+
           }, { headers: { 'X-Node-ID': id } }).then(() => {
           }, { headers: { 'X-Node-ID': id } }).then(() => {
             notification.success({
             notification.success({
               message: $gettext('Duplicate successfully'),
               message: $gettext('Duplicate successfully'),
@@ -84,7 +82,6 @@ function onSubmit() {
                 $gettext('Duplicate %{conf_name} to %{node_name} successfully',
                 $gettext('Duplicate %{conf_name} to %{node_name} successfully',
                   { conf_name: props.name, node_name: node_map[id] }),
                   { conf_name: props.name, node_name: node_map[id] }),
             })
             })
-            // eslint-disable-next-line promise/no-nesting
           }).catch(e => {
           }).catch(e => {
             notification.error({
             notification.error({
               message: $gettext('Duplicate failed'),
               message: $gettext('Duplicate failed'),
@@ -92,7 +89,6 @@ function onSubmit() {
             })
             })
           })
           })
           if (r.enabled) {
           if (r.enabled) {
-            // eslint-disable-next-line promise/no-nesting
             domain.enable(modelRef.name, { headers: { 'X-Node-ID': id } }).then(() => {
             domain.enable(modelRef.name, { headers: { 'X-Node-ID': id } }).then(() => {
               notification.success({
               notification.success({
                 message: $gettext('Enabled successfully'),
                 message: $gettext('Enabled successfully'),

+ 1 - 2
app/src/views/other/Install.vue

@@ -61,11 +61,10 @@ const onSubmit = () => {
   validate().then(() => {
   validate().then(() => {
     // modelRef
     // modelRef
     loading.value = true
     loading.value = true
-    // eslint-disable-next-line promise/no-nesting
+
     install.install_nginx_ui(modelRef).then(async () => {
     install.install_nginx_ui(modelRef).then(async () => {
       message.success($gettext('Install successfully'))
       message.success($gettext('Install successfully'))
       await router.push('/login')
       await router.push('/login')
-      // eslint-disable-next-line promise/no-nesting
     }).catch(e => {
     }).catch(e => {
       message.error(e.message ?? $gettext('Server error'))
       message.error(e.message ?? $gettext('Server error'))
     }).finally(() => {
     }).finally(() => {

+ 15 - 3
app/src/views/other/Login.vue

@@ -46,16 +46,28 @@ const { validate, validateInfos, clearValidate } = Form.useForm(modelRef, rulesR
 const onSubmit = () => {
 const onSubmit = () => {
   validate().then(async () => {
   validate().then(async () => {
     loading.value = true
     loading.value = true
-    // eslint-disable-next-line promise/no-nesting
+
     await auth.login(modelRef.username, modelRef.password).then(async () => {
     await auth.login(modelRef.username, modelRef.password).then(async () => {
       message.success($gettext('Login successful'), 1)
       message.success($gettext('Login successful'), 1)
 
 
       const next = (route.query?.next || '').toString() || '/'
       const next = (route.query?.next || '').toString() || '/'
 
 
       await router.push(next)
       await router.push(next)
-      // eslint-disable-next-line promise/no-nesting
     }).catch(e => {
     }).catch(e => {
-      message.error($gettext(e.message ?? 'Server error'))
+      switch (e.code) {
+        case 4031:
+          message.error($gettext('Incorrect username or password'))
+          break
+        case 4291:
+          message.error($gettext('Too many login failed attempts, please try again later'))
+          break
+        case 4033:
+          message.error($gettext('User is banned'))
+          break
+        default:
+          message.error($gettext(e.message ?? 'Server error'))
+          break
+      }
     })
     })
     loading.value = false
     loading.value = false
   })
   })

+ 114 - 0
app/src/views/preference/AuthSettings.vue

@@ -0,0 +1,114 @@
+<script setup lang="tsx">
+import { message } from 'ant-design-vue'
+import type { Ref } from 'vue'
+import { inject } from 'vue'
+import dayjs from 'dayjs'
+import type { BannedIP } from '@/api/settings'
+import setting from '@/api/settings'
+import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
+import type { Settings } from '@/views/preference/typedef'
+
+const data: Settings = inject('data') as Settings
+
+const bannedIPColumns = [{
+  title: $gettext('IP'),
+  dataIndex: 'ip',
+}, {
+  title: $gettext('Attempts'),
+  dataIndex: 'attempts',
+}, {
+  title: $gettext('Banned Until'),
+  dataIndex: 'expired_at',
+  customRender: (args: customRender) => {
+    return dayjs.unix(args.text).format('YYYY-MM-DD HH:mm:ss')
+  },
+}, {
+  title: $gettext('Action'),
+  dataIndex: 'action',
+}]
+
+const bannedIPs: Ref<BannedIP[]> = ref([])
+
+function getBannedIPs() {
+  setting.get_banned_ips().then(r => {
+    bannedIPs.value = r
+  })
+}
+
+getBannedIPs()
+
+defineExpose({
+  getBannedIPs,
+})
+
+function removeBannedIP(ip: string) {
+  setting.remove_banned_ip(ip).then(() => {
+    bannedIPs.value = bannedIPs.value.filter(v => v.ip !== ip)
+    message.success($gettext('Remove successfully'))
+  }).catch((e: { message?: string }) => {
+    message.error(e?.message ?? $gettext('Server error'))
+  })
+}
+</script>
+
+<template>
+  <div class="flex justify-center">
+    <div>
+      <AAlert
+        class="mb-4"
+        :message="$gettext('Tips')"
+        :description="$gettext('If the number of login failed attempts from a ip reach the max attempts in ban threshold minutes,'
+          + ' the ip will be banned for a period of time.')"
+        type="info"
+      />
+      <AForm
+        layout="horizontal"
+        style="width:90%;max-width: 500px"
+      >
+        <AFormItem :label="$gettext('Ban Threshold Minutes')">
+          <AInputNumber
+            v-model:value="data.auth.ban_threshold_minutes"
+            min="1"
+          />
+        </AFormItem>
+        <AFormItem :label="$gettext('Max Attempts')">
+          <AInputNumber
+            v-model:value="data.auth.max_attempts"
+            min="1"
+          />
+        </AFormItem>
+      </AForm>
+      <h3>
+        {{ $gettext('Banned IPs') }}
+      </h3>
+      <div class="mb-6">
+        <ATable
+          :columns="bannedIPColumns"
+          row-key="ip"
+          :data-source="bannedIPs"
+          size="small"
+        >
+          <template #bodyCell="{ column, record }">
+            <template v-if="column.dataIndex === 'action'">
+              <APopconfirm
+                :title="$gettext('Are you sure to delete this banned IP immediately?')"
+                :ok-text="$gettext('Yes')"
+                :cancel-text="$gettext('No')"
+                placement="bottom"
+                @confirm="() => removeBannedIP(record.ip)"
+              >
+                <a>
+                  {{ $gettext('Remove') }}
+                </a>
+              </APopconfirm>
+            </template>
+          </template>
+        </ATable>
+      </div>
+    </div>
+  </div>
+</template>
+
+<style lang="less" scoped>
+
+</style>

+ 16 - 1
app/src/views/preference/Preference.vue

@@ -10,6 +10,7 @@ import NginxSettings from '@/views/preference/NginxSettings.vue'
 import type { Settings } from '@/views/preference/typedef'
 import type { Settings } from '@/views/preference/typedef'
 import LogrotateSettings from '@/views/preference/LogrotateSettings.vue'
 import LogrotateSettings from '@/views/preference/LogrotateSettings.vue'
 import { useSettingsStore } from '@/pinia'
 import { useSettingsStore } from '@/pinia'
+import AuthSettings from '@/views/preference/AuthSettings.vue'
 
 
 const data = ref<Settings>({
 const data = ref<Settings>({
   server: {
   server: {
@@ -46,15 +47,21 @@ const data = ref<Settings>({
     cmd: '',
     cmd: '',
     interval: 1440,
     interval: 1440,
   },
   },
+  auth: {
+    ip_white_list: [],
+    ban_threshold_minutes: 10,
+    max_attempts: 10,
+  },
 })
 })
 
 
-settings.get().then(r => {
+settings.get<Settings>().then(r => {
   data.value = r
   data.value = r
 })
 })
 
 
 const settingsStore = useSettingsStore()
 const settingsStore = useSettingsStore()
 const { server_name } = storeToRefs(settingsStore)
 const { server_name } = storeToRefs(settingsStore)
 const errors = ref({}) as Ref<Record<string, Record<string, string>>>
 const errors = ref({}) as Ref<Record<string, Record<string, string>>>
+const refAuthSettings = ref()
 
 
 async function save() {
 async function save() {
   // fix type
   // fix type
@@ -63,6 +70,7 @@ async function save() {
     if (!settingsStore.is_remote)
     if (!settingsStore.is_remote)
       server_name.value = r?.server?.name ?? ''
       server_name.value = r?.server?.name ?? ''
     data.value = r
     data.value = r
+    refAuthSettings.value.getBannedIPs()
     message.success($gettext('Save successfully'))
     message.success($gettext('Save successfully'))
     errors.value = {}
     errors.value = {}
   }).catch(e => {
   }).catch(e => {
@@ -90,6 +98,7 @@ onMounted(() => {
   if (route.query?.tab)
   if (route.query?.tab)
     activeKey.value = route.query.tab.toString()
     activeKey.value = route.query.tab.toString()
 })
 })
+
 </script>
 </script>
 
 
 <template>
 <template>
@@ -102,6 +111,12 @@ onMounted(() => {
         >
         >
           <BasicSettings />
           <BasicSettings />
         </ATabPane>
         </ATabPane>
+        <ATabPane
+          key="auth"
+          :tab="$gettext('Auth')"
+        >
+          <AuthSettings ref="refAuthSettings" />
+        </ATabPane>
         <ATabPane
         <ATabPane
           key="nginx"
           key="nginx"
           :tab="$gettext('Nginx')"
           :tab="$gettext('Nginx')"

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

@@ -33,4 +33,9 @@ export interface Settings {
     cmd: string
     cmd: string
     interval: number
     interval: number
   }
   }
+  auth: {
+    ip_white_list: string[]
+    ban_threshold_minutes: number
+    max_attempts: number
+  }
 }
 }

+ 0 - 2
app/src/views/stream/StreamEdit.vue

@@ -97,10 +97,8 @@ function on_mode_change(advanced: CheckedType) {
       build_config()
       build_config()
     }
     }
     else {
     else {
-      // eslint-disable-next-line promise/no-nesting
       return ngx.tokenize_config(configText.value).then(r => {
       return ngx.tokenize_config(configText.value).then(r => {
         Object.assign(ngx_config, r)
         Object.assign(ngx_config, r)
-        // eslint-disable-next-line promise/no-nesting
       }).catch(handle_parse_error)
       }).catch(handle_parse_error)
     }
     }
   })
   })

+ 1 - 4
app/src/views/stream/components/Deploy.vue

@@ -29,7 +29,7 @@ function deploy() {
             name: name.value,
             name: name.value,
             content: r.config,
             content: r.config,
             overwrite: overwrite.value,
             overwrite: overwrite.value,
-            // eslint-disable-next-line promise/no-nesting
+
           }, { headers: { 'X-Node-ID': id } }).then(async () => {
           }, { headers: { 'X-Node-ID': id } }).then(async () => {
             notification.success({
             notification.success({
               message: $gettext('Deploy successfully'),
               message: $gettext('Deploy successfully'),
@@ -38,7 +38,6 @@ function deploy() {
                   { conf_name: name.value, node_name }),
                   { conf_name: name.value, node_name }),
             })
             })
             if (enabled.value) {
             if (enabled.value) {
-              // eslint-disable-next-line promise/no-nesting
               stream.enable(name.value).then(() => {
               stream.enable(name.value).then(() => {
                 notification.success({
                 notification.success({
                   message: $gettext('Enable successfully'),
                   message: $gettext('Enable successfully'),
@@ -46,7 +45,6 @@ function deploy() {
                     $gettext('Enable %{conf_name} in %{node_name} successfully',
                     $gettext('Enable %{conf_name} in %{node_name} successfully',
                       { conf_name: name.value, node_name }),
                       { conf_name: name.value, node_name }),
                 })
                 })
-                // eslint-disable-next-line promise/no-nesting
               }).catch(e => {
               }).catch(e => {
                 notification.error({
                 notification.error({
                   message: $gettext('Enable %{conf_name} in %{node_name} failed', {
                   message: $gettext('Enable %{conf_name} in %{node_name} failed', {
@@ -57,7 +55,6 @@ function deploy() {
                 })
                 })
               })
               })
             }
             }
-            // eslint-disable-next-line promise/no-nesting
           }).catch(e => {
           }).catch(e => {
             notification.error({
             notification.error({
               message: $gettext('Deploy %{conf_name} to %{node_name} failed', {
               message: $gettext('Deploy %{conf_name} to %{node_name} failed', {

+ 2 - 6
app/src/views/stream/components/StreamDuplicate.vue

@@ -59,24 +59,22 @@ function onSubmit() {
 
 
     modelRef.target.forEach(id => {
     modelRef.target.forEach(id => {
       if (id === 0) {
       if (id === 0) {
-        // eslint-disable-next-line promise/no-nesting
         stream.duplicate(props.name, { name: modelRef.name }).then(() => {
         stream.duplicate(props.name, { name: modelRef.name }).then(() => {
           message.success($gettext('Duplicate to local successfully'))
           message.success($gettext('Duplicate to local successfully'))
           show.value = false
           show.value = false
           emit('duplicated')
           emit('duplicated')
-          // eslint-disable-next-line promise/no-nesting
         }).catch(e => {
         }).catch(e => {
           message.error($gettext(e?.message ?? 'Server error'))
           message.error($gettext(e?.message ?? 'Server error'))
         })
         })
       }
       }
       else {
       else {
         // get source content
         // get source content
-        // eslint-disable-next-line promise/no-nesting
+
         stream.get(props.name).then(r => {
         stream.get(props.name).then(r => {
           stream.save(modelRef.name, {
           stream.save(modelRef.name, {
             name: modelRef.name,
             name: modelRef.name,
             content: r.config,
             content: r.config,
-            // eslint-disable-next-line promise/no-nesting
+
           }, { headers: { 'X-Node-ID': id } }).then(() => {
           }, { headers: { 'X-Node-ID': id } }).then(() => {
             notification.success({
             notification.success({
               message: $gettext('Duplicate successfully'),
               message: $gettext('Duplicate successfully'),
@@ -84,7 +82,6 @@ function onSubmit() {
                 $gettext('Duplicate %{conf_name} to %{node_name} successfully',
                 $gettext('Duplicate %{conf_name} to %{node_name} successfully',
                   { conf_name: props.name, node_name: node_map[id] }),
                   { conf_name: props.name, node_name: node_map[id] }),
             })
             })
-            // eslint-disable-next-line promise/no-nesting
           }).catch(e => {
           }).catch(e => {
             notification.error({
             notification.error({
               message: $gettext('Duplicate failed'),
               message: $gettext('Duplicate failed'),
@@ -92,7 +89,6 @@ function onSubmit() {
             })
             })
           })
           })
           if (r.enabled) {
           if (r.enabled) {
-            // eslint-disable-next-line promise/no-nesting
             stream.enable(modelRef.name, { headers: { 'X-Node-ID': id } }).then(() => {
             stream.enable(modelRef.name, { headers: { 'X-Node-ID': id } }).then(() => {
               notification.success({
               notification.success({
                 message: $gettext('Enabled successfully'),
                 message: $gettext('Enabled successfully'),

File diff suppressed because it is too large
+ 86 - 0
app/vite.config.ts.timestamp-1721542436572-91822f5b8889d.mjs


+ 1 - 1
docs/guide/config-auth.md

@@ -9,7 +9,7 @@ From v2.0.0-beta.26, you can authorization settings in the `auth` section of the
 [auth]
 [auth]
 IPWhiteList = 10.0.0.1
 IPWhiteList = 10.0.0.1
 IPWhiteList = 10.0.0.2
 IPWhiteList = 10.0.0.2
-IPWhiteList = 10.0.0.3
+IPWhiteList = 2001:0000:130F:0000:0000:09C0:876A:130B
 ```
 ```
 
 
 By default, if you do not set the `IPWhiteList`, all IP addresses are allowed to access the Nginx UI.
 By default, if you do not set the `IPWhiteList`, all IP addresses are allowed to access the Nginx UI.

+ 1 - 1
docs/zh_CN/guide/config-auth.md

@@ -9,7 +9,7 @@
 [auth]
 [auth]
 IPWhiteList = 10.0.0.1
 IPWhiteList = 10.0.0.1
 IPWhiteList = 10.0.0.2
 IPWhiteList = 10.0.0.2
-IPWhiteList = 10.0.0.3
+IPWhiteList = 2001:0000:130F:0000:0000:09C0:876A:130B
 ```
 ```
 
 
 默认情况下,如果您没有设置 `IPWhiteList`,所有 IP 地址都允许访问 Nginx UI。
 默认情况下,如果您没有设置 `IPWhiteList`,所有 IP 地址都允许访问 Nginx UI。

+ 1 - 1
docs/zh_TW/guide/config-auth.md

@@ -9,7 +9,7 @@
 [auth]
 [auth]
 IPWhiteList = 10.0.0.1
 IPWhiteList = 10.0.0.1
 IPWhiteList = 10.0.0.2
 IPWhiteList = 10.0.0.2
-IPWhiteList = 10.0.0.3
+IPWhiteList = 2001:0000:130F:0000:0000:09C0:876A:130B
 ```
 ```
 
 
 默認情況下,如果您沒有設置 IPWhiteList,所有 IP 地址都允許訪問 Nginx UI。
 默認情況下,如果您沒有設置 IPWhiteList,所有 IP 地址都允許訪問 Nginx UI。

+ 19 - 4
internal/user/login.go

@@ -1,10 +1,12 @@
 package user
 package user
 
 
 import (
 import (
-    "errors"
-    "github.com/0xJacky/Nginx-UI/model"
-    "github.com/0xJacky/Nginx-UI/query"
-    "golang.org/x/crypto/bcrypt"
+	"errors"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"golang.org/x/crypto/bcrypt"
+	"time"
 )
 )
 
 
 var (
 var (
@@ -30,3 +32,16 @@ func Login(name string, password string) (user *model.Auth, err error) {
 
 
 	return
 	return
 }
 }
+
+func BanIP(ip string) {
+	b := query.BanIP
+	banIP, err := b.Where(b.IP.Eq(ip)).First()
+	if err != nil || banIP.ExpiredAt <= time.Now().Unix() {
+		_ = b.Create(&model.BanIP{
+			IP:        ip,
+			Attempts:  1,
+			ExpiredAt: time.Now().Unix() + int64(settings.AuthSettings.BanThresholdMinutes*60),
+		})
+	}
+	_, _ = b.Where(b.IP.Eq(ip)).UpdateSimple(b.Attempts.Add(1))
+}

+ 7 - 0
model/ban_ip.go

@@ -0,0 +1,7 @@
+package model
+
+type BanIP struct {
+	IP        string `json:"ip"`
+	Attempts  int    `json:"attempts"`
+	ExpiredAt int64  `json:"expired_at" gorm:"index"`
+}

+ 1 - 0
model/model.go

@@ -35,6 +35,7 @@ func GenerateAllModel() []any {
 		Environment{},
 		Environment{},
 		Notification{},
 		Notification{},
 		AcmeUser{},
 		AcmeUser{},
+		BanIP{},
 	}
 	}
 }
 }
 
 

+ 5 - 1
query/auths.gen.go

@@ -34,6 +34,7 @@ func newAuth(db *gorm.DB, opts ...gen.DOOption) auth {
 	_auth.DeletedAt = field.NewField(tableName, "deleted_at")
 	_auth.DeletedAt = field.NewField(tableName, "deleted_at")
 	_auth.Name = field.NewString(tableName, "name")
 	_auth.Name = field.NewString(tableName, "name")
 	_auth.Password = field.NewString(tableName, "password")
 	_auth.Password = field.NewString(tableName, "password")
+	_auth.Status = field.NewBool(tableName, "status")
 
 
 	_auth.fillFieldMap()
 	_auth.fillFieldMap()
 
 
@@ -50,6 +51,7 @@ type auth struct {
 	DeletedAt field.Field
 	DeletedAt field.Field
 	Name      field.String
 	Name      field.String
 	Password  field.String
 	Password  field.String
+	Status    field.Bool
 
 
 	fieldMap map[string]field.Expr
 	fieldMap map[string]field.Expr
 }
 }
@@ -72,6 +74,7 @@ func (a *auth) updateTableName(table string) *auth {
 	a.DeletedAt = field.NewField(table, "deleted_at")
 	a.DeletedAt = field.NewField(table, "deleted_at")
 	a.Name = field.NewString(table, "name")
 	a.Name = field.NewString(table, "name")
 	a.Password = field.NewString(table, "password")
 	a.Password = field.NewString(table, "password")
+	a.Status = field.NewBool(table, "status")
 
 
 	a.fillFieldMap()
 	a.fillFieldMap()
 
 
@@ -88,13 +91,14 @@ func (a *auth) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
 }
 }
 
 
 func (a *auth) fillFieldMap() {
 func (a *auth) fillFieldMap() {
-	a.fieldMap = make(map[string]field.Expr, 6)
+	a.fieldMap = make(map[string]field.Expr, 7)
 	a.fieldMap["id"] = a.ID
 	a.fieldMap["id"] = a.ID
 	a.fieldMap["created_at"] = a.CreatedAt
 	a.fieldMap["created_at"] = a.CreatedAt
 	a.fieldMap["updated_at"] = a.UpdatedAt
 	a.fieldMap["updated_at"] = a.UpdatedAt
 	a.fieldMap["deleted_at"] = a.DeletedAt
 	a.fieldMap["deleted_at"] = a.DeletedAt
 	a.fieldMap["name"] = a.Name
 	a.fieldMap["name"] = a.Name
 	a.fieldMap["password"] = a.Password
 	a.fieldMap["password"] = a.Password
+	a.fieldMap["status"] = a.Status
 }
 }
 
 
 func (a auth) clone(db *gorm.DB) auth {
 func (a auth) clone(db *gorm.DB) auth {

+ 358 - 0
query/ban_ips.gen.go

@@ -0,0 +1,358 @@
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+
+package query
+
+import (
+	"context"
+	"strings"
+
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"gorm.io/gorm/schema"
+
+	"gorm.io/gen"
+	"gorm.io/gen/field"
+
+	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
+)
+
+func newBanIP(db *gorm.DB, opts ...gen.DOOption) banIP {
+	_banIP := banIP{}
+
+	_banIP.banIPDo.UseDB(db, opts...)
+	_banIP.banIPDo.UseModel(&model.BanIP{})
+
+	tableName := _banIP.banIPDo.TableName()
+	_banIP.ALL = field.NewAsterisk(tableName)
+	_banIP.IP = field.NewString(tableName, "ip")
+	_banIP.Attempts = field.NewInt(tableName, "attempts")
+	_banIP.ExpiredAt = field.NewInt64(tableName, "expired_at")
+
+	_banIP.fillFieldMap()
+
+	return _banIP
+}
+
+type banIP struct {
+	banIPDo
+
+	ALL       field.Asterisk
+	IP        field.String
+	Attempts  field.Int
+	ExpiredAt field.Int64
+
+	fieldMap map[string]field.Expr
+}
+
+func (b banIP) Table(newTableName string) *banIP {
+	b.banIPDo.UseTable(newTableName)
+	return b.updateTableName(newTableName)
+}
+
+func (b banIP) As(alias string) *banIP {
+	b.banIPDo.DO = *(b.banIPDo.As(alias).(*gen.DO))
+	return b.updateTableName(alias)
+}
+
+func (b *banIP) updateTableName(table string) *banIP {
+	b.ALL = field.NewAsterisk(table)
+	b.IP = field.NewString(table, "ip")
+	b.Attempts = field.NewInt(table, "attempts")
+	b.ExpiredAt = field.NewInt64(table, "expired_at")
+
+	b.fillFieldMap()
+
+	return b
+}
+
+func (b *banIP) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
+	_f, ok := b.fieldMap[fieldName]
+	if !ok || _f == nil {
+		return nil, false
+	}
+	_oe, ok := _f.(field.OrderExpr)
+	return _oe, ok
+}
+
+func (b *banIP) fillFieldMap() {
+	b.fieldMap = make(map[string]field.Expr, 3)
+	b.fieldMap["ip"] = b.IP
+	b.fieldMap["attempts"] = b.Attempts
+	b.fieldMap["expired_at"] = b.ExpiredAt
+}
+
+func (b banIP) clone(db *gorm.DB) banIP {
+	b.banIPDo.ReplaceConnPool(db.Statement.ConnPool)
+	return b
+}
+
+func (b banIP) replaceDB(db *gorm.DB) banIP {
+	b.banIPDo.ReplaceDB(db)
+	return b
+}
+
+type banIPDo struct{ gen.DO }
+
+// FirstByID Where("id=@id")
+func (b banIPDo) FirstByID(id int) (result *model.BanIP, err error) {
+	var params []interface{}
+
+	var generateSQL strings.Builder
+	params = append(params, id)
+	generateSQL.WriteString("id=? ")
+
+	var executeSQL *gorm.DB
+	executeSQL = b.UnderlyingDB().Where(generateSQL.String(), params...).Take(&result) // ignore_security_alert
+	err = executeSQL.Error
+
+	return
+}
+
+// DeleteByID update @@table set deleted_at=strftime('%Y-%m-%d %H:%M:%S','now') where id=@id
+func (b banIPDo) DeleteByID(id int) (err error) {
+	var params []interface{}
+
+	var generateSQL strings.Builder
+	params = append(params, id)
+	generateSQL.WriteString("update ban_ips set deleted_at=strftime('%Y-%m-%d %H:%M:%S','now') where id=? ")
+
+	var executeSQL *gorm.DB
+	executeSQL = b.UnderlyingDB().Exec(generateSQL.String(), params...) // ignore_security_alert
+	err = executeSQL.Error
+
+	return
+}
+
+func (b banIPDo) Debug() *banIPDo {
+	return b.withDO(b.DO.Debug())
+}
+
+func (b banIPDo) WithContext(ctx context.Context) *banIPDo {
+	return b.withDO(b.DO.WithContext(ctx))
+}
+
+func (b banIPDo) ReadDB() *banIPDo {
+	return b.Clauses(dbresolver.Read)
+}
+
+func (b banIPDo) WriteDB() *banIPDo {
+	return b.Clauses(dbresolver.Write)
+}
+
+func (b banIPDo) Session(config *gorm.Session) *banIPDo {
+	return b.withDO(b.DO.Session(config))
+}
+
+func (b banIPDo) Clauses(conds ...clause.Expression) *banIPDo {
+	return b.withDO(b.DO.Clauses(conds...))
+}
+
+func (b banIPDo) Returning(value interface{}, columns ...string) *banIPDo {
+	return b.withDO(b.DO.Returning(value, columns...))
+}
+
+func (b banIPDo) Not(conds ...gen.Condition) *banIPDo {
+	return b.withDO(b.DO.Not(conds...))
+}
+
+func (b banIPDo) Or(conds ...gen.Condition) *banIPDo {
+	return b.withDO(b.DO.Or(conds...))
+}
+
+func (b banIPDo) Select(conds ...field.Expr) *banIPDo {
+	return b.withDO(b.DO.Select(conds...))
+}
+
+func (b banIPDo) Where(conds ...gen.Condition) *banIPDo {
+	return b.withDO(b.DO.Where(conds...))
+}
+
+func (b banIPDo) Order(conds ...field.Expr) *banIPDo {
+	return b.withDO(b.DO.Order(conds...))
+}
+
+func (b banIPDo) Distinct(cols ...field.Expr) *banIPDo {
+	return b.withDO(b.DO.Distinct(cols...))
+}
+
+func (b banIPDo) Omit(cols ...field.Expr) *banIPDo {
+	return b.withDO(b.DO.Omit(cols...))
+}
+
+func (b banIPDo) Join(table schema.Tabler, on ...field.Expr) *banIPDo {
+	return b.withDO(b.DO.Join(table, on...))
+}
+
+func (b banIPDo) LeftJoin(table schema.Tabler, on ...field.Expr) *banIPDo {
+	return b.withDO(b.DO.LeftJoin(table, on...))
+}
+
+func (b banIPDo) RightJoin(table schema.Tabler, on ...field.Expr) *banIPDo {
+	return b.withDO(b.DO.RightJoin(table, on...))
+}
+
+func (b banIPDo) Group(cols ...field.Expr) *banIPDo {
+	return b.withDO(b.DO.Group(cols...))
+}
+
+func (b banIPDo) Having(conds ...gen.Condition) *banIPDo {
+	return b.withDO(b.DO.Having(conds...))
+}
+
+func (b banIPDo) Limit(limit int) *banIPDo {
+	return b.withDO(b.DO.Limit(limit))
+}
+
+func (b banIPDo) Offset(offset int) *banIPDo {
+	return b.withDO(b.DO.Offset(offset))
+}
+
+func (b banIPDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *banIPDo {
+	return b.withDO(b.DO.Scopes(funcs...))
+}
+
+func (b banIPDo) Unscoped() *banIPDo {
+	return b.withDO(b.DO.Unscoped())
+}
+
+func (b banIPDo) Create(values ...*model.BanIP) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return b.DO.Create(values)
+}
+
+func (b banIPDo) CreateInBatches(values []*model.BanIP, batchSize int) error {
+	return b.DO.CreateInBatches(values, batchSize)
+}
+
+// Save : !!! underlying implementation is different with GORM
+// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
+func (b banIPDo) Save(values ...*model.BanIP) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return b.DO.Save(values)
+}
+
+func (b banIPDo) First() (*model.BanIP, error) {
+	if result, err := b.DO.First(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.BanIP), nil
+	}
+}
+
+func (b banIPDo) Take() (*model.BanIP, error) {
+	if result, err := b.DO.Take(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.BanIP), nil
+	}
+}
+
+func (b banIPDo) Last() (*model.BanIP, error) {
+	if result, err := b.DO.Last(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.BanIP), nil
+	}
+}
+
+func (b banIPDo) Find() ([]*model.BanIP, error) {
+	result, err := b.DO.Find()
+	return result.([]*model.BanIP), err
+}
+
+func (b banIPDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.BanIP, err error) {
+	buf := make([]*model.BanIP, 0, batchSize)
+	err = b.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
+		defer func() { results = append(results, buf...) }()
+		return fc(tx, batch)
+	})
+	return results, err
+}
+
+func (b banIPDo) FindInBatches(result *[]*model.BanIP, batchSize int, fc func(tx gen.Dao, batch int) error) error {
+	return b.DO.FindInBatches(result, batchSize, fc)
+}
+
+func (b banIPDo) Attrs(attrs ...field.AssignExpr) *banIPDo {
+	return b.withDO(b.DO.Attrs(attrs...))
+}
+
+func (b banIPDo) Assign(attrs ...field.AssignExpr) *banIPDo {
+	return b.withDO(b.DO.Assign(attrs...))
+}
+
+func (b banIPDo) Joins(fields ...field.RelationField) *banIPDo {
+	for _, _f := range fields {
+		b = *b.withDO(b.DO.Joins(_f))
+	}
+	return &b
+}
+
+func (b banIPDo) Preload(fields ...field.RelationField) *banIPDo {
+	for _, _f := range fields {
+		b = *b.withDO(b.DO.Preload(_f))
+	}
+	return &b
+}
+
+func (b banIPDo) FirstOrInit() (*model.BanIP, error) {
+	if result, err := b.DO.FirstOrInit(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.BanIP), nil
+	}
+}
+
+func (b banIPDo) FirstOrCreate() (*model.BanIP, error) {
+	if result, err := b.DO.FirstOrCreate(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.BanIP), nil
+	}
+}
+
+func (b banIPDo) FindByPage(offset int, limit int) (result []*model.BanIP, count int64, err error) {
+	result, err = b.Offset(offset).Limit(limit).Find()
+	if err != nil {
+		return
+	}
+
+	if size := len(result); 0 < limit && 0 < size && size < limit {
+		count = int64(size + offset)
+		return
+	}
+
+	count, err = b.Offset(-1).Limit(-1).Count()
+	return
+}
+
+func (b banIPDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
+	count, err = b.Count()
+	if err != nil {
+		return
+	}
+
+	err = b.Offset(offset).Limit(limit).Scan(result)
+	return
+}
+
+func (b banIPDo) Scan(result interface{}) (err error) {
+	return b.DO.Scan(result)
+}
+
+func (b banIPDo) Delete(models ...*model.BanIP) (result gen.ResultInfo, err error) {
+	return b.DO.Delete(models)
+}
+
+func (b *banIPDo) withDO(do gen.Dao) *banIPDo {
+	b.DO = *do.(*gen.DO)
+	return b
+}

+ 8 - 0
query/gen.go

@@ -20,6 +20,7 @@ var (
 	AcmeUser      *acmeUser
 	AcmeUser      *acmeUser
 	Auth          *auth
 	Auth          *auth
 	AuthToken     *authToken
 	AuthToken     *authToken
+	BanIP         *banIP
 	Cert          *cert
 	Cert          *cert
 	ChatGPTLog    *chatGPTLog
 	ChatGPTLog    *chatGPTLog
 	ConfigBackup  *configBackup
 	ConfigBackup  *configBackup
@@ -35,6 +36,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
 	AcmeUser = &Q.AcmeUser
 	AcmeUser = &Q.AcmeUser
 	Auth = &Q.Auth
 	Auth = &Q.Auth
 	AuthToken = &Q.AuthToken
 	AuthToken = &Q.AuthToken
+	BanIP = &Q.BanIP
 	Cert = &Q.Cert
 	Cert = &Q.Cert
 	ChatGPTLog = &Q.ChatGPTLog
 	ChatGPTLog = &Q.ChatGPTLog
 	ConfigBackup = &Q.ConfigBackup
 	ConfigBackup = &Q.ConfigBackup
@@ -51,6 +53,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
 		AcmeUser:      newAcmeUser(db, opts...),
 		AcmeUser:      newAcmeUser(db, opts...),
 		Auth:          newAuth(db, opts...),
 		Auth:          newAuth(db, opts...),
 		AuthToken:     newAuthToken(db, opts...),
 		AuthToken:     newAuthToken(db, opts...),
+		BanIP:         newBanIP(db, opts...),
 		Cert:          newCert(db, opts...),
 		Cert:          newCert(db, opts...),
 		ChatGPTLog:    newChatGPTLog(db, opts...),
 		ChatGPTLog:    newChatGPTLog(db, opts...),
 		ConfigBackup:  newConfigBackup(db, opts...),
 		ConfigBackup:  newConfigBackup(db, opts...),
@@ -68,6 +71,7 @@ type Query struct {
 	AcmeUser      acmeUser
 	AcmeUser      acmeUser
 	Auth          auth
 	Auth          auth
 	AuthToken     authToken
 	AuthToken     authToken
+	BanIP         banIP
 	Cert          cert
 	Cert          cert
 	ChatGPTLog    chatGPTLog
 	ChatGPTLog    chatGPTLog
 	ConfigBackup  configBackup
 	ConfigBackup  configBackup
@@ -86,6 +90,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
 		AcmeUser:      q.AcmeUser.clone(db),
 		AcmeUser:      q.AcmeUser.clone(db),
 		Auth:          q.Auth.clone(db),
 		Auth:          q.Auth.clone(db),
 		AuthToken:     q.AuthToken.clone(db),
 		AuthToken:     q.AuthToken.clone(db),
+		BanIP:         q.BanIP.clone(db),
 		Cert:          q.Cert.clone(db),
 		Cert:          q.Cert.clone(db),
 		ChatGPTLog:    q.ChatGPTLog.clone(db),
 		ChatGPTLog:    q.ChatGPTLog.clone(db),
 		ConfigBackup:  q.ConfigBackup.clone(db),
 		ConfigBackup:  q.ConfigBackup.clone(db),
@@ -111,6 +116,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
 		AcmeUser:      q.AcmeUser.replaceDB(db),
 		AcmeUser:      q.AcmeUser.replaceDB(db),
 		Auth:          q.Auth.replaceDB(db),
 		Auth:          q.Auth.replaceDB(db),
 		AuthToken:     q.AuthToken.replaceDB(db),
 		AuthToken:     q.AuthToken.replaceDB(db),
+		BanIP:         q.BanIP.replaceDB(db),
 		Cert:          q.Cert.replaceDB(db),
 		Cert:          q.Cert.replaceDB(db),
 		ChatGPTLog:    q.ChatGPTLog.replaceDB(db),
 		ChatGPTLog:    q.ChatGPTLog.replaceDB(db),
 		ConfigBackup:  q.ConfigBackup.replaceDB(db),
 		ConfigBackup:  q.ConfigBackup.replaceDB(db),
@@ -126,6 +132,7 @@ type queryCtx struct {
 	AcmeUser      *acmeUserDo
 	AcmeUser      *acmeUserDo
 	Auth          *authDo
 	Auth          *authDo
 	AuthToken     *authTokenDo
 	AuthToken     *authTokenDo
+	BanIP         *banIPDo
 	Cert          *certDo
 	Cert          *certDo
 	ChatGPTLog    *chatGPTLogDo
 	ChatGPTLog    *chatGPTLogDo
 	ConfigBackup  *configBackupDo
 	ConfigBackup  *configBackupDo
@@ -141,6 +148,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
 		AcmeUser:      q.AcmeUser.WithContext(ctx),
 		AcmeUser:      q.AcmeUser.WithContext(ctx),
 		Auth:          q.Auth.WithContext(ctx),
 		Auth:          q.Auth.WithContext(ctx),
 		AuthToken:     q.AuthToken.WithContext(ctx),
 		AuthToken:     q.AuthToken.WithContext(ctx),
+		BanIP:         q.BanIP.WithContext(ctx),
 		Cert:          q.Cert.WithContext(ctx),
 		Cert:          q.Cert.WithContext(ctx),
 		ChatGPTLog:    q.ChatGPTLog.WithContext(ctx),
 		ChatGPTLog:    q.ChatGPTLog.WithContext(ctx),
 		ConfigBackup:  q.ConfigBackup.WithContext(ctx),
 		ConfigBackup:  q.ConfigBackup.WithContext(ctx),

+ 3 - 1
router/routers.go

@@ -8,6 +8,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/api/nginx"
 	"github.com/0xJacky/Nginx-UI/api/nginx"
 	"github.com/0xJacky/Nginx-UI/api/notification"
 	"github.com/0xJacky/Nginx-UI/api/notification"
 	"github.com/0xJacky/Nginx-UI/api/openai"
 	"github.com/0xJacky/Nginx-UI/api/openai"
+	"github.com/0xJacky/Nginx-UI/api/settings"
 	"github.com/0xJacky/Nginx-UI/api/sites"
 	"github.com/0xJacky/Nginx-UI/api/sites"
 	"github.com/0xJacky/Nginx-UI/api/streams"
 	"github.com/0xJacky/Nginx-UI/api/streams"
 	"github.com/0xJacky/Nginx-UI/api/system"
 	"github.com/0xJacky/Nginx-UI/api/system"
@@ -25,7 +26,7 @@ func InitRouter() *gin.Engine {
 	r.Use(gin.Logger())
 	r.Use(gin.Logger())
 	r.Use(recovery())
 	r.Use(recovery())
 	r.Use(cacheJs())
 	r.Use(cacheJs())
-    r.Use(ipWhiteList())
+	r.Use(ipWhiteList())
 
 
 	//r.Use(OperationSync())
 	//r.Use(OperationSync())
 
 
@@ -56,6 +57,7 @@ func InitRouter() *gin.Engine {
 			certificate.InitDNSCredentialRouter(g)
 			certificate.InitDNSCredentialRouter(g)
 			certificate.InitAcmeUserRouter(g)
 			certificate.InitAcmeUserRouter(g)
 			system.InitPrivateRouter(g)
 			system.InitPrivateRouter(g)
+			settings.InitRouter(g)
 			openai.InitRouter(g)
 			openai.InitRouter(g)
 			cluster.InitRouter(g)
 			cluster.InitRouter(g)
 			notification.InitRouter(g)
 			notification.InitRouter(g)

+ 5 - 2
settings/auth.go

@@ -1,9 +1,12 @@
 package settings
 package settings
 
 
 type Auth struct {
 type Auth struct {
-	IPWhiteList         []string `ini:",,allowshadow"`
+	IPWhiteList         []string `json:"ip_white_list" binding:"omitempty,dive,ip" ini:",,allowshadow"`
 	BanThresholdMinutes int      `json:"ban_threshold_minutes" binding:"min=1"`
 	BanThresholdMinutes int      `json:"ban_threshold_minutes" binding:"min=1"`
 	MaxAttempts         int      `json:"max_attempts" binding:"min=1"`
 	MaxAttempts         int      `json:"max_attempts" binding:"min=1"`
 }
 }
 
 
-var AuthSettings = Auth{}
+var AuthSettings = Auth{
+	BanThresholdMinutes: 10,
+	MaxAttempts:         10,
+}

+ 8 - 0
settings/settings.go

@@ -71,6 +71,14 @@ func Setup() {
 		NginxSettings.RestartCmd = "nginx -s stop"
 		NginxSettings.RestartCmd = "nginx -s stop"
 	}
 	}
 
 
+	if AuthSettings.BanThresholdMinutes <= 0 {
+		AuthSettings.BanThresholdMinutes = 10
+	}
+
+	if AuthSettings.MaxAttempts <= 0 {
+		AuthSettings.MaxAttempts = 10
+	}
+
 	err = Save()
 	err = Save()
 	if err != nil {
 	if err != nil {
 		log.Fatalf("settings.Setup: %v\n", err)
 		log.Fatalf("settings.Setup: %v\n", err)

Some files were not shown because too many files changed in this diff