🗑️ Clean up code
This commit is contained in:
parent
da557fbe60
commit
4709760edc
12
.idea/dataSources.xml
Normal file
12
.idea/dataSources.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||||
|
<data-source source="LOCAL" name="hy_interactive@localhost" uuid="a2f70c83-03f8-4240-bb8b-ac697502cfe2">
|
||||||
|
<driver-ref>postgresql</driver-ref>
|
||||||
|
<synchronize>true</synchronize>
|
||||||
|
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||||
|
<jdbc-url>jdbc:postgresql://localhost:5432/hy_interactive</jdbc-url>
|
||||||
|
<working-dir>$ProjectFileDir$</working-dir>
|
||||||
|
</data-source>
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -5,23 +5,19 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DatabaseAutoActionRange = []any{
|
var AutoMaintainRange = []any{
|
||||||
&models.Account{},
|
&models.Account{},
|
||||||
&models.Realm{},
|
&models.Realm{},
|
||||||
&models.Category{},
|
&models.Category{},
|
||||||
&models.Tag{},
|
&models.Tag{},
|
||||||
&models.Moment{},
|
&models.Post{},
|
||||||
&models.Article{},
|
|
||||||
&models.Comment{},
|
|
||||||
&models.Reaction{},
|
&models.Reaction{},
|
||||||
&models.Attachment{},
|
&models.Attachment{},
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunMigration(source *gorm.DB) error {
|
func RunMigration(source *gorm.DB) error {
|
||||||
if err := source.AutoMigrate(
|
if err := source.AutoMigrate(
|
||||||
append([]any{
|
AutoMaintainRange...,
|
||||||
&models.AccountMembership{},
|
|
||||||
}, DatabaseAutoActionRange...)...,
|
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
// Account profiles basically fetched from Hydrogen.Passport
|
// Account profiles basically fetched from Hydrogen.Passport
|
||||||
// But cache at here for better usage
|
// But cache at here for better usage
|
||||||
// At the same time this model can make relations between local models
|
// At the same time this model can make relations between local models
|
||||||
@ -15,19 +13,8 @@ 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"`
|
||||||
Moments []Moment `json:"moments" gorm:"foreignKey:AuthorID"`
|
Posts []Post `json:"posts" gorm:"foreignKey:AuthorID"`
|
||||||
Articles []Article `json:"articles" gorm:"foreignKey:AuthorID"`
|
|
||||||
Attachments []Attachment `json:"attachments" gorm:"foreignKey:AuthorID"`
|
Attachments []Attachment `json:"attachments" gorm:"foreignKey:AuthorID"`
|
||||||
Reactions []Reaction `json:"reactions"`
|
Reactions []Reaction `json:"reactions"`
|
||||||
ExternalID uint `json:"external_id"`
|
ExternalID uint `json:"external_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccountMembership struct {
|
|
||||||
ID uint `json:"id" gorm:"primaryKey"`
|
|
||||||
CreatedAt time.Time `json:"created_at"`
|
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
|
||||||
Follower Account `json:"follower"`
|
|
||||||
Following Account `json:"following"`
|
|
||||||
FollowerID uint
|
|
||||||
FollowingID uint
|
|
||||||
}
|
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
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"`
|
|
||||||
Reactions []Reaction `json:"reactions"`
|
|
||||||
Attachments []Attachment `json:"attachments"`
|
|
||||||
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
|
|
||||||
}
|
|
@ -19,18 +19,16 @@ const (
|
|||||||
type Attachment struct {
|
type Attachment struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
FileID string `json:"file_id"`
|
FileID string `json:"file_id"`
|
||||||
Filesize int64 `json:"filesize"`
|
Filesize int64 `json:"filesize"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
Mimetype string `json:"mimetype"`
|
Mimetype string `json:"mimetype"`
|
||||||
Hashcode string `json:"hashcode"`
|
Hashcode string `json:"hashcode"`
|
||||||
Type AttachmentType `json:"type"`
|
Type AttachmentType `json:"type"`
|
||||||
ExternalUrl string `json:"external_url"`
|
Author Account `json:"author"`
|
||||||
Author Account `json:"author"`
|
AuthorID uint `json:"author_id"`
|
||||||
ArticleID *uint `json:"article_id"`
|
|
||||||
MomentID *uint `json:"moment_id"`
|
PostID *uint `json:"post_id"`
|
||||||
CommentID *uint `json:"comment_id"`
|
|
||||||
AuthorID uint `json:"author_id"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Attachment) GetStoragePath() string {
|
func (v Attachment) GetStoragePath() string {
|
||||||
|
@ -3,21 +3,17 @@ package models
|
|||||||
type Tag struct {
|
type Tag struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
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"`
|
||||||
Articles []Article `json:"articles" gorm:"many2many:article_tags"`
|
Posts []Post `json:"posts" gorm:"many2many:post_tags"`
|
||||||
Moments []Moment `json:"moments" gorm:"many2many:moment_tags"`
|
|
||||||
Comments []Comment `json:"comments" gorm:"many2many:comment_tags"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Category struct {
|
type Category struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
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"`
|
||||||
Articles []Article `json:"articles" gorm:"many2many:article_categories"`
|
Posts []Post `json:"posts" gorm:"many2many:post_categories"`
|
||||||
Moments []Moment `json:"moments" gorm:"many2many:moment_categories"`
|
|
||||||
Comments []Comment `json:"comments" gorm:"many2many:comment_categories"`
|
|
||||||
}
|
}
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
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"`
|
|
||||||
Reactions []Reaction `json:"reactions"`
|
|
||||||
Attachments []Attachment `json:"attachments"`
|
|
||||||
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
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
type Feed struct {
|
|
||||||
BaseModel
|
|
||||||
|
|
||||||
Alias string `json:"alias"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
ModelType string `json:"model_type"`
|
|
||||||
|
|
||||||
CommentCount int64 `json:"comment_count"`
|
|
||||||
ReactionCount int64 `json:"reaction_count"`
|
|
||||||
|
|
||||||
AuthorID uint `json:"author_id"`
|
|
||||||
RealmID *uint `json:"realm_id"`
|
|
||||||
|
|
||||||
Author Account `json:"author" gorm:"embedded"`
|
|
||||||
|
|
||||||
Attachments []Attachment `json:"attachments" gorm:"-"`
|
|
||||||
ReactionList map[string]int64 `json:"reaction_list" gorm:"-"`
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
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"`
|
|
||||||
Reactions []Reaction `json:"reactions"`
|
|
||||||
Attachments []Attachment `json:"attachments"`
|
|
||||||
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
|
|
||||||
}
|
|
@ -12,53 +12,30 @@ type PostReactInfo struct {
|
|||||||
RepostCount int64 `json:"repost_count"`
|
RepostCount int64 `json:"repost_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PostBase struct {
|
type Post struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
Alias string `json:"alias" gorm:"uniqueIndex"`
|
Alias string `json:"alias" gorm:"uniqueIndex"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
Tags []Tag `json:"tags" gorm:"many2many:post_tags"`
|
||||||
|
Categories []Category `json:"categories" gorm:"many2many:post_categories"`
|
||||||
|
Reactions []Reaction `json:"reactions"`
|
||||||
|
Attachments []Attachment `json:"attachments"`
|
||||||
|
Replies []Post `json:"replies" gorm:"foreignKey:ReplyID"`
|
||||||
|
ReplyID *uint `json:"reply_id"`
|
||||||
|
RepostID *uint `json:"repost_id"`
|
||||||
|
RealmID *uint `json:"realm_id"`
|
||||||
|
ReplyTo *Post `json:"reply_to" gorm:"foreignKey:ReplyID"`
|
||||||
|
RepostTo *Post `json:"repost_to" gorm:"foreignKey:RepostID"`
|
||||||
|
Realm *Realm `json:"realm"`
|
||||||
|
|
||||||
PublishedAt *time.Time `json:"published_at"`
|
PublishedAt *time.Time `json:"published_at"`
|
||||||
|
|
||||||
AuthorID uint `json:"author_id"`
|
AuthorID uint `json:"author_id"`
|
||||||
Author Account `json:"author"`
|
Author Account `json:"author"`
|
||||||
|
|
||||||
// Dynamic Calculated Values
|
// Dynamic Calculated Values
|
||||||
ReactionList map[string]int64 `json:"reaction_list" gorm:"-"`
|
ReplyCount int64 `json:"comment_count"`
|
||||||
}
|
ReactionCount int64 `json:"reaction_count"`
|
||||||
|
ReactionList map[string]int64 `json:"reaction_list" 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) SetReactionList(list map[string]int64) {
|
|
||||||
p.ReactionList = list
|
|
||||||
}
|
|
||||||
|
|
||||||
type PostInterface interface {
|
|
||||||
GetID() uint
|
|
||||||
GetHashtags() []Tag
|
|
||||||
GetCategories() []Category
|
|
||||||
GetReplyTo() PostInterface
|
|
||||||
GetRepostTo() PostInterface
|
|
||||||
GetAuthor() Account
|
|
||||||
GetRealm() *Realm
|
|
||||||
|
|
||||||
SetHashtags([]Tag)
|
|
||||||
SetCategories([]Category)
|
|
||||||
SetReactionList(map[string]int64)
|
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,6 @@ type Reaction struct {
|
|||||||
Symbol string `json:"symbol"`
|
Symbol string `json:"symbol"`
|
||||||
Attitude ReactionAttitude `json:"attitude"`
|
Attitude ReactionAttitude `json:"attitude"`
|
||||||
|
|
||||||
ArticleID *uint `json:"article_id"`
|
PostID *uint `json:"post_id"`
|
||||||
MomentID *uint `json:"moment_id"`
|
|
||||||
CommentID *uint `json:"comment_id"`
|
|
||||||
AccountID uint `json:"account_id"`
|
AccountID uint `json:"account_id"`
|
||||||
}
|
}
|
||||||
|
@ -5,12 +5,11 @@ package models
|
|||||||
type Realm struct {
|
type Realm struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
Alias string `json:"alias"`
|
Alias string `json:"alias"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Articles []Article `json:"article"`
|
Posts []Post `json:"posts"`
|
||||||
Moments []Moment `json:"moments"`
|
IsPublic bool `json:"is_public"`
|
||||||
IsPublic bool `json:"is_public"`
|
IsCommunity bool `json:"is_community"`
|
||||||
IsCommunity bool `json:"is_community"`
|
ExternalID uint `json:"external_id"`
|
||||||
ExternalID uint `json:"external_id"`
|
|
||||||
}
|
}
|
||||||
|
@ -1,141 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
func contextArticle() *services.PostTypeContext {
|
|
||||||
return &services.PostTypeContext{
|
|
||||||
Tx: database.C,
|
|
||||||
TableName: "articles",
|
|
||||||
ColumnName: "article",
|
|
||||||
CanReply: false,
|
|
||||||
CanRepost: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createArticle(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Alias string `json:"alias" form:"alias"`
|
|
||||||
Title string `json:"title" form:"title" validate:"required"`
|
|
||||||
Description string `json:"description" form:"description"`
|
|
||||||
Content string `json:"content" form:"content" validate:"required"`
|
|
||||||
Hashtags []models.Tag `json:"hashtags" form:"hashtags"`
|
|
||||||
Categories []models.Category `json:"categories" form:"categories"`
|
|
||||||
Attachments []models.Attachment `json:"attachments" form:"attachments"`
|
|
||||||
PublishedAt *time.Time `json:"published_at" form:"published_at"`
|
|
||||||
RealmAlias string `json:"realm" form:"realm"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
} else if len(data.Alias) == 0 {
|
|
||||||
data.Alias = strings.ReplaceAll(uuid.NewString(), "-", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
item := &models.Article{
|
|
||||||
PostBase: models.PostBase{
|
|
||||||
Alias: data.Alias,
|
|
||||||
PublishedAt: data.PublishedAt,
|
|
||||||
AuthorID: user.ID,
|
|
||||||
},
|
|
||||||
Hashtags: data.Hashtags,
|
|
||||||
Categories: data.Categories,
|
|
||||||
Attachments: data.Attachments,
|
|
||||||
Title: data.Title,
|
|
||||||
Description: data.Description,
|
|
||||||
Content: data.Content,
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data.RealmAlias) > 0 {
|
|
||||||
if realm, err := services.GetRealmWithAlias(data.RealmAlias); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else if _, err := services.GetRealmMember(realm.ExternalID, user.ExternalID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("you aren't a part of related realm: %v", err))
|
|
||||||
} else {
|
|
||||||
item.RealmID = &realm.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if item, err := services.NewPost(item); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.JSON(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func editArticle(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
id, _ := c.ParamsInt("articleId", 0)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Alias string `json:"alias" form:"alias" validate:"required"`
|
|
||||||
Title string `json:"title" form:"title" validate:"required"`
|
|
||||||
Description string `json:"description" form:"description"`
|
|
||||||
Content string `json:"content" form:"content" validate:"required"`
|
|
||||||
PublishedAt *time.Time `json:"published_at" form:"published_at"`
|
|
||||||
Hashtags []models.Tag `json:"hashtags" form:"hashtags"`
|
|
||||||
Categories []models.Category `json:"categories" form:"categories"`
|
|
||||||
Attachments []models.Attachment `json:"attachments" form:"attachments"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var item *models.Article
|
|
||||||
if err := database.C.Where(models.Article{
|
|
||||||
PostBase: models.PostBase{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
|
||||||
AuthorID: user.ID,
|
|
||||||
},
|
|
||||||
}).First(&item).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
item.Alias = data.Alias
|
|
||||||
item.Title = data.Title
|
|
||||||
item.Description = data.Description
|
|
||||||
item.Content = data.Content
|
|
||||||
item.PublishedAt = data.PublishedAt
|
|
||||||
item.Hashtags = data.Hashtags
|
|
||||||
item.Categories = data.Categories
|
|
||||||
item.Attachments = data.Attachments
|
|
||||||
|
|
||||||
if item, err := services.EditPost(item); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.JSON(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteArticle(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
id, _ := c.ParamsInt("articleId", 0)
|
|
||||||
|
|
||||||
var item *models.Article
|
|
||||||
if err := database.C.Where(models.Article{
|
|
||||||
PostBase: models.PostBase{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
|
||||||
AuthorID: user.ID,
|
|
||||||
},
|
|
||||||
}).First(&item).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := services.DeletePost(item); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
@ -1,196 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
func contextComment() *services.PostTypeContext {
|
|
||||||
return &services.PostTypeContext{
|
|
||||||
Tx: database.C,
|
|
||||||
TableName: "comments",
|
|
||||||
ColumnName: "comment",
|
|
||||||
CanReply: false,
|
|
||||||
CanRepost: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func listComment(c *fiber.Ctx) error {
|
|
||||||
take := c.QueryInt("take", 0)
|
|
||||||
offset := c.QueryInt("offset", 0)
|
|
||||||
|
|
||||||
alias := c.Params("postId")
|
|
||||||
|
|
||||||
mx := c.Locals(postContextKey).(*services.PostTypeContext).
|
|
||||||
FilterPublishedAt(time.Now())
|
|
||||||
|
|
||||||
item, err := mx.GetViaAlias(alias)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := mx.ListComment(item.ID, take, offset)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
count, err := mx.CountComment(item.ID)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
|
||||||
"count": count,
|
|
||||||
"data": data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func createComment(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Content string `json:"content" form:"content" validate:"required"`
|
|
||||||
PublishedAt *time.Time `json:"published_at" form:"published_at"`
|
|
||||||
Hashtags []models.Tag `json:"hashtags" form:"hashtags"`
|
|
||||||
Categories []models.Category `json:"categories" form:"categories"`
|
|
||||||
Attachments []models.Attachment `json:"attachments" form:"attachments"`
|
|
||||||
ReplyTo uint `json:"reply_to" form:"reply_to"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
item := &models.Comment{
|
|
||||||
PostBase: models.PostBase{
|
|
||||||
Alias: strings.ReplaceAll(uuid.NewString(), "-", ""),
|
|
||||||
PublishedAt: data.PublishedAt,
|
|
||||||
AuthorID: user.ID,
|
|
||||||
},
|
|
||||||
Hashtags: data.Hashtags,
|
|
||||||
Categories: data.Categories,
|
|
||||||
Attachments: data.Attachments,
|
|
||||||
Content: data.Content,
|
|
||||||
}
|
|
||||||
|
|
||||||
postType := c.Params("postType")
|
|
||||||
alias := c.Params("postId")
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var res models.Feed
|
|
||||||
|
|
||||||
var columnName string
|
|
||||||
var tableName string
|
|
||||||
|
|
||||||
switch postType {
|
|
||||||
case "moments":
|
|
||||||
columnName = "moment"
|
|
||||||
tableName = viper.GetString("database.table_prefix") + "moments"
|
|
||||||
err = database.C.Model(&models.Moment{}).Where("alias = ?", alias).Select("id").First(&res).Error
|
|
||||||
case "articles":
|
|
||||||
columnName = "article"
|
|
||||||
tableName = viper.GetString("database.table_prefix") + "articles"
|
|
||||||
err = database.C.Model(&models.Article{}).Where("alias = ?", alias).Select("id").First(&res).Error
|
|
||||||
default:
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "comment must belongs to a resource")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("belongs to resource was not found: %v", err))
|
|
||||||
} else {
|
|
||||||
switch postType {
|
|
||||||
case "moments":
|
|
||||||
item.MomentID = &res.ID
|
|
||||||
case "articles":
|
|
||||||
item.ArticleID = &res.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var relatedCount int64
|
|
||||||
if data.ReplyTo > 0 {
|
|
||||||
if err := database.C.Where("id = ?", data.ReplyTo).
|
|
||||||
Model(&models.Comment{}).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 {
|
|
||||||
item.ReplyID = &data.ReplyTo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item, err = services.NewPost(item)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify the original poster their post is commented by someone
|
|
||||||
go services.CommentNotify(item, res, columnName, tableName)
|
|
||||||
|
|
||||||
return c.JSON(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
func editComment(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
id, _ := c.ParamsInt("commentId", 0)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Content string `json:"content" form:"content" validate:"required"`
|
|
||||||
PublishedAt *time.Time `json:"published_at" form:"published_at"`
|
|
||||||
Hashtags []models.Tag `json:"hashtags" form:"hashtags"`
|
|
||||||
Categories []models.Category `json:"categories" form:"categories"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var item *models.Comment
|
|
||||||
if err := database.C.Where(models.Comment{
|
|
||||||
PostBase: models.PostBase{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
|
||||||
AuthorID: user.ID,
|
|
||||||
},
|
|
||||||
}).First(&item).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
item.Content = data.Content
|
|
||||||
item.PublishedAt = data.PublishedAt
|
|
||||||
item.Hashtags = data.Hashtags
|
|
||||||
item.Categories = data.Categories
|
|
||||||
|
|
||||||
if item, err := services.EditPost(item); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.JSON(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteComment(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
id, _ := c.ParamsInt("commentId", 0)
|
|
||||||
|
|
||||||
var item *models.Comment
|
|
||||||
if err := database.C.Where(models.Comment{
|
|
||||||
PostBase: models.PostBase{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
|
||||||
AuthorID: user.ID,
|
|
||||||
},
|
|
||||||
}).First(&item).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := services.DeletePost(item); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
@ -1,200 +1,49 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/services"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
||||||
|
"git.solsynth.dev/hydrogen/interactive/pkg/services"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/samber/lo"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
queryArticle = "id, created_at, updated_at, alias, title, NULL as content, description, realm_id, author_id, 'article' as model_type"
|
|
||||||
queryMoment = "id, created_at, updated_at, alias, NULL as title, content, NULL as description, realm_id, author_id, 'moment' as model_type"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func listFeed(c *fiber.Ctx) error {
|
func listFeed(c *fiber.Ctx) error {
|
||||||
take := c.QueryInt("take", 0)
|
take := c.QueryInt("take", 0)
|
||||||
offset := c.QueryInt("offset", 0)
|
offset := c.QueryInt("offset", 0)
|
||||||
realmAlias := c.Query("realm")
|
realmId := c.QueryInt("realmId", 0)
|
||||||
|
|
||||||
if take > 20 {
|
tx := database.C
|
||||||
take = 20
|
if realmId > 0 {
|
||||||
|
tx = services.FilterWithRealm(tx, uint(realmId))
|
||||||
}
|
}
|
||||||
|
|
||||||
var whereConditions []string
|
|
||||||
|
|
||||||
if len(realmAlias) > 0 {
|
|
||||||
realm, err := services.GetRealmWithAlias(realmAlias)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("related realm was not found: %v", err))
|
|
||||||
}
|
|
||||||
whereConditions = append(whereConditions, fmt.Sprintf("feed.realm_id = %d", realm.ID))
|
|
||||||
}
|
|
||||||
|
|
||||||
var author models.Account
|
|
||||||
if len(c.Query("authorId")) > 0 {
|
if len(c.Query("authorId")) > 0 {
|
||||||
|
var author models.Account
|
||||||
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())
|
||||||
} else {
|
|
||||||
whereConditions = append(whereConditions, fmt.Sprintf("feed.author_id = %d", author.ID))
|
|
||||||
}
|
}
|
||||||
|
tx = tx.Where("author_id = ?", author.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
var whereStatement string
|
if len(c.Query("category")) > 0 {
|
||||||
if len(whereConditions) > 0 {
|
tx = services.FilterPostWithCategory(tx, c.Query("category"))
|
||||||
whereStatement += "WHERE " + strings.Join(whereConditions, " AND ")
|
}
|
||||||
|
if len(c.Query("tag")) > 0 {
|
||||||
|
tx = services.FilterPostWithTag(tx, c.Query("tag"))
|
||||||
}
|
}
|
||||||
|
|
||||||
var result []*models.Feed
|
count, err := services.CountPost(tx)
|
||||||
|
if err != nil {
|
||||||
userTable := viper.GetString("database.prefix") + "accounts"
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
commentTable := viper.GetString("database.prefix") + "comments"
|
|
||||||
reactionTable := viper.GetString("database.prefix") + "reactions"
|
|
||||||
|
|
||||||
database.C.Raw(
|
|
||||||
fmt.Sprintf(`SELECT feed.*, author.*,
|
|
||||||
COALESCE(comment_count, 0) AS comment_count,
|
|
||||||
COALESCE(reaction_count, 0) AS reaction_count
|
|
||||||
FROM (? UNION ALL ?) AS feed
|
|
||||||
INNER JOIN %s AS author ON author_id = author.id
|
|
||||||
LEFT JOIN (SELECT article_id, moment_id, COUNT(*) AS comment_count
|
|
||||||
FROM %s
|
|
||||||
GROUP BY article_id, moment_id) AS comments
|
|
||||||
ON (feed.model_type = 'article' AND feed.id = comments.article_id) OR
|
|
||||||
(feed.model_type = 'moment' AND feed.id = comments.moment_id)
|
|
||||||
LEFT JOIN (SELECT article_id, moment_id, COUNT(*) AS reaction_count
|
|
||||||
FROM %s
|
|
||||||
GROUP BY article_id, moment_id) AS reactions
|
|
||||||
ON (feed.model_type = 'article' AND feed.id = reactions.article_id) OR
|
|
||||||
(feed.model_type = 'moment' AND feed.id = reactions.moment_id)
|
|
||||||
%s ORDER BY feed.created_at desc LIMIT ? OFFSET ?`,
|
|
||||||
userTable,
|
|
||||||
commentTable,
|
|
||||||
reactionTable,
|
|
||||||
whereStatement,
|
|
||||||
),
|
|
||||||
database.C.Select(queryArticle).Model(&models.Article{}),
|
|
||||||
database.C.Select(queryMoment).Model(&models.Moment{}),
|
|
||||||
take,
|
|
||||||
offset,
|
|
||||||
).Scan(&result)
|
|
||||||
|
|
||||||
if !c.QueryBool("noReact", false) {
|
|
||||||
var reactions []struct {
|
|
||||||
PostID uint
|
|
||||||
Symbol string
|
|
||||||
Count int64
|
|
||||||
}
|
|
||||||
|
|
||||||
revertReaction := func(dataset string) error {
|
|
||||||
itemMap := lo.SliceToMap(lo.FilterMap(result, func(item *models.Feed, index int) (*models.Feed, bool) {
|
|
||||||
return item, item.ModelType == dataset
|
|
||||||
}), func(item *models.Feed) (uint, *models.Feed) {
|
|
||||||
return item.ID, item
|
|
||||||
})
|
|
||||||
|
|
||||||
idx := lo.Map(lo.Filter(result, func(item *models.Feed, index int) bool {
|
|
||||||
return item.ModelType == dataset
|
|
||||||
}), func(item *models.Feed, index int) uint {
|
|
||||||
return item.ID
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := database.C.Model(&models.Reaction{}).
|
|
||||||
Select(dataset+"_id as post_id, symbol, COUNT(id) as count").
|
|
||||||
Where(dataset+"_id IN (?)", idx).
|
|
||||||
Group("post_id, symbol").
|
|
||||||
Scan(&reactions).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
list := map[uint]map[string]int64{}
|
|
||||||
for _, info := range reactions {
|
|
||||||
if _, ok := list[info.PostID]; !ok {
|
|
||||||
list[info.PostID] = make(map[string]int64)
|
|
||||||
}
|
|
||||||
list[info.PostID][info.Symbol] = info.Count
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range list {
|
|
||||||
if post, ok := itemMap[k]; ok {
|
|
||||||
post.ReactionList = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := revertReaction("article"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := revertReaction("moment"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.QueryBool("noAttachment", false) {
|
items, err := services.ListPost(tx, take, offset)
|
||||||
revertAttachment := func(dataset string) error {
|
if err != nil {
|
||||||
var attachments []struct {
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
models.Attachment
|
|
||||||
|
|
||||||
PostID uint `json:"post_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
itemMap := lo.SliceToMap(lo.FilterMap(result, func(item *models.Feed, index int) (*models.Feed, bool) {
|
|
||||||
return item, item.ModelType == dataset
|
|
||||||
}), func(item *models.Feed) (uint, *models.Feed) {
|
|
||||||
return item.ID, item
|
|
||||||
})
|
|
||||||
|
|
||||||
idx := lo.Map(lo.Filter(result, func(item *models.Feed, index int) bool {
|
|
||||||
return item.ModelType == dataset
|
|
||||||
}), func(item *models.Feed, index int) uint {
|
|
||||||
return item.ID
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := database.C.
|
|
||||||
Model(&models.Attachment{}).
|
|
||||||
Select(dataset+"_id as post_id, *").
|
|
||||||
Where(dataset+"_id IN (?)", idx).
|
|
||||||
Scan(&attachments).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
list := map[uint][]models.Attachment{}
|
|
||||||
for _, info := range attachments {
|
|
||||||
list[info.PostID] = append(list[info.PostID], info.Attachment)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range list {
|
|
||||||
if post, ok := itemMap[k]; ok {
|
|
||||||
post.Attachments = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := revertAttachment("article"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := revertAttachment("moment"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var count int64
|
|
||||||
database.C.Raw(`SELECT COUNT(*) FROM (? UNION ALL ?) as feed`,
|
|
||||||
database.C.Select(queryArticle).Model(&models.Article{}),
|
|
||||||
database.C.Select(queryMoment).Model(&models.Moment{}),
|
|
||||||
).Scan(&count)
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"count": count,
|
"count": count,
|
||||||
"data": result,
|
"data": items,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,147 +0,0 @@
|
|||||||
package server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
func contextMoment() *services.PostTypeContext {
|
|
||||||
return &services.PostTypeContext{
|
|
||||||
Tx: database.C,
|
|
||||||
TableName: "moments",
|
|
||||||
ColumnName: "moment",
|
|
||||||
CanReply: false,
|
|
||||||
CanRepost: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createMoment(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Alias string `json:"alias" form:"alias"`
|
|
||||||
Content string `json:"content" form:"content" validate:"required,max=1024"`
|
|
||||||
Hashtags []models.Tag `json:"hashtags" form:"hashtags"`
|
|
||||||
Categories []models.Category `json:"categories" form:"categories"`
|
|
||||||
Attachments []models.Attachment `json:"attachments" form:"attachments"`
|
|
||||||
PublishedAt *time.Time `json:"published_at" form:"published_at"`
|
|
||||||
RealmAlias string `json:"realm" form:"realm"`
|
|
||||||
RepostTo uint `json:"repost_to" form:"repost_to"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
} else if len(data.Alias) == 0 {
|
|
||||||
data.Alias = strings.ReplaceAll(uuid.NewString(), "-", "")
|
|
||||||
}
|
|
||||||
|
|
||||||
item := &models.Moment{
|
|
||||||
PostBase: models.PostBase{
|
|
||||||
Alias: data.Alias,
|
|
||||||
PublishedAt: data.PublishedAt,
|
|
||||||
AuthorID: user.ID,
|
|
||||||
},
|
|
||||||
Hashtags: data.Hashtags,
|
|
||||||
Categories: data.Categories,
|
|
||||||
Attachments: data.Attachments,
|
|
||||||
Content: data.Content,
|
|
||||||
}
|
|
||||||
|
|
||||||
var relatedCount int64
|
|
||||||
if data.RepostTo > 0 {
|
|
||||||
if err := database.C.Where("id = ?", data.RepostTo).
|
|
||||||
Model(&models.Moment{}).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 {
|
|
||||||
item.RepostID = &data.RepostTo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data.RealmAlias) > 0 {
|
|
||||||
if realm, err := services.GetRealmWithAlias(data.RealmAlias); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else if _, err := services.GetRealmMember(realm.ExternalID, user.ExternalID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("you aren't a part of related realm: %v", err))
|
|
||||||
} else {
|
|
||||||
item.RealmID = &realm.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item, err := services.NewPost(item)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(item)
|
|
||||||
}
|
|
||||||
|
|
||||||
func editMoment(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
id, _ := c.ParamsInt("momentId", 0)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
Alias string `json:"alias" form:"alias" validate:"required"`
|
|
||||||
Content string `json:"content" form:"content" validate:"required,max=1024"`
|
|
||||||
PublishedAt *time.Time `json:"published_at" form:"published_at"`
|
|
||||||
Hashtags []models.Tag `json:"hashtags" form:"hashtags"`
|
|
||||||
Categories []models.Category `json:"categories" form:"categories"`
|
|
||||||
Attachments []models.Attachment `json:"attachments" form:"attachments"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var item *models.Moment
|
|
||||||
if err := database.C.Where(models.Moment{
|
|
||||||
PostBase: models.PostBase{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
|
||||||
AuthorID: user.ID,
|
|
||||||
},
|
|
||||||
}).First(&item).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
item.Alias = data.Alias
|
|
||||||
item.Content = data.Content
|
|
||||||
item.PublishedAt = data.PublishedAt
|
|
||||||
item.Hashtags = data.Hashtags
|
|
||||||
item.Categories = data.Categories
|
|
||||||
item.Attachments = data.Attachments
|
|
||||||
|
|
||||||
if item, err := services.EditPost(item); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
} else {
|
|
||||||
return c.JSON(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteMoment(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
id, _ := c.ParamsInt("momentId", 0)
|
|
||||||
|
|
||||||
var item *models.Moment
|
|
||||||
if err := database.C.Where(models.Moment{
|
|
||||||
PostBase: models.PostBase{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
|
||||||
AuthorID: user.ID,
|
|
||||||
},
|
|
||||||
}).First(&item).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := services.DeletePost(item); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
@ -2,6 +2,8 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
||||||
@ -11,38 +13,17 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var postContextKey = "ptx"
|
|
||||||
|
|
||||||
func useDynamicContext(c *fiber.Ctx) error {
|
|
||||||
postType := c.Params("postType")
|
|
||||||
switch postType {
|
|
||||||
case "articles":
|
|
||||||
c.Locals(postContextKey, contextArticle())
|
|
||||||
case "moments":
|
|
||||||
c.Locals(postContextKey, contextMoment())
|
|
||||||
case "comments":
|
|
||||||
c.Locals(postContextKey, contextComment())
|
|
||||||
default:
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "invalid dataset")
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Next()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPost(c *fiber.Ctx) error {
|
func getPost(c *fiber.Ctx) error {
|
||||||
alias := c.Params("postId")
|
alias := c.Params("postId")
|
||||||
|
|
||||||
mx := c.Locals(postContextKey).(*services.PostTypeContext).
|
item, err := services.GetPostWithAlias(alias)
|
||||||
FilterPublishedAt(time.Now())
|
|
||||||
|
|
||||||
item, err := mx.GetViaAlias(alias)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
item.CommentCount = mx.CountComments(item.ID)
|
item.ReplyCount = services.CountPostReply(item.ID)
|
||||||
item.ReactionCount = mx.CountReactions(item.ID)
|
item.ReactionCount = services.CountPostReactions(item.ID)
|
||||||
item.ReactionList, err = mx.ListReactions(item.ID)
|
item.ReactionList, err = services.ListPostReactions(item.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
@ -55,36 +36,32 @@ func listPost(c *fiber.Ctx) error {
|
|||||||
offset := c.QueryInt("offset", 0)
|
offset := c.QueryInt("offset", 0)
|
||||||
realmId := c.QueryInt("realmId", 0)
|
realmId := c.QueryInt("realmId", 0)
|
||||||
|
|
||||||
mx := c.Locals(postContextKey).(*services.PostTypeContext).
|
tx := database.C
|
||||||
FilterPublishedAt(time.Now()).
|
if realmId > 0 {
|
||||||
FilterRealm(uint(realmId)).
|
tx = services.FilterWithRealm(tx, uint(realmId))
|
||||||
SortCreatedAt("desc")
|
}
|
||||||
|
|
||||||
var author models.Account
|
|
||||||
if len(c.Query("authorId")) > 0 {
|
if len(c.Query("authorId")) > 0 {
|
||||||
|
var author models.Account
|
||||||
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())
|
||||||
}
|
}
|
||||||
mx = mx.FilterAuthor(author.ID)
|
tx = tx.Where("author_id = ?", author.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(c.Query("category")) > 0 {
|
if len(c.Query("category")) > 0 {
|
||||||
mx = mx.FilterWithCategory(c.Query("category"))
|
tx = services.FilterPostWithCategory(tx, c.Query("category"))
|
||||||
}
|
}
|
||||||
if len(c.Query("tag")) > 0 {
|
if len(c.Query("tag")) > 0 {
|
||||||
mx = mx.FilterWithTag(c.Query("tag"))
|
tx = services.FilterPostWithTag(tx, c.Query("tag"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !c.QueryBool("reply", true) {
|
count, err := services.CountPost(tx)
|
||||||
mx = mx.FilterReply(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
count, err := mx.Count()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
items, err := mx.List(take, offset)
|
items, err := services.ListPost(tx, take, offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
@ -95,6 +72,124 @@ func listPost(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createPost(c *fiber.Ctx) error {
|
||||||
|
user := c.Locals("principal").(models.Account)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Alias string `json:"alias" form:"alias"`
|
||||||
|
Content string `json:"content" form:"content" validate:"required,max=4096"`
|
||||||
|
Tags []models.Tag `json:"tags" form:"tags"`
|
||||||
|
Categories []models.Category `json:"categories" form:"categories"`
|
||||||
|
Attachments []models.Attachment `json:"attachments" form:"attachments"`
|
||||||
|
PublishedAt *time.Time `json:"published_at" form:"published_at"`
|
||||||
|
RealmAlias string `json:"realm" form:"realm"`
|
||||||
|
RepostTo uint `json:"repost_to" form:"repost_to"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
} else if len(data.Alias) == 0 {
|
||||||
|
data.Alias = strings.ReplaceAll(uuid.NewString(), "-", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
item := models.Post{
|
||||||
|
Alias: data.Alias,
|
||||||
|
PublishedAt: data.PublishedAt,
|
||||||
|
AuthorID: user.ID,
|
||||||
|
Tags: data.Tags,
|
||||||
|
Categories: data.Categories,
|
||||||
|
Attachments: data.Attachments,
|
||||||
|
Content: data.Content,
|
||||||
|
}
|
||||||
|
|
||||||
|
var relatedCount int64
|
||||||
|
if data.RepostTo > 0 {
|
||||||
|
if err := database.C.Where("id = ?", data.RepostTo).
|
||||||
|
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 {
|
||||||
|
item.RepostID = &data.RepostTo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data.RealmAlias) > 0 {
|
||||||
|
if realm, err := services.GetRealmWithAlias(data.RealmAlias); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else if _, err := services.GetRealmMember(realm.ExternalID, user.ExternalID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("you aren't a part of related realm: %v", err))
|
||||||
|
} else {
|
||||||
|
item.RealmID = &realm.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item, err := services.NewPost(user, item)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func editPost(c *fiber.Ctx) error {
|
||||||
|
user := c.Locals("principal").(models.Account)
|
||||||
|
id, _ := c.ParamsInt("postId", 0)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Alias string `json:"alias" form:"alias" validate:"required"`
|
||||||
|
Content string `json:"content" form:"content" validate:"required,max=1024"`
|
||||||
|
PublishedAt *time.Time `json:"published_at" form:"published_at"`
|
||||||
|
Tags []models.Tag `json:"tags" form:"tags"`
|
||||||
|
Categories []models.Category `json:"categories" form:"categories"`
|
||||||
|
Attachments []models.Attachment `json:"attachments" form:"attachments"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var item models.Post
|
||||||
|
if err := database.C.Where(models.Post{
|
||||||
|
BaseModel: models.BaseModel{ID: uint(id)},
|
||||||
|
AuthorID: user.ID,
|
||||||
|
}).First(&item).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
item.Alias = data.Alias
|
||||||
|
item.Content = data.Content
|
||||||
|
item.PublishedAt = data.PublishedAt
|
||||||
|
item.Tags = data.Tags
|
||||||
|
item.Categories = data.Categories
|
||||||
|
item.Attachments = data.Attachments
|
||||||
|
|
||||||
|
if item, err := services.EditPost(item); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.JSON(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deletePost(c *fiber.Ctx) error {
|
||||||
|
user := c.Locals("principal").(models.Account)
|
||||||
|
id, _ := c.ParamsInt("postId", 0)
|
||||||
|
|
||||||
|
var item models.Post
|
||||||
|
if err := database.C.Where(models.Post{
|
||||||
|
BaseModel: models.BaseModel{ID: uint(id)},
|
||||||
|
AuthorID: user.ID,
|
||||||
|
}).First(&item).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := services.DeletePost(item); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
func reactPost(c *fiber.Ctx) error {
|
func reactPost(c *fiber.Ctx) error {
|
||||||
user := c.Locals("principal").(models.Account)
|
user := c.Locals("principal").(models.Account)
|
||||||
|
|
||||||
@ -107,45 +202,23 @@ func reactPost(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
mx := c.Locals(postContextKey).(*services.PostTypeContext)
|
|
||||||
|
|
||||||
reaction := models.Reaction{
|
reaction := models.Reaction{
|
||||||
Symbol: data.Symbol,
|
Symbol: data.Symbol,
|
||||||
Attitude: data.Attitude,
|
Attitude: data.Attitude,
|
||||||
AccountID: user.ID,
|
AccountID: user.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
postType := c.Params("postType")
|
|
||||||
alias := c.Params("postId")
|
alias := c.Params("postId")
|
||||||
|
|
||||||
var err error
|
var res models.Post
|
||||||
var res models.Feed
|
|
||||||
|
|
||||||
switch postType {
|
if err := database.C.Where("id = ?", alias).Select("id").First(&res).Error; err != nil {
|
||||||
case "moments":
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to find post to react: %v", err))
|
||||||
err = database.C.Model(&models.Moment{}).Where("id = ?", alias).Select("id").First(&res).Error
|
|
||||||
case "articles":
|
|
||||||
err = database.C.Model(&models.Article{}).Where("id = ?", alias).Select("id").First(&res).Error
|
|
||||||
case "comments":
|
|
||||||
err = database.C.Model(&models.Comment{}).Where("id = ?", alias).Select("id").First(&res).Error
|
|
||||||
default:
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "comment must belongs to a resource")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("belongs to resource was not found: %v", err))
|
|
||||||
} else {
|
} else {
|
||||||
switch postType {
|
reaction.PostID = &res.ID
|
||||||
case "moments":
|
|
||||||
reaction.MomentID = &res.ID
|
|
||||||
case "articles":
|
|
||||||
reaction.ArticleID = &res.ID
|
|
||||||
case "comments":
|
|
||||||
reaction.CommentID = &res.ID
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if positive, reaction, err := mx.React(reaction); err != nil {
|
if positive, reaction, err := services.ReactPost(reaction); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
return c.Status(lo.Ternary(positive, fiber.StatusCreated, fiber.StatusNoContent)).JSON(reaction)
|
return c.Status(lo.Ternary(positive, fiber.StatusCreated, fiber.StatusNoContent)).JSON(reaction)
|
||||||
|
52
pkg/server/replies_api.go
Normal file
52
pkg/server/replies_api.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
||||||
|
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
||||||
|
"git.solsynth.dev/hydrogen/interactive/pkg/services"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func listReplies(c *fiber.Ctx) error {
|
||||||
|
take := c.QueryInt("take", 0)
|
||||||
|
offset := c.QueryInt("offset", 0)
|
||||||
|
|
||||||
|
tx := database.C
|
||||||
|
var post models.Post
|
||||||
|
if err := database.C.Where("alias = ?", c.Params("postId")).First(&post).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to find post: %v", err))
|
||||||
|
} else {
|
||||||
|
tx = services.FilterPostReply(tx, post.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(c.Query("authorId")) > 0 {
|
||||||
|
var author models.Account
|
||||||
|
if err := database.C.Where(&models.Account{Name: c.Query("authorId")}).First(&author).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
tx = tx.Where("author_id = ?", author.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
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"))
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := services.CountPost(tx)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
items, err := services.ListPost(tx, take, offset)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"count": count,
|
||||||
|
"data": items,
|
||||||
|
})
|
||||||
|
}
|
@ -62,8 +62,6 @@ func NewServer() {
|
|||||||
{
|
{
|
||||||
api.Get("/users/me", authMiddleware, getUserinfo)
|
api.Get("/users/me", authMiddleware, getUserinfo)
|
||||||
api.Get("/users/:accountId", getOthersInfo)
|
api.Get("/users/:accountId", getOthersInfo)
|
||||||
api.Get("/users/:accountId/follow", authMiddleware, getAccountFollowed)
|
|
||||||
api.Post("/users/:accountId/follow", authMiddleware, doFollowAccount)
|
|
||||||
|
|
||||||
api.Get("/attachments/o/:fileId", readAttachment)
|
api.Get("/attachments/o/:fileId", readAttachment)
|
||||||
api.Post("/attachments", authMiddleware, uploadAttachment)
|
api.Post("/attachments", authMiddleware, uploadAttachment)
|
||||||
@ -71,33 +69,16 @@ func NewServer() {
|
|||||||
|
|
||||||
api.Get("/feed", listFeed)
|
api.Get("/feed", listFeed)
|
||||||
|
|
||||||
posts := api.Group("/p/:postType").Use(useDynamicContext).Name("Dataset Universal API")
|
posts := api.Group("/posts").Name("Posts API")
|
||||||
{
|
{
|
||||||
posts.Get("/", listPost)
|
posts.Get("/", listPost)
|
||||||
posts.Get("/:postId", getPost)
|
posts.Get("/:postId", getPost)
|
||||||
|
posts.Post("/", authMiddleware, createPost)
|
||||||
posts.Post("/:postId/react", authMiddleware, reactPost)
|
posts.Post("/:postId/react", authMiddleware, reactPost)
|
||||||
posts.Get("/:postId/comments", listComment)
|
posts.Put("/:postId", authMiddleware, editPost)
|
||||||
posts.Post("/:postId/comments", authMiddleware, createComment)
|
posts.Delete("/:postId", authMiddleware, deletePost)
|
||||||
}
|
|
||||||
|
|
||||||
moments := api.Group("/p/moments").Name("Moments API")
|
posts.Get("/:postId/replies", listReplies)
|
||||||
{
|
|
||||||
moments.Post("/", authMiddleware, createMoment)
|
|
||||||
moments.Put("/:momentId", authMiddleware, editMoment)
|
|
||||||
moments.Delete("/:momentId", authMiddleware, deleteMoment)
|
|
||||||
}
|
|
||||||
|
|
||||||
articles := api.Group("/p/articles").Name("Articles API")
|
|
||||||
{
|
|
||||||
articles.Post("/", authMiddleware, createArticle)
|
|
||||||
articles.Put("/:articleId", authMiddleware, editArticle)
|
|
||||||
articles.Delete("/:articleId", authMiddleware, deleteArticle)
|
|
||||||
}
|
|
||||||
|
|
||||||
comments := api.Group("/p/comments").Name("Comments API")
|
|
||||||
{
|
|
||||||
comments.Put("/:commentId", authMiddleware, editComment)
|
|
||||||
comments.Delete("/:commentId", authMiddleware, deleteComment)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
api.Get("/categories", listCategories)
|
api.Get("/categories", listCategories)
|
||||||
|
@ -3,7 +3,6 @@ package server
|
|||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,45 +31,3 @@ func getOthersInfo(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
return c.JSON(data)
|
return c.JSON(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAccountFollowed(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
accountId, _ := c.ParamsInt("accountId", 0)
|
|
||||||
|
|
||||||
var data models.Account
|
|
||||||
if err := database.C.
|
|
||||||
Where(&models.Account{BaseModel: models.BaseModel{ID: uint(accountId)}}).
|
|
||||||
First(&data).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
_, status := services.GetAccountFollowed(user, data)
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
|
||||||
"is_followed": status,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func doFollowAccount(c *fiber.Ctx) error {
|
|
||||||
user := c.Locals("principal").(models.Account)
|
|
||||||
id, _ := c.ParamsInt("accountId", 0)
|
|
||||||
|
|
||||||
var account models.Account
|
|
||||||
if err := database.C.Where(&models.Account{
|
|
||||||
BaseModel: models.BaseModel{ID: uint(id)},
|
|
||||||
}).First(&account).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := services.GetAccountFollowed(user, account); ok {
|
|
||||||
if err := services.UnfollowAccount(user.ID, account.ID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
return c.SendStatus(fiber.StatusNoContent)
|
|
||||||
} else {
|
|
||||||
if err := services.FollowAccount(user.ID, account.ID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
return c.SendStatus(fiber.StatusCreated)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -11,30 +11,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FollowAccount(followerId, followingId uint) error {
|
|
||||||
relationship := models.AccountMembership{
|
|
||||||
FollowerID: followerId,
|
|
||||||
FollowingID: followingId,
|
|
||||||
}
|
|
||||||
return database.C.Create(&relationship).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func UnfollowAccount(followerId, followingId uint) error {
|
|
||||||
return database.C.Where(models.AccountMembership{
|
|
||||||
FollowerID: followerId,
|
|
||||||
FollowingID: followingId,
|
|
||||||
}).Delete(&models.AccountMembership{}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAccountFollowed(user models.Account, target models.Account) (models.AccountMembership, bool) {
|
|
||||||
var relationship models.AccountMembership
|
|
||||||
err := database.C.Model(&models.AccountMembership{}).
|
|
||||||
Where(&models.AccountMembership{FollowerID: user.ID, FollowingID: target.ID}).
|
|
||||||
First(&relationship).
|
|
||||||
Error
|
|
||||||
return relationship, err == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetAccountFriend(userId, relatedId uint, status int) (*proto.FriendshipResponse, error) {
|
func GetAccountFriend(userId, relatedId uint, status int) (*proto.FriendshipResponse, error) {
|
||||||
var user models.Account
|
var user models.Account
|
||||||
if err := database.C.Where("id = ?", userId).First(&user).Error; err != nil {
|
if err := database.C.Where("id = ?", userId).First(&user).Error; err != nil {
|
||||||
|
@ -11,7 +11,7 @@ func DoAutoDatabaseCleanup() {
|
|||||||
log.Debug().Time("deadline", deadline).Msg("Now cleaning up entire database...")
|
log.Debug().Time("deadline", deadline).Msg("Now cleaning up entire database...")
|
||||||
|
|
||||||
var count int64
|
var count int64
|
||||||
for _, model := range database.DatabaseAutoActionRange {
|
for _, model := range database.AutoMaintainRange {
|
||||||
tx := database.C.Unscoped().Delete(model, "deleted_at >= ?", deadline)
|
tx := database.C.Unscoped().Delete(model, "deleted_at >= ?", deadline)
|
||||||
if tx.Error != nil {
|
if tx.Error != nil {
|
||||||
log.Error().Err(tx.Error).Msg("An error occurred when running auth context cleanup...")
|
log.Error().Err(tx.Error).Msg("An error occurred when running auth context cleanup...")
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hydrogen/passport/pkg/grpc/proto"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (v *PostTypeContext) ListComment(id uint, take int, offset int, noReact ...bool) ([]*models.Feed, error) {
|
|
||||||
if take > 20 {
|
|
||||||
take = 20
|
|
||||||
}
|
|
||||||
|
|
||||||
var items []*models.Feed
|
|
||||||
table := viper.GetString("database.prefix") + "comments"
|
|
||||||
userTable := viper.GetString("database.prefix") + "accounts"
|
|
||||||
if err := v.Tx.
|
|
||||||
Table(table).
|
|
||||||
Select("*, ? as model_type", "comment").
|
|
||||||
Where(v.ColumnName+"_id = ?", id).
|
|
||||||
Joins(fmt.Sprintf("INNER JOIN %s as author ON author_id = author.id", userTable)).
|
|
||||||
Limit(take).Offset(offset).Find(&items).Error; err != nil {
|
|
||||||
return items, err
|
|
||||||
}
|
|
||||||
|
|
||||||
idx := lo.Map(items, func(item *models.Feed, index int) uint {
|
|
||||||
return item.ID
|
|
||||||
})
|
|
||||||
|
|
||||||
if len(noReact) <= 0 || !noReact[0] {
|
|
||||||
var reactions []struct {
|
|
||||||
PostID uint
|
|
||||||
Symbol string
|
|
||||||
Count int64
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.C.Model(&models.Reaction{}).
|
|
||||||
Select("comment_id as post_id, symbol, COUNT(id) as count").
|
|
||||||
Where("comment_id IN (?)", idx).
|
|
||||||
Group("post_id, symbol").
|
|
||||||
Scan(&reactions).Error; err != nil {
|
|
||||||
return items, err
|
|
||||||
}
|
|
||||||
|
|
||||||
itemMap := lo.SliceToMap(items, func(item *models.Feed) (uint, *models.Feed) {
|
|
||||||
return item.ID, item
|
|
||||||
})
|
|
||||||
|
|
||||||
list := map[uint]map[string]int64{}
|
|
||||||
for _, info := range reactions {
|
|
||||||
if _, ok := list[info.PostID]; !ok {
|
|
||||||
list[info.PostID] = make(map[string]int64)
|
|
||||||
}
|
|
||||||
list[info.PostID][info.Symbol] = info.Count
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range list {
|
|
||||||
if post, ok := itemMap[k]; ok {
|
|
||||||
post.ReactionList = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return items, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *PostTypeContext) CountComment(id uint) (int64, error) {
|
|
||||||
var count int64
|
|
||||||
if err := database.C.
|
|
||||||
Model(&models.Comment{}).
|
|
||||||
Where(v.ColumnName+"_id = ?", id).
|
|
||||||
Where("published_at <= ?", time.Now()).
|
|
||||||
Count(&count).Error; err != nil {
|
|
||||||
return count, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return count, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func CommentNotify(this models.PostInterface, original models.Feed, columnName, tableName string) {
|
|
||||||
var op models.Feed
|
|
||||||
if err := database.C.
|
|
||||||
Where(columnName+"_id = ?", original.ID).
|
|
||||||
Preload("Author").
|
|
||||||
Table(tableName).
|
|
||||||
First(&op).Error; err == nil {
|
|
||||||
if op.Author.ID != this.GetAuthor().ID {
|
|
||||||
postUrl := fmt.Sprintf("https://%s/posts/%d", viper.GetString("domain"), this.GetID())
|
|
||||||
err := NotifyAccount(
|
|
||||||
op.Author,
|
|
||||||
fmt.Sprintf("%s commented you", this.GetAuthor().Name),
|
|
||||||
fmt.Sprintf("%s commented your post. Check it out!", this.GetAuthor().Name),
|
|
||||||
false,
|
|
||||||
&proto.NotifyLink{Label: "Related post", Url: postUrl},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("An error occurred when notifying user...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
package services
|
|
@ -3,8 +3,6 @@ package services
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
"git.solsynth.dev/hydrogen/interactive/pkg/database"
|
||||||
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
"git.solsynth.dev/hydrogen/interactive/pkg/models"
|
||||||
"git.solsynth.dev/hydrogen/passport/pkg/grpc/proto"
|
"git.solsynth.dev/hydrogen/passport/pkg/grpc/proto"
|
||||||
@ -12,131 +10,90 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PostTypeContext struct {
|
func FilterPostWithCategory(tx *gorm.DB, alias string) *gorm.DB {
|
||||||
Tx *gorm.DB
|
return tx.Joins("JOIN post_categories ON posts.id = post_categories.post_id").
|
||||||
|
Joins("JOIN post_categories ON post_categories.id = post_categories.category_id").
|
||||||
TableName string
|
Where("post_categories.alias = ?", alias)
|
||||||
ColumnName string
|
|
||||||
CanReply bool
|
|
||||||
CanRepost bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) FilterWithCategory(alias string) *PostTypeContext {
|
func FilterPostWithTag(tx *gorm.DB, alias string) *gorm.DB {
|
||||||
name := v.ColumnName
|
return tx.Joins("JOIN post_tags ON posts.id = post_tags.post_id").
|
||||||
v.Tx.Joins(fmt.Sprintf("JOIN %s_categories ON %s.id = %s_categories.%s_id", name, v.TableName, name, name)).
|
Joins("JOIN post_tags ON post_tags.id = post_tags.category_id").
|
||||||
Joins(fmt.Sprintf("JOIN %s_categories ON %s_categories.id = %s_categories.category_id", name, name, name)).
|
Where("post_tags.alias = ?", alias)
|
||||||
Where(name+"_categories.alias = ?", alias)
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) FilterWithTag(alias string) *PostTypeContext {
|
func FilterWithRealm(tx *gorm.DB, id uint) *gorm.DB {
|
||||||
name := v.ColumnName
|
|
||||||
v.Tx.Joins(fmt.Sprintf("JOIN %s_tags ON %s.id = %s_tags.%s_id", name, v.TableName, name, name)).
|
|
||||||
Joins(fmt.Sprintf("JOIN %s_tags ON %s_tags.id = %s_tags.category_id", name, name, name)).
|
|
||||||
Where(name+"_tags.alias = ?", alias)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *PostTypeContext) FilterPublishedAt(date time.Time) *PostTypeContext {
|
|
||||||
v.Tx.Where("published_at <= ? AND published_at IS NULL", date)
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *PostTypeContext) FilterRealm(id uint) *PostTypeContext {
|
|
||||||
if id > 0 {
|
if id > 0 {
|
||||||
v.Tx = v.Tx.Where("realm_id = ?", id)
|
return tx.Where("realm_id = ?", id)
|
||||||
} else {
|
} else {
|
||||||
v.Tx = v.Tx.Where("realm_id IS NULL")
|
return tx.Where("realm_id IS NULL")
|
||||||
}
|
}
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) FilterAuthor(id uint) *PostTypeContext {
|
func FilterPostReply(tx *gorm.DB, replyTo ...uint) *gorm.DB {
|
||||||
v.Tx = v.Tx.Where("author_id = ?", id)
|
if len(replyTo) > 0 && replyTo[0] > 0 {
|
||||||
return v
|
return tx.Where("reply_id = ?", replyTo[0])
|
||||||
}
|
|
||||||
|
|
||||||
func (v *PostTypeContext) FilterReply(condition bool) *PostTypeContext {
|
|
||||||
if condition {
|
|
||||||
v.Tx = v.Tx.Where("reply_id IS NOT NULL")
|
|
||||||
} else {
|
} else {
|
||||||
v.Tx = v.Tx.Where("reply_id IS NULL")
|
return tx.Where("reply_id IS NULL")
|
||||||
}
|
}
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) SortCreatedAt(order string) *PostTypeContext {
|
func FilterPostWithPublishedAt(tx *gorm.DB, date time.Time) *gorm.DB {
|
||||||
v.Tx.Order(fmt.Sprintf("created_at %s", order))
|
return tx.Where("published_at <= ? AND published_at IS NULL", date)
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) GetViaAlias(alias string) (models.Feed, error) {
|
func GetPostWithAlias(alias string, ignoreLimitation ...bool) (models.Post, error) {
|
||||||
var item models.Feed
|
tx := database.C
|
||||||
table := viper.GetString("database.prefix") + v.TableName
|
if len(ignoreLimitation) == 0 || !ignoreLimitation[0] {
|
||||||
userTable := viper.GetString("database.prefix") + "accounts"
|
tx = FilterPostWithPublishedAt(tx, time.Now())
|
||||||
if err := v.Tx.
|
}
|
||||||
Table(table).
|
|
||||||
Select("*, ? as model_type", v.ColumnName).
|
var item models.Post
|
||||||
Joins(fmt.Sprintf("INNER JOIN %s AS author ON author_id = author.id", userTable)).
|
if err := tx.
|
||||||
Where("alias = ?", alias).
|
Where("alias = ?", alias).
|
||||||
|
Preload("Author").
|
||||||
|
Preload("Attachments").
|
||||||
First(&item).Error; err != nil {
|
First(&item).Error; err != nil {
|
||||||
return item, err
|
return item, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var attachments []models.Attachment
|
return item, nil
|
||||||
if err := database.C.
|
}
|
||||||
Model(&models.Attachment{}).
|
|
||||||
Where(v.ColumnName+"_id = ?", item.ID).
|
func GetPost(id uint, ignoreLimitation ...bool) (models.Post, error) {
|
||||||
Scan(&attachments).Error; err != nil {
|
tx := database.C
|
||||||
|
if len(ignoreLimitation) == 0 || !ignoreLimitation[0] {
|
||||||
|
tx = FilterPostWithPublishedAt(tx, time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
var item models.Post
|
||||||
|
if err := tx.
|
||||||
|
Where("id = ?", id).
|
||||||
|
Preload("Author").
|
||||||
|
Preload("Attachments").
|
||||||
|
First(&item).Error; err != nil {
|
||||||
return item, err
|
return item, err
|
||||||
} else {
|
|
||||||
item.Attachments = attachments
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return item, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) Get(id uint, noComments ...bool) (models.Feed, error) {
|
func CountPost(tx *gorm.DB) (int64, error) {
|
||||||
var item models.Feed
|
|
||||||
table := viper.GetString("database.prefix") + v.TableName
|
|
||||||
userTable := viper.GetString("database.prefix") + "accounts"
|
|
||||||
if err := v.Tx.
|
|
||||||
Table(table).
|
|
||||||
Select("*, ? as model_type", v.ColumnName).
|
|
||||||
Joins(fmt.Sprintf("INNER JOIN %s AS author ON author_id = author.id", userTable)).
|
|
||||||
Where("id = ?", id).First(&item).Error; err != nil {
|
|
||||||
return item, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var attachments []models.Attachment
|
|
||||||
if err := database.C.
|
|
||||||
Model(&models.Attachment{}).
|
|
||||||
Where(v.ColumnName+"_id = ?", id).
|
|
||||||
Scan(&attachments).Error; err != nil {
|
|
||||||
return item, err
|
|
||||||
} else {
|
|
||||||
item.Attachments = attachments
|
|
||||||
}
|
|
||||||
|
|
||||||
return item, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *PostTypeContext) Count() (int64, error) {
|
|
||||||
var count int64
|
var count int64
|
||||||
table := viper.GetString("database.prefix") + v.TableName
|
if err := tx.Model(&models.Post{}).Count(&count).Error; err != nil {
|
||||||
if err := v.Tx.Table(table).Count(&count).Error; err != nil {
|
|
||||||
return count, err
|
return count, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) CountComments(id uint) int64 {
|
func CountPostReply(id uint) int64 {
|
||||||
var count int64
|
var count int64
|
||||||
if err := database.C.Model(&models.Comment{}).
|
if err := database.C.Model(&models.Post{}).
|
||||||
Where(v.ColumnName+"_id = ?", id).
|
Where("reply_id = ?", id).
|
||||||
Count(&count).Error; err != nil {
|
Count(&count).Error; err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -144,10 +101,10 @@ func (v *PostTypeContext) CountComments(id uint) int64 {
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) CountReactions(id uint) int64 {
|
func CountPostReactions(id uint) int64 {
|
||||||
var count int64
|
var count int64
|
||||||
if err := database.C.Model(&models.Reaction{}).
|
if err := database.C.Model(&models.Reaction{}).
|
||||||
Where(v.ColumnName+"_id = ?", id).
|
Where("post_id = ?", id).
|
||||||
Count(&count).Error; err != nil {
|
Count(&count).Error; err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -155,7 +112,7 @@ func (v *PostTypeContext) CountReactions(id uint) int64 {
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) ListReactions(id uint) (map[string]int64, error) {
|
func ListPostReactions(id uint) (map[string]int64, error) {
|
||||||
var reactions []struct {
|
var reactions []struct {
|
||||||
Symbol string
|
Symbol string
|
||||||
Count int64
|
Count int64
|
||||||
@ -163,7 +120,7 @@ func (v *PostTypeContext) ListReactions(id uint) (map[string]int64, error) {
|
|||||||
|
|
||||||
if err := database.C.Model(&models.Reaction{}).
|
if err := database.C.Model(&models.Reaction{}).
|
||||||
Select("symbol, COUNT(id) as count").
|
Select("symbol, COUNT(id) as count").
|
||||||
Where(v.ColumnName+"_id = ?", id).
|
Where("post_id = ?", id).
|
||||||
Group("symbol").
|
Group("symbol").
|
||||||
Scan(&reactions).Error; err != nil {
|
Scan(&reactions).Error; err != nil {
|
||||||
return map[string]int64{}, err
|
return map[string]int64{}, err
|
||||||
@ -178,21 +135,22 @@ func (v *PostTypeContext) ListReactions(id uint) (map[string]int64, error) {
|
|||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) List(take int, offset int, noReact ...bool) ([]*models.Feed, error) {
|
func ListPost(tx *gorm.DB, take int, offset int, noReact ...bool) ([]models.Post, error) {
|
||||||
if take > 20 {
|
if take > 20 {
|
||||||
take = 20
|
take = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
var items []*models.Feed
|
var items []models.Post
|
||||||
table := viper.GetString("database.prefix") + v.TableName
|
if err := tx.
|
||||||
if err := v.Tx.
|
Limit(take).Offset(offset).
|
||||||
Table(table).
|
Order("created_at DESC").
|
||||||
Select("*, ? as model_type", v.ColumnName).
|
Preload("Author").
|
||||||
Limit(take).Offset(offset).Find(&items).Error; err != nil {
|
Preload("Attachments").
|
||||||
|
Find(&items).Error; err != nil {
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
idx := lo.Map(items, func(item *models.Feed, index int) uint {
|
idx := lo.Map(items, func(item models.Post, index int) uint {
|
||||||
return item.ID
|
return item.ID
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -204,14 +162,14 @@ func (v *PostTypeContext) List(take int, offset int, noReact ...bool) ([]*models
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := database.C.Model(&models.Reaction{}).
|
if err := database.C.Model(&models.Reaction{}).
|
||||||
Select(v.ColumnName+"_id as post_id, symbol, COUNT(id) as count").
|
Select("post_id as post_id, symbol, COUNT(id) as count").
|
||||||
Where(v.ColumnName+"_id IN (?)", idx).
|
Where("post_id IN (?)", idx).
|
||||||
Group("post_id, symbol").
|
Group("post_id, symbol").
|
||||||
Scan(&reactions).Error; err != nil {
|
Scan(&reactions).Error; err != nil {
|
||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
itemMap := lo.SliceToMap(items, func(item *models.Feed) (uint, *models.Feed) {
|
itemMap := lo.SliceToMap(items, func(item models.Post) (uint, models.Post) {
|
||||||
return item.ID, item
|
return item.ID, item
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -230,73 +188,34 @@ func (v *PostTypeContext) List(take int, offset int, noReact ...bool) ([]*models
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
var attachments []struct {
|
|
||||||
models.Attachment
|
|
||||||
|
|
||||||
PostID uint `json:"post_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
itemMap := lo.SliceToMap(items, func(item *models.Feed) (uint, *models.Feed) {
|
|
||||||
return item.ID, item
|
|
||||||
})
|
|
||||||
|
|
||||||
idx := lo.Map(items, func(item *models.Feed, index int) uint {
|
|
||||||
return item.ID
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := database.C.
|
|
||||||
Model(&models.Attachment{}).
|
|
||||||
Select(v.ColumnName+"_id as post_id, *").
|
|
||||||
Where(v.ColumnName+"_id IN (?)", idx).
|
|
||||||
Scan(&attachments).Error; err != nil {
|
|
||||||
return items, err
|
|
||||||
}
|
|
||||||
|
|
||||||
list := map[uint][]models.Attachment{}
|
|
||||||
for _, info := range attachments {
|
|
||||||
list[info.PostID] = append(list[info.PostID], info.Attachment)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range list {
|
|
||||||
if post, ok := itemMap[k]; ok {
|
|
||||||
post.Attachments = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MapCategoriesAndTags[T models.PostInterface](item T) (T, error) {
|
func InitPostCategoriesAndTags(item models.Post) (models.Post, error) {
|
||||||
var err error
|
var err error
|
||||||
categories := item.GetCategories()
|
for idx, category := range item.Categories {
|
||||||
for idx, category := range categories {
|
item.Categories[idx], err = GetCategory(category.Alias)
|
||||||
categories[idx], err = GetCategory(category.Alias)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return item, err
|
return item, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item.SetCategories(categories)
|
for idx, tag := range item.Tags {
|
||||||
tags := item.GetHashtags()
|
item.Tags[idx], err = GetTagOrCreate(tag.Alias, tag.Name)
|
||||||
for idx, tag := range tags {
|
|
||||||
tags[idx], err = GetTagOrCreate(tag.Alias, tag.Name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return item, err
|
return item, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item.SetHashtags(tags)
|
|
||||||
return item, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPost[T models.PostInterface](item T) (T, error) {
|
func NewPost(user models.Account, item models.Post) (models.Post, error) {
|
||||||
item, err := MapCategoriesAndTags(item)
|
item, err := InitPostCategoriesAndTags(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return item, err
|
return item, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.GetRealm() != nil {
|
if item.RealmID != nil {
|
||||||
_, err := GetRealmMember(item.GetRealm().ID, item.GetAuthor().ExternalID)
|
_, err := GetRealmMember(*item.RealmID, user.ExternalID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return item, fmt.Errorf("you aren't a part of that realm: %v", err)
|
return item, fmt.Errorf("you aren't a part of that realm: %v", err)
|
||||||
}
|
}
|
||||||
@ -306,19 +225,20 @@ func NewPost[T models.PostInterface](item T) (T, error) {
|
|||||||
return item, err
|
return item, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if item.GetReplyTo() != nil {
|
// Notify the original poster its post has been replied
|
||||||
|
if item.ReplyID != nil {
|
||||||
go func() {
|
go func() {
|
||||||
var op models.Moment
|
var op models.Post
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Where("id = ?", item.GetReplyTo()).
|
Where("id = ?", item.ReplyID).
|
||||||
Preload("Author").
|
Preload("Author").
|
||||||
First(&op).Error; err == nil {
|
First(&op).Error; err == nil {
|
||||||
if op.Author.ID != item.GetAuthor().ID {
|
if op.Author.ID != user.ID {
|
||||||
postUrl := fmt.Sprintf("https://%s/posts/%d", viper.GetString("domain"), item.GetID())
|
postUrl := fmt.Sprintf("https://%s/posts/%s", viper.GetString("domain"), item.Alias)
|
||||||
err := NotifyAccount(
|
err := NotifyAccount(
|
||||||
op.Author,
|
op.Author,
|
||||||
fmt.Sprintf("%s replied you", item.GetAuthor().Name),
|
fmt.Sprintf("%s replied you", user.Nick),
|
||||||
fmt.Sprintf("%s replied your post. Check it out!", item.GetAuthor().Name),
|
fmt.Sprintf("%s (%s) replied your post #%s.", user.Nick, user.Name, op.Alias),
|
||||||
false,
|
false,
|
||||||
&proto.NotifyLink{Label: "Related post", Url: postUrl},
|
&proto.NotifyLink{Label: "Related post", Url: postUrl},
|
||||||
)
|
)
|
||||||
@ -330,36 +250,11 @@ func NewPost[T models.PostInterface](item T) (T, error) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
var subscribers []models.AccountMembership
|
|
||||||
if err := database.C.Where(&models.AccountMembership{
|
|
||||||
FollowingID: item.GetAuthor().ID,
|
|
||||||
}).Preload("Follower").Find(&subscribers).Error; err == nil && len(subscribers) > 0 {
|
|
||||||
go func() {
|
|
||||||
accounts := lo.Map(subscribers, func(item models.AccountMembership, index int) models.Account {
|
|
||||||
return item.Follower
|
|
||||||
})
|
|
||||||
|
|
||||||
for _, account := range accounts {
|
|
||||||
postUrl := fmt.Sprintf("https://%s/posts/%d", viper.GetString("domain"), item.GetID())
|
|
||||||
err := NotifyAccount(
|
|
||||||
account,
|
|
||||||
fmt.Sprintf("%s just posted a post", item.GetAuthor().Name),
|
|
||||||
"Someone you followed post a brand new post. Check it out!",
|
|
||||||
false,
|
|
||||||
&proto.NotifyLink{Label: "Related post", Url: postUrl},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("An error occurred when notifying user...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
return item, nil
|
return item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditPost[T models.PostInterface](item T) (T, error) {
|
func EditPost(item models.Post) (models.Post, error) {
|
||||||
item, err := MapCategoriesAndTags(item)
|
item, err := InitPostCategoriesAndTags(item)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return item, err
|
return item, err
|
||||||
}
|
}
|
||||||
@ -369,11 +264,11 @@ func EditPost[T models.PostInterface](item T) (T, error) {
|
|||||||
return item, err
|
return item, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeletePost[T models.PostInterface](item T) error {
|
func DeletePost(item models.Post) error {
|
||||||
return database.C.Delete(&item).Error
|
return database.C.Delete(&item).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *PostTypeContext) React(reaction models.Reaction) (bool, models.Reaction, error) {
|
func ReactPost(reaction models.Reaction) (bool, models.Reaction, error) {
|
||||||
if err := database.C.Where(reaction).First(&reaction).Error; err != nil {
|
if err := database.C.Where(reaction).First(&reaction).Error; err != nil {
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return true, reaction, database.C.Save(&reaction).Error
|
return true, reaction, database.C.Save(&reaction).Error
|
||||||
|
Loading…
Reference in New Issue
Block a user