Files
esforum-2024/pkg/handler/api.go
T

287 lines
8.0 KiB
Go

package handler
import (
"esforum"
"esforum/pkg/util"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
type threadRequest struct {
Title string `json:"title" binding:"required,min=3,max=100"`
}
type postRequest struct {
Title string `json:"title" binding:"required,min=3,max=100"`
Content string `json:"content" binding:"required,min=5"`
Thread int64 `json:"thread" binding:"required"`
}
// thread godoc
// @Summary Create new thread (only for admins)
// @Tags thread
// @Produce json
// @Param thread body handler.threadRequest true "Thread data in JSON"
// @Param Authorization header string true "JWT Bearer token (authorization)"
// @Success 200 {object} esforum.Thread "created thread"
// @Router /api/thread [post]
func (h *Handler) create(c *gin.Context) {
var input threadRequest
if !util.MatchRole(c, "admin") {
util.NewErrorResponse(c, http.StatusForbidden, "no perms")
return
}
if err := c.BindJSON(&input); err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, err.Error())
return
}
thread, err := h.repo.CreateThread(input.Title)
if err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, err.Error())
return
}
c.JSON(http.StatusOK, thread)
}
// thread/delete godoc
// @Summary Delete thread by id (only for admins)
// @Tags thread
// @Produce json
// @Param id path int true "id of thread"
// @Param Authorization header string true "JWT Bearer token (authorization)"
// @Success 200
// @Router /api/thread/{id} [delete]
func (h *Handler) delete(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, "invalid id format")
return
}
_, err = h.repo.GetThread(id)
if err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, err.Error())
return
}
current, exist := c.Get(userCtx)
if !exist {
util.NewErrorResponse(c, http.StatusUnauthorized, "user not found in context")
return
}
currentUser, err := h.services.AuthorizeById(current.(int64))
if (err != nil) || (currentUser.Access != "admin") {
util.NewErrorResponse(c, http.StatusForbidden, "no permissions")
return
}
err = h.repo.DeleteThread(id)
if err != nil {
util.NewErrorResponse(c, http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}
// createPost godoc
// @Summary Create new post
// @Tags post
// @Produce json
// @Param post body handler.postRequest true "Post data in JSON"
// @Param Authorization header string true "JWT Bearer token (authorization)"
// @Success 200 {object} esforum.Post "created post"
// @Router /api/post [post]
func (h *Handler) createPost(c *gin.Context) {
var input postRequest
if err := c.BindJSON(&input); err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, err.Error())
return
}
// Проверяем существование треда
_, err := h.repo.GetThread(input.Thread)
if err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, "thread does not exist")
return
}
post, err := h.repo.CreatePost(input.Title, input.Content, input.Thread)
if err != nil {
util.NewErrorResponse(c, http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, post)
}
// deletePost godoc
// @Summary Delete post by id
// @Tags post
// @Produce json
// @Param id path int true "id of post"
// @Param Authorization header string true "JWT Bearer token (authorization)"
// @Success 200
// @Router /api/post/{id} [delete]
func (h *Handler) deletePost(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, "invalid id format")
return
}
post, err := h.repo.GetPost(id)
if err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, err.Error())
return
}
// Только админ может удалять посты
if !util.MatchRole(c, "admin") {
util.NewErrorResponse(c, http.StatusForbidden, "no permissions")
return
}
err = h.repo.DeletePost(id)
if err != nil {
util.NewErrorResponse(c, http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, gin.H{"status": "ok", "deleted_post_id": post.Id})
}
// getThread godoc
// @Summary Get thread by id
// @Tags thread
// @Produce json
// @Param id path int true "id of thread"
// @Success 200 {object} esforum.Thread
// @Router /api/guest/thread/{id} [get]
func (h *Handler) getThread(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, "invalid id format")
return
}
thread, err := h.repo.GetThread(id)
if err != nil {
util.NewErrorResponse(c, http.StatusNotFound, "thread not found")
return
}
c.JSON(http.StatusOK, thread)
}
// getAllThreads godoc
// @Summary Get all threads
// @Tags thread
// @Produce json
// @Success 200 {array} esforum.Thread
// @Router /api/guest/thread/all [get]
func (h *Handler) getAllThreads(c *gin.Context) {
// Получаем параметры для пагинации
startParam := c.DefaultQuery("start", "0")
limitParam := c.DefaultQuery("limit", "20")
start, err := strconv.Atoi(startParam)
if err != nil || start < 0 {
start = 0
}
limit, err := strconv.Atoi(limitParam)
if err != nil || limit < 1 || limit > 100 {
limit = 20
}
// Получаем ID всех тредов с учетом пагинации
threadIds := h.repo.GetThreads(start, limit)
// Получаем общее количество тредов
totalThreads := h.repo.GetThreadsCount()
// Получаем информацию о каждом треде
threads := make([]esforum.Thread, 0, len(threadIds))
for _, threadId := range threadIds {
thread, err := h.repo.GetThread(threadId)
if err == nil {
threads = append(threads, *thread)
}
}
c.JSON(http.StatusOK, gin.H{
"threads": threads,
"total": totalThreads,
"start": start,
"limit": limit,
})
}
// getThreadPosts godoc
// @Summary Get posts in thread
// @Tags post
// @Produce json
// @Param id path int true "id of thread"
// @Success 200 {array} esforum.Post
// @Router /api/guest/thread/{id}/posts [get]
func (h *Handler) getThreadPosts(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, "invalid id format")
return
}
// Проверяем существование треда
_, err = h.repo.GetThread(id)
if err != nil {
util.NewErrorResponse(c, http.StatusNotFound, "thread not found")
return
}
// Получаем все посты треда
// По умолчанию первые 20 постов
postIds := h.repo.FindPostsByThread(id, 0, 20)
posts := make([]esforum.Post, 0, len(postIds))
for _, postId := range postIds {
post, err := h.repo.GetPost(postId)
if err == nil {
posts = append(posts, *post)
}
}
c.JSON(http.StatusOK, posts)
}
// getPost godoc
// @Summary Get post by id
// @Tags post
// @Produce json
// @Param id path int true "id of post"
// @Success 200 {object} esforum.Post
// @Router /api/guest/post/{id} [get]
func (h *Handler) getPost(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
util.NewErrorResponse(c, http.StatusBadRequest, "invalid id format")
return
}
post, err := h.repo.GetPost(id)
if err != nil {
util.NewErrorResponse(c, http.StatusNotFound, "post not found")
return
}
c.JSON(http.StatusOK, post)
}