some ref after review
This commit is contained in:
parent
881add8f6d
commit
e5c3b3786d
2
go.mod
2
go.mod
@ -8,6 +8,7 @@ require (
|
||||
github.com/gofiber/fiber/v2 v2.51.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/pioz/faker v1.7.3
|
||||
github.com/rs/xid v1.5.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
go.mongodb.org/mongo-driver v1.13.1
|
||||
go.uber.org/zap v1.26.0
|
||||
@ -27,7 +28,6 @@ require (
|
||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
||||
|
||||
@ -17,10 +17,13 @@ import (
|
||||
func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
||||
logger.Info("Запуск приложения", zap.String("AppName", cfg.AppName))
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
mdb, err := initialize.MongoDB(ctx, cfg)
|
||||
if err != nil {
|
||||
logger.Error("Failed to initialize MongoDB", zap.Error(err))
|
||||
return err
|
||||
cancel()
|
||||
}
|
||||
|
||||
rdb, err := initialize.Redis(ctx, cfg)
|
||||
|
||||
@ -2,8 +2,11 @@ package recovery
|
||||
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"codeword/internal/repository"
|
||||
"codeword/internal/services"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
@ -24,34 +27,53 @@ func NewRecoveryController(logger *zap.Logger, service *services.RecoveryService
|
||||
}
|
||||
|
||||
func (r *RecoveryController) HandlePingDB(c *fiber.Ctx) error {
|
||||
return r.service.Ping(c.Context())
|
||||
startTime := time.Now()
|
||||
if err := r.service.Ping(c.Context()); err != nil {
|
||||
r.logger.Error("Failed to ping the database", zap.Error(err))
|
||||
return c.Status(fiber.StatusServiceUnavailable).SendString("DB ping failed")
|
||||
}
|
||||
duration := time.Since(startTime)
|
||||
|
||||
durationMillis := duration.Milliseconds()
|
||||
responseMessage := fmt.Sprintf("DB ping success - Time taken: %d ms", durationMillis)
|
||||
|
||||
return c.Status(fiber.StatusOK).SendString(responseMessage)
|
||||
}
|
||||
|
||||
// HandleRecoveryRequest обрабатывает запрос на восстановление пароля
|
||||
// todo update docs
|
||||
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
|
||||
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"})
|
||||
}
|
||||
|
||||
user, err := r.service.FindUserByEmail(c.Context(), email)
|
||||
if err != nil || user == nil {
|
||||
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 {
|
||||
r.logger.Error("Failed to find user by email", zap.Error(err))
|
||||
|
||||
if errors.Is(err, repository.ErrPromoUserNotFound) {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||
}
|
||||
|
||||
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)
|
||||
signUrl := req.RedirectionURL + base64.URLEncoding.EncodeToString(key)
|
||||
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})
|
||||
@ -62,7 +84,7 @@ func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
||||
|
||||
signWithID := sign + id // подпись с id записи
|
||||
|
||||
err = r.service.RecoveryEmailTask(c.Context(), models.RecEmailDeps{UserID: user.ID.Hex(), Email: email, SignWithID: signWithID, ID: id})
|
||||
err = r.service.RecoveryEmailTask(c.Context(), models.RecEmailDeps{UserID: user.ID.Hex(), Email: req.Email, SignWithID: signWithID, ID: 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"})
|
||||
@ -73,20 +95,21 @@ func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
||||
})
|
||||
}
|
||||
|
||||
// todo тут скорее всего помимо подписи будет передаваться еще что-то, например email пользователя от фронта для поиска в бд
|
||||
|
||||
// HandleRecoveryLink обрабатывает ссылку восстановления и обменивает ее на токены
|
||||
func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
|
||||
key := c.Params("sign")
|
||||
var req models.RecoveryLinkRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
r.logger.Error("Failed to parse recovery link request", zap.Error(err))
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Bad Request"})
|
||||
}
|
||||
|
||||
record, err := r.service.GetRecoveryRecord(c.Context(), key)
|
||||
record, err := r.service.GetRecoveryRecord(c.Context(), req.Sign)
|
||||
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))
|
||||
r.logger.Error("Recovery link expired", zap.String("signature", req.Sign))
|
||||
return c.Status(fiber.StatusNotAcceptable).JSON(fiber.Map{"error": "Recovery link expired"})
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,9 @@ func MongoDB(ctx context.Context, cfg Config) (*mongo.Database, error) {
|
||||
MongoAuth: cfg.MongoAuth,
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
mongoDeps := &mdb.ConnectDeps{
|
||||
Configuration: dbConfig,
|
||||
Timeout: 10 * time.Second,
|
||||
|
||||
@ -35,3 +35,12 @@ type RecoveryRecord struct {
|
||||
Email string
|
||||
Key string
|
||||
}
|
||||
|
||||
type RecoveryRequest struct {
|
||||
Email string `json:"email"`
|
||||
RedirectionURL string `json:"redirectionURL"`
|
||||
}
|
||||
|
||||
type RecoveryLinkRequest struct {
|
||||
Sign string `json:"sign"`
|
||||
}
|
||||
|
||||
@ -81,6 +81,8 @@ func (r *CodewordRepository) GetRecoveryRecord(ctx context.Context, key string)
|
||||
|
||||
// пингует в монгу чтобы проверить подключение
|
||||
func (r *CodewordRepository) Ping(ctx context.Context) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
if err := r.mdb.Database().Client().Ping(ctx, readpref.Primary()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package repository
|
||||
import (
|
||||
"codeword/internal/models"
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
@ -17,6 +18,8 @@ type UserRepository struct {
|
||||
mdb *mongo.Collection
|
||||
}
|
||||
|
||||
var ErrPromoUserNotFound = errors.New("user not found")
|
||||
|
||||
func NewUserRepository(deps Deps) *UserRepository {
|
||||
|
||||
return &UserRepository{mdb: deps.Mdb}
|
||||
@ -31,7 +34,7 @@ func (r *UserRepository) FindByEmail(ctx context.Context, email string) (*models
|
||||
if err == mongo.ErrNoDocuments {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
return nil, ErrPromoUserNotFound
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
@ -4,10 +4,8 @@ import (
|
||||
"codeword/internal/controller/promocode"
|
||||
"codeword/internal/controller/recovery"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/zap"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
@ -52,7 +50,7 @@ func (s *Server) Shutdown(ctx context.Context) error {
|
||||
|
||||
func (s *Server) registerRoutes() {
|
||||
s.app.Get("/liveness", s.handleLiveness)
|
||||
s.app.Get("/readiness", s.handleReadiness)
|
||||
s.app.Get("/readiness", s.RecoveryController.HandlePingDB)
|
||||
|
||||
s.app.Post("/recover", s.RecoveryController.HandleRecoveryRequest)
|
||||
s.app.Get("/recover/:sign", s.RecoveryController.HandleRecoveryLink)
|
||||
@ -69,17 +67,3 @@ func (s *Server) registerRoutes() {
|
||||
func (s *Server) handleLiveness(c *fiber.Ctx) error {
|
||||
return c.SendStatus(fiber.StatusOK)
|
||||
}
|
||||
|
||||
func (s *Server) handleReadiness(c *fiber.Ctx) error {
|
||||
startTime := time.Now()
|
||||
if err := s.RecoveryController.HandlePingDB(c); err != nil {
|
||||
s.Logger.Error("Failed to ping the database", zap.Error(err))
|
||||
return c.Status(fiber.StatusServiceUnavailable).SendString("DB ping failed")
|
||||
}
|
||||
duration := time.Since(startTime)
|
||||
|
||||
durationMillis := duration.Milliseconds()
|
||||
responseMessage := fmt.Sprintf("DB ping success - Time taken: %d ms", durationMillis)
|
||||
|
||||
return c.Status(fiber.StatusOK).SendString(responseMessage)
|
||||
}
|
||||
|
||||
@ -73,10 +73,6 @@ func (s *RecoveryService) FindUserByEmail(ctx context.Context, email string) (*m
|
||||
s.logger.Error("Failed to find user by email", zap.String("email", email), zap.Error(err))
|
||||
return nil, err
|
||||
}
|
||||
if user == nil {
|
||||
s.logger.Info("No user found with email", zap.String("email", email))
|
||||
return nil, nil
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user