Realms

This commit is contained in:
LittleSheep 2024-02-05 15:51:31 +08:00
parent 28b4f11ccf
commit 0c8afb2bae
10 changed files with 204 additions and 17 deletions

View File

@ -9,6 +9,7 @@ type Account struct {
BaseModel BaseModel
Name string `json:"name"` Name string `json:"name"`
Nick string `json:"nick"`
Avatar string `json:"avatar"` Avatar string `json:"avatar"`
Description string `json:"description"` Description string `json:"description"`
EmailAddress string `json:"email_address"` EmailAddress string `json:"email_address"`

126
pkg/server/realms_api.go Normal file
View File

@ -0,0 +1,126 @@
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"
"github.com/samber/lo"
"time"
)
func listRealms(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
realms, err := services.ListRealms(user)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.JSON(realms)
}
func listPostInRealm(c *fiber.Ctx) error {
take := c.QueryInt("take", 0)
offset := c.QueryInt("offset", 0)
authorId := c.QueryInt("authorId", 0)
realmId, _ := c.ParamsInt("realmId", 0)
tx := database.C.
Where(&models.Post{RealmID: lo.ToPtr(uint(realmId))}).
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)})
}
var count int64
if err := tx.
Model(&models.Post{}).
Count(&count).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
posts, err := services.ListPost(tx, take, offset)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.JSON(fiber.Map{
"count": count,
"data": posts,
})
}
func createRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
if user.PowerLevel < 10 {
return fiber.NewError(fiber.StatusForbidden, "require power level 10 to create realm")
}
var data struct {
Name string `json:"name" validate:"required"`
Description string `json:"description"`
}
if err := BindAndValidate(c, &data); err != nil {
return err
}
realm, err := services.NewRealm(user, data.Name, data.Description)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.JSON(realm)
}
func editRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
id, _ := c.ParamsInt("realmId", 0)
var data struct {
Name string `json:"name" validate:"required"`
Description string `json:"description"`
}
if err := BindAndValidate(c, &data); err != nil {
return err
}
var realm models.Realm
if err := database.C.Where(&models.Realm{
BaseModel: models.BaseModel{ID: uint(id)},
AccountID: user.ID,
}).First(&realm).Error; err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
realm, err := services.EditRealm(realm, data.Name, data.Description)
if err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.JSON(realm)
}
func deleteRealm(c *fiber.Ctx) error {
user := c.Locals("principal").(models.Account)
id, _ := c.ParamsInt("realmId", 0)
var realm models.Realm
if err := database.C.Where(&models.Realm{
BaseModel: models.BaseModel{ID: uint(id)},
AccountID: user.ID,
}).First(&realm).Error; err != nil {
return fiber.NewError(fiber.StatusNotFound, err.Error())
}
if err := services.DeleteRealm(realm); err != nil {
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
return c.SendStatus(fiber.StatusOK)
}

View File

@ -73,6 +73,12 @@ func NewServer() {
api.Post("/posts/:postId/react/:reactType", auth, reactPost) api.Post("/posts/:postId/react/:reactType", auth, reactPost)
api.Put("/posts/:postId", auth, editPost) api.Put("/posts/:postId", auth, editPost)
api.Delete("/posts/:postId", auth, deletePost) api.Delete("/posts/:postId", auth, deletePost)
api.Get("/realms", auth, listRealms)
api.Get("/realms/:realmId/posts", listPostInRealm)
api.Post("/realms", auth, createRealm)
api.Put("/realms/:realmId", auth, editRealm)
api.Delete("/realms/:realmId", auth, deleteRealm)
} }
A.Use("/", cache.New(cache.Config{ A.Use("/", cache.New(cache.Config{

View File

@ -21,11 +21,11 @@ func getUserinfo(c *fiber.Ctx) error {
} }
func getOthersInfo(c *fiber.Ctx) error { func getOthersInfo(c *fiber.Ctx) error {
accountId, _ := c.ParamsInt("accountId", 0) accountId := c.Params("accountId")
var data models.Account var data models.Account
if err := database.C. if err := database.C.
Where(&models.Account{BaseModel: models.BaseModel{ID: uint(accountId)}}). Where(&models.Account{Name: accountId}).
First(&data).Error; err != nil { First(&data).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) return fiber.NewError(fiber.StatusInternalServerError, err.Error())
} }

View File

@ -14,6 +14,7 @@ import (
type PassportUserinfo struct { type PassportUserinfo struct {
Sub string `json:"sub"` Sub string `json:"sub"`
Name string `json:"name"`
Email string `json:"email"` Email string `json:"email"`
Picture string `json:"picture"` Picture string `json:"picture"`
PreferredUsername string `json:"preferred_username"` PreferredUsername string `json:"preferred_username"`
@ -28,7 +29,8 @@ func LinkAccount(userinfo PassportUserinfo) (models.Account, error) {
}).First(&account).Error; err != nil { }).First(&account).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
account = models.Account{ account = models.Account{
Name: userinfo.PreferredUsername, Name: userinfo.Name,
Nick: userinfo.PreferredUsername,
Avatar: userinfo.Picture, Avatar: userinfo.Picture,
EmailAddress: userinfo.Email, EmailAddress: userinfo.Email,
PowerLevel: 0, PowerLevel: 0,
@ -39,7 +41,14 @@ func LinkAccount(userinfo PassportUserinfo) (models.Account, error) {
return account, err return account, err
} }
return account, nil account.Name = userinfo.Name
account.Nick = userinfo.PreferredUsername
account.Avatar = userinfo.Picture
account.EmailAddress = userinfo.Email
err := database.C.Save(&account).Error
return account, err
} }
func GetToken(account models.Account) (string, string, error) { func GetToken(account models.Account) (string, string, error) {

View File

@ -12,11 +12,8 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
func ListPost(tx *gorm.DB, take int, offset int) ([]*models.Post, error) { func PreloadRelatedPost(tx *gorm.DB) *gorm.DB {
var posts []*models.Post return tx.
if err := tx.
Limit(take).
Offset(offset).
Preload("Author"). Preload("Author").
Preload("Attachments"). Preload("Attachments").
Preload("Categories"). Preload("Categories").
@ -30,7 +27,14 @@ func ListPost(tx *gorm.DB, take int, offset int) ([]*models.Post, error) {
Preload("RepostTo.Categories"). Preload("RepostTo.Categories").
Preload("ReplyTo.Categories"). Preload("ReplyTo.Categories").
Preload("RepostTo.Tags"). Preload("RepostTo.Tags").
Preload("ReplyTo.Tags"). Preload("ReplyTo.Tags")
}
func ListPost(tx *gorm.DB, take int, offset int) ([]*models.Post, error) {
var posts []*models.Post
if err := PreloadRelatedPost(tx).
Limit(take).
Offset(offset).
Find(&posts).Error; err != nil { Find(&posts).Error; err != nil {
return posts, err return posts, err
} }

40
pkg/services/realms.go Normal file
View File

@ -0,0 +1,40 @@
package services
import (
"code.smartsheep.studio/hydrogen/interactive/pkg/database"
"code.smartsheep.studio/hydrogen/interactive/pkg/models"
)
func ListRealms(user models.Account) ([]models.Realm, error) {
var realms []models.Realm
if err := database.C.Where(&models.Realm{AccountID: user.ID}).Find(&realms).Error; err != nil {
return realms, err
}
return realms, nil
}
func NewRealm(user models.Account, name, description string) (models.Realm, error) {
realm := models.Realm{
Name: name,
Description: description,
AccountID: user.ID,
}
err := database.C.Save(&realm).Error
return realm, err
}
func EditRealm(realm models.Realm, name, description string) (models.Realm, error) {
realm.Name = name
realm.Description = description
err := database.C.Save(&realm).Error
return realm, err
}
func DeleteRealm(realm models.Realm) error {
return database.C.Delete(&realm).Error
}

View File

@ -35,7 +35,7 @@ export default function PostItem(props: {
return ( return (
<div class="post-item"> <div class="post-item">
<Show when={!props.noAuthor}> <Show when={!props.noAuthor}>
<a href={`/accounts/${props.post.author.id}`}> <a href={`/accounts/${props.post.author.name}`}>
<div class="flex bg-base-200"> <div class="flex bg-base-200">
<div class="avatar pl-[20px]"> <div class="avatar pl-[20px]">
<div class="w-12"> <div class="w-12">
@ -47,7 +47,7 @@ export default function PostItem(props: {
</div> </div>
<div class="flex items-center px-5"> <div class="flex items-center px-5">
<div> <div>
<h3 class="font-bold text-sm">{props.post.author.name}</h3> <h3 class="font-bold text-sm">{props.post.author.nick}</h3>
<p class="text-xs">{props.post.author.description}</p> <p class="text-xs">{props.post.author.description}</p>
</div> </div>
</div> </div>
@ -61,12 +61,12 @@ export default function PostItem(props: {
<div class="mt-2 flex gap-2"> <div class="mt-2 flex gap-2">
<For each={props.post.categories}> <For each={props.post.categories}>
{item => <a href={`/categories/${item.alias}`} class="link link-primary"> {item => <a class="link link-primary">
#{item.name} #{item.name}
</a>} </a>}
</For> </For>
<For each={props.post.tags}> <For each={props.post.tags}>
{item => <a href={`/tags/${item.alias}`} class="link link-primary"> {item => <a class="link link-primary">
#{item.name} #{item.name}
</a>} </a>}
</For> </For>

View File

@ -184,7 +184,7 @@ export default function PostPublish(props: {
<div role="alert" class="bg-base-200 flex justify-between"> <div role="alert" class="bg-base-200 flex justify-between">
<div class="px-5 py-3"> <div class="px-5 py-3">
<i class="fa-solid fa-circle-info me-3"></i> <i class="fa-solid fa-circle-info me-3"></i>
You are reposting a post from <b>{props.reposting?.author?.name}</b> You are reposting a post from <b>{props.reposting?.author?.nick}</b>
</div> </div>
<button type="reset" class="btn btn-ghost w-12" disabled={submitting()}> <button type="reset" class="btn btn-ghost w-12" disabled={submitting()}>
<i class="fa-solid fa-xmark"></i> <i class="fa-solid fa-xmark"></i>
@ -195,7 +195,7 @@ export default function PostPublish(props: {
<div role="alert" class="bg-base-200 flex justify-between"> <div role="alert" class="bg-base-200 flex justify-between">
<div class="px-5 py-3"> <div class="px-5 py-3">
<i class="fa-solid fa-circle-info me-3"></i> <i class="fa-solid fa-circle-info me-3"></i>
You are replying a post from <b>{props.replying?.author?.name}</b> You are replying a post from <b>{props.replying?.author?.nick}</b>
</div> </div>
<button type="reset" class="btn btn-ghost w-12" disabled={submitting()}> <button type="reset" class="btn btn-ghost w-12" disabled={submitting()}>
<i class="fa-solid fa-xmark"></i> <i class="fa-solid fa-xmark"></i>

View File

@ -11,7 +11,8 @@ interface MenuItem {
export default function Navbar() { export default function Navbar() {
const nav: MenuItem[] = [ const nav: MenuItem[] = [
{ label: "Feed", href: "/" } { label: "Feed", href: "/" },
{ label: "Realms", href: "/realms" }
]; ];
const wellKnown = useWellKnown(); const wellKnown = useWellKnown();