package repositories import ( "GoMembership/internal/constants" "time" "gorm.io/gorm" "GoMembership/internal/models" "GoMembership/pkg/errors" "gorm.io/gorm/clause" ) type UserRepository interface { CreateUser(user *models.User) (int64, 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) (int64, error) } type userRepository struct { db *gorm.DB } func NewUserRepository(db *gorm.DB) UserRepository { return &userRepository{db} } func (repo *userRepository) CreateUser(user *models.User) (int64, error) { result := repo.db.Create(user) if result.Error != nil { return 0, result.Error } return user.ID, nil } func (repo *userRepository) UpdateUser(userID int64, updates map[string]interface{}) error { if len(updates) == 0 { return errors.ErrNoData } result := repo.db.Session(&gorm.Session{FullSaveAssociations: true}).Model(&models.User{}).Where("id = ?", userID).Updates(&updates) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.ErrNoRowsAffected } return nil } func (repo *userRepository) FindUserByID(id int64) (*models.User, error) { var user models.User result := repo.db.First(&user, id) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { return nil, gorm.ErrRecordNotFound } return nil, result.Error } return &user, nil } func (repo *userRepository) FindUserByEmail(email string) (*models.User, error) { var user models.User result := repo.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 (repo *userRepository) IsVerified(userID *int64) (bool, error) { var user models.User result := repo.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 (repo *userRepository) VerifyUserOfToken(token *string) (int64, error) { var emailVerification models.Verification result := repo.db.Where("verification_token = ?", token).First(&emailVerification) if result.Error != nil { if result.Error == gorm.ErrRecordNotFound { return 0, gorm.ErrRecordNotFound } return 0, result.Error } // Check if the user is already verified verified, err := repo.IsVerified(&emailVerification.UserID) if err != nil { return 0, err } if verified { return emailVerification.UserID, gorm.ErrRecordNotFound } // Update user status to active t := time.Now() emailVerification.EmailVerifiedAt = &t update := map[string]interface{}{ "status": constants.VerifiedStatus, "verifications": emailVerification, } err = repo.UpdateUser(emailVerification.UserID, update) if err != nil { return 0, err } return emailVerification.UserID, nil } func (repo *userRepository) SetVerificationToken(user *models.User, token *string) (int64, error) { // Check if user is already verified verified, err := repo.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 := repo.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 }