package handlers import ( "net/http" "os" "strings" "gauth-central/config" "gauth-central/internal/services" "gauth-central/pkg/utils" "github.com/gin-gonic/gin" ) type BannerHandler struct { bannerService *services.BannerService } func NewBannerHandler(bannerService *services.BannerService) *BannerHandler { return &BannerHandler{bannerService: bannerService} } // GetAllBanners godoc // @Summary Get all active banners // @Description Retrieve a list of active banners // @Tags banners // @Produce json // @Success 200 {array} models.Banner // @Failure 500 {object} map[string]string // @Router /banners [get] func (h *BannerHandler) GetAllBanners(c *gin.Context) { items, err := h.bannerService.GetAllBanners(true) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, items) } // GetActiveBanner godoc // @Summary Get active banner // @Description Retrieve the newest active banner // @Tags banners // @Produce json // @Success 200 {object} models.Banner // @Failure 404 {object} map[string]string // @Router /banners/active [get] func (h *BannerHandler) GetActiveBanner(c *gin.Context) { item, err := h.bannerService.GetFirstActiveBanner() if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, item) } // AdminGetAllBanners godoc // @Summary Get all banners (Admin) // @Description Retrieve a list of all banners including inactive ones // @Tags admin // @Security ApiKeyAuth // @Produce json // @Success 200 {array} models.Banner // @Failure 500 {object} map[string]string // @Router /admin/banners [get] func (h *BannerHandler) AdminGetAllBanners(c *gin.Context) { items, err := h.bannerService.GetAllBanners(false) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, items) } // AdminGetBannerByID godoc // @Summary Get banner by ID (Admin) // @Description Retrieve details of a specific banner // @Tags admin // @Security ApiKeyAuth // @Produce json // @Param id path string true "Banner ID" // @Success 200 {object} models.Banner // @Failure 404 {object} map[string]string // @Router /admin/banners/{id} [get] func (h *BannerHandler) AdminGetBannerByID(c *gin.Context) { id := c.Param("id") item, err := h.bannerService.GetBannerByID(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, item) } // CreateBanner godoc // @Summary Create a new banner (Admin) // @Description Create a new banner entry // @Tags admin // @Security ApiKeyAuth // @Accept multipart/form-data // @Produce json // @Param color formData string false "Text color" // @Param title formData string false "Title" // @Param text1 formData string false "Text 1" // @Param text2 formData string false "Text 2" // @Param text4 formData string false "Text 4" // @Param text5 formData string false "Text 5" // @Param image formData file true "Image" // @Param image_k formData file false "Small image" // @Param image_k_txt formData string false "Small image text" // @Param is_active formData bool false "Is active" // @Success 201 {object} models.Banner // @Failure 400 {object} map[string]string // @Router /admin/banners [post] func (h *BannerHandler) CreateBanner(c *gin.Context) { color := strings.TrimSpace(c.PostForm("color")) title := strings.TrimSpace(c.PostForm("title")) text1 := strings.TrimSpace(c.PostForm("text1")) text2 := strings.TrimSpace(c.PostForm("text2")) text4 := strings.TrimSpace(c.PostForm("text4")) text5 := strings.TrimSpace(c.PostForm("text5")) imageKTxt := strings.TrimSpace(c.PostForm("image_k_txt")) isActive := true isActivePtr, err := parseOptionalBool(c, "is_active") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "is_active must be true or false"}) return } if isActivePtr != nil { isActive = *isActivePtr } imageFile, imageErr := c.FormFile("image") if imageErr != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "image is required"}) return } if imageFile.Size > 5*1024*1024 { c.JSON(http.StatusBadRequest, gin.H{"error": "Image size exceeds 5MB limit"}) return } imageURL, err := utils.SaveOptimizedImage(imageFile, "./uploads/banner", "banner", &utils.ImageOptions{ Width: config.AppConfig.BannerImageWidth, Height: config.AppConfig.BannerImageHeight, Quality: float32(config.AppConfig.BannerImageQuality), Format: config.AppConfig.BannerImageFormat, Mode: config.AppConfig.BannerImageMode, }) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + err.Error()}) return } imageKURL := "" imageKFile, imageKErr := c.FormFile("image_k") if imageKErr == nil { if imageKFile.Size > 5*1024*1024 { c.JSON(http.StatusBadRequest, gin.H{"error": "image_k size exceeds 5MB limit"}) return } imageKURL, err = utils.SaveOptimizedImage(imageKFile, "./uploads/banner/kucuk", "banner_k", &utils.ImageOptions{ Width: config.AppConfig.BannerThumbWidth, Height: config.AppConfig.BannerThumbHeight, Quality: float32(config.AppConfig.BannerThumbQuality), Format: config.AppConfig.BannerThumbFormat, Mode: config.AppConfig.BannerThumbMode, }) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image_k: " + err.Error()}) return } } item, err := h.bannerService.CreateBanner( color, title, text1, text2, text4, text5, imageURL, imageKURL, imageKTxt, isActive, ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusCreated, item) } // UpdateBanner godoc // @Summary Update a banner (Admin) // @Description Update an existing banner entry // @Tags admin // @Security ApiKeyAuth // @Accept multipart/form-data // @Produce json // @Param id path string true "Banner ID" // @Param color formData string false "Text color" // @Param title formData string false "Title" // @Param text1 formData string false "Text 1" // @Param text2 formData string false "Text 2" // @Param text4 formData string false "Text 4" // @Param text5 formData string false "Text 5" // @Param image formData file false "Image" // @Param image_k formData file false "Small image" // @Param image_k_txt formData string false "Small image text" // @Param is_active formData bool false "Is active" // @Success 200 {object} models.Banner // @Failure 400 {object} map[string]string // @Failure 404 {object} map[string]string // @Router /admin/banners/{id} [put] func (h *BannerHandler) UpdateBanner(c *gin.Context) { id := c.Param("id") color, hasColor := getOptionalFormValue(c, "color") title, hasTitle := getOptionalFormValue(c, "title") text1, hasText1 := getOptionalFormValue(c, "text1") text2, hasText2 := getOptionalFormValue(c, "text2") text4, hasText4 := getOptionalFormValue(c, "text4") text5, hasText5 := getOptionalFormValue(c, "text5") imageKTxt, hasImageKTxt := getOptionalFormValue(c, "image_k_txt") var colorPtr *string var titlePtr *string var text1Ptr *string var text2Ptr *string var text4Ptr *string var text5Ptr *string var imageKTxtPtr *string if hasColor { colorPtr = &color } if hasTitle { titlePtr = &title } if hasText1 { text1Ptr = &text1 } if hasText2 { text2Ptr = &text2 } if hasText4 { text4Ptr = &text4 } if hasText5 { text5Ptr = &text5 } if hasImageKTxt { imageKTxtPtr = &imageKTxt } isActivePtr, err := parseOptionalBool(c, "is_active") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "is_active must be true or false"}) return } var imagePtr *string imageFile, imageErr := c.FormFile("image") if imageErr == nil { if imageFile.Size > 5*1024*1024 { c.JSON(http.StatusBadRequest, gin.H{"error": "Image size exceeds 5MB limit"}) return } item, fetchErr := h.bannerService.GetBannerByID(id) if fetchErr != nil { c.JSON(http.StatusNotFound, gin.H{"error": fetchErr.Error()}) return } if item.Image != "" && strings.HasPrefix(item.Image, "/uploads/") { _ = os.Remove("." + item.Image) } imageURL, saveErr := utils.SaveOptimizedImage(imageFile, "./uploads/banner", id, &utils.ImageOptions{ Width: config.AppConfig.BannerImageWidth, Height: config.AppConfig.BannerImageHeight, Quality: float32(config.AppConfig.BannerImageQuality), Format: config.AppConfig.BannerImageFormat, Mode: config.AppConfig.BannerImageMode, }) if saveErr != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()}) return } imagePtr = &imageURL } var imageKPtr *string imageKFile, imageKErr := c.FormFile("image_k") if imageKErr == nil { if imageKFile.Size > 5*1024*1024 { c.JSON(http.StatusBadRequest, gin.H{"error": "image_k size exceeds 5MB limit"}) return } item, fetchErr := h.bannerService.GetBannerByID(id) if fetchErr != nil { c.JSON(http.StatusNotFound, gin.H{"error": fetchErr.Error()}) return } if item.ImageK != "" && strings.HasPrefix(item.ImageK, "/uploads/") { _ = os.Remove("." + item.ImageK) } imageKURL, saveErr := utils.SaveOptimizedImage(imageKFile, "./uploads/banner/kucuk", id+"_k", &utils.ImageOptions{ Width: config.AppConfig.BannerThumbWidth, Height: config.AppConfig.BannerThumbHeight, Quality: float32(config.AppConfig.BannerThumbQuality), Format: config.AppConfig.BannerThumbFormat, Mode: config.AppConfig.BannerThumbMode, }) if saveErr != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image_k: " + saveErr.Error()}) return } imageKPtr = &imageKURL } item, err := h.bannerService.UpdateBanner( id, colorPtr, titlePtr, text1Ptr, text2Ptr, text4Ptr, text5Ptr, imagePtr, imageKPtr, imageKTxtPtr, isActivePtr, ) if err != nil { status := http.StatusInternalServerError if err.Error() == "banner not found" { status = http.StatusNotFound } c.JSON(status, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, item) } // DeleteBanner godoc // @Summary Delete a banner (Admin) // @Description Delete a banner by ID // @Tags admin // @Security ApiKeyAuth // @Param id path string true "Banner ID" // @Success 200 {object} map[string]string // @Failure 404 {object} map[string]string // @Router /admin/banners/{id} [delete] func (h *BannerHandler) DeleteBanner(c *gin.Context) { id := c.Param("id") if err := h.bannerService.DeleteBanner(id); err != nil { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Banner deleted successfully"}) }