Files

99 lines
2.7 KiB
Go
Raw Permalink Normal View History

package store
import (
"context"
"errors"
"github.com/jackc/pgerrcode"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgconn"
)
// CreateUser inserts a new user returning the created record or ErrDuplicate on conflict.
func (s *Store) CreateUser(ctx context.Context, username, displayName, passwordHash string) (*User, error) {
var u User
err := s.pool.QueryRow(ctx, `
INSERT INTO users(username, display_name, password_hash)
VALUES($1, $2, $3)
RETURNING id, username, display_name, password_hash, avatar, created_at
`, username, displayName, passwordHash).Scan(
&u.ID, &u.Username, &u.DisplayName, &u.PasswordHash, &u.Avatar, &u.CreatedAt,
)
if err != nil {
var pgErr *pgconn.PgError
if errors.As(err, &pgErr) && pgErr.Code == pgerrcode.UniqueViolation {
return nil, ErrDuplicate
}
return nil, err
}
return &u, nil
}
// GetUserByUsername retrieves a user by username or returns ErrNotFound.
func (s *Store) GetUserByUsername(ctx context.Context, username string) (*User, error) {
var u User
err := s.pool.QueryRow(ctx, `
SELECT id, username, display_name, password_hash, avatar, created_at
FROM users WHERE username=$1
`, username).Scan(
&u.ID, &u.Username, &u.DisplayName, &u.PasswordHash, &u.Avatar, &u.CreatedAt,
)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
return nil, err
}
return &u, nil
}
// GetUserByID retrieves a user by ID or returns ErrNotFound.
func (s *Store) GetUserByID(ctx context.Context, id int64) (*User, error) {
var u User
err := s.pool.QueryRow(ctx, `
SELECT id, username, display_name, password_hash, avatar, created_at
FROM users WHERE id=$1
`, id).Scan(
&u.ID, &u.Username, &u.DisplayName, &u.PasswordHash, &u.Avatar, &u.CreatedAt,
)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return nil, ErrNotFound
}
return nil, err
}
return &u, nil
}
// SearchUsers returns users matching query in username or display_name via ILIKE.
// Empty query returns an empty slice.
func (s *Store) SearchUsers(ctx context.Context, query string, limit int) ([]User, error) {
if query == "" {
return []User{}, nil
}
pattern := "%" + query + "%"
rows, err := s.pool.Query(ctx, `
SELECT id, username, display_name, password_hash, avatar, created_at
FROM users
WHERE username ILIKE $1 OR display_name ILIKE $1
ORDER BY username
LIMIT $2
`, pattern, limit)
if err != nil {
return nil, err
}
defer rows.Close()
var users []User
for rows.Next() {
var u User
if err := rows.Scan(
&u.ID, &u.Username, &u.DisplayName, &u.PasswordHash, &u.Avatar, &u.CreatedAt,
); err != nil {
return nil, err
}
users = append(users, u)
}
return users, nil
}