package handlers import ( "net/http" "strconv" "time" blogModels "ginimageApi/app/blogs/models" "ginimageApi/configs" "github.com/gin-gonic/gin" "gorm.io/gorm" ) type createPostRequest struct { Title string `json:"title" binding:"required,min=3"` Content string `json:"content"` Keywords string `json:"keywords"` Image string `json:"image"` Video string `json:"video"` IsActive *bool `json:"is_active"` IsFront *bool `json:"is_front"` } type updatePostRequest struct { Title string `json:"title"` Content string `json:"content"` Keywords string `json:"keywords"` Image string `json:"image"` Video string `json:"video"` IsActive *bool `json:"is_active"` IsFront *bool `json:"is_front"` } type BlogErrorResponse struct { Error string `json:"error"` } type BlogPostResponse struct { ID uint64 `json:"id"` Title string `json:"title"` Slug string `json:"slug"` Content string `json:"content"` Keywords string `json:"keywords"` Image string `json:"image"` Video string `json:"video"` IsActive bool `json:"is_active"` IsFront bool `json:"is_front"` CreatedAt string `json:"created_at"` UpdatedAt string `json:"updated_at"` } type BlogListResponse struct { Count int `json:"count"` Items []BlogPostResponse `json:"items"` } type BlogCategoryResponse struct { ID uint64 `json:"id"` Title string `json:"title"` Slug string `json:"slug"` Keywords string `json:"keywords"` Desc string `json:"description"` Image string `json:"image"` IsActive bool `json:"is_active"` Order int `json:"order"` } type BlogTagResponse struct { ID uint64 `json:"id"` Tag string `json:"tag"` Slug string `json:"slug"` IsActive bool `json:"is_active"` } type BlogCategoryListResponse struct { Count int `json:"count"` Items []BlogCategoryResponse `json:"items"` } type BlogTagListResponse struct { Count int `json:"count"` Items []BlogTagResponse `json:"items"` } type createCategoryRequest struct { Title string `json:"title" binding:"required,min=2"` Keywords string `json:"keywords"` Desc string `json:"description"` Image string `json:"image"` Order int `json:"order"` IsActive *bool `json:"is_active"` ParentID *uint64 `json:"parent_id"` } type updateCategoryRequest struct { Title string `json:"title"` Keywords string `json:"keywords"` Desc string `json:"description"` Image string `json:"image"` Order *int `json:"order"` IsActive *bool `json:"is_active"` ParentID *uint64 `json:"parent_id"` } type createTagRequest struct { Tag string `json:"tag" binding:"required,min=2"` IsActive *bool `json:"is_active"` } type updateTagRequest struct { Tag string `json:"tag"` IsActive *bool `json:"is_active"` } func toBlogPostResponse(p blogModels.Post) BlogPostResponse { return BlogPostResponse{ ID: p.ID, Title: p.Title, Slug: p.Slug, Content: p.Content, Keywords: p.Keywords, Image: p.Image, Video: p.Video, IsActive: p.IsActive, IsFront: p.IsFront, CreatedAt: p.CreatedAt.Format(time.RFC3339), UpdatedAt: p.UpdatedAt.Format(time.RFC3339), } } func toBlogCategoryResponse(c blogModels.Category) BlogCategoryResponse { return BlogCategoryResponse{ ID: c.ID, Title: c.Title, Slug: c.Slug, Keywords: c.Keywords, Desc: c.Desc, Image: c.Image, IsActive: c.IsActive, Order: c.Order, } } func toBlogTagResponse(t blogModels.Tag) BlogTagResponse { return BlogTagResponse{ ID: t.ID, Tag: t.Tag, Slug: t.Slug, IsActive: t.IsActive, } } func userIDFromContext(c *gin.Context) (uint64, bool) { v, ok := c.Get("user_id") if !ok { return 0, false } switch id := v.(type) { case uint: return uint64(id), true case int: if id < 0 { return 0, false } return uint64(id), true case string: parsed, err := strconv.ParseUint(id, 10, 64) if err != nil { return 0, false } return parsed, true default: return 0, false } } // ListPosts godoc // @Summary Public blog post listesini getirir // @Tags blogs // @Produce json // @Success 200 {object} BlogListResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs [get] func ListPosts(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } var posts []blogModels.Post if err := configs.DB.Where("is_active = ?", true).Order("id desc").Find(&posts).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "postlar listelenemedi"}) return } items := make([]BlogPostResponse, 0, len(posts)) for _, p := range posts { items = append(items, toBlogPostResponse(p)) } c.JSON(http.StatusOK, BlogListResponse{Count: len(items), Items: items}) } // GetPost godoc // @Summary Public tekil blog postu getirir // @Tags blogs // @Produce json // @Param slug path string true "Post slug" // @Success 200 {object} BlogPostResponse // @Failure 404 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/{slug} [get] func GetPost(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } slug := c.Param("slug") var post blogModels.Post err := configs.DB.Where("slug = ? AND is_active = ?", slug, true).First(&post).Error if err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "post bulunamadi"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "post getirilemedi"}) return } c.JSON(http.StatusOK, toBlogPostResponse(post)) } // CreatePost godoc // @Summary Admin blog post olusturur // @Tags blogs // @Accept json // @Produce json // @Security BearerAuth // @Param request body createPostRequest true "Post bilgileri" // @Success 201 {object} BlogPostResponse // @Failure 400 {object} BlogErrorResponse // @Failure 401 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs [post] func CreatePost(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } userID, ok := userIDFromContext(c) if !ok { c.JSON(http.StatusUnauthorized, gin.H{"error": "kullanici bulunamadi"}) return } var req createPostRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } post := blogModels.Post{ Title: req.Title, Content: req.Content, Keywords: req.Keywords, Image: req.Image, Video: req.Video, UserID: &userID, IsActive: true, IsFront: true, } if req.IsActive != nil { post.IsActive = *req.IsActive } if req.IsFront != nil { post.IsFront = *req.IsFront } if err := configs.DB.Create(&post).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "post olusturulamadi"}) return } c.JSON(http.StatusCreated, toBlogPostResponse(post)) } // UpdatePost godoc // @Summary Admin blog post gunceller // @Tags blogs // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "Post ID" // @Param request body updatePostRequest true "Guncellenecek alanlar" // @Success 200 {object} BlogPostResponse // @Failure 400 {object} BlogErrorResponse // @Failure 404 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/{id} [put] func UpdatePost(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "gecersiz post id"}) return } var post blogModels.Post if err := configs.DB.First(&post, id).Error; err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "post bulunamadi"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "post bulunamadi"}) return } var req updatePostRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if req.Title != "" { post.Title = req.Title } if req.Content != "" { post.Content = req.Content } if req.Keywords != "" { post.Keywords = req.Keywords } if req.Image != "" { post.Image = req.Image } if req.Video != "" { post.Video = req.Video } if req.IsActive != nil { post.IsActive = *req.IsActive } if req.IsFront != nil { post.IsFront = *req.IsFront } if err := configs.DB.Save(&post).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "post guncellenemedi"}) return } c.JSON(http.StatusOK, toBlogPostResponse(post)) } // DeletePost godoc // @Summary Admin blog post siler // @Tags blogs // @Security BearerAuth // @Param id path int true "Post ID" // @Success 204 // @Failure 400 {object} BlogErrorResponse // @Failure 404 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/{id} [delete] func DeletePost(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "gecersiz post id"}) return } res := configs.DB.Delete(&blogModels.Post{}, id) if res.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "post silinemedi"}) return } if res.RowsAffected == 0 { c.JSON(http.StatusNotFound, gin.H{"error": "post bulunamadi"}) return } c.Status(http.StatusNoContent) } // ListCategories godoc // @Summary Public kategori listesini getirir // @Tags blogs // @Produce json // @Success 200 {object} BlogCategoryListResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/categories [get] func ListCategories(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } var categories []blogModels.Category if err := configs.DB.Where("is_active = ?", true).Order("`order` asc, id desc").Find(&categories).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "kategoriler listelenemedi"}) return } items := make([]BlogCategoryResponse, 0, len(categories)) for _, item := range categories { items = append(items, toBlogCategoryResponse(item)) } c.JSON(http.StatusOK, BlogCategoryListResponse{Count: len(items), Items: items}) } // GetCategory godoc // @Summary Public tekil kategori getirir // @Tags blogs // @Produce json // @Param slug path string true "Kategori slug" // @Success 200 {object} BlogCategoryResponse // @Failure 404 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/categories/{slug} [get] func GetCategory(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } var category blogModels.Category err := configs.DB.Where("slug = ? AND is_active = ?", c.Param("slug"), true).First(&category).Error if err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "kategori bulunamadi"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "kategori getirilemedi"}) return } c.JSON(http.StatusOK, toBlogCategoryResponse(category)) } // CreateCategory godoc // @Summary Admin kategori olusturur // @Tags blogs // @Accept json // @Produce json // @Security BearerAuth // @Param request body createCategoryRequest true "Kategori bilgileri" // @Success 201 {object} BlogCategoryResponse // @Failure 400 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/categories [post] func CreateCategory(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } var req createCategoryRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } category := blogModels.Category{ Title: req.Title, Keywords: req.Keywords, Desc: req.Desc, Image: req.Image, Order: req.Order, ParentID: req.ParentID, IsActive: true, } if req.IsActive != nil { category.IsActive = *req.IsActive } if category.Order == 0 { category.Order = 1 } if err := configs.DB.Create(&category).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "kategori olusturulamadi"}) return } c.JSON(http.StatusCreated, toBlogCategoryResponse(category)) } // UpdateCategory godoc // @Summary Admin kategori gunceller // @Tags blogs // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "Kategori ID" // @Param request body updateCategoryRequest true "Guncellenecek alanlar" // @Success 200 {object} BlogCategoryResponse // @Failure 400 {object} BlogErrorResponse // @Failure 404 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/categories/{id} [put] func UpdateCategory(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "gecersiz kategori id"}) return } var category blogModels.Category if err := configs.DB.First(&category, id).Error; err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "kategori bulunamadi"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "kategori bulunamadi"}) return } var req updateCategoryRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if req.Title != "" { category.Title = req.Title } if req.Keywords != "" { category.Keywords = req.Keywords } if req.Desc != "" { category.Desc = req.Desc } if req.Image != "" { category.Image = req.Image } if req.Order != nil { category.Order = *req.Order } if req.IsActive != nil { category.IsActive = *req.IsActive } if req.ParentID != nil { category.ParentID = req.ParentID } if err := configs.DB.Save(&category).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "kategori guncellenemedi"}) return } c.JSON(http.StatusOK, toBlogCategoryResponse(category)) } // DeleteCategory godoc // @Summary Admin kategori siler // @Tags blogs // @Security BearerAuth // @Param id path int true "Kategori ID" // @Success 204 // @Failure 400 {object} BlogErrorResponse // @Failure 404 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/categories/{id} [delete] func DeleteCategory(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "gecersiz kategori id"}) return } res := configs.DB.Delete(&blogModels.Category{}, id) if res.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "kategori silinemedi"}) return } if res.RowsAffected == 0 { c.JSON(http.StatusNotFound, gin.H{"error": "kategori bulunamadi"}) return } c.Status(http.StatusNoContent) } // ListTags godoc // @Summary Public tag listesini getirir // @Tags blogs // @Produce json // @Success 200 {object} BlogTagListResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/tags [get] func ListTags(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } var tags []blogModels.Tag if err := configs.DB.Where("is_active = ?", true).Order("id desc").Find(&tags).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "tagler listelenemedi"}) return } items := make([]BlogTagResponse, 0, len(tags)) for _, item := range tags { items = append(items, toBlogTagResponse(item)) } c.JSON(http.StatusOK, BlogTagListResponse{Count: len(items), Items: items}) } // GetTag godoc // @Summary Public tekil tag getirir // @Tags blogs // @Produce json // @Param slug path string true "Tag slug" // @Success 200 {object} BlogTagResponse // @Failure 404 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/tags/{slug} [get] func GetTag(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } var tag blogModels.Tag err := configs.DB.Where("slug = ? AND is_active = ?", c.Param("slug"), true).First(&tag).Error if err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "tag bulunamadi"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "tag getirilemedi"}) return } c.JSON(http.StatusOK, toBlogTagResponse(tag)) } // CreateTag godoc // @Summary Admin tag olusturur // @Tags blogs // @Accept json // @Produce json // @Security BearerAuth // @Param request body createTagRequest true "Tag bilgileri" // @Success 201 {object} BlogTagResponse // @Failure 400 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/tags [post] func CreateTag(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } var req createTagRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } tag := blogModels.Tag{Tag: req.Tag, IsActive: true} if req.IsActive != nil { tag.IsActive = *req.IsActive } if err := configs.DB.Create(&tag).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "tag olusturulamadi"}) return } c.JSON(http.StatusCreated, toBlogTagResponse(tag)) } // UpdateTag godoc // @Summary Admin tag gunceller // @Tags blogs // @Accept json // @Produce json // @Security BearerAuth // @Param id path int true "Tag ID" // @Param request body updateTagRequest true "Guncellenecek alanlar" // @Success 200 {object} BlogTagResponse // @Failure 400 {object} BlogErrorResponse // @Failure 404 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/tags/{id} [put] func UpdateTag(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "gecersiz tag id"}) return } var tag blogModels.Tag if err := configs.DB.First(&tag, id).Error; err != nil { if err == gorm.ErrRecordNotFound { c.JSON(http.StatusNotFound, gin.H{"error": "tag bulunamadi"}) return } c.JSON(http.StatusInternalServerError, gin.H{"error": "tag bulunamadi"}) return } var req updateTagRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if req.Tag != "" { tag.Tag = req.Tag } if req.IsActive != nil { tag.IsActive = *req.IsActive } if err := configs.DB.Save(&tag).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "tag guncellenemedi"}) return } c.JSON(http.StatusOK, toBlogTagResponse(tag)) } // DeleteTag godoc // @Summary Admin tag siler // @Tags blogs // @Security BearerAuth // @Param id path int true "Tag ID" // @Success 204 // @Failure 400 {object} BlogErrorResponse // @Failure 404 {object} BlogErrorResponse // @Failure 500 {object} BlogErrorResponse // @Router /api/v1/blogs/tags/{id} [delete] func DeleteTag(c *gin.Context) { if configs.DB == nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "db baglantisi yok"}) return } id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "gecersiz tag id"}) return } res := configs.DB.Delete(&blogModels.Tag{}, id) if res.Error != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "tag silinemedi"}) return } if res.RowsAffected == 0 { c.JSON(http.StatusNotFound, gin.H{"error": "tag bulunamadi"}) return } c.Status(http.StatusNoContent) }