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

+ 7 - 11
api/system/router.go

@@ -1,21 +1,17 @@
 package system
 
 import (
-    "github.com/gin-gonic/gin"
+	"github.com/gin-gonic/gin"
 )
 
 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) {
-    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/internal/logger"
 	"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/pkg/errors"
 	"net/http"
+	"sync"
 	"time"
 )
 
+var mutex = &sync.Mutex{}
+
 type LoginUser struct {
 	Name     string `json:"name" binding:"required,max=255"`
 	Password string `json:"password" binding:"required,max=255"`
@@ -29,6 +34,25 @@ type LoginResponse struct {
 }
 
 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
 	ok := api.BindAndValid(c, &json)
 	if !ok {
@@ -37,7 +61,7 @@ func Login(c *gin.Context) {
 
 	u, err := user.Login(json.Name, json.Password)
 	if err != nil {
-		time.Sleep(5 * time.Second)
+		// time.Sleep(5 * time.Second)
 		switch {
 		case errors.Is(err, user.ErrPasswordIncorrect):
 			c.JSON(http.StatusForbidden, LoginResponse{
@@ -52,9 +76,13 @@ func Login(c *gin.Context) {
 		default:
 			api.ErrHandler(c, err)
 		}
+		user.BanIP(clientIP)
 		return
 	}
 
+	// login success, clear banned record
+	_, _ = b.Where(b.IP.Eq(clientIP)).Delete()
+
 	logger.Info("[User Login]", u.Name)
 	token, err := user.GenerateJWT(u.Name)
 	if err != nil {

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

@@ -13,7 +13,7 @@ export interface Pagination {
   total_pages: number
 }
 
-export interface IGetListResponse<T> {
+export interface GetListResponse<T> {
   data: T[]
   pagination: Pagination
 }
@@ -35,7 +35,7 @@ class Curd<T> {
   }
 
   // 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 })
   }
 

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

@@ -1,17 +1,27 @@
 import http from '@/lib/http'
 
+export interface BannedIP {
+  ip: string
+  attempts: number
+  expired_at: string
+}
+
 const settings = {
-  get() {
+  get<T>(): Promise<T> {
     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)
   },
-
-  get_server_name() {
+  get_server_name(): Promise<{ name: string }> {
     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

+ 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/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: 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"
 msgstr "Action"
 
@@ -51,7 +52,7 @@ msgstr "Add Directive Below"
 msgid "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"
 msgstr "Add Site"
 
@@ -70,11 +71,11 @@ msgstr "Saved successfully"
 msgid "Additional"
 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"
 msgstr "Advance Mode"
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgstr ""
 
@@ -82,11 +83,11 @@ msgstr ""
 msgid "API Document"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgstr ""
 
@@ -94,7 +95,12 @@ msgstr ""
 msgid "Arch"
 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
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
@@ -147,6 +153,14 @@ msgstr ""
 msgid "Assistant"
 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:120
 msgid "Author"
@@ -170,8 +184,8 @@ msgstr "Auto-renewal enabled for %{name}"
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: 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"
 msgstr "Back"
 
@@ -180,19 +194,31 @@ msgstr "Back"
 msgid "Back Home"
 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"
 msgstr "Base information"
 
 #: src/views/config/ConfigEdit.vue:115
 #: 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
 #, fuzzy
 msgid "Basic"
 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"
 msgstr "Basic Mode"
 
@@ -280,12 +306,12 @@ msgid "Cleaning environment variables"
 msgstr ""
 
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr ""
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
@@ -312,7 +338,7 @@ msgstr "Configurations"
 msgid "Configuration file is test successful"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgstr "Configuration Name"
 
@@ -320,7 +346,7 @@ msgstr "Configuration Name"
 msgid "Configurations"
 msgstr "Configurations"
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgstr "Configure SSL"
 
@@ -351,7 +377,7 @@ msgstr "CPU:"
 msgid "Create"
 msgstr "Created at"
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgstr "Create Another"
 
@@ -390,7 +416,7 @@ msgstr ""
 msgid "Dashboard"
 msgstr "Dashboard"
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgstr "Database (Optional, default: database)"
 
@@ -423,15 +449,15 @@ msgstr ""
 msgid "Deleted 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/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 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"
 msgstr ""
 
@@ -481,9 +507,9 @@ msgstr "Disabled"
 msgid "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/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Disabled"
 
@@ -562,7 +588,7 @@ msgstr "Are you sure you want to remove this directive?"
 msgid "Domain"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgstr "Domain Config Created Successfully"
 
@@ -582,38 +608,38 @@ msgstr ""
 msgid "Dry run mode enabled"
 msgstr ""
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: 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
 msgid "Duplicate"
 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
 msgid "Duplicate %{conf_name} to %{node_name} 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
 msgid "Duplicate 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
 msgid "Duplicate 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
 msgid "Duplicate to local 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}"
 msgstr "Edit %{n}"
 
@@ -635,25 +661,25 @@ msgstr "Edit Site"
 msgid "Email"
 msgstr "Email (*)"
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "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/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #, fuzzy
 msgid "Enable"
 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"
 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"
 msgstr ""
 
@@ -661,12 +687,12 @@ msgstr ""
 msgid "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"
 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
 msgid "Enable successfully"
 msgstr "Enabled successfully"
@@ -677,19 +703,19 @@ msgstr "Enable TLS"
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgstr "Enabled"
 
 #: 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/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgstr "Enabled successfully"
@@ -759,7 +785,7 @@ msgstr "Failed to enable %{msg}"
 msgid "Failed to get certificate information"
 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."
 msgstr ""
 
@@ -785,7 +811,7 @@ msgstr "File Not Found"
 msgid "Filter"
 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"
 msgstr "Finished"
 
@@ -857,6 +883,12 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 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
 msgid "Import"
 msgstr ""
@@ -866,6 +898,10 @@ msgstr ""
 msgid "Import Certificate"
 msgstr "Certificate Status"
 
+#: src/views/other/Login.vue:59
+msgid "Incorrect username or password"
+msgstr ""
+
 #: src/constants/index.ts:18
 msgid "Info"
 msgstr ""
@@ -878,7 +914,7 @@ msgstr ""
 msgid "Initialing core upgrader"
 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"
 msgstr "Install"
 
@@ -900,6 +936,10 @@ msgstr ""
 msgid "Invalid"
 msgstr "Invalid E-mail!"
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
@@ -933,7 +973,7 @@ msgstr ""
 msgid "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/"
 msgstr ""
 
@@ -990,11 +1030,11 @@ msgstr "Locations"
 msgid "Log"
 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"
 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"
 msgstr "Login successful"
 
@@ -1002,7 +1042,7 @@ msgstr "Login successful"
 msgid "Logout successful"
 msgstr "Logout successful"
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgstr ""
 
@@ -1047,6 +1087,10 @@ msgstr "Manage Users"
 msgid "Managed Certificate"
 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:218
 msgid "Memory"
@@ -1060,7 +1104,7 @@ msgstr "Memory and Storage"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 msgid "Model"
 msgstr "Advance Mode"
@@ -1077,7 +1121,7 @@ msgstr "Modify Config"
 msgid "Modify Certificate"
 msgstr "Certificate Status"
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "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/domain/cert/ChangeCert.vue:17
 #: 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/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: 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
 msgid "Name"
 msgstr "Name"
@@ -1129,11 +1173,11 @@ msgstr ""
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgstr "Next"
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgstr ""
 
@@ -1141,7 +1185,7 @@ msgstr ""
 msgid "Nginx Access Log Path"
 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
 msgid "Nginx Configuration Parse Error"
 msgstr "Configuration Name"
@@ -1169,7 +1213,7 @@ msgid "Nginx restarted successfully"
 msgstr "Saved successfully"
 
 #: 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:535
 #: 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/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1208,7 +1253,7 @@ msgstr ""
 msgid "Notification"
 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
 msgid "Notifications"
 msgstr "Certificate is valid"
@@ -1234,7 +1279,7 @@ msgid "Ok"
 msgstr ""
 
 #: 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/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1264,7 +1309,7 @@ msgstr ""
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgstr ""
 
@@ -1277,13 +1322,13 @@ msgstr "OS:"
 msgid "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"
 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"
 msgstr ""
 
@@ -1291,11 +1336,11 @@ msgstr ""
 msgid "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"
 msgstr "Password"
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgstr "Password (*)"
 
@@ -1358,7 +1403,7 @@ msgstr ""
 msgid "Pre-release"
 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"
 msgstr ""
 
@@ -1454,7 +1499,16 @@ msgstr ""
 msgid "Reloading nginx"
 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
 msgid "Removed successfully"
 msgstr "Saved successfully"
@@ -1513,9 +1567,9 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: 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/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgstr "Save"
 
@@ -1523,7 +1577,7 @@ msgstr "Save"
 msgid "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
 msgid "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/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 #, fuzzy
 msgid "Save 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/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgstr "Saved successfully"
 
@@ -1564,8 +1618,9 @@ msgstr "Send"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: 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
 msgid "Server error"
 msgstr "Server error"
@@ -1584,7 +1639,7 @@ msgid "server_name not found in directives"
 msgstr "server_name not found in directives"
 
 #: 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"
 msgstr "server_name parameter is required"
 
@@ -1641,7 +1696,7 @@ msgstr "Certificate Status"
 msgid "SSL Certificate Path"
 msgstr "Certificate Status"
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 msgid "SSO Login"
 msgstr "Login"
@@ -1728,8 +1783,8 @@ msgstr ""
 msgid "System"
 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"
 msgstr ""
 
@@ -1763,7 +1818,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr "Certificate Status"
 
-#: 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."
@@ -1798,8 +1853,8 @@ msgstr ""
 msgid "The url is invalid"
 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."
 msgstr ""
 
@@ -1825,6 +1880,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgstr ""
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgstr ""
@@ -1841,10 +1897,14 @@ msgid ""
 "continue?"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 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
 msgid "Trash"
 msgstr ""
@@ -1900,11 +1960,15 @@ msgstr ""
 msgid "User"
 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"
 msgstr "Username"
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgstr "Username (*)"
 
@@ -1918,7 +1982,7 @@ msgstr ""
 msgid "View"
 msgstr ""
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 msgid "View all notifications"
 msgstr "Certificate is valid"
@@ -1934,7 +1998,7 @@ msgstr "Basic Mode"
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgstr "Warning"
 
@@ -1965,6 +2029,7 @@ msgstr ""
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 msgid "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/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: 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"
 msgstr "Acción"
 
@@ -56,7 +57,7 @@ msgstr "Añadir directiva a continuación"
 msgid "Add Location"
 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"
 msgstr "Agregar Sitio"
 
@@ -72,11 +73,11 @@ msgstr "Agregado exitoso"
 msgid "Additional"
 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"
 msgstr "Modo avanzado"
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgstr "URL Base de la API"
 
@@ -85,11 +86,11 @@ msgstr "URL Base de la API"
 msgid "API Document"
 msgstr "Token de la API"
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgstr "Proxy de la API"
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgstr "Token de la API"
 
@@ -97,7 +98,12 @@ msgstr "Token de la API"
 msgid "Arch"
 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
 msgid "Are you sure you want to clear all notifications?"
 msgstr "¿Está seguro de que desea borrar todas las notificaciones?"
@@ -146,6 +152,15 @@ msgstr "Preguntar por ayuda a ChatGPT"
 msgid "Assistant"
 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:120
 msgid "Author"
@@ -169,8 +184,8 @@ msgstr "Renovación automática habilitada por %{name}"
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: 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"
 msgstr "Volver"
 
@@ -178,18 +193,30 @@ msgstr "Volver"
 msgid "Back Home"
 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"
 msgstr "Información general"
 
 #: src/views/config/ConfigEdit.vue:115
 #: 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
 msgid "Basic"
 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"
 msgstr "Modo Básico"
 
@@ -273,12 +300,12 @@ msgid "Cleaning environment variables"
 msgstr "Borrar las variables de entorno"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "Borrar"
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgstr "Limpiado exitoso"
@@ -303,7 +330,7 @@ msgstr "Plantillas de configuración"
 msgid "Configuration file is test successful"
 msgstr "El archivo de configuración se probó exitosamente"
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgstr "Nombre de la configuración"
 
@@ -311,7 +338,7 @@ msgstr "Nombre de la configuración"
 msgid "Configurations"
 msgstr "Configuraciones"
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgstr "Configurar SSL"
 
@@ -341,7 +368,7 @@ msgstr "CPU:"
 msgid "Create"
 msgstr "Crear"
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgstr "Crear otro"
 
@@ -380,7 +407,7 @@ msgstr ""
 msgid "Dashboard"
 msgstr "Panel"
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgstr "Base de datos (Opcional, default: database)"
 
@@ -412,15 +439,15 @@ msgstr "Eliminar stream: %{site_name}"
 msgid "Deleted successfully"
 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/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 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"
 msgstr "Falló el desplegado de %{conf_name} a %{node_name}"
 
@@ -467,9 +494,9 @@ msgstr "Desactivar"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "No se pudo desactivar la renovación automática por %{name}"
 
-#: src/views/domain/cert/ChangeCert.vue: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/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Desactivado"
 
@@ -540,7 +567,7 @@ msgstr "¿Quieres eliminar esta transmisión?"
 msgid "Domain"
 msgstr "Dominio"
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgstr "Configuración de dominio creada con éxito"
 
@@ -562,34 +589,34 @@ msgstr "Descargando la última versión"
 msgid "Dry run mode enabled"
 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/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 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"
 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"
 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"
 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"
 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}"
 msgstr "Editar %{n}"
 
@@ -610,24 +637,24 @@ msgstr "Editar Transmisión"
 msgid "Email"
 msgstr "Correo (*)"
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgstr "Correo (*)"
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: 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
 msgid "Enable"
 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"
 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"
 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}"
 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"
 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"
 msgstr "Habilitado con Éxito"
 
@@ -650,19 +677,19 @@ msgstr "Habilitar TLS"
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgstr "Habilitado"
 
 #: 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/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgstr "Habilitado con éxito"
@@ -731,7 +758,7 @@ msgstr "Error al habilitar %{msg}"
 msgid "Failed to get certificate information"
 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."
 msgstr ""
 "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"
 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"
 msgstr "Terminado"
 
@@ -825,6 +852,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 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
 msgid "Import"
 msgstr "Importar"
@@ -833,6 +866,11 @@ msgstr "Importar"
 msgid "Import Certificate"
 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
 msgid "Info"
 msgstr "Información"
@@ -845,7 +883,7 @@ msgstr "Error de actualización de kernel inicial"
 msgid "Initialing core upgrader"
 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"
 msgstr "Instalar"
 
@@ -866,6 +904,10 @@ msgstr ""
 msgid "Invalid"
 msgstr "Válido"
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 msgid "Issue wildcard certificate"
 msgstr "Obtener certificado comodín"
@@ -897,7 +939,7 @@ msgstr "Comprobado por última vez el"
 msgid "Leave blank for no change"
 msgstr "Para no modificar dejar en blanco"
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Dejar en blanco para el valor predeterminado: https://api.openai.com/"
 
@@ -949,11 +991,11 @@ msgstr "Ubicaciones"
 msgid "Log"
 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"
 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"
 msgstr "Acceso exitoso"
 
@@ -961,7 +1003,7 @@ msgstr "Acceso exitoso"
 msgid "Logout successful"
 msgstr "Cierre de sesión exitoso"
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgstr ""
 
@@ -1003,6 +1045,10 @@ msgstr "Administrar usuarios"
 msgid "Managed Certificate"
 msgstr "Certificado Administrado"
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
@@ -1016,7 +1062,7 @@ msgstr "Memoria y almacenamiento"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 msgid "Model"
 msgstr "Modo de ejecución"
@@ -1031,7 +1077,7 @@ msgstr "Modificar"
 msgid "Modify Certificate"
 msgstr "Modificar Certificado"
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 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/domain/cert/ChangeCert.vue:17
 #: 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/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: 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
 msgid "Name"
 msgstr "Nombre"
@@ -1082,11 +1128,11 @@ msgstr "Se liberó una nueva versión"
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgstr "Siguiente"
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1094,7 +1140,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 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"
 msgstr "Error de análisis de configuración de Nginx"
 
@@ -1119,7 +1165,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx reiniciado con éxito"
 
 #: 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:535
 #: 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/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1157,7 +1204,7 @@ msgstr "Nota"
 msgid "Notification"
 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"
 msgstr "Notificaciones"
 
@@ -1181,7 +1228,7 @@ msgid "Ok"
 msgstr ""
 
 #: 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/StdTable.vue:522
 #: 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"
 msgstr "En línea"
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1223,13 +1270,13 @@ msgstr "SO"
 msgid "OS:"
 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"
 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"
 msgstr "Sobrescribir archivo existente"
 
@@ -1237,11 +1284,11 @@ msgstr "Sobrescribir archivo existente"
 msgid "Params"
 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"
 msgstr "Contraseña"
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgstr "Contraseña (*)"
 
@@ -1313,7 +1360,7 @@ msgstr "¡Seleccione al menos un nodo!"
 msgid "Pre-release"
 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"
 msgstr "Configuración"
 
@@ -1409,7 +1456,16 @@ msgstr "Recargando"
 msgid "Reloading 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"
 msgstr "Eliminado con éxito"
 
@@ -1461,9 +1517,9 @@ msgstr "Corriendo"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: 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/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgstr "Guardar"
 
@@ -1471,7 +1527,7 @@ msgstr "Guardar"
 msgid "Save Directive"
 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
 msgid "Save error %{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/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 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/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgstr "Guardado con éxito"
 
@@ -1511,8 +1567,9 @@ msgstr "Enviado"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: 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
 msgid "Server error"
 msgstr "Error del servidor"
@@ -1531,7 +1588,7 @@ msgid "server_name not found in directives"
 msgstr "No se encuentra server_name en las directivas"
 
 #: 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"
 msgstr "Se requiere el parámetro server_name"
 
@@ -1583,7 +1640,7 @@ msgstr "Ruta de la llave del certificado SSL"
 msgid "SSL Certificate Path"
 msgstr "Ruta del certificado SSL"
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 msgid "SSO Login"
 msgstr "Acceso SSO"
 
@@ -1667,8 +1724,8 @@ msgstr ""
 msgid "System"
 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"
 msgstr "Objetivo"
 
@@ -1701,7 +1758,7 @@ msgstr "La entrada no es un Certificado SSL"
 msgid "The input is not a SSL Certificate Key"
 msgstr "La entrada no es una clave de certificado SSL"
 
-#: src/views/preference/OpenAISettings.vue:33
+#: src/views/preference/OpenAISettings.vue:36
 msgid ""
 "The model name should only contain letters, unicode, numbers, hyphens, "
 "dashes, and dots."
@@ -1736,8 +1793,8 @@ msgstr ""
 msgid "The url is invalid"
 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
 msgid "The url is invalid."
 msgstr "La URL no es válida"
@@ -1764,6 +1821,7 @@ msgstr "Este campo es obligatorio"
 msgid "This field should not be empty"
 msgstr "Este campo no debe estar vacío"
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgstr ""
@@ -1784,10 +1842,14 @@ msgstr ""
 "de la autoridad al backend, y debemos guardar este archivo y volver a cargar "
 "Nginx. ¿Estás seguro de que quieres continuar?"
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 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
 msgid "Trash"
 msgstr ""
@@ -1840,11 +1902,15 @@ msgstr "URL"
 msgid "User"
 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"
 msgstr "Nombre de usuario"
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgstr "Nombre de usuario (*)"
 
@@ -1858,7 +1924,7 @@ msgstr "Válido"
 msgid "View"
 msgstr "Ver"
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 msgid "View all notifications"
 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/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 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/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 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/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: 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"
 msgstr "Action"
 
@@ -53,7 +54,7 @@ msgstr "Ajouter une directive"
 msgid "Add Location"
 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"
 msgstr "Ajouter un site"
 
@@ -72,11 +73,11 @@ msgstr "Mis à jour avec succés"
 msgid "Additional"
 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"
 msgstr "Mode avancé"
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgstr "URL de base de l'API"
 
@@ -85,11 +86,11 @@ msgstr "URL de base de l'API"
 msgid "API Document"
 msgstr "Jeton d'API"
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgstr "Proxy d'API"
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgstr "Jeton d'API"
 
@@ -98,7 +99,12 @@ msgstr "Jeton d'API"
 msgid "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
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
@@ -149,6 +155,15 @@ msgstr "Modèle ChatGPT"
 msgid "Assistant"
 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:120
 msgid "Author"
@@ -172,8 +187,8 @@ msgstr "Renouvellement automatique activé pour %{name}"
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: 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"
 msgstr "Retour"
 
@@ -181,18 +196,30 @@ msgstr "Retour"
 msgid "Back Home"
 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"
 msgstr "Information générale"
 
 #: src/views/config/ConfigEdit.vue:115
 #: 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
 msgid "Basic"
 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"
 msgstr "Mode simple"
 
@@ -279,12 +306,12 @@ msgid "Cleaning environment variables"
 msgstr "Nettoyage des variables d'environnement"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "Effacer"
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
@@ -310,7 +337,7 @@ msgstr "Modèles de configuration"
 msgid "Configuration file is test successful"
 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"
 msgstr "Nom de la configuration"
 
@@ -318,7 +345,7 @@ msgstr "Nom de la configuration"
 msgid "Configurations"
 msgstr "Configurations"
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgstr "Configurer SSL"
 
@@ -349,7 +376,7 @@ msgstr "CPU :"
 msgid "Create"
 msgstr "Créé le"
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgstr "Créer un autre"
 
@@ -388,7 +415,7 @@ msgstr ""
 msgid "Dashboard"
 msgstr "Dashboard"
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgstr "Base de données (Facultatif, par défaut : database)"
 
@@ -422,15 +449,15 @@ msgstr "Supprimer le site : %{site_name}"
 msgid "Deleted successfully"
 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/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 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"
 msgstr ""
 
@@ -480,9 +507,9 @@ msgstr "Désactivé"
 msgid "Disable auto-renewal failed for %{name}"
 msgstr "La désactivation du renouvellement automatique a échoué pour %{name}"
 
-#: src/views/domain/cert/ChangeCert.vue: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/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Désactivé"
 
@@ -557,7 +584,7 @@ msgstr "Voulez-vous supprimer ce serveur ?"
 msgid "Domain"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 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"
 msgstr ""
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: 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
 msgid "Duplicate"
 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
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 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
 msgid "Duplicate failed"
 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
 msgid "Duplicate successfully"
 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
 msgid "Duplicate to local successfully"
 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}"
 msgstr "Modifier %{n}"
 
@@ -633,25 +660,25 @@ msgstr "Modifier le site"
 msgid "Email"
 msgstr "Email (*)"
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "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/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #, fuzzy
 msgid "Enable"
 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"
 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"
 msgstr ""
 
@@ -659,12 +686,12 @@ msgstr ""
 msgid "Enable auto-renewal failed for %{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"
 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
 msgid "Enable successfully"
 msgstr "Activé avec succès"
@@ -675,19 +702,19 @@ msgstr "Activer TLS"
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgstr "Activé"
 
 #: 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/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgstr "Activé avec succès"
@@ -758,7 +785,7 @@ msgstr "Impossible d'activer %{msg}"
 msgid "Failed to get certificate information"
 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."
 msgstr ""
 "Échec de l'enregistrement, une ou plusieurs erreurs de syntaxe ont été "
@@ -786,7 +813,7 @@ msgstr "Fichier introuvable"
 msgid "Filter"
 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"
 msgstr "Finie"
 
@@ -856,6 +883,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 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
 #, fuzzy
 msgid "Import"
@@ -866,6 +899,11 @@ msgstr "Exporter"
 msgid "Import Certificate"
 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
 msgid "Info"
 msgstr ""
@@ -878,7 +916,7 @@ msgstr "Erreur du programme de mise à niveau initial du core"
 msgid "Initialing core upgrader"
 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"
 msgstr "Installer"
 
@@ -898,6 +936,10 @@ msgstr ""
 msgid "Invalid"
 msgstr ""
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
@@ -931,7 +973,7 @@ msgstr "Dernière vérification le"
 msgid "Leave blank for no change"
 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/"
 msgstr "Laissez vide pour la valeur par défaut : https://api.openai.com/"
 
@@ -990,11 +1032,11 @@ msgstr "Localisations"
 msgid "Log"
 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"
 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"
 msgstr "Connexion réussie"
 
@@ -1002,7 +1044,7 @@ msgstr "Connexion réussie"
 msgid "Logout successful"
 msgstr "Déconnexion réussie"
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgstr ""
 
@@ -1047,6 +1089,10 @@ msgstr "Gérer les utilisateurs"
 msgid "Managed Certificate"
 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:218
 msgid "Memory"
@@ -1060,7 +1106,7 @@ msgstr "Mémoire et stockage"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 msgid "Model"
 msgstr "Mode d'exécution"
@@ -1076,7 +1122,7 @@ msgstr "Modifier"
 msgid "Modify Certificate"
 msgstr "État du certificat"
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 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/domain/cert/ChangeCert.vue:17
 #: 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/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: 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
 msgid "Name"
 msgstr "Nom"
@@ -1127,11 +1173,11 @@ msgstr "Nouvelle version publiée"
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgstr "Suivant"
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 #, fuzzy
 msgid "Nginx"
 msgstr "Journal Nginx"
@@ -1140,7 +1186,7 @@ msgstr "Journal Nginx"
 msgid "Nginx Access Log Path"
 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"
 msgstr "Erreur d'analyse de configuration Nginx"
 
@@ -1165,7 +1211,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx a redémarré avec succès"
 
 #: 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:535
 #: 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/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1205,7 +1252,7 @@ msgstr "Note"
 msgid "Notification"
 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
 msgid "Notifications"
 msgstr "Certification"
@@ -1230,7 +1277,7 @@ msgid "Ok"
 msgstr ""
 
 #: 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/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1260,7 +1307,7 @@ msgstr ""
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1272,13 +1319,13 @@ msgstr "OS"
 msgid "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"
 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"
 msgstr ""
 
@@ -1286,11 +1333,11 @@ msgstr ""
 msgid "Params"
 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"
 msgstr "Mot de passe"
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgstr "Mot de passe (*)"
 
@@ -1360,7 +1407,7 @@ msgstr ""
 msgid "Pre-release"
 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"
 msgstr "Préférence"
 
@@ -1459,7 +1506,16 @@ msgstr "Rechargement"
 msgid "Reloading 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
 msgid "Removed successfully"
 msgstr "Enregistré avec succès"
@@ -1517,9 +1573,9 @@ msgstr "En cours d'éxécution"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: 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/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgstr "Enregistrer"
 
@@ -1527,7 +1583,7 @@ msgstr "Enregistrer"
 msgid "Save 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
 msgid "Save error %{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/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 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/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgstr "Enregistré avec succès"
 
@@ -1567,8 +1623,9 @@ msgstr "Envoyer"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: 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
 msgid "Server error"
 msgstr "Erreur du serveur"
@@ -1587,7 +1644,7 @@ msgid "server_name not found in directives"
 msgstr "server_name introuvable dans les directives"
 
 #: 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"
 msgstr "Le paramètre server_name est obligatoire"
 
@@ -1643,7 +1700,7 @@ msgstr "Chemin de la clé du certificat SSL"
 msgid "SSL Certificate Path"
 msgstr "Chemin du certificat SSL"
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 msgid "SSO Login"
 msgstr "Connexion"
@@ -1731,8 +1788,8 @@ msgstr ""
 msgid "System"
 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"
 msgstr ""
 
@@ -1766,7 +1823,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr "Chemin de la clé du certificat SSL"
 
-#: 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."
@@ -1802,8 +1859,8 @@ msgstr ""
 msgid "The url is invalid"
 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."
 msgstr ""
 
@@ -1832,6 +1889,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgstr ""
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgstr ""
@@ -1852,10 +1910,14 @@ msgstr ""
 "transmettre la demande de l'autorité au backend, et nous devons enregistrer "
 "ce fichier et recharger le Nginx. Êtes-vous sûr de vouloir continuer?"
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 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
 msgid "Trash"
 msgstr ""
@@ -1909,11 +1971,15 @@ msgstr ""
 msgid "User"
 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"
 msgstr "Nom d'utilisateur"
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgstr "Nom d'utilisateur (*)"
 
@@ -1927,7 +1993,7 @@ msgstr ""
 msgid "View"
 msgstr "Voir"
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 msgid "View all notifications"
 msgstr "Certification"
@@ -1943,7 +2009,7 @@ msgstr "Mode simple"
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 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/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 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/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: 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"
 msgstr "작업"
 
@@ -55,7 +56,7 @@ msgstr "아래에 지시문 추가"
 msgid "Add Location"
 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"
 msgstr "사이트 추가"
 
@@ -71,11 +72,11 @@ msgstr "성공적으로 추가됨"
 msgid "Additional"
 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"
 msgstr "고급 모드"
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgstr "API 기본 URL"
 
@@ -84,11 +85,11 @@ msgstr "API 기본 URL"
 msgid "API Document"
 msgstr "API 토큰"
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgstr "API 프록시"
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgstr "API 토큰"
 
@@ -96,7 +97,12 @@ msgstr "API 토큰"
 msgid "Arch"
 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
 msgid "Are you sure you want to clear all notifications?"
 msgstr "모든 알림을 지우시겠습니까?"
@@ -145,6 +151,15 @@ msgstr "ChatGPT에게 도움 요청"
 msgid "Assistant"
 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:120
 msgid "Author"
@@ -168,8 +183,8 @@ msgstr "%{name}에 대한 자동 갱신 활성화됨"
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: 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"
 msgstr "뒤로"
 
@@ -177,18 +192,30 @@ msgstr "뒤로"
 msgid "Back Home"
 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"
 msgstr "기본 정보"
 
 #: src/views/config/ConfigEdit.vue:115
 #: 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
 msgid "Basic"
 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"
 msgstr "기본 모드"
 
@@ -272,12 +299,12 @@ msgid "Cleaning environment variables"
 msgstr "환경 변수 정리"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "클리어"
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgstr "성공적으로 제거됨"
@@ -301,7 +328,7 @@ msgstr "구성 템플릿"
 msgid "Configuration file is test successful"
 msgstr "구성 파일 테스트 성공"
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgstr "구성 이름"
 
@@ -309,7 +336,7 @@ msgstr "구성 이름"
 msgid "Configurations"
 msgstr "구성들"
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgstr "SSL 구성하기"
 
@@ -339,7 +366,7 @@ msgstr "CPU:"
 msgid "Create"
 msgstr "생성"
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgstr "다른 것 생성하기"
 
@@ -378,7 +405,7 @@ msgstr ""
 msgid "Dashboard"
 msgstr "대시보드"
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgstr "데이터베이스 (선택사항, 기본값: database)"
 
@@ -410,15 +437,15 @@ msgstr "스트림 삭제: %{stream_name}"
 msgid "Deleted successfully"
 msgstr "성공적으로 삭제됨"
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: 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
 msgid "Deploy"
 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"
 msgstr "%{conf_name}을(를) %{node_name}(으)로 배포 실패"
 
@@ -465,9 +492,9 @@ msgstr "비활성화"
 msgid "Disable auto-renewal failed for %{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/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "비활성화됨"
 
@@ -538,7 +565,7 @@ msgstr "이 업스트림을 제거하시겠습니까?"
 msgid "Domain"
 msgstr "도메인"
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgstr "도메인 구성이 성공적으로 생성되었습니다"
 
@@ -559,35 +586,35 @@ msgstr "최신 릴리스 다운로드 중"
 msgid "Dry run mode enabled"
 msgstr "드라이런 모드 활성화됨"
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: 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
 msgid "Duplicate"
 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"
 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"
 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
 msgid "Duplicate successfully"
 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"
 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}"
 msgstr "%{n} 편집"
 
@@ -608,24 +635,24 @@ msgstr "스트림 편집"
 msgid "Email"
 msgstr "이메일 (*)"
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgstr "이메일 (*)"
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: 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
 msgid "Enable"
 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"
 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"
 msgstr "%{node_name}에서 %{conf_name} 성공적으로 활성화됨"
 
@@ -633,12 +660,12 @@ msgstr "%{node_name}에서 %{conf_name} 성공적으로 활성화됨"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "%{name}에 대한 자동 갱신 활성화 실패"
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 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"
 msgstr "성공적으로 활성화"
 
@@ -648,19 +675,19 @@ msgstr "TLS 활성화"
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgstr "활성화됨"
 
 #: 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/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgstr "성공적으로 활성화됨"
@@ -730,7 +757,7 @@ msgstr "%{msg} 활성화 실패"
 msgid "Failed to get certificate information"
 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."
 msgstr "저장 실패, 구성에서 구문 오류가 감지되었습니다."
 
@@ -756,7 +783,7 @@ msgstr "파일을 찾을 수 없음"
 msgid "Filter"
 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"
 msgstr "완료됨"
 
@@ -828,6 +855,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 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
 msgid "Import"
 msgstr "가져오기"
@@ -837,6 +870,11 @@ msgstr "가져오기"
 msgid "Import Certificate"
 msgstr "인증서 상태"
 
+#: src/views/other/Login.vue:59
+#, fuzzy
+msgid "Incorrect username or password"
+msgstr "사용자 이름 또는 비밀번호가 올바르지 않습니다"
+
 #: src/constants/index.ts:18
 msgid "Info"
 msgstr "정보"
@@ -849,7 +887,7 @@ msgstr "초기 코어 업그레이더 오류"
 msgid "Initialing core upgrader"
 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"
 msgstr "설치"
 
@@ -871,6 +909,10 @@ msgstr "간격"
 msgid "Invalid"
 msgstr "유효함"
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
@@ -904,7 +946,7 @@ msgstr "마지막 확인 시간"
 msgid "Leave blank for no change"
 msgstr "변경사항이 없으면 비워두세요"
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "기본값을 사용하려면 비워 두세요: https://api.openai.com/"
 
@@ -961,11 +1003,11 @@ msgstr "위치들"
 msgid "Log"
 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"
 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"
 msgstr "로그인 성공"
 
@@ -973,7 +1015,7 @@ msgstr "로그인 성공"
 msgid "Logout successful"
 msgstr "로그아웃 성공"
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgstr "로그관리"
 
@@ -1023,6 +1065,10 @@ msgstr "사용자 관리"
 msgid "Managed Certificate"
 msgstr "인증서 유효"
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
@@ -1036,7 +1082,7 @@ msgstr "메모리 및 저장소"
 msgid "Minutes"
 msgstr "분"
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 msgid "Model"
 msgstr "실행 모드"
@@ -1053,7 +1099,7 @@ msgstr "설정 수정"
 msgid "Modify Certificate"
 msgstr "인증서 상태"
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgstr "설정 수정"
 
@@ -1073,12 +1119,12 @@ msgstr "단일 지시문"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: 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/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: 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
 msgid "Name"
 msgstr "이름"
@@ -1105,11 +1151,11 @@ msgstr "새 버전 출시"
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgstr "다음"
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1117,7 +1163,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 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
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 구성 오류름"
@@ -1145,7 +1191,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx가 성공적으로 재시작됨"
 
 #: 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:535
 #: 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/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1184,7 +1231,7 @@ msgstr "참고"
 msgid "Notification"
 msgstr "알림"
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 #, fuzzy
 msgid "Notifications"
 msgstr "알림"
@@ -1210,7 +1257,7 @@ msgid "Ok"
 msgstr ""
 
 #: 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/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1240,7 +1287,7 @@ msgstr "검증이 완료되면, 레코드는 제거됩니다."
 msgid "Online"
 msgstr "온라인"
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgstr "오픈AI"
 
@@ -1253,13 +1300,13 @@ msgstr "OS"
 msgid "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"
 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"
 msgstr "기존 파일 덮어쓰기"
 
@@ -1267,11 +1314,11 @@ msgstr "기존 파일 덮어쓰기"
 msgid "Params"
 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"
 msgstr "비밀번호"
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgstr "비밀번호 (*)"
 
@@ -1336,7 +1383,7 @@ msgstr "적어도 하나의 노드를 선택해주세요!"
 msgid "Pre-release"
 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"
 msgstr "환경설정"
 
@@ -1434,7 +1481,16 @@ msgstr "리로딩 중"
 msgid "Reloading 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
 msgid "Removed successfully"
 msgstr "성공적으로 제거됨"
@@ -1493,9 +1549,9 @@ msgstr "실행 중"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: 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/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgstr "저장"
 
@@ -1503,7 +1559,7 @@ msgstr "저장"
 msgid "Save Directive"
 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
 msgid "Save error %{msg}"
 msgstr "저장 오류 %{msg}"
@@ -1511,15 +1567,15 @@ msgstr "저장 오류 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 #, fuzzy
 msgid "Save successfully"
 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/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgstr "성공적으로 저장됨"
 
@@ -1544,8 +1600,9 @@ msgstr "보내기"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: 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
 msgid "Server error"
 msgstr "서버 오류"
@@ -1564,7 +1621,7 @@ msgid "server_name not found in directives"
 msgstr "directives에서 server_name을 찾을 수 없습니다"
 
 #: 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"
 msgstr "server_name 매개변수가 필요합니다"
 
@@ -1620,7 +1677,7 @@ msgstr "SSL 인증서 키 경로"
 msgid "SSL Certificate Path"
 msgstr "SSL 인증서 경로"
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 msgid "SSO Login"
 msgstr "SSO 로그인"
@@ -1707,8 +1764,8 @@ msgstr ""
 msgid "System"
 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"
 msgstr "대상"
 
@@ -1742,7 +1799,7 @@ msgstr "입력이 SSL 인증서가 아닙니다"
 msgid "The input is not a SSL Certificate Key"
 msgstr "Certificate Status"
 
-#: 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."
@@ -1778,8 +1835,8 @@ msgstr ""
 msgid "The url is invalid"
 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
 msgid "The url is invalid."
 msgstr "유효한 URL이 아닙니다"
@@ -1806,6 +1863,7 @@ msgstr "이 필드는 필수입니다"
 msgid "This field should not be empty"
 msgstr "이 필드는 비워둘 수 없습니다"
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgstr "팁"
@@ -1825,10 +1883,14 @@ msgstr ""
 "시할 수 있는 위치를 추가해야 하며,이 파일을 저장하고 Nginx를 다시로드해야 합"
 "니다.계속하시겠습니까?"
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 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
 msgid "Trash"
 msgstr ""
@@ -1884,11 +1946,15 @@ msgstr "URL"
 msgid "User"
 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"
 msgstr "사용자 이름"
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgstr "사용자 이름 (*)"
 
@@ -1902,7 +1968,7 @@ msgstr "유효함"
 msgid "View"
 msgstr "보기"
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 msgid "View all notifications"
 msgstr "Certificate is valid"
@@ -1919,7 +1985,7 @@ msgstr "기본 모드"
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgstr "경고"
 
@@ -1954,6 +2020,7 @@ msgstr "인증서를 디스크에 쓰기"
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgstr "예"

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

@@ -24,6 +24,7 @@ msgstr ""
 #: src/views/domain/DomainList.vue:47
 #: src/views/environment/Environment.vue:129
 #: src/views/notification/Notification.vue:37
+#: src/views/preference/AuthSettings.vue:26
 #: src/views/stream/StreamList.vue:47
 #: src/views/user/User.vue:43
 msgid "Action"
@@ -49,7 +50,7 @@ msgid "Add Location"
 msgstr ""
 
 #: src/routes/index.ts:64
-#: src/views/domain/DomainAdd.vue:93
+#: src/views/domain/DomainAdd.vue:89
 msgid "Add Site"
 msgstr ""
 
@@ -65,12 +66,12 @@ msgstr ""
 msgid "Additional"
 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"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgstr ""
 
@@ -78,11 +79,11 @@ msgstr ""
 msgid "API Document"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgstr ""
 
@@ -90,7 +91,11 @@ msgstr ""
 msgid "Arch"
 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
 msgid "Are you sure you want to clear all notifications?"
 msgstr ""
@@ -136,6 +141,14 @@ msgstr ""
 msgid "Assistant"
 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:120
 msgid "Author"
@@ -160,9 +173,9 @@ msgstr ""
 #: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73
 #: 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/stream/StreamEdit.vue:247
+#: src/views/stream/StreamEdit.vue:245
 msgid "Back"
 msgstr ""
 
@@ -170,19 +183,31 @@ msgstr ""
 msgid "Back Home"
 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"
 msgstr ""
 
 #: src/views/config/ConfigEdit.vue:115
 #: 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
 msgid "Basic"
 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"
 msgstr ""
 
@@ -266,12 +291,12 @@ msgid "Cleaning environment variables"
 msgstr ""
 
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr ""
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgstr ""
@@ -295,7 +320,7 @@ msgstr ""
 msgid "Configuration file is test successful"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgstr ""
 
@@ -303,7 +328,7 @@ msgstr ""
 msgid "Configurations"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgstr ""
 
@@ -333,7 +358,7 @@ msgstr ""
 msgid "Create"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgstr ""
 
@@ -371,7 +396,7 @@ msgstr ""
 msgid "Dashboard"
 msgstr ""
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgstr ""
 
@@ -403,15 +428,15 @@ msgstr ""
 msgid "Deleted successfully"
 msgstr ""
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: 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
 msgid "Deploy"
 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"
 msgstr ""
 
@@ -460,10 +485,10 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr ""
 
 #: 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/environment/Environment.vue:93
-#: src/views/stream/StreamEdit.vue:177
+#: src/views/stream/StreamEdit.vue:175
 #: src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr ""
@@ -536,7 +561,7 @@ msgstr ""
 msgid "Domain"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgstr ""
 
@@ -556,35 +581,35 @@ msgstr ""
 msgid "Dry run mode enabled"
 msgstr ""
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: 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
 msgid "Duplicate"
 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"
 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"
 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"
 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"
 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}"
 msgstr ""
 
@@ -605,24 +630,24 @@ msgstr ""
 msgid "Email"
 msgstr ""
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgstr ""
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: 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
 msgid "Enable"
 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"
 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"
 msgstr ""
 
@@ -630,12 +655,12 @@ msgstr ""
 msgid "Enable auto-renewal failed for %{name}"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 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"
 msgstr ""
 
@@ -645,22 +670,22 @@ msgstr ""
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: 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/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171
+#: src/views/stream/StreamEdit.vue:169
 #: src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgstr ""
 
 #: 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/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
 msgid "Enabled successfully"
 msgstr ""
@@ -730,8 +755,8 @@ msgstr ""
 msgid "Failed to get certificate information"
 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."
 msgstr ""
 
@@ -757,7 +782,7 @@ msgid "Filter"
 msgstr ""
 
 #: src/language/constants.ts:19
-#: src/views/domain/DomainAdd.vue:101
+#: src/views/domain/DomainAdd.vue:97
 msgid "Finished"
 msgstr ""
 
@@ -825,6 +850,10 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 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
 msgid "Import"
 msgstr ""
@@ -834,6 +863,10 @@ msgstr ""
 msgid "Import Certificate"
 msgstr ""
 
+#: src/views/other/Login.vue:59
+msgid "Incorrect username or password"
+msgstr ""
+
 #: src/constants/index.ts:18
 msgid "Info"
 msgstr ""
@@ -847,7 +880,7 @@ msgid "Initialing core upgrader"
 msgstr ""
 
 #: src/routes/index.ts:273
-#: src/views/other/Install.vue:136
+#: src/views/other/Install.vue:135
 msgid "Install"
 msgstr ""
 
@@ -867,6 +900,10 @@ msgstr ""
 msgid "Invalid"
 msgstr ""
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 msgid "Issue wildcard certificate"
 msgstr ""
@@ -897,7 +934,7 @@ msgstr ""
 msgid "Leave blank for no change"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr ""
 
@@ -949,12 +986,12 @@ msgid "Log"
 msgstr ""
 
 #: src/routes/index.ts:279
-#: src/views/other/Login.vue:147
+#: src/views/other/Login.vue:159
 msgid "Login"
 msgstr ""
 
+#: src/views/other/Login.vue:109
 #: src/views/other/Login.vue:51
-#: src/views/other/Login.vue:97
 msgid "Login successful"
 msgstr ""
 
@@ -962,7 +999,7 @@ msgstr ""
 msgid "Logout successful"
 msgstr ""
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgstr ""
 
@@ -997,6 +1034,10 @@ msgstr ""
 msgid "Managed Certificate"
 msgstr ""
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
@@ -1010,7 +1051,7 @@ msgstr ""
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 msgid "Model"
 msgstr ""
 
@@ -1025,7 +1066,7 @@ msgstr ""
 msgid "Modify Certificate"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgstr ""
 
@@ -1044,12 +1085,12 @@ msgstr ""
 #: src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: 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/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: 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
 msgid "Name"
@@ -1077,11 +1118,11 @@ msgstr ""
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgstr ""
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgstr ""
 
@@ -1089,8 +1130,8 @@ msgstr ""
 msgid "Nginx Access Log Path"
 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"
 msgstr ""
 
@@ -1116,7 +1157,7 @@ msgid "Nginx restarted successfully"
 msgstr ""
 
 #: 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:535
 #: 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/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1154,7 +1196,7 @@ msgstr ""
 msgid "Notification"
 msgstr ""
 
-#: src/components/Notification/Notification.vue:80
+#: src/components/Notification/Notification.vue:82
 #: src/routes/index.ts:221
 msgid "Notifications"
 msgstr ""
@@ -1179,7 +1221,7 @@ msgid "Ok"
 msgstr ""
 
 #: 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/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1209,7 +1251,7 @@ msgstr ""
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgstr ""
 
@@ -1221,13 +1263,13 @@ msgstr ""
 msgid "OS:"
 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"
 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"
 msgstr ""
 
@@ -1235,12 +1277,12 @@ msgstr ""
 msgid "Params"
 msgstr ""
 
-#: src/views/other/Login.vue:132
+#: src/views/other/Login.vue:144
 #: src/views/user/User.vue:18
 msgid "Password"
 msgstr ""
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgstr ""
 
@@ -1300,7 +1342,7 @@ msgid "Pre-release"
 msgstr ""
 
 #: src/routes/index.ts:239
-#: src/views/preference/Preference.vue:96
+#: src/views/preference/Preference.vue:105
 msgid "Preference"
 msgstr ""
 
@@ -1391,7 +1433,15 @@ msgstr ""
 msgid "Reloading nginx"
 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"
 msgstr ""
 
@@ -1444,10 +1494,10 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/views/certificate/CertificateEditor.vue:249
 #: 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/preference/Preference.vue:130
-#: src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145
+#: src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgstr ""
 
@@ -1456,7 +1506,7 @@ msgid "Save Directive"
 msgstr ""
 
 #: 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
 msgid "Save error %{msg}"
 msgstr ""
@@ -1464,15 +1514,15 @@ msgstr ""
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 msgstr ""
 
 #: 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/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgstr ""
 
@@ -1499,8 +1549,9 @@ msgstr ""
 #: 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/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
@@ -1520,7 +1571,7 @@ msgid "server_name not found in directives"
 msgstr ""
 
 #: 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"
 msgstr ""
 
@@ -1570,7 +1621,7 @@ msgstr ""
 msgid "SSL Certificate Path"
 msgstr ""
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 msgid "SSO Login"
 msgstr ""
 
@@ -1649,8 +1700,8 @@ msgstr ""
 msgid "System"
 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"
 msgstr ""
 
@@ -1679,7 +1730,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 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."
 msgstr ""
 
@@ -1704,8 +1755,8 @@ msgstr ""
 msgid "The url is invalid"
 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."
 msgstr ""
 
@@ -1731,6 +1782,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgstr ""
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 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?"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 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
 msgid "Trash"
 msgstr ""
@@ -1804,12 +1860,16 @@ msgstr ""
 msgid "User"
 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
 msgid "Username"
 msgstr ""
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgstr ""
 
@@ -1823,7 +1883,7 @@ msgstr ""
 msgid "View"
 msgstr ""
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 msgid "View all notifications"
 msgstr ""
 
@@ -1838,7 +1898,7 @@ msgstr ""
 #: src/constants/index.ts:17
 #: src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgstr ""
 
@@ -1865,6 +1925,7 @@ msgstr ""
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 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/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: 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"
 msgstr "Действие"
 
@@ -51,7 +52,7 @@ msgstr "Добавить директиву ниже"
 msgid "Add 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"
 msgstr "Добавть Сайт"
 
@@ -70,11 +71,11 @@ msgstr "Обновлено успешно"
 msgid "Additional"
 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"
 msgstr "Расширенный режим"
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgstr ""
 
@@ -82,11 +83,11 @@ msgstr ""
 msgid "API Document"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgstr ""
 
@@ -94,7 +95,12 @@ msgstr ""
 msgid "Arch"
 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
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
@@ -147,6 +153,15 @@ msgstr "Обратитесь за помощью к ChatGPT"
 msgid "Assistant"
 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:120
 msgid "Author"
@@ -170,8 +185,8 @@ msgstr "Автообновление включено для %{name}"
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: 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"
 msgstr "Назад"
 
@@ -180,19 +195,31 @@ msgstr "Назад"
 msgid "Back Home"
 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"
 msgstr "Основная информация"
 
 #: src/views/config/ConfigEdit.vue:115
 #: 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
 #, fuzzy
 msgid "Basic"
 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"
 msgstr "Простой режим"
 
@@ -280,12 +307,12 @@ msgid "Cleaning environment variables"
 msgstr "Очистка переменных среды"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "Очистить"
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
@@ -312,7 +339,7 @@ msgstr "Шаблоны конфигураций"
 msgid "Configuration file is test successful"
 msgstr "Проверка конфигурации успешна"
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgstr "Название конфигурации"
 
@@ -320,7 +347,7 @@ msgstr "Название конфигурации"
 msgid "Configurations"
 msgstr "Конфигурации"
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgstr "Настроить SSL"
 
@@ -351,7 +378,7 @@ msgstr "CPU:"
 msgid "Create"
 msgstr "Создан в"
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgstr "Создать еще"
 
@@ -390,7 +417,7 @@ msgstr ""
 msgid "Dashboard"
 msgstr "Доска"
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgstr "База данных (Опционально, по умолчанию: database)"
 
@@ -423,15 +450,15 @@ msgstr ""
 msgid "Deleted successfully"
 msgstr "Отключено успешно"
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: 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
 msgid "Deploy"
 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"
 msgstr ""
 
@@ -481,9 +508,9 @@ msgstr "Отключить"
 msgid "Disable auto-renewal failed for %{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/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Отключено"
 
@@ -562,7 +589,7 @@ msgstr "Вы хотите удалить этот сервер?"
 msgid "Domain"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgstr "Конфигурация домена успешно создана"
 
@@ -584,38 +611,38 @@ msgstr "Загрузка последней версии"
 msgid "Dry run mode enabled"
 msgstr "Включен пробный режим"
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: 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
 msgid "Duplicate"
 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
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 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
 msgid "Duplicate failed"
 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
 msgid "Duplicate successfully"
 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
 msgid "Duplicate to local 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}"
 msgstr "Редактировать %{n}"
 
@@ -637,25 +664,25 @@ msgstr "Редактировать Сайт"
 msgid "Email"
 msgstr "Email (*)"
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "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/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #, fuzzy
 msgid "Enable"
 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"
 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"
 msgstr "Включение %{conf_name} in %{node_name} успешно"
 
@@ -663,12 +690,12 @@ msgstr "Включение %{conf_name} in %{node_name} успешно"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "Не удалось включить автоматическое продление для %{name}"
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 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
 msgid "Enable successfully"
 msgstr "Активировано успешно"
@@ -679,19 +706,19 @@ msgstr "Включить TLS"
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgstr "Включено"
 
 #: 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/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgstr "Активировано успешно"
@@ -762,7 +789,7 @@ msgstr "Не удалось включить %{msg}"
 msgid "Failed to get certificate information"
 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."
 msgstr "Не удалось сохранить, обнаружены синтаксические ошибки в конфигурации."
 
@@ -788,7 +815,7 @@ msgstr "Файл не найден"
 msgid "Filter"
 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"
 msgstr "Готово"
 
@@ -860,6 +887,12 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 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
 #, fuzzy
 msgid "Import"
@@ -870,6 +903,11 @@ msgstr "Экспорт"
 msgid "Import Certificate"
 msgstr "Статус сертификата"
 
+#: src/views/other/Login.vue:59
+#, fuzzy
+msgid "Incorrect username or password"
+msgstr "Имя пользователя или пароль неверны"
+
 #: src/constants/index.ts:18
 msgid "Info"
 msgstr ""
@@ -882,7 +920,7 @@ msgstr "Ошибка первоначального обновления ядр
 msgid "Initialing core upgrader"
 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"
 msgstr "Установить"
 
@@ -904,6 +942,10 @@ msgstr ""
 msgid "Invalid"
 msgstr "Действительный"
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
@@ -938,7 +980,7 @@ msgstr "Последняя проверка в"
 msgid "Leave blank for no change"
 msgstr "Оставьте пустым без изменений"
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Оставьте пустым для значения по умолчанию: https://api.openai.com/"
 
@@ -995,11 +1037,11 @@ msgstr "Locations"
 msgid "Log"
 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"
 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"
 msgstr "Авторизация успешна"
 
@@ -1007,7 +1049,7 @@ msgstr "Авторизация успешна"
 msgid "Logout successful"
 msgstr "Выход выполнен успешно"
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgstr ""
 
@@ -1052,6 +1094,10 @@ msgstr "Пользователи"
 msgid "Managed Certificate"
 msgstr "Управление сертификатами"
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
@@ -1065,7 +1111,7 @@ msgstr "Память и хранилище"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 msgid "Model"
 msgstr "Расширенный режим"
@@ -1082,7 +1128,7 @@ msgstr "Изменить"
 msgid "Modify Certificate"
 msgstr "Статус сертификата"
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgstr "Изменить конфигурацию"
 
@@ -1102,12 +1148,12 @@ msgstr "Одиночная директива"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: 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/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: 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
 msgid "Name"
 msgstr "Имя"
@@ -1134,11 +1180,11 @@ msgstr "Вышла новая версия"
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgstr "Дальше"
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 #, fuzzy
 msgid "Nginx"
 msgstr "Журнал"
@@ -1147,7 +1193,7 @@ msgstr "Журнал"
 msgid "Nginx Access Log Path"
 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
 msgid "Nginx Configuration Parse Error"
 msgstr "Ошибка синтаксического анализа конфигурации Nginx"
@@ -1175,7 +1221,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx успешно перезапущен"
 
 #: 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:535
 #: 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/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1214,7 +1261,7 @@ msgstr "Заметка"
 msgid "Notification"
 msgstr "Сертификат"
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 #, fuzzy
 msgid "Notifications"
 msgstr "Уведомления"
@@ -1240,7 +1287,7 @@ msgid "Ok"
 msgstr ""
 
 #: 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/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1270,7 +1317,7 @@ msgstr ""
 msgid "Online"
 msgstr ""
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgstr ""
 
@@ -1283,13 +1330,13 @@ msgstr "OS:"
 msgid "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"
 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"
 msgstr ""
 
@@ -1297,11 +1344,11 @@ msgstr ""
 msgid "Params"
 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"
 msgstr "Пароль"
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgstr "Пароль (*)"
 
@@ -1366,7 +1413,7 @@ msgstr ""
 msgid "Pre-release"
 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"
 msgstr "Настройки"
 
@@ -1464,7 +1511,16 @@ msgstr "Перезагружается"
 msgid "Reloading 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
 msgid "Removed successfully"
 msgstr "Успешно сохранено"
@@ -1523,9 +1579,9 @@ msgstr "Выполняется"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: 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/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgstr "Сохранить"
 
@@ -1533,7 +1589,7 @@ msgstr "Сохранить"
 msgid "Save Directive"
 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
 msgid "Save error %{msg}"
 msgstr "Ошибка сохранения %{msg}"
@@ -1541,15 +1597,15 @@ msgstr "Ошибка сохранения %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 #, fuzzy
 msgid "Save successfully"
 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/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgstr "Успешно сохранено"
 
@@ -1574,8 +1630,9 @@ msgstr "Отправлено"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: 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
 msgid "Server error"
 msgstr "Ошибка сервера"
@@ -1594,7 +1651,7 @@ msgid "server_name not found in directives"
 msgstr "server_name не нашел в директивах"
 
 #: 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"
 msgstr "server_name параметр обязателен"
 
@@ -1651,7 +1708,7 @@ msgstr "Путь к ключу сертификата SSL"
 msgid "SSL Certificate Path"
 msgstr "Путь к сертификату SSL"
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 msgid "SSO Login"
 msgstr "Логин"
@@ -1738,8 +1795,8 @@ msgstr ""
 msgid "System"
 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"
 msgstr ""
 
@@ -1773,7 +1830,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr "Путь к ключу сертификата SSL"
 
-#: 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."
@@ -1809,8 +1866,8 @@ msgstr ""
 msgid "The url is invalid"
 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
 msgid "The url is invalid."
 msgstr "URL-адрес неверный"
@@ -1838,6 +1895,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgstr "Это поле обязательно к заполнению"
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgstr ""
@@ -1854,10 +1912,14 @@ msgid ""
 "continue?"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 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
 msgid "Trash"
 msgstr ""
@@ -1913,11 +1975,15 @@ msgstr ""
 msgid "User"
 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"
 msgstr "Имя пользователя"
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgstr "Имя пользователя (*)"
 
@@ -1931,7 +1997,7 @@ msgstr "Действительный"
 msgid "View"
 msgstr "Просмотр"
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 msgid "View all notifications"
 msgstr "Просмотреть все уведомления"
@@ -1947,7 +2013,7 @@ msgstr "Простой режим"
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgstr "Внимание"
 
@@ -1980,6 +2046,7 @@ msgstr "Запись сертификата на диск"
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 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/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: 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"
 msgstr "Hành động"
 
@@ -51,7 +52,7 @@ msgstr "Thêm Directive"
 msgid "Add 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"
 msgstr "Thêm Website"
 
@@ -70,11 +71,11 @@ msgstr "Cập nhật thành công"
 msgid "Additional"
 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"
 msgstr "Nâng cao"
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgstr ""
 
@@ -82,11 +83,11 @@ msgstr ""
 msgid "API Document"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgstr ""
 
@@ -94,7 +95,12 @@ msgstr ""
 msgid "Arch"
 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
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
@@ -147,6 +153,15 @@ msgstr "Hỏi ChatGPT"
 msgid "Assistant"
 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:120
 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/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"
 msgstr "Quay lại"
 
@@ -180,19 +195,31 @@ msgstr "Quay lại"
 msgid "Back Home"
 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"
 msgstr "Thông tin"
 
 #: src/views/config/ConfigEdit.vue:115
 #: 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
 #, fuzzy
 msgid "Basic"
 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"
 msgstr "Cơ bản"
 
@@ -280,12 +307,12 @@ msgid "Cleaning environment variables"
 msgstr "Xoá các biến môi trường"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "Xoá"
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
@@ -312,7 +339,7 @@ msgstr "Mẫu Cấu hình"
 msgid "Configuration file is test successful"
 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"
 msgstr "Tên cấu hình"
 
@@ -320,7 +347,7 @@ msgstr "Tên cấu hình"
 msgid "Configurations"
 msgstr "Cấu hình"
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgstr "Cấu hình SSL"
 
@@ -351,7 +378,7 @@ msgstr "CPU:"
 msgid "Create"
 msgstr "Ngày tạo"
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgstr "Tạo thêm"
 
@@ -390,7 +417,7 @@ msgstr ""
 msgid "Dashboard"
 msgstr "Bảng điều khiển"
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: 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"
 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/stream/components/Deploy.vue:106
+#: src/views/stream/components/Deploy.vue:103
 #: src/views/stream/components/RightSettings.vue:92
 msgid "Deploy"
 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"
 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}"
 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/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "Đã tắt"
 
@@ -563,7 +590,7 @@ msgstr "Bạn muốn xóa máy chủ này ?"
 msgid "Domain"
 msgstr ""
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 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"
 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/stream/components/StreamDuplicate.vue:126
+#: src/views/stream/components/StreamDuplicate.vue:122
 #: src/views/stream/StreamList.vue:161
 msgid "Duplicate"
 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
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 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
 msgid "Duplicate failed"
 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
 msgid "Duplicate successfully"
 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
 msgid "Duplicate to local successfully"
 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}"
 msgstr "Sửa %{n}"
 
@@ -638,25 +665,25 @@ msgstr "Sửa trang web"
 msgid "Email"
 msgstr "Email (*)"
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "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/stream/components/Deploy.vue:86
+#: src/views/stream/components/Deploy.vue:83
 #: src/views/stream/StreamList.vue:153
 #, fuzzy
 msgid "Enable"
 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"
 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"
 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}"
 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"
 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
 msgid "Enable successfully"
 msgstr "Đã bật"
@@ -680,19 +707,19 @@ msgstr "Bật TLS"
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgstr "Đã bật"
 
 #: 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/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgstr "Đã bật"
@@ -763,7 +790,7 @@ msgstr "Không thể bật %{msg}"
 msgid "Failed to get certificate information"
 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."
 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"
 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"
 msgstr "Đã hoàn thành"
 
@@ -862,6 +889,12 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 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
 #, fuzzy
 msgid "Import"
@@ -872,6 +905,11 @@ msgstr "Xuất"
 msgid "Import Certificate"
 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
 msgid "Info"
 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"
 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"
 msgstr "Cài đặt"
 
@@ -906,6 +944,10 @@ msgstr ""
 msgid "Invalid"
 msgstr "Hợp lệ"
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
@@ -940,7 +982,7 @@ msgstr "Kiểm tra lần cuối lúc"
 msgid "Leave blank for no change"
 msgstr "Bỏ trống nếu không thay đổi"
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 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/"
 
@@ -997,11 +1039,11 @@ msgstr "Locations"
 msgid "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"
 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"
 msgstr "Đăng nhập thành công"
 
@@ -1009,7 +1051,7 @@ msgstr "Đăng nhập thành công"
 msgid "Logout successful"
 msgstr "Đã đăng xuất"
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgstr ""
 
@@ -1053,6 +1095,10 @@ msgstr "Người dùng"
 msgid "Managed Certificate"
 msgstr ""
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
@@ -1066,7 +1112,7 @@ msgstr "Memory và Storage"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 msgid "Model"
 msgstr "Run Mode"
@@ -1083,7 +1129,7 @@ msgstr "Sửa"
 msgid "Modify Certificate"
 msgstr "Sửa chứng chỉ"
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 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/domain/cert/ChangeCert.vue:17
 #: 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/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: 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
 msgid "Name"
 msgstr "Tên"
@@ -1135,11 +1181,11 @@ msgstr "Đã có phiên bản mới"
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgstr "Tiếp theo"
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgstr ""
 
@@ -1147,7 +1193,7 @@ msgstr ""
 msgid "Nginx Access Log Path"
 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
 msgid "Nginx Configuration Parse Error"
 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"
 
 #: 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:535
 #: 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/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1214,7 +1261,7 @@ msgstr "Ghi chú"
 msgid "Notification"
 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
 msgid "Notifications"
 msgstr "Thông báo"
@@ -1240,7 +1287,7 @@ msgid "Ok"
 msgstr ""
 
 #: 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/StdTable.vue:522
 #: 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"
 msgstr "Trực tuyến"
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgstr ""
 
@@ -1283,13 +1330,13 @@ msgstr "Hệ điều hành"
 msgid "OS:"
 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"
 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"
 msgstr "Ghi đè tập tin đã tồn tại"
 
@@ -1297,11 +1344,11 @@ msgstr "Ghi đè tập tin đã tồn tại"
 msgid "Params"
 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"
 msgstr "Mật khẩu"
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgstr "Mật khẩu (*)"
 
@@ -1368,7 +1415,7 @@ msgstr ""
 msgid "Pre-release"
 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"
 msgstr "Cài đặt"
 
@@ -1466,7 +1513,16 @@ msgstr "Đang tải lại"
 msgid "Reloading 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
 msgid "Removed successfully"
 msgstr "Xoá thành công"
@@ -1525,9 +1581,9 @@ msgstr "Running"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: 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/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgstr "Lưu"
 
@@ -1535,7 +1591,7 @@ msgstr "Lưu"
 msgid "Save 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
 msgid "Save error %{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/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 #, fuzzy
 msgid "Save successfully"
 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/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 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:235
 #: 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
 msgid "Server error"
 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"
 
 #: 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"
 msgstr "Tham số server_name là bắt buộc"
 
@@ -1650,7 +1707,7 @@ msgstr ""
 msgid "SSL Certificate Path"
 msgstr ""
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 msgid "SSO Login"
 msgstr ""
 
@@ -1736,8 +1793,8 @@ msgstr ""
 msgid "System"
 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"
 msgstr "Mục tiêu"
 
@@ -1770,7 +1827,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 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."
@@ -1803,8 +1860,8 @@ msgstr ""
 msgid "The url is invalid"
 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."
 msgstr ""
 
@@ -1830,6 +1887,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgstr "Trường này không được để trống"
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 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 "
 "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"
 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
 msgid "Trash"
 msgstr ""
@@ -1909,11 +1971,15 @@ msgstr ""
 msgid "User"
 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"
 msgstr "Username"
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgstr "Username (*)"
 
@@ -1927,7 +1993,7 @@ msgstr "Hợp lệ"
 msgid "View"
 msgstr "Xem"
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 msgid "View all notifications"
 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/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 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/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 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/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: 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"
 msgstr "操作"
 
@@ -54,7 +55,7 @@ msgstr "在下面添加指令"
 msgid "Add 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"
 msgstr "添加站点"
 
@@ -70,11 +71,11 @@ msgstr "添加成功"
 msgid "Additional"
 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"
 msgstr "高级模式"
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgstr "API 地址"
 
@@ -82,11 +83,11 @@ msgstr "API 地址"
 msgid "API Document"
 msgstr "API 文档"
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgstr "API 代理"
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgstr "API Token"
 
@@ -94,7 +95,11 @@ msgstr "API Token"
 msgid "Arch"
 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
 msgid "Are you sure you want to clear all notifications?"
 msgstr "您确定要清除所有通知吗?"
@@ -139,6 +144,14 @@ msgstr "与ChatGPT聊天"
 msgid "Assistant"
 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:120
 msgid "Author"
@@ -162,8 +175,8 @@ msgstr "成功启用 %{name} 自动续签"
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: 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"
 msgstr "返回"
 
@@ -171,18 +184,30 @@ msgstr "返回"
 msgid "Back Home"
 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"
 msgstr "基本信息"
 
 #: src/views/config/ConfigEdit.vue:115
 #: 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
 msgid "Basic"
 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"
 msgstr "基本模式"
 
@@ -265,12 +290,12 @@ msgid "Cleaning environment variables"
 msgstr "正在清理环境变量"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "清空"
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgstr "清除成功"
@@ -294,7 +319,7 @@ msgstr "配置"
 msgid "Configuration file is test successful"
 msgstr "配置文件测试成功"
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgstr "配置名称"
 
@@ -302,7 +327,7 @@ msgstr "配置名称"
 msgid "Configurations"
 msgstr "配置"
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgstr "配置 SSL"
 
@@ -332,7 +357,7 @@ msgstr "CPU:"
 msgid "Create"
 msgstr "创建"
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgstr "再创建一个"
 
@@ -371,7 +396,7 @@ msgstr "自定义显示在环境指示器中的本地服务器名称。"
 msgid "Dashboard"
 msgstr "仪表盘"
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgstr "数据库 (可选,默认: database)"
 
@@ -403,15 +428,15 @@ msgstr "删除 Stream: %{stream_name}"
 msgid "Deleted successfully"
 msgstr "删除成功"
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: 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
 msgid "Deploy"
 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"
 msgstr "部署%{conf_name}到%{node_name}失败"
 
@@ -458,9 +483,9 @@ msgstr "禁用"
 msgid "Disable auto-renewal failed for %{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/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "禁用"
 
@@ -530,7 +555,7 @@ msgstr "你想删除这个 Upstream 吗?"
 msgid "Domain"
 msgstr "域名"
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgstr "域名配置文件创建成功"
 
@@ -550,34 +575,34 @@ msgstr "下载最新版本"
 msgid "Dry run mode enabled"
 msgstr "试运行模式已启动"
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: 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
 msgid "Duplicate"
 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"
 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"
 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"
 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"
 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}"
 msgstr "编辑 %{n}"
 
@@ -597,24 +622,24 @@ msgstr "编辑 Stream"
 msgid "Email"
 msgstr "邮箱"
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgstr "邮箱 (*)"
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: 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
 msgid "Enable"
 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"
 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"
 msgstr "成功启用%{node_name}中的%{conf_name}"
 
@@ -622,12 +647,12 @@ msgstr "成功启用%{node_name}中的%{conf_name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "启用 %{name} 自动续签失败"
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 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"
 msgstr "启用成功"
 
@@ -637,19 +662,19 @@ msgstr "启用 TLS"
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgstr "启用"
 
 #: 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/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgstr "启用成功"
@@ -716,7 +741,7 @@ msgstr "启用失败 %{msg}"
 msgid "Failed to get certificate information"
 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."
 msgstr "保存失败,在配置中检测到语法错误。"
 
@@ -741,7 +766,7 @@ msgstr "未找到文件"
 msgid "Filter"
 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"
 msgstr "完成"
 
@@ -809,6 +834,14 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 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
 msgid "Import"
 msgstr "导入"
@@ -817,6 +850,10 @@ msgstr "导入"
 msgid "Import Certificate"
 msgstr "导入证书"
 
+#: src/views/other/Login.vue:59
+msgid "Incorrect username or password"
+msgstr "用户名或密码错误"
+
 #: src/constants/index.ts:18
 msgid "Info"
 msgstr "信息"
@@ -829,7 +866,7 @@ msgstr "初始化核心升级程序错误"
 msgid "Initialing core upgrader"
 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"
 msgstr "安装"
 
@@ -849,6 +886,10 @@ msgstr "间隔"
 msgid "Invalid"
 msgstr "无效的"
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr "IP"
+
 #: src/views/certificate/Certificate.vue:144
 msgid "Issue wildcard certificate"
 msgstr "签发通配符证书"
@@ -879,7 +920,7 @@ msgstr "最后检查时间"
 msgid "Leave blank for no change"
 msgstr "留空表示不修改"
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "留空为默认:https://api.openai.com/"
 
@@ -930,11 +971,11 @@ msgstr "Locations"
 msgid "Log"
 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"
 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"
 msgstr "登录成功"
 
@@ -942,7 +983,7 @@ msgstr "登录成功"
 msgid "Logout successful"
 msgstr "登出成功"
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgstr "Logrotate"
 
@@ -988,6 +1029,10 @@ msgstr "用户管理"
 msgid "Managed Certificate"
 msgstr "托管证书"
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr "最大尝试次数"
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
@@ -1001,7 +1046,7 @@ msgstr "内存与存储"
 msgid "Minutes"
 msgstr "分钟"
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 msgid "Model"
 msgstr "模型"
 
@@ -1015,7 +1060,7 @@ msgstr "修改"
 msgid "Modify Certificate"
 msgstr "修改证书"
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgstr "修改配置文件"
 
@@ -1033,12 +1078,12 @@ msgstr "多行指令"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: 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/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: 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
 msgid "Name"
 msgstr "名称"
@@ -1065,11 +1110,11 @@ msgstr "新版本发布"
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgstr "下一步"
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1077,7 +1122,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 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"
 msgstr "Nginx 配置解析错误"
 
@@ -1102,7 +1147,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx 重启成功"
 
 #: 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:535
 #: 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/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1140,7 +1186,7 @@ msgstr "注意"
 msgid "Notification"
 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"
 msgstr "通知"
 
@@ -1164,7 +1210,7 @@ msgid "Ok"
 msgstr "确定"
 
 #: 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/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1194,7 +1240,7 @@ msgstr "一旦验证完成,这些记录将被删除。"
 msgid "Online"
 msgstr "在线"
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1206,13 +1252,13 @@ msgstr "OS"
 msgid "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"
 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"
 msgstr "覆盖现有文件"
 
@@ -1220,11 +1266,11 @@ msgstr "覆盖现有文件"
 msgid "Params"
 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"
 msgstr "密码"
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgstr "密码 (*)"
 
@@ -1289,7 +1335,7 @@ msgstr "请至少选择一个节点!"
 msgid "Pre-release"
 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"
 msgstr "偏好设置"
 
@@ -1380,7 +1426,15 @@ msgstr "重载中"
 msgid "Reloading 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"
 msgstr "删除成功"
 
@@ -1432,9 +1486,9 @@ msgstr "运行中"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: 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/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgstr "保存"
 
@@ -1442,7 +1496,7 @@ msgstr "保存"
 msgid "Save Directive"
 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
 msgid "Save error %{msg}"
 msgstr "保存错误 %{msg}"
@@ -1450,14 +1504,14 @@ msgstr "保存错误 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 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/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgstr "保存成功"
 
@@ -1482,8 +1536,9 @@ msgstr "上传"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: 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
 msgid "Server error"
 msgstr "服务器错误"
@@ -1501,7 +1556,7 @@ msgid "server_name not found in directives"
 msgstr "未在指令集合中找到 server_name"
 
 #: 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"
 msgstr "必须为 server_name 指令指明参数"
 
@@ -1553,7 +1608,7 @@ msgstr "SSL证书密钥路径"
 msgid "SSL Certificate Path"
 msgstr "SSL证书路径"
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 msgid "SSO Login"
 msgstr "SSO 登录"
 
@@ -1633,8 +1688,8 @@ msgstr "同步到"
 msgid "System"
 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"
 msgstr "目标"
 
@@ -1666,7 +1721,7 @@ msgstr "输入的内容不是 SSL 证书"
 msgid "The input is not a SSL Certificate Key"
 msgstr "输入的内容不是 SSL 证书密钥"
 
-#: 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."
@@ -1697,8 +1752,8 @@ msgstr "当前配置中的 server_name 必须是获取证书所需的域名,
 msgid "The url is invalid"
 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."
 msgstr "URL 无效."
 
@@ -1724,6 +1779,7 @@ msgstr "此字段必填"
 msgid "This field should not be empty"
 msgstr "该字段不能为空"
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgstr "提示"
@@ -1742,10 +1798,14 @@ msgstr ""
 "为了确保认证自动更新能够正常工作,我们需要添加一个能够代理从权威机构到后端的"
 "请求的 Location,并且我们需要保存这个文件并重新加载Nginx。你确定要继续吗?"
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 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
 msgid "Trash"
 msgstr "回收站"
@@ -1798,11 +1858,15 @@ msgstr "URL"
 msgid "User"
 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"
 msgstr "用户名"
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgstr "用户名 (*)"
 
@@ -1816,7 +1880,7 @@ msgstr "有效的"
 msgid "View"
 msgstr "查看"
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 msgid "View all notifications"
 msgstr "查看全部通知"
 
@@ -1830,7 +1894,7 @@ msgstr "预览模式"
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgstr "警告"
 
@@ -1862,6 +1926,7 @@ msgstr "正在将证书写入磁盘"
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 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/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
 #: 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"
 msgstr "操作"
 
@@ -56,7 +57,7 @@ msgstr "在下方新增指令"
 msgid "Add 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"
 msgstr "新增網站"
 
@@ -74,11 +75,11 @@ msgstr "更新成功"
 msgid "Additional"
 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"
 msgstr "進階模式"
 
-#: src/views/preference/OpenAISettings.vue:42
+#: src/views/preference/OpenAISettings.vue:45
 msgid "API Base Url"
 msgstr "API 基礎網址"
 
@@ -87,11 +88,11 @@ msgstr "API 基礎網址"
 msgid "API Document"
 msgstr "API Token"
 
-#: src/views/preference/OpenAISettings.vue:54
+#: src/views/preference/OpenAISettings.vue:57
 msgid "API Proxy"
 msgstr "API 代理"
 
-#: src/views/preference/OpenAISettings.vue:66
+#: src/views/preference/OpenAISettings.vue:69
 msgid "API Token"
 msgstr "API Token"
 
@@ -99,7 +100,12 @@ msgstr "API Token"
 msgid "Arch"
 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
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
@@ -149,6 +155,15 @@ msgstr "向 ChatGPT 尋求幫助"
 msgid "Assistant"
 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:120
 msgid "Author"
@@ -172,8 +187,8 @@ msgstr "已啟用 %{name} 的自動續簽"
 
 #: src/views/certificate/CertificateEditor.vue:242
 #: 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"
 msgstr "返回"
 
@@ -181,18 +196,30 @@ msgstr "返回"
 msgid "Back Home"
 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"
 msgstr "基本資訊"
 
 #: src/views/config/ConfigEdit.vue:115
 #: 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
 msgid "Basic"
 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"
 msgstr "基本模式"
 
@@ -278,12 +305,12 @@ msgid "Cleaning environment variables"
 msgstr "清理環境變數"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
-#: src/components/Notification/Notification.vue:89
+#: src/components/Notification/Notification.vue:91
 #: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "清除"
 
-#: src/components/Notification/Notification.vue:40
+#: src/components/Notification/Notification.vue:42
 #: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
@@ -309,7 +336,7 @@ msgstr "設定模板"
 msgid "Configuration file is test successful"
 msgstr "設定檔案測試成功"
 
-#: src/views/domain/DomainAdd.vue:105
+#: src/views/domain/DomainAdd.vue:101
 msgid "Configuration Name"
 msgstr "設定名稱"
 
@@ -317,7 +344,7 @@ msgstr "設定名稱"
 msgid "Configurations"
 msgstr "設定"
 
-#: src/views/domain/DomainAdd.vue:100
+#: src/views/domain/DomainAdd.vue:96
 msgid "Configure SSL"
 msgstr "設定 SSL"
 
@@ -348,7 +375,7 @@ msgstr "中央處理器:"
 msgid "Create"
 msgstr "建立時間"
 
-#: src/views/domain/DomainAdd.vue:158
+#: src/views/domain/DomainAdd.vue:154
 msgid "Create Another"
 msgstr "再建立一個"
 
@@ -387,7 +414,7 @@ msgstr ""
 msgid "Dashboard"
 msgstr "儀表板"
 
-#: src/views/other/Install.vue:121
+#: src/views/other/Install.vue:120
 msgid "Database (Optional, default: database)"
 msgstr "資料庫 (可選,預設: database)"
 
@@ -421,15 +448,15 @@ msgstr "刪除網站:%{site_name}"
 msgid "Deleted successfully"
 msgstr "成功停用"
 
-#: src/views/domain/components/Deploy.vue:106
+#: src/views/domain/components/Deploy.vue:103
 #: 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
 msgid "Deploy"
 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"
 msgstr "部署 %{conf_name} 至 %{node_name} 失敗"
 
@@ -477,9 +504,9 @@ msgstr "停用"
 msgid "Disable auto-renewal failed for %{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/stream/StreamEdit.vue:177 src/views/stream/StreamList.vue:33
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:33
 msgid "Disabled"
 msgstr "停用"
 
@@ -552,7 +579,7 @@ msgstr "您要移除此伺服器嗎?"
 msgid "Domain"
 msgstr "網域"
 
-#: src/views/domain/DomainAdd.vue:148
+#: src/views/domain/DomainAdd.vue:144
 msgid "Domain Config Created Successfully"
 msgstr "網域設定檔成功建立"
 
@@ -573,34 +600,34 @@ msgstr "正在下載最新版本"
 msgid "Dry run mode enabled"
 msgstr "試運轉模式已啟用"
 
-#: src/views/domain/components/SiteDuplicate.vue:126
+#: src/views/domain/components/SiteDuplicate.vue:122
 #: 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
 msgid "Duplicate"
 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"
 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"
 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"
 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"
 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}"
 msgstr "編輯 %{n}"
 
