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