Search code examples
ruby-on-railsrails-activestorage

ActiveStorage: Old urls still request deprecated :combine_options in variation_key


Recently I upgraded from Rails 6.0.3.4 to 6.1.3. ActiveStorage deprecated combine_options, which I cleared from my app. All fresh request work as expected. Internet Bots (Facebook, Google, ...) cache urls to images hosted on a website (like mine). According to my Rollbar records they request these a couple of times a day.

The cached URL's that should load ActiveStorage attachments include an old variation_key in the URL. When the blob wants to load using the decoded variation_key, I see that combine_options is still present. This throws a 500 Internal Server Error with ArgumentError (Active Storage's ImageProcessing transformer doesn't support :combine_options, as it always generates a single ImageMagick command.):.

Is there any way I can stop these errors from showing up?

Rails version: 6.1.3. Ruby version: 2.7.2p137


Solution

  • I have resolved this issue using some middleware. This will intercept all incoming requests, scan if they are ActiveStorage urls, find the ones with the deprecated combine_options and just return 404 not found. This code will also raise an error is the current environment is development, this way I don't accidentally introduce the deprecated code again.

    For those of you who might have the same problem, here's the code.

    application.rb

    require_relative '../lib/stopper'
    config.middleware.use ::Stopper
    

    lib/stopper.rb

    class Stopper
      def initialize(app)
        @app = app
      end
    
      def call(env)
    
        req = Rack::Request.new(env)
        path = req.path
    
        if problematic_active_storage_url?(path)
          if ENV["RACK_ENV"] == 'development'
            raise "Problematic route, includes deprecated combine_options"
          end
          [404, {}, ['not found']]
        else
          @app.call(env)
        end
      end
    
      def problematic_active_storage_url?(path)
        if active_storage_path?(path) && !valid_variation_key?(variation_key_from_path(path))
          return true
        end
        false
      end
    
      def active_storage_path?(path)
        path.start_with?("/rails/active_storage/representations/")
      end
    
      def variation_key_from_path(path)
        if path.start_with?("/rails/active_storage/representations/redirect/")
          path.split('/')[6]
        elsif path.start_with?("/rails/active_storage/representations/")
          path.split('/')[5]
        end
      end
    
      def valid_variation_key?(var_key)
        if decoded_variation = ActiveStorage::Variation.decode(var_key)
          if transformations = decoded_variation.transformations
            if transformations[:combine_options].present?
              return false
            end
          end
        end
        true
      end
    end