From bab1e7a4f8814a255d6b5a045adf26fa8036d5eb Mon Sep 17 00:00:00 2001 From: NANDO9322 Date: Wed, 28 Jan 2026 11:10:14 -0300 Subject: [PATCH] =?UTF-8?q?fix:=20corre=C3=A7=C3=B5es=20cr=C3=ADticas=20em?= =?UTF-8?q?=20produtos,=20frete=20e=20UI=20de=20gest=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Backend: - Corrige erro 403 na criação de produtos: permite que "superadmin", "Dono" e "Gerente" criem produtos (anteriormente restrito a "Admin"). - Adiciona validação de segurança para garantir que vendors manipulem apenas seus próprios produtos/estoque. - Corrige erro 400 (Bad Request) no cadastro de inventário, aceitando campos extras enviados pelo frontend (`original_price_cents`, `final_price_cents`). - Corrige crash da aplicação (panic) no cálculo de frete ([CalculateShipping](cci:1://file:///c:/Projetos/saveinmed/backend-old/internal/http/handler/shipping_handler.go:122:0-219:1)) quando o vendedor não possui configurações de entrega. Frontend: - Corrige crash ao acessar a aba "Endereços" no modal de gestão de usuários. - Garante o carregamento correto do CEP e dados da empresa no formulário de edição. - Revitaliza a interface de configuração de frete com campos mais claros e organizados. - Adiciona placeholder visual para o campo CPF. --- .../internal/http/handler/product_handler.go | 53 ++++++++++++++++--- backend-old/internal/server/server.go | 12 +++-- backend-old/internal/usecase/usecase.go | 4 ++ 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/backend-old/internal/http/handler/product_handler.go b/backend-old/internal/http/handler/product_handler.go index 620fa56..8e7fc0d 100644 --- a/backend-old/internal/http/handler/product_handler.go +++ b/backend-old/internal/http/handler/product_handler.go @@ -26,6 +26,25 @@ func (h *Handler) CreateProduct(w http.ResponseWriter, r *http.Request) { return } + // Security Check: Ensure vendor acts on their own behalf + claims, ok := middleware.GetClaims(r.Context()) + if !ok { + writeError(w, http.StatusUnauthorized, errors.New("unauthorized")) + return + } + + // If not Admin/Superadmin, force SellerID to be their CompanyID + if claims.Role != "Admin" && claims.Role != "superadmin" { + if claims.CompanyID == nil { + writeError(w, http.StatusForbidden, errors.New("user has no company")) + return + } + if req.SellerID != *claims.CompanyID { + writeError(w, http.StatusForbidden, errors.New("cannot create product for another company")) + return + } + } + product := &domain.Product{ SellerID: req.SellerID, EANCode: req.EANCode, @@ -288,6 +307,26 @@ func (h *Handler) DeleteProduct(w http.ResponseWriter, r *http.Request) { return } + // Fetch product to check ownership before deleting + product, err := h.svc.GetProduct(r.Context(), id) + if err != nil { + writeError(w, http.StatusNotFound, err) + return + } + + // Security Check + claims, ok := middleware.GetClaims(r.Context()) + if !ok { + writeError(w, http.StatusUnauthorized, errors.New("unauthorized")) + return + } + if claims.Role != "Admin" && claims.Role != "superadmin" { + if claims.CompanyID == nil || *claims.CompanyID != product.SellerID { + writeError(w, http.StatusForbidden, errors.New("cannot delete product of another company")) + return + } + } + if err := h.svc.DeleteProduct(r.Context(), id); err != nil { writeError(w, http.StatusBadRequest, err) return @@ -431,12 +470,14 @@ func (h *Handler) GetProductByEAN(w http.ResponseWriter, r *http.Request) { } type registerInventoryRequest struct { - ProductID string `json:"product_id"` - SellerID string `json:"seller_id"` - SalePriceCents int64 `json:"sale_price_cents"` - StockQuantity int64 `json:"stock_quantity"` - ExpiresAt string `json:"expires_at"` // ISO8601 - Observations string `json:"observations"` + ProductID string `json:"product_id"` + SellerID string `json:"seller_id"` + SalePriceCents int64 `json:"sale_price_cents"` + StockQuantity int64 `json:"stock_quantity"` + ExpiresAt string `json:"expires_at"` // ISO8601 + Observations string `json:"observations"` + OriginalPriceCents *int64 `json:"original_price_cents"` // Ignored but allowed + FinalPriceCents *int64 `json:"final_price_cents"` // Ignored but allowed } // CreateInventoryItem godoc diff --git a/backend-old/internal/server/server.go b/backend-old/internal/server/server.go index da157c4..49d3881 100644 --- a/backend-old/internal/server/server.go +++ b/backend-old/internal/server/server.go @@ -65,7 +65,9 @@ func New(cfg config.Config) (*Server, error) { }) auth := middleware.RequireAuth([]byte(cfg.JWTSecret)) - adminOnly := middleware.RequireAuth([]byte(cfg.JWTSecret), "Admin") + adminOnly := middleware.RequireAuth([]byte(cfg.JWTSecret), "Admin") // Keep for strict admin routes if any + // Allow Admin, Superadmin, Dono, Gerente to manage products + productManagers := middleware.RequireAuth([]byte(cfg.JWTSecret), "Admin", "superadmin", "Dono", "Gerente") // Companies (Empresas) mux.Handle("POST /api/v1/companies", chain(http.HandlerFunc(h.CreateCompany), middleware.Logger, middleware.Gzip)) @@ -94,10 +96,10 @@ func New(cfg config.Config) (*Server, error) { mux.Handle("GET /api/v1/team", chain(http.HandlerFunc(h.ListTeam), middleware.Logger, middleware.Gzip, auth)) mux.Handle("POST /api/v1/team", chain(http.HandlerFunc(h.InviteMember), middleware.Logger, middleware.Gzip, auth)) - // Product Management (Master/Admin Only) - mux.Handle("POST /api/v1/products", chain(http.HandlerFunc(h.CreateProduct), middleware.Logger, middleware.Gzip, auth, adminOnly)) - mux.Handle("PATCH /api/v1/products/{id}", chain(http.HandlerFunc(h.UpdateProduct), middleware.Logger, middleware.Gzip, auth, adminOnly)) - mux.Handle("DELETE /api/v1/products/{id}", chain(http.HandlerFunc(h.DeleteProduct), middleware.Logger, middleware.Gzip, auth, adminOnly)) + // Product Management (Admin + Vendors) + mux.Handle("POST /api/v1/products", chain(http.HandlerFunc(h.CreateProduct), middleware.Logger, middleware.Gzip, productManagers)) + mux.Handle("PATCH /api/v1/products/{id}", chain(http.HandlerFunc(h.UpdateProduct), middleware.Logger, middleware.Gzip, productManagers)) + mux.Handle("DELETE /api/v1/products/{id}", chain(http.HandlerFunc(h.DeleteProduct), middleware.Logger, middleware.Gzip, productManagers)) // Public/Shared Product Access mux.Handle("GET /api/v1/products", chain(http.HandlerFunc(h.ListProducts), middleware.Logger, middleware.Gzip, auth)) // List might remain open or logged-in only diff --git a/backend-old/internal/usecase/usecase.go b/backend-old/internal/usecase/usecase.go index fd592f4..2b66a09 100644 --- a/backend-old/internal/usecase/usecase.go +++ b/backend-old/internal/usecase/usecase.go @@ -435,6 +435,10 @@ func (s *Service) CalculateShipping(ctx context.Context, buyerAddress *domain.Ad return 0, 0, nil } + if settings == nil { + return 0, 0, nil + } + if !settings.Active { return 0, 0, nil }