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")
|
||||
}
|
||||
26
internal/handler/admin/handler.go
Normal file
26
internal/handler/admin/handler.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
view "gobeyhan/views/admin"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Handler struct{}
|
||||
|
||||
func NewHandler() *Handler {
|
||||
return &Handler{}
|
||||
}
|
||||
|
||||
func (h *Handler) LoginPage(c *gin.Context) {
|
||||
view.Login().Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *Handler) LoginPost(c *gin.Context) {
|
||||
// TODO: Implement actual login logic
|
||||
c.Redirect(303, "/admin/dashboard")
|
||||
}
|
||||
|
||||
func (h *Handler) Dashboard(c *gin.Context) {
|
||||
view.Dashboard().Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
308
internal/handler/admin/settings_handler.go
Normal file
308
internal/handler/admin/settings_handler.go
Normal file
@@ -0,0 +1,308 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"gobeyhan/app/settings/services"
|
||||
"gobeyhan/database/models"
|
||||
"gobeyhan/views/admin/settings" // We will create this package
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type SettingsHandler struct {
|
||||
service *services.SettingsService
|
||||
}
|
||||
|
||||
func NewSettingsHandler() *SettingsHandler {
|
||||
return &SettingsHandler{
|
||||
service: services.NewSettingsService(),
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== WHITELIST ====================
|
||||
|
||||
func (h *SettingsHandler) ListWhitelist(c *gin.Context) {
|
||||
items, err := h.service.GetAllCorsWhitelist()
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error fetching whitelist")
|
||||
return
|
||||
}
|
||||
settings.WhitelistList(items).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) NewWhitelist(c *gin.Context) {
|
||||
settings.WhitelistCreate(nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) CreateWhitelist(c *gin.Context) {
|
||||
origin := c.PostForm("origin")
|
||||
description := c.PostForm("description")
|
||||
|
||||
// Basic Validation
|
||||
errors := make(map[string]string)
|
||||
if origin == "" {
|
||||
errors["origin"] = "Origin is required"
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
settings.WhitelistCreate(errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
item := &models.CorsWhitelist{
|
||||
Origin: origin,
|
||||
Description: description,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
if err := h.service.CreateCorsWhitelist(item); err != nil {
|
||||
errors["origin"] = "Error creating whitelist entry: " + err.Error()
|
||||
settings.WhitelistCreate(errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/settings/whitelist")
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) EditWhitelist(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid ID")
|
||||
return
|
||||
}
|
||||
|
||||
item, err := h.service.GetCorsWhitelistByID(id)
|
||||
if err != nil {
|
||||
c.String(http.StatusNotFound, "Item not found")
|
||||
return
|
||||
}
|
||||
|
||||
settings.WhitelistEdit(item, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) UpdateWhitelist(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
origin := c.PostForm("origin")
|
||||
description := c.PostForm("description")
|
||||
|
||||
// Basic Validation
|
||||
errors := make(map[string]string)
|
||||
if origin == "" {
|
||||
errors["origin"] = "Origin is required"
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
// Fetch item again to display form with errors
|
||||
idUint, _ := strconv.ParseUint(id, 10, 64)
|
||||
item, _ := h.service.GetCorsWhitelistByID(idUint)
|
||||
if item == nil {
|
||||
c.String(http.StatusNotFound, "Item not found")
|
||||
return
|
||||
}
|
||||
// Preserve user input
|
||||
item.Origin = origin
|
||||
item.Description = description
|
||||
settings.WhitelistEdit(item, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"origin": origin,
|
||||
"description": description,
|
||||
}
|
||||
|
||||
if err := h.service.UpdateCorsWhitelist(id, updates); err != nil {
|
||||
idUint, _ := strconv.ParseUint(id, 10, 64)
|
||||
item, _ := h.service.GetCorsWhitelistByID(idUint)
|
||||
settings.WhitelistEdit(item, map[string]string{"origin": "Error updating: " + err.Error()}).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/settings/whitelist")
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) DeleteWhitelist(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if err := h.service.DeleteCorsWhitelist(id); err != nil { // Service takes string ID
|
||||
c.String(http.StatusInternalServerError, "Error deleting item")
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusSeeOther, "/admin/settings/whitelist")
|
||||
}
|
||||
|
||||
// ==================== BLACKLIST ====================
|
||||
|
||||
func (h *SettingsHandler) ListBlacklist(c *gin.Context) {
|
||||
items, err := h.service.GetAllCorsBlacklist()
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error fetching blacklist")
|
||||
return
|
||||
}
|
||||
settings.BlacklistList(items).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) NewBlacklist(c *gin.Context) {
|
||||
settings.BlacklistCreate(nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) CreateBlacklist(c *gin.Context) {
|
||||
origin := c.PostForm("origin")
|
||||
description := c.PostForm("description")
|
||||
|
||||
errors := make(map[string]string)
|
||||
if origin == "" {
|
||||
errors["origin"] = "Origin is required"
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
settings.BlacklistCreate(errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
item := &models.CorsBlacklist{
|
||||
Origin: origin,
|
||||
Reason: description,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
if err := h.service.CreateCorsBlacklist(item); err != nil {
|
||||
errors["origin"] = "Error creating entry: " + err.Error()
|
||||
settings.BlacklistCreate(errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/settings/blacklist")
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) DeleteBlacklist(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if err := h.service.DeleteCorsBlacklist(id); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error deleting item")
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusSeeOther, "/admin/settings/blacklist")
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) EditBlacklist(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid ID")
|
||||
return
|
||||
}
|
||||
|
||||
item, err := h.service.GetCorsBlacklistByID(id)
|
||||
if err != nil {
|
||||
c.String(http.StatusNotFound, "Item not found")
|
||||
return
|
||||
}
|
||||
|
||||
settings.BlacklistEdit(item, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) UpdateBlacklist(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
origin := c.PostForm("origin")
|
||||
reason := c.PostForm("reason")
|
||||
|
||||
errors := make(map[string]string)
|
||||
if origin == "" {
|
||||
errors["origin"] = "Origin is required"
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
idUint, _ := strconv.ParseUint(id, 10, 64)
|
||||
item, _ := h.service.GetCorsBlacklistByID(idUint)
|
||||
if item == nil {
|
||||
c.String(http.StatusNotFound, "Item not found")
|
||||
return
|
||||
}
|
||||
item.Origin = origin
|
||||
item.Reason = reason
|
||||
settings.BlacklistEdit(item, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"origin": origin,
|
||||
"reason": reason,
|
||||
}
|
||||
|
||||
if err := h.service.UpdateCorsBlacklist(id, updates); err != nil {
|
||||
idUint, _ := strconv.ParseUint(id, 10, 64)
|
||||
item, _ := h.service.GetCorsBlacklistByID(idUint)
|
||||
settings.BlacklistEdit(item, map[string]string{"origin": "Error updating: " + err.Error()}).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/settings/blacklist")
|
||||
}
|
||||
|
||||
// ==================== RATE LIMITS ====================
|
||||
|
||||
func (h *SettingsHandler) ListRateLimits(c *gin.Context) {
|
||||
items, err := h.service.GetAllRateLimitSettings()
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error fetching rate limits")
|
||||
return
|
||||
}
|
||||
settings.RateLimitList(items).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) EditRateLimit(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid ID")
|
||||
return
|
||||
}
|
||||
|
||||
item, err := h.service.GetRateLimitSettingByID(id)
|
||||
if err != nil {
|
||||
c.String(http.StatusNotFound, "Item not found")
|
||||
return
|
||||
}
|
||||
|
||||
settings.RateLimitEdit(item, nil).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) UpdateRateLimit(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
limitStr := c.PostForm("max_requests")
|
||||
windowStr := c.PostForm("window_seconds")
|
||||
description := c.PostForm("description")
|
||||
|
||||
limit, _ := strconv.ParseInt(limitStr, 10, 64)
|
||||
window, _ := strconv.Atoi(windowStr)
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"description": description,
|
||||
}
|
||||
if limit > 0 {
|
||||
updates["max_requests"] = limit
|
||||
}
|
||||
if window > 0 {
|
||||
updates["window_seconds"] = window
|
||||
}
|
||||
|
||||
if err := h.service.UpdateRateLimitSetting(id, updates); err != nil {
|
||||
// Handle error (redisplay form)
|
||||
idUint, _ := strconv.ParseUint(id, 10, 64)
|
||||
item, _ := h.service.GetRateLimitSettingByID(idUint)
|
||||
settings.RateLimitEdit(item, map[string]string{"general": "Error updating: " + err.Error()}).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/settings/rate-limits")
|
||||
}
|
||||
|
||||
func (h *SettingsHandler) DeleteRateLimit(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
if err := h.service.DeleteRateLimitSetting(id); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error deleting item")
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusSeeOther, "/admin/settings/rate-limits")
|
||||
}
|
||||
195
internal/handler/admin/user_handler.go
Normal file
195
internal/handler/admin/user_handler.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"gobeyhan/app/account/services"
|
||||
"gobeyhan/database/models"
|
||||
view "gobeyhan/views/admin/user"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type UserHandler struct {
|
||||
userService *services.UserService
|
||||
roleService *services.RoleService
|
||||
}
|
||||
|
||||
func NewUserHandler() *UserHandler {
|
||||
return &UserHandler{
|
||||
userService: services.NewUserService(),
|
||||
roleService: services.NewRoleService(),
|
||||
}
|
||||
}
|
||||
|
||||
// List Users
|
||||
func (h *UserHandler) List(c *gin.Context) {
|
||||
users, _, err := h.userService.GetAllUsers(false, 1, 100) // TODO: Implement pagination
|
||||
if err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error fetching users")
|
||||
return
|
||||
}
|
||||
view.List(users).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
// New User Form
|
||||
func (h *UserHandler) New(c *gin.Context) {
|
||||
roles, _ := h.roleService.GetAllRoles()
|
||||
view.Create(roles, map[string]string{}).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
// Create User Action
|
||||
func (h *UserHandler) Create(c *gin.Context) {
|
||||
username := c.PostForm("username")
|
||||
email := c.PostForm("email")
|
||||
password := c.PostForm("password")
|
||||
|
||||
// Basic Validation
|
||||
errors := make(map[string]string)
|
||||
if username == "" {
|
||||
errors["username"] = "Username is required"
|
||||
}
|
||||
if email == "" {
|
||||
errors["email"] = "Email is required"
|
||||
}
|
||||
if password == "" {
|
||||
errors["password"] = "Password is required"
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
roles, _ := h.roleService.GetAllRoles()
|
||||
view.Create(roles, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
user := &models.User{
|
||||
UserName: username,
|
||||
Email: email,
|
||||
}
|
||||
|
||||
if err := h.userService.CreateUser(user, password); err != nil {
|
||||
errors["email"] = "Error creating user (e.g. email exists)"
|
||||
roles, _ := h.roleService.GetAllRoles()
|
||||
view.Create(roles, errors).Render(c.Request.Context(), c.Writer)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle Role Assignment
|
||||
roleIDStr := c.PostForm("role_id")
|
||||
if roleID, err := strconv.ParseUint(roleIDStr, 10, 64); err == nil && roleID > 0 {
|
||||
h.userService.AssignRole(user.ID, roleID)
|
||||
} else {
|
||||
// Assign default role if no role selected (or as fallback)
|
||||
h.userService.AssignDefaultRole(user.ID)
|
||||
}
|
||||
|
||||
// Handle Email Verification
|
||||
emailVerified := c.PostForm("email_verified") == "on"
|
||||
if emailVerified {
|
||||
h.userService.UpdateUser(user.ID, map[string]interface{}{
|
||||
"email_verified": true,
|
||||
})
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/users")
|
||||
}
|
||||
|
||||
// Edit User Form
|
||||
func (h *UserHandler) 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
|
||||
}
|
||||
|
||||
user, err := h.userService.GetUserByID(id)
|
||||
if err != nil || user == nil {
|
||||
c.String(http.StatusNotFound, "User not found")
|
||||
return
|
||||
}
|
||||
|
||||
roles, _ := h.roleService.GetAllRoles()
|
||||
view.Edit(*user, roles, map[string]string{}).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
|
||||
// Update User Action
|
||||
func (h *UserHandler) Update(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid ID")
|
||||
return
|
||||
}
|
||||
|
||||
username := c.PostForm("username")
|
||||
email := c.PostForm("email")
|
||||
password := c.PostForm("password")
|
||||
|
||||
// Basic Validation
|
||||
errors := make(map[string]string)
|
||||
if username == "" {
|
||||
errors["username"] = "Username is required"
|
||||
}
|
||||
if email == "" {
|
||||
errors["email"] = "Email is required"
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
user, _ := h.userService.GetUserByID(id)
|
||||
if user != nil {
|
||||
// Keep submitted values? simplified for now
|
||||
user.UserName = username
|
||||
user.Email = email
|
||||
roles, _ := h.roleService.GetAllRoles()
|
||||
view.Edit(*user, roles, errors).Render(c.Request.Context(), c.Writer)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
updates := map[string]interface{}{
|
||||
"username": username,
|
||||
"email": email,
|
||||
"email_verified": c.PostForm("email_verified") == "on",
|
||||
}
|
||||
if password != "" {
|
||||
updates["password"] = password
|
||||
}
|
||||
|
||||
if err := h.userService.UpdateUser(id, updates); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error updating user")
|
||||
return
|
||||
}
|
||||
|
||||
// Update Role
|
||||
roleIDStr := c.PostForm("role_id")
|
||||
if roleID, err := strconv.ParseUint(roleIDStr, 10, 64); err == nil && roleID > 0 {
|
||||
// Remove existing roles first (simplified approach for single role)
|
||||
// Ideally we should check if role changed
|
||||
user, _ := h.userService.GetUserByID(id)
|
||||
if len(user.Roles) > 0 {
|
||||
h.userService.RemoveRole(id, user.Roles[0].ID)
|
||||
}
|
||||
h.userService.AssignRole(id, roleID)
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/users")
|
||||
}
|
||||
|
||||
// Delete User Action
|
||||
func (h *UserHandler) Delete(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||||
if err != nil {
|
||||
c.String(http.StatusBadRequest, "Invalid ID")
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.userService.DeleteUser(id); err != nil {
|
||||
c.String(http.StatusInternalServerError, "Error deleting user")
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, "/admin/users")
|
||||
}
|
||||
Reference in New Issue
Block a user