Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
[akkoma] / lib / pleroma / web / admin_api / controllers / admin_api_controller.ex
index cc93fb5098a0a2fb14cd5f127a9edc0dcf8b6c96..50aa294f0ff0aed48ac6089323a47a4a6e2353b7 100644 (file)
@@ -1,59 +1,40 @@
 # Pleroma: A lightweight social networking server
-# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
 # SPDX-License-Identifier: AGPL-3.0-only
 
 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   use Pleroma.Web, :controller
 
-  import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+  import Pleroma.Web.ControllerHelper,
+    only: [json_response: 3, fetch_integer_param: 3]
 
-  alias Pleroma.Activity
   alias Pleroma.Config
-  alias Pleroma.ConfigDB
   alias Pleroma.MFA
   alias Pleroma.ModerationLog
-  alias Pleroma.Plugs.OAuthScopesPlug
-  alias Pleroma.ReportNote
   alias Pleroma.Stats
   alias Pleroma.User
   alias Pleroma.Web.ActivityPub.ActivityPub
-  alias Pleroma.Web.ActivityPub.Builder
-  alias Pleroma.Web.ActivityPub.Pipeline
-  alias Pleroma.Web.ActivityPub.Relay
-  alias Pleroma.Web.ActivityPub.Utils
   alias Pleroma.Web.AdminAPI
   alias Pleroma.Web.AdminAPI.AccountView
-  alias Pleroma.Web.AdminAPI.ConfigView
   alias Pleroma.Web.AdminAPI.ModerationLogView
-  alias Pleroma.Web.AdminAPI.Report
-  alias Pleroma.Web.AdminAPI.ReportView
-  alias Pleroma.Web.AdminAPI.Search
-  alias Pleroma.Web.CommonAPI
   alias Pleroma.Web.Endpoint
+  alias Pleroma.Web.Plugs.OAuthScopesPlug
   alias Pleroma.Web.Router
 
