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

288 lines
7.9 KiB
Go

package handlers
import (
"net/http"
"os"
"strings"
"gauth-central/config"
"gauth-central/internal/services"
"gauth-central/pkg/utils"
"github.com/gin-gonic/gin"
)
type ServiceHandler struct {
serviceService *services.ServiceService
}
func NewServiceHandler(serviceService *services.ServiceService) *ServiceHandler {
return &ServiceHandler{serviceService: serviceService}
}
// GetAllServices godoc
// @Summary Get all active services
// @Description Retrieve a list of active services
// @Tags services
// @Produce json
// @Success 200 {array} models.Service
// @Failure 500 {object} map[string]string
// @Router /services [get]
func (h *ServiceHandler) GetAllServices(c *gin.Context) {
items, err := h.serviceService.GetAllServices(true)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, items)
}
// GetServiceBySlug godoc
// @Summary Get service by slug
// @Description Retrieve a single active service by slug
// @Tags services
// @Produce json
// @Param slug path string true "Service Slug"
// @Success 200 {object} models.Service
// @Failure 404 {object} map[string]string
// @Router /services/{slug} [get]
func (h *ServiceHandler) GetServiceBySlug(c *gin.Context) {
slug := c.Param("slug")
item, err := h.serviceService.GetServiceBySlug(slug, true)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, item)
}
// AdminGetAllServices godoc
// @Summary Get all services (Admin)
// @Description Retrieve a list of all services including inactive ones
// @Tags admin
// @Security ApiKeyAuth
// @Produce json
// @Success 200 {array} models.Service
// @Failure 500 {object} map[string]string
// @Router /admin/services [get]
func (h *ServiceHandler) AdminGetAllServices(c *gin.Context) {
items, err := h.serviceService.GetAllServices(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, items)
}
// AdminGetServiceByID godoc
// @Summary Get a service by ID (Admin)
// @Description Retrieve details of a specific service
// @Tags admin
// @Security ApiKeyAuth
// @Produce json
// @Param id path string true "Service ID"
// @Success 200 {object} models.Service
// @Failure 404 {object} map[string]string
// @Router /admin/services/{id} [get]
func (h *ServiceHandler) AdminGetServiceByID(c *gin.Context) {
id := c.Param("id")
item, err := h.serviceService.GetServiceByID(id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, item)
}
// CreateService godoc
// @Summary Create a new service (Admin)
// @Description Create a new service entry
// @Tags admin
// @Security ApiKeyAuth
// @Accept multipart/form-data
// @Produce json
// @Param title formData string true "Title"
// @Param content formData string false "Content"
// @Param image formData file false "Image"
// @Param is_active formData bool false "Is active"
// @Success 201 {object} models.Service
// @Failure 400 {object} map[string]string
// @Router /admin/services [post]
func (h *ServiceHandler) CreateService(c *gin.Context) {
title := strings.TrimSpace(c.PostForm("title"))
content := strings.TrimSpace(c.PostForm("content"))
if title == "" {
c.JSON(http.StatusBadRequest, gin.H{"error": "title is required"})
return
}
isActive := false
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
}
item, err := h.serviceService.CreateService(title, content, "", isActive)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
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
}
imageURL, saveErr := utils.SaveOptimizedImage(file, "./uploads/services", item.ID.String(), &utils.ImageOptions{
Width: config.AppConfig.ServiceImageWidth,
Height: config.AppConfig.ServiceImageHeight,
Quality: float32(config.AppConfig.ServiceImageQuality),
Format: config.AppConfig.ServiceImageFormat,
Mode: config.AppConfig.ServiceImageMode,
})
if saveErr != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()})
return
}
updated, updateErr := h.serviceService.UpdateService(
item.ID.String(),
nil,
nil,
&imageURL,
nil,
nil,
)
if updateErr != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": updateErr.Error()})
return
}
item = updated
}
c.JSON(http.StatusCreated, item)
}
// UpdateService godoc
// @Summary Update a service (Admin)
// @Description Update an existing service entry
// @Tags admin
// @Security ApiKeyAuth
// @Accept multipart/form-data
// @Produce json
// @Param id path string true "Service ID"
// @Param title formData string false "Title"
// @Param content formData string false "Content"
// @Param image formData file false "Image"
// @Param slug formData string false "Slug"
// @Param is_active formData bool false "Is active"
// @Success 200 {object} models.Service
// @Failure 400 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /admin/services/{id} [put]
func (h *ServiceHandler) UpdateService(c *gin.Context) {
id := c.Param("id")
title, hasTitle := getOptionalFormValue(c, "title")
content, hasContent := getOptionalFormValue(c, "content")
slug, hasSlug := getOptionalFormValue(c, "slug")
var titlePtr *string
var contentPtr *string
var slugPtr *string
if hasTitle {
titlePtr = &title
}
if hasContent {
contentPtr = &content
}
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 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.serviceService.GetServiceByID(id)
if fetchErr != nil {
c.JSON(http.StatusNotFound, gin.H{"error": fetchErr.Error()})
return
}
if item.Image != "" && strings.HasPrefix(item.Image, "/uploads/") {
oldPath := "." + item.Image
_ = os.Remove(oldPath)
}
imageURL, saveErr := utils.SaveOptimizedImage(file, "./uploads/services", id, &utils.ImageOptions{
Width: config.AppConfig.ServiceImageWidth,
Height: config.AppConfig.ServiceImageHeight,
Quality: float32(config.AppConfig.ServiceImageQuality),
Format: config.AppConfig.ServiceImageFormat,
Mode: config.AppConfig.ServiceImageMode,
})
if saveErr != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save image: " + saveErr.Error()})
return
}
imagePtr = &imageURL
}
item, err := h.serviceService.UpdateService(
id,
titlePtr,
contentPtr,
imagePtr,
slugPtr,
isActivePtr,
)
if err != nil {
status := http.StatusInternalServerError
if err.Error() == "service not found" {
status = http.StatusNotFound
} else if err.Error() == "slug already exists" || err.Error() == "slug cannot be empty" {
status = http.StatusBadRequest
}
c.JSON(status, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, item)
}
// DeleteService godoc
// @Summary Delete a service (Admin)
// @Description Delete a service by ID
// @Tags admin
// @Security ApiKeyAuth
// @Param id path string true "Service ID"
// @Success 200 {object} map[string]string
// @Failure 404 {object} map[string]string
// @Router /admin/services/{id} [delete]
func (h *ServiceHandler) DeleteService(c *gin.Context) {
id := c.Param("id")
if err := h.serviceService.DeleteService(id); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Service deleted successfully"})
}