♻️ Interactive v2 #1
1
go.mod
1
go.mod
@ -24,6 +24,7 @@ require (
|
|||||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
|
github.com/gertd/go-pluralize v0.2.1 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
||||||
|
2
go.sum
2
go.sum
@ -17,6 +17,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
|||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
|
github.com/gertd/go-pluralize v0.2.1 h1:M3uASbVjMnTsPb0PNqg+E/24Vwigyo/tvyMTtAlLgiA=
|
||||||
|
github.com/gertd/go-pluralize v0.2.1/go.mod h1:rbYaKDbsXxmRfr8uygAEKhOWsjyrrqrkHVpZvoOp8zk=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
|
@ -13,9 +13,15 @@ func RunMigration(source *gorm.DB) error {
|
|||||||
&models.RealmMember{},
|
&models.RealmMember{},
|
||||||
&models.Category{},
|
&models.Category{},
|
||||||
&models.Tag{},
|
&models.Tag{},
|
||||||
&models.Post{},
|
&models.Moment{},
|
||||||
&models.PostLike{},
|
&models.MomentLike{},
|
||||||
&models.PostDislike{},
|
&models.MomentDislike{},
|
||||||
|
&models.Article{},
|
||||||
|
&models.ArticleLike{},
|
||||||
|
&models.ArticleDislike{},
|
||||||
|
&models.Comment{},
|
||||||
|
&models.CommentLike{},
|
||||||
|
&models.CommentDislike{},
|
||||||
&models.Attachment{},
|
&models.Attachment{},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -14,10 +14,15 @@ type Account struct {
|
|||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
EmailAddress string `json:"email_address"`
|
EmailAddress string `json:"email_address"`
|
||||||
PowerLevel int `json:"power_level"`
|
PowerLevel int `json:"power_level"`
|
||||||
Posts []Post `json:"posts" gorm:"foreignKey:AuthorID"`
|
Moments []Moment `json:"moments" gorm:"foreignKey:AuthorID"`
|
||||||
|
Articles []Article `json:"articles" gorm:"foreignKey:AuthorID"`
|
||||||
Attachments []Attachment `json:"attachments" gorm:"foreignKey:AuthorID"`
|
Attachments []Attachment `json:"attachments" gorm:"foreignKey:AuthorID"`
|
||||||
LikedPosts []PostLike `json:"liked_posts"`
|
LikedMoments []MomentLike `json:"liked_moments"`
|
||||||
DislikedPosts []PostDislike `json:"disliked_posts"`
|
DislikedMoments []MomentDislike `json:"disliked_moments"`
|
||||||
|
LikedArticles []ArticleLike `json:"liked_articles"`
|
||||||
|
DislikedArticles []ArticleDislike `json:"disliked_articles"`
|
||||||
|
LikedComments []CommentLike `json:"liked_comments"`
|
||||||
|
DislikedComments []CommentDislike `json:"disliked_comments"`
|
||||||
RealmIdentities []RealmMember `json:"identities"`
|
RealmIdentities []RealmMember `json:"identities"`
|
||||||
Realms []Realm `json:"realms"`
|
Realms []Realm `json:"realms"`
|
||||||
ExternalID uint `json:"external_id"`
|
ExternalID uint `json:"external_id"`
|
||||||
|
41
pkg/models/articles.go
Normal file
41
pkg/models/articles.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type Article struct {
|
||||||
|
PostBase
|
||||||
|
|
||||||
|
Title string `json:"title"`
|
||||||
|
Hashtags []Tag `json:"tags" gorm:"many2many:article_tags"`
|
||||||
|
Categories []Category `json:"categories" gorm:"many2many:article_categories"`
|
||||||
|
LikedAccounts []ArticleLike `json:"liked_accounts"`
|
||||||
|
DislikedAccounts []ArticleDislike `json:"disliked_accounts"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
RealmID *uint `json:"realm_id"`
|
||||||
|
Realm *Realm `json:"realm"`
|
||||||
|
|
||||||
|
Comments []Comment `json:"comments" gorm:"foreignKey:ArticleID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Article) GetReplyTo() PostInterface {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Article) GetRepostTo() PostInterface {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Article) GetHashtags() []Tag {
|
||||||
|
return p.Hashtags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Article) GetCategories() []Category {
|
||||||
|
return p.Categories
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Article) SetHashtags(tags []Tag) {
|
||||||
|
p.Hashtags = tags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Article) SetCategories(categories []Category) {
|
||||||
|
p.Categories = categories
|
||||||
|
}
|
@ -6,6 +6,15 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AttachmentType = uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
AttachmentOthers = AttachmentType(iota)
|
||||||
|
AttachmentPhoto
|
||||||
|
AttachmentVideo
|
||||||
|
AttachmentAudio
|
||||||
|
)
|
||||||
|
|
||||||
type Attachment struct {
|
type Attachment struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
@ -13,10 +22,12 @@ type Attachment struct {
|
|||||||
Filesize int64 `json:"filesize"`
|
Filesize int64 `json:"filesize"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
Mimetype string `json:"mimetype"`
|
Mimetype string `json:"mimetype"`
|
||||||
|
Type AttachmentType `json:"type"`
|
||||||
ExternalUrl string `json:"external_url"`
|
ExternalUrl string `json:"external_url"`
|
||||||
Post *Post `json:"post"`
|
|
||||||
Author Account `json:"author"`
|
Author Account `json:"author"`
|
||||||
PostID *uint `json:"post_id"`
|
ArticleID *uint `json:"article_id"`
|
||||||
|
MomentID *uint `json:"moment_id"`
|
||||||
|
CommentID *uint `json:"comment_id"`
|
||||||
AuthorID uint `json:"author_id"`
|
AuthorID uint `json:"author_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,9 @@ type Tag struct {
|
|||||||
Alias string `json:"alias" gorm:"uniqueIndex" validate:"lowercase,alphanum,min=4,max=24"`
|
Alias string `json:"alias" gorm:"uniqueIndex" validate:"lowercase,alphanum,min=4,max=24"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Posts []Post `json:"posts" gorm:"many2many:post_tags"`
|
Articles []Article `json:"articles" gorm:"many2many:article_tags"`
|
||||||
|
Moments []Moment `json:"moments" gorm:"many2many:moment_tags"`
|
||||||
|
Comments []Comment `json:"comments" gorm:"many2many:comment_tags"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Category struct {
|
type Category struct {
|
||||||
@ -15,5 +17,7 @@ type Category struct {
|
|||||||
Alias string `json:"alias" gorm:"uniqueIndex" validate:"lowercase,alphanum,min=4,max=24"`
|
Alias string `json:"alias" gorm:"uniqueIndex" validate:"lowercase,alphanum,min=4,max=24"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Posts []Post `json:"categories" gorm:"many2many:post_categories"`
|
Articles []Article `json:"articles" gorm:"many2many:article_categories"`
|
||||||
|
Moments []Moment `json:"moments" gorm:"many2many:moment_categories"`
|
||||||
|
Comments []Comment `json:"comments" gorm:"many2many:comment_categories"`
|
||||||
}
|
}
|
||||||
|
38
pkg/models/comments.go
Normal file
38
pkg/models/comments.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type Comment struct {
|
||||||
|
PostBase
|
||||||
|
|
||||||
|
Content string `json:"content"`
|
||||||
|
Hashtags []Tag `json:"tags" gorm:"many2many:comment_tags"`
|
||||||
|
Categories []Category `json:"categories" gorm:"many2many:comment_categories"`
|
||||||
|
LikedAccounts []CommentLike `json:"liked_accounts"`
|
||||||
|
DislikedAccounts []CommentDislike `json:"disliked_accounts"`
|
||||||
|
ReplyID *uint `json:"reply_id"`
|
||||||
|
ReplyTo *Comment `json:"reply_to" gorm:"foreignKey:ReplyID"`
|
||||||
|
|
||||||
|
ArticleID *uint `json:"article_id"`
|
||||||
|
MomentID *uint `json:"moment_id"`
|
||||||
|
Article *Article `json:"article"`
|
||||||
|
Moment *Moment `json:"moment"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Comment) GetReplyTo() PostInterface {
|
||||||
|
return p.ReplyTo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Comment) GetHashtags() []Tag {
|
||||||
|
return p.Hashtags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Comment) GetCategories() []Category {
|
||||||
|
return p.Categories
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Comment) SetHashtags(tags []Tag) {
|
||||||
|
p.Hashtags = tags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Comment) SetCategories(categories []Category) {
|
||||||
|
p.Categories = categories
|
||||||
|
}
|
41
pkg/models/moments.go
Normal file
41
pkg/models/moments.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type Moment struct {
|
||||||
|
PostBase
|
||||||
|
|
||||||
|
Content string `json:"content"`
|
||||||
|
Hashtags []Tag `json:"tags" gorm:"many2many:moment_tags"`
|
||||||
|
Categories []Category `json:"categories" gorm:"many2many:moment_categories"`
|
||||||
|
LikedAccounts []MomentLike `json:"liked_accounts"`
|
||||||
|
DislikedAccounts []MomentDislike `json:"disliked_accounts"`
|
||||||
|
RealmID *uint `json:"realm_id"`
|
||||||
|
RepostID *uint `json:"repost_id"`
|
||||||
|
Realm *Realm `json:"realm"`
|
||||||
|
RepostTo *Moment `json:"repost_to" gorm:"foreignKey:RepostID"`
|
||||||
|
|
||||||
|
Comments []Comment `json:"comments" gorm:"foreignKey:MomentID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Moment) GetRepostTo() PostInterface {
|
||||||
|
return p.RepostTo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Moment) GetRealm() *Realm {
|
||||||
|
return p.Realm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Moment) GetHashtags() []Tag {
|
||||||
|
return p.Hashtags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Moment) GetCategories() []Category {
|
||||||
|
return p.Categories
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Moment) SetHashtags(tags []Tag) {
|
||||||
|
p.Hashtags = tags
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Moment) SetCategories(categories []Category) {
|
||||||
|
p.Categories = categories
|
||||||
|
}
|
@ -1,24 +1,24 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type Post struct {
|
type PostReactInfo struct {
|
||||||
|
PostID uint `json:"post_id"`
|
||||||
|
LikeCount int64 `json:"like_count"`
|
||||||
|
DislikeCount int64 `json:"dislike_count"`
|
||||||
|
ReplyCount int64 `json:"reply_count"`
|
||||||
|
RepostCount int64 `json:"repost_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostBase struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
Content string `json:"content"`
|
Alias string `json:"alias" gorm:"uniqueIndex"`
|
||||||
Hashtags []Tag `json:"tags" gorm:"many2many:post_tags"`
|
|
||||||
Categories []Category `json:"categories" gorm:"many2many:post_categories"`
|
|
||||||
Attachments []Attachment `json:"attachments"`
|
Attachments []Attachment `json:"attachments"`
|
||||||
LikedAccounts []PostLike `json:"liked_accounts"`
|
PublishedAt *time.Time `json:"published_at"`
|
||||||
DislikedAccounts []PostDislike `json:"disliked_accounts"`
|
|
||||||
RepostTo *Post `json:"repost_to" gorm:"foreignKey:RepostID"`
|
|
||||||
ReplyTo *Post `json:"reply_to" gorm:"foreignKey:ReplyID"`
|
|
||||||
PinnedAt *time.Time `json:"pinned_at"`
|
|
||||||
EditedAt *time.Time `json:"edited_at"`
|
|
||||||
PublishedAt time.Time `json:"published_at"`
|
|
||||||
RepostID *uint `json:"repost_id"`
|
|
||||||
ReplyID *uint `json:"reply_id"`
|
|
||||||
RealmID *uint `json:"realm_id"`
|
|
||||||
AuthorID uint `json:"author_id"`
|
AuthorID uint `json:"author_id"`
|
||||||
Author Account `json:"author"`
|
Author Account `json:"author"`
|
||||||
|
|
||||||
@ -28,3 +28,44 @@ type Post struct {
|
|||||||
ReplyCount int64 `json:"reply_count" gorm:"-"`
|
ReplyCount int64 `json:"reply_count" gorm:"-"`
|
||||||
RepostCount int64 `json:"repost_count" gorm:"-"`
|
RepostCount int64 `json:"repost_count" gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p PostBase) GetID() uint {
|
||||||
|
return p.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PostBase) GetReplyTo() PostInterface {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PostBase) GetRepostTo() PostInterface {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PostBase) GetAuthor() Account {
|
||||||
|
return p.Author
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PostBase) GetRealm() *Realm {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PostBase) SetReactInfo(info PostReactInfo) {
|
||||||
|
p.LikeCount = info.LikeCount
|
||||||
|
p.DislikeCount = info.DislikeCount
|
||||||
|
p.ReplyCount = info.ReplyCount
|
||||||
|
p.RepostCount = info.RepostCount
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostInterface interface {
|
||||||
|
GetID() uint
|
||||||
|
GetHashtags() []Tag
|
||||||
|
GetCategories() []Category
|
||||||
|
GetReplyTo() PostInterface
|
||||||
|
GetRepostTo() PostInterface
|
||||||
|
GetAuthor() Account
|
||||||
|
GetRealm() *Realm
|
||||||
|
|
||||||
|
SetHashtags([]Tag)
|
||||||
|
SetCategories([]Category)
|
||||||
|
SetReactInfo(PostReactInfo)
|
||||||
|
}
|
||||||
|
@ -2,18 +2,62 @@ package models
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type PostLike struct {
|
type CommentLike struct {
|
||||||
ID uint `json:"id" gorm:"primaryKey"`
|
ID uint `json:"id" gorm:"primaryKey"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
PostID uint `json:"post_id"`
|
ArticleID *uint `json:"article_id"`
|
||||||
|
MomentID *uint `json:"moment_id"`
|
||||||
|
CommentID *uint `json:"comment_id"`
|
||||||
AccountID uint `json:"account_id"`
|
AccountID uint `json:"account_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostDislike struct {
|
type CommentDislike struct {
|
||||||
ID uint `json:"id" gorm:"primaryKey"`
|
ID uint `json:"id" gorm:"primaryKey"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
PostID uint `json:"post_id"`
|
ArticleID *uint `json:"article_id"`
|
||||||
|
MomentID *uint `json:"moment_id"`
|
||||||
|
CommentID *uint `json:"comment_id"`
|
||||||
|
AccountID uint `json:"account_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MomentLike struct {
|
||||||
|
ID uint `json:"id" gorm:"primaryKey"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
ArticleID *uint `json:"article_id"`
|
||||||
|
MomentID *uint `json:"moment_id"`
|
||||||
|
CommentID *uint `json:"comment_id"`
|
||||||
|
AccountID uint `json:"account_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type MomentDislike struct {
|
||||||
|
ID uint `json:"id" gorm:"primaryKey"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
ArticleID *uint `json:"article_id"`
|
||||||
|
MomentID *uint `json:"moment_id"`
|
||||||
|
CommentID *uint `json:"comment_id"`
|
||||||
|
AccountID uint `json:"account_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArticleLike struct {
|
||||||
|
ID uint `json:"id" gorm:"primaryKey"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
ArticleID *uint `json:"article_id"`
|
||||||
|
MomentID *uint `json:"moment_id"`
|
||||||
|
CommentID *uint `json:"comment_id"`
|
||||||
|
AccountID uint `json:"account_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArticleDislike struct {
|
||||||
|
ID uint `json:"id" gorm:"primaryKey"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
ArticleID *uint `json:"article_id"`
|
||||||
|
MomentID *uint `json:"moment_id"`
|
||||||
|
CommentID *uint `json:"comment_id"`
|
||||||
AccountID uint `json:"account_id"`
|
AccountID uint `json:"account_id"`
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@ type Realm struct {
|
|||||||
|
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Posts []Post `json:"posts"`
|
Articles []Article `json:"article"`
|
||||||
|
Moments []Moment `json:"moments"`
|
||||||
Members []RealmMember `json:"members"`
|
Members []RealmMember `json:"members"`
|
||||||
IsPublic bool `json:"is_public"`
|
IsPublic bool `json:"is_public"`
|
||||||
AccountID uint `json:"account_id"`
|
AccountID uint `json:"account_id"`
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func listCategroies(c *fiber.Ctx) error {
|
func listCategories(c *fiber.Ctx) error {
|
||||||
categories, err := services.ListCategory()
|
categories, err := services.ListCategory()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
@ -1,93 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"code.smartsheep.studio/hydrogen/interactive/pkg/database"
|
|
||||||
"code.smartsheep.studio/hydrogen/interactive/pkg/models"
|
|
||||||
"code.smartsheep.studio/hydrogen/interactive/pkg/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getOwnPost(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
|
|
||||||
id, _ := c.ParamsInt("postId", 0)
|
|
||||||
take := c.QueryInt("take", 0)
|
|
||||||
offset := c.QueryInt("offset", 0)
|
|
||||||
|
|
||||||
tx := database.C.Where(&models.Post{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
|
||||||
AuthorID: user.ID,
|
|
||||||
})
|
|
||||||
|
|
||||||
post, err := services.GetPost(tx)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
tx = database.C.
|
|
||||||
Where(&models.Post{ReplyID: &post.ID}).
|
|
||||||
Where("published_at <= ? OR published_at IS NULL", time.Now()).
|
|
||||||
Order("created_at desc")
|
|
||||||
|
|
||||||
var count int64
|
|
||||||
if err := tx.
|
|
||||||
Model(&models.Post{}).
|
|
||||||
Count(&count).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
posts, err := services.ListPost(tx, take, offset)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
|
||||||
"data": post,
|
|
||||||
"count": count,
|
|
||||||
"related": posts,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func listOwnPost(c *fiber.Ctx) error {
|
|
||||||
take := c.QueryInt("take", 0)
|
|
||||||
offset := c.QueryInt("offset", 0)
|
|
||||||
realmId := c.QueryInt("realmId", 0)
|
|
||||||
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
|
|
||||||
tx := database.C.
|
|
||||||
Where(&models.Post{AuthorID: user.ID}).
|
|
||||||
Where("published_at <= ? OR published_at IS NULL", time.Now()).
|
|
||||||
Order("created_at desc")
|
|
||||||
|
|
||||||
if realmId > 0 {
|
|
||||||
tx = tx.Where(&models.Post{RealmID: lo.ToPtr(uint(realmId))})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.Query("category")) > 0 {
|
|
||||||
tx = services.FilterPostWithCategory(tx, c.Query("category"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(c.Query("tag")) > 0 {
|
|
||||||
tx = services.FilterPostWithTag(tx, c.Query("tag"))
|
|
||||||
}
|
|
||||||
|
|
||||||
var count int64
|
|
||||||
if err := tx.
|
|
||||||
Model(&models.Post{}).
|
|
||||||
Count(&count).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
posts, err := services.ListPost(tx, take, offset)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
|
||||||
"count": count,
|
|
||||||
"data": posts,
|
|
||||||
})
|
|
||||||
}
|
|
@ -12,105 +12,81 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getPost(c *fiber.Ctx) error {
|
func getMomentContext() *services.PostTypeContext[models.Moment] {
|
||||||
id, _ := c.ParamsInt("postId", 0)
|
return &services.PostTypeContext[models.Moment]{
|
||||||
take := c.QueryInt("take", 0)
|
Tx: database.C,
|
||||||
offset := c.QueryInt("offset", 0)
|
TypeName: "Moment",
|
||||||
|
CanReply: false,
|
||||||
|
CanRepost: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tx := database.C.Where(&models.Post{
|
func getMoment(c *fiber.Ctx) error {
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
id, _ := c.ParamsInt("momentId", 0)
|
||||||
}).Where("published_at <= ? OR published_at IS NULL", time.Now())
|
|
||||||
|
|
||||||
post, err := services.GetPost(tx)
|
mx := getMomentContext().FilterPublishedAt(time.Now())
|
||||||
|
|
||||||
|
item, err := mx.Get(uint(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
tx = database.C.
|
return c.JSON(item)
|
||||||
Where(&models.Post{ReplyID: &post.ID}).
|
|
||||||
Where("published_at <= ? OR published_at IS NULL", time.Now()).
|
|
||||||
Order("created_at desc")
|
|
||||||
|
|
||||||
var count int64
|
|
||||||
if err := tx.
|
|
||||||
Model(&models.Post{}).
|
|
||||||
Count(&count).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
posts, err := services.ListPost(tx, take, offset)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
|
||||||
"data": post,
|
|
||||||
"count": count,
|
|
||||||
"related": posts,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func listPost(c *fiber.Ctx) error {
|
func listMoment(c *fiber.Ctx) error {
|
||||||
take := c.QueryInt("take", 0)
|
take := c.QueryInt("take", 0)
|
||||||
offset := c.QueryInt("offset", 0)
|
offset := c.QueryInt("offset", 0)
|
||||||
|
|
||||||
realmId := c.QueryInt("realmId", 0)
|
realmId := c.QueryInt("realmId", 0)
|
||||||
|
|
||||||
tx := database.C.
|
mx := getMomentContext().
|
||||||
Where("published_at <= ? OR published_at IS NULL", time.Now()).
|
FilterPublishedAt(time.Now()).
|
||||||
Order("created_at desc")
|
FilterRealm(uint(realmId)).
|
||||||
|
SortCreatedAt("desc")
|
||||||
if realmId > 0 {
|
|
||||||
tx = tx.Where(&models.Post{RealmID: lo.ToPtr(uint(realmId))})
|
|
||||||
} else {
|
|
||||||
tx = tx.Where("realm_id IS NULL")
|
|
||||||
}
|
|
||||||
|
|
||||||
var author models.Account
|
var author models.Account
|
||||||
if len(c.Query("authorId")) > 0 {
|
if len(c.Query("authorId")) > 0 {
|
||||||
if err := database.C.Where(&models.Account{Name: c.Query("authorId")}).First(&author).Error; err != nil {
|
if err := database.C.Where(&models.Account{Name: c.Query("authorId")}).First(&author).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
tx = tx.Where(&models.Post{AuthorID: author.ID})
|
mx = mx.FilterAuthor(author.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Query("category")) > 0 {
|
if len(c.Query("category")) > 0 {
|
||||||
tx = services.FilterPostWithCategory(tx, c.Query("category"))
|
mx = mx.FilterWithCategory(c.Query("category"))
|
||||||
}
|
}
|
||||||
if len(c.Query("tag")) > 0 {
|
if len(c.Query("tag")) > 0 {
|
||||||
tx = services.FilterPostWithTag(tx, c.Query("tag"))
|
mx = mx.FilterWithTag(c.Query("tag"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.QueryBool("reply", true) {
|
if !c.QueryBool("reply", true) {
|
||||||
tx = tx.Where("reply_id IS NULL")
|
mx = mx.FilterReply(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
var count int64
|
count, err := mx.Count()
|
||||||
if err := tx.
|
if err != nil {
|
||||||
Model(&models.Post{}).
|
|
||||||
Count(&count).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
posts, err := services.ListPost(tx, take, offset)
|
items, err := mx.List(take, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"count": count,
|
"count": count,
|
||||||
"data": posts,
|
"data": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createPost(c *fiber.Ctx) error {
|
func createMoment(c *fiber.Ctx) error {
|
||||||
user := c.Locals("principal").(models.Account)
|
user := c.Locals("principal").(models.Account)
|
||||||
|
|
||||||
var data struct {
|
var data struct {
|
||||||
Alias string `json:"alias"`
|
Alias string `json:"alias"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Content string `json:"content" validate:"required"`
|
Content string `json:"content" validate:"required"`
|
||||||
Tags []models.Tag `json:"tags"`
|
Hashtags []models.Tag `json:"hashtags"`
|
||||||
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"`
|
||||||
@ -125,28 +101,30 @@ func createPost(c *fiber.Ctx) error {
|
|||||||
data.Alias = strings.ReplaceAll(uuid.NewString(), "-", "")
|
data.Alias = strings.ReplaceAll(uuid.NewString(), "-", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
var repostTo *uint = nil
|
mx := getMomentContext()
|
||||||
var replyTo *uint = nil
|
|
||||||
|
item := models.Moment{
|
||||||
|
PostBase: models.PostBase{
|
||||||
|
Alias: data.Alias,
|
||||||
|
Attachments: data.Attachments,
|
||||||
|
PublishedAt: data.PublishedAt,
|
||||||
|
AuthorID: user.ID,
|
||||||
|
},
|
||||||
|
Hashtags: data.Hashtags,
|
||||||
|
Categories: data.Categories,
|
||||||
|
Content: data.Content,
|
||||||
|
RealmID: data.RealmID,
|
||||||
|
}
|
||||||
|
|
||||||
var relatedCount int64
|
var relatedCount int64
|
||||||
if data.RepostTo > 0 {
|
if data.RepostTo > 0 {
|
||||||
if err := database.C.Where(&models.Post{
|
if err := database.C.Where("id = ?", data.RepostTo).
|
||||||
BaseModel: models.BaseModel{ID: data.RepostTo},
|
Model(&models.Moment{}).Count(&relatedCount).Error; err != nil {
|
||||||
}).Model(&models.Post{}).Count(&relatedCount).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else if relatedCount <= 0 {
|
} else if relatedCount <= 0 {
|
||||||
return fiber.NewError(fiber.StatusNotFound, "related post was not found")
|
return fiber.NewError(fiber.StatusNotFound, "related post was not found")
|
||||||
} else {
|
} else {
|
||||||
repostTo = &data.RepostTo
|
item.RepostID = &data.RepostTo
|
||||||
}
|
|
||||||
} else if data.ReplyTo > 0 {
|
|
||||||
if err := database.C.Where(&models.Post{
|
|
||||||
BaseModel: models.BaseModel{ID: data.ReplyTo},
|
|
||||||
}).Model(&models.Post{}).Count(&relatedCount).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else if relatedCount <= 0 {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, "related post was not found")
|
|
||||||
} else {
|
|
||||||
replyTo = &data.ReplyTo
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,34 +137,23 @@ func createPost(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post, err := services.NewPost(
|
item, err := mx.New(item)
|
||||||
user,
|
|
||||||
realm,
|
|
||||||
data.Content,
|
|
||||||
data.Attachments,
|
|
||||||
data.Categories,
|
|
||||||
data.Tags,
|
|
||||||
data.PublishedAt,
|
|
||||||
replyTo,
|
|
||||||
repostTo,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(post)
|
return c.JSON(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func editPost(c *fiber.Ctx) error {
|
func editMoment(c *fiber.Ctx) error {
|
||||||
user := c.Locals("principal").(models.Account)
|
user := c.Locals("principal").(models.Account)
|
||||||
id, _ := c.ParamsInt("postId", 0)
|
id, _ := c.ParamsInt("momentId", 0)
|
||||||
|
|
||||||
var data struct {
|
var data struct {
|
||||||
Alias string `json:"alias" validate:"required"`
|
Alias string `json:"alias" validate:"required"`
|
||||||
Title string `json:"title"`
|
|
||||||
Content string `json:"content" validate:"required"`
|
Content string `json:"content" validate:"required"`
|
||||||
PublishedAt *time.Time `json:"published_at"`
|
PublishedAt *time.Time `json:"published_at"`
|
||||||
Tags []models.Tag `json:"tags"`
|
Hashtags []models.Tag `json:"hashtags"`
|
||||||
Categories []models.Category `json:"categories"`
|
Categories []models.Category `json:"categories"`
|
||||||
Attachments []models.Attachment `json:"attachments"`
|
Attachments []models.Attachment `json:"attachments"`
|
||||||
}
|
}
|
||||||
@ -195,49 +162,48 @@ func editPost(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var post models.Post
|
mx := getMomentContext().FilterAuthor(user.ID)
|
||||||
if err := database.C.Where(&models.Post{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
item, err := mx.Get(uint(id), true)
|
||||||
AuthorID: user.ID,
|
if err != nil {
|
||||||
}).First(&post).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
post, err := services.EditPost(
|
item.Alias = data.Alias
|
||||||
post,
|
item.Content = data.Content
|
||||||
data.Content,
|
item.PublishedAt = data.PublishedAt
|
||||||
data.PublishedAt,
|
item.Hashtags = data.Hashtags
|
||||||
data.Categories,
|
item.Categories = data.Categories
|
||||||
data.Tags,
|
item.Attachments = data.Attachments
|
||||||
data.Attachments,
|
|
||||||
)
|
item, err = mx.Edit(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(post)
|
return c.JSON(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
func reactPost(c *fiber.Ctx) error {
|
func reactMoment(c *fiber.Ctx) error {
|
||||||
user := c.Locals("principal").(models.Account)
|
user := c.Locals("principal").(models.Account)
|
||||||
id, _ := c.ParamsInt("postId", 0)
|
id, _ := c.ParamsInt("momentId", 0)
|
||||||
|
|
||||||
var post models.Post
|
mx := getMomentContext()
|
||||||
if err := database.C.Where(&models.Post{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
item, err := mx.Get(uint(id), true)
|
||||||
}).First(&post).Error; err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
switch strings.ToLower(c.Params("reactType")) {
|
switch strings.ToLower(c.Params("reactType")) {
|
||||||
case "like":
|
case "like":
|
||||||
if positive, err := services.LikePost(user, post); err != nil {
|
if positive, err := mx.ReactLike(user, item.ID); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
return c.SendStatus(lo.Ternary(positive, fiber.StatusCreated, fiber.StatusNoContent))
|
return c.SendStatus(lo.Ternary(positive, fiber.StatusCreated, fiber.StatusNoContent))
|
||||||
}
|
}
|
||||||
case "dislike":
|
case "dislike":
|
||||||
if positive, err := services.DislikePost(user, post); err != nil {
|
if positive, err := mx.ReactDislike(user, item.ID); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
return c.SendStatus(lo.Ternary(positive, fiber.StatusCreated, fiber.StatusNoContent))
|
return c.SendStatus(lo.Ternary(positive, fiber.StatusCreated, fiber.StatusNoContent))
|
||||||
@ -247,19 +213,18 @@ func reactPost(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func deletePost(c *fiber.Ctx) error {
|
func deleteMoment(c *fiber.Ctx) error {
|
||||||
user := c.Locals("principal").(models.Account)
|
user := c.Locals("principal").(models.Account)
|
||||||
id, _ := c.ParamsInt("postId", 0)
|
id, _ := c.ParamsInt("momentId", 0)
|
||||||
|
|
||||||
var post models.Post
|
mx := getMomentContext().FilterAuthor(user.ID)
|
||||||
if err := database.C.Where(&models.Post{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
item, err := mx.Get(uint(id), true)
|
||||||
AuthorID: user.ID,
|
if err != nil {
|
||||||
}).First(&post).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := services.DeletePost(post); err != nil {
|
if err := mx.Delete(item); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
|
@ -69,21 +69,23 @@ func NewServer() {
|
|||||||
}), openAttachment)
|
}), openAttachment)
|
||||||
api.Post("/attachments", authMiddleware, uploadAttachment)
|
api.Post("/attachments", authMiddleware, uploadAttachment)
|
||||||
|
|
||||||
api.Get("/posts", listPost)
|
// TODO Feed (aka. Union source)
|
||||||
api.Get("/posts/:postId", getPost)
|
|
||||||
api.Post("/posts", authMiddleware, createPost)
|
|
||||||
api.Post("/posts/:postId/react/:reactType", authMiddleware, reactPost)
|
|
||||||
api.Put("/posts/:postId", authMiddleware, editPost)
|
|
||||||
api.Delete("/posts/:postId", authMiddleware, deletePost)
|
|
||||||
|
|
||||||
api.Get("/categories", listCategroies)
|
moments := api.Group("/moments").Name("Moments API")
|
||||||
|
{
|
||||||
|
moments.Get("/", listMoment)
|
||||||
|
moments.Get("/:momentId", getMoment)
|
||||||
|
moments.Post("/", authMiddleware, createMoment)
|
||||||
|
moments.Post("/:momentId/react/:reactType", authMiddleware, reactMoment)
|
||||||
|
moments.Put("/:momentId", authMiddleware, editMoment)
|
||||||
|
moments.Delete("/:momentId", authMiddleware, deleteMoment)
|
||||||
|
}
|
||||||
|
|
||||||
|
api.Get("/categories", listCategories)
|
||||||
api.Post("/categories", authMiddleware, newCategory)
|
api.Post("/categories", authMiddleware, newCategory)
|
||||||
api.Put("/categories/:categoryId", authMiddleware, editCategory)
|
api.Put("/categories/:categoryId", authMiddleware, editCategory)
|
||||||
api.Delete("/categories/:categoryId", authMiddleware, deleteCategory)
|
api.Delete("/categories/:categoryId", authMiddleware, deleteCategory)
|
||||||
|
|
||||||
api.Get("/creators/posts", authMiddleware, listOwnPost)
|
|
||||||
api.Get("/creators/posts/:postId", authMiddleware, getOwnPost)
|
|
||||||
|
|
||||||
api.Get("/realms", listRealm)
|
api.Get("/realms", listRealm)
|
||||||
api.Get("/realms/me", authMiddleware, listOwnedRealm)
|
api.Get("/realms/me", authMiddleware, listOwnedRealm)
|
||||||
api.Get("/realms/me/available", authMiddleware, listAvailableRealm)
|
api.Get("/realms/me/available", authMiddleware, listAvailableRealm)
|
||||||
|
@ -14,7 +14,7 @@ func NewAttachment(user models.Account, header *multipart.FileHeader) (models.At
|
|||||||
Filesize: header.Size,
|
Filesize: header.Size,
|
||||||
Filename: header.Filename,
|
Filename: header.Filename,
|
||||||
Mimetype: "unknown/unknown",
|
Mimetype: "unknown/unknown",
|
||||||
PostID: nil,
|
Type: models.AttachmentOthers,
|
||||||
AuthorID: user.ID,
|
AuthorID: user.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
pkg/services/moments.go
Normal file
1
pkg/services/moments.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package services
|
@ -2,112 +2,191 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"code.smartsheep.studio/hydrogen/identity/pkg/grpc/proto"
|
"code.smartsheep.studio/hydrogen/identity/pkg/grpc/proto"
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
|
|
||||||
"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"
|
||||||
|
pluralize "github.com/gertd/go-pluralize"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PreloadRelatedPost(tx *gorm.DB) *gorm.DB {
|
const (
|
||||||
return tx.
|
reactUnionSelect = `SELECT t.id AS post_id,
|
||||||
Preload("Author").
|
|
||||||
Preload("Attachments").
|
|
||||||
Preload("Categories").
|
|
||||||
Preload("Hashtags").
|
|
||||||
Preload("RepostTo").
|
|
||||||
Preload("ReplyTo").
|
|
||||||
Preload("RepostTo.Author").
|
|
||||||
Preload("ReplyTo.Author").
|
|
||||||
Preload("RepostTo.Attachments").
|
|
||||||
Preload("ReplyTo.Attachments").
|
|
||||||
Preload("RepostTo.Categories").
|
|
||||||
Preload("ReplyTo.Categories").
|
|
||||||
Preload("RepostTo.Hashtags").
|
|
||||||
Preload("ReplyTo.Hashtags")
|
|
||||||
}
|
|
||||||
|
|
||||||
func FilterPostWithCategory(tx *gorm.DB, alias string) *gorm.DB {
|
|
||||||
prefix := viper.GetString("database.prefix")
|
|
||||||
return tx.Joins(fmt.Sprintf("JOIN %spost_categories ON %sposts.id = %spost_categories.post_id", prefix, prefix, prefix)).
|
|
||||||
Joins(fmt.Sprintf("JOIN %scategories ON %scategories.id = %spost_categories.category_id", prefix, prefix, prefix)).
|
|
||||||
Where(fmt.Sprintf("%scategories.alias = ?", prefix), alias)
|
|
||||||
}
|
|
||||||
|
|
||||||
func FilterPostWithTag(tx *gorm.DB, alias string) *gorm.DB {
|
|
||||||
prefix := viper.GetString("database.prefix")
|
|
||||||
return tx.Joins(fmt.Sprintf("JOIN %spost_tags ON %sposts.id = %spost_tags.post_id", prefix, prefix, prefix)).
|
|
||||||
Joins(fmt.Sprintf("JOIN %stags ON %stags.id = %spost_tags.tag_id", prefix, prefix, prefix)).
|
|
||||||
Where(fmt.Sprintf("%stags.alias = ?", prefix), alias)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetPost(tx *gorm.DB) (*models.Post, error) {
|
|
||||||
var post *models.Post
|
|
||||||
if err := PreloadRelatedPost(tx).First(&post).Error; err != nil {
|
|
||||||
return post, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var reactInfo struct {
|
|
||||||
PostID uint `json:"post_id"`
|
|
||||||
LikeCount int64 `json:"like_count"`
|
|
||||||
DislikeCount int64 `json:"dislike_count"`
|
|
||||||
ReplyCount int64 `json:"reply_count"`
|
|
||||||
RepostCount int64 `json:"repost_count"`
|
|
||||||
}
|
|
||||||
|
|
||||||
prefix := viper.GetString("database.prefix")
|
|
||||||
database.C.Raw(fmt.Sprintf(`
|
|
||||||
SELECT t.id as post_id,
|
|
||||||
COALESCE(l.like_count, 0) AS like_count,
|
COALESCE(l.like_count, 0) AS like_count,
|
||||||
COALESCE(d.dislike_count, 0) AS dislike_count,
|
COALESCE(d.dislike_count, 0) AS dislike_count--!COMMA!--
|
||||||
COALESCE(r.reply_count, 0) AS reply_count,
|
--!REPLY_UNION_COLUMN!-- --!BOTH_COMMA!--
|
||||||
COALESCE(rp.repost_count, 0) AS repost_count
|
--!REPOST_UNION_COLUMN!--
|
||||||
FROM %sposts t
|
FROM %s t
|
||||||
LEFT JOIN (SELECT post_id, COUNT(*) AS like_count
|
LEFT JOIN (SELECT %s_id, COUNT(*) AS like_count
|
||||||
FROM %spost_likes
|
FROM %s_likes
|
||||||
GROUP BY post_id) l ON t.id = l.post_id
|
GROUP BY %s_id) l ON t.id = l.%s_id
|
||||||
LEFT JOIN (SELECT post_id, COUNT(*) AS dislike_count
|
LEFT JOIN (SELECT %s_id, COUNT(*) AS dislike_count
|
||||||
FROM %spost_dislikes
|
FROM %s_likes
|
||||||
GROUP BY post_id) d ON t.id = d.post_id
|
GROUP BY %s_id) d ON t.id = d.%s_id
|
||||||
LEFT JOIN (SELECT reply_id, COUNT(*) AS reply_count
|
--!REPLY_UNION_SELECT!--
|
||||||
FROM %sposts
|
--!REPOST_UNION_SELECT!--
|
||||||
|
WHERE t.id = ?`
|
||||||
|
// TODO Solve for the cross table query(like articles -> comments)
|
||||||
|
replyUnionColumn = `COALESCE(r.reply_count, 0) AS reply_count`
|
||||||
|
replyUnionSelect = `LEFT JOIN (SELECT reply_id, COUNT(*) AS reply_count
|
||||||
|
FROM %s
|
||||||
WHERE reply_id IS NOT NULL
|
WHERE reply_id IS NOT NULL
|
||||||
GROUP BY reply_id) r ON t.id = r.reply_id
|
GROUP BY reply_id) r ON t.id = r.reply_id`
|
||||||
LEFT JOIN (SELECT repost_id, COUNT(*) AS repost_count
|
repostUnionColumn = `COALESCE(rp.repost_count, 0) AS repost_count`
|
||||||
FROM %sposts
|
repostUnionSelect = `LEFT JOIN (SELECT repost_id, COUNT(*) AS repost_count
|
||||||
|
FROM %s
|
||||||
WHERE repost_id IS NOT NULL
|
WHERE repost_id IS NOT NULL
|
||||||
GROUP BY repost_id) rp ON t.id = rp.repost_id
|
GROUP BY repost_id) rp ON t.id = rp.repost_id`
|
||||||
WHERE t.id = ?`, prefix, prefix, prefix, prefix, prefix), post.ID).Scan(&reactInfo)
|
)
|
||||||
|
|
||||||
post.LikeCount = reactInfo.LikeCount
|
type PostTypeContext[T models.PostInterface] struct {
|
||||||
post.DislikeCount = reactInfo.DislikeCount
|
Tx *gorm.DB
|
||||||
post.ReplyCount = reactInfo.ReplyCount
|
|
||||||
post.RepostCount = reactInfo.RepostCount
|
|
||||||
|
|
||||||
return post, nil
|
TypeName string
|
||||||
|
CanReply bool
|
||||||
|
CanRepost bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListPost(tx *gorm.DB, take int, offset int) ([]*models.Post, error) {
|
var pluralizeHelper = pluralize.NewClient()
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) GetTableName(plural ...bool) string {
|
||||||
|
if len(plural) <= 0 || !plural[0] {
|
||||||
|
return strings.ToLower(v.TypeName)
|
||||||
|
} else {
|
||||||
|
return pluralizeHelper.Plural(strings.ToLower(v.TypeName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) Preload() *PostTypeContext[T] {
|
||||||
|
v.Tx.Preload("Author").Preload("Attachments").Preload("Categories").Preload("Hashtags")
|
||||||
|
|
||||||
|
if v.CanReply {
|
||||||
|
v.Tx.Preload("ReplyTo")
|
||||||
|
}
|
||||||
|
if v.CanRepost {
|
||||||
|
v.Tx.Preload("RepostTo")
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) FilterWithCategory(alias string) *PostTypeContext[T] {
|
||||||
|
table := v.GetTableName()
|
||||||
|
v.Tx.Joins(fmt.Sprintf("JOIN %s_categories ON %s.id = %s_categories.%s_id", table, v.GetTableName(true), table, v.GetTableName())).
|
||||||
|
Joins(fmt.Sprintf("JOIN %s_categories ON %s_categories.id = %s_categories.category_id", table, table, table)).
|
||||||
|
Where(table+"_categories.alias = ?", alias)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) FilterWithTag(alias string) *PostTypeContext[T] {
|
||||||
|
table := v.GetTableName()
|
||||||
|
v.Tx.Joins(fmt.Sprintf("JOIN %s_tags ON %s.id = %s_tags.%s_id", table, v.GetTableName(true), table, v.GetTableName())).
|
||||||
|
Joins(fmt.Sprintf("JOIN %s_tags ON %s_tags.id = %s_tags.category_id", table, table, table)).
|
||||||
|
Where(table+"_tags.alias = ?", alias)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) FilterPublishedAt(date time.Time) *PostTypeContext[T] {
|
||||||
|
v.Tx.Where("published_at <= ? AND published_at IS NULL", date)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) FilterRealm(id uint) *PostTypeContext[T] {
|
||||||
|
if id > 0 {
|
||||||
|
v.Tx = v.Tx.Where("realm_id = ?", id)
|
||||||
|
} else {
|
||||||
|
v.Tx = v.Tx.Where("realm_id IS NULL")
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) FilterAuthor(id uint) *PostTypeContext[T] {
|
||||||
|
v.Tx = v.Tx.Where("author_id = ?", id)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) FilterReply(condition bool) *PostTypeContext[T] {
|
||||||
|
if condition {
|
||||||
|
v.Tx = v.Tx.Where("reply_id IS NOT NULL")
|
||||||
|
} else {
|
||||||
|
v.Tx = v.Tx.Where("reply_id IS NULL")
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) SortCreatedAt(order string) *PostTypeContext[T] {
|
||||||
|
v.Tx.Order(fmt.Sprintf("created_at %s", order))
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) BuildReactInfoSql() string {
|
||||||
|
column := strings.ToLower(v.TypeName)
|
||||||
|
table := viper.GetString("database.prefix") + v.GetTableName()
|
||||||
|
pluralTable := viper.GetString("database.prefix") + v.GetTableName(true)
|
||||||
|
sql := fmt.Sprintf(reactUnionSelect, pluralTable, column, table, column, column, column, table, column, column)
|
||||||
|
|
||||||
|
if v.CanReply {
|
||||||
|
sql = strings.Replace(sql, "--!REPLY_UNION_COLUMN!--", replyUnionColumn, 1)
|
||||||
|
sql = strings.Replace(sql, "--!REPLY_UNION_SELECT!--", fmt.Sprintf(replyUnionSelect, pluralTable), 1)
|
||||||
|
}
|
||||||
|
if v.CanRepost {
|
||||||
|
sql = strings.Replace(sql, "--!REPOST_UNION_COLUMN!--", repostUnionColumn, 1)
|
||||||
|
sql = strings.Replace(sql, "--!REPOST_UNION_SELECT!--", fmt.Sprintf(repostUnionSelect, pluralTable), 1)
|
||||||
|
}
|
||||||
|
if v.CanReply || v.CanRepost {
|
||||||
|
sql = strings.ReplaceAll(sql, "--!COMMA!--", ",")
|
||||||
|
}
|
||||||
|
if v.CanReply && v.CanRepost {
|
||||||
|
sql = strings.ReplaceAll(sql, "--!BOTH_COMMA!--", ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) Get(id uint, noReact ...bool) (T, error) {
|
||||||
|
var item T
|
||||||
|
if err := v.Preload().Tx.Where("id = ?", id).First(&item).Error; err != nil {
|
||||||
|
return item, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var reactInfo models.PostReactInfo
|
||||||
|
|
||||||
|
if len(noReact) <= 0 || !noReact[0] {
|
||||||
|
sql := v.BuildReactInfoSql()
|
||||||
|
database.C.Raw(sql, item.GetID()).Scan(&reactInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
item.SetReactInfo(reactInfo)
|
||||||
|
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) Count() (int64, error) {
|
||||||
|
var count int64
|
||||||
|
table := viper.GetString("database.prefix") + v.GetTableName(true)
|
||||||
|
if err := v.Tx.Table(table).Count(&count).Error; err != nil {
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) List(take int, offset int, noReact ...bool) ([]T, error) {
|
||||||
if take > 20 {
|
if take > 20 {
|
||||||
take = 20
|
take = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
var posts []*models.Post
|
var items []T
|
||||||
if err := PreloadRelatedPost(tx).
|
if err := v.Preload().Tx.Limit(take).Offset(offset).Find(&items).Error; err != nil {
|
||||||
Limit(take).
|
return items, err
|
||||||
Offset(offset).
|
|
||||||
Find(&posts).Error; err != nil {
|
|
||||||
return posts, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
postIds := lo.Map(posts, func(item *models.Post, _ int) uint {
|
idx := lo.Map(items, func(item T, _ int) uint {
|
||||||
return item.ID
|
return item.GetID()
|
||||||
})
|
})
|
||||||
|
|
||||||
var reactInfo []struct {
|
var reactInfo []struct {
|
||||||
@ -118,116 +197,77 @@ func ListPost(tx *gorm.DB, take int, offset int) ([]*models.Post, error) {
|
|||||||
RepostCount int64 `json:"repost_count"`
|
RepostCount int64 `json:"repost_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix := viper.GetString("database.prefix")
|
if len(noReact) <= 0 || !noReact[0] {
|
||||||
database.C.Raw(fmt.Sprintf(`
|
sql := v.BuildReactInfoSql()
|
||||||
SELECT t.id as post_id,
|
database.C.Raw(sql, idx).Scan(&reactInfo)
|
||||||
COALESCE(l.like_count, 0) AS like_count,
|
}
|
||||||
COALESCE(d.dislike_count, 0) AS dislike_count,
|
|
||||||
COALESCE(r.reply_count, 0) AS reply_count,
|
|
||||||
COALESCE(rp.repost_count, 0) AS repost_count
|
|
||||||
FROM %sposts t
|
|
||||||
LEFT JOIN (SELECT post_id, COUNT(*) AS like_count
|
|
||||||
FROM %spost_likes
|
|
||||||
GROUP BY post_id) l ON t.id = l.post_id
|
|
||||||
LEFT JOIN (SELECT post_id, COUNT(*) AS dislike_count
|
|
||||||
FROM %spost_dislikes
|
|
||||||
GROUP BY post_id) d ON t.id = d.post_id
|
|
||||||
LEFT JOIN (SELECT reply_id, COUNT(*) AS reply_count
|
|
||||||
FROM %sposts
|
|
||||||
WHERE reply_id IS NOT NULL
|
|
||||||
GROUP BY reply_id) r ON t.id = r.reply_id
|
|
||||||
LEFT JOIN (SELECT repost_id, COUNT(*) AS repost_count
|
|
||||||
FROM %sposts
|
|
||||||
WHERE repost_id IS NOT NULL
|
|
||||||
GROUP BY repost_id) rp ON t.id = rp.repost_id
|
|
||||||
WHERE t.id IN ?`, prefix, prefix, prefix, prefix, prefix), postIds).Scan(&reactInfo)
|
|
||||||
|
|
||||||
postMap := lo.SliceToMap(posts, func(item *models.Post) (uint, *models.Post) {
|
itemMap := lo.SliceToMap(items, func(item T) (uint, T) {
|
||||||
return item.ID, item
|
return item.GetID(), item
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, info := range reactInfo {
|
for _, info := range reactInfo {
|
||||||
if post, ok := postMap[info.PostID]; ok {
|
if item, ok := itemMap[info.PostID]; ok {
|
||||||
post.LikeCount = info.LikeCount
|
item.SetReactInfo(info)
|
||||||
post.DislikeCount = info.DislikeCount
|
|
||||||
post.ReplyCount = info.ReplyCount
|
|
||||||
post.RepostCount = info.RepostCount
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return posts, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPost(
|
func (v *PostTypeContext[T]) MapCategoriesAndTags(item T) (T, error) {
|
||||||
user models.Account,
|
|
||||||
realm *models.Realm,
|
|
||||||
content string,
|
|
||||||
attachments []models.Attachment,
|
|
||||||
categories []models.Category,
|
|
||||||
tags []models.Tag,
|
|
||||||
publishedAt *time.Time,
|
|
||||||
replyTo, repostTo *uint,
|
|
||||||
) (models.Post, error) {
|
|
||||||
var err error
|
var err error
|
||||||
var post models.Post
|
categories := item.GetCategories()
|
||||||
for idx, category := range categories {
|
for idx, category := range categories {
|
||||||
categories[idx], err = GetCategory(category.Alias)
|
categories[idx], err = GetCategory(category.Alias)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return post, err
|
return item, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
item.SetCategories(categories)
|
||||||
|
tags := item.GetHashtags()
|
||||||
for idx, tag := range tags {
|
for idx, tag := range tags {
|
||||||
tags[idx], err = GetTagOrCreate(tag.Alias, tag.Name)
|
tags[idx], err = GetTagOrCreate(tag.Alias, tag.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return post, err
|
return item, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
item.SetHashtags(tags)
|
||||||
|
return item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *PostTypeContext[T]) New(item T) (T, error) {
|
||||||
|
item, err := v.MapCategoriesAndTags(item)
|
||||||
|
if err != nil {
|
||||||
|
return item, err
|
||||||
|
}
|
||||||
|
|
||||||
var realmId *uint
|
if item.GetRealm() != nil {
|
||||||
if realm != nil {
|
if !item.GetRealm().IsPublic {
|
||||||
if !realm.IsPublic {
|
|
||||||
var member models.RealmMember
|
var member models.RealmMember
|
||||||
if err := database.C.Where(&models.RealmMember{
|
if err := database.C.Where(&models.RealmMember{
|
||||||
RealmID: realm.ID,
|
RealmID: item.GetRealm().ID,
|
||||||
AccountID: user.ID,
|
AccountID: item.GetAuthor().ID,
|
||||||
}).First(&member).Error; err != nil {
|
}).First(&member).Error; err != nil {
|
||||||
return post, fmt.Errorf("you aren't a part of that realm")
|
return item, fmt.Errorf("you aren't a part of that realm")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
realmId = &realm.ID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if publishedAt == nil {
|
if err := database.C.Save(&item).Error; err != nil {
|
||||||
publishedAt = lo.ToPtr(time.Now())
|
return item, err
|
||||||
}
|
}
|
||||||
|
|
||||||
post = models.Post{
|
if item.GetReplyTo() != nil {
|
||||||
Content: content,
|
go func() {
|
||||||
Attachments: attachments,
|
var op models.Moment
|
||||||
Hashtags: tags,
|
if err := database.C.Where("id = ?", item.GetReplyTo()).Preload("Author").First(&op).Error; err == nil {
|
||||||
Categories: categories,
|
if op.Author.ID != item.GetAuthor().ID {
|
||||||
AuthorID: user.ID,
|
postUrl := fmt.Sprintf("https://%s/posts/%d", viper.GetString("domain"), item.GetID())
|
||||||
RealmID: realmId,
|
|
||||||
PublishedAt: *publishedAt,
|
|
||||||
RepostID: repostTo,
|
|
||||||
ReplyID: replyTo,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.C.Save(&post).Error; err != nil {
|
|
||||||
return post, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if post.ReplyID != nil {
|
|
||||||
var op models.Post
|
|
||||||
if err := database.C.Where(&models.Post{
|
|
||||||
BaseModel: models.BaseModel{ID: *post.ReplyID},
|
|
||||||
}).Preload("Author").First(&op).Error; err == nil {
|
|
||||||
if op.Author.ID != user.ID {
|
|
||||||
postUrl := fmt.Sprintf("https://%s/posts/%d", viper.GetString("domain"), post.ID)
|
|
||||||
err := NotifyAccount(
|
err := NotifyAccount(
|
||||||
op.Author,
|
op.Author,
|
||||||
fmt.Sprintf("%s replied you", user.Name),
|
fmt.Sprintf("%s replied you", item.GetAuthor().Name),
|
||||||
fmt.Sprintf("%s replied your post. Check it out!", user.Name),
|
fmt.Sprintf("%s replied your post. Check it out!", item.GetAuthor().Name),
|
||||||
&proto.NotifyLink{Label: "Related post", Url: postUrl},
|
&proto.NotifyLink{Label: "Related post", Url: postUrl},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -235,25 +275,23 @@ func NewPost(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
|
||||||
var subscribers []models.AccountMembership
|
var subscribers []models.AccountMembership
|
||||||
if err := database.C.Where(&models.AccountMembership{
|
if err := database.C.Where(&models.AccountMembership{
|
||||||
FollowingID: user.ID,
|
FollowingID: item.GetAuthor().ID,
|
||||||
}).Preload("Follower").Find(&subscribers).Error; err != nil {
|
}).Preload("Follower").Find(&subscribers).Error; err == nil && len(subscribers) > 0 {
|
||||||
return
|
go func() {
|
||||||
}
|
|
||||||
|
|
||||||
accounts := lo.Map(subscribers, func(item models.AccountMembership, index int) models.Account {
|
accounts := lo.Map(subscribers, func(item models.AccountMembership, index int) models.Account {
|
||||||
return item.Follower
|
return item.Follower
|
||||||
})
|
})
|
||||||
|
|
||||||
for _, account := range accounts {
|
for _, account := range accounts {
|
||||||
postUrl := fmt.Sprintf("https://%s/posts/%d", viper.GetString("domain"), post.ID)
|
postUrl := fmt.Sprintf("https://%s/posts/%d", viper.GetString("domain"), item.GetID())
|
||||||
err := NotifyAccount(
|
err := NotifyAccount(
|
||||||
account,
|
account,
|
||||||
fmt.Sprintf("%s just posted a post", user.Name),
|
fmt.Sprintf("%s just posted a post", item.GetAuthor().Name),
|
||||||
"Account you followed post a brand new post. Check it out!",
|
"Account you followed post a brand new post. Check it out!",
|
||||||
&proto.NotifyLink{Label: "Related post", Url: postUrl},
|
&proto.NotifyLink{Label: "Related post", Url: postUrl},
|
||||||
)
|
)
|
||||||
@ -262,85 +300,52 @@ func NewPost(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
return post, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditPost(
|
func (v *PostTypeContext[T]) Edit(item T) (T, error) {
|
||||||
post models.Post,
|
item, err := v.MapCategoriesAndTags(item)
|
||||||
content string,
|
|
||||||
publishedAt *time.Time,
|
|
||||||
categories []models.Category,
|
|
||||||
tags []models.Tag,
|
|
||||||
attachments []models.Attachment,
|
|
||||||
) (models.Post, error) {
|
|
||||||
var err error
|
|
||||||
for idx, category := range categories {
|
|
||||||
categories[idx], err = GetCategory(category.Alias)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return post, err
|
return item, err
|
||||||
}
|
|
||||||
}
|
|
||||||
for idx, tag := range tags {
|
|
||||||
tags[idx], err = GetTagOrCreate(tag.Alias, tag.Name)
|
|
||||||
if err != nil {
|
|
||||||
return post, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if publishedAt == nil {
|
err = database.C.Save(&item).Error
|
||||||
publishedAt = lo.ToPtr(time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
post.Content = content
|
return item, err
|
||||||
post.PublishedAt = *publishedAt
|
|
||||||
post.Hashtags = tags
|
|
||||||
post.Categories = categories
|
|
||||||
post.Attachments = attachments
|
|
||||||
|
|
||||||
err = database.C.Save(&post).Error
|
|
||||||
|
|
||||||
return post, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func LikePost(user models.Account, post models.Post) (bool, error) {
|
func (v *PostTypeContext[T]) Delete(item T) error {
|
||||||
var like models.PostLike
|
return database.C.Delete(&item).Error
|
||||||
if err := database.C.Where(&models.PostLike{
|
}
|
||||||
AccountID: user.ID,
|
|
||||||
PostID: post.ID,
|
func (v *PostTypeContext[T]) ReactLike(user models.Account, id uint) (bool, error) {
|
||||||
}).First(&like).Error; err != nil {
|
var count int64
|
||||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
table := viper.GetString("database.prefix") + v.GetTableName() + "_likes"
|
||||||
return true, err
|
tx := database.C.Where("account_id = ?", user.ID).Where(v.GetTableName()+"id = ?", id)
|
||||||
}
|
if tx.Count(&count); count <= 0 {
|
||||||
like = models.PostLike{
|
return true, database.C.Table(table).Create(map[string]any{
|
||||||
AccountID: user.ID,
|
"AccountID": user.ID,
|
||||||
PostID: post.ID,
|
v.TypeName + "ID": id,
|
||||||
}
|
}).Error
|
||||||
return true, database.C.Save(&like).Error
|
|
||||||
} else {
|
} else {
|
||||||
return false, database.C.Delete(&like).Error
|
column := strings.ToLower(v.TypeName)
|
||||||
|
return false, tx.Raw(fmt.Sprintf("DELETE FROM %s WHERE account_id = ? AND %s_id = ?", table, column), user.ID, id).Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DislikePost(user models.Account, post models.Post) (bool, error) {
|
func (v *PostTypeContext[T]) ReactDislike(user models.Account, id uint) (bool, error) {
|
||||||
var dislike models.PostDislike
|
var count int64
|
||||||
if err := database.C.Where(&models.PostDislike{
|
table := viper.GetString("database.prefix") + v.GetTableName() + "_dislikes"
|
||||||
AccountID: user.ID,
|
tx := database.C.Where("account_id = ?", user.ID).Where(v.GetTableName()+"id = ?", id)
|
||||||
PostID: post.ID,
|
if tx.Count(&count); count <= 0 {
|
||||||
}).First(&dislike).Error; err != nil {
|
return true, database.C.Table(table).Create(map[string]any{
|
||||||
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
"AccountID": user.ID,
|
||||||
return true, err
|
v.TypeName + "ID": id,
|
||||||
}
|
}).Error
|
||||||
dislike = models.PostDislike{
|
|
||||||
AccountID: user.ID,
|
|
||||||
PostID: post.ID,
|
|
||||||
}
|
|
||||||
return true, database.C.Save(&dislike).Error
|
|
||||||
} else {
|
} else {
|
||||||
return false, database.C.Delete(&dislike).Error
|
column := strings.ToLower(v.TypeName)
|
||||||
|
return false, tx.Raw(fmt.Sprintf("DELETE FROM %s WHERE account_id = ? AND %s_id = ?", table, column), user.ID, id).Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeletePost(post models.Post) error {
|
|
||||||
return database.C.Delete(&post).Error
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user