Comments API

This commit is contained in:
LittleSheep 2024-03-06 22:35:10 +08:00
parent 69ced62715
commit 450c1e4450
9 changed files with 207 additions and 25 deletions

46
.air.toml Normal file
View 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

3
.gitignore vendored
View File

@ -1 +1,4 @@
/uploads
/dist
.DS_Store

View File

@ -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 {
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)
}
}
func editComment(c *fiber.Ctx) error {

View File

@ -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"

View File

@ -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 {

View File

@ -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
View 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
}

View File

@ -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

View File

@ -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 {