diff --git a/.idea/vcs.xml b/.idea/vcs.xml index c8e2b47f9..35eb1ddfb 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/api/api.go b/api/api.go index c27460bf8..89c59005b 100644 --- a/api/api.go +++ b/api/api.go @@ -3,6 +3,7 @@ package api import ( "errors" "github.com/0xJacky/Nginx-UI/internal/logger" + "github.com/0xJacky/Nginx-UI/model" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "net/http" @@ -11,6 +12,10 @@ import ( "strings" ) +func CurrentUser(c *gin.Context) *model.Auth { + return c.MustGet("user").(*model.Auth) +} + func ErrHandler(c *gin.Context, err error) { logger.GetLogger().Errorln(err) c.JSON(http.StatusInternalServerError, gin.H{ diff --git a/api/user/auth.go b/api/user/auth.go index 82feef408..5f750c081 100644 --- a/api/user/auth.go +++ b/api/user/auth.go @@ -16,14 +16,19 @@ import ( var mutex = &sync.Mutex{} type LoginUser struct { - Name string `json:"name" binding:"required,max=255"` - Password string `json:"password" binding:"required,max=255"` + Name string `json:"name" binding:"required,max=255"` + Password string `json:"password" binding:"required,max=255"` + OTP string `json:"otp"` + RecoveryCode string `json:"recovery_code"` } const ( ErrPasswordIncorrect = 4031 ErrMaxAttempts = 4291 ErrUserBanned = 4033 + Enabled2FA = 199 + Error2FACode = 4034 + LoginSuccess = 200 ) type LoginResponse struct { @@ -80,11 +85,32 @@ func Login(c *gin.Context) { return } + // Check if the user enables 2FA + if u.EnabledOTP() { + if json.OTP == "" && json.RecoveryCode == "" { + c.JSON(http.StatusOK, LoginResponse{ + Message: "The user has enabled 2FA", + Code: Enabled2FA, + }) + user.BanIP(clientIP) + return + } + + if err = user.VerifyOTP(u, json.OTP, json.RecoveryCode); err != nil { + c.JSON(http.StatusForbidden, LoginResponse{ + Message: "Invalid 2FA or recovery code", + Code: Error2FACode, + }) + 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) + token, err := user.GenerateJWT(u) if err != nil { c.JSON(http.StatusInternalServerError, LoginResponse{ Message: err.Error(), @@ -93,6 +119,7 @@ func Login(c *gin.Context) { } c.JSON(http.StatusOK, LoginResponse{ + Code: LoginSuccess, Message: "ok", Token: token, }) @@ -101,13 +128,7 @@ func Login(c *gin.Context) { func Logout(c *gin.Context) { token := c.GetHeader("Authorization") if token != "" { - err := user.DeleteToken(token) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{ - "message": err.Error(), - }) - return - } + user.DeleteToken(token) } c.JSON(http.StatusNoContent, nil) } diff --git a/api/user/casdoor.go b/api/user/casdoor.go index 3fb105063..d8e210cb9 100644 --- a/api/user/casdoor.go +++ b/api/user/casdoor.go @@ -65,7 +65,7 @@ func CasdoorCallback(c *gin.Context) { return } - userToken, err := user.GenerateJWT(u.Name) + userToken, err := user.GenerateJWT(u) if err != nil { api.ErrHandler(c, err) return diff --git a/api/user/otp.go b/api/user/otp.go new file mode 100644 index 000000000..e5eb000d0 --- /dev/null +++ b/api/user/otp.go @@ -0,0 +1,208 @@ +package user + +import ( + "bytes" + "crypto/sha1" + "encoding/base64" + "encoding/hex" + "fmt" + "github.com/0xJacky/Nginx-UI/api" + "github.com/0xJacky/Nginx-UI/internal/crypto" + "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/pquerna/otp" + "github.com/pquerna/otp/totp" + "image/jpeg" + "net/http" + "strings" +) + +func GenerateTOTP(c *gin.Context) { + u := api.CurrentUser(c) + + issuer := fmt.Sprintf("Nginx UI %s", settings.ServerSettings.Name) + issuer = strings.TrimSpace(issuer) + + otpOpts := totp.GenerateOpts{ + Issuer: issuer, + AccountName: u.Name, + Period: 30, // seconds + Digits: otp.DigitsSix, + Algorithm: otp.AlgorithmSHA1, + } + otpKey, err := totp.Generate(otpOpts) + if err != nil { + api.ErrHandler(c, err) + return + } + ciphertext, err := crypto.AesEncrypt([]byte(otpKey.Secret())) + if err != nil { + api.ErrHandler(c, err) + return + } + + qrCode, err := otpKey.Image(512, 512) + if err != nil { + api.ErrHandler(c, err) + return + } + + // Encode the image to a buffer + var buf []byte + buffer := bytes.NewBuffer(buf) + err = jpeg.Encode(buffer, qrCode, nil) + if err != nil { + fmt.Println("Error encoding image:", err) + return + } + + // Convert the buffer to a base64 string + base64Str := "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(buffer.Bytes()) + + c.JSON(http.StatusOK, gin.H{ + "secret": base64.StdEncoding.EncodeToString(ciphertext), + "qr_code": base64Str, + }) +} + +func EnrollTOTP(c *gin.Context) { + cUser := api.CurrentUser(c) + if cUser.EnabledOTP() { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "User already enrolled", + }) + return + } + + if settings.ServerSettings.Demo { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "This feature is disabled in demo mode", + }) + return + } + + var json struct { + Secret string `json:"secret" binding:"required"` + Passcode string `json:"passcode" binding:"required"` + } + if !api.BindAndValid(c, &json) { + return + } + + secret, err := base64.StdEncoding.DecodeString(json.Secret) + if err != nil { + api.ErrHandler(c, err) + return + } + + decrypted, err := crypto.AesDecrypt(secret) + if err != nil { + api.ErrHandler(c, err) + return + } + + if ok := totp.Validate(json.Passcode, string(decrypted)); !ok { + c.JSON(http.StatusNotAcceptable, gin.H{ + "message": "Invalid passcode", + }) + return + } + + ciphertext, err := crypto.AesEncrypt(decrypted) + if err != nil { + api.ErrHandler(c, err) + return + } + + u := query.Auth + _, err = u.Where(u.ID.Eq(cUser.ID)).Update(u.OTPSecret, ciphertext) + if err != nil { + api.ErrHandler(c, err) + return + } + + recoveryCode := sha1.Sum(ciphertext) + + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + "recovery_code": hex.EncodeToString(recoveryCode[:]), + }) +} + +func ResetOTP(c *gin.Context) { + var json struct { + RecoveryCode string `json:"recovery_code"` + } + if !api.BindAndValid(c, &json) { + return + } + recoverCode, err := hex.DecodeString(json.RecoveryCode) + if err != nil { + api.ErrHandler(c, err) + return + } + cUser := api.CurrentUser(c) + k := sha1.Sum(cUser.OTPSecret) + if !bytes.Equal(k[:], recoverCode) { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "Invalid recovery code", + }) + return + } + + u := query.Auth + _, err = u.Where(u.ID.Eq(cUser.ID)).UpdateSimple(u.OTPSecret.Null()) + if err != nil { + api.ErrHandler(c, err) + return + } + + c.JSON(http.StatusOK, gin.H{ + "message": "ok", + }) +} + +func OTPStatus(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "status": len(api.CurrentUser(c).OTPSecret) > 0, + }) +} + +func StartSecure2FASession(c *gin.Context) { + var json struct { + OTP string `json:"otp"` + RecoveryCode string `json:"recovery_code"` + } + if !api.BindAndValid(c, &json) { + return + } + u := api.CurrentUser(c) + if !u.EnabledOTP() { + c.JSON(http.StatusBadRequest, gin.H{ + "message": "User not configured with 2FA", + }) + return + } + + if json.OTP == "" && json.RecoveryCode == "" { + c.JSON(http.StatusBadRequest, LoginResponse{ + Message: "The user has enabled 2FA", + }) + return + } + + if err := user.VerifyOTP(u, json.OTP, json.RecoveryCode); err != nil { + c.JSON(http.StatusBadRequest, LoginResponse{ + Message: "Invalid 2FA or recovery code", + }) + return + } + + sessionId := user.SetSecureSessionID(u.ID) + + c.JSON(http.StatusOK, gin.H{ + "session_id": sessionId, + }) +} diff --git a/api/user/router.go b/api/user/router.go index ee50f7029..f4b8c3543 100644 --- a/api/user/router.go +++ b/api/user/router.go @@ -2,18 +2,26 @@ package user import "github.com/gin-gonic/gin" -func InitAuthRouter(r *gin.RouterGroup) { - r.POST("/login", Login) - r.DELETE("/logout", Logout) +func InitAuthRouter(r *gin.RouterGroup) { + r.POST("/login", Login) + r.DELETE("/logout", Logout) - r.GET("/casdoor_uri", GetCasdoorUri) - r.POST("/casdoor_callback", CasdoorCallback) + r.GET("/casdoor_uri", GetCasdoorUri) + r.POST("/casdoor_callback", CasdoorCallback) } func InitManageUserRouter(r *gin.RouterGroup) { - r.GET("users", GetUsers) - r.GET("user/:id", GetUser) - r.POST("user", AddUser) - r.POST("user/:id", EditUser) - r.DELETE("user/:id", DeleteUser) + r.GET("users", GetUsers) + r.GET("user/:id", GetUser) + r.POST("user", AddUser) + r.POST("user/:id", EditUser) + r.DELETE("user/:id", DeleteUser) +} + +func InitUserRouter(r *gin.RouterGroup) { + r.GET("/otp_status", OTPStatus) + r.GET("/otp_secret", GenerateTOTP) + r.POST("/otp_enroll", EnrollTOTP) + r.POST("/otp_reset", ResetOTP) + r.POST("/otp_secure_session", StartSecure2FASession) } diff --git a/app.example.ini b/app.example.ini index ca687a996..ec86357f9 100644 --- a/app.example.ini +++ b/app.example.ini @@ -51,3 +51,11 @@ Interval = 1440 Node = http://10.0.0.1:9000?name=node1&node_secret=my-node-secret&enabled=true Node = http://10.0.0.2:9000?name=node2&node_secret=my-node-secret&enabled=true Node = http://10.0.0.3?name=node3&node_secret=my-node-secret&enabled=true + +[auth] +IPWhiteList = +BanThresholdMinutes = 10 +MaxAttempts = 10 + +[crypto] +Secret = secret2 diff --git a/app/components.d.ts b/app/components.d.ts index 2d3e57bd3..a4430ce22 100644 --- a/app/components.d.ts +++ b/app/components.d.ts @@ -78,6 +78,11 @@ declare module 'vue' { NginxControlNginxControl: typeof import('./src/components/NginxControl/NginxControl.vue')['default'] NodeSelectorNodeSelector: typeof import('./src/components/NodeSelector/NodeSelector.vue')['default'] NotificationNotification: typeof import('./src/components/Notification/Notification.vue')['default'] + OTP: typeof import('./src/components/OTP.vue')['default'] + OTPInput: typeof import('./src/components/OTPInput.vue')['default'] + OTPInputOTPInput: typeof import('./src/components/OTPInput/OTPInput.vue')['default'] + OTPOTPAuthorization: typeof import('./src/components/OTP/OTPAuthorization.vue')['default'] + OTPOTPAuthorizationModal: typeof import('./src/components/OTP/OTPAuthorizationModal.vue')['default'] PageHeaderPageHeader: typeof import('./src/components/PageHeader/PageHeader.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] diff --git a/app/package.json b/app/package.json index a0806938c..88eaaf426 100644 --- a/app/package.json +++ b/app/package.json @@ -16,6 +16,7 @@ "@vue/reactivity": "^3.4.33", "@vue/shared": "^3.4.33", "@vueuse/core": "^10.11.0", + "@vueuse/integrations": "^10.11.0", "@xterm/addon-attach": "^0.11.0", "@xterm/addon-fit": "^0.10.0", "@xterm/xterm": "^5.5.0", @@ -31,6 +32,7 @@ "pinia-plugin-persistedstate": "^3.2.1", "reconnecting-websocket": "^4.4.0", "sortablejs": "^1.15.2", + "universal-cookie": "^7", "vite-plugin-build-id": "^0.2.9", "vue": "^3.4.33", "vue-github-button": "github:0xJacky/vue-github-button", @@ -38,6 +40,7 @@ "vue3-ace-editor": "2.2.4", "vue3-apexcharts": "1.4.4", "vue3-gettext": "3.0.0-beta.4", + "vue3-otp-input": "^0.5.21", "vuedraggable": "^4.1.0" }, "devDependencies": { diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index 865f2fa92..204d2df66 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@vueuse/core': specifier: ^10.11.0 version: 10.11.0(vue@3.4.33(typescript@5.3.3)) + '@vueuse/integrations': + specifier: ^10.11.0 + version: 10.11.0(async-validator@4.2.5)(axios@1.7.2)(nprogress@0.2.0)(sortablejs@1.15.2)(universal-cookie@7.2.0)(vue@3.4.33(typescript@5.3.3)) '@xterm/addon-attach': specifier: ^0.11.0 version: 0.11.0(@xterm/xterm@5.5.0) @@ -68,6 +71,9 @@ importers: sortablejs: specifier: ^1.15.2 version: 1.15.2 + universal-cookie: + specifier: ^7 + version: 7.2.0 vite-plugin-build-id: specifier: ^0.2.9 version: 0.2.9(less@4.2.0) @@ -89,6 +95,9 @@ importers: vue3-gettext: specifier: 3.0.0-beta.4 version: 3.0.0-beta.4(@vue/compiler-sfc@3.4.33)(typescript@5.3.3)(vue@3.4.33(typescript@5.3.3)) + vue3-otp-input: + specifier: ^0.5.21 + version: 0.5.21(vue@3.4.33(typescript@5.3.3)) vuedraggable: specifier: ^4.1.0 version: 4.1.0(vue@3.4.33(typescript@5.3.3)) @@ -695,6 +704,9 @@ packages: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -922,6 +934,47 @@ packages: '@vueuse/core@10.11.0': resolution: {integrity: sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==} + '@vueuse/integrations@10.11.0': + resolution: {integrity: sha512-Pp6MtWEIr+NDOccWd8j59Kpjy5YDXogXI61Kb1JxvSfVBO8NzFQkmrKmSZz47i+ZqHnIzxaT38L358yDHTncZg==} + peerDependencies: + async-validator: ^4 + axios: ^1 + change-case: ^4 + drauu: ^0.3 + focus-trap: ^7 + fuse.js: ^6 + idb-keyval: ^6 + jwt-decode: ^3 + nprogress: ^0.2 + qrcode: ^1.5 + sortablejs: ^1 + universal-cookie: ^6 + peerDependenciesMeta: + async-validator: + optional: true + axios: + optional: true + change-case: + optional: true + drauu: + optional: true + focus-trap: + optional: true + fuse.js: + optional: true + idb-keyval: + optional: true + jwt-decode: + optional: true + nprogress: + optional: true + qrcode: + optional: true + sortablejs: + optional: true + universal-cookie: + optional: true + '@vueuse/metadata@10.11.0': resolution: {integrity: sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==} @@ -1197,6 +1250,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + copy-anything@2.0.6: resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} @@ -2824,6 +2881,9 @@ packages: unist-util-stringify-position@2.0.3: resolution: {integrity: sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==} + universal-cookie@7.2.0: + resolution: {integrity: sha512-PvcyflJAYACJKr28HABxkGemML5vafHmiL4ICe3e+BEKXRMt0GaFLZhAwgv637kFFnnfiSJ8e6jknrKkMrU+PQ==} + unplugin-auto-import@0.17.8: resolution: {integrity: sha512-CHryj6HzJ+n4ASjzwHruD8arhbdl+UXvhuAIlHDs15Y/IMecG3wrf7FVg4pVH/DIysbq/n0phIjNHAjl7TG7Iw==} engines: {node: '>=14'} @@ -2969,6 +3029,11 @@ packages: '@vue/compiler-sfc': '>=3.0.0' vue: '>=3.0.0' + vue3-otp-input@0.5.21: + resolution: {integrity: sha512-dRxmGJqXlU+U5dCijNCyY7ird49+pyfeQspSTqvIp2Xs+VByIluNlTOjgHrftzSdeVZggtx+Ojb8uKiRLaob4Q==} + peerDependencies: + vue: ^3.0.* + vue@3.4.33: resolution: {integrity: sha512-VdMCWQOummbhctl4QFMcW6eNtXHsFyDlX60O/tsSQuCcuDOnJ1qPOhhVla65Niece7xq/P2zyZReIO5mP+LGTQ==} peerDependencies: @@ -3572,6 +3637,8 @@ snapshots: '@trysound/sax@0.2.0': {} + '@types/cookie@0.6.0': {} + '@types/estree@1.0.5': {} '@types/glob@7.2.0': @@ -3890,6 +3957,21 @@ snapshots: - '@vue/composition-api' - vue + '@vueuse/integrations@10.11.0(async-validator@4.2.5)(axios@1.7.2)(nprogress@0.2.0)(sortablejs@1.15.2)(universal-cookie@7.2.0)(vue@3.4.33(typescript@5.3.3))': + dependencies: + '@vueuse/core': 10.11.0(vue@3.4.33(typescript@5.3.3)) + '@vueuse/shared': 10.11.0(vue@3.4.33(typescript@5.3.3)) + vue-demi: 0.14.8(vue@3.4.33(typescript@5.3.3)) + optionalDependencies: + async-validator: 4.2.5 + axios: 1.7.2 + nprogress: 0.2.0 + sortablejs: 1.15.2 + universal-cookie: 7.2.0 + transitivePeerDependencies: + - '@vue/composition-api' + - vue + '@vueuse/metadata@10.11.0': {} '@vueuse/shared@10.11.0(vue@3.4.33(typescript@5.3.3))': @@ -4201,6 +4283,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@0.6.0: {} + copy-anything@2.0.6: dependencies: is-what: 3.14.1 @@ -6009,6 +6093,11 @@ snapshots: dependencies: '@types/unist': 2.0.10 + universal-cookie@7.2.0: + dependencies: + '@types/cookie': 0.6.0 + cookie: 0.6.0 + unplugin-auto-import@0.17.8(@vueuse/core@10.11.0(vue@3.4.33(typescript@5.3.3)))(rollup@4.19.0): dependencies: '@antfu/utils': 0.7.10 @@ -6175,6 +6264,10 @@ snapshots: transitivePeerDependencies: - typescript + vue3-otp-input@0.5.21(vue@3.4.33(typescript@5.3.3)): + dependencies: + vue: 3.4.33(typescript@5.3.3) + vue@3.4.33(typescript@5.3.3): dependencies: '@vue/compiler-dom': 3.4.33 diff --git a/app/src/api/auth.ts b/app/src/api/auth.ts index 7541a9653..7381ed94d 100644 --- a/app/src/api/auth.ts +++ b/app/src/api/auth.ts @@ -6,15 +6,16 @@ const { login, logout } = useUserStore() export interface AuthResponse { message: string token: string + code: number } const auth = { - async login(name: string, password: string) { + async login(name: string, password: string, otp: string, recoveryCode: string): Promise { return http.post('/login', { name, password, - }).then((r: AuthResponse) => { - login(r.token) + otp, + recovery_code: recoveryCode, }) }, async casdoor_login(code?: string, state?: string) { diff --git a/app/src/api/otp.ts b/app/src/api/otp.ts new file mode 100644 index 000000000..aacd02895 --- /dev/null +++ b/app/src/api/otp.ts @@ -0,0 +1,29 @@ +import http from '@/lib/http' + +export interface OTPGenerateSecretResponse { + secret: string + qr_code: string +} + +const otp = { + status(): Promise<{ status: boolean }> { + return http.get('/otp_status') + }, + generate_secret(): Promise { + return http.get('/otp_secret') + }, + enroll_otp(secret: string, passcode: string): Promise<{ recovery_code: string }> { + return http.post('/otp_enroll', { secret, passcode }) + }, + reset(recovery_code: string) { + return http.post('/otp_reset', { recovery_code }) + }, + start_secure_session(passcode: string, recovery_code: string): Promise<{ session_id: string }> { + return http.post('/otp_secure_session', { + otp: passcode, + recovery_code, + }) + }, +} + +export default otp diff --git a/app/src/components/OTP/OTPAuthorization.vue b/app/src/components/OTP/OTPAuthorization.vue new file mode 100644 index 000000000..840ebf994 --- /dev/null +++ b/app/src/components/OTP/OTPAuthorization.vue @@ -0,0 +1,78 @@ + + + + + diff --git a/app/src/components/OTP/useOTPModal.ts b/app/src/components/OTP/useOTPModal.ts new file mode 100644 index 000000000..9e6e8cf3d --- /dev/null +++ b/app/src/components/OTP/useOTPModal.ts @@ -0,0 +1,92 @@ +import { createVNode, render } from 'vue' +import { Modal, message } from 'ant-design-vue' +import { useCookies } from '@vueuse/integrations/useCookies' +import OTPAuthorization from '@/components/OTP/OTPAuthorization.vue' +import otp from '@/api/otp' + +export interface OTPModalProps { + onOk?: (secureSessionId: string) => void + onCancel?: () => void +} + +const useOTPModal = () => { + const refOTPAuthorization = ref() + const randomId = Math.random().toString(36).substring(2, 8) + + const injectStyles = () => { + const style = document.createElement('style') + + style.innerHTML = ` + .${randomId} .ant-modal-title { + font-size: 1.125rem; + } + ` + document.head.appendChild(style) + } + + const open = async ({ onOk, onCancel }: OTPModalProps) => { + const { status } = await otp.status() + if (!status) { + onOk?.('') + + return + } + + const cookies = useCookies(['nginx-ui-2fa']) + const ssid = cookies.get('secure_session_id') + if (ssid) { + onOk?.(ssid) + + return + } + + injectStyles() + let container: HTMLDivElement | null = document.createElement('div') + document.body.appendChild(container) + + const close = () => { + render(null, container!) + document.body.removeChild(container!) + container = null + } + + const verify = (passcode: string, recovery: string) => { + otp.start_secure_session(passcode, recovery).then(r => { + cookies.set('secure_session_id', r.session_id, { maxAge: 60 * 3 }) + onOk?.(r.session_id) + close() + }).catch(async () => { + refOTPAuthorization.value?.clearInput() + await message.error($gettext('Invalid passcode or recovery code')) + }) + } + + const vnode = createVNode(Modal, { + open: true, + title: $gettext('Two-factor authentication required'), + centered: true, + maskClosable: false, + class: randomId, + footer: false, + onCancel: () => { + close() + onCancel?.() + }, + }, { + default: () => h( + OTPAuthorization, + { + ref: refOTPAuthorization, + class: 'mt-3', + onOnSubmit: verify, + }, + ), + }) + + render(vnode, container) + } + + return { open } +} + +export default useOTPModal diff --git a/app/src/components/OTPInput/OTPInput.vue b/app/src/components/OTPInput/OTPInput.vue new file mode 100644 index 000000000..39fc6947e --- /dev/null +++ b/app/src/components/OTPInput/OTPInput.vue @@ -0,0 +1,72 @@ + + + + + + + diff --git a/app/src/language/LINGUAS b/app/src/language/LINGUAS index 052f2613d..1dc98da20 100644 --- a/app/src/language/LINGUAS +++ b/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 \ No newline at end of file diff --git a/app/src/language/en/app.po b/app/src/language/en/app.po index 895a69355..8ade9448c 100644 --- a/app/src/language/en/app.po +++ b/app/src/language/en/app.po @@ -9,7 +9,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/routes/index.ts:256 +#: src/views/preference/components/TOTP.vue:88 +msgid "2FA Settings" +msgstr "" + +#: src/routes/index.ts:261 msgid "About" msgstr "About" @@ -28,7 +32,7 @@ msgstr "Username" #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34 #: src/views/domain/DomainList.vue:47 src/views/environment/envColumns.tsx:131 #: src/views/notification/Notification.vue:37 -#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47 +#: src/views/preference/AuthSettings.vue:27 src/views/stream/StreamList.vue:47 #: src/views/user/User.vue:43 msgid "Action" msgstr "Action" @@ -95,7 +99,7 @@ msgstr "" msgid "Arch" msgstr "" -#: src/views/preference/AuthSettings.vue:94 +#: src/views/preference/AuthSettings.vue:95 #, fuzzy msgid "Are you sure to delete this banned IP immediately?" msgstr "Are you sure you want to remove this directive?" @@ -153,7 +157,7 @@ msgstr "" msgid "Assistant" msgstr "" -#: src/views/preference/AuthSettings.vue:17 +#: src/views/preference/AuthSettings.vue:18 msgid "Attempts" msgstr "" @@ -198,15 +202,15 @@ msgstr "Back" msgid "Back to list" msgstr "" -#: src/views/preference/AuthSettings.vue:68 +#: src/views/preference/AuthSettings.vue:69 msgid "Ban Threshold Minutes" msgstr "" -#: src/views/preference/AuthSettings.vue:82 +#: src/views/preference/AuthSettings.vue:83 msgid "Banned IPs" msgstr "" -#: src/views/preference/AuthSettings.vue:20 +#: src/views/preference/AuthSettings.vue:21 msgid "Banned Until" msgstr "" @@ -362,6 +366,10 @@ msgstr "Configure SSL" msgid "Connected" msgstr "" +#: src/views/pty/Terminal.vue:120 +msgid "Connection lost, please refresh the page." +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111 #: src/views/domain/ngx_conf/LocationEditor.vue:125 #: src/views/domain/ngx_conf/LocationEditor.vue:97 @@ -405,6 +413,14 @@ msgstr "" msgid "Credentials" msgstr "" +#: src/views/preference/components/TOTP.vue:96 +msgid "Current account is enabled 2FA." +msgstr "" + +#: src/views/preference/components/TOTP.vue:93 +msgid "Current account is not enabled 2FA." +msgstr "" + #: src/views/system/Upgrade.vue:167 msgid "Current Version" msgstr "" @@ -692,6 +708,16 @@ msgstr "" msgid "Enable %{conf_name} in %{node_name} successfully" msgstr "" +#: src/views/preference/components/TOTP.vue:122 +#, fuzzy +msgid "Enable 2FA" +msgstr "Enabled" + +#: src/views/preference/components/TOTP.vue:52 +#, fuzzy +msgid "Enable 2FA successfully" +msgstr "Enabled successfully" + #: src/views/domain/cert/components/ObtainCert.vue:70 msgid "Enable auto-renewal failed for %{name}" msgstr "Enable auto-renewal failed for %{name}" @@ -893,12 +919,18 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "" -#: src/views/preference/AuthSettings.vue:60 +#: src/views/preference/AuthSettings.vue:61 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/preference/components/TOTP.vue:108 +msgid "" +"If you lose your mobile phone, you can use the recovery code to reset your " +"2FA." +msgstr "" + #: src/views/certificate/Certificate.vue:136 msgid "Import" msgstr "" @@ -908,7 +940,7 @@ msgstr "" msgid "Import Certificate" msgstr "Certificate Status" -#: src/views/other/Login.vue:59 +#: src/views/other/Login.vue:71 msgid "Incorrect username or password" msgstr "" @@ -924,7 +956,16 @@ msgstr "" msgid "Initialing core upgrader" msgstr "" -#: src/routes/index.ts:273 src/views/other/Install.vue:135 +#: src/views/preference/components/TOTP.vue:144 +msgid "Input the code from the app:" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:49 +#: src/views/preference/components/TOTP.vue:157 +msgid "Input the recovery code:" +msgstr "" + +#: src/routes/index.ts:283 src/views/other/Install.vue:135 msgid "Install" msgstr "Install" @@ -946,7 +987,15 @@ msgstr "" msgid "Invalid" msgstr "Invalid E-mail!" -#: src/views/preference/AuthSettings.vue:14 +#: src/views/other/Login.vue:81 +msgid "Invalid 2FA or recovery code" +msgstr "" + +#: src/components/OTP/useOTPModal.ts:43 +msgid "Invalid passcode or recovery code" +msgstr "" + +#: src/views/preference/AuthSettings.vue:15 msgid "IP" msgstr "" @@ -1040,11 +1089,11 @@ msgstr "Locations" msgid "Log" msgstr "Login" -#: src/routes/index.ts:279 src/views/other/Login.vue:159 +#: src/routes/index.ts:289 src/views/other/Login.vue:192 msgid "Login" msgstr "Login" -#: src/views/other/Login.vue:109 src/views/other/Login.vue:51 +#: src/views/other/Login.vue:125 src/views/other/Login.vue:60 msgid "Login successful" msgstr "Login successful" @@ -1088,7 +1137,7 @@ msgstr "Manage Sites" msgid "Manage Streams" msgstr "Manage Sites" -#: src/routes/index.ts:230 src/views/user/User.vue:50 +#: src/routes/index.ts:235 src/views/user/User.vue:50 msgid "Manage Users" msgstr "Manage Users" @@ -1097,7 +1146,7 @@ msgstr "Manage Users" msgid "Managed Certificate" msgstr "Certificate is valid" -#: src/views/preference/AuthSettings.vue:74 +#: src/views/preference/AuthSettings.vue:75 msgid "Max Attempts" msgstr "" @@ -1231,7 +1280,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/AuthSettings.vue:97 #: src/views/preference/BasicSettings.vue:101 #: src/views/stream/StreamList.vue:165 msgid "No" @@ -1245,7 +1294,7 @@ msgstr "" msgid "Not After" msgstr "" -#: src/routes/index.ts:285 +#: src/routes/index.ts:295 msgid "Not Found" msgstr "Not Found" @@ -1263,7 +1312,7 @@ msgstr "" msgid "Notification" msgstr "Certificate is valid" -#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221 +#: src/components/Notification/Notification.vue:82 src/routes/index.ts:226 #, fuzzy msgid "Notifications" msgstr "Certificate is valid" @@ -1346,7 +1395,7 @@ msgstr "" msgid "Params" msgstr "Params" -#: src/views/other/Login.vue:144 src/views/user/User.vue:18 +#: src/views/other/Login.vue:169 src/views/user/User.vue:18 msgid "Password" msgstr "Password" @@ -1372,6 +1421,10 @@ msgstr "" msgid "Performing core upgrade" msgstr "" +#: src/components/OTP/OTPAuthorization.vue:37 +msgid "Please enter the 2FA code:" +msgstr "" + #: src/views/certificate/DNSCredential.vue:53 msgid "" "Please fill in the API authentication credentials provided by your DNS " @@ -1399,11 +1452,11 @@ msgstr "" msgid "Please input your E-mail!" msgstr "Please input your E-mail!" -#: src/views/other/Install.vue:45 src/views/other/Login.vue:39 +#: src/views/other/Install.vue:45 src/views/other/Login.vue:43 msgid "Please input your password!" msgstr "Please input your password!" -#: src/views/other/Install.vue:39 src/views/other/Login.vue:33 +#: src/views/other/Install.vue:39 src/views/other/Login.vue:37 msgid "Please input your username!" msgstr "Please input your username!" @@ -1423,7 +1476,7 @@ msgstr "" msgid "Pre-release" msgstr "" -#: src/routes/index.ts:239 src/views/preference/Preference.vue:105 +#: src/routes/index.ts:244 src/views/preference/Preference.vue:105 msgid "Preference" msgstr "" @@ -1467,6 +1520,19 @@ msgstr "" msgid "Recovered Successfully" msgstr "Saved successfully" +#: src/components/OTP/OTPAuthorization.vue:56 +#: src/views/preference/components/TOTP.vue:167 +msgid "Recovery" +msgstr "" + +#: src/views/preference/components/TOTP.vue:101 +msgid "Recovery Code" +msgstr "" + +#: src/views/preference/components/TOTP.vue:110 +msgid "Recovery Code:" +msgstr "" + #: src/views/preference/BasicSettings.vue:68 msgid "Recursive Nameservers" msgstr "" @@ -1519,11 +1585,11 @@ msgstr "" msgid "Reloading nginx" msgstr "" -#: src/views/preference/AuthSettings.vue:101 +#: src/views/preference/AuthSettings.vue:102 msgid "Remove" msgstr "" -#: src/views/preference/AuthSettings.vue:47 +#: src/views/preference/AuthSettings.vue:48 #, fuzzy msgid "Remove successfully" msgstr "Saved successfully" @@ -1568,6 +1634,10 @@ msgstr "" msgid "Reset" msgstr "" +#: src/views/preference/components/TOTP.vue:130 +msgid "Reset 2FA" +msgstr "" + #: src/components/NginxControl/NginxControl.vue:93 msgid "Restart" msgstr "" @@ -1617,6 +1687,10 @@ msgstr "Saved successfully" msgid "Saved successfully" msgstr "Saved successfully" +#: src/views/preference/components/TOTP.vue:91 +msgid "Scan the QR code with your mobile phone to add the account to the app." +msgstr "" + #: src/views/certificate/DNSChallenge.vue:89 msgid "SDK" msgstr "" @@ -1640,7 +1714,9 @@ msgstr "Send" #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81 #: src/views/environment/BatchUpgrader.vue:57 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:69 -#: src/views/preference/AuthSettings.vue:49 +#: src/views/preference/AuthSettings.vue:50 +#: src/views/preference/components/TOTP.vue:42 +#: src/views/preference/components/TOTP.vue:55 #: 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" @@ -1717,7 +1793,7 @@ msgstr "Certificate Status" msgid "SSL Certificate Path" msgstr "Certificate Status" -#: src/views/other/Login.vue:170 +#: src/views/other/Login.vue:203 #, fuzzy msgid "SSO Login" msgstr "Login" @@ -1802,7 +1878,7 @@ msgstr "Certificate is valid" msgid "Sync to" msgstr "" -#: src/routes/index.ts:248 +#: src/routes/index.ts:253 msgid "System" msgstr "" @@ -1811,7 +1887,7 @@ msgstr "" msgid "Target" msgstr "" -#: src/routes/index.ts:172 src/views/pty/Terminal.vue:91 +#: src/routes/index.ts:172 src/views/pty/Terminal.vue:114 msgid "Terminal" msgstr "Terminal" @@ -1856,6 +1932,11 @@ msgstr "Certificate Status" msgid "The path exists, but the file is not a private key" msgstr "" +#: src/views/preference/components/TOTP.vue:109 +msgid "" +"The recovery code is only displayed once, please save it in a safe place." +msgstr "" + #: src/views/dashboard/Environments.vue:148 msgid "" "The remote Nginx UI version is not compatible with the local Nginx UI " @@ -1915,7 +1996,7 @@ msgid "" "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}." msgstr "" -#: src/views/preference/AuthSettings.vue:59 +#: src/views/preference/AuthSettings.vue:60 #: src/views/preference/LogrotateSettings.vue:12 msgid "Tips" msgstr "" @@ -1924,6 +2005,12 @@ msgstr "" msgid "Title" msgstr "" +#: src/views/preference/components/TOTP.vue:90 +msgid "" +"To enable it, you need to install the Google or Microsoft Authenticator app " +"on your mobile phone." +msgstr "" + #: src/views/domain/ngx_conf/NgxConfigEditor.vue:44 msgid "" "To make sure the certification auto-renewal can work normally, we need to " @@ -1936,14 +2023,24 @@ msgstr "" msgid "Token is not valid" msgstr "" -#: src/views/other/Login.vue:62 +#: src/views/other/Login.vue:74 msgid "Too many login failed attempts, please try again later" msgstr "" +#: src/views/preference/components/TOTP.vue:89 +msgid "" +"TOTP is a two-factor authentication method that uses a time-based one-time " +"password algorithm." +msgstr "" + #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:209 msgid "Trash" msgstr "" +#: src/components/OTP/useOTPModal.ts:49 +msgid "Two-factor authentication required" +msgstr "" + #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12 #: src/views/notification/Notification.vue:13 msgid "Type" @@ -1964,7 +2061,7 @@ msgstr "Updated at" msgid "Updated successfully" msgstr "Saved successfully" -#: src/routes/index.ts:263 src/views/environment/Environment.vue:50 +#: src/routes/index.ts:268 src/views/environment/Environment.vue:50 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228 msgid "Upgrade" msgstr "" @@ -1995,16 +2092,24 @@ msgstr "Uptime:" msgid "URL" msgstr "" +#: src/components/OTP/OTPAuthorization.vue:69 +msgid "Use OTP" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:65 +msgid "Use recovery code" +msgstr "" + #: src/components/ChatGPT/ChatGPT.vue:229 #, fuzzy msgid "User" msgstr "Username" -#: src/views/other/Login.vue:65 +#: src/views/other/Login.vue:77 msgid "User is banned" msgstr "" -#: src/views/other/Login.vue:134 src/views/user/User.vue:9 +#: src/views/other/Login.vue:159 src/views/user/User.vue:9 msgid "Username" msgstr "Username" @@ -2073,7 +2178,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/AuthSettings.vue:96 #: src/views/preference/BasicSettings.vue:100 msgid "Yes" msgstr "Yes" diff --git a/app/src/language/es/app.po b/app/src/language/es/app.po index 2fd7b9a27..19cc26e38 100644 --- a/app/src/language/es/app.po +++ b/app/src/language/es/app.po @@ -14,7 +14,11 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 5.3.1\n" -#: src/routes/index.ts:256 +#: src/views/preference/components/TOTP.vue:88 +msgid "2FA Settings" +msgstr "" + +#: src/routes/index.ts:261 msgid "About" msgstr "Acerca de" @@ -33,7 +37,7 @@ msgstr "Usuario" #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34 #: src/views/domain/DomainList.vue:47 src/views/environment/envColumns.tsx:131 #: src/views/notification/Notification.vue:37 -#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47 +#: src/views/preference/AuthSettings.vue:27 src/views/stream/StreamList.vue:47 #: src/views/user/User.vue:43 msgid "Action" msgstr "Acción" @@ -98,7 +102,7 @@ msgstr "Token de la API" msgid "Arch" msgstr "Arquitectura" -#: src/views/preference/AuthSettings.vue:94 +#: src/views/preference/AuthSettings.vue:95 #, fuzzy msgid "Are you sure to delete this banned IP immediately?" msgstr "¿Está seguro de que quiere borrar?" @@ -152,7 +156,7 @@ msgstr "Preguntar por ayuda a ChatGPT" msgid "Assistant" msgstr "Asistente" -#: src/views/preference/AuthSettings.vue:17 +#: src/views/preference/AuthSettings.vue:18 msgid "Attempts" msgstr "" @@ -197,15 +201,15 @@ msgstr "Volver al Inicio" msgid "Back to list" msgstr "" -#: src/views/preference/AuthSettings.vue:68 +#: src/views/preference/AuthSettings.vue:69 msgid "Ban Threshold Minutes" msgstr "" -#: src/views/preference/AuthSettings.vue:82 +#: src/views/preference/AuthSettings.vue:83 msgid "Banned IPs" msgstr "" -#: src/views/preference/AuthSettings.vue:20 +#: src/views/preference/AuthSettings.vue:21 msgid "Banned Until" msgstr "" @@ -355,6 +359,10 @@ msgstr "Configurar SSL" msgid "Connected" msgstr "Conectado" +#: src/views/pty/Terminal.vue:120 +msgid "Connection lost, please refresh the page." +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111 #: src/views/domain/ngx_conf/LocationEditor.vue:125 #: src/views/domain/ngx_conf/LocationEditor.vue:97 @@ -397,6 +405,14 @@ msgstr "Credencial" msgid "Credentials" msgstr "Credenciales" +#: src/views/preference/components/TOTP.vue:96 +msgid "Current account is enabled 2FA." +msgstr "" + +#: src/views/preference/components/TOTP.vue:93 +msgid "Current account is not enabled 2FA." +msgstr "" + #: src/views/system/Upgrade.vue:167 msgid "Current Version" msgstr "Versión actual" @@ -668,6 +684,16 @@ msgstr "Falló el habilitado de %{conf_name} en %{node_name}" msgid "Enable %{conf_name} in %{node_name} successfully" msgstr "Habilitado exitoso de %{conf_name} en %{node_name}" +#: src/views/preference/components/TOTP.vue:122 +#, fuzzy +msgid "Enable 2FA" +msgstr "Habilitar" + +#: src/views/preference/components/TOTP.vue:52 +#, fuzzy +msgid "Enable 2FA successfully" +msgstr "Habilitado con Éxito" + #: src/views/domain/cert/components/ObtainCert.vue:70 msgid "Enable auto-renewal failed for %{name}" msgstr "No se pudo activar la renovación automática por %{name}" @@ -863,12 +889,18 @@ msgstr "HTTP01" msgid "If left blank, the default CA Dir will be used." msgstr "" -#: src/views/preference/AuthSettings.vue:60 +#: src/views/preference/AuthSettings.vue:61 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/preference/components/TOTP.vue:108 +msgid "" +"If you lose your mobile phone, you can use the recovery code to reset your " +"2FA." +msgstr "" + #: src/views/certificate/Certificate.vue:136 msgid "Import" msgstr "Importar" @@ -877,7 +909,7 @@ msgstr "Importar" msgid "Import Certificate" msgstr "Importar Certificado" -#: src/views/other/Login.vue:59 +#: src/views/other/Login.vue:71 #, fuzzy msgid "Incorrect username or password" msgstr "El nombre de usuario o contraseña son incorrectos" @@ -894,7 +926,16 @@ 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:135 +#: src/views/preference/components/TOTP.vue:144 +msgid "Input the code from the app:" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:49 +#: src/views/preference/components/TOTP.vue:157 +msgid "Input the recovery code:" +msgstr "" + +#: src/routes/index.ts:283 src/views/other/Install.vue:135 msgid "Install" msgstr "Instalar" @@ -915,7 +956,15 @@ msgstr "" msgid "Invalid" msgstr "Válido" -#: src/views/preference/AuthSettings.vue:14 +#: src/views/other/Login.vue:81 +msgid "Invalid 2FA or recovery code" +msgstr "" + +#: src/components/OTP/useOTPModal.ts:43 +msgid "Invalid passcode or recovery code" +msgstr "" + +#: src/views/preference/AuthSettings.vue:15 msgid "IP" msgstr "" @@ -1002,11 +1051,11 @@ msgstr "Ubicaciones" msgid "Log" msgstr "Registro" -#: src/routes/index.ts:279 src/views/other/Login.vue:159 +#: src/routes/index.ts:289 src/views/other/Login.vue:192 msgid "Login" msgstr "Acceso" -#: src/views/other/Login.vue:109 src/views/other/Login.vue:51 +#: src/views/other/Login.vue:125 src/views/other/Login.vue:60 msgid "Login successful" msgstr "Acceso exitoso" @@ -1048,7 +1097,7 @@ msgstr "Administrar sitios" msgid "Manage Streams" msgstr "Administrar Transmisiones" -#: src/routes/index.ts:230 src/views/user/User.vue:50 +#: src/routes/index.ts:235 src/views/user/User.vue:50 msgid "Manage Users" msgstr "Administrar usuarios" @@ -1056,7 +1105,7 @@ msgstr "Administrar usuarios" msgid "Managed Certificate" msgstr "Certificado Administrado" -#: src/views/preference/AuthSettings.vue:74 +#: src/views/preference/AuthSettings.vue:75 msgid "Max Attempts" msgstr "" @@ -1184,7 +1233,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/AuthSettings.vue:97 #: src/views/preference/BasicSettings.vue:101 #: src/views/stream/StreamList.vue:165 msgid "No" @@ -1198,7 +1247,7 @@ msgstr "Secreto del nodo" msgid "Not After" msgstr "No después de" -#: src/routes/index.ts:285 +#: src/routes/index.ts:295 msgid "Not Found" msgstr "No encontrado" @@ -1215,7 +1264,7 @@ msgstr "Nota" msgid "Notification" msgstr "Notificación" -#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221 +#: src/components/Notification/Notification.vue:82 src/routes/index.ts:226 msgid "Notifications" msgstr "Notificaciones" @@ -1295,7 +1344,7 @@ msgstr "Sobrescribir archivo existente" msgid "Params" msgstr "Parámetros" -#: src/views/other/Login.vue:144 src/views/user/User.vue:18 +#: src/views/other/Login.vue:169 src/views/user/User.vue:18 msgid "Password" msgstr "Contraseña" @@ -1321,6 +1370,10 @@ msgstr "Error al ejecutar la actualización del kernel" msgid "Performing core upgrade" msgstr "Realizando la actualizaciónd el kernel" +#: src/components/OTP/OTPAuthorization.vue:37 +msgid "Please enter the 2FA code:" +msgstr "" + #: src/views/certificate/DNSCredential.vue:53 msgid "" "Please fill in the API authentication credentials provided by your DNS " @@ -1355,11 +1408,11 @@ msgstr "" msgid "Please input your E-mail!" msgstr "¡Por favor ingrese su correo electrónico!" -#: src/views/other/Install.vue:45 src/views/other/Login.vue:39 +#: src/views/other/Install.vue:45 src/views/other/Login.vue:43 msgid "Please input your password!" msgstr "¡Por favor ingrese su contraseña!" -#: src/views/other/Install.vue:39 src/views/other/Login.vue:33 +#: src/views/other/Install.vue:39 src/views/other/Login.vue:37 msgid "Please input your username!" msgstr "¡Por favor ingrese su nombre de usuario!" @@ -1381,7 +1434,7 @@ msgstr "¡Seleccione al menos un nodo!" msgid "Pre-release" msgstr "Prelanzamiento" -#: src/routes/index.ts:239 src/views/preference/Preference.vue:105 +#: src/routes/index.ts:244 src/views/preference/Preference.vue:105 msgid "Preference" msgstr "Configuración" @@ -1424,6 +1477,19 @@ msgstr "" msgid "Recovered Successfully" msgstr "Eliminado con éxito" +#: src/components/OTP/OTPAuthorization.vue:56 +#: src/views/preference/components/TOTP.vue:167 +msgid "Recovery" +msgstr "" + +#: src/views/preference/components/TOTP.vue:101 +msgid "Recovery Code" +msgstr "" + +#: src/views/preference/components/TOTP.vue:110 +msgid "Recovery Code:" +msgstr "" + #: src/views/preference/BasicSettings.vue:68 msgid "Recursive Nameservers" msgstr "" @@ -1477,11 +1543,11 @@ msgstr "Recargando" msgid "Reloading nginx" msgstr "Recargando Nginx" -#: src/views/preference/AuthSettings.vue:101 +#: src/views/preference/AuthSettings.vue:102 msgid "Remove" msgstr "" -#: src/views/preference/AuthSettings.vue:47 +#: src/views/preference/AuthSettings.vue:48 #, fuzzy msgid "Remove successfully" msgstr "Eliminado con éxito" @@ -1520,6 +1586,11 @@ msgstr "Pedido con parámetros incorrectos" msgid "Reset" msgstr "Limpiar" +#: src/views/preference/components/TOTP.vue:130 +#, fuzzy +msgid "Reset 2FA" +msgstr "Limpiar" + #: src/components/NginxControl/NginxControl.vue:93 msgid "Restart" msgstr "Reiniciar" @@ -1567,6 +1638,10 @@ msgstr "Guardado con éxito" msgid "Saved successfully" msgstr "Guardado con éxito" +#: src/views/preference/components/TOTP.vue:91 +msgid "Scan the QR code with your mobile phone to add the account to the app." +msgstr "" + #: src/views/certificate/DNSChallenge.vue:89 msgid "SDK" msgstr "" @@ -1590,7 +1665,9 @@ msgstr "Enviado" #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81 #: src/views/environment/BatchUpgrader.vue:57 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:69 -#: src/views/preference/AuthSettings.vue:49 +#: src/views/preference/AuthSettings.vue:50 +#: src/views/preference/components/TOTP.vue:42 +#: src/views/preference/components/TOTP.vue:55 #: 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" @@ -1662,7 +1739,7 @@ msgstr "Ruta de la llave del certificado SSL" msgid "SSL Certificate Path" msgstr "Ruta del certificado SSL" -#: src/views/other/Login.vue:170 +#: src/views/other/Login.vue:203 msgid "SSO Login" msgstr "Acceso SSO" @@ -1744,7 +1821,7 @@ msgstr "Renovado de Certificado exitoso" msgid "Sync to" msgstr "" -#: src/routes/index.ts:248 +#: src/routes/index.ts:253 msgid "System" msgstr "Sistema" @@ -1753,7 +1830,7 @@ msgstr "Sistema" msgid "Target" msgstr "Objetivo" -#: src/routes/index.ts:172 src/views/pty/Terminal.vue:91 +#: src/routes/index.ts:172 src/views/pty/Terminal.vue:114 msgid "Terminal" msgstr "Terminal" @@ -1797,6 +1874,11 @@ msgstr "La ruta existe, pero el archivo no es una clave privada" msgid "The path exists, but the file is not a private key" msgstr "La ruta existe, pero el archivo no es una clave privada" +#: src/views/preference/components/TOTP.vue:109 +msgid "" +"The recovery code is only displayed once, please save it in a safe place." +msgstr "" + #: src/views/dashboard/Environments.vue:148 msgid "" "The remote Nginx UI version is not compatible with the local Nginx UI " @@ -1857,7 +1939,7 @@ msgid "" "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}." msgstr "" -#: src/views/preference/AuthSettings.vue:59 +#: src/views/preference/AuthSettings.vue:60 #: src/views/preference/LogrotateSettings.vue:12 msgid "Tips" msgstr "" @@ -1866,6 +1948,12 @@ msgstr "" msgid "Title" msgstr "Título" +#: src/views/preference/components/TOTP.vue:90 +msgid "" +"To enable it, you need to install the Google or Microsoft Authenticator app " +"on your mobile phone." +msgstr "" + #: src/views/domain/ngx_conf/NgxConfigEditor.vue:44 msgid "" "To make sure the certification auto-renewal can work normally, we need to " @@ -1882,14 +1970,24 @@ msgstr "" msgid "Token is not valid" msgstr "El token no es válido" -#: src/views/other/Login.vue:62 +#: src/views/other/Login.vue:74 msgid "Too many login failed attempts, please try again later" msgstr "" +#: src/views/preference/components/TOTP.vue:89 +msgid "" +"TOTP is a two-factor authentication method that uses a time-based one-time " +"password algorithm." +msgstr "" + #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:209 msgid "Trash" msgstr "" +#: src/components/OTP/useOTPModal.ts:49 +msgid "Two-factor authentication required" +msgstr "" + #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12 #: src/views/notification/Notification.vue:13 msgid "Type" @@ -1909,7 +2007,7 @@ msgstr "Actualizado a" msgid "Updated successfully" msgstr "Actualización exitosa" -#: src/routes/index.ts:263 src/views/environment/Environment.vue:50 +#: src/routes/index.ts:268 src/views/environment/Environment.vue:50 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228 msgid "Upgrade" msgstr "Actualizar" @@ -1939,15 +2037,23 @@ msgstr "Tiempo encendido:" msgid "URL" msgstr "URL" +#: src/components/OTP/OTPAuthorization.vue:69 +msgid "Use OTP" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:65 +msgid "Use recovery code" +msgstr "" + #: src/components/ChatGPT/ChatGPT.vue:229 msgid "User" msgstr "Usuario" -#: src/views/other/Login.vue:65 +#: src/views/other/Login.vue:77 msgid "User is banned" msgstr "" -#: src/views/other/Login.vue:134 src/views/user/User.vue:9 +#: src/views/other/Login.vue:159 src/views/user/User.vue:9 msgid "Username" msgstr "Nombre de usuario" @@ -2021,7 +2127,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/AuthSettings.vue:96 #: src/views/preference/BasicSettings.vue:100 msgid "Yes" msgstr "Si" diff --git a/app/src/language/fr_FR/app.po b/app/src/language/fr_FR/app.po index 33933d39a..9cb3d7dc8 100644 --- a/app/src/language/fr_FR/app.po +++ b/app/src/language/fr_FR/app.po @@ -11,7 +11,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 3.3\n" -#: src/routes/index.ts:256 +#: src/views/preference/components/TOTP.vue:88 +msgid "2FA Settings" +msgstr "" + +#: src/routes/index.ts:261 msgid "About" msgstr "À propos" @@ -30,7 +34,7 @@ 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/envColumns.tsx:131 #: src/views/notification/Notification.vue:37 -#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47 +#: src/views/preference/AuthSettings.vue:27 src/views/stream/StreamList.vue:47 #: src/views/user/User.vue:43 msgid "Action" msgstr "Action" @@ -99,7 +103,7 @@ msgstr "Jeton d'API" msgid "Arch" msgstr "Arch" -#: src/views/preference/AuthSettings.vue:94 +#: src/views/preference/AuthSettings.vue:95 #, fuzzy msgid "Are you sure to delete this banned IP immediately?" msgstr "Etes-vous sûr que vous voulez supprimer ?" @@ -155,7 +159,7 @@ msgstr "Modèle ChatGPT" msgid "Assistant" msgstr "" -#: src/views/preference/AuthSettings.vue:17 +#: src/views/preference/AuthSettings.vue:18 msgid "Attempts" msgstr "" @@ -200,15 +204,15 @@ msgstr "Retour au menu principal" msgid "Back to list" msgstr "" -#: src/views/preference/AuthSettings.vue:68 +#: src/views/preference/AuthSettings.vue:69 msgid "Ban Threshold Minutes" msgstr "" -#: src/views/preference/AuthSettings.vue:82 +#: src/views/preference/AuthSettings.vue:83 msgid "Banned IPs" msgstr "" -#: src/views/preference/AuthSettings.vue:20 +#: src/views/preference/AuthSettings.vue:21 msgid "Banned Until" msgstr "" @@ -362,6 +366,10 @@ msgstr "Configurer SSL" msgid "Connected" msgstr "" +#: src/views/pty/Terminal.vue:120 +msgid "Connection lost, please refresh the page." +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111 #: src/views/domain/ngx_conf/LocationEditor.vue:125 #: src/views/domain/ngx_conf/LocationEditor.vue:97 @@ -405,6 +413,14 @@ msgstr "Identifiant" msgid "Credentials" msgstr "Identifiants" +#: src/views/preference/components/TOTP.vue:96 +msgid "Current account is enabled 2FA." +msgstr "" + +#: src/views/preference/components/TOTP.vue:93 +msgid "Current account is not enabled 2FA." +msgstr "" + #: src/views/system/Upgrade.vue:167 msgid "Current Version" msgstr "Version actuelle" @@ -692,6 +708,16 @@ msgstr "" msgid "Enable %{conf_name} in %{node_name} successfully" msgstr "" +#: src/views/preference/components/TOTP.vue:122 +#, fuzzy +msgid "Enable 2FA" +msgstr "Activé" + +#: src/views/preference/components/TOTP.vue:52 +#, fuzzy +msgid "Enable 2FA successfully" +msgstr "Activé avec succès" + #: src/views/domain/cert/components/ObtainCert.vue:70 msgid "Enable auto-renewal failed for %{name}" msgstr "Échec de l'activation du renouvellement automatique pour %{name}" @@ -894,12 +920,18 @@ msgstr "HTTP01" msgid "If left blank, the default CA Dir will be used." msgstr "" -#: src/views/preference/AuthSettings.vue:60 +#: src/views/preference/AuthSettings.vue:61 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/preference/components/TOTP.vue:108 +msgid "" +"If you lose your mobile phone, you can use the recovery code to reset your " +"2FA." +msgstr "" + #: src/views/certificate/Certificate.vue:136 #, fuzzy msgid "Import" @@ -910,7 +942,7 @@ msgstr "Exporter" msgid "Import Certificate" msgstr "État du certificat" -#: src/views/other/Login.vue:59 +#: src/views/other/Login.vue:71 #, fuzzy msgid "Incorrect username or password" msgstr "Le pseudo ou mot de passe est incorect" @@ -927,7 +959,16 @@ 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:135 +#: src/views/preference/components/TOTP.vue:144 +msgid "Input the code from the app:" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:49 +#: src/views/preference/components/TOTP.vue:157 +msgid "Input the recovery code:" +msgstr "" + +#: src/routes/index.ts:283 src/views/other/Install.vue:135 msgid "Install" msgstr "Installer" @@ -947,7 +988,15 @@ msgstr "" msgid "Invalid" msgstr "" -#: src/views/preference/AuthSettings.vue:14 +#: src/views/other/Login.vue:81 +msgid "Invalid 2FA or recovery code" +msgstr "" + +#: src/components/OTP/useOTPModal.ts:43 +msgid "Invalid passcode or recovery code" +msgstr "" + +#: src/views/preference/AuthSettings.vue:15 msgid "IP" msgstr "" @@ -1043,11 +1092,11 @@ msgstr "Localisations" msgid "Log" msgstr "Connexion" -#: src/routes/index.ts:279 src/views/other/Login.vue:159 +#: src/routes/index.ts:289 src/views/other/Login.vue:192 msgid "Login" msgstr "Connexion" -#: src/views/other/Login.vue:109 src/views/other/Login.vue:51 +#: src/views/other/Login.vue:125 src/views/other/Login.vue:60 msgid "Login successful" msgstr "Connexion réussie" @@ -1091,7 +1140,7 @@ msgstr "Gérer les sites" msgid "Manage Streams" msgstr "Gérer les sites" -#: src/routes/index.ts:230 src/views/user/User.vue:50 +#: src/routes/index.ts:235 src/views/user/User.vue:50 msgid "Manage Users" msgstr "Gérer les utilisateurs" @@ -1100,7 +1149,7 @@ msgstr "Gérer les utilisateurs" msgid "Managed Certificate" msgstr "Changer de certificat" -#: src/views/preference/AuthSettings.vue:74 +#: src/views/preference/AuthSettings.vue:75 msgid "Max Attempts" msgstr "" @@ -1230,7 +1279,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/AuthSettings.vue:97 #: src/views/preference/BasicSettings.vue:101 #: src/views/stream/StreamList.vue:165 msgid "No" @@ -1245,7 +1294,7 @@ msgstr "Secret Jwt" msgid "Not After" msgstr "" -#: src/routes/index.ts:285 +#: src/routes/index.ts:295 msgid "Not Found" msgstr "Introuvable" @@ -1263,7 +1312,7 @@ msgstr "Note" msgid "Notification" msgstr "Certification" -#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221 +#: src/components/Notification/Notification.vue:82 src/routes/index.ts:226 #, fuzzy msgid "Notifications" msgstr "Certification" @@ -1344,7 +1393,7 @@ msgstr "" msgid "Params" msgstr "Paramètres" -#: src/views/other/Login.vue:144 src/views/user/User.vue:18 +#: src/views/other/Login.vue:169 src/views/user/User.vue:18 msgid "Password" msgstr "Mot de passe" @@ -1370,6 +1419,10 @@ msgstr "Erreur lors de la mise a niveau du core" msgid "Performing core upgrade" msgstr "Exécution de la mise à niveau du core" +#: src/components/OTP/OTPAuthorization.vue:37 +msgid "Please enter the 2FA code:" +msgstr "" + #: src/views/certificate/DNSCredential.vue:53 msgid "" "Please fill in the API authentication credentials provided by your DNS " @@ -1404,11 +1457,11 @@ msgstr "" msgid "Please input your E-mail!" msgstr "Veuillez saisir votre e-mail !" -#: src/views/other/Install.vue:45 src/views/other/Login.vue:39 +#: src/views/other/Install.vue:45 src/views/other/Login.vue:43 msgid "Please input your password!" msgstr "Veuillez saisir votre mot de passe !" -#: src/views/other/Install.vue:39 src/views/other/Login.vue:33 +#: src/views/other/Install.vue:39 src/views/other/Login.vue:37 msgid "Please input your username!" msgstr "Veuillez saisir votre nom d'utilisateur !" @@ -1428,7 +1481,7 @@ msgstr "" msgid "Pre-release" msgstr "" -#: src/routes/index.ts:239 src/views/preference/Preference.vue:105 +#: src/routes/index.ts:244 src/views/preference/Preference.vue:105 msgid "Preference" msgstr "Préférence" @@ -1474,6 +1527,19 @@ msgstr "" msgid "Recovered Successfully" msgstr "Enregistré avec succès" +#: src/components/OTP/OTPAuthorization.vue:56 +#: src/views/preference/components/TOTP.vue:167 +msgid "Recovery" +msgstr "" + +#: src/views/preference/components/TOTP.vue:101 +msgid "Recovery Code" +msgstr "" + +#: src/views/preference/components/TOTP.vue:110 +msgid "Recovery Code:" +msgstr "" + #: src/views/preference/BasicSettings.vue:68 msgid "Recursive Nameservers" msgstr "" @@ -1527,11 +1593,11 @@ msgstr "Rechargement" msgid "Reloading nginx" msgstr "Rechargement de nginx" -#: src/views/preference/AuthSettings.vue:101 +#: src/views/preference/AuthSettings.vue:102 msgid "Remove" msgstr "" -#: src/views/preference/AuthSettings.vue:47 +#: src/views/preference/AuthSettings.vue:48 #, fuzzy msgid "Remove successfully" msgstr "Enregistré avec succès" @@ -1576,6 +1642,11 @@ msgstr "" msgid "Reset" msgstr "Réinitialiser" +#: src/views/preference/components/TOTP.vue:130 +#, fuzzy +msgid "Reset 2FA" +msgstr "Réinitialiser" + #: src/components/NginxControl/NginxControl.vue:93 msgid "Restart" msgstr "Redémarrer" @@ -1623,6 +1694,10 @@ msgstr "Sauvegarde réussie" msgid "Saved successfully" msgstr "Enregistré avec succès" +#: src/views/preference/components/TOTP.vue:91 +msgid "Scan the QR code with your mobile phone to add the account to the app." +msgstr "" + #: src/views/certificate/DNSChallenge.vue:89 msgid "SDK" msgstr "" @@ -1646,7 +1721,9 @@ msgstr "Envoyer" #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81 #: src/views/environment/BatchUpgrader.vue:57 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:69 -#: src/views/preference/AuthSettings.vue:49 +#: src/views/preference/AuthSettings.vue:50 +#: src/views/preference/components/TOTP.vue:42 +#: src/views/preference/components/TOTP.vue:55 #: 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" @@ -1722,7 +1799,7 @@ msgstr "Chemin de la clé du certificat SSL" msgid "SSL Certificate Path" msgstr "Chemin du certificat SSL" -#: src/views/other/Login.vue:170 +#: src/views/other/Login.vue:203 #, fuzzy msgid "SSO Login" msgstr "Connexion" @@ -1808,7 +1885,7 @@ msgstr "Changer de certificat" msgid "Sync to" msgstr "" -#: src/routes/index.ts:248 +#: src/routes/index.ts:253 msgid "System" msgstr "Système" @@ -1817,7 +1894,7 @@ msgstr "Système" msgid "Target" msgstr "" -#: src/routes/index.ts:172 src/views/pty/Terminal.vue:91 +#: src/routes/index.ts:172 src/views/pty/Terminal.vue:114 msgid "Terminal" msgstr "Terminal" @@ -1862,6 +1939,11 @@ msgstr "Chemin de la clé du certificat SSL" msgid "The path exists, but the file is not a private key" msgstr "" +#: src/views/preference/components/TOTP.vue:109 +msgid "" +"The recovery code is only displayed once, please save it in a safe place." +msgstr "" + #: src/views/dashboard/Environments.vue:148 msgid "" "The remote Nginx UI version is not compatible with the local Nginx UI " @@ -1925,7 +2007,7 @@ msgid "" "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}." msgstr "" -#: src/views/preference/AuthSettings.vue:59 +#: src/views/preference/AuthSettings.vue:60 #: src/views/preference/LogrotateSettings.vue:12 msgid "Tips" msgstr "" @@ -1934,6 +2016,12 @@ msgstr "" msgid "Title" msgstr "" +#: src/views/preference/components/TOTP.vue:90 +msgid "" +"To enable it, you need to install the Google or Microsoft Authenticator app " +"on your mobile phone." +msgstr "" + #: src/views/domain/ngx_conf/NgxConfigEditor.vue:44 msgid "" "To make sure the certification auto-renewal can work normally, we need to " @@ -1950,14 +2038,24 @@ msgstr "" msgid "Token is not valid" msgstr "" -#: src/views/other/Login.vue:62 +#: src/views/other/Login.vue:74 msgid "Too many login failed attempts, please try again later" msgstr "" +#: src/views/preference/components/TOTP.vue:89 +msgid "" +"TOTP is a two-factor authentication method that uses a time-based one-time " +"password algorithm." +msgstr "" + #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:209 msgid "Trash" msgstr "" +#: src/components/OTP/useOTPModal.ts:49 +msgid "Two-factor authentication required" +msgstr "" + #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12 #: src/views/notification/Notification.vue:13 msgid "Type" @@ -1977,7 +2075,7 @@ msgstr "Mis à jour le" msgid "Updated successfully" msgstr "Mis à jour avec succés" -#: src/routes/index.ts:263 src/views/environment/Environment.vue:50 +#: src/routes/index.ts:268 src/views/environment/Environment.vue:50 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228 msgid "Upgrade" msgstr "Mettre à niveau" @@ -2007,16 +2105,24 @@ msgstr "Disponibilité :" msgid "URL" msgstr "" +#: src/components/OTP/OTPAuthorization.vue:69 +msgid "Use OTP" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:65 +msgid "Use recovery code" +msgstr "" + #: src/components/ChatGPT/ChatGPT.vue:229 #, fuzzy msgid "User" msgstr "Nom d'utilisateur" -#: src/views/other/Login.vue:65 +#: src/views/other/Login.vue:77 msgid "User is banned" msgstr "" -#: src/views/other/Login.vue:134 src/views/user/User.vue:9 +#: src/views/other/Login.vue:159 src/views/user/User.vue:9 msgid "Username" msgstr "Nom d'utilisateur" @@ -2088,7 +2194,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/AuthSettings.vue:96 #: src/views/preference/BasicSettings.vue:100 msgid "Yes" msgstr "Oui" diff --git a/app/src/language/ko_KR/app.po b/app/src/language/ko_KR/app.po index 2afc96719..aa25a9292 100644 --- a/app/src/language/ko_KR/app.po +++ b/app/src/language/ko_KR/app.po @@ -13,7 +13,11 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Weblate 5.3.1\n" -#: src/routes/index.ts:256 +#: src/views/preference/components/TOTP.vue:88 +msgid "2FA Settings" +msgstr "" + +#: src/routes/index.ts:261 msgid "About" msgstr "소개" @@ -32,7 +36,7 @@ msgstr "사용자 이름" #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34 #: src/views/domain/DomainList.vue:47 src/views/environment/envColumns.tsx:131 #: src/views/notification/Notification.vue:37 -#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47 +#: src/views/preference/AuthSettings.vue:27 src/views/stream/StreamList.vue:47 #: src/views/user/User.vue:43 msgid "Action" msgstr "작업" @@ -97,7 +101,7 @@ msgstr "API 토큰" msgid "Arch" msgstr "아키텍처" -#: src/views/preference/AuthSettings.vue:94 +#: src/views/preference/AuthSettings.vue:95 #, fuzzy msgid "Are you sure to delete this banned IP immediately?" msgstr "정말 삭제하시겠습니까?" @@ -151,7 +155,7 @@ msgstr "ChatGPT에게 도움 요청" msgid "Assistant" msgstr "조수" -#: src/views/preference/AuthSettings.vue:17 +#: src/views/preference/AuthSettings.vue:18 msgid "Attempts" msgstr "" @@ -196,15 +200,15 @@ msgstr "홈으로" msgid "Back to list" msgstr "" -#: src/views/preference/AuthSettings.vue:68 +#: src/views/preference/AuthSettings.vue:69 msgid "Ban Threshold Minutes" msgstr "" -#: src/views/preference/AuthSettings.vue:82 +#: src/views/preference/AuthSettings.vue:83 msgid "Banned IPs" msgstr "" -#: src/views/preference/AuthSettings.vue:20 +#: src/views/preference/AuthSettings.vue:21 msgid "Banned Until" msgstr "" @@ -353,6 +357,10 @@ msgstr "SSL 구성하기" msgid "Connected" msgstr "연결됨" +#: src/views/pty/Terminal.vue:120 +msgid "Connection lost, please refresh the page." +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111 #: src/views/domain/ngx_conf/LocationEditor.vue:125 #: src/views/domain/ngx_conf/LocationEditor.vue:97 @@ -395,6 +403,14 @@ msgstr "인증 정보" msgid "Credentials" msgstr "인증 정보들" +#: src/views/preference/components/TOTP.vue:96 +msgid "Current account is enabled 2FA." +msgstr "" + +#: src/views/preference/components/TOTP.vue:93 +msgid "Current account is not enabled 2FA." +msgstr "" + #: src/views/system/Upgrade.vue:167 msgid "Current Version" msgstr "현재 버전" @@ -666,6 +682,16 @@ msgstr "%{node_name}에서 %{conf_name} 활성화 실패" msgid "Enable %{conf_name} in %{node_name} successfully" msgstr "%{node_name}에서 %{conf_name} 성공적으로 활성화됨" +#: src/views/preference/components/TOTP.vue:122 +#, fuzzy +msgid "Enable 2FA" +msgstr "활성화" + +#: src/views/preference/components/TOTP.vue:52 +#, fuzzy +msgid "Enable 2FA successfully" +msgstr "성공적으로 활성화" + #: src/views/domain/cert/components/ObtainCert.vue:70 msgid "Enable auto-renewal failed for %{name}" msgstr "%{name}에 대한 자동 갱신 활성화 실패" @@ -866,12 +892,18 @@ msgstr "HTTP01" msgid "If left blank, the default CA Dir will be used." msgstr "" -#: src/views/preference/AuthSettings.vue:60 +#: src/views/preference/AuthSettings.vue:61 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/preference/components/TOTP.vue:108 +msgid "" +"If you lose your mobile phone, you can use the recovery code to reset your " +"2FA." +msgstr "" + #: src/views/certificate/Certificate.vue:136 msgid "Import" msgstr "가져오기" @@ -881,7 +913,7 @@ msgstr "가져오기" msgid "Import Certificate" msgstr "인증서 상태" -#: src/views/other/Login.vue:59 +#: src/views/other/Login.vue:71 #, fuzzy msgid "Incorrect username or password" msgstr "사용자 이름 또는 비밀번호가 올바르지 않습니다" @@ -898,7 +930,16 @@ msgstr "초기 코어 업그레이더 오류" msgid "Initialing core upgrader" msgstr "코어 업그레이더 초기화" -#: src/routes/index.ts:273 src/views/other/Install.vue:135 +#: src/views/preference/components/TOTP.vue:144 +msgid "Input the code from the app:" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:49 +#: src/views/preference/components/TOTP.vue:157 +msgid "Input the recovery code:" +msgstr "" + +#: src/routes/index.ts:283 src/views/other/Install.vue:135 msgid "Install" msgstr "설치" @@ -920,7 +961,15 @@ msgstr "간격" msgid "Invalid" msgstr "유효함" -#: src/views/preference/AuthSettings.vue:14 +#: src/views/other/Login.vue:81 +msgid "Invalid 2FA or recovery code" +msgstr "" + +#: src/components/OTP/useOTPModal.ts:43 +msgid "Invalid passcode or recovery code" +msgstr "" + +#: src/views/preference/AuthSettings.vue:15 msgid "IP" msgstr "" @@ -1014,11 +1063,11 @@ msgstr "위치들" msgid "Log" msgstr "로그인" -#: src/routes/index.ts:279 src/views/other/Login.vue:159 +#: src/routes/index.ts:289 src/views/other/Login.vue:192 msgid "Login" msgstr "로그인" -#: src/views/other/Login.vue:109 src/views/other/Login.vue:51 +#: src/views/other/Login.vue:125 src/views/other/Login.vue:60 msgid "Login successful" msgstr "로그인 성공" @@ -1067,7 +1116,7 @@ msgstr "사이트 관리" msgid "Manage Streams" msgstr "스트림 관리" -#: src/routes/index.ts:230 src/views/user/User.vue:50 +#: src/routes/index.ts:235 src/views/user/User.vue:50 msgid "Manage Users" msgstr "사용자 관리" @@ -1076,7 +1125,7 @@ msgstr "사용자 관리" msgid "Managed Certificate" msgstr "인증서 유효" -#: src/views/preference/AuthSettings.vue:74 +#: src/views/preference/AuthSettings.vue:75 msgid "Max Attempts" msgstr "" @@ -1210,7 +1259,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/AuthSettings.vue:97 #: src/views/preference/BasicSettings.vue:101 #: src/views/stream/StreamList.vue:165 msgid "No" @@ -1224,7 +1273,7 @@ msgstr "노드 시크릿" msgid "Not After" msgstr "만료일" -#: src/routes/index.ts:285 +#: src/routes/index.ts:295 msgid "Not Found" msgstr "찾을 수 없음" @@ -1242,7 +1291,7 @@ msgstr "참고" msgid "Notification" msgstr "알림" -#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221 +#: src/components/Notification/Notification.vue:82 src/routes/index.ts:226 #, fuzzy msgid "Notifications" msgstr "알림" @@ -1325,7 +1374,7 @@ msgstr "기존 파일 덮어쓰기" msgid "Params" msgstr "파라미터" -#: src/views/other/Login.vue:144 src/views/user/User.vue:18 +#: src/views/other/Login.vue:169 src/views/user/User.vue:18 msgid "Password" msgstr "비밀번호" @@ -1351,6 +1400,10 @@ msgstr "핵심 업그레이드 오류 수행" msgid "Performing core upgrade" msgstr "핵심 업그레이드 수행 중" +#: src/components/OTP/OTPAuthorization.vue:37 +msgid "Please enter the 2FA code:" +msgstr "" + #: src/views/certificate/DNSCredential.vue:53 msgid "" "Please fill in the API authentication credentials provided by your DNS " @@ -1380,11 +1433,11 @@ msgstr "이름을 입력해주세요, 이것은 새 구성의 파일 이름으 msgid "Please input your E-mail!" msgstr "이메일을 입력해주세요!" -#: src/views/other/Install.vue:45 src/views/other/Login.vue:39 +#: src/views/other/Install.vue:45 src/views/other/Login.vue:43 msgid "Please input your password!" msgstr "비밀번호를 입력해주세요!" -#: src/views/other/Install.vue:39 src/views/other/Login.vue:33 +#: src/views/other/Install.vue:39 src/views/other/Login.vue:37 msgid "Please input your username!" msgstr "사용자 이름을 입력해주세요!" @@ -1404,7 +1457,7 @@ msgstr "적어도 하나의 노드를 선택해주세요!" msgid "Pre-release" msgstr "사전 출시" -#: src/routes/index.ts:239 src/views/preference/Preference.vue:105 +#: src/routes/index.ts:244 src/views/preference/Preference.vue:105 msgid "Preference" msgstr "환경설정" @@ -1448,6 +1501,19 @@ msgstr "" msgid "Recovered Successfully" msgstr "성공적으로 제거됨" +#: src/components/OTP/OTPAuthorization.vue:56 +#: src/views/preference/components/TOTP.vue:167 +msgid "Recovery" +msgstr "" + +#: src/views/preference/components/TOTP.vue:101 +msgid "Recovery Code" +msgstr "" + +#: src/views/preference/components/TOTP.vue:110 +msgid "Recovery Code:" +msgstr "" + #: src/views/preference/BasicSettings.vue:68 msgid "Recursive Nameservers" msgstr "" @@ -1502,11 +1568,11 @@ msgstr "리로딩 중" msgid "Reloading nginx" msgstr "Nginx 리로딩 중" -#: src/views/preference/AuthSettings.vue:101 +#: src/views/preference/AuthSettings.vue:102 msgid "Remove" msgstr "" -#: src/views/preference/AuthSettings.vue:47 +#: src/views/preference/AuthSettings.vue:48 #, fuzzy msgid "Remove successfully" msgstr "성공적으로 제거됨" @@ -1551,6 +1617,11 @@ msgstr "잘못된 매개변수로 요청됨" msgid "Reset" msgstr "재설정" +#: src/views/preference/components/TOTP.vue:130 +#, fuzzy +msgid "Reset 2FA" +msgstr "재설정" + #: src/components/NginxControl/NginxControl.vue:93 msgid "Restart" msgstr "재시작" @@ -1600,6 +1671,10 @@ msgstr "성공적으로 저장됨" msgid "Saved successfully" msgstr "성공적으로 저장됨" +#: src/views/preference/components/TOTP.vue:91 +msgid "Scan the QR code with your mobile phone to add the account to the app." +msgstr "" + #: src/views/certificate/DNSChallenge.vue:89 msgid "SDK" msgstr "" @@ -1623,7 +1698,9 @@ msgstr "보내기" #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81 #: src/views/environment/BatchUpgrader.vue:57 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:69 -#: src/views/preference/AuthSettings.vue:49 +#: src/views/preference/AuthSettings.vue:50 +#: src/views/preference/components/TOTP.vue:42 +#: src/views/preference/components/TOTP.vue:55 #: 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" @@ -1699,7 +1776,7 @@ msgstr "SSL 인증서 키 경로" msgid "SSL Certificate Path" msgstr "SSL 인증서 경로" -#: src/views/other/Login.vue:170 +#: src/views/other/Login.vue:203 #, fuzzy msgid "SSO Login" msgstr "SSO 로그인" @@ -1784,7 +1861,7 @@ msgstr "인증서 갱신 성공" msgid "Sync to" msgstr "" -#: src/routes/index.ts:248 +#: src/routes/index.ts:253 msgid "System" msgstr "시스템" @@ -1793,7 +1870,7 @@ msgstr "시스템" msgid "Target" msgstr "대상" -#: src/routes/index.ts:172 src/views/pty/Terminal.vue:91 +#: src/routes/index.ts:172 src/views/pty/Terminal.vue:114 msgid "Terminal" msgstr "터미널" @@ -1838,6 +1915,11 @@ msgstr "Certificate Status" msgid "The path exists, but the file is not a private key" msgstr "경로는 존재하지만 파일은 개인 키가 아닙니다" +#: src/views/preference/components/TOTP.vue:109 +msgid "" +"The recovery code is only displayed once, please save it in a safe place." +msgstr "" + #: src/views/dashboard/Environments.vue:148 msgid "" "The remote Nginx UI version is not compatible with the local Nginx UI " @@ -1899,7 +1981,7 @@ msgid "" "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}." msgstr "" -#: src/views/preference/AuthSettings.vue:59 +#: src/views/preference/AuthSettings.vue:60 #: src/views/preference/LogrotateSettings.vue:12 msgid "Tips" msgstr "팁" @@ -1908,6 +1990,12 @@ msgstr "팁" msgid "Title" msgstr "제목" +#: src/views/preference/components/TOTP.vue:90 +msgid "" +"To enable it, you need to install the Google or Microsoft Authenticator app " +"on your mobile phone." +msgstr "" + #: src/views/domain/ngx_conf/NgxConfigEditor.vue:44 msgid "" "To make sure the certification auto-renewal can work normally, we need to " @@ -1923,14 +2011,24 @@ msgstr "" msgid "Token is not valid" msgstr "토큰이 유효하지 않습니다" -#: src/views/other/Login.vue:62 +#: src/views/other/Login.vue:74 msgid "Too many login failed attempts, please try again later" msgstr "" +#: src/views/preference/components/TOTP.vue:89 +msgid "" +"TOTP is a two-factor authentication method that uses a time-based one-time " +"password algorithm." +msgstr "" + #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:209 msgid "Trash" msgstr "" +#: src/components/OTP/useOTPModal.ts:49 +msgid "Two-factor authentication required" +msgstr "" + #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12 #: src/views/notification/Notification.vue:13 msgid "Type" @@ -1951,7 +2049,7 @@ msgstr "업데이트됨" msgid "Updated successfully" msgstr "성공적으로 저장되었습니다" -#: src/routes/index.ts:263 src/views/environment/Environment.vue:50 +#: src/routes/index.ts:268 src/views/environment/Environment.vue:50 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228 msgid "Upgrade" msgstr "업그레이드" @@ -1982,16 +2080,24 @@ msgstr "가동 시간:" msgid "URL" msgstr "URL" +#: src/components/OTP/OTPAuthorization.vue:69 +msgid "Use OTP" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:65 +msgid "Use recovery code" +msgstr "" + #: src/components/ChatGPT/ChatGPT.vue:229 #, fuzzy msgid "User" msgstr "사용자 이름" -#: src/views/other/Login.vue:65 +#: src/views/other/Login.vue:77 msgid "User is banned" msgstr "" -#: src/views/other/Login.vue:134 src/views/user/User.vue:9 +#: src/views/other/Login.vue:159 src/views/user/User.vue:9 msgid "Username" msgstr "사용자 이름" @@ -2066,7 +2172,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/AuthSettings.vue:96 #: src/views/preference/BasicSettings.vue:100 msgid "Yes" msgstr "예" diff --git a/app/src/language/messages.pot b/app/src/language/messages.pot index 97f442fec..6caefceb0 100644 --- a/app/src/language/messages.pot +++ b/app/src/language/messages.pot @@ -2,7 +2,11 @@ msgid "" msgstr "" "Content-Type: text/plain; charset=UTF-8\n" -#: src/routes/index.ts:256 +#: src/views/preference/components/TOTP.vue:88 +msgid "2FA Settings" +msgstr "" + +#: src/routes/index.ts:261 msgid "About" msgstr "" @@ -24,7 +28,7 @@ msgstr "" #: src/views/domain/DomainList.vue:47 #: src/views/environment/envColumns.tsx:131 #: src/views/notification/Notification.vue:37 -#: src/views/preference/AuthSettings.vue:26 +#: src/views/preference/AuthSettings.vue:27 #: src/views/stream/StreamList.vue:47 #: src/views/user/User.vue:43 msgid "Action" @@ -91,7 +95,7 @@ msgstr "" msgid "Arch" msgstr "" -#: src/views/preference/AuthSettings.vue:94 +#: src/views/preference/AuthSettings.vue:95 msgid "Are you sure to delete this banned IP immediately?" msgstr "" @@ -141,7 +145,7 @@ msgstr "" msgid "Assistant" msgstr "" -#: src/views/preference/AuthSettings.vue:17 +#: src/views/preference/AuthSettings.vue:18 msgid "Attempts" msgstr "" @@ -187,15 +191,15 @@ msgstr "" msgid "Back to list" msgstr "" -#: src/views/preference/AuthSettings.vue:68 +#: src/views/preference/AuthSettings.vue:69 msgid "Ban Threshold Minutes" msgstr "" -#: src/views/preference/AuthSettings.vue:82 +#: src/views/preference/AuthSettings.vue:83 msgid "Banned IPs" msgstr "" -#: src/views/preference/AuthSettings.vue:20 +#: src/views/preference/AuthSettings.vue:21 msgid "Banned Until" msgstr "" @@ -345,6 +349,10 @@ msgstr "" msgid "Connected" msgstr "" +#: src/views/pty/Terminal.vue:120 +msgid "Connection lost, please refresh the page." +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111 #: src/views/domain/ngx_conf/LocationEditor.vue:125 #: src/views/domain/ngx_conf/LocationEditor.vue:97 @@ -388,6 +396,14 @@ msgstr "" msgid "Credentials" msgstr "" +#: src/views/preference/components/TOTP.vue:96 +msgid "Current account is enabled 2FA." +msgstr "" + +#: src/views/preference/components/TOTP.vue:93 +msgid "Current account is not enabled 2FA." +msgstr "" + #: src/views/system/Upgrade.vue:167 msgid "Current Version" msgstr "" @@ -662,6 +678,14 @@ msgstr "" msgid "Enable %{conf_name} in %{node_name} successfully" msgstr "" +#: src/views/preference/components/TOTP.vue:122 +msgid "Enable 2FA" +msgstr "" + +#: src/views/preference/components/TOTP.vue:52 +msgid "Enable 2FA successfully" +msgstr "" + #: src/views/domain/cert/components/ObtainCert.vue:70 msgid "Enable auto-renewal failed for %{name}" msgstr "" @@ -863,10 +887,14 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "" -#: src/views/preference/AuthSettings.vue:60 +#: src/views/preference/AuthSettings.vue:61 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/preference/components/TOTP.vue:108 +msgid "If you lose your mobile phone, you can use the recovery code to reset your 2FA." +msgstr "" + #: src/views/certificate/Certificate.vue:136 msgid "Import" msgstr "" @@ -876,7 +904,7 @@ msgstr "" msgid "Import Certificate" msgstr "" -#: src/views/other/Login.vue:59 +#: src/views/other/Login.vue:71 msgid "Incorrect username or password" msgstr "" @@ -892,7 +920,16 @@ msgstr "" msgid "Initialing core upgrader" msgstr "" -#: src/routes/index.ts:273 +#: src/views/preference/components/TOTP.vue:144 +msgid "Input the code from the app:" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:49 +#: src/views/preference/components/TOTP.vue:157 +msgid "Input the recovery code:" +msgstr "" + +#: src/routes/index.ts:283 #: src/views/other/Install.vue:135 msgid "Install" msgstr "" @@ -913,7 +950,15 @@ msgstr "" msgid "Invalid" msgstr "" -#: src/views/preference/AuthSettings.vue:14 +#: src/views/other/Login.vue:81 +msgid "Invalid 2FA or recovery code" +msgstr "" + +#: src/components/OTP/useOTPModal.ts:43 +msgid "Invalid passcode or recovery code" +msgstr "" + +#: src/views/preference/AuthSettings.vue:15 msgid "IP" msgstr "" @@ -998,13 +1043,13 @@ msgstr "" msgid "Log" msgstr "" -#: src/routes/index.ts:279 -#: src/views/other/Login.vue:159 +#: src/routes/index.ts:289 +#: src/views/other/Login.vue:192 msgid "Login" msgstr "" -#: src/views/other/Login.vue:109 -#: src/views/other/Login.vue:51 +#: src/views/other/Login.vue:125 +#: src/views/other/Login.vue:60 msgid "Login successful" msgstr "" @@ -1038,7 +1083,7 @@ msgstr "" msgid "Manage Streams" msgstr "" -#: src/routes/index.ts:230 +#: src/routes/index.ts:235 #: src/views/user/User.vue:50 msgid "Manage Users" msgstr "" @@ -1047,7 +1092,7 @@ msgstr "" msgid "Managed Certificate" msgstr "" -#: src/views/preference/AuthSettings.vue:74 +#: src/views/preference/AuthSettings.vue:75 msgid "Max Attempts" msgstr "" @@ -1178,7 +1223,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/AuthSettings.vue:97 #: src/views/preference/BasicSettings.vue:101 #: src/views/stream/StreamList.vue:165 msgid "No" @@ -1192,7 +1237,7 @@ msgstr "" msgid "Not After" msgstr "" -#: src/routes/index.ts:285 +#: src/routes/index.ts:295 msgid "Not Found" msgstr "" @@ -1210,7 +1255,7 @@ msgid "Notification" msgstr "" #: src/components/Notification/Notification.vue:82 -#: src/routes/index.ts:221 +#: src/routes/index.ts:226 msgid "Notifications" msgstr "" @@ -1290,7 +1335,7 @@ msgstr "" msgid "Params" msgstr "" -#: src/views/other/Login.vue:144 +#: src/views/other/Login.vue:169 #: src/views/user/User.vue:18 msgid "Password" msgstr "" @@ -1317,6 +1362,10 @@ msgstr "" msgid "Performing core upgrade" msgstr "" +#: src/components/OTP/OTPAuthorization.vue:37 +msgid "Please enter the 2FA code:" +msgstr "" + #: src/views/certificate/DNSCredential.vue:53 msgid "Please fill in the API authentication credentials provided by your DNS provider." msgstr "" @@ -1339,12 +1388,12 @@ msgid "Please input your E-mail!" msgstr "" #: src/views/other/Install.vue:45 -#: src/views/other/Login.vue:39 +#: src/views/other/Login.vue:43 msgid "Please input your password!" msgstr "" #: src/views/other/Install.vue:39 -#: src/views/other/Login.vue:33 +#: src/views/other/Login.vue:37 msgid "Please input your username!" msgstr "" @@ -1364,7 +1413,7 @@ msgstr "" msgid "Pre-release" msgstr "" -#: src/routes/index.ts:239 +#: src/routes/index.ts:244 #: src/views/preference/Preference.vue:105 msgid "Preference" msgstr "" @@ -1407,6 +1456,19 @@ msgstr "" msgid "Recovered Successfully" msgstr "" +#: src/components/OTP/OTPAuthorization.vue:56 +#: src/views/preference/components/TOTP.vue:167 +msgid "Recovery" +msgstr "" + +#: src/views/preference/components/TOTP.vue:101 +msgid "Recovery Code" +msgstr "" + +#: src/views/preference/components/TOTP.vue:110 +msgid "Recovery Code:" +msgstr "" + #: src/views/preference/BasicSettings.vue:68 msgid "Recursive Nameservers" msgstr "" @@ -1456,11 +1518,11 @@ msgstr "" msgid "Reloading nginx" msgstr "" -#: src/views/preference/AuthSettings.vue:101 +#: src/views/preference/AuthSettings.vue:102 msgid "Remove" msgstr "" -#: src/views/preference/AuthSettings.vue:47 +#: src/views/preference/AuthSettings.vue:48 msgid "Remove successfully" msgstr "" @@ -1498,6 +1560,10 @@ msgstr "" msgid "Reset" msgstr "" +#: src/views/preference/components/TOTP.vue:130 +msgid "Reset 2FA" +msgstr "" + #: src/components/NginxControl/NginxControl.vue:93 msgid "Restart" msgstr "" @@ -1549,6 +1615,10 @@ msgstr "" msgid "Saved successfully" msgstr "" +#: src/views/preference/components/TOTP.vue:91 +msgid "Scan the QR code with your mobile phone to add the account to the app." +msgstr "" + #: src/views/certificate/DNSChallenge.vue:89 msgid "SDK" msgstr "" @@ -1574,7 +1644,9 @@ msgstr "" #: src/views/environment/BatchUpgrader.vue:57 #: src/views/environment/Environment.vue:15 #: src/views/other/Install.vue:69 -#: src/views/preference/AuthSettings.vue:49 +#: src/views/preference/AuthSettings.vue:50 +#: src/views/preference/components/TOTP.vue:42 +#: src/views/preference/components/TOTP.vue:55 #: src/views/preference/Preference.vue:78 #: src/views/stream/StreamList.vue:113 #: src/views/stream/StreamList.vue:81 @@ -1645,7 +1717,7 @@ msgstr "" msgid "SSL Certificate Path" msgstr "" -#: src/views/other/Login.vue:170 +#: src/views/other/Login.vue:203 msgid "SSO Login" msgstr "" @@ -1722,7 +1794,7 @@ msgstr "" msgid "Sync to" msgstr "" -#: src/routes/index.ts:248 +#: src/routes/index.ts:253 msgid "System" msgstr "" @@ -1732,7 +1804,7 @@ msgid "Target" msgstr "" #: src/routes/index.ts:172 -#: src/views/pty/Terminal.vue:91 +#: src/views/pty/Terminal.vue:114 msgid "Terminal" msgstr "" @@ -1768,6 +1840,10 @@ msgstr "" msgid "The path exists, but the file is not a private key" msgstr "" +#: src/views/preference/components/TOTP.vue:109 +msgid "The recovery code is only displayed once, please save it in a safe place." +msgstr "" + #: src/views/dashboard/Environments.vue:148 msgid "The remote Nginx UI version is not compatible with the local Nginx UI version. To avoid potential errors, please upgrade the remote Nginx UI to match the local version." msgstr "" @@ -1816,7 +1892,7 @@ msgstr "" msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}." msgstr "" -#: src/views/preference/AuthSettings.vue:59 +#: src/views/preference/AuthSettings.vue:60 #: src/views/preference/LogrotateSettings.vue:12 msgid "Tips" msgstr "" @@ -1825,6 +1901,10 @@ msgstr "" msgid "Title" msgstr "" +#: src/views/preference/components/TOTP.vue:90 +msgid "To enable it, you need to install the Google or Microsoft Authenticator app on your mobile phone." +msgstr "" + #: src/views/domain/ngx_conf/NgxConfigEditor.vue:44 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 "" @@ -1833,14 +1913,22 @@ msgstr "" msgid "Token is not valid" msgstr "" -#: src/views/other/Login.vue:62 +#: src/views/other/Login.vue:74 msgid "Too many login failed attempts, please try again later" msgstr "" +#: src/views/preference/components/TOTP.vue:89 +msgid "TOTP is a two-factor authentication method that uses a time-based one-time password algorithm." +msgstr "" + #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:209 msgid "Trash" msgstr "" +#: src/components/OTP/useOTPModal.ts:49 +msgid "Two-factor authentication required" +msgstr "" + #: src/views/certificate/Certificate.vue:37 #: src/views/config/config.ts:12 #: src/views/notification/Notification.vue:13 @@ -1864,7 +1952,7 @@ msgstr "" msgid "Updated successfully" msgstr "" -#: src/routes/index.ts:263 +#: src/routes/index.ts:268 #: src/views/environment/Environment.vue:50 #: src/views/system/Upgrade.vue:145 #: src/views/system/Upgrade.vue:228 @@ -1896,15 +1984,23 @@ msgstr "" msgid "URL" msgstr "" +#: src/components/OTP/OTPAuthorization.vue:69 +msgid "Use OTP" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:65 +msgid "Use recovery code" +msgstr "" + #: src/components/ChatGPT/ChatGPT.vue:229 msgid "User" msgstr "" -#: src/views/other/Login.vue:65 +#: src/views/other/Login.vue:77 msgid "User is banned" msgstr "" -#: src/views/other/Login.vue:134 +#: src/views/other/Login.vue:159 #: src/views/user/User.vue:9 msgid "Username" msgstr "" @@ -1969,7 +2065,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/AuthSettings.vue:96 #: src/views/preference/BasicSettings.vue:100 msgid "Yes" msgstr "" diff --git a/app/src/language/ru_RU/app.po b/app/src/language/ru_RU/app.po index 53780a4e5..52a1c6d69 100644 --- a/app/src/language/ru_RU/app.po +++ b/app/src/language/ru_RU/app.po @@ -9,7 +9,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/routes/index.ts:256 +#: src/views/preference/components/TOTP.vue:88 +msgid "2FA Settings" +msgstr "" + +#: src/routes/index.ts:261 msgid "About" msgstr "О проекте" @@ -28,7 +32,7 @@ msgstr "Пользователь" #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34 #: src/views/domain/DomainList.vue:47 src/views/environment/envColumns.tsx:131 #: src/views/notification/Notification.vue:37 -#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47 +#: src/views/preference/AuthSettings.vue:27 src/views/stream/StreamList.vue:47 #: src/views/user/User.vue:43 msgid "Action" msgstr "Действие" @@ -95,7 +99,7 @@ msgstr "" msgid "Arch" msgstr "" -#: src/views/preference/AuthSettings.vue:94 +#: src/views/preference/AuthSettings.vue:95 #, fuzzy msgid "Are you sure to delete this banned IP immediately?" msgstr "Вы уверены, что хотите удалить?" @@ -153,7 +157,7 @@ msgstr "Обратитесь за помощью к ChatGPT" msgid "Assistant" msgstr "Ассистент" -#: src/views/preference/AuthSettings.vue:17 +#: src/views/preference/AuthSettings.vue:18 msgid "Attempts" msgstr "" @@ -199,15 +203,15 @@ msgstr "Вернутся" msgid "Back to list" msgstr "" -#: src/views/preference/AuthSettings.vue:68 +#: src/views/preference/AuthSettings.vue:69 msgid "Ban Threshold Minutes" msgstr "" -#: src/views/preference/AuthSettings.vue:82 +#: src/views/preference/AuthSettings.vue:83 msgid "Banned IPs" msgstr "" -#: src/views/preference/AuthSettings.vue:20 +#: src/views/preference/AuthSettings.vue:21 msgid "Banned Until" msgstr "" @@ -364,6 +368,10 @@ msgstr "Настроить SSL" msgid "Connected" msgstr "Подключено" +#: src/views/pty/Terminal.vue:120 +msgid "Connection lost, please refresh the page." +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111 #: src/views/domain/ngx_conf/LocationEditor.vue:125 #: src/views/domain/ngx_conf/LocationEditor.vue:97 @@ -407,6 +415,14 @@ msgstr "Учетные данные" msgid "Credentials" msgstr "Учетные данные" +#: src/views/preference/components/TOTP.vue:96 +msgid "Current account is enabled 2FA." +msgstr "" + +#: src/views/preference/components/TOTP.vue:93 +msgid "Current account is not enabled 2FA." +msgstr "" + #: src/views/system/Upgrade.vue:167 msgid "Current Version" msgstr "Текущяя версия" @@ -696,6 +712,16 @@ msgstr "Включение %{conf_name} in %{node_name} нипалучилася msgid "Enable %{conf_name} in %{node_name} successfully" msgstr "Включение %{conf_name} in %{node_name} успешно" +#: src/views/preference/components/TOTP.vue:122 +#, fuzzy +msgid "Enable 2FA" +msgstr "Включить" + +#: src/views/preference/components/TOTP.vue:52 +#, fuzzy +msgid "Enable 2FA successfully" +msgstr "Активировано успешно" + #: src/views/domain/cert/components/ObtainCert.vue:70 msgid "Enable auto-renewal failed for %{name}" msgstr "Не удалось включить автоматическое продление для %{name}" @@ -898,12 +924,18 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "" -#: src/views/preference/AuthSettings.vue:60 +#: src/views/preference/AuthSettings.vue:61 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/preference/components/TOTP.vue:108 +msgid "" +"If you lose your mobile phone, you can use the recovery code to reset your " +"2FA." +msgstr "" + #: src/views/certificate/Certificate.vue:136 #, fuzzy msgid "Import" @@ -914,7 +946,7 @@ msgstr "Экспорт" msgid "Import Certificate" msgstr "Статус сертификата" -#: src/views/other/Login.vue:59 +#: src/views/other/Login.vue:71 #, fuzzy msgid "Incorrect username or password" msgstr "Имя пользователя или пароль неверны" @@ -931,7 +963,16 @@ msgstr "Ошибка первоначального обновления ядр msgid "Initialing core upgrader" msgstr "Инициализация программы обновления ядра" -#: src/routes/index.ts:273 src/views/other/Install.vue:135 +#: src/views/preference/components/TOTP.vue:144 +msgid "Input the code from the app:" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:49 +#: src/views/preference/components/TOTP.vue:157 +msgid "Input the recovery code:" +msgstr "" + +#: src/routes/index.ts:283 src/views/other/Install.vue:135 msgid "Install" msgstr "Установить" @@ -953,7 +994,15 @@ msgstr "" msgid "Invalid" msgstr "Действительный" -#: src/views/preference/AuthSettings.vue:14 +#: src/views/other/Login.vue:81 +msgid "Invalid 2FA or recovery code" +msgstr "" + +#: src/components/OTP/useOTPModal.ts:43 +msgid "Invalid passcode or recovery code" +msgstr "" + +#: src/views/preference/AuthSettings.vue:15 msgid "IP" msgstr "" @@ -1048,11 +1097,11 @@ msgstr "Locations" msgid "Log" msgstr "Логин" -#: src/routes/index.ts:279 src/views/other/Login.vue:159 +#: src/routes/index.ts:289 src/views/other/Login.vue:192 msgid "Login" msgstr "Логин" -#: src/views/other/Login.vue:109 src/views/other/Login.vue:51 +#: src/views/other/Login.vue:125 src/views/other/Login.vue:60 msgid "Login successful" msgstr "Авторизация успешна" @@ -1096,7 +1145,7 @@ msgstr "Сайты" msgid "Manage Streams" msgstr "Управление потоками" -#: src/routes/index.ts:230 src/views/user/User.vue:50 +#: src/routes/index.ts:235 src/views/user/User.vue:50 msgid "Manage Users" msgstr "Пользователи" @@ -1105,7 +1154,7 @@ msgstr "Пользователи" msgid "Managed Certificate" msgstr "Управление сертификатами" -#: src/views/preference/AuthSettings.vue:74 +#: src/views/preference/AuthSettings.vue:75 msgid "Max Attempts" msgstr "" @@ -1240,7 +1289,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/AuthSettings.vue:97 #: src/views/preference/BasicSettings.vue:101 #: src/views/stream/StreamList.vue:165 msgid "No" @@ -1254,7 +1303,7 @@ msgstr "" msgid "Not After" msgstr "" -#: src/routes/index.ts:285 +#: src/routes/index.ts:295 msgid "Not Found" msgstr "Не найден" @@ -1272,7 +1321,7 @@ msgstr "Заметка" msgid "Notification" msgstr "Сертификат" -#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221 +#: src/components/Notification/Notification.vue:82 src/routes/index.ts:226 #, fuzzy msgid "Notifications" msgstr "Уведомления" @@ -1355,7 +1404,7 @@ msgstr "" msgid "Params" msgstr "Параметры" -#: src/views/other/Login.vue:144 src/views/user/User.vue:18 +#: src/views/other/Login.vue:169 src/views/user/User.vue:18 msgid "Password" msgstr "Пароль" @@ -1381,6 +1430,10 @@ msgstr "" msgid "Performing core upgrade" msgstr "" +#: src/components/OTP/OTPAuthorization.vue:37 +msgid "Please enter the 2FA code:" +msgstr "" + #: src/views/certificate/DNSCredential.vue:53 msgid "" "Please fill in the API authentication credentials provided by your DNS " @@ -1410,11 +1463,11 @@ msgstr "" msgid "Please input your E-mail!" msgstr "Введите ваш E-mail!" -#: src/views/other/Install.vue:45 src/views/other/Login.vue:39 +#: src/views/other/Install.vue:45 src/views/other/Login.vue:43 msgid "Please input your password!" msgstr "Введите ваш пароль!" -#: src/views/other/Install.vue:39 src/views/other/Login.vue:33 +#: src/views/other/Install.vue:39 src/views/other/Login.vue:37 msgid "Please input your username!" msgstr "Введите ваше имя пользователя!" @@ -1434,7 +1487,7 @@ msgstr "" msgid "Pre-release" msgstr "" -#: src/routes/index.ts:239 src/views/preference/Preference.vue:105 +#: src/routes/index.ts:244 src/views/preference/Preference.vue:105 msgid "Preference" msgstr "Настройки" @@ -1478,6 +1531,19 @@ msgstr "" msgid "Recovered Successfully" msgstr "Успешно сохранено" +#: src/components/OTP/OTPAuthorization.vue:56 +#: src/views/preference/components/TOTP.vue:167 +msgid "Recovery" +msgstr "" + +#: src/views/preference/components/TOTP.vue:101 +msgid "Recovery Code" +msgstr "" + +#: src/views/preference/components/TOTP.vue:110 +msgid "Recovery Code:" +msgstr "" + #: src/views/preference/BasicSettings.vue:68 msgid "Recursive Nameservers" msgstr "" @@ -1532,11 +1598,11 @@ msgstr "Перезагружается" msgid "Reloading nginx" msgstr "Перезагружается nginx" -#: src/views/preference/AuthSettings.vue:101 +#: src/views/preference/AuthSettings.vue:102 msgid "Remove" msgstr "" -#: src/views/preference/AuthSettings.vue:47 +#: src/views/preference/AuthSettings.vue:48 #, fuzzy msgid "Remove successfully" msgstr "Успешно сохранено" @@ -1581,6 +1647,11 @@ msgstr "Запрос с неправильными параметрами" msgid "Reset" msgstr "Сброс" +#: src/views/preference/components/TOTP.vue:130 +#, fuzzy +msgid "Reset 2FA" +msgstr "Сброс" + #: src/components/NginxControl/NginxControl.vue:93 msgid "Restart" msgstr "Перезапуск" @@ -1630,6 +1701,10 @@ msgstr "Успешно сохранено" msgid "Saved successfully" msgstr "Успешно сохранено" +#: src/views/preference/components/TOTP.vue:91 +msgid "Scan the QR code with your mobile phone to add the account to the app." +msgstr "" + #: src/views/certificate/DNSChallenge.vue:89 msgid "SDK" msgstr "" @@ -1653,7 +1728,9 @@ msgstr "Отправлено" #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81 #: src/views/environment/BatchUpgrader.vue:57 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:69 -#: src/views/preference/AuthSettings.vue:49 +#: src/views/preference/AuthSettings.vue:50 +#: src/views/preference/components/TOTP.vue:42 +#: src/views/preference/components/TOTP.vue:55 #: 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" @@ -1730,7 +1807,7 @@ msgstr "Путь к ключу сертификата SSL" msgid "SSL Certificate Path" msgstr "Путь к сертификату SSL" -#: src/views/other/Login.vue:170 +#: src/views/other/Login.vue:203 #, fuzzy msgid "SSO Login" msgstr "Логин" @@ -1815,7 +1892,7 @@ msgstr "Сертификат действителен" msgid "Sync to" msgstr "" -#: src/routes/index.ts:248 +#: src/routes/index.ts:253 msgid "System" msgstr "Система" @@ -1824,7 +1901,7 @@ msgstr "Система" msgid "Target" msgstr "" -#: src/routes/index.ts:172 src/views/pty/Terminal.vue:91 +#: src/routes/index.ts:172 src/views/pty/Terminal.vue:114 msgid "Terminal" msgstr "Терминал" @@ -1869,6 +1946,11 @@ msgstr "Путь к ключу сертификата SSL" msgid "The path exists, but the file is not a private key" msgstr "Путь существует, но файл не является приватным ключом" +#: src/views/preference/components/TOTP.vue:109 +msgid "" +"The recovery code is only displayed once, please save it in a safe place." +msgstr "" + #: src/views/dashboard/Environments.vue:148 msgid "" "The remote Nginx UI version is not compatible with the local Nginx UI " @@ -1931,7 +2013,7 @@ msgid "" "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}." msgstr "" -#: src/views/preference/AuthSettings.vue:59 +#: src/views/preference/AuthSettings.vue:60 #: src/views/preference/LogrotateSettings.vue:12 msgid "Tips" msgstr "" @@ -1940,6 +2022,12 @@ msgstr "" msgid "Title" msgstr "Заголовок" +#: src/views/preference/components/TOTP.vue:90 +msgid "" +"To enable it, you need to install the Google or Microsoft Authenticator app " +"on your mobile phone." +msgstr "" + #: src/views/domain/ngx_conf/NgxConfigEditor.vue:44 msgid "" "To make sure the certification auto-renewal can work normally, we need to " @@ -1952,14 +2040,24 @@ msgstr "" msgid "Token is not valid" msgstr "" -#: src/views/other/Login.vue:62 +#: src/views/other/Login.vue:74 msgid "Too many login failed attempts, please try again later" msgstr "" +#: src/views/preference/components/TOTP.vue:89 +msgid "" +"TOTP is a two-factor authentication method that uses a time-based one-time " +"password algorithm." +msgstr "" + #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:209 msgid "Trash" msgstr "" +#: src/components/OTP/useOTPModal.ts:49 +msgid "Two-factor authentication required" +msgstr "" + #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12 #: src/views/notification/Notification.vue:13 msgid "Type" @@ -1980,7 +2078,7 @@ msgstr "Обновлено в" msgid "Updated successfully" msgstr "Обновлено успешно" -#: src/routes/index.ts:263 src/views/environment/Environment.vue:50 +#: src/routes/index.ts:268 src/views/environment/Environment.vue:50 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228 msgid "Upgrade" msgstr "Обновление" @@ -2011,16 +2109,24 @@ msgstr "Аптайм:" msgid "URL" msgstr "" +#: src/components/OTP/OTPAuthorization.vue:69 +msgid "Use OTP" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:65 +msgid "Use recovery code" +msgstr "" + #: src/components/ChatGPT/ChatGPT.vue:229 #, fuzzy msgid "User" msgstr "Пользователь" -#: src/views/other/Login.vue:65 +#: src/views/other/Login.vue:77 msgid "User is banned" msgstr "" -#: src/views/other/Login.vue:134 src/views/user/User.vue:9 +#: src/views/other/Login.vue:159 src/views/user/User.vue:9 msgid "Username" msgstr "Имя пользователя" @@ -2092,7 +2198,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/AuthSettings.vue:96 #: src/views/preference/BasicSettings.vue:100 msgid "Yes" msgstr "Да" diff --git a/app/src/language/vi_VN/app.po b/app/src/language/vi_VN/app.po index 5a8ff3ada..c444b48d5 100644 --- a/app/src/language/vi_VN/app.po +++ b/app/src/language/vi_VN/app.po @@ -9,7 +9,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: src/routes/index.ts:256 +#: src/views/preference/components/TOTP.vue:88 +msgid "2FA Settings" +msgstr "" + +#: src/routes/index.ts:261 msgid "About" msgstr "Tác giả" @@ -28,7 +32,7 @@ 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/envColumns.tsx:131 #: src/views/notification/Notification.vue:37 -#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47 +#: src/views/preference/AuthSettings.vue:27 src/views/stream/StreamList.vue:47 #: src/views/user/User.vue:43 msgid "Action" msgstr "Hành động" @@ -95,7 +99,7 @@ msgstr "" msgid "Arch" msgstr "" -#: src/views/preference/AuthSettings.vue:94 +#: src/views/preference/AuthSettings.vue:95 #, fuzzy msgid "Are you sure to delete this banned IP immediately?" msgstr "Bạn chắc chắn muốn xóa nó " @@ -153,7 +157,7 @@ msgstr "Hỏi ChatGPT" msgid "Assistant" msgstr "Trợ lý" -#: src/views/preference/AuthSettings.vue:17 +#: src/views/preference/AuthSettings.vue:18 msgid "Attempts" msgstr "" @@ -199,15 +203,15 @@ msgstr "Quay lại" msgid "Back to list" msgstr "" -#: src/views/preference/AuthSettings.vue:68 +#: src/views/preference/AuthSettings.vue:69 msgid "Ban Threshold Minutes" msgstr "" -#: src/views/preference/AuthSettings.vue:82 +#: src/views/preference/AuthSettings.vue:83 msgid "Banned IPs" msgstr "" -#: src/views/preference/AuthSettings.vue:20 +#: src/views/preference/AuthSettings.vue:21 msgid "Banned Until" msgstr "" @@ -364,6 +368,10 @@ msgstr "Cấu hình SSL" msgid "Connected" msgstr "Đã kết nối" +#: src/views/pty/Terminal.vue:120 +msgid "Connection lost, please refresh the page." +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111 #: src/views/domain/ngx_conf/LocationEditor.vue:125 #: src/views/domain/ngx_conf/LocationEditor.vue:97 @@ -407,6 +415,14 @@ msgstr "Chứng chỉ" msgid "Credentials" msgstr "Chứng chỉ" +#: src/views/preference/components/TOTP.vue:96 +msgid "Current account is enabled 2FA." +msgstr "" + +#: src/views/preference/components/TOTP.vue:93 +msgid "Current account is not enabled 2FA." +msgstr "" + #: src/views/system/Upgrade.vue:167 msgid "Current Version" msgstr "Phiên bản hiện tại" @@ -697,6 +713,16 @@ msgstr "Không thể bật %{conf_name} trên %{node_name}" msgid "Enable %{conf_name} in %{node_name} successfully" msgstr "Đã bật %{conf_name} trên %{node_name}" +#: src/views/preference/components/TOTP.vue:122 +#, fuzzy +msgid "Enable 2FA" +msgstr "Đã bật" + +#: src/views/preference/components/TOTP.vue:52 +#, fuzzy +msgid "Enable 2FA successfully" +msgstr "Đã bật" + #: src/views/domain/cert/components/ObtainCert.vue:70 msgid "Enable auto-renewal failed for %{name}" msgstr "Không thể bật tự động gia hạn SSL cho %{name}" @@ -900,12 +926,18 @@ msgstr "" msgid "If left blank, the default CA Dir will be used." msgstr "" -#: src/views/preference/AuthSettings.vue:60 +#: src/views/preference/AuthSettings.vue:61 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/preference/components/TOTP.vue:108 +msgid "" +"If you lose your mobile phone, you can use the recovery code to reset your " +"2FA." +msgstr "" + #: src/views/certificate/Certificate.vue:136 #, fuzzy msgid "Import" @@ -916,7 +948,7 @@ msgstr "Xuất" msgid "Import Certificate" msgstr "Chứng chỉ" -#: src/views/other/Login.vue:59 +#: src/views/other/Login.vue:71 #, 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" @@ -933,7 +965,16 @@ 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:135 +#: src/views/preference/components/TOTP.vue:144 +msgid "Input the code from the app:" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:49 +#: src/views/preference/components/TOTP.vue:157 +msgid "Input the recovery code:" +msgstr "" + +#: src/routes/index.ts:283 src/views/other/Install.vue:135 msgid "Install" msgstr "Cài đặt" @@ -955,7 +996,15 @@ msgstr "" msgid "Invalid" msgstr "Hợp lệ" -#: src/views/preference/AuthSettings.vue:14 +#: src/views/other/Login.vue:81 +msgid "Invalid 2FA or recovery code" +msgstr "" + +#: src/components/OTP/useOTPModal.ts:43 +msgid "Invalid passcode or recovery code" +msgstr "" + +#: src/views/preference/AuthSettings.vue:15 msgid "IP" msgstr "" @@ -1050,11 +1099,11 @@ msgstr "Locations" msgid "Log" msgstr "Log" -#: src/routes/index.ts:279 src/views/other/Login.vue:159 +#: src/routes/index.ts:289 src/views/other/Login.vue:192 msgid "Login" msgstr "Đăng nhập" -#: src/views/other/Login.vue:109 src/views/other/Login.vue:51 +#: src/views/other/Login.vue:125 src/views/other/Login.vue:60 msgid "Login successful" msgstr "Đăng nhập thành công" @@ -1098,7 +1147,7 @@ msgstr "Quản lý Website" msgid "Manage Streams" msgstr "Quản lý Website" -#: src/routes/index.ts:230 src/views/user/User.vue:50 +#: src/routes/index.ts:235 src/views/user/User.vue:50 msgid "Manage Users" msgstr "Người dùng" @@ -1106,7 +1155,7 @@ msgstr "Người dùng" msgid "Managed Certificate" msgstr "" -#: src/views/preference/AuthSettings.vue:74 +#: src/views/preference/AuthSettings.vue:75 msgid "Max Attempts" msgstr "" @@ -1240,7 +1289,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/AuthSettings.vue:97 #: src/views/preference/BasicSettings.vue:101 #: src/views/stream/StreamList.vue:165 msgid "No" @@ -1254,7 +1303,7 @@ msgstr "" msgid "Not After" msgstr "Không phải sau khi" -#: src/routes/index.ts:285 +#: src/routes/index.ts:295 msgid "Not Found" msgstr "Không tìm thấy" @@ -1272,7 +1321,7 @@ msgstr "Ghi chú" msgid "Notification" msgstr "Thông báo" -#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221 +#: src/components/Notification/Notification.vue:82 src/routes/index.ts:226 #, fuzzy msgid "Notifications" msgstr "Thông báo" @@ -1355,7 +1404,7 @@ msgstr "Ghi đè tập tin đã tồn tại" msgid "Params" msgstr "Tham số" -#: src/views/other/Login.vue:144 src/views/user/User.vue:18 +#: src/views/other/Login.vue:169 src/views/user/User.vue:18 msgid "Password" msgstr "Mật khẩu" @@ -1381,6 +1430,10 @@ msgstr "Nâng cấp core không thành công" msgid "Performing core upgrade" msgstr "Nâng cấp core" +#: src/components/OTP/OTPAuthorization.vue:37 +msgid "Please enter the 2FA code:" +msgstr "" + #: src/views/certificate/DNSCredential.vue:53 msgid "" "Please fill in the API authentication credentials provided by your DNS " @@ -1412,11 +1465,11 @@ msgstr "" msgid "Please input your E-mail!" msgstr "Vui lòng nhập E-mail của bạn!" -#: src/views/other/Install.vue:45 src/views/other/Login.vue:39 +#: src/views/other/Install.vue:45 src/views/other/Login.vue:43 msgid "Please input your password!" msgstr "Vui lòng nhập mật khẩu!" -#: src/views/other/Install.vue:39 src/views/other/Login.vue:33 +#: src/views/other/Install.vue:39 src/views/other/Login.vue:37 msgid "Please input your username!" msgstr "Vui lòng nhập username!" @@ -1436,7 +1489,7 @@ msgstr "" msgid "Pre-release" msgstr "" -#: src/routes/index.ts:239 src/views/preference/Preference.vue:105 +#: src/routes/index.ts:244 src/views/preference/Preference.vue:105 msgid "Preference" msgstr "Cài đặt" @@ -1480,6 +1533,19 @@ msgstr "" msgid "Recovered Successfully" msgstr "Xoá thành công" +#: src/components/OTP/OTPAuthorization.vue:56 +#: src/views/preference/components/TOTP.vue:167 +msgid "Recovery" +msgstr "" + +#: src/views/preference/components/TOTP.vue:101 +msgid "Recovery Code" +msgstr "" + +#: src/views/preference/components/TOTP.vue:110 +msgid "Recovery Code:" +msgstr "" + #: src/views/preference/BasicSettings.vue:68 msgid "Recursive Nameservers" msgstr "" @@ -1534,11 +1600,11 @@ msgstr "Đang tải lại" msgid "Reloading nginx" msgstr "Tải lại nginx" -#: src/views/preference/AuthSettings.vue:101 +#: src/views/preference/AuthSettings.vue:102 msgid "Remove" msgstr "" -#: src/views/preference/AuthSettings.vue:47 +#: src/views/preference/AuthSettings.vue:48 #, fuzzy msgid "Remove successfully" msgstr "Xoá thành công" @@ -1583,6 +1649,11 @@ msgstr "Yêu cầu có chứa tham số sai" msgid "Reset" msgstr "Đặt lại" +#: src/views/preference/components/TOTP.vue:130 +#, fuzzy +msgid "Reset 2FA" +msgstr "Đặt lại" + #: src/components/NginxControl/NginxControl.vue:93 msgid "Restart" msgstr "Khởi động lại" @@ -1632,6 +1703,10 @@ msgstr "Lưu thành công" msgid "Saved successfully" msgstr "Lưu thành công" +#: src/views/preference/components/TOTP.vue:91 +msgid "Scan the QR code with your mobile phone to add the account to the app." +msgstr "" + #: src/views/certificate/DNSChallenge.vue:89 msgid "SDK" msgstr "" @@ -1655,7 +1730,9 @@ msgstr "Gửi" #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81 #: src/views/environment/BatchUpgrader.vue:57 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:69 -#: src/views/preference/AuthSettings.vue:49 +#: src/views/preference/AuthSettings.vue:50 +#: src/views/preference/components/TOTP.vue:42 +#: src/views/preference/components/TOTP.vue:55 #: 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" @@ -1729,7 +1806,7 @@ msgstr "" msgid "SSL Certificate Path" msgstr "" -#: src/views/other/Login.vue:170 +#: src/views/other/Login.vue:203 msgid "SSO Login" msgstr "" @@ -1813,7 +1890,7 @@ msgstr "Gia hạn chứng chỉ SSL thành công" msgid "Sync to" msgstr "" -#: src/routes/index.ts:248 +#: src/routes/index.ts:253 msgid "System" msgstr "Thông tin" @@ -1822,7 +1899,7 @@ msgstr "Thông tin" msgid "Target" msgstr "Mục tiêu" -#: src/routes/index.ts:172 src/views/pty/Terminal.vue:91 +#: src/routes/index.ts:172 src/views/pty/Terminal.vue:114 msgid "Terminal" msgstr "Terminal" @@ -1865,6 +1942,11 @@ msgstr "" msgid "The path exists, but the file is not a private key" msgstr "" +#: src/views/preference/components/TOTP.vue:109 +msgid "" +"The recovery code is only displayed once, please save it in a safe place." +msgstr "" + #: src/views/dashboard/Environments.vue:148 msgid "" "The remote Nginx UI version is not compatible with the local Nginx UI " @@ -1923,7 +2005,7 @@ msgid "" "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}." msgstr "" -#: src/views/preference/AuthSettings.vue:59 +#: src/views/preference/AuthSettings.vue:60 #: src/views/preference/LogrotateSettings.vue:12 msgid "Tips" msgstr "" @@ -1932,6 +2014,12 @@ msgstr "" msgid "Title" msgstr "Tiêu đề" +#: src/views/preference/components/TOTP.vue:90 +msgid "" +"To enable it, you need to install the Google or Microsoft Authenticator app " +"on your mobile phone." +msgstr "" + #: src/views/domain/ngx_conf/NgxConfigEditor.vue:44 msgid "" "To make sure the certification auto-renewal can work normally, we need to " @@ -1948,14 +2036,24 @@ msgstr "" msgid "Token is not valid" msgstr "" -#: src/views/other/Login.vue:62 +#: src/views/other/Login.vue:74 msgid "Too many login failed attempts, please try again later" msgstr "" +#: src/views/preference/components/TOTP.vue:89 +msgid "" +"TOTP is a two-factor authentication method that uses a time-based one-time " +"password algorithm." +msgstr "" + #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:209 msgid "Trash" msgstr "" +#: src/components/OTP/useOTPModal.ts:49 +msgid "Two-factor authentication required" +msgstr "" + #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12 #: src/views/notification/Notification.vue:13 msgid "Type" @@ -1976,7 +2074,7 @@ msgstr "Ngày cập nhật" msgid "Updated successfully" msgstr "Cập nhật thành công" -#: src/routes/index.ts:263 src/views/environment/Environment.vue:50 +#: src/routes/index.ts:268 src/views/environment/Environment.vue:50 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228 msgid "Upgrade" msgstr "Cập nhật" @@ -2007,16 +2105,24 @@ msgstr "Thời gian hoạt động:" msgid "URL" msgstr "" +#: src/components/OTP/OTPAuthorization.vue:69 +msgid "Use OTP" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:65 +msgid "Use recovery code" +msgstr "" + #: src/components/ChatGPT/ChatGPT.vue:229 #, fuzzy msgid "User" msgstr "Người dùng" -#: src/views/other/Login.vue:65 +#: src/views/other/Login.vue:77 msgid "User is banned" msgstr "" -#: src/views/other/Login.vue:134 src/views/user/User.vue:9 +#: src/views/other/Login.vue:159 src/views/user/User.vue:9 msgid "Username" msgstr "Username" @@ -2091,7 +2197,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/AuthSettings.vue:96 #: src/views/preference/BasicSettings.vue:100 msgid "Yes" msgstr "Có" diff --git a/app/src/language/zh_CN/app.mo b/app/src/language/zh_CN/app.mo index 9f04da9b0..689854084 100644 Binary files a/app/src/language/zh_CN/app.mo and b/app/src/language/zh_CN/app.mo differ diff --git a/app/src/language/zh_CN/app.po b/app/src/language/zh_CN/app.po index 6b8c20496..598b5bdbb 100644 --- a/app/src/language/zh_CN/app.po +++ b/app/src/language/zh_CN/app.po @@ -13,7 +13,11 @@ msgstr "" "Generated-By: easygettext\n" "X-Generator: Poedit 3.4.4\n" -#: src/routes/index.ts:256 +#: src/views/preference/components/TOTP.vue:88 +msgid "2FA Settings" +msgstr "2FA 设置" + +#: src/routes/index.ts:261 msgid "About" msgstr "关于" @@ -31,7 +35,7 @@ msgstr "ACME 用户" #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34 #: src/views/domain/DomainList.vue:47 src/views/environment/envColumns.tsx:131 #: src/views/notification/Notification.vue:37 -#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47 +#: src/views/preference/AuthSettings.vue:27 src/views/stream/StreamList.vue:47 #: src/views/user/User.vue:43 msgid "Action" msgstr "操作" @@ -95,7 +99,7 @@ msgstr "API Token" msgid "Arch" msgstr "架构" -#: src/views/preference/AuthSettings.vue:94 +#: src/views/preference/AuthSettings.vue:95 msgid "Are you sure to delete this banned IP immediately?" msgstr "您确定要立即删除这个被禁用的 IP 吗?" @@ -144,7 +148,7 @@ msgstr "与ChatGPT聊天" msgid "Assistant" msgstr "助手" -#: src/views/preference/AuthSettings.vue:17 +#: src/views/preference/AuthSettings.vue:18 msgid "Attempts" msgstr "尝试次数" @@ -188,15 +192,15 @@ msgstr "返回首页" msgid "Back to list" msgstr "返回列表" -#: src/views/preference/AuthSettings.vue:68 +#: src/views/preference/AuthSettings.vue:69 msgid "Ban Threshold Minutes" msgstr "禁止阈值(分钟)" -#: src/views/preference/AuthSettings.vue:82 +#: src/views/preference/AuthSettings.vue:83 msgid "Banned IPs" msgstr "禁止 IP 列表" -#: src/views/preference/AuthSettings.vue:20 +#: src/views/preference/AuthSettings.vue:21 msgid "Banned Until" msgstr "禁用至" @@ -343,6 +347,10 @@ msgstr "配置 SSL" msgid "Connected" msgstr "已连接" +#: src/views/pty/Terminal.vue:120 +msgid "Connection lost, please refresh the page." +msgstr "连接中断,请刷新页面。" + #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111 #: src/views/domain/ngx_conf/LocationEditor.vue:125 #: src/views/domain/ngx_conf/LocationEditor.vue:97 @@ -385,6 +393,14 @@ msgstr "DNS 凭证" msgid "Credentials" msgstr "凭证" +#: src/views/preference/components/TOTP.vue:96 +msgid "Current account is enabled 2FA." +msgstr "当前账户已启用二步验证。" + +#: src/views/preference/components/TOTP.vue:93 +msgid "Current account is not enabled 2FA." +msgstr "当前用户未启用二步验证。" + #: src/views/system/Upgrade.vue:167 msgid "Current Version" msgstr "当前版本" @@ -652,6 +668,14 @@ msgstr "在%{node_name}中启用%{conf_name}失败" msgid "Enable %{conf_name} in %{node_name} successfully" msgstr "成功启用%{node_name}中的%{conf_name}" +#: src/views/preference/components/TOTP.vue:122 +msgid "Enable 2FA" +msgstr "启用二步验证" + +#: src/views/preference/components/TOTP.vue:52 +msgid "Enable 2FA successfully" +msgstr "二步验证启用成功" + #: src/views/domain/cert/components/ObtainCert.vue:70 msgid "Enable auto-renewal failed for %{name}" msgstr "启用 %{name} 自动续签失败" @@ -844,7 +868,7 @@ msgstr "HTTP01" msgid "If left blank, the default CA Dir will be used." msgstr "如果留空,则使用默认 CA Dir。" -#: src/views/preference/AuthSettings.vue:60 +#: src/views/preference/AuthSettings.vue:61 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." @@ -852,6 +876,12 @@ msgstr "" "如果某个 IP 的登录失败次数达到禁用阈值分钟内的最大尝试次数,该 IP 将被禁止登" "录一段时间。" +#: src/views/preference/components/TOTP.vue:108 +msgid "" +"If you lose your mobile phone, you can use the recovery code to reset your " +"2FA." +msgstr "如果丢失了手机,可以使用恢复代码重置二步验证。" + #: src/views/certificate/Certificate.vue:136 msgid "Import" msgstr "导入" @@ -860,7 +890,7 @@ msgstr "导入" msgid "Import Certificate" msgstr "导入证书" -#: src/views/other/Login.vue:59 +#: src/views/other/Login.vue:71 msgid "Incorrect username or password" msgstr "用户名或密码错误" @@ -876,7 +906,16 @@ msgstr "初始化核心升级程序错误" msgid "Initialing core upgrader" msgstr "初始化核心升级器" -#: src/routes/index.ts:273 src/views/other/Install.vue:135 +#: src/views/preference/components/TOTP.vue:144 +msgid "Input the code from the app:" +msgstr "输入应用程序中的代码:" + +#: src/components/OTP/OTPAuthorization.vue:49 +#: src/views/preference/components/TOTP.vue:157 +msgid "Input the recovery code:" +msgstr "输入恢复代码:" + +#: src/routes/index.ts:283 src/views/other/Install.vue:135 msgid "Install" msgstr "安装" @@ -896,7 +935,15 @@ msgstr "间隔" msgid "Invalid" msgstr "无效的" -#: src/views/preference/AuthSettings.vue:14 +#: src/views/other/Login.vue:81 +msgid "Invalid 2FA or recovery code" +msgstr "无效的二步验证码或恢复密码" + +#: src/components/OTP/useOTPModal.ts:43 +msgid "Invalid passcode or recovery code" +msgstr "二次验证码或恢复代码无效" + +#: src/views/preference/AuthSettings.vue:15 msgid "IP" msgstr "IP" @@ -981,11 +1028,11 @@ msgstr "Locations" msgid "Log" msgstr "日志" -#: src/routes/index.ts:279 src/views/other/Login.vue:159 +#: src/routes/index.ts:289 src/views/other/Login.vue:192 msgid "Login" msgstr "登录" -#: src/views/other/Login.vue:109 src/views/other/Login.vue:51 +#: src/views/other/Login.vue:125 src/views/other/Login.vue:60 msgid "Login successful" msgstr "登录成功" @@ -1031,7 +1078,7 @@ msgstr "网站管理" msgid "Manage Streams" msgstr "管理 Stream" -#: src/routes/index.ts:230 src/views/user/User.vue:50 +#: src/routes/index.ts:235 src/views/user/User.vue:50 msgid "Manage Users" msgstr "用户管理" @@ -1039,7 +1086,7 @@ msgstr "用户管理" msgid "Managed Certificate" msgstr "托管证书" -#: src/views/preference/AuthSettings.vue:74 +#: src/views/preference/AuthSettings.vue:75 msgid "Max Attempts" msgstr "最大尝试次数" @@ -1165,7 +1212,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/AuthSettings.vue:97 #: src/views/preference/BasicSettings.vue:101 #: src/views/stream/StreamList.vue:165 msgid "No" @@ -1179,7 +1226,7 @@ msgstr "节点密钥" msgid "Not After" msgstr "有效期" -#: src/routes/index.ts:285 +#: src/routes/index.ts:295 msgid "Not Found" msgstr "找不到页面" @@ -1196,7 +1243,7 @@ msgstr "注意" msgid "Notification" msgstr "通知" -#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221 +#: src/components/Notification/Notification.vue:82 src/routes/index.ts:226 msgid "Notifications" msgstr "通知" @@ -1276,7 +1323,7 @@ msgstr "覆盖现有文件" msgid "Params" msgstr "参数" -#: src/views/other/Login.vue:144 src/views/user/User.vue:18 +#: src/views/other/Login.vue:169 src/views/user/User.vue:18 msgid "Password" msgstr "密码" @@ -1302,6 +1349,10 @@ msgstr "执行核心升级错误" msgid "Performing core upgrade" msgstr "正在进行核心升级" +#: src/components/OTP/OTPAuthorization.vue:37 +msgid "Please enter the 2FA code:" +msgstr "请输入二步验证码:" + #: src/views/certificate/DNSCredential.vue:53 msgid "" "Please fill in the API authentication credentials provided by your DNS " @@ -1331,11 +1382,11 @@ msgstr "请输入名称,这将被用作新配置的文件名!" msgid "Please input your E-mail!" msgstr "请输入您的邮箱!" -#: src/views/other/Install.vue:45 src/views/other/Login.vue:39 +#: src/views/other/Install.vue:45 src/views/other/Login.vue:43 msgid "Please input your password!" msgstr "请输入您的密码!" -#: src/views/other/Install.vue:39 src/views/other/Login.vue:33 +#: src/views/other/Install.vue:39 src/views/other/Login.vue:37 msgid "Please input your username!" msgstr "请输入您的用户名!" @@ -1355,7 +1406,7 @@ msgstr "请至少选择一个节点!" msgid "Pre-release" msgstr "预发布" -#: src/routes/index.ts:239 src/views/preference/Preference.vue:105 +#: src/routes/index.ts:244 src/views/preference/Preference.vue:105 msgid "Preference" msgstr "偏好设置" @@ -1397,6 +1448,19 @@ msgstr "恢复" msgid "Recovered Successfully" msgstr "恢复成功" +#: src/components/OTP/OTPAuthorization.vue:56 +#: src/views/preference/components/TOTP.vue:167 +msgid "Recovery" +msgstr "恢复" + +#: src/views/preference/components/TOTP.vue:101 +msgid "Recovery Code" +msgstr "恢复代码" + +#: src/views/preference/components/TOTP.vue:110 +msgid "Recovery Code:" +msgstr "恢复代码:" + #: src/views/preference/BasicSettings.vue:68 msgid "Recursive Nameservers" msgstr "递归域名服务器" @@ -1446,11 +1510,11 @@ msgstr "重载中" msgid "Reloading nginx" msgstr "正在重载 Nginx" -#: src/views/preference/AuthSettings.vue:101 +#: src/views/preference/AuthSettings.vue:102 msgid "Remove" msgstr "删除" -#: src/views/preference/AuthSettings.vue:47 +#: src/views/preference/AuthSettings.vue:48 msgid "Remove successfully" msgstr "移除成功" @@ -1488,6 +1552,10 @@ msgstr "请求参数错误" msgid "Reset" msgstr "重置" +#: src/views/preference/components/TOTP.vue:130 +msgid "Reset 2FA" +msgstr "重置二步验证" + #: src/components/NginxControl/NginxControl.vue:93 msgid "Restart" msgstr "重启" @@ -1535,6 +1603,10 @@ msgstr "保存成功" msgid "Saved successfully" msgstr "保存成功" +#: src/views/preference/components/TOTP.vue:91 +msgid "Scan the QR code with your mobile phone to add the account to the app." +msgstr "用手机扫描二维码,将账户添加到应用程序中。" + #: src/views/certificate/DNSChallenge.vue:89 msgid "SDK" msgstr "SDK" @@ -1558,7 +1630,9 @@ msgstr "上传" #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81 #: src/views/environment/BatchUpgrader.vue:57 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:69 -#: src/views/preference/AuthSettings.vue:49 +#: src/views/preference/AuthSettings.vue:50 +#: src/views/preference/components/TOTP.vue:42 +#: src/views/preference/components/TOTP.vue:55 #: 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" @@ -1629,7 +1703,7 @@ msgstr "SSL证书密钥路径" msgid "SSL Certificate Path" msgstr "SSL证书路径" -#: src/views/other/Login.vue:170 +#: src/views/other/Login.vue:203 msgid "SSO Login" msgstr "SSO 登录" @@ -1707,7 +1781,7 @@ msgstr "同步证书成功" msgid "Sync to" msgstr "同步到" -#: src/routes/index.ts:248 +#: src/routes/index.ts:253 msgid "System" msgstr "系统" @@ -1716,7 +1790,7 @@ msgstr "系统" msgid "Target" msgstr "目标" -#: src/routes/index.ts:172 src/views/pty/Terminal.vue:91 +#: src/routes/index.ts:172 src/views/pty/Terminal.vue:114 msgid "Terminal" msgstr "终端" @@ -1758,6 +1832,11 @@ msgstr "路径存在,但文件不是证书" msgid "The path exists, but the file is not a private key" msgstr "路径存在,但文件不是私钥" +#: src/views/preference/components/TOTP.vue:109 +msgid "" +"The recovery code is only displayed once, please save it in a safe place." +msgstr "恢复密码只会显示一次,请妥善保存。" + #: src/views/dashboard/Environments.vue:148 msgid "" "The remote Nginx UI version is not compatible with the local Nginx UI " @@ -1816,7 +1895,7 @@ msgid "" "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}." msgstr "将 %{nodeNames} 上的 Nginx UI 升级或重新安装到 %{version} 版本。" -#: src/views/preference/AuthSettings.vue:59 +#: src/views/preference/AuthSettings.vue:60 #: src/views/preference/LogrotateSettings.vue:12 msgid "Tips" msgstr "提示" @@ -1825,6 +1904,13 @@ msgstr "提示" msgid "Title" msgstr "标题" +#: src/views/preference/components/TOTP.vue:90 +msgid "" +"To enable it, you need to install the Google or Microsoft Authenticator app " +"on your mobile phone." +msgstr "" +"要启用该功能,您需要在手机上安装 Google 或 Microsoft Authenticator 应用程序。" + #: src/views/domain/ngx_conf/NgxConfigEditor.vue:44 msgid "" "To make sure the certification auto-renewal can work normally, we need to " @@ -1839,14 +1925,24 @@ msgstr "" msgid "Token is not valid" msgstr "Token 无效" -#: src/views/other/Login.vue:62 +#: src/views/other/Login.vue:74 msgid "Too many login failed attempts, please try again later" msgstr "登录失败次数过多,请稍后再试" +#: src/views/preference/components/TOTP.vue:89 +msgid "" +"TOTP is a two-factor authentication method that uses a time-based one-time " +"password algorithm." +msgstr "TOTP 是一种使用基于时间的一次性密码算法的双因素身份验证方法。" + #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:209 msgid "Trash" msgstr "回收站" +#: src/components/OTP/useOTPModal.ts:49 +msgid "Two-factor authentication required" +msgstr "需要两步验证" + #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12 #: src/views/notification/Notification.vue:13 msgid "Type" @@ -1866,7 +1962,7 @@ msgstr "修改时间" msgid "Updated successfully" msgstr "更新成功" -#: src/routes/index.ts:263 src/views/environment/Environment.vue:50 +#: src/routes/index.ts:268 src/views/environment/Environment.vue:50 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228 msgid "Upgrade" msgstr "升级" @@ -1895,15 +1991,23 @@ msgstr "运行时间:" msgid "URL" msgstr "URL" +#: src/components/OTP/OTPAuthorization.vue:69 +msgid "Use OTP" +msgstr "使用二步验证码" + +#: src/components/OTP/OTPAuthorization.vue:65 +msgid "Use recovery code" +msgstr "使用恢复代码" + #: src/components/ChatGPT/ChatGPT.vue:229 msgid "User" msgstr "用户" -#: src/views/other/Login.vue:65 +#: src/views/other/Login.vue:77 msgid "User is banned" msgstr "用户被禁止" -#: src/views/other/Login.vue:134 src/views/user/User.vue:9 +#: src/views/other/Login.vue:159 src/views/user/User.vue:9 msgid "Username" msgstr "用户名" @@ -1971,7 +2075,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/AuthSettings.vue:96 #: src/views/preference/BasicSettings.vue:100 msgid "Yes" msgstr "是的" diff --git a/app/src/language/zh_TW/app.po b/app/src/language/zh_TW/app.po index 4b60fe7ad..12070fb33 100644 --- a/app/src/language/zh_TW/app.po +++ b/app/src/language/zh_TW/app.po @@ -14,7 +14,11 @@ msgstr "" "Generated-By: easygettext\n" "X-Generator: Poedit 3.4.1\n" -#: src/routes/index.ts:256 +#: src/views/preference/components/TOTP.vue:88 +msgid "2FA Settings" +msgstr "" + +#: src/routes/index.ts:261 msgid "About" msgstr "關於" @@ -33,7 +37,7 @@ msgstr "使用者名稱" #: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34 #: src/views/domain/DomainList.vue:47 src/views/environment/envColumns.tsx:131 #: src/views/notification/Notification.vue:37 -#: src/views/preference/AuthSettings.vue:26 src/views/stream/StreamList.vue:47 +#: src/views/preference/AuthSettings.vue:27 src/views/stream/StreamList.vue:47 #: src/views/user/User.vue:43 msgid "Action" msgstr "操作" @@ -100,7 +104,7 @@ msgstr "API Token" msgid "Arch" msgstr "架構" -#: src/views/preference/AuthSettings.vue:94 +#: src/views/preference/AuthSettings.vue:95 #, fuzzy msgid "Are you sure to delete this banned IP immediately?" msgstr "您確定要刪除嗎?" @@ -155,7 +159,7 @@ msgstr "向 ChatGPT 尋求幫助" msgid "Assistant" msgstr "助理" -#: src/views/preference/AuthSettings.vue:17 +#: src/views/preference/AuthSettings.vue:18 msgid "Attempts" msgstr "" @@ -200,15 +204,15 @@ msgstr "返回首頁" msgid "Back to list" msgstr "" -#: src/views/preference/AuthSettings.vue:68 +#: src/views/preference/AuthSettings.vue:69 msgid "Ban Threshold Minutes" msgstr "" -#: src/views/preference/AuthSettings.vue:82 +#: src/views/preference/AuthSettings.vue:83 msgid "Banned IPs" msgstr "" -#: src/views/preference/AuthSettings.vue:20 +#: src/views/preference/AuthSettings.vue:21 msgid "Banned Until" msgstr "" @@ -361,6 +365,10 @@ msgstr "設定 SSL" msgid "Connected" msgstr "已連結" +#: src/views/pty/Terminal.vue:120 +msgid "Connection lost, please refresh the page." +msgstr "" + #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111 #: src/views/domain/ngx_conf/LocationEditor.vue:125 #: src/views/domain/ngx_conf/LocationEditor.vue:97 @@ -404,6 +412,14 @@ msgstr "認證" msgid "Credentials" msgstr "認證資訊" +#: src/views/preference/components/TOTP.vue:96 +msgid "Current account is enabled 2FA." +msgstr "" + +#: src/views/preference/components/TOTP.vue:93 +msgid "Current account is not enabled 2FA." +msgstr "" + #: src/views/system/Upgrade.vue:167 msgid "Current Version" msgstr "目前版本" @@ -680,6 +696,16 @@ msgstr "在 %{node_name} 啟用 %{conf_name} 失敗" msgid "Enable %{conf_name} in %{node_name} successfully" msgstr "成功在 %{node_name} 啟用 %{conf_name}" +#: src/views/preference/components/TOTP.vue:122 +#, fuzzy +msgid "Enable 2FA" +msgstr "啟用" + +#: src/views/preference/components/TOTP.vue:52 +#, fuzzy +msgid "Enable 2FA successfully" +msgstr "啟用成功" + #: src/views/domain/cert/components/ObtainCert.vue:70 msgid "Enable auto-renewal failed for %{name}" msgstr "啟用 %{name} 自動續簽失敗" @@ -878,12 +904,18 @@ msgstr "HTTP01" msgid "If left blank, the default CA Dir will be used." msgstr "" -#: src/views/preference/AuthSettings.vue:60 +#: src/views/preference/AuthSettings.vue:61 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/preference/components/TOTP.vue:108 +msgid "" +"If you lose your mobile phone, you can use the recovery code to reset your " +"2FA." +msgstr "" + #: src/views/certificate/Certificate.vue:136 #, fuzzy msgid "Import" @@ -894,7 +926,7 @@ msgstr "匯出" msgid "Import Certificate" msgstr "憑證狀態" -#: src/views/other/Login.vue:59 +#: src/views/other/Login.vue:71 #, fuzzy msgid "Incorrect username or password" msgstr "使用者名稱或密碼不正確" @@ -911,7 +943,16 @@ msgstr "初始化核心升級程式錯誤" msgid "Initialing core upgrader" msgstr "正在初始化核心升級程式" -#: src/routes/index.ts:273 src/views/other/Install.vue:135 +#: src/views/preference/components/TOTP.vue:144 +msgid "Input the code from the app:" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:49 +#: src/views/preference/components/TOTP.vue:157 +msgid "Input the recovery code:" +msgstr "" + +#: src/routes/index.ts:283 src/views/other/Install.vue:135 msgid "Install" msgstr "安裝" @@ -932,7 +973,15 @@ msgstr "" msgid "Invalid" msgstr "無效的郵箱!" -#: src/views/preference/AuthSettings.vue:14 +#: src/views/other/Login.vue:81 +msgid "Invalid 2FA or recovery code" +msgstr "" + +#: src/components/OTP/useOTPModal.ts:43 +msgid "Invalid passcode or recovery code" +msgstr "" + +#: src/views/preference/AuthSettings.vue:15 msgid "IP" msgstr "" @@ -1025,11 +1074,11 @@ msgstr "Locations" msgid "Log" msgstr "登入" -#: src/routes/index.ts:279 src/views/other/Login.vue:159 +#: src/routes/index.ts:289 src/views/other/Login.vue:192 msgid "Login" msgstr "登入" -#: src/views/other/Login.vue:109 src/views/other/Login.vue:51 +#: src/views/other/Login.vue:125 src/views/other/Login.vue:60 msgid "Login successful" msgstr "登入成功" @@ -1071,7 +1120,7 @@ msgstr "管理網站" msgid "Manage Streams" msgstr "管理網站" -#: src/routes/index.ts:230 src/views/user/User.vue:50 +#: src/routes/index.ts:235 src/views/user/User.vue:50 msgid "Manage Users" msgstr "管理使用者" @@ -1080,7 +1129,7 @@ msgstr "管理使用者" msgid "Managed Certificate" msgstr "更換憑證" -#: src/views/preference/AuthSettings.vue:74 +#: src/views/preference/AuthSettings.vue:75 msgid "Max Attempts" msgstr "" @@ -1209,7 +1258,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/AuthSettings.vue:97 #: src/views/preference/BasicSettings.vue:101 #: src/views/stream/StreamList.vue:165 msgid "No" @@ -1223,7 +1272,7 @@ msgstr "Node Secret" msgid "Not After" msgstr "" -#: src/routes/index.ts:285 +#: src/routes/index.ts:295 msgid "Not Found" msgstr "找不到頁面" @@ -1241,7 +1290,7 @@ msgstr "備註" msgid "Notification" msgstr "憑證" -#: src/components/Notification/Notification.vue:82 src/routes/index.ts:221 +#: src/components/Notification/Notification.vue:82 src/routes/index.ts:226 #, fuzzy msgid "Notifications" msgstr "憑證" @@ -1322,7 +1371,7 @@ msgstr "覆蓋現有檔案" msgid "Params" msgstr "參數" -#: src/views/other/Login.vue:144 src/views/user/User.vue:18 +#: src/views/other/Login.vue:169 src/views/user/User.vue:18 msgid "Password" msgstr "密碼" @@ -1348,6 +1397,10 @@ msgstr "執行核心升級錯誤" msgid "Performing core upgrade" msgstr "正在執行核心升級" +#: src/components/OTP/OTPAuthorization.vue:37 +msgid "Please enter the 2FA code:" +msgstr "" + #: src/views/certificate/DNSCredential.vue:53 msgid "" "Please fill in the API authentication credentials provided by your DNS " @@ -1378,11 +1431,11 @@ msgstr "請輸入名稱,這將作為新設定的檔名!" msgid "Please input your E-mail!" msgstr "請輸入您的電子郵件!" -#: src/views/other/Install.vue:45 src/views/other/Login.vue:39 +#: src/views/other/Install.vue:45 src/views/other/Login.vue:43 msgid "Please input your password!" msgstr "請輸入您的密碼!" -#: src/views/other/Install.vue:39 src/views/other/Login.vue:33 +#: src/views/other/Install.vue:39 src/views/other/Login.vue:37 msgid "Please input your username!" msgstr "請輸入您的使用者名稱!" @@ -1402,7 +1455,7 @@ msgstr "請至少選擇一個節點!" msgid "Pre-release" msgstr "預先發布" -#: src/routes/index.ts:239 src/views/preference/Preference.vue:105 +#: src/routes/index.ts:244 src/views/preference/Preference.vue:105 msgid "Preference" msgstr "偏好設定" @@ -1445,6 +1498,19 @@ msgstr "" msgid "Recovered Successfully" msgstr "儲存成功" +#: src/components/OTP/OTPAuthorization.vue:56 +#: src/views/preference/components/TOTP.vue:167 +msgid "Recovery" +msgstr "" + +#: src/views/preference/components/TOTP.vue:101 +msgid "Recovery Code" +msgstr "" + +#: src/views/preference/components/TOTP.vue:110 +msgid "Recovery Code:" +msgstr "" + #: src/views/preference/BasicSettings.vue:68 #, fuzzy msgid "Recursive Nameservers" @@ -1499,11 +1565,11 @@ msgstr "重新載入中" msgid "Reloading nginx" msgstr "正在重新載入 Nginx" -#: src/views/preference/AuthSettings.vue:101 +#: src/views/preference/AuthSettings.vue:102 msgid "Remove" msgstr "" -#: src/views/preference/AuthSettings.vue:47 +#: src/views/preference/AuthSettings.vue:48 #, fuzzy msgid "Remove successfully" msgstr "儲存成功" @@ -1548,6 +1614,11 @@ msgstr "請求參數錯誤" msgid "Reset" msgstr "重設" +#: src/views/preference/components/TOTP.vue:130 +#, fuzzy +msgid "Reset 2FA" +msgstr "重設" + #: src/components/NginxControl/NginxControl.vue:93 msgid "Restart" msgstr "重新啟動" @@ -1595,6 +1666,10 @@ msgstr "儲存成功" msgid "Saved successfully" msgstr "儲存成功" +#: src/views/preference/components/TOTP.vue:91 +msgid "Scan the QR code with your mobile phone to add the account to the app." +msgstr "" + #: src/views/certificate/DNSChallenge.vue:89 msgid "SDK" msgstr "" @@ -1618,7 +1693,9 @@ msgstr "傳送" #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81 #: src/views/environment/BatchUpgrader.vue:57 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:69 -#: src/views/preference/AuthSettings.vue:49 +#: src/views/preference/AuthSettings.vue:50 +#: src/views/preference/components/TOTP.vue:42 +#: src/views/preference/components/TOTP.vue:55 #: 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" @@ -1694,7 +1771,7 @@ msgstr "SSL 憑證金鑰路徑" msgid "SSL Certificate Path" msgstr "SSL 憑證路徑" -#: src/views/other/Login.vue:170 +#: src/views/other/Login.vue:203 #, fuzzy msgid "SSO Login" msgstr "登入" @@ -1778,7 +1855,7 @@ msgstr "更換憑證" msgid "Sync to" msgstr "" -#: src/routes/index.ts:248 +#: src/routes/index.ts:253 msgid "System" msgstr "系統" @@ -1787,7 +1864,7 @@ msgstr "系統" msgid "Target" msgstr "目標" -#: src/routes/index.ts:172 src/views/pty/Terminal.vue:91 +#: src/routes/index.ts:172 src/views/pty/Terminal.vue:114 msgid "Terminal" msgstr "終端機" @@ -1832,6 +1909,11 @@ msgstr "SSL 憑證金鑰路徑" msgid "The path exists, but the file is not a private key" msgstr "" +#: src/views/preference/components/TOTP.vue:109 +msgid "" +"The recovery code is only displayed once, please save it in a safe place." +msgstr "" + #: src/views/dashboard/Environments.vue:148 msgid "" "The remote Nginx UI version is not compatible with the local Nginx UI " @@ -1892,7 +1974,7 @@ msgid "" "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}." msgstr "" -#: src/views/preference/AuthSettings.vue:59 +#: src/views/preference/AuthSettings.vue:60 #: src/views/preference/LogrotateSettings.vue:12 msgid "Tips" msgstr "" @@ -1901,6 +1983,12 @@ msgstr "" msgid "Title" msgstr "" +#: src/views/preference/components/TOTP.vue:90 +msgid "" +"To enable it, you need to install the Google or Microsoft Authenticator app " +"on your mobile phone." +msgstr "" + #: src/views/domain/ngx_conf/NgxConfigEditor.vue:44 msgid "" "To make sure the certification auto-renewal can work normally, we need to " @@ -1915,14 +2003,24 @@ msgstr "" msgid "Token is not valid" msgstr "" -#: src/views/other/Login.vue:62 +#: src/views/other/Login.vue:74 msgid "Too many login failed attempts, please try again later" msgstr "" +#: src/views/preference/components/TOTP.vue:89 +msgid "" +"TOTP is a two-factor authentication method that uses a time-based one-time " +"password algorithm." +msgstr "" + #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:209 msgid "Trash" msgstr "" +#: src/components/OTP/useOTPModal.ts:49 +msgid "Two-factor authentication required" +msgstr "" + #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12 #: src/views/notification/Notification.vue:13 msgid "Type" @@ -1942,7 +2040,7 @@ msgstr "更新時間" msgid "Updated successfully" msgstr "更新成功" -#: src/routes/index.ts:263 src/views/environment/Environment.vue:50 +#: src/routes/index.ts:268 src/views/environment/Environment.vue:50 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228 msgid "Upgrade" msgstr "升級" @@ -1972,15 +2070,23 @@ msgstr "運作時間:" msgid "URL" msgstr "URL" +#: src/components/OTP/OTPAuthorization.vue:69 +msgid "Use OTP" +msgstr "" + +#: src/components/OTP/OTPAuthorization.vue:65 +msgid "Use recovery code" +msgstr "" + #: src/components/ChatGPT/ChatGPT.vue:229 msgid "User" msgstr "使用者名稱" -#: src/views/other/Login.vue:65 +#: src/views/other/Login.vue:77 msgid "User is banned" msgstr "" -#: src/views/other/Login.vue:134 src/views/user/User.vue:9 +#: src/views/other/Login.vue:159 src/views/user/User.vue:9 msgid "Username" msgstr "使用者名稱" @@ -2052,7 +2158,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/AuthSettings.vue:96 #: src/views/preference/BasicSettings.vue:100 msgid "Yes" msgstr "是的" diff --git a/app/src/views/other/Login.vue b/app/src/views/other/Login.vue index 96bfbdf30..b2e3a2ca5 100644 --- a/app/src/views/other/Login.vue +++ b/app/src/views/other/Login.vue @@ -1,13 +1,13 @@