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