From 56aab905e898429b7e2744c3ed2afc96f2a9e97c Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 27 Jun 2025 10:56:04 -0700 Subject: [PATCH 1/2] Queue individual jobs for each user that needs to be deleted when deleting an instance. --- changelog.d/delete-instance.change | 1 + lib/pleroma/instances/instance.ex | 13 ------- lib/pleroma/workers/delete_worker.ex | 11 ++++-- test/pleroma/workers/delete_worker_test.exs | 39 +++++++++++++++++++++ 4 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 changelog.d/delete-instance.change create mode 100644 test/pleroma/workers/delete_worker_test.exs diff --git a/changelog.d/delete-instance.change b/changelog.d/delete-instance.change new file mode 100644 index 000000000..9d84dac54 --- /dev/null +++ b/changelog.d/delete-instance.change @@ -0,0 +1 @@ +Deleting an instance queues individual jobs for each user that needs to be deleted from the server. diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex index 33f1229d0..7bf38deee 100644 --- a/lib/pleroma/instances/instance.ex +++ b/lib/pleroma/instances/instance.ex @@ -9,7 +9,6 @@ defmodule Pleroma.Instances.Instance do alias Pleroma.Instances.Instance alias Pleroma.Maps alias Pleroma.Repo - alias Pleroma.User alias Pleroma.Workers.DeleteWorker use Ecto.Schema @@ -300,16 +299,4 @@ defmodule Pleroma.Instances.Instance do DeleteWorker.new(%{"op" => "delete_instance", "host" => host}) |> Oban.insert() end - - def perform(:delete_instance, host) when is_binary(host) do - User.Query.build(%{nickname: "@#{host}"}) - |> Repo.chunk_stream(100, :batches) - |> Stream.each(fn users -> - users - |> Enum.each(fn user -> - User.perform(:delete, user) - end) - end) - |> Stream.run() - end end diff --git a/lib/pleroma/workers/delete_worker.ex b/lib/pleroma/workers/delete_worker.ex index 6a1c7bb38..4f52edd28 100644 --- a/lib/pleroma/workers/delete_worker.ex +++ b/lib/pleroma/workers/delete_worker.ex @@ -3,7 +3,6 @@ # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Workers.DeleteWorker do - alias Pleroma.Instances.Instance alias Pleroma.User use Oban.Worker, queue: :slow @@ -15,7 +14,15 @@ defmodule Pleroma.Workers.DeleteWorker do end def perform(%Job{args: %{"op" => "delete_instance", "host" => host}}) do - Instance.perform(:delete_instance, host) + Pleroma.Repo.transaction(fn -> + User.Query.build(%{nickname: "@#{host}"}) + |> Pleroma.Repo.all() + |> Enum.each(fn user -> + %{"op" => "delete_user", "user_id" => user.id} + |> __MODULE__.new() + |> Oban.insert() + end) + end) end @impl true diff --git a/test/pleroma/workers/delete_worker_test.exs b/test/pleroma/workers/delete_worker_test.exs new file mode 100644 index 000000000..b914aaee2 --- /dev/null +++ b/test/pleroma/workers/delete_worker_test.exs @@ -0,0 +1,39 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Workers.DeleteWorkerTest do + use Pleroma.DataCase, async: true + use Oban.Testing, repo: Pleroma.Repo + + import Pleroma.Factory + + alias Pleroma.Instances.Instance + alias Pleroma.Tests.ObanHelpers + alias Pleroma.Workers.DeleteWorker + + describe "instance deletion" do + test "creates individual Oban jobs for each user when deleting an instance" do + user1 = insert(:user, nickname: "alice@example.com", name: "Alice") + user2 = insert(:user, nickname: "bob@example.com", name: "Bob") + + {:ok, job} = Instance.delete_users_and_activities("example.com") + + assert_enqueued( + worker: DeleteWorker, + args: %{"op" => "delete_instance", "host" => "example.com"} + ) + + {:ok, :ok} = ObanHelpers.perform(job) + + delete_user_jobs = all_enqueued(worker: DeleteWorker, args: %{"op" => "delete_user"}) + + assert length(delete_user_jobs) == 2 + + user_ids = [user1.id, user2.id] + job_user_ids = Enum.map(delete_user_jobs, fn job -> job.args["user_id"] end) + + assert Enum.sort(user_ids) == Enum.sort(job_user_ids) + end + end +end From ca616e9e73aae9a3f27a44db928bdfe82add21d1 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Fri, 27 Jun 2025 12:10:25 -0700 Subject: [PATCH 2/2] Fix Instance and Admin API controller tests for deleting instances Ensure the job was queued, remove the other test validation. We already prove elsewhere that Pleroma.User.delete/1 works, so repeating that here is a waste. --- test/pleroma/instances/instance_test.exs | 35 +++++-------------- .../controllers/instance_controller_test.exs | 14 ++++---- 2 files changed, 14 insertions(+), 35 deletions(-) diff --git a/test/pleroma/instances/instance_test.exs b/test/pleroma/instances/instance_test.exs index 6a718be21..4b03655cb 100644 --- a/test/pleroma/instances/instance_test.exs +++ b/test/pleroma/instances/instance_test.exs @@ -6,9 +6,8 @@ defmodule Pleroma.Instances.InstanceTest do alias Pleroma.Instances alias Pleroma.Instances.Instance alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers - alias Pleroma.Web.CommonAPI + use Oban.Testing, repo: Pleroma.Repo use Pleroma.DataCase import ExUnit.CaptureLog @@ -213,32 +212,14 @@ defmodule Pleroma.Instances.InstanceTest do end end - test "delete_users_and_activities/1 deletes remote instance users and activities" do - [mario, luigi, _peach, wario] = - users = [ - insert(:user, nickname: "mario@mushroom.kingdom", name: "Mario"), - insert(:user, nickname: "luigi@mushroom.kingdom", name: "Luigi"), - insert(:user, nickname: "peach@mushroom.kingdom", name: "Peach"), - insert(:user, nickname: "wario@greedville.biz", name: "Wario") - ] + test "delete_users_and_activities/1 schedules a job to delete the instance and users" do + insert(:user, nickname: "mario@mushroom.kingdom", name: "Mario") - {:ok, post1} = CommonAPI.post(mario, %{status: "letsa go!"}) - {:ok, post2} = CommonAPI.post(luigi, %{status: "itsa me... luigi"}) - {:ok, post3} = CommonAPI.post(wario, %{status: "WHA-HA-HA!"}) + {:ok, _job} = Instance.delete_users_and_activities("mushroom.kingdom") - {:ok, job} = Instance.delete_users_and_activities("mushroom.kingdom") - :ok = ObanHelpers.perform(job) - - [mario, luigi, peach, wario] = Repo.reload(users) - - refute mario.is_active - refute luigi.is_active - refute peach.is_active - refute peach.name == "Peach" - - assert wario.is_active - assert wario.name == "Wario" - - assert [nil, nil, %{}] = Repo.reload([post1, post2, post3]) + assert_enqueued( + worker: Pleroma.Workers.DeleteWorker, + args: %{"op" => "delete_instance", "host" => "mushroom.kingdom"} + ) end end diff --git a/test/pleroma/web/admin_api/controllers/instance_controller_test.exs b/test/pleroma/web/admin_api/controllers/instance_controller_test.exs index 6cca623f3..5adcd069d 100644 --- a/test/pleroma/web/admin_api/controllers/instance_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/instance_controller_test.exs @@ -8,8 +8,6 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do import Pleroma.Factory - alias Pleroma.Repo - alias Pleroma.Tests.ObanHelpers alias Pleroma.Web.CommonAPI setup_all do @@ -69,19 +67,19 @@ defmodule Pleroma.Web.AdminAPI.InstanceControllerTest do test "DELETE /instances/:instance", %{conn: conn} do clear_config([:instance, :admin_privileges], [:instances_delete]) - user = insert(:user, nickname: "lain@lain.com") - post = insert(:note_activity, user: user) + insert(:user, nickname: "lain@lain.com") response = conn |> delete("/api/pleroma/admin/instances/lain.com") |> json_response(200) - [:ok] = ObanHelpers.perform_all() - assert response == "lain.com" - refute Repo.reload(user).is_active - refute Repo.reload(post) + + assert_enqueued( + worker: Pleroma.Workers.DeleteWorker, + args: %{"op" => "delete_instance", "host" => "lain.com"} + ) clear_config([:instance, :admin_privileges], [])