From 36cda3fd5a12b8b6eb0f78e256adf5da57b3ee95 Mon Sep 17 00:00:00 2001 From: AmirMahdi Qiasvand Date: Mon, 15 Sep 2025 13:03:48 +0330 Subject: [PATCH] feat: add otp redis repo --- config/config.go | 5 +++ internal/domain/otp.go | 29 +++++++++++++++ internal/repository/cache/otp_repo.go | 53 +++++++++++++++++++++++++++ internal/usecase/auth_service.go | 4 ++ pkg/util/otp.go | 14 +++++++ sample.config.yaml | 5 ++- 6 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 internal/domain/otp.go create mode 100644 internal/repository/cache/otp_repo.go create mode 100644 pkg/util/otp.go diff --git a/config/config.go b/config/config.go index 98da692..0373932 100644 --- a/config/config.go +++ b/config/config.go @@ -6,6 +6,7 @@ type Config struct { DB DB `mapstructure:"db"` Redis Redis `mapstructure:"redis"` KYCProvider KYC `mapstructure:"kyc"` + OTP OTP `mapstructure:"otp"` } type Server struct { Host string `mapstructure:"host"` @@ -36,3 +37,7 @@ type KYC struct { APIKey string `mapstructure:"api_key"` URL string `mapstructure:"url"` } + +type OTP struct { + CodeExpMinutes uint `mapstructure:"code_exp_minutes"` +} diff --git a/internal/domain/otp.go b/internal/domain/otp.go new file mode 100644 index 0000000..ef1d66e --- /dev/null +++ b/internal/domain/otp.go @@ -0,0 +1,29 @@ +package domain + +import ( + "backend/pkg/util" + "context" + "time" + + "github.com/google/uuid" +) + +type OTPRepo interface { + Create(ctx context.Context, phone string, otp *OTP) error + Delete(ctx context.Context, phone string) error +} + +type OTP struct { + ID uuid.UUID + Code string + Phone string + ExpiredAt time.Time +} + +func NewOTP(phone string) *OTP { + return &OTP{ + ID: uuid.New(), + Code: util.GenOTPCode(), + Phone: phone, + } +} diff --git a/internal/repository/cache/otp_repo.go b/internal/repository/cache/otp_repo.go new file mode 100644 index 0000000..c6089c7 --- /dev/null +++ b/internal/repository/cache/otp_repo.go @@ -0,0 +1,53 @@ +package cache + +import ( + "backend/internal/domain" + "backend/pkg/cache" + "context" + "encoding/json" + "fmt" + "time" +) + +type OTPRepository struct { + redis *cache.Redis +} + +func NewOTPRepository(redis *cache.Redis) domain.OTPRepo { + return &OTPRepository{ + redis: redis, + } +} + +func (r *OTPRepository) Create(ctx context.Context, phone string, otp *domain.OTP) error { + key := r.getOTPKey(phone) + + otpData, err := json.Marshal(otp) + if err != nil { + return fmt.Errorf("failed to marshal otp: %w", err) + } + + ttl := time.Until(otp.ExpiredAt) + if ttl <= 0 { + return fmt.Errorf("OTP expired already") + } + + if err := r.redis.Client().Set(ctx, key, otpData, ttl).Err(); err != nil { + return fmt.Errorf("failed to store otp in redis: %w", err) + } + return nil +} + +func (r *OTPRepository) Delete(ctx context.Context, phone string) error { + key := r.getOTPKey(phone) + + if err := r.redis.Client().Del(ctx, key).Err(); err != nil { + return fmt.Errorf("failed to del otp from redis") + } + + return nil +} + +func (r *OTPRepository) getOTPKey(phone string) string { + return fmt.Sprintf("challenge:%s", phone) +} diff --git a/internal/usecase/auth_service.go b/internal/usecase/auth_service.go index 8280a07..75f271a 100644 --- a/internal/usecase/auth_service.go +++ b/internal/usecase/auth_service.go @@ -189,3 +189,7 @@ func (s *authService) VerifyKYC(ctx context.Context, userID, nationalID, birthDa return fmt.Errorf("KYC verification failed - shahkar status: %d, identity status: %d", shahkarResp.StatusCode, identityResp.StatusCode) } + +func (*authService) SendOTPVerifaction(ctx context.Context, phone string) (string, error) { + return "", nil +} diff --git a/pkg/util/otp.go b/pkg/util/otp.go new file mode 100644 index 0000000..6afa2dc --- /dev/null +++ b/pkg/util/otp.go @@ -0,0 +1,14 @@ +package util + +import ( + "math/rand" + "strconv" + "time" +) + +func GenOTPCode() string { + newRand := rand.New(rand.NewSource(time.Now().UnixNano())) + minNum := 100000 + maxNum := 999999 + return strconv.Itoa(newRand.Intn(maxNum-minNum+1) + minNum) +} diff --git a/sample.config.yaml b/sample.config.yaml index a07dc1f..c706700 100644 --- a/sample.config.yaml +++ b/sample.config.yaml @@ -20,4 +20,7 @@ jwt: token_secret: "Secret" RPC: - url: "exmaple.com" \ No newline at end of file + url: "exmaple.com" + +otp: + code_exp_minutes: 2 \ No newline at end of file