1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
6 use Pleroma.Web, :controller
9 alias Pleroma.UserInviteToken
10 alias Pleroma.Web.ActivityPub.ActivityPub
11 alias Pleroma.Web.ActivityPub.Relay
12 alias Pleroma.Web.AdminAPI.AccountView
13 alias Pleroma.Web.AdminAPI.Config
14 alias Pleroma.Web.AdminAPI.ConfigView
15 alias Pleroma.Web.AdminAPI.ReportView
16 alias Pleroma.Web.AdminAPI.Search
17 alias Pleroma.Web.CommonAPI
18 alias Pleroma.Web.MastodonAPI.StatusView
20 import Pleroma.Web.ControllerHelper, only: [json_response: 3]
26 action_fallback(:errors)
28 def user_delete(conn, %{"nickname" => nickname}) do
29 User.get_cached_by_nickname(nickname)
36 def user_follow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
37 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
38 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
39 User.follow(follower, followed)
46 def user_unfollow(conn, %{"follower" => follower_nick, "followed" => followed_nick}) do
47 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
48 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
49 User.unfollow(follower, followed)
56 def users_create(conn, %{"users" => users}) do
58 Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
64 password_confirmation: password,
68 User.register_changeset(%User{}, user_data, need_confirmation: false)
70 |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
71 Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
74 case Pleroma.Repo.transaction(changesets) do
79 |> Enum.map(fn user ->
80 {:ok, user} = User.post_register_action(user)
83 |> Enum.map(&AccountView.render("created.json", %{user: &1}))
88 {:error, id, changeset, _} ->
90 Enum.map(changesets.operations, fn
91 {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
92 AccountView.render("create-error.json", %{changeset: changeset})
94 {_, {:changeset, current_changeset, _}} ->
95 AccountView.render("create-error.json", %{changeset: current_changeset})
99 |> put_status(:conflict)
104 def user_show(conn, %{"nickname" => nickname}) do
105 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
107 |> json(AccountView.render("show.json", %{user: user}))
109 _ -> {:error, :not_found}
113 def list_user_statuses(conn, %{"nickname" => nickname} = params) do
114 godmode = params["godmode"] == "true" || params["godmode"] == true
116 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
117 {_, page_size} = page_params(params)
120 ActivityPub.fetch_user_activities(user, nil, %{
121 "limit" => page_size,
126 |> json(StatusView.render("index.json", %{activities: activities, as: :activity}))
128 _ -> {:error, :not_found}
132 def user_toggle_activation(conn, %{"nickname" => nickname}) do
133 user = User.get_cached_by_nickname(nickname)
135 {:ok, updated_user} = User.deactivate(user, !user.info.deactivated)
138 |> json(AccountView.render("show.json", %{user: updated_user}))
141 def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
142 with {:ok, _} <- User.tag(nicknames, tags),
143 do: json_response(conn, :no_content, "")
146 def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do
147 with {:ok, _} <- User.untag(nicknames, tags),
148 do: json_response(conn, :no_content, "")
151 def list_users(conn, params) do
152 {page, page_size} = page_params(params)
153 filters = maybe_parse_filters(params["filters"])
156 query: params["query"],
158 page_size: page_size,
159 tags: params["tags"],
160 name: params["name"],
161 email: params["email"]
164 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
168 AccountView.render("index.json",
176 @filters ~w(local external active deactivated is_admin is_moderator)
178 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
179 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
181 defp maybe_parse_filters(filters) do
184 |> Enum.filter(&Enum.member?(@filters, &1))
185 |> Enum.map(&String.to_atom(&1))
186 |> Enum.into(%{}, &{&1, true})
189 def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})
190 when permission_group in ["moderator", "admin"] do
191 user = User.get_cached_by_nickname(nickname)
195 |> Map.put("is_" <> permission_group, true)
197 info_cng = User.Info.admin_api_update(user.info, info)
201 |> Ecto.Changeset.change()
202 |> Ecto.Changeset.put_embed(:info, info_cng)
204 {:ok, _user} = User.update_and_set_cache(cng)
209 def right_add(conn, _) do
210 render_error(conn, :not_found, "No such permission_group")
213 def right_get(conn, %{"nickname" => nickname}) do
214 user = User.get_cached_by_nickname(nickname)
218 is_moderator: user.info.is_moderator,
219 is_admin: user.info.is_admin
224 %{assigns: %{user: %User{:nickname => admin_nickname}}} = conn,
226 "permission_group" => permission_group,
227 "nickname" => nickname
230 when permission_group in ["moderator", "admin"] do
231 if admin_nickname == nickname do
232 render_error(conn, :forbidden, "You can't revoke your own admin status.")
234 user = User.get_cached_by_nickname(nickname)
238 |> Map.put("is_" <> permission_group, false)
240 info_cng = User.Info.admin_api_update(user.info, info)
243 Ecto.Changeset.change(user)
244 |> Ecto.Changeset.put_embed(:info, info_cng)
246 {:ok, _user} = User.update_and_set_cache(cng)
252 def right_delete(conn, _) do
253 render_error(conn, :not_found, "No such permission_group")
256 def set_activation_status(conn, %{"nickname" => nickname, "status" => status}) do
257 with {:ok, status} <- Ecto.Type.cast(:boolean, status),
258 %User{} = user <- User.get_cached_by_nickname(nickname),
259 {:ok, _} <- User.deactivate(user, !status),
260 do: json_response(conn, :no_content, "")
263 def relay_follow(conn, %{"relay_url" => target}) do
264 with {:ok, _message} <- Relay.follow(target) do
274 def relay_unfollow(conn, %{"relay_url" => target}) do
275 with {:ok, _message} <- Relay.unfollow(target) do
285 @doc "Sends registration invite via email"
286 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
288 Pleroma.Config.get([:instance, :invites_enabled]) &&
289 !Pleroma.Config.get([:instance, :registrations_open]),
290 {:ok, invite_token} <- UserInviteToken.create_invite(),
292 Pleroma.Emails.UserEmail.user_invitation_email(
298 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
299 json_response(conn, :no_content, "")
303 @doc "Get a account registeration invite token (base64 string)"
304 def get_invite_token(conn, params) do
305 options = params["invite"] || %{}
306 {:ok, invite} = UserInviteToken.create_invite(options)
309 |> json(invite.token)
312 @doc "Get list of created invites"
313 def invites(conn, _params) do
314 invites = UserInviteToken.list_invites()
317 |> json(AccountView.render("invites.json", %{invites: invites}))
320 @doc "Revokes invite by token"
321 def revoke_invite(conn, %{"token" => token}) do
322 with {:ok, invite} <- UserInviteToken.find_by_token(token),
323 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
325 |> json(AccountView.render("invite.json", %{invite: updated_invite}))
327 nil -> {:error, :not_found}
331 @doc "Get a password reset token (base64 string) for given nickname"
332 def get_password_reset(conn, %{"nickname" => nickname}) do
333 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
334 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
340 def list_reports(conn, params) do
343 |> Map.put("type", "Flag")
344 |> Map.put("skip_preload", true)
348 |> ActivityPub.fetch_activities(params)
352 |> put_view(ReportView)
353 |> render("index.json", %{reports: reports})
356 def report_show(conn, %{"id" => id}) do
357 with %Activity{} = report <- Activity.get_by_id(id) do
359 |> put_view(ReportView)
360 |> render("show.json", %{report: report})
362 _ -> {:error, :not_found}
366 def report_update_state(conn, %{"id" => id, "state" => state}) do
367 with {:ok, report} <- CommonAPI.update_report_state(id, state) do
369 |> put_view(ReportView)
370 |> render("show.json", %{report: report})
374 def report_respond(%{assigns: %{user: user}} = conn, %{"id" => id} = params) do
375 with false <- is_nil(params["status"]),
376 %Activity{} <- Activity.get_by_id(id) do
379 |> Map.put("in_reply_to_status_id", id)
380 |> Map.put("visibility", "direct")
382 {:ok, activity} = CommonAPI.post(user, params)
385 |> put_view(StatusView)
386 |> render("status.json", %{activity: activity})
396 def status_update(conn, %{"id" => id} = params) do
397 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
399 |> put_view(StatusView)
400 |> render("status.json", %{activity: activity})
404 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
405 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
410 def migrate_to_db(conn, _params) do
411 Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
415 def migrate_from_db(conn, _params) do
416 Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "true"])
420 def config_show(conn, _params) do
421 configs = Pleroma.Repo.all(Config)
424 |> put_view(ConfigView)
425 |> render("index.json", %{configs: configs})
428 def config_update(conn, %{"configs" => configs}) do
430 if Pleroma.Config.get([:instance, :dynamic_configuration]) do
433 %{"group" => group, "key" => key, "delete" => "true"} = params ->
434 {:ok, config} = Config.delete(%{group: group, key: key, subkeys: params["subkeys"]})
437 %{"group" => group, "key" => key, "value" => value} ->
438 {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
441 |> Enum.reject(&is_nil(&1))
443 Pleroma.Config.TransferTask.load_and_update_env()
444 Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"])
451 |> put_view(ConfigView)
452 |> render("index.json", %{configs: updated})
455 def errors(conn, {:error, :not_found}) do
457 |> put_status(:not_found)
458 |> json(dgettext("errors", "Not found"))
461 def errors(conn, {:error, reason}) do
463 |> put_status(:bad_request)
467 def errors(conn, {:param_cast, _}) do
469 |> put_status(:bad_request)
470 |> json(dgettext("errors", "Invalid parameters"))
473 def errors(conn, _) do
475 |> put_status(:internal_server_error)
476 |> json(dgettext("errors", "Something went wrong"))
479 defp page_params(params) do
480 {get_page(params["page"]), get_page_size(params["page_size"])}
483 defp get_page(page_string) when is_nil(page_string), do: 1
485 defp get_page(page_string) do
486 case Integer.parse(page_string) do
492 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
494 defp get_page_size(page_size_string) do
495 case Integer.parse(page_size_string) do
496 {page_size, _} -> page_size
497 :error -> @users_page_size