package verificationimp import ( "encoding/json" "net/http" "github.com/tech/sendico/pkg/api/http/response" "github.com/tech/sendico/pkg/db/verification" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mutil/mask" "github.com/tech/sendico/pkg/mutil/mzap" emodel "github.com/tech/sendico/server/interface/model" mutil "github.com/tech/sendico/server/internal/mutil/verification" "go.uber.org/zap" ) func (a *VerificationAPI) requestCode(r *http.Request, account *model.Account, token *emodel.AccountToken) http.HandlerFunc { var req verificationCodeRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { a.logger.Warn("Failed to decode confirmation resend request", zap.Error(err)) return response.BadPayload(a.logger, a.Name(), err) } purpose, err := model.VPFromString(req.Purpose) if err != nil { return response.BadRequest(a.logger, a.Name(), "invalid_target", err.Error()) } if purpose == model.PurposeLogin && (token == nil || !token.Pending) { return response.Forbidden(a.logger, a.Name(), "pending_token_required", "login confirmation requires pending token") } target := a.resolveTarget(req.Target, account) if target == "" { return response.BadRequest(a.logger, a.Name(), "missing_destination", "email destination is required") } vReq := verification.NewOTPRequest(account.ID, purpose, target). WithTTL(a.config.TTL). WithCooldown(a.config.Cooldown). WithMaxRetries(a.config.ResendLimit). WithIdempotencyKey(req.IdempotencyKey) otp, err := a.store.Create(r.Context(), vReq) if err != nil { a.logger.Warn("Failed to create confirmation code for resend", zap.Error(err), mzap.AccRef(account.ID)) return mutil.MapTokenErrorToResponse(a.logger, a.Name(), err) } a.sendCode(account, purpose, target, otp) return response.Accepted(a.logger, verificationResponse{ TTLSeconds: int(vReq.Ttl.Seconds()), CooldownSeconds: int(a.config.Cooldown.Seconds()), Target: mask.Email(target), IdempotencyKey: req.IdempotencyKey, }) }