package controllers import ( database "ares/database/config" "ares/database/models" "errors" "net/http" "strconv" "github.com/gofiber/fiber/v3" "gorm.io/gorm" ) // GetProducts godoc // @Summary List products (public) with pagination // @Tags Products // @Produce json // @Param page query int false "Page number" // @Param per_page query int false "Items per page" // @Success 200 {object} map[string]interface{} // @Router /api/v1/products [get] func GetProducts(c fiber.Ctx) error { pageStr := c.Query("page", "1") perPageStr := c.Query("per_page", "10") page, _ := strconv.Atoi(pageStr) perPage, _ := strconv.Atoi(perPageStr) if page < 1 { page = 1 } if perPage < 1 { perPage = 10 } if perPage > 100 { perPage = 100 } offset := (page - 1) * perPage var total int64 database.DB.Model(&models.Product{}).Count(&total) var products []models.Product database.DB.Preload("Categories").Preload("Tags").Limit(perPage).Offset(offset).Order("created_at desc").Find(&products) return c.JSON(fiber.Map{ "data": products, "meta": fiber.Map{"page": page, "per_page": perPage, "total": total}, }) } // GetProduct godoc // @Summary Get single product (public) by slug // @Tags Products // @Produce json // @Param slug path string true "Product slug" // @Success 200 {object} models.ProductDoc // @Failure 404 {object} map[string]string // @Router /api/v1/products/{slug} [get] func GetProduct(c fiber.Ctx) error { slug := c.Params("slug") if slug == "" { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid slug"}) } var product models.Product if err := database.DB.Preload("Categories").Preload("Tags").Where("slug = ? AND deleted_at IS NULL", slug).First(&product).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "product not found"}) } return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "db error"}) } return c.JSON(product) } // AddProductCommentRequest represents payload type AddProductCommentRequest struct { ProductID uint `json:"product_id" validate:"required"` Body string `json:"body" validate:"required,min=3"` } func AddProductComment(c fiber.Ctx) error { userID, ok := c.Locals("user_id").(uint) if !ok || userID == 0 { return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "unauthorized"}) } var input AddProductCommentRequest if err := c.Bind().Body(&input); err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid payload"}) } // Add validation if needed if input.Body == "" { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "body is required"}) } var product models.Product if err := database.DB.First(&product, input.ProductID).Error; err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "product not found"}) } comment := models.ProductComment{ UserID: userID, ProductID: input.ProductID, Body: input.Body, } if err := database.DB.Create(&comment).Error; err != nil { return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "could not save comment"}) } return c.Status(http.StatusCreated).JSON(comment) } // GetProductComments godoc // @Summary Get comments for a product // @Tags Products // @Produce json // @Param id path int true "Product ID" // @Success 200 {array} models.ProductCommentDoc // @Router /api/v1/products/{id}/comments [get] func GetProductComments(c fiber.Ctx) error { productID, err := strconv.Atoi(c.Params("id")) if err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid product id"}) } var comments []models.ProductComment database.DB.Where("product_id = ?", productID).Order("created_at desc").Find(&comments) return c.JSON(comments) } // RecordProductCategoryView godoc // @Summary Record a view for a product category // @Tags Products // @Produce json // @Param id path int true "Category ID" // @Success 201 {object} models.ProductCategoryViewDoc // @Router /api/v1/product-categories/{id}/view [post] func RecordProductCategoryView(c fiber.Ctx) error { categoryID, err := strconv.Atoi(c.Params("id")) if err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "invalid category id"}) } var category models.ProductCategory if err := database.DB.First(&category, categoryID).Error; err != nil { return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "category not found"}) } view := models.ProductCategoryView{ CategoryID: uint(categoryID), IPAddress: c.IP(), } database.DB.Create(&view) return c.Status(http.StatusCreated).JSON(view) }