104 lines
3.3 KiB
Go
104 lines
3.3 KiB
Go
package recovery
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/hibiken/asynq"
|
|
"github.com/perfect-panel/server/internal/config"
|
|
commonLogic "github.com/perfect-panel/server/internal/logic/common"
|
|
"github.com/perfect-panel/server/internal/svc"
|
|
"github.com/perfect-panel/server/internal/types"
|
|
"github.com/perfect-panel/server/pkg/constant"
|
|
"github.com/perfect-panel/server/pkg/limit"
|
|
"github.com/perfect-panel/server/pkg/logger"
|
|
"github.com/perfect-panel/server/pkg/random"
|
|
"github.com/perfect-panel/server/pkg/xerr"
|
|
queue "github.com/perfect-panel/server/queue/types"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type SendCodeLogic struct {
|
|
logger.Logger
|
|
ctx context.Context
|
|
svcCtx *svc.ServiceContext
|
|
}
|
|
|
|
func NewSendCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SendCodeLogic {
|
|
return &SendCodeLogic{
|
|
Logger: logger.WithContext(ctx),
|
|
ctx: ctx,
|
|
svcCtx: svcCtx,
|
|
}
|
|
}
|
|
|
|
func (l *SendCodeLogic) SendCode(req *types.RecoverySendCodeRequest) (*types.SendCodeResponse, error) {
|
|
email := strings.ToLower(strings.TrimSpace(req.Email))
|
|
if email == "" {
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.InvalidParams), "email is required")
|
|
}
|
|
|
|
scene := constant.Security.String()
|
|
cacheKey := fmt.Sprintf("%s:%s:%s", config.AuthCodeCacheKey, scene, email)
|
|
limiter := limit.NewPeriodLimit(60, 1, l.svcCtx.Redis, fmt.Sprintf("%s:%s:%s", config.SendIntervalKeyPrefix, "email", scene))
|
|
permit, err := limiter.Take(email)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Failed to take limit")
|
|
}
|
|
if !limiter.ParsePermitState(permit) {
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.TooManyRequests), "send email too many requests")
|
|
}
|
|
permit, err = l.svcCtx.AuthLimiter.Take(fmt.Sprintf("%s:%s:%s", "email", scene, email))
|
|
if err != nil {
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.ERROR), "Failed to take limit")
|
|
}
|
|
if !l.svcCtx.AuthLimiter.ParsePermitState(permit) {
|
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.TodaySendCountExceedsLimit), "send email too many requests")
|
|
}
|
|
|
|
code := random.Key(6, 0)
|
|
expireTime := l.svcCtx.Config.VerifyCode.VerifyCodeExpireTime
|
|
if expireTime == 0 {
|
|
expireTime = 900
|
|
}
|
|
val, _ := json.Marshal(commonLogic.CacheKeyPayload{
|
|
Code: code,
|
|
LastAt: time.Now().Unix(),
|
|
})
|
|
if err = l.svcCtx.Redis.Set(l.ctx, cacheKey, string(val), time.Second*time.Duration(expireTime)).Err(); err != nil {
|
|
return nil, errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to set verification code")
|
|
}
|
|
|
|
expireMinutes := expireTime / 60
|
|
taskPayload := queue.SendEmailPayload{
|
|
Type: queue.EmailTypeVerify,
|
|
Scene: scene,
|
|
Email: email,
|
|
Subject: "Verification code",
|
|
Content: map[string]interface{}{
|
|
"Type": uint8(constant.Security),
|
|
"SiteLogo": l.svcCtx.Config.Site.SiteLogo,
|
|
"SiteName": l.svcCtx.Config.Site.SiteName,
|
|
"Expire": expireMinutes,
|
|
"Code": code,
|
|
},
|
|
}
|
|
payload, err := json.Marshal(taskPayload)
|
|
if err != nil {
|
|
return nil, errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to marshal task payload")
|
|
}
|
|
task := asynq.NewTask(queue.ForthwithSendEmail, payload, asynq.MaxRetry(3))
|
|
if _, err = l.svcCtx.Queue.Enqueue(task); err != nil {
|
|
return nil, errors.Wrap(xerr.NewErrCode(xerr.ERROR), "Failed to enqueue task")
|
|
}
|
|
|
|
resp := &types.SendCodeResponse{Status: true}
|
|
if l.svcCtx.Config.Model == constant.DevMode {
|
|
resp.Code = code
|
|
}
|
|
return resp, nil
|
|
}
|