create backend
This commit is contained in:
75
lib/mound_hunters_web/plugs/bounds_check.ex
Normal file
75
lib/mound_hunters_web/plugs/bounds_check.ex
Normal file
@@ -0,0 +1,75 @@
|
||||
defmodule MoundHuntersWeb.Plugs.BoundsCheck do
|
||||
@moduledoc """
|
||||
Plug to validate that coordinates are within Ohio boundaries.
|
||||
Applies to tile requests but not to geometry sharing API.
|
||||
"""
|
||||
import Plug.Conn
|
||||
require Logger
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
def call(conn, _opts) do
|
||||
# Skip bounds check for non-tile endpoints
|
||||
if skip_bounds_check?(conn.request_path) do
|
||||
conn
|
||||
else
|
||||
check_bounds(conn)
|
||||
end
|
||||
end
|
||||
|
||||
defp skip_bounds_check?(path) do
|
||||
# Skip bounds check for geometry API and static files
|
||||
String.starts_with?(path, "/api/share") or
|
||||
String.starts_with?(path, "/static/") or
|
||||
path == "/"
|
||||
end
|
||||
|
||||
defp check_bounds(conn) do
|
||||
cond do
|
||||
# Check query params for tile request
|
||||
conn.query_params["lat"] != nil and conn.query_params["lng"] != nil ->
|
||||
check_query_params(conn)
|
||||
|
||||
# For tile file serving, we assume tiles in storage are valid
|
||||
# (they were validated when created)
|
||||
String.starts_with?(conn.request_path, "/tiles/") ->
|
||||
conn
|
||||
|
||||
true ->
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
defp check_query_params(conn) do
|
||||
with {:ok, lat} <- parse_float(conn.query_params["lat"]),
|
||||
{:ok, lng} <- parse_float(conn.query_params["lng"]),
|
||||
:ok <- MoundHunters.Boundary.check_bounds(lat, lng) do
|
||||
conn
|
||||
else
|
||||
{:error, :invalid_number} ->
|
||||
send_error(conn, 400, "Invalid coordinate format")
|
||||
|
||||
{:error, reason} ->
|
||||
send_error(conn, 400, reason)
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_float(str) when is_binary(str) do
|
||||
case Float.parse(str) do
|
||||
{num, ""} -> {:ok, num}
|
||||
_ -> {:error, :invalid_number}
|
||||
end
|
||||
end
|
||||
|
||||
defp parse_float(_), do: {:error, :invalid_number}
|
||||
|
||||
defp send_error(conn, status, message) do
|
||||
Logger.warning("Bounds check failed: #{message}")
|
||||
|
||||
conn
|
||||
|> put_private(:error_message, message)
|
||||
|> put_resp_content_type("application/json")
|
||||
|> send_resp(status, Jason.encode!(%{error: message}))
|
||||
|> halt()
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user