turash/bugulma/backend/internal/handler/content_handler.go

520 lines
15 KiB
Go

package handler
import (
"net/http"
"strings"
"time"
"bugulma/backend/internal/domain"
"bugulma/backend/internal/service"
"github.com/gin-gonic/gin"
)
type ContentHandler struct {
contentService *service.ContentService
}
func NewContentHandler(contentService *service.ContentService) *ContentHandler {
return &ContentHandler{
contentService: contentService,
}
}
// Static Pages
// ListPages lists static pages (admin only)
// @Summary List static pages
// @Tags admin
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/admin/content/pages [get]
func (h *ContentHandler) ListPages(c *gin.Context) {
pages, err := h.contentService.ListPages(c.Request.Context(), nil)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"pages": pages})
}
// GetPage gets a page by ID (admin only)
// @Summary Get page
// @Tags admin
// @Produce json
// @Param id path string true "Page ID"
// @Success 200 {object} domain.StaticPage
// @Router /api/v1/admin/content/pages/:id [get]
func (h *ContentHandler) GetPage(c *gin.Context) {
id := c.Param("id")
page, err := h.contentService.GetPage(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Page not found"})
return
}
c.JSON(http.StatusOK, page)
}
// CreatePage creates a new page (admin only)
// @Summary Create page
// @Tags admin
// @Accept json
// @Produce json
// @Param request body CreatePageRequest true "Page request"
// @Success 201 {object} domain.StaticPage
// @Router /api/v1/admin/content/pages [post]
func (h *ContentHandler) CreatePage(c *gin.Context) {
var req CreatePageRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userID, _ := c.Get("user_id")
page := &domain.StaticPage{
Slug: req.Slug,
Title: req.Title,
Content: req.Content,
MetaDescription: req.MetaDescription,
Status: domain.PageStatus(req.Status),
Visibility: domain.PageVisibility(req.Visibility),
Template: req.Template,
CreatedBy: userID.(string),
UpdatedBy: userID.(string),
}
if err := h.contentService.CreatePage(c.Request.Context(), page); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, page)
}
type CreatePageRequest struct {
Slug string `json:"slug" binding:"required"`
Title string `json:"title" binding:"required"`
Content string `json:"content"`
MetaDescription string `json:"metaDescription"`
SEOKeywords []string `json:"seoKeywords"`
Status string `json:"status"`
Visibility string `json:"visibility"`
Template string `json:"template"`
}
// UpdatePage updates a page (admin only)
// @Summary Update page
// @Tags admin
// @Accept json
// @Produce json
// @Param id path string true "Page ID"
// @Param request body UpdatePageRequest true "Update request"
// @Success 200 {object} domain.StaticPage
// @Router /api/v1/admin/content/pages/:id [put]
func (h *ContentHandler) UpdatePage(c *gin.Context) {
id := c.Param("id")
var req UpdatePageRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
page, err := h.contentService.GetPage(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Page not found"})
return
}
userID, _ := c.Get("user_id")
if req.Title != nil {
page.Title = *req.Title
}
if req.Content != nil {
page.Content = *req.Content
}
if req.MetaDescription != nil {
page.MetaDescription = *req.MetaDescription
}
if req.Status != nil {
page.Status = domain.PageStatus(*req.Status)
}
if req.Visibility != nil {
page.Visibility = domain.PageVisibility(*req.Visibility)
}
page.UpdatedBy = userID.(string)
if err := h.contentService.UpdatePage(c.Request.Context(), page); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, page)
}
type UpdatePageRequest struct {
Title *string `json:"title"`
Content *string `json:"content"`
MetaDescription *string `json:"metaDescription"`
Status *string `json:"status"`
Visibility *string `json:"visibility"`
}
// DeletePage deletes a page (admin only)
// @Summary Delete page
// @Tags admin
// @Success 200 {object} map[string]string
// @Router /api/v1/admin/content/pages/:id [delete]
func (h *ContentHandler) DeletePage(c *gin.Context) {
id := c.Param("id")
if err := h.contentService.DeletePage(c.Request.Context(), id); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Page not found"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Page deleted"})
}
// PublishPage publishes a page (admin only)
// @Summary Publish page
// @Tags admin
// @Success 200 {object} domain.StaticPage
// @Router /api/v1/admin/content/pages/:id/publish [post]
func (h *ContentHandler) PublishPage(c *gin.Context) {
id := c.Param("id")
if err := h.contentService.PublishPage(c.Request.Context(), id); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Page not found"})
return
}
page, _ := h.contentService.GetPage(c.Request.Context(), id)
c.JSON(http.StatusOK, page)
}
// Announcements
// ListAnnouncements lists announcements (admin only)
// @Summary List announcements
// @Tags admin
// @Produce json
// @Param isActive query bool false "Filter by active status"
// @Param priority query string false "Filter by priority"
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/admin/content/announcements [get]
func (h *ContentHandler) ListAnnouncements(c *gin.Context) {
filters := domain.AnnouncementFilters{}
if isActiveStr := c.Query("isActive"); isActiveStr != "" {
isActive := isActiveStr == "true"
filters.IsActive = &isActive
}
if priorityStr := c.Query("priority"); priorityStr != "" {
priority := domain.AnnouncementPriority(priorityStr)
filters.Priority = &priority
}
announcements, err := h.contentService.ListAnnouncements(c.Request.Context(), filters)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"announcements": announcements})
}
// GetAnnouncement gets an announcement by ID (admin only)
// @Summary Get announcement
// @Tags admin
// @Produce json
// @Param id path string true "Announcement ID"
// @Success 200 {object} domain.Announcement
// @Router /api/v1/admin/content/announcements/:id [get]
func (h *ContentHandler) GetAnnouncement(c *gin.Context) {
id := c.Param("id")
announcement, err := h.contentService.GetAnnouncement(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Announcement not found"})
return
}
c.JSON(http.StatusOK, announcement)
}
// CreateAnnouncement creates a new announcement (admin only)
// @Summary Create announcement
// @Tags admin
// @Accept json
// @Produce json
// @Param request body CreateAnnouncementRequest true "Announcement request"
// @Success 201 {object} domain.Announcement
// @Router /api/v1/admin/content/announcements [post]
func (h *ContentHandler) CreateAnnouncement(c *gin.Context) {
var req CreateAnnouncementRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userID, _ := c.Get("user_id")
announcement := &domain.Announcement{
Title: req.Title,
Content: req.Content,
Priority: domain.AnnouncementPriority(req.Priority),
DisplayType: domain.AnnouncementDisplayType(req.DisplayType),
TargetAudience: domain.AnnouncementTargetAudience(req.TargetAudience),
StartDate: req.StartDate,
EndDate: req.EndDate,
IsActive: req.IsActive,
CreatedBy: userID.(string),
UpdatedBy: userID.(string),
}
if err := h.contentService.CreateAnnouncement(c.Request.Context(), announcement); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, announcement)
}
type CreateAnnouncementRequest struct {
Title string `json:"title" binding:"required"`
Content string `json:"content" binding:"required"`
Priority string `json:"priority"`
DisplayType string `json:"displayType"`
TargetAudience string `json:"targetAudience"`
StartDate *time.Time `json:"startDate"`
EndDate *time.Time `json:"endDate"`
IsActive bool `json:"isActive"`
}
// UpdateAnnouncement updates an announcement (admin only)
// @Summary Update announcement
// @Tags admin
// @Accept json
// @Produce json
// @Param id path string true "Announcement ID"
// @Param request body UpdateAnnouncementRequest true "Update request"
// @Success 200 {object} domain.Announcement
// @Router /api/v1/admin/content/announcements/:id [put]
func (h *ContentHandler) UpdateAnnouncement(c *gin.Context) {
id := c.Param("id")
var req UpdateAnnouncementRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
announcement, err := h.contentService.GetAnnouncement(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Announcement not found"})
return
}
userID, _ := c.Get("user_id")
if req.Title != nil {
announcement.Title = *req.Title
}
if req.Content != nil {
announcement.Content = *req.Content
}
if req.Priority != nil {
announcement.Priority = domain.AnnouncementPriority(*req.Priority)
}
if req.IsActive != nil {
announcement.IsActive = *req.IsActive
}
announcement.UpdatedBy = userID.(string)
if err := h.contentService.UpdateAnnouncement(c.Request.Context(), announcement); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, announcement)
}
type UpdateAnnouncementRequest struct {
Title *string `json:"title"`
Content *string `json:"content"`
Priority *string `json:"priority"`
IsActive *bool `json:"isActive"`
}
// DeleteAnnouncement deletes an announcement (admin only)
// @Summary Delete announcement
// @Tags admin
// @Success 200 {object} map[string]string
// @Router /api/v1/admin/content/announcements/:id [delete]
func (h *ContentHandler) DeleteAnnouncement(c *gin.Context) {
id := c.Param("id")
if err := h.contentService.DeleteAnnouncement(c.Request.Context(), id); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Announcement not found"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Announcement deleted"})
}
// Media Assets
// ListMediaAssets lists media assets (admin only)
// @Summary List media assets
// @Tags admin
// @Produce json
// @Param type query string false "Filter by type"
// @Param tags query string false "Filter by tags (comma-separated)"
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/admin/content/media [get]
func (h *ContentHandler) ListMediaAssets(c *gin.Context) {
filters := domain.MediaAssetFilters{}
if typeStr := c.Query("type"); typeStr != "" {
assetType := domain.MediaAssetType(typeStr)
filters.Type = &assetType
}
if tagsStr := c.Query("tags"); tagsStr != "" {
// Parse comma-separated tags
tags := strings.Split(tagsStr, ",")
for i := range tags {
tags[i] = strings.TrimSpace(tags[i])
}
filters.Tags = tags
}
assets, err := h.contentService.ListMediaAssets(c.Request.Context(), filters)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"assets": assets})
}
// GetMediaAsset gets a media asset by ID (admin only)
// @Summary Get media asset
// @Tags admin
// @Produce json
// @Param id path string true "Media Asset ID"
// @Success 200 {object} domain.MediaAsset
// @Router /api/v1/admin/content/media/:id [get]
func (h *ContentHandler) GetMediaAsset(c *gin.Context) {
id := c.Param("id")
asset, err := h.contentService.GetMediaAsset(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Media asset not found"})
return
}
c.JSON(http.StatusOK, asset)
}
// CreateMediaAsset creates a new media asset (admin only)
// @Summary Create media asset
// @Tags admin
// @Accept json
// @Produce json
// @Param request body CreateMediaAssetRequest true "Media asset request"
// @Success 201 {object} domain.MediaAsset
// @Router /api/v1/admin/content/media [post]
func (h *ContentHandler) CreateMediaAsset(c *gin.Context) {
var req CreateMediaAssetRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userID, _ := c.Get("user_id")
asset := &domain.MediaAsset{
Filename: req.Filename,
OriginalName: req.OriginalName,
URL: req.URL,
Type: domain.MediaAssetType(req.Type),
MimeType: req.MimeType,
Size: req.Size,
Width: req.Width,
Height: req.Height,
Duration: req.Duration,
AltText: req.AltText,
UploadedBy: userID.(string),
}
if err := h.contentService.CreateMediaAsset(c.Request.Context(), asset); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, asset)
}
type CreateMediaAssetRequest struct {
Filename string `json:"filename" binding:"required"`
OriginalName string `json:"originalName" binding:"required"`
URL string `json:"url" binding:"required"`
Type string `json:"type" binding:"required"`
MimeType string `json:"mimeType"`
Size int64 `json:"size"`
Width *int `json:"width"`
Height *int `json:"height"`
Duration *int `json:"duration"`
AltText string `json:"altText"`
Tags []string `json:"tags"`
}
// UpdateMediaAsset updates a media asset (admin only)
// @Summary Update media asset
// @Tags admin
// @Accept json
// @Produce json
// @Param id path string true "Media Asset ID"
// @Param request body UpdateMediaAssetRequest true "Update request"
// @Success 200 {object} domain.MediaAsset
// @Router /api/v1/admin/content/media/:id [put]
func (h *ContentHandler) UpdateMediaAsset(c *gin.Context) {
id := c.Param("id")
var req UpdateMediaAssetRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
asset, err := h.contentService.GetMediaAsset(c.Request.Context(), id)
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Media asset not found"})
return
}
if req.AltText != nil {
asset.AltText = *req.AltText
}
if req.Tags != nil {
// Tags would need JSON marshaling
}
if err := h.contentService.UpdateMediaAsset(c.Request.Context(), asset); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, asset)
}
type UpdateMediaAssetRequest struct {
AltText *string `json:"altText"`
Tags *[]string `json:"tags"`
}
// DeleteMediaAsset deletes a media asset (admin only)
// @Summary Delete media asset
// @Tags admin
// @Success 200 {object} map[string]string
// @Router /api/v1/admin/content/media/:id [delete]
func (h *ContentHandler) DeleteMediaAsset(c *gin.Context) {
id := c.Param("id")
if err := h.contentService.DeleteMediaAsset(c.Request.Context(), id); err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Media asset not found"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Media asset deleted"})
}