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