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