add logging events

This commit is contained in:
Pavel 2024-05-28 14:05:31 +03:00
parent 9ee582a12e
commit 201b8b4207
9 changed files with 250 additions and 63 deletions

@ -10,10 +10,10 @@ import (
"codeword/internal/server/grpc"
httpserver "codeword/internal/server/http"
"codeword/internal/services"
"codeword/internal/utils/middleware"
"codeword/internal/worker/purge_worker"
"codeword/internal/worker/recovery_worker"
"codeword/pkg/closer"
"codeword/utils"
"context"
"errors"
"github.com/themakers/hlog"
@ -126,11 +126,15 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger, build B
DiscountClient: discountRpcClient,
})
jwtUtil := utils.NewJWT(&cfg)
authMiddleware := utils.NewAuthenticator(jwtUtil)
jwtUtil := middleware.NewJWT(&cfg)
recoveryController := recovery.NewRecoveryController(logger, recoveryService, cfg.DefaultRedirectionURL)
promoCodeController := promocode.NewPromoCodeController(promocode.Deps{Logger: logger, PromoCodeService: promoService, AuthMiddleware: authMiddleware})
recoveryController := recovery.NewRecoveryController(recovery.Deps{
Logger: logger,
Service: recoveryService,
DefaultURL: cfg.DefaultRedirectionURL,
RecoveryURL: cfg.RecoveryUrl,
})
promoCodeController := promocode.NewPromoCodeController(promocode.Deps{Logger: logger, PromoCodeService: promoService})
controllerRpc := rpc_controllers.InitRpcControllers(promoService)
grpcServer, err := grpc.NewGRPC(logger)
@ -159,6 +163,7 @@ func Run(ctx context.Context, cfg initialize.Config, logger *zap.Logger, build B
Logger: logger,
Controllers: []httpserver.Controller{recoveryController, promoCodeController},
Hlogger: loggerHlog,
JWT: jwtUtil,
})
go func() {

@ -4,33 +4,34 @@ import (
"codeword/internal/models"
"codeword/internal/repository"
"codeword/internal/services"
"codeword/internal/utils/middleware"
"errors"
"fmt"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"strings"
)
type Deps struct {
Logger *zap.Logger
PromoCodeService *services.PromoCodeService
AuthMiddleware func(*fiber.Ctx) error
}
type PromoCodeController struct {
logger *zap.Logger
promoCodeService *services.PromoCodeService
authMiddleware func(*fiber.Ctx) error
}
func NewPromoCodeController(deps Deps) *PromoCodeController {
return &PromoCodeController{
logger: deps.Logger,
promoCodeService: deps.PromoCodeService,
authMiddleware: deps.AuthMiddleware,
}
}
func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
userID := middleware.ExtractUserID(c)
hlogger := middleware.ExtractLogger(c)
var req models.PromoCode
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
@ -53,10 +54,52 @@ func (p *PromoCodeController) CreatePromoCode(c *fiber.Ctx) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
var keyType string
var ctxFactor float64
var keyTargetType string
var ctxTarget string
var ctxAmount int64
if createdPromoCode.Bonus.Privilege.PrivilegeID != "" && createdPromoCode.Bonus.Discount.Layer > 0 {
keyType = "privilege,discount"
keyTargetType = "privilege,service"
ctxTarget = fmt.Sprintf("%s,%s", createdPromoCode.Bonus.Privilege.PrivilegeID, createdPromoCode.Bonus.Discount.Target)
ctxAmount = int64(createdPromoCode.Bonus.Privilege.Amount)
ctxFactor = createdPromoCode.Bonus.Discount.Factor
} else if createdPromoCode.Bonus.Privilege.PrivilegeID != "" {
keyType = "privilege"
keyTargetType = "privilege"
ctxTarget = createdPromoCode.Bonus.Privilege.PrivilegeID
ctxAmount = int64(createdPromoCode.Bonus.Privilege.Amount)
} else if createdPromoCode.Bonus.Discount.Factor > 0 {
keyType = "discount"
keyTargetType = "service"
ctxFactor = createdPromoCode.Bonus.Discount.Factor
ctxTarget = createdPromoCode.Bonus.Discount.Target
}
hlogger.Emit(models.InfoPromocodeCreated{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
CtxID: createdPromoCode.ID.String(),
CtxUserID: userID,
KeyType: keyType,
CtxFactor: ctxFactor,
KeyTargetType: keyTargetType,
CtxTarget: ctxTarget,
CtxAmount: ctxAmount,
CtxCode: createdPromoCode.Codeword,
})
return c.Status(fiber.StatusOK).JSON(createdPromoCode)
}
func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
userID := middleware.ExtractUserID(c)
hlogger := middleware.ExtractLogger(c)
var req models.ReqEditPromoCode
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request payload"})
@ -79,6 +122,45 @@ func (p *PromoCodeController) EditPromoCode(c *fiber.Ctx) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
var keyType string
var ctxFactor float64
var keyTargetType string
var ctxTarget string
var ctxAmount int64
if editedPromoCode.Bonus.Privilege.PrivilegeID != "" && editedPromoCode.Bonus.Discount.Layer > 0 {
keyType = "privilege,discount"
keyTargetType = "privilege,service"
ctxTarget = fmt.Sprintf("%s,%s", editedPromoCode.Bonus.Privilege.PrivilegeID, editedPromoCode.Bonus.Discount.Target)
ctxAmount = int64(editedPromoCode.Bonus.Privilege.Amount)
ctxFactor = editedPromoCode.Bonus.Discount.Factor
} else if editedPromoCode.Bonus.Privilege.PrivilegeID != "" {
keyType = "privilege"
keyTargetType = "privilege"
ctxTarget = editedPromoCode.Bonus.Privilege.PrivilegeID
ctxAmount = int64(editedPromoCode.Bonus.Privilege.Amount)
} else if editedPromoCode.Bonus.Discount.Factor > 0 {
keyType = "discount"
keyTargetType = "service"
ctxFactor = editedPromoCode.Bonus.Discount.Factor
ctxTarget = editedPromoCode.Bonus.Discount.Target
}
hlogger.Emit(models.InfoPromocodeUpdated{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
CtxID: editedPromoCode.ID.String(),
CtxUserID: userID,
KeyType: keyType,
CtxFactor: ctxFactor,
KeyTargetType: keyTargetType,
CtxTarget: ctxTarget,
CtxAmount: ctxAmount,
CtxCode: editedPromoCode.Codeword,
})
return c.Status(fiber.StatusOK).JSON(editedPromoCode)
}
@ -101,13 +183,8 @@ func (p *PromoCodeController) GetList(c *fiber.Ctx) error {
}
func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
err := p.authMiddleware(c)
fmt.Println("SKER0", err)
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err})
}
userID := c.Locals(models.AuthJWTDecodedUserIDKey).(string)
userID := middleware.ExtractUserID(c)
hlogger := middleware.ExtractLogger(c)
fmt.Println("SKER1", userID)
if userID == "" {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "failed to get jwt payload"})
@ -123,7 +200,7 @@ func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
}
fmt.Println("SKER2", req)
greetings, err := p.promoCodeService.ActivatePromo(c.Context(), &req, userID)
promocode, err := p.promoCodeService.ActivatePromo(c.Context(), &req, userID)
fmt.Println("SKER3", err)
if err != nil {
p.logger.Error("Failed to activate promocode", zap.Error(err))
@ -134,20 +211,58 @@ func (p *PromoCodeController) Activate(c *fiber.Ctx) error {
case errors.Is(err, repository.ErrPromoCodeAlreadyActivated):
return c.Status(fiber.StatusForbidden).JSON(fiber.Map{"error": "PromoCode already activated"})
case errors.Is(err, repository.ErrPromoCodeExpired):
hlogger.Emit(models.InfoPromocodeDeadlined{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
CtxID: promocode.ID.String(),
})
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
case errors.Is(err, repository.ErrPromoCodeExhausted):
hlogger.Emit(models.InfoPromocodeExhausted{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
CtxID: promocode.ID.String(),
})
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode exhausted"})
default:
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
}
return c.Status(fiber.StatusOK).JSON(models.ActivateResp{Greetings: greetings})
if req.Codeword != "" {
hlogger.Emit(models.InfoPromocodeActivated{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
CtxID: promocode.ID.String(),
CtxUserID: userID,
CtxCode: req.Codeword,
})
} else if req.FastLink != "" {
hlogger.Emit(models.InfoFastlinkActivated{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
CtxID: promocode.ID.String(),
CtxUserID: userID,
CtxPromocodeID: req.FastLink,
})
}
return c.Status(fiber.StatusOK).JSON(models.ActivateResp{Greetings: promocode.Greetings})
}
func (p *PromoCodeController) Delete(c *fiber.Ctx) error {
promoCodeID := c.Params("promocodeID")
userID := middleware.ExtractUserID(c)
hlogger := middleware.ExtractLogger(c)
promoCodeID := c.Params("promocodeID")
if promoCodeID == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "PromoCode ID is required"})
}
@ -163,10 +278,22 @@ func (p *PromoCodeController) Delete(c *fiber.Ctx) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
hlogger.Emit(models.InfoPromocodeDeleted{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
CtxID: promoCodeID,
CtxUserID: userID,
})
return c.SendStatus(fiber.StatusOK)
}
func (p *PromoCodeController) CreateFastLink(c *fiber.Ctx) error {
userID := middleware.ExtractUserID(c)
hlogger := middleware.ExtractLogger(c)
var req struct {
PromoCodeID string `json:"id"`
}
@ -189,6 +316,16 @@ func (p *PromoCodeController) CreateFastLink(c *fiber.Ctx) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
hlogger.Emit(models.InfoFastlinkCreated{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
CtxID: fastLink,
CtxPromocodeID: req.PromoCodeID,
CtxUserID: userID,
})
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"fastlink": fastLink})
}

