538 lines
16 KiB
Go
538 lines
16 KiB
Go
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")
|
|
}
|