99 lines
3.5 KiB
Go
99 lines
3.5 KiB
Go
package controller
|
||
|
||
import (
|
||
"codeword/internal/services"
|
||
"encoding/base64"
|
||
"github.com/gofiber/fiber/v2"
|
||
"go.uber.org/zap"
|
||
"time"
|
||
)
|
||
|
||
type RecoveryController struct {
|
||
logger *zap.Logger
|
||
service *services.RecoveryService
|
||
defaultURL string
|
||
}
|
||
|
||
func NewRecoveryController(logger *zap.Logger, service *services.RecoveryService, defaultRedirectionURL string) *RecoveryController {
|
||
return &RecoveryController{
|
||
logger: logger,
|
||
service: service,
|
||
defaultURL: defaultRedirectionURL,
|
||
}
|
||
}
|
||
|
||
func (r *RecoveryController) HandlePingDB(c *fiber.Ctx) error {
|
||
return r.service.Ping(c.Context())
|
||
}
|
||
|
||
// TODO add deps struct, counnt params >3
|
||
// HandleRecoveryRequest обрабатывает запрос на восстановление пароля
|
||
func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
||
email := c.FormValue("email")
|
||
referralURL := c.Get("Referrer")
|
||
redirectionURL := c.FormValue("RedirectionURL")
|
||
|
||
if redirectionURL == "" && referralURL != "" {
|
||
redirectionURL = referralURL
|
||
} else if redirectionURL == "" {
|
||
redirectionURL = r.defaultURL
|
||
}
|
||
|
||
user, err := r.service.FindUserByEmail(c.Context(), email)
|
||
if err != nil || user == nil {
|
||
r.logger.Error("Failed to find user by email", zap.Error(err))
|
||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||
}
|
||
|
||
key, err := r.service.GenerateKey()
|
||
if err != nil {
|
||
r.logger.Error("Failed to generate key", zap.Error(err))
|
||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||
}
|
||
|
||
signUrl := redirectionURL + base64.URLEncoding.EncodeToString(key)
|
||
sign := base64.URLEncoding.EncodeToString(key)
|
||
|
||
id, err := r.service.StoreRecoveryRecord(c.Context(), user.ID.Hex(), user.Email, sign, signUrl)
|
||
if err != nil {
|
||
r.logger.Error("Failed to store recovery record", zap.Error(err))
|
||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||
}
|
||
|
||
err = r.service.RecoveryEmailTask(c.Context(), user.ID.Hex(), email, key, id)
|
||
if err != nil {
|
||
r.logger.Error("Failed to send recovery email", zap.Error(err))
|
||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||
}
|
||
|
||
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
||
"id": id,
|
||
})
|
||
}
|
||
|
||
// todo тут скорее всего помимо подписи будет передаваться еще что-то, например email пользователя от фронта для поиска в бд
|
||
|
||
// HandleRecoveryLink обрабатывает ссылку восстановления и обменивает ее на токены
|
||
func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
|
||
key := c.Params("sign")
|
||
|
||
record, err := r.service.GetRecoveryRecord(c.Context(), key)
|
||
if err != nil {
|
||
r.logger.Error("Failed to get recovery record", zap.Error(err))
|
||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||
}
|
||
|
||
if time.Since(record.CreatedAt) > 15*time.Minute {
|
||
r.logger.Error("Recovery link expired", zap.String("signature", key))
|
||
return c.Status(fiber.StatusNotAcceptable).JSON(fiber.Map{"error": "Recovery link expired"})
|
||
}
|
||
|
||
tokens, err := r.service.ExchangeForTokens(record.UserID, record.Sign)
|
||
if err != nil {
|
||
r.logger.Error("Failed to exchange recovery link for tokens", zap.Error(err))
|
||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||
}
|
||
|
||
return c.Status(fiber.StatusOK).JSON(tokens)
|
||
}
|