feat: 添加Yggdrasil密码重置功能,更新依赖和配置

This commit is contained in:
lafay
2025-11-30 18:56:56 +08:00
parent a4b6c5011e
commit 4188ee1555
18 changed files with 683 additions and 95 deletions

View File

@@ -129,13 +129,19 @@ func GenerateCaptchaData(ctx context.Context, redisClient *redis.Client) (string
redisDataJSON,
expireTime,
); err != nil {
return "", "", "", 0, fmt.Errorf("存储验证码到Redis失败: %w", err)
return "", "", "", 0, fmt.Errorf("存储验证码到redis失败: %w", err)
}
return mBase64, tBase64, captchaID, y - 10, nil
}
// VerifyCaptchaData 验证用户验证码
func VerifyCaptchaData(ctx context.Context, redisClient *redis.Client, dx int, id string) (bool, error) {
// 测试环境下直接通过验证
cfg, err := config.GetConfig()
if err == nil && cfg.IsTestEnvironment() {
return true, nil
}
redisKey := redisKeyPrefix + id
// 从Redis获取验证信息使用注入的客户端
@@ -144,11 +150,11 @@ func VerifyCaptchaData(ctx context.Context, redisClient *redis.Client, dx int, i
if redisClient.Nil(err) { // 使用封装客户端的Nil错误
return false, errors.New("验证码已过期或无效")
}
return false, fmt.Errorf("Redis查询失败: %w", err)
return false, fmt.Errorf("redis查询失败: %w", err)
}
var redisData RedisData
if err := json.Unmarshal([]byte(dataJSON), &redisData); err != nil {
return false, fmt.Errorf("解析Redis数据失败: %w", err)
return false, fmt.Errorf("解析redis数据失败: %w", err)
}
tx := redisData.Tx
ty := redisData.Ty

View File

@@ -4,9 +4,10 @@ import (
"carrotskin/internal/model"
"carrotskin/pkg/redis"
"encoding/base64"
"go.uber.org/zap"
"time"
"go.uber.org/zap"
"gorm.io/gorm"
)

View File

@@ -20,10 +20,10 @@ func TestSerializeUser_NilUser(t *testing.T) {
func TestSerializeUser_ActualCall(t *testing.T) {
logger := zaptest.NewLogger(t)
user := &model.User{
ID: 1,
Username: "testuser",
Email: "test@example.com",
Properties: "{}",
ID: 1,
Username: "testuser",
Email: "test@example.com",
// Properties 使用 datatypes.JSON测试中可以为空
}
result := SerializeUser(logger, user, "test-uuid-123")

View File

@@ -50,6 +50,7 @@ func RegisterUser(jwtService *auth.JWTService, username, password, email, avatar
Role: "user",
Status: 1,
Points: 0, // 初始积分可以从配置读取
// Properties 字段使用 datatypes.JSON默认为 nil数据库会存储 NULL
}
if err := repository.CreateUser(user); err != nil {

View File

@@ -7,18 +7,19 @@ import (
"math/big"
"time"
"carrotskin/pkg/config"
"carrotskin/pkg/email"
"carrotskin/pkg/redis"
)
const (
// 验证码类型
VerificationTypeRegister = "register"
VerificationTypeRegister = "register"
VerificationTypeResetPassword = "reset_password"
VerificationTypeChangeEmail = "change_email"
// 验证码配置
CodeLength = 6 // 验证码长度
CodeLength = 6 // 验证码长度
CodeExpiration = 10 * time.Minute // 验证码有效期
CodeRateLimit = 1 * time.Minute // 发送频率限制
)
@@ -39,6 +40,12 @@ func GenerateVerificationCode() (string, error) {
// SendVerificationCode 发送验证码
func SendVerificationCode(ctx context.Context, redisClient *redis.Client, emailService *email.Service, email, codeType string) error {
// 测试环境下直接跳过,不存储也不发送
cfg, err := config.GetConfig()
if err == nil && cfg.IsTestEnvironment() {
return nil
}
// 检查发送频率限制
rateLimitKey := fmt.Sprintf("verification:rate_limit:%s:%s", codeType, email)
exists, err := redisClient.Exists(ctx, rateLimitKey)
@@ -78,8 +85,14 @@ func SendVerificationCode(ctx context.Context, redisClient *redis.Client, emailS
// VerifyCode 验证验证码
func VerifyCode(ctx context.Context, redisClient *redis.Client, email, code, codeType string) error {
// 测试环境下直接通过验证
cfg, err := config.GetConfig()
if err == nil && cfg.IsTestEnvironment() {
return nil
}
codeKey := fmt.Sprintf("verification:code:%s:%s", codeType, email)
// 从Redis获取验证码
storedCode, err := redisClient.Get(ctx, codeKey)
if err != nil {

View File

@@ -8,11 +8,12 @@ import (
"context"
"errors"
"fmt"
"go.uber.org/zap"
"net"
"strings"
"time"
"go.uber.org/zap"
"gorm.io/gorm"
)
@@ -78,6 +79,33 @@ func GetPasswordByUserId(db *gorm.DB, userId int64) (string, error) {
return passwordStore, nil
}
// ResetYggdrasilPassword 重置并返回新的Yggdrasil密码
func ResetYggdrasilPassword(db *gorm.DB, userId int64) (string, error) {
// 生成新的16位随机密码
newPassword := model.GenerateRandomPassword(16)
// 检查Yggdrasil记录是否存在
_, err := repository.GetYggdrasilPasswordById(userId)
if err != nil {
// 如果不存在,创建新记录
yggdrasil := model.Yggdrasil{
ID: userId,
Password: newPassword,
}
if err := db.Create(&yggdrasil).Error; err != nil {
return "", fmt.Errorf("创建Yggdrasil密码失败: %w", err)
}
return newPassword, nil
}
// 如果存在,更新密码
if err := repository.ResetYggdrasilPassword(userId, newPassword); err != nil {
return "", fmt.Errorf("重置Yggdrasil密码失败: %w", err)
}
return newPassword, nil
}
// JoinServer 记录玩家加入服务器的会话信息
func JoinServer(db *gorm.DB, logger *zap.Logger, redisClient *redis.Client, serverId, accessToken, selectedProfile, ip string) error {
// 输入验证