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 }