Merge branch 'fix/instance-gen-custom-static' into 'develop'
[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_instance_activities(%{
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 status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
745 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
746 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
747
748 ModerationLog.insert_log(%{
749 action: "status_update",
750 actor: admin,
751 subject: activity,
752 sensitive: sensitive,
753 visibility: params["visibility"]
754 })
755
756 conn
757 |> put_view(StatusView)
758 |> render("show.json", %{activity: activity})
759 end
760 end
761
762 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
763 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
764 ModerationLog.insert_log(%{
765 action: "status_delete",
766 actor: user,
767 subject_id: id
768 })
769
770 json(conn, %{})
771 end
772 end
773
774 def list_log(conn, params) do
775 {page, page_size} = page_params(params)
776
777 log =
778 ModerationLog.get_all(%{
779 page: page,
780 page_size: page_size,
781 start_date: params["start_date"],
782 end_date: params["end_date"],
783 user_id: params["user_id"],
784 search: params["search"]
785 })
786
787 conn
788 |> put_view(ModerationLogView)
789 |> render("index.json", %{log: log})
790 end
791
792 def config_descriptions(conn, _params) do
793 conn
794 |> Plug.Conn.put_resp_content_type("application/json")
795 |> Plug.Conn.send_resp(200, @descriptions_json)
796 end
797
798 def config_show(conn, %{"only_db" => true}) do
799 with :ok <- configurable_from_database(conn) do
800 configs = Pleroma.Repo.all(ConfigDB)
801
802 conn
803 |> put_view(ConfigView)
804 |> render("index.json", %{configs: configs})
805 end
806 end
807
808 def config_show(conn, _params) do
809 with :ok <- configurable_from_database(conn) do
810 configs = ConfigDB.get_all_as_keyword()
811
812 merged =
813 Config.Holder.config()
814 |> ConfigDB.merge(configs)
815 |> Enum.map(fn {group, values} ->
816 Enum.map(values, fn {key, value} ->
817 db =
818 if configs[group][key] do
819 ConfigDB.get_db_keys(configs[group][key], key)
820 end
821
822 db_value = configs[group][key]
823
824 merged_value =
825 if !is_nil(db_value) and Keyword.keyword?(db_value) and
826 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
827 ConfigDB.merge_group(group, key, value, db_value)
828 else
829 value
830 end
831
832 setting = %{
833 group: ConfigDB.convert(group),
834 key: ConfigDB.convert(key),
835 value: ConfigDB.convert(merged_value)
836 }
837
838 if db, do: Map.put(setting, :db, db), else: setting
839 end)
840 end)
841 |> List.flatten()
842
843 response = %{configs: merged}
844
845 response =
846 if Restarter.Pleroma.need_reboot?() do
847 Map.put(response, :need_reboot, true)
848 else
849 response
850 end
851
852 json(conn, response)
853 end
854 end
855
856 def config_update(conn, %{"configs" => configs}) do
857 with :ok <- configurable_from_database(conn) do
858 {_errors, results} =
859 Enum.map(configs, fn
860 %{"group" => group, "key" => key, "delete" => true} = params ->
861 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
862
863 %{"group" => group, "key" => key, "value" => value} ->
864 ConfigDB.update_or_create(%{group: group, key: key, value: value})
865 end)
866 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
867
868 {deleted, updated} =
869 results
870 |> Enum.map(fn {:ok, config} ->
871 Map.put(config, :db, ConfigDB.get_db_keys(config))
872 end)
873 |> Enum.split_with(fn config ->
874 Ecto.get_meta(config, :state) == :deleted
875 end)
876
877 Config.TransferTask.load_and_update_env(deleted, false)
878
879 need_reboot? =
880 Restarter.Pleroma.need_reboot?() ||
881 Enum.any?(updated, fn config ->
882 group = ConfigDB.from_string(config.group)
883 key = ConfigDB.from_string(config.key)
884 value = ConfigDB.from_binary(config.value)
885 Config.TransferTask.pleroma_need_restart?(group, key, value)
886 end)
887
888 response = %{configs: updated}
889
890 response =
891 if need_reboot? do
892 Restarter.Pleroma.need_reboot()
893 Map.put(response, :need_reboot, need_reboot?)
894 else
895 response
896 end
897
898 conn
899 |> put_view(ConfigView)
900 |> render("index.json", response)
901 end
902 end
903
904 def restart(conn, _params) do
905 with :ok <- configurable_from_database(conn) do
906 Restarter.Pleroma.restart(Config.get(:env), 50)
907
908 json(conn, %{})
909 end
910 end
911
912 defp configurable_from_database(conn) do
913 if Config.get(:configurable_from_database) do
914 :ok
915 else
916 errors(
917 conn,
918 {:error, "To use this endpoint you need to enable configuration from database."}
919 )
920 end
921 end
922
923 def reload_emoji(conn, _params) do
924 Pleroma.Emoji.reload()
925
926 conn |> json("ok")
927 end
928
929 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
930 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
931
932 User.toggle_confirmation(users)
933
934 ModerationLog.insert_log(%{
935 actor: admin,
936 subject: users,
937 action: "confirm_email"
938 })
939
940 conn |> json("")
941 end
942
943 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
944 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
945
946 User.try_send_confirmation_email(users)
947
948 ModerationLog.insert_log(%{
949 actor: admin,
950 subject: users,
951 action: "resend_confirmation_email"
952 })
953
954 conn |> json("")
955 end
956
957 def stats(conn, _) do
958 count = Stats.get_status_visibility_count()
959
960 conn
961 |> json(%{"status_visibility" => count})
962 end
963
964 def errors(conn, {:error, :not_found}) do
965 conn
966 |> put_status(:not_found)
967 |> json(dgettext("errors", "Not found"))
968 end
969
970 def errors(conn, {:error, reason}) do
971 conn
972 |> put_status(:bad_request)
973 |> json(reason)
974 end
975
976 def errors(conn, {:param_cast, _}) do
977 conn
978 |> put_status(:bad_request)
979 |> json(dgettext("errors", "Invalid parameters"))
980 end
981
982 def errors(conn, _) do
983 conn
984 |> put_status(:internal_server_error)
985 |> json(dgettext("errors", "Something went wrong"))
986 end
987
988 defp page_params(params) do
989 {get_page(params["page"]), get_page_size(params["page_size"])}
990 end
991
992 defp get_page(page_string) when is_nil(page_string), do: 1
993
994 defp get_page(page_string) do
995 case Integer.parse(page_string) do
996 {page, _} -> page
997 :error -> 1
998 end
999 end
1000
1001 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1002
1003 defp get_page_size(page_size_string) do
1004 case Integer.parse(page_size_string) do
1005 {page_size, _} -> page_size
1006 :error -> @users_page_size
1007 end
1008 end
1009 end