190 lines
4.8 KiB
Elixir
190 lines
4.8 KiB
Elixir
defmodule Plausible.Geo do
|
|
@moduledoc """
|
|
This module provides an API for fetching IP geolocation.
|
|
"""
|
|
|
|
require Logger
|
|
|
|
@db :geolocation
|
|
|
|
@doc """
|
|
Starts the geodatabase loading process. Two modes are supported: local file
|
|
and MaxMind license key.
|
|
|
|
## Options
|
|
|
|
* `:path` - the path to the .mmdb database local file. When present,
|
|
`:license_key` and `:edition` are not required.
|
|
|
|
* `:license_key` - the [license key](https://support.maxmind.com/hc/en-us/articles/4407111582235-Generate-a-License-Key)
|
|
from MaxMind to authenticate requests to MaxMind.
|
|
|
|
* `:edition` - the name of the MaxMind database to be downloaded from MaxMind
|
|
servers. Defaults to `GeoLite2-City`.
|
|
|
|
* `:async` - when used, configures the database loading to run
|
|
asynchronously.
|
|
|
|
## Examples
|
|
|
|
Loading from a local file:
|
|
|
|
iex> load_db(path: "/etc/plausible/dbip-city.mmdb")
|
|
:ok
|
|
|
|
Downloading a MaxMind DB (this license key is no longer active):
|
|
|
|
iex> load_db(license_key: "LNpsJCCKPis6XvBP", edition: "GeoLite2-City", async: true)
|
|
:ok
|
|
|
|
"""
|
|
def load_db(opts) do
|
|
cond do
|
|
license_key = opts[:license_key] ->
|
|
edition = opts[:edition] || "GeoLite2-City"
|
|
:ok = :locus.start_loader(@db, {:maxmind, edition}, license_key: license_key)
|
|
|
|
path = opts[:path] ->
|
|
:ok = :locus.start_loader(@db, path)
|
|
|
|
true ->
|
|
raise "failed to load geolocation db: need :path or :license_key to be provided"
|
|
end
|
|
|
|
unless opts[:async] do
|
|
{:ok, _version} = :locus.await_loader(@db)
|
|
end
|
|
|
|
:ok
|
|
end
|
|
|
|
@doc """
|
|
Waits for the database to start after calling `load_db/1` with the async option.
|
|
"""
|
|
def await_loader, do: :locus.await_loader(@db)
|
|
|
|
@doc """
|
|
Returns geodatabase type.
|
|
|
|
Used for deciding whether to show the DB-IP disclaimer or not.
|
|
|
|
## Examples
|
|
|
|
In the case of a DB-IP database:
|
|
|
|
iex> database_type()
|
|
"DBIP-City-Lite"
|
|
|
|
In the case of a MaxMind database:
|
|
|
|
iex> database_type()
|
|
"GeoLite2-City"
|
|
|
|
"""
|
|
def database_type do
|
|
case :locus.get_info(@db, :metadata) do
|
|
{:ok, %{database_type: type}} -> type
|
|
_other -> nil
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Looks up geo info about an IP address.
|
|
|
|
## Examples
|
|
|
|
iex> lookup("8.7.6.5")
|
|
%{
|
|
"city" => %{
|
|
"geoname_id" => 5349755,
|
|
"names" => %{
|
|
"de" => "Fontana",
|
|
"en" => "Fontana",
|
|
"ja" => "フォンタナ",
|
|
"ru" => "Фонтана"
|
|
}
|
|
},
|
|
"continent" => %{
|
|
"code" => "NA",
|
|
"geoname_id" => 6255149,
|
|
"names" => %{
|
|
"de" => "Nordamerika",
|
|
"en" => "North America",
|
|
"es" => "Norteamérica",
|
|
"fr" => "Amérique du Nord",
|
|
"ja" => "北アメリカ",
|
|
"pt-BR" => "América do Norte",
|
|
"ru" => "Северная Америка",
|
|
"zh-CN" => "北美洲"
|
|
}
|
|
},
|
|
"country" => %{
|
|
"geoname_id" => 6252001,
|
|
"iso_code" => "US",
|
|
"names" => %{
|
|
"de" => "Vereinigte Staaten",
|
|
"en" => "United States",
|
|
"es" => "Estados Unidos",
|
|
"fr" => "États Unis",
|
|
"ja" => "アメリカ",
|
|
"pt-BR" => "EUA",
|
|
"ru" => "США",
|
|
"zh-CN" => "美国"
|
|
}
|
|
},
|
|
"location" => %{
|
|
"accuracy_radius" => 50,
|
|
"latitude" => 34.1211,
|
|
"longitude" => -117.4362,
|
|
"metro_code" => 803,
|
|
"time_zone" => "America/Los_Angeles"
|
|
},
|
|
"postal" => %{"code" => "92336"},
|
|
"registered_country" => %{
|
|
"geoname_id" => 6252001,
|
|
"iso_code" => "US",
|
|
"names" => %{
|
|
"de" => "Vereinigte Staaten",
|
|
"en" => "United States",
|
|
"es" => "Estados Unidos",
|
|
"fr" => "États Unis",
|
|
"ja" => "アメリカ",
|
|
"pt-BR" => "EUA",
|
|
"ru" => "США",
|
|
"zh-CN" => "美国"
|
|
}
|
|
},
|
|
"subdivisions" => [
|
|
%{
|
|
"geoname_id" => 5332921,
|
|
"iso_code" => "CA",
|
|
"names" => %{
|
|
"de" => "Kalifornien",
|
|
"en" => "California",
|
|
"es" => "California",
|
|
"fr" => "Californie",
|
|
"ja" => "カリフォルニア州",
|
|
"pt-BR" => "Califórnia",
|
|
"ru" => "Калифорния",
|
|
"zh-CN" => "加州"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
"""
|
|
def lookup(ip_address) do
|
|
case :locus.lookup(@db, ip_address) do
|
|
{:ok, entry} ->
|
|
entry
|
|
|
|
:not_found ->
|
|
nil
|
|
|
|
{:error, reason} ->
|
|
Logger.error("failed to lookup ip address: " <> inspect(reason))
|
|
nil
|
|
end
|
|
end
|
|
end
|