diff options
Diffstat (limited to 'pkg/handler')
-rw-r--r-- | pkg/handler/location.go | 141 | ||||
-rw-r--r-- | pkg/handler/user.go | 155 |
2 files changed, 296 insertions, 0 deletions
diff --git a/pkg/handler/location.go b/pkg/handler/location.go new file mode 100644 index 0000000..9f4a2c1 --- /dev/null +++ b/pkg/handler/location.go @@ -0,0 +1,141 @@ +package handler + +import ( + "encoding/json" + "time" + + "github.com/alexedwards/scs/v2" + "github.com/labstack/echo/v4" + "gitrepo.ru/neonxp/track/pkg/models" + "go.etcd.io/bbolt" + "go.neonxp.ru/objectid" +) + +type Location struct { + db *bbolt.DB + sessions *scs.SessionManager +} + +func NewLocation(db *bbolt.DB, sessions *scs.SessionManager) *Location { + return &Location{ + db: db, + sessions: sessions, + } +} + +func (l *Location) AddPoint(c echo.Context) error { + uu := l.sessions.Get(c.Request().Context(), "user") + if uu == nil { + return echo.ErrForbidden + } + user, ok := uu.(*models.User) + if !ok { + return echo.ErrForbidden + } + + req := new(PointArgs) + if err := c.Bind(req); err != nil { + return err + } + + point := &models.Point{ + ID: objectid.FromTime(req.Time), + UserID: user.ID, + Lat: req.Lat, + Lon: req.Lon, + Time: req.Time, + Speed: req.Speed, + Direction: req.Direction, + Accuracy: req.Accuracy, + } + + err := l.db.Update(func(tx *bbolt.Tx) error { + points, err := tx.CreateBucketIfNotExists([]byte("points")) + if err != nil { + return err + } + jp, err := json.Marshal(point) + if err != nil { + return err + } + + return points.Put(point.ID, jp) + }) + if err != nil { + return err + } + + return c.NoContent(201) +} + +func (l *Location) GetPoints(c echo.Context) error { + // uu := l.sessions.Get(c.Request().Context(), "user") + // if uu == nil { + // return echo.ErrForbidden + // } + // user, ok := uu.(*models.User) + // if !ok { + // return echo.ErrForbidden + // } + + pointsResp := []models.Point{} + err := l.db.View(func(tx *bbolt.Tx) error { + points := tx.Bucket([]byte("points")) + point := new(models.Point) + return points.ForEach(func(k, v []byte) error { + if err := json.Unmarshal(v, point); err != nil { + return err + } + // if slices.Equal(point.UserID, user.ID) { + pointsResp = append(pointsResp, *point) + // } + return nil + }) + }) + if err != nil { + return err + } + + return c.JSON(200, pointsResp) +} + +func (l *Location) GetLast(c echo.Context) error { + // uu := l.sessions.Get(c.Request().Context(), "user") + // if uu == nil { + // return echo.ErrForbidden + // } + // user, ok := uu.(*models.User) + // if !ok { + // return echo.ErrForbidden + // } + + lastPoint := new(models.Point) + err := l.db.View(func(tx *bbolt.Tx) error { + points := tx.Bucket([]byte("points")) + point := new(models.Point) + return points.ForEach(func(k, v []byte) error { + if err := json.Unmarshal(v, point); err != nil { + return err + } + // if slices.Equal(point.UserID, user.ID) && lastPoint.Time.Before(point.Time) { + if lastPoint.Time.Before(point.Time) { + lastPoint = point + } + return nil + }) + }) + if err != nil { + return err + } + + return c.JSON(200, lastPoint) +} + +type PointArgs struct { + Lat float64 `query:"lat"` + Lon float64 `query:"lon"` + Time time.Time `query:"time"` + Speed float64 `query:"spd"` + Direction float64 `query:"dir"` + Accuracy float64 `query:"acc"` +} 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"` +} |