nquest/pkg/service/user.go

156 lines
3.4 KiB
Go

package service
import (
"context"
"encoding/hex"
"errors"
"net/mail"
normalizer "github.com/dimuska139/go-email-normalizer"
"github.com/google/uuid"
"github.com/labstack/echo-contrib/session"
"github.com/labstack/echo/v4"
"golang.org/x/crypto/bcrypt"
"gorm.io/gorm"
"gitrepo.ru/neonxp/nquest/pkg/models"
)
var (
ErrInvalidUsername = errors.New("invalid username")
ErrInvalidPassword = errors.New("invalid password")
ErrInvalidEmail = errors.New("invalid email")
ErrDiffferentPassword = errors.New("different password")
ErrDuplicateUser = errors.New("duplicate user")
ErrIvalidUserOrPassword = errors.New("invalid user or password")
)
type User struct {
DB *gorm.DB
}
// NewUser returns new User.
func NewUser(db *gorm.DB) *User {
return &User{
DB: db,
}
}
func (s *User) Register(ctx context.Context, username, email, password, password2 string) (*models.User, error) {
if len(username) < 3 || len(username) > 36 {
return nil, ErrInvalidUsername
}
if len(password) < 8 {
return nil, ErrInvalidPassword
}
if password != password2 {
return nil, ErrDiffferentPassword
}
if !isValidEmail(email) {
return nil, ErrInvalidEmail
}
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
u := &models.User{
ID: uuid.New(),
Username: username,
Email: normalizer.NewNormalizer().Normalize(email),
Password: hex.EncodeToString(hashed),
}
err = s.DB.WithContext(ctx).Create(u).Error
switch {
case errors.Is(err, models.ErrEmptyPassword):
return nil, ErrInvalidPassword
case errors.Is(err, gorm.ErrDuplicatedKey):
return nil, ErrDuplicateUser
case err != nil:
return nil, err
}
return u, nil
}
func (s *User) Login(ctx context.Context, email, password string) (*models.User, error) {
u := new(models.User)
nemail := normalizer.NewNormalizer().Normalize(email)
err := s.DB.
WithContext(ctx).
Preload("Games", `Finish = true`).
Preload("Games.Game").
Where("email = ?", nemail).
First(u).
Error
if err != nil {
return nil, ErrIvalidUserOrPassword
}
b, err := hex.DecodeString(u.Password)
if err != nil {
return nil, ErrIvalidUserOrPassword
}
if err := bcrypt.CompareHashAndPassword(b, []byte(password)); err != nil {
return nil, ErrIvalidUserOrPassword
}
return u, nil
}
func (s *User) GetUserByID(ctx context.Context, userID uuid.UUID) (*models.User, error) {
u := new(models.User)
return u, s.DB.WithContext(ctx).
Preload("Games", `Finish = true`).
Preload("Games.Game").
First(u, userID).Error
}
func (s *User) GetUser(c echo.Context) *models.User {
sess, err := session.Get("session", c)
if err != nil {
return nil
}
userID, ok := sess.Values["userID"].(string)
if !ok {
return nil
}
uid, err := uuid.Parse(userID)
if err != nil {
return nil
}
user, err := s.GetUserByID(c.Request().Context(), uid)
if err != nil {
return nil
}
return user
}
func (s *User) List(ctx context.Context) ([]models.User, error) {
res := make([]models.User, 0)
return res, s.DB.WithContext(ctx).
Preload("Games").
Order("experience DESC").
Limit(100).
Find(&res).
Error
}
func (s *User) Update(ctx context.Context, user *models.User) error {
return s.DB.WithContext(ctx).Session(&gorm.Session{FullSaveAssociations: true}).Save(user).Error
}
func isValidEmail(email string) bool {
_, err := mail.ParseAddress(email)
return err == nil
}