浏览代码

feat: copy secret to register otp #551

Jacky 7 月之前
父节点
当前提交
cb3599e721

+ 206 - 223
api/user/otp.go

@@ -1,245 +1,228 @@
 package user
 package user
 
 
 import (
 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/model"
-	"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"
+    "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/model"
+    "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) {
 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,
-	})
+    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
+    }
+
+    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":  otpKey.Secret(),
+        "qr_code": base64Str,
+    })
 }
 }
 
 
 func EnrollTOTP(c *gin.Context) {
 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[:]),
-	})
+    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
+    }
+
+    if ok := totp.Validate(json.Passcode, json.Secret); !ok {
+        c.JSON(http.StatusNotAcceptable, gin.H{
+            "message": "Invalid passcode",
+        })
+        return
+    }
+
+    ciphertext, err := crypto.AesEncrypt([]byte(json.Secret))
+    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) {
 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",
-	})
+    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) {
 func OTPStatus(c *gin.Context) {
-	status := false
-	u, ok := c.Get("user")
-	if ok {
-		status = u.(*model.Auth).EnabledOTP()
-	}
-	c.JSON(http.StatusOK, gin.H{
-		"status": status,
-	})
+    status := false
+    u, ok := c.Get("user")
+    if ok {
+        status = u.(*model.Auth).EnabledOTP()
+    }
+    c.JSON(http.StatusOK, gin.H{
+        "status": status,
+    })
 }
 }
 
 
 func SecureSessionStatus(c *gin.Context) {
 func SecureSessionStatus(c *gin.Context) {
-	u, ok := c.Get("user")
-	if !ok || !u.(*model.Auth).EnabledOTP() {
-		c.JSON(http.StatusOK, gin.H{
-			"status": false,
-		})
-		return
-	}
-	ssid := c.GetHeader("X-Secure-Session-ID")
-	if ssid == "" {
-		ssid = c.Query("X-Secure-Session-ID")
-	}
-	if ssid == "" {
-		c.JSON(http.StatusOK, gin.H{
-			"status": false,
-		})
-		return
-	}
-
-	if user.VerifySecureSessionID(ssid, u.(*model.Auth).ID) {
-		c.JSON(http.StatusOK, gin.H{
-			"status": true,
-		})
-		return
-	}
-
-	c.JSON(http.StatusOK, gin.H{
-		"status": false,
-	})
+    u, ok := c.Get("user")
+    if !ok || !u.(*model.Auth).EnabledOTP() {
+        c.JSON(http.StatusOK, gin.H{
+            "status": false,
+        })
+        return
+    }
+    ssid := c.GetHeader("X-Secure-Session-ID")
+    if ssid == "" {
+        ssid = c.Query("X-Secure-Session-ID")
+    }
+    if ssid == "" {
+        c.JSON(http.StatusOK, gin.H{
+            "status": false,
+        })
+        return
+    }
+
+    if user.VerifySecureSessionID(ssid, u.(*model.Auth).ID) {
+        c.JSON(http.StatusOK, gin.H{
+            "status": true,
+        })
+        return
+    }
+
+    c.JSON(http.StatusOK, gin.H{
+        "status": false,
+    })
 }
 }
 
 
 func StartSecure2FASession(c *gin.Context) {
 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,
-	})
+    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,
+    })
 }
 }

+ 26 - 18
app/src/language/en/app.po

@@ -13,7 +13,7 @@ msgstr ""
 msgid "2FA"
 msgid "2FA"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:88
+#: src/views/preference/components/TOTP.vue:90
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr ""
 msgstr ""
 
 
@@ -259,6 +259,10 @@ msgstr ""
 msgid "CADir"
 msgid "CADir"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:150
+msgid "Can't scan? Use text key binding"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
@@ -452,11 +456,11 @@ msgstr ""
 msgid "Credentials"
 msgid "Credentials"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:96
+#: src/views/preference/components/TOTP.vue:98
 msgid "Current account is enabled 2FA."
 msgid "Current account is enabled 2FA."
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:93
+#: src/views/preference/components/TOTP.vue:95
 msgid "Current account is not enabled 2FA."
 msgid "Current account is not enabled 2FA."
 msgstr ""
 msgstr ""
 
 
@@ -753,12 +757,12 @@ msgstr ""
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:122
+#: src/views/preference/components/TOTP.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Enable 2FA"
 msgid "Enable 2FA"
 msgstr "Enabled"
 msgstr "Enabled"
 
 
-#: src/views/preference/components/TOTP.vue:52
+#: src/views/preference/components/TOTP.vue:54
 #, fuzzy
 #, fuzzy
 msgid "Enable 2FA successfully"
 msgid "Enable 2FA successfully"
 msgstr "Enabled successfully"
 msgstr "Enabled successfully"
@@ -976,7 +980,7 @@ msgid ""
 "ban threshold minutes, the ip will be banned for a period of time."
 "ban threshold minutes, the ip will be banned for a period of time."
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:108
+#: src/views/preference/components/TOTP.vue:110
 msgid ""
 msgid ""
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "2FA."
 "2FA."
@@ -1013,12 +1017,12 @@ msgstr ""
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:144
+#: src/views/preference/components/TOTP.vue:157
 msgid "Input the code from the app:"
 msgid "Input the code from the app:"
 msgstr ""
 msgstr ""
 
 
 #: src/components/OTP/OTPAuthorization.vue:49
 #: src/components/OTP/OTPAuthorization.vue:49
-#: src/views/preference/components/TOTP.vue:157
+#: src/views/preference/components/TOTP.vue:170
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
@@ -1631,15 +1635,15 @@ msgid "Recovered Successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
 #: src/components/OTP/OTPAuthorization.vue:56
 #: src/components/OTP/OTPAuthorization.vue:56
-#: src/views/preference/components/TOTP.vue:164
+#: src/views/preference/components/TOTP.vue:177
 msgid "Recovery"
 msgid "Recovery"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:101
+#: src/views/preference/components/TOTP.vue:103
 msgid "Recovery Code"
 msgid "Recovery Code"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:110
+#: src/views/preference/components/TOTP.vue:112
 msgid "Recovery Code:"
 msgid "Recovery Code:"
 msgstr ""
 msgstr ""
 
 
@@ -1779,7 +1783,7 @@ msgstr ""
 msgid "Reset"
 msgid "Reset"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:130
+#: src/views/preference/components/TOTP.vue:132
 msgid "Reset 2FA"
 msgid "Reset 2FA"
 msgstr ""
 msgstr ""
 
 
@@ -1832,7 +1836,7 @@ msgstr "Saved successfully"
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/views/preference/components/TOTP.vue:91
+#: src/views/preference/components/TOTP.vue:93
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 msgstr ""
 
 
@@ -1840,6 +1844,10 @@ msgstr ""
 msgid "SDK"
 msgid "SDK"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:149
+msgid "Secret has been copied"
+msgstr ""
+
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgid "Selector"
 msgstr ""
 msgstr ""
@@ -1862,8 +1870,8 @@ msgstr "Send"
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/preference/AuthSettings.vue:49
 #: src/views/preference/AuthSettings.vue:49
-#: src/views/preference/components/TOTP.vue:42
-#: src/views/preference/components/TOTP.vue:55
+#: src/views/preference/components/TOTP.vue:44
+#: src/views/preference/components/TOTP.vue:57
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
@@ -2112,7 +2120,7 @@ msgstr "Certificate Status"
 msgid "The path exists, but the file is not a private key"
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:109
+#: src/views/preference/components/TOTP.vue:111
 msgid ""
 msgid ""
 "The recovery code is only displayed once, please save it in a safe place."
 "The recovery code is only displayed once, please save it in a safe place."
 msgstr ""
 msgstr ""
@@ -2185,7 +2193,7 @@ msgstr ""
 msgid "Title"
 msgid "Title"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:90
+#: src/views/preference/components/TOTP.vue:92
 msgid ""
 msgid ""
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "on your mobile phone."
 "on your mobile phone."
@@ -2207,7 +2215,7 @@ msgstr ""
 msgid "Too many login failed attempts, please try again later"
 msgid "Too many login failed attempts, please try again later"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:89
+#: src/views/preference/components/TOTP.vue:91
 msgid ""
 msgid ""
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "password algorithm."
 "password algorithm."

+ 26 - 18
app/src/language/es/app.po

