diff --git a/config/test.exs b/config/test.exs
index 1eb80b963..6afeb1d86 100644
--- a/config/test.exs
+++ b/config/test.exs
@@ -28,13 +28,15 @@ config :beacon, Beacon.BeaconTest.ProxyEndpoint,
url: [port: 80],
secret_key_base: "dVxFbSNspBVvkHPN5m6FE6iqNtMnhrmPNw7mO57CJ6beUADllH0ux3nhAI1ic65X",
live_view: [signing_salt: "LKBurgGF"],
- check_origin: false
+ check_origin: false,
+ render_errors: [formats: [html: Beacon.Web.ErrorHTML]],
+ debug_errors: true
config :beacon, Beacon.BeaconTest.Endpoint,
url: [host: "site_a.com", port: 4001],
secret_key_base: "dVxFbSNspBVvkHPN5m6FE6iqNtMnhrmPNw7mO57CJ6beUADllH0ux3nhAI1ic65X",
live_view: [signing_salt: "LKBurgGF"],
- render_errors: [view: Beacon.BeaconTest.ErrorView],
+ render_errors: [formats: [html: Beacon.Web.ErrorHTML]],
pubsub_server: Beacon.BeaconTest.PubSub,
check_origin: false,
debug_errors: true
@@ -43,7 +45,7 @@ config :beacon, Beacon.BeaconTest.EndpointB,
url: [host: "site_b.com", port: 4002],
secret_key_base: "dVxFbSNspBVvkHPN5m6FE6iqNtMnhrmPNw7mO57CJ6beUADllH0ux3nhAI1ic65X",
live_view: [signing_salt: "LKBurgGF"],
- render_errors: [view: Beacon.BeaconTest.ErrorView],
+ render_errors: [formats: [html: Beacon.Web.ErrorHTML]],
pubsub_server: Beacon.BeaconTest.PubSub,
check_origin: false,
debug_errors: true
diff --git a/lib/beacon/proxy_endpoint.ex b/lib/beacon/proxy_endpoint.ex
index e575510ac..ba1da8bbc 100644
--- a/lib/beacon/proxy_endpoint.ex
+++ b/lib/beacon/proxy_endpoint.ex
@@ -16,15 +16,59 @@ defmodule Beacon.ProxyEndpoint do
Module.put_attribute(__MODULE__, :__beacon_proxy_fallback__, fallback)
use Phoenix.Endpoint, otp_app: otp_app
+ import Plug.Conn, only: [put_resp_content_type: 2, put_resp_header: 3, halt: 1]
+ import Phoenix.Controller, only: [accepts: 2, put_view: 2, render: 3]
socket "/live", Phoenix.LiveView.Socket,
websocket: [connect_info: [session: @session_options]],
longpoll: [connect_info: [session: @session_options]]
+ plug :robots
+ plug :sitemap_index
plug :proxy
+ defp robots(%{path_info: ["robots.txt"]} = conn, _opts) do
+ sitemap_index_url =
+ String.Chars.URI.to_string(%URI{scheme: Atom.to_string(conn.scheme), host: conn.host, port: conn.port, path: "/sitemap_index.xml"})
+
+ conn
+ |> accepts(["txt"])
+ |> put_view(Beacon.Web.RobotsTxt)
+ |> put_resp_content_type("text/txt")
+ |> put_resp_header("cache-control", "public max-age=300")
+ |> render(:robots, sitemap_index_url: sitemap_index_url)
+ |> halt()
+ end
+
+ defp robots(conn, _opts), do: conn
+
+ defp sitemap_index(%{path_info: ["sitemap_index.xml"]} = conn, _opts) do
+ sites = Beacon.ProxyEndpoint.sites_per_host(conn.host)
+
+ conn
+ |> accepts(["xml"])
+ |> put_view(Beacon.Web.SitemapXML)
+ |> put_resp_content_type("text/xml")
+ |> put_resp_header("cache-control", "public max-age=300")
+ |> render(:sitemap_index, urls: get_sitemap_urls(sites))
+ |> halt()
+ end
+
+ defp sitemap_index(conn, _opts), do: conn
+
+ defp get_sitemap_urls(sites) do
+ sites
+ |> Enum.map(fn site ->
+ routes_module = Beacon.Loader.fetch_routes_module(site)
+ Beacon.apply_mfa(site, routes_module, :public_sitemap_url, [])
+ end)
+ |> Enum.reject(&is_nil/1)
+ |> Enum.sort()
+ |> Enum.uniq()
+ end
+
# TODO: cache endpoint resolver
- def proxy(%{host: host} = conn, opts) do
+ defp proxy(%{host: host} = conn, opts) do
matching_endpoint = fn ->
Enum.reduce_while(Beacon.Registry.running_sites(), @__beacon_proxy_fallback__, fn site, default ->
%{endpoint: endpoint} = Beacon.Config.fetch!(site)
@@ -136,4 +180,17 @@ defmodule Beacon.ProxyEndpoint do
defp port_to_integer({:system, env_var}), do: port_to_integer(System.get_env(env_var))
defp port_to_integer(port) when is_binary(port), do: String.to_integer(port)
defp port_to_integer(port) when is_integer(port), do: port
+
+ @doc false
+ def sites_per_host(host) when is_binary(host) do
+ Enum.reduce(Beacon.Registry.running_sites(), [], fn site, acc ->
+ %{endpoint: endpoint} = Beacon.Config.fetch!(site)
+
+ if endpoint.host() == host do
+ [site | acc]
+ else
+ acc
+ end
+ end)
+ end
end
diff --git a/lib/beacon/router.ex b/lib/beacon/router.ex
index 5284bbf81..8830d0ccd 100644
--- a/lib/beacon/router.ex
+++ b/lib/beacon/router.ex
@@ -91,7 +91,7 @@ defmodule Beacon.Router do
defp prelude do
quote do
Module.register_attribute(__MODULE__, :beacon_sites, accumulate: true)
- import Beacon.Router, only: [beacon_site: 2, beacon_sitemap_index: 1]
+ import Beacon.Router, only: [beacon_site: 2]
@before_compile unquote(__MODULE__)
end
end
@@ -118,7 +118,8 @@ defmodule Beacon.Router do
@doc """
Mounts a site in the `prefix` in your host application router.
- This will automatically serve `sitemap.xml` and `robots.txt` files from the `prefix` path defined for this site.
+ This will automatically serve `sitemap.xml` from the `prefix` path defined for this site,
+ and also `robots.txt` and `sitemap_index.xml` in the top-level host.
## Options
@@ -148,7 +149,6 @@ defmodule Beacon.Router do
get "/__beacon_assets__/js-:md5", Beacon.Web.AssetsController, :js, assigns: %{site: opts[:site]}
get "/sitemap.xml", Beacon.Web.SitemapController, :show, as: :beacon_sitemap, assigns: %{site: opts[:site]}
- get "/robots.txt", Beacon.Web.RobotsController, :show, as: :beacon_robots, assigns: %{site: opts[:site]}
# simulate a beacon page inside site prefix so we can check this site is reachable?/2
get "/__beacon_check__", Beacon.Web.CheckController, :check, metadata: %{site: opts[:site]}
@@ -161,67 +161,6 @@ defmodule Beacon.Router do
end
end
- @doc """
- Creates a sitemap index at the given path (including the filename and extension).
-
- ## Example
-
- defmodule MyApp.Router do
- ...
- scope "/" do
- pipe_through :browser
-
- beacon_sitemap_index "/sitemap_index.xml"
-
- beacon_site "/other", site: :other
- beacon_site "/", site: :home
- end
- end
-
- In the above example, there are two Beacon sites, so Beacon will serve two sitemaps:
- * `my_domain.com/sitemap.xml` for site `:home`
- * `my_domain.com/other/sitemap.xml` for site `:other`
-
- Then Beacon will reference both of those sitemaps in the top-level index:
- * `my_domain.com/sitemap_index.xml`
-
- Note that `beacon_sitemap_index` will include the sitemap URL of all mounted sites
- in the router, so that macro should be at the root and not duplicated.
-
- ## Requirements
-
- Note that your sitemap index cannot have a path which is "deeper" in the directory structure than
- your Beacon sites (which will be contained in the index).
-
- For example, the following is NOT allowed:
-
- scope "/" do
- ...
- beacon_sitemap_index "/root/nested/sitemap_index.xml"
-
- beacon_site "/root", site: :root
- end
-
- However, the opposite case (nesting the sites deeper than the index) is perfectly fine:
-
- scope "/" do
- ...
- beacon_sitemap_index "/sitemap_index.xml"
-
- beacon_site "/nested/path/to/site", site: :nested
- end
-
- """
- defmacro beacon_sitemap_index(path_with_filename) do
- quote bind_quoted: binding(), location: :keep, generated: true do
- import Phoenix.Router, only: [scope: 3, get: 4]
-
- scope "/", alias: false, as: false do
- get path_with_filename, Beacon.Web.SitemapController, :index, as: :beacon_sitemap
- end
- end
- end
-
@doc false
@spec __options__(keyword()) :: {atom(), atom(), keyword()}
def __options__(opts) do
diff --git a/lib/beacon/web/controllers/robots_controller.ex b/lib/beacon/web/controllers/robots_controller.ex
deleted file mode 100644
index 1d9c01b73..000000000
--- a/lib/beacon/web/controllers/robots_controller.ex
+++ /dev/null
@@ -1,20 +0,0 @@
-defmodule Beacon.Web.RobotsController do
- @moduledoc false
- use Beacon.Web, :controller
-
- def init(:show), do: :show
-
- def call(%{assigns: %{site: site}} = conn, :show) do
- conn
- |> accepts(["txt"])
- |> put_view(Beacon.Web.RobotsTxt)
- |> put_resp_content_type("text/txt")
- |> put_resp_header("cache-control", "public max-age=300")
- |> render(:robots, sitemap_url: get_sitemap_url(site))
- end
-
- defp get_sitemap_url(site) do
- routes_module = Beacon.Loader.fetch_routes_module(site)
- Beacon.apply_mfa(site, routes_module, :public_sitemap_url, [])
- end
-end
diff --git a/lib/beacon/web/controllers/sitemap_controller.ex b/lib/beacon/web/controllers/sitemap_controller.ex
index 067b532c0..44e538987 100644
--- a/lib/beacon/web/controllers/sitemap_controller.ex
+++ b/lib/beacon/web/controllers/sitemap_controller.ex
@@ -2,18 +2,7 @@ defmodule Beacon.Web.SitemapController do
@moduledoc false
use Beacon.Web, :controller
- def init(action) when action in [:index, :show], do: action
-
- def call(conn, :index) do
- sites = Beacon.Private.router(conn).__beacon_sites__()
-
- conn
- |> accepts(["xml"])
- |> put_view(Beacon.Web.SitemapXML)
- |> put_resp_content_type("text/xml")
- |> put_resp_header("cache-control", "public max-age=300")
- |> render(:sitemap_index, urls: get_sitemap_urls(sites))
- end
+ def init(action) when action in [:show], do: action
def call(%{assigns: %{site: site}} = conn, :show) do
conn
@@ -24,16 +13,6 @@ defmodule Beacon.Web.SitemapController do
|> render(:sitemap, pages: get_pages(site))
end
- defp get_sitemap_urls(sites) do
- sites
- |> Enum.map(fn {site, _} ->
- routes_module = Beacon.Loader.fetch_routes_module(site)
- Beacon.apply_mfa(site, routes_module, :public_sitemap_url, [])
- end)
- |> Enum.reject(&is_nil/1)
- |> Enum.sort()
- end
-
defp get_pages(site) do
routes_module = Beacon.Loader.fetch_routes_module(site)
diff --git a/lib/beacon/web/robots/robots.txt.eex b/lib/beacon/web/robots/robots.txt.eex
index 6a683c9f8..90faf079b 100644
--- a/lib/beacon/web/robots/robots.txt.eex
+++ b/lib/beacon/web/robots/robots.txt.eex
@@ -1,3 +1,3 @@
# http://www.robotstxt.org
User-agent: *
-Sitemap: <%= @sitemap_url %>
+Sitemap: <%= @sitemap_index_url %>
diff --git a/lib/beacon/web/sitemap/sitemap.xml.eex b/lib/beacon/web/sitemap/sitemap.xml.eex
index a1cff3610..cfbe721cd 100644
--- a/lib/beacon/web/sitemap/sitemap.xml.eex
+++ b/lib/beacon/web/sitemap/sitemap.xml.eex
@@ -1,7 +1,7 @@
- <%= for page <- @pages do %>
- <%= page.loc %>
- <%= page.lastmod %>
- <% end %>
+ <%= for page <- @pages do %>
+ <%= page.loc %>
+ <%= page.lastmod %>
+ <% end %>
diff --git a/test/beacon_web/controllers/robots_controller_test.exs b/test/beacon_web/controllers/robots_controller_test.exs
index 3fb4ff411..f90f40b5f 100644
--- a/test/beacon_web/controllers/robots_controller_test.exs
+++ b/test/beacon_web/controllers/robots_controller_test.exs
@@ -2,23 +2,12 @@ defmodule Beacon.Web.RobotsControllerTest do
use Beacon.Web.ConnCase, async: false
test "show", %{conn: conn} do
- conn = get(conn, "/robots.txt")
+ conn = get(%{conn | host: "site_a.com"}, "/robots.txt")
assert response(conn, 200) == """
# http://www.robotstxt.org
User-agent: *
- Sitemap: http://site_a.com/sitemap.xml
- """
-
- assert response_content_type(conn, :txt) =~ "charset=utf-8"
-
- # site: :not_booted
- conn = get(conn, "/other/robots.txt")
-
- assert response(conn, 200) == """
- # http://www.robotstxt.org
- User-agent: *
- Sitemap: http://site_a.com/other/sitemap.xml
+ Sitemap: http://site_a.com/sitemap_index.xml
"""
assert response_content_type(conn, :txt) =~ "charset=utf-8"
diff --git a/test/beacon_web/controllers/sitemap_controller_test.exs b/test/beacon_web/controllers/sitemap_controller_test.exs
index 8a08933b2..7b86b87e2 100644
--- a/test/beacon_web/controllers/sitemap_controller_test.exs
+++ b/test/beacon_web/controllers/sitemap_controller_test.exs
@@ -1,47 +1,46 @@
defmodule Beacon.Web.SitemapControllerTest do
use Beacon.Web.ConnCase, async: false
+ use Beacon.Test, site: :my_site
- setup do
- site = :my_site
-
- layout =
- beacon_published_layout_fixture(
- site: site,
- template: """
-
- <%= @inner_content %>
-
- """
- )
-
- page = beacon_published_page_fixture(site: site, path: "/foo", layout_id: layout.id)
-
- [site: site, layout: layout, page: page]
+ setup %{conn: conn} do
+ [
+ conn: %{conn | host: "site_a.com"},
+ page: beacon_published_page_fixture(site: default_site(), path: "/foo")
+ ]
end
- test "index only includes sitemap of mounted sites", %{conn: conn} do
- conn = get(conn, "/sitemap_index.xml")
-
- assert response(conn, 200) == """
-
-
-
- http://site_a.com/nested/media/sitemap.xml
-
-
- http://site_a.com/nested/site/sitemap.xml
-
-
- http://site_a.com/other/sitemap.xml
-
-
- http://site_a.com/sitemap.xml
-
-
- http://site_b.com/sitemap.xml
-
-
- """
+ describe "sitemap_index" do
+ test "only includes sitemap of sites in the same host", %{conn: conn} do
+ conn = get(conn, "/sitemap_index.xml")
+
+ assert response(conn, 200) == """
+
+
+
+ http://site_a.com/nested/media/sitemap.xml
+
+
+ http://site_a.com/nested/site/sitemap.xml
+
+
+ http://site_a.com/other/sitemap.xml
+
+
+ http://site_a.com/sitemap.xml
+
+
+ """
+ end
+
+ test "empty when no sites found", %{conn: conn} do
+ conn = get(%{conn | host: "other.com"}, "/sitemap_index.xml")
+
+ assert response(conn, 200) == """
+
+
+
+ """
+ end
end
test "show", %{conn: conn, page: page} do
@@ -50,10 +49,10 @@ defmodule Beacon.Web.SitemapControllerTest do
assert response(conn, 200) == """
-
- http://site_a.com/foo
- #{DateTime.to_iso8601(page.updated_at)}
-
+
+ http://site_a.com/foo
+ #{DateTime.to_iso8601(page.updated_at)}
+
"""
diff --git a/test/support/beacon_web.ex b/test/support/beacon_web.ex
index c9fd6fac2..23e73272c 100644
--- a/test/support/beacon_web.ex
+++ b/test/support/beacon_web.ex
@@ -43,9 +43,3 @@ end
defmodule Beacon.BeaconTest.LayoutView do
use Beacon.BeaconTest.Web, :view
end
-
-defmodule Beacon.BeaconTest.ErrorView do
- use Beacon.BeaconTest.Web, :view
-
- def render(_template, _assigns), do: "Error"
-end
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
index 7ab129afd..d34067f52 100644
--- a/test/support/conn_case.ex
+++ b/test/support/conn_case.ex
@@ -5,10 +5,8 @@ defmodule Beacon.Web.ConnCase do
using do
quote do
- # The default endpoint for testing
- @endpoint Beacon.BeaconTest.Endpoint
+ @endpoint Beacon.BeaconTest.ProxyEndpoint
- # Import conveniences for testing with connections
import Plug.Conn
import Phoenix.ConnTest
import Phoenix.LiveViewTest
diff --git a/test/support/routers.ex b/test/support/routers.ex
index 57fb22333..752143295 100644
--- a/test/support/routers.ex
+++ b/test/support/routers.ex
@@ -48,11 +48,6 @@ defmodule Beacon.BeaconTest.Router do
plug :put_secure_browser_headers
end
- scope "/" do
- pipe_through :browser
- beacon_sitemap_index "/sitemap_index.xml"
- end
-
scope host: "site_b.com" do
pipe_through :browser
beacon_site "/", site: :host_test
diff --git a/test/test_helper.exs b/test/test_helper.exs
index 33dc50548..2dd15c53c 100644
--- a/test/test_helper.exs
+++ b/test/test_helper.exs
@@ -5,9 +5,9 @@ Beacon.BeaconTest.Repo.start_link()
Supervisor.start_link(
[
{Phoenix.PubSub, name: Beacon.BeaconTest.PubSub},
+ Beacon.BeaconTest.ProxyEndpoint,
Beacon.BeaconTest.Endpoint,
Beacon.BeaconTest.EndpointB,
- Beacon.BeaconTest.ProxyEndpoint,
{Beacon,
sites: [
[