Packages

phoenix_kit

1.7.85
1.7.208 1.7.207 1.7.206 1.7.205 1.7.204 1.7.203 1.7.202 1.7.201 1.7.200 1.7.199 1.7.198 1.7.197 1.7.196 1.7.194 1.7.193 1.7.192 1.7.191 1.7.190 1.7.189 1.7.187 1.7.186 1.7.185 1.7.184 1.7.183 1.7.182 1.7.181 1.7.180 1.7.179 1.7.178 1.7.177 1.7.176 1.7.175 1.7.174 1.7.173 1.7.172 1.7.171 1.7.170 1.7.169 1.7.168 1.7.167 1.7.166 1.7.165 1.7.164 1.7.162 1.7.161 1.7.160 1.7.159 1.7.157 1.7.156 1.7.155 1.7.154 1.7.153 1.7.152 1.7.151 1.7.150 1.7.149 1.7.146 1.7.145 1.7.144 1.7.143 1.7.138 1.7.133 1.7.132 1.7.131 1.7.130 1.7.128 1.7.126 1.7.125 1.7.121 1.7.120 1.7.119 1.7.118 1.7.117 1.7.116 1.7.115 1.7.114 1.7.113 1.7.112 1.7.111 1.7.110 1.7.109 1.7.108 1.7.107 1.7.106 1.7.105 1.7.104 1.7.103 1.7.102 1.7.101 1.7.100 1.7.99 1.7.98 1.7.97 1.7.96 1.7.95 1.7.94 1.7.93 1.7.92 1.7.91 1.7.90 1.7.89 1.7.88 1.7.87 1.7.86 1.7.85 1.7.84 1.7.83 1.7.82 1.7.81 1.7.80 1.7.79 1.7.78 1.7.77 1.7.76 1.7.75 1.7.74 1.7.71 1.7.70 1.7.69 1.7.66 1.7.65 1.7.64 1.7.63 1.7.62 1.7.61 1.7.59 1.7.58 1.7.57 1.7.56 1.7.55 1.7.54 1.7.53 1.7.52 1.7.51 1.7.49 1.7.44 1.7.43 1.7.42 1.7.41 1.7.39 1.7.38 1.7.37 1.7.36 1.7.34 1.7.33 1.7.31 1.7.30 1.7.29 1.7.28 1.7.27 1.7.26 1.7.25 1.7.24 1.7.23 1.7.22 1.7.21 1.7.20 1.7.19 1.7.18 1.7.17 1.7.16 1.7.15 1.7.14 1.7.13 1.7.12 1.7.11 1.7.10 1.7.9 1.7.8 1.7.7 1.7.6 1.7.5 1.7.4 1.7.3 1.7.2 1.7.1 1.7.0 1.6.20 1.6.19 1.6.18 1.6.17 1.6.16 1.6.15 1.6.14 1.6.13 1.6.12 1.6.11 1.6.10 1.6.9 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.5.2 1.5.1 1.5.0 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.2 1.3.1 1.3.0 1.2.10 1.2.9 1.2.8 1.2.7 1.2.5 1.2.4 1.2.2 1.2.1 1.2.0 1.1.0 1.0.0

A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more

Current section

Files

Jump to
phoenix_kit lib phoenix_kit utils routes.ex
Raw

lib/phoenix_kit/utils/routes.ex