@@ -20,7 +20,7 @@ msgstr ""
 msgid "2FA"
 msgid "2FA"
 msgstr "2FA"
 msgstr "2FA"
 
 
-#: src/views/preference/components/TOTP.vue:88
+#: src/views/preference/components/TOTP.vue:90
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "Configuración de 2FA"
 msgstr "Configuración de 2FA"
 
 
@@ -249,6 +249,10 @@ msgstr "Dir CA"
 msgid "CADir"
 msgid "CADir"
 msgstr "Directorio CA"
 msgstr "Directorio CA"
 
 
+#: src/views/preference/components/TOTP.vue:150
+msgid "Can't scan? Use text key binding"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
@@ -428,12 +432,12 @@ msgstr "Credencial"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Credenciales"
 msgstr "Credenciales"
 
 
-#: src/views/preference/components/TOTP.vue:96
+#: src/views/preference/components/TOTP.vue:98
 msgid "Current account is enabled 2FA."
 msgid "Current account is enabled 2FA."
 msgstr ""
 msgstr ""
 "La cuenta actual tiene habilitada la autenticación de dos factores (2FA)."
 "La cuenta actual tiene habilitada la autenticación de dos factores (2FA)."
 
 
-#: src/views/preference/components/TOTP.vue:93
+#: src/views/preference/components/TOTP.vue:95
 msgid "Current account is not enabled 2FA."
 msgid "Current account is not enabled 2FA."
 msgstr ""
 msgstr ""
 "La cuenta actual no tiene habilitada la autenticación de dos factores (2FA)."
 "La cuenta actual no tiene habilitada la autenticación de dos factores (2FA)."
@@ -716,11 +720,11 @@ msgstr "Falló el habilitado de %{conf_name} en %{node_name}"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "Habilitado exitoso de %{conf_name} en %{node_name}"
 msgstr "Habilitado exitoso de %{conf_name} en %{node_name}"
 
 
-#: src/views/preference/components/TOTP.vue:122
+#: src/views/preference/components/TOTP.vue:124
 msgid "Enable 2FA"
 msgid "Enable 2FA"
 msgstr "Habilitar 2FA"
 msgstr "Habilitar 2FA"
 
 
-#: src/views/preference/components/TOTP.vue:52
+#: src/views/preference/components/TOTP.vue:54
 msgid "Enable 2FA successfully"
 msgid "Enable 2FA successfully"
 msgstr "Habilitar 2FA exitoso"
 msgstr "Habilitar 2FA exitoso"
 
 
@@ -932,7 +936,7 @@ msgstr ""
 "el máximo de intentos en los minutos del umbral de prohibición, la IP será "
 "el máximo de intentos en los minutos del umbral de prohibición, la IP será "
 "bloqueada por un período de tiempo."
 "bloqueada por un período de tiempo."
 
 
-#: src/views/preference/components/TOTP.vue:108
+#: src/views/preference/components/TOTP.vue:110
 msgid ""
 msgid ""
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "2FA."
 "2FA."
@@ -972,12 +976,12 @@ msgstr "Error de actualización de kernel inicial"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Inicializando la actualización del kernel"
 msgstr "Inicializando la actualización del kernel"
 
 
-#: src/views/preference/components/TOTP.vue:144
+#: src/views/preference/components/TOTP.vue:157
 msgid "Input the code from the app:"
 msgid "Input the code from the app:"
 msgstr "Ingrese el código de la aplicación:"
 msgstr "Ingrese el código de la aplicación:"
 
 
 #: src/components/OTP/OTPAuthorization.vue:49
 #: src/components/OTP/OTPAuthorization.vue:49
-#: src/views/preference/components/TOTP.vue:157
+#: src/views/preference/components/TOTP.vue:170
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "Ingrese el código de recuperación:"
 msgstr "Ingrese el código de recuperación:"
 
 
@@ -1575,15 +1579,15 @@ msgid "Recovered Successfully"
 msgstr "Recuperado con éxito"
 msgstr "Recuperado con éxito"
 
 
 #: src/components/OTP/OTPAuthorization.vue:56
 #: src/components/OTP/OTPAuthorization.vue:56
-#: src/views/preference/components/TOTP.vue:164
+#: src/views/preference/components/TOTP.vue:177
 msgid "Recovery"
 msgid "Recovery"
 msgstr "Recuperación"
 msgstr "Recuperación"
 
 
-#: src/views/preference/components/TOTP.vue:101
+#: src/views/preference/components/TOTP.vue:103
 msgid "Recovery Code"
 msgid "Recovery Code"
 msgstr "Código de Recuperación"
 msgstr "Código de Recuperación"
 
 
-#: src/views/preference/components/TOTP.vue:110
+#: src/views/preference/components/TOTP.vue:112
 msgid "Recovery Code:"
 msgid "Recovery Code:"
 msgstr "Código de Recuperación:"
 msgstr "Código de Recuperación:"
 
 
@@ -1710,7 +1714,7 @@ msgstr "Pedido con parámetros incorrectos"
 msgid "Reset"
 msgid "Reset"
 msgstr "Limpiar"
 msgstr "Limpiar"
 
 
-#: src/views/preference/components/TOTP.vue:130
+#: src/views/preference/components/TOTP.vue:132
 msgid "Reset 2FA"
 msgid "Reset 2FA"
 msgstr "Restablecer 2FA"
 msgstr "Restablecer 2FA"
 
 
@@ -1761,7 +1765,7 @@ msgstr "Guardado con éxito"
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Guardado con éxito"
 msgstr "Guardado con éxito"
 
 
-#: src/views/preference/components/TOTP.vue:91
+#: src/views/preference/components/TOTP.vue:93
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 msgstr ""
 "Escanee el código QR con su teléfono móvil para agregar la cuenta a la "
 "Escanee el código QR con su teléfono móvil para agregar la cuenta a la "
@@ -1771,6 +1775,10 @@ msgstr ""
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
 
 
+#: src/views/preference/components/TOTP.vue:149
+msgid "Secret has been copied"
+msgstr ""
+
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgid "Selector"
 msgstr "Seleccionador"
 msgstr "Seleccionador"
@@ -1793,8 +1801,8 @@ msgstr "Enviado"
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/preference/AuthSettings.vue:49
 #: src/views/preference/AuthSettings.vue:49
-#: src/views/preference/components/TOTP.vue:42
-#: src/views/preference/components/TOTP.vue:55
+#: src/views/preference/components/TOTP.vue:44
+#: src/views/preference/components/TOTP.vue:57
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
@@ -2037,7 +2045,7 @@ msgstr "La ruta existe, pero el archivo no es un certificado"
 msgid "The path exists, but the file is not a private key"
 msgid "The path exists, but the file is not a private key"
 msgstr "La ruta existe, pero el archivo no es una clave privada"
 msgstr "La ruta existe, pero el archivo no es una clave privada"
 
 
-#: src/views/preference/components/TOTP.vue:109
+#: src/views/preference/components/TOTP.vue:111
 msgid ""
 msgid ""
 "The recovery code is only displayed once, please save it in a safe place."
 "The recovery code is only displayed once, please save it in a safe place."
 msgstr ""
 msgstr ""
@@ -2118,7 +2126,7 @@ msgstr "Consejos"
 msgid "Title"
 msgid "Title"
 msgstr "Título"
 msgstr "Título"
 
 
-#: src/views/preference/components/TOTP.vue:90
+#: src/views/preference/components/TOTP.vue:92
 msgid ""
 msgid ""
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "on your mobile phone."
 "on your mobile phone."
@@ -2148,7 +2156,7 @@ msgstr ""
 "Demasiados intentos fallidos de inicio de sesión, por favor intente "
 "Demasiados intentos fallidos de inicio de sesión, por favor intente "
 "nuevamente más tarde"
 "nuevamente más tarde"
 
 
-#: src/views/preference/components/TOTP.vue:89
+#: src/views/preference/components/TOTP.vue:91
 msgid ""
 msgid ""
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "password algorithm."
 "password algorithm."

+ 26 - 18
app/src/language/fr_FR/app.po

@@ -15,7 +15,7 @@ msgstr ""
 msgid "2FA"
 msgid "2FA"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:88
+#: src/views/preference/components/TOTP.vue:90
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr ""
 msgstr ""
 
 
@@ -261,6 +261,10 @@ msgstr ""
 msgid "CADir"
 msgid "CADir"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:150
