diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex index 45bd6070a..f3a075491 100644 --- a/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex +++ b/lib/pleroma/ecto_type/activity_pub/object_validators/object_id.ex @@ -19,7 +19,7 @@ def cast(object) when is_binary(object) do def cast(%{"id" => object}), do: cast(object) - def cast(_), do: :error + def cast(o), do: {:error, o} def dump(data), do: {:ok, data} diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index bf8717ffb..e0136953f 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -152,6 +152,7 @@ defmodule Pleroma.User do field(:also_known_as, {:array, ObjectValidators.ObjectID}, default: []) field(:inbox, :string) field(:shared_inbox, :string) + field(:outbox, :string, default: nil) field(:last_active_at, :naive_datetime) field(:disclose_client, :boolean, default: true) field(:pinned_objects, :map, default: %{}) @@ -469,6 +470,7 @@ def remote_user_changeset(struct \\ %User{local: false}, params) do :ap_id, :inbox, :shared_inbox, + :outbox, :nickname, :avatar, :ap_enabled, diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 9b28e64d9..869fefa17 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1587,7 +1587,8 @@ defp object_to_user_data(data, additional) do actor_type = data["type"] || "Person" featured_address = data["featured"] - {:ok, pinned_objects} = fetch_and_prepare_featured_from_ap_id(featured_address) + {:ok, pinned_objects} = fetch_and_prepare_outbox_from_ap_id(featured_address) + outbox_address = data["outbox"] # first, check that the owner is correct signing_key = @@ -1644,6 +1645,7 @@ defp object_to_user_data(data, additional) do also_known_as: also_known_as, signing_key: signing_key, inbox: data["inbox"], + outbox: outbox_address, shared_inbox: shared_inbox, pinned_objects: pinned_objects, nickname: nickname @@ -1790,7 +1792,7 @@ def maybe_handle_clashing_nickname(data) do end end - def pin_data_from_featured_collection(%{ + def activity_data_from_outbox_collection(%{ "type" => "OrderedCollection", "first" => first }) do @@ -1805,7 +1807,7 @@ def pin_data_from_featured_collection(%{ end end - def pin_data_from_featured_collection( + def activity_data_from_outbox_collection( %{ "type" => type } = collection @@ -1821,21 +1823,23 @@ def pin_data_from_featured_collection( end) end - def pin_data_from_featured_collection(obj) do - Logger.error("Could not parse featured collection #{inspect(obj)}") + def activity_data_from_outbox_collection(obj) do + Logger.error("Could not parse outbox collection #{inspect(obj)}") %{} end - def fetch_and_prepare_featured_from_ap_id(nil) do + def fetch_and_prepare_outbox_from_ap_id(nil) do {:ok, %{}} end - def fetch_and_prepare_featured_from_ap_id(ap_id) do + def fetch_and_prepare_outbox_from_ap_id(ap_id) do + Logger.info("Fetching outbox #{ap_id}") + with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do - {:ok, pin_data_from_featured_collection(data)} + {:ok, activity_data_from_outbox_collection(data)} else e -> - Logger.error("Could not decode featured collection at fetch #{ap_id}, #{inspect(e)}") + Logger.error("Could not decode outbox collection at fetch #{ap_id}, #{inspect(e)}") {:ok, %{}} end end @@ -1854,6 +1858,19 @@ def enqueue_pin_fetches(%{pinned_objects: pins}) do def enqueue_pin_fetches(_), do: nil + def enqueue_outbox_fetches(%{outbox: outbox_address}) do + # enqueue a task to fetch the outbox + Logger.debug("Refetching outbox #{outbox_address}") + + Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_outbox", %{ + "id" => outbox_address + }) + + :ok + end + + def enqueue_outbox_fetches(_), do: :error + def make_user_from_ap_id(ap_id, additional \\ []) do user = User.get_cached_by_ap_id(ap_id) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 75c1f0f0c..b6163a571 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -637,7 +637,10 @@ defp handle_incoming_normalised( end end - defp handle_incoming_normalised(_, _), do: :error + defp handle_incoming_normalised(%{"object" => o}, options), + do: handle_incoming_normalised(o, options) + + defp handle_incoming_normalised(o, _), do: {:error, {:unknown_object_class, o}} @spec get_obj_helper(String.t(), Keyword.t()) :: {:ok, Object.t()} | nil def get_obj_helper(id, options \\ []) do diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex index 2c7d6a893..f5a46f361 100644 --- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex @@ -306,6 +306,8 @@ def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id} = params) d def statuses(%{assigns: %{user: reading_user}} = conn, params) do with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user), :visible <- User.visible_for(user, reading_user) do + ActivityPub.enqueue_outbox_fetches(user) + params = params |> Map.delete(:tagged) diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex index 1bc0e5d0c..241d76f3d 100644 --- a/lib/pleroma/workers/remote_fetcher_worker.ex +++ b/lib/pleroma/workers/remote_fetcher_worker.ex @@ -4,12 +4,32 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do alias Pleroma.Object.Fetcher + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.ActivityPub use Pleroma.Workers.WorkerHelper, queue: "remote_fetcher", unique: [period: 300, states: Oban.Job.states(), keys: [:op, :id]] @impl Oban.Worker + + def perform(%Job{args: %{"op" => "fetch_outbox", "id" => address}}) do + with {:ok, outbox} <- ActivityPub.fetch_and_prepare_outbox_from_ap_id(address) do + Enum.each(Enum.reverse(outbox), fn {ap_id, _} -> + if is_nil(Object.get_cached_by_ap_id(ap_id)) do + enqueue("fetch_remote", %{ + "id" => ap_id, + "depth" => 1 + }) + end + end) + + :ok + else + e -> {:error, e} + end + end + def perform(%Job{args: %{"op" => "fetch_remote", "id" => id} = args}) do case Fetcher.fetch_object_from_id(id, depth: args["depth"]) do {:ok, _object} -> diff --git a/priv/repo/migrations/20241027040000_users_add_outboxes.exs b/priv/repo/migrations/20241027040000_users_add_outboxes.exs new file mode 100644 index 000000000..cf4ab76cf --- /dev/null +++ b/priv/repo/migrations/20241027040000_users_add_outboxes.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.UsersAddOutboxes do + use Ecto.Migration + + def up do + alter table(:users) do + add_if_not_exists(:outbox, :text) + end + end + + def down do + alter table(:users) do + remove_if_exists(:outbox, :text) + end + end +end