ip lookup on page load
This commit is contained in:
parent
7da91f5ccb
commit
4f0fa59be8
5 changed files with 373 additions and 132 deletions
|
|
@ -67,7 +67,7 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def mount(params, _session, socket) do
|
||||
def mount(params, session, socket) do
|
||||
require Logger
|
||||
|
||||
if connected?(socket) do
|
||||
|
|
@ -86,10 +86,27 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
one_hour_ago = TimeUtils.one_day_ago()
|
||||
|
||||
# Parse map state from URL parameters
|
||||
{map_center, map_zoom} = parse_map_params(params)
|
||||
{url_center, url_zoom} = parse_map_params(params)
|
||||
|
||||
Logger.debug("Parsed map params from URL: center=#{inspect(map_center)}, zoom=#{map_zoom}")
|
||||
Logger.debug("Raw params: #{inspect(params)}")
|
||||
# Check for IP geolocation in session
|
||||
{map_center, map_zoom} =
|
||||
case session["ip_geolocation"] do
|
||||
%{"lat" => lat, "lng" => lng} when is_number(lat) and is_number(lng) ->
|
||||
# Use IP geolocation if available and no URL params specified
|
||||
if params["lat"] || params["lng"] do
|
||||
# URL params take precedence
|
||||
{url_center, url_zoom}
|
||||
else
|
||||
# Use IP geolocation with closer zoom
|
||||
{%{lat: lat, lng: lng}, 12}
|
||||
end
|
||||
_ ->
|
||||
# No geolocation available, use URL params or defaults
|
||||
{url_center, url_zoom}
|
||||
end
|
||||
|
||||
Logger.debug("Final map params: center=#{inspect(map_center)}, zoom=#{map_zoom}")
|
||||
Logger.debug("Raw params: #{inspect(params)}, session geo: #{inspect(session["ip_geolocation"])}")
|
||||
|
||||
socket = assign_defaults(socket, one_hour_ago)
|
||||
|
||||
|
|
@ -102,8 +119,6 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
if connected?(socket) do
|
||||
Endpoint.subscribe("aprs_messages")
|
||||
Phoenix.PubSub.subscribe(Aprsme.PubSub, "postgres:aprsme_packets")
|
||||
# Defer IP geolocation to avoid blocking initial page load
|
||||
Process.send_after(self(), :start_geolocation, 2000)
|
||||
end
|
||||
|
||||
{:ok,
|
||||
|
|
@ -180,7 +195,6 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
packet_age_threshold: one_hour_ago,
|
||||
map_ready: false,
|
||||
historical_loaded: false,
|
||||
pending_geolocation: nil,
|
||||
bounds_update_timer: nil,
|
||||
pending_bounds: nil,
|
||||
initial_bounds_loaded: false,
|
||||
|
|
@ -290,15 +304,6 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
# The calculated bounds might be too small/inaccurate
|
||||
Logger.debug("Map ready - waiting for JavaScript to send actual bounds before loading historical packets")
|
||||
|
||||
# If we have pending geolocation, zoom to it now
|
||||
socket =
|
||||
if socket.assigns.pending_geolocation do
|
||||
%{lat: lat, lng: lng} = socket.assigns.pending_geolocation
|
||||
push_event(socket, "zoom_to_location", %{lat: lat, lng: lng, zoom: 12})
|
||||
else
|
||||
socket
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
|
|
@ -514,10 +519,6 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
@impl true
|
||||
def handle_info({:process_bounds_update, map_bounds}, socket), do: handle_info_process_bounds_update(map_bounds, socket)
|
||||
|
||||
def handle_info({:delayed_zoom, %{lat: lat, lng: lng}}, socket), do: handle_info_delayed_zoom(lat, lng, socket)
|
||||
|
||||
def handle_info({:ip_location, %{lat: lat, lng: lng}}, socket), do: handle_info_ip_location(lat, lng, socket)
|
||||
|
||||
def handle_info(:initialize_replay, socket), do: handle_info_initialize_replay(socket)
|
||||
|
||||
def handle_info(:cleanup_old_packets, socket), do: handle_cleanup_old_packets(socket)
|
||||
|
|
@ -526,8 +527,6 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
|
||||
def handle_info({:postgres_packet, packet}, socket), do: handle_info_postgres_packet(packet, socket)
|
||||
|
||||
def handle_info(:start_geolocation, socket), do: handle_info_start_geolocation(socket)
|
||||
|
||||
def handle_info({:load_historical_batch, batch_offset}, socket) do
|
||||
socket = load_historical_batch(socket, batch_offset)
|
||||
{:noreply, socket}
|
||||
|
|
@ -562,34 +561,6 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end
|
||||
end
|
||||
|
||||
defp handle_info_delayed_zoom(lat, lng, socket) do
|
||||
socket = push_event(socket, "zoom_to_location", %{lat: lat, lng: lng, zoom: 12})
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp handle_info_ip_location(lat, lng, socket) do
|
||||
lat_float =
|
||||
cond do
|
||||
is_binary(lat) -> String.to_float(lat)
|
||||
is_integer(lat) -> lat / 1.0
|
||||
true -> lat
|
||||
end
|
||||
|
||||
lng_float =
|
||||
cond do
|
||||
is_binary(lng) -> String.to_float(lng)
|
||||
is_integer(lng) -> lng / 1.0
|
||||
true -> lng
|
||||
end
|
||||
|
||||
# Schedule a delayed zoom to give the user a moment to see the map
|
||||
Process.send_after(self(), {:delayed_zoom, %{lat: lat_float, lng: lng_float}}, 500)
|
||||
|
||||
# We can still optimistically set the center and zoom
|
||||
socket = assign(socket, map_center: %{lat: lat_float, lng: lng_float}, map_zoom: 12)
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp handle_info_initialize_replay(socket) do
|
||||
if not socket.assigns.historical_loaded and socket.assigns.map_ready do
|
||||
# Only proceed if we have actual map bounds - don't use world bounds
|
||||
|
|
@ -694,52 +665,6 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp handle_info_start_geolocation(socket) do
|
||||
if geolocation_enabled?() do
|
||||
ip_for_geolocation =
|
||||
if Application.get_env(:aprsme, AprsmeWeb.Endpoint)[:code_reloader] do
|
||||
# For testing geolocation in dev environment, use a public IP address.
|
||||
# This will be geolocated to Mountain View, CA.
|
||||
"8.8.8.8"
|
||||
else
|
||||
extract_ip(socket)
|
||||
end
|
||||
|
||||
if valid_ip_for_geolocation?(ip_for_geolocation) do
|
||||
start_geolocation_task(ip_for_geolocation)
|
||||
end
|
||||
end
|
||||
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
defp geolocation_enabled? do
|
||||
Application.get_env(:aprsme, :disable_aprs_connection, false) != true
|
||||
end
|
||||
|
||||
defp extract_ip(socket) do
|
||||
case socket.private[:connect_info][:peer_data][:address] do
|
||||
{a, b, c, d} -> "#{a}.#{b}.#{c}.#{d}"
|
||||
{a, b, c, d, e, f, g, h} -> "#{a}:#{b}:#{c}:#{d}:#{e}:#{f}:#{g}:#{h}"
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
||||
defp valid_ip_for_geolocation?(ip) do
|
||||
ip && !String.starts_with?(ip, "127.") && !String.starts_with?(ip, "::1")
|
||||
end
|
||||
|
||||
defp start_geolocation_task(ip) do
|
||||
Task.start(fn ->
|
||||
try do
|
||||
get_ip_location(ip)
|
||||
rescue
|
||||
_error ->
|
||||
send(self(), {:ip_location, @default_center})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
# Handle replaying the next historical packet
|
||||
|
||||
@impl true
|
||||
|
|
@ -1715,42 +1640,6 @@ defmodule AprsmeWeb.MapLive.Index do
|
|||
end
|
||||
end
|
||||
|
||||
# Get location from IP using ip-api.com
|
||||
@spec get_ip_location(String.t() | nil) :: {float(), float()} | nil
|
||||
defp get_ip_location(nil), do: nil
|
||||
|
||||
defp get_ip_location(ip) do
|
||||
# Asynchronously fetch IP location
|
||||
case Req.get("https://ip-api.com/json/#{ip}") do
|
||||
{:ok, %Req.Response{status: 200, body: body}} -> handle_ip_api_response(body)
|
||||
{:ok, _response} -> send_default_ip_location()
|
||||
{:error, %{reason: :timeout}} -> send_default_ip_location()
|
||||
{:error, _error} -> send_default_ip_location()
|
||||
end
|
||||
end
|
||||
|
||||
defp handle_ip_api_response(body) do
|
||||
case Jason.decode(body) do
|
||||
{:ok, %{"status" => "success", "lat" => lat, "lon" => lng}}
|
||||
when is_number(lat) and is_number(lng) ->
|
||||
if lat >= -90 and lat <= 90 and lng >= -180 and lng <= 180 do
|
||||
lat_float = lat / 1.0
|
||||
lng_float = lng / 1.0
|
||||
send(self(), {:ip_location, %{lat: lat_float, lng: lng_float}})
|
||||
else
|
||||
send_default_ip_location()
|
||||
end
|
||||
|
||||
{:ok, _} ->
|
||||
send_default_ip_location()
|
||||
|
||||
{:error, _decode_error} ->
|
||||
send_default_ip_location()
|
||||
end
|
||||
end
|
||||
|
||||
defp send_default_ip_location, do: send(self(), {:ip_location, @default_center})
|
||||
|
||||
@impl true
|
||||
def terminate(_reason, socket) do
|
||||
if socket.assigns.buffer_timer, do: Process.cancel_timer(socket.assigns.buffer_timer)
|
||||
|
|
|
|||
133
lib/aprsme_web/plugs/ip_geolocation.ex
Normal file
133
lib/aprsme_web/plugs/ip_geolocation.ex
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
defmodule AprsmeWeb.Plugs.IPGeolocation do
|
||||
@moduledoc """
|
||||
Plug that performs IP-based geolocation on initial page load.
|
||||
Results are stored in the session to avoid repeated API calls.
|
||||
"""
|
||||
import Plug.Conn
|
||||
require Logger
|
||||
|
||||
@behaviour Plug
|
||||
|
||||
@impl true
|
||||
def init(opts), do: opts
|
||||
|
||||
@impl true
|
||||
def call(conn, _opts) do
|
||||
# Only run for the main map page
|
||||
if conn.request_path == "/" && conn.method == "GET" do
|
||||
case get_session(conn, :ip_geolocation) do
|
||||
nil ->
|
||||
# No cached geolocation, fetch it
|
||||
perform_geolocation(conn)
|
||||
|
||||
_cached ->
|
||||
# Already have geolocation in session
|
||||
conn
|
||||
end
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
defp perform_geolocation(conn) do
|
||||
ip = get_client_ip(conn)
|
||||
|
||||
if valid_ip_for_geolocation?(ip) do
|
||||
case fetch_ip_location(ip) do
|
||||
{:ok, location} ->
|
||||
put_session(conn, :ip_geolocation, location)
|
||||
|
||||
{:error, reason} ->
|
||||
Logger.debug("IP geolocation failed for #{ip}: #{inspect(reason)}")
|
||||
conn
|
||||
end
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
defp get_client_ip(conn) do
|
||||
# Check for forwarded IP first (when behind proxy/load balancer)
|
||||
forwarded_for = get_req_header(conn, "x-forwarded-for")
|
||||
|
||||
case forwarded_for do
|
||||
[forwarded | _] ->
|
||||
# Take the first IP from the X-Forwarded-For header
|
||||
forwarded
|
||||
|> String.split(",")
|
||||
|> List.first()
|
||||
|> String.trim()
|
||||
|
||||
[] ->
|
||||
# Fall back to remote_ip
|
||||
case conn.remote_ip do
|
||||
{a, b, c, d} -> "#{a}.#{b}.#{c}.#{d}"
|
||||
{a, b, c, d, e, f, g, h} -> "#{a}:#{b}:#{c}:#{d}:#{e}:#{f}:#{g}:#{h}"
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp valid_ip_for_geolocation?(ip) when is_binary(ip) do
|
||||
# Skip local/private IPs
|
||||
not (String.starts_with?(ip, "127.") or
|
||||
String.starts_with?(ip, "::1") or
|
||||
String.starts_with?(ip, "10.") or
|
||||
String.starts_with?(ip, "172.16.") or
|
||||
String.starts_with?(ip, "172.17.") or
|
||||
String.starts_with?(ip, "172.18.") or
|
||||
String.starts_with?(ip, "172.19.") or
|
||||
String.starts_with?(ip, "172.20.") or
|
||||
String.starts_with?(ip, "172.21.") or
|
||||
String.starts_with?(ip, "172.22.") or
|
||||
String.starts_with?(ip, "172.23.") or
|
||||
String.starts_with?(ip, "172.24.") or
|
||||
String.starts_with?(ip, "172.25.") or
|
||||
String.starts_with?(ip, "172.26.") or
|
||||
String.starts_with?(ip, "172.27.") or
|
||||
String.starts_with?(ip, "172.28.") or
|
||||
String.starts_with?(ip, "172.29.") or
|
||||
String.starts_with?(ip, "172.30.") or
|
||||
String.starts_with?(ip, "172.31.") or
|
||||
String.starts_with?(ip, "192.168."))
|
||||
end
|
||||
|
||||
defp valid_ip_for_geolocation?(_), do: false
|
||||
|
||||
defp fetch_ip_location(ip) do
|
||||
case Req.get("https://ip-api.com/json/#{ip}") do
|
||||
{:ok, %Req.Response{status: 200, body: body}} ->
|
||||
parse_ip_api_response(body)
|
||||
|
||||
{:ok, _response} ->
|
||||
{:error, :invalid_response}
|
||||
|
||||
{:error, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_ip_api_response(body) when is_map(body) do
|
||||
case body do
|
||||
%{"status" => "success", "lat" => lat, "lon" => lon}
|
||||
when is_number(lat) and is_number(lon) ->
|
||||
if lat >= -90 and lat <= 90 and lon >= -180 and lon <= 180 do
|
||||
{:ok, %{"lat" => lat / 1.0, "lng" => lon / 1.0}}
|
||||
else
|
||||
{:error, :invalid_coordinates}
|
||||
end
|
||||
|
||||
_data ->
|
||||
{:error, :api_failure}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_ip_api_response(body) when is_binary(body) do
|
||||
case Jason.decode(body) do
|
||||
{:ok, decoded} -> parse_ip_api_response(decoded)
|
||||
{:error, _} -> {:error, :json_decode_error}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_ip_api_response(_), do: {:error, :invalid_response}
|
||||
end
|
||||
|
|
@ -6,6 +6,7 @@ defmodule AprsmeWeb.Router do
|
|||
import Phoenix.LiveDashboard.Router
|
||||
|
||||
alias AprsmeWeb.Plugs.RateLimiter
|
||||
alias AprsmeWeb.Plugs.IPGeolocation
|
||||
|
||||
pipeline :browser do
|
||||
plug :accepts, ["html"]
|
||||
|
|
@ -16,6 +17,7 @@ defmodule AprsmeWeb.Router do
|
|||
plug :put_secure_browser_headers
|
||||
plug :fetch_current_user
|
||||
plug AprsmeWeb.Plugs.SetLocale
|
||||
plug IPGeolocation
|
||||
plug RateLimiter, scale: 60_000, limit: 200
|
||||
end
|
||||
|
||||
|
|
|
|||
85
test/aprsme_web/live/map_live/geolocation_test.exs
Normal file
85
test/aprsme_web/live/map_live/geolocation_test.exs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
defmodule AprsmeWeb.MapLive.GeolocationTest do
|
||||
use AprsmeWeb.ConnCase
|
||||
import Phoenix.LiveViewTest
|
||||
import Mox
|
||||
|
||||
setup :verify_on_exit!
|
||||
|
||||
describe "mount with IP geolocation" do
|
||||
test "uses IP geolocation when available in session", %{conn: conn} do
|
||||
# Add geolocation data to session
|
||||
conn =
|
||||
conn
|
||||
|> init_test_session(%{
|
||||
"ip_geolocation" => %{"lat" => 37.4056, "lng" => -122.0775}
|
||||
})
|
||||
|
||||
{:ok, _view, html} = live(conn, "/")
|
||||
|
||||
# Verify the map is initialized with the geolocation data
|
||||
assert html =~ "data-center"
|
||||
assert html =~ "37.4056"
|
||||
assert html =~ "-122.0775"
|
||||
assert html =~ "data-zoom=\"12\""
|
||||
end
|
||||
|
||||
test "uses default center when no geolocation in session", %{conn: conn} do
|
||||
{:ok, _view, html} = live(conn, "/")
|
||||
|
||||
# Verify default center is used
|
||||
assert html =~ "data-center"
|
||||
assert html =~ "39.8283"
|
||||
assert html =~ "-98.5795"
|
||||
assert html =~ "data-zoom=\"5\""
|
||||
end
|
||||
|
||||
test "URL parameters override IP geolocation", %{conn: conn} do
|
||||
# Add geolocation data to session
|
||||
conn =
|
||||
conn
|
||||
|> init_test_session(%{
|
||||
"ip_geolocation" => %{"lat" => 37.4056, "lng" => -122.0775}
|
||||
})
|
||||
|
||||
# Visit with URL parameters
|
||||
{:ok, _view, html} = live(conn, "/?lat=40.7128&lng=-74.0060&z=10")
|
||||
|
||||
# Verify URL params take precedence
|
||||
assert html =~ "40.7128"
|
||||
assert html =~ "-74.0060"
|
||||
assert html =~ "data-zoom=\"10\""
|
||||
end
|
||||
|
||||
test "handles invalid geolocation data gracefully", %{conn: conn} do
|
||||
# Add invalid geolocation data to session
|
||||
conn =
|
||||
conn
|
||||
|> init_test_session(%{
|
||||
"ip_geolocation" => %{"lat" => "invalid", "lng" => "invalid"}
|
||||
})
|
||||
|
||||
{:ok, _view, html} = live(conn, "/")
|
||||
|
||||
# Should fall back to defaults
|
||||
assert html =~ "39.8283"
|
||||
assert html =~ "-98.5795"
|
||||
assert html =~ "data-zoom=\"5\""
|
||||
end
|
||||
|
||||
test "handles missing lat/lng in geolocation data", %{conn: conn} do
|
||||
# Add incomplete geolocation data
|
||||
conn =
|
||||
conn
|
||||
|> init_test_session(%{
|
||||
"ip_geolocation" => %{"city" => "Mountain View"}
|
||||
})
|
||||
|
||||
{:ok, _view, html} = live(conn, "/")
|
||||
|
||||
# Should fall back to defaults
|
||||
assert html =~ "39.8283"
|
||||
assert html =~ "-98.5795"
|
||||
assert html =~ "data-zoom=\"5\""
|
||||
end
|
||||
end
|
||||
end
|
||||
132
test/aprsme_web/plugs/ip_geolocation_test.exs
Normal file
132
test/aprsme_web/plugs/ip_geolocation_test.exs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
defmodule AprsmeWeb.Plugs.IPGeolocationTest do
|
||||
use AprsmeWeb.ConnCase
|
||||
use ExVCR.Mock, adapter: ExVCR.Adapter.Hackney
|
||||
|
||||
alias AprsmeWeb.Plugs.IPGeolocation
|
||||
|
||||
@test_ip {204, 110, 191, 254} # Test IP address
|
||||
|
||||
describe "call/2" do
|
||||
test "adds geolocation data to session when successful", %{conn: conn} do
|
||||
use_cassette "ip_geolocation_success" do
|
||||
conn =
|
||||
conn
|
||||
|> Map.put(:remote_ip, @test_ip)
|
||||
|> IPGeolocation.call([])
|
||||
|
||||
geo = get_session(conn, :ip_geolocation)
|
||||
assert is_map(geo)
|
||||
assert is_number(geo["lat"])
|
||||
assert is_number(geo["lng"])
|
||||
# Test IP should geolocate to somewhere in the US
|
||||
assert geo["lat"] >= 24 and geo["lat"] <= 49
|
||||
assert geo["lng"] >= -125 and geo["lng"] <= -66
|
||||
end
|
||||
end
|
||||
|
||||
test "skips geolocation for local IP addresses", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> Map.put(:remote_ip, {127, 0, 0, 1})
|
||||
|> IPGeolocation.call([])
|
||||
|
||||
assert get_session(conn, :ip_geolocation) == nil
|
||||
end
|
||||
|
||||
test "handles API failure gracefully", %{conn: conn} do
|
||||
use_cassette "ip_geolocation_failure" do
|
||||
conn =
|
||||
conn
|
||||
|> Map.put(:remote_ip, {192, 0, 2, 1})
|
||||
|> IPGeolocation.call([])
|
||||
|
||||
assert get_session(conn, :ip_geolocation) == nil
|
||||
end
|
||||
end
|
||||
|
||||
test "validates latitude and longitude bounds", %{conn: conn} do
|
||||
use_cassette "ip_geolocation_invalid_coords" do
|
||||
conn =
|
||||
conn
|
||||
|> Map.put(:remote_ip, {192, 0, 2, 2})
|
||||
|> IPGeolocation.call([])
|
||||
|
||||
assert get_session(conn, :ip_geolocation) == nil
|
||||
end
|
||||
end
|
||||
|
||||
test "handles IPv6 addresses", %{conn: conn} do
|
||||
use_cassette "ip_geolocation_ipv6" do
|
||||
conn =
|
||||
conn
|
||||
|> Map.put(:remote_ip, {0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888})
|
||||
|> IPGeolocation.call([])
|
||||
|
||||
geo = get_session(conn, :ip_geolocation)
|
||||
assert is_map(geo)
|
||||
assert is_number(geo["lat"])
|
||||
assert is_number(geo["lng"])
|
||||
end
|
||||
end
|
||||
|
||||
test "uses forwarded IP when behind proxy", %{conn: conn} do
|
||||
use_cassette "ip_geolocation_forwarded" do
|
||||
conn =
|
||||
conn
|
||||
|> Map.put(:remote_ip, {10, 0, 0, 1})
|
||||
|> put_req_header("x-forwarded-for", "204.110.191.254, 10.0.0.1")
|
||||
|> IPGeolocation.call([])
|
||||
|
||||
geo = get_session(conn, :ip_geolocation)
|
||||
assert is_map(geo)
|
||||
assert is_number(geo["lat"])
|
||||
assert is_number(geo["lng"])
|
||||
end
|
||||
end
|
||||
|
||||
test "caches geolocation in session", %{conn: conn} do
|
||||
use_cassette "ip_geolocation_cached" do
|
||||
# First request should call API
|
||||
conn =
|
||||
conn
|
||||
|> Map.put(:remote_ip, @test_ip)
|
||||
|> IPGeolocation.call([])
|
||||
|> fetch_session()
|
||||
|
||||
geo1 = get_session(conn, :ip_geolocation)
|
||||
|
||||
# Second request with same session should not call API
|
||||
conn2 =
|
||||
conn
|
||||
|> recycle()
|
||||
|> Map.put(:remote_ip, @test_ip)
|
||||
|> IPGeolocation.call([])
|
||||
|
||||
geo2 = get_session(conn2, :ip_geolocation)
|
||||
|
||||
# Should return the same cached data
|
||||
assert geo2 == geo1
|
||||
end
|
||||
end
|
||||
|
||||
test "skips non-root paths", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> Map.put(:remote_ip, @test_ip)
|
||||
|> Map.put(:request_path, "/about")
|
||||
|> IPGeolocation.call([])
|
||||
|
||||
assert get_session(conn, :ip_geolocation) == nil
|
||||
end
|
||||
|
||||
test "skips non-GET requests", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> Map.put(:remote_ip, @test_ip)
|
||||
|> Map.put(:method, "POST")
|
||||
|> IPGeolocation.call([])
|
||||
|
||||
assert get_session(conn, :ip_geolocation) == nil
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue