feat: +register
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
package user
|
||||
|
||||
import "errors"
|
||||
|
||||
var ErrDuplicateKey = errors.New("duplicate key")
|
||||
var ErrEmailAlreadyExists = errors.New("email already exists")
|
||||
@@ -0,0 +1,86 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
service Service
|
||||
}
|
||||
|
||||
func NewHandler(pool *pgxpool.Pool) Handler {
|
||||
return Handler {
|
||||
service: NewService(NewRepo(pool)),
|
||||
}
|
||||
}
|
||||
|
||||
type RegisterRequest struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type RegisterValidationError struct {
|
||||
message string
|
||||
}
|
||||
|
||||
func (e RegisterValidationError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (req RegisterRequest) Validate() error {
|
||||
if _, err := mail.ParseAddress(req.Email); err != nil {
|
||||
return RegisterValidationError {
|
||||
message: "invalid email",
|
||||
}
|
||||
}
|
||||
|
||||
if len(req.Password) < 10 {
|
||||
return RegisterValidationError {
|
||||
message: "password is too short",
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h Handler) RegisterHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var req RegisterRequest
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "invalid json", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if err := req.Validate(); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.service.Register(r.Context(), req.Email, req.Password)
|
||||
if err != nil {
|
||||
log.Println("could not register: ", err)
|
||||
switch {
|
||||
case errors.Is(err, ErrEmailAlreadyExists):
|
||||
http.Error(w, "email already exists", http.StatusConflict)
|
||||
default:
|
||||
http.Error(w, "internal error", http.StatusInternalServerError)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
fmt.Fprintln(w, "success")
|
||||
}
|
||||
|
||||
func (h Handler) AttachHandlers(router chi.Router) {
|
||||
router.Post("/register", h.RegisterHandler)
|
||||
// router.Post("/login", LoginHandler)
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
// "github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgconn"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
type Repo struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func NewRepo(pool *pgxpool.Pool) Repo {
|
||||
return Repo {
|
||||
pool: pool,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Repo) Exec(ctx context.Context, query string, args ...any) (pgconn.CommandTag, error) {
|
||||
conn, err := r.pool.Acquire(context.Background())
|
||||
if err != nil {
|
||||
return pgconn.CommandTag{}, err
|
||||
}
|
||||
|
||||
return conn.Exec(ctx, query, args...)
|
||||
}
|
||||
|
||||
func (r *Repo) CreateUser(ctx context.Context, email, hash string) error {
|
||||
_, err := r.Exec(
|
||||
ctx,
|
||||
"INSERT INTO users(email, password_hash) VALUES ($1, $2)",
|
||||
email, hash,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
var pgErr *pgconn.PgError
|
||||
if errors.As(err, &pgErr) {
|
||||
return ErrDuplicateKey
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
repo Repo
|
||||
}
|
||||
|
||||
func NewService(repo Repo) Service {
|
||||
return Service {
|
||||
repo: repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (s Service) Register(ctx context.Context, email, password string) error {
|
||||
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = s.repo.CreateUser(ctx, email, string(bytes)); err != nil {
|
||||
switch {
|
||||
case errors.Is(err, ErrDuplicateKey):
|
||||
return ErrEmailAlreadyExists
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// func (s Service) Login(password string) (string, error) {
|
||||
// hash, err := s.repo.
|
||||
// err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
// }
|
||||
Reference in New Issue
Block a user