diff options
author | Alexander Neonxp Kiryukhin <i@neonxp.ru> | 2024-08-18 13:29:54 +0300 |
---|---|---|
committer | Alexander Neonxp Kiryukhin <i@neonxp.ru> | 2024-08-18 13:29:54 +0300 |
commit | fd70f95224374d23157ee7c0357733102cd0df53 (patch) | |
tree | e490c12e021cedaf211b292d5d623baa32a673fc /pkg/handler/user.go |
Diffstat (limited to 'pkg/handler/user.go')
-rw-r--r-- | pkg/handler/user.go | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/pkg/handler/user.go b/pkg/handler/user.go new file mode 100644 index 0000000..9506c85 --- /dev/null +++ b/pkg/handler/user.go @@ -0,0 +1,155 @@ +package handler + +import ( + "encoding/json" + "net/mail" + "strings" + + "github.com/alexedwards/scs/v2" + "github.com/labstack/echo/v4" + "gitrepo.ru/neonxp/track/pkg/models" + "go.etcd.io/bbolt" + "go.neonxp.ru/objectid" + "golang.org/x/crypto/bcrypt" +) + +var ( + ErrInvalidPasswordLen = echo.NewHTTPError(400, "Неверная длина пароля (должно быть от 8 до 32 символов)") + ErrPasswordsNotSame = echo.NewHTTPError(400, "Пароли не совпадают") + ErrInvalidEmailOrPassword = echo.NewHTTPError(400, "Неверный email или пароль") +) + +type User struct { + db *bbolt.DB + sessions *scs.SessionManager +} + +func NewUser(db *bbolt.DB, sessions *scs.SessionManager) *User { + return &User{ + db: db, + sessions: sessions, + } +} + +func (u *User) Register(c echo.Context) error { + req := new(RegisterRequest) + if err := c.Bind(req); err != nil { + return err + } + + if _, err := mail.ParseAddress(req.Email); err != nil { + return echo.NewHTTPError(400, err.Error()) + } + + if len(req.Password) < 8 || len(req.Password) > 32 { + return ErrInvalidPasswordLen + } + + if req.Password != req.Password2 { + return ErrPasswordsNotSame + } + + password, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) + if err != nil { + return err + } + + user := &models.User{ + ID: objectid.New(), + Email: req.Email, + Password: password, + } + + err = u.db.Update(func(tx *bbolt.Tx) error { + users, err := tx.CreateBucketIfNotExists([]byte("users")) + if err != nil { + return err + } + + jb, err := json.Marshal(user) + if err != nil { + return err + } + + if err := users.Put([]byte(strings.ToLower(user.Email)), jb); err != nil { + return err + } + + return nil + }) + if err != nil { + return err + } + + return c.JSON(201, UserResponse{ + ID: user.ID, + Email: user.Email, + }) +} + +func (u *User) Login(c echo.Context) error { + req := new(LoginRequest) + if err := c.Bind(req); err != nil { + return err + } + + user := new(models.User) + + err := u.db.View(func(tx *bbolt.Tx) error { + users := tx.Bucket([]byte("users")) + jb := users.Get([]byte(strings.ToLower(req.Email))) + if jb == nil { + return ErrInvalidEmailOrPassword + } + if err := json.Unmarshal(jb, user); err != nil { + return err + } + if err := bcrypt.CompareHashAndPassword(user.Password, []byte(req.Password)); err != nil { + return ErrInvalidEmailOrPassword + } + + return nil + }) + if err != nil { + return err + } + + u.sessions.Put(c.Request().Context(), "user", user) + + return c.JSON(200, UserResponse{ + ID: user.ID, + Email: user.Email, + }) +} + +func (u *User) User(c echo.Context) error { + uu := u.sessions.Get(c.Request().Context(), "user") + if uu == nil { + return echo.ErrForbidden + } + user, ok := uu.(*models.User) + if !ok { + return echo.ErrForbidden + } + + return c.JSON(200, UserResponse{ + ID: user.ID, + Email: user.Email, + }) +} + +type RegisterRequest struct { + Email string `json:"email"` + Password string `json:"password"` + Password2 string `json:"password2"` +} + +type LoginRequest struct { + Email string `json:"email"` + Password string `json:"password"` +} + +type UserResponse struct { + ID objectid.ID `json:"id"` + Email string `json:"email"` +} |