Changes to eliminate @dialyzer suppressions by fixing underlying causes:
NIF stubs (towerops_native.ex, mib_translator.ex):
- Change stubs to :erlang.nif_error(:nif_not_loaded) (no_return type).
Real NIF replaces stubs at load time; calls to unloaded stubs now fail
loudly instead of returning fake data. Lets dialyzer trust @spec.
- Remove @dialyzer :nowarn_function on three NIFs and on translate/1.
Discovery sync_* functions (snmp/discovery.ex, channels/agent_channel.ex):
- agent_channel passes %{device_id: _, interfaces: _} and %{id: _} maps
into Discovery.sync_ip_addresses/sync_processors/sync_storage, which
@spec'd only %Device{}. Add narrow map-type unions (ip_sync_device,
snmp_device_ref) reflecting what the functions actually access.
- Remove @dialyzer :nowarn_function on three agent_channel helpers.
remote_ip.ex — real bug caught and fixed:
- `:ranch.get_addr(socket.transport_pid)` was always raising since
Bandit uses ThousandIsland, not Ranch; the rescue _ -> nil silently
returned nil every time. Switched to Phoenix's documented
:peer_data connect_info (already enabled in endpoint.ex) via
socket.assigns; remote IP now actually works.
- Remove remote_ip.ex entry from .dialyzer_ignore.exs.
Accounts / Organizations (Ecto.Multi opacity):
- Add @specs to Multi-building helpers, refactor into pipe chains.
- 6 @dialyzer :nowarn_function → 0, but 7 :no_opaque remain. Root
cause is upstream: Ecto.Multi.new/0 returns a struct with a literal
%MapSet{} whose @opaque internal representation trips dialyzer on
every subsequent Multi.* call. Unfixable without an Ecto patch or
bypassing Multi entirely. Comments document the specific upstream
issue rather than a vague "Ecto.Multi opacity" claim.
Devices.ex:
- Adding @specs made it worse (call_without_opaque → contract_with_
opaque); inlining the Multi didn't help either — same MapSet root
cause. Suppression kept with a sharper comment.
48 lines
1.3 KiB
Elixir
48 lines
1.3 KiB
Elixir
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
|
|
# Use peer_data stashed into socket assigns at connect time via
|
|
# `connect_info: [:peer_data]`. This is the documented Phoenix API
|
|
# and is adapter-agnostic (works with both Bandit and Cowboy).
|
|
case socket.assigns[:peer_data] do
|
|
%{address: ip} when is_tuple(ip) -> format(ip)
|
|
_ -> nil
|
|
end
|
|
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
|