update api.go, config.yml, handler.go, middleware.go, repository.go
This commit is contained in:
1 parent
43505548f7
commit
f99ac8ffb4
5 files changed
+415
No files matched your search
@@ -0,0 +1,6 @@
|
||||
port: "8013"
|
||||
db:
|
||||
host: "mysql-pl-wa10.joinserver.xyz"
|
||||
port: "3306"
|
||||
username: "u45925_HjjJCz8EVf"
|
||||
dbname: "s45925_tentula"
|
||||
@@ -0,0 +1,286 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"esforum/docs"
|
||||
"esforum/pkg/repository"
|
||||
"esforum/pkg/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
services *service.Service
|
||||
repo *repository.Repository
|
||||
}
|
||||
|
||||
func NewHandler(services *service.Service, repo *repository.Repository) *Handler {
|
||||
return &Handler{services: services, repo: repo}
|
||||
}
|
||||
|
||||
// @title EasyStartup Forum
|
||||
// @version 1.0
|
||||
// @description Forum service for EasyStartup.
|
||||
//
|
||||
// @contact.name API Support
|
||||
// @contact.email red.tea.dev@gmail.com
|
||||
//
|
||||
// @host forum.easystartup.su
|
||||
// @BasePath /api
|
||||
//
|
||||
|
||||
func (h *Handler) InitRoutes() *gin.Engine {
|
||||
router := gin.New()
|
||||
docs.SwaggerInfo.BasePath = "/"
|
||||
|
||||
guest := router.Group("/api/guest")
|
||||
{
|
||||
guest.GET("thread/:id", h.getThread)
|
||||
guest.GET("thread/all", h.getAllThreads)
|
||||
guest.GET("thread/:id/posts", h.getThreadPosts)
|
||||
guest.GET("post/:id", h.getPost)
|
||||
}
|
||||
|
||||
api := router.Group("/api", h.userIdentity)
|
||||
{
|
||||
api.POST("thread", h.create)
|
||||
api.DELETE("thread/:id", h.delete)
|
||||
|
||||
api.POST("post", h.createPost)
|
||||
api.DELETE("post/:id", h.deletePost)
|
||||
}
|
||||
|
||||
router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
|
||||
return router
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"esforum/pkg/util"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
authorizationHeader = "Authorization"
|
||||
userCtx = "UserInfo"
|
||||
userHeader = "UserHeader"
|
||||
)
|
||||
|
||||
func (h *Handler) userIdentity(c *gin.Context) {
|
||||
header := c.GetHeader(authorizationHeader)
|
||||
if header == "" {
|
||||
util.NewErrorResponse(c, http.StatusUnauthorized, "empty auth header")
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.services.Authorization.Authorize(header)
|
||||
if err != nil {
|
||||
util.NewErrorResponse(c, http.StatusUnauthorized, err.Error())
|
||||
}
|
||||
|
||||
c.Set(userCtx, user.Id)
|
||||
c.Set(userHeader, header)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"esforum"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Post interface {
|
||||
CreatePost(title string, content string, thread int64) (*esforum.Post, error)
|
||||
GetPost(id int64) (*esforum.Post, error)
|
||||
DeletePost(id int64) error
|
||||
GetPosts(start, limit int) []int64
|
||||
FindPostsByThread(thread int64, start, limit int) []int64
|
||||
GetPostsCount() int64
|
||||
}
|
||||
|
||||
type Thread interface {
|
||||
CreateThread(title string) (*esforum.Thread, error)
|
||||
GetThread(id int64) (*esforum.Thread, error)
|
||||
FindThread(title string) (*esforum.Thread, error)
|
||||
DeleteThread(id int64) error
|
||||
GetThreads(start, limit int) []int64
|
||||
GetThreadsCount() int64
|
||||
}
|
||||
|
||||
type Repository struct {
|
||||
Post
|
||||
Thread
|
||||
}
|
||||
|
||||
func NewRepository(db *gorm.DB) *Repository {
|
||||
return &Repository{
|
||||
Post: NewPostSchema(db),
|
||||
Thread: NewThreadSchema(db),
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user