mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: uploads are processed a faster
Also cleans up API to always return 422 on upload error. (previously returned 200) Uploads are processed using new hijack pattern
This commit is contained in:
parent
71942e4f62
commit
eb428ef54d
@ -416,20 +416,9 @@ export default Ember.Component.extend({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$element.on("fileuploadfail", (e, data) => {
|
$element.on("fileuploaddone", (e, data) => {
|
||||||
this._resetUpload(true);
|
let upload = data.result;
|
||||||
|
|
||||||
const userCancelled = this._xhr && this._xhr._userCancelled;
|
|
||||||
this._xhr = null;
|
|
||||||
|
|
||||||
if (!userCancelled) {
|
|
||||||
displayErrorForUpload(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.messageBus.subscribe("/uploads/composer", upload => {
|
|
||||||
// replace upload placeholder
|
|
||||||
if (upload && upload.url) {
|
|
||||||
if (!this._xhr || !this._xhr._userCancelled) {
|
if (!this._xhr || !this._xhr._userCancelled) {
|
||||||
const markdown = getUploadMarkdown(upload);
|
const markdown = getUploadMarkdown(upload);
|
||||||
cacheShortUploadUrl(upload.short_url, upload.url);
|
cacheShortUploadUrl(upload.short_url, upload.url);
|
||||||
@ -438,9 +427,16 @@ export default Ember.Component.extend({
|
|||||||
} else {
|
} else {
|
||||||
this._resetUpload(true);
|
this._resetUpload(true);
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
|
|
||||||
|
$element.on("fileuploadfail", (e, data) => {
|
||||||
this._resetUpload(true);
|
this._resetUpload(true);
|
||||||
displayErrorForUpload(upload);
|
|
||||||
|
const userCancelled = this._xhr && this._xhr._userCancelled;
|
||||||
|
this._xhr = null;
|
||||||
|
|
||||||
|
if (!userCancelled) {
|
||||||
|
displayErrorForUpload(data.jqXHR.responseJSON);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -18,12 +18,9 @@ export default Em.Mixin.create({
|
|||||||
uploadUrl = Discourse.getURL(this.getWithDefault("uploadUrl", "/uploads")),
|
uploadUrl = Discourse.getURL(this.getWithDefault("uploadUrl", "/uploads")),
|
||||||
reset = () => this.setProperties({ uploading: false, uploadProgress: 0});
|
reset = () => this.setProperties({ uploading: false, uploadProgress: 0});
|
||||||
|
|
||||||
this.messageBus.subscribe("/uploads/" + this.get("type"), upload => {
|
$upload.on("fileuploaddone", (e, data) => {
|
||||||
if (upload && upload.url) {
|
let upload = data.result;
|
||||||
this.uploadDone(upload);
|
this.uploadDone(upload);
|
||||||
} else {
|
|
||||||
displayErrorForUpload(upload);
|
|
||||||
}
|
|
||||||
reset();
|
reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -59,7 +56,7 @@ export default Em.Mixin.create({
|
|||||||
});
|
});
|
||||||
|
|
||||||
$upload.on("fileuploadfail", (e, data) => {
|
$upload.on("fileuploadfail", (e, data) => {
|
||||||
displayErrorForUpload(data);
|
displayErrorForUpload(data.jqXHR.responseJSON);
|
||||||
reset();
|
reset();
|
||||||
});
|
});
|
||||||
}.on("didInsertElement"),
|
}.on("didInsertElement"),
|
||||||
|
@ -20,19 +20,23 @@ class UploadsController < ApplicationController
|
|||||||
file = params[:file] || params[:files]&.first
|
file = params[:file] || params[:files]&.first
|
||||||
pasted = params[:pasted] == "true"
|
pasted = params[:pasted] == "true"
|
||||||
for_private_message = params[:for_private_message] == "true"
|
for_private_message = params[:for_private_message] == "true"
|
||||||
|
is_api = is_api?
|
||||||
|
retain_hours = params[:retain_hours].to_i
|
||||||
|
|
||||||
if params[:synchronous] && (current_user.staff? || is_api?)
|
# note, atm hijack is processed in its own context and has not access to controller
|
||||||
data = create_upload(current_user, file, url, type, for_private_message, pasted)
|
# longer term we may change this
|
||||||
render json: serialize_upload(data)
|
hijack do
|
||||||
else
|
info = UploadsController.create_upload(
|
||||||
Scheduler::Defer.later("Create Upload") do
|
current_user: me,
|
||||||
begin
|
file: file,
|
||||||
data = create_upload(me, file, url, type, for_private_message, pasted)
|
url: url,
|
||||||
ensure
|
type: type,
|
||||||
MessageBus.publish("/uploads/#{type}", serialize_upload(data), client_ids: [params[:client_id]])
|
for_private_message: for_private_message,
|
||||||
end
|
pasted: pasted,
|
||||||
end
|
is_api: is_api,
|
||||||
render json: success_json
|
retain_hours: retain_hours
|
||||||
|
)
|
||||||
|
render json: UploadsController.serialize_upload(info), status: Upload === info ? 200 : 422
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -72,20 +76,20 @@ class UploadsController < ApplicationController
|
|||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def serialize_upload(data)
|
def render_404
|
||||||
|
raise Discourse::NotFound
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.serialize_upload(data)
|
||||||
# as_json.as_json is not a typo... as_json in AM serializer returns keys as symbols, we need them
|
# as_json.as_json is not a typo... as_json in AM serializer returns keys as symbols, we need them
|
||||||
# as strings here
|
# as strings here
|
||||||
serialized = UploadSerializer.new(data, root: nil).as_json.as_json if Upload === data
|
serialized = UploadSerializer.new(data, root: nil).as_json.as_json if Upload === data
|
||||||
serialized ||= (data || {}).as_json
|
serialized ||= (data || {}).as_json
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_404
|
def self.create_upload(current_user:, file:, url:, type:, for_private_message:, pasted:, is_api:, retain_hours:)
|
||||||
raise Discourse::NotFound
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_upload(current_user, file, url, type, for_private_message, pasted)
|
|
||||||
if file.nil?
|
if file.nil?
|
||||||
if url.present? && is_api?
|
if url.present? && is_api
|
||||||
maximum_upload_size = [SiteSetting.max_image_size_kb, SiteSetting.max_attachment_size_kb].max.kilobytes
|
maximum_upload_size = [SiteSetting.max_image_size_kb, SiteSetting.max_attachment_size_kb].max.kilobytes
|
||||||
tempfile = FileHelper.download(
|
tempfile = FileHelper.download(
|
||||||
url,
|
url,
|
||||||
@ -112,7 +116,6 @@ class UploadsController < ApplicationController
|
|||||||
upload = UploadCreator.new(tempfile, filename, opts).create_for(current_user.id)
|
upload = UploadCreator.new(tempfile, filename, opts).create_for(current_user.id)
|
||||||
|
|
||||||
if upload.errors.empty? && current_user.admin?
|
if upload.errors.empty? && current_user.admin?
|
||||||
retain_hours = params[:retain_hours].to_i
|
|
||||||
upload.update_columns(retain_hours: retain_hours) if retain_hours > 0
|
upload.update_columns(retain_hours: retain_hours) if retain_hours > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -25,9 +25,17 @@ module Hijack
|
|||||||
end
|
end
|
||||||
|
|
||||||
if opts.key?(:plain)
|
if opts.key?(:plain)
|
||||||
@content_type = 'text/plain'
|
@content_type = 'text/plain; charset=utf-8'
|
||||||
@body = opts[:plain].to_s
|
@body = opts[:plain].to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if opts.key?(:json)
|
||||||
|
@content_type = 'application/json; charset=utf-8'
|
||||||
|
@body = opts[:json]
|
||||||
|
unless String === @body
|
||||||
|
@body = @body.to_json
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -40,12 +40,10 @@ describe UploadsController do
|
|||||||
it 'is successful with an image' do
|
it 'is successful with an image' do
|
||||||
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything)
|
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything)
|
||||||
|
|
||||||
message = MessageBus.track_publish('/uploads/avatar') do
|
|
||||||
post :create, params: { file: logo, type: "avatar", format: :json }
|
post :create, params: { file: logo, type: "avatar", format: :json }
|
||||||
end.first
|
|
||||||
|
|
||||||
expect(response.status).to eq 200
|
expect(response.status).to eq 200
|
||||||
expect(message.data["id"]).to be
|
expect(JSON.parse(response.body)["id"]).to be
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is successful with an attachment' do
|
it 'is successful with an attachment' do
|
||||||
@ -53,12 +51,11 @@ describe UploadsController do
|
|||||||
|
|
||||||
Jobs.expects(:enqueue).never
|
Jobs.expects(:enqueue).never
|
||||||
|
|
||||||
message = MessageBus.track_publish('/uploads/composer') do
|
|
||||||
post :create, params: { file: text_file, type: "composer", format: :json }
|
post :create, params: { file: text_file, type: "composer", format: :json }
|
||||||
end.first
|
|
||||||
|
|
||||||
expect(response.status).to eq 200
|
expect(response.status).to eq 200
|
||||||
expect(message.data["id"]).to be
|
id = JSON.parse(response.body)["id"]
|
||||||
|
expect(id).to be
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'is successful with synchronous api' do
|
it 'is successful with synchronous api' do
|
||||||
@ -88,28 +85,25 @@ describe UploadsController do
|
|||||||
log_in :admin
|
log_in :admin
|
||||||
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything).never
|
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything).never
|
||||||
|
|
||||||
message = MessageBus.track_publish('/uploads/profile_background') do
|
|
||||||
post :create, params: {
|
post :create, params: {
|
||||||
file: logo,
|
file: logo,
|
||||||
retain_hours: 100,
|
retain_hours: 100,
|
||||||
type: "profile_background",
|
type: "profile_background",
|
||||||
format: :json
|
format: :json
|
||||||
}
|
}
|
||||||
end.first
|
|
||||||
|
|
||||||
id = message.data["id"]
|
id = JSON.parse(response.body)["id"]
|
||||||
expect(Upload.find(id).retain_hours).to eq(100)
|
expect(Upload.find(id).retain_hours).to eq(100)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'requires a file' do
|
it 'requires a file' do
|
||||||
Jobs.expects(:enqueue).never
|
Jobs.expects(:enqueue).never
|
||||||
|
|
||||||
message = MessageBus.track_publish('/uploads/composer') do
|
|
||||||
post :create, params: { type: "composer", format: :json }
|
post :create, params: { type: "composer", format: :json }
|
||||||
end.first
|
|
||||||
|
|
||||||
expect(response.status).to eq 200
|
message = JSON.parse(response.body)
|
||||||
expect(message.data["errors"]).to contain_exactly(I18n.t("upload.file_missing"))
|
expect(response.status).to eq 422
|
||||||
|
expect(message["errors"]).to contain_exactly(I18n.t("upload.file_missing"))
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'properly returns errors' do
|
it 'properly returns errors' do
|
||||||
@ -117,12 +111,11 @@ describe UploadsController do
|
|||||||
|
|
||||||
Jobs.expects(:enqueue).never
|
Jobs.expects(:enqueue).never
|
||||||
|
|
||||||
message = MessageBus.track_publish("/uploads/avatar") do
|
|
||||||
post :create, params: { file: text_file, type: "avatar", format: :json }
|
post :create, params: { file: text_file, type: "avatar", format: :json }
|
||||||
end.first
|
|
||||||
|
|
||||||
expect(response.status).to eq 200
|
expect(response.status).to eq 422
|
||||||
expect(message.data["errors"]).to be
|
errors = JSON.parse(response.body)["errors"]
|
||||||
|
expect(errors).to be
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'ensures allow_uploaded_avatars is enabled when uploading an avatar' do
|
it 'ensures allow_uploaded_avatars is enabled when uploading an avatar' do
|
||||||
@ -142,28 +135,26 @@ describe UploadsController do
|
|||||||
SiteSetting.allow_staff_to_upload_any_file_in_pm = true
|
SiteSetting.allow_staff_to_upload_any_file_in_pm = true
|
||||||
@user.update_columns(moderator: true)
|
@user.update_columns(moderator: true)
|
||||||
|
|
||||||
message = MessageBus.track_publish('/uploads/composer') do
|
|
||||||
post :create, params: {
|
post :create, params: {
|
||||||
file: text_file,
|
file: text_file,
|
||||||
type: "composer",
|
type: "composer",
|
||||||
for_private_message: "true",
|
for_private_message: "true",
|
||||||
format: :json
|
format: :json
|
||||||
}
|
}
|
||||||
end.first
|
|
||||||
|
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
expect(message.data["id"]).to be
|
id = JSON.parse(response.body)["id"]
|
||||||
|
expect(id).to be
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an error when it could not determine the dimensions of an image' do
|
it 'returns an error when it could not determine the dimensions of an image' do
|
||||||
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything).never
|
Jobs.expects(:enqueue).with(:create_avatar_thumbnails, anything).never
|
||||||
|
|
||||||
message = MessageBus.track_publish('/uploads/composer') do
|
|
||||||
post :create, params: { file: fake_jpg, type: "composer", format: :json }
|
post :create, params: { file: fake_jpg, type: "composer", format: :json }
|
||||||
end.first
|
|
||||||
|
|
||||||
expect(response.status).to eq 200
|
expect(response.status).to eq 422
|
||||||
expect(message.data["errors"]).to contain_exactly(I18n.t("upload.images.size_not_found"))
|
message = JSON.parse(response.body)["errors"]
|
||||||
|
expect(message).to contain_exactly(I18n.t("upload.images.size_not_found"))
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user