summaryrefslogtreecommitdiff
path: root/lib/mix/tasks/pleroma/config.ex
blob: 18f99318db09d1ad5a53fe777fc25d22bd39f33f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Mix.Tasks.Pleroma.Config do
  use Mix.Task

  import Mix.Pleroma

  alias Pleroma.ConfigDB
  alias Pleroma.Repo

  @shortdoc "Manages the location of the config"
  @moduledoc File.read!("docs/administration/CLI_tasks/config.md")

  def run(["migrate_to_db"]) do
    start_pleroma()
    migrate_to_db()
  end

  def run(["migrate_from_db" | options]) do
    start_pleroma()

    {opts, _} =
      OptionParser.parse!(options,
        strict: [env: :string, delete: :boolean],
        aliases: [d: :delete]
      )

    migrate_from_db(opts)
  end

  @spec migrate_to_db(Path.t() | nil) :: any()
  def migrate_to_db(file_path \\ nil) do
    with true <- Pleroma.Config.get([:configurable_from_database]),
         :ok <- Pleroma.Config.DeprecationWarnings.warn() do
      config_file =
        if file_path do
          file_path
        else
          if Pleroma.Config.get(:release) do
            Pleroma.Config.get(:config_path)
          else
            "config/#{Pleroma.Config.get(:env)}.secret.exs"
          end
        end

      do_migrate_to_db(config_file)
    else
      :error -> deprecation_error()
      _ -> migration_error()
    end
  end

  defp do_migrate_to_db(config_file) do
    if File.exists?(config_file) do
      shell_info("Migrating settings from file: #{Path.expand(config_file)}")
      Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
      Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")

      custom_config =
        config_file
        |> read_file()
        |> elem(0)

      custom_config
      |> Keyword.keys()
      |> Enum.each(&create(&1, custom_config))
    else
      shell_info("To migrate settings, you must define custom settings in #{config_file}.")
    end
  end

  defp create(group, settings) do
    group
    |> Pleroma.Config.Loader.filter_group(settings)
    |> Enum.each(fn {key, value} ->
      {:ok, _} = ConfigDB.update_or_create(%{group: group, key: key, value: value})

      shell_info("Settings for key #{key} migrated.")
    end)

    shell_info("Settings for group :#{group} migrated.")
  end

  defp migrate_from_db(opts) do
    if Pleroma.Config.get([:configurable_from_database]) do
      env = opts[:env] || Pleroma.Config.get(:env)

      config_path =
        if Pleroma.Config.get(:release) do
          :config_path
          |> Pleroma.Config.get()
          |> Path.dirname()
        else
          "config"
        end
        |> Path.join("#{env}.exported_from_db.secret.exs")

      file = File.open!(config_path, [:write, :utf8])

      IO.write(file, config_header())

      ConfigDB
      |> Repo.all()
      |> Enum.each(&write_and_delete(&1, file, opts[:delete]))

      :ok = File.close(file)
      System.cmd("mix", ["format", config_path])

      shell_info(
        "Database configuration settings have been exported to config/#{env}.exported_from_db.secret.exs"
      )
    else
      migration_error()
    end
  end

  defp migration_error do
    shell_error(
      "Migration is not allowed in config. You can change this behavior by setting `config :pleroma, configurable_from_database: true`"
    )
  end

  defp deprecation_error do
    shell_error("Migration is not allowed until all deprecation warnings have been resolved.")
  end

  if Code.ensure_loaded?(Config.Reader) do
    defp config_header, do: "import Config\r\n\r\n"
    defp read_file(config_file), do: Config.Reader.read_imports!(config_file)
  else
    defp config_header, do: "use Mix.Config\r\n\r\n"
    defp read_file(config_file), do: Mix.Config.eval!(config_file)
  end

  defp write_and_delete(config, file, delete?) do
    config
    |> write(file)
    |> delete(delete?)
  end

  defp write(config, file) do
    value = inspect(config.value, limit: :infinity)

    IO.write(file, "config #{inspect(config.group)}, #{inspect(config.key)}, #{value}\r\n\r\n")

    config
  end

  defp delete(config, true) do
    {:ok, _} = Repo.delete(config)
    shell_info("#{config.key} deleted from DB.")
  end

  defp delete(_config, _), do: :ok
end