Fork of Pleroma with site-specific changes and feature branches https://git.pleroma.social/pleroma/pleroma
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

410 lines
9.7KB

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