diff --git a/lib/towerops_web/controllers/api/agent_controller.ex b/lib/towerops_web/controllers/api/agent_controller.ex index 75a76fa0..3d2aafd4 100644 --- a/lib/towerops_web/controllers/api/agent_controller.ex +++ b/lib/towerops_web/controllers/api/agent_controller.ex @@ -98,14 +98,31 @@ defmodule ToweropsWeb.Api.AgentController do case get_req_header(conn, "content-type") do ["application/x-protobuf" | _] -> # Decode protobuf - {:ok, body, _conn} = Plug.Conn.read_body(conn) - proto_metadata = HeartbeatMetadata.decode(body) + case Plug.Conn.read_body(conn) do + {:ok, body, _conn} -> + try do + proto_metadata = HeartbeatMetadata.decode(body) - %{ - "version" => proto_metadata.version, - "hostname" => proto_metadata.hostname, - "uptime_seconds" => proto_metadata.uptime_seconds - } + %{ + "version" => proto_metadata.version, + "hostname" => proto_metadata.hostname, + "uptime_seconds" => proto_metadata.uptime_seconds + } + rescue + e -> + require Logger + + Logger.error("Failed to decode protobuf heartbeat: #{inspect(e)}") + # Return empty metadata on decode error + %{} + end + + {:error, reason} -> + require Logger + + Logger.error("Failed to read heartbeat body: #{inspect(reason)}") + %{} + end _ -> # JSON format (fallback) diff --git a/lib/towerops_web/endpoint.ex b/lib/towerops_web/endpoint.ex index bff37663..5f5d7b02 100644 --- a/lib/towerops_web/endpoint.ex +++ b/lib/towerops_web/endpoint.ex @@ -49,8 +49,25 @@ defmodule ToweropsWeb.Endpoint do plug Plug.Parsers, parsers: [:urlencoded, :multipart, :json], pass: ["*/*"], + body_reader: {ToweropsWeb.Endpoint.BodyReader, :read_body, []}, json_decoder: Phoenix.json_library() + # Custom body reader that skips parsing for protobuf content type + defmodule BodyReader do + @moduledoc false + def read_body(conn, opts) do + case Plug.Conn.get_req_header(conn, "content-type") do + ["application/x-protobuf" | _] -> + # Don't parse protobuf, let the controller handle it + {:ok, "", conn} + + _ -> + # Use default body reader for other content types + Plug.Conn.read_body(conn, opts) + end + end + end + plug Plug.MethodOverride plug Plug.Head plug Plug.Session, @session_options