defmodule PhoenixKit.Utils.Routes do
@moduledoc """
Utility functions for working with PhoenixKit routes and URLs.
This module provides helpers for constructing URLs with the correct
PhoenixKit prefix configured in the application.
"""
alias PhoenixKit.Config
alias PhoenixKit.Modules.Languages.DialectMapper
@default_locale Config.default_locale()
# List of path prefixes that should NEVER have a locale added.
# This should be kept in sync with @reserved_segments in LocaleExtractor plug.
@reserved_prefixes ~w(/api /webhooks /assets /static /files /images /fonts /js /css /sitemap)
@doc """
Returns the configured PhoenixKit URL prefix.
## Examples
iex> PhoenixKit.Utils.Routes.url_prefix()
"/phoenix_kit"
"""
@spec url_prefix() :: String.t()
def url_prefix, do: Config.get_url_prefix()
# NOTE: Locale override logic below exists for the publishing component system integration.
# Switch to the upcoming media/storage helpers once they land.
def path(url_path, opts \\ [])
def path("/" <> _ = url_path, opts) do
url_prefix = PhoenixKit.Config.get_url_prefix()
base_path = if url_prefix === "/", do: "", else: url_prefix
cond do
# Admin paths ALWAYS get locale prefix to stay within the
# :phoenix_kit_admin_locale live_session and avoid full-page reloads.
admin_path?(url_path) ->
locale = resolve_locale(opts)
build_admin_path(base_path, url_path, locale)
# Reserved paths (API, webhooks, assets) NEVER get a locale prefix.
reserved_path?(url_path) ->
"#{base_path}#{url_path}"
true ->
build_localized_path(base_path, url_path, opts)
end
end
def path(_url_path, _opts) do
raise """
Url path must start with "/".
"""
end
defp build_localized_path(base_path, url_path, opts) do
locale = resolve_locale(opts)
build_path_with_locale(base_path, url_path, locale)
end
defp resolve_locale(opts) do
case Keyword.fetch(opts, :locale) do
{:ok, :none} -> :none
{:ok, nil} -> determine_locale()
{:ok, locale_value} -> locale_value
:error -> determine_locale()
end
end
defp build_path_with_locale(base_path, url_path, :none), do: "#{base_path}#{url_path}"
defp build_path_with_locale(base_path, url_path, locale_value) do
if default_locale?(locale_value) do
"#{base_path}#{url_path}"
else
"#{base_path}/#{locale_value}#{url_path}"
end
end
# Check if a path is an admin path.
defp admin_path?(url_path), do: String.starts_with?(url_path, "/admin")
# Admin paths ALWAYS include locale (even default locale) to match the
# :phoenix_kit_admin_locale live_session scope (/:locale/admin/*).
# This prevents live_session boundary crossings that cause full-page reloads.
defp build_admin_path(base_path, url_path, :none), do: "#{base_path}#{url_path}"
defp build_admin_path(base_path, url_path, locale) when is_binary(locale),
do: "#{base_path}/#{locale}#{url_path}"
defp build_admin_path(base_path, url_path, _), do: "#{base_path}#{url_path}"
# Check if a path starts with one of the reserved prefixes.
defp reserved_path?(path) do
Enum.any?(@reserved_prefixes, &String.starts_with?(path, &1))
end
defp determine_locale do
# Fall back to extracting base from Gettext locale
# This is only used when locale is not explicitly passed to Routes.path
# Gettext.get_locale/1 always returns a string
locale = Gettext.get_locale(PhoenixKitWeb.Gettext)
DialectMapper.extract_base(locale)
end
# Check if the given locale is the default (first in admin_languages list)
# Default locale doesn't need a prefix in URLs for cleaner URLs
defp default_locale?(locale) do
default = get_default_admin_language()
locale == default
end
defp get_default_admin_language do
# During mix tasks (like phoenix_kit.install), the database may not have
# the settings table yet. We detect this by checking if we're in a mix task
# context and fall back to "en" to avoid database errors.
if mix_task_context?() do
"en"
else
case PhoenixKit.Settings.get_json_setting_cached("admin_languages", [@default_locale]) do
nil ->
# No setting exists, default is "en"
"en"
[first | _] ->
# Extract base code from full dialect (e.g., "en-US" -> "en")
DialectMapper.extract_base(first)
_ ->
"en"
end
end
end
# Detect if we're running in a mix task context where the database
# may not be fully set up yet
defp mix_task_context? do
# Check if Mix is loaded and we're not in a running application context
# The settings cache being unavailable is a reliable indicator
case Process.get(:phoenix_kit_config_status) do
nil -> false
_ -> true
end
end
@doc """
Returns a locale-prefixed admin path, bypassing the reserved-path
locale stripping that `path/2` applies.
Admin routes use a `/:locale/admin/*` scope, so they need locale
in the URL even though `/admin` is a reserved prefix.
## Examples
iex> Routes.admin_path("/admin/users", "uk")
"/phoenix_kit/uk/admin/users"
iex> Routes.admin_path("/admin/users", nil)
"/phoenix_kit/admin/users"
"""
def admin_path(url_path, locale) when is_binary(locale) do
url_prefix = Config.get_url_prefix()
base_prefix = if url_prefix == "/", do: "", else: url_prefix
"#{base_prefix}/#{locale}#{url_path}"
end
def admin_path(url_path, _locale), do: path(url_path)
@doc """
Returns a locale-aware path using locale from assigns.
This function is specifically designed for use in component templates
where the locale needs to be passed explicitly via assigns.
Prefers base locale code for URL generation (current_locale_base),
falls back to extracting base from full dialect code (current_locale).
"""
def locale_aware_path(assigns, url_path) do
# Prefer base code, fall back to extracting from full dialect
locale =
assigns[:current_locale_base] ||
DialectMapper.extract_base(assigns[:current_locale] || @default_locale)
path(url_path, locale: locale)
end
@doc """
Returns the default admin locale (base code).
This is the first language in the admin_languages setting list,
extracted to its base code (e.g., "en-US" becomes "en").
Falls back to "en" if no admin languages are configured.
## Examples
iex> Routes.get_default_admin_locale()
"en" # or "ko" if Korean is first in admin_languages
## Use Case
This is used automatically in the `on_mount` hook to set `current_locale`
in socket assigns. LiveViews can then simply use:
locale = params["locale"] || socket.assigns[:current_locale]
"""
def get_default_admin_locale do
get_default_admin_language()
end
@doc """
Returns the path to the AI endpoints page.
"""
def ai_path do
path("/admin/ai")
end
@doc """
Returns a full url with preconfigured prefix.
This function first checks for a configured site URL in Settings,
then automatically detects the correct URL from the running Phoenix
application endpoint when possible, falling back to static configuration.
This ensures that magic links and other email links work correctly in both
development and production environments, with full control over the base URL
through the Settings admin panel.
"""
def url(url_path) do
base_url = get_base_url_for_emails()
full_path = path(url_path)
normalized_base = String.trim_trailing(base_url, "/")
"#{normalized_base}#{full_path}"
end
# Gets the base URL for email links.
#
# Priority:
# 1. site_url setting from Settings (if configured)
# 2. Dynamic URL from Phoenix endpoint
# 3. Static configuration fallback
#
# This allows administrators to override the email link URLs through
# the Settings panel, which is especially useful in production.
defp get_base_url_for_emails do
case PhoenixKit.Settings.get_setting("site_url", "") do
"" ->
PhoenixKit.Config.get_dynamic_base_url()
site_url when is_binary(site_url) ->
site_url
end
end
@doc """
Gets the base module name for the parent application.
Reads from :phoenix_kit, :layouts_module config (e.g., MprojectWeb.Layouts -> MprojectWeb).
## Examples
iex> PhoenixKit.Utils.Routes.phoenix_kit_app_base()
"MprojectWeb"
"""
@spec phoenix_kit_app_base() :: String.t()
def phoenix_kit_app_base do
case PhoenixKit.Config.get(:layouts_module) do
{:ok, module} when is_atom(module) ->
module
|> Module.split()
# Drop last segment (Layouts)
|> Enum.slice(0..-2//1)
|> Module.concat()
_ ->
"AppWeb"
end
end
end