- PUT /api/v1/me (update name, city, bio, gender, photo) - GET/PUT /api/v1/me/prefs (drinks, activities, purposes, language) - PUT /api/v1/me/location (auto-noise ~300m) - PUT /api/v1/me/visibility (hide/show on map) - GET /api/v1/search/nearby?lat&lng&radius (haversine, 1h TTL) - GET /api/v1/users/:id (public profile, no email/phone) - migrations 0002: user_preferences, user_locations
112 lines
2.7 KiB
Go
112 lines
2.7 KiB
Go
package users
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
type User struct {
|
|
ID uuid.UUID
|
|
Email string
|
|
Phone string
|
|
Name string
|
|
Birthdate *time.Time
|
|
Gender string
|
|
City string
|
|
Bio string
|
|
PhotoURL string
|
|
IsVerified bool
|
|
IsBlocked bool
|
|
CreatedAt time.Time
|
|
UpdatedAt time.Time
|
|
LastSeenAt *time.Time
|
|
}
|
|
|
|
type Repo struct {
|
|
pool *pgxpool.Pool
|
|
}
|
|
|
|
func NewRepo(pool *pgxpool.Pool) *Repo {
|
|
return &Repo{pool: pool}
|
|
}
|
|
|
|
func (r *Repo) CreateWithPassword(ctx context.Context, u *User, passwordHash string) error {
|
|
return r.pool.QueryRow(ctx, `
|
|
INSERT INTO users (email, phone, password_hash, name, birthdate, gender, city)
|
|
VALUES (NULLIF($1, ''), NULLIF($2, ''), $3, $4, $5, NULLIF($6, ''), NULLIF($7, ''))
|
|
RETURNING id, created_at, updated_at`,
|
|
u.Email, u.Phone, passwordHash, u.Name, u.Birthdate, u.Gender, u.City,
|
|
).Scan(&u.ID, &u.CreatedAt, &u.UpdatedAt)
|
|
}
|
|
|
|
func (r *Repo) UpdateRaw(ctx context.Context, sql string, args ...interface{}) (int64, error) {
|
|
tag, err := r.pool.Exec(ctx, sql, args...)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return tag.RowsAffected(), nil
|
|
}
|
|
|
|
func (r *Repo) GetByID(ctx context.Context, id uuid.UUID) (*User, error) {
|
|
u := &User{}
|
|
var email, phone, gender, city, bio, photo *string
|
|
err := r.pool.QueryRow(ctx, `
|
|
SELECT id, email, phone, name, birthdate, gender, city, bio, photo_url,
|
|
is_verified, is_blocked, created_at, updated_at, last_seen_at
|
|
FROM users WHERE id=$1`, id,
|
|
).Scan(&u.ID, &email, &phone, &u.Name, &u.Birthdate, &gender, &city, &bio, &photo,
|
|
&u.IsVerified, &u.IsBlocked, &u.CreatedAt, &u.UpdatedAt, &u.LastSeenAt)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if email != nil {
|
|
u.Email = *email
|
|
}
|
|
if phone != nil {
|
|
u.Phone = *phone
|
|
}
|
|
if gender != nil {
|
|
u.Gender = *gender
|
|
}
|
|
if city != nil {
|
|
u.City = *city
|
|
}
|
|
if bio != nil {
|
|
u.Bio = *bio
|
|
}
|
|
if photo != nil {
|
|
u.PhotoURL = *photo
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func (r *Repo) GetByEmail(ctx context.Context, email string) (*User, string, error) {
|
|
u := &User{}
|
|
var hash string
|
|
var em *string
|
|
err := r.pool.QueryRow(ctx, `
|
|
SELECT id, email, password_hash, name, birthdate, gender, city, bio, photo_url,
|
|
is_verified, is_blocked, created_at, updated_at, last_seen_at
|
|
FROM users WHERE email=$1`, email,
|
|
).Scan(&u.ID, &em, &hash, &u.Name, &u.Birthdate, &u.Gender, &u.City, &u.Bio, &u.PhotoURL,
|
|
&u.IsVerified, &u.IsBlocked, &u.CreatedAt, &u.UpdatedAt, &u.LastSeenAt)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, "", nil
|
|
}
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
if em != nil {
|
|
u.Email = *em
|
|
}
|
|
return u, hash, nil
|
|
}
|