-  require Logger
-
-  @descriptions Pleroma.Docs.JSON.compile()
   @users_page_size 50
 
   plug(
     OAuthScopesPlug,
-    %{scopes: ["read:accounts"], admin: true}
-    when action in [:list_users, :user_show, :right_get, :show_user_credentials]
+    %{scopes: ["admin:read:accounts"]}
+    when action in [:right_get, :show_user_credentials, :create_backup]
   )
 
   plug(
     OAuthScopesPlug,
-    %{scopes: ["write:accounts"], admin: true}
+    %{scopes: ["admin:write:accounts"]}
     when action in [
            :get_password_reset,
            :force_password_reset,
-           :user_delete,
-           :users_create,
-           :user_toggle_activation,
-           :user_activate,
-           :user_deactivate,
            :tag_users,
            :untag_users,
            :right_add,
@@ -67,47 +48,31 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
   plug(
     OAuthScopesPlug,
-    %{scopes: ["write:follows"], admin: true}
-    when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
-  )
-
-  plug(
-    OAuthScopesPlug,
-    %{scopes: ["read:reports"], admin: true}
-    when action in [:list_reports, :report_show]
-  )
-
-  plug(
-    OAuthScopesPlug,
-    %{scopes: ["write:reports"], admin: true}
-    when action in [:reports_update, :report_notes_create, :report_notes_delete]
+    %{scopes: ["admin:read:statuses"]}
+    when action in [:list_user_statuses]
   )
 
   plug(
     OAuthScopesPlug,
-    %{scopes: ["read:statuses"], admin: true}
-    when action in [:list_user_statuses, :list_instance_statuses]
+    %{scopes: ["admin:read:chats"]}
+    when action in [:list_user_chats]
   )
 
   plug(
     OAuthScopesPlug,
-    %{scopes: ["read"], admin: true}
+    %{scopes: ["admin:read"]}
     when action in [
-           :config_show,
            :list_log,
            :stats,
-           :relay_list,
-           :config_descriptions,
            :need_reboot
          ]
   )
 
   plug(
     OAuthScopesPlug,
-    %{scopes: ["write"], admin: true}
+    %{scopes: ["admin:write"]}
     when action in [
            :restart,
-           :config_update,
            :resend_confirmation_email,
            :confirm_email,
            :reload_emoji
@@ -116,221 +81,43 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
   action_fallback(AdminAPI.FallbackController)
 
-  def user_delete(conn, %{"nickname" => nickname}) do
-    user_delete(conn, %{"nicknames" => [nickname]})
-  end
-
-  def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users =
-      nicknames
-      |> Enum.map(&User.get_cached_by_nickname/1)
-
-    users
-    |> Enum.each(fn user ->
-      {:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
-      Pipeline.common_pipeline(delete_data, local: true)
-    end)
-
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "delete"
-    })
-
-    conn
-    |> json(nicknames)
-  end
-
-  def user_follow(%{assigns: %{user: admin}} = conn, %{
-        "follower" => follower_nick,
-        "followed" => followed_nick
-      }) do
-    with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
-         %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
-      User.follow(follower, followed)
-
-      ModerationLog.insert_log(%{
-        actor: admin,
-        followed: followed,
-        follower: follower,
-        action: "follow"
-      })
-    end
-
-    conn
-    |> json("ok")
-  end
-
-  def user_unfollow(%{assigns: %{user: admin}} = conn, %{
-        "follower" => follower_nick,
-        "followed" => followed_nick
-      }) do
-    with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
-         %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
-      User.unfollow(follower, followed)
-
-      ModerationLog.insert_log(%{
-        actor: admin,
-        followed: followed,
-        follower: follower,
-        action: "unfollow"
-      })
-    end
-
-    conn
-    |> json("ok")
-  end
-
-  def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
-    changesets =
-      Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
-        user_data = %{
-          nickname: nickname,
-          name: nickname,
-          email: email,
-          password: password,
-          password_confirmation: password,
-          bio: "."
-        }
-
-        User.register_changeset(%User{}, user_data, need_confirmation: false)
-      end)
-      |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
-        Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
-      end)
-
-    case Pleroma.Repo.transaction(changesets) do
-      {:ok, users} ->
-        res =
-          users
-          |> Map.values()
-          |> Enum.map(fn user ->
-            {:ok, user} = User.post_register_action(user)
-
-            user
-          end)
-          |> Enum.map(&AccountView.render("created.json", %{user: &1}))
-
-        ModerationLog.insert_log(%{
-          actor: admin,
-          subjects: Map.values(users),
-          action: "create"
-        })
-
-        conn
-        |> json(res)
-
-      {:error, id, changeset, _} ->
-        res =
-          Enum.map(changesets.operations, fn
-            {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
-              AccountView.render("create-error.json", %{changeset: changeset})
-
-            {_, {:changeset, current_changeset, _}} ->
-              AccountView.render("create-error.json", %{changeset: current_changeset})
-          end)
-
-        conn
-        |> put_status(:conflict)
-        |> json(res)
-    end
-  end
-
-  def user_show(conn, %{"nickname" => nickname}) do
-    with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
-      conn
-      |> put_view(AccountView)
-      |> render("show.json", %{user: user})
-    else
-      _ -> {:error, :not_found}
-    end
-  end
-
-  def list_instance_statuses(conn, %{"instance" => instance} = params) do
-    with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
-    {page, page_size} = page_params(params)
-
-    activities =
-      ActivityPub.fetch_statuses(nil, %{
-        "instance" => instance,
-        "limit" => page_size,
-        "offset" => (page - 1) * page_size,
-        "exclude_reblogs" => !with_reblogs && "true"
-      })
-
-    conn
-    |> put_view(AdminAPI.StatusView)
-    |> render("index.json", %{activities: activities, as: :activity})
-  end
-
-  def list_user_statuses(conn, %{"nickname" => nickname} = params) do
+  def list_user_statuses(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = params) do
     with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
     godmode = params["godmode"] == "true" || params["godmode"] == true
 
-    with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
-      {_, page_size} = page_params(params)
+    with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
+      {page, page_size} = page_params(params)
 
-      activities =
+      result =
         ActivityPub.fetch_user_activities(user, nil, %{
-          "limit" => page_size,
-          "godmode" => godmode,
-          "exclude_reblogs" => !with_reblogs && "true"
+          limit: page_size,
+          offset: (page - 1) * page_size,
+          godmode: godmode,
+          exclude_reblogs: not with_reblogs,
+          pagination_type: :offset,
+          total: true
         })
 
       conn
       |> put_view(AdminAPI.StatusView)
-      |> render("index.json", %{activities: activities, as: :activity})
+      |> render("index.json", %{total: result[:total], activities: result[:items], as: :activity})
     else
       _ -> {:error, :not_found}
     end
   end
 
-  def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
-    user = User.get_cached_by_nickname(nickname)
-
-    {:ok, updated_user} = User.deactivate(user, !user.deactivated)
-
-    action = if user.deactivated, do: "activate", else: "deactivate"
-
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: [user],
-      action: action
-    })
-
-    conn
-    |> put_view(AccountView)
-    |> render("show.json", %{user: updated_user})
-  end
-
-  def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
-    {:ok, updated_users} = User.deactivate(users, false)
+  def list_user_chats(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname} = _params) do
+    with %User{id: user_id} <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
+      chats =
+        Pleroma.Chat.for_user_query(user_id)
+        |> Pleroma.Repo.all()
 
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "activate"
-    })
-
-    conn
-    |> put_view(AccountView)
-    |> render("index.json", %{users: Keyword.values(updated_users)})
-  end
-
-  def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
-    {:ok, updated_users} = User.deactivate(users, true)
-
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "deactivate"
-    })
-
-    conn
-    |> put_view(AccountView)
-    |> render("index.json", %{users: Keyword.values(updated_users)})
+      conn
+      |> put_view(AdminAPI.ChatView)
+      |> render("index.json", chats: chats)
+    else
+      _ -> {:error, :not_found}
+    end
   end
 
   def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
