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