aboutsummaryrefslogtreecommitdiff
path: root/lib/pleroma/config/transfer_task.ex
blob: d54f38ee4c022db43703b638a3de24317294474f (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
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Config.TransferTask do
  use Task

  alias Pleroma.ConfigDB
  alias Pleroma.Repo

  require Logger

  def start_link(_) do
    load_and_update_env()
    if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Repo)
    :ignore
  end

  @spec load_and_update_env([ConfigDB.t()]) :: :ok | false
  def load_and_update_env(deleted \\ []) do
    with true <- Pleroma.Config.get(:configurable_from_database),
         true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"),
         started_applications <- Application.started_applications() do
      # We need to restart applications for loaded settings take effect
      in_db = Repo.all(ConfigDB)

      with_deleted = in_db ++ deleted

      with_deleted
      |> Enum.map(&merge_and_update(&1))
      |> Enum.uniq()
      # TODO: some problem with prometheus after restart!
      |> Enum.reject(&(&1 in [:pleroma, nil, :prometheus]))
      |> Enum.each(&restart(started_applications, &1))

      :ok
    end
  end

  defp merge_and_update(setting) do
    try do
      key = ConfigDB.from_string(setting.key)
      group = ConfigDB.from_string(setting.group)

      default = Pleroma.Config.Holder.config(group, key)
      merged_value = merge_value(setting, default, group, key)

      :ok = update_env(group, key, merged_value)

      if group != :logger do
        group
      else
        # change logger configuration in runtime, without restart
        if Keyword.keyword?(merged_value) and
             key not in [:compile_time_application, :backends, :compile_time_purge_matching] do
          Logger.configure_backend(key, merged_value)
        else
          Logger.configure([{key, merged_value}])
        end

        nil
      end
    rescue
      error ->
        error_msg =
          "updating env causes error, group: " <>
            inspect(setting.group) <>
            " key: " <>
            inspect(setting.key) <>
            " value: " <>
            inspect(ConfigDB.from_binary(setting.value)) <> " error: " <> inspect(error)

        Logger.warn(error_msg)

        nil
    end
  end

  defp merge_value(%{__meta__: %{state: :deleted}}, default, _group, _key), do: default

  defp merge_value(setting, default, group, key) do
    value = ConfigDB.from_binary(setting.value)

    if can_be_merged?(default, value) do
      ConfigDB.merge_group(group, key, default, value)
    else
      value
    end
  end

  defp update_env(group, key, nil), do: Application.delete_env(group, key)
  defp update_env(group, key, value), do: Application.put_env(group, key, value)

  defp restart(started_applications, app) do
    with {^app, _, _} <- List.keyfind(started_applications, app, 0),
         :ok <- Application.stop(app) do
      :ok = Application.start(app)
    else
      nil ->
        Logger.warn("#{app} is not started.")

      error ->
        error
        |> inspect()
        |> Logger.warn()
    end
  end

  defp can_be_merged?(val1, val2) when is_list(val1) and is_list(val2) do
    Keyword.keyword?(val1) and Keyword.keyword?(val2)
  end

  defp can_be_merged?(_val1, _val2), do: false
end