defmodule ClacksWeb.ActorController do use ClacksWeb, :controller alias Clacks.Actor @context "https://www.w3.org/ns/activitystreams" plug :get_actor defp get_actor(%Plug.Conn{path_params: %{"nickname" => nickname}} = conn, _opts) do case Actor.get_by_nickname(nickname) do nil -> conn |> put_status(404) |> halt() actor -> assign(conn, :actor, actor) end end defp get_actor(conn, _opts), do: conn def get(conn, _params) do case conn.assigns[:actor] do %Actor{local: true, data: data} -> conn |> put_resp_header("content-type", "application/activity+json") |> json(data) %Actor{local: false, ap_id: ap_id} -> conn |> redirect(external: ap_id) end end def followers(conn, %{"page" => page}) do {page, _} = Integer.parse(page) followers = conn.assigns[:actor].followers data = collection_page(conn, followers, page) |> Map.put("@context", @context) conn |> put_resp_header("content-type", "application/activity+json") |> json(data) end def followers(conn, _params) do %Actor{followers: followers} = conn.assigns[:actor] data = %{ "@context" => @context, "type" => "OrderedCollection", "id" => current_url(conn, %{}), "totalItems" => length(followers), "first" => collection_page(conn, followers, 1) } conn |> put_resp_header("content-type", "application/activity+json") |> json(data) end def following(conn, %{"page" => page}) do actor = conn.assigns[:actor] following = Actor.get_all_following(actor) |> Enum.map(fn actor -> actor.ap_id end) data = collection_page(conn, following, page) |> Map.put("@context", @context) conn |> put_resp_header("content-type", "application/activity+json") |> json(data) end def following(conn, _params) do actor = conn.assigns[:actor] following = Actor.get_all_following(actor) |> Enum.map(fn actor -> actor.ap_id end) data = %{ "@context" => @context, "type" => "OrderedCollection", "id" => current_url(conn, %{}), "totalItems" => length(following), "first" => collection_page(conn, following, 1) } conn |> put_resp_header("content-type", "application/activity+json") |> json(data) end defp collection_page(conn, collection, page) do chunks = Enum.chunk_every(collection, 20) # page is 1 indexed, so subtract 1 to get the current chunk current_chunk = Enum.at(chunks, page - 1) data = %{ "type" => "OrderedCollectionPage", "totalItems" => length(collection), "partOf" => current_url(conn, %{}), "id" => current_url(conn, %{page: page}), "orderedItems" => current_chunk || [] } if page < length(chunks) do Map.put(data, "next", current_url(conn, %{page: page + 1})) else data end end end