# frozen_string_literal: true class ThemeJavascriptsController < ApplicationController DISK_CACHE_PATH = "#{Rails.root}/tmp/javascript-cache" skip_before_action( :check_xhr, :handle_theme, :preload_json, :redirect_to_login_if_required, :verify_authenticity_token, only: [:show] ) before_action :is_asset_path, :no_cookies, only: [:show] def show raise Discourse::NotFound unless last_modified.present? return render body: nil, status: 304 if not_modified? # Security: safe due to route constraint cache_file = "#{DISK_CACHE_PATH}/#{params[:digest]}.js" unless File.exist?(cache_file) content = query.pluck_first(:content) raise Discourse::NotFound if content.nil? FileUtils.mkdir_p(DISK_CACHE_PATH) File.write(cache_file, content) end # this is only required for NGINX X-SendFile it seems response.headers["Content-Length"] = File.size(cache_file).to_s set_cache_control_headers send_file(cache_file, disposition: :inline) end private def query @query ||= JavascriptCache.where(digest: params[:digest]).limit(1) end def last_modified @last_modified ||= query.pluck_first(:updated_at) end def not_modified? cache_time = begin Time.rfc2822(request.env["HTTP_IF_MODIFIED_SINCE"]) rescue ArgumentError nil end cache_time && last_modified && last_modified <= cache_time end def set_cache_control_headers if Rails.env.development? response.headers['Last-Modified'] = Time.zone.now.httpdate immutable_for(1.second) else response.headers['Last-Modified'] = last_modified.httpdate if last_modified immutable_for(1.year) end end end