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"}) }