[#2332] Misc. improvements per code change requests.
[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 true <-
580 Config.get([:instance, :invites_enabled]) &&
581 !Config.get([:instance, :registrations_open]),
582 {:ok, invite_token} <- UserInviteToken.create_invite(),
583 email <-
584 Pleroma.Emails.UserEmail.user_invitation_email(
585 user,
586 invite_token,
587 email,
588 params["name"]
589 ),
590 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
591 json_response(conn, :no_content, "")
592 end
593 end
594
595 @doc "Create an account registration invite token"
596 def create_invite_token(conn, params) do
597 opts = %{}
598
599 opts =
600 if params["max_use"],
601 do: Map.put(opts, :max_use, params["max_use"]),
602 else: opts
603
604 opts =
605 if params["expires_at"],
606 do: Map.put(opts, :expires_at, params["expires_at"]),
607 else: opts
608
609 {:ok, invite} = UserInviteToken.create_invite(opts)
610
611 json(conn, AccountView.render("invite.json", %{invite: invite}))
612 end
613
614 @doc "Get list of created invites"
615 def invites(conn, _params) do
616 invites = UserInviteToken.list_invites()
617
618 conn
619 |> put_view(AccountView)
620 |> render("invites.json", %{invites: invites})
621 end
622
623 @doc "Revokes invite by token"
624 def revoke_invite(conn, %{"token" => token}) do
625 with {:ok, invite} <- UserInviteToken.find_by_token(token),
626 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
627 conn
628 |> put_view(AccountView)
629 |> render("invite.json", %{invite: updated_invite})
630 else
631 nil -> {:error, :not_found}
632 end
633 end
634
635 @doc "Get a password reset token (base64 string) for given nickname"
636 def get_password_reset(conn, %{"nickname" => nickname}) do
637 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
638 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
639
640 conn
641 |> json(%{
642 token: token.token,
643 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
644 })
645 end
646
647 @doc "Force password reset for a given user"
648 def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
649 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
650
651 Enum.each(users, &User.force_password_reset_async/1)
652
653 ModerationLog.insert_log(%{
654 actor: admin,
655 subject: users,
656 action: "force_password_reset"
657 })
658
659 json_response(conn, :no_content, "")
660 end
661
662 @doc "Show a given user's credentials"
663 def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
664 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
665 conn
666 |> put_view(AccountView)
667 |> render("credentials.json", %{user: user, for: admin})
668 else
669 _ -> {:error, :not_found}
670 end
671 end
672
673 @doc "Updates a given user"
674 def update_user_credentials(
675 %{assigns: %{user: admin}} = conn,
676 %{"nickname" => nickname} = params
677 ) do
678 with {_, user} <- {:user, User.get_cached_by_nickname(nickname)},
679 {:ok, _user} <-
680 User.update_as_admin(user, params) do
681 ModerationLog.insert_log(%{
682 actor: admin,
683 subject: [user],
684 action: "updated_users"
685 })
686
687 if params["password"] do
688 User.force_password_reset_async(user)
689 end
690
691 ModerationLog.insert_log(%{
692 actor: admin,
693 subject: [user],
694 action: "force_password_reset"
695 })
696
697 json(conn, %{status: "success"})
698 else
699 {:error, changeset} ->
700 {_, {error, _}} = Enum.at(changeset.errors, 0)
701 json(conn, %{error: "New password #{error}."})
702
703 _ ->
704 json(conn, %{error: "Unable to change password."})
705 end
706 end
707
708 def list_reports(conn, params) do
709 {page, page_size} = page_params(params)
710
711 reports = Utils.get_reports(params, page, page_size)
712
713 conn
714 |> put_view(ReportView)
715 |> render("index.json", %{reports: reports})
716 end
717
718 def list_grouped_reports(conn, _params) do
719 statuses = Utils.get_reported_activities()
720
721 conn
722 |> put_view(ReportView)
723 |> render("index_grouped.json", Utils.get_reports_grouped_by_status(statuses))
724 end
725
726 def report_show(conn, %{"id" => id}) do
727 with %Activity{} = report <- Activity.get_by_id(id) do
728 conn
729 |> put_view(ReportView)
730 |> render("show.json", Report.extract_report_info(report))
731 else
732 _ -> {:error, :not_found}
733 end
734 end
735
736 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
737 result =
738 reports
739 |> Enum.map(fn report ->
740 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
741 ModerationLog.insert_log(%{
742 action: "report_update",
743 actor: admin,
744 subject: activity
745 })
746
747 activity
748 else
749 {:error, message} -> %{id: report["id"], error: message}
750 end
751 end)
752
753 case Enum.any?(result, &Map.has_key?(&1, :error)) do
754 true -> json_response(conn, :bad_request, result)
755 false -> json_response(conn, :no_content, "")
756 end
757 end
758
759 def report_notes_create(%{assigns: %{user: user}} = conn, %{
760 "id" => report_id,
761 "content" => content
762 }) do
763 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
764 ModerationLog.insert_log(%{
765 action: "report_note",
766 actor: user,
767 subject: Activity.get_by_id(report_id),
768 text: content
769 })
770
771 json_response(conn, :no_content, "")
772 else
773 _ -> json_response(conn, :bad_request, "")
774 end
775 end
776
777 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
778 "id" => note_id,
779 "report_id" => report_id
780 }) do
781 with {:ok, note} <- ReportNote.destroy(note_id) do
782 ModerationLog.insert_log(%{
783 action: "report_note_delete",
784 actor: user,
785 subject: Activity.get_by_id(report_id),
786 text: note.content
787 })
788
789 json_response(conn, :no_content, "")
790 else
791 _ -> json_response(conn, :bad_request, "")
792 end
793 end
794
795 def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
796 godmode = params["godmode"] == "true" || params["godmode"] == true
797 local_only = params["local_only"] == "true" || params["local_only"] == true
798 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
799 {page, page_size} = page_params(params)
800
801 activities =
802 ActivityPub.fetch_statuses(nil, %{
803 "godmode" => godmode,
804 "local_only" => local_only,
805 "limit" => page_size,
806 "offset" => (page - 1) * page_size,
807 "exclude_reblogs" => !with_reblogs && "true"
808 })
809
810 conn
811 |> put_view(Pleroma.Web.AdminAPI.StatusView)
812 |> render("index.json", %{activities: activities, as: :activity})
813 end
814
815 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
816 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
817 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
818
819 ModerationLog.insert_log(%{
820 action: "status_update",
821 actor: admin,
822 subject: activity,
823 sensitive: sensitive,
824 visibility: params["visibility"]
825 })
826
827 conn
828 |> put_view(StatusView)
829 |> render("show.json", %{activity: activity})
830 end
831 end
832
833 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
834 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
835 ModerationLog.insert_log(%{
836 action: "status_delete",
837 actor: user,
838 subject_id: id
839 })
840
841 json(conn, %{})
842 end
843 end
844
845 def list_log(conn, params) do
846 {page, page_size} = page_params(params)
847
848 log =
849 ModerationLog.get_all(%{
850 page: page,
851 page_size: page_size,
852 start_date: params["start_date"],
853 end_date: params["end_date"],
854 user_id: params["user_id"],
855 search: params["search"]
856 })
857
858 conn
859 |> put_view(ModerationLogView)
860 |> render("index.json", %{log: log})
861 end
862
863 def config_descriptions(conn, _params) do
864 conn
865 |> Plug.Conn.put_resp_content_type("application/json")
866 |> Plug.Conn.send_resp(200, @descriptions_json)
867 end
868
869 def config_show(conn, %{"only_db" => true}) do
870 with :ok <- configurable_from_database(conn) do
871 configs = Pleroma.Repo.all(ConfigDB)
872
873 conn
874 |> put_view(ConfigView)
875 |> render("index.json", %{configs: configs})
876 end
877 end
878
879 def config_show(conn, _params) do
880 with :ok <- configurable_from_database(conn) do
881 configs = ConfigDB.get_all_as_keyword()
882
883 merged =
884 Config.Holder.default_config()
885 |> ConfigDB.merge(configs)
886 |> Enum.map(fn {group, values} ->
887 Enum.map(values, fn {key, value} ->
888 db =
889 if configs[group][key] do
890 ConfigDB.get_db_keys(configs[group][key], key)
891 end
892
893 db_value = configs[group][key]
894
895 merged_value =
896 if !is_nil(db_value) and Keyword.keyword?(db_value) and
897 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
898 ConfigDB.merge_group(group, key, value, db_value)
899 else
900 value
901 end
902
903 setting = %{
904 group: ConfigDB.convert(group),
905 key: ConfigDB.convert(key),
906 value: ConfigDB.convert(merged_value)
907 }
908
909 if db, do: Map.put(setting, :db, db), else: setting
910 end)
911 end)
912 |> List.flatten()
913
914 response = %{configs: merged}
915
916 response =
917 if Restarter.Pleroma.need_reboot?() do
918 Map.put(response, :need_reboot, true)
919 else
920 response
921 end
922
923 json(conn, response)
924 end
925 end
926
927 def config_update(conn, %{"configs" => configs}) do
928 with :ok <- configurable_from_database(conn) do
929 {_errors, results} =
930 Enum.map(configs, fn
931 %{"group" => group, "key" => key, "delete" => true} = params ->
932 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
933
934 %{"group" => group, "key" => key, "value" => value} ->
935 ConfigDB.update_or_create(%{group: group, key: key, value: value})
936 end)
937 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
938
939 {deleted, updated} =
940 results
941 |> Enum.map(fn {:ok, config} ->
942 Map.put(config, :db, ConfigDB.get_db_keys(config))
943 end)
944 |> Enum.split_with(fn config ->
945 Ecto.get_meta(config, :state) == :deleted
946 end)
947
948 Config.TransferTask.load_and_update_env(deleted, false)
949
950 need_reboot? =
951 Restarter.Pleroma.need_reboot?() ||
952 Enum.any?(updated, 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 response = %{configs: updated}
960
961 response =
962 if need_reboot? do
963 Restarter.Pleroma.need_reboot()
964 Map.put(response, :need_reboot, need_reboot?)
965 else
966 response
967 end
968
969 conn
970 |> put_view(ConfigView)
971 |> render("index.json", response)
972 end
973 end
974
975 def restart(conn, _params) do
976 with :ok <- configurable_from_database(conn) do
977 Restarter.Pleroma.restart(Config.get(:env), 50)
978
979 json(conn, %{})
980 end
981 end
982
983 defp configurable_from_database(conn) do
984 if Config.get(:configurable_from_database) do
985 :ok
986 else
987 errors(
988 conn,
989 {:error, "To use this endpoint you need to enable configuration from database."}
990 )
991 end
992 end
993
994 def reload_emoji(conn, _params) do
995 Pleroma.Emoji.reload()
996
997 conn |> json("ok")
998 end
999
1000 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1001 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1002
1003 User.toggle_confirmation(users)
1004
1005 ModerationLog.insert_log(%{
1006 actor: admin,
1007 subject: users,
1008 action: "confirm_email"
1009 })
1010
1011 conn |> json("")
1012 end
1013
1014 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1015 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1016
1017 User.try_send_confirmation_email(users)
1018
1019 ModerationLog.insert_log(%{
1020 actor: admin,
1021 subject: users,
1022 action: "resend_confirmation_email"
1023 })
1024
1025 conn |> json("")
1026 end
1027
1028 def stats(conn, _) do
1029 count = Stats.get_status_visibility_count()
1030
1031 conn
1032 |> json(%{"status_visibility" => count})
1033 end
1034
1035 def errors(conn, {:error, :not_found}) do
1036 conn
1037 |> put_status(:not_found)
1038 |> json(dgettext("errors", "Not found"))
1039 end
1040
1041 def errors(conn, {:error, reason}) do
1042 conn
1043 |> put_status(:bad_request)
1044 |> json(reason)
1045 end
1046
1047 def errors(conn, {:param_cast, _}) do
1048 conn
1049 |> put_status(:bad_request)
1050 |> json(dgettext("errors", "Invalid parameters"))
1051 end
1052
1053 def errors(conn, _) do
1054 conn
1055 |> put_status(:internal_server_error)
1056 |> json(dgettext("errors", "Something went wrong"))
1057 end
1058
1059 defp page_params(params) do
1060 {get_page(params["page"]), get_page_size(params["page_size"])}
1061 end
1062
1063 defp get_page(page_string) when is_nil(page_string), do: 1
1064
1065 defp get_page(page_string) do
1066 case Integer.parse(page_string) do
1067 {page, _} -> page
1068 :error -> 1
1069 end
1070 end
1071
1072 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1073
1074 defp get_page_size(page_size_string) do
1075 case Integer.parse(page_size_string) do
1076 {page_size, _} -> page_size
1077 :error -> @users_page_size
1078 end
1079 end
1080 end