+msgid "Can't scan? Use text key binding"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
@@ -452,11 +456,11 @@ msgstr "Identifiant"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Identifiants"
 msgstr "Identifiants"
 
 
-#: src/views/preference/components/TOTP.vue:96
+#: src/views/preference/components/TOTP.vue:98
 msgid "Current account is enabled 2FA."
 msgid "Current account is enabled 2FA."
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:93
+#: src/views/preference/components/TOTP.vue:95
 msgid "Current account is not enabled 2FA."
 msgid "Current account is not enabled 2FA."
 msgstr ""
 msgstr ""
 
 
@@ -753,12 +757,12 @@ msgstr ""
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:122
+#: src/views/preference/components/TOTP.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Enable 2FA"
 msgid "Enable 2FA"
 msgstr "Activé"
 msgstr "Activé"
 
 
-#: src/views/preference/components/TOTP.vue:52
+#: src/views/preference/components/TOTP.vue:54
 #, fuzzy
 #, fuzzy
 msgid "Enable 2FA successfully"
 msgid "Enable 2FA successfully"
 msgstr "Activé avec succès"
 msgstr "Activé avec succès"
@@ -977,7 +981,7 @@ msgid ""
 "ban threshold minutes, the ip will be banned for a period of time."
 "ban threshold minutes, the ip will be banned for a period of time."
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:108
+#: src/views/preference/components/TOTP.vue:110
 msgid ""
 msgid ""
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "2FA."
 "2FA."
@@ -1016,12 +1020,12 @@ msgstr "Erreur du programme de mise à niveau initial du core"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Initialisation du programme de mise à niveau du core"
 msgstr "Initialisation du programme de mise à niveau du core"
 
 
-#: src/views/preference/components/TOTP.vue:144
+#: src/views/preference/components/TOTP.vue:157
 msgid "Input the code from the app:"
 msgid "Input the code from the app:"
 msgstr ""
 msgstr ""
 
 
 #: src/components/OTP/OTPAuthorization.vue:49
 #: src/components/OTP/OTPAuthorization.vue:49
-#: src/views/preference/components/TOTP.vue:157
+#: src/views/preference/components/TOTP.vue:170
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
@@ -1637,15 +1641,15 @@ msgid "Recovered Successfully"
 msgstr "Enregistré avec succès"
 msgstr "Enregistré avec succès"
 
 
 #: src/components/OTP/OTPAuthorization.vue:56
 #: src/components/OTP/OTPAuthorization.vue:56
-#: src/views/preference/components/TOTP.vue:164
+#: src/views/preference/components/TOTP.vue:177
 msgid "Recovery"
 msgid "Recovery"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:101
+#: src/views/preference/components/TOTP.vue:103
 msgid "Recovery Code"
 msgid "Recovery Code"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:110
+#: src/views/preference/components/TOTP.vue:112
 msgid "Recovery Code:"
 msgid "Recovery Code:"
 msgstr ""
 msgstr ""
 
 
@@ -1786,7 +1790,7 @@ msgstr ""
 msgid "Reset"
 msgid "Reset"
 msgstr "Réinitialiser"
 msgstr "Réinitialiser"
 
 
-#: src/views/preference/components/TOTP.vue:130
+#: src/views/preference/components/TOTP.vue:132
 #, fuzzy
 #, fuzzy
 msgid "Reset 2FA"
 msgid "Reset 2FA"
 msgstr "Réinitialiser"
 msgstr "Réinitialiser"
@@ -1838,7 +1842,7 @@ msgstr "Sauvegarde réussie"
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Enregistré avec succès"
 msgstr "Enregistré avec succès"
 
 
-#: src/views/preference/components/TOTP.vue:91
+#: src/views/preference/components/TOTP.vue:93
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 msgstr ""
 
 
@@ -1846,6 +1850,10 @@ msgstr ""
 msgid "SDK"
 msgid "SDK"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:149
+msgid "Secret has been copied"
+msgstr ""
+
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgid "Selector"
 msgstr "Sélecteur"
 msgstr "Sélecteur"
@@ -1868,8 +1876,8 @@ msgstr "Envoyer"
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/preference/AuthSettings.vue:49
 #: src/views/preference/AuthSettings.vue:49
-#: src/views/preference/components/TOTP.vue:42
-#: src/views/preference/components/TOTP.vue:55
+#: src/views/preference/components/TOTP.vue:44
+#: src/views/preference/components/TOTP.vue:57
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
@@ -2118,7 +2126,7 @@ msgstr "Chemin de la clé du certificat SSL"
 msgid "The path exists, but the file is not a private key"
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:109
+#: src/views/preference/components/TOTP.vue:111
 msgid ""
 msgid ""
 "The recovery code is only displayed once, please save it in a safe place."
 "The recovery code is only displayed once, please save it in a safe place."
 msgstr ""
 msgstr ""
@@ -2195,7 +2203,7 @@ msgstr ""
 msgid "Title"
 msgid "Title"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:90
+#: src/views/preference/components/TOTP.vue:92
 msgid ""
 msgid ""
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "on your mobile phone."
 "on your mobile phone."
@@ -2221,7 +2229,7 @@ msgstr ""
 msgid "Too many login failed attempts, please try again later"
 msgid "Too many login failed attempts, please try again later"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:89
+#: src/views/preference/components/TOTP.vue:91
 msgid ""
 msgid ""
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "password algorithm."
 "password algorithm."

+ 26 - 18
app/src/language/ko_KR/app.po

@@ -18,7 +18,7 @@ msgstr ""
 msgid "2FA"
 msgid "2FA"
 msgstr "2FA"
 msgstr "2FA"
 
 
-#: src/views/preference/components/TOTP.vue:88
+#: src/views/preference/components/TOTP.vue:90
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "2FA 설정"
 msgstr "2FA 설정"
 
 
@@ -247,6 +247,10 @@ msgstr "CA 디렉토리"
 msgid "CADir"
 msgid "CADir"
 msgstr "CA 디렉토리"
 msgstr "CA 디렉토리"
 
 
+#: src/views/preference/components/TOTP.vue:150
+msgid "Can't scan? Use text key binding"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
@@ -431,11 +435,11 @@ msgstr "인증 정보"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "인증 정보들"
 msgstr "인증 정보들"
 
 
-#: src/views/preference/components/TOTP.vue:96
+#: src/views/preference/components/TOTP.vue:98
 msgid "Current account is enabled 2FA."
 msgid "Current account is enabled 2FA."
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:93
+#: src/views/preference/components/TOTP.vue:95
 msgid "Current account is not enabled 2FA."
 msgid "Current account is not enabled 2FA."
 msgstr ""
 msgstr ""
 
 
@@ -716,12 +720,12 @@ msgstr "%{node_name}에서 %{conf_name} 활성화 실패"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "%{node_name}에서 %{conf_name} 성공적으로 활성화됨"
 msgstr "%{node_name}에서 %{conf_name} 성공적으로 활성화됨"
 
 
-#: src/views/preference/components/TOTP.vue:122
+#: src/views/preference/components/TOTP.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Enable 2FA"
 msgid "Enable 2FA"
 msgstr "활성화"
 msgstr "활성화"
 
 
-#: src/views/preference/components/TOTP.vue:52
+#: src/views/preference/components/TOTP.vue:54
 #, fuzzy
 #, fuzzy
 msgid "Enable 2FA successfully"
 msgid "Enable 2FA successfully"
 msgstr "성공적으로 활성화"
 msgstr "성공적으로 활성화"
@@ -938,7 +942,7 @@ msgid ""
 "ban threshold minutes, the ip will be banned for a period of time."
 "ban threshold minutes, the ip will be banned for a period of time."
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:108
+#: src/views/preference/components/TOTP.vue:110
 msgid ""
 msgid ""
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "2FA."
 "2FA."
@@ -976,12 +980,12 @@ msgstr "초기 코어 업그레이더 오류"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "코어 업그레이더 초기화"
 msgstr "코어 업그레이더 초기화"
 
 
-#: src/views/preference/components/TOTP.vue:144
+#: src/views/preference/components/TOTP.vue:157
 msgid "Input the code from the app:"
 msgid "Input the code from the app:"
 msgstr ""
 msgstr ""
 
 
 #: src/components/OTP/OTPAuthorization.vue:49
 #: src/components/OTP/OTPAuthorization.vue:49
