database.ex 3.21 KB
Newer Older
1
# Pleroma: A lightweight social networking server
2
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3
4
5
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Mix.Tasks.Pleroma.Database do
feld's avatar
feld committed
6
  alias Pleroma.Conversation
7
  alias Pleroma.Object
8
9
  alias Pleroma.Repo
  alias Pleroma.User
rinpatch's avatar
rinpatch committed
10
  require Logger
11
  require Pleroma.Constants
12
  import Mix.Pleroma
13
14
15
  use Mix.Task

  @shortdoc "A collection of database related tasks"
16
  @moduledoc File.read!("docs/administration/CLI_tasks/database.md")
17
18
19
20
21
22
23
24
25
26

  def run(["remove_embedded_objects" | args]) do
    {options, [], []} =
      OptionParser.parse(
        args,
        strict: [
          vacuum: :boolean
        ]
      )

27
    start_pleroma()
rinpatch's avatar
rinpatch committed
28
    Logger.info("Removing embedded objects")
29

30
    Repo.query!(
rinpatch's avatar
rinpatch committed
31
32
33
      "update activities set data = jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
      [],
      timeout: :infinity
34
35
36
    )

    if Keyword.get(options, :vacuum) do
rinpatch's avatar
rinpatch committed
37
38
      Logger.info("Runnning VACUUM FULL")

39
      Repo.query!(
rinpatch's avatar
rinpatch committed
40
41
42
        "vacuum full;",
        [],
        timeout: :infinity
43
44
45
      )
    end
  end
feld's avatar
feld committed
46
47

  def run(["bump_all_conversations"]) do
48
    start_pleroma()
feld's avatar
feld committed
49
50
    Conversation.bump_for_all_activities()
  end
51
52

  def run(["update_users_following_followers_counts"]) do
53
    start_pleroma()
54

55
56
57
    User
    |> Repo.all()
    |> Enum.each(&User.update_follower_count/1)
58
  end
59
60

  def run(["prune_objects" | args]) do
61
62
    import Ecto.Query

63
64
65
66
67
68
69
70
    {options, [], []} =
      OptionParser.parse(
        args,
        strict: [
          vacuum: :boolean
        ]
      )

71
    start_pleroma()
72
73
74
75
76
77
78
79
80

    deadline = Pleroma.Config.get([:instance, :remote_post_retention_days])

    Logger.info("Pruning objects older than #{deadline} days")

    time_deadline =
      NaiveDateTime.utc_now()
      |> NaiveDateTime.add(-(deadline * 86_400))

81
    from(o in Object,
82
83
84
85
86
87
88
89
      where:
        fragment(
          "?->'to' \\? ? OR ?->'cc' \\? ?",
          o.data,
          ^Pleroma.Constants.as_public(),
          o.data,
          ^Pleroma.Constants.as_public()
        ),
90
91
92
      where: o.inserted_at < ^time_deadline,
      where:
        fragment("split_part(?->>'actor', '/', 3) != ?", o.data, ^Pleroma.Web.Endpoint.host())
93
    )
94
    |> Repo.delete_all(timeout: :infinity)
95
96
97
98
99
100
101
102
103
104
105

    if Keyword.get(options, :vacuum) do
      Logger.info("Runnning VACUUM FULL")

      Repo.query!(
        "vacuum full;",
        [],
        timeout: :infinity
      )
    end
  end
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

  def run(["fix_likes_collections"]) do
    import Ecto.Query

    start_pleroma()

    from(object in Object,
      where: fragment("(?)->>'likes' is not null", object.data),
      select: %{id: object.id, likes: fragment("(?)->>'likes'", object.data)}
    )
    |> Pleroma.RepoStreamer.chunk_stream(100)
    |> Stream.each(fn objects ->
      ids =
        objects
        |> Enum.filter(fn object -> object.likes |> Jason.decode!() |> is_map() end)
        |> Enum.map(& &1.id)

      Object
      |> where([object], object.id in ^ids)
      |> update([object],
        set: [
          data:
            fragment(
              "jsonb_set(?, '{likes}', '[]'::jsonb, true)",
              object.data
            )
        ]
      )
      |> Repo.update_all([], timeout: :infinity)
    end)
    |> Stream.run()
  end
138
end