diff --git a/lib/activity_pub.ex b/lib/activity_pub.ex index 79d8e8d..ef64556 100644 --- a/lib/activity_pub.ex +++ b/lib/activity_pub.ex @@ -1,2 +1,15 @@ defmodule ActivityPub do + def fetch_actor(actor_id) do + request = + Req.new(url: actor_id) + |> Req.Request.put_header("accept", "application/json") + + case Req.get(request) do + {:ok, result} -> + {:ok, result.body} + + error -> + error + end + end end diff --git a/lib/activity_pub/headers.ex b/lib/activity_pub/headers.ex index 24025a9..bde3cdb 100644 --- a/lib/activity_pub/headers.ex +++ b/lib/activity_pub/headers.ex @@ -85,13 +85,9 @@ defmodule ActivityPub.Headers do end def fetch_actor_key(key_id) do - request = - Req.new(url: key_id) - |> Req.Request.put_header("accept", "application/json") - - case Req.get(request) do - {:ok, result} -> - key_map = result.body["publicKey"] + case ActivityPub.fetch_actor(key_id) do + {:ok, body} -> + key_map = body["publicKey"] if key_map["id"] == key_id do [public_key | _] = diff --git a/lib/postland/activities.ex b/lib/postland/activities.ex index 5dbbb0b..14ede6c 100644 --- a/lib/postland/activities.ex +++ b/lib/postland/activities.ex @@ -1,8 +1,11 @@ defmodule Postland.Activities do use Phoenix.VerifiedRoutes, endpoint: PostlandWeb.Endpoint, router: PostlandWeb.Router + require Logger + alias Postland.Activity alias Postland.Repo + alias Postland.Follows def process_activity(params) do case record_activity(params) do @@ -20,6 +23,20 @@ defmodule Postland.Activities do |> Repo.insert() end + def cause_effects(%Activity{actor_id: actor_id, type: "Accept", data: %{"object" => %{"type" => "Follow"}}} = activity) do + case Follows.get(Postland.my_actor_id(), actor_id) do + nil -> + Logger.warning("Got accept for a follow we don't have in the db: #{actor_id}") + + {:ok, activity} + + request -> + Follows.confirm_request(request) + end + + {:ok, activity} + end + def cause_effects(%Activity{actor_id: actor_id, type: "Follow", data: %{"object" => object}} = activity) do if object == Postland.my_actor_id() do case Postland.Follows.record_inbound_request(actor_id) do diff --git a/lib/postland/follow.ex b/lib/postland/follow.ex index da31a9d..cc5f6bc 100644 --- a/lib/postland/follow.ex +++ b/lib/postland/follow.ex @@ -3,6 +3,7 @@ defmodule Postland.Follow do import Ecto.Changeset + @primary_key false schema "follows" do field :follower, :string, primary_key: true field :followee, :string, primary_key: true diff --git a/lib/postland/follows.ex b/lib/postland/follows.ex index 19abe8a..4bdf3fd 100644 --- a/lib/postland/follows.ex +++ b/lib/postland/follows.ex @@ -1,8 +1,47 @@ defmodule Postland.Follows do + use Phoenix.VerifiedRoutes, endpoint: PostlandWeb.Endpoint, router: PostlandWeb.Router + import Ecto.Query, warn: false + alias Postland.Accounts alias Postland.Follow alias Postland.Repo + alias ActivityPub.Headers + + alias Ecto.Multi + + def record_and_send_follow_request(to_actor_id) do + Multi.new() + |> Multi.run(:follow_record, fn _data, _repo -> record_outbound_request(to_actor_id) end) + |> Multi.run(:send_request, fn _data, _repo -> + send_follow_request(to_actor_id) + end) + |> Repo.transaction() + end + + def send_follow_request(to_actor_id) do + encoded_followee = Base.url_encode64(to_actor_id) + encoded_follower = Base.url_encode64(Postland.my_actor_id()) + + actor = ActivityPub.fetch_actor(to_actor_id) + inbox = Map.get(actor, "inbox") + follow_request = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "id" => url(~p"/follows/#{encoded_followee}/#{encoded_follower}"), + "type" => "Follow", + "actor" => Postland.my_actor_id(), + "object" => to_actor_id + } + |> Jason.encode!() + + headers = Headers.signing_headers("POST", inbox, follow_request, Postland.my_actor_id(), Accounts.solo_user().private_key) + + Req.post(inbox, headers: headers, body: follow_request) + end + + def get(follower, followee) do + from(f in Follow, where: f.followee == ^followee, where: f.follower == ^follower) |> Repo.one() + end def pending_inbound_requests() do my_actor_id = Postland.my_actor_id()