85 lines
2.4 KiB
Ruby
85 lines
2.4 KiB
Ruby
|
# frozen_string_literal: true
|
||
|
|
||
|
class ExternalUploadManager
|
||
|
DOWNLOAD_LIMIT = 100.megabytes
|
||
|
|
||
|
class ChecksumMismatchError < StandardError; end
|
||
|
class DownloadFailedError < StandardError; end
|
||
|
class CannotPromoteError < StandardError; end
|
||
|
|
||
|
attr_reader :external_upload_stub
|
||
|
|
||
|
def initialize(external_upload_stub)
|
||
|
@external_upload_stub = external_upload_stub
|
||
|
end
|
||
|
|
||
|
def can_promote?
|
||
|
external_upload_stub.status == ExternalUploadStub.statuses[:created]
|
||
|
end
|
||
|
|
||
|
def promote_to_upload!
|
||
|
raise CannotPromoteError if !can_promote?
|
||
|
|
||
|
external_upload_stub.update!(status: ExternalUploadStub.statuses[:uploaded])
|
||
|
external_stub_object = Discourse.store.object_from_path(external_upload_stub.key)
|
||
|
external_etag = external_stub_object.etag
|
||
|
external_size = external_stub_object.size
|
||
|
external_sha1 = external_stub_object.metadata["sha1-checksum"]
|
||
|
|
||
|
# This could be legitimately nil, if it's too big to download on the
|
||
|
# server, or it could have failed. To this end we set a should_download
|
||
|
# variable as well to check.
|
||
|
tempfile = nil
|
||
|
should_download = external_size < DOWNLOAD_LIMIT
|
||
|
if should_download
|
||
|
tempfile = download(external_upload_stub.key, external_upload_stub.upload_type)
|
||
|
|
||
|
raise DownloadFailedError if tempfile.blank?
|
||
|
|
||
|
actual_sha1 = Upload.generate_digest(tempfile)
|
||
|
if external_sha1 && external_sha1 != actual_sha1
|
||
|
raise ChecksumMismatchError
|
||
|
end
|
||
|
end
|
||
|
|
||
|
# TODO (martin): See if these additional opts will be needed
|
||
|
#
|
||
|
# for_private_message: for_private_message,
|
||
|
# for_site_setting: for_site_setting,
|
||
|
# pasted: pasted,
|
||
|
#
|
||
|
# also check if retain_hours is needed
|
||
|
opts = {
|
||
|
type: external_upload_stub.upload_type,
|
||
|
existing_external_upload_key: external_upload_stub.key,
|
||
|
external_upload_too_big: external_size > DOWNLOAD_LIMIT,
|
||
|
filesize: external_size
|
||
|
}
|
||
|
|
||
|
UploadCreator.new(tempfile, external_upload_stub.original_filename, opts).create_for(
|
||
|
external_upload_stub.created_by_id
|
||
|
)
|
||
|
rescue
|
||
|
external_upload_stub.update!(status: ExternalUploadStub.statuses[:failed])
|
||
|
raise
|
||
|
ensure
|
||
|
tempfile&.close!
|
||
|
end
|
||
|
|
||
|
def destroy!
|
||
|
external_upload_stub.destroy!
|
||
|
end
|
||
|
|
||
|
private
|
||
|
|
||
|
def download(key, type)
|
||
|
url = Discourse.store.signed_url_for_path(external_upload_stub.key)
|
||
|
FileHelper.download(
|
||
|
url,
|
||
|
max_file_size: DOWNLOAD_LIMIT,
|
||
|
tmp_file_name: "discourse-upload-#{type}",
|
||
|
follow_redirect: true
|
||
|
)
|
||
|
end
|
||
|
end
|