aprs.me/lib/aprsme_web/plugs/ip_geolocation.ex
Graham McIntire b5d7cf4a38
Replace ip-api.com geolocation with Cloudflare headers
Use CF-IPLatitude/CF-IPLongitude headers instead of making HTTP calls
to ip-api.com on every page load. Eliminates ~500ms latency, external
API dependency, and flaky tests.

Also disable test server to prevent port conflicts during development.
2026-02-19 15:01:49 -06:00

36 lines
1.1 KiB
Elixir

defmodule AprsmeWeb.Plugs.IPGeolocation do
@moduledoc """
Plug that extracts geolocation from Cloudflare's CF-IPLatitude and CF-IPLongitude
headers on initial page load. Results are stored in the session to avoid repeated parsing.
"""
@behaviour Plug
import Plug.Conn
@impl true
def init(opts), do: opts
@impl true
def call(%{request_path: "/", method: "GET"} = conn, _opts) do
conn
|> get_session(:ip_geolocation)
|> handle_geolocation(conn)
end
def call(conn, _opts), do: conn
defp handle_geolocation(nil, conn), do: extract_cf_headers(conn)
defp handle_geolocation(_cached, conn), do: conn
defp extract_cf_headers(conn) do
with [lat_str] <- get_req_header(conn, "cf-iplatitude"),
[lng_str] <- get_req_header(conn, "cf-iplongitude"),
{lat, ""} <- Float.parse(lat_str),
{lng, ""} <- Float.parse(lng_str),
true <- lat >= -90 and lat <= 90 and lng >= -180 and lng <= 180 do
put_session(conn, :ip_geolocation, %{"lat" => lat, "lng" => lng})
else
_ -> conn
end
end
end