diff --git a/app/assets/stylesheets/blazer/bootstrap-propshaft.css b/app/assets/stylesheets/blazer/bootstrap-propshaft.css index 1f02e2205..00e4afb86 100644 --- a/app/assets/stylesheets/blazer/bootstrap-propshaft.css +++ b/app/assets/stylesheets/blazer/bootstrap-propshaft.css @@ -5,6 +5,6 @@ */ @font-face { font-family: "Glyphicons Halflings"; - src: url('/blazer/glyphicons-halflings-regular.eot'); - src: url('/blazer/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('/blazer/glyphicons-halflings-regular.woff2') format('woff2'), url('/blazer/glyphicons-halflings-regular.woff') format('woff'), url('/blazer/glyphicons-halflings-regular.ttf') format('truetype'), url('/blazer/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); + src: url('glyphicons-halflings-regular.eot'); + src: url('glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('glyphicons-halflings-regular.woff2') format('woff2'), url('glyphicons-halflings-regular.woff') format('woff'), url('glyphicons-halflings-regular.ttf') format('truetype'), url('glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); } diff --git a/app/controllers/blazer/assets_controller.rb b/app/controllers/blazer/assets_controller.rb new file mode 100644 index 000000000..32af6f040 --- /dev/null +++ b/app/controllers/blazer/assets_controller.rb @@ -0,0 +1,48 @@ +module Blazer + # Serves Blazer's bundled JS/CSS/fonts directly from the engine so Blazer + # works without a host asset pipeline (no Sprockets, Propshaft, or importmap). + class AssetsController < ActionController::Base + # Public, GET-only static files. Skip CSRF and the cross-origin JavaScript + # check so the vendored JS can be embedded via plain <% end %> + <%= javascript_tag nonce: true do %> <%= blazer_js_var "rootPath", root_path %> <% end %> diff --git a/config/routes.rb b/config/routes.rb index 5df190d91..ba6f240c7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,7 @@ Blazer::Engine.routes.draw do + # Serve Blazer's bundled assets from the engine (no host asset pipeline required) + get "blazer-assets/*path", to: "assets#show", as: :asset_file, format: false + resources :queries do post :run, on: :collection # err on the side of caution post :cancel, on: :collection diff --git a/lib/blazer/engine.rb b/lib/blazer/engine.rb index ed681ecd4..63b5026a7 100644 --- a/lib/blazer/engine.rb +++ b/lib/blazer/engine.rb @@ -3,25 +3,8 @@ class Engine < ::Rails::Engine isolate_namespace Blazer initializer "blazer" do |app| - if app.config.respond_to?(:assets) && defined?(Sprockets) - if Sprockets::VERSION.to_i >= 4 - app.config.assets.precompile += [ - "blazer/application.js", - "blazer/application.css", - "blazer/glyphicons-halflings-regular.eot", - "blazer/glyphicons-halflings-regular.svg", - "blazer/glyphicons-halflings-regular.ttf", - "blazer/glyphicons-halflings-regular.woff", - "blazer/glyphicons-halflings-regular.woff2", - "blazer/favicon.png" - ] - else - # use a proc instead of a string - app.config.assets.precompile << proc { |path| path =~ /\Ablazer\/application\.(js|css)\z/ } - app.config.assets.precompile << proc { |path| path =~ /\Ablazer\/.+\.(eot|svg|ttf|woff|woff2)\z/ } - app.config.assets.precompile << proc { |path| path == "blazer/favicon.png" } - end - end + # Blazer serves its own bundled assets via Blazer::AssetsController, so it + # no longer depends on the host app's asset pipeline (Sprockets/Propshaft). Blazer.time_zone ||= Blazer.settings["time_zone"] || Time.zone Blazer.audit = Blazer.settings.key?("audit") ? Blazer.settings["audit"] : true diff --git a/test/assets_test.rb b/test/assets_test.rb new file mode 100644 index 000000000..c9950d8b8 --- /dev/null +++ b/test/assets_test.rb @@ -0,0 +1,59 @@ +require_relative "test_helper" + +class AssetsTest < ActionDispatch::IntegrationTest + def test_javascript + get blazer.asset_file_path("moment.js") + assert_response :success + assert_match(/javascript/, content_type) + assert response.body.size > 0 + end + + def test_stylesheet + get blazer.asset_file_path("application.css") + assert_response :success + assert_equal "text/css", content_type + assert response.body.size > 0 + end + + def test_nested_asset + get blazer.asset_file_path("ace/ace.js") + assert_response :success + assert_match(/javascript/, content_type) + end + + def test_font + get blazer.asset_file_path("glyphicons-halflings-regular.woff2") + assert_response :success + assert response.body.size > 0 + end + + def test_favicon + get blazer.asset_file_path("favicon.png") + assert_response :success + assert_equal "image/png", content_type + end + + def test_missing_asset + get blazer.asset_file_path("does-not-exist.js") + assert_response :not_found + end + + def test_path_traversal_rejected + get "/blazer-assets/../../../Gemfile" + assert_response :not_found + end + + def test_layout_references_engine_assets + get blazer.root_path + assert_response :success + assert_match %r{blazer-assets/moment\.js}, response.body + assert_match %r{blazer-assets/application\.css}, response.body + assert_no_match %r{/assets/blazer}, response.body + end + + private + + def content_type + response.media_type + end +end