Files

133 lines
3.9 KiB
Elixir

defmodule MoundHuntersWeb.ApiController do
@moduledoc """
API endpoints for sharing geometries.
"""
import Plug.Conn
require Logger
@doc """
Create a new shared geometry.
POST /api/share
Body: {"geojson": {...}}
Returns: {"id": "abc12345"}
"""
def create_share(conn) do
with {:ok, body, conn} <- read_body(conn),
{:ok, params} <- Jason.decode(body),
geojson when is_map(geojson) <- params["geojson"],
geojson_str <- Jason.encode!(geojson),
{:ok, %{id: id}} <- MoundHunters.Repo.create_geometry(geojson_str) do
conn
|> put_resp_content_type("application/json")
|> put_resp_header("cache-control", "no-cache")
|> send_resp(200, Jason.encode!(%{id: id}))
else
{:error, :invalid_json} ->
send_error(conn, 400, "Invalid JSON")
nil ->
send_error(conn, 400, "Missing geojson field")
{:error, reason} ->
Logger.error("Failed to create geometry: #{inspect(reason)}")
send_error(conn, 500, "Failed to create geometry")
_ ->
send_error(conn, 400, "Invalid request")
end
end
@doc """
Get a shared geometry by ID.
GET /api/share/:id
Returns: {"geojson": {...}}
"""
def get_share(conn) do
geometry_id = conn.path_params["id"]
case MoundHunters.Repo.get_geometry(geometry_id) do
{:ok, geometry} ->
geojson = Jason.decode!(geometry.geojson)
conn
|> put_resp_content_type("application/json")
|> put_resp_header("cache-control", "no-cache")
|> send_resp(200, Jason.encode!(%{geojson: geojson}))
{:error, :not_found} ->
send_error(conn, 404, "Geometry not found")
{:error, reason} ->
Logger.error("Failed to get geometry: #{inspect(reason)}")
send_error(conn, 500, "Failed to retrieve geometry")
end
end
@doc """
Get tile metadata by ID.
GET /api/meta/tile/:id
Returns: tile metadata including bounds, status, availability flags
"""
def get_tile(conn) do
tile_id = conn.path_params["id"]
case MoundHunters.Repo.get_tile(tile_id) do
{:ok, tile} ->
conn
|> put_resp_content_type("application/json")
|> put_resp_header("cache-control", "public, max-age=60")
|> send_resp(200, Jason.encode!(tile))
{:error, :not_found} ->
send_error(conn, 404, "Tile not found")
{:error, reason} ->
Logger.error("Failed to get tile: #{inspect(reason)}")
send_error(conn, 500, "Failed to retrieve tile")
end
end
@doc """
Get tile metadata by coordinates.
GET /api/meta/tile?lat=40.0&lng=-82.5
Returns: tile metadata for the tile containing the given coordinates
"""
def get_tile_by_coords(conn) do
with lat_str when is_binary(lat_str) <- conn.query_params["lat"],
lng_str when is_binary(lng_str) <- conn.query_params["lng"],
{lat, _} <- Float.parse(lat_str),
{lng, _} <- Float.parse(lng_str) do
case MoundHunters.Repo.get_tile_at_coords(lat, lng) do
{:ok, tile} ->
conn
|> put_resp_content_type("application/json")
|> put_resp_header("cache-control", "public, max-age=60")
|> send_resp(200, Jason.encode!(tile))
{:error, :not_found} ->
send_error(conn, 404, "No tile found at coordinates")
{:error, reason} ->
Logger.error("Failed to get tile by coords: #{inspect(reason)}")
send_error(conn, 500, "Failed to retrieve tile")
end
else
nil ->
send_error(conn, 400, "Missing lat or lng parameter")
:error ->
send_error(conn, 400, "Invalid lat or lng format")
_ ->
send_error(conn, 400, "Invalid request")
end
end
defp send_error(conn, status, message) do
conn
|> put_private(:error_message, message)
|> put_resp_content_type("application/json")
|> send_resp(status, Jason.encode!(%{error: message}))
end
end