diff --git a/pkg/models/messages.go b/pkg/models/messages.go index ca267c6..ab1fe0d 100644 --- a/pkg/models/messages.go +++ b/pkg/models/messages.go @@ -17,6 +17,8 @@ type Message struct { Metadata datatypes.JSONMap `json:"metadata"` Type MessageType `json:"type"` Attachments []Attachment `json:"attachments"` + Channel Channel `json:"channel"` + Sender Account `json:"sender"` ChannelID uint `json:"channel_id"` SenderID uint `json:"sender_id"` } diff --git a/pkg/server/messages_api.go b/pkg/server/messages_api.go index 1e4a705..6180cf9 100644 --- a/pkg/server/messages_api.go +++ b/pkg/server/messages_api.go @@ -6,7 +6,7 @@ import ( "github.com/gofiber/fiber/v2" ) -func getMessageHistory(c *fiber.Ctx) error { +func listMessage(c *fiber.Ctx) error { take := c.QueryInt("take", 0) offset := c.QueryInt("offset", 0) alias := c.Params("channel") @@ -50,3 +50,55 @@ func newTextMessage(c *fiber.Ctx) error { return c.JSON(message) } + +func editMessage(c *fiber.Ctx) error { + user := c.Locals("principal").(models.Account) + alias := c.Params("channel") + messageId, _ := c.ParamsInt("messageId", 0) + + 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.GetMessageWithPrincipal(channel, member, uint(messageId)); err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + message.Content = data.Content + message.Attachments = data.Attachments + + message, err := services.EditMessage(message) + if err != nil { + return fiber.NewError(fiber.StatusBadRequest, err.Error()) + } + + return c.JSON(message) +} + +func deleteMessage(c *fiber.Ctx) error { + user := c.Locals("principal").(models.Account) + alias := c.Params("channel") + messageId, _ := c.ParamsInt("messageId", 0) + + 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.GetMessageWithPrincipal(channel, member, uint(messageId)); err != nil { + return fiber.NewError(fiber.StatusNotFound, err.Error()) + } + + message, err := services.DeleteMessage(message) + if 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 ed2450d..4b92bc5 100644 --- a/pkg/server/startup.go +++ b/pkg/server/startup.go @@ -77,17 +77,22 @@ func NewServer() { channels := api.Group("/channels").Name("Channels API") { channels.Get("/", listChannel) + channels.Get("/:channel", getChannel) channels.Get("/me", authMiddleware, listOwnedChannel) channels.Get("/me/available", authMiddleware, listAvailableChannel) - 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) channels.Delete("/:channelId", authMiddleware, deleteChannel) + + channels.Get("/:channelId/members", listChannelMembers) + channels.Post("/:channelId/invite", authMiddleware, inviteChannel) + channels.Post("/:channelId/kick", authMiddleware, kickChannel) + + channels.Get("/:channel/messages", listMessage) + channels.Post("/:channel/messages", authMiddleware, newTextMessage) + channels.Put("/:channel/messages/:messageId", authMiddleware, editMessage) + channels.Delete("/:channel/messages/:messageId", authMiddleware, deleteMessage) } api.Get("/unified", authMiddleware, websocket.New(unifiedGateway)) diff --git a/pkg/services/connections.go b/pkg/services/connections.go index e475ab9..53117a1 100644 --- a/pkg/services/connections.go +++ b/pkg/services/connections.go @@ -3,7 +3,6 @@ package services import ( "git.solsynth.dev/hydrogen/messaging/pkg/models" "github.com/gofiber/contrib/websocket" - "github.com/samber/lo" ) var WsConn = make(map[uint][]*websocket.Conn) @@ -20,28 +19,6 @@ func PushCommand(userId uint, task models.UnifiedCommand) { func DealCommand(task models.UnifiedCommand, user models.Account) *models.UnifiedCommand { switch task.Action { - case "messages.send.text": - var req struct { - 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)) - } else if _, err = NewTextMessage(req.Content, member, channel); err != nil { - return lo.ToPtr(models.UnifiedCommandFromError(err)) - } else { - return nil - } default: return &models.UnifiedCommand{ Action: "error", diff --git a/pkg/services/messages.go b/pkg/services/messages.go index df49a1c..3ce559e 100644 --- a/pkg/services/messages.go +++ b/pkg/services/messages.go @@ -22,15 +22,44 @@ func ListMessage(channel models.Channel, take int, offset int) ([]models.Message } var messages []models.Message - if err := database.C.Where(models.Message{ - ChannelID: channel.ID, - }).Limit(take).Offset(offset).Find(&messages).Error; err != nil { + if err := database.C. + Where(models.Message{ + ChannelID: channel.ID, + }).Limit(take).Offset(offset). + Order("created_at DESC"). + Preload("Sender"). + Find(&messages).Error; err != nil { return messages, err } else { return messages, nil } } +func GetMessage(channel models.Channel, id uint) (models.Message, error) { + var message models.Message + if err := database.C.Where(models.Message{ + BaseModel: models.BaseModel{ID: id}, + ChannelID: channel.ID, + }).First(&message).Error; err != nil { + return message, err + } else { + return message, nil + } +} + +func GetMessageWithPrincipal(channel models.Channel, member models.ChannelMember, id uint) (models.Message, error) { + var message models.Message + if err := database.C.Where(models.Message{ + BaseModel: models.BaseModel{ID: id}, + ChannelID: channel.ID, + SenderID: member.ID, + }).First(&message).Error; err != nil { + return message, err + } else { + return message, nil + } +} + func NewTextMessage(content string, sender models.ChannelMember, channel models.Channel, attachments ...models.Attachment) (models.Message, error) { message := models.Message{ Content: content, @@ -57,3 +86,39 @@ func NewTextMessage(content string, sender models.ChannelMember, channel models. return message, nil } + +func EditMessage(message models.Message) (models.Message, error) { + var members []models.ChannelMember + if err := database.C.Save(&message).Error; err != nil { + return message, err + } else if err = database.C.Where(models.ChannelMember{ + ChannelID: message.ChannelID, + }).Find(&members).Error; err == nil { + for _, member := range members { + PushCommand(member.ID, models.UnifiedCommand{ + Action: "messages.update", + Payload: message, + }) + } + } + + return message, nil +} + +func DeleteMessage(message models.Message) (models.Message, error) { + var members []models.ChannelMember + if err := database.C.Delete(&message).Error; err != nil { + return message, err + } else if err = database.C.Where(models.ChannelMember{ + ChannelID: message.ChannelID, + }).Find(&members).Error; err == nil { + for _, member := range members { + PushCommand(member.ID, models.UnifiedCommand{ + Action: "messages.burnt", + Payload: message, + }) + } + } + + return message, nil +}