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