# `PhoenixKit.Utils.Routes`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.232/lib/phoenix_kit/utils/routes.ex#L1)

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.

# `admin_path`

Returns a locale-aware admin path. For non-primary locales the locale
segment is always emitted. For the primary locale the shape follows
the site-wide `default_language_no_prefix` setting
(`Languages.default_language_no_prefix?/0`): prefixless when the
setting is on, prefixed when off.

Both URL shapes resolve at the router level — the admin route macros
declare `/:locale/admin/*` AND `/admin/*` scopes — so either shape is
routable. The two shapes share one `live_session :phoenix_kit_admin`,
so locale switching across them stays on the WebSocket
(`push_navigate`) without a full-page reload.

## Examples

    iex> Routes.admin_path("/admin/users", "uk")
    "/phoenix_kit/uk/admin/users"

    iex> Routes.admin_path("/admin/users", nil)
    "/phoenix_kit/admin/users"

Primary-locale shape depends on the `default_language_no_prefix`
setting (not shown as doctests because the result varies with
runtime state):

    # setting OFF (default)
    Routes.admin_path("/admin/users", "en") #=> "/phoenix_kit/en/admin/users"

    # setting ON
    Routes.admin_path("/admin/users", "en") #=> "/phoenix_kit/admin/users"

# `auth_page?`

```elixir
@spec auth_page?(term()) :: boolean()
```

Whether a path lands on one of the sign-in pages (or `/users/log-out`).

Public because the `after_login_path` / `after_registration_path` changeset
applies the same rule when the setting is saved — one list, one predicate, so
a new auth route can't be guarded on read and forgotten on write.

Suffix-matched, since the real URL carries the host's mount prefix and an
optional locale segment (`/app/et/users/log-in`). A host page whose own path
happens to end in one of these segments is refused too — over-strict rather
than allowing a redirect loop.

## Examples

    iex> PhoenixKit.Utils.Routes.auth_page?("/users/log-in")
    true
    iex> PhoenixKit.Utils.Routes.auth_page?("/et/users/log-out/")
    true
    iex> PhoenixKit.Utils.Routes.auth_page?("/dashboard")
    false

# `base_url`

```elixir
@spec base_url() :: String.t()
```

The bare base URL (scheme + host, no trailing slash), same source `url/1` uses.

For absolutizing a path that is **already** url-prefixed and locale-prefixed
(e.g. a notification's `link`, built via `path/1`) — concatenate it onto this
directly. Do NOT pass such a path to `url/1`, which re-applies `path/1` and
would double-prefix it.

# `get_default_admin_locale`

Returns the default locale (base code) from the Languages module.

Extracts the base code from the default language (e.g., "en-US" becomes "en").
Falls back to "en" if no default language is configured.

## Examples

    iex> Routes.get_default_admin_locale()
    "en"

# `local_path?`

```elixir
@spec local_path?(term()) :: boolean()
```

Returns `true` when `path` is safe to use as a local redirect / `return_to`
target: a binary that begins with a single `/` but not `//` or `/\` — both of
which browsers resolve as protocol-relative, host-switching URLs. Use this to
guard user-supplied redirect params against open-redirect attacks.

ASCII control characters are rejected too. Browsers strip tab/CR/LF while
parsing a URL, so `"/\t/evil.example"` (from `?return_to=%2F%09%2Fevil.example`)
becomes `//evil.example` — a cross-origin navigation — once it reaches
`window.location`. `Phoenix.Controller.redirect/2` blocks those itself, but
LiveView's `validate_local_url!` only rejects `\\` and a leading `//`, so a
LiveView `redirect(socket, to: ...)` would otherwise pass it through.

## Examples

    iex> PhoenixKit.Utils.Routes.local_path?("/admin/dashboard")
    true
    iex> PhoenixKit.Utils.Routes.local_path?("//evil.com")
    false
    iex> PhoenixKit.Utils.Routes.local_path?("https://evil.com")
    false
    iex> PhoenixKit.Utils.Routes.local_path?("/\t/evil.com")
    false

# `locale_aware_path`

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).

# `path`

Builds a PhoenixKit path: the host's mount prefix, plus a locale segment when
the site is multilingual.

## The `:locale` option

This is the contract, and it is what a bilingual site needs when auth pages
come out in the wrong language. Without it the locale is *determined* — from
the process's Gettext locale — which is right for a link rendered inside a
request and wrong for a link built outside one (an email, a background job, a
script).

- `locale: "et"` — force this locale.
- `locale: :none` — emit no locale segment at all. For anything that is not a
  page: assets, webhooks, `sitemap.xml`.
- `locale: nil` or omitted — determine it from the current process.

### Examples

    Routes.path("/users/log-in")                 # current locale
    Routes.path("/users/log-in", locale: "et")   # /et/users/log-in
    Routes.path("/sitemap.xml", locale: :none)   # never localized

Note that the primary language is emitted prefixlessly when the site is
configured that way, so `locale: "en"` on an English-primary site yields a
path with no `/en` segment — that is deliberate, not a dropped option.

See the multilang guide for how locales are resolved and switched; the symptom
of getting this wrong (English auth pages on a translated site) reads like an
i18n bug rather than a routing one.

# `phoenix_kit_app_base`

```elixir
@spec phoenix_kit_app_base() :: String.t()
```

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"

# `post_auth_path`

```elixir
@spec post_auth_path([term()]) :: String.t()
```

Resolves where to send a user once they are signed in and confirmed.

Takes candidate destinations in priority order (e.g. a `?return_to=` param,
then the session's `user_return_to`) and returns the first that passes
`local_path?/1`. Falls back to the `after_login_path` setting, and finally
to `"/"`.

The setting is validated as a local path when saved, but is re-guarded here
so a hand-edited DB row can't turn a post-auth redirect into an open
redirect. Single source of truth for the post-auth landing page — used by
the login flow (`signed_in_path/1`) and by both confirmation LiveViews.

## Examples

    iex> PhoenixKit.Utils.Routes.post_auth_path(["/checkout"])
    "/checkout"
    iex> PhoenixKit.Utils.Routes.post_auth_path(["https://evil.com", nil])
    "/"
    iex> PhoenixKit.Utils.Routes.post_auth_path(["/users/log-out"])
    "/"

# `return_to_query`

```elixir
@spec return_to_query(term()) :: String.t()
```

Renders `?return_to=<path>` for a link, or `""` when there is nothing safe to
carry. Lets the auth pages hand the pending destination to each other instead
of dropping it the moment a visitor switches to another sign-in method.

# `url`

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.

# `url_prefix`

```elixir
@spec url_prefix() :: String.t()
```

Returns the configured PhoenixKit URL prefix.

## Examples

    iex> PhoenixKit.Utils.Routes.url_prefix()
    "/phoenix_kit"

---

*Consult [api-reference.md](api-reference.md) for complete listing*
