I’ve recently upgraded a project from Rails 6.1 → 7 and noticed that in production I’m receiving 404s on ActionText attachments.
I can’t see anything in the upgrade guide that needs to be actioned around ActionText specifically.
When I look at the RichText record where the images fail, I can see that body.attachments has a number of RemoteImage attachables that were uploaded to the 6.1 app. These are producing 404s
When I upload a new image to the record under 7.x, it is being attached to the record as a Blob and displays correctly.
ActionText relies on ActiveStorage, which generates encrypted urls. When you submit, ActionText saves that URL as part of the HTML code, so if something causes ActiveStorage urls to change, the urls in ActionText will no longer work.
The migration from rails 6 to 7 changed the key generator from SHA1 to SHA256, which should have impacted the urls in ActiveStorage, which in turn must have broken your images. Here’s a sample code I used to fix mine:
# After active storage urls are changed, use this to recreate all trix attachments
def self.refresh_trixes
ActionText::RichText.where.not(body: nil).find_each do |trix|
refresh_trix(trix)
end
end
# After active storage urls are changed, use this to recreate a specific trix attachments
def self.refresh_trix(trix)
return unless trix.embeds.size.positive?
trix.body.fragment.find_all("action-text-attachment").each do |node|
embed = trix.embeds.find { |attachment| attachment.filename.to_s == node["filename"] && attachment.byte_size.to_s == node["filesize"] }
node.attributes["url"].value = Rails.application.routes.url_helpers.rails_storage_redirect_url(embed.blob, host: "YOUR_DOMAIN")
node.attributes["sgid"].value = embed.attachable_sgid
end
trix.update_column :body, trix.body.to_s
end
Pleas make sure you test this before running in production. I’ve used a few times and it works well enough, but you never know… and also replace YOUR_DOMAIN with your actual domain.
It’s undocumented. I had to read through the code to figure out why images were broken and how to fix them. Since I rely heavily in signed_id, I’ve created this initializer to block deploy in case something we did caused them to change (which would break action text and a lot of other things)
Rails.application.config.after_initialize do
if Rails.env.production? && User.find_signed("A_SAMPLE_SIGNED_ID").nil?
raise ArgumentError, "Something has changed the signed ids. DO NOT DEPLOY"
end
end
So I grabbed my user, did user.signed_id in production, and placed the result in A_SAMPLE_SIGNED_ID. This way, next time something like the SHA causes the signed ids to change the initializer will raise and block my deploy.
Probably not many apps using action text, and even less who made the Rails 6 → 7 migration already. But I know of another dev, who maintains a blogging sass, who had the same problem and came with a similar solution to mine after also reading through the source code
Thanks for providing this! I’m using it for a different reason: Moving the assets to a different AWS S3 bucket. I’m trying to create different buckets for the different environments so that I can do testing. I’m getting the following error:
Yes, that method was only added in Rails 6.1 with the proxy feature. In your case I think you need to use rails_blob_path. Check the 6.0 guide, section “Linking files”
Sorry if I should create a new topic for this question. I can if needed! But maybe you can help steer me in the right direction. We originally had one single Amazon S3 bucket to store our active-storage assets. I’m trying to create a development environment so that we can do dev/testing and not mess up production assets.
Important detail:
We create courses on our app/site that have text, images, gifs, etc. The fields use ActionText to create the content. We usually create the content in production. Occasionally, we’ll snapshot the production database, and use the copy as our updated development database. I am unsure how this plays out with how active storage works.
What do you recommend would be the best way to create a development environment for this actiontext/active-storage setup but not mess up our production environment?
I don’t remember if Rails 6.0 had environment specific credentials, but you can just use ENV["RAILS_ACTIVE_STORAGE_BUCKET"] of a similar env variable to have separate buckets for production and development.
Then you set the production bucket to mirror every file to the development bucket (which of course means you are doubling your storage costs).
ActionText will still have it’s saved urls pointed to the production url, but when if something causes it to purge a file, it will do so on the development bucket, not the production one.
Of course, I’m not sure if that will work on Rails 6.0 since I only started doing this on 6.1, so you will have to test, but that’s the basic idea.
Yes, but that means your servers are taking on extra load they don’t need to and taking longer to respond to other requests. Better let AWS do it themselves.
@brenogazzola I’m back to this thread again! This time, I’m actually in the process of updating from rails 6.1 to 7.
I am running into the issue where once I deploy the Rails 7 changes to production, the images (that we uploaded into the content elements via actiontext) are broken:
I took your code from above, modified it a little, and made a rake task. Here is the code:
namespace :refresh_trix do
desc "To refresh trix attachments due to Rails 6 to 7 upgrade"
puts Rails.env
task refresh_embeds: :environment do
ActionText::RichText.where.not(body: nil).find_each do |trix|
next unless trix.embeds.size.positive?
trix.body.fragment.find_all("action-text-attachment").each do |node|
# puts "node is: "
# puts node.inspect
embed = trix.embeds.find { |attachment| attachment.filename.to_s == node["filename"] && attachment.byte_size.to_s == node["filesize"] }
puts "Found one. Original url is: "
puts node.attributes["url"].value
puts "Original sgid is: "
puts node.attributes["sgid"].value
if embed.present?
#puts "embed.blob is: "
#puts embed.blob.inspect
# Files
node.attributes["url"].value = Rails.application.routes.url_helpers.rails_storage_redirect_url(embed.blob, host: "https://www.cs2n.org")
node.attributes["sgid"].value = embed.attachable_sgid
puts "New url is: "
puts node.attributes["url"].value
puts "New sgid is: "
puts node.attributes["sgid"].value
end
end
puts "Body is now: "
puts trix.body.to_s
break
#trix.update_column :body, trix.body.to_s
end
end
end
I’m having it “break” after one iteration so that I could see what’s going on. Here is the logger output I have:
Found one. Original url is:
https://www.cs2n.org/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBbVFJIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--2440b027ff68860db4b94e34c580f46c4095bd2d/image.png
Original sgid is:
BAh7CEkiCGdpZAY6BkVUSSIzZ2lkOi8vY3Mybi9BY3RpdmVTdG9yYWdlOjpCbG9iLzIxNDg_ZXhwaXJlc19pbgY7AFRJIgxwdXJwb3NlBjsAVEkiD2F0dGFjaGFibGUGOwBUSSIPZXhwaXJlc19hdAY7AFQw--e16e52d244fcb768d902977f199e77b853675c69
New url is:
https://www.cs2n.org/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBbVFJIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--5c0bcf4fcb0617930b18e514f0341890ca0650ae/image.png
New sgid is:
BAh7CEkiCGdpZAY6BkVUSSIzZ2lkOi8vY3Mybi9BY3RpdmVTdG9yYWdlOjpCbG9iLzIxNDg_ZXhwaXJlc19pbgY7AFRJIgxwdXJwb3NlBjsAVEkiD2F0dGFjaGFibGUGOwBUSSIPZXhwaXJlc19hdAY7AFQw--30154bfac9fd69e0d142b7d701b56a6a917ee0a2
Body is now:
ActiveStorage::Blob Load (30.6ms) SELECT `active_storage_blobs`.* FROM `active_storage_blobs` WHERE `active_storage_blobs`.`id` = 2148 LIMIT 1
Rendered active_storage/blobs/_blob.html.haml (Duration: 35.4ms | Allocations: 10706)
Rendered /Users/vnguyen/.rvm/gems/ruby-2.7.1/gems/actiontext-7.0.2/app/views/action_text/contents/_content.html.erb within layouts/action_text/contents/_content (Duration: 84.5ms | Allocations: 13274)
<div class="trix-content">
<div>Power on your VEX Brain. On your desktop, find and launch the VEXcode V5 Pro software.<br><br><action-text-attachment sgid="BAh7CEkiCGdpZAY6BkVUSSIzZ2lkOi8vY3Mybi9BY3RpdmVTdG9yYWdlOjpCbG9iLzIxNDg_ZXhwaXJlc19pbgY7AFRJIgxwdXJwb3NlBjsAVEkiD2F0dGFjaGFibGUGOwBUSSIPZXhwaXJlc19hdAY7AFQw--30154bfac9fd69e0d142b7d701b56a6a917ee0a2" content-type="image/png" url="https://www.cs2n.org/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBbVFJIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--5c0bcf4fcb0617930b18e514f0341890ca0650ae/image.png" filename="image.png" filesize="4356" width="90" height="90" previewable="true" presentation="gallery" caption="VEXcode V5 Pro"><figure class="attachment attachment--preview attachment--png">
<img src="http://example.org/rails/active_storage/representations/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBbVFJIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--5c0bcf4fcb0617930b18e514f0341890ca0650ae/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdCem9MWm05eWJXRjBTU0lJY0c1bkJqb0dSVlE2RkhKbGMybDZaVjkwYjE5c2FXMXBkRnNIYVFJQUJHa0NBQU09IiwiZXhwIjpudWxsLCJwdXIiOiJ2YXJpYXRpb24ifX0=--9fa28db11c216db60bd6118944e58f8742ab52d8/image.png">
<figcaption class="attachment__caption">VEXcode V5 Pro</figcaption>
</figure></action-text-attachment>
</div>
</div>
After deploying the Rails 7 upgrade to production, I tried to run this (uncommenting out the one line where it actually updates the column), but it doesn’t seem to fix the one image it found.
Any pointers you can provide would be great. Thanks so much!
Weird. Did you check if the update worked? By that I mean checking that the url in the body actually changed after the update?
I remember I had some trouble with it and had to change the command between versions because it had stopped working. The line you have is the one I use currently (I follow Rails Edge, not release versions), so version 7.0 might need something a bit different?
Not sure if it matters, but I have it set to 7.0.2
Because the content in the actiontext-created data was created in the production platform, when I go and update the local/development and staging environments with Rails 7.0.2, I don’t see any changes. (Some background info: we snapshot the production database in order to use for development/staging so that we have updated/real data). Should I be seeing something different in the development/staging database?
EDIT: Oh! I think I understand what you meant by “checking that the URL in the body actually changed after the update”. You meant if it change the database record after that line. Good point. I’ll check that.
@brenogazzola Ok. I finally got time to record one image before and after the script is ran. The record that changes is in the “body” field of the “action_text_rich_texts” table.
BEFORE:
'423', 'content', '<div>Power on your VEX Brain. On your desktop, find and launch the VEXcode V5 Pro software.<br><br><action-text-attachment sgid=\"BAh7CEkiCGdpZAY6BkVUSSIzZ2lkOi8vY3Mybi9BY3RpdmVTdG9yYWdlOjpCbG9iLzIxNDg_ZXhwaXJlc19pbgY7AFRJIgxwdXJwb3NlBjsAVEkiD2F0dGFjaGFibGUGOwBUSSIPZXhwaXJlc19hdAY7AFQw--e16e52d244fcb768d902977f199e77b853675c69\" content-type=\"image/png\" url=\"https://www.cs2n.org/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBbVFJIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--2440b027ff68860db4b94e34c580f46c4095bd2d/image.png\" filename=\"image.png\" filesize=\"4356\" width=\"90\" height=\"90\" presentation=\"gallery\" caption=\"VEXcode V5 Pro\"></action-text-attachment></div>', 'ContentElement', '426', '2020-08-06 03:59:58', '2021-04-22 15:17:21'
AFTER:
'423', 'content', '<div class=\"trix-content\">\n <div>Power on your VEX Brain. On your desktop, find and launch the VEXcode V5 Pro software.<br><br><action-text-attachment sgid=\"BAh7CEkiCGdpZAY6BkVUSSIzZ2lkOi8vY3Mybi9BY3RpdmVTdG9yYWdlOjpCbG9iLzIxNDg_ZXhwaXJlc19pbgY7AFRJIgxwdXJwb3NlBjsAVEkiD2F0dGFjaGFibGUGOwBUSSIPZXhwaXJlc19hdAY7AFQw--30154bfac9fd69e0d142b7d701b56a6a917ee0a2\" content-type=\"image/png\" url=\"https://www.cs2n.org/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBbVFJIiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--5c0bcf4fcb0617930b18e514f0341890ca0650ae/image.png\" filename=\"image.png\" filesize=\"4356\" width=\"90\" height=\"90\" previewable=\"true\" presentation=\"gallery\" caption=\"VEXcode V5 Pro\"></action-text-attachment>\n</div>\n</div>', 'ContentElement', '426', '2020-08-06 03:59:58', '2021-04-22 15:17:21'