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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,7 +16,8 @@ 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"`
|
||||||
|
AdminAuth adminauthservice.Config `koanf:"admin_auth"`
|
||||||
Redis redis.Config `koanf:"redis"`
|
Redis redis.Config `koanf:"redis"`
|
||||||
KavenegarSmsProvider smsprovider.Config `koanf:"kavenegar_sms_provider"`
|
KavenegarSmsProvider smsprovider.Config `koanf:"kavenegar_sms_provider"`
|
||||||
BenefactorSvc benefactorservice.Config `koanf:"benefactor_service"`
|
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
|
||||||
|
password string
|
||||||
PhoneNumber string
|
PhoneNumber string
|
||||||
Role AdminRole
|
Role AdminRole
|
||||||
Address string
|
|
||||||
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"`
|
||||||
|
}
|
|
@ -6,10 +6,12 @@ const (
|
||||||
ErrorMsgInvalidInput = "invalid input"
|
ErrorMsgInvalidInput = "invalid input"
|
||||||
ErrorMsgInvalidStatus = "invalid status"
|
ErrorMsgInvalidStatus = "invalid status"
|
||||||
ErrorMsgPhoneNumberIsNotUnique = "phone number is not unique"
|
ErrorMsgPhoneNumberIsNotUnique = "phone number is not unique"
|
||||||
|
ErrorMsgEmailIsNotUnique = "email is not unique"
|
||||||
ErrorMsgPhoneNumberIsNotValid = "phone number is not valid"
|
ErrorMsgPhoneNumberIsNotValid = "phone number is not valid"
|
||||||
ErrorMsgUserNotAllowed = "user not allowed"
|
ErrorMsgUserNotAllowed = "user not allowed"
|
||||||
ErrorMsgUserNotFound = "benefactor not found"
|
ErrorMsgUserNotFound = "benefactor not found"
|
||||||
ErrorMsgOtpCodeExist = "please wait a little bit"
|
ErrorMsgOtpCodeExist = "please wait a little bit"
|
||||||
ErrorMsgOtpCodeIsNotValid = "verification code is not valid"
|
ErrorMsgOtpCodeIsNotValid = "verification code is not valid"
|
||||||
ErrorMsgCantScanQueryResult = "can't scan query result"
|
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