80a4ebaac2f4e2d2888e4cfd92774473b57015a1
[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, :status_show]
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)) do
395 json(
396 conn,
397 AccountView.render("index.json", users: users, count: count, page_size: page_size)
398 )
399 end
400 end
401
402 @filters ~w(local external active deactivated is_admin is_moderator)
403
404 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
405 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
406
407 defp maybe_parse_filters(filters) do
408 filters
409 |> String.split(",")
410 |> Enum.filter(&Enum.member?(@filters, &1))
411 |> Enum.map(&String.to_atom(&1))
412 |> Enum.into(%{}, &{&1, true})
413 end
414
415 def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
416 "permission_group" => permission_group,
417 "nicknames" => nicknames
418 })
419 when permission_group in ["moderator", "admin"] do
420 update = %{:"is_#{permission_group}" => true}
421
422 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
423
424 for u <- users, do: User.admin_api_update(u, update)
425
426 ModerationLog.insert_log(%{
427 action: "grant",
428 actor: admin,
429 subject: users,
430 permission: permission_group
431 })
432
433 json(conn, update)
434 end
435
436 def right_add_multiple(conn, _) do
437 render_error(conn, :not_found, "No such permission_group")
438 end
439
440 def right_add(%{assigns: %{user: admin}} = conn, %{
441 "permission_group" => permission_group,
442 "nickname" => nickname
443 })
444 when permission_group in ["moderator", "admin"] do
445 fields = %{:"is_#{permission_group}" => true}
446
447 {:ok, user} =
448 nickname
449 |> User.get_cached_by_nickname()
450 |> User.admin_api_update(fields)
451
452 ModerationLog.insert_log(%{
453 action: "grant",
454 actor: admin,
455 subject: [user],
456 permission: permission_group
457 })
458
459 json(conn, fields)
460 end
461
462 def right_add(conn, _) do
463 render_error(conn, :not_found, "No such permission_group")
464 end
465
466 def right_get(conn, %{"nickname" => nickname}) do
467 user = User.get_cached_by_nickname(nickname)
468
469 conn
470 |> json(%{
471 is_moderator: user.is_moderator,
472 is_admin: user.is_admin
473 })
474 end
475
476 def right_delete_multiple(
477 %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
478 %{
479 "permission_group" => permission_group,
480 "nicknames" => nicknames
481 }
482 )
483 when permission_group in ["moderator", "admin"] do
484 with false <- Enum.member?(nicknames, admin_nickname) do
485 update = %{:"is_#{permission_group}" => false}
486
487 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
488
489 for u <- users, do: User.admin_api_update(u, update)
490
491 ModerationLog.insert_log(%{
492 action: "revoke",
493 actor: admin,
494 subject: users,
495 permission: permission_group
496 })
497
498 json(conn, update)
499 else
500 _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
501 end
502 end
503
504 def right_delete_multiple(conn, _) do
505 render_error(conn, :not_found, "No such permission_group")
506 end
507
508 def right_delete(
509 %{assigns: %{user: admin}} = conn,
510 %{
511 "permission_group" => permission_group,
512 "nickname" => nickname
513 }
514 )
515 when permission_group in ["moderator", "admin"] do
516 fields = %{:"is_#{permission_group}" => false}
517
518 {:ok, user} =
519 nickname
520 |> User.get_cached_by_nickname()
521 |> User.admin_api_update(fields)
522
523 ModerationLog.insert_log(%{
524 action: "revoke",
525 actor: admin,
526 subject: [user],
527 permission: permission_group
528 })
529
530 json(conn, fields)
531 end
532
533 def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
534 render_error(conn, :forbidden, "You can't revoke your own admin status.")
535 end
536
537 def relay_list(conn, _params) do
538 with {:ok, list} <- Relay.list() do
539 json(conn, %{relays: list})
540 else
541 _ ->
542 conn
543 |> put_status(500)
544 end
545 end
546
547 def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
548 with {:ok, _message} <- Relay.follow(target) do
549 ModerationLog.insert_log(%{
550 action: "relay_follow",
551 actor: admin,
552 target: target
553 })
554
555 json(conn, target)
556 else
557 _ ->
558 conn
559 |> put_status(500)
560 |> json(target)
561 end
562 end
563
564 def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
565 with {:ok, _message} <- Relay.unfollow(target) do
566 ModerationLog.insert_log(%{
567 action: "relay_unfollow",
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 @doc "Sends registration invite via email"
582 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
583 with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
584 {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
585 {:ok, invite_token} <- UserInviteToken.create_invite(),
586 email <-
587 Pleroma.Emails.UserEmail.user_invitation_email(
588 user,
589 invite_token,
590 email,
591 params["name"]
592 ),
593 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
594 json_response(conn, :no_content, "")
595 else
596 {:registrations_open, _} ->
597 errors(
598 conn,
599 {:error, "To send invites you need to set the `registrations_open` option to false."}
600 )
601
602 {:invites_enabled, _} ->
603 errors(
604 conn,
605 {:error, "To send invites you need to set the `invites_enabled` option to true."}
606 )
607 end
608 end
609
610 @doc "Create an account registration invite token"
611 def create_invite_token(conn, params) do
612 opts = %{}
613
614 opts =
615 if params["max_use"],
616 do: Map.put(opts, :max_use, params["max_use"]),
617 else: opts
618
619 opts =
620 if params["expires_at"],
621 do: Map.put(opts, :expires_at, params["expires_at"]),
622 else: opts
623
624 {:ok, invite} = UserInviteToken.create_invite(opts)
625
626 json(conn, AccountView.render("invite.json", %{invite: invite}))
627 end
628
629 @doc "Get list of created invites"
630 def invites(conn, _params) do
631 invites = UserInviteToken.list_invites()
632
633 conn
634 |> put_view(AccountView)
635 |> render("invites.json", %{invites: invites})
636 end
637
638 @doc "Revokes invite by token"
639 def revoke_invite(conn, %{"token" => token}) do
640 with {:ok, invite} <- UserInviteToken.find_by_token(token),
641 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
642 conn
643 |> put_view(AccountView)
644 |> render("invite.json", %{invite: updated_invite})
645 else
646 nil -> {:error, :not_found}
647 end
648 end
649
650 @doc "Get a password reset token (base64 string) for given nickname"
651 def get_password_reset(conn, %{"nickname" => nickname}) do
652 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
653 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
654
655 conn
656 |> json(%{
657 token: token.token,
658 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
659 })
660 end
661
662 @doc "Force password reset for a given user"
663 def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
664 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
665
666 Enum.each(users, &User.force_password_reset_async/1)
667
668 ModerationLog.insert_log(%{
669 actor: admin,
670 subject: users,
671 action: "force_password_reset"
672 })
673
674 json_response(conn, :no_content, "")
675 end
676
677 @doc "Show a given user's credentials"
678 def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
679 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
680 conn
681 |> put_view(AccountView)
682 |> render("credentials.json", %{user: user, for: admin})
683 else
684 _ -> {:error, :not_found}
685 end
686 end
687
688 @doc "Updates a given user"
689 def update_user_credentials(
690 %{assigns: %{user: admin}} = conn,
691 %{"nickname" => nickname} = params
692 ) do
693 with {_, user} <- {:user, User.get_cached_by_nickname(nickname)},
694 {:ok, _user} <-
695 User.update_as_admin(user, params) do
696 ModerationLog.insert_log(%{
697 actor: admin,
698 subject: [user],
699 action: "updated_users"
700 })
701
702 if params["password"] do
703 User.force_password_reset_async(user)
704 end
705
706 ModerationLog.insert_log(%{
707 actor: admin,
708 subject: [user],
709 action: "force_password_reset"
710 })
711
712 json(conn, %{status: "success"})
713 else
714 {:error, changeset} ->
715 {_, {error, _}} = Enum.at(changeset.errors, 0)
716 json(conn, %{error: "New password #{error}."})
717
718 _ ->
719 json(conn, %{error: "Unable to change password."})
720 end
721 end
722
723 def list_reports(conn, params) do
724 {page, page_size} = page_params(params)
725
726 reports = Utils.get_reports(params, page, page_size)
727
728 conn
729 |> put_view(ReportView)
730 |> render("index.json", %{reports: reports})
731 end
732
733 def report_show(conn, %{"id" => id}) do
734 with %Activity{} = report <- Activity.get_by_id(id) do
735 conn
736 |> put_view(ReportView)
737 |> render("show.json", Report.extract_report_info(report))
738 else
739 _ -> {:error, :not_found}
740 end
741 end
742
743 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
744 result =
745 reports
746 |> Enum.map(fn report ->
747 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
748 ModerationLog.insert_log(%{
749 action: "report_update",
750 actor: admin,
751 subject: activity
752 })
753
754 activity
755 else
756 {:error, message} -> %{id: report["id"], error: message}
757 end
758 end)
759
760 case Enum.any?(result, &Map.has_key?(&1, :error)) do
761 true -> json_response(conn, :bad_request, result)
762 false -> json_response(conn, :no_content, "")
763 end
764 end
765
766 def report_notes_create(%{assigns: %{user: user}} = conn, %{
767 "id" => report_id,
768 "content" => content
769 }) do
770 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
771 ModerationLog.insert_log(%{
772 action: "report_note",
773 actor: user,
774 subject: Activity.get_by_id(report_id),
775 text: content
776 })
777
778 json_response(conn, :no_content, "")
779 else
780 _ -> json_response(conn, :bad_request, "")
781 end
782 end
783
784 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
785 "id" => note_id,
786 "report_id" => report_id
787 }) do
788 with {:ok, note} <- ReportNote.destroy(note_id) do
789 ModerationLog.insert_log(%{
790 action: "report_note_delete",
791 actor: user,
792 subject: Activity.get_by_id(report_id),
793 text: note.content
794 })
795
796 json_response(conn, :no_content, "")
797 else
798 _ -> json_response(conn, :bad_request, "")
799 end
800 end
801
802 def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
803 godmode = params["godmode"] == "true" || params["godmode"] == true
804 local_only = params["local_only"] == "true" || params["local_only"] == true
805 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
806 {page, page_size} = page_params(params)
807
808 activities =
809 ActivityPub.fetch_statuses(nil, %{
810 "godmode" => godmode,
811 "local_only" => local_only,
812 "limit" => page_size,
813 "offset" => (page - 1) * page_size,
814 "exclude_reblogs" => !with_reblogs && "true"
815 })
816
817 conn
818 |> put_view(Pleroma.Web.AdminAPI.StatusView)
819 |> render("index.json", %{activities: activities, as: :activity, skip_relationships: false})
820 end
821
822 def status_show(conn, %{"id" => id}) do
823 with %Activity{} = activity <- Activity.get_by_id(id) do
824 conn
825 |> put_view(StatusView)
826 |> render("show.json", %{activity: activity})
827 else
828 _ -> errors(conn, {:error, :not_found})
829 end
830 end
831
832 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
833 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
834 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
835
836 ModerationLog.insert_log(%{
837 action: "status_update",
838 actor: admin,
839 subject: activity,
840 sensitive: sensitive,
841 visibility: params["visibility"]
842 })
843
844 conn
845 |> put_view(StatusView)
846 |> render("show.json", %{activity: activity})
847 end
848 end
849
850 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
851 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
852 ModerationLog.insert_log(%{
853 action: "status_delete",
854 actor: user,
855 subject_id: id
856 })
857
858 json(conn, %{})
859 end
860 end
861
862 def list_log(conn, params) do
863 {page, page_size} = page_params(params)
864
865 log =
866 ModerationLog.get_all(%{
867 page: page,
868 page_size: page_size,
869 start_date: params["start_date"],
870 end_date: params["end_date"],
871 user_id: params["user_id"],
872 search: params["search"]
873 })
874
875 conn
876 |> put_view(ModerationLogView)
877 |> render("index.json", %{log: log})
878 end
879
880 def config_descriptions(conn, _params) do
881 conn
882 |> Plug.Conn.put_resp_content_type("application/json")
883 |> Plug.Conn.send_resp(200, @descriptions_json)
884 end
885
886 def config_show(conn, %{"only_db" => true}) do
887 with :ok <- configurable_from_database(conn) do
888 configs = Pleroma.Repo.all(ConfigDB)
889
890 conn
891 |> put_view(ConfigView)
892 |> render("index.json", %{configs: configs})
893 end
894 end
895
896 def config_show(conn, _params) do
897 with :ok <- configurable_from_database(conn) do
898 configs = ConfigDB.get_all_as_keyword()
899
900 merged =
901 Config.Holder.default_config()
902 |> ConfigDB.merge(configs)
903 |> Enum.map(fn {group, values} ->
904 Enum.map(values, fn {key, value} ->
905 db =
906 if configs[group][key] do
907 ConfigDB.get_db_keys(configs[group][key], key)
908 end
909
910 db_value = configs[group][key]
911
912 merged_value =
913 if !is_nil(db_value) and Keyword.keyword?(db_value) and
914 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
915 ConfigDB.merge_group(group, key, value, db_value)
916 else
917 value
918 end
919
920 setting = %{
921 group: ConfigDB.convert(group),
922 key: ConfigDB.convert(key),
923 value: ConfigDB.convert(merged_value)
924 }
925
926 if db, do: Map.put(setting, :db, db), else: setting
927 end)
928 end)
929 |> List.flatten()
930
931 json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
932 end
933 end
934
935 def config_update(conn, %{"configs" => configs}) do
936 with :ok <- configurable_from_database(conn) do
937 {_errors, results} =
938 Enum.map(configs, fn
939 %{"group" => group, "key" => key, "delete" => true} = params ->
940 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
941
942 %{"group" => group, "key" => key, "value" => value} ->
943 ConfigDB.update_or_create(%{group: group, key: key, value: value})
944 end)
945 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
946
947 {deleted, updated} =
948 results
949 |> Enum.map(fn {:ok, config} ->
950 Map.put(config, :db, ConfigDB.get_db_keys(config))
951 end)
952 |> Enum.split_with(fn config ->
953 Ecto.get_meta(config, :state) == :deleted
954 end)
955
956 Config.TransferTask.load_and_update_env(deleted, false)
957
958 if !Restarter.Pleroma.need_reboot?() do
959 changed_reboot_settings? =
960 (updated ++ deleted)
961 |> Enum.any?(fn config ->
962 group = ConfigDB.from_string(config.group)
963 key = ConfigDB.from_string(config.key)
964 value = ConfigDB.from_binary(config.value)
965 Config.TransferTask.pleroma_need_restart?(group, key, value)
966 end)
967
968 if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
969 end
970
971 conn
972 |> put_view(ConfigView)
973 |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
974 end
975 end
976
977 def restart(conn, _params) do
978 with :ok <- configurable_from_database(conn) do
979 Restarter.Pleroma.restart(Config.get(:env), 50)
980
981 json(conn, %{})
982 end
983 end
984
985 def need_reboot(conn, _params) do
986 json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
987 end
988
989 defp configurable_from_database(conn) do
990 if Config.get(:configurable_from_database) do
991 :ok
992 else
993 errors(
994 conn,
995 {:error, "To use this endpoint you need to enable configuration from database."}
996 )
997 end
998 end
999
1000 def reload_emoji(conn, _params) do
1001 Pleroma.Emoji.reload()
1002
1003 conn |> json("ok")
1004 end
1005
1006 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1007 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1008
1009 User.toggle_confirmation(users)
1010
1011 ModerationLog.insert_log(%{
1012 actor: admin,
1013 subject: users,
1014 action: "confirm_email"
1015 })
1016
1017 conn |> json("")
1018 end
1019
1020 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1021 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1022
1023 User.try_send_confirmation_email(users)
1024
1025 ModerationLog.insert_log(%{
1026 actor: admin,
1027 subject: users,
1028 action: "resend_confirmation_email"
1029 })
1030
1031 conn |> json("")
1032 end
1033
1034 def oauth_app_create(conn, params) do
1035 params =
1036 if params["name"] do
1037 Map.put(params, "client_name", params["name"])
1038 else
1039 params
1040 end
1041
1042 result =
1043 case App.create(params) do
1044 {:ok, app} ->
1045 AppView.render("show.json", %{app: app, admin: true})
1046
1047 {:error, changeset} ->
1048 App.errors(changeset)
1049 end
1050
1051 json(conn, result)
1052 end
1053
1054 def oauth_app_update(conn, params) do
1055 params =
1056 if params["name"] do
1057 Map.put(params, "client_name", params["name"])
1058 else
1059 params
1060 end
1061
1062 with {:ok, app} <- App.update(params) do
1063 json(conn, AppView.render("show.json", %{app: app, admin: true}))
1064 else
1065 {:error, changeset} ->
1066 json(conn, App.errors(changeset))
1067
1068 nil ->
1069 json_response(conn, :bad_request, "")
1070 end
1071 end
1072
1073 def oauth_app_list(conn, params) do
1074 {page, page_size} = page_params(params)
1075
1076 search_params = %{
1077 client_name: params["name"],
1078 client_id: params["client_id"],
1079 page: page,
1080 page_size: page_size
1081 }
1082
1083 search_params =
1084 if Map.has_key?(params, "trusted") do
1085 Map.put(search_params, :trusted, params["trusted"])
1086 else
1087 search_params
1088 end
1089
1090 with {:ok, apps, count} <- App.search(search_params) do
1091 json(
1092 conn,
1093 AppView.render("index.json",
1094 apps: apps,
1095 count: count,
1096 page_size: page_size,
1097 admin: true
1098 )
1099 )
1100 end
1101 end
1102
1103 def oauth_app_delete(conn, params) do
1104 with {:ok, _app} <- App.destroy(params["id"]) do
1105 json_response(conn, :no_content, "")
1106 else
1107 _ -> json_response(conn, :bad_request, "")
1108 end
1109 end
1110
1111 def stats(conn, _) do
1112 count = Stats.get_status_visibility_count()
1113
1114 conn
1115 |> json(%{"status_visibility" => count})
1116 end
1117
1118 defp errors(conn, {:error, :not_found}) do
1119 conn
1120 |> put_status(:not_found)
1121 |> json(dgettext("errors", "Not found"))
1122 end
1123
1124 defp errors(conn, {:error, reason}) do
1125 conn
1126 |> put_status(:bad_request)
1127 |> json(reason)
1128 end
1129
1130 defp errors(conn, {:param_cast, _}) do
1131 conn
1132 |> put_status(:bad_request)
1133 |> json(dgettext("errors", "Invalid parameters"))
1134 end
1135
1136 defp errors(conn, _) do
1137 conn
1138 |> put_status(:internal_server_error)
1139 |> json(dgettext("errors", "Something went wrong"))
1140 end
1141
1142 defp page_params(params) do
1143 {get_page(params["page"]), get_page_size(params["page_size"])}
1144 end
1145
1146 defp get_page(page_string) when is_nil(page_string), do: 1
1147
1148 defp get_page(page_string) do
1149 case Integer.parse(page_string) do
1150 {page, _} -> page
1151 :error -> 1
1152 end
1153 end
1154
1155 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1156
1157 defp get_page_size(page_size_string) do
1158 case Integer.parse(page_size_string) do
1159 {page_size, _} -> page_size
1160 :error -> @users_page_size
1161 end
1162 end
1163 end