codeword/internal/controller/client/client_recovery/recovery_controller.go
skeris cc37aef684
All checks were successful
Deploy / CreateImage (push) Successful in 2m51s
Deploy / DeployService (push) Successful in 20s
remove recover from recovery url
2025-03-24 01:46:41 +03:00

152 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package client_recovery
import (
"encoding/base64"
"errors"
"gitea.pena/PenaSide/codeword/internal/models"
"gitea.pena/PenaSide/codeword/internal/repository"
"gitea.pena/PenaSide/codeword/internal/services"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"gitea.pena/PenaSide/common/log_mw"
"time"
)
type Deps struct {
Logger *zap.Logger
Service *services.RecoveryService
DefaultURL string
RecoveryURL string
}
type RecoveryController struct {
logger *zap.Logger
service *services.RecoveryService
defaultURL string
recoveryURL string
}
func NewRecoveryController(deps Deps) *RecoveryController {
return &RecoveryController{
logger: deps.Logger,
service: deps.Service,
defaultURL: deps.DefaultURL,
recoveryURL: deps.RecoveryURL,
}
}
func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
hlogger := log_mw.ExtractLogger(c)
var req models.RecoveryRequest
if err := c.BodyParser(&req); err != nil {
r.logger.Error("Failed to parse recovery request", zap.Error(err))
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Bad Request"})
}
if req.Email == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "email is required"})
}
referralURL := c.Get("Referrer")
if req.RedirectionURL == "" && referralURL != "" {
req.RedirectionURL = referralURL
} else if req.RedirectionURL == "" {
req.RedirectionURL = r.defaultURL
}
user, err := r.service.FindUserByEmail(c.Context(), req.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 := req.RedirectionURL
sign := base64.URLEncoding.EncodeToString(key)
id, err := r.service.StoreRecoveryRecord(c.Context(), models.StoreRecDeps{
UserID: user.ID.Hex(),
Email: user.Email,
Key: sign,
Url: 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"})
}
signWithID := sign + id // подпись с id записи
err = r.service.RecoveryEmailTask(c.Context(), models.RecEmailDeps{UserID: user.ID.Hex(), Email: req.Email, SignWithID: signUrl + "/"+signWithID, ID: id})
if err != nil {
r.logger.Error("Failed to send recovery email", zap.Error(err))
if errors.Is(err, repository.ErrAlreadyReported) {
return c.Status(fiber.StatusAlreadyReported).JSON(fiber.Map{"error": "already reported"})
}
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
hlogger.Emit(models.InfoPasswordRestorationRequested{
CtxID: id,
CtxUserID: user.ID.Hex(),
CtxReturnURL: r.recoveryURL + signWithID,
CtxEmail: req.Email,
})
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Recovery email sent successfully"})
}
func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
hlogger := log_mw.ExtractLogger(c)
sign := c.Params("sign")
record, err := r.service.GetRecoveryRecord(c.Context(), sign)
if err != nil {
r.logger.Error("Recovery link expired", zap.String("signature", sign))
return c.Redirect("https://hub.pena.digital/recover/expired")
}
if time.Since(record.CreatedAt) > 15*time.Minute {
r.logger.Error("Recovery link expired", zap.String("signature", sign))
return c.Redirect(record.SignUrl + "/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"})
}
c.Cookie(&fiber.Cookie{
Name: "refreshToken",
Value: tokens["refreshToken"],
Domain: ".pena.digital",
Expires: time.Now().Add(30 * 24 * time.Hour),
Secure: true,
HTTPOnly: true,
})
c.Cookie(&fiber.Cookie{
Name: "refreshToken",
Value: tokens["refreshToken"],
Domain: ".pena.digital",
Expires: time.Now().Add(30 * 24 * time.Hour),
Secure: true,
HTTPOnly: true,
})
hlogger.Emit(models.InfoPasswordRestored{
CtxID: record.ID.String(),
CtxUserID: record.UserID,
})
return c.Redirect(record.SignUrl + "?auth=" + tokens["accessToken"])
}