some ref after review

This commit is contained in:
Pavel 2024-01-15 11:43:55 +03:00
parent 881add8f6d
commit e5c3b3786d
9 changed files with 68 additions and 45 deletions

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,25 +27,44 @@ 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))
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
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()
@ -51,7 +73,7 @@ func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
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
}