diff --git a/pkg/server/posts_api.go b/pkg/server/posts_api.go index d1919fa..86f3935 100644 --- a/pkg/server/posts_api.go +++ b/pkg/server/posts_api.go @@ -2,6 +2,7 @@ package server import ( "strings" + "time" "code.smartsheep.studio/hydrogen/interactive/pkg/database" "code.smartsheep.studio/hydrogen/interactive/pkg/models" @@ -16,7 +17,10 @@ func listPost(c *fiber.Ctx) error { offset := c.QueryInt("offset", 0) authorId := c.QueryInt("authorId", 0) - tx := database.C.Where(&models.Post{RealmID: nil}).Order("created_at desc") + tx := database.C. + Where(&models.Post{RealmID: nil}). + Where("published_at <= ? OR published_at IS NULL", time.Now()). + Order("created_at desc") if authorId > 0 { tx = tx.Where(&models.Post{AuthorID: uint(authorId)}) @@ -44,13 +48,14 @@ func createPost(c *fiber.Ctx) error { user := c.Locals("principal").(models.Account) var data struct { - Alias string `json:"alias"` - Title string `json:"title"` - Content string `json:"content" validate:"required"` - Tags []models.Tag `json:"tags"` - Categories []models.Category `json:"categories"` - RepostTo uint `json:"repost_to"` - ReplyTo uint `json:"reply_to"` + Alias string `json:"alias"` + Title string `json:"title"` + Content string `json:"content" validate:"required"` + Tags []models.Tag `json:"tags"` + Categories []models.Category `json:"categories"` + PublishedAt *time.Time `json:"published_at"` + RepostTo uint `json:"repost_to"` + ReplyTo uint `json:"reply_to"` } if err := BindAndValidate(c, &data); err != nil { @@ -84,7 +89,58 @@ func createPost(c *fiber.Ctx) error { } } - post, err := services.NewPost(user, data.Alias, data.Title, data.Content, data.Categories, data.Tags, replyTo, repostTo) + post, err := services.NewPost( + user, + data.Alias, + data.Title, + data.Content, + data.Categories, + data.Tags, + data.PublishedAt, + replyTo, + repostTo, + ) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + return c.JSON(post) +} + +func editPost(c *fiber.Ctx) error { + user := c.Locals("principal").(models.Account) + id, _ := c.ParamsInt("postId", 0) + + var data struct { + Alias string `json:"alias" validate:"required"` + Title string `json:"title"` + Content string `json:"content" validate:"required"` + PublishedAt *time.Time `json:"published_at"` + Tags []models.Tag `json:"tags"` + Categories []models.Category `json:"categories"` + } + + if err := BindAndValidate(c, &data); err != nil { + return err + } + + var post models.Post + if err := database.C.Where(&models.Post{ + BaseModel: models.BaseModel{ID: uint(id)}, + AuthorID: user.ID, + }).First(&post).Error; err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + post, err := services.EditPost( + post, + data.Alias, + data.Title, + data.Content, + data.PublishedAt, + data.Categories, + data.Tags, + ) if err != nil { return fiber.NewError(fiber.StatusBadRequest, err.Error()) } @@ -120,3 +176,22 @@ func reactPost(c *fiber.Ctx) error { return fiber.NewError(fiber.StatusBadRequest, "unsupported reaction") } } + +func deletePost(c *fiber.Ctx) error { + user := c.Locals("principal").(models.Account) + id, _ := c.ParamsInt("postId", 0) + + var post models.Post + if err := database.C.Where(&models.Post{ + BaseModel: models.BaseModel{ID: uint(id)}, + AuthorID: user.ID, + }).First(&post).Error; err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + if err := services.DeletePost(post); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + return c.SendStatus(fiber.StatusOK) +} diff --git a/pkg/server/startup.go b/pkg/server/startup.go index 4e0f3a7..f868576 100644 --- a/pkg/server/startup.go +++ b/pkg/server/startup.go @@ -68,6 +68,8 @@ func NewServer() { api.Get("/posts", listPost) api.Post("/posts", auth, createPost) api.Post("/posts/:postId/react/:reactType", auth, reactPost) + api.Put("/posts/:postId", auth, editPost) + api.Delete("/posts/:postId", auth, deletePost) } A.Use("/", cache.New(cache.Config{ diff --git a/pkg/services/posts.go b/pkg/services/posts.go index 297b672..18918d2 100644 --- a/pkg/services/posts.go +++ b/pkg/services/posts.go @@ -3,6 +3,7 @@ package services import ( "errors" "fmt" + "time" "code.smartsheep.studio/hydrogen/interactive/pkg/database" "code.smartsheep.studio/hydrogen/interactive/pkg/models" @@ -67,9 +68,21 @@ func NewPost( alias, title, content string, categories []models.Category, tags []models.Tag, + publishedAt *time.Time, replyTo, repostTo *uint, ) (models.Post, error) { - return NewPostWithRealm(user, nil, alias, title, content, categories, tags, replyTo, repostTo) + return NewPostWithRealm( + user, + nil, + alias, + title, + content, + categories, + tags, + publishedAt, + replyTo, + repostTo, + ) } func NewPostWithRealm( @@ -78,6 +91,7 @@ func NewPostWithRealm( alias, title, content string, categories []models.Category, tags []models.Tag, + publishedAt *time.Time, replyTo, repostTo *uint, ) (models.Post, error) { var err error @@ -100,16 +114,21 @@ func NewPostWithRealm( realmId = &realm.ID } + if publishedAt == nil { + publishedAt = lo.ToPtr(time.Now()) + } + post = models.Post{ - Alias: alias, - Title: title, - Content: content, - Tags: tags, - Categories: categories, - AuthorID: user.ID, - RealmID: realmId, - RepostID: repostTo, - ReplyID: replyTo, + Alias: alias, + Title: title, + Content: content, + Tags: tags, + Categories: categories, + AuthorID: user.ID, + RealmID: realmId, + PublishedAt: *publishedAt, + RepostID: repostTo, + ReplyID: replyTo, } if err := database.C.Save(&post).Error; err != nil { @@ -119,6 +138,41 @@ func NewPostWithRealm( return post, nil } +func EditPost( + post models.Post, + alias, title, content string, + publishedAt *time.Time, + categories []models.Category, + tags []models.Tag, +) (models.Post, error) { + var err error + for idx, category := range categories { + categories[idx], err = GetCategory(category.Alias, category.Name) + if err != nil { + return post, err + } + } + for idx, tag := range tags { + tags[idx], err = GetTag(tag.Alias, tag.Name) + if err != nil { + return post, err + } + } + + if publishedAt == nil { + publishedAt = lo.ToPtr(time.Now()) + } + + post.Alias = alias + post.Title = title + post.Content = content + post.PublishedAt = *publishedAt + + err = database.C.Save(&post).Error + + return post, err +} + func LikePost(user models.Account, post models.Post) (bool, error) { var like models.PostLike if err := database.C.Where(&models.PostLike{ @@ -156,3 +210,7 @@ func DislikePost(user models.Account, post models.Post) (bool, error) { return false, database.C.Delete(&dislike).Error } } + +func DeletePost(post models.Post) error { + return database.C.Delete(&post).Error +} diff --git a/pkg/view/src/components/PostItem.tsx b/pkg/view/src/components/PostItem.tsx index fef6fb9..21102d9 100644 --- a/pkg/view/src/components/PostItem.tsx +++ b/pkg/view/src/components/PostItem.tsx @@ -8,6 +8,7 @@ export default function PostItem(props: { onRepost?: (post: any) => void, onReply?: (post: any) => void, onEdit?: (post: any) => void, + onDelete?: (post: any) => void, onError: (message: string | null) => void, onReact: () => void }) { @@ -127,6 +128,9 @@ export default function PostItem(props: {