156 lines
3.1 KiB
Go
156 lines
3.1 KiB
Go
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"`
|
|
}
|