-#: src/views/preference/components/TOTP.vue:157
+#: src/views/preference/components/TOTP.vue:170
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
@@ -1601,15 +1605,15 @@ msgid "Recovered Successfully"
 msgstr "성공적으로 제거됨"
 msgstr "성공적으로 제거됨"
 
 
 #: src/components/OTP/OTPAuthorization.vue:56
 #: src/components/OTP/OTPAuthorization.vue:56
-#: src/views/preference/components/TOTP.vue:164
+#: src/views/preference/components/TOTP.vue:177
 msgid "Recovery"
 msgid "Recovery"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:101
+#: src/views/preference/components/TOTP.vue:103
 msgid "Recovery Code"
 msgid "Recovery Code"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:110
+#: src/views/preference/components/TOTP.vue:112
 msgid "Recovery Code:"
 msgid "Recovery Code:"
 msgstr ""
 msgstr ""
 
 
@@ -1751,7 +1755,7 @@ msgstr "잘못된 매개변수로 요청됨"
 msgid "Reset"
 msgid "Reset"
 msgstr "재설정"
 msgstr "재설정"
 
 
-#: src/views/preference/components/TOTP.vue:130
+#: src/views/preference/components/TOTP.vue:132
 #, fuzzy
 #, fuzzy
 msgid "Reset 2FA"
 msgid "Reset 2FA"
 msgstr "재설정"
 msgstr "재설정"
@@ -1805,7 +1809,7 @@ msgstr "성공적으로 저장됨"
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "성공적으로 저장됨"
 msgstr "성공적으로 저장됨"
 
 
-#: src/views/preference/components/TOTP.vue:91
+#: src/views/preference/components/TOTP.vue:93
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 msgstr ""
 
 
@@ -1813,6 +1817,10 @@ msgstr ""
 msgid "SDK"
 msgid "SDK"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:149
+msgid "Secret has been copied"
+msgstr ""
+
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgid "Selector"
 msgstr "선택"
 msgstr "선택"
@@ -1835,8 +1843,8 @@ msgstr "보내기"
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/preference/AuthSettings.vue:49
 #: src/views/preference/AuthSettings.vue:49
-#: src/views/preference/components/TOTP.vue:42
-#: src/views/preference/components/TOTP.vue:55
+#: src/views/preference/components/TOTP.vue:44
+#: src/views/preference/components/TOTP.vue:57
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
@@ -2084,7 +2092,7 @@ msgstr "Certificate Status"
 msgid "The path exists, but the file is not a private key"
 msgid "The path exists, but the file is not a private key"
 msgstr "경로는 존재하지만 파일은 개인 키가 아닙니다"
 msgstr "경로는 존재하지만 파일은 개인 키가 아닙니다"
 
 
-#: src/views/preference/components/TOTP.vue:109
+#: src/views/preference/components/TOTP.vue:111
 msgid ""
 msgid ""
 "The recovery code is only displayed once, please save it in a safe place."
 "The recovery code is only displayed once, please save it in a safe place."
 msgstr ""
 msgstr ""
@@ -2159,7 +2167,7 @@ msgstr "팁"
 msgid "Title"
 msgid "Title"
 msgstr "제목"
 msgstr "제목"
 
 
-#: src/views/preference/components/TOTP.vue:90
+#: src/views/preference/components/TOTP.vue:92
 msgid ""
 msgid ""
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "on your mobile phone."
 "on your mobile phone."
@@ -2184,7 +2192,7 @@ msgstr "토큰이 유효하지 않습니다"
 msgid "Too many login failed attempts, please try again later"
 msgid "Too many login failed attempts, please try again later"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:89
+#: src/views/preference/components/TOTP.vue:91
 msgid ""
 msgid ""
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "password algorithm."
 "password algorithm."

+ 26 - 18
app/src/language/messages.pot

@@ -6,7 +6,7 @@ msgstr ""
 msgid "2FA"
 msgid "2FA"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:88
+#: src/views/preference/components/TOTP.vue:90
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr ""
 msgstr ""
 
 
@@ -247,6 +247,10 @@ msgstr ""
 msgid "CADir"
 msgid "CADir"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:150
+msgid "Can't scan? Use text key binding"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
@@ -428,11 +432,11 @@ msgstr ""
 msgid "Credentials"
 msgid "Credentials"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:96
+#: src/views/preference/components/TOTP.vue:98
 msgid "Current account is enabled 2FA."
 msgid "Current account is enabled 2FA."
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:93
+#: src/views/preference/components/TOTP.vue:95
 msgid "Current account is not enabled 2FA."
 msgid "Current account is not enabled 2FA."
 msgstr ""
 msgstr ""
 
 
@@ -718,11 +722,11 @@ msgstr ""
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:122
+#: src/views/preference/components/TOTP.vue:124
 msgid "Enable 2FA"
 msgid "Enable 2FA"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:52
+#: src/views/preference/components/TOTP.vue:54
 msgid "Enable 2FA successfully"
 msgid "Enable 2FA successfully"
 msgstr ""
 msgstr ""
 
 
@@ -937,7 +941,7 @@ msgstr ""
 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."
 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 ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:108
+#: src/views/preference/components/TOTP.vue:110
 msgid "If you lose your mobile phone, you can use the recovery code to reset your 2FA."
 msgid "If you lose your mobile phone, you can use the recovery code to reset your 2FA."
 msgstr ""
 msgstr ""
 
 
@@ -971,12 +975,12 @@ msgstr ""
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:144
+#: src/views/preference/components/TOTP.vue:157
 msgid "Input the code from the app:"
 msgid "Input the code from the app:"
 msgstr ""
 msgstr ""
 
 
 #: src/components/OTP/OTPAuthorization.vue:49
 #: src/components/OTP/OTPAuthorization.vue:49
-#: src/views/preference/components/TOTP.vue:157
+#: src/views/preference/components/TOTP.vue:170
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
@@ -1556,15 +1560,15 @@ msgid "Recovered Successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/components/OTP/OTPAuthorization.vue:56
 #: src/components/OTP/OTPAuthorization.vue:56
-#: src/views/preference/components/TOTP.vue:164
+#: src/views/preference/components/TOTP.vue:177
 msgid "Recovery"
 msgid "Recovery"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:101
+#: src/views/preference/components/TOTP.vue:103
 msgid "Recovery Code"
 msgid "Recovery Code"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:110
+#: src/views/preference/components/TOTP.vue:112
 msgid "Recovery Code:"
 msgid "Recovery Code:"
 msgstr ""
 msgstr ""
 
 
@@ -1685,7 +1689,7 @@ msgstr ""
 msgid "Reset"
 msgid "Reset"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:130
+#: src/views/preference/components/TOTP.vue:132
 msgid "Reset 2FA"
 msgid "Reset 2FA"
 msgstr ""
 msgstr ""
 
 
@@ -1740,7 +1744,7 @@ msgstr ""
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:91
+#: src/views/preference/components/TOTP.vue:93
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 msgstr ""
 
 
@@ -1748,6 +1752,10 @@ msgstr ""
 msgid "SDK"
 msgid "SDK"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:149
+msgid "Secret has been copied"
+msgstr ""
+
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgid "Selector"
 msgstr ""
 msgstr ""
@@ -1772,8 +1780,8 @@ msgstr ""
 #: src/views/environment/Environment.vue:15
 #: src/views/environment/Environment.vue:15
 #: src/views/other/Install.vue:68
 #: src/views/other/Install.vue:68
 #: src/views/preference/AuthSettings.vue:49
 #: src/views/preference/AuthSettings.vue:49
-#: src/views/preference/components/TOTP.vue:42
-#: src/views/preference/components/TOTP.vue:55
+#: src/views/preference/components/TOTP.vue:44
+#: src/views/preference/components/TOTP.vue:57
 #: src/views/preference/Preference.vue:83
 #: src/views/preference/Preference.vue:83
 #: src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81
 #: src/views/stream/StreamList.vue:81
@@ -1993,7 +2001,7 @@ msgstr ""
 msgid "The path exists, but the file is not a private key"
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:109
+#: src/views/preference/components/TOTP.vue:111
 msgid "The recovery code is only displayed once, please save it in a safe place."
 msgid "The recovery code is only displayed once, please save it in a safe place."
 msgstr ""
 msgstr ""
 
 
