Add mix tasks to give additional recovery and debugging options
[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.each(&dump(&1))
44 else
45 _ -> configdb_not_enabled()
46 end
47 end
48
49 def run(["dump" | dbkey]) do
50 with true <- Pleroma.Config.get([:configurable_from_database]) do
51 start_pleroma()
52
53 dbkey = dbkey |> List.first() |> String.to_atom()
54
55 ConfigDB
56 |> Repo.all()
57 |> Enum.filter(fn x ->
58 if x.key == dbkey do
59 x |> dump
60 end
61 end)
62 else
63 _ -> configdb_not_enabled()
64 end
65 end
66
67 def run(["keylist"]) do
68 with true <- Pleroma.Config.get([:configurable_from_database]) do
69 start_pleroma()
70
71 keys =
72 ConfigDB
73 |> Repo.all()
74 |> Enum.map(fn x -> x.key end)
75
76 if length(keys) > 0 do
77 shell_info("The following configuration keys are set in ConfigDB:\r\n")
78 keys |> Enum.each(fn x -> shell_info("- #{x}") end)
79 shell_info("\r\n")
80 end
81 else
82 _ -> configdb_not_enabled()
83 end
84 end
85
86 def run(["keydel" | dbkey]) do
87 unless [] == dbkey do
88 with true <- Pleroma.Config.get([:configurable_from_database]) do
89 start_pleroma()
90
91 dbkey = dbkey |> List.first() |> String.to_atom()
92
93 ConfigDB
94 |> Repo.all()
95 |> Enum.filter(fn x ->
96 if x.key == dbkey do
97 x |> delete(true)
98 end
99 end)
100 else
101 _ -> configdb_not_enabled()
102 end
103 else
104 shell_error(
105 "You must provide a key to delete. Use the keylist command to get a list of valid keys."
106 )
107 end
108 end
109
110 @spec migrate_to_db(Path.t() | nil) :: any()
111 def migrate_to_db(file_path \\ nil) do
112 with true <- Pleroma.Config.get([:configurable_from_database]),
113 :ok <- Pleroma.Config.DeprecationWarnings.warn() do
114 config_file =
115 if file_path do
116 file_path
117 else
118 if Pleroma.Config.get(:release) do
119 Pleroma.Config.get(:config_path)
120 else
121 "config/#{Pleroma.Config.get(:env)}.secret.exs"
122 end
123 end
124
125 do_migrate_to_db(config_file)
126 else
127 :error -> deprecation_error()
128 _ -> migration_error()
129 end
130 end
131
132 defp do_migrate_to_db(config_file) do
133 if File.exists?(config_file) do
134 shell_info("Migrating settings from file: #{Path.expand(config_file)}")
135 Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
136 Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
137
138 custom_config =
139 config_file
140 |> read_file()
141 |> elem(0)
142
143 custom_config
144 |> Keyword.keys()
145 |> Enum.each(&create(&1, custom_config))
146 else
147 shell_info("To migrate settings, you must define custom settings in #{config_file}.")
148 end
149 end
150
151 defp create(group, settings) do
152 group
153 |> Pleroma.Config.Loader.filter_group(settings)
154 |> Enum.each(fn {key, value} ->
155 {:ok, _} = ConfigDB.update_or_create(%{group: group, key: key, value: value})
156
157 shell_info("Settings for key #{key} migrated.")
158 end)
159
160 shell_info("Settings for group :#{group} migrated.")
161 end
162
163 defp migrate_from_db(opts) do
164 if Pleroma.Config.get([:configurable_from_database]) do
165 env = opts[:env] || Pleroma.Config.get(:env)
166
167 config_path =
168 if Pleroma.Config.get(:release) do
169 :config_path
170 |> Pleroma.Config.get()
171 |> Path.dirname()
172 else
173 "config"
174 end
175 |> Path.join("#{env}.exported_from_db.secret.exs")
176
177 file = File.open!(config_path, [:write, :utf8])
178
179 IO.write(file, config_header())
180
181 ConfigDB
182 |> Repo.all()
183 |> Enum.each(&write_and_delete(&1, file, opts[:delete]))
184
185 :ok = File.close(file)
186 System.cmd("mix", ["format", config_path])
187
188 shell_info(
189 "Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs"
190 )
191 else
192 migration_error()
193 end
194 end
195
196 defp migration_error do
197 shell_error(
198 "Migration is not allowed in config. You can change this behavior by setting `config :pleroma, configurable_from_database: true`"
199 )
200 end
201
202 defp deprecation_error do
203 shell_error("Migration is not allowed until all deprecation warnings have been resolved.")
204 end
205
206 if Code.ensure_loaded?(Config.Reader) do
207 defp config_header, do: "import Config\r\n\r\n"
208 defp read_file(config_file), do: Config.Reader.read_imports!(config_file)
209 else
210 defp config_header, do: "use Mix.Config\r\n\r\n"
211 defp read_file(config_file), do: Mix.Config.eval!(config_file)
212 end
213
214 defp write_and_delete(config, file, delete?) do
215 config
216 |> write(file)
217 |> delete(delete?)
218 end
219
220 defp write(config, file) do
221 value = inspect(config.value, limit: :infinity)
222
223 IO.write(file, "config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")
224
225 config
226 end
227
228 defp delete(config, true) do
229 {:ok, _} = Repo.delete(config)
230 shell_info("#{config.key} deleted from DB.")
231 end
232
233 defp delete(_config, _), do: :ok
234
235 defp dump(%Pleroma.ConfigDB{} = config) do
236 value = inspect(config.value, limit: :infinity)
237
238 shell_info("config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")
239 end
240
241 defp configdb_not_enabled do
242 shell_error(
243 "ConfigDB not enabled. Please check the value of :configurable_from_database in your configuration."
244 )
245 end
246 end