Skip to content

Commit

Permalink
replace geolix with locus
Browse files Browse the repository at this point in the history
  • Loading branch information
ruslandoga committed Oct 22, 2022
1 parent 93bb62e commit c279785
Show file tree
Hide file tree
Showing 14 changed files with 283 additions and 74 deletions.
2 changes: 2 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,6 @@ config :plausible, Plausible.Repo,
connect_timeout: 300_000,
handshake_timeout: 300_000

config :plausible, Plausible.Geo, adapter: Plausible.Geo.Locus

import_config "#{config_env()}.exs"
31 changes: 20 additions & 11 deletions config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ geolite2_country_db =

ip_geolocation_db = get_var_from_path_or_env(config_dir, "IP_GEOLOCATION_DB", geolite2_country_db)
geonames_source_file = get_var_from_path_or_env(config_dir, "GEONAMES_SOURCE_FILE")
maxmind_license_key = get_var_from_path_or_env(config_dir, "MAXMIND_LICENSE_KEY")
maxmind_edition = get_var_from_path_or_env(config_dir, "MAXMIND_EDITION")

disable_auth =
config_dir
Expand Down Expand Up @@ -430,17 +432,24 @@ config :kaffy,
]
]

if config_env() != :test do
config :geolix,
databases: [
%{
id: :geolocation,
adapter: Geolix.Adapter.MMDB2,
source: ip_geolocation_db,
result_as: :raw
}
]
end
geo_opts =
cond do
maxmind_license_key ->
[
license_key: maxmind_license_key,
edition: maxmind_edition
]

ip_geolocation_db ->
[path: ip_geolocation_db]

true ->
# adapter would fail on incorrect / missing opts anyway
# so leaving unhandled cases as empty opts
[]
end

config :plausible, Plausible.Geo, geo_opts

if geonames_source_file do
config :location, :geonames_source_file, geonames_source_file
Expand Down
39 changes: 2 additions & 37 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,43 +24,8 @@ config :plausible, :google,

config :bamboo, :refute_timeout, 10

geolix_sample_lookup = %{
city: %{geoname_id: 2_988_507, names: %{en: "Paris"}},
continent: %{code: "EU", geoname_id: 6_255_148, names: %{en: "Europe"}},
country: %{
geoname_id: 3_017_382,
is_in_european_union: true,
iso_code: "FR",
names: %{en: "France"}
},
ip_address: {2, 2, 2, 2},
location: %{
latitude: 48.8566,
longitude: 2.35222,
time_zone: "Europe/Paris",
weather_code: "FRXX0076"
},
postal: %{code: "75000"},
subdivisions: [
%{geoname_id: 3_012_874, iso_code: "IDF", names: %{en: "Île-de-France"}},
%{geoname_id: 2_968_815, iso_code: "75", names: %{en: "Paris"}}
]
}

config :geolix,
databases: [
%{
id: :geolocation,
adapter: Geolix.Adapter.Fake,
data: %{
{1, 1, 1, 1} => %{country: %{iso_code: "US"}},
{2, 2, 2, 2} => geolix_sample_lookup,
{1, 1, 1, 1, 1, 1, 1, 1} => %{country: %{iso_code: "US"}},
{0, 0, 0, 0} => %{country: %{iso_code: "ZZ"}}
}
}
]

config :plausible,
session_timeout: 0,
http_impl: Plausible.HTTPClient.Mock

config :plausible, Plausible.Geo, adapter: Plausible.Geo.Stub
6 changes: 6 additions & 0 deletions lib/plausible/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ defmodule Plausible.Application do
opts = [strategy: :one_for_one, name: Plausible.Supervisor]
setup_sentry()
setup_opentelemetry()
setup_geolocation()
Location.load_all()
Supervisor.start_link(children, opts)
end
Expand Down Expand Up @@ -116,4 +117,9 @@ defmodule Plausible.Application do
OpentelemetryEcto.setup([:plausible, :clickhouse_repo])
OpentelemetryOban.setup()
end

defp setup_geolocation do
opts = Application.fetch_env!(:plausible, Plausible.Geo)
:ok = Plausible.Geo.load_db(opts)
end
end
130 changes: 130 additions & 0 deletions lib/plausible/geo.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
defmodule Plausible.Geo do
@moduledoc "Geolocation functions"
@adapter Application.compile_env!(:plausible, [__MODULE__, :adapter])

@doc """
Looks up geo info about an ip address.
Example:
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
@adapter.lookup(ip_address)
end

@doc """
Starts the geodatabase loading process. Two options are supported, local file and maxmind key.
Loading a local file:
iex> load_db(path: "/etc/plausible/dbip-city.mmdb")
:ok
Loading 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
@adapter.load_db(opts)
end