@@ -2054,7 +2062,7 @@ msgstr ""
 msgid "Title"
 msgid "Title"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:90
+#: src/views/preference/components/TOTP.vue:92
 msgid "To enable it, you need to install the Google or Microsoft Authenticator app on your mobile phone."
 msgid "To enable it, you need to install the Google or Microsoft Authenticator app on your mobile phone."
 msgstr ""
 msgstr ""
 
 
@@ -2070,7 +2078,7 @@ msgstr ""
 msgid "Too many login failed attempts, please try again later"
 msgid "Too many login failed attempts, please try again later"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:89
+#: src/views/preference/components/TOTP.vue:91
 msgid "TOTP is a two-factor authentication method that uses a time-based one-time password algorithm."
 msgid "TOTP is a two-factor authentication method that uses a time-based one-time password algorithm."
 msgstr ""
 msgstr ""
 
 

+ 26 - 18
app/src/language/ru_RU/app.po

@@ -18,7 +18,7 @@ msgstr ""
 msgid "2FA"
 msgid "2FA"
 msgstr "2ФА"
 msgstr "2ФА"
 
 
-#: src/views/preference/components/TOTP.vue:88
+#: src/views/preference/components/TOTP.vue:90
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "Настройки 2ФА"
 msgstr "Настройки 2ФА"
 
 
@@ -250,6 +250,10 @@ msgstr "Директория корневого сертификата"
 msgid "CADir"
 msgid "CADir"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:150
+msgid "Can't scan? Use text key binding"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
@@ -429,11 +433,11 @@ msgstr "Учетные данные"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Учетные данные"
 msgstr "Учетные данные"
 
 
-#: src/views/preference/components/TOTP.vue:96
+#: src/views/preference/components/TOTP.vue:98
 msgid "Current account is enabled 2FA."
 msgid "Current account is enabled 2FA."
 msgstr "Текущая учетная запись имеет включенную 2ФА."
 msgstr "Текущая учетная запись имеет включенную 2ФА."
 
 
-#: src/views/preference/components/TOTP.vue:93
+#: src/views/preference/components/TOTP.vue:95
 msgid "Current account is not enabled 2FA."
 msgid "Current account is not enabled 2FA."
 msgstr ""
 msgstr ""
 "Текущая учетная запись не имеет включенной двухфакторной аутентификации."
 "Текущая учетная запись не имеет включенной двухфакторной аутентификации."
@@ -716,11 +720,11 @@ msgstr "Включение %{conf_name} in %{node_name} нипалучилася
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "Включение %{conf_name} in %{node_name} успешно"
 msgstr "Включение %{conf_name} in %{node_name} успешно"
 
 
-#: src/views/preference/components/TOTP.vue:122
+#: src/views/preference/components/TOTP.vue:124
 msgid "Enable 2FA"
 msgid "Enable 2FA"
 msgstr "Включить 2ФА"
 msgstr "Включить 2ФА"
 
 
-#: src/views/preference/components/TOTP.vue:52
+#: src/views/preference/components/TOTP.vue:54
 msgid "Enable 2FA successfully"
 msgid "Enable 2FA successfully"
 msgstr "Двухфакторная аутентификация успешно включена"
 msgstr "Двухфакторная аутентификация успешно включена"
 
 
@@ -931,7 +935,7 @@ msgstr ""
 "количества попыток в течение пороговых минут блокировки, IP будет "
 "количества попыток в течение пороговых минут блокировки, IP будет "
 "заблокирован на определенный период времени."
 "заблокирован на определенный период времени."
 
 
-#: src/views/preference/components/TOTP.vue:108
+#: src/views/preference/components/TOTP.vue:110
 msgid ""
 msgid ""
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "2FA."
 "2FA."
@@ -971,12 +975,12 @@ msgstr "Ошибка первоначального обновления ядр
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Инициализация программы обновления ядра"
 msgstr "Инициализация программы обновления ядра"
 
 
-#: src/views/preference/components/TOTP.vue:144
+#: src/views/preference/components/TOTP.vue:157
 msgid "Input the code from the app:"
 msgid "Input the code from the app:"
 msgstr "Введите код из приложения:"
 msgstr "Введите код из приложения:"
 
 
 #: src/components/OTP/OTPAuthorization.vue:49
 #: src/components/OTP/OTPAuthorization.vue:49
-#: src/views/preference/components/TOTP.vue:157
+#: src/views/preference/components/TOTP.vue:170
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "Введите код восстановления:"
 msgstr "Введите код восстановления:"
 
 
@@ -1573,15 +1577,15 @@ msgid "Recovered Successfully"
 msgstr "Восстановлено успешно"
 msgstr "Восстановлено успешно"
 
 
 #: src/components/OTP/OTPAuthorization.vue:56
 #: src/components/OTP/OTPAuthorization.vue:56
-#: src/views/preference/components/TOTP.vue:164
+#: src/views/preference/components/TOTP.vue:177
 msgid "Recovery"
 msgid "Recovery"
 msgstr "Восстановление"
 msgstr "Восстановление"
 
 
-#: src/views/preference/components/TOTP.vue:101
+#: src/views/preference/components/TOTP.vue:103
 msgid "Recovery Code"
 msgid "Recovery Code"
 msgstr "Код восстановления"
 msgstr "Код восстановления"
 
 
-#: src/views/preference/components/TOTP.vue:110
+#: src/views/preference/components/TOTP.vue:112
 msgid "Recovery Code:"
 msgid "Recovery Code:"
 msgstr "Код восстановления:"
 msgstr "Код восстановления:"
 
 
@@ -1709,7 +1713,7 @@ msgstr "Запрос с неправильными параметрами"
 msgid "Reset"
 msgid "Reset"
 msgstr "Сброс"
 msgstr "Сброс"
 
 
-#: src/views/preference/components/TOTP.vue:130
+#: src/views/preference/components/TOTP.vue:132
 msgid "Reset 2FA"
 msgid "Reset 2FA"
 msgstr "Сброс 2FA"
 msgstr "Сброс 2FA"
 
 
@@ -1760,7 +1764,7 @@ msgstr "Сохранено успешно"
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Успешно сохранено"
 msgstr "Успешно сохранено"
 
 
-#: src/views/preference/components/TOTP.vue:91
+#: src/views/preference/components/TOTP.vue:93
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 msgstr ""
 "Отсканируйте QR-код с помощью мобильного телефона, чтобы добавить учетную "
 "Отсканируйте QR-код с помощью мобильного телефона, чтобы добавить учетную "
@@ -1770,6 +1774,10 @@ msgstr ""
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
 
 
+#: src/views/preference/components/TOTP.vue:149
+msgid "Secret has been copied"
+msgstr ""
+
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgid "Selector"
 msgstr "Выбор"
 msgstr "Выбор"
@@ -1792,8 +1800,8 @@ msgstr "Отправлено"
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/preference/AuthSettings.vue:49
 #: src/views/preference/AuthSettings.vue:49
-#: src/views/preference/components/TOTP.vue:42
-#: src/views/preference/components/TOTP.vue:55
+#: src/views/preference/components/TOTP.vue:44
+#: src/views/preference/components/TOTP.vue:57
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
@@ -2034,7 +2042,7 @@ msgstr "Путь существует, но файл не является се
 msgid "The path exists, but the file is not a private key"
 msgid "The path exists, but the file is not a private key"
 msgstr "Путь существует, но файл не является приватным ключом"
 msgstr "Путь существует, но файл не является приватным ключом"
 
 
-#: src/views/preference/components/TOTP.vue:109
+#: src/views/preference/components/TOTP.vue:111
 msgid ""
 msgid ""
 "The recovery code is only displayed once, please save it in a safe place."
 "The recovery code is only displayed once, please save it in a safe place."
 msgstr ""
 msgstr ""
@@ -2115,7 +2123,7 @@ msgstr "Советы"
 msgid "Title"
 msgid "Title"
 msgstr "Заголовок"
 msgstr "Заголовок"
 
 
-#: src/views/preference/components/TOTP.vue:90
+#: src/views/preference/components/TOTP.vue:92
 msgid ""
 msgid ""
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "on your mobile phone."
 "on your mobile phone."
@@ -2143,7 +2151,7 @@ msgstr "Токен недействителен"
 msgid "Too many login failed attempts, please try again later"
 msgid "Too many login failed attempts, please try again later"
 msgstr "Слишком много неудачных попыток входа, попробуйте позже"
 msgstr "Слишком много неудачных попыток входа, попробуйте позже"
 
 