@@ -622,24 +649,24 @@ msgstr "編輯網站"
 msgid "Email"
 msgstr "電子郵件 (*)"
 
-#: src/views/other/Install.vue:90
+#: src/views/other/Install.vue:89
 msgid "Email (*)"
 msgstr "電子郵件 (*)"
 
-#: src/views/domain/components/Deploy.vue:86
+#: src/views/domain/components/Deploy.vue:83
 #: 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
 msgid "Enable"
 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"
 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"
 msgstr "成功在 %{node_name} 啟用 %{conf_name}"
 
@@ -647,12 +674,12 @@ msgstr "成功在 %{node_name} 啟用 %{conf_name}"
 msgid "Enable auto-renewal failed for %{name}"
 msgstr "啟用 %{name} 自動續簽失敗"
 
-#: src/views/domain/DomainAdd.vue:46
+#: src/views/domain/DomainAdd.vue:43
 msgid "Enable failed"
 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"
 msgstr "啟用成功"
 
@@ -662,19 +689,19 @@ msgstr "啟用 TLS"
 
 #: src/views/domain/cert/ChangeCert.vue:40
 #: src/views/domain/components/RightSettings.vue:77
-#: src/views/domain/DomainEdit.vue:179 src/views/domain/DomainList.vue:29
+#: src/views/domain/DomainEdit.vue:177 src/views/domain/DomainList.vue:29
 #: src/views/environment/Environment.vue:102
 #: src/views/preference/LogrotateSettings.vue:20
 #: src/views/stream/components/RightSettings.vue:76
