package core import ( "context" "errors" "fmt" "log" "regexp" "gitea.mrixs.me/minecraft-platform/backend/internal/database" "gitea.mrixs.me/minecraft-platform/backend/internal/models" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" ) var ( ErrInvalidUsername = errors.New("invalid username format or length") ErrInvalidEmail = errors.New("invalid email format") ErrPasswordTooShort = errors.New("password is too short (minimum 8 characters)") ) // Регулярное выражение для валидации username var usernameRegex = regexp.MustCompile(`^[a-zA-Z0-9_]{3,16}$`) // Регулярное выражение для валидации email (упрощенное, но эффективное) var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`) type UserService struct { Repo *database.UserRepository } // RegisterNewUser выполняет полный алгоритм регистрации func (s *UserService) RegisterNewUser(ctx context.Context, req models.RegisterRequest) error { // Шаг 2 из ТЗ: Валидация if !usernameRegex.MatchString(req.Username) { return ErrInvalidUsername } if !emailRegex.MatchString(req.Email) { return ErrInvalidEmail } if len(req.Password) < 8 { return ErrPasswordTooShort } // Шаг 5 из ТЗ: Генерация хеша пароля passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 12) // Стоимость 12, как в ТЗ if err != nil { return err } // Шаг 6 из ТЗ: Генерация UUID userUUID, err := uuid.NewRandom() if err != nil { return err } user := &models.User{ UUID: userUUID, Username: req.Username, Email: req.Email, PasswordHash: string(passwordHash), Role: "user", // По умолчанию } // Вызываем метод репозитория для сохранения в БД return s.Repo.CreateUserTx(ctx, user) } // ValidateJoinRequest проверяет запрос на присоединение к серверу. func (s *AuthService) ValidateJoinRequest(ctx context.Context, req models.JoinRequest) error { // Преобразуем UUID из строки без дефисов в стандартный формат var uuidStr string if len(req.SelectedProfile) == 32 { uuidStr = fmt.Sprintf("%s-%s-%s-%s-%s", req.SelectedProfile[0:8], req.SelectedProfile[8:12], req.SelectedProfile[12:16], req.SelectedProfile[16:20], req.SelectedProfile[20:32], ) } else { return errors.New("invalid profile UUID format") } userUUID, err := uuid.Parse(uuidStr) if err != nil { return fmt.Errorf("failed to parse profile UUID: %w", err) } // Проверяем токен в базе данных err = s.UserRepo.ValidateAccessToken(ctx, req.AccessToken, userUUID) if err != nil { if errors.Is(err, database.ErrTokenNotFound) { // Возвращаем ту же ошибку, что и при неверных кредах, чтобы не давать лишней информации return ErrInvalidCredentials } return err } // В ТЗ указано "привязка serverId". В простой реализации это может быть просто логирование // или запись в кеш (например, Redis). Для начала, просто прохождение валидации достаточно. log.Printf("User %s successfully joined server with serverId %s", userUUID, req.ServerID) return nil }