-#: src/views/preference/components/TOTP.vue:89
+#: src/views/preference/components/TOTP.vue:91
 msgid ""
 msgid ""
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "password algorithm."
 "password algorithm."

+ 26 - 18
app/src/language/vi_VN/app.po

@@ -13,7 +13,7 @@ msgstr ""
 msgid "2FA"
 msgid "2FA"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:88
+#: src/views/preference/components/TOTP.vue:90
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr ""
 msgstr ""
 
 
@@ -261,6 +261,10 @@ msgstr ""
 msgid "CADir"
 msgid "CADir"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:150
+msgid "Can't scan? Use text key binding"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
@@ -454,11 +458,11 @@ msgstr "Chứng chỉ"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "Chứng chỉ"
 msgstr "Chứng chỉ"
 
 
-#: src/views/preference/components/TOTP.vue:96
+#: src/views/preference/components/TOTP.vue:98
 msgid "Current account is enabled 2FA."
 msgid "Current account is enabled 2FA."
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:93
+#: src/views/preference/components/TOTP.vue:95
 msgid "Current account is not enabled 2FA."
 msgid "Current account is not enabled 2FA."
 msgstr ""
 msgstr ""
 
 
@@ -758,12 +762,12 @@ msgstr "Không thể bật %{conf_name} trên %{node_name}"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "Đã bật %{conf_name} trên %{node_name}"
 msgstr "Đã bật %{conf_name} trên %{node_name}"
 
 
-#: src/views/preference/components/TOTP.vue:122
+#: src/views/preference/components/TOTP.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Enable 2FA"
 msgid "Enable 2FA"
 msgstr "Đã bật"
 msgstr "Đã bật"
 
 
-#: src/views/preference/components/TOTP.vue:52
+#: src/views/preference/components/TOTP.vue:54
 #, fuzzy
 #, fuzzy
 msgid "Enable 2FA successfully"
 msgid "Enable 2FA successfully"
 msgstr "Đã bật"
 msgstr "Đã bật"
@@ -983,7 +987,7 @@ msgid ""
 "ban threshold minutes, the ip will be banned for a period of time."
 "ban threshold minutes, the ip will be banned for a period of time."
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:108
+#: src/views/preference/components/TOTP.vue:110
 msgid ""
 msgid ""
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "2FA."
 "2FA."
@@ -1022,12 +1026,12 @@ msgstr "Không thể khởi tạo trình nâng cấp"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Đang khởi tạo trình nâng cấp"
 msgstr "Đang khởi tạo trình nâng cấp"
 
 
-#: src/views/preference/components/TOTP.vue:144
+#: src/views/preference/components/TOTP.vue:157
 msgid "Input the code from the app:"
 msgid "Input the code from the app:"
 msgstr ""
 msgstr ""
 
 
 #: src/components/OTP/OTPAuthorization.vue:49
 #: src/components/OTP/OTPAuthorization.vue:49
-#: src/views/preference/components/TOTP.vue:157
+#: src/views/preference/components/TOTP.vue:170
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
@@ -1644,15 +1648,15 @@ msgid "Recovered Successfully"
 msgstr "Xoá thành công"
 msgstr "Xoá thành công"
 
 
 #: src/components/OTP/OTPAuthorization.vue:56
 #: src/components/OTP/OTPAuthorization.vue:56
-#: src/views/preference/components/TOTP.vue:164
+#: src/views/preference/components/TOTP.vue:177
 msgid "Recovery"
 msgid "Recovery"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:101
+#: src/views/preference/components/TOTP.vue:103
 msgid "Recovery Code"
 msgid "Recovery Code"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:110
+#: src/views/preference/components/TOTP.vue:112
 msgid "Recovery Code:"
 msgid "Recovery Code:"
 msgstr ""
 msgstr ""
 
 
@@ -1794,7 +1798,7 @@ msgstr "Yêu cầu có chứa tham số sai"
 msgid "Reset"
 msgid "Reset"
 msgstr "Đặt lại"
 msgstr "Đặt lại"
 
 
-#: src/views/preference/components/TOTP.vue:130
+#: src/views/preference/components/TOTP.vue:132
 #, fuzzy
 #, fuzzy
 msgid "Reset 2FA"
 msgid "Reset 2FA"
 msgstr "Đặt lại"
 msgstr "Đặt lại"
@@ -1848,7 +1852,7 @@ msgstr "Lưu thành công"
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Lưu thành công"
 msgstr "Lưu thành công"
 
 
-#: src/views/preference/components/TOTP.vue:91
+#: src/views/preference/components/TOTP.vue:93
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr ""
 msgstr ""
 
 
@@ -1856,6 +1860,10 @@ msgstr ""
 msgid "SDK"
 msgid "SDK"
 msgstr ""
 msgstr ""
 
 
+#: src/views/preference/components/TOTP.vue:149
+msgid "Secret has been copied"
+msgstr ""
+
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgid "Selector"
 msgstr "Bộ chọn"
 msgstr "Bộ chọn"
@@ -1878,8 +1886,8 @@ msgstr "Gửi"
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/preference/AuthSettings.vue:49
 #: src/views/preference/AuthSettings.vue:49
-#: src/views/preference/components/TOTP.vue:42
-#: src/views/preference/components/TOTP.vue:55
+#: src/views/preference/components/TOTP.vue:44
+#: src/views/preference/components/TOTP.vue:57
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
@@ -2122,7 +2130,7 @@ msgstr ""
 msgid "The path exists, but the file is not a private key"
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:109
+#: src/views/preference/components/TOTP.vue:111
 msgid ""
 msgid ""
 "The recovery code is only displayed once, please save it in a safe place."
 "The recovery code is only displayed once, please save it in a safe place."
 msgstr ""
 msgstr ""
@@ -2194,7 +2202,7 @@ msgstr ""
 msgid "Title"
 msgid "Title"
 msgstr "Tiêu đề"
 msgstr "Tiêu đề"
 
 
-#: src/views/preference/components/TOTP.vue:90
+#: src/views/preference/components/TOTP.vue:92
 msgid ""
 msgid ""
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "on your mobile phone."
 "on your mobile phone."
@@ -2220,7 +2228,7 @@ msgstr ""
 msgid "Too many login failed attempts, please try again later"
 msgid "Too many login failed attempts, please try again later"
 msgstr ""
 msgstr ""
 
 
-#: src/views/preference/components/TOTP.vue:89
+#: src/views/preference/components/TOTP.vue:91
 msgid ""
 msgid ""
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "password algorithm."
 "password algorithm."

二进制
app/src/language/zh_CN/app.mo


+ 26 - 18
app/src/language/zh_CN/app.po

@@ -17,7 +17,7 @@ msgstr ""
 msgid "2FA"
 msgid "2FA"
 msgstr "2FA"
 msgstr "2FA"
 
 
-#: src/views/preference/components/TOTP.vue:88
+#: src/views/preference/components/TOTP.vue:90
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "2FA 设置"
 msgstr "2FA 设置"
 
 
@@ -246,6 +246,10 @@ msgstr "CA Dir"
 msgid "CADir"
 msgid "CADir"
 msgstr "CADir"
 msgstr "CADir"
 
 
+#: src/views/preference/components/TOTP.vue:150
+msgid "Can't scan? Use text key binding"
+msgstr "无法扫描?使用文本密钥绑定"
+
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
@@ -423,11 +427,11 @@ msgstr "DNS 凭证"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "凭证"
 msgstr "凭证"
 
 
-#: src/views/preference/components/TOTP.vue:96
+#: src/views/preference/components/TOTP.vue:98
 msgid "Current account is enabled 2FA."
 msgid "Current account is enabled 2FA."
 msgstr "当前账户已启用二步验证。"
 msgstr "当前账户已启用二步验证。"
 
 
-#: src/views/preference/components/TOTP.vue:93
+#: src/views/preference/components/TOTP.vue:95
 msgid "Current account is not enabled 2FA."
 msgid "Current account is not enabled 2FA."
 msgstr "当前用户未启用二步验证。"
 msgstr "当前用户未启用二步验证。"
 
 
