package handlers import ( "gobeyhan/app/blog/services" "gobeyhan/database/models" "net/http" "strconv" "github.com/gin-gonic/gin" ) type PostHandler struct { service *services.PostService } func NewPostHandler(service *services.PostService) *PostHandler { return &PostHandler{service: service} } // GetAllPosts godoc // @Summary Get all active posts // @Description Get paginated list of active posts (public endpoint) // @Tags posts // @Accept json // @Produce json // @Param page query int false "Page number" default(1) // @Param limit query int false "Items per page" default(10) // @Success 200 {object} map[string]interface{} // @Router /api/v1/posts [get] func (h *PostHandler) GetAllPosts(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10")) if page < 1 { page = 1 } if limit < 1 || limit > 100 { limit = 10 } posts, total, err := h.service.GetAllPosts(page, limit, true) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "data": posts, "total": total, "page": page, "limit": limit, }) } // GetPostBySlug godoc // @Summary Get post by slug // @Description Get a single post by its slug (public endpoint) // @Tags posts // @Accept json // @Produce json // @Param slug path string true "Post Slug" // @Success 200 {object} models.Post // @Router /api/v1/posts/{slug} [get] func (h *PostHandler) GetPostBySlug(c *gin.Context) { slug := c.Param("slug") post, err := h.service.GetPostBySlug(slug) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if post == nil { c.JSON(http.StatusNotFound, gin.H{"error": "Post not found"}) return } c.JSON(http.StatusOK, gin.H{"data": post}) } // AdminGetAllPosts godoc // @Summary Get all posts (Admin) // @Description Get paginated list of all posts including inactive // @Tags admin,posts // @Accept json // @Produce json // @Security BearerAuth // @Param page query int false "Page number" default(1) // @Param limit query int false "Items per page" default(10) // @Success 200 {object} map[string]interface{} // @Router /api/v1/admin/posts [get] func (h *PostHandler) AdminGetAllPosts(c *gin.Context) { page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10")) if page < 1 { page = 1 } if limit < 1 || limit > 100 { limit = 10 } posts, total, err := h.service.GetAllPosts(page, limit, false) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{ "data": posts, "total": total, "page": page, "limit": limit, }) } // GetPostByID godoc // @Summary Get post by ID (Admin) // @Description Get a single post by ID // @Tags admin,posts // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "Post ID" // @Success 200 {object} models.Post // @Router /api/v1/admin/posts/{id} [get] func (h *PostHandler) GetPostByID(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid post ID"}) return } post, err := h.service.GetPostByID(id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } if post == nil { c.JSON(http.StatusNotFound, gin.H{"error": "Post not found"}) return } c.JSON(http.StatusOK, gin.H{"data": post}) } // CreatePost godoc // @Summary Create a new post (Admin) // @Description Create a new post // @Tags admin,posts // @Accept json // @Produce json // @Security BearerAuth // @Param post body models.Post true "Post object" // @Success 201 {object} models.Post // @Router /api/v1/admin/posts [post] func (h *PostHandler) CreatePost(c *gin.Context) { var input struct { Title string `json:"title" binding:"required"` UserID *uint64 `json:"user_id"` Content string `json:"content"` Keywords string `json:"keywords"` Image string `json:"image"` Thumb string `json:"thumb"` Video string `json:"video"` Slug string `json:"slug"` IsActive *bool `json:"is_active"` IsFront *bool `json:"is_front"` ParentID *uint64 `json:"parent_id"` Categories []uint64 `json:"categories"` Tags []uint64 `json:"tags"` } if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } post := &models.Post{ Title: input.Title, UserID: input.UserID, Content: input.Content, Keywords: input.Keywords, Image: input.Image, Thumb: input.Thumb, Video: input.Video, Slug: input.Slug, ParentID: input.ParentID, } if input.IsActive != nil { post.IsActive = *input.IsActive } else { post.IsActive = true } if input.IsFront != nil { post.IsFront = *input.IsFront } else { post.IsFront = true } // Create post first if err := h.service.CreatePost(post); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Add categories and tags if provided if len(input.Categories) > 0 || len(input.Tags) > 0 { updates := make(map[string]interface{}) if len(input.Categories) > 0 { var categories []*models.Category for _, catID := range input.Categories { categories = append(categories, &models.Category{ID: catID}) } updates["categories"] = categories } if len(input.Tags) > 0 { var tags []*models.Tag for _, tagID := range input.Tags { tags = append(tags, &models.Tag{ID: tagID}) } updates["tags"] = tags } if err := h.service.UpdatePost(post.ID, updates); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } } // Fetch created post with relationships createdPost, _ := h.service.GetPostByID(post.ID) c.JSON(http.StatusCreated, gin.H{"data": createdPost}) } // UpdatePost godoc // @Summary Update a post (Admin) // @Description Update an existing post // @Tags admin,posts // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "Post ID" // @Param post body models.Post true "Post object" // @Success 200 {object} models.Post // @Router /api/v1/admin/posts/{id} [put] func (h *PostHandler) UpdatePost(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid post ID"}) return } var input map[string]interface{} if err := c.ShouldBindJSON(&input); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := h.service.UpdatePost(id, input); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // Fetch updated post post, err := h.service.GetPostByID(id) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"data": post}) } // DeletePost godoc // @Summary Delete a post (Admin) // @Description Delete a post by ID // @Tags admin,posts // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "Post ID" // @Success 200 {object} map[string]string // @Router /api/v1/admin/posts/{id} [delete] func (h *PostHandler) DeletePost(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid post ID"}) return } if err := h.service.DeletePost(id); err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "Post deleted successfully"}) }