Support listing groups, listing keys in a group, and dumping the config based on...
[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" | args]) when is_list(args) and length(args) < 3 do
51 with true <- Pleroma.Config.get([:configurable_from_database]) do
52 start_pleroma()
53
54 if length(args) > 1 do
55 [group, key] = args
56 dump_key(group, key)
57 else
58 [group] = args
59 dump_group(group)
60 end
61 else
62 _ -> configdb_not_enabled()
63 end
64 end
65
66 def run(["groups"]) do
67 with true <- Pleroma.Config.get([:configurable_from_database]) do
68 start_pleroma()
69
70 groups =
71 ConfigDB
72 |> Repo.all()
73 |> Enum.map(fn x -> x.group end)
74 |> Enum.sort()
75 |> Enum.uniq()
76
77 if length(groups) > 0 do
78 shell_info("The following configuration groups are set in ConfigDB:\r\n")
79 groups |> Enum.each(fn x -> shell_info("- #{x}") end)
80 shell_info("\r\n")
81 end
82 else
83 _ -> configdb_not_enabled()
84 end
85 end
86
87 def run(["keys" | group]) do
88 with true <- Pleroma.Config.get([:configurable_from_database]) do
89 start_pleroma()
90
91 keys =
92 ConfigDB
93 |> Repo.all()
94 |> Enum.map(fn x ->
95 if x.group == group do
96 x.key
97 end
98 end)
99 |> Enum.sort()
100 |> Enum.uniq()
101 |> Enum.reject(fn x -> x == nil end)
102
103 if length(keys) > 0 do
104 shell_info("The following configuration keys under :#{group} are set in ConfigDB:\r\n")
105 keys |> Enum.each(fn x -> shell_info("- #{x}") end)
106 shell_info("\r\n")
107 end
108 else
109 _ -> configdb_not_enabled()
110 end
111 end
112
113 def run(["reset"]) do
114 with true <- Pleroma.Config.get([:configurable_from_database]) do
115 start_pleroma()
116
117 Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
118 Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
119
120 shell_info("The ConfigDB settings have been removed from the database.")
121 else
122 _ -> configdb_not_enabled()
123 end
124 end
125
126 def run(["groupdel" | dbkey]) do
127 unless [] == dbkey do
128 with true <- Pleroma.Config.get([:configurable_from_database]) do
129 start_pleroma()
130
131 dbkey = dbkey |> List.first() |> String.to_atom()
132
133 ConfigDB
134 |> Repo.all()
135 |> Enum.filter(fn x ->
136 if x.key == dbkey do
137 x |> delete(true)
138 end
139 end)
140 else
141 _ -> configdb_not_enabled()
142 end
143 else
144 shell_error(
145 "You must provide a group to delete. Use the groups command to get a list of valid configDB groups."
146 )
147 end
148 end
149
150 @spec migrate_to_db(Path.t() | nil) :: any()
151 def migrate_to_db(file_path \\ nil) do
152 with true <- Pleroma.Config.get([:configurable_from_database]),
153 :ok <- Pleroma.Config.DeprecationWarnings.warn() do
154 config_file =
155 if file_path do
156 file_path
157 else
158 if Pleroma.Config.get(:release) do
159 Pleroma.Config.get(:config_path)
160 else
161 "config/#{Pleroma.Config.get(:env)}.secret.exs"
162 end
163 end
164
165 do_migrate_to_db(config_file)
166 else
167 :error -> deprecation_error()
168 _ -> migration_error()
169 end
170 end
171
172 defp do_migrate_to_db(config_file) do
173 if File.exists?(config_file) do
174 shell_info("Migrating settings from file: #{Path.expand(config_file)}")
175 Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
176 Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
177
178 custom_config =
179 config_file
180 |> read_file()
181 |> elem(0)
182
183 custom_config
184 |> Keyword.keys()
185 |> Enum.each(&create(&1, custom_config))
186 else
187 shell_info("To migrate settings, you must define custom settings in #{config_file}.")
188 end
189 end
190
191 defp create(group, settings) do
192 group
193 |> Pleroma.Config.Loader.filter_group(settings)
194 |> Enum.each(fn {key, value} ->
195 {:ok, _} = ConfigDB.update_or_create(%{group: group, key: key, value: value})
196
197 shell_info("Settings for key #{key} migrated.")
198 end)
199
200 shell_info("Settings for group :#{group} migrated.")
201 end
202
203 defp migrate_from_db(opts) do
204 if Pleroma.Config.get([:configurable_from_database]) do
205 env = opts[:env] || Pleroma.Config.get(:env)
206
207 config_path =
208 if Pleroma.Config.get(:release) do
209 :config_path
210 |> Pleroma.Config.get()
211 |> Path.dirname()
212 else
213 "config"
214 end
215 |> Path.join("#{env}.exported_from_db.secret.exs")
216
217 file = File.open!(config_path, [:write, :utf8])
218
219 IO.write(file, config_header())
220
221 ConfigDB
222 |> Repo.all()
223 |> Enum.each(&write_and_delete(&1, file, opts[:delete]))
224
225 :ok = File.close(file)
226 System.cmd("mix", ["format", config_path])
227
228 shell_info(
229 "Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs"
230 )
231 else
232 migration_error()
233 end
234 end
235
236 defp migration_error do
237 shell_error(
238 "Migration is not allowed in config. You can change this behavior by setting `config :pleroma, configurable_from_database: true`"
239 )
240 end
241
242 defp deprecation_error do
243 shell_error("Migration is not allowed until all deprecation warnings have been resolved.")
244 end
245
246 if Code.ensure_loaded?(Config.Reader) do
247 defp config_header, do: "import Config\r\n\r\n"
248 defp read_file(config_file), do: Config.Reader.read_imports!(config_file)
249 else
250 defp config_header, do: "use Mix.Config\r\n\r\n"
251 defp read_file(config_file), do: Mix.Config.eval!(config_file)
252 end
253
254 defp write_and_delete(config, file, delete?) do
255 config
256 |> write(file)
257 |> delete(delete?)
258 end
259
260 defp write(config, file) do
261 value = inspect(config.value, limit: :infinity)
262
263 IO.write(file, "config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")
264
265 config
266 end
267
268 defp delete(config, true) do
269 {:ok, _} = Repo.delete(config)
270 shell_info("#{config.key} deleted from the ConfigDB.")
271 end
272
273 defp delete(_config, _), do: :ok
274
275 defp dump(%Pleroma.ConfigDB{} = config) do
276 value = inspect(config.value, limit: :infinity)
277
278 shell_info("config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")
279 end
280
281 defp configdb_not_enabled do
282 shell_error(
283 "ConfigDB not enabled. Please check the value of :configurable_from_database in your configuration."
284 )
285 end
286
287 defp dump_key(group, key) do
288 group = group |> String.to_atom()
289 key = key |> String.to_atom()
290
291 ConfigDB
292 |> Repo.all()
293 |> Enum.filter(fn x ->
294 if x.group == group && x.key == key do
295 x |> dump
296 end
297 end)
298 end
299
300 defp dump_group(group) do
301 group = group |> String.to_atom()
302
303 ConfigDB
304 |> Repo.all()
305 |> Enum.filter(fn x ->
306 if x.group == group do
307 x |> dump
308 end
309 end)
310 end
311 end