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/gofiber/fiber/v2 v2.51.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
github.com/pioz/faker v1.7.3
|
github.com/pioz/faker v1.7.3
|
||||||
|
github.com/rs/xid v1.5.0
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.1
|
||||||
go.mongodb.org/mongo-driver v1.13.1
|
go.mongodb.org/mongo-driver v1.13.1
|
||||||
go.uber.org/zap v1.26.0
|
go.uber.org/zap v1.26.0
|
||||||
@ -27,7 +28,6 @@ require (
|
|||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/rivo/uniseg v0.2.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/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.50.0 // indirect
|
github.com/valyala/fasthttp v1.50.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.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 {
|
func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger) error {
|
||||||
logger.Info("Запуск приложения", zap.String("AppName", cfg.AppName))
|
logger.Info("Запуск приложения", zap.String("AppName", cfg.AppName))
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
mdb, err := initialize.MongoDB(ctx, cfg)
|
mdb, err := initialize.MongoDB(ctx, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to initialize MongoDB", zap.Error(err))
|
logger.Error("Failed to initialize MongoDB", zap.Error(err))
|
||||||
return err
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
rdb, err := initialize.Redis(ctx, cfg)
|
rdb, err := initialize.Redis(ctx, cfg)
|
||||||
|
|||||||
@ -2,8 +2,11 @@ package recovery
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"codeword/internal/models"
|
"codeword/internal/models"
|
||||||
|
"codeword/internal/repository"
|
||||||
"codeword/internal/services"
|
"codeword/internal/services"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"time"
|
"time"
|
||||||
@ -24,25 +27,44 @@ func NewRecoveryController(logger *zap.Logger, service *services.RecoveryService
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RecoveryController) HandlePingDB(c *fiber.Ctx) error {
|
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 {
|
func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
|
||||||
email := c.FormValue("email")
|
var req models.RecoveryRequest
|
||||||
referralURL := c.Get("Referrer")
|
if err := c.BodyParser(&req); err != nil {
|
||||||
redirectionURL := c.FormValue("RedirectionURL")
|
r.logger.Error("Failed to parse recovery request", zap.Error(err))
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Bad Request"})
|
||||||
if redirectionURL == "" && referralURL != "" {
|
|
||||||
redirectionURL = referralURL
|
|
||||||
} else if redirectionURL == "" {
|
|
||||||
redirectionURL = r.defaultURL
|
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := r.service.FindUserByEmail(c.Context(), email)
|
referralURL := c.Get("Referrer")
|
||||||
if err != nil || user == nil {
|
|
||||||
|
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))
|
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()
|
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"})
|
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)
|
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})
|
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 записи
|
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 {
|
if err != nil {
|
||||||
r.logger.Error("Failed to send recovery email", zap.Error(err))
|
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.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 {
|
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 {
|
if err != nil {
|
||||||
r.logger.Error("Failed to get recovery record", zap.Error(err))
|
r.logger.Error("Failed to get recovery record", zap.Error(err))
|
||||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Since(record.CreatedAt) > 15*time.Minute {
|
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"})
|
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,
|
MongoAuth: cfg.MongoAuth,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
mongoDeps := &mdb.ConnectDeps{
|
mongoDeps := &mdb.ConnectDeps{
|
||||||
Configuration: dbConfig,
|
Configuration: dbConfig,
|
||||||
Timeout: 10 * time.Second,
|
Timeout: 10 * time.Second,
|
||||||
|
|||||||
@ -35,3 +35,12 @@ type RecoveryRecord struct {
|
|||||||
Email string
|
Email string
|
||||||
Key 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 {
|
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 {
|
if err := r.mdb.Database().Client().Ping(ctx, readpref.Primary()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ package repository
|
|||||||
import (
|
import (
|
||||||
"codeword/internal/models"
|
"codeword/internal/models"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"github.com/go-redis/redis/v8"
|
"github.com/go-redis/redis/v8"
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
@ -17,6 +18,8 @@ type UserRepository struct {
|
|||||||
mdb *mongo.Collection
|
mdb *mongo.Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ErrPromoUserNotFound = errors.New("user not found")
|
||||||
|
|
||||||
func NewUserRepository(deps Deps) *UserRepository {
|
func NewUserRepository(deps Deps) *UserRepository {
|
||||||
|
|
||||||
return &UserRepository{mdb: deps.Mdb}
|
return &UserRepository{mdb: deps.Mdb}
|
||||||
@ -31,7 +34,7 @@ func (r *UserRepository) FindByEmail(ctx context.Context, email string) (*models
|
|||||||
if err == mongo.ErrNoDocuments {
|
if err == mongo.ErrNoDocuments {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, ErrPromoUserNotFound
|
||||||
}
|
}
|
||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,8 @@ import (
|
|||||||
"codeword/internal/controller/promocode"
|
"codeword/internal/controller/promocode"
|
||||||
"codeword/internal/controller/recovery"
|
"codeword/internal/controller/recovery"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
@ -52,7 +50,7 @@ func (s *Server) Shutdown(ctx context.Context) error {
|
|||||||
|
|
||||||
func (s *Server) registerRoutes() {
|
func (s *Server) registerRoutes() {
|
||||||
s.app.Get("/liveness", s.handleLiveness)
|
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.Post("/recover", s.RecoveryController.HandleRecoveryRequest)
|
||||||
s.app.Get("/recover/:sign", s.RecoveryController.HandleRecoveryLink)
|
s.app.Get("/recover/:sign", s.RecoveryController.HandleRecoveryLink)
|
||||||
@ -69,17 +67,3 @@ func (s *Server) registerRoutes() {
|
|||||||
func (s *Server) handleLiveness(c *fiber.Ctx) error {
|
func (s *Server) handleLiveness(c *fiber.Ctx) error {
|
||||||
return c.SendStatus(fiber.StatusOK)
|
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))
|
s.logger.Error("Failed to find user by email", zap.String("email", email), zap.Error(err))
|
||||||
return nil, 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
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user