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