defmodule ToweropsWeb.RemoteIp do @moduledoc """ Shared helpers for extracting and formatting remote IP addresses. """ import Plug.Conn, only: [get_req_header: 2] @spec from_conn(Plug.Conn.t()) :: String.t() | nil def from_conn(conn) do case get_req_header(conn, "x-forwarded-for") do [forwarded | _] -> forwarded |> String.split(",") |> List.first() |> String.trim() [] -> from_x_real_ip_or_tuple(conn) end end @spec from_socket(Phoenix.Socket.t()) :: String.t() | nil def from_socket(socket) do case socket.transport_pid do nil -> nil pid when is_pid(pid) -> case :ranch.get_addr(pid) do {ip, _port} when is_tuple(ip) -> format(ip) _ -> nil end end rescue _ -> nil end @spec format(tuple() | nil) :: String.t() | nil def format({a, b, c, d}), do: "#{a}.#{b}.#{c}.#{d}" def format({a, b, c, d, e, f, g, h}) do Enum.map_join([a, b, c, d, e, f, g, h], ":", &(&1 |> Integer.to_string(16) |> String.upcase())) end def format(_), do: nil defp from_x_real_ip_or_tuple(conn) do case get_req_header(conn, "x-real-ip") do [real_ip | _] -> real_ip [] -> format(conn.remote_ip) end end end