logo

pleroma

My custom branche(s) on git.pleroma.social/pleroma/pleroma

admin_api_controller.ex (32782B)


      1 # Pleroma: A lightweight social networking server
      2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
      3 # SPDX-License-Identifier: AGPL-3.0-only
      4 
      5 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
      6   use Pleroma.Web, :controller
      7 
      8   import Pleroma.Web.ControllerHelper, only: [json_response: 3]
      9 
     10   alias Pleroma.Activity
     11   alias Pleroma.Config
     12   alias Pleroma.ConfigDB
     13   alias Pleroma.MFA
     14   alias Pleroma.ModerationLog
     15   alias Pleroma.Plugs.OAuthScopesPlug
     16   alias Pleroma.ReportNote
     17   alias Pleroma.Stats
     18   alias Pleroma.User
     19   alias Pleroma.UserInviteToken
     20   alias Pleroma.Web.ActivityPub.ActivityPub
     21   alias Pleroma.Web.ActivityPub.Builder
     22   alias Pleroma.Web.ActivityPub.Pipeline
     23   alias Pleroma.Web.ActivityPub.Relay
     24   alias Pleroma.Web.ActivityPub.Utils
     25   alias Pleroma.Web.AdminAPI
     26   alias Pleroma.Web.AdminAPI.AccountView
     27   alias Pleroma.Web.AdminAPI.ConfigView
     28   alias Pleroma.Web.AdminAPI.ModerationLogView
     29   alias Pleroma.Web.AdminAPI.Report
     30   alias Pleroma.Web.AdminAPI.ReportView
     31   alias Pleroma.Web.AdminAPI.Search
     32   alias Pleroma.Web.CommonAPI
     33   alias Pleroma.Web.Endpoint
     34   alias Pleroma.Web.MastodonAPI
     35   alias Pleroma.Web.MastodonAPI.AppView
     36   alias Pleroma.Web.OAuth.App
     37   alias Pleroma.Web.Router
     38 
     39   require Logger
     40 
     41   @descriptions Pleroma.Docs.JSON.compile()
     42   @users_page_size 50
     43 
     44   plug(
     45     OAuthScopesPlug,
     46     %{scopes: ["read:accounts"], admin: true}
     47     when action in [:list_users, :user_show, :right_get, :show_user_credentials]
     48   )
     49 
     50   plug(
     51     OAuthScopesPlug,
     52     %{scopes: ["write:accounts"], admin: true}
     53     when action in [
     54            :get_password_reset,
     55            :force_password_reset,
     56            :user_delete,
     57            :users_create,
     58            :user_toggle_activation,
     59            :user_activate,
     60            :user_deactivate,
     61            :tag_users,
     62            :untag_users,
     63            :right_add,
     64            :right_add_multiple,
     65            :right_delete,
     66            :disable_mfa,
     67            :right_delete_multiple,
     68            :update_user_credentials
     69          ]
     70   )
     71 
     72   plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites)
     73 
     74   plug(
     75     OAuthScopesPlug,
     76     %{scopes: ["write:invites"], admin: true}
     77     when action in [:create_invite_token, :revoke_invite, :email_invite]
     78   )
     79 
     80   plug(
     81     OAuthScopesPlug,
     82     %{scopes: ["write:follows"], admin: true}
     83     when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
     84   )
     85 
     86   plug(
     87     OAuthScopesPlug,
     88     %{scopes: ["read:reports"], admin: true}
     89     when action in [:list_reports, :report_show]
     90   )
     91 
     92   plug(
     93     OAuthScopesPlug,
     94     %{scopes: ["write:reports"], admin: true}
     95     when action in [:reports_update, :report_notes_create, :report_notes_delete]
     96   )
     97 
     98   plug(
     99     OAuthScopesPlug,
    100     %{scopes: ["read:statuses"], admin: true}
    101     when action in [:list_statuses, :list_user_statuses, :list_instance_statuses, :status_show]
    102   )
    103 
    104   plug(
    105     OAuthScopesPlug,
    106     %{scopes: ["write:statuses"], admin: true}
    107     when action in [:status_update, :status_delete]
    108   )
    109 
    110   plug(
    111     OAuthScopesPlug,
    112     %{scopes: ["read"], admin: true}
    113     when action in [
    114            :config_show,
    115            :list_log,
    116            :stats,
    117            :relay_list,
    118            :config_descriptions,
    119            :need_reboot
    120          ]
    121   )
    122 
    123   plug(
    124     OAuthScopesPlug,
    125     %{scopes: ["write"], admin: true}
    126     when action in [
    127            :restart,
    128            :config_update,
    129            :resend_confirmation_email,
    130            :confirm_email,
    131            :oauth_app_create,
    132            :oauth_app_list,
    133            :oauth_app_update,
    134            :oauth_app_delete,
    135            :reload_emoji
    136          ]
    137   )
    138 
    139   action_fallback(:errors)
    140 
    141   def user_delete(conn, %{"nickname" => nickname}) do
    142     user_delete(conn, %{"nicknames" => [nickname]})
    143   end
    144 
    145   def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
    146     users =
    147       nicknames
    148       |> Enum.map(&User.get_cached_by_nickname/1)
    149 
    150     users
    151     |> Enum.each(fn user ->
    152       {:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
    153       Pipeline.common_pipeline(delete_data, local: true)
    154     end)
    155 
    156     ModerationLog.insert_log(%{
    157       actor: admin,
    158       subject: users,
    159       action: "delete"
    160     })
    161 
    162     conn
    163     |> json(nicknames)
    164   end
    165 
    166   def user_follow(%{assigns: %{user: admin}} = conn, %{
    167         "follower" => follower_nick,
    168         "followed" => followed_nick
    169       }) do
    170     with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
    171          %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
    172       User.follow(follower, followed)
    173 
    174       ModerationLog.insert_log(%{
    175         actor: admin,
    176         followed: followed,
    177         follower: follower,
    178         action: "follow"
    179       })
    180     end
    181 
    182     conn
    183     |> json("ok")
    184   end
    185 
    186   def user_unfollow(%{assigns: %{user: admin}} = conn, %{
    187         "follower" => follower_nick,
    188         "followed" => followed_nick
    189       }) do
    190     with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
    191          %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
    192       User.unfollow(follower, followed)
    193 
    194       ModerationLog.insert_log(%{
    195         actor: admin,
    196         followed: followed,
    197         follower: follower,
    198         action: "unfollow"
    199       })
    200     end
    201 
    202     conn
    203     |> json("ok")
    204   end
    205 
    206   def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
    207     changesets =
    208       Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
    209         user_data = %{
    210           nickname: nickname,
    211           name: nickname,
    212           email: email,
    213           password: password,
    214           password_confirmation: password,
    215           bio: "."
    216         }
    217 
    218         User.register_changeset(%User{}, user_data, need_confirmation: false)
    219       end)
    220       |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
    221         Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
    222       end)
    223 
    224     case Pleroma.Repo.transaction(changesets) do
    225       {:ok, users} ->
    226         res =
    227           users
    228           |> Map.values()
    229           |> Enum.map(fn user ->
    230             {:ok, user} = User.post_register_action(user)
    231 
    232             user
    233           end)
    234           |> Enum.map(&AccountView.render("created.json", %{user: &1}))
    235 
    236         ModerationLog.insert_log(%{
    237           actor: admin,
    238           subjects: Map.values(users),
    239           action: "create"
    240         })
    241 
    242         conn
    243         |> json(res)
    244 
    245       {:error, id, changeset, _} ->
    246         res =
    247           Enum.map(changesets.operations, fn
    248             {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
    249               AccountView.render("create-error.json", %{changeset: changeset})
    250 
    251             {_, {:changeset, current_changeset, _}} ->
    252               AccountView.render("create-error.json", %{changeset: current_changeset})
    253           end)
    254 
    255         conn
    256         |> put_status(:conflict)
    257         |> json(res)
    258     end
    259   end
    260 
    261   def user_show(conn, %{"nickname" => nickname}) do
    262     with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
    263       conn
    264       |> put_view(AccountView)
    265       |> render("show.json", %{user: user})
    266     else
    267       _ -> {:error, :not_found}
    268     end
    269   end
    270 
    271   def list_instance_statuses(conn, %{"instance" => instance} = params) do
    272     with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
    273     {page, page_size} = page_params(params)
    274 
    275     activities =
    276       ActivityPub.fetch_statuses(nil, %{
    277         "instance" => instance,
    278         "limit" => page_size,
    279         "offset" => (page - 1) * page_size,
    280         "exclude_reblogs" => !with_reblogs && "true"
    281       })
    282 
    283     conn
    284     |> put_view(AdminAPI.StatusView)
    285     |> render("index.json", %{activities: activities, as: :activity})
    286   end
    287 
    288   def list_user_statuses(conn, %{"nickname" => nickname} = params) do
    289     with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
    290     godmode = params["godmode"] == "true" || params["godmode"] == true
    291 
    292     with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
    293       {_, page_size} = page_params(params)
    294 
    295       activities =
    296         ActivityPub.fetch_user_activities(user, nil, %{
    297           "limit" => page_size,
    298           "godmode" => godmode,
    299           "exclude_reblogs" => !with_reblogs && "true"
    300         })
    301 
    302       conn
    303       |> put_view(MastodonAPI.StatusView)
    304       |> render("index.json", %{activities: activities, as: :activity})
    305     else
    306       _ -> {:error, :not_found}
    307     end
    308   end
    309 
    310   def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
    311     user = User.get_cached_by_nickname(nickname)
    312 
    313     {:ok, updated_user} = User.deactivate(user, !user.deactivated)
    314 
    315     action = if user.deactivated, do: "activate", else: "deactivate"
    316 
    317     ModerationLog.insert_log(%{
    318       actor: admin,
    319       subject: [user],
    320       action: action
    321     })
    322 
    323     conn
    324     |> put_view(AccountView)
    325     |> render("show.json", %{user: updated_user})
    326   end
    327 
    328   def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
    329     users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
    330     {:ok, updated_users} = User.deactivate(users, false)
    331 
    332     ModerationLog.insert_log(%{
    333       actor: admin,
    334       subject: users,
    335       action: "activate"
    336     })
    337 
    338     conn
    339     |> put_view(AccountView)
    340     |> render("index.json", %{users: Keyword.values(updated_users)})
    341   end
    342 
    343   def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
    344     users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
    345     {:ok, updated_users} = User.deactivate(users, true)
    346 
    347     ModerationLog.insert_log(%{
    348       actor: admin,
    349       subject: users,
    350       action: "deactivate"
    351     })
    352 
    353     conn
    354     |> put_view(AccountView)
    355     |> render("index.json", %{users: Keyword.values(updated_users)})
    356   end
    357 
    358   def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
    359     with {:ok, _} <- User.tag(nicknames, tags) do
    360       ModerationLog.insert_log(%{
    361         actor: admin,
    362         nicknames: nicknames,
    363         tags: tags,
    364         action: "tag"
    365       })
    366 
    367       json_response(conn, :no_content, "")
    368     end
    369   end
    370 
    371   def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
    372     with {:ok, _} <- User.untag(nicknames, tags) do
    373       ModerationLog.insert_log(%{
    374         actor: admin,
    375         nicknames: nicknames,
    376         tags: tags,
    377         action: "untag"
    378       })
    379 
    380       json_response(conn, :no_content, "")
    381     end
    382   end
    383 
    384   def list_users(conn, params) do
    385     {page, page_size} = page_params(params)
    386     filters = maybe_parse_filters(params["filters"])
    387 
    388     search_params = %{
    389       query: params["query"],
    390       page: page,
    391       page_size: page_size,
    392       tags: params["tags"],
    393       name: params["name"],
    394       email: params["email"]
    395     }
    396 
    397     with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
    398       json(
    399         conn,
    400         AccountView.render("index.json", users: users, count: count, page_size: page_size)
    401       )
    402     end
    403   end
    404 
    405   @filters ~w(local external active deactivated is_admin is_moderator)
    406 
    407   @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
    408   defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
    409 
    410   defp maybe_parse_filters(filters) do
    411     filters
    412     |> String.split(",")
    413     |> Enum.filter(&Enum.member?(@filters, &1))
    414     |> Enum.map(&String.to_atom(&1))
    415     |> Enum.into(%{}, &{&1, true})
    416   end
    417 
    418   def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
    419         "permission_group" => permission_group,
    420         "nicknames" => nicknames
    421       })
    422       when permission_group in ["moderator", "admin"] do
    423     update = %{:"is_#{permission_group}" => true}
    424 
    425     users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
    426 
    427     for u <- users, do: User.admin_api_update(u, update)
    428 
    429     ModerationLog.insert_log(%{
    430       action: "grant",
    431       actor: admin,
    432       subject: users,
    433       permission: permission_group
    434     })
    435 
    436     json(conn, update)
    437   end
    438 
    439   def right_add_multiple(conn, _) do
    440     render_error(conn, :not_found, "No such permission_group")
    441   end
    442 
    443   def right_add(%{assigns: %{user: admin}} = conn, %{
    444         "permission_group" => permission_group,
    445         "nickname" => nickname
    446       })
    447       when permission_group in ["moderator", "admin"] do
    448     fields = %{:"is_#{permission_group}" => true}
    449 
    450     {:ok, user} =
    451       nickname
    452       |> User.get_cached_by_nickname()
    453       |> User.admin_api_update(fields)
    454 
    455     ModerationLog.insert_log(%{
    456       action: "grant",
    457       actor: admin,
    458       subject: [user],
    459       permission: permission_group
    460     })
    461 
    462     json(conn, fields)
    463   end
    464 
    465   def right_add(conn, _) do
    466     render_error(conn, :not_found, "No such permission_group")
    467   end
    468 
    469   def right_get(conn, %{"nickname" => nickname}) do
    470     user = User.get_cached_by_nickname(nickname)
    471 
    472     conn
    473     |> json(%{
    474       is_moderator: user.is_moderator,
    475       is_admin: user.is_admin
    476     })
    477   end
    478 
    479   def right_delete_multiple(
    480         %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
    481         %{
    482           "permission_group" => permission_group,
    483           "nicknames" => nicknames
    484         }
    485       )
    486       when permission_group in ["moderator", "admin"] do
    487     with false <- Enum.member?(nicknames, admin_nickname) do
    488       update = %{:"is_#{permission_group}" => false}
    489 
    490       users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
    491 
    492       for u <- users, do: User.admin_api_update(u, update)
    493 
    494       ModerationLog.insert_log(%{
    495         action: "revoke",
    496         actor: admin,
    497         subject: users,
    498         permission: permission_group
    499       })
    500 
    501       json(conn, update)
    502     else
    503       _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
    504     end
    505   end
    506 
    507   def right_delete_multiple(conn, _) do
    508     render_error(conn, :not_found, "No such permission_group")
    509   end
    510 
    511   def right_delete(
    512         %{assigns: %{user: admin}} = conn,
    513         %{
    514           "permission_group" => permission_group,
    515           "nickname" => nickname
    516         }
    517       )
    518       when permission_group in ["moderator", "admin"] do
    519     fields = %{:"is_#{permission_group}" => false}
    520 
    521     {:ok, user} =
    522       nickname
    523       |> User.get_cached_by_nickname()
    524       |> User.admin_api_update(fields)
    525 
    526     ModerationLog.insert_log(%{
    527       action: "revoke",
    528       actor: admin,
    529       subject: [user],
    530       permission: permission_group
    531     })
    532 
    533     json(conn, fields)
    534   end
    535 
    536   def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
    537     render_error(conn, :forbidden, "You can't revoke your own admin status.")
    538   end
    539 
    540   def relay_list(conn, _params) do
    541     with {:ok, list} <- Relay.list() do
    542       json(conn, %{relays: list})
    543     else
    544       _ ->
    545         conn
    546         |> put_status(500)
    547     end
    548   end
    549 
    550   def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
    551     with {:ok, _message} <- Relay.follow(target) do
    552       ModerationLog.insert_log(%{
    553         action: "relay_follow",
    554         actor: admin,
    555         target: target
    556       })
    557 
    558       json(conn, target)
    559     else
    560       _ ->
    561         conn
    562         |> put_status(500)
    563         |> json(target)
    564     end
    565   end
    566 
    567   def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
    568     with {:ok, _message} <- Relay.unfollow(target) do
    569       ModerationLog.insert_log(%{
    570         action: "relay_unfollow",
    571         actor: admin,
    572         target: target
    573       })
    574 
    575       json(conn, target)
    576     else
    577       _ ->
    578         conn
    579         |> put_status(500)
    580         |> json(target)
    581     end
    582   end
    583 
    584   @doc "Sends registration invite via email"
    585   def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
    586     with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
    587          {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
    588          {:ok, invite_token} <- UserInviteToken.create_invite(),
    589          email <-
    590            Pleroma.Emails.UserEmail.user_invitation_email(
    591              user,
    592              invite_token,
    593              email,
    594              params["name"]
    595            ),
    596          {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
    597       json_response(conn, :no_content, "")
    598     else
    599       {:registrations_open, _} ->
    600         errors(
    601           conn,
    602           {:error, "To send invites you need to set the `registrations_open` option to false."}
    603         )
    604 
    605       {:invites_enabled, _} ->
    606         errors(
    607           conn,
    608           {:error, "To send invites you need to set the `invites_enabled` option to true."}
    609         )
    610     end
    611   end
    612 
    613   @doc "Create an account registration invite token"
    614   def create_invite_token(conn, params) do
    615     opts = %{}
    616 
    617     opts =
    618       if params["max_use"],
    619         do: Map.put(opts, :max_use, params["max_use"]),
    620         else: opts
    621 
    622     opts =
    623       if params["expires_at"],
    624         do: Map.put(opts, :expires_at, params["expires_at"]),
    625         else: opts
    626 
    627     {:ok, invite} = UserInviteToken.create_invite(opts)
    628 
    629     json(conn, AccountView.render("invite.json", %{invite: invite}))
    630   end
    631 
    632   @doc "Get list of created invites"
    633   def invites(conn, _params) do
    634     invites = UserInviteToken.list_invites()
    635 
    636     conn
    637     |> put_view(AccountView)
    638     |> render("invites.json", %{invites: invites})
    639   end
    640 
    641   @doc "Revokes invite by token"
    642   def revoke_invite(conn, %{"token" => token}) do
    643     with {:ok, invite} <- UserInviteToken.find_by_token(token),
    644          {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
    645       conn
    646       |> put_view(AccountView)
    647       |> render("invite.json", %{invite: updated_invite})
    648     else
    649       nil -> {:error, :not_found}
    650     end
    651   end
    652 
    653   @doc "Get a password reset token (base64 string) for given nickname"
    654   def get_password_reset(conn, %{"nickname" => nickname}) do
    655     (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
    656     {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
    657 
    658     conn
    659     |> json(%{
    660       token: token.token,
    661       link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
    662     })
    663   end
    664 
    665   @doc "Force password reset for a given user"
    666   def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
    667     users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
    668 
    669     Enum.each(users, &User.force_password_reset_async/1)
    670 
    671     ModerationLog.insert_log(%{
    672       actor: admin,
    673       subject: users,
    674       action: "force_password_reset"
    675     })
    676 
    677     json_response(conn, :no_content, "")
    678   end
    679 
    680   @doc "Disable mfa for user's account."
    681   def disable_mfa(conn, %{"nickname" => nickname}) do
    682     case User.get_by_nickname(nickname) do
    683       %User{} = user ->
    684         MFA.disable(user)
    685         json(conn, nickname)
    686 
    687       _ ->
    688         {:error, :not_found}
    689     end
    690   end
    691 
    692   @doc "Show a given user's credentials"
    693   def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
    694     with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
    695       conn
    696       |> put_view(AccountView)
    697       |> render("credentials.json", %{user: user, for: admin})
    698     else
    699       _ -> {:error, :not_found}
    700     end
    701   end
    702 
    703   @doc "Updates a given user"
    704   def update_user_credentials(
    705         %{assigns: %{user: admin}} = conn,
    706         %{"nickname" => nickname} = params
    707       ) do
    708     with {_, user} <- {:user, User.get_cached_by_nickname(nickname)},
    709          {:ok, _user} <-
    710            User.update_as_admin(user, params) do
    711       ModerationLog.insert_log(%{
    712         actor: admin,
    713         subject: [user],
    714         action: "updated_users"
    715       })
    716 
    717       if params["password"] do
    718         User.force_password_reset_async(user)
    719       end
    720 
    721       ModerationLog.insert_log(%{
    722         actor: admin,
    723         subject: [user],
    724         action: "force_password_reset"
    725       })
    726 
    727       json(conn, %{status: "success"})
    728     else
    729       {:error, changeset} ->
    730         {_, {error, _}} = Enum.at(changeset.errors, 0)
    731         json(conn, %{error: "New password #{error}."})
    732 
    733       _ ->
    734         json(conn, %{error: "Unable to change password."})
    735     end
    736   end
    737 
    738   def list_reports(conn, params) do
    739     {page, page_size} = page_params(params)
    740 
    741     reports = Utils.get_reports(params, page, page_size)
    742 
    743     conn
    744     |> put_view(ReportView)
    745     |> render("index.json", %{reports: reports})
    746   end
    747 
    748   def report_show(conn, %{"id" => id}) do
    749     with %Activity{} = report <- Activity.get_by_id(id) do
    750       conn
    751       |> put_view(ReportView)
    752       |> render("show.json", Report.extract_report_info(report))
    753     else
    754       _ -> {:error, :not_found}
    755     end
    756   end
    757 
    758   def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
    759     result =
    760       reports
    761       |> Enum.map(fn report ->
    762         with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
    763           ModerationLog.insert_log(%{
    764             action: "report_update",
    765             actor: admin,
    766             subject: activity
    767           })
    768 
    769           activity
    770         else
    771           {:error, message} -> %{id: report["id"], error: message}
    772         end
    773       end)
    774 
    775     case Enum.any?(result, &Map.has_key?(&1, :error)) do
    776       true -> json_response(conn, :bad_request, result)
    777       false -> json_response(conn, :no_content, "")
    778     end
    779   end
    780 
    781   def report_notes_create(%{assigns: %{user: user}} = conn, %{
    782         "id" => report_id,
    783         "content" => content
    784       }) do
    785     with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
    786       ModerationLog.insert_log(%{
    787         action: "report_note",
    788         actor: user,
    789         subject: Activity.get_by_id(report_id),
    790         text: content
    791       })
    792 
    793       json_response(conn, :no_content, "")
    794     else
    795       _ -> json_response(conn, :bad_request, "")
    796     end
    797   end
    798 
    799   def report_notes_delete(%{assigns: %{user: user}} = conn, %{
    800         "id" => note_id,
    801         "report_id" => report_id
    802       }) do
    803     with {:ok, note} <- ReportNote.destroy(note_id) do
    804       ModerationLog.insert_log(%{
    805         action: "report_note_delete",
    806         actor: user,
    807         subject: Activity.get_by_id(report_id),
    808         text: note.content
    809       })
    810 
    811       json_response(conn, :no_content, "")
    812     else
    813       _ -> json_response(conn, :bad_request, "")
    814     end
    815   end
    816 
    817   def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
    818     godmode = params["godmode"] == "true" || params["godmode"] == true
    819     local_only = params["local_only"] == "true" || params["local_only"] == true
    820     with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
    821     {page, page_size} = page_params(params)
    822 
    823     activities =
    824       ActivityPub.fetch_statuses(nil, %{
    825         "godmode" => godmode,
    826         "local_only" => local_only,
    827         "limit" => page_size,
    828         "offset" => (page - 1) * page_size,
    829         "exclude_reblogs" => !with_reblogs && "true"
    830       })
    831 
    832     conn
    833     |> put_view(AdminAPI.StatusView)
    834     |> render("index.json", %{activities: activities, as: :activity})
    835   end
    836 
    837   def status_show(conn, %{"id" => id}) do
    838     with %Activity{} = activity <- Activity.get_by_id(id) do
    839       conn
    840       |> put_view(MastodonAPI.StatusView)
    841       |> render("show.json", %{activity: activity})
    842     else
    843       _ -> errors(conn, {:error, :not_found})
    844     end
    845   end
    846 
    847   def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
    848     params =
    849       params
    850       |> Map.take(["sensitive", "visibility"])
    851       |> Map.new(fn {key, value} -> {String.to_existing_atom(key), value} end)
    852 
    853     with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
    854       {:ok, sensitive} = Ecto.Type.cast(:boolean, params[:sensitive])
    855 
    856       ModerationLog.insert_log(%{
    857         action: "status_update",
    858         actor: admin,
    859         subject: activity,
    860         sensitive: sensitive,
    861         visibility: params[:visibility]
    862       })
    863 
    864       conn
    865       |> put_view(MastodonAPI.StatusView)
    866       |> render("show.json", %{activity: activity})
    867     end
    868   end
    869 
    870   def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
    871     with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
    872       ModerationLog.insert_log(%{
    873         action: "status_delete",
    874         actor: user,
    875         subject_id: id
    876       })
    877 
    878       json(conn, %{})
    879     end
    880   end
    881 
    882   def list_log(conn, params) do
    883     {page, page_size} = page_params(params)
    884 
    885     log =
    886       ModerationLog.get_all(%{
    887         page: page,
    888         page_size: page_size,
    889         start_date: params["start_date"],
    890         end_date: params["end_date"],
    891         user_id: params["user_id"],
    892         search: params["search"]
    893       })
    894 
    895     conn
    896     |> put_view(ModerationLogView)
    897     |> render("index.json", %{log: log})
    898   end
    899 
    900   def config_descriptions(conn, _params) do
    901     descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
    902 
    903     json(conn, descriptions)
    904   end
    905 
    906   def config_show(conn, %{"only_db" => true}) do
    907     with :ok <- configurable_from_database(conn) do
    908       configs = Pleroma.Repo.all(ConfigDB)
    909 
    910       conn
    911       |> put_view(ConfigView)
    912       |> render("index.json", %{configs: configs})
    913     end
    914   end
    915 
    916   def config_show(conn, _params) do
    917     with :ok <- configurable_from_database(conn) do
    918       configs = ConfigDB.get_all_as_keyword()
    919 
    920       merged =
    921         Config.Holder.default_config()
    922         |> ConfigDB.merge(configs)
    923         |> Enum.map(fn {group, values} ->
    924           Enum.map(values, fn {key, value} ->
    925             db =
    926               if configs[group][key] do
    927                 ConfigDB.get_db_keys(configs[group][key], key)
    928               end
    929 
    930             db_value = configs[group][key]
    931 
    932             merged_value =
    933               if !is_nil(db_value) and Keyword.keyword?(db_value) and
    934                    ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
    935                 ConfigDB.merge_group(group, key, value, db_value)
    936               else
    937                 value
    938               end
    939 
    940             setting = %{
    941               group: ConfigDB.convert(group),
    942               key: ConfigDB.convert(key),
    943               value: ConfigDB.convert(merged_value)
    944             }
    945 
    946             if db, do: Map.put(setting, :db, db), else: setting
    947           end)
    948         end)
    949         |> List.flatten()
    950 
    951       json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
    952     end
    953   end
    954 
    955   def config_update(conn, %{"configs" => configs}) do
    956     with :ok <- configurable_from_database(conn) do
    957       {_errors, results} =
    958         configs
    959         |> Enum.filter(&whitelisted_config?/1)
    960         |> Enum.map(fn
    961           %{"group" => group, "key" => key, "delete" => true} = params ->
    962             ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
    963 
    964           %{"group" => group, "key" => key, "value" => value} ->
    965             ConfigDB.update_or_create(%{group: group, key: key, value: value})
    966         end)
    967         |> Enum.split_with(fn result -> elem(result, 0) == :error end)
    968 
    969       {deleted, updated} =
    970         results
    971         |> Enum.map(fn {:ok, config} ->
    972           Map.put(config, :db, ConfigDB.get_db_keys(config))
    973         end)
    974         |> Enum.split_with(fn config ->
    975           Ecto.get_meta(config, :state) == :deleted
    976         end)
    977 
    978       Config.TransferTask.load_and_update_env(deleted, false)
    979 
    980       if !Restarter.Pleroma.need_reboot?() do
    981         changed_reboot_settings? =
    982           (updated ++ deleted)
    983           |> Enum.any?(fn config ->
    984             group = ConfigDB.from_string(config.group)
    985             key = ConfigDB.from_string(config.key)
    986             value = ConfigDB.from_binary(config.value)
    987             Config.TransferTask.pleroma_need_restart?(group, key, value)
    988           end)
    989 
    990         if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
    991       end
    992 
    993       conn
    994       |> put_view(ConfigView)
    995       |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
    996     end
    997   end
    998 
    999   def restart(conn, _params) do
   1000     with :ok <- configurable_from_database(conn) do
   1001       Restarter.Pleroma.restart(Config.get(:env), 50)
   1002 
   1003       json(conn, %{})
   1004     end
   1005   end
   1006 
   1007   def need_reboot(conn, _params) do
   1008     json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
   1009   end
   1010 
   1011   defp configurable_from_database(conn) do
   1012     if Config.get(:configurable_from_database) do
   1013       :ok
   1014     else
   1015       errors(
   1016         conn,
   1017         {:error, "To use this endpoint you need to enable configuration from database."}
   1018       )
   1019     end
   1020   end
   1021 
   1022   defp whitelisted_config?(group, key) do
   1023     if whitelisted_configs = Config.get(:database_config_whitelist) do
   1024       Enum.any?(whitelisted_configs, fn
   1025         {whitelisted_group} ->
   1026           group == inspect(whitelisted_group)
   1027 
   1028         {whitelisted_group, whitelisted_key} ->
   1029           group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
   1030       end)
   1031     else
   1032       true
   1033     end
   1034   end
   1035 
   1036   defp whitelisted_config?(%{"group" => group, "key" => key}) do
   1037     whitelisted_config?(group, key)
   1038   end
   1039 
   1040   defp whitelisted_config?(%{:group => group} = config) do
   1041     whitelisted_config?(group, config[:key])
   1042   end
   1043 
   1044   def reload_emoji(conn, _params) do
   1045     Pleroma.Emoji.reload()
   1046 
   1047     conn |> json("ok")
   1048   end
   1049 
   1050   def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
   1051     users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
   1052 
   1053     User.toggle_confirmation(users)
   1054 
   1055     ModerationLog.insert_log(%{
   1056       actor: admin,
   1057       subject: users,
   1058       action: "confirm_email"
   1059     })
   1060 
   1061     conn |> json("")
   1062   end
   1063 
   1064   def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
   1065     users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
   1066 
   1067     User.try_send_confirmation_email(users)
   1068 
   1069     ModerationLog.insert_log(%{
   1070       actor: admin,
   1071       subject: users,
   1072       action: "resend_confirmation_email"
   1073     })
   1074 
   1075     conn |> json("")
   1076   end
   1077 
   1078   def oauth_app_create(conn, params) do
   1079     params =
   1080       if params["name"] do
   1081         Map.put(params, "client_name", params["name"])
   1082       else
   1083         params
   1084       end
   1085 
   1086     result =
   1087       case App.create(params) do
   1088         {:ok, app} ->
   1089           AppView.render("show.json", %{app: app, admin: true})
   1090 
   1091         {:error, changeset} ->
   1092           App.errors(changeset)
   1093       end
   1094 
   1095     json(conn, result)
   1096   end
   1097 
   1098   def oauth_app_update(conn, params) do
   1099     params =
   1100       if params["name"] do
   1101         Map.put(params, "client_name", params["name"])
   1102       else
   1103         params
   1104       end
   1105 
   1106     with {:ok, app} <- App.update(params) do
   1107       json(conn, AppView.render("show.json", %{app: app, admin: true}))
   1108     else
   1109       {:error, changeset} ->
   1110         json(conn, App.errors(changeset))
   1111 
   1112       nil ->
   1113         json_response(conn, :bad_request, "")
   1114     end
   1115   end
   1116 
   1117   def oauth_app_list(conn, params) do
   1118     {page, page_size} = page_params(params)
   1119 
   1120     search_params = %{
   1121       client_name: params["name"],
   1122       client_id: params["client_id"],
   1123       page: page,
   1124       page_size: page_size
   1125     }
   1126 
   1127     search_params =
   1128       if Map.has_key?(params, "trusted") do
   1129         Map.put(search_params, :trusted, params["trusted"])
   1130       else
   1131         search_params
   1132       end
   1133 
   1134     with {:ok, apps, count} <- App.search(search_params) do
   1135       json(
   1136         conn,
   1137         AppView.render("index.json",
   1138           apps: apps,
   1139           count: count,
   1140           page_size: page_size,
   1141           admin: true
   1142         )
   1143       )
   1144     end
   1145   end
   1146 
   1147   def oauth_app_delete(conn, params) do
   1148     with {:ok, _app} <- App.destroy(params["id"]) do
   1149       json_response(conn, :no_content, "")
   1150     else
   1151       _ -> json_response(conn, :bad_request, "")
   1152     end
   1153   end
   1154 
   1155   def stats(conn, _) do
   1156     count = Stats.get_status_visibility_count()
   1157 
   1158     conn
   1159     |> json(%{"status_visibility" => count})
   1160   end
   1161 
   1162   defp errors(conn, {:error, :not_found}) do
   1163     conn
   1164     |> put_status(:not_found)
   1165     |> json(dgettext("errors", "Not found"))
   1166   end
   1167 
   1168   defp errors(conn, {:error, reason}) do
   1169     conn
   1170     |> put_status(:bad_request)
   1171     |> json(reason)
   1172   end
   1173 
   1174   defp errors(conn, {:param_cast, _}) do
   1175     conn
   1176     |> put_status(:bad_request)
   1177     |> json(dgettext("errors", "Invalid parameters"))
   1178   end
   1179 
   1180   defp errors(conn, _) do
   1181     conn
   1182     |> put_status(:internal_server_error)
   1183     |> json(dgettext("errors", "Something went wrong"))
   1184   end
   1185 
   1186   defp page_params(params) do
   1187     {get_page(params["page"]), get_page_size(params["page_size"])}
   1188   end
   1189 
   1190   defp get_page(page_string) when is_nil(page_string), do: 1
   1191 
   1192   defp get_page(page_string) do
   1193     case Integer.parse(page_string) do
   1194       {page, _} -> page
   1195       :error -> 1
   1196     end
   1197   end
   1198 
   1199   defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
   1200 
   1201   defp get_page_size(page_size_string) do
   1202     case Integer.parse(page_size_string) do
   1203       {page_size, _} -> page_size
   1204       :error -> @users_page_size
   1205     end
   1206   end
   1207 end