mirror of
https://github.com/wanderer-industries/wanderer
synced 2025-12-11 18:26:04 +00:00
Add api for visible system kill information (#133)
* feat: api for zkill information
This commit is contained in:
63
lib/wanderer_app_web/controllers/common_api_controller.ex
Normal file
63
lib/wanderer_app_web/controllers/common_api_controller.ex
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
defmodule WandererAppWeb.CommonAPIController do
|
||||||
|
use WandererAppWeb, :controller
|
||||||
|
|
||||||
|
alias WandererApp.CachedInfo
|
||||||
|
alias WandererAppWeb.UtilAPIController, as: Util
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
GET /api/common/system_static?id=<solar_system_id>
|
||||||
|
|
||||||
|
Requires 'id' (the solar_system_id).
|
||||||
|
|
||||||
|
Example:
|
||||||
|
GET /api/common/system_static?id=31002229
|
||||||
|
"""
|
||||||
|
def show_system_static(conn, params) do
|
||||||
|
with {:ok, solar_system_str} <- Util.require_param(params, "id"),
|
||||||
|
{:ok, solar_system_id} <- Util.parse_int(solar_system_str) do
|
||||||
|
case CachedInfo.get_system_static_info(solar_system_id) do
|
||||||
|
{:ok, system} ->
|
||||||
|
data = static_system_to_json(system)
|
||||||
|
json(conn, %{data: data})
|
||||||
|
|
||||||
|
{:error, :not_found} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:not_found)
|
||||||
|
|> json(%{error: "System not found"})
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{:error, msg} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(%{error: msg})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# ----------------------------------------------
|
||||||
|
# Private helpers
|
||||||
|
# ----------------------------------------------
|
||||||
|
|
||||||
|
defp static_system_to_json(system) do
|
||||||
|
system
|
||||||
|
|> Map.take([
|
||||||
|
:solar_system_id,
|
||||||
|
:region_id,
|
||||||
|
:constellation_id,
|
||||||
|
:solar_system_name,
|
||||||
|
:solar_system_name_lc,
|
||||||
|
:constellation_name,
|
||||||
|
:region_name,
|
||||||
|
:system_class,
|
||||||
|
:security,
|
||||||
|
:type_description,
|
||||||
|
:class_title,
|
||||||
|
:is_shattered,
|
||||||
|
:effect_name,
|
||||||
|
:effect_power,
|
||||||
|
:statics,
|
||||||
|
:wandering,
|
||||||
|
:triglavian_invasion_status,
|
||||||
|
:sun_type_id
|
||||||
|
])
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,87 +1,48 @@
|
|||||||
defmodule WandererAppWeb.APIController do
|
defmodule WandererAppWeb.MapAPIController do
|
||||||
use WandererAppWeb, :controller
|
use WandererAppWeb, :controller
|
||||||
|
|
||||||
import Ash.Query, only: [filter: 2]
|
import Ash.Query, only: [filter: 2]
|
||||||
|
require Logger
|
||||||
|
|
||||||
alias WandererApp.Api
|
alias WandererApp.Api
|
||||||
|
alias WandererApp.Api.Character
|
||||||
alias WandererApp.MapSystemRepo
|
alias WandererApp.MapSystemRepo
|
||||||
alias WandererApp.MapCharacterSettingsRepo
|
alias WandererApp.MapCharacterSettingsRepo
|
||||||
alias WandererApp.Api.Character
|
|
||||||
alias WandererApp.CachedInfo
|
|
||||||
|
|
||||||
|
alias WandererApp.Zkb.KillsProvider.KillsCache
|
||||||
|
|
||||||
# -----------------------------------------------------------------
|
alias WandererAppWeb.UtilAPIController, as: Util
|
||||||
# Common
|
|
||||||
# -----------------------------------------------------------------
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
GET /api/system-static-info
|
|
||||||
|
|
||||||
Requires 'id' (the solar_system_id)
|
|
||||||
|
|
||||||
Example:
|
|
||||||
GET /api/common/system_static?id=31002229
|
|
||||||
GET /api/common/system_static?id=31002229
|
|
||||||
"""
|
|
||||||
def show_system_static(conn, params) do
|
|
||||||
with {:ok, solar_system_str} <- require_param(params, "id"),
|
|
||||||
{:ok, solar_system_id} <- parse_int(solar_system_str) do
|
|
||||||
case CachedInfo.get_system_static_info(solar_system_id) do
|
|
||||||
{:ok, system} ->
|
|
||||||
data = static_system_to_json(system)
|
|
||||||
json(conn, %{data: data})
|
|
||||||
|
|
||||||
{:error, :not_found} ->
|
|
||||||
conn
|
|
||||||
|> put_status(:not_found)
|
|
||||||
|> json(%{error: "System not found"})
|
|
||||||
end
|
|
||||||
else
|
|
||||||
{:error, msg} ->
|
|
||||||
conn
|
|
||||||
|> put_status(:bad_request)
|
|
||||||
|> json(%{error: msg})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
# MAP endpoints
|
||||||
|
# -----------------------------------------------------------------
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
GET /api/map/systems
|
GET /api/map/systems
|
||||||
|
|
||||||
Requires either `?map_id=<UUID>` **OR** `?slug=<map-slug>` in the query params.
|
Requires either `?map_id=<UUID>` **OR** `?slug=<map-slug>` in the query params.
|
||||||
|
|
||||||
If `?all=true` is provided, **all** systems are returned.
|
Only "visible" systems are returned.
|
||||||
Otherwise, only "visible" systems are returned.
|
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
GET /api/map/systems?map_id=466e922b-e758-485e-9b86-afae06b88363
|
GET /api/map/systems?map_id=466e922b-e758-485e-9b86-afae06b88363
|
||||||
GET /api/map/systems?slug=my-unique-wormhole-map
|
GET /api/map/systems?slug=my-unique-wormhole-map
|
||||||
GET /api/map/systems?map_id=<UUID>&all=true
|
|
||||||
"""
|
"""
|
||||||
def list_systems(conn, params) do
|
def list_systems(conn, params) do
|
||||||
with {:ok, map_id} <- fetch_map_id(params) do
|
with {:ok, map_id} <- Util.fetch_map_id(params),
|
||||||
repo_fun =
|
{:ok, systems} <- MapSystemRepo.get_visible_by_map(map_id) do
|
||||||
if params["all"] == "true" do
|
data = Enum.map(systems, &map_system_to_json/1)
|
||||||
&MapSystemRepo.get_all_by_map/1
|
json(conn, %{data: data})
|
||||||
else
|
|
||||||
&MapSystemRepo.get_visible_by_map/1
|
|
||||||
end
|
|
||||||
|
|
||||||
case repo_fun.(map_id) do
|
|
||||||
{:ok, systems} ->
|
|
||||||
data = Enum.map(systems, &map_system_to_json/1)
|
|
||||||
json(conn, %{data: data})
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
conn
|
|
||||||
|> put_status(:not_found)
|
|
||||||
|> json(%{error: "Could not fetch systems for map_id=#{map_id}: #{inspect(reason)}"})
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
{:error, msg} ->
|
{:error, msg} when is_binary(msg) ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(%{error: msg})
|
|> json(%{error: msg})
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:not_found)
|
||||||
|
|> json(%{error: "Could not fetch systems: #{inspect(reason)}"})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -96,29 +57,30 @@ defmodule WandererAppWeb.APIController do
|
|||||||
GET /api/map/system?id=31002229&slug=my-unique-wormhole-map
|
GET /api/map/system?id=31002229&slug=my-unique-wormhole-map
|
||||||
"""
|
"""
|
||||||
def show_system(conn, params) do
|
def show_system(conn, params) do
|
||||||
with {:ok, solar_system_str} <- require_param(params, "id"),
|
with {:ok, solar_system_str} <- Util.require_param(params, "id"),
|
||||||
{:ok, solar_system_id} <- parse_int(solar_system_str),
|
{:ok, solar_system_id} <- Util.parse_int(solar_system_str),
|
||||||
{:ok, map_id} <- fetch_map_id(params) do
|
{:ok, map_id} <- Util.fetch_map_id(params),
|
||||||
case MapSystemRepo.get_by_map_and_solar_system_id(map_id, solar_system_id) do
|
{:ok, system} <- MapSystemRepo.get_by_map_and_solar_system_id(map_id, solar_system_id) do
|
||||||
{:ok, system} ->
|
data = map_system_to_json(system)
|
||||||
data = map_system_to_json(system)
|
json(conn, %{data: data})
|
||||||
json(conn, %{data: data})
|
|
||||||
|
|
||||||
{:error, :not_found} ->
|
|
||||||
conn
|
|
||||||
|> put_status(:not_found)
|
|
||||||
|> json(%{error: "System not found in map=#{map_id}"})
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
{:error, msg} ->
|
{:error, msg} when is_binary(msg) ->
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|> json(%{error: msg})
|
|> json(%{error: msg})
|
||||||
|
|
||||||
|
{:error, :not_found} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:not_found)
|
||||||
|
|> json(%{error: "System not found"})
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:internal_server_error)
|
||||||
|
|> json(%{error: "Could not load system: #{inspect(reason)}"})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
GET /api/map/tracked_characters_with_info
|
GET /api/map/tracked_characters_with_info
|
||||||
|
|
||||||
@@ -129,11 +91,9 @@ defmodule WandererAppWeb.APIController do
|
|||||||
Returns a list of tracked records, plus their fully-loaded `character` data.
|
Returns a list of tracked records, plus their fully-loaded `character` data.
|
||||||
"""
|
"""
|
||||||
def tracked_characters_with_info(conn, params) do
|
def tracked_characters_with_info(conn, params) do
|
||||||
with {:ok, map_id} <- fetch_map_id(params),
|
with {:ok, map_id} <- Util.fetch_map_id(params),
|
||||||
{:ok, settings_list} <- get_tracked_by_map_ids(map_id),
|
{:ok, settings_list} <- get_tracked_by_map_ids(map_id),
|
||||||
{:ok, char_list} <-
|
{:ok, char_list} <- read_characters_by_ids_wrapper(Enum.map(settings_list, & &1.character_id)) do
|
||||||
read_characters_by_ids_wrapper(Enum.map(settings_list, & &1.character_id)) do
|
|
||||||
|
|
||||||
chars_by_id = Map.new(char_list, &{&1.id, &1})
|
chars_by_id = Map.new(char_list, &{&1.id, &1})
|
||||||
|
|
||||||
data =
|
data =
|
||||||
@@ -175,8 +135,24 @@ defmodule WandererAppWeb.APIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
GET /api/map/structure_timers
|
||||||
|
|
||||||
|
Returns structure timers for visible systems on the map
|
||||||
|
or for a specific system if `system_id` is specified.
|
||||||
|
|
||||||
|
**Example usage**:
|
||||||
|
- All visible systems:
|
||||||
|
```
|
||||||
|
GET /api/map/structure_timers?map_id=<uuid>
|
||||||
|
```
|
||||||
|
- For a single system:
|
||||||
|
```
|
||||||
|
GET /api/map/structure_timers?map_id=<uuid>&system_id=31002229
|
||||||
|
```
|
||||||
|
"""
|
||||||
def show_structure_timers(conn, params) do
|
def show_structure_timers(conn, params) do
|
||||||
with {:ok, map_id} <- fetch_map_id(params) do
|
with {:ok, map_id} <- Util.fetch_map_id(params) do
|
||||||
system_id_str = params["system_id"]
|
system_id_str = params["system_id"]
|
||||||
|
|
||||||
case system_id_str do
|
case system_id_str do
|
||||||
@@ -184,7 +160,7 @@ defmodule WandererAppWeb.APIController do
|
|||||||
handle_all_structure_timers(conn, map_id)
|
handle_all_structure_timers(conn, map_id)
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
case parse_int(system_id_str) do
|
case Util.parse_int(system_id_str) do
|
||||||
{:ok, system_id} ->
|
{:ok, system_id} ->
|
||||||
handle_single_structure_timers(conn, map_id, system_id)
|
handle_single_structure_timers(conn, map_id, system_id)
|
||||||
|
|
||||||
@@ -202,6 +178,102 @@ defmodule WandererAppWeb.APIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
GET /api/map/systems_kills
|
||||||
|
|
||||||
|
Returns kills data for all *visible* systems on the map.
|
||||||
|
|
||||||
|
Requires either `?map_id=<UUID>` or `?slug=<map-slug>`.
|
||||||
|
Optional hours_ago
|
||||||
|
|
||||||
|
Example:
|
||||||
|
GET /api/map/systems_kills?map_id=<uuid>
|
||||||
|
GET /api/map/systems_kills?slug=<map-slug>
|
||||||
|
GET /api/map/systems_kills?map_id=<uuid>&hour_ago=<somehours>
|
||||||
|
|
||||||
|
"""
|
||||||
|
def list_systems_kills(conn, params) do
|
||||||
|
with {:ok, map_id} <- Util.fetch_map_id(params),
|
||||||
|
# fetch visible systems from the repo
|
||||||
|
{:ok, systems} <- MapSystemRepo.get_visible_by_map(map_id) do
|
||||||
|
|
||||||
|
Logger.debug(fn -> "[list_systems_kills] Found #{length(systems)} visible systems for map_id=#{map_id}" end)
|
||||||
|
|
||||||
|
# Parse the hours_ago param
|
||||||
|
hours_ago = parse_hours_ago(params["hours_ago"])
|
||||||
|
|
||||||
|
# Gather system IDs
|
||||||
|
solar_ids = Enum.map(systems, & &1.solar_system_id)
|
||||||
|
|
||||||
|
# Fetch kills for each system from the cache
|
||||||
|
kills_map = KillsCache.fetch_cached_kills_for_systems(solar_ids)
|
||||||
|
|
||||||
|
# Build final JSON data
|
||||||
|
data =
|
||||||
|
Enum.map(systems, fn sys ->
|
||||||
|
kills = Map.get(kills_map, sys.solar_system_id, [])
|
||||||
|
|
||||||
|
# Filter out kills older than hours_ago
|
||||||
|
filtered_kills = maybe_filter_kills_by_time(kills, hours_ago)
|
||||||
|
|
||||||
|
Logger.debug(fn -> "
|
||||||
|
[list_systems_kills] For system_id=#{sys.solar_system_id},
|
||||||
|
found #{length(kills)} kills total,
|
||||||
|
returning #{length(filtered_kills)} kills after hours_ago filter
|
||||||
|
" end)
|
||||||
|
|
||||||
|
%{
|
||||||
|
solar_system_id: sys.solar_system_id,
|
||||||
|
kills: filtered_kills
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|
||||||
|
json(conn, %{data: data})
|
||||||
|
else
|
||||||
|
{:error, msg} when is_binary(msg) ->
|
||||||
|
Logger.warn("[list_systems_kills] Bad request: #{msg}")
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(%{error: msg})
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
Logger.error("[list_systems_kills] Could not fetch systems: #{inspect(reason)}")
|
||||||
|
conn
|
||||||
|
|> put_status(:not_found)
|
||||||
|
|> json(%{error: "Could not fetch systems: #{inspect(reason)}"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# If hours_str is present and valid, parse it. Otherwise return nil (no filter).
|
||||||
|
defp parse_hours_ago(nil), do: nil
|
||||||
|
defp parse_hours_ago(hours_str) do
|
||||||
|
case Integer.parse(hours_str) do
|
||||||
|
{num, ""} when num > 0 -> num
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_filter_kills_by_time(kills, hours_ago) when is_integer(hours_ago) do
|
||||||
|
cutoff = DateTime.utc_now() |> DateTime.add(-hours_ago * 3600, :second)
|
||||||
|
|
||||||
|
Enum.filter(kills, fn kill ->
|
||||||
|
kill_time = kill["kill_time"]
|
||||||
|
|
||||||
|
case kill_time do
|
||||||
|
%DateTime{} = dt ->
|
||||||
|
# Keep kills that occurred after the cutoff
|
||||||
|
DateTime.compare(dt, cutoff) != :lt
|
||||||
|
|
||||||
|
# If it's something else (nil, or a weird format), skip
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
# If hours_ago is nil, maybe no time filtering:
|
||||||
|
defp maybe_filter_kills_by_time(kills, nil), do: kills
|
||||||
|
|
||||||
defp handle_all_structure_timers(conn, map_id) do
|
defp handle_all_structure_timers(conn, map_id) do
|
||||||
case MapSystemRepo.get_visible_by_map(map_id) do
|
case MapSystemRepo.get_visible_by_map(map_id) do
|
||||||
{:ok, systems} ->
|
{:ok, systems} ->
|
||||||
@@ -268,11 +340,8 @@ defmodule WandererAppWeb.APIController do
|
|||||||
|
|
||||||
defp get_tracked_by_map_ids(map_id) do
|
defp get_tracked_by_map_ids(map_id) do
|
||||||
case MapCharacterSettingsRepo.get_tracked_by_map_all(map_id) do
|
case MapCharacterSettingsRepo.get_tracked_by_map_all(map_id) do
|
||||||
{:ok, settings_list} ->
|
{:ok, settings_list} -> {:ok, settings_list}
|
||||||
{:ok, settings_list}
|
{:error, reason} -> {:error, :get_tracked_error, reason}
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
{:error, :get_tracked_error, reason}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -286,38 +355,6 @@ defmodule WandererAppWeb.APIController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_map_id(%{"map_id" => mid}) when is_binary(mid) and mid != "" do
|
|
||||||
{:ok, mid}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp fetch_map_id(%{"slug" => slug}) when is_binary(slug) and slug != "" do
|
|
||||||
case WandererApp.Api.Map.get_map_by_slug(slug) do
|
|
||||||
{:ok, map} ->
|
|
||||||
{:ok, map.id}
|
|
||||||
|
|
||||||
{:error, _reason} ->
|
|
||||||
{:error, "No map found for slug=#{slug}"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp fetch_map_id(_),
|
|
||||||
do: {:error, "Must provide either ?map_id=UUID or ?slug=SLUG"}
|
|
||||||
|
|
||||||
defp require_param(params, key) do
|
|
||||||
case params[key] do
|
|
||||||
nil -> {:error, "Missing required param: #{key}"}
|
|
||||||
"" -> {:error, "Param #{key} cannot be empty"}
|
|
||||||
val -> {:ok, val}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp parse_int(str) do
|
|
||||||
case Integer.parse(str) do
|
|
||||||
{num, ""} -> {:ok, num}
|
|
||||||
_ -> {:error, "Invalid integer for param id=#{str}"}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp read_characters_by_ids(ids) when is_list(ids) do
|
defp read_characters_by_ids(ids) when is_list(ids) do
|
||||||
if ids == [] do
|
if ids == [] do
|
||||||
{:ok, []}
|
{:ok, []}
|
||||||
@@ -366,30 +403,4 @@ defmodule WandererAppWeb.APIController do
|
|||||||
:updated_at
|
:updated_at
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
defp static_system_to_json(system) do
|
|
||||||
system
|
|
||||||
|> Map.take([
|
|
||||||
:solar_system_id,
|
|
||||||
:region_id,
|
|
||||||
:constellation_id,
|
|
||||||
:solar_system_name,
|
|
||||||
:solar_system_name_lc,
|
|
||||||
:constellation_name,
|
|
||||||
:region_name,
|
|
||||||
:system_class,
|
|
||||||
:security,
|
|
||||||
:type_description,
|
|
||||||
:class_title,
|
|
||||||
:is_shattered,
|
|
||||||
:effect_name,
|
|
||||||
:effect_power,
|
|
||||||
:statics,
|
|
||||||
:wandering,
|
|
||||||
:triglavian_invasion_status,
|
|
||||||
:sun_type_id
|
|
||||||
])
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
defmodule WandererAppWeb.Plugs.CheckKillsDisabled do
|
||||||
|
import Plug.Conn
|
||||||
|
|
||||||
|
def init(opts), do: opts
|
||||||
|
|
||||||
|
def call(conn, _opts) do
|
||||||
|
if WandererApp.Env.zkill_preload_disabled?() do
|
||||||
|
conn
|
||||||
|
|> send_resp(403, "Map kill feed is disabled")
|
||||||
|
|> halt()
|
||||||
|
else
|
||||||
|
conn
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
41
lib/wanderer_app_web/controllers/util_api_controller.ex
Normal file
41
lib/wanderer_app_web/controllers/util_api_controller.ex
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
defmodule WandererAppWeb.UtilAPIController do
|
||||||
|
@moduledoc """
|
||||||
|
Utility functions for parameter handling, fetch helpers, etc.
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias WandererApp.Api
|
||||||
|
|
||||||
|
def fetch_map_id(%{"map_id" => mid}) when is_binary(mid) and mid != "" do
|
||||||
|
{:ok, mid}
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_map_id(%{"slug" => slug}) when is_binary(slug) and slug != "" do
|
||||||
|
case Api.Map.get_map_by_slug(slug) do
|
||||||
|
{:ok, map} ->
|
||||||
|
{:ok, map.id}
|
||||||
|
|
||||||
|
{:error, _reason} ->
|
||||||
|
{:error, "No map found for slug=#{slug}"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_map_id(_),
|
||||||
|
do: {:error, "Must provide either ?map_id=UUID or ?slug=SLUG"}
|
||||||
|
|
||||||
|
# Require a given param to be present and non-empty
|
||||||
|
def require_param(params, key) do
|
||||||
|
case params[key] do
|
||||||
|
nil -> {:error, "Missing required param: #{key}"}
|
||||||
|
"" -> {:error, "Param #{key} cannot be empty"}
|
||||||
|
val -> {:ok, val}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Parse a string into an integer
|
||||||
|
def parse_int(str) do
|
||||||
|
case Integer.parse(str) do
|
||||||
|
{num, ""} -> {:ok, num}
|
||||||
|
_ -> {:error, "Invalid integer for param id=#{str}"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -114,33 +114,39 @@ defmodule WandererAppWeb.Router do
|
|||||||
plug WandererAppWeb.Plugs.CheckMapApiKey
|
plug WandererAppWeb.Plugs.CheckMapApiKey
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/map", WandererAppWeb do
|
pipeline :api_kills do
|
||||||
pipe_through [:api_map]
|
plug WandererAppWeb.Plugs.CheckApiDisabled
|
||||||
pipe_through [:api]
|
end
|
||||||
|
|
||||||
# GET /api/map/systems?map_id=... or ?slug=...
|
scope "/api/map/systems-kills", WandererAppWeb do
|
||||||
get "/systems", APIController, :list_systems
|
pipe_through [:api, :api_map, :api_kills]
|
||||||
|
|
||||||
# GET /api/map/system-static-info?id=... plus either map_id=... or slug=...
|
get "/", MapAPIController, :list_systems_kills
|
||||||
get "/system-static-info", APIController, :show_system_static
|
end
|
||||||
|
|
||||||
# GET /api/map/system?id=... plus either map_id=... or slug=...
|
scope "/api/map", WandererAppWeb do
|
||||||
get "/system", APIController, :show_system
|
pipe_through [:api, :api_map]
|
||||||
|
|
||||||
# GET /api/map/characters?map_id=... or slug=...
|
# GET /api/map/systems?map_id=... or ?slug=...
|
||||||
get "/characters", APIController, :tracked_characters_with_info
|
get "/systems", MapAPIController, :list_systems
|
||||||
|
|
||||||
# GET /api/map/structure-timers?map_id=... or slug=... and optionally ?system_id=...
|
# GET /api/map/system?id=... plus either map_id=... or slug=...
|
||||||
get "/structure-timers", APIController, :show_structure_timers
|
get "/system", MapAPIController, :show_system
|
||||||
end
|
|
||||||
|
|
||||||
scope "/api/common", WandererAppWeb do
|
# GET /api/map/characters?map_id=... or slug=...
|
||||||
pipe_through [:api]
|
get "/characters", MapAPIController, :tracked_characters_with_info
|
||||||
|
|
||||||
# GET /api/common/system-static-info?id=...
|
# GET /api/map/structure-timers?map_id=... or slug=... and optionally ?system_id=...
|
||||||
get "/system-static-info", APIController, :show_system_static
|
get "/structure-timers", MapAPIController, :show_structure_timers
|
||||||
|
end
|
||||||
|
|
||||||
end
|
scope "/api/common", WandererAppWeb do
|
||||||
|
pipe_through [:api]
|
||||||
|
|
||||||
|
# GET /api/common/system-static-info?id=...
|
||||||
|
get "/system-static-info", CommonAPIController, :show_system_static
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
scope "/", WandererAppWeb do
|
scope "/", WandererAppWeb do
|
||||||
pipe_through [:browser, :blog, :redirect_if_user_is_authenticated]
|
pipe_through [:browser, :blog, :redirect_if_user_is_authenticated]
|
||||||
|
|||||||
@@ -199,6 +199,101 @@ No api key is required for routes that being with /api/common
|
|||||||
```
|
```
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 4. Kills Activity
|
||||||
|
|
||||||
|
GET /api/map/systems-kills?map_id=<UUID>
|
||||||
|
GET /api/map/systems-kills?slug=<map-slug>"
|
||||||
|
|
||||||
|
- **Description:** Retrieves the kill activity for the specified map (by `map_id` or `slug`), including details on the attacker and victim
|
||||||
|
|
||||||
|
#### Example Request
|
||||||
|
```
|
||||||
|
curl -H "Authorization: Bearer <REDACTED_TOKEN>" "https://wanderer.example.com/api/map/systems-kills?slug==some-slug"
|
||||||
|
```
|
||||||
|
#### Example Response
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"kills": [
|
||||||
|
{
|
||||||
|
"attacker_count": 1,
|
||||||
|
"final_blow_alliance_id": 99013806,
|
||||||
|
"final_blow_alliance_ticker": "TCE",
|
||||||
|
"final_blow_char_id": 2116802670,
|
||||||
|
"final_blow_char_name": "Bambi Bunny",
|
||||||
|
"final_blow_corp_id": 98140648,
|
||||||
|
"final_blow_corp_ticker": "GNK3D",
|
||||||
|
"final_blow_ship_name": "Thrasher",
|
||||||
|
"final_blow_ship_type_id": 16242,
|
||||||
|
"kill_time": "2025-01-21T21:00:59Z",
|
||||||
|
"killmail_id": 124181782,
|
||||||
|
"npc": false,
|
||||||
|
"solar_system_id": 30002768,
|
||||||
|
"total_value": 10000,
|
||||||
|
"victim_alliance_id": null,
|
||||||
|
"victim_char_id": 2121725410,
|
||||||
|
"victim_char_name": "Bill Drummond",
|
||||||
|
"victim_corp_id": 98753095,
|
||||||
|
"victim_corp_ticker": "KSTJK",
|
||||||
|
"victim_ship_name": "Capsule",
|
||||||
|
"victim_ship_type_id": 670,
|
||||||
|
"zkb": {
|
||||||
|
"awox": false,
|
||||||
|
"destroyedValue": 10000,
|
||||||
|
"droppedValue": 0,
|
||||||
|
"fittedValue": 10000,
|
||||||
|
"hash": "777148f8bf344bade68a6a0821bfe0a37491a7a6",
|
||||||
|
"labels": ["cat:6","#:1","pvp","loc:highsec"],
|
||||||
|
"locationID": 50014064,
|
||||||
|
"npc": false,
|
||||||
|
"points": 1,
|
||||||
|
"solo": false,
|
||||||
|
"totalValue": 10000
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"attacker_count": 3,
|
||||||
|
"final_blow_alliance_id": null,
|
||||||
|
"final_blow_char_id": null,
|
||||||
|
"final_blow_corp_id": null,
|
||||||
|
"final_blow_ship_type_id": 3740,
|
||||||
|
"kill_time": "2025-01-21T21:00:38Z",
|
||||||
|
"killmail_id": 124181769,
|
||||||
|
"npc": true,
|
||||||
|
"solar_system_id": 30002768,
|
||||||
|
"total_value": 2656048.48,
|
||||||
|
"victim_alliance_id": 99013806,
|
||||||
|
"victim_alliance_ticker": "TCE",
|
||||||
|
"victim_char_id": 2116802745,
|
||||||
|
"victim_char_name": "Brittni Bunny",
|
||||||
|
"victim_corp_id": 98140648,
|
||||||
|
"victim_corp_ticker": "GNK3D",
|
||||||
|
"victim_ship_name": "Coercer",
|
||||||
|
"victim_ship_type_id": 16236,
|
||||||
|
"zkb": {
|
||||||
|
"awox": false,
|
||||||
|
"destroyedValue": 2509214.44,
|
||||||
|
"droppedValue": 146834.04,
|
||||||
|
"fittedValue": 2607449.82,
|
||||||
|
"hash": "d3dd6b8833b2a9d36dd5a3eecf9838c4c8b01acd",
|
||||||
|
"labels": ["cat:6","#:2+","npc","loc:highsec"],
|
||||||
|
"locationID": 50014064,
|
||||||
|
"npc": true,
|
||||||
|
"points": 1,
|
||||||
|
"solo": false,
|
||||||
|
"totalValue": 2656048.48
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"solar_system_id": 30002768
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
---
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
Using these APIs, you can programmatically retrieve system and character information from your map. Whether you’re building a custom analytics dashboard, a corp management tool, or just want to explore data outside the standard UI, these endpoints provide a straightforward way to fetch up-to-date map details.
|
Using these APIs, you can programmatically retrieve system and character information from your map. Whether you’re building a custom analytics dashboard, a corp management tool, or just want to explore data outside the standard UI, these endpoints provide a straightforward way to fetch up-to-date map details.
|
||||||
|
|||||||
Reference in New Issue
Block a user