Merge branch 'featureflag/emoji_reactions' 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 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 report_show(conn, %{"id" => id}) do
719 with %Activity{} = report <- Activity.get_by_id(id) do
720 conn
721 |> put_view(ReportView)
722 |> render("show.json", Report.extract_report_info(report))
723 else
724 _ -> {:error, :not_found}
725 end
726 end
727
728 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
729 result =
730 reports
731 |> Enum.map(fn report ->
732 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
733 ModerationLog.insert_log(%{
734 action: "report_update",
735 actor: admin,
736 subject: activity
737 })
738
739 activity
740 else
741 {:error, message} -> %{id: report["id"], error: message}
742 end
743 end)
744
745 case Enum.any?(result, &Map.has_key?(&1, :error)) do
746 true -> json_response(conn, :bad_request, result)
747 false -> json_response(conn, :no_content, "")
748 end
749 end
750
751 def report_notes_create(%{assigns: %{user: user}} = conn, %{
752 "id" => report_id,
753 "content" => content
754 }) do
755 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
756 ModerationLog.insert_log(%{
757 action: "report_note",
758 actor: user,
759 subject: Activity.get_by_id(report_id),
760 text: content
761 })
762
763 json_response(conn, :no_content, "")
764 else
765 _ -> json_response(conn, :bad_request, "")
766 end
767 end
768
769 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
770 "id" => note_id,
771 "report_id" => report_id
772 }) do
773 with {:ok, note} <- ReportNote.destroy(note_id) do
774 ModerationLog.insert_log(%{
775 action: "report_note_delete",
776 actor: user,
777 subject: Activity.get_by_id(report_id),
778 text: note.content
779 })
780
781 json_response(conn, :no_content, "")
782 else
783 _ -> json_response(conn, :bad_request, "")
784 end
785 end
786
787 def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
788 godmode = params["godmode"] == "true" || params["godmode"] == true
789 local_only = params["local_only"] == "true" || params["local_only"] == true
790 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
791 {page, page_size} = page_params(params)
792
793 activities =
794 ActivityPub.fetch_statuses(nil, %{
795 "godmode" => godmode,
796 "local_only" => local_only,
797 "limit" => page_size,
798 "offset" => (page - 1) * page_size,
799 "exclude_reblogs" => !with_reblogs && "true"
800 })
801
802 conn
803 |> put_view(Pleroma.Web.AdminAPI.StatusView)
804 |> render("index.json", %{activities: activities, as: :activity})
805 end
806
807 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
808 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
809 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
810
811 ModerationLog.insert_log(%{
812 action: "status_update",
813 actor: admin,
814 subject: activity,
815 sensitive: sensitive,
816 visibility: params["visibility"]
817 })
818
819 conn
820 |> put_view(StatusView)
821 |> render("show.json", %{activity: activity})
822 end
823 end
824
825 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
826 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
827 ModerationLog.insert_log(%{
828 action: "status_delete",
829 actor: user,
830 subject_id: id
831 })
832
833 json(conn, %{})
834 end
835 end
836
837 def list_log(conn, params) do
838 {page, page_size} = page_params(params)
839
840 log =
841 ModerationLog.get_all(%{
842 page: page,
843 page_size: page_size,
844 start_date: params["start_date"],
845 end_date: params["end_date"],
846 user_id: params["user_id"],
847 search: params["search"]
848 })
849
850 conn
851 |> put_view(ModerationLogView)
852 |> render("index.json", %{log: log})
853 end
854
855 def config_descriptions(conn, _params) do
856 conn
857 |> Plug.Conn.put_resp_content_type("application/json")
858 |> Plug.Conn.send_resp(200, @descriptions_json)
859 end
860
861 def config_show(conn, %{"only_db" => true}) do
862 with :ok <- configurable_from_database(conn) do
863 configs = Pleroma.Repo.all(ConfigDB)
864
865 conn
866 |> put_view(ConfigView)
867 |> render("index.json", %{configs: configs})
868 end
869 end
870
871 def config_show(conn, _params) do
872 with :ok <- configurable_from_database(conn) do
873 configs = ConfigDB.get_all_as_keyword()
874
875 merged =
876 Config.Holder.default_config()
877 |> ConfigDB.merge(configs)
878 |> Enum.map(fn {group, values} ->
879 Enum.map(values, fn {key, value} ->
880 db =
881 if configs[group][key] do
882 ConfigDB.get_db_keys(configs[group][key], key)
883 end
884
885 db_value = configs[group][key]
886
887 merged_value =
888 if !is_nil(db_value) and Keyword.keyword?(db_value) and
889 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
890 ConfigDB.merge_group(group, key, value, db_value)
891 else
892 value
893 end
894
895 setting = %{
896 group: ConfigDB.convert(group),
897 key: ConfigDB.convert(key),
898 value: ConfigDB.convert(merged_value)
899 }
900
901 if db, do: Map.put(setting, :db, db), else: setting
902 end)
903 end)
904 |> List.flatten()
905
906 response = %{configs: merged}
907
908 response =
909 if Restarter.Pleroma.need_reboot?() do
910 Map.put(response, :need_reboot, true)
911 else
912 response
913 end
914
915 json(conn, response)
916 end
917 end
918
919 def config_update(conn, %{"configs" => configs}) do
920 with :ok <- configurable_from_database(conn) do
921 {_errors, results} =
922 Enum.map(configs, fn
923 %{"group" => group, "key" => key, "delete" => true} = params ->
924 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
925
926 %{"group" => group, "key" => key, "value" => value} ->
927 ConfigDB.update_or_create(%{group: group, key: key, value: value})
928 end)
929 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
930
931 {deleted, updated} =
932 results
933 |> Enum.map(fn {:ok, config} ->
934 Map.put(config, :db, ConfigDB.get_db_keys(config))
935 end)
936 |> Enum.split_with(fn config ->
937 Ecto.get_meta(config, :state) == :deleted
938 end)
939
940 Config.TransferTask.load_and_update_env(deleted, false)
941
942 need_reboot? =
943 Restarter.Pleroma.need_reboot?() ||
944 Enum.any?(updated, fn config ->
945 group = ConfigDB.from_string(config.group)
946 key = ConfigDB.from_string(config.key)
947 value = ConfigDB.from_binary(config.value)
948 Config.TransferTask.pleroma_need_restart?(group, key, value)
949 end)
950
951 response = %{configs: updated}
952
953 response =
954 if need_reboot? do
955 Restarter.Pleroma.need_reboot()
956 Map.put(response, :need_reboot, need_reboot?)
957 else
958 response
959 end
960
961 conn
962 |> put_view(ConfigView)
963 |> render("index.json", response)
964 end
965 end
966
967 def restart(conn, _params) do
968 with :ok <- configurable_from_database(conn) do
969 Restarter.Pleroma.restart(Config.get(:env), 50)
970
971 json(conn, %{})
972 end
973 end
974
975 defp configurable_from_database(conn) do
976 if Config.get(:configurable_from_database) do
977 :ok
978 else
979 errors(
980 conn,
981 {:error, "To use this endpoint you need to enable configuration from database."}
982 )
983 end
984 end
985
986 def reload_emoji(conn, _params) do
987 Pleroma.Emoji.reload()
988
989 conn |> json("ok")
990 end
991
992 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
993 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
994
995 User.toggle_confirmation(users)
996
997 ModerationLog.insert_log(%{
998 actor: admin,
999 subject: users,
1000 action: "confirm_email"
1001 })
1002
1003 conn |> json("")
1004 end
1005
1006 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
1007 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
1008
1009 User.try_send_confirmation_email(users)
1010
1011 ModerationLog.insert_log(%{
1012 actor: admin,
1013 subject: users,
1014 action: "resend_confirmation_email"
1015 })
1016
1017 conn |> json("")
1018 end
1019
1020 def stats(conn, _) do
1021 count = Stats.get_status_visibility_count()
1022
1023 conn
1024 |> json(%{"status_visibility" => count})
1025 end
1026
1027 def errors(conn, {:error, :not_found}) do
1028 conn
1029 |> put_status(:not_found)
1030 |> json(dgettext("errors", "Not found"))
1031 end
1032
1033 def errors(conn, {:error, reason}) do
1034 conn
1035 |> put_status(:bad_request)
1036 |> json(reason)
1037 end
1038
1039 def errors(conn, {:param_cast, _}) do
1040 conn
1041 |> put_status(:bad_request)
1042 |> json(dgettext("errors", "Invalid parameters"))
1043 end
1044
1045 def errors(conn, _) do
1046 conn
1047 |> put_status(:internal_server_error)
1048 |> json(dgettext("errors", "Something went wrong"))
1049 end
1050
1051 defp page_params(params) do
1052 {get_page(params["page"]), get_page_size(params["page_size"])}
1053 end
1054
1055 defp get_page(page_string) when is_nil(page_string), do: 1
1056
1057 defp get_page(page_string) do
1058 case Integer.parse(page_string) do
1059 {page, _} -> page
1060 :error -> 1
1061 end
1062 end
1063
1064 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1065
1066 defp get_page_size(page_size_string) do
1067 case Integer.parse(page_size_string) do
1068 {page_size, _} -> page_size
1069 :error -> @users_page_size
1070 end
1071 end
1072 end