mirror of
				https://github.com/abhinavxd/libredesk.git
				synced 2025-11-04 05:53:30 +00:00 
			
		
		
		
	- introduce new permission `messages:write_as_contact` that needs to be set to allow this.
		
			
				
	
	
		
			218 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package main
 | 
						|
 | 
						|
import (
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	amodels "github.com/abhinavxd/libredesk/internal/auth/models"
 | 
						|
	authzModels "github.com/abhinavxd/libredesk/internal/authz/models"
 | 
						|
	cmodels "github.com/abhinavxd/libredesk/internal/conversation/models"
 | 
						|
	"github.com/abhinavxd/libredesk/internal/envelope"
 | 
						|
	medModels "github.com/abhinavxd/libredesk/internal/media/models"
 | 
						|
	umodels "github.com/abhinavxd/libredesk/internal/user/models"
 | 
						|
	"github.com/valyala/fasthttp"
 | 
						|
	"github.com/zerodha/fastglue"
 | 
						|
)
 | 
						|
 | 
						|
type messageReq struct {
 | 
						|
	Attachments []int    `json:"attachments"`
 | 
						|
	Message     string   `json:"message"`
 | 
						|
	Private     bool     `json:"private"`
 | 
						|
	To          []string `json:"to"`
 | 
						|
	CC          []string `json:"cc"`
 | 
						|
	BCC         []string `json:"bcc"`
 | 
						|
	SenderType  string   `json:"sender_type"`
 | 
						|
}
 | 
						|
 | 
						|
// handleGetMessages returns messages for a conversation.
 | 
						|
