Merge branch 'exclude-posts-visible-to-admin' into 'develop'
[akkoma] / lib / pleroma / web / admin_api / admin_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper, only: [json_response: 3]
9
10 alias Pleroma.Activity
11 alias Pleroma.Config
12 alias Pleroma.ConfigDB
13 alias Pleroma.ModerationLog
14 alias Pleroma.Plugs.OAuthScopesPlug
15 alias Pleroma.ReportNote
16 alias Pleroma.Stats
17 alias Pleroma.User
18 alias Pleroma.UserInviteToken
19 alias Pleroma.Web.ActivityPub.ActivityPub
20 alias Pleroma.Web.ActivityPub.Relay
21 alias Pleroma.Web.ActivityPub.Utils
22 alias Pleroma.Web.AdminAPI.AccountView
23 alias Pleroma.Web.AdminAPI.ConfigView
24 alias Pleroma.Web.AdminAPI.ModerationLogView
25 alias Pleroma.Web.AdminAPI.Report
26 alias Pleroma.Web.AdminAPI.ReportView
27 alias Pleroma.Web.AdminAPI.Search
28 alias Pleroma.Web.CommonAPI
29 alias Pleroma.Web.Endpoint
30 alias Pleroma.Web.MastodonAPI.StatusView
31 alias Pleroma.Web.Router
32
33 require Logger
34
35 @descriptions_json Pleroma.Docs.JSON.compile()
36 @users_page_size 50
37
38 plug(
39 OAuthScopesPlug,
40 %{scopes: ["read:accounts"], admin: true}
41 when action in [:list_users, :user_show, :right_get]
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 def list_reports(conn, params) do
662 {page, page_size} = page_params(params)
663
664 reports = Utils.get_reports(params, page, page_size)
665
666 conn
667 |> put_view(ReportView)
668 |> render("index.json", %{reports: reports})
669 end
670
671 def list_grouped_reports(conn, _params) do
672 statuses = Utils.get_reported_activities()
673
674 conn
675 |> put_view(ReportView)
676 |> render("index_grouped.json", Utils.get_reports_grouped_by_status(statuses))
677 end
678
679 def report_show(conn, %{"id" => id}) do
680 with %Activity{} = report <- Activity.get_by_id(id) do
681 conn
682 |> put_view(ReportView)
683 |> render("show.json", Report.extract_report_info(report))
684 else
685 _ -> {:error, :not_found}
686 end
687 end
688
689 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
690 result =
691 reports
692 |> Enum.map(fn report ->
693 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
694 ModerationLog.insert_log(%{
695 action: "report_update",
696 actor: admin,
697 subject: activity
698 })
699
700 activity
701 else
702 {:error, message} -> %{id: report["id"], error: message}
703 end
704 end)
705
706 case Enum.any?(result, &Map.has_key?(&1, :error)) do
707 true -> json_response(conn, :bad_request, result)
708 false -> json_response(conn, :no_content, "")
709 end
710 end
711
712 def report_notes_create(%{assigns: %{user: user}} = conn, %{
713 "id" => report_id,
714 "content" => content
715 }) do
716 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
717 ModerationLog.insert_log(%{
718 action: "report_note",
719 actor: user,
720 subject: Activity.get_by_id(report_id),
721 text: content
722 })
723
724 json_response(conn, :no_content, "")
725 else
726 _ -> json_response(conn, :bad_request, "")
727 end
728 end
729
730 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
731 "id" => note_id,
732 "report_id" => report_id
733 }) do
734 with {:ok, note} <- ReportNote.destroy(note_id) do
735 ModerationLog.insert_log(%{
736 action: "report_note_delete",
737 actor: user,
738 subject: Activity.get_by_id(report_id),
739 text: note.content
740 })
741
742 json_response(conn, :no_content, "")
743 else
744 _ -> json_response(conn, :bad_request, "")
745 end
746 end
747
748 def list_statuses(%{assigns: %{user: _admin}} = conn, params) do
749 godmode = params["godmode"] == "true" || params["godmode"] == true
750 local_only = params["local_only"] == "true" || params["local_only"] == true
751 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
752 {page, page_size} = page_params(params)
753
754 activities =
755 ActivityPub.fetch_statuses(nil, %{
756 "godmode" => godmode,
757 "local_only" => local_only,
758 "limit" => page_size,
759 "offset" => (page - 1) * page_size,
760 "exclude_reblogs" => !with_reblogs && "true"
761 })
762
763 conn
764 |> put_view(Pleroma.Web.AdminAPI.StatusView)
765 |> render("index.json", %{activities: activities, as: :activity})
766 end
767
768 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
769 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
770 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
771
772 ModerationLog.insert_log(%{
773 action: "status_update",
774 actor: admin,
775 subject: activity,
776 sensitive: sensitive,
777 visibility: params["visibility"]
778 })
779
780 conn
781 |> put_view(StatusView)
782 |> render("show.json", %{activity: activity})
783 end
784 end
785
786 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
787 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
788 ModerationLog.insert_log(%{
789 action: "status_delete",
790 actor: user,
791 subject_id: id
792 })
793
794 json(conn, %{})
795 end
796 end
797
798 def list_log(conn, params) do
799 {page, page_size} = page_params(params)
800
801 log =
802 ModerationLog.get_all(%{
803 page: page,
804 page_size: page_size,
805 start_date: params["start_date"],
806 end_date: params["end_date"],
807 user_id: params["user_id"],
808 search: params["search"]
809 })
810
811 conn
812 |> put_view(ModerationLogView)
813 |> render("index.json", %{log: log})
814 end
815
816 def config_descriptions(conn, _params) do
817 conn
818 |> Plug.Conn.put_resp_content_type("application/json")
819 |> Plug.Conn.send_resp(200, @descriptions_json)
820 end
821
822 def config_show(conn, %{"only_db" => true}) do
823 with :ok <- configurable_from_database(conn) do
824 configs = Pleroma.Repo.all(ConfigDB)
825
826 conn
827 |> put_view(ConfigView)
828 |> render("index.json", %{configs: configs})
829 end
830 end
831
832 def config_show(conn, _params) do
833 with :ok <- configurable_from_database(conn) do
834 configs = ConfigDB.get_all_as_keyword()
835
836 merged =
837 Config.Holder.default_config()
838 |> ConfigDB.merge(configs)
839 |> Enum.map(fn {group, values} ->
840 Enum.map(values, fn {key, value} ->
841 db =
842 if configs[group][key] do
843 ConfigDB.get_db_keys(configs[group][key], key)
844 end
845
846 db_value = configs[group][key]
847
848 merged_value =
849 if !is_nil(db_value) and Keyword.keyword?(db_value) and
850 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
851 ConfigDB.merge_group(group, key, value, db_value)
852 else
853 value
854 end
855
856 setting = %{
857 group: ConfigDB.convert(group),
858 key: ConfigDB.convert(key),
859 value: ConfigDB.convert(merged_value)
860 }
861
862 if db, do: Map.put(setting, :db, db), else: setting
863 end)
864 end)
865 |> List.flatten()
866
867 response = %{configs: merged}
868
869 response =
870 if Restarter.Pleroma.need_reboot?() do
871 Map.put(response, :need_reboot, true)
872 else
873 response
874 end
875
876 json(conn, response)
877 end
878 end
879
880 def config_update(conn, %{"configs" => configs}) do
881 with :ok <- configurable_from_database(conn) do
882 {_errors, results} =
883 Enum.map(configs, fn
884 %{"group" => group, "key" => key, "delete" => true} = params ->
885 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
886
887 %{"group" => group, "key" => key, "value" => value} ->
888 ConfigDB.update_or_create(%{group: group, key: key, value: value})
889 end)
890 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
891
892 {deleted, updated} =
893 results
894 |> Enum.map(fn {:ok, config} ->
895 Map.put(config, :db, ConfigDB.get_db_keys(config))
896 end)
897 |> Enum.split_with(fn config ->
898 Ecto.get_meta(config, :state) == :deleted
899 end)
900
901 Config.TransferTask.load_and_update_env(deleted, false)
902
903 need_reboot? =
904 Restarter.Pleroma.need_reboot?() ||
905 Enum.any?(updated, fn config ->
906 group = ConfigDB.from_string(config.group)
907 key = ConfigDB.from_string(config.key)
908 value = ConfigDB.from_binary(config.value)
909 Config.TransferTask.pleroma_need_restart?(group, key, value)
910 end)
911
912 response = %{configs: updated}
913
914 response =
915 if need_reboot? do
916 Restarter.Pleroma.need_reboot()
917 Map.put(response, :need_reboot, need_reboot?)
918 else
919 response
920 end
921
922 conn
923 |> put_view(ConfigView)
924 |> render("index.json", response)
925 end
926 end
927
928 def restart(conn, _params) do
929 with :ok <- configurable_from_database(conn) do
930 Restarter.Pleroma.restart(Config.get(:env), 50)
931
932 json(conn, %{})
933 end
934 end
935
936 defp configurable_from_database(conn) do
937 if Config.get(:configurable_from_database) do
938 :ok
939 else
940 errors(
941 conn,
942 {:error, "To use this endpoint you need to enable configuration from database."}
943 )
944 end
945 end
946
947 def reload_emoji(conn, _params) do
948 Pleroma.Emoji.reload()
949
950 conn |> json("ok")
951 end
952
953 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
954 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
955
956 User.toggle_confirmation(users)
957
958 ModerationLog.insert_log(%{
959 actor: admin,
960 subject: users,
961 action: "confirm_email"
962 })
963
964 conn |> json("")
965 end
966
967 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
968 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
969
970 User.try_send_confirmation_email(users)
971
972 ModerationLog.insert_log(%{
973 actor: admin,
974 subject: users,
975 action: "resend_confirmation_email"
976 })
977
978 conn |> json("")
979 end
980
981 def stats(conn, _) do
982 count = Stats.get_status_visibility_count()
983
984 conn
985 |> json(%{"status_visibility" => count})
986 end
987
988 def errors(conn, {:error, :not_found}) do
989 conn
990 |> put_status(:not_found)
991 |> json(dgettext("errors", "Not found"))
992 end
993
994 def errors(conn, {:error, reason}) do
995 conn
996 |> put_status(:bad_request)
997 |> json(reason)
998 end
999
1000 def errors(conn, {:param_cast, _}) do
1001 conn
1002 |> put_status(:bad_request)
1003 |> json(dgettext("errors", "Invalid parameters"))
1004 end
1005
1006 def errors(conn, _) do
1007 conn
1008 |> put_status(:internal_server_error)
1009 |> json(dgettext("errors", "Something went wrong"))
1010 end
1011
1012 defp page_params(params) do
1013 {get_page(params["page"]), get_page_size(params["page_size"])}
1014 end
1015
1016 defp get_page(page_string) when is_nil(page_string), do: 1
1017
1018 defp get_page(page_string) do
1019 case Integer.parse(page_string) do
1020 {page, _} -> page
1021 :error -> 1
1022 end
1023 end
1024
1025 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1026
1027 defp get_page_size(page_size_string) do
1028 case Integer.parse(page_size_string) do
1029 {page_size, _} -> page_size
1030 :error -> @users_page_size
1031 end
1032 end
1033 end