total rebase
[anni] / lib / pleroma / web / admin_api / controllers / config_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.AdminAPI.ConfigController do
6   use Pleroma.Web, :controller
7
8   alias Pleroma.Config
9   alias Pleroma.ConfigDB
10   alias Pleroma.Web.Plugs.OAuthScopesPlug
11
12   plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
13   plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action == :update)
14
15   plug(
16     OAuthScopesPlug,
17     %{scopes: ["admin:read"]}
18     when action in [:show, :descriptions]
19   )
20
21   action_fallback(Pleroma.Web.AdminAPI.FallbackController)
22
23   defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ConfigOperation
24
25   defp translate_descriptions(descriptions, path \\ []) do
26     Enum.map(descriptions, fn desc -> translate_item(desc, path) end)
27   end
28
29   defp translate_string(str, path, type) do
30     Gettext.dpgettext(
31       Pleroma.Web.Gettext,
32       "config_descriptions",
33       Pleroma.Docs.Translator.Compiler.msgctxt_for(path, type),
34       str
35     )
36   end
37
38   defp maybe_put_translated(item, key, path) do
39     if item[key] do
40       Map.put(
41         item,
42         key,
43         translate_string(
44           item[key],
45           path ++ [Pleroma.Docs.Translator.Compiler.key_for(item)],
46           to_string(key)
47         )
48       )
49     else
50       item
51     end
52   end
53
54   defp translate_item(item, path) do
55     item
56     |> maybe_put_translated(:label, path)
57     |> maybe_put_translated(:description, path)
58     |> translate_children(path)
59   end
60
61   defp translate_children(%{children: children} = item, path) when is_list(children) do
62     item
63     |> Map.put(
64       :children,
65       translate_descriptions(children, path ++ [Pleroma.Docs.Translator.Compiler.key_for(item)])
66     )
67   end
68
69   defp translate_children(item, _path) do
70     item
71   end
72
73   def descriptions(conn, _params) do
74     descriptions = Enum.filter(Pleroma.Docs.JSON.compiled_descriptions(), &whitelisted_config?/1)
75
76     json(conn, translate_descriptions(descriptions))
77   end
78
79   def show(%{private: %{open_api_spex: %{params: %{only_db: true}}}} = conn, _) do
80     with :ok <- configurable_from_database() do
81       configs = Pleroma.Repo.all(ConfigDB)
82
83       render(conn, "index.json", %{
84         configs: configs,
85         need_reboot: Restarter.Pleroma.need_reboot?()
86       })
87     end
88   end
89
90   def show(conn, _params) do
91     with :ok <- configurable_from_database() do
92       configs = ConfigDB.get_all_as_keyword()
93
94       merged =
95         Config.Holder.default_config()
96         |> ConfigDB.merge(configs)
97         |> Enum.map(fn {group, values} ->
98           Enum.map(values, fn {key, value} ->
99             db =
100               if configs[group][key] do
101                 ConfigDB.get_db_keys(configs[group][key], key)
102               end
103
104             db_value = configs[group][key]
105
106             merged_value =
107               if not is_nil(db_value) and Keyword.keyword?(db_value) and
108                    ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
109                 ConfigDB.merge_group(group, key, value, db_value)
110               else
111                 value
112               end
113
114             %ConfigDB{
115               group: group,
116               key: key,
117               value: merged_value
118             }
119             |> Pleroma.Maps.put_if_present(:db, db)
120           end)
121         end)
122         |> List.flatten()
123
124       render(conn, "index.json", %{
125         configs: merged,
126         need_reboot: Restarter.Pleroma.need_reboot?()
127       })
128     end
129   end
130
131   def update(%{private: %{open_api_spex: %{body_params: %{configs: configs}}}} = conn, _) do
132     with :ok <- configurable_from_database() do
133       results =
134         configs
135         |> Enum.filter(&whitelisted_config?/1)
136         |> Enum.map(fn
137           %{group: group, key: key, delete: true} = params ->
138             ConfigDB.delete(%{group: group, key: key, subkeys: params[:subkeys]})
139
140           %{group: group, key: key, value: value} ->
141             ConfigDB.update_or_create(%{group: group, key: key, value: value})
142         end)
143         |> Enum.reject(fn {result, _} -> result == :error end)
144
145       {deleted, updated} =
146         results
147         |> Enum.map(fn {:ok, %{key: key, value: value} = config} ->
148           Map.put(config, :db, ConfigDB.get_db_keys(value, key))
149         end)
150         |> Enum.split_with(&(Ecto.get_meta(&1, :state) == :deleted))
151
152       Config.TransferTask.load_and_update_env(deleted, false)
153
154       if not Restarter.Pleroma.need_reboot?() do
155         changed_reboot_settings? =
156           (updated ++ deleted)
157           |> Enum.any?(&Config.TransferTask.pleroma_need_restart?(&1.group, &1.key, &1.value))
158
159         if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
160       end
161
162       render(conn, "index.json", %{
163         configs: updated,
164         need_reboot: Restarter.Pleroma.need_reboot?()
165       })
166     end
167   end
168
169   defp configurable_from_database do
170     if Config.get(:configurable_from_database) do
171       :ok
172     else
173       {:error, "You must enable configurable_from_database in your config file."}
174     end
175   end
176
177   defp whitelisted_config?(group, key) do
178     if whitelisted_configs = Config.get(:database_config_whitelist) do
179       Enum.any?(whitelisted_configs, fn
180         {whitelisted_group} ->
181           group == inspect(whitelisted_group)
182
183         {whitelisted_group, whitelisted_key} ->
184           group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
185       end)
186     else
187       true
188     end
189   end
190
191   defp whitelisted_config?(%{group: group, key: key}) do
192     whitelisted_config?(group, key)
193   end
194
195   defp whitelisted_config?(%{group: group} = config) do
196     whitelisted_config?(group, config[:key])
197   end
198 end