✨ 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 {
|
func createComment(c *fiber.Ctx) error {
|
||||||
user := c.Locals("principal").(models.Account)
|
user := c.Locals("principal").(models.Account)
|
||||||
|
|
||||||
@ -32,8 +63,6 @@ func createComment(c *fiber.Ctx) error {
|
|||||||
Categories []models.Category `json:"categories"`
|
Categories []models.Category `json:"categories"`
|
||||||
Attachments []models.Attachment `json:"attachments"`
|
Attachments []models.Attachment `json:"attachments"`
|
||||||
PublishedAt *time.Time `json:"published_at"`
|
PublishedAt *time.Time `json:"published_at"`
|
||||||
ArticleID *uint `json:"article_id"`
|
|
||||||
MomentID *uint `json:"moment_id"`
|
|
||||||
ReplyTo uint `json:"reply_to"`
|
ReplyTo uint `json:"reply_to"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,13 +84,29 @@ func createComment(c *fiber.Ctx) error {
|
|||||||
Content: data.Content,
|
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")
|
return fiber.NewError(fiber.StatusBadRequest, "comment must belongs to a resource")
|
||||||
}
|
}
|
||||||
if data.ArticleID != nil {
|
|
||||||
var article models.Article
|
if err != nil {
|
||||||
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))
|
||||||
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 item, err := services.NewPost(item); err != nil {
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.JSON(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(item)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func editComment(c *fiber.Ctx) error {
|
func editComment(c *fiber.Ctx) error {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"code.smartsheep.studio/hydrogen/interactive/pkg/database"
|
"code.smartsheep.studio/hydrogen/interactive/pkg/database"
|
||||||
"code.smartsheep.studio/hydrogen/interactive/pkg/models"
|
"code.smartsheep.studio/hydrogen/interactive/pkg/models"
|
||||||
"fmt"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
@ -31,7 +31,8 @@ func useDynamicContext(c *fiber.Ctx) error {
|
|||||||
func getPost(c *fiber.Ctx) error {
|
func getPost(c *fiber.Ctx) error {
|
||||||
alias := c.Params("postId")
|
alias := c.Params("postId")
|
||||||
|
|
||||||
mx := c.Locals(postContextKey).(*services.PostTypeContext)
|
mx := c.Locals(postContextKey).(*services.PostTypeContext).
|
||||||
|
FilterPublishedAt(time.Now())
|
||||||
|
|
||||||
item, err := mx.GetViaAlias(alias)
|
item, err := mx.GetViaAlias(alias)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -76,6 +76,8 @@ func NewServer() {
|
|||||||
posts.Get("/", listPost)
|
posts.Get("/", listPost)
|
||||||
posts.Get("/:postId", getPost)
|
posts.Get("/:postId", getPost)
|
||||||
posts.Post("/:postId/react", authMiddleware, reactPost)
|
posts.Post("/:postId/react", authMiddleware, reactPost)
|
||||||
|
posts.Get("/:postId/comments", listComment)
|
||||||
|
posts.Post("/:postId/comments", authMiddleware, createComment)
|
||||||
}
|
}
|
||||||
|
|
||||||
moments := api.Group("/moments").Name("Moments API")
|
moments := api.Group("/moments").Name("Moments API")
|
||||||
@ -94,7 +96,6 @@ func NewServer() {
|
|||||||
|
|
||||||
comments := api.Group("/p/comments").Name("Comments API")
|
comments := api.Group("/p/comments").Name("Comments API")
|
||||||
{
|
{
|
||||||
comments.Post("/", authMiddleware, createComment)
|
|
||||||
comments.Put("/:commentId", authMiddleware, editComment)
|
comments.Put("/:commentId", authMiddleware, editComment)
|
||||||
comments.Delete("/:commentId", authMiddleware, deleteComment)
|
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
|
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
|
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
|
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) {
|
func (v *PostTypeContext) Get(id uint, noComments ...bool) (models.Feed, error) {
|
||||||
var item models.Feed
|
var item models.Feed
|
||||||
|
table := viper.GetString("database.prefix") + v.TableName
|
||||||
if err := v.Tx.
|
if err := v.Tx.
|
||||||
|
Table(table).
|
||||||
Select("*, ? as model_type", v.ColumnName).
|
Select("*, ? as model_type", v.ColumnName).
|
||||||
Where("id = ?", id).First(&item).Error; err != nil {
|
Where("id = ?", id).First(&item).Error; err != nil {
|
||||||
return item, err
|
return item, err
|
||||||
@ -131,7 +137,9 @@ func (v *PostTypeContext) List(take int, offset int, noReact ...bool) ([]*models
|
|||||||
}
|
}
|
||||||
|
|
||||||
var items []*models.Feed
|
var items []*models.Feed
|
||||||
|
table := viper.GetString("database.prefix") + v.TableName
|
||||||
if err := v.Tx.
|
if err := v.Tx.
|
||||||
|
Table(table).
|
||||||
Select("*, ? as model_type", v.ColumnName).
|
Select("*, ? as model_type", v.ColumnName).
|
||||||
Limit(take).Offset(offset).Find(&items).Error; err != nil {
|
Limit(take).Offset(offset).Find(&items).Error; err != nil {
|
||||||
return items, err
|
return items, err
|
||||||
|
@ -24,12 +24,8 @@
|
|||||||
|
|
||||||
<v-card title="Reactions" class="mt-3">
|
<v-card title="Reactions" class="mt-3">
|
||||||
<div class="px-[1rem] pb-[0.825rem] mt-[-12px]">
|
<div class="px-[1rem] pb-[0.825rem] mt-[-12px]">
|
||||||
<post-reaction
|
<post-reaction :item="post" :model="route.params.postType" :reactions="post?.reaction_list ?? {}"
|
||||||
:item="post"
|
@update="updateReactions" />
|
||||||
:model="route.params.postType"
|
|
||||||
:reactions="post?.reaction_list ?? {}"
|
|
||||||
@update="updateReactions"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</v-card>
|
</v-card>
|
||||||
</div>
|
</div>
|
||||||
@ -51,7 +47,7 @@ const route = useRoute();
|
|||||||
|
|
||||||
async function readPost() {
|
async function readPost() {
|
||||||
loading.value = true;
|
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) {
|
if (res.status !== 200) {
|
||||||
error.value = await res.text();
|
error.value = await res.text();
|
||||||
} else {
|
} else {
|
||||||
@ -70,4 +66,4 @@ function updateReactions(symbol: string, num: number) {
|
|||||||
post.value.reaction_list[symbol] = num;
|
post.value.reaction_list[symbol] = num;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user