-#: src/views/stream/StreamEdit.vue:171 src/views/stream/StreamList.vue:29
+#: src/views/stream/StreamEdit.vue:169 src/views/stream/StreamList.vue:29
 msgid "Enabled"
 msgstr "已啟用"
 
 #: 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/StreamDuplicate.vue:98
+#: src/views/stream/components/StreamDuplicate.vue:94
 #: src/views/stream/StreamList.vue:57
 msgid "Enabled successfully"
 msgstr "成功啟用"
@@ -744,7 +771,7 @@ msgstr "啟用 %{msg} 失敗"
 msgid "Failed to get certificate information"
 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."
 msgstr "儲存失敗,在設定中檢測到語法錯誤。"
 
@@ -770,7 +797,7 @@ msgstr "找不到檔案"
 msgid "Filter"
 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"
 msgstr "完成"
 
@@ -840,6 +867,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 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
 #, fuzzy
 msgid "Import"
@@ -850,6 +883,11 @@ msgstr "匯出"
 msgid "Import Certificate"
 msgstr "憑證狀態"
 
+#: src/views/other/Login.vue:59
+#, fuzzy
+msgid "Incorrect username or password"
+msgstr "使用者名稱或密碼不正確"
+
 #: src/constants/index.ts:18
 msgid "Info"
 msgstr ""
