mirror of
https://github.com/abhinavxd/libredesk.git
synced 2026-09-11 13:28:57 +00:00
Merge pull request #450 from abhinavxd/fix/s3-thumbnail-preview
Fix live chat image thumbnails for S3 store.
This commit is contained in:
@@ -688,9 +688,6 @@ func sendChatMessageResponse(app *App, r *fastglue.Request, messageUUID string)
|
||||
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, app.i18n.T("globals.messages.somethingWentWrong"), nil, envelope.GeneralError)
|
||||
}
|
||||
|
||||
for i := range message.Attachments {
|
||||
message.Attachments[i].URL = app.media.GetSignedURL(message.Attachments[i].UUID)
|
||||
}
|
||||
app.conversation.SignAvatarURL(&message.Author.AvatarURL)
|
||||
|
||||
// Strip agent email from widget responses.
|
||||
|
||||
+2
-9
@@ -66,11 +66,7 @@ func handleGetMessages(r *fastglue.Request) error {
|
||||
rootURL, _ := app.setting.GetAppRootURL()
|
||||
for i := range messages {
|
||||
total = messages[i].Total
|
||||
// Populate attachment URLs
|
||||
for j := range messages[i].Attachments {
|
||||
att := messages[i].Attachments[j]
|
||||
messages[i].Attachments[j].URL = app.media.GetURL(att.UUID, att.ContentType, att.Name)
|
||||
}
|
||||
app.conversation.SignAttachmentURLs(messages[i].Attachments)
|
||||
resolveQuotedCIDs(app, &messages[i])
|
||||
resolveAttachmentCIDs(&messages[i], rootURL)
|
||||
}
|
||||
@@ -135,10 +131,7 @@ func handleGetMessage(r *fastglue.Request) error {
|
||||
}
|
||||
|
||||
rootURL, _ := app.setting.GetAppRootURL()
|
||||
for j := range message.Attachments {
|
||||
att := message.Attachments[j]
|
||||
message.Attachments[j].URL = app.media.GetURL(att.UUID, att.ContentType, att.Name)
|
||||
}
|
||||
app.conversation.SignAttachmentURLs(message.Attachments)
|
||||
resolveQuotedCIDs(app, &message)
|
||||
resolveAttachmentCIDs(&message, rootURL)
|
||||
|
||||
|
||||
+8
-1
@@ -13,9 +13,10 @@
|
||||
>
|
||||
<template v-if="isImage">
|
||||
<img
|
||||
:src="getThumbFilepath(attachment.url)"
|
||||
:src="attachment.thumbnail_url || getThumbFilepath(attachment.url)"
|
||||
:alt="attachment.name"
|
||||
class="w-full h-full object-cover"
|
||||
@error="fallbackToOriginal($event, attachment.url)"
|
||||
/>
|
||||
<div
|
||||
class="absolute inset-x-0 top-0 flex items-start justify-between gap-2 px-2 pt-1.5 pb-5 bg-gradient-to-b from-black/75 via-black/40 to-transparent opacity-0 group-hover:opacity-100 transition-opacity pointer-events-none"
|
||||
@@ -84,6 +85,12 @@ const showAudio = ref(false)
|
||||
|
||||
const shortName = (name) => (name || '').substring(0, 40)
|
||||
|
||||
const fallbackToOriginal = (event, originalUrl) => {
|
||||
if (event.target.dataset.originalFallback) return
|
||||
event.target.dataset.originalFallback = 'true'
|
||||
event.target.src = originalUrl
|
||||
}
|
||||
|
||||
const isImage = computed(() => (props.attachment.content_type || '').startsWith('image/'))
|
||||
|
||||
const isAudio = computed(() => (props.attachment.content_type || '').startsWith('audio/'))
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
:src="getThumbnailUrl(attachment)"
|
||||
:alt="attachment.name"
|
||||
class="max-w-48 max-h-32 rounded-lg object-cover"
|
||||
@error="fallbackToOriginal($event, attachment.url)"
|
||||
@click="openImage(attachment.url)"
|
||||
/>
|
||||
</div>
|
||||
@@ -49,7 +50,13 @@ const isImage = (attachment) => {
|
||||
|
||||
const getThumbnailUrl = (attachment) => {
|
||||
if (!isImage(attachment)) return attachment.url
|
||||
return getThumbFilepath(attachment.url)
|
||||
return attachment.thumbnail_url || getThumbFilepath(attachment.url)
|
||||
}
|
||||
|
||||
const fallbackToOriginal = (event, originalUrl) => {
|
||||
if (event.target.dataset.originalFallback) return
|
||||
event.target.dataset.originalFallback = 'true'
|
||||
event.target.src = originalUrl
|
||||
}
|
||||
|
||||
const openImage = (url) => {
|
||||
|
||||
@@ -13,15 +13,16 @@ const (
|
||||
|
||||
// Attachment represents a file or blob attachment that can be sent or received on a message.
|
||||
type Attachment struct {
|
||||
Name string `json:"name"`
|
||||
Size int `json:"size"`
|
||||
Content []byte `json:"content"`
|
||||
ContentID string `json:"content_id"`
|
||||
ContentType string `json:"content_type"`
|
||||
Disposition string `json:"disposition"`
|
||||
UUID string `json:"uuid"`
|
||||
URL string `json:"url"`
|
||||
Header textproto.MIMEHeader `json:"-"`
|
||||
Name string `json:"name"`
|
||||
Size int `json:"size"`
|
||||
Content []byte `json:"content"`
|
||||
ContentID string `json:"content_id"`
|
||||
ContentType string `json:"content_type"`
|
||||
Disposition string `json:"disposition"`
|
||||
UUID string `json:"uuid"`
|
||||
URL string `json:"url"`
|
||||
ThumbnailURL string `json:"thumbnail_url"`
|
||||
Header textproto.MIMEHeader `json:"-"`
|
||||
}
|
||||
|
||||
type Attachments []Attachment
|
||||
|
||||
@@ -168,6 +168,7 @@ type mediaStore interface {
|
||||
GetBlob(name string) ([]byte, error)
|
||||
GetURL(uuid, contentType, fileName string) string
|
||||
GetSignedURL(name string) string
|
||||
GetThumbnailURL(uuid string) string
|
||||
Attach(id int, model string, modelID int) error
|
||||
SetContentID(id int, contentID string) error
|
||||
GetByModel(id int, model string) ([]mmodels.Media, error)
|
||||
@@ -1829,9 +1830,7 @@ func (m *Manager) BuildWidgetConversationResponse(conversation models.Conversati
|
||||
for _, msg := range messages {
|
||||
m.SignAvatarURL(&msg.Author.AvatarURL)
|
||||
attachments := msg.Attachments
|
||||
for j := range attachments {
|
||||
attachments[j].URL = m.mediaStore.GetSignedURL(attachments[j].UUID)
|
||||
}
|
||||
m.SignAttachmentURLs(attachments)
|
||||
|
||||
// Strip agent email from widget responses.
|
||||
author := msg.Author
|
||||
|
||||
@@ -392,13 +392,21 @@ func (m *Manager) GetMessage(uuid string) (models.Message, error) {
|
||||
}
|
||||
|
||||
// Generate signed URLs for attachments.
|
||||
for i := range message.Attachments {
|
||||
message.Attachments[i].URL = m.mediaStore.GetSignedURL(message.Attachments[i].UUID)
|
||||
}
|
||||
m.SignAttachmentURLs(message.Attachments)
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
// SignAttachmentURLs adds access URLs for the original image and its thumbnail.
|
||||
func (m *Manager) SignAttachmentURLs(attachments attachment.Attachments) {
|
||||
for i := range attachments {
|
||||
attachments[i].URL = m.mediaStore.GetURL(attachments[i].UUID, attachments[i].ContentType, attachments[i].Name)
|
||||
if strings.HasPrefix(attachments[i].ContentType, "image/") {
|
||||
attachments[i].ThumbnailURL = m.mediaStore.GetThumbnailURL(attachments[i].UUID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateMessageStatus updates the status of a message.
|
||||
func (m *Manager) UpdateMessageStatus(messageUUID string, status string) error {
|
||||
if _, err := m.q.UpdateMessageStatus.Exec(status, messageUUID); err != nil {
|
||||
@@ -1314,7 +1322,10 @@ func (m *Manager) fetchMessageAttachments(messageID int) (attachment.Attachments
|
||||
Content: blob,
|
||||
Size: media.Size,
|
||||
Header: attachment.MakeHeader(media.ContentType, contentID, media.Filename, "base64", media.Disposition.String),
|
||||
URL: m.mediaStore.GetSignedURL(media.UUID),
|
||||
URL: m.mediaStore.GetURL(media.UUID, media.ContentType, media.Filename),
|
||||
}
|
||||
if strings.HasPrefix(media.ContentType, "image/") {
|
||||
attachment.ThumbnailURL = m.mediaStore.GetThumbnailURL(media.UUID)
|
||||
}
|
||||
attachments = append(attachments, attachment)
|
||||
}
|
||||
@@ -1449,6 +1460,7 @@ func (m *Manager) broadcastMessageToWidgetClients(message *models.Message) {
|
||||
return
|
||||
}
|
||||
|
||||
m.SignAttachmentURLs(message.Attachments)
|
||||
m.SignAvatarURL(&message.Author.AvatarURL)
|
||||
liveChatInbox.BroadcastMessageToClients(message.ConversationUUID, conversation.ContactID, models.ChatMessage{
|
||||
UUID: message.UUID,
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -241,6 +242,23 @@ func (m *Manager) GetSignedURL(name string) string {
|
||||
return m.GetURL(name, "", "")
|
||||
}
|
||||
|
||||
// GetThumbnailURL returns the URL for an image thumbnail.
|
||||
func (m *Manager) GetThumbnailURL(uuid string) string {
|
||||
if m.store.Name() == "fs" {
|
||||
// FS validates thumbnail requests with the original UUID signature.
|
||||
u, err := url.Parse(m.GetSignedURL(uuid))
|
||||
if err == nil {
|
||||
if idx := strings.LastIndex(u.Path, "/"); idx >= 0 {
|
||||
u.Path = u.Path[:idx+1] + image.ThumbPrefix + uuid
|
||||
} else {
|
||||
u.Path = image.ThumbPrefix + uuid
|
||||
}
|
||||
return u.String()
|
||||
}
|
||||
}
|
||||
return m.store.GetURL(image.ThumbPrefix+uuid, "inline", "")
|
||||
}
|
||||
|
||||
// SignedURLValidator returns the store's signature validator if available.
|
||||
// Returns nil if the store doesn't support signed URL validation.
|
||||
func (m *Manager) SignedURLValidator() func(name, sig string, exp int64) bool {
|
||||
|
||||
Reference in New Issue
Block a user