first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:43:40 +03:00
commit f34e54c5a5
100 changed files with 27342 additions and 0 deletions

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

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

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

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