Merge remote-tracking branch 'remotes/origin/develop' into 1505-threads-federation
[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.User
17 alias Pleroma.UserInviteToken
18 alias Pleroma.Web.ActivityPub.ActivityPub
19 alias Pleroma.Web.ActivityPub.Relay
20 alias Pleroma.Web.ActivityPub.Utils
21 alias Pleroma.Web.AdminAPI.AccountView
22 alias Pleroma.Web.AdminAPI.ConfigView
23 alias Pleroma.Web.AdminAPI.ModerationLogView
24 alias Pleroma.Web.AdminAPI.Report
25 alias Pleroma.Web.AdminAPI.ReportView
26 alias Pleroma.Web.AdminAPI.Search
27 alias Pleroma.Web.CommonAPI
28 alias Pleroma.Web.Endpoint
29 alias Pleroma.Web.MastodonAPI.StatusView
30 alias Pleroma.Web.Router
31
32 require Logger
33
34 @descriptions_json Pleroma.Docs.JSON.compile()
35 @users_page_size 50
36
37 plug(
38 OAuthScopesPlug,
39 %{scopes: ["read:accounts"], admin: true}
40 when action in [:list_users, :user_show, :right_get]
41 )
42
43 plug(
44 OAuthScopesPlug,
45 %{scopes: ["write:accounts"], admin: true}
46 when action in [
47 :get_password_reset,
48 :user_delete,
49 :users_create,
50 :user_toggle_activation,
51 :user_activate,
52 :user_deactivate,
53 :tag_users,
54 :untag_users,
55 :right_add,
56 :right_delete
57 ]
58 )
59
60 plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites)
61
62 plug(
63 OAuthScopesPlug,
64 %{scopes: ["write:invites"], admin: true}
65 when action in [:create_invite_token, :revoke_invite, :email_invite]
66 )
67
68 plug(
69 OAuthScopesPlug,
70 %{scopes: ["write:follows"], admin: true}
71 when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
72 )
73
74 plug(
75 OAuthScopesPlug,
76 %{scopes: ["read:reports"], admin: true}
77 when action in [:list_reports, :report_show]
78 )
79
80 plug(
81 OAuthScopesPlug,
82 %{scopes: ["write:reports"], admin: true}
83 when action in [:reports_update]
84 )
85
86 plug(
87 OAuthScopesPlug,
88 %{scopes: ["read:statuses"], admin: true}
89 when action == :list_user_statuses
90 )
91
92 plug(
93 OAuthScopesPlug,
94 %{scopes: ["write:statuses"], admin: true}
95 when action in [:status_update, :status_delete]
96 )
97
98 plug(
99 OAuthScopesPlug,
100 %{scopes: ["read"], admin: true}
101 when action in [:config_show, :list_log]
102 )
103
104 plug(
105 OAuthScopesPlug,
106 %{scopes: ["write"], admin: true}
107 when action == :config_update
108 )
109
110 action_fallback(:errors)
111
112 def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
113 user = User.get_cached_by_nickname(nickname)
114 User.delete(user)
115
116 ModerationLog.insert_log(%{
117 actor: admin,
118 subject: [user],
119 action: "delete"
120 })
121
122 conn
123 |> json(nickname)
124 end
125
126 def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
127 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
128 User.delete(users)
129
130 ModerationLog.insert_log(%{
131 actor: admin,
132 subject: users,
133 action: "delete"
134 })
135
136 conn
137 |> json(nicknames)
138 end
139
140 def user_follow(%{assigns: %{user: admin}} = conn, %{
141 "follower" => follower_nick,
142 "followed" => followed_nick
143 }) do
144 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
145 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
146 User.follow(follower, followed)
147
148 ModerationLog.insert_log(%{
149 actor: admin,
150 followed: followed,
151 follower: follower,
152 action: "follow"
153 })
154 end
155
156 conn
157 |> json("ok")
158 end
159
160 def user_unfollow(%{assigns: %{user: admin}} = conn, %{
161 "follower" => follower_nick,
162 "followed" => followed_nick
163 }) do
164 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
165 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
166 User.unfollow(follower, followed)
167
168 ModerationLog.insert_log(%{
169 actor: admin,
170 followed: followed,
171 follower: follower,
172 action: "unfollow"
173 })
174 end
175
176 conn
177 |> json("ok")
178 end
179
180 def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
181 changesets =
182 Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
183 user_data = %{
184 nickname: nickname,
185 name: nickname,
186 email: email,
187 password: password,
188 password_confirmation: password,
189 bio: "."
190 }
191
192 User.register_changeset(%User{}, user_data, need_confirmation: false)
193 end)
194 |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
195 Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
196 end)
197
198 case Pleroma.Repo.transaction(changesets) do
199 {:ok, users} ->
200 res =
201 users
202 |> Map.values()
203 |> Enum.map(fn user ->
204 {:ok, user} = User.post_register_action(user)
205
206 user
207 end)
208 |> Enum.map(&AccountView.render("created.json", %{user: &1}))
209
210 ModerationLog.insert_log(%{
211 actor: admin,
212 subjects: Map.values(users),
213 action: "create"
214 })
215
216 conn
217 |> json(res)
218
219 {:error, id, changeset, _} ->
220 res =
221 Enum.map(changesets.operations, fn
222 {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
223 AccountView.render("create-error.json", %{changeset: changeset})
224
225 {_, {:changeset, current_changeset, _}} ->
226 AccountView.render("create-error.json", %{changeset: current_changeset})
227 end)
228
229 conn
230 |> put_status(:conflict)
231 |> json(res)
232 end
233 end
234
235 def user_show(conn, %{"nickname" => nickname}) do
236 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
237 conn
238 |> put_view(AccountView)
239 |> render("show.json", %{user: user})
240 else
241 _ -> {:error, :not_found}
242 end
243 end
244
245 def list_instance_statuses(conn, %{"instance" => instance} = params) do
246 {page, page_size} = page_params(params)
247
248 activities =
249 ActivityPub.fetch_instance_activities(%{
250 "instance" => instance,
251 "limit" => page_size,
252 "offset" => (page - 1) * page_size
253 })
254
255 conn
256 |> put_view(Pleroma.Web.AdminAPI.StatusView)
257 |> render("index.json", %{activities: activities, as: :activity})
258 end
259
260 def list_user_statuses(conn, %{"nickname" => nickname} = params) do
261 godmode = params["godmode"] == "true" || params["godmode"] == true
262
263 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
264 {_, page_size} = page_params(params)
265
266 activities =
267 ActivityPub.fetch_user_activities(user, nil, %{
268 "limit" => page_size,
269 "godmode" => godmode
270 })
271
272 conn
273 |> put_view(StatusView)
274 |> render("index.json", %{activities: activities, as: :activity})
275 else
276 _ -> {:error, :not_found}
277 end
278 end
279
280 def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
281 user = User.get_cached_by_nickname(nickname)
282
283 {:ok, updated_user} = User.deactivate(user, !user.deactivated)
284
285 action = if user.deactivated, do: "activate", else: "deactivate"
286
287 ModerationLog.insert_log(%{
288 actor: admin,
289 subject: [user],
290 action: action
291 })
292
293 conn
294 |> put_view(AccountView)
295 |> render("show.json", %{user: updated_user})
296 end
297
298 def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
299 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
300 {:ok, updated_users} = User.deactivate(users, false)
301
302 ModerationLog.insert_log(%{
303 actor: admin,
304 subject: users,
305 action: "activate"
306 })
307
308 conn
309 |> put_view(AccountView)
310 |> render("index.json", %{users: Keyword.values(updated_users)})
311 end
312
313 def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
314 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
315 {:ok, updated_users} = User.deactivate(users, true)
316
317 ModerationLog.insert_log(%{
318 actor: admin,
319 subject: users,
320 action: "deactivate"
321 })
322
323 conn
324 |> put_view(AccountView)
325 |> render("index.json", %{users: Keyword.values(updated_users)})
326 end
327
328 def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
329 with {:ok, _} <- User.tag(nicknames, tags) do
330 ModerationLog.insert_log(%{
331 actor: admin,
332 nicknames: nicknames,
333 tags: tags,
334 action: "tag"
335 })
336
337 json_response(conn, :no_content, "")
338 end
339 end
340
341 def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
342 with {:ok, _} <- User.untag(nicknames, tags) do
343 ModerationLog.insert_log(%{
344 actor: admin,
345 nicknames: nicknames,
346 tags: tags,
347 action: "untag"
348 })
349
350 json_response(conn, :no_content, "")
351 end
352 end
353
354 def list_users(conn, params) do
355 {page, page_size} = page_params(params)
356 filters = maybe_parse_filters(params["filters"])
357
358 search_params = %{
359 query: params["query"],
360 page: page,
361 page_size: page_size,
362 tags: params["tags"],
363 name: params["name"],
364 email: params["email"]
365 }
366
367 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)),
368 {:ok, users, count} <- filter_service_users(users, count),
369 do:
370 conn
371 |> json(
372 AccountView.render("index.json",
373 users: users,
374 count: count,
375 page_size: page_size
376 )
377 )
378 end
379
380 defp filter_service_users(users, count) do
381 filtered_users = Enum.reject(users, &service_user?/1)
382 count = if Enum.any?(users, &service_user?/1), do: length(filtered_users), else: count
383
384 {:ok, filtered_users, count}
385 end
386
387 defp service_user?(user) do
388 String.match?(user.ap_id, ~r/.*\/relay$/) or
389 String.match?(user.ap_id, ~r/.*\/internal\/fetch$/)
390 end
391
392 @filters ~w(local external active deactivated is_admin is_moderator)
393
394 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
395 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
396
397 defp maybe_parse_filters(filters) do
398 filters
399 |> String.split(",")
400 |> Enum.filter(&Enum.member?(@filters, &1))
401 |> Enum.map(&String.to_atom(&1))
402 |> Enum.into(%{}, &{&1, true})
403 end
404
405 def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
406 "permission_group" => permission_group,
407 "nicknames" => nicknames
408 })
409 when permission_group in ["moderator", "admin"] do
410 update = %{:"is_#{permission_group}" => true}
411
412 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
413
414 for u <- users, do: User.admin_api_update(u, update)
415
416 ModerationLog.insert_log(%{
417 action: "grant",
418 actor: admin,
419 subject: users,
420 permission: permission_group
421 })
422
423 json(conn, update)
424 end
425
426 def right_add_multiple(conn, _) do
427 render_error(conn, :not_found, "No such permission_group")
428 end
429
430 def right_add(%{assigns: %{user: admin}} = conn, %{
431 "permission_group" => permission_group,
432 "nickname" => nickname
433 })
434 when permission_group in ["moderator", "admin"] do
435 fields = %{:"is_#{permission_group}" => true}
436
437 {:ok, user} =
438 nickname
439 |> User.get_cached_by_nickname()
440 |> User.admin_api_update(fields)
441
442 ModerationLog.insert_log(%{
443 action: "grant",
444 actor: admin,
445 subject: [user],
446 permission: permission_group
447 })
448
449 json(conn, fields)
450 end
451
452 def right_add(conn, _) do
453 render_error(conn, :not_found, "No such permission_group")
454 end
455
456 def right_get(conn, %{"nickname" => nickname}) do
457 user = User.get_cached_by_nickname(nickname)
458
459 conn
460 |> json(%{
461 is_moderator: user.is_moderator,
462 is_admin: user.is_admin
463 })
464 end
465
466 def right_delete_multiple(
467 %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
468 %{
469 "permission_group" => permission_group,
470 "nicknames" => nicknames
471 }
472 )
473 when permission_group in ["moderator", "admin"] do
474 with false <- Enum.member?(nicknames, admin_nickname) do
475 update = %{:"is_#{permission_group}" => false}
476
477 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
478
479 for u <- users, do: User.admin_api_update(u, update)
480
481 ModerationLog.insert_log(%{
482 action: "revoke",
483 actor: admin,
484 subject: users,
485 permission: permission_group
486 })
487
488 json(conn, update)
489 else
490 _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
491 end
492 end
493
494 def right_delete_multiple(conn, _) do
495 render_error(conn, :not_found, "No such permission_group")
496 end
497
498 def right_delete(
499 %{assigns: %{user: admin}} = conn,
500 %{
501 "permission_group" => permission_group,
502 "nickname" => nickname
503 }
504 )
505 when permission_group in ["moderator", "admin"] do
506 fields = %{:"is_#{permission_group}" => false}
507
508 {:ok, user} =
509 nickname
510 |> User.get_cached_by_nickname()
511 |> User.admin_api_update(fields)
512
513 ModerationLog.insert_log(%{
514 action: "revoke",
515 actor: admin,
516 subject: [user],
517 permission: permission_group
518 })
519
520 json(conn, fields)
521 end
522
523 def right_delete(%{assigns: %{user: %{nickname: nickname}}} = conn, %{"nickname" => nickname}) do
524 render_error(conn, :forbidden, "You can't revoke your own admin status.")
525 end
526
527 def relay_list(conn, _params) do
528 with {:ok, list} <- Relay.list() do
529 json(conn, %{relays: list})
530 else
531 _ ->
532 conn
533 |> put_status(500)
534 end
535 end
536
537 def relay_follow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
538 with {:ok, _message} <- Relay.follow(target) do
539 ModerationLog.insert_log(%{
540 action: "relay_follow",
541 actor: admin,
542 target: target
543 })
544
545 json(conn, target)
546 else
547 _ ->
548 conn
549 |> put_status(500)
550 |> json(target)
551 end
552 end
553
554 def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target}) do
555 with {:ok, _message} <- Relay.unfollow(target) do
556 ModerationLog.insert_log(%{
557 action: "relay_unfollow",
558 actor: admin,
559 target: target
560 })
561
562 json(conn, target)
563 else
564 _ ->
565 conn
566 |> put_status(500)
567 |> json(target)
568 end
569 end
570
571 @doc "Sends registration invite via email"
572 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
573 with true <-
574 Config.get([:instance, :invites_enabled]) &&
575 !Config.get([:instance, :registrations_open]),
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 end
587 end
588
589 @doc "Create an account registration invite token"
590 def create_invite_token(conn, params) do
591 opts = %{}
592
593 opts =
594 if params["max_use"],
595 do: Map.put(opts, :max_use, params["max_use"]),
596 else: opts
597
598 opts =
599 if params["expires_at"],
600 do: Map.put(opts, :expires_at, params["expires_at"]),
601 else: opts
602
603 {:ok, invite} = UserInviteToken.create_invite(opts)
604
605 json(conn, AccountView.render("invite.json", %{invite: invite}))
606 end
607
608 @doc "Get list of created invites"
609 def invites(conn, _params) do
610 invites = UserInviteToken.list_invites()
611
612 conn
613 |> put_view(AccountView)
614 |> render("invites.json", %{invites: invites})
615 end
616
617 @doc "Revokes invite by token"
618 def revoke_invite(conn, %{"token" => token}) do
619 with {:ok, invite} <- UserInviteToken.find_by_token(token),
620 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
621 conn
622 |> put_view(AccountView)
623 |> render("invite.json", %{invite: updated_invite})
624 else
625 nil -> {:error, :not_found}
626 end
627 end
628
629 @doc "Get a password reset token (base64 string) for given nickname"
630 def get_password_reset(conn, %{"nickname" => nickname}) do
631 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
632 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
633
634 conn
635 |> json(%{
636 token: token.token,
637 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
638 })
639 end
640
641 @doc "Force password reset for a given user"
642 def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
643 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
644
645 Enum.each(users, &User.force_password_reset_async/1)
646
647 ModerationLog.insert_log(%{
648 actor: admin,
649 subject: users,
650 action: "force_password_reset"
651 })
652
653 json_response(conn, :no_content, "")
654 end
655
656 def list_reports(conn, params) do
657 {page, page_size} = page_params(params)
658
659 reports = Utils.get_reports(params, page, page_size)
660
661 conn
662 |> put_view(ReportView)
663 |> render("index.json", %{reports: reports})
664 end
665
666 def list_grouped_reports(conn, _params) do
667 statuses = Utils.get_reported_activities()
668
669 conn
670 |> put_view(ReportView)
671 |> render("index_grouped.json", Utils.get_reports_grouped_by_status(statuses))
672 end
673
674 def report_show(conn, %{"id" => id}) do
675 with %Activity{} = report <- Activity.get_by_id(id) do
676 conn
677 |> put_view(ReportView)
678 |> render("show.json", Report.extract_report_info(report))
679 else
680 _ -> {:error, :not_found}
681 end
682 end
683
684 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
685 result =
686 reports
687 |> Enum.map(fn report ->
688 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
689 ModerationLog.insert_log(%{
690 action: "report_update",
691 actor: admin,
692 subject: activity
693 })
694
695 activity
696 else
697 {:error, message} -> %{id: report["id"], error: message}
698 end
699 end)
700
701 case Enum.any?(result, &Map.has_key?(&1, :error)) do
702 true -> json_response(conn, :bad_request, result)
703 false -> json_response(conn, :no_content, "")
704 end
705 end
706
707 def report_notes_create(%{assigns: %{user: user}} = conn, %{
708 "id" => report_id,
709 "content" => content
710 }) do
711 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
712 ModerationLog.insert_log(%{
713 action: "report_note",
714 actor: user,
715 subject: Activity.get_by_id(report_id),
716 text: content
717 })
718
719 json_response(conn, :no_content, "")
720 else
721 _ -> json_response(conn, :bad_request, "")
722 end
723 end
724
725 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
726 "id" => note_id,
727 "report_id" => report_id
728 }) do
729 with {:ok, note} <- ReportNote.destroy(note_id) do
730 ModerationLog.insert_log(%{
731 action: "report_note_delete",
732 actor: user,
733 subject: Activity.get_by_id(report_id),
734 text: note.content
735 })
736
737 json_response(conn, :no_content, "")
738 else
739 _ -> json_response(conn, :bad_request, "")
740 end
741 end
742
743 def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
744 with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
745 {:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
746
747 ModerationLog.insert_log(%{
748 action: "status_update",
749 actor: admin,
750 subject: activity,
751 sensitive: sensitive,
752 visibility: params["visibility"]
753 })
754
755 conn
756 |> put_view(StatusView)
757 |> render("show.json", %{activity: activity})
758 end
759 end
760
761 def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
762 with {:ok, %Activity{}} <- CommonAPI.delete(id, user) do
763 ModerationLog.insert_log(%{
764 action: "status_delete",
765 actor: user,
766 subject_id: id
767 })
768
769 json(conn, %{})
770 end
771 end
772
773 def list_log(conn, params) do
774 {page, page_size} = page_params(params)
775
776 log =
777 ModerationLog.get_all(%{
778 page: page,
779 page_size: page_size,
780 start_date: params["start_date"],
781 end_date: params["end_date"],
782 user_id: params["user_id"],
783 search: params["search"]
784 })
785
786 conn
787 |> put_view(ModerationLogView)
788 |> render("index.json", %{log: log})
789 end
790
791 def config_descriptions(conn, _params) do
792 conn
793 |> Plug.Conn.put_resp_content_type("application/json")
794 |> Plug.Conn.send_resp(200, @descriptions_json)
795 end
796
797 def config_show(conn, %{"only_db" => true}) do
798 with :ok <- configurable_from_database(conn) do
799 configs = Pleroma.Repo.all(ConfigDB)
800
801 conn
802 |> put_view(ConfigView)
803 |> render("index.json", %{configs: configs})
804 end
805 end
806
807 def config_show(conn, _params) do
808 with :ok <- configurable_from_database(conn) do
809 configs = ConfigDB.get_all_as_keyword()
810
811 merged =
812 Config.Holder.config()
813 |> ConfigDB.merge(configs)
814 |> Enum.map(fn {group, values} ->
815 Enum.map(values, fn {key, value} ->
816 db =
817 if configs[group][key] do
818 ConfigDB.get_db_keys(configs[group][key], key)
819 end
820
821 db_value = configs[group][key]
822
823 merged_value =
824 if !is_nil(db_value) and Keyword.keyword?(db_value) and
825 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
826 ConfigDB.merge_group(group, key, value, db_value)
827 else
828 value
829 end
830
831 setting = %{
832 group: ConfigDB.convert(group),
833 key: ConfigDB.convert(key),
834 value: ConfigDB.convert(merged_value)
835 }
836
837 if db, do: Map.put(setting, :db, db), else: setting
838 end)
839 end)
840 |> List.flatten()
841
842 response = %{configs: merged}
843
844 response =
845 if Restarter.Pleroma.need_reboot?() do
846 Map.put(response, :need_reboot, true)
847 else
848 response
849 end
850
851 json(conn, response)
852 end
853 end
854
855 def config_update(conn, %{"configs" => configs}) do
856 with :ok <- configurable_from_database(conn) do
857 {_errors, results} =
858 Enum.map(configs, fn
859 %{"group" => group, "key" => key, "delete" => true} = params ->
860 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
861
862 %{"group" => group, "key" => key, "value" => value} ->
863 ConfigDB.update_or_create(%{group: group, key: key, value: value})
864 end)
865 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
866
867 {deleted, updated} =
868 results
869 |> Enum.map(fn {:ok, config} ->
870 Map.put(config, :db, ConfigDB.get_db_keys(config))
871 end)
872 |> Enum.split_with(fn config ->
873 Ecto.get_meta(config, :state) == :deleted
874 end)
875
876 Config.TransferTask.load_and_update_env(deleted, false)
877
878 need_reboot? =
879 Restarter.Pleroma.need_reboot?() ||
880 Enum.any?(updated, fn config ->
881 group = ConfigDB.from_string(config.group)
882 key = ConfigDB.from_string(config.key)
883 value = ConfigDB.from_binary(config.value)
884 Config.TransferTask.pleroma_need_restart?(group, key, value)
885 end)
886
887 response = %{configs: updated}
888
889 response =
890 if need_reboot? do
891 Restarter.Pleroma.need_reboot()
892 Map.put(response, :need_reboot, need_reboot?)
893 else
894 response
895 end
896
897 conn
898 |> put_view(ConfigView)
899 |> render("index.json", response)
900 end
901 end
902
903 def restart(conn, _params) do
904 with :ok <- configurable_from_database(conn) do
905 Restarter.Pleroma.restart(Config.get(:env), 50)
906
907 json(conn, %{})
908 end
909 end
910
911 defp configurable_from_database(conn) do
912 if Config.get(:configurable_from_database) do
913 :ok
914 else
915 errors(
916 conn,
917 {:error, "To use this endpoint you need to enable configuration from database."}
918 )
919 end
920 end
921
922 def reload_emoji(conn, _params) do
923 Pleroma.Emoji.reload()
924
925 conn |> json("ok")
926 end
927
928 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
929 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
930
931 User.toggle_confirmation(users)
932
933 ModerationLog.insert_log(%{
934 actor: admin,
935 subject: users,
936 action: "confirm_email"
937 })
938
939 conn |> json("")
940 end
941
942 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
943 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
944
945 User.try_send_confirmation_email(users)
946
947 ModerationLog.insert_log(%{
948 actor: admin,
949 subject: users,
950 action: "resend_confirmation_email"
951 })
952
953 conn |> json("")
954 end
955
956 def errors(conn, {:error, :not_found}) do
957 conn
958 |> put_status(:not_found)
959 |> json(dgettext("errors", "Not found"))
960 end
961
962 def errors(conn, {:error, reason}) do
963 conn
964 |> put_status(:bad_request)
965 |> json(reason)
966 end
967
968 def errors(conn, {:param_cast, _}) do
969 conn
970 |> put_status(:bad_request)
971 |> json(dgettext("errors", "Invalid parameters"))
972 end
973
974 def errors(conn, _) do
975 conn
976 |> put_status(:internal_server_error)
977 |> json(dgettext("errors", "Something went wrong"))
978 end
979
980 defp page_params(params) do
981 {get_page(params["page"]), get_page_size(params["page_size"])}
982 end
983
984 defp get_page(page_string) when is_nil(page_string), do: 1
985
986 defp get_page(page_string) do
987 case Integer.parse(page_string) do
988 {page, _} -> page
989 :error -> 1
990 end
991 end
992
993 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
994
995 defp get_page_size(page_size_string) do
996 case Integer.parse(page_size_string) do
997 {page_size, _} -> page_size
998 :error -> @users_page_size
999 end
1000 end
1001 end