2f1f3346988c8d9aa1aaec0c57dd3cf0911f959f
[akkoma] / lib / mix / tasks / pleroma / database.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Mix.Tasks.Pleroma.Database do
6 alias Pleroma.Conversation
7 alias Pleroma.Object
8 alias Pleroma.Repo
9 alias Pleroma.User
10 require Logger
11 require Pleroma.Constants
12 import Mix.Pleroma
13 use Mix.Task
14
15 @shortdoc "A collection of database related tasks"
16 @moduledoc File.read!("docs/administration/CLI_tasks/database.md")
17
18 def run(["remove_embedded_objects" | args]) do
19 {options, [], []} =
20 OptionParser.parse(
21 args,
22 strict: [
23 vacuum: :boolean
24 ]
25 )
26
27 start_pleroma()
28 Logger.info("Removing embedded objects")
29
30 Repo.query!(
31 "update activities set data = safe_jsonb_set(data, '{object}'::text[], data->'object'->'id') where data->'object'->>'id' is not null;",
32 [],
33 timeout: :infinity
34 )
35
36 if Keyword.get(options, :vacuum) do
37 Logger.info("Runnning VACUUM FULL.")
38
39 Logger.warn(
40 "Re-packing your entire database may take a while and will consume extra disk space during the process."
41 )
42
43 Repo.query!(
44 "vacuum full;",
45 [],
46 timeout: :infinity
47 )
48 end
49 end
50
51 def run(["bump_all_conversations"]) do
52 start_pleroma()
53 Conversation.bump_for_all_activities()
54 end
55
56 def run(["update_users_following_followers_counts"]) do
57 start_pleroma()
58
59 User
60 |> Repo.all()
61 |> Enum.each(&User.update_follower_count/1)
62 end
63
64 def run(["prune_objects" | args]) do
65 import Ecto.Query
66
67 {options, [], []} =
68 OptionParser.parse(
69 args,
70 strict: [
71 vacuum: :boolean
72 ]
73 )
74
75 start_pleroma()
76
77 deadline = Pleroma.Config.get([:instance, :remote_post_retention_days])
78
79 Logger.info("Pruning objects older than #{deadline} days")
80
81 time_deadline =
82 NaiveDateTime.utc_now()
83 |> NaiveDateTime.add(-(deadline * 86_400))
84
85 from(o in Object,
86 where:
87 fragment(
88 "?->'to' \\? ? OR ?->'cc' \\? ?",
89 o.data,
90 ^Pleroma.Constants.as_public(),
91 o.data,
92 ^Pleroma.Constants.as_public()
93 ),
94 where: o.inserted_at < ^time_deadline,
95 where:
96 fragment("split_part(?->>'actor', '/', 3) != ?", o.data, ^Pleroma.Web.Endpoint.host())
97 )
98 |> Repo.delete_all(timeout: :infinity)
99
100 if Keyword.get(options, :vacuum) do
101 Logger.info("Runnning VACUUM FULL.")
102
103 Logger.warn(
104 "Re-packing your entire database may take a while and will consume extra disk space during the process."
105 )
106
107 Repo.query!(
108 "vacuum full;",
109 [],
110 timeout: :infinity
111 )
112 end
113 end
114
115 def run(["fix_likes_collections"]) do
116 import Ecto.Query
117
118 start_pleroma()
119
120 from(object in Object,
121 where: fragment("(?)->>'likes' is not null", object.data),
122 select: %{id: object.id, likes: fragment("(?)->>'likes'", object.data)}
123 )
124 |> Pleroma.RepoStreamer.chunk_stream(100)
125 |> Stream.each(fn objects ->
126 ids =
127 objects
128 |> Enum.filter(fn object -> object.likes |> Jason.decode!() |> is_map() end)
129 |> Enum.map(& &1.id)
130
131 Object
132 |> where([object], object.id in ^ids)
133 |> update([object],
134 set: [
135 data:
136 fragment(
137 "safe_jsonb_set(?, '{likes}', '[]'::jsonb, true)",
138 object.data
139 )
140 ]
141 )
142 |> Repo.update_all([], timeout: :infinity)
143 end)
144 |> Stream.run()
145 end
146
147 def run(["vacuum", args]) do
148 start_pleroma()
149
150 case args do
151 "analyze" ->
152 Logger.info("Runnning VACUUM ANALYZE.")
153
154 Repo.query!(
155 "vacuum analyze;",
156 [],
157 timeout: :infinity
158 )
159
160 "full" ->
161 Logger.info("Runnning VACUUM FULL.")
162
163 Logger.warn(
164 "Re-packing your entire database may take a while and will consume extra disk space during the process."
165 )
166
167 Repo.query!(
168 "vacuum full;",
169 [],
170 timeout: :infinity
171 )
172
173 _ ->
174 Logger.error("Error: invalid vacuum argument.")
175 end
176 end
177 end