✨ Comments API
This commit is contained in:
parent
69ced62715
commit
450c1e4450
46
.air.toml
Normal file
46
.air.toml
Normal file
@ -0,0 +1,46 @@
|
||||
root = "."
|
||||
testdata_dir = "testdata"
|
||||
tmp_dir = "dist"
|
||||
|
||||
[build]
|
||||
args_bin = []
|
||||
bin = "./dist/server"
|
||||
cmd = "go build -o ./dist/server ./pkg/cmd/main.go"
|
||||
delay = 1000
|
||||
exclude_dir = ["assets", "tmp", "vendor", "testdata", "pkg/views"]
|
||||
exclude_file = []
|
||||
exclude_regex = ["_test.go"]
|
||||
exclude_unchanged = false
|
||||
follow_symlink = false
|
||||
full_bin = ""
|
||||
include_dir = []
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
include_file = []
|
||||
kill_delay = "0s"
|
||||
log = "build-errors.log"
|
||||
poll = false
|
||||
poll_interval = 0
|
||||
post_cmd = []
|
||||
pre_cmd = []
|
||||
rerun = false
|
||||
rerun_delay = 500
|
||||
send_interrupt = false
|
||||
stop_on_error = false
|
||||
|
||||
[color]
|
||||
app = ""
|
||||
build = "yellow"
|
||||
main = "magenta"
|
||||
runner = "green"
|
||||
watcher = "cyan"
|
||||
|
||||
[log]
|
||||
main_only = false
|
||||
time = false
|
||||
|
||||
[misc]
|
||||
clean_on_exit = false
|
||||
|
||||
[screen]
|
||||
clear_on_rebuild = false
|
||||
keep_scroll = true
|
5
.gitignore
vendored
5
.gitignore
vendored
@ -1 +1,4 @@
|
||||
/uploads
|
||||
/uploads
|
||||
/dist
|
||||
|
||||
.DS_Store
|
||||
|
@ -22,6 +22,37 @@ func contextComment() *services.PostTypeContext {
|
||||
}
|
||||
}
|
||||
|
||||
func listComment(c *fiber.Ctx) error {
|
||||
take := c.QueryInt("take", 0)
|
||||
offset := c.QueryInt("offset", 0)
|
||||
noReact := c.QueryBool("noReact", false)
|
||||
|
||||
alias := c.Params("postId")
|
||||
|
||||
mx := c.Locals(postContextKey).(*services.PostTypeContext).
|
||||
FilterPublishedAt(time.Now())
|
||||
|
||||
item, err := mx.GetViaAlias(alias)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||
}
|
||||
|
||||
data, err := mx.ListComment(item.ID, take, offset, noReact)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
count, err := mx.CountComment(item.ID)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"count": count,
|
||||
"data": data,
|
||||
})
|
||||
}
|
||||
|
||||
func createComment(c *fiber.Ctx) error {
|
||||
user := c.Locals("principal").(models.Account)
|
||||
|
||||
@ -32,8 +63,6 @@ func createComment(c *fiber.Ctx) error {
|
||||
Categories []models.Category `json:"categories"`
|
||||
Attachments []models.Attachment `json:"attachments"`
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
ArticleID *uint `json:"article_id"`
|
||||
MomentID *uint `json:"moment_id"`
|
||||
ReplyTo uint `json:"reply_to"`
|
||||
}
|
||||
|
||||
@ -55,13 +84,29 @@ func createComment(c *fiber.Ctx) error {
|
||||
Content: data.Content,
|
||||
}
|
||||
|
||||
if data.ArticleID == nil && data.MomentID == nil {
|
||||
postType := c.Params("postType")
|
||||
alias := c.Params("postId")
|
||||
|
||||
var err error
|
||||
var res models.Feed
|
||||
|
||||
switch postType {
|
||||
case "moments":
|
||||
err = database.C.Model(&models.Moment{}).Where("alias = ?", alias).Select("id").First(&res).Error
|
||||
case "articles":
|
||||
err = database.C.Model(&models.Article{}).Where("alias = ?", alias).Select("id").First(&res).Error
|
||||
default:
|
||||
return fiber.NewError(fiber.StatusBadRequest, "comment must belongs to a resource")
|
||||
}
|
||||
if data.ArticleID != nil {
|
||||
var article models.Article
|
||||
if err := database.C.Where("id = ?", data.ArticleID).First(&article).Error; err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("belongs to resource was not found: %v", err))
|
||||
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("belongs to resource was not found: %v", err))
|
||||
} else {
|
||||
switch postType {
|
||||
case "moments":
|
||||
item.MomentID = &res.ID
|
||||
case "articles":
|
||||
item.ArticleID = &res.ID
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,12 +122,11 @@ func createComment(c *fiber.Ctx) error {
|
||||
}
|
||||
}
|
||||
|
||||
item, err := services.NewPost(item)
|
||||
if err != nil {
|
||||
if item, err := services.NewPost(item); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
} else {
|
||||
return c.JSON(item)
|
||||
}
|
||||
|
||||
return c.JSON(item)
|
||||
}
|
||||
|
||||
func editComment(c *fiber.Ctx) error {
|
||||
|
@ -1,9 +1,10 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"code.smartsheep.studio/hydrogen/interactive/pkg/database"
|
||||
"code.smartsheep.studio/hydrogen/interactive/pkg/models"
|
||||
"fmt"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/viper"
|
||||
|
@ -31,7 +31,8 @@ func useDynamicContext(c *fiber.Ctx) error {
|
||||
func getPost(c *fiber.Ctx) error {
|
||||
alias := c.Params("postId")
|
||||
|
||||
mx := c.Locals(postContextKey).(*services.PostTypeContext)
|
||||
mx := c.Locals(postContextKey).(*services.PostTypeContext).
|
||||
FilterPublishedAt(time.Now())
|
||||
|
||||
item, err := mx.GetViaAlias(alias)
|
||||
if err != nil {
|
||||
|
@ -76,6 +76,8 @@ func NewServer() {
|
||||
posts.Get("/", listPost)
|
||||
posts.Get("/:postId", getPost)
|
||||
posts.Post("/:postId/react", authMiddleware, reactPost)
|
||||
posts.Get("/:postId/comments", listComment)
|
||||
posts.Post("/:postId/comments", authMiddleware, createComment)
|
||||
}
|
||||
|
||||
moments := api.Group("/moments").Name("Moments API")
|
||||
@ -94,7 +96,6 @@ func NewServer() {
|
||||
|
||||
comments := api.Group("/p/comments").Name("Comments API")
|
||||
{
|
||||
comments.Post("/", authMiddleware, createComment)
|
||||
comments.Put("/:commentId", authMiddleware, editComment)
|
||||
comments.Delete("/:commentId", authMiddleware, deleteComment)
|
||||
}
|
||||
|
82
pkg/services/comments.go
Normal file
82
pkg/services/comments.go
Normal file
@ -0,0 +1,82 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.smartsheep.studio/hydrogen/interactive/pkg/database"
|
||||
"code.smartsheep.studio/hydrogen/interactive/pkg/models"
|
||||
"github.com/samber/lo"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func (v *PostTypeContext) ListComment(id uint, take int, offset int, noReact ...bool) ([]*models.Feed, error) {
|
||||
if take > 20 {
|
||||
take = 20
|
||||
}
|
||||
|
||||
var items []*models.Feed
|
||||
table := viper.GetString("database.prefix") + "comments"
|
||||
userTable := viper.GetString("database.prefix") + "accounts"
|
||||
if err := v.Tx.
|
||||
Table(table).
|
||||
Select("*, ? as model_type", "comments").
|
||||
Where(v.ColumnName+"_id = ?", id).
|
||||
Joins(fmt.Sprintf("INNER JOIN %s as author ON author_id = author.id", userTable)).
|
||||
Limit(take).Offset(offset).Find(&items).Error; err != nil {
|
||||
return items, err
|
||||
}
|
||||
|
||||
idx := lo.Map(items, func(item *models.Feed, index int) uint {
|
||||
return item.ID
|
||||
})
|
||||
|
||||
if len(noReact) <= 0 || !noReact[0] {
|
||||
var reactions []struct {
|
||||
PostID uint
|
||||
Symbol string
|
||||
Count int64
|
||||
}
|
||||
|
||||
if err := database.C.Model(&models.Reaction{}).
|
||||
Select("comment_id as post_id, symbol, COUNT(id) as count").
|
||||
Where("comment_id IN (?)", idx).
|
||||
Group("post_id, symbol").
|
||||
Scan(&reactions).Error; err != nil {
|
||||
return items, err
|
||||
}
|
||||
|
||||
itemMap := lo.SliceToMap(items, func(item *models.Feed) (uint, *models.Feed) {
|
||||
return item.ID, item
|
||||
})
|
||||
|
||||
list := map[uint]map[string]int64{}
|
||||
for _, info := range reactions {
|
||||
if _, ok := list[info.PostID]; !ok {
|
||||
list[info.PostID] = make(map[string]int64)
|
||||
}
|
||||
list[info.PostID][info.Symbol] = info.Count
|
||||
}
|
||||
|
||||
for k, v := range list {
|
||||
if post, ok := itemMap[k]; ok {
|
||||
post.ReactionList = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func (v *PostTypeContext) CountComment(id uint) (int64, error) {
|
||||
var count int64
|
||||
if err := database.C.
|
||||
Model(&models.Comment{}).
|
||||
Where(v.ColumnName+"_id = ?", id).
|
||||
Where("published_at <= ?", time.Now()).
|
||||
Count(&count).Error; err != nil {
|
||||
return count, err
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
@ -72,9 +72,13 @@ func (v *PostTypeContext) SortCreatedAt(order string) *PostTypeContext {
|
||||
return v
|
||||
}
|
||||
|
||||
func (v *PostTypeContext) GetViaAlias(alias string, noComments ...bool) (models.Feed, error) {
|
||||
func (v *PostTypeContext) GetViaAlias(alias string) (models.Feed, error) {
|
||||
var item models.Feed
|
||||
if err := v.Tx.Where("alias = ?", alias).First(&item).Error; err != nil {
|
||||
table := viper.GetString("database.prefix") + v.TableName
|
||||
if err := v.Tx.
|
||||
Table(table).
|
||||
Where("alias = ?", alias).
|
||||
First(&item).Error; err != nil {
|
||||
return item, err
|
||||
}
|
||||
|
||||
@ -83,7 +87,9 @@ func (v *PostTypeContext) GetViaAlias(alias string, noComments ...bool) (models.
|
||||
|
||||
func (v *PostTypeContext) Get(id uint, noComments ...bool) (models.Feed, error) {
|
||||
var item models.Feed
|
||||
table := viper.GetString("database.prefix") + v.TableName
|
||||
if err := v.Tx.
|
||||
Table(table).
|
||||
Select("*, ? as model_type", v.ColumnName).
|
||||
Where("id = ?", id).First(&item).Error; err != nil {
|
||||
return item, err
|
||||
@ -131,7 +137,9 @@ func (v *PostTypeContext) List(take int, offset int, noReact ...bool) ([]*models
|
||||
}
|
||||
|
||||
var items []*models.Feed
|
||||
table := viper.GetString("database.prefix") + v.TableName
|
||||
if err := v.Tx.
|
||||
Table(table).
|
||||
Select("*, ? as model_type", v.ColumnName).
|
||||
Limit(take).Offset(offset).Find(&items).Error; err != nil {
|
||||
return items, err
|
||||
|
@ -24,12 +24,8 @@
|
||||
|
||||
<v-card title="Reactions" class="mt-3">
|
||||
<div class="px-[1rem] pb-[0.825rem] mt-[-12px]">
|
||||
<post-reaction
|
||||
:item="post"
|
||||
:model="route.params.postType"
|
||||
:reactions="post?.reaction_list ?? {}"
|
||||
@update="updateReactions"
|
||||
/>
|
||||
<post-reaction :item="post" :model="route.params.postType" :reactions="post?.reaction_list ?? {}"
|
||||
@update="updateReactions" />
|
||||
</div>
|
||||
</v-card>
|
||||
</div>
|
||||
@ -51,7 +47,7 @@ const route = useRoute();
|
||||
|
||||
async function readPost() {
|
||||
loading.value = true;
|
||||
const res = await request(`/api/${route.params.postType}/${route.params.alias}?`);
|
||||
const res = await request(`/api/p/${route.params.postType}/${route.params.alias}?`);
|
||||
if (res.status !== 200) {
|
||||
error.value = await res.text();
|
||||
} else {
|
||||
@ -70,4 +66,4 @@ function updateReactions(symbol: string, num: number) {
|
||||
post.value.reaction_list[symbol] = num;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user