Merge branch 'openapi/instance' into 'develop'
[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, :status_show]
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, skip_relationships: false})
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, skip_relationships: false})
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, skip_relationships: false})
838 end
839
840 def status_show(conn, %{"id" => id}) do
841 with %Activity{} = activity <- Activity.get_by_id(id) do
842 conn
843 |> put_view(StatusView)
844 |> render("show.json", %{activity: activity})
845 else
846 _ -> errors(conn, {:error, :not_found})
847 end
848 end
849
850 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
851 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
852 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
853
854 ModerationLog.insert_log(%{
855 action: "status_update",
856 actor: admin,
857 subject: activity,
858 sensitive: sensitive,
859 visibility: params["visibility"]
860 })
861
862 conn
863 |> put_view(StatusView)
864 |> render("show.json", %{activity: activity})
865 end
866 end
867
868 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
869 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
870 ModerationLog.insert_log(%{
871 action: "status_delete",
872 actor: user,
873 subject_id: id
874 })
875
876 json(conn, %{})
877 end
878 end
879
880 def list_log(conn, params) do
881 {page, page_size} = page_params(params)
882
883 log =
884 ModerationLog.get_all(%{
885 page: page,
886 page_size: page_size,
887 start_date: params["start_date"],
888 end_date: params["end_date"],
889 user_id: params["user_id"],
890 search: params["search"]
891 })
892
893 conn
894 |> put_view(ModerationLogView)
895 |> render("index.json", %{log: log})
896 end
897
898 def config_descriptions(conn, _params) do
899 conn
900 |> Plug.Conn.put_resp_content_type("application/json")
901 |> Plug.Conn.send_resp(200, @descriptions_json)
902 end
903
904 def config_show(conn, %{"only_db" => true}) do
905 with :ok <- configurable_from_database(conn) do
906 configs = Pleroma.Repo.all(ConfigDB)
907
908 conn
909 |> put_view(ConfigView)
910 |> render("index.json", %{configs: configs})
911 end
912 end
913
914 def config_show(conn, _params) do
915 with :ok <- configurable_from_database(conn) do
916 configs = ConfigDB.get_all_as_keyword()
917
918 merged =
919 Config.Holder.default_config()
920 |> ConfigDB.merge(configs)
921 |> Enum.map(fn {group, values} ->
922 Enum.map(values, fn {key, value} ->
923 db =
924 if configs[group][key] do
925 ConfigDB.get_db_keys(configs[group][key], key)
926 end
927
928 db_value = configs[group][key]
929
930 merged_value =
931 if !is_nil(db_value) and Keyword.keyword?(db_value) and
932 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
933 ConfigDB.merge_group(group, key, value, db_value)
934 else
935 value
936 end
937
938 setting = %{
939 group: ConfigDB.convert(group),
940 key: ConfigDB.convert(key),
941 value: ConfigDB.convert(merged_value)
942 }
943
944 if db, do: Map.put(setting, :db, db), else: setting
945 end)
946 end)
947 |> List.flatten()
948
949 json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
950 end
951 end
952
953 def config_update(conn, %{"configs" => configs}) do
954 with :ok <- configurable_from_database(conn) do
955 {_errors, results} =
956 Enum.map(configs, fn
957 %{"group" => group, "key" => key, "delete" => true} = params ->
958 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
959
960 %{"group" => group, "key" => key, "value" => value} ->
961 ConfigDB.update_or_create(%{group: group, key: key, value: value})
962 end)
963 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
964
965 {deleted, updated} =
966 results
967 |> Enum.map(fn {:ok, config} ->
968 Map.put(config, :db, ConfigDB.get_db_keys(config))
969 end)
970 |> Enum.split_with(fn config ->
971 Ecto.get_meta(config, :state) == :deleted
972 end)
973
974 Config.TransferTask.load_and_update_env(deleted, false)
975
976 if !Restarter.Pleroma.need_reboot?() do
977 changed_reboot_settings? =
978 (updated ++ deleted)
979 |> Enum.any?(fn config ->
980 group = ConfigDB.from_string(config.group)
981 key = ConfigDB.from_string(config.key)
982 value = ConfigDB.from_binary(config.value)
983 Config.TransferTask.pleroma_need_restart?(group, key, value)
984 end)
985
986 if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
987 end
988
989 conn
990 |> put_view(ConfigView)
991 |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
992 end
993 end
994
995 def restart(conn, _params) do
996 with :ok <- configurable_from_database(conn) do
997 Restarter.Pleroma.restart(Config.get(:env), 50)
998
999 json(conn, %{})
1000 end
1001 end
1002
1003 def need_reboot(conn, _params) do
1004 json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
1005 end
1006
1007 defp configurable_from_database(conn) do
1008 if Config.get(:configurable_from_database) do
1009 :ok
1010 else
1011 errors(
1012 conn,
1013 {:error, "To use this endpoint you need to enable configuration from database."}
1014 )
1015 end
1016 end
1017
1018 def reload_emoji(conn, _params) do
1019 Pleroma.Emoji.reload()
1020
1021 conn |> json("ok")
1022 end
1023
1024 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1025 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1026
1027 User.toggle_confirmation(users)
1028
1029 ModerationLog.insert_log(%{
1030 actor: admin,
1031 subject: users,
1032 action: "confirm_email"
1033 })
1034
1035 conn |> json("")
1036 end
1037
1038 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1039 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1040
1041 User.try_send_confirmation_email(users)
1042
1043 ModerationLog.insert_log(%{
1044 actor: admin,
1045 subject: users,
1046 action: "resend_confirmation_email"
1047 })
1048
1049 conn |> json("")
1050 end
1051
1052 def oauth_app_create(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 result =
1061 case App.create(params) do
1062 {:ok, app} ->
1063 AppView.render("show.json", %{app: app, admin: true})
1064
1065 {:error, changeset} ->
1066 App.errors(changeset)
1067 end
1068
1069 json(conn, result)
1070 end
1071
1072 def oauth_app_update(conn, params) do
1073 params =
1074 if params["name"] do
1075 Map.put(params, "client_name", params["name"])
1076 else
1077 params
1078 end
1079
1080 with {:ok, app} <- App.update(params) do
1081 json(conn, AppView.render("show.json", %{app: app, admin: true}))
1082 else
1083 {:error, changeset} ->
1084 json(conn, App.errors(changeset))
1085
1086 nil ->
1087 json_response(conn, :bad_request, "")
1088 end
1089 end
1090
1091 def oauth_app_list(conn, params) do
1092 {page, page_size} = page_params(params)
1093
1094 search_params = %{
1095 client_name: params["name"],
1096 client_id: params["client_id"],
1097 page: page,
1098 page_size: page_size
1099 }
1100
1101 search_params =
1102 if Map.has_key?(params, "trusted") do
1103 Map.put(search_params, :trusted, params["trusted"])
1104 else
1105 search_params
1106 end
1107
1108 with {:ok, apps, count} <- App.search(search_params) do
1109 json(
1110 conn,
1111 AppView.render("index.json",
1112 apps: apps,
1113 count: count,
1114 page_size: page_size,
1115 admin: true
1116 )
1117 )
1118 end
1119 end
1120
1121 def oauth_app_delete(conn, params) do
1122 with {:ok, _app} <- App.destroy(params["id"]) do
1123 json_response(conn, :no_content, "")
1124 else
1125 _ -> json_response(conn, :bad_request, "")
1126 end
1127 end
1128
1129 def stats(conn, _) do
1130 count = Stats.get_status_visibility_count()
1131
1132 conn
1133 |> json(%{"status_visibility" => count})
1134 end
1135
1136 defp errors(conn, {:error, :not_found}) do
1137 conn
1138 |> put_status(:not_found)
1139 |> json(dgettext("errors", "Not found"))
1140 end
1141
1142 defp errors(conn, {:error, reason}) do
1143 conn
1144 |> put_status(:bad_request)
1145 |> json(reason)
1146 end
1147
1148 defp errors(conn, {:param_cast, _}) do
1149 conn
1150 |> put_status(:bad_request)
1151 |> json(dgettext("errors", "Invalid parameters"))
1152 end
1153
1154 defp errors(conn, _) do
1155 conn
1156 |> put_status(:internal_server_error)
1157 |> json(dgettext("errors", "Something went wrong"))
1158 end
1159
1160 defp page_params(params) do
1161 {get_page(params["page"]), get_page_size(params["page_size"])}
1162 end
1163
1164 defp get_page(page_string) when is_nil(page_string), do: 1
1165
1166 defp get_page(page_string) do
1167 case Integer.parse(page_string) do
1168 {page, _} -> page
1169 :error -> 1
1170 end
1171 end
1172
1173 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1174
1175 defp get_page_size(page_size_string) do
1176 case Integer.parse(page_size_string) do
1177 {page_size, _} -> page_size
1178 :error -> @users_page_size
1179 end
1180 end
1181 end