aboutsummaryrefslogtreecommitdiff
path: root/pkg/service/user
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/service/user')
-rw-r--r--pkg/service/user/login.go31
-rw-r--r--pkg/service/user/register.go85
-rw-r--r--pkg/service/user/user.go12
3 files changed, 128 insertions, 0 deletions
diff --git a/pkg/service/user/login.go b/pkg/service/user/login.go
new file mode 100644
index 0000000..d1f5c65
--- /dev/null
+++ b/pkg/service/user/login.go
@@ -0,0 +1,31 @@
+package user
+
+import (
+ "context"
+ "errors"
+
+ normalizer "github.com/dimuska139/go-email-normalizer/v3"
+ "golang.org/x/crypto/bcrypt"
+
+ "go.neonxp.ru/framework/pkg/model"
+ "go.neonxp.ru/framework/pkg/tpl"
+)
+
+var ErrInvalidUserOrPassword = errors.New("invalid_user_or_password")
+
+func (s *Service) Login(ctx context.Context, form *tpl.LoginForm) (*model.User, error) {
+ n := normalizer.NewNormalizer()
+ u := &model.User{
+ Email: n.Normalize(form.Email),
+ }
+
+ if err := s.db.NewSelect().Model(u).Where("email = ?", u.Email).Scan(ctx, u); err != nil {
+ return nil, errors.Join(err, ErrInvalidUserOrPassword)
+ }
+
+ if err := bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(form.Password)); err != nil {
+ return nil, errors.Join(err, ErrInvalidUserOrPassword)
+ }
+
+ return u, nil
+}
diff --git a/pkg/service/user/register.go b/pkg/service/user/register.go
new file mode 100644
index 0000000..69f19a2
--- /dev/null
+++ b/pkg/service/user/register.go
@@ -0,0 +1,85 @@
+package user
+
+import (
+ "context"
+ "errors"
+ "regexp"
+
+ normalizer "github.com/dimuska139/go-email-normalizer/v3"
+ "github.com/uptrace/bun/driver/pgdriver"
+ "golang.org/x/crypto/bcrypt"
+
+ "go.neonxp.ru/framework/pkg/model"
+ "go.neonxp.ru/framework/pkg/tpl"
+)
+
+var (
+ ErrUsernameToShort = errors.New("username_to_short")
+ ErrUserAlreadyExist = errors.New("user_already_exists")
+ ErrPasswordTooWeak = errors.New("password_too_weak")
+ ErrPasswordTooShort = errors.New("password_too_short")
+)
+
+const (
+ minUsernameLen = 3
+ minPasswordLen = 8
+)
+
+func (s *Service) Register(ctx context.Context, form *tpl.RegisterForm) (*model.User, error) {
+ if len(form.Username) < minUsernameLen {
+ return nil, ErrUsernameToShort
+ }
+
+ if err := checkPasswordLever(form.Password); err != nil {
+ return nil, err
+ }
+
+ password, err := bcrypt.GenerateFromPassword([]byte(form.Password), bcrypt.DefaultCost)
+ if err != nil {
+ return nil, err
+ }
+
+ n := normalizer.NewNormalizer()
+ u := &model.User{
+ Username: form.Username,
+ Email: n.Normalize(form.Email),
+ Password: string(password),
+ }
+
+ if _, err := s.db.NewInsert().Model(u).Returning("*").Exec(ctx); err != nil {
+ pqErr := pgdriver.Error{}
+ if errors.As(err, &pqErr) {
+ if pqErr.Field('C') == "23505" {
+ return nil, ErrUserAlreadyExist
+ }
+ }
+
+ return nil, err
+ }
+
+ return u, nil
+}
+
+func checkPasswordLever(ps string) error {
+ if len(ps) < minPasswordLen {
+ return ErrPasswordTooShort
+ }
+
+ lowerCase := `[a-z]{1}`
+ upperCase := `[A-Z]{1}`
+ symbol := `[0-9!@#~$%^&*()+|_]{1}`
+
+ if b, err := regexp.MatchString(lowerCase, ps); !b || err != nil {
+ return ErrPasswordTooWeak
+ }
+
+ if b, err := regexp.MatchString(upperCase, ps); !b || err != nil {
+ return ErrPasswordTooWeak
+ }
+
+ if b, err := regexp.MatchString(symbol, ps); !b || err != nil {
+ return ErrPasswordTooWeak
+ }
+
+ return nil
+}
diff --git a/pkg/service/user/user.go b/pkg/service/user/user.go
new file mode 100644
index 0000000..aa27eee
--- /dev/null
+++ b/pkg/service/user/user.go
@@ -0,0 +1,12 @@
+package user
+
+import "github.com/uptrace/bun"
+
+type Service struct {
+ db *bun.DB
+}
+
+// NewService returns new Service.
+func NewService(db *bun.DB) *Service {
+ return &Service{db: db}
+}