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.

389 lines
9.1KB

  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],
  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. config_path =
  207. if Pleroma.Config.get(:release) do
  208. :config_path
  209. |> Pleroma.Config.get()
  210. |> Path.dirname()
  211. else
  212. "config"
  213. end
  214. |> Path.join("#{env}.exported_from_db.secret.exs")
  215. file = File.open!(config_path, [:write, :utf8])
  216. IO.write(file, config_header())
  217. ConfigDB
  218. |> Repo.all()
  219. |> Enum.each(&write_and_delete(&1, file, opts[:delete]))
  220. :ok = File.close(file)
  221. System.cmd("mix", ["format", config_path])
  222. shell_info(
  223. "Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs"
  224. )
  225. end
  226. if Code.ensure_loaded?(Config.Reader) do
  227. defp config_header, do: "import Config\r\n\r\n"
  228. defp read_file(config_file), do: Config.Reader.read_imports!(config_file)
  229. else
  230. defp config_header, do: "use Mix.Config\r\n\r\n"
  231. defp read_file(config_file), do: Mix.Config.eval!(config_file)
  232. end
  233. defp write_and_delete(config, file, delete?) do
  234. config
  235. |> write(file)
  236. |> delete(delete?)
  237. end
  238. defp write(config, file) do
  239. value = inspect(config.value, limit: :infinity)
  240. IO.write(file, "config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")
  241. config
  242. end
  243. defp delete(config, true) do
  244. {:ok, _} = Repo.delete(config)
  245. shell_info(
  246. "config #{inspect(config.group)}, #{inspect(config.key)} was deleted from the ConfigDB."
  247. )
  248. end
  249. defp delete(_config, _), do: :ok
  250. defp dump(%ConfigDB{} = config) do
  251. value = inspect(config.value, limit: :infinity)
  252. shell_info("config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")
  253. end
  254. defp dump(_), do: :noop
  255. defp dump_group(group) when is_atom(group) do
  256. group
  257. |> ConfigDB.get_all_by_group()
  258. |> Enum.each(&dump/1)
  259. end
  260. defp group_exists?(group) do
  261. group
  262. |> ConfigDB.get_all_by_group()
  263. |> Enum.any?()
  264. end
  265. defp key_exists?(group, key) do
  266. group
  267. |> ConfigDB.get_by_group_and_key(key)
  268. |> is_nil
  269. |> Kernel.!()
  270. end
  271. defp maybe_atomize(arg) when is_atom(arg), do: arg
  272. defp maybe_atomize(":" <> arg), do: maybe_atomize(arg)
  273. defp maybe_atomize(arg) when is_binary(arg) do
  274. if ConfigDB.module_name?(arg) do
  275. String.to_existing_atom("Elixir." <> arg)
  276. else
  277. String.to_atom(arg)
  278. end
  279. end
  280. defp check_configdb(callback) do
  281. with true <- Pleroma.Config.get([:configurable_from_database]) do
  282. callback.()
  283. else
  284. _ ->
  285. shell_error(
  286. "ConfigDB not enabled. Please check the value of :configurable_from_database in your configuration."
  287. )
  288. end
  289. end
  290. defp delete_key(group, key) do
  291. check_configdb(fn ->
  292. ConfigDB.delete(%{group: group, key: key})
  293. end)
  294. end
  295. defp delete_group(group) do
  296. check_configdb(fn ->
  297. group
  298. |> ConfigDB.get_all_by_group()
  299. |> Enum.each(&ConfigDB.delete/1)
  300. end)
  301. end
  302. defp truncatedb do
  303. Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
  304. Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
  305. end
  306. end