towerops/lib/towerops_web/endpoint.ex
Graham McIntire ae521d2108
Fix protobuf body parsing causing 400 errors on agent heartbeat
Plug.Parsers was trying to parse protobuf bodies as JSON, failing with
400 errors before the request reached the controller.

Changes:
- Added custom BodyReader to endpoint that skips parsing for protobuf
- When Content-Type is application/x-protobuf, return empty body to parser
- Controller reads the raw body directly for protobuf requests
- Added error handling for protobuf decode failures in heartbeat endpoint

This fixes the 400 errors agents were seeing on heartbeat requests.
2026-01-14 18:35:20 -06:00

79 lines
2.4 KiB
Elixir

defmodule ToweropsWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :towerops
# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it.
@session_options [
store: :cookie,
key: "_towerops_key",
signing_salt: "hrDZxLhd",
same_site: "Lax"
]
socket "/live", Phoenix.LiveView.Socket,
websocket: [connect_info: [session: @session_options]],
longpoll: [connect_info: [session: @session_options]]
# Serve at "/" the static files from "priv/static" directory.
#
# When code reloading is disabled (e.g., in production),
# the `gzip` option is enabled to serve compressed
# static files generated by running `phx.digest`.
plug Plug.Static,
at: "/",
from: :towerops,
gzip: not code_reloading?,
only: ToweropsWeb.static_paths(),
raise_on_missing_only: code_reloading?
# Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint.
if code_reloading? do
socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
plug Phoenix.LiveReloader
plug Phoenix.CodeReloader
plug Phoenix.Ecto.CheckRepoStatus, otp_app: :towerops
end
plug Phoenix.LiveDashboard.RequestLogger,
param_key: "request_logger",
cookie_key: "request_logger"
plug Plug.RequestId
plug Plug.Telemetry,
event_prefix: [:phoenix, :endpoint],
log: {__MODULE__, :log_level, []}
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
plug ToweropsWeb.Router
# Disable logging for health check endpoint to reduce log noise from K8s probes
def log_level(%{path_info: ["health"]}), do: false
def log_level(_), do: :info
end