Files
atahango/api/handlers/post_category_handler.go
Beyhan Oğur bbbf76b184 first commit
2026-04-26 21:35:24 +03:00

360 lines
10 KiB
Go

package handlers
import (
"net/http"
"os"
"strconv"
"strings"
"gauth-central/config"
"gauth-central/internal/services"
"gauth-central/pkg/utils"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type PostCategoryHandler struct {
categoryService *services.PostCategoryService
}
func NewPostCategoryHandler(categoryService *services.PostCategoryService) *PostCategoryHandler {
return &PostCategoryHandler{categoryService: categoryService}
}
// GetAllPostCategories godoc
// @Summary Get all active post categories
// @Description Retrieve a list of active post categories
// @Tags post-categories
// @Produce json
// @Success 200 {array} models.PostCategory
// @Failure 500 {object} map[string]string
// @Router /post-categories [get]
// GetAllPostCategories returns active post categories.
func (h *PostCategoryHandler) GetAllPostCategories(c *gin.Context) {
items, err := h.categoryService.GetAllPostCategories(true)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, items)
}
// GetPostCategoryBySlug godoc
// @Summary Get post category by slug
// @Description Retrieve an active post category by slug
// @Tags post-categories
// @Produce json
// @Param slug path string true "Category Slug"
// @Success 200 {object} models.PostCategory
// @Failure 404 {object} map[string]string
// @Router /post-categories/{slug} [get]
// GetPostCategoryBySlug returns a post category by slug.
func (h *PostCategoryHandler) GetPostCategoryBySlug(c *gin.Context) {
slug := c.Param("slug")
item, err := h.categoryService.GetPostCategoryBySlug(slug, true)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, item)
}
// AdminGetAllPostCategories godoc
// @Summary Get all post categories (Admin)
// @Description Retrieve a list of all post categories including inactive ones
// @Tags admin
// @Security ApiKeyAuth
// @Produce json
// @Success 200 {array} models.PostCategory
// @Failure 500 {object} map[string]string
// @Router /admin/post-categories [get]
// AdminGetAllPostCategories returns all categories.
func (h *PostCategoryHandler) AdminGetAllPostCategories(c *gin.Context) {
items, err := h.categoryService.GetAllPostCategories(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, items)
}
// AdminGetPostCategoryByID godoc
// @Summary Get post category by ID (Admin)
// @Description Retrieve a post category by ID
// @Tags admin
// @Security ApiKeyAuth
// @Produce json
// @Param id path string true "Category ID"
// @Success 200 {object} models.PostCategory
// @Failure 404 {object} map[string]string
// @Router /admin/post-categories/{id} [get]
// AdminGetPostCategoryByID returns a category by ID.
func (h *PostCategoryHandler) AdminGetPostCategoryByID(c *gin.Context) {
id := c.Param("id")
item, err := h.categoryService.GetPostCategoryByID(id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, item)
}
// CreatePostCategory godoc
// @Summary Create a post category (Admin)
// @Description Create a new post category
// @Tags admin
// @Security ApiKeyAuth
// @Accept multipart/form-data
// @Produce json
// @Param title formData string true "Title"
// @Param keywords formData string true "Keywords"
// @Param description formData string true "Description"
// @Param order formData int false "Order"
// @Param parent_id formData string false "Parent ID"
// @Param image formData file false "Image"
// @Param is_active formData bool false "Is active"
// @Success 201 {object} models.PostCategory
// @Failure 400 {object} map[string]string
// @Router /admin/post-categories [post]
// CreatePostCategory creates a new category.
func (h *PostCategoryHandler) CreatePostCategory(c *gin.Context) {
title := strings.TrimSpace(c.PostForm("title"))
keywords := strings.TrimSpace(c.PostForm("keywords"))
description := strings.TrimSpace(c.PostForm("description"))
if title == "" || keywords == "" || description == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "title, keywords, and description are required"})
return
}
order := 1
if rawOrder := strings.TrimSpace(c.PostForm("order")); rawOrder != "" {
parsed, err := strconv.Atoi(rawOrder)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "order must be an integer"})
return
}
order = parsed
}
parentID, err := parseUUIDPtr(c.PostForm("parent_id"))
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parent_id"})
return
}
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
}
imageURL := ""
file, fileErr := c.FormFile("image")
if fileErr == nil {
if file.Size > 5*1024*1024 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Image size exceeds 5MB limit"})
return
}
saved, saveErr := utils.SaveOptimizedImage(file, "./uploads/post-categories", "category", &utils.ImageOptions{
Width: config.AppConfig.PostCategoryImageWidth,
Height: config.AppConfig.PostCategoryImageHeight,
Quality: float32(config.AppConfig.PostCategoryImageQuality),
Format: config.AppConfig.PostCategoryImageFormat,
Mode: config.AppConfig.PostCategoryImageMode,
})
if saveErr != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()})
return
}
imageURL = saved
}
item, err := h.categoryService.CreatePostCategory(
title,
keywords,
description,
imageURL,
order,
parentID,
isActive,
)
if err != nil {
status := http.StatusInternalServerError
if err.Error() == "slug cannot be empty" || err.Error() == "slug already exists" {
status = http.StatusBadRequest
}
c.JSON(status, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, item)
}
// UpdatePostCategory godoc
// @Summary Update a post category (Admin)
// @Description Update an existing post category
// @Tags admin
// @Security ApiKeyAuth
// @Accept multipart/form-data
// @Produce json
// @Param id path string true "Category ID"
// @Param title formData string false "Title"
// @Param keywords formData string false "Keywords"
// @Param description formData string false "Description"
// @Param order formData int false "Order"
// @Param parent_id formData string false "Parent ID"
// @Param image formData file false "Image"
// @Param slug formData string false "Slug"
// @Param is_active formData bool false "Is active"
// @Success 200 {object} models.PostCategory
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /admin/post-categories/{id} [put]
// UpdatePostCategory updates a category.
func (h *PostCategoryHandler) UpdatePostCategory(c *gin.Context) {
id := c.Param("id")
title, hasTitle := getOptionalFormValue(c, "title")
keywords, hasKeywords := getOptionalFormValue(c, "keywords")
description, hasDescription := getOptionalFormValue(c, "description")
slug, hasSlug := getOptionalFormValue(c, "slug")
var titlePtr *string
var keywordsPtr *string
var descriptionPtr *string
var slugPtr *string
if hasTitle {
titlePtr = &title
}
if hasKeywords {
keywordsPtr = &keywords
}
if hasDescription {
descriptionPtr = &description
}
if hasSlug {
slugPtr = &slug
}
var orderPtr *int
if rawOrder, hasOrder := getOptionalFormValue(c, "order"); hasOrder {
if rawOrder == "" {
orderPtr = nil
} else {
parsed, err := strconv.Atoi(rawOrder)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "order must be an integer"})
return
}
orderPtr = &parsed
}
}
parentIDValue, hasParent := getOptionalFormValue(c, "parent_id")
parentIDSet := false
var parentIDPtr *uuid.UUID
if hasParent {
parentIDSet = true
if parentIDValue == "" {
parentIDPtr = nil
} else {
parsed, err := parseUUIDPtr(parentIDValue)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid parent_id"})
return
}
parentIDPtr = parsed
}
}
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
file, fileErr := c.FormFile("image")
if fileErr == nil {
if file.Size > 5*1024*1024 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Image size exceeds 5MB limit"})
return
}
item, fetchErr := h.categoryService.GetPostCategoryByID(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)
}
saved, saveErr := utils.SaveOptimizedImage(file, "./uploads/post-categories", id, &utils.ImageOptions{
Width: config.AppConfig.PostCategoryImageWidth,
Height: config.AppConfig.PostCategoryImageHeight,
Quality: float32(config.AppConfig.PostCategoryImageQuality),
Format: config.AppConfig.PostCategoryImageFormat,
Mode: config.AppConfig.PostCategoryImageMode,
})
if saveErr != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()})
return
}
imagePtr = &saved
}
item, err := h.categoryService.UpdatePostCategory(
id,
titlePtr,
keywordsPtr,
descriptionPtr,
imagePtr,
orderPtr,
parentIDPtr,
parentIDSet,
slugPtr,
isActivePtr,
)
if err != nil {
status := http.StatusInternalServerError
switch err.Error() {
case "post category not found":
status = http.StatusNotFound
case "slug cannot be empty", "slug already exists":
status = http.StatusBadRequest
}
c.JSON(status, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, item)
}
// DeletePostCategory godoc
// @Summary Delete a post category (Admin)
// @Description Delete a post category by ID
// @Tags admin
// @Security ApiKeyAuth
// @Param id path string true "Category ID"
// @Success 200 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /admin/post-categories/{id} [delete]
// DeletePostCategory deletes a category by ID.
func (h *PostCategoryHandler) DeletePostCategory(c *gin.Context) {
id := c.Param("id")
if err := h.categoryService.DeletePostCategory(id); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Post category deleted successfully"})
}