defmodule PhoenixKit.Config.EndpointUrlSync do @moduledoc """ Syncs the `site_url` from PhoenixKit settings to the parent Phoenix Endpoint's `:url` configuration. This ensures `Endpoint.url()` returns the correct public URL, which is critical for OAuth callback URLs generated by Ueberauth. Opt-in via `config :phoenix_kit, sync_site_url_to_endpoint: true`. """ require Logger alias PhoenixKit.Config alias PhoenixKit.Settings @doc """ Syncs `site_url` from settings to the parent Endpoint's `:url` config. No-op when `sync_site_url_to_endpoint` is `false` (the default), when `site_url` is empty, or when the parent endpoint cannot be resolved. """ @spec sync() :: :ok def sync do unless Config.get_boolean(:sync_site_url_to_endpoint, false) do throw(:disabled) end site_url = Settings.get_setting_cached("site_url", "") if site_url == "" do Logger.debug("EndpointUrlSync: site_url is empty, skipping") throw(:empty) end uri = URI.parse(site_url) unless uri.scheme in ["http", "https"] and is_binary(uri.host) and uri.host != "" do Logger.warning("EndpointUrlSync: invalid site_url #{inspect(site_url)}, skipping") throw(:invalid) end case {Config.get_parent_app(), Config.get_parent_endpoint()} do {app, {:ok, endpoint}} when not is_nil(app) -> url_config = build_url_config(uri) current = Application.get_env(app, endpoint, []) updated = Keyword.put(current, :url, url_config) Application.put_env(app, endpoint, updated) Logger.info("EndpointUrlSync: set Endpoint URL to #{site_url}") _ -> Logger.warning("EndpointUrlSync: could not resolve parent endpoint, skipping") end :ok catch :throw, reason when reason in [:disabled, :empty, :invalid] -> :ok end defp build_url_config(uri) do config = [scheme: uri.scheme, host: uri.host] case {uri.scheme, uri.port} do {"http", port} when is_integer(port) and port != 80 -> config ++ [port: port] {"https", port} when is_integer(port) and port != 443 -> config ++ [port: port] _ -> config end end end