golang-template/internal/services/user.go

114 lines
2.6 KiB
Go

package services
import (
"context"
"database/sql"
"errors"
"go-backend/internal/auth"
"go-backend/internal/database/sqlc"
)
type UserService struct {
db *sql.DB
queries *sqlc.Queries
}
func NewUserService(db *sql.DB, queries *sqlc.Queries) *UserService {
return &UserService{
db: db,
queries: queries,
}
}
func (s *UserService) FindByID(ctx context.Context, id string) (*sqlc.UserAuth, error) {
user, err := s.queries.SelectUserById(ctx, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
return &user, nil
}
func (s *UserService) FindByUsername(ctx context.Context, username string) (*sqlc.UserAuth, error) {
user, err := s.queries.SelectUserByUsername(ctx, username)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
return &user, nil
}
func (s *UserService) CreateUser(ctx context.Context, userInput sqlc.InsertUserParams) (*sqlc.UserAuth, error) {
user, err := s.queries.InsertUser(ctx, userInput)
if err != nil {
return nil, err
}
return &user, nil
}
var (
ErrCurrentPasswordRequired = errors.New("current password is required to set a new password")
ErrInvalidCurrentPassword = errors.New("current password is incorrect")
)
// PatchUserInput carries a partial update: a nil field means "leave this
// field alone", distinguishing "not provided" from a provided zero value,
// since Go has no nullish type to express that on its own.
type PatchUserInput struct {
Username *string
Email *string
CurrentPassword *string
NewPassword *string
}
func (s *UserService) PatchUser(ctx context.Context, id string, patchUserInput PatchUserInput) (*sqlc.UserAuth, error) {
params := sqlc.PatchUserParams{ID: id}
if patchUserInput.Username != nil {
params.Username = sql.NullString{String: *patchUserInput.Username, Valid: true}
}
if patchUserInput.Email != nil {
params.Email = sql.NullString{String: *patchUserInput.Email, Valid: true}
}
if patchUserInput.NewPassword != nil {
if patchUserInput.CurrentPassword == nil {
return nil, ErrCurrentPasswordRequired
}
user, err := s.queries.SelectUserById(ctx, id)
if err != nil {
return nil, err
}
if !auth.VerifyPassword(user.PasswordHash, *patchUserInput.CurrentPassword) {
return nil, ErrInvalidCurrentPassword
}
newHash, err := auth.HashPassword(*patchUserInput.NewPassword)
if err != nil {
return nil, err
}
params.PasswordHash = sql.NullString{String: newHash, Valid: true}
}
user, err := s.queries.PatchUser(ctx, params)
if err != nil {
return nil, err
}
return &user, nil
}