@@ -359,40 +146,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     end
   end
 
-  def list_users(conn, params) do
-    {page, page_size} = page_params(params)
-    filters = maybe_parse_filters(params["filters"])
-
-    search_params = %{
-      query: params["query"],
-      page: page,
-      page_size: page_size,
-      tags: params["tags"],
-      name: params["name"],
-      email: params["email"]
-    }
-
-    with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
-      json(
-        conn,
-        AccountView.render("index.json", users: users, count: count, page_size: page_size)
-      )
-    end
-  end
-
-  @filters ~w(local external active deactivated is_admin is_moderator)
-
-  @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
-  defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
-
-  defp maybe_parse_filters(filters) do
-    filters
-    |> String.split(",")
-    |> Enum.filter(&Enum.member?(@filters, &1))
-    |> Enum.map(&String.to_atom(&1))
-    |> Enum.into(%{}, &{&1, true})
-  end
-
   def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
         "permission_group" => permission_group,
         "nicknames" => nicknames
@@ -515,50 +268,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     render_error(conn, :forbidden, "You can't revoke your own admin status.")
   end
 
-  def relay_list(conn, _params) do
-    with {:ok, list} <- Relay.list() do
-      json(conn, %{relays: list})
-    else
-      _ ->
-        conn
-        |> put_status(500)
-    end
-  end
-
-  def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
-    with {:ok, _message} <- Relay.follow(target) do
-      ModerationLog.insert_log(%{
-        action: "relay_follow",
-        actor: admin,
-        target: target
-      })
-
-      json(conn, target)
-    else
-      _ ->
-        conn
-        |> put_status(500)
-        |> json(target)
-    end
-  end
-
-  def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
-    with {:ok, _message} <- Relay.unfollow(target) do
-      ModerationLog.insert_log(%{
-        action: "relay_unfollow",
-        actor: admin,
-        target: target
-      })
-
-      json(conn, target)
-    else
-      _ ->
-        conn
-        |> put_status(500)
-        |> json(target)
-    end
-  end
-
   @doc "Get a password reset token (base64 string) for given nickname"
   def get_password_reset(conn, %{"nickname" => nickname}) do
     (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
@@ -600,7 +309,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
 
   @doc "Show a given user's credentials"
   def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
-    with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
+    with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
       conn
       |> put_view(AccountView)
       |> render("credentials.json", %{user: user, for: admin})
@@ -638,89 +347,10 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
       {:error, changeset} ->
         errors = Map.new(changeset.errors, fn {key, {error, _}} -> {key, error} end)
 
-        json(conn, %{errors: errors})
+        {:errors, errors}
 
       _ ->
-        json(conn, %{error: "Unable to update user."})
-    end
-  end
-
-  def list_reports(conn, params) do
-    {page, page_size} = page_params(params)
-
-    reports = Utils.get_reports(params, page, page_size)
-
-    conn
-    |> put_view(ReportView)
-    |> render("index.json", %{reports: reports})
-  end
-
-  def report_show(conn, %{"id" => id}) do
-    with %Activity{} = report <- Activity.get_by_id(id) do
-      conn
-      |> put_view(ReportView)
-      |> render("show.json", Report.extract_report_info(report))
-    else
-      _ -> {:error, :not_found}
-    end
-  end
-
-  def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
-    result =
-      reports
-      |> Enum.map(fn report ->
-        with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
-          ModerationLog.insert_log(%{
-            action: "report_update",
-            actor: admin,
-            subject: activity
-          })
-
-          activity
-        else
-          {:error, message} -> %{id: report["id"], error: message}
-        end
-      end)
-
-    case Enum.any?(result, &Map.has_key?(&1, :error)) do
-      true -> json_response(conn, :bad_request, result)
-      false -> json_response(conn, :no_content, "")
-    end
-  end
-
-  def report_notes_create(%{assigns: %{user: user}} = conn, %{
-        "id" => report_id,
-        "content" => content
-      }) do
-    with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
-      ModerationLog.insert_log(%{
-        action: "report_note",
-        actor: user,
-        subject: Activity.get_by_id(report_id),
-        text: content
-      })
-
-      json_response(conn, :no_content, "")
-    else
-      _ -> json_response(conn, :bad_request, "")
-    end
-  end
-
-  def report_notes_delete(%{assigns: %{user: user}} = conn, %{
-        "id" => note_id,
-        "report_id" => report_id
-      }) do
-    with {:ok, note} <- ReportNote.destroy(note_id) do
-      ModerationLog.insert_log(%{
-        action: "report_note_delete",
-        actor: user,
-        subject: Activity.get_by_id(report_id),
-        text: note.content
-      })
-
-      json_response(conn, :no_content, "")
-    else
-      _ -> json_response(conn, :bad_request, "")
+        {:error, :not_found}
     end
   end
 
@@ -742,105 +372,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     |> render("index.json", %{log: log})
   end
 
-  def config_descriptions(conn, _params) do
-    descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
-
-    json(conn, descriptions)
-  end
-
-  def config_show(conn, %{"only_db" => true}) do
-    with :ok <- configurable_from_database() do
-      configs = Pleroma.Repo.all(ConfigDB)
-
-      conn
-      |> put_view(ConfigView)
-      |> render("index.json", %{configs: configs})
-    end
-  end
-
-  def config_show(conn, _params) do
-    with :ok <- configurable_from_database() do
-      configs = ConfigDB.get_all_as_keyword()
-
-      merged =
-        Config.Holder.default_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()
-
-      json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
-    end
-  end
-
-  def config_update(conn, %{"configs" => configs}) do
-    with :ok <- configurable_from_database() do
-      {_errors, results} =
-        configs
-        |> Enum.filter(&whitelisted_config?/1)
-        |> Enum.map(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)
-
-      if !Restarter.Pleroma.need_reboot?() do
-        changed_reboot_settings? =
-          (updated ++ deleted)
-          |> Enum.any?(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)
-
-        if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
-      end
-
-      conn
-      |> put_view(ConfigView)
-      |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
-    end
-  end
-
   def restart(conn, _params) do
     with :ok <- configurable_from_database() do
       Restarter.Pleroma.restart(Config.get(:env), 50)
@@ -857,92 +388,58 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     if Config.get(:configurable_from_database) do
       :ok
     else
-      {:error, "To use this endpoint you need to enable configuration from database."}
+      {:error, "You must enable configurable_from_database in your config file."}
     end
   end
 
-  defp whitelisted_config?(group, key) do
-    if whitelisted_configs = Config.get(:database_config_whitelist) do
-      Enum.any?(whitelisted_configs, fn
-        {whitelisted_group} ->
-          group == inspect(whitelisted_group)
-
-        {whitelisted_group, whitelisted_key} ->
-          group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
-      end)
-    else
-      true
-    end
-  end
-
-  defp whitelisted_config?(%{"group" => group, "key" => key}) do
-    whitelisted_config?(group, key)
-  end
-
-  defp whitelisted_config?(%{:group => group} = config) do
-    whitelisted_config?(group, config[:key])
-  end
-
   def reload_emoji(conn, _params) do
     Pleroma.Emoji.reload()
 
-    conn |> json("ok")
+    json(conn, "ok")
   end
 
   def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
+    users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
 
-    User.toggle_confirmation(users)
+    User.confirm(users)
 
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "confirm_email"
-    })
+    ModerationLog.insert_log(%{actor: admin, subject: users, action: "confirm_email"})
 
