119 lines
3.0 KiB
Elixir

defmodule MessageServer.Router do
use Plug.Router
require Logger
alias MessageServer.{
MessageRequest,
RemoteHandler,
MessageHandler
}
plug(:match)
plug(:debug_content_type)
plug(Plug.Parsers,
parsers: [:urlencoded, :multipart, :json],
json_decoder: Jason,
pass: ["application/octet-stream"]
)
plug(:dispatch)
def json_request?(conn) do
conn.request_path != "/api/remote/messages"
end
post "/api/messages" do
with {:ok, message} <- validate_message_request(conn.body_params),
:ok <- MessageHandler.handle_message(message) do
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Jason.encode!(%{status: "success"}))
else
{:error, reason} ->
Logger.warning("Message handling failed: #{inspect(reason)}")
conn
|> put_resp_content_type("application/json")
|> send_resp(400, Jason.encode!(%{error: reason}))
end
end
post "/api/remote/messages" do
case handle_etf_body(conn) do
{:ok, payload} ->
payload
|> RemoteHandler.handle_remote_message()
|> case do
:ok ->
conn
|> put_resp_content_type("application/json")
|> send_resp(200, Jason.encode!(%{status: "success"}))
{:error, reason} ->
Logger.warning("Remote message handling failed: #{inspect(reason)}")
conn
|> put_resp_content_type("application/json")
|> send_resp(400, Jason.encode!(%{error: reason}))
end
{:error, reason} ->
conn
|> put_resp_content_type("application/json")
|> send_resp(400, Jason.encode!(%{error: reason, message: "Invalid request body"}))
end
end
match _ do
send_resp(conn, 404, "Not found")
end
defp debug_content_type(conn, _opts) do
conn
|> Plug.Conn.get_req_header("content-type")
|> IO.inspect(label: "Received Content-Type")
conn
end
defp handle_etf_body(conn) do
with {:ok, body, _conn} <- Plug.Conn.read_body(conn),
{:ok, decoded} <- decode_etf(body) do
{:ok, decoded}
else
{:error, reason} -> {:error, "Failed to read body: #{reason}"}
{:more, _partial, _conn} -> {:error, "Body too large"}
end
end
@spec decode_etf(binary()) :: {:ok, map()} | {:error, String.t()}
defp decode_etf(binary_body) do
try do
payload = :erlang.binary_to_term(binary_body, [:safe])
{:ok, payload}
rescue
error -> {:error, "Deserialization failed: #{inspect(error)}"}
end
end
@spec validate_message_request(map()) :: {:ok, map()} | {:error, String.t()}
defp validate_message_request(params) do
required_fields = ["from", "to", "message"]
case Enum.all?(required_fields, &Map.has_key?(params, &1)) do
true ->
{:ok,
%MessageRequest{
from: params["from"],
to: params["to"],
message: params["message"]
}}
false ->
{:error, "Missing required fields: from, to, message"}
end
end
end