Merge remote-tracking branch 'remotes/origin/develop' into 1505-threads-federation
[akkoma] / lib / pleroma / web / admin_api / admin_api_controller.ex
index 7118faf94b66d77ebdb95013bfff299808064423..67222ebaebd9ceb241f464906d7d1c74c5d196c9 100644 (file)
@@ -4,7 +4,12 @@
 
 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   use Pleroma.Web, :controller
+
+  import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+
   alias Pleroma.Activity
+  alias Pleroma.Config
+  alias Pleroma.ConfigDB
   alias Pleroma.ModerationLog
   alias Pleroma.Plugs.OAuthScopesPlug
   alias Pleroma.ReportNote
@@ -14,7 +19,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   alias Pleroma.Web.ActivityPub.Relay
   alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.AdminAPI.AccountView
-  alias Pleroma.Web.AdminAPI.Config
   alias Pleroma.Web.AdminAPI.ConfigView
   alias Pleroma.Web.AdminAPI.ModerationLogView
   alias Pleroma.Web.AdminAPI.Report
@@ -25,10 +29,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   alias Pleroma.Web.MastodonAPI.StatusView
   alias Pleroma.Web.Router
 
-  import Pleroma.Web.ControllerHelper, only: [json_response: 3]
-
   require Logger
 
+  @descriptions_json Pleroma.Docs.JSON.compile()
+  @users_page_size 50
+
   plug(
     OAuthScopesPlug,
     %{scopes: ["read:accounts"], admin: true}
@@ -93,7 +98,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   plug(
     OAuthScopesPlug,
     %{scopes: ["read"], admin: true}
-    when action in [:config_show, :migrate_to_db, :migrate_from_db, :list_log]
+    when action in [:config_show, :list_log]
   )
 
   plug(
@@ -102,8 +107,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     when action == :config_update
   )
 
-  @users_page_size 50
-
   action_fallback(:errors)
 
   def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
@@ -568,8 +571,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   @doc "Sends registration invite via email"
   def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
     with true <-
-           Pleroma.Config.get([:instance, :invites_enabled]) &&
-             !Pleroma.Config.get([:instance, :registrations_open]),
+           Config.get([:instance, :invites_enabled]) &&
+             !Config.get([:instance, :registrations_open]),
          {:ok, invite_token} <- UserInviteToken.create_invite(),
          email <-
            Pleroma.Emails.UserEmail.user_invitation_email(
@@ -785,49 +788,135 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> render("index.json", %{log: log})
   end
 
-  def migrate_to_db(conn, _params) do
-    Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
-    json(conn, %{})
+  def config_descriptions(conn, _params) do
+    conn
+    |> Plug.Conn.put_resp_content_type("application/json")
+    |> Plug.Conn.send_resp(200, @descriptions_json)
   end
 
-  def migrate_from_db(conn, _params) do
-    Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "true"])
-    json(conn, %{})
+  def config_show(conn, %{"only_db" => true}) do
+    with :ok <- configurable_from_database(conn) do
+      configs = Pleroma.Repo.all(ConfigDB)
+
+      conn
+      |> put_view(ConfigView)
+      |> render("index.json", %{configs: configs})
+    end
   end
 
   def config_show(conn, _params) do
-    configs = Pleroma.Repo.all(Config)
+    with :ok <- configurable_from_database(conn) do
+      configs = ConfigDB.get_all_as_keyword()
+
+      merged =
+        Config.Holder.config()
+        |> ConfigDB.merge(configs)
+        |> Enum.map(fn {group, values} ->
+          Enum.map(values, fn {key, value} ->
+            db =
+              if configs[group][key] do
+                ConfigDB.get_db_keys(configs[group][key], key)
+              end
+
+            db_value = configs[group][key]
+
+            merged_value =
+              if !is_nil(db_value) and Keyword.keyword?(db_value) and
+                   ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
+                ConfigDB.merge_group(group, key, value, db_value)
+              else
+                value
+              end
+
+            setting = %{
+              group: ConfigDB.convert(group),
+              key: ConfigDB.convert(key),
+              value: ConfigDB.convert(merged_value)
+            }
+
+            if db, do: Map.put(setting, :db, db), else: setting
+          end)
+        end)
+        |> List.flatten()
 
-    conn
-    |> put_view(ConfigView)
-    |> render("index.json", %{configs: configs})
+      response = %{configs: merged}
+
+      response =
+        if Restarter.Pleroma.need_reboot?() do
+          Map.put(response, :need_reboot, true)
+        else
+          response
+        end
+
+      json(conn, response)
+    end
   end
 
   def config_update(conn, %{"configs" => configs}) do
-    updated =
-      if Pleroma.Config.get([:instance, :dynamic_configuration]) do
-        updated =
-          Enum.map(configs, fn
-            %{"group" => group, "key" => key, "delete" => "true"} = params ->
-              {:ok, config} = Config.delete(%{group: group, key: key, subkeys: params["subkeys"]})
-              config
-
-            %{"group" => group, "key" => key, "value" => value} ->
-              {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
-              config
+    with :ok <- configurable_from_database(conn) do
+      {_errors, results} =
+        Enum.map(configs, fn
+          %{"group" => group, "key" => key, "delete" => true} = params ->
+            ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
+
+          %{"group" => group, "key" => key, "value" => value} ->
+            ConfigDB.update_or_create(%{group: group, key: key, value: value})
+        end)
+        |> Enum.split_with(fn result -> elem(result, 0) == :error end)
+
+      {deleted, updated} =
+        results
+        |> Enum.map(fn {:ok, config} ->
+          Map.put(config, :db, ConfigDB.get_db_keys(config))
+        end)
+        |> Enum.split_with(fn config ->
+          Ecto.get_meta(config, :state) == :deleted
+        end)
+
+      Config.TransferTask.load_and_update_env(deleted, false)
+
+      need_reboot? =
+        Restarter.Pleroma.need_reboot?() ||
+          Enum.any?(updated, fn config ->
+            group = ConfigDB.from_string(config.group)
+            key = ConfigDB.from_string(config.key)
+            value = ConfigDB.from_binary(config.value)
+            Config.TransferTask.pleroma_need_restart?(group, key, value)
           end)
-          |> Enum.reject(&is_nil(&1))
 
-        Pleroma.Config.TransferTask.load_and_update_env()
-        Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"])
-        updated
-      else
-        []
-      end
+      response = %{configs: updated}
 
-    conn
-    |> put_view(ConfigView)
-    |> render("index.json", %{configs: updated})
+      response =
+        if need_reboot? do
+          Restarter.Pleroma.need_reboot()
+          Map.put(response, :need_reboot, need_reboot?)
+        else
+          response
+        end
+
+      conn
+      |> put_view(ConfigView)
+      |> render("index.json", response)
+    end
+  end
+
+  def restart(conn, _params) do
+    with :ok <- configurable_from_database(conn) do
+      Restarter.Pleroma.restart(Config.get(:env), 50)
+
+      json(conn, %{})
+    end
+  end
+
+  defp configurable_from_database(conn) do
+    if Config.get(:configurable_from_database) do
+      :ok
+    else
+      errors(
+        conn,
+        {:error, "To use this endpoint you need to enable configuration from database."}
+      )
+    end
   end
 
   def reload_emoji(conn, _params) do