@@ -704,11 +708,11 @@ msgstr "在%{node_name}中启用%{conf_name}失败"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "成功启用%{node_name}中的%{conf_name}"
 msgstr "成功启用%{node_name}中的%{conf_name}"
 
 
-#: src/views/preference/components/TOTP.vue:122
+#: src/views/preference/components/TOTP.vue:124
 msgid "Enable 2FA"
 msgid "Enable 2FA"
 msgstr "启用二步验证"
 msgstr "启用二步验证"
 
 
-#: src/views/preference/components/TOTP.vue:52
+#: src/views/preference/components/TOTP.vue:54
 msgid "Enable 2FA successfully"
 msgid "Enable 2FA successfully"
 msgstr "二步验证启用成功"
 msgstr "二步验证启用成功"
 
 
@@ -918,7 +922,7 @@ msgstr ""
 "如果某个 IP 的登录失败次数达到禁用阈值分钟内的最大尝试次数,该 IP 将被禁止登"
 "如果某个 IP 的登录失败次数达到禁用阈值分钟内的最大尝试次数,该 IP 将被禁止登"
 "录一段时间。"
 "录一段时间。"
 
 
-#: src/views/preference/components/TOTP.vue:108
+#: src/views/preference/components/TOTP.vue:110
 msgid ""
 msgid ""
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "2FA."
 "2FA."
@@ -954,12 +958,12 @@ msgstr "初始化核心升级程序错误"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "初始化核心升级器"
 msgstr "初始化核心升级器"
 
 
-#: src/views/preference/components/TOTP.vue:144
+#: src/views/preference/components/TOTP.vue:157
 msgid "Input the code from the app:"
 msgid "Input the code from the app:"
 msgstr "输入应用程序中的代码:"
 msgstr "输入应用程序中的代码:"
 
 
 #: src/components/OTP/OTPAuthorization.vue:49
 #: src/components/OTP/OTPAuthorization.vue:49
-#: src/views/preference/components/TOTP.vue:157
+#: src/views/preference/components/TOTP.vue:170
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "输入恢复代码:"
 msgstr "输入恢复代码:"
 
 
@@ -1545,15 +1549,15 @@ msgid "Recovered Successfully"
 msgstr "恢复成功"
 msgstr "恢复成功"
 
 
 #: src/components/OTP/OTPAuthorization.vue:56
 #: src/components/OTP/OTPAuthorization.vue:56
-#: src/views/preference/components/TOTP.vue:164
+#: src/views/preference/components/TOTP.vue:177
 msgid "Recovery"
 msgid "Recovery"
 msgstr "恢复"
 msgstr "恢复"
 
 
-#: src/views/preference/components/TOTP.vue:101
+#: src/views/preference/components/TOTP.vue:103
 msgid "Recovery Code"
 msgid "Recovery Code"
 msgstr "恢复代码"
 msgstr "恢复代码"
 
 
-#: src/views/preference/components/TOTP.vue:110
+#: src/views/preference/components/TOTP.vue:112
 msgid "Recovery Code:"
 msgid "Recovery Code:"
 msgstr "恢复代码:"
 msgstr "恢复代码:"
 
 
@@ -1680,7 +1684,7 @@ msgstr "请求参数错误"
 msgid "Reset"
 msgid "Reset"
 msgstr "重置"
 msgstr "重置"
 
 
-#: src/views/preference/components/TOTP.vue:130
+#: src/views/preference/components/TOTP.vue:132
 msgid "Reset 2FA"
 msgid "Reset 2FA"
 msgstr "重置二步验证"
 msgstr "重置二步验证"
 
 
@@ -1731,7 +1735,7 @@ msgstr "保存成功"
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "保存成功"
 msgstr "保存成功"
 
 
-#: src/views/preference/components/TOTP.vue:91
+#: src/views/preference/components/TOTP.vue:93
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "用手机扫描二维码,将账户添加到应用程序中。"
 msgstr "用手机扫描二维码,将账户添加到应用程序中。"
 
 
@@ -1739,6 +1743,10 @@ msgstr "用手机扫描二维码,将账户添加到应用程序中。"
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
 
 
+#: src/views/preference/components/TOTP.vue:149
+msgid "Secret has been copied"
+msgstr "密钥已复制"
+
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgid "Selector"
 msgstr "选择器"
 msgstr "选择器"
@@ -1761,8 +1769,8 @@ msgstr "上传"
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/preference/AuthSettings.vue:49
 #: src/views/preference/AuthSettings.vue:49
-#: src/views/preference/components/TOTP.vue:42
-#: src/views/preference/components/TOTP.vue:55
+#: src/views/preference/components/TOTP.vue:44
+#: src/views/preference/components/TOTP.vue:57
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
@@ -1992,7 +2000,7 @@ msgstr "路径存在,但文件不是证书"
 msgid "The path exists, but the file is not a private key"
 msgid "The path exists, but the file is not a private key"
 msgstr "路径存在,但文件不是私钥"
 msgstr "路径存在,但文件不是私钥"
 
 
-#: src/views/preference/components/TOTP.vue:109
+#: src/views/preference/components/TOTP.vue:111
 msgid ""
 msgid ""
 "The recovery code is only displayed once, please save it in a safe place."
 "The recovery code is only displayed once, please save it in a safe place."
 msgstr "恢复密码只会显示一次,请妥善保存。"
 msgstr "恢复密码只会显示一次,请妥善保存。"
@@ -2064,7 +2072,7 @@ msgstr "提示"
 msgid "Title"
 msgid "Title"
 msgstr "标题"
 msgstr "标题"
 
 
-#: src/views/preference/components/TOTP.vue:90
+#: src/views/preference/components/TOTP.vue:92
 msgid ""
 msgid ""
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "on your mobile phone."
 "on your mobile phone."
@@ -2089,7 +2097,7 @@ msgstr "Token 无效"
 msgid "Too many login failed attempts, please try again later"
 msgid "Too many login failed attempts, please try again later"
 msgstr "登录失败次数过多,请稍后再试"
 msgstr "登录失败次数过多,请稍后再试"
 
 
-#: src/views/preference/components/TOTP.vue:89
+#: src/views/preference/components/TOTP.vue:91
 msgid ""
 msgid ""
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "password algorithm."
 "password algorithm."

+ 26 - 18
app/src/language/zh_TW/app.po

@@ -21,7 +21,7 @@ msgstr ""
 msgid "2FA"
 msgid "2FA"
 msgstr "多重要素驗證"
 msgstr "多重要素驗證"
 
 
-#: src/views/preference/components/TOTP.vue:88
+#: src/views/preference/components/TOTP.vue:90
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "多重要素驗證設定"
 msgstr "多重要素驗證設定"
 
 
@@ -250,6 +250,10 @@ msgstr "CA Dir"
 msgid "CADir"
 msgid "CADir"
 msgstr "CADir"
 msgstr "CADir"
 
 
+#: src/views/preference/components/TOTP.vue:150
+msgid "Can't scan? Use text key binding"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/ChatGPT/ChatGPT.vue:252
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:55
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:253
@@ -427,11 +431,11 @@ msgstr "認證"
 msgid "Credentials"
 msgid "Credentials"
 msgstr "認證資訊"
 msgstr "認證資訊"
 
 
-#: src/views/preference/components/TOTP.vue:96
+#: src/views/preference/components/TOTP.vue:98
 msgid "Current account is enabled 2FA."
 msgid "Current account is enabled 2FA."
 msgstr "當前帳戶已啟用多因素身份驗證。"
 msgstr "當前帳戶已啟用多因素身份驗證。"
 
 
-#: src/views/preference/components/TOTP.vue:93
+#: src/views/preference/components/TOTP.vue:95
 msgid "Current account is not enabled 2FA."
 msgid "Current account is not enabled 2FA."
 msgstr "當前帳戶未啟用多因素身份驗證。"
 msgstr "當前帳戶未啟用多因素身份驗證。"
 
 
@@ -708,11 +712,11 @@ msgstr "在 %{node_name} 啟用 %{conf_name} 失敗"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "成功在 %{node_name} 啟用 %{conf_name}"
 msgstr "成功在 %{node_name} 啟用 %{conf_name}"
 
 
-#: src/views/preference/components/TOTP.vue:122
+#: src/views/preference/components/TOTP.vue:124
 msgid "Enable 2FA"
 msgid "Enable 2FA"
 msgstr "啟用多因素身份驗證"
 msgstr "啟用多因素身份驗證"
 
 
