Skip to content

Commit

Permalink
[30] Proxy Static Pages (#317)
Browse files Browse the repository at this point in the history
* Proxy Static Pages

Map a proxy such that URLs attempt to load jekyll content from the
static pages content.
* make static pages configurable, proxy static pages through rails

---------

Co-authored-by: Stephen Chudleigh <[email protected]>
  • Loading branch information
danivovich and stepchud authored Dec 17, 2024
1 parent 348d3af commit e1ce5be
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 63 deletions.
2 changes: 1 addition & 1 deletion .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ exclude_patterns:
- vendor/
- "**/vendor/**/*"
- app/assets/images/
- spec/**/*
- spec/**/*
10 changes: 10 additions & 0 deletions .env_login → .env_dev
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ export LOGOUT_REDIRECT_EVAL_URL=http://localhost:3000/
export PHOENIX_URI="http://localhost:4000"
export LOGIN_SECRET="f4d3c40a00a8e6ed72fae5204d9ddacd40f087865d40803a6fcfb935591a271838533f06081067dac24c0085c74123e7e1c8b3e0ab562c6645b17eb769854d0d"
export JWT_SECRET="fc28c5738ca45162f61126e770a8fbdbd938d0fedcfe8fbb9f851b855b0264866364a9130e96aca8b1977e9f58edf064f1aa435ceccf415ff22fd3c24adba320"

# static pages on localhost
# export PAGES_DOMAIN="localhost:4001"
# export PAGES_HOST="http://localhost:4001"
# export PAGES_BASE_URL="/"

# static pages on eval-dev branch
export PAGES_DOMAIN="federalist-2c628203-05c2-48ab-8f87-3eda79380559.sites.pages.cloud.gov"
export PAGES_HOST="https://federalist-2c628203-05c2-48ab-8f87-3eda79380559.sites.pages.cloud.gov"
export PAGES_BASE_URL="/preview/gsa/challenges-and-prizes/eval-dev"
2 changes: 1 addition & 1 deletion .envrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ mkdir -p .nix-bundler
export BUNDLE_PATH=./.nix-bundler

# Login Env Vars
source .env_login
source .env_dev
1 change: 1 addition & 0 deletions .simplecov
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ SimpleCov.start 'rails' do
add_filter '/app/jobs/application_job.rb'
add_filter '/app/mailers/application_mailer.rb'
add_filter '/app/models/application_record.rb'
add_filter '/app/controllers/pages_controller.rb'

add_filter '/app/controllers/sandbox_controller.rb'

Expand Down
2 changes: 1 addition & 1 deletion DEVCONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Once direnv is installed and your shell is restarted, clone the project and `cd`
```
./bin/dev
```
> _NOTE for login.gov configuration_ -- if you are **not** using direnv/nix to eval `.envrc`, you can run `source .env_login` in your terminal before starting the server or add the env vars in that file to your local environment directly.
> _NOTE for login.gov configuration_ -- if you are **not** using direnv/nix to eval `.envrc`, you can run `source .env_dev` in your terminal before starting the server or add the env vars in that file to your local environment directly.
Now you can visit [`localhost:3000`](http://localhost:3000) from your browser.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,5 @@ end
gem "factory_bot", "~> 6.5"

gem "faker", "~> 3.4"

gem "rails-reverse-proxy"
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ GEM
rails-html-sanitizer (1.6.0)
loofah (~> 2.21)
nokogiri (~> 1.14)
rails-reverse-proxy (0.13.0)
actionpack
addressable
railties (7.2.2)
actionpack (= 7.2.2)
activesupport (= 7.2.2)
Expand Down Expand Up @@ -398,6 +401,7 @@ DEPENDENCIES
puma (>= 6.4.3)
rails (~> 7.2.1)
rails-controller-testing
rails-reverse-proxy
rspec-rails
rspec_junit_formatter
rubocop (>= 1.66.0)
Expand Down
64 changes: 64 additions & 0 deletions app/controllers/pages_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# frozen_string_literal: true

# Proxy Cloud.gov pages content at the root of the application
class PagesController < ApplicationController
include ReverseProxy::Controller
# We must remove this for proxy of JS assets to be loaded by the browser
protect_from_forgery except: :assets

# TODO: When launched, the cloud.gov pages need to move off the www.challenge.gov domain
DOMAIN = Rails.configuration.static_site_interop.fetch(:domain)
HOST = Rails.configuration.static_site_interop.fetch(:host)
BASE_URL = Rails.configuration.static_site_interop.fetch(:base_url)

def index
path = "#{BASE_URL}/#{params[:path]}/"
reverse_proxy(HOST, path:, reset_accept_encoding: true, headers: { host: DOMAIN }) do |config|
config.on_missing do |_code, _response|
redirect_to "/dashboard"
return true
end

config.on_response do |_code, response|
response.body = rewrite_links(response.body)
end
end
end

def assets
if params[:ext] == "min"
path = "#{HOST}#{BASE_URL}/assets/#{params[:path]}.#{params[:ext]}.js"
response = Faraday.get(path)
send_data(response.body, type: 'application/javascript')
else
path = "#{BASE_URL}/assets/#{params[:path]}.#{params[:ext]}"
reverse_proxy(HOST, path:, reset_accept_encoding: true, headers: { host: DOMAIN })
end
end

def root
path = "#{BASE_URL}/"
reverse_proxy(HOST, path:, reset_accept_encoding: true, headers: { host: DOMAIN }) do |config|
config.on_response do |_code, response|
if response.body.present?
response.body = rewrite_links(response.body)
end
end
end
end

private

def rewrite_links(html)
parsed_html = html.gsub(HOST, "/")
if BASE_URL.length > 1
parsed_html = parsed_html.gsub(BASE_URL, "")
end
# delete the data-public-url attribute from the react app element to send requests through rails proxy
parsed_html = parsed_html.sub(/(<div id="challenge-gov-react-app".+)(data-public-url=[^ ]+)/, '\1')

# rubocop:disable Rails/OutputSafety
parsed_html.html_safe
# rubocop:enable Rails/OutputSafety
end
end
6 changes: 6 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ class Application < Rails::Application
jwt_secret: ENV.fetch("JWT_SECRET", "jwt_secret_123")
}

config.static_site_interop = {
domain: ENV.fetch("PAGES_DOMAIN", "federalist-2c628203-05c2-48ab-8f87-3eda79380559.sites.pages.cloud.gov"),
host: ENV.fetch("PAGES_HOST", "https://federalist-2c628203-05c2-48ab-8f87-3eda79380559.sites.pages.cloud.gov"),
base_url: ENV.fetch("PAGES_BASE_URL", "/preview/gsa/challenges-and-prizes/staging")
}

config.assets.initialize_on_precompile = false
end
end
3 changes: 2 additions & 1 deletion config/initializers/assets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

Rails.application.config.assets.enabled = true


# Move the rails assets so they don't conflict with the static pages
Rails.application.config.assets.prefix = "/platform-assets"

# Rails.application.config.assets.paths << Rails.root.join("app", "assets", "plugins", "uswds","js")

Expand Down
7 changes: 4 additions & 3 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
delete 'timeout'
end

get '/', to: "dashboard#index"
get '/dashboard', to: "dashboard#index"

resources :evaluations, only: [:index]
Expand All @@ -35,13 +34,15 @@
# Can be used by load balancers and uptime monitors to verify that the app is live.
get "up" => "rails/health#show", as: :rails_health_check

# Defines the root path route ("/")
# root "posts#index"
if Rails.env.development? || Rails.env.dev? || Rails.env.test?
namespace :dev do
get "/sandbox", to: "sandbox#index"
get "/accounts", to: "accounts#index"
post "/login", to: "accounts#login"
end
end

match '/assets/*path.:ext' => 'pages#assets', via: [:get]
match '/*path' => 'pages#index', via: [:get]
match '/' => 'pages#root', via: [:get]
end
55 changes: 0 additions & 55 deletions spec/requests/dashboard_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,6 @@
RSpec.describe "DashboardController" do
let(:user) { create_and_log_in_user }

describe "GET /" do
before { get "/" }

it_behaves_like "a page with footer content"
it_behaves_like "a page with header content"

context "when logged in as super admin on the root url" do
before do
user.update(role: "super_admin")
get "/"
end

it_behaves_like "a page with dashboard content for a super admin"
end

context "when logged in as admin on the root url" do
before do
user.update(role: "admin")
get "/"
end

it_behaves_like "a page with dashboard content for an admin"
end

context "when logged in as public solver on the root url" do
before do
user.update(role: "solver")
get "/"
end

it_behaves_like "a page with dashboard content for a public solver"
end

context "when logged in as a challenge manager on the root url" do
before do
user.update(role: "challenge_manager")
get "/"
end

it_behaves_like "a page with utility menu links for all users"
it_behaves_like "a page with utility menu links for a challenge manager"
it_behaves_like "a page with dashboard content for a challenge manager"
end

context "when logged in as an evaluator on the root url" do
before do
user.update(role: "evaluator")
get "/"
end

it_behaves_like "a page with utility menu links for all users"
it_behaves_like "a page with dashboard content for an evaluator"
end
end

describe "GET /dashboard" do
before { get "/dashboard" }

Expand Down
43 changes: 43 additions & 0 deletions spec/requests/pages_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require "rails_helper"

RSpec.describe "PagesController" do
it "get root renders successfully" do
stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/").
to_return(status: 200, body: "", headers: {})

get "/"
expect(response).to be_ok
end

it "removes full paths to assets so they proxy too" do
stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/").
to_return(status: 200, body: PagesController::HOST + PagesController::BASE_URL, headers: {})

get "/"
expect(response.body).to eq("/")
end

it "works for minified js assets" do
stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/assets/uswds.min.js").
to_return(status: 200, body: "", headers: {})

get "/assets/uswds.min.js"
expect(response).to be_ok
end

it "works for image assets" do
stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/assets/logo.svg").
to_return(status: 200, body: "", headers: {})

get "/assets/logo.svg"
expect(response).to be_ok
end

it "404s to the dashboard" do
stub_request(:get, PagesController::HOST + PagesController::BASE_URL + "/not_found/").
to_return(status: 404, body: "", headers: {})

get "/not_found"
expect(response).to redirect_to("/dashboard")
end
end
3 changes: 2 additions & 1 deletion spec/system/logins_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
end

describe "Logged-out" do
it "web root page is accessible" do
xit "web root page is accessible" do
# Marking as pending as the root page is proxied pages content for now
visit "/"
expect(page).to(be_axe_clean)
end
Expand Down

0 comments on commit e1ce5be

Please sign in to comment.