package store import ( "context" "fmt" "time" "github.com/google/uuid" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgxpool" "git.kundeng.us/phoenix/textsender-models/tx0/user" ) type UserStore interface { CreateUser(ctx context.Context, user *user.User) error GetUserByID(ctx context.Context, id uuid.UUID) (*user.User, error) GetUserByUsername(ctx context.Context, username string) (*user.User, error) GetAllUsers(ctx context.Context) ([]*user.User, error) UserExists(ctx context.Context, username string) (bool, error) UpdatePassword(ctx context.Context, id uuid.UUID, password string) (int64, error) UpdateLastLogin(ctx context.Context, id uuid.UUID, lastLogin time.Time) (int64, error) } type PGUserStore struct { db *pgxpool.Pool } func NewUserStore(db *pgxpool.Pool) *PGUserStore { return &PGUserStore{db: db} } func (s *PGUserStore) CreateUser(ctx context.Context, user *user.User) error { query := ` INSERT INTO users (phone_number, username, password) VALUES ($1, $2, $3) RETURNING id, phone_number, username, created ` return s.db.QueryRow(ctx, query, user.PhoneNumber, user.Username, user.Password).Scan( &user.Id, &user.PhoneNumber, &user.Username, &user.Created, ) } func (s *PGUserStore) GetUserByID(ctx context.Context, id uuid.UUID) (*user.User, error) { query := `SELECT id, username, password, phone_number, created FROM users WHERE id = $1` var user user.User err := s.db.QueryRow(ctx, query, id).Scan( &user.Id, &user.Username, &user.Password, &user.PhoneNumber, &user.Created, ) if err == pgx.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("getting user by ID: %w", err) } return &user, nil } func (s *PGUserStore) GetUserByUsername(ctx context.Context, username string) (*user.User, error) { query := `SELECT id, username, password, phone_number, created FROM users WHERE username = $1` var user user.User err := s.db.QueryRow(ctx, query, username).Scan( &user.Id, &user.Username, &user.Password, &user.PhoneNumber, &user.Created, ) if err == pgx.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("getting user by ID: %w", err) } return &user, nil } func (s *PGUserStore) GetAllUsers(ctx context.Context) ([]*user.User, error) { query := `SELECT id, username, password, phone_number, created FROM users` rows, err := s.db.Query(ctx, query) if err != nil { return nil, fmt.Errorf("querying all users: %w", err) } defer rows.Close() var users []*user.User for rows.Next() { var user user.User if err := rows.Scan( &user.Id, &user.Username, &user.Password, &user.PhoneNumber, &user.Created, ); err != nil { return nil, fmt.Errorf("scanning user row: %w", err) } users = append(users, &user) } if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterating user rows: %w", err) } return users, nil } func (s *PGUserStore) UserExists(ctx context.Context, username string) (bool, error) { query := `SELECT EXISTS(SELECT 1 FROM users WHERE username = $1)` var exists bool err := s.db.QueryRow(ctx, query, username).Scan(&exists) if err != nil { return false, fmt.Errorf("checking if user exists: %w", err) } return exists, nil } func (s *PGUserStore) UpdatePassword(ctx context.Context, id uuid.UUID, password string) (int64, error) { query := `UPDATE users SET password = $1 WHERE id = $2` if affected, err := s.db.Exec(ctx, query, password, id); err != nil { return 0, err } else { return affected.RowsAffected(), nil } } func (s *PGUserStore) UpdateLastLogin(ctx context.Context, id uuid.UUID, lastLogin time.Time) (int64, error) { query := `UPDATE users SET last_login = $1 WHERE id = $2` if affected, err := s.db.Exec(ctx, query, lastLogin, id); err != nil { return 0, err } else { return affected.RowsAffected(), nil } }