✨ Realms
This commit is contained in:
parent
28b4f11ccf
commit
0c8afb2bae
@ -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
126
pkg/server/realms_api.go
Normal 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)
|
||||||
|
}
|
@ -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{
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -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
40
pkg/services/realms.go
Normal 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
|
||||||
|
}
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user