Sign avatar URLs in widget WS broadcasts, add CORS for launcher settings

This commit is contained in:
Abhinav Raut
2026-03-29 19:08:09 +05:30
parent 2d64cfcca3
commit fb3ab68ef6
5 changed files with 29 additions and 16 deletions
+2 -1
View File
@@ -77,8 +77,9 @@ type conversationResponseWithBusinessHours struct {
WorkingHoursUTCOffset *int `json:"working_hours_utc_offset,omitempty"`
}
// handleGetChatLauncherSettings returns the live chat launcher settings for the widget
// handleGetChatLauncherSettings returns the live chat launcher settings for the widget.
func handleGetChatLauncherSettings(r *fastglue.Request) error {
r.RequestCtx.Response.Header.Set("Access-Control-Allow-Origin", "*")
_, config, err := validateLiveChatInbox(r)
if err != nil {
return sendErrorEnvelope(r, err)
+11 -9
View File
@@ -65,6 +65,7 @@ import (
_ "github.com/lib/pq"
"github.com/redis/go-redis/v9"
flag "github.com/spf13/pflag"
"github.com/volatiletech/null/v9"
"github.com/zerodha/logf"
)
@@ -662,7 +663,7 @@ func initEmailInbox(inboxRecord imodels.Inbox, msgStore inbox.MessageStore, usrS
}
// initLiveChatInbox initializes the live chat inbox.
func initLiveChatInbox(inboxRecord imodels.Inbox, msgStore inbox.MessageStore, usrStore inbox.UserStore) (inbox.Inbox, error) {
func initLiveChatInbox(inboxRecord imodels.Inbox, msgStore inbox.MessageStore, usrStore inbox.UserStore, signAvatarURL func(*null.String)) (inbox.Inbox, error) {
var config livechat.Config
// Load JSON data into Koanf.
@@ -675,9 +676,10 @@ func initLiveChatInbox(inboxRecord imodels.Inbox, msgStore inbox.MessageStore, u
}
inbox, err := livechat.New(msgStore, usrStore, livechat.Opts{
ID: inboxRecord.ID,
Config: config,
Lo: initLogger("livechat_inbox"),
ID: inboxRecord.ID,
Config: config,
Lo: initLogger("livechat_inbox"),
SignAvatarURL: signAvatarURL,
})
if err != nil {
@@ -690,13 +692,13 @@ func initLiveChatInbox(inboxRecord imodels.Inbox, msgStore inbox.MessageStore, u
}
// makeInboxInitializer creates an inbox initializer function.
func makeInboxInitializer(mgr *inbox.Manager) func(imodels.Inbox, inbox.MessageStore, inbox.UserStore) (inbox.Inbox, error) {
func makeInboxInitializer(mgr *inbox.Manager, signAvatarURL func(*null.String)) func(imodels.Inbox, inbox.MessageStore, inbox.UserStore) (inbox.Inbox, error) {
return func(inboxR imodels.Inbox, msgStore inbox.MessageStore, usrStore inbox.UserStore) (inbox.Inbox, error) {
switch inboxR.Channel {
case inbox.ChannelEmail:
return initEmailInbox(inboxR, msgStore, usrStore, mgr)
case inbox.ChannelLiveChat:
return initLiveChatInbox(inboxR, msgStore, usrStore)
return initLiveChatInbox(inboxR, msgStore, usrStore, signAvatarURL)
default:
return nil, fmt.Errorf("unknown inbox channel: %s", inboxR.Channel)
}
@@ -706,15 +708,15 @@ func makeInboxInitializer(mgr *inbox.Manager) func(imodels.Inbox, inbox.MessageS
// reloadInboxes reloads all inboxes.
func reloadInboxes(app *App) error {
app.lo.Info("reloading inboxes")
return app.inbox.Reload(ctx, makeInboxInitializer(app.inbox))
return app.inbox.Reload(ctx, makeInboxInitializer(app.inbox, app.conversation.SignAvatarURL))
}
// startInboxes registers the active inboxes and starts receiver for each.
func startInboxes(ctx context.Context, mgr *inbox.Manager, msgStore inbox.MessageStore, usrStore inbox.UserStore) {
func startInboxes(ctx context.Context, mgr *inbox.Manager, msgStore inbox.MessageStore, usrStore inbox.UserStore, signAvatarURL func(*null.String)) {
mgr.SetMessageStore(msgStore)
mgr.SetUserStore(usrStore)
if err := mgr.InitInboxes(makeInboxInitializer(mgr)); err != nil {
if err := mgr.InitInboxes(makeInboxInitializer(mgr, signAvatarURL)); err != nil {
log.Fatalf("error initializing inboxes: %v", err)
}
+1 -1
View File
@@ -229,7 +229,7 @@ func main() {
automation.SetConversationStore(conversation)
// Start inboxes.
startInboxes(ctx, inbox, conversation, user)
startInboxes(ctx, inbox, conversation, user, conversation.SignAvatarURL)
go automation.Run(ctx, automationWorkers)
go autoassigner.Run(ctx, autoAssignInterval)
+1
View File
@@ -1212,6 +1212,7 @@ func (m *Manager) broadcastMessageToWidgetClients(message *models.Message) {
return
}
m.SignAvatarURL(&message.Author.AvatarURL)
liveChatInbox.BroadcastMessageToClients(message.ConversationUUID, conversation.ContactID, models.ChatMessage{
UUID: message.UUID,
Status: message.Status,
+14 -5
View File
@@ -10,6 +10,7 @@ import (
"github.com/abhinavxd/libredesk/internal/conversation/models"
"github.com/abhinavxd/libredesk/internal/inbox"
"github.com/volatiletech/null/v9"
"github.com/zerodha/logf"
)
@@ -108,16 +109,18 @@ type LiveChat struct {
lo *logf.Logger
messageStore inbox.MessageStore
userStore inbox.UserStore
signAvatarURL func(*null.String) // Signs a raw /uploads/ avatar path into a signed URL.
clients map[string][]*Client // Maps user IDs to slices of clients (to handle multiple devices)
clientsMutex sync.RWMutex
}
// Opts holds the options required for the live chat inbox.
type Opts struct {
ID int
Config Config
From string
Lo *logf.Logger
ID int
Config Config
From string
Lo *logf.Logger
SignAvatarURL func(*null.String)
}
// New returns a new instance of the live chat inbox.
@@ -129,6 +132,7 @@ func New(store inbox.MessageStore, userStore inbox.UserStore, opts Opts) (*LiveC
lo: opts.Lo,
messageStore: store,
userStore: userStore,
signAvatarURL: opts.SignAvatarURL,
clients: make(map[string][]*Client),
}
return lc, nil
@@ -170,6 +174,11 @@ func (lc *LiveChat) Send(message models.OutboundMessage) error {
message.Attachments[i].Content = nil
}
avatarURL := sender.AvatarURL
if lc.signAvatarURL != nil {
lc.signAvatarURL(&avatarURL)
}
messageData := map[string]any{
"type": "new_message",
"data": models.ChatMessage{
@@ -183,7 +192,7 @@ func (lc *LiveChat) Send(message models.OutboundMessage) error {
ID: message.SenderID,
FirstName: sender.FirstName,
LastName: sender.LastName,
AvatarURL: sender.AvatarURL,
AvatarURL: avatarURL,
AvailabilityStatus: sender.AvailabilityStatus,
Type: sender.Type,
LastActiveAt: sender.LastActiveAt,