backend barebone done, big chunk of work done
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/jackc/pgconn"
|
||||
"github.com/jackc/pgerrcode"
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user