package repositories import ( "time" "GoMembership/internal/constants" "gorm.io/gorm" "gorm.io/gorm/clause" "GoMembership/internal/models" "GoMembership/pkg/errors" "GoMembership/pkg/logger" ) type UserRepository 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 { 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, user *models.User) error { logger.Info.Printf("Updating User: %#v\n", user) if user == nil { return errors.ErrNoData } result := repo.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 (repo *userRepository) FindUserByID(id int64) (*models.User, error) { var user models.User result := repo.db.Preload("Consents").Preload("BankAccount").Preload("Verification").Preload("Membership").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) (*models.User, error) { var emailVerification models.Verification result := repo.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 := repo.IsVerified(&emailVerification.UserID) if err != nil { return nil, err } user, err := repo.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 = repo.UpdateUser(emailVerification.UserID, user) if err != nil { return nil, err } return user, 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 }