501 lines
13 KiB
Go
501 lines
13 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"
|
|
)
|
|
|
|
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
|
|
}
|