@ -4,25 +4,36 @@ import (
"codeword/internal/models"
"codeword/internal/repository"
"codeword/internal/services"
"codeword/internal/utils/middleware"
"encoding/base64"
"errors"
"fmt"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"strings"
"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(logger *zap.Logger, service *services.RecoveryService, defaultRedirectionURL string) *RecoveryController {
func NewRecoveryController(deps Deps) *RecoveryController {
return &RecoveryController{
logger: logger,
service: service,
defaultURL: defaultRedirectionURL,
logger: deps.Logger,
service: deps.Service,
defaultURL: deps.DefaultURL,
recoveryURL: deps.RecoveryURL,
}
}
@ -45,6 +56,7 @@ func (r *RecoveryController) HandlePingDB(c *fiber.Ctx) error {
}
func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
hlogger := middleware.ExtractLogger(c)
var req models.RecoveryRequest
if err := c.BodyParser(&req); err != nil {
r.logger.Error("Failed to parse recovery request", zap.Error(err))
@ -102,10 +114,22 @@ func (r *RecoveryController) HandleRecoveryRequest(c *fiber.Ctx) error {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal Server Error"})
}
hlogger.Emit(models.InfoPasswordRestorationRequested{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
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 := middleware.ExtractLogger(c)
sign := c.Params("sign")
record, err := r.service.GetRecoveryRecord(c.Context(), sign)
@ -116,7 +140,7 @@ func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
if time.Since(record.CreatedAt) > 15*time.Minute {
r.logger.Error("Recovery link expired", zap.String("signature", sign))
return c.Redirect(record.SignUrl+"/expired")
return c.Redirect(record.SignUrl + "/expired")
}
tokens, err := r.service.ExchangeForTokens(record.UserID, record.Sign)
@ -142,5 +166,14 @@ func (r *RecoveryController) HandleRecoveryLink(c *fiber.Ctx) error {
HTTPOnly: true,
})
hlogger.Emit(models.InfoPasswordRestored{
CtxUserIP: c.IP(),
CtxUserPort: c.Port(),
KeyDomain: strings.Join(c.Subdomains(), "/"),
KeyPath: c.Path(),
CtxID: record.ID.String(),
CtxUserID: record.UserID,
})
return c.Redirect(record.SignUrl + "?auth=" + tokens["accessToken"])
}

@ -1,7 +1,7 @@
package http
import (
"codeword/internal/utils/hlogger_mw"
"codeword/internal/utils/middleware"
"context"
"fmt"
"github.com/gofiber/fiber/v2"
@ -13,6 +13,7 @@ type ServerConfig struct {
Logger *zap.Logger
Controllers []Controller
Hlogger hlog.Logger
JWT *middleware.JWT
}
type Server struct {
@ -23,7 +24,8 @@ type Server struct {
func NewServer(config ServerConfig) *Server {
app := fiber.New()
app.Use(hlogger_mw.ContextLogger(config.Hlogger))
app.Use(middleware.ContextLogger(config.Hlogger))
app.Use("/promocode", middleware.NewAuthenticator(config.JWT))
s := &Server{
Logger: config.Logger,
Controllers: config.Controllers,

@ -93,30 +93,30 @@ func (s *PromoCodeService) GetPromoCodesList(ctx context.Context, req *models.Ge
// разделяется ли она или они всегда вместе, если разделяются то что-то из этого может быть пустым либо все заполеннное,
// соответсвенно надо сделать соответствующие проверки до записи в кафку и до отправки в дискаунт сервис
func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq, userID string) (string, error) {
func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.ActivateReq, userID string) (*models.PromoCode, error) {
promoCode, err := s.promoCodeRepo.ActivatePromo(ctx, req)
fmt.Println("SKER20", err, promoCode, time.Now().Unix())
if err != nil {
s.logger.Error("Failed to activate promocode", zap.Error(err))
return "", err
return nil, err
}
//todo такая реализация проверок кажется довольно массивной, думаю как то это стоит сделать параллельно обхаживая все условия
if promoCode.DueTo < time.Now().Unix() && promoCode.DueTo > 0 {
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
fmt.Println("SKER21", err)
if err != nil {
return "", err
return nil, err
}
return "", fmt.Errorf("%w: expired on %s", repository.ErrPromoCodeExpired, time.Unix(promoCode.DueTo, 0).Format(time.RFC3339))
return nil, fmt.Errorf("%w: expired on %s", repository.ErrPromoCodeExpired, time.Unix(promoCode.DueTo, 0).Format(time.RFC3339))
}
if promoCode.DueTo == 0 && promoCode.ActivationCount < 0 && promoCode.ActivationLimit != 0 {
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
fmt.Println("SKER22", err)
if err != nil {
return "", err
return nil, err
}
return "", repository.ErrPromoCodeExhausted
return nil, repository.ErrPromoCodeExhausted
}
err = s.statsRepo.UpdateStatistics(ctx, req, promoCode, userID)
@ -125,12 +125,12 @@ func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.Activa
if errors.Is(err, repository.ErrPromoCodeAlreadyActivated) {
err := s.promoCodeRepo.IncreaseActivationCount(ctx, promoCode.ID)
if err != nil {
return "", err
return nil, err
}
return "", repository.ErrPromoCodeAlreadyActivated
return nil, repository.ErrPromoCodeAlreadyActivated
}
s.logger.Error("Failed add in stats", zap.Error(err))
return "", err
return nil, err
}
var postfix string
@ -158,7 +158,7 @@ func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.Activa
fmt.Println("SKER24", err)
if err := s.kafka.Send(ctx, userID, fakeTariff); err != nil {
s.logger.Error("Failed to send fake tariff to Kafka", zap.Error(err))
return "", err
return nil, err
}
}
@ -205,11 +205,11 @@ func (s *PromoCodeService) ActivatePromo(ctx context.Context, req *models.Activa
_, err = s.discountClient.CreateDiscount(ctx, discountRequest)
if err != nil {
s.logger.Error("Failed to create discount", zap.Error(err))
return "", err
return nil, err
}
}
return promoCode.Greetings, nil
return promoCode, nil
}
func (s *PromoCodeService) DeletePromoCode(ctx context.Context, promoCodeID string) error {

@ -1,9 +1,9 @@
package utils
package middleware
import (
"codeword/internal/models"
"strings"
"fmt"
"strings"
"github.com/gofiber/fiber/v2"
)
@ -44,3 +44,8 @@ func authenticate(c *fiber.Ctx, jwtUtil *JWT) error {
return nil
}
func ExtractUserID(c *fiber.Ctx) string {
userID := c.Context().UserValue(models.AuthJWTDecodedUserIDKey).(string)
return userID
}

@ -1,4 +1,4 @@
package utils
package middleware
import (
"codeword/internal/initialize"

@ -1,4 +1,4 @@
package hlogger_mw
package middleware
import (
"codeword/internal/models"
@ -12,3 +12,8 @@ func ContextLogger(logger hlog.Logger) fiber.Handler {
return c.Next()
}
}
func ExtractLogger(c *fiber.Ctx) hlog.Logger {
logger := c.Context().UserValue(models.LoggerKey).(hlog.Logger)
return logger
}

@ -2,11 +2,11 @@ package helpers
import (
"codeword/internal/initialize"
"codeword/utils"
"codeword/internal/utils/middleware"
"strings"
)
func InitializeJWT() *utils.JWT {
func InitializeJWT() *middleware.JWT {
publicKey := strings.Replace(`-----BEGIN PUBLIC KEY-----
MIGeMA0GCSqGSIb3DQEBAQUAA4GMADCBiAKBgHgnvr7O2tiApjJfid1orFnIGm69
80fZp+Lpbjo+NC/0whMFga2Biw5b1G2Q/B2u0tpO1Fs/E8z7Lv1nYfr5jx2S8x6B
@ -29,7 +29,7 @@ func InitializeJWT() *utils.JWT {
7McQvEk12dU/JNTX8wJAOlAtSNjp9tVwpMpC0w2St1eKc1L2SknjeohA5ldoBz8sGeZsPhTU3eHSD1neAZXLKN5K68z3zFBr20ubY9nyLw==
-----END RSA PRIVATE KEY-----`, "\t", "", -1)
return utils.NewJWT(&initialize.Config{
return middleware.NewJWT(&initialize.Config{
PrivateKey: privateKey,
PublicKey: publicKey,
Audience: "pena",