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