Add OpenAPI spec for AdminAPI.OAuthAppContoller
[akkoma] / lib / pleroma / web / admin_api / controllers / admin_api_controller.ex
1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
4
5 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
6 use Pleroma.Web, :controller
7
8 import Pleroma.Web.ControllerHelper, only: [json_response: 3]
9
10 alias Pleroma.Activity
11 alias Pleroma.Config
12 alias Pleroma.ConfigDB
13 alias Pleroma.MFA
14 alias Pleroma.ModerationLog
15 alias Pleroma.Plugs.OAuthScopesPlug
16 alias Pleroma.ReportNote
17 alias Pleroma.Stats
18 alias Pleroma.User
19 alias Pleroma.UserInviteToken
20 alias Pleroma.Web.ActivityPub.ActivityPub
21 alias Pleroma.Web.ActivityPub.Builder
22 alias Pleroma.Web.ActivityPub.Pipeline
23 alias Pleroma.Web.ActivityPub.Relay
24 alias Pleroma.Web.ActivityPub.Utils
25 alias Pleroma.Web.AdminAPI
26 alias Pleroma.Web.AdminAPI.AccountView
27 alias Pleroma.Web.AdminAPI.ConfigView
28 alias Pleroma.Web.AdminAPI.ModerationLogView
29 alias Pleroma.Web.AdminAPI.Report
30 alias Pleroma.Web.AdminAPI.ReportView
31 alias Pleroma.Web.AdminAPI.Search
32 alias Pleroma.Web.CommonAPI
33 alias Pleroma.Web.Endpoint
34 alias Pleroma.Web.MastodonAPI
35 alias Pleroma.Web.Router
36
37 require Logger
38
39 @descriptions Pleroma.Docs.JSON.compile()
40 @users_page_size 50
41
42 plug(
43 OAuthScopesPlug,
44 %{scopes: ["read:accounts"], admin: true}
45 when action in [:list_users, :user_show, :right_get, :show_user_credentials]
46 )
47
48 plug(
49 OAuthScopesPlug,
50 %{scopes: ["write:accounts"], admin: true}
51 when action in [
52 :get_password_reset,
53 :force_password_reset,
54 :user_delete,
55 :users_create,
56 :user_toggle_activation,
57 :user_activate,
58 :user_deactivate,
59 :tag_users,
60 :untag_users,
61 :right_add,
62 :right_add_multiple,
63 :right_delete,
64 :disable_mfa,
65 :right_delete_multiple,
66 :update_user_credentials
67 ]
68 )
69
70 plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites)
71
72 plug(
73 OAuthScopesPlug,
74 %{scopes: ["write:invites"], admin: true}
75 when action in [:create_invite_token, :revoke_invite, :email_invite]
76 )
77
78 plug(
79 OAuthScopesPlug,
80 %{scopes: ["write:follows"], admin: true}
81 when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
82 )
83
84 plug(
85 OAuthScopesPlug,
86 %{scopes: ["read:reports"], admin: true}
87 when action in [:list_reports, :report_show]
88 )
89
90 plug(
91 OAuthScopesPlug,
92 %{scopes: ["write:reports"], admin: true}
93 when action in [:reports_update, :report_notes_create, :report_notes_delete]
94 )
95
96 plug(
97 OAuthScopesPlug,
98 %{scopes: ["read:statuses"], admin: true}
99 when action in [:list_user_statuses, :list_instance_statuses]
100 )
101
102 plug(
103 OAuthScopesPlug,
104 %{scopes: ["read"], admin: true}
105 when action in [
106 :config_show,
107 :list_log,
108 :stats,
109 :relay_list,
110 :config_descriptions,
111 :need_reboot
112 ]
113 )
114
115 plug(
116 OAuthScopesPlug,
117 %{scopes: ["write"], admin: true}
118 when action in [
119 :restart,
120 :config_update,
121 :resend_confirmation_email,
122 :confirm_email,
123 :reload_emoji
124 ]
125 )
126
127 action_fallback(AdminAPI.FallbackController)
128
129 def user_delete(conn, %{"nickname" => nickname}) do
130 user_delete(conn, %{"nicknames" => [nickname]})
131 end
132
133 def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
134 users =
135 nicknames
136 |> Enum.map(&User.get_cached_by_nickname/1)
137
138 users
139 |> Enum.each(fn user ->
140 {:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
141 Pipeline.common_pipeline(delete_data, local: true)
142 end)
143
144 ModerationLog.insert_log(%{
145 actor: admin,
146 subject: users,
147 action: "delete"
148 })
149
150 conn
151 |> json(nicknames)
152 end
153
154 def user_follow(%{assigns: %{user: admin}} = conn, %{
155 "follower" => follower_nick,
156 "followed" => followed_nick
157 }) do
158 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
159 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
160 User.follow(follower, followed)
161
162 ModerationLog.insert_log(%{
163 actor: admin,
164 followed: followed,
165 follower: follower,
166 action: "follow"
167 })
168 end
169
170 conn
171 |> json("ok")
172 end
173
174 def user_unfollow(%{assigns: %{user: admin}} = conn, %{
175 "follower" => follower_nick,
176 "followed" => followed_nick
177 }) do
178 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
179 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
180 User.unfollow(follower, followed)
181
182 ModerationLog.insert_log(%{
183 actor: admin,
184 followed: followed,
185 follower: follower,
186 action: "unfollow"
187 })
188 end
189
190 conn
191 |> json("ok")
192 end
193
194 def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
195 changesets =
196 Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
197 user_data = %{
198 nickname: nickname,
199 name: nickname,
200 email: email,
201 password: password,
202 password_confirmation: password,
203 bio: "."
204 }
205
206 User.register_changeset(%User{}, user_data, need_confirmation: false)
207 end)
208 |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
209 Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
210 end)
211
212 case Pleroma.Repo.transaction(changesets) do
213 {:ok, users} ->
214 res =
215 users
216 |> Map.values()
217 |> Enum.map(fn user ->
218 {:ok, user} = User.post_register_action(user)
219
220 user
221 end)
222 |> Enum.map(&AccountView.render("created.json", %{user: &1}))
223
224 ModerationLog.insert_log(%{
225 actor: admin,
226 subjects: Map.values(users),
227 action: "create"
228 })
229
230 conn
231 |> json(res)
232
233 {:error, id, changeset, _} ->
234 res =
235 Enum.map(changesets.operations, fn
236 {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
237 AccountView.render("create-error.json", %{changeset: changeset})
238
239 {_, {:changeset, current_changeset, _}} ->
240 AccountView.render("create-error.json", %{changeset: current_changeset})
241 end)
242
243 conn
244 |> put_status(:conflict)
245 |> json(res)
246 end
247 end
248
249 def user_show(conn, %{"nickname" => nickname}) do
250 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
251 conn
252 |> put_view(AccountView)
253 |> render("show.json", %{user: user})
254 else
255 _ -> {:error, :not_found}
256 end
257 end
258
259 def list_instance_statuses(conn, %{"instance" => instance} = params) do
260 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
261 {page, page_size} = page_params(params)
262
263 activities =
264 ActivityPub.fetch_statuses(nil, %{
265 "instance" => instance,
266 "limit" => page_size,
267 "offset" => (page - 1) * page_size,
268 "exclude_reblogs" => !with_reblogs && "true"
269 })
270
271 conn
272 |> put_view(AdminAPI.StatusView)
273 |> render("index.json", %{activities: activities, as: :activity})
274 end
275
276 def list_user_statuses(conn, %{"nickname" => nickname} = params) do
277 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
278 godmode = params["godmode"] == "true" || params["godmode"] == true
279
280 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
281 {_, page_size} = page_params(params)
282
283 activities =
284 ActivityPub.fetch_user_activities(user, nil, %{
285 "limit" => page_size,
286 "godmode" => godmode,
287 "exclude_reblogs" => !with_reblogs && "true"
288 })
289
290 conn
291 |> put_view(MastodonAPI.StatusView)
292 |> render("index.json", %{activities: activities, as: :activity})
293 else
294 _ -> {:error, :not_found}
295 end
296 end
297
298 def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
299 user = User.get_cached_by_nickname(nickname)
300
301 {:ok, updated_user} = User.deactivate(user, !user.deactivated)
302
303 action = if user.deactivated, do: "activate", else: "deactivate"
304
305 ModerationLog.insert_log(%{
306 actor: admin,
307 subject: [user],
308 action: action
309 })
310
311 conn
312 |> put_view(AccountView)
313 |> render("show.json", %{user: updated_user})
314 end
315
316 def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
317 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
318 {:ok, updated_users} = User.deactivate(users, false)
319
320 ModerationLog.insert_log(%{
321 actor: admin,
322 subject: users,
323 action: "activate"
324 })
325
326 conn
327 |> put_view(AccountView)
328 |> render("index.json", %{users: Keyword.values(updated_users)})
329 end
330
331 def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
332 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
333 {:ok, updated_users} = User.deactivate(users, true)
334
335 ModerationLog.insert_log(%{
336 actor: admin,
337 subject: users,
338 action: "deactivate"
339 })
340
341 conn
342 |> put_view(AccountView)
343 |> render("index.json", %{users: Keyword.values(updated_users)})
344 end
345
346 def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
347 with {:ok, _} <- User.tag(nicknames, tags) do
348 ModerationLog.insert_log(%{
349 actor: admin,
350 nicknames: nicknames,
351 tags: tags,
352 action: "tag"
353 })
354
355 json_response(conn, :no_content, "")
356 end
357 end
358
359 def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
360 with {:ok, _} <- User.untag(nicknames, tags) do
361 ModerationLog.insert_log(%{
362 actor: admin,
363 nicknames: nicknames,
364 tags: tags,
365 action: "untag"
366 })
367
368 json_response(conn, :no_content, "")
369 end
370 end
371
372 def list_users(conn, params) do
373 {page, page_size} = page_params(params)
374 filters = maybe_parse_filters(params["filters"])
375
376 search_params = %{
377 query: params["query"],
378 page: page,
379 page_size: page_size,
380 tags: params["tags"],
381 name: params["name"],
382 email: params["email"]
383 }
384
385 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
386 json(
387 conn,
388 AccountView.render("index.json", users: users, count: count, page_size: page_size)
389 )
390 end
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 {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
575 {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
576 {:ok, invite_token} <- UserInviteToken.create_invite(),
577 email <-
578 Pleroma.Emails.UserEmail.user_invitation_email(
579 user,
580 invite_token,
581 email,
582 params["name"]
583 ),
584 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
585 json_response(conn, :no_content, "")
586 else
587 {:registrations_open, _} ->
588 {:error, "To send invites you need to set the `registrations_open` option to false."}
589
590 {:invites_enabled, _} ->
591 {:error, "To send invites you need to set the `invites_enabled` option to true."}
592 end
593 end
594
595 @doc "Create an account registration invite token"
596 def create_invite_token(conn, params) do
597 opts = %{}
598
599 opts =
600 if params["max_use"],
601 do: Map.put(opts, :max_use, params["max_use"]),
602 else: opts
603
604 opts =
605 if params["expires_at"],
606 do: Map.put(opts, :expires_at, params["expires_at"]),
607 else: opts
608
609 {:ok, invite} = UserInviteToken.create_invite(opts)
610
611 json(conn, AccountView.render("invite.json", %{invite: invite}))
612 end
613
614 @doc "Get list of created invites"
615 def invites(conn, _params) do
616 invites = UserInviteToken.list_invites()
617
618 conn
619 |> put_view(AccountView)
620 |> render("invites.json", %{invites: invites})
621 end
622
623 @doc "Revokes invite by token"
624 def revoke_invite(conn, %{"token" => token}) do
625 with {:ok, invite} <- UserInviteToken.find_by_token(token),
626 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
627 conn
628 |> put_view(AccountView)
629 |> render("invite.json", %{invite: updated_invite})
630 else
631 nil -> {:error, :not_found}
632 end
633 end
634
635 @doc "Get a password reset token (base64 string) for given nickname"
636 def get_password_reset(conn, %{"nickname" => nickname}) do
637 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
638 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
639
640 conn
641 |> json(%{
642 token: token.token,
643 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
644 })
645 end
646
647 @doc "Force password reset for a given user"
648 def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
649 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
650
651 Enum.each(users, &User.force_password_reset_async/1)
652
653 ModerationLog.insert_log(%{
654 actor: admin,
655 subject: users,
656 action: "force_password_reset"
657 })
658
659 json_response(conn, :no_content, "")
660 end
661
662 @doc "Disable mfa for user's account."
663 def disable_mfa(conn, %{"nickname" => nickname}) do
664 case User.get_by_nickname(nickname) do
665 %User{} = user ->
666 MFA.disable(user)
667 json(conn, nickname)
668
669 _ ->
670 {:error, :not_found}
671 end
672 end
673
674 @doc "Show a given user's credentials"
675 def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
676 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
677 conn
678 |> put_view(AccountView)
679 |> render("credentials.json", %{user: user, for: admin})
680 else
681 _ -> {:error, :not_found}
682 end
683 end
684
685 @doc "Updates a given user"
686 def update_user_credentials(
687 %{assigns: %{user: admin}} = conn,
688 %{"nickname" => nickname} = params
689 ) do
690 with {_, user} <- {:user, User.get_cached_by_nickname(nickname)},
691 {:ok, _user} <-
692 User.update_as_admin(user, params) do
693 ModerationLog.insert_log(%{
694 actor: admin,
695 subject: [user],
696 action: "updated_users"
697 })
698
699 if params["password"] do
700 User.force_password_reset_async(user)
701 end
702
703 ModerationLog.insert_log(%{
704 actor: admin,
705 subject: [user],
706 action: "force_password_reset"
707 })
708
709 json(conn, %{status: "success"})
710 else
711 {:error, changeset} ->
712 {_, {error, _}} = Enum.at(changeset.errors, 0)
713 json(conn, %{error: "New password #{error}."})
714
715 _ ->
716 json(conn, %{error: "Unable to change password."})
717 end
718 end
719
720 def list_reports(conn, params) do
721 {page, page_size} = page_params(params)
722
723 reports = Utils.get_reports(params, page, page_size)
724
725 conn
726 |> put_view(ReportView)
727 |> render("index.json", %{reports: reports})
728 end
729
730 def report_show(conn, %{"id" => id}) do
731 with %Activity{} = report <- Activity.get_by_id(id) do
732 conn
733 |> put_view(ReportView)
734 |> render("show.json", Report.extract_report_info(report))
735 else
736 _ -> {:error, :not_found}
737 end
738 end
739
740 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
741 result =
742 reports
743 |> Enum.map(fn report ->
744 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
745 ModerationLog.insert_log(%{
746 action: "report_update",
747 actor: admin,
748 subject: activity
749 })
750
751 activity
752 else
753 {:error, message} -> %{id: report["id"], error: message}
754 end
755 end)
756
757 case Enum.any?(result, &Map.has_key?(&1, :error)) do
758 true -> json_response(conn, :bad_request, result)
759 false -> json_response(conn, :no_content, "")
760 end
761 end
762
763 def report_notes_create(%{assigns: %{user: user}} = conn, %{
764 "id" => report_id,
765 "content" => content
766 }) do
767 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
768 ModerationLog.insert_log(%{
769 action: "report_note",
770 actor: user,
771 subject: Activity.get_by_id(report_id),
772 text: content
773 })
774
775 json_response(conn, :no_content, "")
776 else
777 _ -> json_response(conn, :bad_request, "")
778 end
779 end
780
781 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
782 "id" => note_id,
783 "report_id" => report_id
784 }) do
785 with {:ok, note} <- ReportNote.destroy(note_id) do
786 ModerationLog.insert_log(%{
787 action: "report_note_delete",
788 actor: user,
789 subject: Activity.get_by_id(report_id),
790 text: note.content
791 })
792
793 json_response(conn, :no_content, "")
794 else
795 _ -> json_response(conn, :bad_request, "")
796 end
797 end
798
799 def list_log(conn, params) do
800 {page, page_size} = page_params(params)
801
802 log =
803 ModerationLog.get_all(%{
804 page: page,
805 page_size: page_size,
806 start_date: params["start_date"],
807 end_date: params["end_date"],
808 user_id: params["user_id"],
809 search: params["search"]
810 })
811
812 conn
813 |> put_view(ModerationLogView)
814 |> render("index.json", %{log: log})
815 end
816
817 def config_descriptions(conn, _params) do
818 descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
819
820 json(conn, descriptions)
821 end
822
823 def config_show(conn, %{"only_db" => true}) do
824 with :ok <- configurable_from_database() do
825 configs = Pleroma.Repo.all(ConfigDB)
826
827 conn
828 |> put_view(ConfigView)
829 |> render("index.json", %{configs: configs})
830 end
831 end
832
833 def config_show(conn, _params) do
834 with :ok <- configurable_from_database() do
835 configs = ConfigDB.get_all_as_keyword()
836
837 merged =
838 Config.Holder.default_config()
839 |> ConfigDB.merge(configs)
840 |> Enum.map(fn {group, values} ->
841 Enum.map(values, fn {key, value} ->
842 db =
843 if configs[group][key] do
844 ConfigDB.get_db_keys(configs[group][key], key)
845 end
846
847 db_value = configs[group][key]
848
849 merged_value =
850 if !is_nil(db_value) and Keyword.keyword?(db_value) and
851 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
852 ConfigDB.merge_group(group, key, value, db_value)
853 else
854 value
855 end
856
857 setting = %{
858 group: ConfigDB.convert(group),
859 key: ConfigDB.convert(key),
860 value: ConfigDB.convert(merged_value)
861 }
862
863 if db, do: Map.put(setting, :db, db), else: setting
864 end)
865 end)
866 |> List.flatten()
867
868 json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
869 end
870 end
871
872 def config_update(conn, %{"configs" => configs}) do
873 with :ok <- configurable_from_database() do
874 {_errors, results} =
875 configs
876 |> Enum.filter(&whitelisted_config?/1)
877 |> Enum.map(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 if !Restarter.Pleroma.need_reboot?() do
898 changed_reboot_settings? =
899 (updated ++ deleted)
900 |> Enum.any?(fn config ->
901 group = ConfigDB.from_string(config.group)
902 key = ConfigDB.from_string(config.key)
903 value = ConfigDB.from_binary(config.value)
904 Config.TransferTask.pleroma_need_restart?(group, key, value)
905 end)
906
907 if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
908 end
909
910 conn
911 |> put_view(ConfigView)
912 |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
913 end
914 end
915
916 def restart(conn, _params) do
917 with :ok <- configurable_from_database() do
918 Restarter.Pleroma.restart(Config.get(:env), 50)
919
920 json(conn, %{})
921 end
922 end
923
924 def need_reboot(conn, _params) do
925 json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
926 end
927
928 defp configurable_from_database do
929 if Config.get(:configurable_from_database) do
930 :ok
931 else
932 {:error, "To use this endpoint you need to enable configuration from database."}
933 end
934 end
935
936 defp whitelisted_config?(group, key) do
937 if whitelisted_configs = Config.get(:database_config_whitelist) do
938 Enum.any?(whitelisted_configs, fn
939 {whitelisted_group} ->
940 group == inspect(whitelisted_group)
941
942 {whitelisted_group, whitelisted_key} ->
943 group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
944 end)
945 else
946 true
947 end
948 end
949
950 defp whitelisted_config?(%{"group" => group, "key" => key}) do
951 whitelisted_config?(group, key)
952 end
953
954 defp whitelisted_config?(%{:group => group} = config) do
955 whitelisted_config?(group, config[:key])
956 end
957
958 def reload_emoji(conn, _params) do
959 Pleroma.Emoji.reload()
960
961 conn |> json("ok")
962 end
963
964 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
965 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
966
967 User.toggle_confirmation(users)
968
969 ModerationLog.insert_log(%{
970 actor: admin,
971 subject: users,
972 action: "confirm_email"
973 })
974
975 conn |> json("")
976 end
977
978 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
979 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
980
981 User.try_send_confirmation_email(users)
982
983 ModerationLog.insert_log(%{
984 actor: admin,
985 subject: users,
986 action: "resend_confirmation_email"
987 })
988
989 conn |> json("")
990 end
991
992 def stats(conn, _) do
993 count = Stats.get_status_visibility_count()
994
995 conn
996 |> json(%{"status_visibility" => count})
997 end
998
999 defp page_params(params) do
1000 {get_page(params["page"]), get_page_size(params["page_size"])}
1001 end
1002
1003 defp get_page(page_string) when is_nil(page_string), do: 1
1004
1005 defp get_page(page_string) do
1006 case Integer.parse(page_string) do
1007 {page, _} -> page
1008 :error -> 1
1009 end
1010 end
1011
1012 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1013
1014 defp get_page_size(page_size_string) do
1015 case Integer.parse(page_size_string) do
1016 {page_size, _} -> page_size
1017 :error -> @users_page_size
1018 end
1019 end
1020 end