defmodule Forum.PublicSiteRouter do @moduledoc """ Serves one network's public website on its own port. """ import Plug.Conn def init(opts), do: opts def call(conn, opts) do network = Keyword.fetch!(opts, :network) started_at = System.monotonic_time(:microsecond) response = with {:ok, pid} <- Forum.PublicSiteSupervisor.get_or_start(network) do Forum.PublicSite.serve(pid, %{ method: conn.method, path: conn.request_path, query_string: conn.query_string, headers: conn.req_headers }) else {:error, reason} -> %{ status: 500, content_type: "text/plain", body: "Unable to start public site: #{inspect(reason)}" } end duration_us = System.monotonic_time(:microsecond) - started_at Forum.LogStore.add(%{ "source_ip" => remote_ip(conn), "host" => conn.host || "", "network" => network, "method" => conn.method, "path" => conn.request_path, "query_string" => conn.query_string, "status" => response.status, "duration_ms" => Float.round(duration_us / 1_000, 2) }) conn |> put_resp_content_type(response.content_type) |> send_resp(response.status, response.body) end defp remote_ip(conn) do case Plug.Conn.get_req_header(conn, "x-real-ip") do [ip | _] -> ip [] -> fallback_ip(conn) end end defp fallback_ip(%{remote_ip: remote_ip}) when is_tuple(remote_ip) do remote_ip |> :inet.ntoa() |> to_string() end defp fallback_ip(_), do: "" end