From 12ebeef1300b70e44b2aa25dbffeb82df261e26d Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 7 Oct 2019 19:36:10 +0700 Subject: [PATCH 01/43] Add CreateFollowingRelationships migration --- ...7073319_create_following_relationships.exs | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 priv/repo/migrations/20191007073319_create_following_relationships.exs diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs new file mode 100644 index 000000000..7b8a2f9bd --- /dev/null +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -0,0 +1,92 @@ +defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do + use Ecto.Migration + + # had to disable these to be able to restore `following` index concurrently + # https://hexdocs.pm/ecto_sql/Ecto.Migration.html#index/3-adding-dropping-indexes-concurrently + @disable_ddl_transaction true + @disable_migration_lock true + + def change do + create_if_not_exists table(:following_relationships) do + add(:follower_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) + add(:following_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) + add(:state, :string, null: false) + + timestamps() + end + + create_if_not_exists(index(:following_relationships, :follower_id)) + create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id])) + + execute(insert_following_relationships_rows(), restore_following_column()) + + drop(index(:users, [:following], concurrently: true, using: :gin)) + + alter table(:users) do + remove(:following, {:array, :string}, default: []) + end + end + + defp insert_following_relationships_rows do + """ + INSERT INTO + following_relationships ( + follower_id, + following_id, + state, + inserted_at, + updated_at + ) + SELECT + followers.id, + following.id, + activities.data ->> 'state', + (activities.data ->> 'published') :: timestamp, + now() + FROM + activities + JOIN users AS followers ON (activities.actor = followers.ap_id) + JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) + WHERE + activities.data ->> 'type' = 'Follow' + AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') ON CONFLICT DO NOTHING + """ + end + + defp restore_following_column do + """ + UPDATE + users + SET + following = following_query.following, + updated_at = now() + FROM + ( + SELECT + followers.id AS follower_id, + array_prepend( + followers.follower_address, + array_agg(DISTINCT following.ap_id) FILTER ( + WHERE + following.ap_id IS NOT NULL + ) + ) as following + FROM + users AS followers + LEFT OUTER JOIN activities ON ( + followers.ap_id = activities.actor + AND activities.data ->> 'type' = 'Follow' + AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') + ) + LEFT OUTER JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) + WHERE + followers.email IS NOT NULL -- Exclude `internal/fetch` and `relay` + GROUP BY + followers.id, + followers.ap_id + ) AS following_query + WHERE + following_query.follower_id = users.id; + """ + end +end From 6291eaa5902d5c1565e8969117fdf92c373716b6 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 8 Oct 2019 13:06:48 +0700 Subject: [PATCH 02/43] Update CreateFollowingRelationships --- ...7073319_create_following_relationships.exs | 79 ++++++++++++------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs index 7b8a2f9bd..fe229240b 100644 --- a/priv/repo/migrations/20191007073319_create_following_relationships.exs +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -18,7 +18,8 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do create_if_not_exists(index(:following_relationships, :follower_id)) create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id])) - execute(insert_following_relationships_rows(), restore_following_column()) + execute(import_following_from_users(), "") + execute(import_following_from_activities(), restore_following_column()) drop(index(:users, [:following], concurrently: true, using: :gin)) @@ -27,7 +28,33 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do end end - defp insert_following_relationships_rows do + defp import_following_from_users do + """ + INSERT INTO following_relationships (follower_id, following_id, state, inserted_at, updated_at) + SELECT + relations.follower_id, + following.id, + 'accept', + now(), + now() + FROM ( + SELECT + users.id AS follower_id, + unnest(users.following) AS following_ap_id + FROM + users + WHERE + users.following != '{}' + AND users.local = false OR users.local = true AND users.email IS NOT NULL -- Exclude `internal/fetch` and `relay` + ) AS relations + JOIN users AS "following" ON "following".follower_address = relations.following_ap_id + + WHERE relations.follower_id != following.id + ON CONFLICT DO NOTHING + """ + end + + defp import_following_from_activities do """ INSERT INTO following_relationships ( @@ -49,7 +76,9 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) WHERE activities.data ->> 'type' = 'Follow' - AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') ON CONFLICT DO NOTHING + AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') + ORDER BY activities.updated_at DESC + ON CONFLICT DO NOTHING """ end @@ -58,35 +87,25 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do UPDATE users SET - following = following_query.following, + following = following_query.following_array, updated_at = now() - FROM - ( - SELECT - followers.id AS follower_id, - array_prepend( - followers.follower_address, - array_agg(DISTINCT following.ap_id) FILTER ( - WHERE - following.ap_id IS NOT NULL - ) - ) as following - FROM - users AS followers - LEFT OUTER JOIN activities ON ( - followers.ap_id = activities.actor - AND activities.data ->> 'type' = 'Follow' - AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') - ) - LEFT OUTER JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) - WHERE - followers.email IS NOT NULL -- Exclude `internal/fetch` and `relay` - GROUP BY - followers.id, - followers.ap_id - ) AS following_query + FROM ( + SELECT + follwer.id AS follower_id, + CASE follwer.local + WHEN TRUE THEN + array_prepend(follwer.follower_address, array_agg(following.follower_address)) + ELSE + array_agg(following.follower_address) + END AS following_array + FROM + following_relationships + JOIN users AS follwer ON follwer.id = following_relationships.follower_id + JOIN users AS FOLLOWING ON following.id = following_relationships.following_id + GROUP BY + follwer.id) AS following_query WHERE - following_query.follower_id = users.id; + following_query.follower_id = users.id """ end end From 761ad0b48e552e7b199fe1a624e3d03aa91981a3 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 8 Oct 2019 20:27:42 +0700 Subject: [PATCH 03/43] Split CreateFollowingRelationships to multiple migrations --- ...7073319_create_following_relationships.exs | 92 ------------------- ...132217_migrate_following_relationships.exs | 89 ++++++++++++++++++ .../20191008132427_drop_users_following.exs | 14 +++ 3 files changed, 103 insertions(+), 92 deletions(-) create mode 100644 priv/repo/migrations/20191008132217_migrate_following_relationships.exs create mode 100644 priv/repo/migrations/20191008132427_drop_users_following.exs diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs index fe229240b..8ffc870d9 100644 --- a/priv/repo/migrations/20191007073319_create_following_relationships.exs +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -3,8 +3,6 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do # had to disable these to be able to restore `following` index concurrently # https://hexdocs.pm/ecto_sql/Ecto.Migration.html#index/3-adding-dropping-indexes-concurrently - @disable_ddl_transaction true - @disable_migration_lock true def change do create_if_not_exists table(:following_relationships) do @@ -17,95 +15,5 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do create_if_not_exists(index(:following_relationships, :follower_id)) create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id])) - - execute(import_following_from_users(), "") - execute(import_following_from_activities(), restore_following_column()) - - drop(index(:users, [:following], concurrently: true, using: :gin)) - - alter table(:users) do - remove(:following, {:array, :string}, default: []) - end - end - - defp import_following_from_users do - """ - INSERT INTO following_relationships (follower_id, following_id, state, inserted_at, updated_at) - SELECT - relations.follower_id, - following.id, - 'accept', - now(), - now() - FROM ( - SELECT - users.id AS follower_id, - unnest(users.following) AS following_ap_id - FROM - users - WHERE - users.following != '{}' - AND users.local = false OR users.local = true AND users.email IS NOT NULL -- Exclude `internal/fetch` and `relay` - ) AS relations - JOIN users AS "following" ON "following".follower_address = relations.following_ap_id - - WHERE relations.follower_id != following.id - ON CONFLICT DO NOTHING - """ - end - - defp import_following_from_activities do - """ - INSERT INTO - following_relationships ( - follower_id, - following_id, - state, - inserted_at, - updated_at - ) - SELECT - followers.id, - following.id, - activities.data ->> 'state', - (activities.data ->> 'published') :: timestamp, - now() - FROM - activities - JOIN users AS followers ON (activities.actor = followers.ap_id) - JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) - WHERE - activities.data ->> 'type' = 'Follow' - AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') - ORDER BY activities.updated_at DESC - ON CONFLICT DO NOTHING - """ - end - - defp restore_following_column do - """ - UPDATE - users - SET - following = following_query.following_array, - updated_at = now() - FROM ( - SELECT - follwer.id AS follower_id, - CASE follwer.local - WHEN TRUE THEN - array_prepend(follwer.follower_address, array_agg(following.follower_address)) - ELSE - array_agg(following.follower_address) - END AS following_array - FROM - following_relationships - JOIN users AS follwer ON follwer.id = following_relationships.follower_id - JOIN users AS FOLLOWING ON following.id = following_relationships.following_id - GROUP BY - follwer.id) AS following_query - WHERE - following_query.follower_id = users.id - """ end end diff --git a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs new file mode 100644 index 000000000..7f996f5a5 --- /dev/null +++ b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs @@ -0,0 +1,89 @@ +defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships do + use Ecto.Migration + + def change do + execute(import_following_from_users(), "") + execute(import_following_from_activities(), restore_following_column()) + end + + defp import_following_from_users do + """ + INSERT INTO following_relationships (follower_id, following_id, state, inserted_at, updated_at) + SELECT + relations.follower_id, + following.id, + 'accept', + now(), + now() + FROM ( + SELECT + users.id AS follower_id, + unnest(users.following) AS following_ap_id + FROM + users + WHERE + users.following != '{}' + AND users.local = false OR users.local = true AND users.email IS NOT NULL -- Exclude `internal/fetch` and `relay` + ) AS relations + JOIN users AS "following" ON "following".follower_address = relations.following_ap_id + + WHERE relations.follower_id != following.id + ON CONFLICT DO NOTHING + """ + end + + defp import_following_from_activities do + """ + INSERT INTO + following_relationships ( + follower_id, + following_id, + state, + inserted_at, + updated_at + ) + SELECT + followers.id, + following.id, + activities.data ->> 'state', + (activities.data ->> 'published') :: timestamp, + now() + FROM + activities + JOIN users AS followers ON (activities.actor = followers.ap_id) + JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) + WHERE + activities.data ->> 'type' = 'Follow' + AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') + ORDER BY activities.updated_at DESC + ON CONFLICT DO NOTHING + """ + end + + defp restore_following_column do + """ + UPDATE + users + SET + following = following_query.following_array, + updated_at = now() + FROM ( + SELECT + follwer.id AS follower_id, + CASE follwer.local + WHEN TRUE THEN + array_prepend(follwer.follower_address, array_agg(following.follower_address)) + ELSE + array_agg(following.follower_address) + END AS following_array + FROM + following_relationships + JOIN users AS follwer ON follwer.id = following_relationships.follower_id + JOIN users AS FOLLOWING ON following.id = following_relationships.following_id + GROUP BY + follwer.id) AS following_query + WHERE + following_query.follower_id = users.id + """ + end +end diff --git a/priv/repo/migrations/20191008132427_drop_users_following.exs b/priv/repo/migrations/20191008132427_drop_users_following.exs new file mode 100644 index 000000000..17805db36 --- /dev/null +++ b/priv/repo/migrations/20191008132427_drop_users_following.exs @@ -0,0 +1,14 @@ +defmodule Pleroma.Repo.Migrations.DropUsersFollowing do + use Ecto.Migration + + @disable_ddl_transaction true + @disable_migration_lock true + + def change do + drop(index(:users, [:following], concurrently: true, using: :gin)) + + alter table(:users) do + remove(:following, {:array, :string}, default: []) + end + end +end From 2c7ff32e5b95926af9b6573a6dc6ea96e3ba7dd5 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Thu, 10 Oct 2019 21:11:34 +0700 Subject: [PATCH 04/43] Add `thread_visibility` to migrations --- ...7073319_create_following_relationships.exs | 136 +++++++++++++++++- .../20191008132427_drop_users_following.exs | 2 + 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs index 8ffc870d9..7daaf0575 100644 --- a/priv/repo/migrations/20191007073319_create_following_relationships.exs +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -1,9 +1,6 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do use Ecto.Migration - # had to disable these to be able to restore `following` index concurrently - # https://hexdocs.pm/ecto_sql/Ecto.Migration.html#index/3-adding-dropping-indexes-concurrently - def change do create_if_not_exists table(:following_relationships) do add(:follower_id, references(:users, type: :uuid, on_delete: :delete_all), null: false) @@ -15,5 +12,138 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do create_if_not_exists(index(:following_relationships, :follower_id)) create_if_not_exists(unique_index(:following_relationships, [:follower_id, :following_id])) + + execute(update_thread_visibility(), restore_thread_visibility()) + end + + # The only difference with the original verion: `actor_user` replaced with `actor_user_following` + def update_thread_visibility do + """ + CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$ + DECLARE + public varchar := 'https://www.w3.org/ns/activitystreams#Public'; + child objects%ROWTYPE; + activity activities%ROWTYPE; + author_fa varchar; + valid_recipients varchar[]; + actor_user_following varchar[]; + BEGIN + --- Fetch actor following + SELECT array_agg(following.follower_address) INTO actor_user_following FROM following_relationships + JOIN users ON users.id = following_relationships.follower_id + JOIN users AS following ON following.id = following_relationships.following_id + WHERE users.ap_id = actor; + + --- Fetch our initial activity. + SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id; + + LOOP + --- Ensure that we have an activity before continuing. + --- If we don't, the thread is not satisfiable. + IF activity IS NULL THEN + RETURN false; + END IF; + + --- We only care about Create activities. + IF activity.data->>'type' != 'Create' THEN + RETURN true; + END IF; + + --- Normalize the child object into child. + SELECT * INTO child FROM objects + INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id'; + + --- Fetch the author's AS2 following collection. + SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor; + + --- Prepare valid recipients array. + valid_recipients := ARRAY[actor, public]; + IF ARRAY[author_fa] && actor_user_following THEN + valid_recipients := valid_recipients || author_fa; + END IF; + + --- Check visibility. + IF NOT valid_recipients && activity.recipients THEN + --- activity not visible, break out of the loop + RETURN false; + END IF; + + --- If there's a parent, load it and do this all over again. + IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN + SELECT * INTO activity FROM activities + INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE child.data->>'inReplyTo' = objects.data->>'id'; + ELSE + RETURN true; + END IF; + END LOOP; + END; + $$ LANGUAGE plpgsql IMMUTABLE; + """ + end + + # priv/repo/migrations/20190515222404_add_thread_visibility_function.exs + def restore_thread_visibility do + """ + CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$ + DECLARE + public varchar := 'https://www.w3.org/ns/activitystreams#Public'; + child objects%ROWTYPE; + activity activities%ROWTYPE; + actor_user users%ROWTYPE; + author_fa varchar; + valid_recipients varchar[]; + BEGIN + --- Fetch our actor. + SELECT * INTO actor_user FROM users WHERE users.ap_id = actor; + + --- Fetch our initial activity. + SELECT * INTO activity FROM activities WHERE activities.data->>'id' = activity_id; + + LOOP + --- Ensure that we have an activity before continuing. + --- If we don't, the thread is not satisfiable. + IF activity IS NULL THEN + RETURN false; + END IF; + + --- We only care about Create activities. + IF activity.data->>'type' != 'Create' THEN + RETURN true; + END IF; + + --- Normalize the child object into child. + SELECT * INTO child FROM objects + INNER JOIN activities ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE COALESCE(activity.data->'object'->>'id', activity.data->>'object') = objects.data->>'id'; + + --- Fetch the author's AS2 following collection. + SELECT COALESCE(users.follower_address, '') INTO author_fa FROM users WHERE users.ap_id = activity.actor; + + --- Prepare valid recipients array. + valid_recipients := ARRAY[actor, public]; + IF ARRAY[author_fa] && actor_user.following THEN + valid_recipients := valid_recipients || author_fa; + END IF; + + --- Check visibility. + IF NOT valid_recipients && activity.recipients THEN + --- activity not visible, break out of the loop + RETURN false; + END IF; + + --- If there's a parent, load it and do this all over again. + IF (child.data->'inReplyTo' IS NOT NULL) AND (child.data->'inReplyTo' != 'null'::jsonb) THEN + SELECT * INTO activity FROM activities + INNER JOIN objects ON COALESCE(activities.data->'object'->>'id', activities.data->>'object') = objects.data->>'id' + WHERE child.data->>'inReplyTo' = objects.data->>'id'; + ELSE + RETURN true; + END IF; + END LOOP; + END; + $$ LANGUAGE plpgsql IMMUTABLE; + """ end end diff --git a/priv/repo/migrations/20191008132427_drop_users_following.exs b/priv/repo/migrations/20191008132427_drop_users_following.exs index 17805db36..21c0af9f4 100644 --- a/priv/repo/migrations/20191008132427_drop_users_following.exs +++ b/priv/repo/migrations/20191008132427_drop_users_following.exs @@ -1,6 +1,8 @@ defmodule Pleroma.Repo.Migrations.DropUsersFollowing do use Ecto.Migration + # had to disable these to be able to restore `following` index concurrently + # https://hexdocs.pm/ecto_sql/Ecto.Migration.html#index/3-adding-dropping-indexes-concurrently @disable_ddl_transaction true @disable_migration_lock true From 059005ff829c0313c62ddf5fbcd95f8892920228 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 11 Oct 2019 02:35:32 +0700 Subject: [PATCH 05/43] Replace `user.following` with Pleroma.FollowingRelationship --- lib/mix/tasks/pleroma/database.ex | 6 +- lib/mix/tasks/pleroma/relay.ex | 5 +- lib/mix/tasks/pleroma/user.ex | 2 +- lib/pleroma/bbs/handler.ex | 2 +- lib/pleroma/following_relationship.ex | 110 ++++++++++++++++++ lib/pleroma/user.ex | 99 ++++------------ lib/pleroma/user/query.ex | 34 +++++- lib/pleroma/web/activity_pub/activity_pub.ex | 6 +- .../activity_pub/activity_pub_controller.ex | 4 +- .../web/activity_pub/transmogrifier.ex | 63 ++++------ lib/pleroma/web/activity_pub/visibility.ex | 2 +- lib/pleroma/web/common_api/common_api.ex | 3 + .../controllers/timeline_controller.ex | 8 +- .../controllers/account_controller.ex | 2 +- ...7073319_create_following_relationships.exs | 2 +- test/support/factory.ex | 3 +- test/tasks/database_test.exs | 8 +- test/tasks/relay_test.exs | 24 ++-- test/tasks/user_test.exs | 5 +- test/user_test.exs | 81 +++++-------- test/web/activity_pub/activity_pub_test.exs | 6 +- test/web/activity_pub/relay_test.exs | 5 +- test/web/activity_pub/transmogrifier_test.exs | 7 +- test/web/activity_pub/visibilty_test.exs | 3 +- .../controllers/account_controller_test.exs | 2 +- .../follow_request_controller_test.exs | 5 +- test/web/streamer/streamer_test.exs | 9 +- test/web/twitter_api/util_controller_test.exs | 4 +- 28 files changed, 275 insertions(+), 235 deletions(-) create mode 100644 lib/pleroma/following_relationship.ex diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index cfd9eeada..72b706e1a 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -52,9 +52,9 @@ defmodule Mix.Tasks.Pleroma.Database do def run(["update_users_following_followers_counts"]) do start_pleroma() - users = Repo.all(User) - Enum.each(users, &User.remove_duplicated_following/1) - Enum.each(users, &User.update_follower_count/1) + User + |> Repo.all() + |> Enum.each(&User.update_follower_count/1) end def run(["prune_objects" | args]) do diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index d7a7b599f..eafddada6 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -36,8 +36,9 @@ defmodule Mix.Tasks.Pleroma.Relay do def run(["list"]) do start_pleroma() - with %User{following: following} = _user <- Relay.get_actor() do - following + with %User{} = user <- Relay.get_actor() do + user + |> User.following() |> Enum.map(fn entry -> URI.parse(entry).host end) |> Enum.uniq() |> Enum.each(&shell_info(&1)) diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 134b5bccc..8866afdf6 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -162,7 +162,7 @@ defmodule Mix.Tasks.Pleroma.User do user = User.get_cached_by_id(user.id) - if Enum.empty?(user.following) do + if Enum.empty?(User.get_friends(user)) do shell_info("Successfully unsubscribed all followers from #{user.nickname}") end else diff --git a/lib/pleroma/bbs/handler.ex b/lib/pleroma/bbs/handler.ex index fa838a4e4..b0e9ebbd0 100644 --- a/lib/pleroma/bbs/handler.ex +++ b/lib/pleroma/bbs/handler.ex @@ -97,7 +97,7 @@ defmodule Pleroma.BBS.Handler do |> Map.put("user", user) activities = - [user.ap_id | user.following] + [user.ap_id | Pleroma.User.following(user)] |> ActivityPub.fetch_activities(params) Enum.each(activities, fn activity -> diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex new file mode 100644 index 000000000..0d789b5a6 --- /dev/null +++ b/lib/pleroma/following_relationship.ex @@ -0,0 +1,110 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.FollowingRelationship do + use Ecto.Schema + + import Ecto.Changeset + import Ecto.Query + + alias FlakeId.Ecto.CompatType + alias Pleroma.Repo + alias Pleroma.User + + schema "following_relationships" do + field(:state, :string, default: "accept") + + belongs_to(:follower, User, type: CompatType) + belongs_to(:following, User, type: CompatType) + + timestamps() + end + + def changeset(%__MODULE__{} = following_relationship, attrs) do + following_relationship + |> cast(attrs, [:state]) + |> put_assoc(:follower, attrs.follower) + |> put_assoc(:following, attrs.following) + |> validate_required([:state, :follower, :following]) + end + + def get(%User{} = follower, %User{} = following) do + __MODULE__ + |> where(follower_id: ^follower.id, following_id: ^following.id) + |> Repo.one() + end + + def update(follower, following, "reject"), do: unfollow(follower, following) + + def update(%User{} = follower, %User{} = following, state) do + case get(follower, following) do + nil -> + follow(follower, following, state) + + following_relationship -> + following_relationship + |> cast(%{state: state}, [:state]) + |> validate_required([:state]) + |> Repo.update() + end + end + + def follow(%User{} = follower, %User{} = following, state \\ "accept") do + %__MODULE__{} + |> changeset(%{follower: follower, following: following, state: state}) + |> Repo.insert(on_conflict: :nothing) + end + + def unfollow(%User{} = follower, %User{} = following) do + case get(follower, following) do + nil -> {:ok, nil} + %__MODULE__{} = following_relationship -> Repo.delete(following_relationship) + end + end + + def follower_count(%User{} = user) do + %{followers: user, deactivated: false} + |> User.Query.build() + |> Repo.aggregate(:count, :id) + end + + def following_count(%User{id: nil}), do: 0 + + def following_count(%User{} = user) do + %{friends: user, deactivated: false} + |> User.Query.build() + |> Repo.aggregate(:count, :id) + end + + def get_follow_requests(%User{id: id}) do + __MODULE__ + |> join(:inner, [r], f in assoc(r, :follower)) + |> where([r], r.state == "pending") + |> where([r], r.following_id == ^id) + |> select([r, f], f) + |> Repo.all() + end + + def following?(%User{id: follower_id}, %User{id: followed_id}) do + __MODULE__ + |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept") + |> Repo.exists?() + end + + def following(%User{} = user) do + following = + __MODULE__ + |> join(:inner, [r], u in User, on: r.following_id == u.id) + |> where([r], r.follower_id == ^user.id) + |> where([r], r.state == "accept") + |> select([r, u], u.follower_address) + |> Repo.all() + + if user.nickname in [nil, "internal.fetch"] do + following + else + [user.follower_address | following] + end + end +end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 2cfb13a8c..c32f6b429 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -13,6 +13,7 @@ defmodule Pleroma.User do alias Pleroma.Activity alias Pleroma.Conversation.Participation alias Pleroma.Delivery + alias Pleroma.FollowingRelationship alias Pleroma.Keys alias Pleroma.Notification alias Pleroma.Object @@ -52,7 +53,6 @@ defmodule Pleroma.User do field(:password, :string, virtual: true) field(:password_confirmation, :string, virtual: true) field(:keys, :string) - field(:following, {:array, :string}, default: []) field(:ap_id, :string) field(:avatar, :map) field(:local, :boolean, default: true) @@ -162,13 +162,7 @@ defmodule Pleroma.User do ) end - def following_count(%User{following: []}), do: 0 - - def following_count(%User{} = user) do - user - |> get_friends_query() - |> Repo.aggregate(:count, :id) - end + defdelegate following_count(user), to: FollowingRelationship defp truncate_if_exists(params, key, max_length) do if Map.has_key?(params, key) and is_binary(params[key]) do @@ -216,7 +210,7 @@ defmodule Pleroma.User do name_limit = Pleroma.Config.get([:instance, :user_name_length], 100) struct - |> cast(params, [:bio, :name, :avatar, :following]) + |> cast(params, [:bio, :name, :avatar]) |> unique_constraint(:nickname) |> validate_format(:nickname, local_nickname_regex()) |> validate_length(:bio, max: bio_limit) @@ -324,7 +318,6 @@ defmodule Pleroma.User do followers = ap_followers(%User{nickname: get_field(changeset, :nickname)}) changeset - |> put_change(:following, [followers]) |> put_change(:follower_address, followers) end @@ -378,8 +371,11 @@ defmodule Pleroma.User do def needs_update?(_), do: true @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()} - def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do - {:ok, follower} + def maybe_direct_follow( + %User{} = follower, + %User{local: true, info: %{locked: true}} = followed + ) do + follow(follower, followed, "pending") end def maybe_direct_follow(%User{} = follower, %User{local: true} = followed) do @@ -397,37 +393,22 @@ defmodule Pleroma.User do @doc "A mass follow for local users. Respects blocks in both directions but does not create activities." @spec follow_all(User.t(), list(User.t())) :: {atom(), User.t()} def follow_all(follower, followeds) do - followed_addresses = - followeds - |> Enum.reject(fn followed -> blocks?(follower, followed) || blocks?(followed, follower) end) - |> Enum.map(fn %{follower_address: fa} -> fa end) + followeds = + Enum.reject(followeds, fn followed -> + blocks?(follower, followed) || blocks?(followed, follower) + end) - q = - from(u in User, - where: u.id == ^follower.id, - update: [ - set: [ - following: - fragment( - "array(select distinct unnest (array_cat(?, ?)))", - u.following, - ^followed_addresses - ) - ] - ], - select: u - ) - - {1, [follower]} = Repo.update_all(q, []) + Enum.each(followeds, &follow(follower, &1, "accept")) Enum.each(followeds, &update_follower_count/1) set_cache(follower) end - def follow(%User{} = follower, %User{info: info} = followed) do + defdelegate following(user), to: FollowingRelationship + + def follow(%User{} = follower, %User{info: info} = followed, state \\ "accept") do deny_follow_blocked = Pleroma.Config.get([:user, :deny_follow_blocked]) - ap_followers = followed.follower_address cond do info.deactivated -> @@ -441,14 +422,7 @@ defmodule Pleroma.User do Websub.subscribe(follower, followed) end - q = - from(u in User, - where: u.id == ^follower.id, - update: [push: [following: ^ap_followers]], - select: u - ) - - {1, [follower]} = Repo.update_all(q, []) + FollowingRelationship.follow(follower, followed, state) follower = maybe_update_following_count(follower) @@ -459,17 +433,8 @@ defmodule Pleroma.User do end def unfollow(%User{} = follower, %User{} = followed) do - ap_followers = followed.follower_address - if following?(follower, followed) and follower.ap_id != followed.ap_id do - q = - from(u in User, - where: u.id == ^follower.id, - update: [pull: [following: ^ap_followers]], - select: u - ) - - {1, [follower]} = Repo.update_all(q, []) + FollowingRelationship.unfollow(follower, followed) follower = maybe_update_following_count(follower) @@ -483,10 +448,7 @@ defmodule Pleroma.User do end end - @spec following?(User.t(), User.t()) :: boolean - def following?(%User{} = follower, %User{} = followed) do - Enum.member?(follower.following, followed.follower_address) - end + defdelegate following?(follower, followed), to: FollowingRelationship def locked?(%User{} = user) do user.info.locked || false @@ -707,16 +669,7 @@ defmodule Pleroma.User do |> Repo.all() end - @spec get_follow_requests(User.t()) :: {:ok, [User.t()]} - def get_follow_requests(%User{} = user) do - user - |> Activity.follow_requests_for_actor() - |> join(:inner, [a], u in User, on: a.actor == u.ap_id) - |> where([a, u], not fragment("? @> ?", u.following, ^[user.follower_address])) - |> group_by([a, u], u.id) - |> select([a, u], u) - |> Repo.all() - end + defdelegate get_follow_requests(user), to: FollowingRelationship def increase_note_count(%User{} = user) do User @@ -899,18 +852,6 @@ defmodule Pleroma.User do def increment_unread_conversation_count(_, _), do: :noop - def remove_duplicated_following(%User{following: following} = user) do - uniq_following = Enum.uniq(following) - - if length(following) == length(uniq_following) do - {:ok, user} - else - user - |> update_changeset(%{following: uniq_following}) - |> update_and_set_cache() - end - end - @spec get_users_from_set([String.t()], boolean()) :: [User.t()] def get_users_from_set(ap_ids, local_only \\ true) do criteria = %{ap_id: ap_ids, deactivated: false} diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex index 2baf016cf..f32def4f5 100644 --- a/lib/pleroma/user/query.ex +++ b/lib/pleroma/user/query.ex @@ -28,6 +28,8 @@ defmodule Pleroma.User.Query do """ import Ecto.Query import Pleroma.Web.AdminAPI.Search, only: [not_empty_string: 1] + + alias Pleroma.FollowingRelationship alias Pleroma.User @type criteria :: @@ -130,18 +132,40 @@ defmodule Pleroma.User.Query do |> where([u], not is_nil(u.nickname)) end - defp compose_query({:followers, %User{id: id, follower_address: follower_address}}, query) do - where(query, [u], fragment("? <@ ?", ^[follower_address], u.following)) + defp compose_query({:followers, %User{id: id}}, query) do + query |> where([u], u.id != ^id) + |> join(:inner, [u], r in FollowingRelationship, + as: :relationships, + on: r.following_id == ^id and r.follower_id == u.id + ) + |> where([relationships: r], r.state == "accept") end - defp compose_query({:friends, %User{id: id, following: following}}, query) do - where(query, [u], u.follower_address in ^following) + defp compose_query({:friends, %User{id: id}}, query) do + query |> where([u], u.id != ^id) + |> join(:inner, [u], r in FollowingRelationship, + as: :relationships, + on: r.following_id == u.id and r.follower_id == ^id + ) + |> where([relationships: r], r.state == "accept") end defp compose_query({:recipients_from_activity, to}, query) do - where(query, [u], u.ap_id in ^to or fragment("? && ?", u.following, ^to)) + query + |> join(:left, [u], r in FollowingRelationship, + as: :relationships, + on: r.follower_id == u.id + ) + |> join(:left, [relationships: r], f in User, + as: :following, + on: f.id == r.following_id + ) + |> where( + [u, following: f, relationships: r], + u.ap_id in ^to or (f.follower_address in ^to and r.state == "accept") + ) end defp compose_query({:order_by, key}, query) do diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 9f29087df..db0855329 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -501,7 +501,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do public = [Pleroma.Constants.as_public()] recipients = - if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public + if opts["user"], + do: [opts["user"].ap_id | User.following(opts["user"])] ++ public, + else: public from(activity in Activity) |> maybe_preload_objects(opts) @@ -652,7 +654,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp user_activities_recipients(%{"reading_user" => reading_user}) do if reading_user do - [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following] + [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)] else [Pleroma.Constants.as_public()] end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 080030eb5..9010eab91 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -319,12 +319,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do when page? in [true, "true"] do activities = if params["max_id"] do - ActivityPub.fetch_activities([user.ap_id | user.following], %{ + ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{ "max_id" => params["max_id"], "limit" => 10 }) else - ActivityPub.fetch_activities([user.ap_id | user.following], %{"limit" => 10}) + ActivityPub.fetch_activities([user.ap_id | User.following(user)], %{"limit" => 10}) end conn diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 872ed0eb2..54ba49520 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do A module to handle coding from internal to wire ActivityPub and back. """ alias Pleroma.Activity + alias Pleroma.FollowingRelationship alias Pleroma.Object alias Pleroma.Object.Containment alias Pleroma.Repo @@ -474,7 +475,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {_, false} <- {:user_locked, User.locked?(followed)}, {_, {:ok, follower}} <- {:follow, User.follow(follower, followed)}, {_, {:ok, _}} <- - {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")} do + {:follow_state_update, Utils.update_follow_state_for_all(activity, "accept")}, + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do ActivityPub.accept(%{ to: [follower.ap_id], actor: followed, @@ -484,6 +486,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do else {:user_blocked, true} -> {:ok, _} = Utils.update_follow_state_for_all(activity, "reject") + {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") ActivityPub.reject(%{ to: [follower.ap_id], @@ -494,6 +497,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:follow, {:error, _}} -> {:ok, _} = Utils.update_follow_state_for_all(activity, "reject") + {:ok, _relationship} = FollowingRelationship.update(follower, followed, "reject") ActivityPub.reject(%{ to: [follower.ap_id], @@ -522,7 +526,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), - {:ok, _follower} = User.follow(follower, followed) do + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept") do ActivityPub.accept(%{ to: follow_activity.data["to"], type: "Accept", @@ -544,6 +548,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do {:ok, follow_activity} <- get_follow_activity(follow_object, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), {:ok, activity} <- ActivityPub.reject(%{ to: follow_activity.data["to"], @@ -552,8 +557,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do object: follow_activity.data["id"], local: false }) do - User.unfollow(follower, followed) - {:ok, activity} else _e -> :error @@ -1050,46 +1053,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def perform(:user_upgrade, user) do # we pass a fake user so that the followers collection is stripped away old_follower_address = User.ap_followers(%User{nickname: user.nickname}) - - q = - from( - u in User, - where: ^old_follower_address in u.following, - update: [ - set: [ - following: - fragment( - "array_replace(?,?,?)", - u.following, - ^old_follower_address, - ^user.follower_address - ) - ] - ] - ) - - Repo.update_all(q, []) - maybe_retire_websub(user.ap_id) - q = - from( - a in Activity, - where: ^old_follower_address in a.recipients, - update: [ - set: [ - recipients: - fragment( - "array_replace(?,?,?)", - a.recipients, - ^old_follower_address, - ^user.follower_address - ) - ] + from( + a in Activity, + where: ^old_follower_address in a.recipients, + update: [ + set: [ + recipients: + fragment( + "array_replace(?,?,?)", + a.recipients, + ^old_follower_address, + ^user.follower_address + ) ] - ) - - Repo.update_all(q, []) + ] + ) + |> Repo.update_all([]) end def upgrade_user_from_ap_id(ap_id) do diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 270d0fa02..47bc4e1ca 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -58,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do end def visible_for_user?(activity, user) do - x = [user.ap_id | user.following] + x = [user.ap_id | User.following(user)] y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || []) visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y)) end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 386408d51..40b3930fb 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.Activity alias Pleroma.ActivityExpiration alias Pleroma.Conversation.Participation + alias Pleroma.FollowingRelationship alias Pleroma.Object alias Pleroma.ThreadMute alias Pleroma.User @@ -40,6 +41,7 @@ defmodule Pleroma.Web.CommonAPI do with {:ok, follower} <- User.follow(follower, followed), %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "accept"), {:ok, _activity} <- ActivityPub.accept(%{ to: [follower.ap_id], @@ -54,6 +56,7 @@ defmodule Pleroma.Web.CommonAPI do def reject_follow_request(follower, followed) do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, "reject"), {:ok, _activity} <- ActivityPub.reject(%{ to: [follower.ap_id], diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index 9f086a8c2..f2d2d3ccb 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -10,6 +10,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do alias Pleroma.Pagination alias Pleroma.Plugs.OAuthScopesPlug + alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct]) @@ -28,7 +29,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do |> Map.put("muting_user", user) |> Map.put("user", user) - recipients = [user.ap_id | user.following] + recipients = [user.ap_id | User.following(user)] activities = recipients @@ -128,9 +129,12 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do # we must filter the following list for the user to avoid leaking statuses the user # does not actually have permission to see (for more info, peruse security issue #270). + + user_following = User.following(user) + activities = following - |> Enum.filter(fn x -> x in user.following end) + |> Enum.filter(fn x -> x in user_following end) |> ActivityPub.fetch_activities_bounded(following, params) |> Enum.reverse() diff --git a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex index 9012e2175..7ce9d5da2 100644 --- a/lib/pleroma/web/pleroma_api/controllers/account_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/account_controller.ex @@ -132,7 +132,7 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do recipients = if for_user do - [Pleroma.Constants.as_public()] ++ [for_user.ap_id | for_user.following] + [Pleroma.Constants.as_public()] ++ [for_user.ap_id | User.following(for_user)] else [Pleroma.Constants.as_public()] end diff --git a/priv/repo/migrations/20191007073319_create_following_relationships.exs b/priv/repo/migrations/20191007073319_create_following_relationships.exs index 7daaf0575..d49e24ee4 100644 --- a/priv/repo/migrations/20191007073319_create_following_relationships.exs +++ b/priv/repo/migrations/20191007073319_create_following_relationships.exs @@ -16,7 +16,7 @@ defmodule Pleroma.Repo.Migrations.CreateFollowingRelationships do execute(update_thread_visibility(), restore_thread_visibility()) end - # The only difference with the original verion: `actor_user` replaced with `actor_user_following` + # The only difference between the original version: `actor_user` replaced with `actor_user_following` def update_thread_visibility do """ CREATE OR REPLACE FUNCTION thread_visibility(actor varchar, activity_id varchar) RETURNS boolean AS $$ diff --git a/test/support/factory.ex b/test/support/factory.ex index b180844cd..74f292a1d 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -39,8 +39,7 @@ defmodule Pleroma.Factory do user | ap_id: User.ap_id(user), follower_address: User.ap_followers(user), - following_address: User.ap_following(user), - following: [User.ap_id(user)] + following_address: User.ap_following(user) } end diff --git a/test/tasks/database_test.exs b/test/tasks/database_test.exs index b63dcac00..bf5ba7883 100644 --- a/test/tasks/database_test.exs +++ b/test/tasks/database_test.exs @@ -72,25 +72,25 @@ defmodule Mix.Tasks.Pleroma.DatabaseTest do describe "running update_users_following_followers_counts" do test "following and followers count are updated" do [user, user2] = insert_pair(:user) - {:ok, %User{following: following, info: info} = user} = User.follow(user, user2) + {:ok, %User{info: info} = user} = User.follow(user, user2) + + following = User.following(user) assert length(following) == 2 assert info.follower_count == 0 {:ok, user} = user - |> Ecto.Changeset.change(%{following: following ++ following}) |> User.change_info(&Ecto.Changeset.change(&1, %{follower_count: 3})) |> Repo.update() - assert length(user.following) == 4 assert user.info.follower_count == 3 assert :ok == Mix.Tasks.Pleroma.Database.run(["update_users_following_followers_counts"]) user = User.get_by_id(user.id) - assert length(user.following) == 2 + assert length(User.following(user)) == 2 assert user.info.follower_count == 0 end end diff --git a/test/tasks/relay_test.exs b/test/tasks/relay_test.exs index c866608ab..04a1e45d7 100644 --- a/test/tasks/relay_test.exs +++ b/test/tasks/relay_test.exs @@ -51,7 +51,7 @@ defmodule Mix.Tasks.Pleroma.RelayTest do target_user = User.get_cached_by_ap_id(target_instance) follow_activity = Utils.fetch_latest_follow(local_user, target_user) User.follow(local_user, target_user) - assert "#{target_instance}/followers" in refresh_record(local_user).following + assert "#{target_instance}/followers" in User.following(local_user) Mix.Tasks.Pleroma.Relay.run(["unfollow", target_instance]) cancelled_activity = Activity.get_by_ap_id(follow_activity.data["id"]) @@ -68,7 +68,7 @@ defmodule Mix.Tasks.Pleroma.RelayTest do assert undo_activity.data["type"] == "Undo" assert undo_activity.data["actor"] == local_user.ap_id assert undo_activity.data["object"] == cancelled_activity.data - refute "#{target_instance}/followers" in refresh_record(local_user).following + refute "#{target_instance}/followers" in User.following(local_user) end end @@ -78,20 +78,18 @@ defmodule Mix.Tasks.Pleroma.RelayTest do refute_receive {:mix_shell, :info, _} - Pleroma.Web.ActivityPub.Relay.get_actor() - |> Ecto.Changeset.change( - following: [ - "http://test-app.com/user/test1", - "http://test-app.com/user/test1", - "http://test-app-42.com/user/test1" - ] - ) - |> Pleroma.User.update_and_set_cache() + relay_user = Relay.get_actor() + + ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] + |> Enum.each(fn ap_id -> + {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) + User.follow(relay_user, user) + end) :ok = Mix.Tasks.Pleroma.Relay.run(["list"]) - assert_receive {:mix_shell, :info, ["test-app.com"]} - assert_receive {:mix_shell, :info, ["test-app-42.com"]} + assert_receive {:mix_shell, :info, ["mstdn.io"]} + assert_receive {:mix_shell, :info, ["mastodon.example.org"]} end end end diff --git a/test/tasks/user_test.exs b/test/tasks/user_test.exs index cf12d9ed6..c0e4ba85c 100644 --- a/test/tasks/user_test.exs +++ b/test/tasks/user_test.exs @@ -139,7 +139,8 @@ defmodule Mix.Tasks.Pleroma.UserTest do describe "running unsubscribe" do test "user is unsubscribed" do followed = insert(:user) - user = insert(:user, %{following: [User.ap_followers(followed)]}) + user = insert(:user) + User.follow(user, followed, "accept") Mix.Tasks.Pleroma.User.run(["unsubscribe", user.nickname]) @@ -154,7 +155,7 @@ defmodule Mix.Tasks.Pleroma.UserTest do assert message =~ "Successfully unsubscribed" user = User.get_cached_by_nickname(user.nickname) - assert Enum.empty?(user.following) + assert Enum.empty?(User.get_friends(user)) assert user.info.deactivated end diff --git a/test/user_test.exs b/test/user_test.exs index 019e7b400..85e55876d 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -88,10 +88,9 @@ defmodule Pleroma.UserTest do CommonAPI.follow(pending_follower, locked) CommonAPI.follow(pending_follower, locked) CommonAPI.follow(accepted_follower, locked) - User.follow(accepted_follower, locked) + Pleroma.FollowingRelationship.update(accepted_follower, locked, "accept") - assert [activity] = User.get_follow_requests(locked) - assert activity + assert [^pending_follower] = User.get_follow_requests(locked) end test "clears follow requests when requester is blocked" do @@ -136,10 +135,10 @@ defmodule Pleroma.UserTest do followed_two = insert(:user) {:ok, user} = User.follow_all(user, [followed_zero, followed_one]) - assert length(user.following) == 3 + assert length(User.following(user)) == 3 {:ok, user} = User.follow_all(user, [followed_one, followed_two]) - assert length(user.following) == 4 + assert length(User.following(user)) == 4 end test "follow takes a user and another user" do @@ -153,7 +152,7 @@ defmodule Pleroma.UserTest do followed = User.get_cached_by_ap_id(followed.ap_id) assert followed.info.follower_count == 1 - assert User.ap_followers(followed) in user.following + assert User.ap_followers(followed) in User.following(user) end test "can't follow a deactivated users" do @@ -198,7 +197,7 @@ defmodule Pleroma.UserTest do # assert followed.local == false # {:ok, user} = User.follow(user, followed) - # assert User.ap_followers(followed) in user.following + # assert User.ap_followers(followed) in User.following(user) # query = from w in WebsubClientSubscription, # where: w.topic == ^followed.info["topic"] @@ -235,26 +234,29 @@ defmodule Pleroma.UserTest do nickname: "fuser2", ap_id: "http://localhost:4001/users/fuser2", follower_address: "http://localhost:4001/users/fuser2/followers", - following_address: "http://localhost:4001/users/fuser2/following", - following: [User.ap_followers(followed)] + following_address: "http://localhost:4001/users/fuser2/following" }) + {:ok, user} = User.follow(user, followed, "accept") + {:ok, user, _activity} = User.unfollow(user, followed) user = User.get_cached_by_id(user.id) - assert user.following == [] + assert User.following(user) == [user.follower_address] end test "unfollow takes a user and another user" do followed = insert(:user) - user = insert(:user, %{following: [User.ap_followers(followed)]}) + user = insert(:user) + + {:ok, user} = User.follow(user, followed, "accept") + + assert User.following(user) == [user.follower_address, followed.follower_address] {:ok, user, _activity} = User.unfollow(user, followed) - user = User.get_cached_by_id(user.id) - - assert user.following == [] + assert User.following(user) == [user.follower_address] end test "unfollow doesn't unfollow yourself" do @@ -262,14 +264,14 @@ defmodule Pleroma.UserTest do {:error, _} = User.unfollow(user, user) - user = User.get_cached_by_id(user.id) - assert user.following == [user.ap_id] + assert User.following(user) == [user.follower_address] end end test "test if a user is following another user" do followed = insert(:user) - user = insert(:user, %{following: [User.ap_followers(followed)]}) + user = insert(:user) + User.follow(user, followed, "accept") assert User.following?(user, followed) refute User.following?(followed, user) @@ -352,7 +354,7 @@ defmodule Pleroma.UserTest do refute changeset.valid? end - test "it sets the password_hash, ap_id and following fields" do + test "it sets the password_hash and ap_id" do changeset = User.register_changeset(%User{}, @full_user_data) assert changeset.valid? @@ -360,10 +362,6 @@ defmodule Pleroma.UserTest do assert is_binary(changeset.changes[:password_hash]) assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname}) - assert changeset.changes[:following] == [ - User.ap_followers(%User{nickname: @full_user_data.nickname}) - ] - assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers" end @@ -671,37 +669,6 @@ defmodule Pleroma.UserTest do end end - describe "remove duplicates from following list" do - test "it removes duplicates" do - user = insert(:user) - follower = insert(:user) - - {:ok, %User{following: following} = follower} = User.follow(follower, user) - assert length(following) == 2 - - {:ok, follower} = - follower - |> User.update_changeset(%{following: following ++ following}) - |> Repo.update() - - assert length(follower.following) == 4 - - {:ok, follower} = User.remove_duplicated_following(follower) - assert length(follower.following) == 2 - end - - test "it does nothing when following is uniq" do - user = insert(:user) - follower = insert(:user) - - {:ok, follower} = User.follow(follower, user) - assert length(follower.following) == 2 - - {:ok, follower} = User.remove_duplicated_following(follower) - assert length(follower.following) == 2 - end - end - describe "follow_import" do test "it imports user followings from list" do [user1, user2, user3] = insert_list(3, :user) @@ -1010,7 +977,9 @@ defmodule Pleroma.UserTest do assert [activity] == ActivityPub.fetch_public_activities(%{}) |> Repo.preload(:bookmark) assert [%{activity | thread_muted?: CommonAPI.thread_muted?(user2, activity)}] == - ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2}) + ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{ + "user" => user2 + }) {:ok, _user} = User.deactivate(user) @@ -1018,7 +987,9 @@ defmodule Pleroma.UserTest do assert [] == Pleroma.Notification.for_user(user2) assert [] == - ActivityPub.fetch_activities([user2.ap_id | user2.following], %{"user" => user2}) + ActivityPub.fetch_activities([user2.ap_id | User.following(user2)], %{ + "user" => user2 + }) end end diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index c9f2a92e7..75e928a14 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -606,7 +606,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do {:ok, announce, _object} = CommonAPI.repeat(activity_three.id, booster) - [announce_activity] = ActivityPub.fetch_activities([user.ap_id | user.following]) + [announce_activity] = ActivityPub.fetch_activities([user.ap_id | User.following(user)]) assert announce_activity.id == announce.id end @@ -1132,7 +1132,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do }) activities = - ActivityPub.fetch_activities([user1.ap_id | user1.following]) + ActivityPub.fetch_activities([user1.ap_id | User.following(user1)]) |> Enum.map(fn a -> a.id end) private_activity_1 = Activity.get_by_ap_id_with_object(private_activity_1.data["id"]) @@ -1142,7 +1142,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do assert length(activities) == 3 activities = - ActivityPub.fetch_activities([user1.ap_id | user1.following], %{"user" => user1}) + ActivityPub.fetch_activities([user1.ap_id | User.following(user1)], %{"user" => user1}) |> Enum.map(fn a -> a.id end) assert [public_activity.id, private_activity_1.id] == activities diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs index 0f7556538..e270cd4c3 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/web/activity_pub/relay_test.exs @@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do alias Pleroma.Activity alias Pleroma.Object + alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Relay @@ -50,14 +51,14 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do service_actor = Relay.get_actor() ActivityPub.follow(service_actor, user) Pleroma.User.follow(service_actor, user) - assert "#{user.ap_id}/followers" in refresh_record(service_actor).following + assert "#{user.ap_id}/followers" in User.following(service_actor) assert {:ok, %Activity{} = activity} = Relay.unfollow(user.ap_id) assert activity.actor == "#{Pleroma.Web.Endpoint.url()}/relay" assert user.ap_id in activity.recipients assert activity.data["type"] == "Undo" assert activity.data["actor"] == service_actor.ap_id assert activity.data["to"] == [user.ap_id] - refute "#{user.ap_id}/followers" in refresh_record(service_actor).following + refute "#{user.ap_id}/followers" in User.following(service_actor) end end diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 50c0bfb84..d96dcd2a0 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -1312,7 +1312,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"}) }) - user_two = insert(:user, %{following: [user.follower_address]}) + user_two = insert(:user) + Pleroma.FollowingRelationship.follow(user_two, user, "accept") {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) {:ok, unrelated_activity} = CommonAPI.post(user_two, %{"status" => "test"}) @@ -1359,8 +1360,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do refute user.follower_address in unrelated_activity.recipients user_two = User.get_cached_by_id(user_two.id) - assert user.follower_address in user_two.following - refute "..." in user_two.following + assert User.following?(user_two, user) + refute "..." in User.following(user_two) end end diff --git a/test/web/activity_pub/visibilty_test.exs b/test/web/activity_pub/visibilty_test.exs index b62a89e68..4c2e0d207 100644 --- a/test/web/activity_pub/visibilty_test.exs +++ b/test/web/activity_pub/visibilty_test.exs @@ -212,7 +212,8 @@ defmodule Pleroma.Web.ActivityPub.VisibilityTest do test "returns true if user following to author" do author = insert(:user) - user = insert(:user, following: [author.ap_id]) + user = insert(:user) + Pleroma.User.follow(user, author) activity = insert(:note_activity, diff --git a/test/web/mastodon_api/controllers/account_controller_test.exs b/test/web/mastodon_api/controllers/account_controller_test.exs index 6a59c3d94..a398ef76a 100644 --- a/test/web/mastodon_api/controllers/account_controller_test.exs +++ b/test/web/mastodon_api/controllers/account_controller_test.exs @@ -457,7 +457,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do conn = build_conn() - |> assign(:user, follower) + |> assign(:user, User.get_cached_by_id(follower.id)) |> post("/api/v1/accounts/#{followed.id}/follow?reblogs=true") assert %{"showing_reblogs" => true} = json_response(conn, 200) diff --git a/test/web/mastodon_api/controllers/follow_request_controller_test.exs b/test/web/mastodon_api/controllers/follow_request_controller_test.exs index 4bf292df5..89b676201 100644 --- a/test/web/mastodon_api/controllers/follow_request_controller_test.exs +++ b/test/web/mastodon_api/controllers/follow_request_controller_test.exs @@ -16,9 +16,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) - - user = User.get_cached_by_id(user.id) - other_user = User.get_cached_by_id(other_user.id) + {:ok, other_user} = User.follow(other_user, user, "pending") assert User.following?(other_user, user) == false @@ -36,6 +34,7 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do other_user = insert(:user) {:ok, _activity} = ActivityPub.follow(other_user, user) + {:ok, other_user} = User.follow(other_user, user, "pending") user = User.get_cached_by_id(user.id) other_user = User.get_cached_by_id(other_user.id) diff --git a/test/web/streamer/streamer_test.exs b/test/web/streamer/streamer_test.exs index d33eb1e42..c674e71f5 100644 --- a/test/web/streamer/streamer_test.exs +++ b/test/web/streamer/streamer_test.exs @@ -169,7 +169,8 @@ defmodule Pleroma.Web.StreamerTest do test "it doesn't send to user if recipients invalid and thread containment is enabled" do Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) - user = insert(:user, following: [author.ap_id]) + user = insert(:user) + User.follow(user, author, "accept") activity = insert(:note_activity, @@ -191,7 +192,8 @@ defmodule Pleroma.Web.StreamerTest do test "it sends message if recipients invalid and thread containment is disabled" do Pleroma.Config.put([:instance, :skip_thread_containment], true) author = insert(:user) - user = insert(:user, following: [author.ap_id]) + user = insert(:user) + User.follow(user, author, "accept") activity = insert(:note_activity, @@ -213,7 +215,8 @@ defmodule Pleroma.Web.StreamerTest do test "it sends message if recipients invalid and thread containment is enabled but user's thread containment is disabled" do Pleroma.Config.put([:instance, :skip_thread_containment], false) author = insert(:user) - user = insert(:user, following: [author.ap_id], info: %{skip_thread_containment: true}) + user = insert(:user, info: %{skip_thread_containment: true}) + User.follow(user, author, "accept") activity = insert(:note_activity, diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index 9d4cb70f0..5234a5271 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -366,7 +366,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do |> response(200) assert response =~ "Account followed!" - assert user2.follower_address in refresh_record(user).following + assert user2.follower_address in User.following(user) end test "returns error when user is deactivated", %{conn: conn} do @@ -438,7 +438,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do |> response(200) assert response =~ "Account followed!" - assert user2.follower_address in refresh_record(user).following + assert user2.follower_address in User.following(user) end test "returns error when followee not found", %{conn: conn} do From 1d46944fbd17d194d744230cd519d1410e821a47 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 14 Oct 2019 13:50:43 +0700 Subject: [PATCH 06/43] Do not add `follower_address` to `following` for non local users --- lib/pleroma/following_relationship.ex | 2 +- test/user_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index 0d789b5a6..2ffac17ee 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -101,7 +101,7 @@ defmodule Pleroma.FollowingRelationship do |> select([r, u], u.follower_address) |> Repo.all() - if user.nickname in [nil, "internal.fetch"] do + if not user.local or user.nickname in [nil, "internal.fetch"] do following else [user.follower_address | following] diff --git a/test/user_test.exs b/test/user_test.exs index 85e55876d..eb1cf4037 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -243,7 +243,7 @@ defmodule Pleroma.UserTest do user = User.get_cached_by_id(user.id) - assert User.following(user) == [user.follower_address] + assert User.following(user) == [] end test "unfollow takes a user and another user" do From 8ad015ef64e0d2a4cd9f2979ff08d28be3a635e5 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 14 Oct 2019 14:16:57 +0700 Subject: [PATCH 07/43] Skip deactivated users in followers import --- .../20191008132217_migrate_following_relationships.exs | 1 + 1 file changed, 1 insertion(+) diff --git a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs index 7f996f5a5..c9bc890aa 100644 --- a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs +++ b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs @@ -55,6 +55,7 @@ defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships do WHERE activities.data ->> 'type' = 'Follow' AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') + AND NOT (followers.info ? 'deactivated' AND followers.info -> 'deactivated' @> 'true') ORDER BY activities.updated_at DESC ON CONFLICT DO NOTHING """ From c6fba62666702013587e0b60723b9dfe60d1c710 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 21 Oct 2019 14:19:31 +0700 Subject: [PATCH 08/43] Fix Relay --- lib/pleroma/web/activity_pub/relay.ex | 5 +++-- .../admin_api/admin_api_controller_test.exs | 18 ++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index 03fc434a9..830d1cde8 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -53,9 +53,10 @@ defmodule Pleroma.Web.ActivityPub.Relay do @spec list() :: {:ok, [String.t()]} | {:error, any()} def list do - with %User{following: following} = _user <- get_actor() do + with %User{} = user <- get_actor() do list = - following + user + |> User.following() |> Enum.map(fn entry -> URI.parse(entry).host end) |> Enum.uniq() diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 9da4940be..6dc0d4dca 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -2572,22 +2572,20 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do end test "GET /relay", %{admin: admin} do - Pleroma.Web.ActivityPub.Relay.get_actor() - |> Ecto.Changeset.change( - following: [ - "http://test-app.com/user/test1", - "http://test-app.com/user/test1", - "http://test-app-42.com/user/test1" - ] - ) - |> Pleroma.User.update_and_set_cache() + relay_user = Pleroma.Web.ActivityPub.Relay.get_actor() + + ["http://mastodon.example.org/users/admin", "https://mstdn.io/users/mayuutann"] + |> Enum.each(fn ap_id -> + {:ok, user} = User.get_or_fetch_by_ap_id(ap_id) + User.follow(relay_user, user) + end) conn = build_conn() |> assign(:user, admin) |> get("/api/pleroma/admin/relay") - assert json_response(conn, 200)["relays"] -- ["test-app.com", "test-app-42.com"] == [] + assert json_response(conn, 200)["relays"] -- ["mastodon.example.org", "mstdn.io"] == [] end test "DELETE /relay", %{admin: admin} do From e37d4b2ddf7928437618d3eb7b5da678e39927cf Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 21 Oct 2019 14:52:52 +0700 Subject: [PATCH 09/43] Revert "Skip deactivated users in followers import" This reverts commit 8ad015ef64e0d2a4cd9f2979ff08d28be3a635e5. --- .../20191008132217_migrate_following_relationships.exs | 1 - 1 file changed, 1 deletion(-) diff --git a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs index c9bc890aa..7f996f5a5 100644 --- a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs +++ b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs @@ -55,7 +55,6 @@ defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships do WHERE activities.data ->> 'type' = 'Follow' AND activities.data ->> 'state' IN ('accept', 'pending', 'reject') - AND NOT (followers.info ? 'deactivated' AND followers.info -> 'deactivated' @> 'true') ORDER BY activities.updated_at DESC ON CONFLICT DO NOTHING """ From f726d953d59b8c4c2099a3c583e7226b99324bb9 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 21 Oct 2019 14:56:39 +0700 Subject: [PATCH 10/43] Fix typos --- ...0191008132217_migrate_following_relationships.exs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs index 7f996f5a5..9d5c2648f 100644 --- a/priv/repo/migrations/20191008132217_migrate_following_relationships.exs +++ b/priv/repo/migrations/20191008132217_migrate_following_relationships.exs @@ -69,19 +69,19 @@ defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships do updated_at = now() FROM ( SELECT - follwer.id AS follower_id, - CASE follwer.local + follower.id AS follower_id, + CASE follower.local WHEN TRUE THEN - array_prepend(follwer.follower_address, array_agg(following.follower_address)) + array_prepend(follower.follower_address, array_agg(following.follower_address)) ELSE array_agg(following.follower_address) END AS following_array FROM following_relationships - JOIN users AS follwer ON follwer.id = following_relationships.follower_id - JOIN users AS FOLLOWING ON following.id = following_relationships.following_id + JOIN users AS follower ON follower.id = following_relationships.follower_id + JOIN users AS following ON following.id = following_relationships.following_id GROUP BY - follwer.id) AS following_query + follower.id) AS following_query WHERE following_query.follower_id = users.id """ From 478f0883eb994c639923b7b27259a02bcef8708c Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 21 Oct 2019 15:55:28 +0700 Subject: [PATCH 11/43] Fix benchmarks --- benchmarks/load_testing/fetcher.ex | 6 ++++-- benchmarks/load_testing/generator.ex | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/benchmarks/load_testing/fetcher.ex b/benchmarks/load_testing/fetcher.ex index e378c51e7..cdc073b2e 100644 --- a/benchmarks/load_testing/fetcher.ex +++ b/benchmarks/load_testing/fetcher.ex @@ -39,10 +39,12 @@ defmodule Pleroma.LoadTesting.Fetcher do "muting_user" => user } + following = User.following(user) + Benchee.run(%{ "User home timeline" => fn -> Pleroma.Web.ActivityPub.ActivityPub.fetch_activities( - [user.ap_id | user.following], + following, home_timeline_params ) end, @@ -60,7 +62,7 @@ defmodule Pleroma.LoadTesting.Fetcher do home_activities = Pleroma.Web.ActivityPub.ActivityPub.fetch_activities( - [user.ap_id | user.following], + following, home_timeline_params ) diff --git a/benchmarks/load_testing/generator.ex b/benchmarks/load_testing/generator.ex index 5c5a5c122..b4432bdb7 100644 --- a/benchmarks/load_testing/generator.ex +++ b/benchmarks/load_testing/generator.ex @@ -45,15 +45,13 @@ defmodule Pleroma.LoadTesting.Generator do %{ ap_id: ap_id, follower_address: ap_id <> "/followers", - following_address: ap_id <> "/following", - following: [ap_id] + following_address: ap_id <> "/following" } else %{ ap_id: User.ap_id(user), follower_address: User.ap_followers(user), - following_address: User.ap_following(user), - following: [User.ap_id(user)] + following_address: User.ap_following(user) } end From 12ab7b3280c8cf2469479381e0d363f7638002d8 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Sat, 26 Oct 2019 03:45:24 +0300 Subject: [PATCH 12/43] User info migration improvements - Move column additions into a separate migration, so postgres doesn't need an exclusive lock on the table for the main part - Fill in columns by using one big update statement instead of a bunch of small ones because it's much faster (the migration took 140s on patch.cx database) --- .../20191009154606_add_user_info_columns.exs | 53 ++++++++++++ ...154608_copy_users_info_fields_to_users.exs | 86 +++++-------------- 2 files changed, 75 insertions(+), 64 deletions(-) create mode 100644 priv/repo/migrations/20191009154606_add_user_info_columns.exs diff --git a/priv/repo/migrations/20191009154606_add_user_info_columns.exs b/priv/repo/migrations/20191009154606_add_user_info_columns.exs new file mode 100644 index 000000000..22a5a377f --- /dev/null +++ b/priv/repo/migrations/20191009154606_add_user_info_columns.exs @@ -0,0 +1,53 @@ +defmodule Pleroma.Repo.Migrations.AddUsersInfoColumns do + use Ecto.Migration + + @jsonb_array_default "'[]'::jsonb" + + def change do + alter table(:users) do + add_if_not_exists(:banner, :map, default: %{}) + add_if_not_exists(:background, :map, default: %{}) + add_if_not_exists(:source_data, :map, default: %{}) + add_if_not_exists(:note_count, :integer, default: 0) + add_if_not_exists(:follower_count, :integer, default: 0) + add_if_not_exists(:following_count, :integer, default: nil) + add_if_not_exists(:locked, :boolean, default: false, null: false) + add_if_not_exists(:confirmation_pending, :boolean, default: false, null: false) + add_if_not_exists(:password_reset_pending, :boolean, default: false, null: false) + add_if_not_exists(:confirmation_token, :text, default: nil) + add_if_not_exists(:default_scope, :string, default: "public") + add_if_not_exists(:blocks, {:array, :text}, default: []) + add_if_not_exists(:domain_blocks, {:array, :text}, default: []) + add_if_not_exists(:mutes, {:array, :text}, default: []) + add_if_not_exists(:muted_reblogs, {:array, :text}, default: []) + add_if_not_exists(:muted_notifications, {:array, :text}, default: []) + add_if_not_exists(:subscribers, {:array, :text}, default: []) + add_if_not_exists(:deactivated, :boolean, default: false, null: false) + add_if_not_exists(:no_rich_text, :boolean, default: false, null: false) + add_if_not_exists(:ap_enabled, :boolean, default: false, null: false) + add_if_not_exists(:is_moderator, :boolean, default: false, null: false) + add_if_not_exists(:is_admin, :boolean, default: false, null: false) + add_if_not_exists(:show_role, :boolean, default: true, null: false) + add_if_not_exists(:settings, :map, default: nil) + add_if_not_exists(:magic_key, :text, default: nil) + add_if_not_exists(:uri, :text, default: nil) + add_if_not_exists(:hide_followers_count, :boolean, default: false, null: false) + add_if_not_exists(:hide_follows_count, :boolean, default: false, null: false) + add_if_not_exists(:hide_followers, :boolean, default: false, null: false) + add_if_not_exists(:hide_follows, :boolean, default: false, null: false) + add_if_not_exists(:hide_favorites, :boolean, default: true, null: false) + add_if_not_exists(:unread_conversation_count, :integer, default: 0) + add_if_not_exists(:pinned_activities, {:array, :text}, default: []) + add_if_not_exists(:email_notifications, :map, default: %{"digest" => false}) + add_if_not_exists(:mascot, :map, default: nil) + add_if_not_exists(:emoji, :map, default: fragment(@jsonb_array_default)) + add_if_not_exists(:pleroma_settings_store, :map, default: %{}) + add_if_not_exists(:fields, :map, default: fragment(@jsonb_array_default)) + add_if_not_exists(:raw_fields, :map, default: fragment(@jsonb_array_default)) + add_if_not_exists(:discoverable, :boolean, default: false, null: false) + add_if_not_exists(:invisible, :boolean, default: false, null: false) + add_if_not_exists(:notification_settings, :map, default: %{}) + add_if_not_exists(:skip_thread_containment, :boolean, default: false, null: false) + end + end +end diff --git a/priv/repo/migrations/20191009154608_copy_users_info_fields_to_users.exs b/priv/repo/migrations/20191009154608_copy_users_info_fields_to_users.exs index 9dd27511c..cc5ae6920 100644 --- a/priv/repo/migrations/20191009154608_copy_users_info_fields_to_users.exs +++ b/priv/repo/migrations/20191009154608_copy_users_info_fields_to_users.exs @@ -95,79 +95,37 @@ defmodule Pleroma.Repo.Migrations.CopyUsersInfoFieldsToUsers do ] def change do - alter table(:users) do - add(:banner, :map, default: %{}) - add(:background, :map, default: %{}) - add(:source_data, :map, default: %{}) - add(:note_count, :integer, default: 0) - add(:follower_count, :integer, default: 0) - add(:following_count, :integer, default: nil) - add(:locked, :boolean, default: false, null: false) - add(:confirmation_pending, :boolean, default: false, null: false) - add(:password_reset_pending, :boolean, default: false, null: false) - add(:confirmation_token, :text, default: nil) - add(:default_scope, :string, default: "public") - add(:blocks, {:array, :text}, default: []) - add(:domain_blocks, {:array, :text}, default: []) - add(:mutes, {:array, :text}, default: []) - add(:muted_reblogs, {:array, :text}, default: []) - add(:muted_notifications, {:array, :text}, default: []) - add(:subscribers, {:array, :text}, default: []) - add(:deactivated, :boolean, default: false, null: false) - add(:no_rich_text, :boolean, default: false, null: false) - add(:ap_enabled, :boolean, default: false, null: false) - add(:is_moderator, :boolean, default: false, null: false) - add(:is_admin, :boolean, default: false, null: false) - add(:show_role, :boolean, default: true, null: false) - add(:settings, :map, default: nil) - add(:magic_key, :text, default: nil) - add(:uri, :text, default: nil) - add(:hide_followers_count, :boolean, default: false, null: false) - add(:hide_follows_count, :boolean, default: false, null: false) - add(:hide_followers, :boolean, default: false, null: false) - add(:hide_follows, :boolean, default: false, null: false) - add(:hide_favorites, :boolean, default: true, null: false) - add(:unread_conversation_count, :integer, default: 0) - add(:pinned_activities, {:array, :text}, default: []) - add(:email_notifications, :map, default: %{"digest" => false}) - add(:mascot, :map, default: nil) - add(:emoji, :map, default: fragment(@jsonb_array_default)) - add(:pleroma_settings_store, :map, default: %{}) - add(:fields, :map, default: fragment(@jsonb_array_default)) - add(:raw_fields, :map, default: fragment(@jsonb_array_default)) - add(:discoverable, :boolean, default: false, null: false) - add(:invisible, :boolean, default: false, null: false) - add(:notification_settings, :map, default: %{}) - add(:skip_thread_containment, :boolean, default: false, null: false) - end - if direction() == :up do - for f <- @info_fields do - set_field = "update users set #{f} =" + sets = + for f <- @info_fields do + set_field = "#{f} =" - # Coercion of null::jsonb to NULL - jsonb = "case when info->>'#{f}' IS NULL then null else info->'#{f}' end" + # Coercion of null::jsonb to NULL + jsonb = "case when info->>'#{f}' IS NULL then null else info->'#{f}' end" - cond do - f in @jsonb_fields -> - execute("#{set_field} #{jsonb}") + cond do + f in @jsonb_fields -> + "#{set_field} #{jsonb}" - f in @array_jsonb_fields -> - execute("#{set_field} coalesce(#{jsonb}, #{@jsonb_array_default})") + f in @array_jsonb_fields -> + "#{set_field} coalesce(#{jsonb}, #{@jsonb_array_default})" - f in @int_fields -> - execute("#{set_field} (info->>'#{f}')::int") + f in @int_fields -> + "#{set_field} (info->>'#{f}')::int" - f in @boolean_fields -> - execute("#{set_field} coalesce((info->>'#{f}')::boolean, false)") + f in @boolean_fields -> + "#{set_field} coalesce((info->>'#{f}')::boolean, false)" - f in @array_text_fields -> - execute("#{set_field} ARRAY(SELECT jsonb_array_elements_text(#{jsonb}))") + f in @array_text_fields -> + "#{set_field} ARRAY(SELECT jsonb_array_elements_text(#{jsonb}))" - true -> - execute("#{set_field} info->>'#{f}'") + true -> + "#{set_field} info->>'#{f}'" + end end - end + |> Enum.join(", ") + + execute("update users set " <> sets) for index_name <- [ :users_deactivated_index, From 6e7fd364a4de922aab8f3123f0a378e79a7029f1 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:28:18 +0300 Subject: [PATCH 13/43] Add migration --- ...191026190317_set_not_null_for_activities.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 priv/repo/migrations/20191026190317_set_not_null_for_activities.exs diff --git a/priv/repo/migrations/20191026190317_set_not_null_for_activities.exs b/priv/repo/migrations/20191026190317_set_not_null_for_activities.exs new file mode 100644 index 000000000..9b66f3cff --- /dev/null +++ b/priv/repo/migrations/20191026190317_set_not_null_for_activities.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForActivities do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE activities + ALTER COLUMN data SET NOT NULL, + ALTER COLUMN local SET NOT NULL") + end + + def down do + execute("ALTER TABLE activities + ALTER COLUMN data DROP NOT NULL, + ALTER COLUMN local DROP NOT NULL") + end +end From cd0218c2053b3f7e04416ea0eb12759d78cfcbf5 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:31:25 +0300 Subject: [PATCH 14/43] Add migration --- ...0415_set_not_null_for_activity_expirations.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 priv/repo/migrations/20191026190415_set_not_null_for_activity_expirations.exs diff --git a/priv/repo/migrations/20191026190415_set_not_null_for_activity_expirations.exs b/priv/repo/migrations/20191026190415_set_not_null_for_activity_expirations.exs new file mode 100644 index 000000000..e41c69e4c --- /dev/null +++ b/priv/repo/migrations/20191026190415_set_not_null_for_activity_expirations.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForActivityExpirations do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE activity_expirations + ALTER COLUMN activity_id SET NOT NULL") + end + + def down do + execute("ALTER TABLE activity_expirations + ALTER COLUMN activity_id DROP NOT NULL") + end +end From 5cb03fe8010d479e32979d2c02ec8bcf1edf1a67 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:32:31 +0300 Subject: [PATCH 15/43] Add migration --- .../20191026190500_set_not_null_for_apps.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 priv/repo/migrations/20191026190500_set_not_null_for_apps.exs diff --git a/priv/repo/migrations/20191026190500_set_not_null_for_apps.exs b/priv/repo/migrations/20191026190500_set_not_null_for_apps.exs new file mode 100644 index 000000000..a6a44ddfe --- /dev/null +++ b/priv/repo/migrations/20191026190500_set_not_null_for_apps.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForApps do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE apps + ALTER COLUMN client_name SET NOT NULL, + ALTER COLUMN redirect_uris SET NOT NULL") + end + + def down do + execute("ALTER TABLE apps + ALTER COLUMN client_name DROP NOT NULL, + ALTER COLUMN redirect_uris DROP NOT NULL") + end +end From c1ff8472fd7fb84aa90cc59df99dffb7cccaa005 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:33:18 +0300 Subject: [PATCH 16/43] Add migration --- ...0191026190533_set_not_null_for_bookmarks.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 priv/repo/migrations/20191026190533_set_not_null_for_bookmarks.exs diff --git a/priv/repo/migrations/20191026190533_set_not_null_for_bookmarks.exs b/priv/repo/migrations/20191026190533_set_not_null_for_bookmarks.exs new file mode 100644 index 000000000..5f3224003 --- /dev/null +++ b/priv/repo/migrations/20191026190533_set_not_null_for_bookmarks.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForBookmarks do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE bookmarks + ALTER COLUMN user_id SET NOT NULL, + ALTER COLUMN activity_id SET NOT NULL") + end + + def down do + execute("ALTER TABLE bookmarks + ALTER COLUMN user_id DROP NOT NULL, + ALTER COLUMN activity_id DROP NOT NULL") + end +end From 7ac42fefe3bd90f20f887e56db4e26e0955128fa Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:36:43 +0300 Subject: [PATCH 17/43] Add migration --- .../20191026190622_set_not_null_for_config.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 priv/repo/migrations/20191026190622_set_not_null_for_config.exs diff --git a/priv/repo/migrations/20191026190622_set_not_null_for_config.exs b/priv/repo/migrations/20191026190622_set_not_null_for_config.exs new file mode 100644 index 000000000..204272442 --- /dev/null +++ b/priv/repo/migrations/20191026190622_set_not_null_for_config.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForConfig do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE config + ALTER COLUMN key SET NOT NULL, + ALTER COLUMN value SET NOT NULL") + end + + def down do + execute("ALTER TABLE config + ALTER COLUMN key DROP NOT NULL, + ALTER COLUMN value DROP NOT NULL") + end +end From d12555a69ecd002e5c2d2be36e9ec1fa6badf001 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:37:30 +0300 Subject: [PATCH 18/43] Add migration --- ...nversation_participation_recipient_ships.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 priv/repo/migrations/20191026190712_set_not_null_for_conversation_participation_recipient_ships.exs diff --git a/priv/repo/migrations/20191026190712_set_not_null_for_conversation_participation_recipient_ships.exs b/priv/repo/migrations/20191026190712_set_not_null_for_conversation_participation_recipient_ships.exs new file mode 100644 index 000000000..a5ab1d3c9 --- /dev/null +++ b/priv/repo/migrations/20191026190712_set_not_null_for_conversation_participation_recipient_ships.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForConversationParticipationRecipientShips do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE conversation_participation_recipient_ships + ALTER COLUMN user_id SET NOT NULL, + ALTER COLUMN participation_id SET NOT NULL") + end + + def down do + execute("ALTER TABLE conversation_participation_recipient_ships + ALTER COLUMN user_id DROP NOT NULL, + ALTER COLUMN participation_id DROP NOT NULL") + end +end From 0223dc4e02cc05eff3f05e4438bbccf29015f10a Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:38:07 +0300 Subject: [PATCH 19/43] Add migration --- ...t_null_for_conversation_participations.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 priv/repo/migrations/20191026190759_set_not_null_for_conversation_participations.exs diff --git a/priv/repo/migrations/20191026190759_set_not_null_for_conversation_participations.exs b/priv/repo/migrations/20191026190759_set_not_null_for_conversation_participations.exs new file mode 100644 index 000000000..cabb1f29f --- /dev/null +++ b/priv/repo/migrations/20191026190759_set_not_null_for_conversation_participations.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForConversationParticipations do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE conversation_participations + ALTER COLUMN user_id SET NOT NULL, + ALTER COLUMN conversation_id SET NOT NULL, + ALTER COLUMN read SET NOT NULL") + end + + def down do + execute("ALTER TABLE conversation_participations + ALTER COLUMN user_id DROP NOT NULL, + ALTER COLUMN conversation_id DROP NOT NULL, + ALTER COLUMN read DROP NOT NULL") + end +end From 382e83fab56ade9f893b117fcac0d66b402ac86f Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:39:24 +0300 Subject: [PATCH 20/43] Add migration --- ...0191026190841_set_not_null_for_filters.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 priv/repo/migrations/20191026190841_set_not_null_for_filters.exs diff --git a/priv/repo/migrations/20191026190841_set_not_null_for_filters.exs b/priv/repo/migrations/20191026190841_set_not_null_for_filters.exs new file mode 100644 index 000000000..52d7e687a --- /dev/null +++ b/priv/repo/migrations/20191026190841_set_not_null_for_filters.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForFilters do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE filters + ALTER COLUMN user_id SET NOT NULL, + ALTER COLUMN filter_id SET NOT NULL, + ALTER COLUMN whole_word SET NOT NULL") + end + + def down do + execute("ALTER TABLE filters + ALTER COLUMN user_id DROP NOT NULL, + ALTER COLUMN filter_id DROP NOT NULL, + ALTER COLUMN whole_word DROP NOT NULL") + end +end From b33aacc4fbbefc16fb21df56f9693c466d929558 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:40:51 +0300 Subject: [PATCH 21/43] Add migration --- .../20191026191023_set_not_null_for_instances.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 priv/repo/migrations/20191026191023_set_not_null_for_instances.exs diff --git a/priv/repo/migrations/20191026191023_set_not_null_for_instances.exs b/priv/repo/migrations/20191026191023_set_not_null_for_instances.exs new file mode 100644 index 000000000..4c2560da5 --- /dev/null +++ b/priv/repo/migrations/20191026191023_set_not_null_for_instances.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForInstances do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE instances + ALTER COLUMN host SET NOT NULL") + end + + def down do + execute("ALTER TABLE instances + ALTER COLUMN host DROP NOT NULL") + end +end From b85bee32da237d2e877adc0218c02a663f2d48ae Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:42:24 +0300 Subject: [PATCH 22/43] Add migration --- .../20191026191100_set_not_null_for_lists.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 priv/repo/migrations/20191026191100_set_not_null_for_lists.exs diff --git a/priv/repo/migrations/20191026191100_set_not_null_for_lists.exs b/priv/repo/migrations/20191026191100_set_not_null_for_lists.exs new file mode 100644 index 000000000..40b8b136a --- /dev/null +++ b/priv/repo/migrations/20191026191100_set_not_null_for_lists.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForLists do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE lists + ALTER COLUMN user_id SET NOT NULL") + end + + def down do + execute("ALTER TABLE lists + ALTER COLUMN user_id DROP NOT NULL") + end +end From 55203c198ba4481204ad898dcee97d4cdcbb0433 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:43:32 +0300 Subject: [PATCH 23/43] Add migration --- .../20191026191134_set_not_null_for_markers.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 priv/repo/migrations/20191026191134_set_not_null_for_markers.exs diff --git a/priv/repo/migrations/20191026191134_set_not_null_for_markers.exs b/priv/repo/migrations/20191026191134_set_not_null_for_markers.exs new file mode 100644 index 000000000..7d7b73e7b --- /dev/null +++ b/priv/repo/migrations/20191026191134_set_not_null_for_markers.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForMarkers do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE markers + ALTER COLUMN user_id SET NOT NULL") + end + + def down do + execute("ALTER TABLE markers + ALTER COLUMN user_id DROP NOT NULL") + end +end From 13cc52dc601f858923d15f3dcf694b7667634bd7 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:44:42 +0300 Subject: [PATCH 24/43] Add migration --- ...1026191218_set_not_null_for_moderation_log.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 priv/repo/migrations/20191026191218_set_not_null_for_moderation_log.exs diff --git a/priv/repo/migrations/20191026191218_set_not_null_for_moderation_log.exs b/priv/repo/migrations/20191026191218_set_not_null_for_moderation_log.exs new file mode 100644 index 000000000..7238ca7f5 --- /dev/null +++ b/priv/repo/migrations/20191026191218_set_not_null_for_moderation_log.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForModerationLog do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE moderation_log + ALTER COLUMN data SET NOT NULL") + end + + def down do + execute("ALTER TABLE moderation_log + ALTER COLUMN data DROP NOT NULL") + end +end From cf0fa124a22a47ccbc6cfd9f279c93ed681a8bab Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:45:24 +0300 Subject: [PATCH 25/43] Add migration --- ...026191249_set_not_null_for_notifications.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 priv/repo/migrations/20191026191249_set_not_null_for_notifications.exs diff --git a/priv/repo/migrations/20191026191249_set_not_null_for_notifications.exs b/priv/repo/migrations/20191026191249_set_not_null_for_notifications.exs new file mode 100644 index 000000000..7e2976bab --- /dev/null +++ b/priv/repo/migrations/20191026191249_set_not_null_for_notifications.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForNotifications do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE notifications + ALTER COLUMN user_id SET NOT NULL, + ALTER COLUMN seen SET NOT NULL") + end + + def down do + execute("ALTER TABLE notifications + ALTER COLUMN user_id DROP NOT NULL, + ALTER COLUMN seen DROP NOT NULL") + end +end From d58cca5f0a407342bdd9a6102df9ee75897dec1d Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:46:09 +0300 Subject: [PATCH 26/43] Add migration --- ..._set_not_null_for_oauth_authorizations.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs diff --git a/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs b/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs new file mode 100644 index 000000000..bc6b36e69 --- /dev/null +++ b/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForOauthAuthorizations do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE oauth_authorizations + ALTER COLUMN app_id SET NOT NULL, + ALTER COLUMN token SET NOT NULL, + ALTER COLUMN used SET NOT NULL") + end + + def down do + execute("ALTER TABLE oauth_authorizations + ALTER COLUMN app_id DROP NOT NULL, + ALTER COLUMN token DROP NOT NULL, + ALTER COLUMN used DROP NOT NULL") + end +end From 5fece5f8bc38d2330c7855c97b44db8a5e2935b1 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:49:25 +0300 Subject: [PATCH 27/43] Put correct migration --- ..._set_not_null_for_oauth_authorizations.exs | 19 ------------------- ...26191401_set_not_null_for_oauth_tokens.exs | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 19 deletions(-) delete mode 100644 priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs create mode 100644 priv/repo/migrations/20191026191401_set_not_null_for_oauth_tokens.exs diff --git a/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs b/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs deleted file mode 100644 index bc6b36e69..000000000 --- a/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs +++ /dev/null @@ -1,19 +0,0 @@ -defmodule Pleroma.Repo.Migrations.SetNotNullForOauthAuthorizations do - use Ecto.Migration - - # modify/3 function will require index recreation, so using execute/1 instead - - def up do - execute("ALTER TABLE oauth_authorizations - ALTER COLUMN app_id SET NOT NULL, - ALTER COLUMN token SET NOT NULL, - ALTER COLUMN used SET NOT NULL") - end - - def down do - execute("ALTER TABLE oauth_authorizations - ALTER COLUMN app_id DROP NOT NULL, - ALTER COLUMN token DROP NOT NULL, - ALTER COLUMN used DROP NOT NULL") - end -end diff --git a/priv/repo/migrations/20191026191401_set_not_null_for_oauth_tokens.exs b/priv/repo/migrations/20191026191401_set_not_null_for_oauth_tokens.exs new file mode 100644 index 000000000..fe67db8cc --- /dev/null +++ b/priv/repo/migrations/20191026191401_set_not_null_for_oauth_tokens.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForOauthTokens do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE oauth_tokens + ALTER COLUMN app_id SET NOT NULL") + end + + def down do + execute("ALTER TABLE oauth_tokens + ALTER COLUMN app_id DROP NOT NULL") + end +end From c0b0fb19c867ef0ae7f511a572eda54a0683fbb3 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:50:52 +0300 Subject: [PATCH 28/43] Add migration --- ..._set_not_null_for_oauth_authorizations.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs diff --git a/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs b/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs new file mode 100644 index 000000000..bc6b36e69 --- /dev/null +++ b/priv/repo/migrations/20191026191328_set_not_null_for_oauth_authorizations.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForOauthAuthorizations do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE oauth_authorizations + ALTER COLUMN app_id SET NOT NULL, + ALTER COLUMN token SET NOT NULL, + ALTER COLUMN used SET NOT NULL") + end + + def down do + execute("ALTER TABLE oauth_authorizations + ALTER COLUMN app_id DROP NOT NULL, + ALTER COLUMN token DROP NOT NULL, + ALTER COLUMN used DROP NOT NULL") + end +end From 776c31267faeb58536ee93c24d806d9b4416baf7 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:52:49 +0300 Subject: [PATCH 29/43] Add migration --- .../20191026191442_set_not_null_for_objects.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 priv/repo/migrations/20191026191442_set_not_null_for_objects.exs diff --git a/priv/repo/migrations/20191026191442_set_not_null_for_objects.exs b/priv/repo/migrations/20191026191442_set_not_null_for_objects.exs new file mode 100644 index 000000000..59e89d6da --- /dev/null +++ b/priv/repo/migrations/20191026191442_set_not_null_for_objects.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForObjects do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE objects + ALTER COLUMN data SET NOT NULL") + end + + def down do + execute("ALTER TABLE objects + ALTER COLUMN data DROP NOT NULL") + end +end From a4cf6643851b0241e6d57b6140025a49366da1b0 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:53:41 +0300 Subject: [PATCH 30/43] Add migration --- ...set_not_null_for_password_reset_tokens.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 priv/repo/migrations/20191026191524_set_not_null_for_password_reset_tokens.exs diff --git a/priv/repo/migrations/20191026191524_set_not_null_for_password_reset_tokens.exs b/priv/repo/migrations/20191026191524_set_not_null_for_password_reset_tokens.exs new file mode 100644 index 000000000..51efadbd3 --- /dev/null +++ b/priv/repo/migrations/20191026191524_set_not_null_for_password_reset_tokens.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForPasswordResetTokens do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE password_reset_tokens + ALTER COLUMN token SET NOT NULL, + ALTER COLUMN user_id SET NOT NULL, + ALTER COLUMN used SET NOT NULL") + end + + def down do + execute("ALTER TABLE password_reset_tokens + ALTER COLUMN token DROP NOT NULL, + ALTER COLUMN user_id DROP NOT NULL, + ALTER COLUMN used DROP NOT NULL") + end +end From cf72b7649e521e03810fcb195ac1262b7d8385dd Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:54:37 +0300 Subject: [PATCH 31/43] Add migration --- ...03_set_not_null_for_push_subscriptions.exs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 priv/repo/migrations/20191026191603_set_not_null_for_push_subscriptions.exs diff --git a/priv/repo/migrations/20191026191603_set_not_null_for_push_subscriptions.exs b/priv/repo/migrations/20191026191603_set_not_null_for_push_subscriptions.exs new file mode 100644 index 000000000..0f3a73067 --- /dev/null +++ b/priv/repo/migrations/20191026191603_set_not_null_for_push_subscriptions.exs @@ -0,0 +1,25 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForPushSubscriptions do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE push_subscriptions + ALTER COLUMN user_id SET NOT NULL, + ALTER COLUMN token_id SET NOT NULL, + ALTER COLUMN endpoint SET NOT NULL, + ALTER COLUMN key_p256dh SET NOT NULL, + ALTER COLUMN key_auth SET NOT NULL, + ALTER COLUMN data SET NOT NULL") + end + + def down do + execute("ALTER TABLE push_subscriptions + ALTER COLUMN user_id DROP NOT NULL, + ALTER COLUMN token_id DROP NOT NULL, + ALTER COLUMN endpoint DROP NOT NULL, + ALTER COLUMN key_p256dh DROP NOT NULL, + ALTER COLUMN key_auth DROP NOT NULL, + ALTER COLUMN data DROP NOT NULL") + end +end From 281072921874465ac2968c82131c0c4730688de2 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:55:29 +0300 Subject: [PATCH 32/43] Add migration --- ...6191635_set_not_null_for_registrations.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 priv/repo/migrations/20191026191635_set_not_null_for_registrations.exs diff --git a/priv/repo/migrations/20191026191635_set_not_null_for_registrations.exs b/priv/repo/migrations/20191026191635_set_not_null_for_registrations.exs new file mode 100644 index 000000000..ddfbf4c5e --- /dev/null +++ b/priv/repo/migrations/20191026191635_set_not_null_for_registrations.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForRegistrations do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE registrations + ALTER COLUMN provider SET NOT NULL, + ALTER COLUMN uid SET NOT NULL, + ALTER COLUMN info SET NOT NULL") + end + + def down do + execute("ALTER TABLE registrations + ALTER COLUMN provider DROP NOT NULL, + ALTER COLUMN uid DROP NOT NULL, + ALTER COLUMN info DROP NOT NULL") + end +end From 2e608f7cbf46a02004982ddc48e5b0902869b401 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:56:48 +0300 Subject: [PATCH 33/43] Add migration --- ...1711_set_not_null_for_scheduled_activities.exs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 priv/repo/migrations/20191026191711_set_not_null_for_scheduled_activities.exs diff --git a/priv/repo/migrations/20191026191711_set_not_null_for_scheduled_activities.exs b/priv/repo/migrations/20191026191711_set_not_null_for_scheduled_activities.exs new file mode 100644 index 000000000..f1830c8c3 --- /dev/null +++ b/priv/repo/migrations/20191026191711_set_not_null_for_scheduled_activities.exs @@ -0,0 +1,15 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForScheduledActivities do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE scheduled_activities + ALTER COLUMN user_id SET NOT NULL") + end + + def down do + execute("ALTER TABLE scheduled_activities + ALTER COLUMN user_id DROP NOT NULL") + end +end From f5f9197fcee26af8d6a3bc8becb310a1c164623c Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:57:43 +0300 Subject: [PATCH 34/43] Add migration --- ...1026191753_set_not_null_for_thread_mutes.exs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 priv/repo/migrations/20191026191753_set_not_null_for_thread_mutes.exs diff --git a/priv/repo/migrations/20191026191753_set_not_null_for_thread_mutes.exs b/priv/repo/migrations/20191026191753_set_not_null_for_thread_mutes.exs new file mode 100644 index 000000000..daa7ce314 --- /dev/null +++ b/priv/repo/migrations/20191026191753_set_not_null_for_thread_mutes.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForThreadMutes do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE thread_mutes + ALTER COLUMN user_id SET NOT NULL, + ALTER COLUMN context SET NOT NULL") + end + + def down do + execute("ALTER TABLE thread_mutes + ALTER COLUMN user_id DROP NOT NULL, + ALTER COLUMN context DROP NOT NULL") + end +end From bdb20394531a71a573a870c7635a4265df7f0cc9 Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:58:37 +0300 Subject: [PATCH 35/43] Add migration --- ...26_set_not_null_for_user_invite_tokens.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 priv/repo/migrations/20191026191826_set_not_null_for_user_invite_tokens.exs diff --git a/priv/repo/migrations/20191026191826_set_not_null_for_user_invite_tokens.exs b/priv/repo/migrations/20191026191826_set_not_null_for_user_invite_tokens.exs new file mode 100644 index 000000000..836544f7f --- /dev/null +++ b/priv/repo/migrations/20191026191826_set_not_null_for_user_invite_tokens.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForUserInviteTokens do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + execute("ALTER TABLE user_invite_tokens + ALTER COLUMN used SET NOT NULL, + ALTER COLUMN uses SET NOT NULL, + ALTER COLUMN invite_type SET NOT NULL") + end + + def down do + execute("ALTER TABLE user_invite_tokens + ALTER COLUMN used DROP NOT NULL, + ALTER COLUMN uses DROP NOT NULL, + ALTER COLUMN invite_type DROP NOT NULL") + end +end From 175f6c83559387f71d49f6f3e4b1d7a12c5d011f Mon Sep 17 00:00:00 2001 From: Roman Chvanikov Date: Sat, 26 Oct 2019 22:59:24 +0300 Subject: [PATCH 36/43] Add migration --- .../20191026191910_set_not_null_for_users.exs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 priv/repo/migrations/20191026191910_set_not_null_for_users.exs diff --git a/priv/repo/migrations/20191026191910_set_not_null_for_users.exs b/priv/repo/migrations/20191026191910_set_not_null_for_users.exs new file mode 100644 index 000000000..f145a89ab --- /dev/null +++ b/priv/repo/migrations/20191026191910_set_not_null_for_users.exs @@ -0,0 +1,46 @@ +defmodule Pleroma.Repo.Migrations.SetNotNullForUsers do + use Ecto.Migration + + # modify/3 function will require index recreation, so using execute/1 instead + + def up do + # irreversible + execute("UPDATE users SET follower_count = 0 WHERE follower_count IS NULL") + + execute("ALTER TABLE users + ALTER COLUMN following SET NOT NULL, + ALTER COLUMN local SET NOT NULL, + ALTER COLUMN source_data SET NOT NULL, + ALTER COLUMN note_count SET NOT NULL, + ALTER COLUMN follower_count SET NOT NULL, + ALTER COLUMN blocks SET NOT NULL, + ALTER COLUMN domain_blocks SET NOT NULL, + ALTER COLUMN mutes SET NOT NULL, + ALTER COLUMN muted_reblogs SET NOT NULL, + ALTER COLUMN muted_notifications SET NOT NULL, + ALTER COLUMN subscribers SET NOT NULL, + ALTER COLUMN pinned_activities SET NOT NULL, + ALTER COLUMN emoji SET NOT NULL, + ALTER COLUMN fields SET NOT NULL, + ALTER COLUMN raw_fields SET NOT NULL") + end + + def down do + execute("ALTER TABLE users + ALTER COLUMN following DROP NOT NULL, + ALTER COLUMN local DROP NOT NULL, + ALTER COLUMN source_data DROP NOT NULL, + ALTER COLUMN note_count DROP NOT NULL, + ALTER COLUMN follower_count DROP NOT NULL, + ALTER COLUMN blocks DROP NOT NULL, + ALTER COLUMN domain_blocks DROP NOT NULL, + ALTER COLUMN mutes DROP NOT NULL, + ALTER COLUMN muted_reblogs DROP NOT NULL, + ALTER COLUMN muted_notifications DROP NOT NULL, + ALTER COLUMN subscribers DROP NOT NULL, + ALTER COLUMN pinned_activities DROP NOT NULL, + ALTER COLUMN emoji DROP NOT NULL, + ALTER COLUMN fields DROP NOT NULL, + ALTER COLUMN raw_fields DROP NOT NULL") + end +end From 68ec0ac3182a5fde005ef133dc674bf0af033fe6 Mon Sep 17 00:00:00 2001 From: Phil Hagelberg Date: Sat, 26 Oct 2019 16:19:58 -0700 Subject: [PATCH 37/43] Clarify that postgres contrib modules are required, not just postgres. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c8420c813..dd49822e9 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ While we don’t provide docker files, other people have written very good ones. ### Dependencies -* Postgresql version 9.6 or newer +* Postgresql version 9.6 or newer, including the contrib modules * Elixir version 1.7 or newer. If your distribution only has an old version available, check [Elixir’s install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf). * Build-essential tools From 3c86a0ab247acf0c037285fb4d22d47d335fe4c1 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Mon, 28 Oct 2019 14:45:50 +0700 Subject: [PATCH 38/43] Fix `SetNotNullForUsers` migration --- priv/repo/migrations/20191026191910_set_not_null_for_users.exs | 2 -- 1 file changed, 2 deletions(-) diff --git a/priv/repo/migrations/20191026191910_set_not_null_for_users.exs b/priv/repo/migrations/20191026191910_set_not_null_for_users.exs index f145a89ab..9d8d0ccf8 100644 --- a/priv/repo/migrations/20191026191910_set_not_null_for_users.exs +++ b/priv/repo/migrations/20191026191910_set_not_null_for_users.exs @@ -8,7 +8,6 @@ defmodule Pleroma.Repo.Migrations.SetNotNullForUsers do execute("UPDATE users SET follower_count = 0 WHERE follower_count IS NULL") execute("ALTER TABLE users - ALTER COLUMN following SET NOT NULL, ALTER COLUMN local SET NOT NULL, ALTER COLUMN source_data SET NOT NULL, ALTER COLUMN note_count SET NOT NULL, @@ -27,7 +26,6 @@ defmodule Pleroma.Repo.Migrations.SetNotNullForUsers do def down do execute("ALTER TABLE users - ALTER COLUMN following DROP NOT NULL, ALTER COLUMN local DROP NOT NULL, ALTER COLUMN source_data DROP NOT NULL, ALTER COLUMN note_count DROP NOT NULL, From 0c361eeb25230b20e1294fc022b20bf1e4be7d05 Mon Sep 17 00:00:00 2001 From: kPherox Date: Tue, 29 Oct 2019 17:12:49 +0900 Subject: [PATCH 39/43] Add pending to handle incoming for Follow activity --- lib/pleroma/web/activity_pub/transmogrifier.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 3c27b0d46..91a164eff 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -507,6 +507,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do }) {:user_locked, true} -> + {:ok, _relationship} = FollowingRelationship.update(follower, followed, "pending") :noop end From c2f2d7bcf66327b0eabf008f3819b374248c3ac2 Mon Sep 17 00:00:00 2001 From: kPherox Date: Tue, 29 Oct 2019 18:46:22 +0900 Subject: [PATCH 40/43] Add test follow activity for locked account --- test/web/activity_pub/transmogrifier_test.exs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 2f25c40d2..d920b969a 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -804,6 +804,25 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) end + test "it works for incoming follows to locked account" do + pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + user = insert(:user, locked: true) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Follow" + assert data["object"] == user.ap_id + assert data["state"] == "pending" + assert data["actor"] == "http://mastodon.example.org/users/admin" + + assert [^pending_follower] = User.get_follow_requests(user) + end + test "it works for incoming blocks" do user = insert(:user) From 5334190056b853dda57f16cbbaa230e8947821c6 Mon Sep 17 00:00:00 2001 From: kPherox Date: Tue, 29 Oct 2019 19:16:34 +0900 Subject: [PATCH 41/43] Migrate missing follow requests --- ...101340_migrate_missing_follow_requests.exs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 priv/repo/migrations/20191029101340_migrate_missing_follow_requests.exs diff --git a/priv/repo/migrations/20191029101340_migrate_missing_follow_requests.exs b/priv/repo/migrations/20191029101340_migrate_missing_follow_requests.exs new file mode 100644 index 000000000..1b2666f3a --- /dev/null +++ b/priv/repo/migrations/20191029101340_migrate_missing_follow_requests.exs @@ -0,0 +1,35 @@ +defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships do + use Ecto.Migration + + def change do + execute(import_pending_follows_from_activities(), "") + end + + defp import_pending_follows_from_activities do + """ + INSERT INTO + following_relationships ( + follower_id, + following_id, + state, + inserted_at, + updated_at + ) + SELECT + followers.id, + following.id, + activities.data ->> 'state', + (activities.data ->> 'published') :: timestamp, + now() + FROM + activities + JOIN users AS followers ON (activities.actor = followers.ap_id) + JOIN users AS following ON (activities.data ->> 'object' = following.ap_id) + WHERE + activities.data ->> 'type' = 'Follow' + AND activities.data ->> 'state' = 'pending' + ORDER BY activities.updated_at DESC + ON CONFLICT DO NOTHING + """ + end +end From 3e09b7c5ae2d8e6d3abb2020f2c63caad278e73f Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 29 Oct 2019 16:56:24 +0300 Subject: [PATCH 42/43] Fix two migrations sharing the same module name This makes ecto execute only the latter one. --- .../20191029101340_migrate_missing_follow_requests.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/priv/repo/migrations/20191029101340_migrate_missing_follow_requests.exs b/priv/repo/migrations/20191029101340_migrate_missing_follow_requests.exs index 1b2666f3a..90b18efc8 100644 --- a/priv/repo/migrations/20191029101340_migrate_missing_follow_requests.exs +++ b/priv/repo/migrations/20191029101340_migrate_missing_follow_requests.exs @@ -1,4 +1,4 @@ -defmodule Pleroma.Repo.Migrations.MigrateFollowingRelationships do +defmodule Pleroma.Repo.Migrations.MigrateMissingFollowingRelationships do use Ecto.Migration def change do From 40d5fb6ef875262795d87b95bff1ef44ee75d659 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 30 Oct 2019 15:52:37 +0700 Subject: [PATCH 43/43] Add a migration to fix blocked follows --- .../20191029172832_fix_blocked_follows.exs | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 priv/repo/migrations/20191029172832_fix_blocked_follows.exs diff --git a/priv/repo/migrations/20191029172832_fix_blocked_follows.exs b/priv/repo/migrations/20191029172832_fix_blocked_follows.exs new file mode 100644 index 000000000..71f8f1330 --- /dev/null +++ b/priv/repo/migrations/20191029172832_fix_blocked_follows.exs @@ -0,0 +1,112 @@ +defmodule Pleroma.Repo.Migrations.FixBlockedFollows do + use Ecto.Migration + + import Ecto.Query + alias Pleroma.Config + alias Pleroma.Repo + + def up do + unfollow_blocked = Config.get([:activitypub, :unfollow_blocked]) + + if unfollow_blocked do + "activities" + |> where([activity], fragment("? ->> 'type' = 'Block'", activity.data)) + |> distinct([activity], [ + activity.actor, + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object')", + activity.data, + activity.data + ) + ]) + |> order_by([activity], [fragment("? desc nulls last", activity.id)]) + |> select([activity], %{ + blocker: activity.actor, + blocked: + fragment("coalesce((?)->'object'->>'id', (?)->>'object')", activity.data, activity.data), + created_at: activity.id + }) + |> Repo.stream() + |> Enum.map(&unfollow_if_blocked/1) + |> Enum.uniq() + |> Enum.each(&update_follower_count/1) + end + end + + def down do + end + + def unfollow_if_blocked(%{blocker: blocker_id, blocked: blocked_id, created_at: blocked_at}) do + query = + from( + activity in "activities", + where: fragment("? ->> 'type' = 'Follow'", activity.data), + where: activity.actor == ^blocked_id, + # this is to use the index + where: + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", + activity.data, + activity.data, + ^blocker_id + ), + where: activity.id > ^blocked_at, + where: fragment("(?)->>'state' = 'accept'", activity.data), + order_by: [fragment("? desc nulls last", activity.id)] + ) + + unless Repo.exists?(query) do + blocker = "users" |> select([:id, :local]) |> Repo.get_by(ap_id: blocker_id) + blocked = "users" |> select([:id]) |> Repo.get_by(ap_id: blocked_id) + + if !is_nil(blocker) && !is_nil(blocked) do + unfollow(blocked, blocker) + end + end + end + + def unfollow(%{id: follower_id}, %{id: followed_id} = followed) do + following_relationship = + "following_relationships" + |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept") + |> select([:id]) + |> Repo.one() + + case following_relationship do + nil -> + {:ok, nil} + + %{id: following_relationship_id} -> + "following_relationships" + |> where(id: ^following_relationship_id) + |> Repo.delete_all() + + followed + end + end + + def update_follower_count(%{id: user_id} = user) do + if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do + follower_count_query = + "users" + |> where([u], u.id != ^user_id) + |> where([u], u.deactivated != ^true) + |> join(:inner, [u], r in "following_relationships", + as: :relationships, + on: r.following_id == ^user_id and r.follower_id == u.id + ) + |> where([relationships: r], r.state == "accept") + |> select([u], %{count: count(u.id)}) + + "users" + |> where(id: ^user_id) + |> join(:inner, [u], s in subquery(follower_count_query)) + |> update([u, s], + set: [follower_count: s.count] + ) + |> Repo.update_all([]) + end + end + + def update_follower_count(_), do: :noop +end