From c185fde553811603c35ef4683604fc4c331f882b Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 31 Oct 2024 23:06:37 +0800 Subject: [PATCH] :sparkles: Publisher CRUD --- pkg/internal/http/api/index.go | 13 ++- pkg/internal/http/api/publishers_api.go | 149 +++++++++++++++++++++++- pkg/internal/http/api/users_api.go | 30 ----- pkg/internal/services/publishers.go | 83 +++++++++++++ 4 files changed, 240 insertions(+), 35 deletions(-) delete mode 100644 pkg/internal/http/api/users_api.go diff --git a/pkg/internal/http/api/index.go b/pkg/internal/http/api/index.go index bbf750e..3cfa863 100644 --- a/pkg/internal/http/api/index.go +++ b/pkg/internal/http/api/index.go @@ -7,9 +7,16 @@ import ( func MapAPIs(app *fiber.App, baseURL string) { api := app.Group(baseURL).Name("API") { - api.Get("/users/:account/pins", listUserPinnedPost) - - api.Get("/publishers/:name", getPublisher) + publishers := api.Group("/publishers").Name("Publisher API") + { + publishers.Get("/:name/pins", listPinnedPost) + publishers.Get("/:name", getPublisher) + publishers.Get("/owned", listOwnedPublisher) + publishers.Post("/:name/personal", createPersonalPublisher) + publishers.Post("/:name/organization", createOrganizationPublisher) + publishers.Put("/:publisherId", editPublisher) + publishers.Delete("/:publisherId", deletePublisher) + } recommendations := api.Group("/recommendations").Name("Recommendations API") { diff --git a/pkg/internal/http/api/publishers_api.go b/pkg/internal/http/api/publishers_api.go index 5f9f041..badc896 100644 --- a/pkg/internal/http/api/publishers_api.go +++ b/pkg/internal/http/api/publishers_api.go @@ -1,9 +1,154 @@ package api import ( + "fmt" + "git.solsynth.dev/hydrogen/interactive/pkg/internal/database" + "git.solsynth.dev/hydrogen/interactive/pkg/internal/gap" + "git.solsynth.dev/hydrogen/interactive/pkg/internal/http/exts" + "git.solsynth.dev/hydrogen/interactive/pkg/internal/models" + "git.solsynth.dev/hydrogen/interactive/pkg/internal/services" + "git.solsynth.dev/hypernet/nexus/pkg/nex/sec" + "git.solsynth.dev/hypernet/passport/pkg/authkit" + authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models" "github.com/gofiber/fiber/v2" ) -func getPublisher(c *fiber.Ctx) error { - panic("TODO") +func listPinnedPost(c *fiber.Ctx) error { + name := c.Params("name") + + var user models.Publisher + if err := database.C. + Where("name = ?", name). + First(&user).Error; err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + tx := services.FilterPostDraft(database.C) + tx = tx.Where("author_id = ?", user.ID) + tx = tx.Where("pinned_at IS NOT NULL") + + items, err := services.ListPost(tx, 100, 0, "published_at DESC") + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + return c.JSON(items) +} + +func getPublisher(c *fiber.Ctx) error { + name := c.Params("name") + + var publisher models.Publisher + if err := database.C.Where("name = ?", name).First(&publisher).Error; err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + return c.JSON(publisher) +} + +func listOwnedPublisher(c *fiber.Ctx) error { + user := c.Locals("user").(authm.Account) + + var publishers []models.Publisher + if err := database.C.Where("account_id = ?", user.ID).Find(&publishers).Error; err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + return c.JSON(publishers) +} + +func createPersonalPublisher(c *fiber.Ctx) error { + if err := sec.EnsureGrantedPerm(c, "CreatePublisher", true); err != nil { + return err + } + user := c.Locals("user").(authm.Account) + + if pub, err := services.CreatePersonalPublisher(user); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else { + return c.JSON(pub) + } +} + +func createOrganizationPublisher(c *fiber.Ctx) error { + if err := sec.EnsureGrantedPerm(c, "CreatePublisher", true); err != nil { + return err + } + user := c.Locals("user").(authm.Account) + + var data struct { + Realm string `json:"realm"` + } + + if err := exts.BindAndValidate(c, &data); err != nil { + return err + } + + realm, err := authkit.GetRealmByAlias(gap.Nx, data.Realm) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to get realm: %v", err)) + } + if !authkit.CheckRealmMemberPerm(gap.Nx, realm.ID, int(user.ID), 100) { + return fiber.NewError(fiber.StatusForbidden, "you least need to be the admin of this realm to create a publisher") + } + + if pub, err := services.CreateOrganizationPublisher(user, realm); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else { + return c.JSON(pub) + } +} + +func editPublisher(c *fiber.Ctx) error { + user := c.Locals("user").(authm.Account) + + id, _ := c.ParamsInt("publisherId", 0) + publisher, err := services.GetPublisher(uint(id), user.ID) + if err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + var data struct { + Name string `json:"name"` + Nick string `json:"nick"` + Description string `json:"description"` + Avatar string `json:"avatar"` + Banner string `json:"banner"` + AccountID *uint `json:"account_id"` + } + + if err := exts.BindAndValidate(c, &data); err != nil { + return err + } + + publisher.Name = data.Name + publisher.Nick = data.Nick + publisher.Description = data.Description + publisher.Avatar = data.Avatar + publisher.Banner = data.Banner + if data.AccountID != nil { + publisher.AccountID = data.AccountID + } + + if publisher, err = services.EditPublisher(user, publisher); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + return c.JSON(publisher) +} + +func deletePublisher(c *fiber.Ctx) error { + user := c.Locals("user").(authm.Account) + + id, _ := c.ParamsInt("publisherId", 0) + publisher, err := services.GetPublisher(uint(id), user.ID) + if err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + if err := services.DeletePublisher(publisher); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + return c.SendStatus(fiber.StatusOK) } diff --git a/pkg/internal/http/api/users_api.go b/pkg/internal/http/api/users_api.go deleted file mode 100644 index 893a136..0000000 --- a/pkg/internal/http/api/users_api.go +++ /dev/null @@ -1,30 +0,0 @@ -package api - -import ( - "git.solsynth.dev/hydrogen/interactive/pkg/internal/database" - "git.solsynth.dev/hydrogen/interactive/pkg/internal/models" - "git.solsynth.dev/hydrogen/interactive/pkg/internal/services" - "github.com/gofiber/fiber/v2" -) - -func listUserPinnedPost(c *fiber.Ctx) error { - account := c.Params("account") - - var user models.Publisher - if err := database.C. - Where("name = ?", account). - First(&user).Error; err != nil { - return fiber.NewError(fiber.StatusInternalServerError, err.Error()) - } - - tx := services.FilterPostDraft(database.C) - tx = tx.Where("author_id = ?", user.ID) - tx = tx.Where("pinned_at IS NOT NULL") - - items, err := services.ListPost(tx, 100, 0, "published_at DESC") - if err != nil { - return fiber.NewError(fiber.StatusBadRequest, err.Error()) - } - - return c.JSON(items) -} diff --git a/pkg/internal/services/publishers.go b/pkg/internal/services/publishers.go index c06af24..9f82d54 100644 --- a/pkg/internal/services/publishers.go +++ b/pkg/internal/services/publishers.go @@ -4,6 +4,7 @@ import ( "fmt" "git.solsynth.dev/hydrogen/interactive/pkg/internal/database" "git.solsynth.dev/hydrogen/interactive/pkg/internal/models" + authm "git.solsynth.dev/hypernet/passport/pkg/authkit/models" ) func GetPublisher(id uint, userID uint) (models.Publisher, error) { @@ -13,3 +14,85 @@ func GetPublisher(id uint, userID uint) (models.Publisher, error) { } return publisher, nil } + +func CreatePersonalPublisher(user authm.Account) (models.Publisher, error) { + var publisher models.Publisher + var count int64 + if err := database.C. + Model(&models.Publisher{}). + Where("account_id = ? AND type = ?", user.ID, models.PublisherTypePersonal). + Count(&count).Error; err != nil { + return publisher, fmt.Errorf("unable to count exsisting publisher: %v", err) + } + if count > 0 { + return publisher, fmt.Errorf("personal publisher already exists") + } + + publisher = models.Publisher{ + Type: models.PublisherTypePersonal, + Name: user.Name, + Nick: user.Nick, + Description: user.Description, + AccountID: &user.ID, + } + if user.Avatar != nil { + publisher.Avatar = *user.Avatar + } + if user.Banner != nil { + publisher.Banner = *user.Banner + } + + if err := database.C.Create(&publisher).Error; err != nil { + return publisher, err + } + return publisher, nil +} + +func CreateOrganizationPublisher(user authm.Account, realm authm.Realm) (models.Publisher, error) { + var publisher models.Publisher + var count int64 + if err := database.C. + Model(&models.Publisher{}). + Where("realm_id = ? AND type = ?", realm.ID, models.PublisherTypeOrganization). + Count(&count).Error; err != nil { + return publisher, fmt.Errorf("unable to count exsisting publisher: %v", err) + } + if count > 0 { + return publisher, fmt.Errorf("organization publisher already exists") + } + + publisher = models.Publisher{ + Type: models.PublisherTypeOrganization, + Name: realm.Alias, + Nick: realm.Name, + Description: realm.Description, + RealmID: &realm.ID, + AccountID: &user.ID, + } + if realm.Avatar != nil { + publisher.Avatar = *realm.Avatar + } + if realm.Banner != nil { + publisher.Banner = *realm.Banner + } + + if err := database.C.Create(&publisher).Error; err != nil { + return publisher, err + } + return publisher, nil +} + +func EditPublisher(user authm.Account, publisher models.Publisher) (models.Publisher, error) { + if publisher.Type == models.PublisherTypePersonal { + if *publisher.AccountID != user.ID { + return publisher, fmt.Errorf("you cannot transfer personal publisher") + } + } + + err := database.C.Save(&publisher).Error + return publisher, err +} + +func DeletePublisher(publisher models.Publisher) error { + return database.C.Delete(&publisher).Error +}