✨ Count post views
This commit is contained in:
parent
14c17eded8
commit
269e79ca58
@ -15,6 +15,7 @@ var AutoMaintainRange = []any{
|
||||
&models.Poll{},
|
||||
&models.PollAnswer{},
|
||||
&models.PostFlag{},
|
||||
&models.PostView{},
|
||||
}
|
||||
|
||||
func RunMigration(source *gorm.DB) error {
|
||||
|
@ -2,13 +2,14 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/models"
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/services"
|
||||
vocab "github.com/go-ap/activitypub"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/samber/lo"
|
||||
"time"
|
||||
)
|
||||
|
||||
func apGetPublisher(c *fiber.Ctx) error {
|
||||
@ -42,7 +43,7 @@ func apGetPost(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, take, offset, "published_at DESC")
|
||||
items, err := services.ListPost(tx, take, offset, "published_at DESC", nil)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
@ -2,13 +2,14 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||
"git.solsynth.dev/hypernet/passport/pkg/authkit"
|
||||
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||
"gorm.io/gorm"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/gap"
|
||||
@ -114,13 +115,18 @@ func searchPost(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
countTx := tx
|
||||
count, err := services.CountPost(countTx)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, take, offset, "published_at DESC")
|
||||
items, err := services.ListPost(tx, take, offset, "published_at DESC", userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
@ -150,13 +156,18 @@ func listPost(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
countTx := tx
|
||||
count, err := services.CountPost(countTx)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, take, offset, "published_at DESC")
|
||||
items, err := services.ListPost(tx, take, offset, "published_at DESC", userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
@ -222,12 +233,17 @@ func listDraftPost(c *fiber.Ctx) error {
|
||||
|
||||
tx := services.FilterPostWithAuthorDraft(database.C, user.ID)
|
||||
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
count, err := services.CountPost(tx)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, take, offset, "created_at DESC", true)
|
||||
items, err := services.ListPost(tx, take, offset, "created_at DESC", userId, true)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
@ -28,7 +28,12 @@ func listPinnedPost(c *fiber.Ctx) error {
|
||||
tx = tx.Where("publisher_id = ?", user.ID)
|
||||
tx = tx.Where("pinned_at IS NOT NULL")
|
||||
|
||||
items, err := services.ListPost(tx, 100, 0, "published_at DESC")
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, 100, 0, "published_at DESC", userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
@ -25,8 +25,13 @@ func listRecommendation(c *fiber.Ctx) error {
|
||||
return item.ID
|
||||
})
|
||||
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
tx := database.C.Where("id IN ?", postIdx)
|
||||
newPosts, err := services.ListPost(tx, featuredMax, 0, "id ASC")
|
||||
newPosts, err := services.ListPost(tx, featuredMax, 0, "id ASC", userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
@ -65,6 +70,11 @@ func listRecommendationFriends(c *fiber.Ctx) error {
|
||||
|
||||
tx = tx.Where("publisher_id IN ?", friendList)
|
||||
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
countTx := tx
|
||||
count, err := services.CountPost(countTx)
|
||||
if err != nil {
|
||||
@ -76,7 +86,7 @@ func listRecommendationFriends(c *fiber.Ctx) error {
|
||||
order = "published_at DESC, (COALESCE(total_upvote, 0) - COALESCE(total_downvote, 0)) DESC"
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, take, offset, order)
|
||||
items, err := services.ListPost(tx, take, offset, order, userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
@ -106,13 +116,18 @@ func listRecommendationShuffle(c *fiber.Ctx) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
countTx := tx
|
||||
count, err := services.CountPost(countTx)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, take, offset, "RANDOM()")
|
||||
items, err := services.ListPost(tx, take, offset, "RANDOM()", userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
@ -2,9 +2,11 @@ package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/models"
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/services"
|
||||
authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
@ -35,12 +37,17 @@ func listPostReplies(c *fiber.Ctx) error {
|
||||
tx = services.FilterPostWithTag(tx, c.Query("tag"))
|
||||
}
|
||||
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
count, err := services.CountPost(tx)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, take, offset, "published_at DESC")
|
||||
items, err := services.ListPost(tx, take, offset, "published_at DESC", userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
@ -55,6 +62,11 @@ func listPostFeaturedReply(c *fiber.Ctx) error {
|
||||
take := c.QueryInt("take", 0)
|
||||
take = max(1, min(take, 3))
|
||||
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
tx := database.C
|
||||
var post models.Post
|
||||
if err := database.C.Where("id = ?", c.Params("postId")).First(&post).Error; err != nil {
|
||||
@ -78,7 +90,7 @@ func listPostFeaturedReply(c *fiber.Ctx) error {
|
||||
tx = services.FilterPostWithTag(tx, c.Query("tag"))
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, take, 0, "(COALESCE(total_upvote, 0) - COALESCE(total_downvote, 0)) DESC, published_at DESC")
|
||||
items, err := services.ListPost(tx, take, 0, "(COALESCE(total_upvote, 0) - COALESCE(total_downvote, 0)) DESC, published_at DESC", userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
@ -24,6 +24,11 @@ func getWhatsNew(c *fiber.Ctx) error {
|
||||
|
||||
tx = tx.Where("id > ?", pivot)
|
||||
|
||||
var userId *uint
|
||||
if user, authenticated := c.Locals("user").(authm.Account); authenticated {
|
||||
userId = &user.ID
|
||||
}
|
||||
|
||||
countTx := tx
|
||||
count, err := services.CountPost(countTx)
|
||||
if err != nil {
|
||||
@ -35,7 +40,7 @@ func getWhatsNew(c *fiber.Ctx) error {
|
||||
order = "published_at DESC, (COALESCE(total_upvote, 0) - COALESCE(total_downvote, 0)) DESC"
|
||||
}
|
||||
|
||||
items, err := services.ListPost(tx, 10, 0, order)
|
||||
items, err := services.ListPost(tx, 10, 0, order, userId)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
|
@ -56,8 +56,10 @@ type Post struct {
|
||||
PublishedAt *time.Time `json:"published_at"`
|
||||
PublishedUntil *time.Time `json:"published_until"`
|
||||
|
||||
TotalUpvote int `json:"total_upvote"`
|
||||
TotalDownvote int `json:"total_downvote"`
|
||||
TotalUpvote int `json:"total_upvote"`
|
||||
TotalDownvote int `json:"total_downvote"`
|
||||
TotalViews int64 `json:"total_views"`
|
||||
TotalAggressiveViews int64 `json:"total_aggressive_views"`
|
||||
|
||||
PollID *uint `json:"poll_id"`
|
||||
Poll *Poll `json:"poll"`
|
||||
@ -111,3 +113,10 @@ type PostInsight struct {
|
||||
Post Post `json:"post"`
|
||||
PostID uint `json:"post_id"`
|
||||
}
|
||||
|
||||
type PostView struct {
|
||||
AccountID uint `json:"account_id" gorm:"primaryKey"`
|
||||
PostID uint `json:"post_id" gorm:"primaryKey"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
49
pkg/internal/services/post_views.go
Normal file
49
pkg/internal/services/post_views.go
Normal file
@ -0,0 +1,49 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/models"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var postViewQueue []models.PostView
|
||||
|
||||
func AddPostView(post models.Post, account uint) {
|
||||
postViewQueue = append(postViewQueue, models.PostView{
|
||||
AccountID: account,
|
||||
PostID: post.ID,
|
||||
})
|
||||
}
|
||||
|
||||
func AddPostViews(posts []models.Post, account uint) {
|
||||
for _, post := range posts {
|
||||
postViewQueue = append(postViewQueue, models.PostView{
|
||||
AccountID: account,
|
||||
PostID: post.ID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FlushPostViews() {
|
||||
if len(postViewQueue) == 0 {
|
||||
return
|
||||
}
|
||||
workingQueue := make([]models.PostView, len(postViewQueue))
|
||||
copy(workingQueue, postViewQueue)
|
||||
clear(postViewQueue)
|
||||
updateRequiredPost := make(map[uint]bool)
|
||||
for _, item := range workingQueue {
|
||||
updateRequiredPost[item.PostID] = true
|
||||
}
|
||||
_ = database.C.CreateInBatches(workingQueue, 1000).Error
|
||||
for k := range updateRequiredPost {
|
||||
var count int64
|
||||
if err := database.C.Model(&models.PostView{}).Where("post_id = ?", k).Count(&count).Error; err != nil {
|
||||
continue
|
||||
}
|
||||
database.C.Model(&models.Post{}).Where("id = ?", k).Updates(map[string]any{
|
||||
"total_views": count,
|
||||
"total_aggressive_views": gorm.Expr("total_aggressive_views + ?", count),
|
||||
})
|
||||
}
|
||||
}
|
@ -262,7 +262,7 @@ func CountPostReactions(id uint) int64 {
|
||||
return count
|
||||
}
|
||||
|
||||
func ListPost(tx *gorm.DB, take int, offset int, order any, noReact ...bool) ([]*models.Post, error) {
|
||||
func ListPost(tx *gorm.DB, take int, offset int, order any, user *uint, noReact ...bool) ([]*models.Post, error) {
|
||||
if take > 100 {
|
||||
take = 100
|
||||
}
|
||||
@ -332,6 +332,12 @@ func ListPost(tx *gorm.DB, take int, offset int, order any, noReact ...bool) ([]
|
||||
}
|
||||
}
|
||||
|
||||
if user != nil {
|
||||
AddPostViews(lo.Map(items, func(item *models.Post, index int) models.Post {
|
||||
return *item
|
||||
}), *user)
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,15 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
pkg "git.solsynth.dev/hypernet/interactive/pkg/internal"
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/cache"
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/gap"
|
||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||
"github.com/fatih/color"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/database"
|
||||
"git.solsynth.dev/hypernet/interactive/pkg/internal/grpc"
|
||||
@ -67,6 +68,7 @@ func main() {
|
||||
// Configure timed tasks
|
||||
quartz := cron.New(cron.WithLogger(cron.VerbosePrintfLogger(&log.Logger)))
|
||||
quartz.AddFunc("@every 60m", services.DoAutoDatabaseCleanup)
|
||||
quartz.AddFunc("@every 1m", services.FlushPostViews)
|
||||
quartz.Start()
|
||||
|
||||
// Initialize cache
|
||||
|
Loading…
x
Reference in New Issue
Block a user