mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 18:52:56 +00:00
hot fix #1
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
package eshop
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"fotbal-club/internal/config"
|
||||
"fotbal-club/internal/models"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type CartController struct {
|
||||
DB *gorm.DB
|
||||
Config *config.Config
|
||||
}
|
||||
|
||||
func NewCartController(db *gorm.DB, cfg *config.Config) *CartController {
|
||||
return &CartController{
|
||||
DB: db,
|
||||
Config: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
type cartContext struct {
|
||||
UserID *uint
|
||||
SessionToken string
|
||||
}
|
||||
|
||||
func (ctrl *CartController) getCartContext(c *gin.Context) cartContext {
|
||||
var res cartContext
|
||||
if uidVal, ok := c.Get("userID"); ok {
|
||||
switch v := uidVal.(type) {
|
||||
case uint:
|
||||
res.UserID = &v
|
||||
case int:
|
||||
u := uint(v)
|
||||
res.UserID = &u
|
||||
case int64:
|
||||
u := uint(v)
|
||||
res.UserID = &u
|
||||
}
|
||||
}
|
||||
res.SessionToken = c.GetHeader("X-Session-Token")
|
||||
if res.SessionToken == "" {
|
||||
if cookie, err := c.Request.Cookie("eshop_session_token"); err == nil {
|
||||
res.SessionToken = cookie.Value
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (ctrl *CartController) findOrCreateCart(c *gin.Context) (*models.EshopCart, error) {
|
||||
cc := ctrl.getCartContext(c)
|
||||
q := ctrl.DB.Where("completed = ?", false)
|
||||
if cc.UserID != nil {
|
||||
q = q.Where("user_id = ?", *cc.UserID)
|
||||
} else if cc.SessionToken != "" {
|
||||
q = q.Where("session_token = ?", cc.SessionToken)
|
||||
}
|
||||
var cart models.EshopCart
|
||||
if err := q.Preload("Items").Preload("Items.Product").First(&cart).Error; err != nil {
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
// Create new cart
|
||||
cart = models.EshopCart{
|
||||
UserID: cc.UserID,
|
||||
SessionToken: cc.SessionToken,
|
||||
Currency: ctrl.Config.StripeCurrency,
|
||||
}
|
||||
if cart.Currency == "" {
|
||||
cart.Currency = "CZK"
|
||||
}
|
||||
if err := ctrl.DB.Create(&cart).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return &cart, nil
|
||||
}
|
||||
|
||||
// GetCart returns the current cart
|
||||
func (ctrl *CartController) GetCart(c *gin.Context) {
|
||||
cartObj, err := ctrl.findOrCreateCart(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load cart"})
|
||||
return
|
||||
}
|
||||
// Ensure items are fully loaded
|
||||
if err := ctrl.DB.
|
||||
Preload("Items").
|
||||
Preload("Items.Product").
|
||||
Preload("Items.Variant").
|
||||
First(cartObj, cartObj.ID).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load cart items"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, cartObj)
|
||||
}
|
||||
|
||||
// AddItem adds an item to the cart
|
||||
func (ctrl *CartController) AddItem(c *gin.Context) {
|
||||
var body struct {
|
||||
ProductID uint `json:"product_id"`
|
||||
VariantID *uint `json:"variant_id"`
|
||||
Quantity int `json:"quantity"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request format: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate required fields
|
||||
if body.ProductID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Product ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
if body.Quantity <= 0 || body.Quantity > 100 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Quantity must be between 1 and 100"})
|
||||
return
|
||||
}
|
||||
|
||||
// Check if product exists and is active
|
||||
var product models.EshopProduct
|
||||
if err := ctrl.DB.Where("id = ? AND active = ?", body.ProductID, true).First(&product).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Product not found or not available"})
|
||||
} else {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load product"})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Validate variant if provided
|
||||
if body.VariantID != nil {
|
||||
var variant models.EshopProductVariant
|
||||
if err := ctrl.DB.Where("id = ? AND product_id = ?", *body.VariantID, body.ProductID).First(&variant).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Product variant not found"})
|
||||
} else {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load variant"})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Check stock (negative values mean unlimited)
|
||||
if variant.StockQty >= 0 && variant.StockQty < body.Quantity {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Insufficient stock for this variant"})
|
||||
return
|
||||
}
|
||||
}
|
||||
cartObj, err := ctrl.findOrCreateCart(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load cart"})
|
||||
return
|
||||
}
|
||||
// Upsert cart item
|
||||
var item models.EshopCartItem
|
||||
q := ctrl.DB.Where("cart_id = ? AND product_id = ?", cartObj.ID, body.ProductID)
|
||||
if body.VariantID != nil {
|
||||
q = q.Where("variant_id = ?", *body.VariantID)
|
||||
}
|
||||
if err := q.First(&item).Error; err != nil {
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update cart"})
|
||||
return
|
||||
}
|
||||
item = models.EshopCartItem{
|
||||
CartID: cartObj.ID,
|
||||
ProductID: body.ProductID,
|
||||
VariantID: body.VariantID,
|
||||
Quantity: body.Quantity,
|
||||
UnitPriceCents: product.PriceCents,
|
||||
Currency: product.Currency,
|
||||
}
|
||||
if item.Currency == "" {
|
||||
item.Currency = cartObj.Currency
|
||||
}
|
||||
if err := ctrl.DB.Create(&item).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to add item"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// Update quantity
|
||||
item.Quantity += body.Quantity
|
||||
if item.Quantity <= 0 {
|
||||
if err := ctrl.DB.Delete(&item).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update cart"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := ctrl.DB.Save(&item).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update cart"})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// UpdateItem updates quantity of a cart item
|
||||
func (ctrl *CartController) UpdateItem(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var body struct {
|
||||
Quantity int `json:"quantity"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil || body.Quantity < 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid payload"})
|
||||
return
|
||||
}
|
||||
var item models.EshopCartItem
|
||||
if err := ctrl.DB.First(&item, id).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Cart item not found"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load cart item"})
|
||||
return
|
||||
}
|
||||
|
||||
// Check ownership (simplified) - in real app check if item belongs to user's cart
|
||||
// Here we assume if they know the ID, they can edit it (or rely on middleware/session match in findOrCreateCart flow if we enforced it strictly)
|
||||
// Ideally we should check if item.CartID belongs to current session/user.
|
||||
// For MVP let's leave it as is, or add a check:
|
||||
cc := ctrl.getCartContext(c)
|
||||
var cart models.EshopCart
|
||||
if err := ctrl.DB.First(&cart, item.CartID).Error; err == nil {
|
||||
if cc.UserID != nil && (cart.UserID == nil || *cart.UserID != *cc.UserID) {
|
||||
// user mismatch
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Unauthorized"})
|
||||
return
|
||||
}
|
||||
if cc.UserID == nil && cc.SessionToken != "" && cart.SessionToken != cc.SessionToken {
|
||||
// token mismatch
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Unauthorized"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if body.Quantity == 0 {
|
||||
if err := ctrl.DB.Delete(&item).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update cart"})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
item.Quantity = body.Quantity
|
||||
if err := ctrl.DB.Save(&item).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update cart"})
|
||||
return
|
||||
}
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// RemoveItem removes an item from the cart
|
||||
func (ctrl *CartController) RemoveItem(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if err := ctrl.DB.Delete(&models.EshopCartItem{}, id).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to remove item"})
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
Reference in New Issue
Block a user