package repositories import ( "GoMembership/internal/models" "GoMembership/pkg/errors" "database/sql" "fmt" "strings" "time" ) 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 *sql.DB } func NewUserRepository(db *sql.DB) UserRepository { return &userRepository{db} } func (repo *userRepository) CreateUser(user *models.User) (int64, error) { query := "INSERT INTO users (first_name, last_name, email, phone, drivers_id_checked, role_id, payment_status, date_of_birth, address, profile_picture, notes, status, password, salt, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" result, err := repo.db.Exec(query, user.FirstName, user.LastName, user.Email, user.Phone, user.DriversIDChecked, user.RoleID, user.PaymentStatus, user.DateOfBirth, user.Address, user.ProfilePicture, user.Notes, user.Status, user.Password, user.Salt, user.CreatedAt, user.UpdatedAt) if err != nil { return -1, err } lastInsertID, err := result.LastInsertId() if err != nil { return -1, err } return lastInsertID, err } func (repo *userRepository) UpdateUser(userID int64, updates map[string]interface{}) error { if len(updates) == 0 { return errors.ErrNoData } // Construct the query setClauses := make([]string, 0, len(updates)) args := make([]interface{}, 0, len(updates)+1) for column, value := range updates { setClauses = append(setClauses, fmt.Sprintf("%s = ?", column)) args = append(args, value) } args = append(args, userID) query := fmt.Sprintf("UPDATE users SET %s WHERE id = ?", strings.Join(setClauses, ", ")) // Execute the query _, err := repo.db.Exec(query, args...) if err != nil { return err } return nil } func (repo *userRepository) FindUserByID(id int64) (*models.User, error) { var user models.User query := "SELECT id, first_name, last_name, email, phone, drivers_id_checked, role_id, payment_status, date_of_birth, address, profile_picture, notes, status FROM users WHERE id = ?" err := repo.db.QueryRow(query, id).Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email, &user.Phone, &user.DriversIDChecked, &user.RoleID, &user.PaymentStatus, &user.DateOfBirth, &user.Address, &user.ProfilePicture, &user.Notes, &user.Status) if err != nil { if err == sql.ErrNoRows { return nil, errors.ErrUserNotFound } return nil, err } return &user, nil } func (repo *userRepository) FindUserByEmail(email string) (*models.User, error) { var user models.User query := "SELECT id, first_name, last_name, email FROM users WHERE email = ?" err := repo.db.QueryRow(query, email).Scan(&user.ID, &user.FirstName, &user.LastName, &user.Email) if err != nil { if err == sql.ErrNoRows { return nil, errors.ErrUserNotFound } return nil, err } return &user, nil } func (repo *userRepository) IsVerified(userID *int64) (bool, error) { var status string query := "SELECT status FROM users where id = ?" err := repo.db.QueryRow(query, userID).Scan(&status) if err != nil { if err == sql.ErrNoRows { return false, errors.ErrUserNotFound } return false, err } return status != "unverified", nil } func (repo *userRepository) VerifyUserOfToken(token *string) (int64, error) { var userID int64 err := repo.db.QueryRow("SELECT user_id FROM email_verifications WHERE verification_token = $1", token).Scan(&userID) if err == sql.ErrNoRows { return -1, errors.ErrTokenNotFound } else if err != nil { return -1, err } verified, err := repo.IsVerified(&userID) if err != nil { return -1, err } if verified { return userID, errors.ErrAlreadyVerified } update := map[string]interface{}{ "status": "active", } err = repo.UpdateUser(userID, update) if err != nil { return -1, err } query := "UPDATE email_verifications SET verified_at = ? WHERE user_id = ?" _, err = repo.db.Exec(query, time.Now(), userID) if err != nil { return -1, err } return userID, nil } func (repo *userRepository) SetVerificationToken(user *models.User, token *string) (int64, error) { // check if user already verified verified, err := repo.IsVerified(&user.ID) if err != nil { return -1, err } if verified { return -1, errors.ErrAlreadyVerified } query := "INSERT OR REPLACE INTO email_verifications (user_id, verification_token, created_at) VALUES (?, ?, ?)" result, err := repo.db.Exec(query, user.ID, token, time.Now()) if err != nil { return -1, err } lastInsertID, err := result.LastInsertId() if err != nil { return -1, err } return lastInsertID, nil }