func handleGetMessages(r *fastglue.Request) error {
 | 
						|
	var (
 | 
						|
		app         = r.Context.(*App)
 | 
						|
		uuid        = r.RequestCtx.UserValue("uuid").(string)
 | 
						|
		auser       = r.RequestCtx.UserValue("user").(amodels.User)
 | 
						|
		page, _     = strconv.Atoi(string(r.RequestCtx.QueryArgs().Peek("page")))
 | 
						|
		pageSize, _ = strconv.Atoi(string(r.RequestCtx.QueryArgs().Peek("page_size")))
 | 
						|
		total       = 0
 | 
						|
	)
 | 
						|
 | 
						|
	user, err := app.user.GetAgent(auser.ID, "")
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Check permission
 | 
						|
	_, err = enforceConversationAccess(app, uuid, user)
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	messages, pageSize, err := app.conversation.GetConversationMessages(uuid, page, pageSize)
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	for i := range messages {
 | 
						|
		total = messages[i].Total
 | 
						|
		// Populate attachment URLs
 | 
						|
		for j := range messages[i].Attachments {
 | 
						|
			messages[i].Attachments[j].URL = app.media.GetURL(messages[i].Attachments[j].UUID)
 | 
						|
		}
 | 
						|
		// Redact CSAT survey link
 | 
						|
		messages[i].CensorCSATContent()
 | 
						|
	}
 | 
						|
 | 
						|
	return r.SendEnvelope(envelope.PageResults{
 | 
						|
		Total:      total,
 | 
						|
		Results:    messages,
 | 
						|
		Page:       page,
 | 
						|
		PerPage:    pageSize,
 | 
						|
		TotalPages: (total + pageSize - 1) / pageSize,
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
// handleGetMessage fetches a single from DB using the uuid.
 | 
						|
func handleGetMessage(r *fastglue.Request) error {
 | 
						|
	var (
 | 
						|
		app   = r.Context.(*App)
 | 
						|
		uuid  = r.RequestCtx.UserValue("uuid").(string)
 | 
						|
		cuuid = r.RequestCtx.UserValue("cuuid").(string)
 | 
						|
		auser = r.RequestCtx.UserValue("user").(amodels.User)
 | 
						|
	)
 | 
						|
	user, err := app.user.GetAgent(auser.ID, "")
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Check permission
 | 
						|
	_, err = enforceConversationAccess(app, cuuid, user)
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	message, err := app.conversation.GetMessage(uuid)
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Redact CSAT survey link
 | 
						|
	message.CensorCSATContent()
 | 
						|
 | 
						|
	for j := range message.Attachments {
 | 
						|
		message.Attachments[j].URL = app.media.GetURL(message.Attachments[j].UUID)
 | 
						|
	}
 | 
						|
 | 
						|
	return r.SendEnvelope(message)
 | 
						|
}
 | 
						|
 | 
						|
// handleRetryMessage changes message status to `pending`, so it's enqueued for sending.
 | 
						|
func handleRetryMessage(r *fastglue.Request) error {
 | 
						|
	var (
 | 
						|
		app   = r.Context.(*App)
 | 
						|
		uuid  = r.RequestCtx.UserValue("uuid").(string)
 | 
						|
		cuuid = r.RequestCtx.UserValue("cuuid").(string)
 | 
						|
		auser = r.RequestCtx.UserValue("user").(amodels.User)
 | 
						|
	)
 | 
						|
 | 
						|
	user, err := app.user.GetAgent(auser.ID, "")
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Check permission
 | 
						|
	_, err = enforceConversationAccess(app, cuuid, user)
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	if err = app.conversation.MarkMessageAsPending(uuid); err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
	return r.SendEnvelope(true)
 | 
						|
}
 | 
						|
 | 
						|
// handleSendMessage sends a message in a conversation.
 | 
						|
func handleSendMessage(r *fastglue.Request) error {
 | 
						|
	var (
 | 
						|
		app   = r.Context.(*App)
 | 
						|
		auser = r.RequestCtx.UserValue("user").(amodels.User)
 | 
						|
		cuuid = r.RequestCtx.UserValue("cuuid").(string)
 | 
						|
		req   = messageReq{}
 | 
						|
	)
 | 
						|
 | 
						|
	user, err := app.user.GetAgent(auser.ID, "")
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	// Check access to conversation.
 | 
						|
	conv, err := enforceConversationAccess(app, cuuid, user)
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := r.Decode(&req, "json"); err != nil {
 | 
						|
		app.lo.Error("error unmarshalling message request", "error", err)
 | 
						|
		return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.Ts("globals.messages.errorParsing", "name", "{globals.terms.request}"), nil, envelope.InputError)
 | 
						|
	}
 | 
						|
 | 
						|
	if req.SenderType != umodels.UserTypeAgent && req.SenderType != umodels.UserTypeContact {
 | 
						|
		return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.invalid", "name", "`sender_type`"), nil, envelope.InputError)
 | 
						|
	}
 | 
						|
 | 
						|
	// Contacts cannot send private messages
 | 
						|
	if req.SenderType == umodels.UserTypeContact && req.Private {
 | 
						|
		return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.T("globals.messages.badRequest"), nil, envelope.InputError)
 | 
						|
	}
 | 
						|
 | 
						|
	// Check if user has permission to send messages as contact
 | 
						|
	if req.SenderType == umodels.UserTypeContact {
 | 
						|
		parts := strings.Split(authzModels.PermMessagesWriteAsContact, ":")
 | 
						|
		if len(parts) != 2 {
 | 
						|
			return sendErrorEnvelope(r, envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.errorChecking", "name", "{globals.terms.permission}"), nil))
 | 
						|
		}
 | 
						|
		ok, err := app.authz.Enforce(user, parts[0], parts[1])
 | 
						|
		if err != nil {
 | 
						|
			return sendErrorEnvelope(r, envelope.NewError(envelope.InputError, app.i18n.Ts("globals.messages.errorChecking", "name", "{globals.terms.permission}"), nil))
 | 
						|
		}
 | 
						|
		if !ok {
 | 
						|
			return r.SendErrorEnvelope(fasthttp.StatusForbidden, app.i18n.Ts("globals.messages.denied", "name", "{globals.terms.permission}"), nil, envelope.PermissionError)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	// Get media for all attachments.
 | 
						|
	var media = make([]medModels.Media, 0, len(req.Attachments))
 | 
						|
	for _, id := range req.Attachments {
 | 
						|
		m, err := app.media.Get(id, "")
 | 
						|
		if err != nil {
 | 
						|
			app.lo.Error("error fetching media", "error", err)
 | 
						|
			return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.media}"), nil, envelope.GeneralError)
 | 
						|
		}
 | 
						|
		media = append(media, m)
 | 
						|
	}
 | 
						|
 | 
						|
	// Create contact message.
 | 
						|
	if req.SenderType == umodels.UserTypeContact {
 | 
						|
		message, err := app.conversation.CreateContactMessage(media, int(conv.ContactID), cuuid, req.Message, cmodels.ContentTypeHTML)
 | 
						|
		if err != nil {
 | 
						|
			return sendErrorEnvelope(r, err)
 | 
						|
		}
 | 
						|
		return r.SendEnvelope(message)
 | 
						|
	}
 | 
						|
 | 
						|
	// Send private note.
 | 
						|
	if req.Private {
 | 
						|
		message, err := app.conversation.SendPrivateNote(media, user.ID, cuuid, req.Message)
 | 
						|
		if err != nil {
 | 
						|
			return sendErrorEnvelope(r, err)
 | 
						|
		}
 | 
						|
		return r.SendEnvelope(message)
 | 
						|
	}
 | 
						|
 | 
						|
	// Queue reply.
 | 
						|
	message, err := app.conversation.QueueReply(media, conv.InboxID, user.ID, cuuid, req.Message, req.To, req.CC, req.BCC, map[string]any{} /**meta**/)
 | 
						|
	if err != nil {
 | 
						|
		return sendErrorEnvelope(r, err)
 | 
						|
	}
 | 
						|
	return r.SendEnvelope(message)
 | 
						|
}
 |