diff --git a/lib/beacon/igniter.ex b/lib/beacon/igniter.ex new file mode 100644 index 00000000..81ed161c --- /dev/null +++ b/lib/beacon/igniter.ex @@ -0,0 +1,36 @@ +defmodule Beacon.Igniter do + @moduledoc false + + def select_router!(igniter, opts \\ []) do + label = Keyword.get(opts, :label) + raise_msg = Keyword.get(opts, :raise_msg, "No router found.") + + case Igniter.Libs.Phoenix.select_router(igniter, label) do + {_igniter, nil} -> Mix.raise(raise_msg) + found -> found + end + end + + def select_endpoint(igniter, router, label \\ "Which endpoint should be modified?") do + case Igniter.Libs.Phoenix.endpoints_for_router(igniter, router) do + {igniter, []} -> + {igniter, nil} + + {igniter, [endpoint]} -> + {igniter, endpoint} + + {igniter, endpoints} -> + {igniter, Igniter.Util.IO.select(label, endpoints, display: &inspect/1)} + end + end + + def select_endpoint!(igniter, router, opts \\ []) do + label = Keyword.get(opts, :label) + raise_msg = Keyword.get(opts, :raise_msg, "No endpoint found.") + + case select_endpoint(igniter, router, label) do + {_igniter, nil} -> Mix.raise(raise_msg) + found -> found + end + end +end diff --git a/lib/mix/tasks/beacon.gen.proxy_endpoint.ex b/lib/mix/tasks/beacon.gen.proxy_endpoint.ex new file mode 100644 index 00000000..c7325b7e --- /dev/null +++ b/lib/mix/tasks/beacon.gen.proxy_endpoint.ex @@ -0,0 +1,113 @@ +defmodule Mix.Tasks.Beacon.Gen.ProxyEndpoint do + use Igniter.Mix.Task + + @example "mix beacon.gen.proxy_endpoint" + @shortdoc "TODO" + + @moduledoc """ + #{@shortdoc} + + ## Example + + ```bash + #{@example} + ``` + """ + + @doc false + def info(_argv, _composing_task) do + %Igniter.Mix.Task.Info{ + group: :beacon, + example: @example, + schema: [key: :string, signing_salt: :string, same_site: :string], + defaults: [same_site: "Lax"] + } + end + + @doc false + def igniter(igniter) do + otp_app = Igniter.Project.Application.app_name(igniter) + {igniter, router} = Beacon.Igniter.select_router!(igniter) + {igniter, fallback_endpoint} = Beacon.Igniter.select_endpoint(igniter, router, "Select a fallback endpoint (default app Endpoint):") + proxy_endpoint_module_name = Igniter.Libs.Phoenix.web_module_name(igniter, "ProxyEndpoint") + signing_salt = Keyword.get_lazy(igniter.args.options, :signing_salt, fn -> random_string(8) end) + + igniter + |> create_proxy_endpoint_module(otp_app, fallback_endpoint, proxy_endpoint_module_name) + |> add_session_options_config(otp_app, signing_salt, igniter.args.options) + |> add_proxy_endpoint_config(otp_app, proxy_endpoint_module_name, signing_salt) + |> update_fallback_endpoint_signing_salt(otp_app, fallback_endpoint, signing_salt) + |> Igniter.add_notice(""" + TODO + """) + end + + defp create_proxy_endpoint_module(igniter, otp_app, fallback_endpoint, proxy_endpoint_module_name) do + if Igniter.Project.Module.module_exists(igniter, proxy_endpoint_module_name) do + Igniter.add_notice(igniter, """ + Module #{inspect(proxy_endpoint_module_name)} already exists. Skipping. + """) + else + Igniter.Project.Module.create_module(igniter, proxy_endpoint_module_name, """ + use Beacon.ProxyEndpoint, + otp_app: #{inspect(otp_app)}, + session_options: Application.compile_env!(#{inspect(otp_app)}, :session_options), + fallback: #{inspect(fallback_endpoint)} + """) + end + end + + def add_session_options_config(igniter, otp_app, signing_salt, options) do + key = Keyword.get_lazy(options, :key, fn -> "_#{otp_app}_key" end) + same_site = Keyword.get(options, :same_site, "Lax") + + Igniter.Project.Config.configure( + igniter, + "config.exs", + otp_app, + [:session_options], + {:code, + Sourceror.parse_string!(""" + [ + store: :cookie, + key: "#{key}", + signing_salt: "#{signing_salt}", + same_site: "#{same_site}" + ] + """)} + ) + end + + def add_proxy_endpoint_config(igniter, otp_app, proxy_endpoint_module_name, signing_salt) do + igniter + |> Igniter.Project.Config.configure( + "config.exs", + otp_app, + [proxy_endpoint_module_name, :adapter], + {:code, Sourceror.parse_string!("Bandit.PhoenixAdapter")} + ) + |> Igniter.Project.Config.configure( + "config.exs", + otp_app, + [proxy_endpoint_module_name, :live_view, :signing_salt], + signing_salt + ) + end + + defp update_fallback_endpoint_signing_salt(igniter, otp_app, fallback_endpoint, signing_salt) do + fallback_endpoint = String.to_atom("#{fallback_endpoint}") + dbg(fallback_endpoint) + + Igniter.Project.Config.configure( + igniter, + "config.exs", + otp_app, + [fallback_endpoint, :live_view, :signing_salt], + signing_salt + ) + end + + # https://github.com/phoenixframework/phoenix/blob/c9b431f3a5d3bdc6a1d0ff3c29a229226e991195/installer/lib/phx_new/generator.ex#L451 + defp random_string(length), + do: :crypto.strong_rand_bytes(length) |> Base.encode64() |> binary_part(0, length) +end diff --git a/lib/mix/tasks/beacon.gen.site.ex b/lib/mix/tasks/beacon.gen.site.ex index 9def9681..1be5e7a5 100644 --- a/lib/mix/tasks/beacon.gen.site.ex +++ b/lib/mix/tasks/beacon.gen.site.ex @@ -44,8 +44,8 @@ defmodule Mix.Tasks.Beacon.Gen.Site do path = Keyword.fetch!(options, :path) validate_options!(site, path) - {igniter, router} = Igniter.Libs.Phoenix.select_router(igniter) - {igniter, [endpoint]} = Igniter.Libs.Phoenix.endpoints_for_router(igniter, router) + {igniter, router} = Beacon.Igniter.select_router!(igniter) + {igniter, endpoint} = Beacon.Igniter.select_endpoint!(igniter, router) repo = Igniter.Project.Module.module_name(igniter, "Repo") igniter diff --git a/lib/mix/tasks/beacon.gen.tailwind_config.ex b/lib/mix/tasks/beacon.gen.tailwind_config.ex index e28e42d9..ff1ba14c 100644 --- a/lib/mix/tasks/beacon.gen.tailwind_config.ex +++ b/lib/mix/tasks/beacon.gen.tailwind_config.ex @@ -35,8 +35,8 @@ defmodule Mix.Tasks.Beacon.Gen.TailwindConfig do site = Keyword.fetch!(options, :site) |> String.to_atom() app_name = Igniter.Project.Application.app_name(igniter) - {igniter, router} = Igniter.Libs.Phoenix.select_router(igniter) - {igniter, [endpoint]} = Igniter.Libs.Phoenix.endpoints_for_router(igniter, router) + {igniter, router} = Beacon.Igniter.select_router!(igniter) + {igniter, endpoint} = Beacon.Igniter.select_endpoint!(igniter, router) igniter |> create_tailwind_config() diff --git a/lib/mix/tasks/beacon.install.ex b/lib/mix/tasks/beacon.install.ex index 96abe454..963a3f46 100644 --- a/lib/mix/tasks/beacon.install.ex +++ b/lib/mix/tasks/beacon.install.ex @@ -49,7 +49,7 @@ defmodule Mix.Tasks.Beacon.Install do argv = igniter.args.argv options = igniter.args.options - {igniter, router} = Igniter.Libs.Phoenix.select_router(igniter) + {igniter, router} = Beacon.Igniter.select_router!(igniter) igniter |> add_beacon_plugin_formatter() @@ -64,7 +64,7 @@ defmodule Mix.Tasks.Beacon.Install do defp replace_error_html(igniter, router) do app_name = Igniter.Project.Application.app_name(igniter) - {igniter, [endpoint]} = Igniter.Libs.Phoenix.endpoints_for_router(igniter, router) + {igniter, endpoint} = Beacon.Igniter.select_endpoint!(igniter, router) Igniter.Project.Config.configure( igniter,