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

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
}