diff --git a/lib/clacks/activitypub.ex b/lib/clacks/activitypub.ex index e667f2c..751e339 100644 --- a/lib/clacks/activitypub.ex +++ b/lib/clacks/activitypub.ex @@ -95,6 +95,17 @@ defmodule Clacks.ActivityPub do } end + @spec accept_follow(follow_activity :: map()) :: map() + def accept_follow(%{"type" => "Follow", "object" => followed} = follow_activity) do + %{ + "@context" => @context, + "type" => "Accept", + "id" => activity_id(Ecto.UUID.generate()), + "actor" => followed, + "object" => follow_activity + } + end + @spec object_id(id :: String.t()) :: String.t() def object_id(id) do url = Application.get_env(:clacks, ClacksWeb.Endpoint)[:url] diff --git a/lib/clacks/inbox.ex b/lib/clacks/inbox.ex index 154f690..6abfbbe 100644 --- a/lib/clacks/inbox.ex +++ b/lib/clacks/inbox.ex @@ -1,24 +1,53 @@ defmodule Clacks.Inbox do - alias Clacks.{Repo, Activity, Object, Actor} - - def handle_create(%{"type" => "Create", "object" => object, "actor" => actor} = activity) do - changeset = Object.changeset(%Object{}, %{data: object}) - {:ok, _object} = Repo.insert(changeset) + require Logger + alias Clacks.{Repo, Activity, Object, Actor, ActivityPub} + defp store_activity(%{"actor" => actor} = activity, local \\ false) when is_binary(actor) do changeset = Activity.changeset(%Activity{}, %{ data: activity, - local: false, + local: local, actor: actor }) - {:ok, _activity} = Repo.insert(changeset) + Repo.insert(changeset) end - def handle_follow(%{"type" => "Follow", "object" => followed_id, "actor" => follower}) + def handle_create(%{"type" => "Create", "object" => object} = activity) do + changeset = Object.changeset(%Object{}, %{data: object}) + {:ok, _object} = Repo.insert(changeset) + + {:ok, _activity} = store_activity(activity) + end + + @spec handle_follow(activity :: map()) :: :ok | {:error, reason :: any()} + def handle_follow( + %{"type" => "Follow", "object" => followed_id, "actor" => follower_id} = activity + ) when is_binary(followed_id) do followed = Actor.get_by_ap_id(followed_id) - changeset = Actor.changeset(followed, %{followers: [follower | followed.followers]}) - {:ok, _followed} = Repo.update(changeset) + follower = Actor.get_by_ap_id(follower_id) + + store_activity(activity) + + changeset = Actor.changeset(followed, %{followers: [follower_id | followed.followers]}) + + case Repo.update(changeset) do + {:error, changeset} -> + Logger.error("Couldn't store Follow activity: #{inspect(changeset)}") + {:error, "Couldn't store Follow activity"} + + {:ok, _followed} -> + accept = ActivityPub.accept_follow(activity) + + case store_activity(accept, true) do + {:error, changeset} -> + Logger.error("Couldn't store Accept activity: #{inspect(changeset)}") + {:error, "Couldn't store Accept activity"} + + {:ok, _accept} -> + ActivityPub.Federator.federate(accept, follower.data["inbox"]) + end + end end end diff --git a/lib/clacks_web/controllers/inbox_controller.ex b/lib/clacks_web/controllers/inbox_controller.ex index 30f2589..573d175 100644 --- a/lib/clacks_web/controllers/inbox_controller.ex +++ b/lib/clacks_web/controllers/inbox_controller.ex @@ -1,4 +1,5 @@ defmodule ClacksWeb.InboxController do + require Logger use ClacksWeb, :controller alias Clacks.Inbox @@ -18,13 +19,22 @@ defmodule ClacksWeb.InboxController do conn |> put_status(200) + |> json(%{}) end def handle(conn, %{"type" => "Follow", "object" => object} = activity) when is_binary(object) do - Inbox.handle_follow(activity) + case Inbox.handle_follow(activity) do + :ok -> + conn + |> put_status(200) - conn - |> put_status(200) + {:error, reason} -> + Logger.error("Could not handle incoming follow: #{inspect(reason)}") + + conn + |> put_status(500) + end + |> json(%{}) end # def handle(conn, _) do