{item.author.name}
+{item.author.description}
+diff --git a/go.mod b/go.mod index 033af90..1cfe372 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module code.smartsheep.studio/hydrogen/interactive go 1.21.6 require ( - code.smartsheep.studio/hydrogen/passport v0.0.0-20240201075828-dbc09bd7af8a github.com/go-playground/validator/v10 v10.17.0 github.com/gofiber/fiber/v2 v2.52.0 github.com/golang-jwt/jwt/v5 v5.2.0 diff --git a/go.sum b/go.sum index aeb3b53..913c534 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -code.smartsheep.studio/hydrogen/passport v0.0.0-20240201075828-dbc09bd7af8a h1:66YEiBiB+S7QaUnNqQ/Q8zzgGNkQwcHlJdE2s/RdV6k= -code.smartsheep.studio/hydrogen/passport v0.0.0-20240201075828-dbc09bd7af8a/go.mod h1:LM5I1tdQLXY6n+hou3TPNV/v9hA/cD59zlYpspdzGks= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= diff --git a/pkg/database/migrator.go b/pkg/database/migrator.go index ed17371..4a1ac28 100644 --- a/pkg/database/migrator.go +++ b/pkg/database/migrator.go @@ -8,6 +8,10 @@ import ( func RunMigration(source *gorm.DB) error { if err := source.AutoMigrate( &models.Account{}, + &models.Realm{}, + &models.Category{}, + &models.Tag{}, + &models.Post{}, ); err != nil { return err } diff --git a/pkg/models/accounts.go b/pkg/models/accounts.go index a72340c..7e511e3 100644 --- a/pkg/models/accounts.go +++ b/pkg/models/accounts.go @@ -6,9 +6,12 @@ package models type Account struct { BaseModel - Name string `json:"name"` - Avatar string `json:"avatar"` - EmailAddress string `json:"email_address"` - PowerLevel int `json:"power_level"` - ExternalID uint `json:"external_id"` + Name string `json:"name"` + Avatar string `json:"avatar"` + Description string `json:"description"` + EmailAddress string `json:"email_address"` + PowerLevel int `json:"power_level"` + Posts []Post `json:"posts" gorm:"foreignKey:AuthorID"` + Realms []Realm `json:"realms"` + ExternalID uint `json:"external_id"` } diff --git a/pkg/models/categories.go b/pkg/models/categories.go new file mode 100644 index 0000000..20c232c --- /dev/null +++ b/pkg/models/categories.go @@ -0,0 +1,19 @@ +package models + +type Tag struct { + BaseModel + + Alias string `json:"alias" gorm:"uniqueIndex"` + Name string `json:"name"` + Description string `json:"description"` + Posts []Post `json:"posts" gorm:"many2many:post_tags"` +} + +type Category struct { + BaseModel + + Alias string `json:"alias" gorm:"uniqueIndex"` + Name string `json:"name"` + Description string `json:"description"` + Posts []Post `json:"categories" gorm:"many2many:post_categories"` +} diff --git a/pkg/models/posts.go b/pkg/models/posts.go new file mode 100644 index 0000000..a04dad9 --- /dev/null +++ b/pkg/models/posts.go @@ -0,0 +1,17 @@ +package models + +import "time" + +type Post struct { + BaseModel + + Alias string `json:"alias" gorm:"uniqueIndex"` + Title string `json:"title"` + Content string `json:"content"` + Tags []Tag `gorm:"many2many:post_tags"` + Categories []Category `gorm:"many2many:post_categories"` + PublishedAt time.Time `json:"published_at"` + RealmID *uint `json:"realm_id"` + AuthorID uint `json:"author_id"` + Author Account `json:"author"` +} diff --git a/pkg/models/realms.go b/pkg/models/realms.go new file mode 100644 index 0000000..370d87b --- /dev/null +++ b/pkg/models/realms.go @@ -0,0 +1,10 @@ +package models + +type Realm struct { + BaseModel + + Name string `json:"name"` + Description string `json:"description"` + Posts []Post `json:"posts"` + AccountID uint `json:"account_id"` +} diff --git a/pkg/server/auth.go b/pkg/server/auth.go index 07ea9ee..b221a4d 100644 --- a/pkg/server/auth.go +++ b/pkg/server/auth.go @@ -3,7 +3,7 @@ package server import ( "code.smartsheep.studio/hydrogen/interactive/pkg/database" "code.smartsheep.studio/hydrogen/interactive/pkg/models" - "code.smartsheep.studio/hydrogen/passport/pkg/security" + "code.smartsheep.studio/hydrogen/interactive/pkg/security" "github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2/middleware/keyauth" "strconv" diff --git a/pkg/server/posts_api.go b/pkg/server/posts_api.go new file mode 100644 index 0000000..0d7fb2a --- /dev/null +++ b/pkg/server/posts_api.go @@ -0,0 +1,59 @@ +package server + +import ( + "code.smartsheep.studio/hydrogen/interactive/pkg/database" + "code.smartsheep.studio/hydrogen/interactive/pkg/models" + "code.smartsheep.studio/hydrogen/interactive/pkg/services" + "github.com/gofiber/fiber/v2" +) + +func listPost(c *fiber.Ctx) error { + take := c.QueryInt("take", 0) + offset := c.QueryInt("offset", 0) + + var count int64 + var posts []models.Post + if err := database.C. + Model(&models.Post{}). + Count(&count).Error; err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + if err := database.C. + Limit(take). + Offset(offset). + Order("created_at desc"). + Preload("Author"). + Find(&posts).Error; err != nil { + return fiber.NewError(fiber.StatusInternalServerError, err.Error()) + } + + return c.JSON(fiber.Map{ + "count": count, + "data": posts, + }) + +} + +func createPost(c *fiber.Ctx) error { + user := c.Locals("principal").(models.Account) + + var data struct { + Alias string `json:"alias" validate:"required"` + Title string `json:"title" validate:"required"` + Content string `json:"content" validate:"required"` + Tags []models.Tag `json:"tags"` + Categories []models.Category `json:"categories"` + } + + if err := BindAndValidate(c, &data); err != nil { + return err + } + + post, err := services.NewPost(user, data.Alias, data.Title, data.Content, data.Categories, data.Tags) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + return c.JSON(post) +} diff --git a/pkg/server/startup.go b/pkg/server/startup.go index f81ace2..a41e4c5 100644 --- a/pkg/server/startup.go +++ b/pkg/server/startup.go @@ -61,6 +61,9 @@ func NewServer() { api.Get("/auth", doLogin) api.Get("/auth/callback", doPostLogin) api.Post("/auth/refresh", doRefreshToken) + + api.Get("/posts", listPost) + api.Post("/posts", auth, createPost) } A.Use("/", cache.New(cache.Config{ diff --git a/pkg/services/categories.go b/pkg/services/categories.go new file mode 100644 index 0000000..416d6fa --- /dev/null +++ b/pkg/services/categories.go @@ -0,0 +1,40 @@ +package services + +import ( + "code.smartsheep.studio/hydrogen/interactive/pkg/database" + "code.smartsheep.studio/hydrogen/interactive/pkg/models" + "errors" + "gorm.io/gorm" +) + +func GetCategory(alias, name string) (models.Category, error) { + var category models.Category + if err := database.C.Where(models.Category{Alias: alias}).First(&category).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + category = models.Category{ + Alias: alias, + Name: name, + } + err := database.C.Save(&category).Error + return category, err + } + return category, err + } + return category, nil +} + +func GetTag(alias, name string) (models.Tag, error) { + var tag models.Tag + if err := database.C.Where(models.Category{Alias: alias}).First(&tag).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + tag = models.Tag{ + Alias: alias, + Name: name, + } + err := database.C.Save(&tag).Error + return tag, err + } + return tag, err + } + return tag, nil +} diff --git a/pkg/services/posts.go b/pkg/services/posts.go new file mode 100644 index 0000000..5ad700e --- /dev/null +++ b/pkg/services/posts.go @@ -0,0 +1,59 @@ +package services + +import ( + "code.smartsheep.studio/hydrogen/interactive/pkg/database" + "code.smartsheep.studio/hydrogen/interactive/pkg/models" +) + +func NewPost( + user models.Account, + alias, title, content string, + categories []models.Category, + tags []models.Tag, +) (models.Post, error) { + return NewPostWithRealm(user, nil, alias, title, content, categories, tags) +} + +func NewPostWithRealm( + user models.Account, + realm *models.Realm, + alias, title, content string, + categories []models.Category, + tags []models.Tag, +) (models.Post, error) { + var err error + var post models.Post + 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 + } + } + + var realmId *uint + if realm != nil { + realmId = &realm.ID + } + + post = models.Post{ + Alias: alias, + Title: title, + Content: content, + Tags: tags, + Categories: categories, + AuthorID: user.ID, + RealmID: realmId, + } + + if err := database.C.Save(&post).Error; err != nil { + return post, err + } + + return post, nil +} diff --git a/pkg/view/index.html b/pkg/view/index.html index 3c940c2..731eaea 100644 --- a/pkg/view/index.html +++ b/pkg/view/index.html @@ -4,7 +4,7 @@ -
{getGreeting()}
-{item.author.description}
+