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