-#: src/views/preference/components/TOTP.vue:52
+#: src/views/preference/components/TOTP.vue:54
 msgid "Enable 2FA successfully"
 msgid "Enable 2FA successfully"
 msgstr "啟用多因素身份驗證成功"
 msgstr "啟用多因素身份驗證成功"
 
 
@@ -922,7 +926,7 @@ msgstr ""
 "如果來自某個 IP 的登錄失敗次數在禁止閾值分鐘內達到最大嘗試次數,該 IP 將被禁"
 "如果來自某個 IP 的登錄失敗次數在禁止閾值分鐘內達到最大嘗試次數,該 IP 將被禁"
 "止一段時間。"
 "止一段時間。"
 
 
-#: src/views/preference/components/TOTP.vue:108
+#: src/views/preference/components/TOTP.vue:110
 msgid ""
 msgid ""
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "If you lose your mobile phone, you can use the recovery code to reset your "
 "2FA."
 "2FA."
@@ -958,12 +962,12 @@ msgstr "初始化核心升級程式錯誤"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "正在初始化核心升級程式"
 msgstr "正在初始化核心升級程式"
 
 
-#: src/views/preference/components/TOTP.vue:144
+#: src/views/preference/components/TOTP.vue:157
 msgid "Input the code from the app:"
 msgid "Input the code from the app:"
 msgstr "請輸入應用程式中的代碼:"
 msgstr "請輸入應用程式中的代碼:"
 
 
 #: src/components/OTP/OTPAuthorization.vue:49
 #: src/components/OTP/OTPAuthorization.vue:49
-#: src/views/preference/components/TOTP.vue:157
+#: src/views/preference/components/TOTP.vue:170
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "輸入恢復碼:"
 msgstr "輸入恢復碼:"
 
 
@@ -1548,15 +1552,15 @@ msgid "Recovered Successfully"
 msgstr "恢復成功"
 msgstr "恢復成功"
 
 
 #: src/components/OTP/OTPAuthorization.vue:56
 #: src/components/OTP/OTPAuthorization.vue:56
-#: src/views/preference/components/TOTP.vue:164
+#: src/views/preference/components/TOTP.vue:177
 msgid "Recovery"
 msgid "Recovery"
 msgstr "恢復"
 msgstr "恢復"
 
 
-#: src/views/preference/components/TOTP.vue:101
+#: src/views/preference/components/TOTP.vue:103
 msgid "Recovery Code"
 msgid "Recovery Code"
 msgstr "恢復碼"
 msgstr "恢復碼"
 
 
-#: src/views/preference/components/TOTP.vue:110
+#: src/views/preference/components/TOTP.vue:112
 msgid "Recovery Code:"
 msgid "Recovery Code:"
 msgstr "恢復碼:"
 msgstr "恢復碼:"
 
 
@@ -1683,7 +1687,7 @@ msgstr "請求參數錯誤"
 msgid "Reset"
 msgid "Reset"
 msgstr "重設"
 msgstr "重設"
 
 
-#: src/views/preference/components/TOTP.vue:130
+#: src/views/preference/components/TOTP.vue:132
 msgid "Reset 2FA"
 msgid "Reset 2FA"
 msgstr "重置多重因素驗證"
 msgstr "重置多重因素驗證"
 
 
@@ -1734,7 +1738,7 @@ msgstr "儲存成功"
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "儲存成功"
 msgstr "儲存成功"
 
 
-#: src/views/preference/components/TOTP.vue:91
+#: src/views/preference/components/TOTP.vue:93
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgid "Scan the QR code with your mobile phone to add the account to the app."
 msgstr "用手機掃描二維碼將賬戶添加到應用程序中。"
 msgstr "用手機掃描二維碼將賬戶添加到應用程序中。"
 
 
@@ -1742,6 +1746,10 @@ msgstr "用手機掃描二維碼將賬戶添加到應用程序中。"
 msgid "SDK"
 msgid "SDK"
 msgstr "SDK"
 msgstr "SDK"
 
 
+#: src/views/preference/components/TOTP.vue:149
+msgid "Secret has been copied"
+msgstr ""
+
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:155
 msgid "Selector"
 msgid "Selector"
 msgstr "選擇器"
 msgstr "選擇器"
@@ -1764,8 +1772,8 @@ msgstr "傳送"
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/BatchUpgrader.vue:57
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/environment/Environment.vue:15 src/views/other/Install.vue:68
 #: src/views/preference/AuthSettings.vue:49
 #: src/views/preference/AuthSettings.vue:49
-#: src/views/preference/components/TOTP.vue:42
-#: src/views/preference/components/TOTP.vue:55
+#: src/views/preference/components/TOTP.vue:44
+#: src/views/preference/components/TOTP.vue:57
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/preference/Preference.vue:83 src/views/stream/StreamList.vue:113
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 #: src/views/stream/StreamList.vue:81 src/views/system/Upgrade.vue:42
 msgid "Server error"
 msgid "Server error"
@@ -1995,7 +2003,7 @@ msgstr "路徑存在,但檔案不是憑證"
 msgid "The path exists, but the file is not a private key"
 msgid "The path exists, but the file is not a private key"
 msgstr "路徑存在,但檔案不是金鑰"
 msgstr "路徑存在,但檔案不是金鑰"
 
 
-#: src/views/preference/components/TOTP.vue:109
+#: src/views/preference/components/TOTP.vue:111
 msgid ""
 msgid ""
 "The recovery code is only displayed once, please save it in a safe place."
 "The recovery code is only displayed once, please save it in a safe place."
 msgstr "恢復碼僅顯示一次,請將其保存在安全的地方。"
 msgstr "恢復碼僅顯示一次,請將其保存在安全的地方。"
@@ -2067,7 +2075,7 @@ msgstr "提示"
 msgid "Title"
 msgid "Title"
 msgstr "標題"
 msgstr "標題"
 
 
-#: src/views/preference/components/TOTP.vue:90
+#: src/views/preference/components/TOTP.vue:92
 msgid ""
 msgid ""
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "on your mobile phone."
 "on your mobile phone."
@@ -2092,7 +2100,7 @@ msgstr "令牌無效"
 msgid "Too many login failed attempts, please try again later"
 msgid "Too many login failed attempts, please try again later"
 msgstr "登錄失敗次數過多,請稍後再試"
 msgstr "登錄失敗次數過多,請稍後再試"
 
 
-#: src/views/preference/components/TOTP.vue:89
+#: src/views/preference/components/TOTP.vue:91
 msgid ""
 msgid ""
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "TOTP is a two-factor authentication method that uses a time-based one-time "
 "password algorithm."
 "password algorithm."

+ 15 - 2
app/src/views/preference/components/TOTP.vue

@@ -1,8 +1,10 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import { message } from 'ant-design-vue'
 import { message } from 'ant-design-vue'
 import { CheckCircleOutlined } from '@ant-design/icons-vue'
 import { CheckCircleOutlined } from '@ant-design/icons-vue'
+import { UseClipboard } from '@vueuse/components'
 import otp from '@/api/otp'
 import otp from '@/api/otp'
 import OTPInput from '@/components/OTPInput/OTPInput.vue'
 import OTPInput from '@/components/OTPInput/OTPInput.vue'
+import { $gettext } from '@/gettext'
 
 
 const status = ref(false)
 const status = ref(false)
 const enrolling = ref(false)
 const enrolling = ref(false)
@@ -131,13 +133,24 @@ function reset2FA() {
     </AButton>
     </AButton>
 
 
     <template v-if="enrolling">
     <template v-if="enrolling">
-      <div class="w-64 h-64 mt-4 mb-2">
+      <div class="mt-4 mb-2">
         <img
         <img
           v-if="qrCode"
           v-if="qrCode"
-          class="w-full"
+          class="w-64 h-64"
           :src="qrCode"
           :src="qrCode"
           alt="qr code"
           alt="qr code"
         >
         >
+        <div class="w-64 flex justify-center">
+          <UseClipboard v-slot="{ copy, copied }">
+            <a
+              class="mr-2"
+              @click="() => copy(secret)"
+            >
+              {{ copied ? $gettext('Secret has been copied')
+                : $gettext('Can\'t scan? Use text key binding') }}
+            </a>
+          </UseClipboard>
+        </div>
       </div>
       </div>
 
 
       <div>
       <div>