@@ -862,7 +900,7 @@ msgstr "初始化核心升級程式錯誤"
 msgid "Initialing core upgrader"
 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"
 msgstr "安裝"
 
@@ -883,6 +921,10 @@ msgstr ""
 msgid "Invalid"
 msgstr "無效的郵箱!"
 
+#: src/views/preference/AuthSettings.vue:14
+msgid "IP"
+msgstr ""
+
 #: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
@@ -916,7 +958,7 @@ msgstr "上次檢查時間"
 msgid "Leave blank for no change"
 msgstr "留空表示不修改"
 
-#: src/views/preference/OpenAISettings.vue:50
+#: src/views/preference/OpenAISettings.vue:53
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "預設留空:https://api.openai.com/"
 
@@ -972,11 +1014,11 @@ msgstr "Locations"
 msgid "Log"
 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"
 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"
 msgstr "登入成功"
 
@@ -984,7 +1026,7 @@ msgstr "登入成功"
 msgid "Logout successful"
 msgstr "登出成功"
 
-#: src/views/preference/Preference.vue:119
+#: src/views/preference/Preference.vue:134
 msgid "Logrotate"
 msgstr ""
 
@@ -1027,6 +1069,10 @@ msgstr "管理使用者"
 msgid "Managed Certificate"
 msgstr "更換憑證"
 
