# `PhoenixKit.Modules.Sitemap.Sources.RouterDiscovery`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v2.2.0/lib/modules/sitemap/sources/router_discovery.ex#L1)

Router Discovery source for sitemap generation.

Automatically scans all GET routes from the parent application's router
and includes them in the sitemap. Routes can be filtered using exclude
patterns and include-only patterns.

## Settings

- `sitemap_router_discovery_enabled` - Enable/disable auto-discovery (default: true)
- `sitemap_router_discovery_exclude_patterns` - JSON array of regex patterns to exclude
- `sitemap_router_discovery_include_only` - JSON array of regex patterns for whitelist mode
- `sitemap_protected_pipelines` - JSON array of pipeline names that require authentication

## Pattern Syntax

Exclude and include-only patterns are **regular expressions** (compiled with
`Regex.compile/1`), not shell globs. A bare `"*"` is an invalid regex and is
ignored with a logged warning — use `".*"` to match everything or `"^/prefix"`
to match a path prefix. Invalid patterns never silently disable the source.

## Default Exclusions

Custom exclude patterns **add to** the built-in defaults; they do not replace
them. A host that saves one pattern of its own keeps every admin, auth and
infrastructure exclusion, and picks up new defaults from later PhoenixKit
versions. Saving `"!replace"` as the first entry opts into replacement.

By default, the following patterns are excluded:
- `^/admin` - Admin routes
- `^/api` - API endpoints
- `^/phoenix_kit` - PhoenixKit admin routes
- `^/dev` - Development routes
- `^/__` - Internal/technical routes (double-underscore convention, e.g.
  Publishing's internal dispatch scope)
- `^/maintenance$` - PhoenixKit's reserved maintenance page route
- `:[a-z_]+` - Routes with parameters
- `\*` - Wildcard routes

Additionally, routes using authentication pipelines are automatically excluded:
- `:phoenix_kit_require_authenticated` - Routes requiring user authentication
- `:phoenix_kit_admin_only` - Routes requiring admin/owner role
- `:authenticated` - Common name for authentication pipeline
- `:require_authenticated` - Alternative authentication pipeline name
- `:admin` - Common admin pipeline name
- `:admin_only` - Alternative admin pipeline name

Custom pipelines can be added via `sitemap_protected_pipelines` setting.

LiveView routes using authentication `on_mount` hooks are also excluded:
- `{PhoenixKitWeb.Users.Auth, :phoenix_kit_ensure_authenticated_scope}` - Ensures user is authenticated
- `{PhoenixKitWeb.Users.Auth, :phoenix_kit_redirect_if_authenticated_scope}` - Redirects if already authenticated

## Examples

    # Enable auto-discovery (default)
    Settings.update_boolean_setting("sitemap_router_discovery_enabled", true)

    # Custom exclude patterns (added to the defaults)
    Settings.update_setting("sitemap_router_discovery_exclude_patterns",
      JSON.encode!(["^/private"]))

    # Replace the defaults entirely (rarely what you want — this is how an
    # authenticated URL ends up in a search index)
    Settings.update_setting("sitemap_router_discovery_exclude_patterns",
      JSON.encode!(["!replace", "^/private"]))

    # Whitelist mode - only include specific paths
    Settings.update_setting("sitemap_router_discovery_include_only",
      JSON.encode!(["^/products", "^/categories"]))

    # Custom protected pipelines (add to defaults)
    Settings.update_setting("sitemap_protected_pipelines",
      JSON.encode!(["my_auth_pipeline", "member_only"]))

## Sitemap Properties

- Priority: 0.5 (default for discovered routes)
- Change frequency: weekly
- Category: "Routes"

# `default_exclude_patterns`

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

Returns the built-in default exclude patterns.

Custom patterns saved in `sitemap_router_discovery_exclude_patterns` **add
to** this list — matching how `sitemap_protected_pipelines` has always
behaved. A saved list used to replace these outright, which made the setting
a one-way door: adding a single pattern of your own silently un-excluded
every admin, auth and infrastructure route, and later PhoenixKit versions
could never ship a new default to that install.

To genuinely replace the defaults, save the special entry `"!replace"` as the
first pattern. That is deliberately awkward, because it is the option that
can publish an authenticated URL to a search engine.

# `default_protected_pipelines`

```elixir
@spec default_protected_pipelines() :: [atom()]
```

Returns the built-in default protected pipelines.

Unlike exclude patterns, `sitemap_protected_pipelines` only *adds* to this
list — these defaults always apply. Exposed so the settings UI can show
admins which pipelines are already protected without configuration.

# `effective_exclude_patterns`

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

The exclude patterns actually in force: the built-in defaults plus whatever
the host saved (or only the saved ones, behind the replace sentinel).

Public so the settings UI and `mix phoenix_kit.doctor` can show the same
answer collection uses, instead of each recomputing the merge.

# `invalid_patterns`

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

Returns the subset of `patterns` that fail to compile as regexes.

Mirrors the same `Regex.compile/1` check `compile_patterns/2` applies at
collection time, so a pattern accepted here is guaranteed not to be
silently dropped later. Used by the settings UI to reject invalid
exclude/include-only patterns before saving, instead of persisting them
and only discovering the problem in the logs.

## Examples

    iex> PhoenixKit.Modules.Sitemap.Sources.RouterDiscovery.invalid_patterns(["^/admin", "*"])
    ["*"]

# `replace_defaults_sentinel`

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

The sentinel that makes a saved exclude list replace the defaults instead of
extending them.

---

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