mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 20:18:08 +00:00
chore: Delayed deploy of direct uploads (#3966)
Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
@@ -29,10 +29,12 @@ class Messages::MessageBuilder
|
|||||||
return if @attachments.blank?
|
return if @attachments.blank?
|
||||||
|
|
||||||
@attachments.each do |uploaded_attachment|
|
@attachments.each do |uploaded_attachment|
|
||||||
@message.attachments.build(
|
attachment = @message.attachments.build(
|
||||||
account_id: @message.account_id,
|
account_id: @message.account_id,
|
||||||
file: uploaded_attachment
|
file: uploaded_attachment
|
||||||
)
|
)
|
||||||
|
|
||||||
|
attachment.file_type = file_type(uploaded_attachment&.content_type) if uploaded_attachment.is_a?(ActionDispatch::Http::UploadedFile)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -29,10 +29,12 @@ class Api::V1::Widget::MessagesController < Api::V1::Widget::BaseController
|
|||||||
return if params[:message][:attachments].blank?
|
return if params[:message][:attachments].blank?
|
||||||
|
|
||||||
params[:message][:attachments].each do |uploaded_attachment|
|
params[:message][:attachments].each do |uploaded_attachment|
|
||||||
@message.attachments.new(
|
attachment = @message.attachments.new(
|
||||||
account_id: @message.account_id,
|
account_id: @message.account_id,
|
||||||
file: uploaded_attachment
|
file: uploaded_attachment
|
||||||
)
|
)
|
||||||
|
|
||||||
|
attachment.file_type = helpers.file_type(uploaded_attachment&.content_type) if uploaded_attachment.is_a?(ActionDispatch::Http::UploadedFile)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class DashboardController < ActionController::Base
|
|||||||
'API_CHANNEL_NAME',
|
'API_CHANNEL_NAME',
|
||||||
'API_CHANNEL_THUMBNAIL',
|
'API_CHANNEL_THUMBNAIL',
|
||||||
'ANALYTICS_TOKEN',
|
'ANALYTICS_TOKEN',
|
||||||
'ANALYTICS_HOST'
|
'ANALYTICS_HOST',
|
||||||
|
'DIRECT_UPLOADS_ENABLED'
|
||||||
).merge(app_config)
|
).merge(app_config)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ class WidgetsController < ActionController::Base
|
|||||||
private
|
private
|
||||||
|
|
||||||
def set_global_config
|
def set_global_config
|
||||||
@global_config = GlobalConfig.get('LOGO_THUMBNAIL', 'BRAND_NAME', 'WIDGET_BRAND_URL')
|
@global_config = GlobalConfig.get('LOGO_THUMBNAIL', 'BRAND_NAME', 'WIDGET_BRAND_URL', 'DIRECT_UPLOADS_ENABLED')
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_web_widget
|
def set_web_widget
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
>
|
>
|
||||||
<div class="thumb-wrap">
|
<div class="thumb-wrap">
|
||||||
<img
|
<img
|
||||||
v-if="isTypeImage(attachment.resource.content_type)"
|
v-if="isTypeImage(attachment.resource)"
|
||||||
class="image-thumb"
|
class="image-thumb"
|
||||||
:src="attachment.thumb"
|
:src="attachment.thumb"
|
||||||
/>
|
/>
|
||||||
@@ -15,12 +15,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="file-name-wrap">
|
<div class="file-name-wrap">
|
||||||
<span class="item">
|
<span class="item">
|
||||||
{{ attachment.resource.filename }}
|
{{ fileName(attachment.resource) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="file-size-wrap">
|
<div class="file-size-wrap">
|
||||||
<span class="item">
|
<span class="item">
|
||||||
{{ formatFileSize(attachment.resource.byte_size) }}
|
{{ formatFileSize(attachment.resource) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="remove-file-wrap">
|
<div class="remove-file-wrap">
|
||||||
@@ -50,12 +50,17 @@ export default {
|
|||||||
onRemoveAttachment(index) {
|
onRemoveAttachment(index) {
|
||||||
this.removeAttachment(index);
|
this.removeAttachment(index);
|
||||||
},
|
},
|
||||||
formatFileSize(size) {
|
formatFileSize(file) {
|
||||||
|
const size = file.byte_size || file.size;
|
||||||
return formatBytes(size, 0);
|
return formatBytes(size, 0);
|
||||||
},
|
},
|
||||||
isTypeImage(type) {
|
isTypeImage(file) {
|
||||||
|
const type = file.content_type || file.type;
|
||||||
return type.includes('image');
|
return type.includes('image');
|
||||||
},
|
},
|
||||||
|
fileName(file) {
|
||||||
|
return file.filename || file.name;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
direct_upload_url: '/rails/active_storage/direct_uploads',
|
direct_upload_url: '/rails/active_storage/direct_uploads',
|
||||||
direct_upload: true,
|
direct_upload: true,
|
||||||
}"
|
}"
|
||||||
@input-file="onDirectFileUpload"
|
@input-file="onFileUpload"
|
||||||
>
|
>
|
||||||
<woot-button
|
<woot-button
|
||||||
v-if="showAttachButton"
|
v-if="showAttachButton"
|
||||||
@@ -134,7 +134,7 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
onDirectFileUpload: {
|
onFileUpload: {
|
||||||
type: Function,
|
type: Function,
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -77,7 +77,7 @@
|
|||||||
:mode="replyType"
|
:mode="replyType"
|
||||||
:inbox="inbox"
|
:inbox="inbox"
|
||||||
:send-button-text="replyButtonLabel"
|
:send-button-text="replyButtonLabel"
|
||||||
:on-direct-file-upload="onDirectFileUpload"
|
:on-file-upload="onFileUpload"
|
||||||
:show-file-upload="showFileUpload"
|
:show-file-upload="showFileUpload"
|
||||||
:toggle-emoji-picker="toggleEmojiPicker"
|
:toggle-emoji-picker="toggleEmojiPicker"
|
||||||
:show-emoji-picker="showEmojiPicker"
|
:show-emoji-picker="showEmojiPicker"
|
||||||
@@ -179,6 +179,7 @@ export default {
|
|||||||
currentChat: 'getSelectedChat',
|
currentChat: 'getSelectedChat',
|
||||||
messageSignature: 'getMessageSignature',
|
messageSignature: 'getMessageSignature',
|
||||||
currentUser: 'getCurrentUser',
|
currentUser: 'getCurrentUser',
|
||||||
|
globalConfig: 'globalConfig/get',
|
||||||
}),
|
}),
|
||||||
|
|
||||||
showRichContentEditor() {
|
showRichContentEditor() {
|
||||||
@@ -544,6 +545,13 @@ export default {
|
|||||||
isPrivate,
|
isPrivate,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
onFileUpload(file) {
|
||||||
|
if (this.globalConfig.directUploadsEnabled) {
|
||||||
|
this.onDirectFileUpload(file);
|
||||||
|
} else {
|
||||||
|
this.onIndirectFileUpload(file);
|
||||||
|
}
|
||||||
|
},
|
||||||
onDirectFileUpload(file) {
|
onDirectFileUpload(file) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
@@ -559,13 +567,7 @@ export default {
|
|||||||
if (error) {
|
if (error) {
|
||||||
this.showAlert(error);
|
this.showAlert(error);
|
||||||
} else {
|
} else {
|
||||||
this.attachedFiles.push({
|
this.attachFile({ file, blob });
|
||||||
currentChatId: this.currentChat.id,
|
|
||||||
resource: blob,
|
|
||||||
isPrivate: this.isPrivate,
|
|
||||||
thumb: null,
|
|
||||||
blobSignedId: blob.signed_id,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -576,22 +578,12 @@ export default {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onFileUpload(file) {
|
onIndirectFileUpload(file) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
||||||
this.attachedFiles = [];
|
this.attachFile({ file });
|
||||||
const reader = new FileReader();
|
|
||||||
reader.readAsDataURL(file.file);
|
|
||||||
reader.onloadend = () => {
|
|
||||||
this.attachedFiles.push({
|
|
||||||
currentChatId: this.currentChat.id,
|
|
||||||
resource: file,
|
|
||||||
isPrivate: this.isPrivate,
|
|
||||||
thumb: reader.result,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
this.showAlert(
|
this.showAlert(
|
||||||
this.$t('CONVERSATION.FILE_SIZE_LIMIT', {
|
this.$t('CONVERSATION.FILE_SIZE_LIMIT', {
|
||||||
@@ -600,6 +592,19 @@ export default {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
attachFile({ blob, file }) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file.file);
|
||||||
|
reader.onloadend = () => {
|
||||||
|
this.attachedFiles.push({
|
||||||
|
currentChatId: this.currentChat.id,
|
||||||
|
resource: blob || file,
|
||||||
|
isPrivate: this.isPrivate,
|
||||||
|
thumb: reader.result,
|
||||||
|
blobSignedId: blob ? blob.signed_id : undefined,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
},
|
||||||
removeAttachment(itemIndex) {
|
removeAttachment(itemIndex) {
|
||||||
this.attachedFiles = this.attachedFiles.filter(
|
this.attachedFiles = this.attachedFiles.filter(
|
||||||
(item, index) => itemIndex !== index
|
(item, index) => itemIndex !== index
|
||||||
@@ -619,7 +624,11 @@ export default {
|
|||||||
if (this.attachedFiles && this.attachedFiles.length) {
|
if (this.attachedFiles && this.attachedFiles.length) {
|
||||||
messagePayload.files = [];
|
messagePayload.files = [];
|
||||||
this.attachedFiles.forEach(attachment => {
|
this.attachedFiles.forEach(attachment => {
|
||||||
|
if (this.globalConfig.directUploadsEnabled) {
|
||||||
messagePayload.files.push(attachment.blobSignedId);
|
messagePayload.files.push(attachment.blobSignedId);
|
||||||
|
} else {
|
||||||
|
messagePayload.files.push(attachment.resource.file);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ const {
|
|||||||
BRAND_NAME: brandName,
|
BRAND_NAME: brandName,
|
||||||
CHATWOOT_INBOX_TOKEN: chatwootInboxToken,
|
CHATWOOT_INBOX_TOKEN: chatwootInboxToken,
|
||||||
CREATE_NEW_ACCOUNT_FROM_DASHBOARD: createNewAccountFromDashboard,
|
CREATE_NEW_ACCOUNT_FROM_DASHBOARD: createNewAccountFromDashboard,
|
||||||
|
DIRECT_UPLOADS_ENABLED: directUploadsEnabled,
|
||||||
DISPLAY_MANIFEST: displayManifest,
|
DISPLAY_MANIFEST: displayManifest,
|
||||||
INSTALLATION_NAME: installationName,
|
INSTALLATION_NAME: installationName,
|
||||||
LOGO_THUMBNAIL: logoThumbnail,
|
LOGO_THUMBNAIL: logoThumbnail,
|
||||||
@@ -21,6 +22,7 @@ const state = {
|
|||||||
brandName,
|
brandName,
|
||||||
chatwootInboxToken,
|
chatwootInboxToken,
|
||||||
createNewAccountFromDashboard,
|
createNewAccountFromDashboard,
|
||||||
|
directUploadsEnabled: directUploadsEnabled === 'true',
|
||||||
displayManifest,
|
displayManifest,
|
||||||
installationName,
|
installationName,
|
||||||
logo,
|
logo,
|
||||||
|
|||||||
@@ -41,7 +41,12 @@ const sendAttachment = ({ attachment }) => {
|
|||||||
const { file } = attachment;
|
const { file } = attachment;
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
if (typeof file === 'string') {
|
||||||
formData.append('message[attachments][]', file);
|
formData.append('message[attachments][]', file);
|
||||||
|
} else {
|
||||||
|
formData.append('message[attachments][]', file, file.name);
|
||||||
|
}
|
||||||
|
|
||||||
formData.append('message[referer_url]', referrerURL);
|
formData.append('message[referer_url]', referrerURL);
|
||||||
formData.append('message[timestamp]', timestamp);
|
formData.append('message[timestamp]', timestamp);
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -2,8 +2,11 @@
|
|||||||
<file-upload
|
<file-upload
|
||||||
:size="4096 * 2048"
|
:size="4096 * 2048"
|
||||||
:accept="allowedFileTypes"
|
:accept="allowedFileTypes"
|
||||||
:data="{ direct_upload_url: '', direct_upload: true }"
|
:data="{
|
||||||
@input-file="onDirectFileUpload"
|
direct_upload_url: '/rails/active_storage/direct_uploads',
|
||||||
|
direct_upload: true,
|
||||||
|
}"
|
||||||
|
@input-file="onFileUpload"
|
||||||
>
|
>
|
||||||
<button class="icon-button flex items-center justify-center">
|
<button class="icon-button flex items-center justify-center">
|
||||||
<fluent-icon v-if="!isUploading.image" icon="attach" />
|
<fluent-icon v-if="!isUploading.image" icon="attach" />
|
||||||
@@ -23,6 +26,7 @@ import {
|
|||||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||||
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
||||||
import { DirectUpload } from 'activestorage';
|
import { DirectUpload } from 'activestorage';
|
||||||
|
import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { FluentIcon, FileUpload, Spinner },
|
components: { FluentIcon, FileUpload, Spinner },
|
||||||
@@ -36,6 +40,7 @@ export default {
|
|||||||
return { isUploading: false };
|
return { isUploading: false };
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapGetters({ globalConfig: 'globalConfig/get' }),
|
||||||
fileUploadSizeLimit() {
|
fileUploadSizeLimit() {
|
||||||
return MAXIMUM_FILE_UPLOAD_SIZE;
|
return MAXIMUM_FILE_UPLOAD_SIZE;
|
||||||
},
|
},
|
||||||
@@ -47,6 +52,13 @@ export default {
|
|||||||
getFileType(fileType) {
|
getFileType(fileType) {
|
||||||
return fileType.includes('image') ? 'image' : 'file';
|
return fileType.includes('image') ? 'image' : 'file';
|
||||||
},
|
},
|
||||||
|
async onFileUpload(file) {
|
||||||
|
if (this.globalConfig.directUploadsEnabled) {
|
||||||
|
this.onDirectFileUpload(file);
|
||||||
|
} else {
|
||||||
|
this.onIndirectFileUpload(file);
|
||||||
|
}
|
||||||
|
},
|
||||||
async onDirectFileUpload(file) {
|
async onDirectFileUpload(file) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
@@ -68,8 +80,8 @@ export default {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.onAttach({
|
this.onAttach({
|
||||||
fileType: blob.content_type,
|
|
||||||
file: blob.signed_id,
|
file: blob.signed_id,
|
||||||
|
...this.getLocalFileAttributes(file),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -85,18 +97,16 @@ export default {
|
|||||||
}
|
}
|
||||||
this.isUploading = false;
|
this.isUploading = false;
|
||||||
},
|
},
|
||||||
async onFileUpload(file) {
|
async onIndirectFileUpload(file) {
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.isUploading = true;
|
this.isUploading = true;
|
||||||
try {
|
try {
|
||||||
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
if (checkFileSizeLimit(file, MAXIMUM_FILE_UPLOAD_SIZE)) {
|
||||||
const thumbUrl = window.URL.createObjectURL(file.file);
|
|
||||||
await this.onAttach({
|
await this.onAttach({
|
||||||
fileType: this.getFileType(file.type),
|
|
||||||
file: file.file,
|
file: file.file,
|
||||||
thumbUrl,
|
...this.getLocalFileAttributes(file),
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
window.bus.$emit(BUS_EVENTS.SHOW_ALERT, {
|
window.bus.$emit(BUS_EVENTS.SHOW_ALERT, {
|
||||||
@@ -110,6 +120,12 @@ export default {
|
|||||||
}
|
}
|
||||||
this.isUploading = false;
|
this.isUploading = false;
|
||||||
},
|
},
|
||||||
|
getLocalFileAttributes(file) {
|
||||||
|
return {
|
||||||
|
thumbUrl: window.URL.createObjectURL(file.file),
|
||||||
|
fileType: this.getFileType(file.type),
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -44,3 +44,6 @@
|
|||||||
value:
|
value:
|
||||||
- name: ANALYTICS_HOST
|
- name: ANALYTICS_HOST
|
||||||
value:
|
value:
|
||||||
|
- name: DIRECT_UPLOADS_ENABLED
|
||||||
|
value: false
|
||||||
|
locked: false
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
class AddDirectUploadsToInstallationConfig < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
ConfigLoader.new.process
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user