forked from ebhomengo/niki
136 lines
4.2 KiB
Go
136 lines
4.2 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"math/rand"
|
|
"time"
|
|
|
|
smscontract "git.gocasts.ir/ebhomengo/niki/contract/sms"
|
|
"git.gocasts.ir/ebhomengo/niki/driverapp/service/entity"
|
|
errmsg "git.gocasts.ir/ebhomengo/niki/pkg/err_msg"
|
|
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
|
|
)
|
|
|
|
type Config struct {
|
|
LengthOfOtpCode int `koanf:"length_of_otp_code"`
|
|
OtpChars string `koanf:"otp_chars"`
|
|
OtpExpireTime time.Duration `koanf:"otp_expire_time"`
|
|
}
|
|
|
|
type RepositoryOtp interface {
|
|
IsExistPhoneNumber(ctx context.Context, phoneNumber string) (bool, error)
|
|
SaveCodeWithPhoneNumber(ctx context.Context, phoneNumber string, code string, expireTime time.Duration) error
|
|
GetCodeByPhoneNumber(ctx context.Context, phoneNumber string) (string, error)
|
|
DeleteCodeByPhoneNumber(ctx context.Context, PhoneNumber string) (bool, error)
|
|
}
|
|
|
|
type Repository interface {
|
|
IsExistDriverByPhoneNumber(ctx context.Context, phoneNumber string) (bool, entity.Driver, error)
|
|
CreateDriver(ctx context.Context, driver entity.Driver) (entity.Driver, error)
|
|
}
|
|
|
|
type Service struct {
|
|
config Config
|
|
repositoryOtp RepositoryOtp
|
|
repository Repository
|
|
smsContract smscontract.SmsAdapter
|
|
validator Validator
|
|
}
|
|
|
|
func NewService(cfg Config,
|
|
repositoryOtp RepositoryOtp,
|
|
repository Repository,
|
|
smsContract smscontract.SmsAdapter,
|
|
validator Validator) Service {
|
|
return Service{
|
|
config: cfg,
|
|
repositoryOtp: repositoryOtp,
|
|
repository: repository,
|
|
smsContract: smsContract,
|
|
validator: validator,
|
|
}
|
|
}
|
|
|
|
func (s Service) SendOtp(ctx context.Context, req SendOtpRequest) (SendOtpResponse, error) {
|
|
const op = "driverService.SendOtp"
|
|
err := s.validator.ValidateSendOtpRequest(req)
|
|
if err != nil {
|
|
return SendOtpResponse{}, richerror.New(op).WithErr(err).WithMessage(err.Error())
|
|
}
|
|
isExist, iErr := s.repositoryOtp.IsExistPhoneNumber(ctx, req.PhoneNumber)
|
|
if iErr != nil {
|
|
return SendOtpResponse{}, richerror.New(op).WithErr(iErr).WithKind(richerror.KindUnexpected)
|
|
}
|
|
|
|
if isExist {
|
|
return SendOtpResponse{}, richerror.New(op).WithMessage(errmsg.ErrorMsgOtpCodeExist).WithKind(richerror.KindForbidden)
|
|
}
|
|
|
|
newCode := s.generateVerificationCode()
|
|
sErr := s.repositoryOtp.SaveCodeWithPhoneNumber(ctx, req.PhoneNumber, newCode, s.config.OtpExpireTime)
|
|
if sErr != nil {
|
|
return SendOtpResponse{}, richerror.New(op).WithErr(sErr).WithKind(richerror.KindUnexpected)
|
|
}
|
|
|
|
go s.smsContract.Send(req.PhoneNumber, newCode)
|
|
|
|
return SendOtpResponse{}, nil
|
|
}
|
|
|
|
func (s Service) LoginOrRegister(ctx context.Context, req LoginOrRegisterRequest) (LoginOrRegisterResponse, error) {
|
|
const op = "driverService.LoginOrRegister"
|
|
|
|
err := s.validator.ValidateLoginOrRegisterRequest(req)
|
|
if err != nil {
|
|
return LoginOrRegisterResponse{}, richerror.New(op).WithErr(err).WithMessage(err.Error())
|
|
}
|
|
|
|
code, gErr := s.repositoryOtp.GetCodeByPhoneNumber(ctx, req.PhoneNumber)
|
|
if gErr != nil {
|
|
return LoginOrRegisterResponse{}, richerror.New(op).WithErr(gErr).WithKind(richerror.KindUnexpected)
|
|
}
|
|
|
|
if code == "" || code != req.VerifyCode {
|
|
return LoginOrRegisterResponse{}, richerror.New(op).WithMessage(errmsg.ErrorMsgOtpCodeIsNotValid).WithKind(richerror.KindForbidden)
|
|
}
|
|
|
|
_, dErr := s.repositoryOtp.DeleteCodeByPhoneNumber(ctx, req.PhoneNumber)
|
|
if dErr != nil {
|
|
return LoginOrRegisterResponse{}, richerror.New(op).WithErr(dErr).WithKind(richerror.KindUnexpected)
|
|
}
|
|
|
|
isExist, driver, eErr := s.repository.IsExistDriverByPhoneNumber(ctx, req.PhoneNumber)
|
|
if eErr != nil {
|
|
return LoginOrRegisterResponse{}, richerror.New(op).WithErr(eErr).WithKind(richerror.KindUnexpected)
|
|
}
|
|
|
|
if !isExist {
|
|
newDriver, cErr := s.repository.CreateDriver(ctx, entity.Driver{
|
|
PhoneNumber: req.PhoneNumber,
|
|
})
|
|
if cErr != nil {
|
|
return LoginOrRegisterResponse{}, richerror.New(op).WithErr(cErr).WithKind(richerror.KindUnexpected)
|
|
}
|
|
|
|
driver = newDriver
|
|
}
|
|
|
|
// TODO : CreateAccessToken and create CreateRefreshToken
|
|
|
|
return LoginOrRegisterResponse{
|
|
Data: Data{
|
|
ID: driver.ID,
|
|
PhoneNumber: driver.PhoneNumber,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func (s Service) generateVerificationCode() string {
|
|
result := make([]byte, s.config.LengthOfOtpCode)
|
|
for i := 0; i < s.config.LengthOfOtpCode; i++ {
|
|
result[i] = s.config.OtpChars[rand.Intn(len(s.config.OtpChars))]
|
|
}
|
|
|
|
return string(result)
|
|
}
|