package handlers import ( "net/http" "os" "strconv" "strings" "gauth-central/config" "gauth-central/internal/services" "gauth-central/pkg/utils" "github.com/gin-gonic/gin" ) type HomeHandler struct { homeService *services.HomeService } func NewHomeHandler(homeService *services.HomeService) *HomeHandler { return &HomeHandler{homeService: homeService} } type CreateHomeRequest struct { Name string `json:"name" binding:"required"` Title string `json:"title" binding:"required"` Button1 string `json:"button1" binding:"required"` Button2 string `json:"button2" binding:"required"` Video string `json:"video"` Keywords string `json:"keywords" binding:"required"` TagIDs []string `json:"tag_ids"` Image string `json:"image"` IsActive *bool `json:"is_active"` } type UpdateHomeRequest struct { Name *string `json:"name"` Title *string `json:"title"` Button1 *string `json:"button1"` Button2 *string `json:"button2"` Video *string `json:"video"` Keywords *string `json:"keywords"` TagIDs *[]string `json:"tag_ids"` Image *string `json:"image"` Slug *string `json:"slug"` IsActive *bool `json:"is_active"` } // GetAllHomes godoc // @Summary Get all active homes // @Description Retrieve a list of active home entries // @Tags home // @Produce json // @Success 200 {array} models.Home // @Failure 500 {object} map[string]string // @Router /homes [get] func (h *HomeHandler) GetAllHomes(c *gin.Context) { homes, err := h.homeService.GetAllHomes(true) if err != nil { status := http.StatusInternalServerError if isHomeBadRequest(err) { status = http.StatusBadRequest } c.JSON(status, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, homes) } // GetHomeBySlug godoc // @Summary Get a home by slug // @Description Retrieve a single active home by slug // @Tags home // @Produce json // @Param slug path string true "Home Slug" // @Success 200 {object} models.Home // @Failure 404 {object} map[string]string // @Router /homes/{slug} [get] func (h *HomeHandler) GetHomeBySlug(c *gin.Context) { slug := c.Param("slug") home, err := h.homeService.GetHomeBySlug(slug, true) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, home) } // AdminGetAllHomes godoc // @Summary Get all homes (Admin) // @Description Retrieve a list of all homes including inactive ones // @Tags admin // @Security ApiKeyAuth // @Produce json // @Success 200 {array} models.Home // @Failure 500 {object} map[string]string // @Router /admin/homes [get] func (h *HomeHandler) AdminGetAllHomes(c *gin.Context) { homes, err := h.homeService.GetAllHomes(false) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, homes) } // AdminGetHomeByID godoc // @Summary Get a home by ID (Admin) // @Description Retrieve details of a specific home // @Tags admin // @Security ApiKeyAuth // @Produce json // @Param id path string true "Home ID" // @Success 200 {object} models.Home // @Failure 404 {object} map[string]string // @Router /admin/homes/{id} [get] func (h *HomeHandler) AdminGetHomeByID(c *gin.Context) { id := c.Param("id") home, err := h.homeService.GetHomeByID(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, home) } // CreateHome godoc // @Summary Create a new home (Admin) // @Description Create a new home entry // @Tags admin // @Security ApiKeyAuth // @Accept multipart/form-data // @Produce json // @Param name formData string true "Name" // @Param title formData string true "Title" // @Param button1 formData string true "Button 1" // @Param button2 formData string true "Button 2" // @Param video formData string false "Video URL" // @Param keywords formData string true "Keywords" // @Param tag_ids formData []string false "Tag IDs" // @Param image formData file false "Home image" // @Param is_active formData bool false "Is active" // @Success 201 {object} models.Home // @Failure 400 {object} map[string]string // @Router /admin/homes [post] func (h *HomeHandler) CreateHome(c *gin.Context) { name := strings.TrimSpace(c.PostForm("name")) title := strings.TrimSpace(c.PostForm("title")) button1 := strings.TrimSpace(c.PostForm("button1")) button2 := strings.TrimSpace(c.PostForm("button2")) video := strings.TrimSpace(c.PostForm("video")) keywords := strings.TrimSpace(c.PostForm("keywords")) if name == "" || title == "" || button1 == "" || button2 == "" || keywords == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "name, title, button1, button2, and keywords are required"}) return } isActive := false if rawActive := strings.TrimSpace(c.PostForm("is_active")); rawActive != "" { parsed, err := strconv.ParseBool(rawActive) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "is_active must be true or false"}) return } isActive = parsed } tagIDs := parseTagIDs(c) home, err := h.homeService.CreateHome( name, title, button1, button2, video, keywords, "", tagIDs, isActive, ) if err != nil { status := http.StatusInternalServerError if isHomeBadRequest(err) { status = http.StatusBadRequest } c.JSON(status, gin.H{"error": err.Error()}) return } file, err := c.FormFile("image") if err == nil { if file.Size > 5*1024*1024 { c.JSON(http.StatusBadRequest, gin.H{"error": "File size exceeds 5MB limit"}) return } imageURL, saveErr := utils.SaveOptimizedImage(file, "./uploads/homes", home.ID.String(), &utils.ImageOptions{ Width: config.AppConfig.HomeImageWidth, Height: config.AppConfig.HomeImageHeight, Quality: float32(config.AppConfig.HomeImageQuality), Format: config.AppConfig.HomeImageFormat, Mode: config.AppConfig.HomeImageMode, }) if saveErr != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()}) return } updated, updateErr := h.homeService.UpdateHome( home.ID.String(), nil, nil, nil, nil, nil, nil, &imageURL, nil, nil, nil, ) if updateErr != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": updateErr.Error()}) return } home = updated } c.JSON(http.StatusCreated, home) } // UpdateHome godoc // @Summary Update a home (Admin) // @Description Update an existing home entry // @Tags admin // @Security ApiKeyAuth // @Accept multipart/form-data // @Produce json // @Param id path string true "Home ID" // @Param name formData string false "Name" // @Param title formData string false "Title" // @Param button1 formData string false "Button 1" // @Param button2 formData string false "Button 2" // @Param video formData string false "Video URL" // @Param keywords formData string false "Keywords" // @Param tag_ids formData []string false "Tag IDs" // @Param image formData file false "Home image" // @Param slug formData string false "Slug" // @Param is_active formData bool false "Is active" // @Success 200 {object} models.Home // @Failure 400 {object} map[string]string // @Failure 404 {object} map[string]string // @Router /admin/homes/{id} [put] func (h *HomeHandler) UpdateHome(c *gin.Context) { id := c.Param("id") name, hasName := getOptionalFormValue(c, "name") title, hasTitle := getOptionalFormValue(c, "title") button1, hasButton1 := getOptionalFormValue(c, "button1") button2, hasButton2 := getOptionalFormValue(c, "button2") video, hasVideo := getOptionalFormValue(c, "video") keywords, hasKeywords := getOptionalFormValue(c, "keywords") slug, hasSlug := getOptionalFormValue(c, "slug") var namePtr *string var titlePtr *string var button1Ptr *string var button2Ptr *string var videoPtr *string var keywordsPtr *string var slugPtr *string if hasName { namePtr = &name } if hasTitle { titlePtr = &title } if hasButton1 { button1Ptr = &button1 } if hasButton2 { button2Ptr = &button2 } if hasVideo { videoPtr = &video } if hasKeywords { keywordsPtr = &keywords } if hasSlug { slugPtr = &slug } 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 tagIDsPtr *[]string if hasTagIDs(c) { tagIDs := parseTagIDs(c) tagIDsPtr = &tagIDs } var imagePtr *string file, fileErr := c.FormFile("image") if fileErr == nil { if file.Size > 5*1024*1024 { c.JSON(http.StatusBadRequest, gin.H{"error": "File size exceeds 5MB limit"}) return } home, fetchErr := h.homeService.GetHomeByID(id) if fetchErr != nil { c.JSON(http.StatusNotFound, gin.H{"error": fetchErr.Error()}) return } if home.Image != "" && strings.HasPrefix(home.Image, "/uploads/") { oldPath := "." + home.Image _ = os.Remove(oldPath) } imageURL, saveErr := utils.SaveOptimizedImage(file, "./uploads/homes", id, &utils.ImageOptions{ Width: config.AppConfig.HomeImageWidth, Height: config.AppConfig.HomeImageHeight, Quality: float32(config.AppConfig.HomeImageQuality), Format: config.AppConfig.HomeImageFormat, Mode: config.AppConfig.HomeImageMode, }) if saveErr != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()}) return } imagePtr = &imageURL } home, err := h.homeService.UpdateHome( id, namePtr, titlePtr, button1Ptr, button2Ptr, videoPtr, keywordsPtr, imagePtr, slugPtr, tagIDsPtr, isActivePtr, ) if err != nil { status := http.StatusInternalServerError if err.Error() == "home not found" { status = http.StatusNotFound } else if isHomeBadRequest(err) { status = http.StatusBadRequest } c.JSON(status, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, home) } // DeleteHome godoc // @Summary Delete a home (Admin) // @Description Delete a home by ID // @Tags admin // @Security ApiKeyAuth // @Param id path string true "Home ID" // @Success 200 {object} map[string]string // @Failure 404 {object} map[string]string // @Router /admin/homes/{id} [delete] func (h *HomeHandler) DeleteHome(c *gin.Context) { id := c.Param("id") if err := h.homeService.DeleteHome(id); err != nil { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Home deleted successfully"}) } // AdminUploadHomeImage godoc // @Summary Upload home image (Admin) // @Description Upload an image for a specific home entry // @Tags admin // @Security ApiKeyAuth // @Accept multipart/form-data // @Produce json // @Param id path string true "Home ID" // @Param image formData file true "Home image" // @Success 200 {object} map[string]interface{} // @Failure 400 {object} map[string]string // @Failure 404 {object} map[string]string // @Router /admin/homes/{id}/image [post] func (h *HomeHandler) AdminUploadHomeImage(c *gin.Context) { id := c.Param("id") file, err := c.FormFile("image") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "No file uploaded"}) return } if file.Size > 5*1024*1024 { c.JSON(http.StatusBadRequest, gin.H{"error": "File size exceeds 5MB limit"}) return } home, err := h.homeService.GetHomeByID(id) if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": err.Error()}) return } if home.Image != "" && strings.HasPrefix(home.Image, "/uploads/") { oldPath := "." + home.Image _ = os.Remove(oldPath) } imageURL, err := utils.SaveOptimizedImage(file, "./uploads/homes", id, &utils.ImageOptions{ Width: config.AppConfig.HomeImageWidth, Height: config.AppConfig.HomeImageHeight, Quality: float32(config.AppConfig.HomeImageQuality), Format: config.AppConfig.HomeImageFormat, Mode: config.AppConfig.HomeImageMode, }) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + err.Error()}) return } updated, err := h.homeService.UpdateHome( id, nil, nil, nil, nil, nil, nil, &imageURL, nil, nil, nil, ) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "message": "Home image uploaded successfully", "image_url": imageURL, "home": updated, }) } func isHomeBadRequest(err error) bool { if err == nil { return false } switch err.Error() { case "slug already exists", "slug cannot be empty", "one or more tags not found": return true default: return false } } func parseTagIDs(c *gin.Context) []string { tagIDs := c.PostFormArray("tag_ids") if len(tagIDs) == 0 { raw := strings.TrimSpace(c.PostForm("tag_ids")) if raw == "" { return tagIDs } parts := strings.Split(raw, ",") for _, part := range parts { trimmed := strings.TrimSpace(part) if trimmed != "" { tagIDs = append(tagIDs, trimmed) } } } return tagIDs } func hasTagIDs(c *gin.Context) bool { if len(c.PostFormArray("tag_ids")) > 0 { return true } if raw := strings.TrimSpace(c.PostForm("tag_ids")); raw != "" { return true } return false }