From 1a947042300045cadc62ea1153a75129f5282281 Mon Sep 17 00:00:00 2001 From: William Pitcock Date: Wed, 21 Mar 2018 17:23:27 +0000 Subject: [PATCH] activitypub: implement following/followers endpoints --- .../web/activity_pub/activity_pub_controller.ex | 38 ++++++++++++ lib/pleroma/web/activity_pub/utils.ex | 20 +++++++ lib/pleroma/web/activity_pub/views/user_view.ex | 67 +++++++++++++++++----- lib/pleroma/web/router.ex | 8 +++ 4 files changed, 118 insertions(+), 15 deletions(-) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index b9a70a33f..419c3307d 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -27,6 +27,44 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end + def following(conn, %{"nickname" => nickname, "page" => page}) do + with %User{} = user <- User.get_cached_by_nickname(nickname), + {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do + {page, _} = Integer.parse(page) + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(UserView.render("following.json", %{user: user, page: page})) + end + end + + def following(conn, %{"nickname" => nickname}) do + with %User{} = user <- User.get_cached_by_nickname(nickname), + {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(UserView.render("following.json", %{user: user})) + end + end + + def followers(conn, %{"nickname" => nickname, "page" => page}) do + with %User{} = user <- User.get_cached_by_nickname(nickname), + {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do + {page, _} = Integer.parse(page) + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(UserView.render("followers.json", %{user: user, page: page})) + end + end + + def followers(conn, %{"nickname" => nickname}) do + with %User{} = user <- User.get_cached_by_nickname(nickname), + {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(UserView.render("followers.json", %{user: user})) + end + end + # TODO: Ensure that this inbox is a recipient of the message def inbox(%{assigns: %{valid_signature: true}} = conn, params) do Federator.enqueue(:incoming_ap_doc, params) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index cda106283..a25b27aab 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -5,6 +5,26 @@ defmodule Pleroma.Web.ActivityPub.Utils do alias Ecto.{Changeset, UUID} import Ecto.Query + def make_json_ld_header do + %{ + "@context" => [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + %{ + "manuallyApprovesFollowers" => "as:manuallyApprovesFollowers", + "sensitive" => "as:sensitive", + "Hashtag" => "as:Hashtag", + "ostatus" => "http://ostatus.org#", + "atomUri" => "ostatus:atomUri", + "inReplyToAtomUri" => "ostatus:inReplyToAtomUri", + "conversation" => "ostatus:conversation", + "toot" => "http://joinmastodon.org/ns#", + "Emoji" => "toot:Emoji" + } + ] + } + end + def make_date do DateTime.utc_now() |> DateTime.to_iso8601 end diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 179636884..7069c248c 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -3,6 +3,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do alias Pleroma.Web.Salmon alias Pleroma.Web.WebFinger alias Pleroma.User + alias Pleroma.Web.ActivityPub.Utils def render("user.json", %{user: user}) do {:ok, user} = WebFinger.ensure_keys_present(user) @@ -10,21 +11,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do public_key = :public_key.pem_entry_encode(:RSAPublicKey, public_key) public_key = :public_key.pem_encode([public_key]) %{ - "@context" => [ - "https://www.w3.org/ns/activitystreams", - "https://w3id.org/security/v1", - %{ - "manuallyApprovesFollowers" => "as:manuallyApprovesFollowers", - "sensitive" => "as:sensitive", - "Hashtag" => "as:Hashtag", - "ostatus" => "http://ostatus.org#", - "atomUri" => "ostatus:atomUri", - "inReplyToAtomUri" => "ostatus:inReplyToAtomUri", - "conversation" => "ostatus:conversation", - "toot" => "http://joinmastodon.org/ns#", - "Emoji" => "toot:Emoji" - } - ], "id" => user.ap_id, "type" => "Person", "following" => "#{user.ap_id}/following", @@ -53,5 +39,56 @@ defmodule Pleroma.Web.ActivityPub.UserView do "url" => User.banner_url(user) } } + |> Map.merge(Utils.make_json_ld_header()) + end + + def collection(collection, iri, page) do + offset = (page - 1) * 10 + items = Enum.slice(collection, offset, 10) + items = Enum.map(items, fn (user) -> user.ap_id end) + map = %{ + "id" => "#{iri}?page=#{page}", + "type" => "OrderedCollectionPage", + "partOf" => iri, + "totalItems" => length(collection), + "orderedItems" => items + } + if offset < length(collection) do + Map.put(map, "next", "#{iri}?page=#{page+1}") + end + end + + def render("following.json", %{user: user, page: page}) do + {:ok, following} = User.get_friends(user) + collection(following, "#{user.ap_id}/following", page) + |> Map.merge(Utils.make_json_ld_header()) + end + + def render("following.json", %{user: user}) do + {:ok, following} = User.get_friends(user) + %{ + "id" => "#{user.ap_id}/following", + "type" => "OrderedCollection", + "totalItems" => length(following), + "first" => collection(following, "#{user.ap_id}/following", 1) + } + |> Map.merge(Utils.make_json_ld_header()) + end + + def render("followers.json", %{user: user, page: page}) do + {:ok, followers} = User.get_followers(user) + collection(followers, "#{user.ap_id}/followers", page) + |> Map.merge(Utils.make_json_ld_header()) + end + + def render("followers.json", %{user: user}) do + {:ok, followers} = User.get_followers(user) + %{ + "id" => "#{user.ap_id}/following", + "type" => "OrderedCollection", + "totalItems" => length(followers), + "first" => collection(followers, "#{user.ap_id}/followers", 1) + } + |> Map.merge(Utils.make_json_ld_header()) end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 3e9a8ba7b..e071a469e 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -249,6 +249,14 @@ defmodule Pleroma.Web.Router do plug Pleroma.Web.Plugs.HTTPSignaturePlug end + scope "/", Pleroma.Web.ActivityPub do + # XXX: not really ostatus + pipe_through :ostatus + + get "/users/:nickname/followers", ActivityPubController, :followers + get "/users/:nickname/following", ActivityPubController, :following + end + if @federating do scope "/", Pleroma.Web.ActivityPub do pipe_through :activitypub