9cb63bc2ea5f1c5613452ce3cde03da899fe9f28
[akkoma] / lib / pleroma / web / admin_api / admin_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2019 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 {page, page_size} = page_params(params)
248
249 activities =
250 ActivityPub.fetch_statuses(nil, %{
251 "instance" => instance,
252 "limit" => page_size,
253 "offset" => (page - 1) * page_size
254 })
255
256 conn
257 |> put_view(Pleroma.Web.AdminAPI.StatusView)
258 |> render("index.json", %{activities: activities, as: :activity})
259 end
260
261 def list_user_statuses(conn, %{"nickname" => nickname} = params) do
262 godmode = params["godmode"] == "true" || params["godmode"] == true
263
264 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
265 {_, page_size} = page_params(params)
266
267 activities =
268 ActivityPub.fetch_user_activities(user, nil, %{
269 "limit" => page_size,
270 "godmode" => godmode
271 })
272
273 conn
274 |> put_view(StatusView)
275 |> render("index.json", %{activities: activities, as: :activity})
276 else
277 _ -> {:error, :not_found}
278 end
279 end
280
281 def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
282 user = User.get_cached_by_nickname(nickname)
283
284 {:ok, updated_user} = User.deactivate(user, !user.deactivated)
285
286 action = if user.deactivated, do: "activate", else: "deactivate"
287
288 ModerationLog.insert_log(%{
289 actor: admin,
290 subject: [user],
291 action: action
292 })
293
294 conn
295 |> put_view(AccountView)
296 |> render("show.json", %{user: updated_user})
297 end
298
299 def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
300 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
301 {:ok, updated_users} = User.deactivate(users, false)
302
303 ModerationLog.insert_log(%{
304 actor: admin,
305 subject: users,
306 action: "activate"
307 })
308
309 conn
310 |> put_view(AccountView)
311 |> render("index.json", %{users: Keyword.values(updated_users)})
312 end
313
314 def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
315 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
316 {:ok, updated_users} = User.deactivate(users, true)
317
318 ModerationLog.insert_log(%{
319 actor: admin,
320 subject: users,
321 action: "deactivate"
322 })
323
324 conn
325 |> put_view(AccountView)
326 |> render("index.json", %{users: Keyword.values(updated_users)})
327 end
328
329 def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
330 with {:ok, _} <- User.tag(nicknames, tags) do
331 ModerationLog.insert_log(%{
332 actor: admin,
333 nicknames: nicknames,
334 tags: tags,
335 action: "tag"
336 })
337
338 json_response(conn, :no_content, "")
339 end
340 end
341
342 def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
343 with {:ok, _} <- User.untag(nicknames, tags) do
344 ModerationLog.insert_log(%{
345 actor: admin,
346 nicknames: nicknames,
347 tags: tags,
348 action: "untag"
349 })
350
351 json_response(conn, :no_content, "")
352 end
353 end
354
355 def list_users(conn, params) do
356 {page, page_size} = page_params(params)
357 filters = maybe_parse_filters(params["filters"])
358
359 search_params = %{
360 query: params["query"],
361 page: page,
362 page_size: page_size,
363 tags: params["tags"],
364 name: params["name"],
365 email: params["email"]
366 }
367
368 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
369 {:ok, users, count} <- filter_service_users(users, count),
370 do:
371 conn
372 |> json(
373 AccountView.render("index.json",
374 users: users,
375 count: count,
376 page_size: page_size
377 )
378 )
379 end
380
381 defp filter_service_users(users, count) do
382 filtered_users = Enum.reject(users, &service_user?/1)
383 count = if Enum.any?(users, &service_user?/1), do: length(filtered_users), else: count
384
385 {:ok, filtered_users, count}
386 end
387
388 defp service_user?(user) do
389 String.match?(user.ap_id, ~r/.*\/relay$/) or
390 String.match?(user.ap_id, ~r/.*\/internal\/fetch$/)
391 end
392
393 @filters ~w(local external active deactivated is_admin is_moderator)
394
395 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
396 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
397
398 defp maybe_parse_filters(filters) do
399 filters
400 |> String.split(",")
401 |> Enum.filter(&Enum.member?(@filters, &1))
402 |> Enum.map(&String.to_atom(&1))
403 |> Enum.into(%{}, &{&1, true})
404 end
405
406 def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
407 "permission_group" => permission_group,
408 "nicknames" => nicknames
409 })
410 when permission_group in ["moderator", "admin"] do
411 update = %{:"is_#{permission_group}" => true}
412
413 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
414
415 for u <- users, do: User.admin_api_update(u, update)
416
417 ModerationLog.insert_log(%{
418 action: "grant",
419 actor: admin,
420 subject: users,
421 permission: permission_group
422 })
423
424 json(conn, update)
425 end
426
427 def right_add_multiple(conn, _) do
428 render_error(conn, :not_found, "No such permission_group")
429 end
430
431 def right_add(%{assigns: %{user: admin}} = conn, %{
432 "permission_group" => permission_group,
433 "nickname" => nickname
434 })
435 when permission_group in ["moderator", "admin"] do
436 fields = %{:"is_#{permission_group}" => true}
437
438 {:ok, user} =
439 nickname
440 |> User.get_cached_by_nickname()
441 |> User.admin_api_update(fields)
442
443 ModerationLog.insert_log(%{
444 action: "grant",
445 actor: admin,
446 subject: [user],
447 permission: permission_group
448 })
449
450 json(conn, fields)
451 end
452
453 def right_add(conn, _) do
454 render_error(conn, :not_found, "No such permission_group")
455 end
456
457 def right_get(conn, %{"nickname" => nickname}) do
458 user = User.get_cached_by_nickname(nickname)
459
460 conn
461 |> json(%{
462 is_moderator: user.is_moderator,
463 is_admin: user.is_admin
464 })
465 end
466
467 def right_delete_multiple(
468 %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
469 %{
470 "permission_group" => permission_group,
471 "nicknames" => nicknames
472 }
473 )
474 when permission_group in ["moderator", "admin"] do
475 with false <- Enum.member?(nicknames, admin_nickname) do
476 update = %{:"is_#{permission_group}" => false}
477
478 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
479
480 for u <- users, do: User.admin_api_update(u, update)
481
482 ModerationLog.insert_log(%{
483 action: "revoke",
484 actor: admin,
485 subject: users,
486 permission: permission_group
487 })
488
489 json(conn, update)
490 else
491 _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
492 end
493 end
494
495 def right_delete_multiple(conn, _) do
496 render_error(conn, :not_found, "No such permission_group")
497 end
498
499 def right_delete(
500 %{assigns: %{user: admin}} = conn,
501 %{
502 "permission_group" => permission_group,
503 "nickname" => nickname
504 }
505 )
506 when permission_group in ["moderator", "admin"] do
507 fields = %{:"is_#{permission_group}" => false}
508
509 {:ok, user} =
510 nickname
511 |> User.get_cached_by_nickname()
512 |> User.admin_api_update(fields)
513
514 ModerationLog.insert_log(%{
515 action: "revoke",
516 actor: admin,
517 subject: [user],
518 permission: permission_group
519 })
520
521 json(conn, fields)
522 end
523
524 def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
525 render_error(conn, :forbidden, "You can't revoke your own admin status.")
526 end
527
528 def relay_list(conn, _params) do
529 with {:ok, list} <- Relay.list() do
530 json(conn, %{relays: list})
531 else
532 _ ->
533 conn
534 |> put_status(500)
535 end
536 end
537
538 def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
539 with {:ok, _message} <- Relay.follow(target) do
540 ModerationLog.insert_log(%{
541 action: "relay_follow",
542 actor: admin,
543 target: target
544 })
545
546 json(conn, target)
547 else
548 _ ->
549 conn
550 |> put_status(500)
551 |> json(target)
552 end
553 end
554
555 def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
556 with {:ok, _message} <- Relay.unfollow(target) do
557 ModerationLog.insert_log(%{
558 action: "relay_unfollow",
559 actor: admin,
560 target: target
561 })
562
563 json(conn, target)
564 else
565 _ ->
566 conn
567 |> put_status(500)
568 |> json(target)
569 end
570 end
571
572 @doc "Sends registration invite via email"
573 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
574 with true <-
575 Config.get([:instance, :invites_enabled]) &&
576 !Config.get([:instance, :registrations_open]),
577 {:ok, invite_token} <- UserInviteToken.create_invite(),
578 email <-
579 Pleroma.Emails.UserEmail.user_invitation_email(
580 user,
581 invite_token,
582 email,
583 params["name"]
584 ),
585 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
586 json_response(conn, :no_content, "")
587 end
588 end
589
590 @doc "Create an account registration invite token"
591 def create_invite_token(conn, params) do
592 opts = %{}
593
594 opts =
595 if params["max_use"],
596 do: Map.put(opts, :max_use, params["max_use"]),
597 else: opts
598
599 opts =
600 if params["expires_at"],
601 do: Map.put(opts, :expires_at, params["expires_at"]),
602 else: opts
603
604 {:ok, invite} = UserInviteToken.create_invite(opts)
605
606 json(conn, AccountView.render("invite.json", %{invite: invite}))
607 end
608
609 @doc "Get list of created invites"
610 def invites(conn, _params) do
611 invites = UserInviteToken.list_invites()
612
613 conn
614 |> put_view(AccountView)
615 |> render("invites.json", %{invites: invites})
616 end
617
618 @doc "Revokes invite by token"
619 def revoke_invite(conn, %{"token" => token}) do
620 with {:ok, invite} <- UserInviteToken.find_by_token(token),
621 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
622 conn
623 |> put_view(AccountView)
624 |> render("invite.json", %{invite: updated_invite})
625 else
626 nil -> {:error, :not_found}
627 end
628 end
629
630 @doc "Get a password reset token (base64 string) for given nickname"
631 def get_password_reset(conn, %{"nickname" => nickname}) do
632 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
633 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
634
635 conn
636 |> json(%{
637 token: token.token,
638 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
639 })
640 end
641
642 @doc "Force password reset for a given user"
643 def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
644 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
645
646 Enum.each(users, &User.force_password_reset_async/1)
647
648 ModerationLog.insert_log(%{
649 actor: admin,
650 subject: users,
651 action: "force_password_reset"
652 })
653
654 json_response(conn, :no_content, "")
655 end
656
657 def list_reports(conn, params) do
658 {page, page_size} = page_params(params)
659
660 reports = Utils.get_reports(params, page, page_size)
661
662 conn
663 |> put_view(ReportView)
664 |> render("index.json", %{reports: reports})
665 end
666
667 def list_grouped_reports(conn, _params) do
668 statuses = Utils.get_reported_activities()
669
670 conn
671 |> put_view(ReportView)
672 |> render("index_grouped.json", Utils.get_reports_grouped_by_status(statuses))
673 end
674
675 def report_show(conn, %{"id" => id}) do
676 with %Activity{} = report <- Activity.get_by_id(id) do
677 conn
678 |> put_view(ReportView)
679 |> render("show.json", Report.extract_report_info(report))
680 else
681 _ -> {:error, :not_found}
682 end
683 end
684
685 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
686 result =
687 reports
688 |> Enum.map(fn report ->
689 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
690 ModerationLog.insert_log(%{
691 action: "report_update",
692 actor: admin,
693 subject: activity
694 })
695
696 activity
697 else
698 {:error, message} -> %{id: report["id"], error: message}
699 end
700 end)
701
702 case Enum.any?(result, &Map.has_key?(&1, :error)) do
703 true -> json_response(conn, :bad_request, result)
704 false -> json_response(conn, :no_content, "")
705 end
706 end
707
708 def report_notes_create(%{assigns: %{user: user}} = conn, %{
709 "id" => report_id,
710 "content" => content
711 }) do
712 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
713 ModerationLog.insert_log(%{
714 action: "report_note",
715 actor: user,
716 subject: Activity.get_by_id(report_id),
717 text: content
718 })
719
720 json_response(conn, :no_content, "")
721 else
722 _ -> json_response(conn, :bad_request, "")
723 end
724 end
725
726 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
727 "id" => note_id,
728 "report_id" => report_id
729 }) do
730 with {:ok, note} <- ReportNote.destroy(note_id) do
731 ModerationLog.insert_log(%{
732 action: "report_note_delete",
733 actor: user,
734 subject: Activity.get_by_id(report_id),
735 text: note.content
736 })
737
738 json_response(conn, :no_content, "")
739 else
740 _ -> json_response(conn, :bad_request, "")
741 end
742 end
743
744 def list_statuses(%{assigns: %{user: admin}} = conn, params) do
745 godmode = params["godmode"] == "true" || params["godmode"] == true
746 local_only = params["local_only"] == "true" || params["local_only"] == true
747 {page, page_size} = page_params(params)
748
749 activities =
750 ActivityPub.fetch_statuses(admin, %{
751 "godmode" => godmode,
752 "local_only" => local_only,
753 "limit" => page_size,
754 "offset" => (page - 1) * page_size
755 })
756
757 conn
758 |> put_view(Pleroma.Web.AdminAPI.StatusView)
759 |> render("index.json", %{activities: activities, as: :activity})
760 end
761
762 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
763 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
764 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
765
766 ModerationLog.insert_log(%{
767 action: "status_update",
768 actor: admin,
769 subject: activity,
770 sensitive: sensitive,
771 visibility: params["visibility"]
772 })
773
774 conn
775 |> put_view(StatusView)
776 |> render("show.json", %{activity: activity})
777 end
778 end
779
780 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
781 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
782 ModerationLog.insert_log(%{
783 action: "status_delete",
784 actor: user,
785 subject_id: id
786 })
787
788 json(conn, %{})
789 end
790 end
791
792 def list_log(conn, params) do
793 {page, page_size} = page_params(params)
794
795 log =
796 ModerationLog.get_all(%{
797 page: page,
798 page_size: page_size,
799 start_date: params["start_date"],
800 end_date: params["end_date"],
801 user_id: params["user_id"],
802 search: params["search"]
803 })
804
805 conn
806 |> put_view(ModerationLogView)
807 |> render("index.json", %{log: log})
808 end
809
810 def config_descriptions(conn, _params) do
811 conn
812 |> Plug.Conn.put_resp_content_type("application/json")
813 |> Plug.Conn.send_resp(200, @descriptions_json)
814 end
815
816 def config_show(conn, %{"only_db" => true}) do
817 with :ok <- configurable_from_database(conn) do
818 configs = Pleroma.Repo.all(ConfigDB)
819
820 conn
821 |> put_view(ConfigView)
822 |> render("index.json", %{configs: configs})
823 end
824 end
825
826 def config_show(conn, _params) do
827 with :ok <- configurable_from_database(conn) do
828 configs = ConfigDB.get_all_as_keyword()
829
830 merged =
831 Config.Holder.config()
832 |> ConfigDB.merge(configs)
833 |> Enum.map(fn {group, values} ->
834 Enum.map(values, fn {key, value} ->
835 db =
836 if configs[group][key] do
837 ConfigDB.get_db_keys(configs[group][key], key)
838 end
839
840 db_value = configs[group][key]
841
842 merged_value =
843 if !is_nil(db_value) and Keyword.keyword?(db_value) and
844 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
845 ConfigDB.merge_group(group, key, value, db_value)
846 else
847 value
848 end
849
850 setting = %{
851 group: ConfigDB.convert(group),
852 key: ConfigDB.convert(key),
853 value: ConfigDB.convert(merged_value)
854 }
855
856 if db, do: Map.put(setting, :db, db), else: setting
857 end)
858 end)
859 |> List.flatten()
860
861 response = %{configs: merged}
862
863 response =
864 if Restarter.Pleroma.need_reboot?() do
865 Map.put(response, :need_reboot, true)
866 else
867 response
868 end
869
870 json(conn, response)
871 end
872 end
873
874 def config_update(conn, %{"configs" => configs}) do
875 with :ok <- configurable_from_database(conn) do
876 {_errors, results} =
877 Enum.map(configs, fn
878 %{"group" => group, "key" => key, "delete" => true} = params ->
879 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
880
881 %{"group" => group, "key" => key, "value" => value} ->
882 ConfigDB.update_or_create(%{group: group, key: key, value: value})
883 end)
884 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
885
886 {deleted, updated} =
887 results
888 |> Enum.map(fn {:ok, config} ->
889 Map.put(config, :db, ConfigDB.get_db_keys(config))
890 end)
891 |> Enum.split_with(fn config ->
892 Ecto.get_meta(config, :state) == :deleted
893 end)
894
895 Config.TransferTask.load_and_update_env(deleted, false)
896
897 need_reboot? =
898 Restarter.Pleroma.need_reboot?() ||
899 Enum.any?(updated, fn config ->
900 group = ConfigDB.from_string(config.group)
901 key = ConfigDB.from_string(config.key)
902 value = ConfigDB.from_binary(config.value)
903 Config.TransferTask.pleroma_need_restart?(group, key, value)
904 end)
905
906 response = %{configs: updated}
907
908 response =
909 if need_reboot? do
910 Restarter.Pleroma.need_reboot()
911 Map.put(response, :need_reboot, need_reboot?)
912 else
913 response
914 end
915
916 conn
917 |> put_view(ConfigView)
918 |> render("index.json", response)
919 end
920 end
921
922 def restart(conn, _params) do
923 with :ok <- configurable_from_database(conn) do
924 Restarter.Pleroma.restart(Config.get(:env), 50)
925
926 json(conn, %{})
927 end
928 end
929
930 defp configurable_from_database(conn) do
931 if Config.get(:configurable_from_database) do
932 :ok
933 else
934 errors(
935 conn,
936 {:error, "To use this endpoint you need to enable configuration from database."}
937 )
938 end
939 end
940
941 def reload_emoji(conn, _params) do
942 Pleroma.Emoji.reload()
943
944 conn |> json("ok")
945 end
946
947 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
948 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
949
950 User.toggle_confirmation(users)
951
952 ModerationLog.insert_log(%{
953 actor: admin,
954 subject: users,
955 action: "confirm_email"
956 })
957
958 conn |> json("")
959 end
960
961 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
962 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
963
964 User.try_send_confirmation_email(users)
965
966 ModerationLog.insert_log(%{
967 actor: admin,
968 subject: users,
969 action: "resend_confirmation_email"
970 })
971
972 conn |> json("")
973 end
974
975 def stats(conn, _) do
976 count = Stats.get_status_visibility_count()
977
978 conn
979 |> json(%{"status_visibility" => count})
980 end
981
982 def errors(conn, {:error, :not_found}) do
983 conn
984 |> put_status(:not_found)
985 |> json(dgettext("errors", "Not found"))
986 end
987
988 def errors(conn, {:error, reason}) do
989 conn
990 |> put_status(:bad_request)
991 |> json(reason)
992 end
993
994 def errors(conn, {:param_cast, _}) do
995 conn
996 |> put_status(:bad_request)
997 |> json(dgettext("errors", "Invalid parameters"))
998 end
999
1000 def errors(conn, _) do
1001 conn
1002 |> put_status(:internal_server_error)
1003 |> json(dgettext("errors", "Something went wrong"))
1004 end
1005
1006 defp page_params(params) do
1007 {get_page(params["page"]), get_page_size(params["page_size"])}
1008 end
1009
1010 defp get_page(page_string) when is_nil(page_string), do: 1
1011
1012 defp get_page(page_string) do
1013 case Integer.parse(page_string) do
1014 {page, _} -> page
1015 :error -> 1
1016 end
1017 end
1018
1019 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1020
1021 defp get_page_size(page_size_string) do
1022 case Integer.parse(page_size_string) do
1023 {page_size, _} -> page_size
1024 :error -> @users_page_size
1025 end
1026 end
1027 end