first commit
This commit is contained in:
537
internal/handler/admin/blog_handler.go
Normal file
537
internal/handler/admin/blog_handler.go
Normal file
@@ -0,0 +1,537 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"gobeyhan/config"
|
||||
"gobeyhan/database"
|
||||
"gobeyhan/database/models"
|
||||
"gobeyhan/pkg/utils"
|
||||
"gobeyhan/views/admin/blog"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type BlogHandler struct{}
|
||||
|
||||
func NewBlogHandler() *BlogHandler {
|
||||
return &BlogHandler{}
|
||||
}
|
||||
|
||||
// List displays all blog posts
|
||||
func (h *BlogHandler) List(c *gin.Context) {
|
||||
var posts []models.Post
|
||||
err := database.DB.
|
||||
Preload("User").
|
||||
Preload("Categories").
|
||||
Preload("Tags").
|
||||
Order("created_at DESC").
|
||||
Find(&posts).Error
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[BlogHandler.List] Error fetching posts: %v", err)
|
||||
c.String(http.StatusInternalServerError, "Error fetching posts")
|
||||
return
|
||||
}
|
||||
|
||||
blog.List(posts).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
// New displays the create form
|
||||
func (h *BlogHandler) New(c *gin.Context) {
|
||||
var categories []models.Category
|
||||
var tags []models.Tag
|
||||
|
||||
database.DB.Where("is_active = ?", true).Find(&categories)
|
||||
database.DB.Where("is_active = ?", true).Find(&tags)
|
||||
|
||||
blog.Create(categories, tags, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
// Create handles post creation
|
||||
func (h *BlogHandler) Create(c *gin.Context) {
|
||||
title := c.PostForm("title")
|
||||
content := c.PostForm("content")
|
||||
keywords := c.PostForm("keywords")
|
||||
isActive := c.PostForm("is_active") == "on"
|
||||
isFront := c.PostForm("is_front") == "on"
|
||||
|
||||
log.Printf("[BlogHandler.Create] Received: Title=%s, ContentSize=%d, Active=%v", title, len(content), isActive)
|
||||
|
||||
// Validation
|
||||
errors := make(map[string]string)
|
||||
if title == "" {
|
||||
errors["title"] = "Title is required"
|
||||
}
|
||||
if content == "" {
|
||||
errors["content"] = "Content is required"
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
log.Printf("[BlogHandler.Create] Validation failed: %v", errors)
|
||||
var categories []models.Category
|
||||
var tags []models.Tag
|
||||
database.DB.Where("is_active = ?", true).Find(&categories)
|
||||
database.DB.Where("is_active = ?", true).Find(&tags)
|
||||
blog.Create(categories, tags, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle image upload
|
||||
imagePath := c.PostForm("image") // Fallback to manual link if provided
|
||||
file, err := c.FormFile("image_file")
|
||||
if err == nil && file != nil {
|
||||
opts := &utils.ImageOptions{
|
||||
Width: config.AppConfig.PostImageWidth,
|
||||
Height: config.AppConfig.PostImageHeight,
|
||||
Quality: float32(config.AppConfig.PostImageQuality),
|
||||
Format: config.AppConfig.PostImageFormat,
|
||||
Mode: config.AppConfig.PostImageMode,
|
||||
}
|
||||
path, err := utils.SaveOptimizedImage(file, filepath.Join("uploads", "blog"), "post", opts)
|
||||
if err == nil {
|
||||
imagePath = path
|
||||
log.Printf("[BlogHandler.Create] Image saved to: %s", imagePath)
|
||||
} else {
|
||||
log.Printf("[BlogHandler.Create] Image upload error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Create post
|
||||
post := models.Post{
|
||||
Title: title,
|
||||
Content: content,
|
||||
Keywords: keywords,
|
||||
Image: imagePath,
|
||||
IsActive: isActive,
|
||||
IsFront: isFront,
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&post).Error; err != nil {
|
||||
log.Printf("[BlogHandler.Create] DB Create error: %v", err)
|
||||
errors["general"] = "Error creating post: " + err.Error()
|
||||
var categories []models.Category
|
||||
var tags []models.Tag
|
||||
database.DB.Where("is_active = ?", true).Find(&categories)
|
||||
database.DB.Where("is_active = ?", true).Find(&tags)
|
||||
blog.Create(categories, tags, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[BlogHandler.Create] Post created successfully: ID=%d", post.ID)
|
||||
|
||||
// Handle categories
|
||||
categoryIDs := c.PostFormArray("category_ids")
|
||||
if len(categoryIDs) > 0 {
|
||||
var categories []*models.Category
|
||||
for _, idStr := range categoryIDs {
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
categories = append(categories, &models.Category{ID: id})
|
||||
}
|
||||
if err := database.DB.Model(&post).Association("Categories").Replace(categories); err != nil {
|
||||
log.Printf("[BlogHandler.Create] Error associating categories: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle tags
|
||||
tagIDs := c.PostFormArray("tag_ids")
|
||||
if len(tagIDs) > 0 {
|
||||
var tags []*models.Tag
|
||||
for _, idStr := range tagIDs {
|
||||
id, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
tags = append(tags, &models.Tag{ID: id})
|
||||
}
|
||||
if err := database.DB.Model(&post).Association("Tags").Replace(tags); err != nil {
|
||||
log.Printf("[BlogHandler.Create] Error associating tags: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog")
|
||||
}
|
||||
|
||||
// Edit displays the edit form
|
||||
func (h *BlogHandler) Edit(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid ID")
|
||||
return
|
||||
}
|
||||
|
||||
var post models.Post
|
||||
err = database.DB.
|
||||
Preload("Categories").
|
||||
Preload("Tags").
|
||||
First(&post, id).Error
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[BlogHandler.Edit] Post not found: ID=%d, error=%v", id, err)
|
||||
c.String(http.StatusNotFound, "Post not found")
|
||||
return
|
||||
}
|
||||
|
||||
var categories []models.Category
|
||||
var tags []models.Tag
|
||||
database.DB.Where("is_active = ?", true).Find(&categories)
|
||||
database.DB.Where("is_active = ?", true).Find(&tags)
|
||||
|
||||
blog.Edit(&post, categories, tags, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
// Update handles post updates
|
||||
func (h *BlogHandler) Update(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
idUint, _ := strconv.ParseUint(id, 10, 64)
|
||||
|
||||
title := c.PostForm("title")
|
||||
content := c.PostForm("content")
|
||||
keywords := c.PostForm("keywords")
|
||||
isActive := c.PostForm("is_active") == "on"
|
||||
isFront := c.PostForm("is_front") == "on"
|
||||
|
||||
log.Printf("[BlogHandler.Update] Received update for ID=%s: Title=%s, ContentSize=%d", id, title, len(content))
|
||||
|
||||
// Validation
|
||||
errors := make(map[string]string)
|
||||
if title == "" {
|
||||
errors["title"] = "Title is required"
|
||||
}
|
||||
if content == "" {
|
||||
errors["content"] = "Content is required"
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
log.Printf("[BlogHandler.Update] Validation failed: %v", errors)
|
||||
var post models.Post
|
||||
database.DB.Preload("Categories").Preload("Tags").First(&post, idUint)
|
||||
var categories []models.Category
|
||||
var tags []models.Tag
|
||||
database.DB.Where("is_active = ?", true).Find(&categories)
|
||||
database.DB.Where("is_active = ?", true).Find(&tags)
|
||||
blog.Edit(&post, categories, tags, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle image upload
|
||||
imagePath := c.PostForm("image") // Keep existing or use manual link
|
||||
file, err := c.FormFile("image_file")
|
||||
if err == nil && file != nil {
|
||||
opts := &utils.ImageOptions{
|
||||
Width: config.AppConfig.PostImageWidth,
|
||||
Height: config.AppConfig.PostImageHeight,
|
||||
Quality: float32(config.AppConfig.PostImageQuality),
|
||||
Format: config.AppConfig.PostImageFormat,
|
||||
Mode: config.AppConfig.PostImageMode,
|
||||
}
|
||||
path, err := utils.SaveOptimizedImage(file, filepath.Join("uploads", "blog"), "post", opts)
|
||||
if err == nil {
|
||||
imagePath = path
|
||||
log.Printf("[BlogHandler.Update] Image updated to: %s", imagePath)
|
||||
} else {
|
||||
log.Printf("[BlogHandler.Update] Image upload error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Update post
|
||||
updates := map[string]interface{}{
|
||||
"title": title,
|
||||
"content": content,
|
||||
"keywords": keywords,
|
||||
"image": imagePath,
|
||||
"is_active": isActive,
|
||||
"is_front": isFront,
|
||||
}
|
||||
|
||||
if err := database.DB.Model(&models.Post{}).Where("id = ?", id).Updates(updates).Error; err != nil {
|
||||
log.Printf("[BlogHandler.Update] DB Update error: %v", err)
|
||||
errors["general"] = "Error updating post"
|
||||
var post models.Post
|
||||
database.DB.Preload("Categories").Preload("Tags").First(&post, idUint)
|
||||
var categories []models.Category
|
||||
var tags []models.Tag
|
||||
database.DB.Where("is_active = ?", true).Find(&categories)
|
||||
database.DB.Where("is_active = ?", true).Find(&tags)
|
||||
blog.Edit(&post, categories, tags, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
// Update categories and tags separately (GORM Associations)
|
||||
var post models.Post
|
||||
if err := database.DB.First(&post, idUint).Error; err == nil {
|
||||
// Categories
|
||||
categoryIDs := c.PostFormArray("category_ids")
|
||||
var categories []*models.Category
|
||||
for _, idStr := range categoryIDs {
|
||||
catID, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
categories = append(categories, &models.Category{ID: catID})
|
||||
}
|
||||
database.DB.Model(&post).Association("Categories").Replace(categories)
|
||||
|
||||
// Tags
|
||||
tagIDs := c.PostFormArray("tag_ids")
|
||||
var tags []*models.Tag
|
||||
for _, idStr := range tagIDs {
|
||||
tagID, _ := strconv.ParseUint(idStr, 10, 64)
|
||||
tags = append(tags, &models.Tag{ID: tagID})
|
||||
}
|
||||
database.DB.Model(&post).Association("Tags").Replace(tags)
|
||||
}
|
||||
|
||||
log.Printf("[BlogHandler.Update] Post updated successfully: ID=%s", id)
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog")
|
||||
}
|
||||
|
||||
// Delete handles post deletion
|
||||
func (h *BlogHandler) Delete(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
log.Printf("[BlogHandler.Delete] Deleting ID=%s", id)
|
||||
|
||||
if err := database.DB.Delete(&models.Post{}, "id = ?", id).Error; err != nil {
|
||||
log.Printf("[BlogHandler.Delete] Error: %v", err)
|
||||
c.String(http.StatusInternalServerError, "Error deleting post")
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// CATEGORY HANDLERS
|
||||
// ============================================
|
||||
|
||||
func (h *BlogHandler) ListCategories(c *gin.Context) {
|
||||
var categories []models.Category
|
||||
database.DB.Order("`order` ASC").Find(&categories)
|
||||
blog.CategoryList(categories).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *BlogHandler) NewCategory(c *gin.Context) {
|
||||
var categories []models.Category
|
||||
database.DB.Find(&categories)
|
||||
blog.CategoryForm(&models.Category{}, categories, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *BlogHandler) CreateCategory(c *gin.Context) {
|
||||
var cat models.Category
|
||||
cat.Title = c.PostForm("title")
|
||||
cat.Slug = c.PostForm("slug")
|
||||
cat.Desc = c.PostForm("description")
|
||||
cat.Keywords = c.PostForm("keywords")
|
||||
cat.IsActive = c.PostForm("is_active") == "on"
|
||||
|
||||
order, _ := strconv.Atoi(c.PostForm("order"))
|
||||
cat.Order = order
|
||||
|
||||
parentIDStr := c.PostForm("parent_id")
|
||||
if parentIDStr != "" {
|
||||
pID, _ := strconv.ParseUint(parentIDStr, 10, 64)
|
||||
cat.ParentID = &pID
|
||||
}
|
||||
|
||||
if cat.Title == "" {
|
||||
errors := map[string]string{"title": "Title is required"}
|
||||
var categories []models.Category
|
||||
database.DB.Find(&categories)
|
||||
blog.CategoryForm(&cat, categories, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&cat).Error; err != nil {
|
||||
errors := map[string]string{"general": err.Error()}
|
||||
var categories []models.Category
|
||||
database.DB.Find(&categories)
|
||||
blog.CategoryForm(&cat, categories, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog/categories")
|
||||
}
|
||||
|
||||
func (h *BlogHandler) EditCategory(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var cat models.Category
|
||||
if err := database.DB.First(&cat, id).Error; err != nil {
|
||||
c.String(http.StatusNotFound, "Category not found")
|
||||
return
|
||||
}
|
||||
|
||||
var categories []models.Category
|
||||
database.DB.Find(&categories)
|
||||
blog.CategoryForm(&cat, categories, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *BlogHandler) UpdateCategory(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var cat models.Category
|
||||
if err := database.DB.First(&cat, id).Error; err != nil {
|
||||
c.String(http.StatusNotFound, "Category not found")
|
||||
return
|
||||
}
|
||||
|
||||
cat.Title = c.PostForm("title")
|
||||
cat.Slug = c.PostForm("slug")
|
||||
cat.Desc = c.PostForm("description")
|
||||
cat.Keywords = c.PostForm("keywords")
|
||||
cat.IsActive = c.PostForm("is_active") == "on"
|
||||
|
||||
order, _ := strconv.Atoi(c.PostForm("order"))
|
||||
cat.Order = order
|
||||
|
||||
parentIDStr := c.PostForm("parent_id")
|
||||
if parentIDStr != "" {
|
||||
pID, _ := strconv.ParseUint(parentIDStr, 10, 64)
|
||||
cat.ParentID = &pID
|
||||
} else {
|
||||
cat.ParentID = nil
|
||||
}
|
||||
|
||||
if cat.Title == "" {
|
||||
errors := map[string]string{"title": "Title is required"}
|
||||
var categories []models.Category
|
||||
database.DB.Find(&categories)
|
||||
blog.CategoryForm(&cat, categories, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DB.Save(&cat).Error; err != nil {
|
||||
errors := map[string]string{"general": err.Error()}
|
||||
var categories []models.Category
|
||||
database.DB.Find(&categories)
|
||||
blog.CategoryForm(&cat, categories, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog/categories")
|
||||
}
|
||||
|
||||
func (h *BlogHandler) DeleteCategory(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
database.DB.Delete(&models.Category{}, id)
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog/categories")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// TAG HANDLERS
|
||||
// ============================================
|
||||
|
||||
func (h *BlogHandler) ListTags(c *gin.Context) {
|
||||
var tags []models.Tag
|
||||
database.DB.Order("tag ASC").Find(&tags)
|
||||
blog.TagList(tags).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *BlogHandler) NewTag(c *gin.Context) {
|
||||
blog.TagForm(&models.Tag{}, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *BlogHandler) CreateTag(c *gin.Context) {
|
||||
var tag models.Tag
|
||||
tag.Tag = c.PostForm("tag")
|
||||
tag.Slug = c.PostForm("slug")
|
||||
tag.IsActive = true // Default
|
||||
|
||||
if tag.Tag == "" {
|
||||
errors := map[string]string{"tag": "Tag name is required"}
|
||||
blog.TagForm(&tag, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&tag).Error; err != nil {
|
||||
errors := map[string]string{"general": err.Error()}
|
||||
blog.TagForm(&tag, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog/tags")
|
||||
}
|
||||
|
||||
func (h *BlogHandler) EditTag(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var tag models.Tag
|
||||
if err := database.DB.First(&tag, id).Error; err != nil {
|
||||
c.String(http.StatusNotFound, "Tag not found")
|
||||
return
|
||||
}
|
||||
blog.TagForm(&tag, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *BlogHandler) UpdateTag(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var tag models.Tag
|
||||
if err := database.DB.First(&tag, id).Error; err != nil {
|
||||
c.String(http.StatusNotFound, "Tag not found")
|
||||
return
|
||||
}
|
||||
|
||||
tag.Tag = c.PostForm("tag")
|
||||
tag.Slug = c.PostForm("slug")
|
||||
|
||||
if tag.Tag == "" {
|
||||
errors := map[string]string{"tag": "Tag name is required"}
|
||||
blog.TagForm(&tag, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DB.Save(&tag).Error; err != nil {
|
||||
errors := map[string]string{"general": err.Error()}
|
||||
blog.TagForm(&tag, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog/tags")
|
||||
}
|
||||
|
||||
func (h *BlogHandler) DeleteTag(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
database.DB.Delete(&models.Tag{}, id)
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog/tags")
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// COMMENT HANDLERS
|
||||
// ============================================
|
||||
|
||||
func (h *BlogHandler) ListComments(c *gin.Context) {
|
||||
var comments []models.Comment
|
||||
database.DB.Preload("Product").Order("created_at DESC").Find(&comments)
|
||||
blog.CommentList(comments).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *BlogHandler) EditComment(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var comment models.Comment
|
||||
if err := database.DB.Preload("Product").First(&comment, id).Error; err != nil {
|
||||
c.String(http.StatusNotFound, "Comment not found")
|
||||
return
|
||||
}
|
||||
blog.CommentForm(&comment, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *BlogHandler) UpdateComment(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var comment models.Comment
|
||||
if err := database.DB.First(&comment, id).Error; err != nil {
|
||||
c.String(http.StatusNotFound, "Comment not found")
|
||||
return
|
||||
}
|
||||
|
||||
comment.Body = c.PostForm("body")
|
||||
comment.IsActive = c.PostForm("is_active") == "on"
|
||||
|
||||
if err := database.DB.Save(&comment).Error; err != nil {
|
||||
errors := map[string]string{"general": err.Error()}
|
||||
blog.CommentForm(&comment, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog/comments")
|
||||
}
|
||||
|
||||
func (h *BlogHandler) DeleteComment(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
database.DB.Delete(&models.Comment{}, id)
|
||||
c.Redirect(http.StatusSeeOther, "/admin/blog/comments")
|
||||
}
|
||||
Reference in New Issue
Block a user