@doc """
Returns geodatabase type. Used for deciding whether to show the DBIP disclaimer.
Example:
# in the case of a dbip db
iex> database_type()
"DBIP-City-Lite"
# in the case of a maxmind db
iex> database_type()
"GeoLite2-City"
"""
def database_type do
@adapter.database_type()
end
end
11 changes: 11 additions & 0 deletions lib/plausible/geo/adapter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
defmodule Plausible.Geo.Adapter do
@moduledoc "Behaviour to be implemented by geolocation adapters"

@type entry :: map
@type opts :: Keyword.t()
@type ip_address :: :inet.ip_address() | String.t()

@callback load_db(opts) :: :ok
@callback database_type :: String.t() | nil
@callback lookup(ip_address) :: entry | nil
end
48 changes: 48 additions & 0 deletions lib/plausible/geo/locus.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
defmodule Plausible.Geo.Locus do
@moduledoc false
@behaviour Plausible.Geo.Adapter
@db :geolocation

@impl true
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

@impl true
def database_type do
case :locus.get_info(@db, :metadata) do
{:ok, %{database_type: type}} -> type
_other -> nil
end
end

@impl true
def lookup(ip_address) do
case :locus.lookup(@db, ip_address) do
{:ok, entry} ->
entry

:not_found ->
nil

{:error, reason} ->
raise "failed to lookup ip address #{inspect(ip_address)}: " <> inspect(reason)
end
end
end
10 changes: 5 additions & 5 deletions lib/plausible/ingestion/event.ex
Original file line number Diff line number Diff line change
Expand Up @@ -203,18 +203,18 @@ defmodule Plausible.Ingestion.Event do
end

defp put_geolocation(%{} = event, %Request{} = request) do
result = Geolix.lookup(request.remote_ip, where: :geolocation)
result = Plausible.Geo.lookup(request.remote_ip)

country_code =
get_in(result, [:country, :iso_code])
get_in(result, ["country", "iso_code"])
|> ignore_unknown_country()

city_geoname_id = get_in(result, [:city, :geoname_id])
city_geoname_id = get_in(result, ["city", "geoname_id"])
city_geoname_id = Map.get(CityOverrides.get(), city_geoname_id, city_geoname_id)

subdivision1_code =
case result do
%{subdivisions: [%{iso_code: iso_code} | _rest]} ->
%{"subdivisions" => [%{"iso_code" => iso_code} | _rest]} ->
country_code <> "-" <> iso_code

_ ->
Expand All @@ -223,7 +223,7 @@ defmodule Plausible.Ingestion.Event do

subdivision2_code =
case result do
%{subdivisions: [_first, %{iso_code: iso_code} | _rest]} ->
%{"subdivisions" => [_first, %{"iso_code" => iso_code} | _rest]} ->
country_code <> "-" <> iso_code

_ ->
Expand Down
9 changes: 1 addition & 8 deletions lib/plausible_web/controllers/api/external_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,7 @@ defmodule PlausibleWeb.Api.ExternalController do
|> Keyword.take([:version, :commit, :created, :tags])
|> Map.new()

geo_database =
case Geolix.metadata(where: :geolocation) do
%{database_type: type} ->
type

_ ->
"(not configured)"
end
geo_database = Plausible.Geo.database_type() || "(not configured)"

json(conn, %{
geo_database: geo_database,
Expand Down
13 changes: 6 additions & 7 deletions lib/plausible_web/controllers/stats_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,13 @@ defmodule PlausibleWeb.StatsController do
end

defp is_dbip() do
if Application.get_env(:plausible, :is_selfhost) do
case Geolix.metadata(where: :geolocation) do
%{database_type: type} ->
is_or_nil =
if Application.get_env(:plausible, :is_selfhost) do
if type = Plausible.Geo.database_type() do
String.starts_with?(type, "DBIP")

_ ->
false
end
end
end

!!is_or_nil
end
end
3 changes: 1 addition & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ defmodule Plausible.MixProject do
{:floki, "~> 0.32.0", only: :test},
{:fun_with_flags, "~> 1.9.0"},
{:fun_with_flags_ui, "~> 0.8"},
{:geolix, "~> 2.0"},
{:geolix_adapter_mmdb2, "~> 0.6.0"},
{:locus, "~> 2.3"},
{:hackney, "~> 1.8"},
{:hammer, "~> 6.0"},
{:httpoison, "~> 1.4"},
Expand Down
Loading

0 comments on commit c279785

Please sign in to comment.