Packages

HTTP telemetry dashboard for Elixir/Phoenix — monitor outbound & inbound requests with LiveView

Current section

Files

Jump to
monitorex lib monitorex consumer_identifier.ex
Raw

lib/monitorex/consumer_identifier.ex

defmodule Monitorex.ConsumerIdentifier do
@moduledoc """
Extracts a consumer label from an inbound `Plug.Conn` for per-consumer dashboard breakdowns.
## Priority order
1. **Custom function**`consumer_fn` in Application config (arity 1, receives `conn`)
2. **Basic-auth username** — decoded from `authorization` header, password discarded
3. **API key header** — value of `x-api-key` truncated to 8 characters
4. **`nil`** — unknown / anonymous consumer
"""
@doc """
Identifies the consumer from a `Plug.Conn`.
Returns `nil` when no consumer can be determined.
"""
@spec identify(conn :: Plug.Conn.t()) :: String.t() | nil
def identify(conn) do
# 1. Custom function from config (highest priority)
with nil <- custom_fn_result(conn) do
# 2. Basic-auth username
with nil <- extract_basic_auth_username(conn) do
# 3. x-api-key header (first 8 chars)
extract_api_key(conn)
end
end
# 4. nil is the implicit fallback from all three returning nil
end
# ── Custom function ──
defp custom_fn_result(conn) do
case Application.get_env(:monitorex, :consumer_fn) do
fun when is_function(fun, 1) -> fun.(conn)
_ -> nil
end
end
# ── Basic-Auth extraction ──
defp extract_basic_auth_username(conn) do
with auth_value when is_binary(auth_value) <-
Plug.Conn.get_req_header(conn, "authorization") |> List.first(),
"Basic " <> encoded <- auth_value,
decoded when is_binary(decoded) <- base64_decode(encoded),
[username | _] <- String.split(decoded, ":", parts: 2),
username when username != "" <- username do
username
else
_ -> nil
end
end
defp base64_decode(encoded) do
encoded = String.trim(encoded)
try do
case Base.decode64(encoded) do
{:ok, decoded} -> decoded
_ -> nil
end
rescue
_ -> nil
end
end
# ── API key extraction ──
defp extract_api_key(conn) do
case Plug.Conn.get_req_header(conn, "x-api-key") |> List.first() do
nil -> nil
key -> String.slice(key, 0, 8)
end
end
end