package repositories import ( "time" "gorm.io/gorm" "GoMembership/internal/constants" "GoMembership/internal/database" "gorm.io/gorm/clause" "GoMembership/internal/models" "GoMembership/pkg/errors" ) type UserRepositoryInterface interface { CreateUser(user *models.User) (int64, error) UpdateUser(userID int64, user *models.User) error FindUserByID(id int64) (*models.User, error) FindUserByEmail(email string) (*models.User, error) SetVerificationToken(user *models.User, token *string) (int64, error) IsVerified(userID *int64) (bool, error) VerifyUserOfToken(token *string) (*models.User, error) } type UserRepository struct{} func (ur *UserRepository) CreateUser(user *models.User) (int64, error) { result := database.DB.Create(user) if result.Error != nil { return 0, result.Error } return user.ID, nil } func (ur *UserRepository) UpdateUser(userID int64, user *models.User) error { // logger.Info.Printf("Updating User: %#v\n", user) if user == nil { return errors.ErrNoData } result := database.DB.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.ErrNoRowsAffected } return nil } func (ur *UserRepository) FindUserByID(id int64) (*models.User, error) { var user models.User result := database.DB.Preload("Consents").Preload("BankAccount").Preload("Verification").Preload("Membership").Preload("Subscription").First(&user, id) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { return nil, gorm.ErrRecordNotFound } return nil, result.Error } return &user, nil } func (ur *UserRepository) FindUserByEmail(email string) (*models.User, error) { var user models.User result := database.DB.Where("email = ?", email).First(&user) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { return nil, gorm.ErrRecordNotFound } return nil, result.Error } return &user, nil } func (ur *UserRepository) IsVerified(userID *int64) (bool, error) { var user models.User result := database.DB.Select("status").First(&user, userID) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { return false, gorm.ErrRecordNotFound } return false, result.Error } return user.Status != constants.UnverifiedStatus, nil } func (ur *UserRepository) VerifyUserOfToken(token *string) (*models.User, error) { var emailVerification models.Verification result := database.DB.Where("verification_token = ?", token).First(&emailVerification) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { return nil, gorm.ErrRecordNotFound } return nil, result.Error } // Check if the user is already verified verified, err := ur.IsVerified(&emailVerification.UserID) if err != nil { return nil, err } user, err := ur.FindUserByID(emailVerification.UserID) if err != nil { return nil, err } if verified { return user, errors.ErrAlreadyVerified } // Update user status to active t := time.Now() emailVerification.EmailVerifiedAt = &t user.Status = constants.VerifiedStatus user.Verification = emailVerification err = ur.UpdateUser(emailVerification.UserID, user) if err != nil { return nil, err } return user, nil } func (ur *UserRepository) SetVerificationToken(user *models.User, token *string) (int64, error) { // Check if user is already verified verified, err := ur.IsVerified(&user.ID) if err != nil { return -1, err } if verified { return -1, errors.ErrAlreadyVerified } // Prepare the Verification record verification := models.Verification{ UserID: user.ID, VerificationToken: *token, } // Use GORM to insert or update the Verification record result := database.DB.Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "user_id"}}, DoUpdates: clause.AssignmentColumns([]string{"verification_token", "created_at"}), }).Create(&verification) if result.Error != nil { return -1, result.Error } return verification.ID, nil }