Skip to content

Commit d9ceaf4

Browse files
authored
Redirect bare private host apex to hexpm_url (#118)
Hitting hexorgs.pm with no subdomain previously hit subdomain/1's default :error branch and returned 400. The natural UX is to land users on hex.pm instead. Same in staging — staging.hexorgs.pm redirects to https://staging.hex.pm. Depends on a Cloudflare DNS record being added for the hexorgs.pm apex to point at the GKE ingress so requests actually reach the app.
1 parent a71b23b commit d9ceaf4

2 files changed

Lines changed: 45 additions & 19 deletions

File tree

lib/hexdocs/plug.ex

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,41 @@ defmodule Hexdocs.Plug do
6767
end
6868

6969
defp run(conn, _opts) do
70-
case subdomain(conn.host) do
71-
:error ->
72-
send_resp(conn, 400, "")
73-
74-
{:ok, subdomain} ->
75-
cond do
76-
# OAuth callback - exchange code for tokens
77-
conn.request_path == "/oauth/callback" ->
78-
handle_oauth_callback(conn, subdomain)
79-
80-
# OAuth access token in session
81-
access_token = get_session(conn, "access_token") ->
82-
try_serve_page_oauth(conn, subdomain, access_token)
83-
84-
true ->
85-
redirect_oauth(conn, subdomain)
86-
end
70+
if conn.host == Application.get_env(:hexdocs, :private_host) do
71+
redirect_to_hexpm(conn)
72+
else
73+
case subdomain(conn.host) do
74+
:error ->
75+
send_resp(conn, 400, "")
76+
77+
{:ok, subdomain} ->
78+
cond do
79+
# OAuth callback - exchange code for tokens
80+
conn.request_path == "/oauth/callback" ->
81+
handle_oauth_callback(conn, subdomain)
82+
83+
# OAuth access token in session
84+
access_token = get_session(conn, "access_token") ->
85+
try_serve_page_oauth(conn, subdomain, access_token)
86+
87+
true ->
88+
redirect_oauth(conn, subdomain)
89+
end
90+
end
8791
end
8892
end
8993

94+
defp redirect_to_hexpm(conn) do
95+
url = Application.get_env(:hexdocs, :hexpm_url)
96+
html = Plug.HTML.html_escape(url)
97+
body = "<html><body>You are being <a href=\"#{html}\">redirected</a>.</body></html>"
98+
99+
conn
100+
|> put_resp_header("location", url)
101+
|> put_resp_header("content-type", "text/html")
102+
|> send_resp(301, body)
103+
end
104+
90105
defp redirect_oauth(conn, organization) do
91106
code_verifier = Hexdocs.OAuth.generate_code_verifier()
92107
code_challenge = Hexdocs.OAuth.generate_code_challenge(code_verifier)

test/hexdocs/plug_test.exs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ defmodule Hexdocs.PlugTest do
99

1010
@bucket :docs_private_bucket
1111

12-
test "requests without subdomain not supported" do
12+
test "bare private host apex redirects to hexpm_url" do
13+
# In the test env :host and :private_host are both "localhost", so a
14+
# bare-host request hits the private-host-apex redirect.
1315
conn = conn(:get, "http://localhost:5002/foo") |> call()
14-
assert conn.status == 400
16+
assert conn.status == 301
17+
[location] = get_resp_header(conn, "location")
18+
assert location == "http://localhost:5000"
1519
end
1620

1721
describe "OAuth flow" do
@@ -340,6 +344,13 @@ defmodule Hexdocs.PlugTest do
340344
conn = conn(:get, "http://phoenix.hexdocs.test:5002/index.html") |> call()
341345
assert conn.status == 400
342346
end
347+
348+
test "redirects bare private host apex to hexpm_url" do
349+
conn = conn(:get, "http://hexorgs.test:5002/") |> call()
350+
assert conn.status == 301
351+
[location] = get_resp_header(conn, "location")
352+
assert location == "http://localhost:5000"
353+
end
343354
end
344355

345356
test "sets security headers" do

0 commit comments

Comments
 (0)