288 lines
7.9 KiB
Go
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"})
|
|
}
|