+#: src/views/preference/AuthSettings.vue:74
+msgid "Max Attempts"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:217
 #: src/views/dashboard/ServerAnalytic.vue:218
 msgid "Memory"
@@ -1040,7 +1086,7 @@ msgstr "記憶體與儲存"
 msgid "Minutes"
 msgstr ""
 
-#: src/views/preference/OpenAISettings.vue:30
+#: src/views/preference/OpenAISettings.vue:33
 #, fuzzy
 msgid "Model"
 msgstr "執行模式"
@@ -1056,7 +1102,7 @@ msgstr "修改"
 msgid "Modify Certificate"
 msgstr "憑證狀態"
 
-#: src/views/domain/DomainAdd.vue:155
+#: src/views/domain/DomainAdd.vue:151
 msgid "Modify Config"
 msgstr "修改設定"
 
@@ -1075,12 +1121,12 @@ msgstr "多行指令"
 #: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: 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/ngx_conf/NgxUpstream.vue:175
 #: src/views/environment/Environment.vue:12
 #: 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
 msgid "Name"
 msgstr "名稱"
@@ -1107,11 +1153,11 @@ msgstr "新版本發布"
 
 #: src/views/certificate/WildcardCertificate.vue:99
 #: src/views/domain/cert/components/ObtainCert.vue:203
