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