move to 2.5.5
[anni] / priv / repo / migrations / 20191029172832_fix_blocked_follows.exs
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Repo.Migrations.FixBlockedFollows do
6   use Ecto.Migration
7
8   import Ecto.Query
9   alias Pleroma.Config
10   alias Pleroma.Repo
11
12   def up do
13     unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
14
15     if unfollow_blocked do
16       "activities"
17       |> where([activity], fragment("? ->> 'type' = 'Block'", activity.data))
18       |> distinct([activity], [
19         activity.actor,
20         fragment(
21           "coalesce((?)->'object'->>'id', (?)->>'object')",
22           activity.data,
23           activity.data
24         )
25       ])
26       |> order_by([activity], [fragment("? desc nulls last", activity.id)])
27       |> select([activity], %{
28         blocker: activity.actor,
29         blocked:
30           fragment("coalesce((?)->'object'->>'id', (?)->>'object')", activity.data, activity.data),
31         created_at: activity.id
32       })
33       |> Repo.stream()
34       |> Enum.map(&unfollow_if_blocked/1)
35       |> Enum.uniq()
36       |> Enum.each(&update_follower_count/1)
37     end
38   end
39
40   def down do
41   end
42
43   def unfollow_if_blocked(%{blocker: blocker_id, blocked: blocked_id, created_at: blocked_at}) do
44     query =
45       from(
46         activity in "activities",
47         where: fragment("? ->> 'type' = 'Follow'", activity.data),
48         where: activity.actor == ^blocked_id,
49         # this is to use the index
50         where:
51           fragment(
52             "coalesce((?)->'object'->>'id', (?)->>'object') = ?",
53             activity.data,
54             activity.data,
55             ^blocker_id
56           ),
57         where: activity.id > ^blocked_at,
58         where: fragment("(?)->>'state' = 'accept'", activity.data),
59         order_by: [fragment("? desc nulls last", activity.id)]
60       )
61
62     unless Repo.exists?(query) do
63       blocker = "users" |> select([:id, :local]) |> Repo.get_by(ap_id: blocker_id)
64       blocked = "users" |> select([:id]) |> Repo.get_by(ap_id: blocked_id)
65
66       if !is_nil(blocker) && !is_nil(blocked) do
67         unfollow(blocked, blocker)
68       end
69     end
70   end
71
72   def unfollow(%{id: follower_id}, %{id: followed_id} = followed) do
73     following_relationship =
74       "following_relationships"
75       |> where(follower_id: ^follower_id, following_id: ^followed_id, state: "accept")
76       |> select([:id])
77       |> Repo.one()
78
79     case following_relationship do
80       nil ->
81         {:ok, nil}
82
83       %{id: following_relationship_id} ->
84         "following_relationships"
85         |> where(id: ^following_relationship_id)
86         |> Repo.delete_all()
87
88         followed
89     end
90   end
91
92   def update_follower_count(%{id: user_id} = user) do
93     if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do
94       follower_count_query =
95         "users"
96         |> where([u], u.id != ^user_id)
97         |> where([u], u.deactivated != ^true)
98         |> join(:inner, [u], r in "following_relationships",
99           as: :relationships,
100           on: r.following_id == ^user_id and r.follower_id == u.id
101         )
102         |> where([relationships: r], r.state == "accept")
103         |> select([u], %{count: count(u.id)})
104
105       "users"
106       |> where(id: ^user_id)
107       |> join(:inner, [u], s in subquery(follower_count_query))
108       |> update([u, s],
109         set: [follower_count: s.count]
110       )
111       |> Repo.update_all([])
112     end
113   end
114
115   def update_follower_count(_), do: :noop
116 end