-#: src/views/domain/DomainAdd.vue:142
+#: src/views/domain/DomainAdd.vue:138
 msgid "Next"
 msgstr "下一步"
 
-#: src/views/preference/Preference.vue:107
+#: src/views/preference/Preference.vue:122
 msgid "Nginx"
 msgstr "Nginx"
 
@@ -1119,7 +1165,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 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"
 msgstr "Nginx 設定解析錯誤"
 
@@ -1144,7 +1190,7 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx 重啟成功"
 
 #: 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:535
 #: 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/LocationEditor.vue:71
 #: src/views/notification/Notification.vue:70
+#: src/views/preference/AuthSettings.vue:96
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1183,7 +1230,7 @@ msgstr "備註"
 msgid "Notification"
 msgstr "憑證"
 
-#: src/components/Notification/Notification.vue:80 src/routes/index.ts:221
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221
 #, fuzzy
 msgid "Notifications"
 msgstr "憑證"
@@ -1208,7 +1255,7 @@ msgid "Ok"
 msgstr ""
 
 #: 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/StdTable.vue:522
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
@@ -1238,7 +1285,7 @@ msgstr ""
 msgid "Online"
 msgstr "線上"
 
-#: src/views/preference/Preference.vue:113
+#: src/views/preference/Preference.vue:128
 msgid "OpenAI"
 msgstr "OpenAI"
 
