chore: Delayed deploy of direct uploads (#3966)

Co-authored-by: Pranav Raj S <pranav@chatwoot.com>
This commit is contained in:
Tejaswini Chile
2022-02-15 16:16:54 +05:30
committed by GitHub
parent 2591a04c0b
commit 94a473c9f8
12 changed files with 90 additions and 40 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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: () => {},
}, },

View File

@@ -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);
}
}); });
} }

View 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,

View File

@@ -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 {

View File

@@ -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>

View File

@@ -44,3 +44,6 @@
value: value:
- name: ANALYTICS_HOST - name: ANALYTICS_HOST
value: value:
- name: DIRECT_UPLOADS_ENABLED
value: false
locked: false

View File

@@ -0,0 +1,5 @@
class AddDirectUploadsToInstallationConfig < ActiveRecord::Migration[6.1]
def change
ConfigLoader.new.process
end
end