From ca44a04b28b462b2b69ebecd3a213879dc155f70 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Thu, 9 Jan 2025 18:53:32 +0800 Subject: [PATCH] :sparkles: Release CRUD apis --- pkg/internal/models/release.go | 7 +- pkg/internal/server/api/index.go | 9 ++ pkg/internal/server/api/releases_api.go | 167 ++++++++++++++++++++++++ pkg/internal/services/release.go | 65 +++++++++ 4 files changed, 245 insertions(+), 3 deletions(-) create mode 100644 pkg/internal/server/api/releases_api.go create mode 100644 pkg/internal/services/release.go diff --git a/pkg/internal/models/release.go b/pkg/internal/models/release.go index 7acc026..fb1135f 100644 --- a/pkg/internal/models/release.go +++ b/pkg/internal/models/release.go @@ -16,9 +16,10 @@ const ( type ProductRelease struct { cruda.BaseModel - Version string `json:"version"` - Type ProductReleaseType `json:"type"` - Assets datatypes.JSON `json:"assets"` + Version string `json:"version"` + Type ProductReleaseType `json:"type"` + Channel string `json:"channel"` + Assets datatypes.JSONType[map[string]any] `json:"assets"` ProductID uint `json:"product_id"` Meta ProductReleaseMeta `json:"meta" gorm:"foreignKey:ReleaseID"` diff --git a/pkg/internal/server/api/index.go b/pkg/internal/server/api/index.go index fff47c6..4bc1e83 100644 --- a/pkg/internal/server/api/index.go +++ b/pkg/internal/server/api/index.go @@ -15,6 +15,15 @@ func MapAPIs(app *fiber.App, baseURL string) { products.Post("/", createProduct) products.Put("/:productId", updateProduct) products.Delete("/:productId", deleteProduct) + + releases := products.Group("/:productId/releases") + { + releases.Get("/", listRelease) + releases.Get("/:releaseId", getRelease) + releases.Post("/", createRelease) + releases.Put("/:releaseId", updateRelease) + releases.Delete("/:releaseId", deleteRelease) + } } } } diff --git a/pkg/internal/server/api/releases_api.go b/pkg/internal/server/api/releases_api.go new file mode 100644 index 0000000..23b4805 --- /dev/null +++ b/pkg/internal/server/api/releases_api.go @@ -0,0 +1,167 @@ +package api + +import ( + "git.solsynth.dev/hypernet/nexus/pkg/nex/sec" + "git.solsynth.dev/matrix/nucleus/pkg/internal/models" + "git.solsynth.dev/matrix/nucleus/pkg/internal/server/exts" + "git.solsynth.dev/matrix/nucleus/pkg/internal/services" + "github.com/gofiber/fiber/v2" + "gorm.io/datatypes" +) + +func listRelease(c *fiber.Ctx) error { + take := c.QueryInt("take", 0) + offset := c.QueryInt("offset", 0) + id, _ := c.ParamsInt("productId", 0) + + count, err := services.CountRelease(id) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + items, err := services.ListRelease(id, take, offset) + if err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.JSON(fiber.Map{ + "count": count, + "data": items, + }) +} + +func getRelease(c *fiber.Ctx) error { + productId, _ := c.ParamsInt("productId", 0) + id, _ := c.ParamsInt("releaseId", 0) + + if item, err := services.GetReleaseWithProduct(uint(id), uint(productId)); err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } else { + return c.JSON(item) + } +} + +func createRelease(c *fiber.Ctx) error { + if err := sec.EnsureAuthenticated(c); err != nil { + return err + } + user := c.Locals("nex_user").(*sec.UserInfo) + + productId, _ := c.ParamsInt("productId", 0) + + var data struct { + Version string `json:"version" validate:"required"` + Type int `json:"type" validate:"required"` + Channel string `json:"channel" validate:"required"` + Title string `json:"title" validate:"required,max=1024"` + Description string `json:"description" validate:"required,max=4096"` + Content string `json:"content" validate:"required"` + Assets map[string]any `json:"assets" validate:"required"` + Attachments []string `json:"attachments"` + } + + if err := exts.BindAndValidate(c, &data); err != nil { + return err + } + + product, err := services.GetProductWithUser(uint(productId), user.ID) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + release := models.ProductRelease{ + Version: data.Version, + Type: models.ProductReleaseType(data.Type), + Channel: data.Channel, + Assets: datatypes.NewJSONType(data.Assets), + ProductID: product.ID, + Meta: models.ProductReleaseMeta{ + Title: data.Title, + Description: data.Description, + Content: data.Content, + Attachments: data.Attachments, + }, + } + + if release, err := services.NewRelease(release); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else { + return c.JSON(release) + } +} + +func updateRelease(c *fiber.Ctx) error { + if err := sec.EnsureAuthenticated(c); err != nil { + return err + } + user := c.Locals("nex_user").(*sec.UserInfo) + + productId, _ := c.ParamsInt("productId", 0) + id, _ := c.ParamsInt("releaseId", 0) + + var data struct { + Version string `json:"version" validate:"required"` + Type int `json:"type" validate:"required"` + Channel string `json:"channel" validate:"required"` + Title string `json:"title" validate:"required,max=1024"` + Description string `json:"description" validate:"required,max=4096"` + Content string `json:"content" validate:"required"` + Assets map[string]any `json:"assets" validate:"required"` + Attachments []string `json:"attachments"` + } + + if err := exts.BindAndValidate(c, &data); err != nil { + return err + } + + product, err := services.GetProductWithUser(uint(productId), user.ID) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + release, err := services.GetReleaseWithProduct(uint(id), product.ID) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + release.Version = data.Version + release.Type = models.ProductReleaseType(data.Type) + release.Channel = data.Channel + release.Assets = datatypes.NewJSONType(data.Assets) + release.Meta.Title = data.Title + release.Meta.Description = data.Description + release.Meta.Content = data.Content + release.Meta.Attachments = data.Attachments + + if release, err := services.UpdateRelease(release); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else { + return c.JSON(release) + } +} + +func deleteRelease(c *fiber.Ctx) error { + if err := sec.EnsureAuthenticated(c); err != nil { + return err + } + user := c.Locals("nex_user").(*sec.UserInfo) + + productId, _ := c.ParamsInt("productId", 0) + id, _ := c.ParamsInt("releaseId", 0) + + product, err := services.GetProductWithUser(uint(productId), user.ID) + if err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + release, err := services.GetReleaseWithProduct(uint(id), product.ID) + if err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + if _, err := services.DeleteRelease(release); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } else { + return c.SendStatus(fiber.StatusOK) + } +} diff --git a/pkg/internal/services/release.go b/pkg/internal/services/release.go new file mode 100644 index 0000000..ec64585 --- /dev/null +++ b/pkg/internal/services/release.go @@ -0,0 +1,65 @@ +package services + +import ( + "git.solsynth.dev/matrix/nucleus/pkg/internal/database" + "git.solsynth.dev/matrix/nucleus/pkg/internal/models" + "gorm.io/gorm" + "gorm.io/gorm/clause" +) + +func CountRelease(product int) (int64, error) { + var count int64 + if err := database.C.Model(&models.ProductRelease{}).Where("product_id = ?", product).Count(&count).Error; err != nil { + return count, err + } + return count, nil +} + +func ListRelease(product int, take, offset int) ([]models.ProductRelease, error) { + var items []models.ProductRelease + if err := database.C. + Where("product_id = ?", product).Preload("Meta"). + Limit(take).Offset(offset).Find(&items).Error; err != nil { + return items, err + } + return items, nil +} + +func GetRelease(id uint) (models.ProductRelease, error) { + var item models.ProductRelease + if err := database.C.Where("id = ?", id).Preload("Meta").First(&item).Error; err != nil { + return item, err + } + return item, nil +} + +func GetReleaseWithProduct(id uint, product uint) (models.ProductRelease, error) { + var item models.ProductRelease + if err := database.C.Where("id = ? AND product_id = ?", id, product).Preload("Meta").First(&item).Error; err != nil { + return item, err + } + return item, nil +} + +func NewRelease(item models.ProductRelease) (models.ProductRelease, error) { + if err := database.C.Create(&item).Error; err != nil { + return item, err + } + return item, nil +} + +func UpdateRelease(item models.ProductRelease) (models.ProductRelease, error) { + if err := database.C.Session(&gorm.Session{ + FullSaveAssociations: true, + }).Save(&item).Error; err != nil { + return item, err + } + return item, nil +} + +func DeleteRelease(item models.ProductRelease) (models.ProductRelease, error) { + if err := database.C.Select(clause.Associations).Delete(&item).Error; err != nil { + return item, err + } + return item, nil +}