@@ -1250,13 +1297,13 @@ msgstr "作業系統"
 msgid "OS:"
 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"
 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"
 msgstr "覆蓋現有檔案"
 
@@ -1264,11 +1311,11 @@ msgstr "覆蓋現有檔案"
 msgid "Params"
 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"
 msgstr "密碼"
 
-#: src/views/other/Install.vue:110
+#: src/views/other/Install.vue:109
 msgid "Password (*)"
 msgstr "密碼 (*)"
 
@@ -1334,7 +1381,7 @@ msgstr "請至少選擇一個節點!"
 msgid "Pre-release"
 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"
 msgstr "偏好設定"
 
@@ -1431,7 +1478,16 @@ msgstr "重新載入中"
 msgid "Reloading 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
 msgid "Removed successfully"
 msgstr "儲存成功"
@@ -1489,9 +1545,9 @@ msgstr "執行中"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: 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/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
+#: src/views/preference/Preference.vue:145 src/views/stream/StreamEdit.vue:252
 msgid "Save"
 msgstr "儲存"
 
@@ -1499,7 +1555,7 @@ msgstr "儲存"
 msgid "Save Directive"
 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
 msgid "Save error %{msg}"
 msgstr "儲存錯誤 %{msg}"
@@ -1507,14 +1563,14 @@ msgstr "儲存錯誤 %{msg}"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
 #: src/views/certificate/CertificateEditor.vue:46
-#: src/views/preference/Preference.vue:66
+#: src/views/preference/Preference.vue:74
 msgid "Save successfully"
 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/stream/StreamEdit.vue:140
+#: src/views/stream/StreamEdit.vue:138
 msgid "Saved successfully"
 msgstr "儲存成功"
 
@@ -1539,8 +1595,9 @@ msgstr "傳送"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: 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
 msgid "Server error"
 msgstr "伺服器錯誤"
@@ -1559,7 +1616,7 @@ msgid "server_name not found in directives"
 msgstr "在指令中未找到 server_name"
 
 #: 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"
 msgstr "必須提供 server_name 參數"
 
@@ -1615,7 +1672,7 @@ msgstr "SSL 憑證金鑰路徑"
 msgid "SSL Certificate Path"
 msgstr "SSL 憑證路徑"
 
-#: src/views/other/Login.vue:158
+#: src/views/other/Login.vue:170
 #, fuzzy
 msgid "SSO Login"
 msgstr "登入"
@@ -1701,8 +1758,8 @@ msgstr ""
 msgid "System"
 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"
 msgstr "目標"
 
@@ -1736,7 +1793,7 @@ msgstr ""
 msgid "The input is not a SSL Certificate Key"
 msgstr "SSL 憑證金鑰路徑"
 
-#: 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."
@@ -1770,8 +1827,8 @@ msgstr "注意:目前設定中的 server_name 必須為需要申請憑證的
 msgid "The url is invalid"
 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
 msgid "The url is invalid."
 msgstr "此功能在演示中不可用。"
@@ -1799,6 +1856,7 @@ msgstr ""
 msgid "This field should not be empty"
 msgstr "此欄位不應為空"
 
+#: src/views/preference/AuthSettings.vue:59
 #: src/views/preference/LogrotateSettings.vue:12
 msgid "Tips"
 msgstr ""
@@ -1817,10 +1875,14 @@ msgstr ""
 "為了確保憑證自動續期能夠正常運作,我們需要新增一個 Location 來代理從授權後端"
 "的請求,我們需要儲存這個檔案並重新載入 Nginx。你確定你要繼續嗎?"
 
-#: src/views/preference/OpenAISettings.vue:69
+#: src/views/preference/OpenAISettings.vue:72
 msgid "Token is not valid"
 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
 msgid "Trash"
 msgstr ""
@@ -1873,11 +1935,15 @@ msgstr "URL"
 msgid "User"
 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"
 msgstr "使用者名稱"
 
-#: src/views/other/Install.vue:100
+#: src/views/other/Install.vue:99
 msgid "Username (*)"
 msgstr "使用者名稱 (*)"
 
@@ -1891,7 +1957,7 @@ msgstr ""
 msgid "View"
 msgstr "檢視"
 
-#: src/components/Notification/Notification.vue:141
+#: src/components/Notification/Notification.vue:143
 #, fuzzy
 msgid "View all notifications"
 msgstr "憑證"
@@ -1907,7 +1973,7 @@ msgstr "基本模式"
 
 #: src/constants/index.ts:17 src/views/config/InspectConfig.vue:33
 #: src/views/domain/cert/components/AutoCertStepOne.vue:28
