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