Files
wanderer/lib/wanderer_app_web/controllers/api_controller.ex
guarzo 2088393c79 feat(Map): add "temporary system names" toggle (#86)
If enabled and set, a temporary name is displayed instead of the system name. The original system name appears on a secondary row if no custom name exists. Temporary names are removed when the system is removed from the map.
2025-01-08 03:04:36 +04:00

250 lines
6.7 KiB
Elixir

defmodule WandererAppWeb.APIController do
use WandererAppWeb, :controller
import Ash.Query, only: [filter: 2]
alias WandererApp.Api
alias WandererApp.MapSystemRepo
alias WandererApp.MapCharacterSettingsRepo
alias WandererApp.Api.Character
# -----------------------------------------------------------------
# SYSTEMS
# -----------------------------------------------------------------
@doc """
GET /api/systems
Requires either `?map_id=<UUID>` **OR** `?slug=<map-slug>` in the query params.
If `?all=true` is provided, **all** systems are returned.
Otherwise, only "visible" systems are returned.
Examples:
GET /api/systems?map_id=466e922b-e758-485e-9b86-afae06b88363
GET /api/systems?slug=my-unique-wormhole-map
GET /api/systems?map_id=<UUID>&all=true
"""
def list_systems(conn, params) do
with {:ok, map_id} <- fetch_map_id(params) do
# Decide which function to call based on the "all" param
repo_fun =
if params["all"] == "true" do
&MapSystemRepo.get_all_by_map/1
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
{:error, msg} ->
conn
|> put_status(:bad_request)
|> json(%{error: msg})
end
end
@doc """
GET /api/system
Requires 'id' (the solar_system_id)
plus either ?map_id=<UUID> or ?slug=<map-slug>.
Example:
GET /api/system?id=31002229&map_id=466e922b-e758-485e-9b86-afae06b88363
GET /api/system?id=31002229&slug=my-unique-wormhole-map
"""
def show_system(conn, params) do
with {:ok, solar_system_str} <- require_param(params, "id"),
{:ok, solar_system_id} <- parse_int(solar_system_str),
{:ok, map_id} <- fetch_map_id(params) do
case MapSystemRepo.get_by_map_and_solar_system_id(map_id, solar_system_id) do
{:ok, system} ->
data = map_system_to_json(system)
json(conn, %{data: data})
{:error, :not_found} ->
conn
|> put_status(:not_found)
|> json(%{error: "System not found in map=#{map_id}"})
end
else
{:error, msg} ->
conn
|> put_status(:bad_request)
|> json(%{error: msg})
end
end
# -----------------------------------------------------------------
# Characters
# -----------------------------------------------------------------
@doc """
GET /api/tracked_characters_with_info
Example usage:
GET /api/tracked_characters_with_info?map_id=<uuid>
GET /api/tracked_characters_with_info?slug=<map-slug>
Returns a list of tracked records, plus their fully-loaded `character` data.
"""
def tracked_characters_with_info(conn, params) do
with {:ok, map_id} <- fetch_map_id(params),
{:ok, settings_list} <- get_tracked_by_map_ids(map_id),
{:ok, char_list} <-
read_characters_by_ids_wrapper(Enum.map(settings_list, & &1.character_id)) do
chars_by_id = Map.new(char_list, &{&1.id, &1})
data =
Enum.map(settings_list, fn setting ->
found_char = Map.get(chars_by_id, setting.character_id)
%{
id: setting.id,
map_id: setting.map_id,
character_id: setting.character_id,
tracked: setting.tracked,
inserted_at: setting.inserted_at,
updated_at: setting.updated_at,
character:
if found_char do
character_to_json(found_char)
else
%{}
end
}
end)
json(conn, %{data: data})
else
{:error, :get_tracked_error, reason} ->
conn
|> put_status(:not_found)
|> json(%{error: "No tracked records found for map_id: #{inspect(reason)}"})
{:error, :read_characters_by_ids_error, reason} ->
conn
|> put_status(:internal_server_error)
|> json(%{error: "Could not load Character records: #{inspect(reason)}"})
{:error, message} ->
conn
|> put_status(:bad_request)
|> json(%{error: message})
end
end
defp get_tracked_by_map_ids(map_id) do
case MapCharacterSettingsRepo.get_tracked_by_map_all(map_id) do
{:ok, settings_list} ->
{:ok, settings_list}
{:error, reason} ->
{:error, :get_tracked_error, reason}
end
end
defp read_characters_by_ids_wrapper(ids) do
case read_characters_by_ids(ids) do
{:ok, char_list} ->
{:ok, char_list}
{:error, reason} ->
{:error, :read_characters_by_ids_error, reason}
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
if ids == [] do
{:ok, []}
else
query =
Character
|> filter(id in ^ids)
Api.read(query)
end
end
defp map_system_to_json(system) do
%{
id: system.id,
map_id: system.map_id,
solar_system_id: system.solar_system_id,
name: system.name,
custom_name: system.custom_name,
temporary_name: system.temporary_name,
description: system.description,
tag: system.tag,
labels: system.labels,
locked: system.locked,
visible: system.visible,
status: system.status,
position_x: system.position_x,
position_y: system.position_y,
inserted_at: system.inserted_at,
updated_at: system.updated_at
}
end
defp character_to_json(ch) do
%{
id: ch.id,
eve_id: ch.eve_id,
name: ch.name,
corporation_id: ch.corporation_id,
corporation_name: ch.corporation_name,
corporation_ticker: ch.corporation_ticker,
alliance_id: ch.alliance_id,
alliance_name: ch.alliance_name,
alliance_ticker: ch.alliance_ticker,
inserted_at: ch.inserted_at,
updated_at: ch.updated_at
}
end
end