-#: src/views/domain/DomainAdd.vue:116
+#: src/views/domain/DomainAdd.vue:112
 msgid "Warning"
 msgstr "警告"
 
@@ -1940,6 +2006,7 @@ msgstr "將憑證寫入磁碟"
 
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 #: src/views/domain/ngx_conf/LocationEditor.vue:70
+#: src/views/preference/AuthSettings.vue:95
 #: src/views/preference/BasicSettings.vue:100
 msgid "Yes"
 msgstr "是的"

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

@@ -33,19 +33,15 @@ function init() {
 
 async function save() {
   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(() => {
       message.success($gettext('Saved successfully'))
 
-      // eslint-disable-next-line promise/no-nesting
       domain.enable(ngx_config.name).then(() => {
         message.success($gettext('Enabled successfully'))
         window.scroll({ top: 0, left: 0, behavior: 'smooth' })
-        // eslint-disable-next-line promise/no-nesting
       }).catch(e => {
         message.error(e.message ?? $gettext('Enable failed'), 5)
       })
-      // eslint-disable-next-line promise/no-nesting
     }).catch(e => {
       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()
     }
     else {
-      // eslint-disable-next-line promise/no-nesting
       return ngx.tokenize_config(configText.value).then(r => {
         Object.assign(ngx_config, r)
-        // eslint-disable-next-line promise/no-nesting
       }).catch(handle_parse_error)
     }
   })

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

@@ -29,7 +29,7 @@ function deploy() {
             name: name.value,
             content: r.config,
             overwrite: overwrite.value,
-            // eslint-disable-next-line promise/no-nesting
+
           }, { headers: { 'X-Node-ID': id } }).then(async () => {
             notification.success({
               message: $gettext('Deploy successfully'),
@@ -38,7 +38,6 @@ function deploy() {
                   { conf_name: name.value, node_name }),
             })
             if (enabled.value) {
-              // eslint-disable-next-line promise/no-nesting
               domain.enable(name.value).then(() => {
                 notification.success({
                   message: $gettext('Enable successfully'),
@@ -46,7 +45,6 @@ function deploy() {
                     $gettext('Enable %{conf_name} in %{node_name} successfully',
                       { conf_name: name.value, node_name }),
                 })
-                // eslint-disable-next-line promise/no-nesting
               }).catch(e => {
                 notification.error({
                   message: $gettext('Enable %{conf_name} in %{node_name} failed', {
@@ -57,7 +55,6 @@ function deploy() {
                 })
               })
             }
-            // eslint-disable-next-line promise/no-nesting
           }).catch(e => {
             notification.error({
               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 => {
       if (id === 0) {
-        // eslint-disable-next-line promise/no-nesting
         domain.duplicate(props.name, { name: modelRef.name }).then(() => {
           message.success($gettext('Duplicate to local successfully'))
           show.value = false
           emit('duplicated')
-          // eslint-disable-next-line promise/no-nesting
         }).catch(e => {
           message.error($gettext(e?.message ?? 'Server error'))
         })
       }
       else {
         // get source content
-        // eslint-disable-next-line promise/no-nesting
+
         domain.get(props.name).then(r => {
           domain.save(modelRef.name, {
             name: modelRef.name,
             content: r.config,
-            // eslint-disable-next-line promise/no-nesting
+
           }, { headers: { 'X-Node-ID': id } }).then(() => {
             notification.success({
               message: $gettext('Duplicate successfully'),
@@ -84,7 +82,6 @@ function onSubmit() {
                 $gettext('Duplicate %{conf_name} to %{node_name} successfully',
                   { conf_name: props.name, node_name: node_map[id] }),
             })
-            // eslint-disable-next-line promise/no-nesting
           }).catch(e => {
             notification.error({
               message: $gettext('Duplicate failed'),
@@ -92,7 +89,6 @@ function onSubmit() {
             })
           })
           if (r.enabled) {
-            // eslint-disable-next-line promise/no-nesting
             domain.enable(modelRef.name, { headers: { 'X-Node-ID': id } }).then(() => {
               notification.success({
                 message: $gettext('Enabled successfully'),

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

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

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

@@ -46,16 +46,28 @@ const { validate, validateInfos, clearValidate } = Form.useForm(modelRef, rulesR
 const onSubmit = () => {
   validate().then(async () => {
     loading.value = true
-    // eslint-disable-next-line promise/no-nesting
+
     await auth.login(modelRef.username, modelRef.password).then(async () => {
       message.success($gettext('Login successful'), 1)
 
       const next = (route.query?.next || '').toString() || '/'
 
       await router.push(next)
-      // eslint-disable-next-line promise/no-nesting
     }).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
   })

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

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

@@ -33,4 +33,9 @@ export interface Settings {
     cmd: string
     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()
     }
     else {
-      // eslint-disable-next-line promise/no-nesting
       return ngx.tokenize_config(configText.value).then(r => {
         Object.assign(ngx_config, r)
-        // eslint-disable-next-line promise/no-nesting
       }).catch(handle_parse_error)
     }
   })

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

@@ -29,7 +29,7 @@ function deploy() {
             name: name.value,
             content: r.config,
             overwrite: overwrite.value,
-            // eslint-disable-next-line promise/no-nesting
+
           }, { headers: { 'X-Node-ID': id } }).then(async () => {
             notification.success({
               message: $gettext('Deploy successfully'),
@@ -38,7 +38,6 @@ function deploy() {
                   { conf_name: name.value, node_name }),
             })
             if (enabled.value) {
-              // eslint-disable-next-line promise/no-nesting
               stream.enable(name.value).then(() => {
                 notification.success({
                   message: $gettext('Enable successfully'),
@@ -46,7 +45,6 @@ function deploy() {
                     $gettext('Enable %{conf_name} in %{node_name} successfully',
                       { conf_name: name.value, node_name }),
                 })
-                // eslint-disable-next-line promise/no-nesting
               }).catch(e => {
                 notification.error({
                   message: $gettext('Enable %{conf_name} in %{node_name} failed', {
@@ -57,7 +55,6 @@ function deploy() {
                 })
               })
             }
-            // eslint-disable-next-line promise/no-nesting
           }).catch(e => {
             notification.error({
               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 => {
       if (id === 0) {
-        // eslint-disable-next-line promise/no-nesting
         stream.duplicate(props.name, { name: modelRef.name }).then(() => {
           message.success($gettext('Duplicate to local successfully'))
           show.value = false
           emit('duplicated')
-          // eslint-disable-next-line promise/no-nesting
         }).catch(e => {
           message.error($gettext(e?.message ?? 'Server error'))
         })
       }
       else {
         // get source content
-        // eslint-disable-next-line promise/no-nesting
+
         stream.get(props.name).then(r => {
           stream.save(modelRef.name, {
             name: modelRef.name,
             content: r.config,
-            // eslint-disable-next-line promise/no-nesting
+
           }, { headers: { 'X-Node-ID': id } }).then(() => {
             notification.success({
               message: $gettext('Duplicate successfully'),
@@ -84,7 +82,6 @@ function onSubmit() {
                 $gettext('Duplicate %{conf_name} to %{node_name} successfully',
                   { conf_name: props.name, node_name: node_map[id] }),
             })
-            // eslint-disable-next-line promise/no-nesting
           }).catch(e => {
             notification.error({
               message: $gettext('Duplicate failed'),
@@ -92,7 +89,6 @@ function onSubmit() {
             })
           })
           if (r.enabled) {
-            // eslint-disable-next-line promise/no-nesting
             stream.enable(modelRef.name, { headers: { 'X-Node-ID': id } }).then(() => {
               notification.success({
                 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]
 IPWhiteList = 10.0.0.1
 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.

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

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

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

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

+ 19 - 4
internal/user/login.go

@@ -1,10 +1,12 @@
 package user
 
 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 (
@@ -30,3 +32,16 @@ func Login(name string, password string) (user *model.Auth, err error) {
 
 	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{},
 		Notification{},
 		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.Name = field.NewString(tableName, "name")
 	_auth.Password = field.NewString(tableName, "password")
+	_auth.Status = field.NewBool(tableName, "status")
 
 	_auth.fillFieldMap()
 
@@ -50,6 +51,7 @@ type auth struct {
 	DeletedAt field.Field
 	Name      field.String
 	Password  field.String
+	Status    field.Bool
 
 	fieldMap map[string]field.Expr
 }
@@ -72,6 +74,7 @@ func (a *auth) updateTableName(table string) *auth {
 	a.DeletedAt = field.NewField(table, "deleted_at")
 	a.Name = field.NewString(table, "name")
 	a.Password = field.NewString(table, "password")
+	a.Status = field.NewBool(table, "status")
 
 	a.fillFieldMap()
 
@@ -88,13 +91,14 @@ func (a *auth) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
 }
 
 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["created_at"] = a.CreatedAt
 	a.fieldMap["updated_at"] = a.UpdatedAt
 	a.fieldMap["deleted_at"] = a.DeletedAt
 	a.fieldMap["name"] = a.Name
 	a.fieldMap["password"] = a.Password
+	a.fieldMap["status"] = a.Status
 }
 
 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
 	Auth          *auth
 	AuthToken     *authToken
+	BanIP         *banIP
 	Cert          *cert
 	ChatGPTLog    *chatGPTLog
 	ConfigBackup  *configBackup
@@ -35,6 +36,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
 	AcmeUser = &Q.AcmeUser
 	Auth = &Q.Auth
 	AuthToken = &Q.AuthToken
+	BanIP = &Q.BanIP
 	Cert = &Q.Cert
 	ChatGPTLog = &Q.ChatGPTLog
 	ConfigBackup = &Q.ConfigBackup
@@ -51,6 +53,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
 		AcmeUser:      newAcmeUser(db, opts...),
 		Auth:          newAuth(db, opts...),
 		AuthToken:     newAuthToken(db, opts...),
+		BanIP:         newBanIP(db, opts...),
 		Cert:          newCert(db, opts...),
 		ChatGPTLog:    newChatGPTLog(db, opts...),
 		ConfigBackup:  newConfigBackup(db, opts...),
@@ -68,6 +71,7 @@ type Query struct {
 	AcmeUser      acmeUser
 	Auth          auth
 	AuthToken     authToken
+	BanIP         banIP
 	Cert          cert
 	ChatGPTLog    chatGPTLog
 	ConfigBackup  configBackup
@@ -86,6 +90,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
 		AcmeUser:      q.AcmeUser.clone(db),
 		Auth:          q.Auth.clone(db),
 		AuthToken:     q.AuthToken.clone(db),
+		BanIP:         q.BanIP.clone(db),
 		Cert:          q.Cert.clone(db),
 		ChatGPTLog:    q.ChatGPTLog.clone(db),
 		ConfigBackup:  q.ConfigBackup.clone(db),
@@ -111,6 +116,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
 		AcmeUser:      q.AcmeUser.replaceDB(db),
 		Auth:          q.Auth.replaceDB(db),
 		AuthToken:     q.AuthToken.replaceDB(db),
+		BanIP:         q.BanIP.replaceDB(db),
 		Cert:          q.Cert.replaceDB(db),
 		ChatGPTLog:    q.ChatGPTLog.replaceDB(db),
 		ConfigBackup:  q.ConfigBackup.replaceDB(db),
@@ -126,6 +132,7 @@ type queryCtx struct {
 	AcmeUser      *acmeUserDo
 	Auth          *authDo
 	AuthToken     *authTokenDo
+	BanIP         *banIPDo
 	Cert          *certDo
 	ChatGPTLog    *chatGPTLogDo
 	ConfigBackup  *configBackupDo
@@ -141,6 +148,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
 		AcmeUser:      q.AcmeUser.WithContext(ctx),
 		Auth:          q.Auth.WithContext(ctx),
 		AuthToken:     q.AuthToken.WithContext(ctx),
+		BanIP:         q.BanIP.WithContext(ctx),
 		Cert:          q.Cert.WithContext(ctx),
 		ChatGPTLog:    q.ChatGPTLog.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/notification"
 	"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/streams"
 	"github.com/0xJacky/Nginx-UI/api/system"
@@ -25,7 +26,7 @@ func InitRouter() *gin.Engine {
 	r.Use(gin.Logger())
 	r.Use(recovery())
 	r.Use(cacheJs())
-    r.Use(ipWhiteList())
+	r.Use(ipWhiteList())
 
 	//r.Use(OperationSync())
 
@@ -56,6 +57,7 @@ func InitRouter() *gin.Engine {
 			certificate.InitDNSCredentialRouter(g)
 			certificate.InitAcmeUserRouter(g)
 			system.InitPrivateRouter(g)
+			settings.InitRouter(g)
 			openai.InitRouter(g)
 			cluster.InitRouter(g)
 			notification.InitRouter(g)

+ 5 - 2
settings/auth.go

@@ -1,9 +1,12 @@
 package settings
 
 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"`
 	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"
 	}
 
+	if AuthSettings.BanThresholdMinutes <= 0 {
+		AuthSettings.BanThresholdMinutes = 10
+	}
+
+	if AuthSettings.MaxAttempts <= 0 {
+		AuthSettings.MaxAttempts = 10
+	}
+
 	err = Save()
 	if err != nil {
 		log.Fatalf("settings.Setup: %v\n", err)

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