e5536d16ab2c5a53b5a281d46732903670831efd
[akkoma] / lib / mix / tasks / pleroma / config.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.Config do
6 use Mix.Task
7
8 import Mix.Pleroma
9
10 alias Pleroma.ConfigDB
11 alias Pleroma.Repo
12
13 @shortdoc "Manages the location of the config"
14 @moduledoc File.read!("docs/administration/CLI_tasks/config.md")
15
16 def run(["migrate_to_db"]) do
17 start_pleroma()
18 migrate_to_db()
19 end
20
21 def run(["migrate_from_db" | options]) do
22 start_pleroma()
23
24 {opts, _} =
25 OptionParser.parse!(options,
26 strict: [env: :string, delete: :boolean],
27 aliases: [d: :delete]
28 )
29
30 migrate_from_db(opts)
31 end
32
33 def run(["dump"]) do
34 with true <- Pleroma.Config.get([:configurable_from_database]) do
35 start_pleroma()
36
37 header = config_header()
38
39 shell_info("#{header}")
40
41 ConfigDB
42 |> Repo.all()
43 |> Enum.sort()
44 |> Enum.each(&dump(&1))
45 else
46 _ -> configdb_not_enabled()
47 end
48 end
49
50 def run(["dump", group, key]) do
51 with true <- Pleroma.Config.get([:configurable_from_database]) do
52 start_pleroma()
53
54 dump_key(group, key)
55 else
56 _ -> configdb_not_enabled()
57 end
58 end
59
60 def run(["dump", group]) do
61 with true <- Pleroma.Config.get([:configurable_from_database]) do
62 start_pleroma()
63
64 dump_group(group)
65 else
66 _ -> configdb_not_enabled()
67 end
68 end
69
70 def run(["groups"]) do
71 with true <- Pleroma.Config.get([:configurable_from_database]) do
72 start_pleroma()
73
74 groups =
75 ConfigDB
76 |> Repo.all()
77 |> Enum.map(fn x -> x.group end)
78 |> Enum.sort()
79 |> Enum.uniq()
80
81 if length(groups) > 0 do
82 shell_info("The following configuration groups are set in ConfigDB:\r\n")
83 groups |> Enum.each(fn x -> shell_info("- #{x}") end)
84 shell_info("\r\n")
85 end
86 else
87 _ -> configdb_not_enabled()
88 end
89 end
90
91 def run(["keys" | group]) do
92 with true <- Pleroma.Config.get([:configurable_from_database]) do
93 start_pleroma()
94
95 keys =
96 ConfigDB
97 |> Repo.all()
98 |> Enum.map(fn x ->
99 if x.group == group do
100 x.key
101 end
102 end)
103 |> Enum.sort()
104 |> Enum.uniq()
105 |> Enum.reject(fn x -> x == nil end)
106
107 if length(keys) > 0 do
108 shell_info("The following configuration keys under :#{group} are set in ConfigDB:\r\n")
109 keys |> Enum.each(fn x -> shell_info("- #{x}") end)
110 shell_info("\r\n")
111 end
112 else
113 _ -> configdb_not_enabled()
114 end
115 end
116
117 def run(["reset"]) do
118 with true <- Pleroma.Config.get([:configurable_from_database]) do
119 start_pleroma()
120
121 if shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
122 Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
123 Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
124
125 shell_info("The ConfigDB settings have been removed from the database.")
126 else
127 shell_info("No changes made.")
128 end
129 else
130 _ -> configdb_not_enabled()
131 end
132 end
133
134 def run(["delete", group]) do
135 with true <- Pleroma.Config.get([:configurable_from_database]) do
136 start_pleroma()
137
138 group = group |> String.to_atom()
139
140 if shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
141 ConfigDB
142 |> Repo.all()
143 |> Enum.filter(fn x ->
144 if x.group == group do
145 x |> delete(true)
146 end
147 end)
148 else
149 shell_info("No changes made.")
150 end
151 else
152 _ -> configdb_not_enabled()
153 end
154 end
155
156 def run(["delete", group, key]) do
157 with true <- Pleroma.Config.get([:configurable_from_database]) do
158 start_pleroma()
159
160 group = group |> String.to_atom()
161 key = key |> String.to_atom()
162
163 if shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
164 ConfigDB
165 |> Repo.all()
166 |> Enum.filter(fn x ->
167 if x.group == group and x.key == key do
168 x |> delete(true)
169 end
170 end)
171 else
172 shell_info("No changes made.")
173 end
174 else
175 _ -> configdb_not_enabled()
176 end
177 end
178
179 @spec migrate_to_db(Path.t() | nil) :: any()
180 def migrate_to_db(file_path \\ nil) do
181 with true <- Pleroma.Config.get([:configurable_from_database]),
182 :ok <- Pleroma.Config.DeprecationWarnings.warn() do
183 config_file =
184 if file_path do
185 file_path
186 else
187 if Pleroma.Config.get(:release) do
188 Pleroma.Config.get(:config_path)
189 else
190 "config/#{Pleroma.Config.get(:env)}.secret.exs"
191 end
192 end
193
194 do_migrate_to_db(config_file)
195 else
196 :error -> deprecation_error()
197 _ -> migration_error()
198 end
199 end
200
201 defp do_migrate_to_db(config_file) do
202 if File.exists?(config_file) do
203 shell_info("Migrating settings from file: #{Path.expand(config_file)}")
204 Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
205 Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
206
207 custom_config =
208 config_file
209 |> read_file()
210 |> elem(0)
211
212 custom_config
213 |> Keyword.keys()
214 |> Enum.each(&create(&1, custom_config))
215 else
216 shell_info("To migrate settings, you must define custom settings in #{config_file}.")
217 end
218 end
219
220 defp create(group, settings) do
221 group
222 |> Pleroma.Config.Loader.filter_group(settings)
223 |> Enum.each(fn {key, value} ->
224 {:ok, _} = ConfigDB.update_or_create(%{group: group, key: key, value: value})
225
226 shell_info("Settings for key #{key} migrated.")
227 end)
228
229 shell_info("Settings for group :#{group} migrated.")
230 end
231
232 defp migrate_from_db(opts) do
233 if Pleroma.Config.get([:configurable_from_database]) do
234 env = opts[:env] || Pleroma.Config.get(:env)
235
236 config_path =
237 if Pleroma.Config.get(:release) do
238 :config_path
239 |> Pleroma.Config.get()
240 |> Path.dirname()
241 else
242 "config"
243 end
244 |> Path.join("#{env}.exported_from_db.secret.exs")
245
246 file = File.open!(config_path, [:write, :utf8])
247
248 IO.write(file, config_header())
249
250 ConfigDB
251 |> Repo.all()
252 |> Enum.each(&write_and_delete(&1, file, opts[:delete]))
253
254 :ok = File.close(file)
255 System.cmd("mix", ["format", config_path])
256
257 shell_info(
258 "Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs"
259 )
260 else
261 migration_error()
262 end
263 end
264
265 defp migration_error do
266 shell_error(
267 "Migration is not allowed in config. You can change this behavior by setting `config :pleroma, configurable_from_database: true`"
268 )
269 end
270
271 defp deprecation_error do
272 shell_error("Migration is not allowed until all deprecation warnings have been resolved.")
273 end
274
275 if Code.ensure_loaded?(Config.Reader) do
276 defp config_header, do: "import Config\r\n\r\n"
277 defp read_file(config_file), do: Config.Reader.read_imports!(config_file)
278 else
279 defp config_header, do: "use Mix.Config\r\n\r\n"
280 defp read_file(config_file), do: Mix.Config.eval!(config_file)
281 end
282
283 defp write_and_delete(config, file, delete?) do
284 config
285 |> write(file)
286 |> delete(delete?)
287 end
288
289 defp write(config, file) do
290 value = inspect(config.value, limit: :infinity)
291
292 IO.write(file, "config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")
293
294 config
295 end
296
297 defp delete(config, true) do
298 {:ok, _} = Repo.delete(config)
299 shell_info(":#{config.group}, :#{config.key} deleted from the ConfigDB.")
300 end
301
302 defp delete(_config, _), do: :ok
303
304 defp dump(%Pleroma.ConfigDB{} = config) do
305 value = inspect(config.value, limit: :infinity)
306
307 shell_info("config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")
308 end
309
310 defp configdb_not_enabled do
311 shell_error(
312 "ConfigDB not enabled. Please check the value of :configurable_from_database in your configuration."
313 )
314 end
315
316 defp dump_key(group, key) do
317 group = group |> String.to_atom()
318 key = key |> String.to_atom()
319
320 ConfigDB
321 |> Repo.all()
322 |> Enum.filter(fn x ->
323 if x.group == group && x.key == key do
324 x |> dump
325 end
326 end)
327 end
328
329 defp dump_group(group) do
330 group = group |> String.to_atom()
331
332 ConfigDB
333 |> Repo.all()
334 |> Enum.filter(fn x ->
335 if x.group == group do
336 x |> dump
337 end
338 end)
339 end
340 end