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