diff --git a/controller/misc.go b/controller/misc.go index 29b3a5c5e180..7c6f9820f37d 100644 --- a/controller/misc.go +++ b/controller/misc.go @@ -8,6 +8,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" + "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/middleware" "github.com/QuantumNous/new-api/model" @@ -231,18 +232,12 @@ func GetHomePageContent(c *gin.Context) { func SendEmailVerification(c *gin.Context) { email := c.Query("email") if err := common.Validate.Var(email, "required,email"); err != nil { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "无效的参数", - }) + common.ApiErrorI18n(c, i18n.MsgInvalidParams) return } parts := strings.Split(email, "@") if len(parts) != 2 { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "无效的邮箱地址", - }) + common.ApiErrorI18n(c, i18n.MsgAuthEmailInvalid) return } localPart := parts[0] @@ -256,37 +251,38 @@ func SendEmailVerification(c *gin.Context) { } } if !allowed { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "The administrator has enabled the email domain name whitelist, and your email address is not allowed due to special symbols or it's not in the whitelist.", - }) + common.ApiErrorI18n(c, i18n.MsgAuthEmailDomainRestricted) return } } if common.EmailAliasRestrictionEnabled { containsSpecialSymbols := strings.Contains(localPart, "+") || strings.Contains(localPart, ".") if containsSpecialSymbols { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "管理员已启用邮箱地址别名限制,您的邮箱地址由于包含特殊符号而被拒绝。", - }) + common.ApiErrorI18n(c, i18n.MsgAuthEmailAliasRestricted) return } } if model.IsEmailAlreadyTaken(email) { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "邮箱地址已被占用", - }) + common.ApiErrorI18n(c, i18n.MsgAuthEmailTaken) return } code := common.GenerateVerificationCode(6) common.RegisterVerificationCodeWithKey(email, code, common.EmailVerificationPurpose) - subject := fmt.Sprintf("%s邮箱验证邮件", common.SystemName) - content := fmt.Sprintf("
您好,你正在进行%s邮箱验证。
"+ - "您的验证码为: %s
"+ - "验证码 %d 分钟内有效,如果不是本人操作,请忽略。
", common.SystemName, code, common.VerificationValidMinutes) + subject := i18n.T(c, i18n.MsgAuthEmailVerificationSubject, map[string]any{ + "SystemName": common.SystemName, + }) + content := fmt.Sprintf("%s
%s
%s
", + i18n.T(c, i18n.MsgAuthEmailVerificationIntro, map[string]any{ + "SystemName": common.SystemName, + }), + i18n.T(c, i18n.MsgAuthEmailVerificationCode, map[string]any{ + "Code": code, + }), + i18n.T(c, i18n.MsgAuthEmailVerificationExpiry, map[string]any{ + "Minutes": common.VerificationValidMinutes, + }), + ) err := common.SendEmail(subject, email, content) if err != nil { common.ApiError(c, err) @@ -302,21 +298,30 @@ func SendEmailVerification(c *gin.Context) { func SendPasswordResetEmail(c *gin.Context) { email := c.Query("email") if err := common.Validate.Var(email, "required,email"); err != nil { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "无效的参数", - }) + common.ApiErrorI18n(c, i18n.MsgInvalidParams) return } if model.IsEmailAlreadyTaken(email) { code := common.GenerateVerificationCode(0) common.RegisterVerificationCodeWithKey(email, code, common.PasswordResetPurpose) link := fmt.Sprintf("%s/user/reset?email=%s&token=%s", system_setting.ServerAddress, email, code) - subject := fmt.Sprintf("%s密码重置", common.SystemName) - content := fmt.Sprintf("您好,你正在进行%s密码重置。
"+ - "点击 此处 进行密码重置。
"+ - "如果链接无法点击,请尝试点击下面的链接或将其复制到浏览器中打开:
%s
重置链接 %d 分钟内有效,如果不是本人操作,请忽略。
", common.SystemName, link, link, common.VerificationValidMinutes) + subject := i18n.T(c, i18n.MsgAuthPasswordResetSubject, map[string]any{ + "SystemName": common.SystemName, + }) + content := fmt.Sprintf("%s
%s
%s
%s
", + i18n.T(c, i18n.MsgAuthPasswordResetIntro, map[string]any{ + "SystemName": common.SystemName, + }), + i18n.T(c, i18n.MsgAuthPasswordResetClick, map[string]any{ + "Link": link, + }), + i18n.T(c, i18n.MsgAuthPasswordResetCopy, map[string]any{ + "Link": link, + }), + i18n.T(c, i18n.MsgAuthPasswordResetExpiry, map[string]any{ + "Minutes": common.VerificationValidMinutes, + }), + ) err := common.SendEmail(subject, email, content) if err != nil { logger.LogError(c.Request.Context(), fmt.Sprintf("failed to send password reset email to %s: %s", email, err.Error())) @@ -337,17 +342,11 @@ func ResetPassword(c *gin.Context) { var req PasswordResetRequest err := json.NewDecoder(c.Request.Body).Decode(&req) if req.Email == "" || req.Token == "" { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "无效的参数", - }) + common.ApiErrorI18n(c, i18n.MsgInvalidParams) return } if !common.VerifyCodeWithKey(req.Email, req.Token, common.PasswordResetPurpose) { - c.JSON(http.StatusOK, gin.H{ - "success": false, - "message": "重置链接非法或已过期", - }) + common.ApiErrorI18n(c, i18n.MsgAuthPasswordResetInvalid) return } password := common.GenerateVerificationCode(12) diff --git a/i18n/keys.go b/i18n/keys.go index 8e551d2ea657..ec4844cfb22b 100644 --- a/i18n/keys.go +++ b/i18n/keys.go @@ -40,6 +40,28 @@ const ( MsgAuthInsufficientPrivilege = "auth.insufficient_privilege" ) +// Public auth and verification messages +const ( + MsgAuthEmailInvalid = "auth.email_invalid" + MsgAuthEmailDomainRestricted = "auth.email_domain_restricted" + MsgAuthEmailAliasRestricted = "auth.email_alias_restricted" + MsgAuthEmailTaken = "auth.email_taken" + MsgAuthEmailVerificationSubject = "auth.email_verification_subject" + MsgAuthEmailVerificationIntro = "auth.email_verification_intro" + MsgAuthEmailVerificationCode = "auth.email_verification_code" + MsgAuthEmailVerificationExpiry = "auth.email_verification_expiry" + MsgAuthPasswordResetSubject = "auth.password_reset_subject" + MsgAuthPasswordResetIntro = "auth.password_reset_intro" + MsgAuthPasswordResetClick = "auth.password_reset_click" + MsgAuthPasswordResetCopy = "auth.password_reset_copy" + MsgAuthPasswordResetExpiry = "auth.password_reset_expiry" + MsgAuthPasswordResetInvalid = "auth.password_reset_invalid" + MsgAuthEmailRateLimitWait = "auth.email_rate_limit_wait" + MsgAuthEmailRateLimitRetry = "auth.email_rate_limit_retry" + MsgAuthTurnstileTokenEmpty = "auth.turnstile_token_empty" + MsgAuthTurnstileFailed = "auth.turnstile_failed" +) + // Token related messages const ( MsgTokenNameTooLong = "token.name_too_long" diff --git a/i18n/locales/en.yaml b/i18n/locales/en.yaml index 064abc708420..bd07951b4c21 100644 --- a/i18n/locales/en.yaml +++ b/i18n/locales/en.yaml @@ -32,6 +32,24 @@ auth.user_id_format_error: "Unauthorized, New-Api-User header format error" auth.user_id_mismatch: "Unauthorized, New-Api-User does not match logged in user" auth.user_banned: "User has been banned" auth.insufficient_privilege: "Unauthorized, insufficient privileges" +auth.email_invalid: "Invalid email address" +auth.email_domain_restricted: "The administrator has enabled the email domain whitelist, and your email address is not allowed because it contains special symbols or is not in the whitelist." +auth.email_alias_restricted: "The administrator has enabled email alias restrictions, and your email address was rejected because it contains special symbols." +auth.email_taken: "Email address is already in use" +auth.email_verification_subject: "{{.SystemName}} email verification" +auth.email_verification_intro: "Hello, you are verifying your email address for {{.SystemName}}." +auth.email_verification_code: "Your verification code is: {{.Code}}" +auth.email_verification_expiry: "This code is valid for {{.Minutes}} minutes. If you did not request this, you can ignore this email." +auth.password_reset_subject: "{{.SystemName}} password reset" +auth.password_reset_intro: "Hello, you requested a password reset for {{.SystemName}}." +auth.password_reset_click: "Click here to reset your password." +auth.password_reset_copy: "If the link is not clickable, copy and paste this URL into your browser: