From 8e316753170a9bc59f06f9c331c6de1ecd70d036 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sat, 30 Mar 2024 18:21:17 +0800 Subject: [PATCH] :sparkles: Channel history --- pkg/models/attachments.go | 4 +-- pkg/models/messages.go | 11 ++++---- pkg/server/channels_api.go | 4 +-- pkg/server/messages_api.go | 52 +++++++++++++++++++++++++++++++++++++ pkg/server/startup.go | 4 ++- pkg/services/channels.go | 45 ++++++++++++++++++++++++++++++-- pkg/services/connections.go | 12 +++++++-- pkg/services/messages.go | 39 +++++++++++++++++++++++----- 8 files changed, 150 insertions(+), 21 deletions(-) create mode 100644 pkg/server/messages_api.go diff --git a/pkg/models/attachments.go b/pkg/models/attachments.go index e2e3e67..45ea6e9 100644 --- a/pkg/models/attachments.go +++ b/pkg/models/attachments.go @@ -27,9 +27,7 @@ type Attachment struct { Type AttachmentType `json:"type"` ExternalUrl string `json:"external_url"` Author Account `json:"author"` - ArticleID *uint `json:"article_id"` - MomentID *uint `json:"moment_id"` - CommentID *uint `json:"comment_id"` + MessageID *uint `json:"message_id"` AuthorID uint `json:"author_id"` } diff --git a/pkg/models/messages.go b/pkg/models/messages.go index d1d462d..ca267c6 100644 --- a/pkg/models/messages.go +++ b/pkg/models/messages.go @@ -13,9 +13,10 @@ const ( type Message struct { BaseModel - Content string `json:"content"` - Metadata datatypes.JSONMap `json:"metadata"` - Type MessageType `json:"type"` - ChannelID uint `json:"channel_id"` - SenderID uint `json:"sender_id"` + Content string `json:"content"` + Metadata datatypes.JSONMap `json:"metadata"` + Type MessageType `json:"type"` + Attachments []Attachment `json:"attachments"` + ChannelID uint `json:"channel_id"` + SenderID uint `json:"sender_id"` } diff --git a/pkg/server/channels_api.go b/pkg/server/channels_api.go index 8622b38..30adfa9 100644 --- a/pkg/server/channels_api.go +++ b/pkg/server/channels_api.go @@ -8,11 +8,11 @@ import ( ) func getChannel(c *fiber.Ctx) error { - id, _ := c.ParamsInt("channelId", 0) + alias := c.Params("channel") var channel models.Channel if err := database.C.Where(&models.Channel{ - BaseModel: models.BaseModel{ID: uint(id)}, + Alias: alias, }).First(&channel).Error; err != nil { return fiber.NewError(fiber.StatusNotFound, err.Error()) } diff --git a/pkg/server/messages_api.go b/pkg/server/messages_api.go new file mode 100644 index 0000000..1e4a705 --- /dev/null +++ b/pkg/server/messages_api.go @@ -0,0 +1,52 @@ +package server + +import ( + "git.solsynth.dev/hydrogen/messaging/pkg/models" + "git.solsynth.dev/hydrogen/messaging/pkg/services" + "github.com/gofiber/fiber/v2" +) + +func getMessageHistory(c *fiber.Ctx) error { + take := c.QueryInt("take", 0) + offset := c.QueryInt("offset", 0) + alias := c.Params("channel") + + channel, err := services.GetChannelWithAlias(alias) + if err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + count := services.CountMessage(channel) + messages, err := services.ListMessage(channel, take, offset) + if err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + return c.JSON(fiber.Map{ + "count": count, + "data": messages, + }) +} + +func newTextMessage(c *fiber.Ctx) error { + user := c.Locals("principal").(models.Account) + alias := c.Params("channel") + + var data struct { + Content string `json:"content" validate:"required"` + Attachments []models.Attachment `json:"attachments"` + } + + if err := BindAndValidate(c, &data); err != nil { + return err + } + + var message models.Message + if channel, member, err := services.GetAvailableChannelWithAlias(alias, user); err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } else if message, err = services.NewTextMessage(data.Content, member, channel, data.Attachments...); err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + return c.JSON(message) +} diff --git a/pkg/server/startup.go b/pkg/server/startup.go index 3a69ed6..ed2450d 100644 --- a/pkg/server/startup.go +++ b/pkg/server/startup.go @@ -79,9 +79,11 @@ func NewServer() { channels.Get("/", listChannel) channels.Get("/me", authMiddleware, listOwnedChannel) channels.Get("/me/available", authMiddleware, listAvailableChannel) - channels.Get("/:channelId", getChannel) + channels.Get("/:channel", getChannel) + channels.Get("/:channel/messages", getMessageHistory) channels.Get("/:channelId/members", listChannelMembers) channels.Post("/", authMiddleware, createChannel) + channels.Post("/:channel/messages", authMiddleware, newTextMessage) channels.Post("/:channelId/invite", authMiddleware, inviteChannel) channels.Post("/:channelId/kick", authMiddleware, kickChannel) channels.Put("/:channelId", authMiddleware, editChannel) diff --git a/pkg/services/channels.go b/pkg/services/channels.go index 3e347fb..3a83014 100644 --- a/pkg/services/channels.go +++ b/pkg/services/channels.go @@ -7,10 +7,51 @@ import ( "github.com/samber/lo" ) -func GetAvailableChannel(id uint, user models.Account) (models.Channel, models.ChannelMember, error) { +func GetChannel(id uint) (models.Channel, error) { + var channel models.Channel + if err := database.C.Where(models.Channel{ + BaseModel: models.BaseModel{ID: id}, + }).First(&channel).Error; err != nil { + return channel, err + } + + return channel, nil +} + +func GetChannelWithAlias(alias string) (models.Channel, error) { + var channel models.Channel + if err := database.C.Where(models.Channel{ + Alias: alias, + }).First(&channel).Error; err != nil { + return channel, err + } + + return channel, nil +} + +func GetAvailableChannelWithAlias(alias string, user models.Account) (models.Channel, models.ChannelMember, error) { + var err error var member models.ChannelMember var channel models.Channel - if err := database.C.Where("id = ?", id).First(&channel).Error; err != nil { + if channel, err = GetChannelWithAlias(alias); err != nil { + return channel, member, err + } + + if err := database.C.Where(models.ChannelMember{ + AccountID: user.ID, + ChannelID: channel.ID, + }).First(&member).Error; err != nil { + return channel, member, fmt.Errorf("channel principal not found: %v", err.Error()) + } + + return channel, member, nil +} + +func GetAvailableChannel(id uint, user models.Account) (models.Channel, models.ChannelMember, error) { + var err error + var member models.ChannelMember + var channel models.Channel + if channel, err = GetChannel(id); err != nil { return channel, member, err } diff --git a/pkg/services/connections.go b/pkg/services/connections.go index 146982a..e475ab9 100644 --- a/pkg/services/connections.go +++ b/pkg/services/connections.go @@ -22,10 +22,18 @@ func DealCommand(task models.UnifiedCommand, user models.Account) *models.Unifie switch task.Action { case "messages.send.text": var req struct { - ChannelID uint `json:"channel_id"` - Content string `json:"content"` + ChannelID uint `json:"channel_id"` + Content string `json:"content"` + Attachments []models.Attachment `json:"attachments"` } + models.FitStruct(task.Payload, &req) + if len(req.Content) == 0 { + return &models.UnifiedCommand{ + Action: "error", + Message: "content cannot be empty", + } + } if channel, member, err := GetAvailableChannel(req.ChannelID, user); err != nil { return lo.ToPtr(models.UnifiedCommandFromError(err)) diff --git a/pkg/services/messages.go b/pkg/services/messages.go index f13f882..df49a1c 100644 --- a/pkg/services/messages.go +++ b/pkg/services/messages.go @@ -5,13 +5,40 @@ import ( "git.solsynth.dev/hydrogen/messaging/pkg/models" ) -func NewTextMessage(content string, sender models.ChannelMember, channel models.Channel) (models.Message, error) { - message := models.Message{ - Content: content, - Metadata: nil, +func CountMessage(channel models.Channel) int64 { + var count int64 + if err := database.C.Where(models.Message{ ChannelID: channel.ID, - SenderID: sender.ID, - Type: models.MessageTypeText, + }).Model(&models.Message{}).Count(&count).Error; err != nil { + return 0 + } else { + return count + } +} + +func ListMessage(channel models.Channel, take int, offset int) ([]models.Message, error) { + if take > 100 { + take = 100 + } + + var messages []models.Message + if err := database.C.Where(models.Message{ + ChannelID: channel.ID, + }).Limit(take).Offset(offset).Find(&messages).Error; err != nil { + return messages, err + } else { + return messages, nil + } +} + +func NewTextMessage(content string, sender models.ChannelMember, channel models.Channel, attachments ...models.Attachment) (models.Message, error) { + message := models.Message{ + Content: content, + Metadata: nil, + ChannelID: channel.ID, + SenderID: sender.ID, + Attachments: attachments, + Type: models.MessageTypeText, } var members []models.ChannelMember