From d4201c6c154257440bbccd1690f9c42aea47d770 Mon Sep 17 00:00:00 2001 From: Ed Lepedus Date: Wed, 17 Jul 2024 14:32:42 +0100 Subject: [PATCH] Add configuration to serve public routes from a different base url path (#2704) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jonatan KÅ‚osko --- README.md | 5 +++ assets/js/hooks/js_view.js | 2 +- lib/livebook.ex | 5 +++ lib/livebook/config.ex | 11 +++++++ lib/livebook_web.ex | 33 +++++++++++++++++-- .../components/layouts/root.html.heex | 1 + 6 files changed, 54 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e6ebdd74e..82fb88137 100644 --- a/README.md +++ b/README.md @@ -196,6 +196,11 @@ The following environment variables can be used to configure Livebook on boot: * `LIVEBOOK_BASE_URL_PATH` - sets the base url path the web application is served on. Useful when deploying behind a reverse proxy. + * `LIVEBOOK_PUBLIC_BASE_URL_PATH` - sets the base url path the `/public/*` routes + are served on. Note that this takes precedence over `LIVEBOOK_BASE_URL_PATH`, + if both are set. Setting this may be useful to create exceptions when deploying + behind a reverse proxy that requires au1thentication. + * `LIVEBOOK_CACERTFILE` - path to a local file containing CA certificates. Those certificates are used during for server authentication when Livebook accesses files from external sources. diff --git a/assets/js/hooks/js_view.js b/assets/js/hooks/js_view.js index 8ffec257e..767314d6a 100644 --- a/assets/js/hooks/js_view.js +++ b/assets/js/hooks/js_view.js @@ -507,7 +507,7 @@ const JSView = { * once and the response is cached. */ function cachedPublicEndpointCheck() { - const healthUrl = window.LIVEBOOK_BASE_URL_PATH + "/public/health"; + const healthUrl = window.LIVEBOOK_PUBLIC_BASE_URL_PATH + "/public/health"; cachedPublicEndpointCheck.promise = cachedPublicEndpointCheck.promise || diff --git a/lib/livebook.ex b/lib/livebook.ex index 9b8639df0..5455ce503 100644 --- a/lib/livebook.ex +++ b/lib/livebook.ex @@ -114,6 +114,11 @@ defmodule Livebook do config :livebook, LivebookWeb.Endpoint, url: [path: base_url_path] end + if public_base_url_path = + Livebook.Config.base_url_path!("LIVEBOOK_PUBLIC_BASE_URL_PATH") do + config :livebook, :public_base_url_path, public_base_url_path + end + if password = Livebook.Config.password!("LIVEBOOK_PASSWORD") do config :livebook, :authentication, {:password, password} else diff --git a/lib/livebook/config.ex b/lib/livebook/config.ex index 036db32bb..616790fdf 100644 --- a/lib/livebook/config.ex +++ b/lib/livebook/config.ex @@ -226,6 +226,17 @@ defmodule Livebook.Config do String.trim_trailing(path, "/") end + @doc """ + Returns the base url path for Livebook public endpoints (health & assets) + """ + @spec public_base_url_path() :: String.t() + def public_base_url_path() do + case Application.get_env(:livebook, :public_base_url_path) do + nil -> base_url_path() + path -> String.trim_trailing(path, "/") + end + end + @doc """ Returns the configured port for the iframe endpoint. """ diff --git a/lib/livebook_web.ex b/lib/livebook_web.ex index 81df01cb9..7acaebde3 100644 --- a/lib/livebook_web.ex +++ b/lib/livebook_web.ex @@ -78,8 +78,10 @@ defmodule LivebookWeb do statics: LivebookWeb.static_paths() # We don't know the hostname Livebook runs on, so we don't use - # absolute URL helpers - import Phoenix.VerifiedRoutes, only: :sigils + # absolute URL helpers. We don't import sigil_p either, because + # we override it. + import Phoenix.VerifiedRoutes, only: [] + import LivebookWeb, only: [sigil_p: 2] end end @@ -89,4 +91,31 @@ defmodule LivebookWeb do defmacro __using__(which) when is_atom(which) do apply(__MODULE__, which, []) end + + # Overrides + require Phoenix.VerifiedRoutes + + defmacro sigil_p({:<<>>, _meta, ["/public/" <> _ | _]} = route, extra) do + # We allow configuring a base path for all routes and we configure + # Phoenix to use it. However, we have an additional configuration + # for base path applying only to /public. We use a custom sigil_p + # to insert this base path if needed. + quote do + path = Phoenix.VerifiedRoutes.sigil_p(unquote(route), unquote(extra)) + LivebookWeb.__rewrite_public_base_path__(path) + end + end + + defmacro sigil_p(route, extra) do + quote do + Phoenix.VerifiedRoutes.sigil_p(unquote(route), unquote(extra)) + end + end + + def __rewrite_public_base_path__(path) do + base_url_path = Livebook.Config.base_url_path() + public_base_url_path = Livebook.Config.public_base_url_path() + ^base_url_path <> rest = path + public_base_url_path <> rest + end end diff --git a/lib/livebook_web/components/layouts/root.html.heex b/lib/livebook_web/components/layouts/root.html.heex index e5adf0c39..9d155ec79 100644 --- a/lib/livebook_web/components/layouts/root.html.heex +++ b/lib/livebook_web/components/layouts/root.html.heex @@ -13,6 +13,7 @@ <%!-- This prevents the script to be loaded twice in Chrome --%>