-    conn |> json("")
+    json(conn, "")
   end
 
   def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
-    users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
-
-    User.try_send_confirmation_email(users)
+    users =
+      Enum.map(nicknames, fn nickname ->
+        nickname
+        |> User.get_cached_by_nickname()
+        |> User.send_confirmation_email()
+      end)
 
-    ModerationLog.insert_log(%{
-      actor: admin,
-      subject: users,
-      action: "resend_confirmation_email"
-    })
+    ModerationLog.insert_log(%{actor: admin, subject: users, action: "resend_confirmation_email"})
 
-    conn |> json("")
+    json(conn, "")
   end
 
-  def stats(conn, _) do
-    count = Stats.get_status_visibility_count()
+  def stats(conn, params) do
+    counters = Stats.get_status_visibility_count(params["instance"])
 
-    conn
-    |> json(%{"status_visibility" => count})
+    json(conn, %{"status_visibility" => counters})
   end
 
-  defp page_params(params) do
-    {get_page(params["page"]), get_page_size(params["page_size"])}
-  end
-
-  defp get_page(page_string) when is_nil(page_string), do: 1
+  def create_backup(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
+    with %User{} = user <- User.get_by_nickname(nickname),
+         {:ok, _} <- Pleroma.User.Backup.create(user, admin.id) do
+      ModerationLog.insert_log(%{actor: admin, subject: user, action: "create_backup"})
 
-  defp get_page(page_string) do
-    case Integer.parse(page_string) do
-      {page, _} -> page
-      :error -> 1
+      json(conn, "")
     end
   end
 
-  defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
-
-  defp get_page_size(page_size_string) do
-    case Integer.parse(page_size_string) do
-      {page_size, _} -> page_size
-      :error -> @users_page_size
-    end
+  defp page_params(params) do
+    {
+      fetch_integer_param(params, "page", 1),
+      fetch_integer_param(params, "page_size", @users_page_size)
+    }
   end
 end