Files
relay-server/database/messages.go
2025-02-11 16:34:59 +01:00

185 lines
5.7 KiB
Go

package database
import (
"database/sql"
"fmt"
"github.com/google/uuid"
"relay-server/model"
"relay-server/utils"
)
func GetMessages(db *sql.DB, userID uuid.UUID, conversationID uuid.UUID, limit int, cursor int) ([]*model.Message, error) {
_, err := checkMembership(db, userID, conversationID)
if err != nil {
return []*model.Message{}, err
}
var query string
var messages []*model.Message
if cursor != 0 {
query = `
SELECT
m.message_id,
m.content AS message,
m.sent_at,
m.attachment_urls,
a.username AS sender
FROM Messages m
JOIN Accounts a ON m.user_id = a.user_id
WHERE m.conversation_id = $1
AND m.message_id < $2
ORDER BY m.message_id DESC
LIMIT $3;
`
rows, err := db.Query(query, conversationID, cursor, limit)
if err != nil {
return []*model.Message{}, utils.NewError(utils.ErrInternal, "Failed to get messages", fmt.Errorf("failed to get messages: %w", err))
}
defer rows.Close()
for rows.Next() {
message := &model.Message{}
err = rows.Scan(&message.ID, &message.Message, &message.SentAt, &message.AttachmentUrl, &message.Sender)
if err != nil {
return []*model.Message{}, utils.NewError(utils.ErrInternal, "Failed to get messages", fmt.Errorf("failed to scan message: %w", err))
}
messages = append(messages, message)
}
if err = rows.Err(); err != nil {
return []*model.Message{}, utils.NewError(utils.ErrInternal, "internal server error", fmt.Errorf("failed to process messages: %w", err))
}
} else {
query = `
SELECT
m.message_id,
m.content AS message,
m.sent_at,
m.attachment_urls,
a.username AS sender
FROM Messages m
JOIN Accounts a ON m.user_id = a.user_id
WHERE m.conversation_id = $1
ORDER BY m.message_id DESC
LIMIT $2;
`
rows, err := db.Query(query, conversationID, cursor, limit)
if err != nil {
return []*model.Message{}, utils.NewError(utils.ErrInternal, "Failed to get messages", fmt.Errorf("failed to get messages: %w", err))
}
defer rows.Close()
for rows.Next() {
message := &model.Message{}
err = rows.Scan(&message.ID, &message.Message, &message.SentAt, &message.AttachmentUrl, &message.Sender)
if err != nil {
return []*model.Message{}, utils.NewError(utils.ErrInternal, "Failed to get messages", fmt.Errorf("failed to scan message: %w", err))
}
messages = append(messages, message)
}
if err = rows.Err(); err != nil {
return []*model.Message{}, utils.NewError(utils.ErrInternal, "internal server error", fmt.Errorf("failed to process messages: %w", err))
}
}
if cursor != 0 {
// Reverse first messages
for i, j := 0, len(messages)-1; i < j; i, j = i+1, j-1 {
messages[i], messages[j] = messages[j], messages[i]
}
}
return messages, nil
}
func checkMembership(db *sql.DB, userID uuid.UUID, conversationID uuid.UUID) (bool, error) {
query := `
SELECT EXISTS (
SELECT 1
FROM Memberships
WHERE user_id = $1
AND conversation_id = $2
) AS is_member;
`
var isMember bool
err := db.QueryRow(query, userID, conversationID).Scan(&isMember)
if err != nil {
return false, utils.NewError(utils.ErrInternal, "internal server error", fmt.Errorf("failed to check membership: %w", err))
}
if !isMember {
return false, utils.NewError(utils.ErrForbidden, "You are member of the conversation", nil)
}
return isMember, nil
}
func DeleteMessage(db *sql.DB, userID uuid.UUID, conversationID uuid.UUID, messageID int) error {
checkMessageOwnershipQuery := `
SELECT user_id FROM Messages WHERE message_id = $1;
`
deleteMessageQuery := `
DELETE FROM Messages WHERE message_id = $1;
`
var messageOwnerID uuid.UUID
err := db.QueryRow(checkMessageOwnershipQuery, messageID).Scan(&messageOwnerID)
if err != nil {
return utils.NewError(utils.ErrInternal, "Failed to delete message", fmt.Errorf("failed to check message ownership: %w", err))
}
var isSelfMessage bool
if messageOwnerID == userID {
isSelfMessage = true
}
isAdmin, err := IsAdmin(db, userID, conversationID)
if err != nil {
return err
}
if !isSelfMessage && !isAdmin {
return utils.NewError(utils.ErrForbidden, "You don't have permissions to delete that message ", nil)
}
row, err := db.Exec(deleteMessageQuery, messageID)
if err != nil {
return utils.NewError(utils.ErrInternal, "Failed to delete message", fmt.Errorf("failed to delete message: %w", err))
}
rowsAffected, err := row.RowsAffected()
if err != nil {
return utils.NewError(utils.ErrInternal, "Failed to delete message", fmt.Errorf("failed to get rows affected: %w", err))
}
if rowsAffected == 0 {
return utils.NewError(utils.ErrNotFound, "Message not found", nil)
}
return nil
}
func InsertMessage(db *sql.DB, senderID uuid.UUID, conversationID uuid.UUID, message string, attachmentUrls []string) (*model.Message, error) {
isMember, err := checkMembership(db, senderID, conversationID)
if err != nil {
return &model.Message{}, fmt.Errorf("failed to check membership: %w", err)
}
if !isMember {
return &model.Message{}, fmt.Errorf("user is not a member of the conversation")
}
query := `
INSERT INTO Messages (conversation_id, user_id, content, attachment_urls)
VALUES ($1, $2, $3, $4)
RETURNING message_id, content AS message, sent_at, attachment_urls, user_id AS sender_id, conversation_id;
`
var msg model.Message
err = db.QueryRow(query, conversationID, senderID, message, attachmentUrls).Scan(
&msg.ID,
&msg.Message,
&msg.SentAt,
&msg.AttachmentUrl,
&msg.SenderID,
&msg.ConversationID,
)
if err != nil {
return &model.Message{}, fmt.Errorf("failed to insert message: %w", err)
}
return &msg, nil
}