Skip to content

Commit c5977fc

Browse files
committed
Migrate private docs from *.hexdocs.pm to *.hexorgs.pm
Serve private org docs on *.hexorgs.pm and 301 redirect old *.hexdocs.pm org URLs to the new host. Public docs on hexdocs.pm are unchanged. - Add private_host config for the new hexorgs.pm domain - Update subdomain matching to distinguish private_host from host - Add 301 redirect from old *.hexdocs.pm org requests - Update OAuth redirect URI and hexdocs_url to use private_host - Add hexorgs.pm to official domains in file rewriter
1 parent e74d130 commit c5977fc

7 files changed

Lines changed: 86 additions & 23 deletions

File tree

config/config.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ config :hexdocs,
2525
session_signing_salt: "QftsNdJO",
2626
session_encryption_salt: "QftsNdJO",
2727
host: "localhost",
28+
private_host: "localhost",
2829
gcs_put_debounce: 0,
2930
special_packages: %{
3031
"eex" => "elixir-lang/elixir",

config/runtime.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ if config_env() == :prod do
1919
queue_concurrency: String.to_integer(System.fetch_env!("HEXDOCS_QUEUE_CONCURRENCY")),
2020
github_user: System.fetch_env!("HEXDOCS_GITHUB_USER"),
2121
github_token: System.fetch_env!("HEXDOCS_GITHUB_TOKEN"),
22-
host: System.fetch_env!("HEXDOCS_HOST")
22+
host: System.fetch_env!("HEXDOCS_HOST"),
23+
private_host: System.fetch_env!("HEXDOCS_PRIVATE_HOST")
2324

2425
config :hexdocs, :repo_bucket, name: System.fetch_env!("HEXDOCS_REPO_BUCKET")
2526

config/test.exs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ config :hexdocs,
88
cdn_impl: Hexdocs.CDN.Local,
99
search_impl: Hexdocs.Search.Local,
1010
source_repo_impl: Hexdocs.SourceRepo.Mock,
11-
hex_repo_impl: Hexdocs.HexRepo.Mock
11+
hex_repo_impl: Hexdocs.HexRepo.Mock,
12+
private_host: "localhost"
1213

1314
config :logger, level: :warning

lib/hexdocs/file_rewriter.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ defmodule Hexdocs.FileRewriter do
99

1010
@noindex_hook ~s|<meta name="robots" content="noindex">|
1111

12-
@official_domains ~w(hex.pm hexdocs.pm elixir-lang.org erlang.org)
12+
@official_domains ~w(hex.pm hexdocs.pm hexorgs.pm elixir-lang.org erlang.org)
1313

1414
def run(path, content) do
1515
content

lib/hexdocs/plug.ex

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,25 +58,43 @@ defmodule Hexdocs.Plug do
5858
end
5959

6060
defp run(conn, _opts) do
61-
subdomain = subdomain(conn.host)
62-
63-
cond do
64-
!subdomain ->
61+
case subdomain(conn.host) do
62+
:error ->
6563
send_resp(conn, 400, "")
6664

67-
# OAuth callback - exchange code for tokens
68-
conn.request_path == "/oauth/callback" ->
69-
handle_oauth_callback(conn, subdomain)
65+
{:redirect, subdomain} ->
66+
redirect_to_private_host(conn, subdomain)
7067

71-
# OAuth access token in session
72-
access_token = get_session(conn, "access_token") ->
73-
try_serve_page_oauth(conn, subdomain, access_token)
68+
{:ok, subdomain} ->
69+
cond do
70+
# OAuth callback - exchange code for tokens
71+
conn.request_path == "/oauth/callback" ->
72+
handle_oauth_callback(conn, subdomain)
7473

75-
true ->
76-
redirect_oauth(conn, subdomain)
74+
# OAuth access token in session
75+
access_token = get_session(conn, "access_token") ->
76+
try_serve_page_oauth(conn, subdomain, access_token)
77+
78+
true ->
79+
redirect_oauth(conn, subdomain)
80+
end
7781
end
7882
end
7983

84+
defp redirect_to_private_host(conn, subdomain) do
85+
scheme = Application.get_env(:hexdocs, :scheme)
86+
host = Application.get_env(:hexdocs, :private_host)
87+
url = "#{scheme}://#{subdomain}.#{host}#{conn.request_path}"
88+
89+
html = Plug.HTML.html_escape(url)
90+
body = "<html><body>You are being <a href=\"#{html}\">redirected</a>.</body></html>"
91+
92+
conn
93+
|> put_resp_header("location", url)
94+
|> put_resp_header("content-type", "text/html")
95+
|> send_resp(301, body)
96+
end
97+
8098
defp redirect_oauth(conn, organization) do
8199
code_verifier = Hexdocs.OAuth.generate_code_verifier()
82100
code_challenge = Hexdocs.OAuth.generate_code_challenge(code_verifier)
@@ -103,7 +121,7 @@ defmodule Hexdocs.Plug do
103121

104122
defp build_oauth_redirect_uri(_conn, organization) do
105123
scheme = Application.get_env(:hexdocs, :scheme)
106-
host = Application.get_env(:hexdocs, :host)
124+
host = Application.get_env(:hexdocs, :private_host)
107125
"#{scheme}://#{organization}.#{host}/oauth/callback"
108126
end
109127

@@ -258,11 +276,13 @@ defmodule Hexdocs.Plug do
258276
end
259277

260278
defp subdomain(host) do
261-
app_host = Application.get_env(:hexdocs, :host)
279+
public_host = Application.get_env(:hexdocs, :host)
280+
private_host = Application.get_env(:hexdocs, :private_host)
262281

263282
case String.split(host, ".", parts: 2) do
264-
[subdomain, ^app_host] -> subdomain
265-
_ -> nil
283+
[subdomain, ^private_host] -> {:ok, subdomain}
284+
[subdomain, ^public_host] -> {:redirect, subdomain}
285+
_ -> :error
266286
end
267287
end
268288

lib/hexdocs/utils.ex

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,16 @@ defmodule Hexdocs.Utils do
55

66
def hexdocs_url(repository, path) do
77
"/" <> _ = path
8-
host = Application.get_env(:hexdocs, :host)
9-
scheme = if host == "hexdocs.pm", do: "https", else: "http"
10-
subdomain = if repository == "hexpm", do: "", else: "#{repository}."
11-
URI.encode("#{scheme}://#{subdomain}#{host}#{path}")
8+
9+
if repository == "hexpm" do
10+
host = Application.get_env(:hexdocs, :host)
11+
scheme = if host == "hexdocs.pm", do: "https", else: "http"
12+
URI.encode("#{scheme}://#{host}#{path}")
13+
else
14+
host = Application.get_env(:hexdocs, :private_host)
15+
scheme = if host in ["hexdocs.pm", "hexorgs.pm"], do: "https", else: "http"
16+
URI.encode("#{scheme}://#{repository}.#{host}#{path}")
17+
end
1218
end
1319

1420
def latest_version(versions) do

test/hexdocs/plug_test.exs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,40 @@ defmodule Hexdocs.PlugTest do
309309
end
310310
end
311311

312+
describe "redirect from public host to private host" do
313+
setup do
314+
original_host = Application.get_env(:hexdocs, :host)
315+
original_private_host = Application.get_env(:hexdocs, :private_host)
316+
Application.put_env(:hexdocs, :host, "hexdocs.test")
317+
Application.put_env(:hexdocs, :private_host, "hexorgs.test")
318+
319+
on_exit(fn ->
320+
Application.put_env(:hexdocs, :host, original_host)
321+
Application.put_env(:hexdocs, :private_host, original_private_host)
322+
end)
323+
end
324+
325+
test "301 redirects from *.hexdocs.test to *.hexorgs.test" do
326+
conn = conn(:get, "http://myorg.hexdocs.test:5002/my_package/index.html") |> call()
327+
assert conn.status == 301
328+
[location] = get_resp_header(conn, "location")
329+
assert location == "http://myorg.hexorgs.test/my_package/index.html"
330+
end
331+
332+
test "serves docs on private host" do
333+
conn = conn(:get, "http://myorg.hexorgs.test:5002/foo") |> call()
334+
assert conn.status == 302
335+
336+
[location] = get_resp_header(conn, "location")
337+
assert String.starts_with?(location, "http://localhost:5000/oauth/authorize?")
338+
end
339+
340+
test "returns 400 for unrecognized host" do
341+
conn = conn(:get, "http://other.example.com:5002/foo") |> call()
342+
assert conn.status == 400
343+
end
344+
end
345+
312346
defp call(conn) do
313347
Hexdocs.Plug.call(conn, [])
314348
end

0 commit comments

Comments
 (0)