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"` }