defmodule Localiser.Web.Controllers.FirmwareController do use Phoenix.Controller, formats: [:json] alias Localiser.Domain.{Firmware, Sensors} # Admin: upload a new firmware version. # Expects multipart/form-data with fields: version (string), firmware (file). def upload(conn, %{"version" => version, "firmware" => %Plug.Upload{} = upload}) do case Firmware.store(version, upload) do {:ok, _version} -> json(conn, %{status: "ok", version: version}) {:error, reason} -> conn |> put_status(:internal_server_error) |> json(%{error: "Upload failed: #{inspect(reason)}"}) end end def upload(conn, _params) do conn |> put_status(:bad_request) |> json(%{error: "version and firmware file are required"}) end # Admin: list available firmware versions. def index(conn, _params) do json(conn, Firmware.list()) end # Public: serve firmware binary. def download(conn, %{"version" => version}) do case Firmware.get(version) do {:ok, path} -> conn |> put_resp_content_type("application/octet-stream") |> put_resp_header("content-disposition", ~s(attachment; filename="#{version}.bin")) |> send_file(200, path) :not_found -> conn |> put_status(:not_found) |> json(%{error: "Firmware version not found"}) end end # Admin: broadcast OTA update to the entire fleet. # POST /api/firmware/:version/ota def ota_fleet(conn, %{"version" => version}) do case Firmware.get(version) do {:ok, _path} -> url = firmware_url(conn, version) Sensors.send_ota_broadcast(url, version) json(conn, %{status: "ok", url: url, version: version}) :not_found -> conn |> put_status(:not_found) |> json(%{error: "Firmware version not found"}) end end # Admin: upload a new firmware version and push OTA update to fleet immediately. # Expects multipart/form-data with fields: version (string), firmware (file). def ota_fleet_instant(conn, %{"version" => version, "firmware" => %Plug.Upload{} = upload}) do case Firmware.store(version, upload) do {:ok, _version} -> url = firmware_url(conn, version) Sensors.send_ota_broadcast(url, version) json(conn, %{status: "ok", version: version}) {:error, reason} -> conn |> put_status(:internal_server_error) |> json(%{error: "Upload failed: #{inspect(reason)}"}) end end defp firmware_url(conn, version) do "#{conn.scheme}://#{conn.host}:#{conn.port}/api/firmware/#{version}" end end