116 lines
3.6 KiB
Go
116 lines
3.6 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
type Repository struct {
|
|
db *sql.DB
|
|
}
|
|
|
|
func NewRepository(db *sql.DB) *Repository {
|
|
return &Repository{db: db}
|
|
}
|
|
|
|
var ErrEmailTaken = errors.New("email already registered")
|
|
|
|
func (r *Repository) CreateUser(ctx context.Context, email, passwordHash, name string) (User, error) {
|
|
user := User{ID: uuid.New(), Email: strings.ToLower(strings.TrimSpace(email)), Name: strings.TrimSpace(name)}
|
|
_, err := r.db.ExecContext(ctx, `
|
|
INSERT INTO users (id, email, password_hash, name)
|
|
VALUES ($1, $2, $3, $4)
|
|
`, user.ID, user.Email, passwordHash, user.Name)
|
|
if err != nil {
|
|
if strings.Contains(strings.ToLower(err.Error()), "unique") {
|
|
return User{}, ErrEmailTaken
|
|
}
|
|
return User{}, fmt.Errorf("create user: %w", err)
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func (r *Repository) FindByEmail(ctx context.Context, email string) (storedUser, error) {
|
|
var user storedUser
|
|
err := r.db.QueryRowContext(ctx, `
|
|
SELECT id, email, name, studio_name, tagline, website_url, instagram_url, password_hash
|
|
FROM users
|
|
WHERE lower(email) = lower($1)
|
|
`, strings.TrimSpace(email)).Scan(&user.ID, &user.Email, &user.Name, &user.StudioName, &user.Tagline, &user.WebsiteURL, &user.InstagramURL, &user.PasswordHash)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return storedUser{}, sql.ErrNoRows
|
|
}
|
|
if err != nil {
|
|
return storedUser{}, fmt.Errorf("find user by email: %w", err)
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func (r *Repository) FindByID(ctx context.Context, id uuid.UUID) (User, error) {
|
|
var user User
|
|
err := r.db.QueryRowContext(ctx, `
|
|
SELECT id, email, name, studio_name, tagline, website_url, instagram_url
|
|
FROM users
|
|
WHERE id = $1
|
|
`, id).Scan(&user.ID, &user.Email, &user.Name, &user.StudioName, &user.Tagline, &user.WebsiteURL, &user.InstagramURL)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return User{}, sql.ErrNoRows
|
|
}
|
|
if err != nil {
|
|
return User{}, fmt.Errorf("find user by id: %w", err)
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func (r *Repository) FindByIDWithPassword(ctx context.Context, id uuid.UUID) (storedUser, error) {
|
|
var user storedUser
|
|
err := r.db.QueryRowContext(ctx, `
|
|
SELECT id, email, name, studio_name, tagline, website_url, instagram_url, password_hash
|
|
FROM users
|
|
WHERE id = $1
|
|
`, id).Scan(&user.ID, &user.Email, &user.Name, &user.StudioName, &user.Tagline, &user.WebsiteURL, &user.InstagramURL, &user.PasswordHash)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return storedUser{}, sql.ErrNoRows
|
|
}
|
|
if err != nil {
|
|
return storedUser{}, fmt.Errorf("find user credentials: %w", err)
|
|
}
|
|
return user, nil
|
|
}
|
|
|
|
func (r *Repository) UpdateUser(ctx context.Context, id uuid.UUID, email, name, studioName, tagline, websiteURL, instagramURL string) (User, error) {
|
|
_, err := r.db.ExecContext(ctx, `
|
|
UPDATE users
|
|
SET email = $1, name = $2, studio_name = $3, tagline = $4, website_url = $5, instagram_url = $6, updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = $7
|
|
`, strings.ToLower(strings.TrimSpace(email)), strings.TrimSpace(name), studioName, tagline, websiteURL, instagramURL, id)
|
|
if err != nil {
|
|
if strings.Contains(strings.ToLower(err.Error()), "unique") {
|
|
return User{}, ErrEmailTaken
|
|
}
|
|
return User{}, fmt.Errorf("update user: %w", err)
|
|
}
|
|
return r.FindByID(ctx, id)
|
|
}
|
|
|
|
func (r *Repository) UpdatePassword(ctx context.Context, id uuid.UUID, passwordHash string) error {
|
|
result, err := r.db.ExecContext(ctx, `
|
|
UPDATE users
|
|
SET password_hash = $1, updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = $2
|
|
`, passwordHash, id)
|
|
if err != nil {
|
|
return fmt.Errorf("update password: %w", err)
|
|
}
|
|
count, err := result.RowsAffected()
|
|
if err != nil || count == 0 {
|
|
return sql.ErrNoRows
|
|
}
|
|
return nil
|
|
}
|