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