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