diff --git a/lib/rails_ext/active_storage_blob_service_url_for_direct_upload_expiry.rb b/lib/rails_ext/active_storage_blob_service_url_for_direct_upload_expiry.rb new file mode 100644 index 000000000..ba918f7f4 --- /dev/null +++ b/lib/rails_ext/active_storage_blob_service_url_for_direct_upload_expiry.rb @@ -0,0 +1,23 @@ +# +# see https://github.com/basecamp/haystack/pull/7862 +# +module ActiveStorage + mattr_accessor :service_urls_for_direct_uploads_expire_in, default: 48.hours +end + +module ActiveStorageBlobServiceUrlForDirectUploadExpiry + # Override default expires_in to accommodate long upload URL expiry + # without having to lengthen download URL expiry. + # + # Accounts for Cloudflare only proxying slow client uploads once they're + # fully buffered, long after the URL expired. + # + # 48 hours covers a 10GB upload at 0.5Mbps. + def service_url_for_direct_upload(expires_in: ActiveStorage.service_urls_for_direct_uploads_expire_in) + super + end +end + +ActiveSupport.on_load :active_storage_blob do + prepend ::ActiveStorageBlobServiceUrlForDirectUploadExpiry +end diff --git a/test/lib/rails_ext/active_storage_blob_service_url_for_direct_upload_expiry_test.rb b/test/lib/rails_ext/active_storage_blob_service_url_for_direct_upload_expiry_test.rb new file mode 100644 index 000000000..79e10fb21 --- /dev/null +++ b/test/lib/rails_ext/active_storage_blob_service_url_for_direct_upload_expiry_test.rb @@ -0,0 +1,24 @@ +require "test_helper" + +class ActiveStorageBlobServiceUrlForDirectUploadExpiryTest < ActiveSupport::TestCase + setup do + @blob = ActiveStorage::Blob.new(key: "test", filename: "test.txt", byte_size: 1024, checksum: "abc") + end + + test "uses extended expiry by default" do + @blob.service.expects(:url_for_direct_upload).with(@blob.key, expires_in: ActiveStorage.service_urls_for_direct_uploads_expire_in, content_type: @blob.content_type, content_length: @blob.byte_size, checksum: @blob.checksum, custom_metadata: {}).returns("https://example.com/upload") + + assert_equal "https://example.com/upload", @blob.service_url_for_direct_upload + end + + test "service_urls_for_direct_uploads_expire_in is configurable" do + original_expire_in = ActiveStorage.service_urls_for_direct_uploads_expire_in + ActiveStorage.service_urls_for_direct_uploads_expire_in = 96.hours + + @blob.service.expects(:url_for_direct_upload).with(@blob.key, expires_in: 96.hours, content_type: @blob.content_type, content_length: @blob.byte_size, checksum: @blob.checksum, custom_metadata: {}).returns("https://example.com/upload") + + assert_equal "https://example.com/upload", @blob.service_url_for_direct_upload + ensure + ActiveStorage.service_urls_for_direct_uploads_expire_in = original_expire_in + end +end