✨ New datasets
This commit is contained in:
		
							
								
								
									
										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
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user