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