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