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