forked from ebhomengo/niki
				
			feat(service): add admin login and register
This commit is contained in:
		
							parent
							
								
									085989538d
								
							
						
					
					
						commit
						6e0d616036
					
				| 
						 | 
					@ -32,6 +32,8 @@ kavenegar_sms_provider:
 | 
				
			||||||
  otp_template_new_user: ebhomeverify
 | 
					  otp_template_new_user: ebhomeverify
 | 
				
			||||||
  otp_template_registered_user: ebhomeverify
 | 
					  otp_template_registered_user: ebhomeverify
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					admin_auth:
 | 
				
			||||||
 | 
					  sign_key: admin-jwt_secret_test_nik
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,8 @@ import (
 | 
				
			||||||
	"git.gocasts.ir/ebhomengo/niki/adapter/redis"
 | 
						"git.gocasts.ir/ebhomengo/niki/adapter/redis"
 | 
				
			||||||
	smsprovider "git.gocasts.ir/ebhomengo/niki/adapter/sms_provider/kavenegar"
 | 
						smsprovider "git.gocasts.ir/ebhomengo/niki/adapter/sms_provider/kavenegar"
 | 
				
			||||||
	"git.gocasts.ir/ebhomengo/niki/repository/mysql"
 | 
						"git.gocasts.ir/ebhomengo/niki/repository/mysql"
 | 
				
			||||||
	authservice "git.gocasts.ir/ebhomengo/niki/service/auth/benefactor"
 | 
						adminauthservice "git.gocasts.ir/ebhomengo/niki/service/auth/admin"
 | 
				
			||||||
 | 
						benefactorauthservice "git.gocasts.ir/ebhomengo/niki/service/auth/benefactor"
 | 
				
			||||||
	benefactorservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/benefactor"
 | 
						benefactorservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/benefactor"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,10 +14,11 @@ type HTTPServer struct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
	HTTPServer           HTTPServer               `koanf:"http_server"`
 | 
						HTTPServer           HTTPServer                   `koanf:"http_server"`
 | 
				
			||||||
	Mysql                mysql.Config             `koanf:"mysql"`
 | 
						Mysql                mysql.Config                 `koanf:"mysql"`
 | 
				
			||||||
	Auth                 authservice.Config       `koanf:"auth"`
 | 
						Auth                 benefactorauthservice.Config `koanf:"auth"`
 | 
				
			||||||
	Redis                redis.Config             `koanf:"redis"`
 | 
						AdminAuth            adminauthservice.Config      `koanf:"admin_auth"`
 | 
				
			||||||
	KavenegarSmsProvider smsprovider.Config       `koanf:"kavenegar_sms_provider"`
 | 
						Redis                redis.Config                 `koanf:"redis"`
 | 
				
			||||||
	BenefactorSvc        benefactorservice.Config `koanf:"benefactor_service"`
 | 
						KavenegarSmsProvider smsprovider.Config           `koanf:"kavenegar_sms_provider"`
 | 
				
			||||||
 | 
						BenefactorSvc        benefactorservice.Config     `koanf:"benefactor_service"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,4 +12,5 @@ const (
 | 
				
			||||||
	AccessTokenExpireDuration  = time.Hour * 24
 | 
						AccessTokenExpireDuration  = time.Hour * 24
 | 
				
			||||||
	RefreshTokenExpireDuration = time.Hour * 24 * 7
 | 
						RefreshTokenExpireDuration = time.Hour * 24 * 7
 | 
				
			||||||
	AuthMiddlewareContextKey   = "claims"
 | 
						AuthMiddlewareContextKey   = "claims"
 | 
				
			||||||
 | 
						BcryptCost                 = 3
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,25 @@
 | 
				
			||||||
 | 
					package adminhandler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						adminservice "git.gocasts.ir/ebhomengo/niki/service/admin/admin"
 | 
				
			||||||
 | 
						adminauthservice "git.gocasts.ir/ebhomengo/niki/service/auth/admin"
 | 
				
			||||||
 | 
						adminvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/admin"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Handler struct {
 | 
				
			||||||
 | 
						authConfig adminauthservice.Config
 | 
				
			||||||
 | 
						authSvc    adminauthservice.Service
 | 
				
			||||||
 | 
						adminSvc   adminservice.Service
 | 
				
			||||||
 | 
						adminVld   adminvalidator.Validator
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(authConfig adminauthservice.Config, authSvc adminauthservice.Service,
 | 
				
			||||||
 | 
						adminSvc adminservice.Service, adminVld adminvalidator.Validator,
 | 
				
			||||||
 | 
					) Handler {
 | 
				
			||||||
 | 
						return Handler{
 | 
				
			||||||
 | 
							authConfig: authConfig,
 | 
				
			||||||
 | 
							authSvc:    authSvc,
 | 
				
			||||||
 | 
							adminSvc:   adminSvc,
 | 
				
			||||||
 | 
							adminVld:   adminVld,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					package adminhandler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
 | 
				
			||||||
 | 
						httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
 | 
				
			||||||
 | 
						"github.com/labstack/echo/v4"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h Handler) LoginByPhoneNumber(c echo.Context) error {
 | 
				
			||||||
 | 
						var req adminserviceparam.LoginWithPhoneNumberRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if bErr := c.Bind(&req); bErr != nil {
 | 
				
			||||||
 | 
							return echo.NewHTTPError(http.StatusBadRequest)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if fieldErrors, err := h.adminVld.ValidateLoginWithPhoneNumberRequest(req); err != nil {
 | 
				
			||||||
 | 
							msg, code := httpmsg.Error(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return c.JSON(code, echo.Map{
 | 
				
			||||||
 | 
								"message": msg,
 | 
				
			||||||
 | 
								"errors":  fieldErrors,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp, sErr := h.adminSvc.LoginWithPhoneNumber(c.Request().Context(), req)
 | 
				
			||||||
 | 
						if sErr != nil {
 | 
				
			||||||
 | 
							msg, code := httpmsg.Error(sErr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return echo.NewHTTPError(code, msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return c.JSON(http.StatusOK, resp)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,33 @@
 | 
				
			||||||
 | 
					package adminhandler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
 | 
				
			||||||
 | 
						httpmsg "git.gocasts.ir/ebhomengo/niki/pkg/http_msg"
 | 
				
			||||||
 | 
						"github.com/labstack/echo/v4"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h Handler) Register(c echo.Context) error {
 | 
				
			||||||
 | 
						var req adminserviceparam.RegisterRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if bErr := c.Bind(&req); bErr != nil {
 | 
				
			||||||
 | 
							return echo.NewHTTPError(http.StatusBadRequest)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if fieldErrors, err := h.adminVld.ValidateRegisterRequest(req); err != nil {
 | 
				
			||||||
 | 
							msg, code := httpmsg.Error(err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return c.JSON(code, echo.Map{
 | 
				
			||||||
 | 
								"message": msg,
 | 
				
			||||||
 | 
								"errors":  fieldErrors,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						resp, sErr := h.adminSvc.Register(c.Request().Context(), req)
 | 
				
			||||||
 | 
						if sErr != nil {
 | 
				
			||||||
 | 
							msg, code := httpmsg.Error(sErr)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return echo.NewHTTPError(code, msg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return c.JSON(http.StatusOK, resp)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,14 @@
 | 
				
			||||||
 | 
					package adminhandler
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "github.com/labstack/echo/v4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (h Handler) SetRoutes(e *echo.Echo) {
 | 
				
			||||||
 | 
						r := e.Group("/admins")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//nolint:gocritic
 | 
				
			||||||
 | 
						//r.POST("/", h.Add).Name = "admin-addkindboxreq"
 | 
				
			||||||
 | 
						r.POST("/register", h.Register)
 | 
				
			||||||
 | 
						r.POST("/login-by-phone", h.LoginByPhoneNumber)
 | 
				
			||||||
 | 
						//nolint:gocritic
 | 
				
			||||||
 | 
						//r.PATCH("/:id", h.Update).Name = "admin-updatekindboxreq"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -2,6 +2,10 @@ package httpserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						adminhandler "git.gocasts.ir/ebhomengo/niki/delivery/http_server/admin/admin"
 | 
				
			||||||
 | 
						adminservice "git.gocasts.ir/ebhomengo/niki/service/admin/admin"
 | 
				
			||||||
 | 
						adminauthservice "git.gocasts.ir/ebhomengo/niki/service/auth/admin"
 | 
				
			||||||
 | 
						adminvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/admin"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	config "git.gocasts.ir/ebhomengo/niki/config"
 | 
						config "git.gocasts.ir/ebhomengo/niki/config"
 | 
				
			||||||
	benefactorbasehandler "git.gocasts.ir/ebhomengo/niki/delivery/http_server/benefactor/base"
 | 
						benefactorbasehandler "git.gocasts.ir/ebhomengo/niki/delivery/http_server/benefactor/base"
 | 
				
			||||||
| 
						 | 
					@ -23,6 +27,7 @@ type Server struct {
 | 
				
			||||||
	benefactorHandler           benefactorhandler.Handler
 | 
						benefactorHandler           benefactorhandler.Handler
 | 
				
			||||||
	benefactorKindBoxReqHandler benefactorkindboxreqhandler.Handler
 | 
						benefactorKindBoxReqHandler benefactorkindboxreqhandler.Handler
 | 
				
			||||||
	benefactorBaseHandler       benefactorbasehandler.Handler
 | 
						benefactorBaseHandler       benefactorbasehandler.Handler
 | 
				
			||||||
 | 
						adminHandler                adminhandler.Handler
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func New(
 | 
					func New(
 | 
				
			||||||
| 
						 | 
					@ -33,6 +38,9 @@ func New(
 | 
				
			||||||
	benefactorKindBoxReqSvc benefactorkindboxreqservice.Service,
 | 
						benefactorKindBoxReqSvc benefactorkindboxreqservice.Service,
 | 
				
			||||||
	benefactorKindBoxReqVld benefactorkindboxreqvalidator.Validator,
 | 
						benefactorKindBoxReqVld benefactorkindboxreqvalidator.Validator,
 | 
				
			||||||
	benefactorAddressSvc benefactoraddressservice.Service,
 | 
						benefactorAddressSvc benefactoraddressservice.Service,
 | 
				
			||||||
 | 
						adminSvc adminservice.Service,
 | 
				
			||||||
 | 
						adminVld adminvalidator.Validator,
 | 
				
			||||||
 | 
						adminAuthSvc adminauthservice.Service,
 | 
				
			||||||
) Server {
 | 
					) Server {
 | 
				
			||||||
	return Server{
 | 
						return Server{
 | 
				
			||||||
		Router:                      echo.New(),
 | 
							Router:                      echo.New(),
 | 
				
			||||||
| 
						 | 
					@ -40,6 +48,7 @@ func New(
 | 
				
			||||||
		benefactorHandler:           benefactorhandler.New(cfg.Auth, authSvc, benefactorSvc, benefactorVld, benefactorAddressSvc),
 | 
							benefactorHandler:           benefactorhandler.New(cfg.Auth, authSvc, benefactorSvc, benefactorVld, benefactorAddressSvc),
 | 
				
			||||||
		benefactorKindBoxReqHandler: benefactorkindboxreqhandler.New(cfg.Auth, authSvc, benefactorKindBoxReqSvc, benefactorKindBoxReqVld),
 | 
							benefactorKindBoxReqHandler: benefactorkindboxreqhandler.New(cfg.Auth, authSvc, benefactorKindBoxReqSvc, benefactorKindBoxReqVld),
 | 
				
			||||||
		benefactorBaseHandler:       benefactorbasehandler.New(benefactorAddressSvc),
 | 
							benefactorBaseHandler:       benefactorbasehandler.New(benefactorAddressSvc),
 | 
				
			||||||
 | 
							adminHandler:                adminhandler.New(cfg.AdminAuth, adminAuthSvc, adminSvc, adminVld),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,6 +62,7 @@ func (s Server) Serve() {
 | 
				
			||||||
	s.benefactorHandler.SetRoutes(s.Router)
 | 
						s.benefactorHandler.SetRoutes(s.Router)
 | 
				
			||||||
	s.benefactorKindBoxReqHandler.SetRoutes(s.Router)
 | 
						s.benefactorKindBoxReqHandler.SetRoutes(s.Router)
 | 
				
			||||||
	s.benefactorBaseHandler.SetRoutes(s.Router)
 | 
						s.benefactorBaseHandler.SetRoutes(s.Router)
 | 
				
			||||||
 | 
						s.adminHandler.SetRoutes(s.Router)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Start server
 | 
						// Start server
 | 
				
			||||||
	address := fmt.Sprintf(":%d", s.config.HTTPServer.Port)
 | 
						address := fmt.Sprintf(":%d", s.config.HTTPServer.Port)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,23 @@
 | 
				
			||||||
package entity
 | 
					package entity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import "time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Admin struct {
 | 
					type Admin struct {
 | 
				
			||||||
	ID              uint
 | 
						ID          uint
 | 
				
			||||||
	FirstName       string
 | 
						FirstName   string
 | 
				
			||||||
	LastName        string
 | 
						LastName    string
 | 
				
			||||||
	PhoneNumber     string
 | 
						password    string
 | 
				
			||||||
	Role            AdminRole
 | 
						PhoneNumber string
 | 
				
			||||||
	Address         string
 | 
						Role        AdminRole
 | 
				
			||||||
	Description     string
 | 
						Description string
 | 
				
			||||||
	Email           string
 | 
						Email       string
 | 
				
			||||||
	City            string
 | 
						Gender      Gender
 | 
				
			||||||
	Gender          Gender
 | 
						Status      AdminStatus
 | 
				
			||||||
	Status          AdminStatus
 | 
					}
 | 
				
			||||||
	Birthday        time.Time
 | 
					
 | 
				
			||||||
	StatusChangedAt time.Time
 | 
					func (a *Admin) GetPassword() string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return a.password
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *Admin) SetPassword(password string) {
 | 
				
			||||||
 | 
						a.password = password
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,10 @@ func (s AdminRole) String() string {
 | 
				
			||||||
	return AdminRoleStrings[s]
 | 
						return AdminRoleStrings[s]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s AdminRole) IsValid() bool {
 | 
				
			||||||
 | 
						return s > 0 && int(s) <= len(AdminRoleStrings)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AllAdminRole returns a slice containing all string values of AdminRole.
 | 
					// AllAdminRole returns a slice containing all string values of AdminRole.
 | 
				
			||||||
func AllAdminRole() []string {
 | 
					func AllAdminRole() []string {
 | 
				
			||||||
	roleStrings := make([]string, len(AdminRoleStrings))
 | 
						roleStrings := make([]string, len(AdminRoleStrings))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,6 +16,10 @@ func (s AdminStatus) String() string {
 | 
				
			||||||
	return AdminStatusStrings[s]
 | 
						return AdminStatusStrings[s]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s AdminStatus) IsValid() bool {
 | 
				
			||||||
 | 
						return s > 0 && int(s) <= len(AdminStatusStrings)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// AllAdminStatus returns a slice containing all string values of AdminStatus.
 | 
					// AllAdminStatus returns a slice containing all string values of AdminStatus.
 | 
				
			||||||
func AllAdminStatus() []string {
 | 
					func AllAdminStatus() []string {
 | 
				
			||||||
	statusStrings := make([]string, len(AdminStatusStrings))
 | 
						statusStrings := make([]string, len(AdminStatusStrings))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,6 +26,10 @@ func AllGender() []string {
 | 
				
			||||||
	return statusStrings
 | 
						return statusStrings
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Gender) IsValid() bool {
 | 
				
			||||||
 | 
						return s > 0 && int(s) <= len(GenderStrings)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MapToGender converts a string to the corresponding Gender value.
 | 
					// MapToGender converts a string to the corresponding Gender value.
 | 
				
			||||||
func MapToGender(statusStr string) Gender {
 | 
					func MapToGender(statusStr string) Gender {
 | 
				
			||||||
	for status, str := range GenderStrings {
 | 
						for status, str := range GenderStrings {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								main.go
								
								
								
								
							
							
						
						
									
										17
									
								
								main.go
								
								
								
								
							| 
						 | 
					@ -9,13 +9,17 @@ import (
 | 
				
			||||||
	"git.gocasts.ir/ebhomengo/niki/repository/migrator"
 | 
						"git.gocasts.ir/ebhomengo/niki/repository/migrator"
 | 
				
			||||||
	"git.gocasts.ir/ebhomengo/niki/repository/mysql"
 | 
						"git.gocasts.ir/ebhomengo/niki/repository/mysql"
 | 
				
			||||||
	mysqladdress "git.gocasts.ir/ebhomengo/niki/repository/mysql/address"
 | 
						mysqladdress "git.gocasts.ir/ebhomengo/niki/repository/mysql/address"
 | 
				
			||||||
 | 
						mysqladmin "git.gocasts.ir/ebhomengo/niki/repository/mysql/admin"
 | 
				
			||||||
	mysqlbenefactor "git.gocasts.ir/ebhomengo/niki/repository/mysql/benefactor"
 | 
						mysqlbenefactor "git.gocasts.ir/ebhomengo/niki/repository/mysql/benefactor"
 | 
				
			||||||
	mysqlkindboxreq "git.gocasts.ir/ebhomengo/niki/repository/mysql/kind_box_req"
 | 
						mysqlkindboxreq "git.gocasts.ir/ebhomengo/niki/repository/mysql/kind_box_req"
 | 
				
			||||||
	redisotp "git.gocasts.ir/ebhomengo/niki/repository/redis/redis_otp"
 | 
						redisotp "git.gocasts.ir/ebhomengo/niki/repository/redis/redis_otp"
 | 
				
			||||||
 | 
						adminservice "git.gocasts.ir/ebhomengo/niki/service/admin/admin"
 | 
				
			||||||
 | 
						adminauthservice "git.gocasts.ir/ebhomengo/niki/service/auth/admin"
 | 
				
			||||||
	authservice "git.gocasts.ir/ebhomengo/niki/service/auth/benefactor"
 | 
						authservice "git.gocasts.ir/ebhomengo/niki/service/auth/benefactor"
 | 
				
			||||||
	benefactoraddressservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/address"
 | 
						benefactoraddressservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/address"
 | 
				
			||||||
	benefactorservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/benefactor"
 | 
						benefactorservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/benefactor"
 | 
				
			||||||
	benefactorkindboxreqservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/kind_box_req"
 | 
						benefactorkindboxreqservice "git.gocasts.ir/ebhomengo/niki/service/benefactor/kind_box_req"
 | 
				
			||||||
 | 
						adminvalidator "git.gocasts.ir/ebhomengo/niki/validator/admin/admin"
 | 
				
			||||||
	benefactorvalidator "git.gocasts.ir/ebhomengo/niki/validator/benefactor/benefactor"
 | 
						benefactorvalidator "git.gocasts.ir/ebhomengo/niki/validator/benefactor/benefactor"
 | 
				
			||||||
	benefactorkindboxreqvalidator "git.gocasts.ir/ebhomengo/niki/validator/benefactor/kind_box_req"
 | 
						benefactorkindboxreqvalidator "git.gocasts.ir/ebhomengo/niki/validator/benefactor/kind_box_req"
 | 
				
			||||||
	_ "github.com/go-sql-driver/mysql"
 | 
						_ "github.com/go-sql-driver/mysql"
 | 
				
			||||||
| 
						 | 
					@ -27,8 +31,10 @@ func main() {
 | 
				
			||||||
	mgr := migrator.New(cfg.Mysql)
 | 
						mgr := migrator.New(cfg.Mysql)
 | 
				
			||||||
	mgr.Up()
 | 
						mgr.Up()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authSvc, benefactorSvc, benefactorVld, benefactorKindBoxReqSvc, benefactorKindBoxReqVld, benefactorAddressSvc := setupServices(cfg)
 | 
						authSvc, benefactorSvc, benefactorVld, benefactorKindBoxReqSvc, benefactorKindBoxReqVld, benefactorAddressSvc,
 | 
				
			||||||
	server := httpserver.New(cfg, benefactorSvc, benefactorVld, authSvc, benefactorKindBoxReqSvc, benefactorKindBoxReqVld, benefactorAddressSvc)
 | 
							adminSvc, adminVld, adminAuthSvc := setupServices(cfg)
 | 
				
			||||||
 | 
						server := httpserver.New(cfg, benefactorSvc, benefactorVld, authSvc, benefactorKindBoxReqSvc, benefactorKindBoxReqVld,
 | 
				
			||||||
 | 
							benefactorAddressSvc, adminSvc, adminVld, adminAuthSvc)
 | 
				
			||||||
	server.Serve()
 | 
						server.Serve()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,7 +42,7 @@ func main() {
 | 
				
			||||||
func setupServices(cfg config.Config) (
 | 
					func setupServices(cfg config.Config) (
 | 
				
			||||||
	authSvc authservice.Service, benefactorSvc benefactorservice.Service, benefactorVld benefactorvalidator.Validator,
 | 
						authSvc authservice.Service, benefactorSvc benefactorservice.Service, benefactorVld benefactorvalidator.Validator,
 | 
				
			||||||
	benefactorKindBoxReqSvc benefactorkindboxreqservice.Service, benefactorKindBoxReqVld benefactorkindboxreqvalidator.Validator,
 | 
						benefactorKindBoxReqSvc benefactorkindboxreqservice.Service, benefactorKindBoxReqVld benefactorkindboxreqvalidator.Validator,
 | 
				
			||||||
	benefactorAddressSvc benefactoraddressservice.Service,
 | 
						benefactorAddressSvc benefactoraddressservice.Service, adminSvc adminservice.Service, adminVld adminvalidator.Validator, adminAuthSvc adminauthservice.Service,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
	authSvc = authservice.New(cfg.Auth)
 | 
						authSvc = authservice.New(cfg.Auth)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,5 +64,10 @@ func setupServices(cfg config.Config) (
 | 
				
			||||||
	benefactorKindBoxReqSvc = benefactorkindboxreqservice.New(benefactorKindBoxReqMysql)
 | 
						benefactorKindBoxReqSvc = benefactorkindboxreqservice.New(benefactorKindBoxReqMysql)
 | 
				
			||||||
	benefactorKindBoxReqVld = benefactorkindboxreqvalidator.New(benefactorKindBoxReqMysql, benefactorSvc, benefactorAddressSvc)
 | 
						benefactorKindBoxReqVld = benefactorkindboxreqvalidator.New(benefactorKindBoxReqMysql, benefactorSvc, benefactorAddressSvc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						adminAuthSvc = adminauthservice.New(cfg.AdminAuth)
 | 
				
			||||||
 | 
						adminMysql := mysqladmin.New(MysqlRepo)
 | 
				
			||||||
 | 
						adminVld = adminvalidator.New(adminMysql)
 | 
				
			||||||
 | 
						adminSvc = adminservice.New(adminMysql, adminAuthSvc)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					package adminserviceparam
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "git.gocasts.ir/ebhomengo/niki/entity"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LoginWithPhoneNumberRequest struct {
 | 
				
			||||||
 | 
						PhoneNumber string `json:"phone_number"`
 | 
				
			||||||
 | 
						Password    string `json:"password"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LoginWithPhoneNumberResponse struct {
 | 
				
			||||||
 | 
						Admin  entity.Admin `json:"admin"`
 | 
				
			||||||
 | 
						Tokens Tokens       `json:"tokens"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,19 @@
 | 
				
			||||||
 | 
					package adminserviceparam
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "git.gocasts.ir/ebhomengo/niki/entity"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RegisterRequest struct {
 | 
				
			||||||
 | 
						FirstName   *string             `json:"first_name"`
 | 
				
			||||||
 | 
						LastName    *string             `json:"last_name"`
 | 
				
			||||||
 | 
						Password    *string             `json:"password"`
 | 
				
			||||||
 | 
						PhoneNumber *string             `json:"phone_number"`
 | 
				
			||||||
 | 
						Role        *entity.AdminRole   `json:"role"`
 | 
				
			||||||
 | 
						Description *string             `json:"description"`
 | 
				
			||||||
 | 
						Email       *string             `json:"email"`
 | 
				
			||||||
 | 
						Gender      *entity.Gender      `json:"gender"`
 | 
				
			||||||
 | 
						Status      *entity.AdminStatus `json:"status"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type RegisterResponse struct {
 | 
				
			||||||
 | 
						Admin entity.Admin
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,6 @@
 | 
				
			||||||
 | 
					package adminserviceparam
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Tokens struct {
 | 
				
			||||||
 | 
						AccessToken  string `json:"access_token"`
 | 
				
			||||||
 | 
						RefreshToken string `json:"refresh_token"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,17 @@
 | 
				
			||||||
package errmsg
 | 
					package errmsg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	ErrorMsgNotFound               = "record not found"
 | 
						ErrorMsgNotFound                     = "record not found"
 | 
				
			||||||
	ErrorMsgSomethingWentWrong     = "something went wrong"
 | 
						ErrorMsgSomethingWentWrong           = "something went wrong"
 | 
				
			||||||
	ErrorMsgInvalidInput           = "invalid input"
 | 
						ErrorMsgInvalidInput                 = "invalid input"
 | 
				
			||||||
	ErrorMsgInvalidStatus          = "invalid status"
 | 
						ErrorMsgInvalidStatus                = "invalid status"
 | 
				
			||||||
	ErrorMsgPhoneNumberIsNotUnique = "phone number is not unique"
 | 
						ErrorMsgPhoneNumberIsNotUnique       = "phone number is not unique"
 | 
				
			||||||
	ErrorMsgPhoneNumberIsNotValid  = "phone number is not valid"
 | 
						ErrorMsgEmailIsNotUnique             = "email is not unique"
 | 
				
			||||||
	ErrorMsgUserNotAllowed         = "user not allowed"
 | 
						ErrorMsgPhoneNumberIsNotValid        = "phone number is not valid"
 | 
				
			||||||
	ErrorMsgUserNotFound           = "benefactor not found"
 | 
						ErrorMsgUserNotAllowed               = "user not allowed"
 | 
				
			||||||
	ErrorMsgOtpCodeExist           = "please wait a little bit"
 | 
						ErrorMsgUserNotFound                 = "benefactor not found"
 | 
				
			||||||
	ErrorMsgOtpCodeIsNotValid      = "verification code is not valid"
 | 
						ErrorMsgOtpCodeExist                 = "please wait a little bit"
 | 
				
			||||||
	ErrorMsgCantScanQueryResult    = "can't scan query result"
 | 
						ErrorMsgOtpCodeIsNotValid            = "verification code is not valid"
 | 
				
			||||||
 | 
						ErrorMsgCantScanQueryResult          = "can't scan query result"
 | 
				
			||||||
 | 
						ErrorMsgPhoneNumberOrPassIsIncorrect = "phone number or password is incorrect"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,11 @@
 | 
				
			||||||
 | 
					package mysqladmin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "git.gocasts.ir/ebhomengo/niki/repository/mysql"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DB struct {
 | 
				
			||||||
 | 
						conn *mysql.DB
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(conn *mysql.DB) *DB {
 | 
				
			||||||
 | 
						return &DB{conn: conn}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,28 @@
 | 
				
			||||||
 | 
					package mysqladmin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"git.gocasts.ir/ebhomengo/niki/entity"
 | 
				
			||||||
 | 
						errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
 | 
				
			||||||
 | 
						richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d DB) AddAdmin(ctx context.Context, admin entity.Admin) (entity.Admin, error) {
 | 
				
			||||||
 | 
						const op = "mysqladmin.AddAdmin"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res, err := d.conn.Conn().ExecContext(ctx, `insert into admins(first_name,last_name,password,phone_number,
 | 
				
			||||||
 | 
					                   role,description,email,gender,status) values (?,?,?,?,?,?,?,?,?)`,
 | 
				
			||||||
 | 
							admin.FirstName, admin.LastName, admin.GetPassword(), admin.PhoneNumber, admin.Role.String(), admin.Description, admin.Email,
 | 
				
			||||||
 | 
							admin.Gender.String(), admin.Status.String())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return entity.Admin{}, richerror.New(op).WithErr(err).
 | 
				
			||||||
 | 
								WithMessage(errmsg.ErrorMsgNotFound).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//nolint
 | 
				
			||||||
 | 
						// err is always nil
 | 
				
			||||||
 | 
						id, _ := res.LastInsertId()
 | 
				
			||||||
 | 
						admin.ID = uint(id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return admin, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					package mysqladmin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
 | 
				
			||||||
 | 
						richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d DB) AdminExistByPhoneNumber(ctx context.Context, phoneNumber string) (bool, error) {
 | 
				
			||||||
 | 
						const op = "mysqlbenefactor.IsExistBenefactorByID"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						row := d.conn.Conn().QueryRowContext(ctx, `select * from admins where phone_number = ?`, phoneNumber)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := scanAdmin(row)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							sErr := sql.ErrNoRows
 | 
				
			||||||
 | 
							//TODO-errorsas: second argument to errors.As should not be *error
 | 
				
			||||||
 | 
							//nolint
 | 
				
			||||||
 | 
							if errors.As(err, &sErr) {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO - log unexpected error for better observability
 | 
				
			||||||
 | 
							return false, richerror.New(op).WithErr(err).
 | 
				
			||||||
 | 
								WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d DB) AdminExistByEmail(ctx context.Context, email string) (bool, error) {
 | 
				
			||||||
 | 
						const op = "mysqlbenefactor.IsExistBenefactorByID"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						row := d.conn.Conn().QueryRowContext(ctx, `select * from admins where email = ?`, email)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := scanAdmin(row)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							sErr := sql.ErrNoRows
 | 
				
			||||||
 | 
							//TODO-errorsas: second argument to errors.As should not be *error
 | 
				
			||||||
 | 
							//nolint
 | 
				
			||||||
 | 
							if errors.As(err, &sErr) {
 | 
				
			||||||
 | 
								return false, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO - log unexpected error for better observability
 | 
				
			||||||
 | 
							return false, richerror.New(op).WithErr(err).
 | 
				
			||||||
 | 
								WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,84 @@
 | 
				
			||||||
 | 
					package mysqladmin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"database/sql"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"git.gocasts.ir/ebhomengo/niki/entity"
 | 
				
			||||||
 | 
						errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
 | 
				
			||||||
 | 
						richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
 | 
				
			||||||
 | 
						"git.gocasts.ir/ebhomengo/niki/repository/mysql"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d DB) GetAdminByPhoneNumber(ctx context.Context, phoneNumber string) (entity.Admin, error) {
 | 
				
			||||||
 | 
						const op = "mysqlbenefactor.IsExistBenefactorByID"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						row := d.conn.Conn().QueryRowContext(ctx, `select * from admins where phone_number = ?`, phoneNumber)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						admin, err := scanAdmin(row)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							sErr := sql.ErrNoRows
 | 
				
			||||||
 | 
							//TODO-errorsas: second argument to errors.As should not be *error
 | 
				
			||||||
 | 
							//nolint
 | 
				
			||||||
 | 
							if errors.As(err, &sErr) {
 | 
				
			||||||
 | 
								return entity.Admin{}, richerror.New(op).WithErr(sErr).
 | 
				
			||||||
 | 
									WithMessage(errmsg.ErrorMsgNotFound).WithKind(richerror.KindNotFound)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO - log unexpected error for better observability
 | 
				
			||||||
 | 
							return entity.Admin{}, richerror.New(op).WithErr(err).
 | 
				
			||||||
 | 
								WithMessage(errmsg.ErrorMsgCantScanQueryResult).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return admin, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func scanAdmin(scanner mysql.Scanner) (entity.Admin, error) {
 | 
				
			||||||
 | 
						var createdAt time.Time
 | 
				
			||||||
 | 
						var admin entity.Admin
 | 
				
			||||||
 | 
						var roleStr, statusStr, password string
 | 
				
			||||||
 | 
						// TODO - use db model and mapper between entity and db model OR use this approach
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var adminNullableFields nullableFields
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := scanner.Scan(&admin.ID, &adminNullableFields.firstName,
 | 
				
			||||||
 | 
							&adminNullableFields.lastName, &password, &admin.PhoneNumber,
 | 
				
			||||||
 | 
							&roleStr, &adminNullableFields.description,
 | 
				
			||||||
 | 
							&adminNullableFields.email, &adminNullableFields.genderStr,
 | 
				
			||||||
 | 
							&statusStr, &createdAt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						admin.Role = entity.MapToAdminRole(roleStr)
 | 
				
			||||||
 | 
						admin.Status = entity.MapToAdminStatus(statusStr)
 | 
				
			||||||
 | 
						admin.SetPassword(password)
 | 
				
			||||||
 | 
						mapNotNullToAdmin(adminNullableFields, &admin)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return admin, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type nullableFields struct {
 | 
				
			||||||
 | 
						firstName   sql.NullString
 | 
				
			||||||
 | 
						lastName    sql.NullString
 | 
				
			||||||
 | 
						description sql.NullString
 | 
				
			||||||
 | 
						email       sql.NullString
 | 
				
			||||||
 | 
						genderStr   sql.NullString
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// TODO - find the other solution.
 | 
				
			||||||
 | 
					func mapNotNullToAdmin(data nullableFields, admin *entity.Admin) {
 | 
				
			||||||
 | 
						if data.firstName.Valid {
 | 
				
			||||||
 | 
							admin.FirstName = data.firstName.String
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if data.lastName.Valid {
 | 
				
			||||||
 | 
							admin.LastName = data.lastName.String
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if data.description.Valid {
 | 
				
			||||||
 | 
							admin.Description = data.description.String
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if data.email.Valid {
 | 
				
			||||||
 | 
							admin.Email = data.email.String
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if data.genderStr.Valid {
 | 
				
			||||||
 | 
							admin.Gender = entity.MapToGender(data.genderStr.String)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,18 @@
 | 
				
			||||||
 | 
					-- +migrate Up
 | 
				
			||||||
 | 
					CREATE TABLE `admins`
 | 
				
			||||||
 | 
					(
 | 
				
			||||||
 | 
					    `id`            INT PRIMARY KEY AUTO_INCREMENT,
 | 
				
			||||||
 | 
					    `first_name`    VARCHAR(191),
 | 
				
			||||||
 | 
					    `last_name`     VARCHAR(191),
 | 
				
			||||||
 | 
					    `password`      TEXT NOT NULL,
 | 
				
			||||||
 | 
					    `phone_number`  VARCHAR(191) NOT NULL UNIQUE,
 | 
				
			||||||
 | 
					    `role`          ENUM('super-admin','admin') NOT NULL,
 | 
				
			||||||
 | 
					    `description`   TEXT,
 | 
				
			||||||
 | 
					    `email`         VARCHAR(191) NOT NULL UNIQUE,
 | 
				
			||||||
 | 
					    `gender`        VARCHAR(191),
 | 
				
			||||||
 | 
					    `status`        VARCHAR(191),
 | 
				
			||||||
 | 
					    `created_at`    TIMESTAMP DEFAULT CURRENT_TIMESTAMP
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- +migrate Down
 | 
				
			||||||
 | 
					DROP TABLE `admins`;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,9 @@
 | 
				
			||||||
 | 
					-- +migrate Up
 | 
				
			||||||
 | 
					-- what can we do for password?
 | 
				
			||||||
 | 
					INSERT INTO `admins` (`id`, `phone_number`, `email`,`password`,`role`,`status`)
 | 
				
			||||||
 | 
					VALUES (1, '09122702856', 'keshvari@gmail.com','Abc123456','super-admin','active');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- +migrate Down
 | 
				
			||||||
 | 
					DELETE
 | 
				
			||||||
 | 
					FROM `admins`
 | 
				
			||||||
 | 
					WHERE id '1' ;
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,40 @@
 | 
				
			||||||
 | 
					package adminservice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
 | 
				
			||||||
 | 
						errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
 | 
				
			||||||
 | 
						richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Service) LoginWithPhoneNumber(ctx context.Context, req adminserviceparam.LoginWithPhoneNumberRequest) (adminserviceparam.LoginWithPhoneNumberResponse, error) {
 | 
				
			||||||
 | 
						const op = richerror.Op("adminservice.LoginWithPhoneNumber")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						admin, err := s.repo.GetAdminByPhoneNumber(ctx, req.PhoneNumber)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return adminserviceparam.LoginWithPhoneNumberResponse{}, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if cErr := CompareHash(admin.GetPassword(), req.Password); cErr != nil {
 | 
				
			||||||
 | 
							return adminserviceparam.LoginWithPhoneNumberResponse{}, richerror.New(op).WithErr(cErr).WithMessage(errmsg.ErrorMsgPhoneNumberOrPassIsIncorrect).WithKind(richerror.KindForbidden)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						accessToken, aErr := s.auth.CreateAccessToken(admin)
 | 
				
			||||||
 | 
						if aErr != nil {
 | 
				
			||||||
 | 
							return adminserviceparam.LoginWithPhoneNumberResponse{}, richerror.New(op).WithErr(aErr).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						refreshToken, rErr := s.auth.CreateRefreshToken(admin)
 | 
				
			||||||
 | 
						if rErr != nil {
 | 
				
			||||||
 | 
							return adminserviceparam.LoginWithPhoneNumberResponse{}, richerror.New(op).WithErr(rErr).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return adminserviceparam.LoginWithPhoneNumberResponse{
 | 
				
			||||||
 | 
							Admin: admin,
 | 
				
			||||||
 | 
							Tokens: adminserviceparam.Tokens{
 | 
				
			||||||
 | 
								AccessToken:  accessToken,
 | 
				
			||||||
 | 
								RefreshToken: refreshToken,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					package adminservice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"git.gocasts.ir/ebhomengo/niki/entity"
 | 
				
			||||||
 | 
						adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
 | 
				
			||||||
 | 
						richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Service) Register(ctx context.Context, req adminserviceparam.RegisterRequest) (adminserviceparam.RegisterResponse, error) {
 | 
				
			||||||
 | 
						const op = richerror.Op("adminservice.Register")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var newAdmin entity.Admin
 | 
				
			||||||
 | 
						if req.FirstName != nil {
 | 
				
			||||||
 | 
							newAdmin.FirstName = *req.FirstName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.LastName != nil {
 | 
				
			||||||
 | 
							newAdmin.LastName = *req.LastName
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.PhoneNumber != nil {
 | 
				
			||||||
 | 
							newAdmin.PhoneNumber = *req.PhoneNumber
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Role != nil {
 | 
				
			||||||
 | 
							newAdmin.Role = *req.Role
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Description != nil {
 | 
				
			||||||
 | 
							newAdmin.LastName = *req.Description
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Email != nil {
 | 
				
			||||||
 | 
							newAdmin.Email = *req.Email
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Gender != nil {
 | 
				
			||||||
 | 
							newAdmin.Gender = *req.Gender
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Description != nil {
 | 
				
			||||||
 | 
							newAdmin.LastName = *req.Description
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if req.Email != nil {
 | 
				
			||||||
 | 
							newAdmin.Status = *req.Status
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if bErr := GenerateHash(req.Password); bErr != nil {
 | 
				
			||||||
 | 
							return adminserviceparam.RegisterResponse{}, richerror.New(op).WithErr(bErr).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						newAdmin.SetPassword(*req.Password)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						admin, err := s.repo.AddAdmin(ctx, newAdmin)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return adminserviceparam.RegisterResponse{}, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return adminserviceparam.RegisterResponse{Admin: admin}, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					package adminservice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"git.gocasts.ir/ebhomengo/niki/config"
 | 
				
			||||||
 | 
						"git.gocasts.ir/ebhomengo/niki/entity"
 | 
				
			||||||
 | 
						"golang.org/x/crypto/bcrypt"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type AuthGenerator interface {
 | 
				
			||||||
 | 
						CreateAccessToken(benefactor entity.Admin) (string, error)
 | 
				
			||||||
 | 
						CreateRefreshToken(benefactor entity.Admin) (string, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Repository interface {
 | 
				
			||||||
 | 
						AddAdmin(ctx context.Context, admin entity.Admin) (entity.Admin, error)
 | 
				
			||||||
 | 
						GetAdminByPhoneNumber(ctx context.Context, phoneNumber string) (entity.Admin, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Service struct {
 | 
				
			||||||
 | 
						repo Repository
 | 
				
			||||||
 | 
						auth AuthGenerator
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(repo Repository, auth AuthGenerator) Service {
 | 
				
			||||||
 | 
						return Service{
 | 
				
			||||||
 | 
							repo: repo,
 | 
				
			||||||
 | 
							auth: auth,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GenerateHash(password *string) error {
 | 
				
			||||||
 | 
						hashedPassword, bErr := bcrypt.GenerateFromPassword([]byte(*password), config.BcryptCost)
 | 
				
			||||||
 | 
						if bErr != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf("bcrypt error: %w", bErr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						*password = string(hashedPassword)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					func CompareHash(hashedPassword, password string) error {
 | 
				
			||||||
 | 
						return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,16 @@
 | 
				
			||||||
 | 
					package adminauthservice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"git.gocasts.ir/ebhomengo/niki/entity"
 | 
				
			||||||
 | 
						"github.com/golang-jwt/jwt/v4"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Claims struct {
 | 
				
			||||||
 | 
						jwt.RegisteredClaims
 | 
				
			||||||
 | 
						UserID uint             `json:"user_id"`
 | 
				
			||||||
 | 
						Role   entity.AdminRole `json:"role"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c Claims) Valid() error {
 | 
				
			||||||
 | 
						return c.RegisteredClaims.Valid()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1 +0,0 @@
 | 
				
			||||||
package admin
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,9 @@
 | 
				
			||||||
package admin
 | 
					package adminauthservice
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"git.gocasts.ir/ebhomengo/niki/entity"
 | 
				
			||||||
 | 
						"github.com/golang-jwt/jwt/v4"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,3 +24,54 @@ func New(cfg Config) Service {
 | 
				
			||||||
		config: cfg,
 | 
							config: cfg,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Service) CreateAccessToken(admin entity.Admin) (string, error) {
 | 
				
			||||||
 | 
						return s.createToken(admin.ID, admin.Role, s.config.AccessSubject, s.config.AccessExpirationTime)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Service) CreateRefreshToken(admin entity.Admin) (string, error) {
 | 
				
			||||||
 | 
						return s.createToken(admin.ID, admin.Role, s.config.RefreshSubject, s.config.RefreshExpirationTime)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Service) ParseToken(bearerToken string) (*Claims, error) {
 | 
				
			||||||
 | 
						// https://pkg.go.dev/github.com/golang-jwt/jwt/v5#example-ParseWithClaims-CustomClaimsType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tokenStr := strings.Replace(bearerToken, "Bearer ", "", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(token *jwt.Token) (interface{}, error) {
 | 
				
			||||||
 | 
							return []byte(s.config.SignKey), nil
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if claims, ok := token.Claims.(*Claims); ok && token.Valid {
 | 
				
			||||||
 | 
							return claims, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil, err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Service) createToken(userID uint, role entity.AdminRole, subject string, expireDuration time.Duration) (string, error) {
 | 
				
			||||||
 | 
						// create a signer for rsa 256
 | 
				
			||||||
 | 
						// TODO - replace with rsa 256 RS256 - https://github.com/golang-jwt/jwt/blob/main/http_example_test.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// set our claims
 | 
				
			||||||
 | 
						claims := Claims{
 | 
				
			||||||
 | 
							RegisteredClaims: jwt.RegisteredClaims{
 | 
				
			||||||
 | 
								Subject:   subject,
 | 
				
			||||||
 | 
								ExpiresAt: jwt.NewNumericDate(time.Now().Add(expireDuration)),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							UserID: userID,
 | 
				
			||||||
 | 
							Role:   role,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO - add sign method to config
 | 
				
			||||||
 | 
						accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
 | 
				
			||||||
 | 
						tokenString, err := accessToken.SignedString([]byte(s.config.SignKey))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return tokenString, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,7 @@ func (s Service) LoginOrRegister(ctx context.Context, req benefactoreparam.Login
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	_, dErr := s.redisOtp.DeleteCodeByPhoneNumber(ctx, req.PhoneNumber)
 | 
						_, dErr := s.redisOtp.DeleteCodeByPhoneNumber(ctx, req.PhoneNumber)
 | 
				
			||||||
	if dErr != nil {
 | 
						if dErr != nil {
 | 
				
			||||||
		return benefactoreparam.LoginOrRegisterResponse{}, richerror.New(op).WithErr(gErr).WithKind(richerror.KindUnexpected)
 | 
							return benefactoreparam.LoginOrRegisterResponse{}, richerror.New(op).WithErr(dErr).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	isExist, benefactor, rErr := s.repo.IsExistBenefactorByPhoneNumber(ctx, req.PhoneNumber)
 | 
						isExist, benefactor, rErr := s.repo.IsExistBenefactorByPhoneNumber(ctx, req.PhoneNumber)
 | 
				
			||||||
| 
						 | 
					@ -36,19 +36,19 @@ func (s Service) LoginOrRegister(ctx context.Context, req benefactoreparam.Login
 | 
				
			||||||
			Role:        entity.UserBenefactorRole,
 | 
								Role:        entity.UserBenefactorRole,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return benefactoreparam.LoginOrRegisterResponse{}, richerror.New(op).WithErr(rErr).WithKind(richerror.KindUnexpected)
 | 
								return benefactoreparam.LoginOrRegisterResponse{}, richerror.New(op).WithErr(err).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		benefactor = newBenefactor
 | 
							benefactor = newBenefactor
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	accessToken, err := s.auth.CreateAccessToken(benefactor)
 | 
						accessToken, aErr := s.auth.CreateAccessToken(benefactor)
 | 
				
			||||||
	if err != nil {
 | 
						if aErr != nil {
 | 
				
			||||||
		return benefactoreparam.LoginOrRegisterResponse{}, richerror.New(op).WithErr(rErr).WithKind(richerror.KindUnexpected)
 | 
							return benefactoreparam.LoginOrRegisterResponse{}, richerror.New(op).WithErr(aErr).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	refreshToken, err := s.auth.CreateRefreshToken(benefactor)
 | 
						refreshToken, rErr := s.auth.CreateRefreshToken(benefactor)
 | 
				
			||||||
	if err != nil {
 | 
						if rErr != nil {
 | 
				
			||||||
		return benefactoreparam.LoginOrRegisterResponse{}, richerror.New(op).WithErr(rErr).WithKind(richerror.KindUnexpected)
 | 
							return benefactoreparam.LoginOrRegisterResponse{}, richerror.New(op).WithErr(rErr).WithKind(richerror.KindUnexpected)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,42 @@
 | 
				
			||||||
 | 
					package adminvalidator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
 | 
				
			||||||
 | 
						errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
 | 
				
			||||||
 | 
						richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
 | 
				
			||||||
 | 
						validation "github.com/go-ozzo/ozzo-validation/v4"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Validator) ValidateLoginWithPhoneNumberRequest(req adminserviceparam.LoginWithPhoneNumberRequest) (map[string]string, error) {
 | 
				
			||||||
 | 
						const op = "adminvalidator.ValidateRegisterRequest"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err := validation.ValidateStruct(&req,
 | 
				
			||||||
 | 
							//TODO - add regex
 | 
				
			||||||
 | 
							validation.Field(&req.Password, validation.Required, validation.NotNil,
 | 
				
			||||||
 | 
								validation.Length(8, 0)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							validation.Field(&req.PhoneNumber,
 | 
				
			||||||
 | 
								validation.Required,
 | 
				
			||||||
 | 
								validation.Match(regexp.MustCompile(phoneNumberRegex)).Error(errmsg.ErrorMsgPhoneNumberIsNotValid),
 | 
				
			||||||
 | 
								validation.By(v.doesAdminExistByPhoneNumber))); err != nil {
 | 
				
			||||||
 | 
							fieldErrors := make(map[string]string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vErr := validation.Errors{}
 | 
				
			||||||
 | 
							if errors.As(err, &vErr) {
 | 
				
			||||||
 | 
								for key, value := range vErr {
 | 
				
			||||||
 | 
									if value != nil {
 | 
				
			||||||
 | 
										fieldErrors[key] = value.Error()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return fieldErrors, richerror.New(op).WithMessage(errmsg.ErrorMsgInvalidInput).
 | 
				
			||||||
 | 
								WithKind(richerror.KindInvalid).
 | 
				
			||||||
 | 
								WithMeta(map[string]interface{}{"req": req}).WithErr(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//nolint
 | 
				
			||||||
 | 
						return nil, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					package adminvalidator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						adminserviceparam "git.gocasts.ir/ebhomengo/niki/param/admin/admin"
 | 
				
			||||||
 | 
						errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
 | 
				
			||||||
 | 
						richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
 | 
				
			||||||
 | 
						"github.com/go-ozzo/ozzo-validation/is"
 | 
				
			||||||
 | 
						validation "github.com/go-ozzo/ozzo-validation/v4"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Validator) ValidateRegisterRequest(req adminserviceparam.RegisterRequest) (map[string]string, error) {
 | 
				
			||||||
 | 
						const op = "adminvalidator.ValidateRegisterRequest"
 | 
				
			||||||
 | 
						if err := validation.ValidateStruct(&req,
 | 
				
			||||||
 | 
							// TODO - add length of code config from benefactor config
 | 
				
			||||||
 | 
							validation.Field(&req.FirstName,
 | 
				
			||||||
 | 
								validation.Length(3, 40)),
 | 
				
			||||||
 | 
							validation.Field(&req.LastName,
 | 
				
			||||||
 | 
								validation.Length(3, 40)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							//TODO - add regex
 | 
				
			||||||
 | 
							validation.Field(&req.Password, validation.Required, validation.NotNil,
 | 
				
			||||||
 | 
								validation.Length(8, 0)),
 | 
				
			||||||
 | 
							validation.Field(&req.Gender, validation.By(v.IsGenderValid)),
 | 
				
			||||||
 | 
							validation.Field(&req.Role, validation.By(v.IsRoleValid), validation.Required),
 | 
				
			||||||
 | 
							validation.Field(&req.Status, validation.By(v.IsStatusValid), validation.Required),
 | 
				
			||||||
 | 
							validation.Field(&req.Email, validation.Required, is.Email,
 | 
				
			||||||
 | 
								validation.By(v.doesAdminExistByEmail)),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							validation.Field(&req.PhoneNumber,
 | 
				
			||||||
 | 
								validation.Required,
 | 
				
			||||||
 | 
								validation.Match(regexp.MustCompile(phoneNumberRegex)).Error(errmsg.ErrorMsgPhoneNumberIsNotValid),
 | 
				
			||||||
 | 
								validation.By(v.IsPhoneNumberUnique))); err != nil {
 | 
				
			||||||
 | 
							fieldErrors := make(map[string]string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							vErr := validation.Errors{}
 | 
				
			||||||
 | 
							if errors.As(err, &vErr) {
 | 
				
			||||||
 | 
								for key, value := range vErr {
 | 
				
			||||||
 | 
									if value != nil {
 | 
				
			||||||
 | 
										fieldErrors[key] = value.Error()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return fieldErrors, richerror.New(op).WithMessage(errmsg.ErrorMsgInvalidInput).
 | 
				
			||||||
 | 
								WithKind(richerror.KindInvalid).
 | 
				
			||||||
 | 
								WithMeta(map[string]interface{}{"req": req}).WithErr(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//nolint
 | 
				
			||||||
 | 
						return nil, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,111 @@
 | 
				
			||||||
 | 
					package adminvalidator
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"git.gocasts.ir/ebhomengo/niki/entity"
 | 
				
			||||||
 | 
						errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						phoneNumberRegex = "^09\\d{9}$"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Repository interface {
 | 
				
			||||||
 | 
						AdminExistByPhoneNumber(ctx context.Context, phoneNumber string) (bool, error)
 | 
				
			||||||
 | 
						AdminExistByEmail(ctx context.Context, email string) (bool, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					type Validator struct {
 | 
				
			||||||
 | 
						repo Repository
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func New(repo Repository) Validator {
 | 
				
			||||||
 | 
						return Validator{repo: repo}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Validator) doesAdminExistByPhoneNumber(value interface{}) error {
 | 
				
			||||||
 | 
						phoneNumber, ok := value.(string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						adminExisted, err := v.repo.AdminExistByPhoneNumber(context.Background(), phoneNumber)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !adminExisted {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgPhoneNumberOrPassIsIncorrect)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Validator) IsPhoneNumberUnique(value interface{}) error {
 | 
				
			||||||
 | 
						phoneNumber, ok := value.(*string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						adminExisted, err := v.repo.AdminExistByPhoneNumber(context.Background(), *phoneNumber)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if adminExisted {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgPhoneNumberIsNotUnique)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Validator) doesAdminExistByEmail(value interface{}) error {
 | 
				
			||||||
 | 
						email, ok := value.(*string)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						adminExisted, err := v.repo.AdminExistByEmail(context.Background(), *email)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if adminExisted {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgPhoneNumberIsNotUnique)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Validator) IsRoleValid(value interface{}) error {
 | 
				
			||||||
 | 
						role, ok := value.(*entity.AdminRole)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if isValid := role.IsValid(); isValid != true {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgInvalidInput)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Validator) IsGenderValid(value interface{}) error {
 | 
				
			||||||
 | 
						gender, ok := value.(*entity.Gender)
 | 
				
			||||||
 | 
						if gender == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if isValid := gender.IsValid(); isValid != true {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgInvalidInput)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (v Validator) IsStatusValid(value interface{}) error {
 | 
				
			||||||
 | 
						status, ok := value.(*entity.AdminStatus)
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgSomethingWentWrong)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if isValid := status.IsValid(); isValid != true {
 | 
				
			||||||
 | 
							return fmt.Errorf(errmsg.ErrorMsgInvalidInput)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue