package handlers import ( "fmt" "io" "log" "strconv" "github.com/gofiber/fiber/v2" "jd-book-uploader/models" "jd-book-uploader/services" "jd-book-uploader/utils" ) // UploadStationery handles stationery upload requests func UploadStationery(c *fiber.Ctx) error { ctx := c.Context() // Parse multipart form form, err := c.MultipartForm() if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "error": "Failed to parse form data", }) } // Extract image file imageFiles := form.File["image"] if len(imageFiles) == 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "error": "Image file is required", }) } imageFile := imageFiles[0] // Validate file type allowedTypes := map[string]bool{ "image/png": true, "image/jpeg": true, "image/jpg": true, "image/webp": true, } if !allowedTypes[imageFile.Header.Get("Content-Type")] { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "error": "Invalid file type. Only PNG, JPEG, and WEBP are allowed", }) } // Validate file size (max 10MB) maxSize := int64(10 * 1024 * 1024) // 10MB if imageFile.Size > maxSize { return c.Status(fiber.StatusRequestEntityTooLarge).JSON(fiber.Map{ "success": false, "error": "File size exceeds 10MB limit", }) } // Read image file file, err := imageFile.Open() if err != nil { log.Printf("Failed to open image file: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "error": "Failed to read image file", }) } defer file.Close() imageData, err := io.ReadAll(file) if err != nil { log.Printf("Failed to read image data: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "error": "Failed to read image data", }) } // Extract form fields stationeryName := c.FormValue("stationery_name") costStr := c.FormValue("cost") priceStr := c.FormValue("price") discountStr := c.FormValue("discount") quantityStr := c.FormValue("quantity") color := c.FormValue("color") material := c.FormValue("material") dimensions := c.FormValue("dimensions") category := c.FormValue("category") description := c.FormValue("description") // Validate required fields if err := validateStationeryFields(stationeryName, costStr, priceStr, quantityStr, category); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "error": err.Error(), }) } // Parse numeric fields cost, err := strconv.ParseFloat(costStr, 64) if err != nil || cost <= 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "error": "Invalid cost value", }) } price, err := strconv.ParseFloat(priceStr, 64) if err != nil || price <= 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "error": "Invalid price value", }) } discount := 0.0 if discountStr != "" { discount, err = strconv.ParseFloat(discountStr, 64) if err != nil || discount < 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "error": "Invalid discount value", }) } } quantity, err := strconv.Atoi(quantityStr) if err != nil || quantity <= 0 { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, "error": "Invalid quantity value", }) } // Generate slug from stationery name slug := utils.GenerateSlug(utils.CleanString(stationeryName)) // Check if slug already exists slugExists, err := services.StationerySlugExists(ctx, slug) if err != nil { log.Printf("Failed to check slug existence: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "error": "Failed to validate product", }) } if slugExists { return c.Status(fiber.StatusConflict).JSON(fiber.Map{ "success": false, "error": fmt.Sprintf("A stationery item with slug '%s' already exists", slug), }) } // Prepare filename: slug.png filename := fmt.Sprintf("%s.png", slug) // Upload image to Firebase Storage with correct path imageURL, err := services.UploadImage(ctx, imageData, "/jd-bookshop/stationery", filename) if err != nil { log.Printf("Firebase upload failed: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "error": "Failed to upload image", }) } // Create stationery request stationeryReq := &models.StationeryCreateRequest{ StationeryName: utils.CleanString(stationeryName), Cost: cost, Price: price, Discount: discount, Quantity: quantity, Color: utils.CleanString(color), Material: utils.CleanString(material), Dimensions: utils.CleanString(dimensions), Category: utils.CleanString(category), Description: utils.CleanString(description), ImageURL: imageURL, } // Create stationery in database stationery, err := services.CreateStationery(ctx, stationeryReq) if err != nil { log.Printf("Database insert failed: %v", err) return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ "success": false, "error": "Failed to create stationery", }) } // Return success response return c.Status(fiber.StatusCreated).JSON(fiber.Map{ "success": true, "data": fiber.Map{ "stationery_code": stationery.StationeryCode, "stationery_name": stationery.StationeryName, "image_url": stationery.ImageURL, "slug": stationery.Slug, "created_at": stationery.CreatedAt, }, }) } // validateStationeryFields validates required stationery fields func validateStationeryFields(stationeryName, cost, price, quantity, category string) error { if stationeryName == "" { return fmt.Errorf("stationery_name is required") } if len(stationeryName) > 200 { return fmt.Errorf("stationery_name exceeds 200 characters") } if cost == "" { return fmt.Errorf("cost is required") } if price == "" { return fmt.Errorf("price is required") } if quantity == "" { return fmt.Errorf("quantity is required") } if category == "" { return fmt.Errorf("category is required") } if len(category) > 100 { return fmt.Errorf("category exceeds 100 characters") } return nil }