From a12ba25f189eaa995c8e845eec95b2435211c196 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Wed, 2 Oct 2019 19:13:52 -0400 Subject: [PATCH] Add user outboxes --- lib/clacks/paginator.ex | 26 +++++++ .../controllers/outbox_controller.ex | 76 +++++++++++++++++++ lib/clacks_web/router.ex | 1 + 3 files changed, 103 insertions(+) create mode 100644 lib/clacks/paginator.ex create mode 100644 lib/clacks_web/controllers/outbox_controller.ex diff --git a/lib/clacks/paginator.ex b/lib/clacks/paginator.ex new file mode 100644 index 0000000..b83b597 --- /dev/null +++ b/lib/clacks/paginator.ex @@ -0,0 +1,26 @@ +defmodule Clacks.Paginator do + import Ecto.Query + + def paginate(query, %{"max_id" => max_id}) do + query + |> where([a], a.id < ^max_id) + |> order_by(desc: :id) + end + + def paginate(query, %{"since_id" => since_id}) do + query + |> where([a], a.id > ^since_id) + |> order_by(desc: :id) + end + + def paginate(query, %{"min_id" => min_id}) do + query + |> where([a], a.id > ^min_id) + |> order_by(asc: :id) + end + + def paginate(query, _params) do + query + |> order_by(desc: :id) + end +end diff --git a/lib/clacks_web/controllers/outbox_controller.ex b/lib/clacks_web/controllers/outbox_controller.ex new file mode 100644 index 0000000..b95838d --- /dev/null +++ b/lib/clacks_web/controllers/outbox_controller.ex @@ -0,0 +1,76 @@ +defmodule ClacksWeb.OutboxController do + use ClacksWeb, :controller + alias Clacks.{Repo, Actor, Activity} + import Ecto.Query + + @context "https://www.w3.org/ns/activitystreams" + @outbox_types ["Create", "Announce"] + + 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) + + %Actor{local: false} -> + conn + |> put_status(404) + + actor -> + assign(conn, :actor, actor) + end + end + + def outbox(conn, params) when params == %{} do + actor = conn.assigns[:actor] + + activities = Repo.all(outbox_query(params, actor)) + + data = %{ + "@context" => @context, + "type" => "OrderedCollection", + "id" => current_url(conn, %{}), + "first" => outbox_page(conn, params, activities) + } + + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(data) + end + + def outbox(conn, params) do + actor = conn.assigns[:actor] + + activities = Repo.all(outbox_query(params, actor)) + + data = + outbox_page(conn, params, activities) + |> Map.put("@context", @context) + + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(data) + end + + defp outbox_query(params, %Actor{ap_id: ap_id}) do + Activity + |> where([a], a.actor == ^ap_id) + |> where([a], fragment("?->>'type'", a.data) in @outbox_types) + |> Clacks.Paginator.paginate(params) + |> limit(^Map.get(params, "limit", 20)) + end + + defp outbox_page(conn, pagination_params, activities) do + last_id = List.last(activities).id + + %{ + "type" => "OrderedColletionPage", + "partOf" => current_url(conn, %{}), + "id" => current_url(conn, pagination_params), + "next" => current_url(conn, %{max_id: last_id}), + "orderedItems" => Enum.map(activities, & &1.data) + } + end +end diff --git a/lib/clacks_web/router.ex b/lib/clacks_web/router.ex index dd0a8e1..be08e25 100644 --- a/lib/clacks_web/router.ex +++ b/lib/clacks_web/router.ex @@ -39,6 +39,7 @@ defmodule ClacksWeb.Router do get "/users/:nickname", ActorController, :get get "/users/:nickname/followers", ActorController, :followers get "/users/:nickname/following", ActorController, :following + get "/users/:nickname/outbox", OutboxController, :outbox post "/inbox", InboxController, :shared post "/users/:nickname/inbox", InboxController, :user_specific