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