1 # Pleroma: A lightweight social networking server
2 # Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
3 # SPDX-License-Identifier: AGPL-3.0-only
5 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
6 use Pleroma.Web, :controller
8 import Pleroma.Web.ControllerHelper, only: [json_response: 3]
10 alias Pleroma.Activity
12 alias Pleroma.ConfigDB
14 alias Pleroma.ModerationLog
15 alias Pleroma.Plugs.OAuthScopesPlug
16 alias Pleroma.ReportNote
19 alias Pleroma.UserInviteToken
20 alias Pleroma.Web.ActivityPub.ActivityPub
21 alias Pleroma.Web.ActivityPub.Builder
22 alias Pleroma.Web.ActivityPub.Pipeline
23 alias Pleroma.Web.ActivityPub.Relay
24 alias Pleroma.Web.ActivityPub.Utils
25 alias Pleroma.Web.AdminAPI
26 alias Pleroma.Web.AdminAPI.AccountView
27 alias Pleroma.Web.AdminAPI.ConfigView
28 alias Pleroma.Web.AdminAPI.ModerationLogView
29 alias Pleroma.Web.AdminAPI.Report
30 alias Pleroma.Web.AdminAPI.ReportView
31 alias Pleroma.Web.AdminAPI.Search
32 alias Pleroma.Web.CommonAPI
33 alias Pleroma.Web.Endpoint
34 alias Pleroma.Web.MastodonAPI
35 alias Pleroma.Web.MastodonAPI.AppView
36 alias Pleroma.Web.OAuth.App
37 alias Pleroma.Web.Router
41 @descriptions Pleroma.Docs.JSON.compile()
46 %{scopes: ["read:accounts"], admin: true}
47 when action in [:list_users, :user_show, :right_get, :show_user_credentials]
52 %{scopes: ["write:accounts"], admin: true}
55 :force_password_reset,
58 :user_toggle_activation,
67 :right_delete_multiple,
68 :update_user_credentials
72 plug(OAuthScopesPlug, %{scopes: ["read:invites"], admin: true} when action == :invites)
76 %{scopes: ["write:invites"], admin: true}
77 when action in [:create_invite_token, :revoke_invite, :email_invite]
82 %{scopes: ["write:follows"], admin: true}
83 when action in [:user_follow, :user_unfollow, :relay_follow, :relay_unfollow]
88 %{scopes: ["read:reports"], admin: true}
89 when action in [:list_reports, :report_show]
94 %{scopes: ["write:reports"], admin: true}
95 when action in [:reports_update, :report_notes_create, :report_notes_delete]
100 %{scopes: ["read:statuses"], admin: true}
101 when action in [:list_user_statuses, :list_instance_statuses]
106 %{scopes: ["read"], admin: true}
112 :config_descriptions,
119 %{scopes: ["write"], admin: true}
123 :resend_confirmation_email,
133 action_fallback(AdminAPI.FallbackController)
135 def user_delete(conn, %{"nickname" => nickname}) do
136 user_delete(conn, %{"nicknames" => [nickname]})
139 def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
142 |> Enum.map(&User.get_cached_by_nickname/1)
145 |> Enum.each(fn user ->
146 {:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
147 Pipeline.common_pipeline(delete_data, local: true)
150 ModerationLog.insert_log(%{
160 def user_follow(%{assigns: %{user: admin}} = conn, %{
161 "follower" => follower_nick,
162 "followed" => followed_nick
164 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
165 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
166 User.follow(follower, followed)
168 ModerationLog.insert_log(%{
180 def user_unfollow(%{assigns: %{user: admin}} = conn, %{
181 "follower" => follower_nick,
182 "followed" => followed_nick
184 with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
185 %User{} = followed <- User.get_cached_by_nickname(followed_nick) do
186 User.unfollow(follower, followed)
188 ModerationLog.insert_log(%{
200 def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
202 Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
208 password_confirmation: password,
212 User.register_changeset(%User{}, user_data, need_confirmation: false)
214 |> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
215 Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
218 case Pleroma.Repo.transaction(changesets) do
223 |> Enum.map(fn user ->
224 {:ok, user} = User.post_register_action(user)
228 |> Enum.map(&AccountView.render("created.json", %{user: &1}))
230 ModerationLog.insert_log(%{
232 subjects: Map.values(users),
239 {:error, id, changeset, _} ->
241 Enum.map(changesets.operations, fn
242 {current_id, {:changeset, _current_changeset, _}} when current_id == id ->
243 AccountView.render("create-error.json", %{changeset: changeset})
245 {_, {:changeset, current_changeset, _}} ->
246 AccountView.render("create-error.json", %{changeset: current_changeset})
250 |> put_status(:conflict)
255 def user_show(conn, %{"nickname" => nickname}) do
256 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
258 |> put_view(AccountView)
259 |> render("show.json", %{user: user})
261 _ -> {:error, :not_found}
265 def list_instance_statuses(conn, %{"instance" => instance} = params) do
266 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
267 {page, page_size} = page_params(params)
270 ActivityPub.fetch_statuses(nil, %{
271 "instance" => instance,
272 "limit" => page_size,
273 "offset" => (page - 1) * page_size,
274 "exclude_reblogs" => !with_reblogs && "true"
278 |> put_view(AdminAPI.StatusView)
279 |> render("index.json", %{activities: activities, as: :activity})
282 def list_user_statuses(conn, %{"nickname" => nickname} = params) do
283 with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
284 godmode = params["godmode"] == "true" || params["godmode"] == true
286 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
287 {_, page_size} = page_params(params)
290 ActivityPub.fetch_user_activities(user, nil, %{
291 "limit" => page_size,
292 "godmode" => godmode,
293 "exclude_reblogs" => !with_reblogs && "true"
297 |> put_view(MastodonAPI.StatusView)
298 |> render("index.json", %{activities: activities, as: :activity})
300 _ -> {:error, :not_found}
304 def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
305 user = User.get_cached_by_nickname(nickname)
307 {:ok, updated_user} = User.deactivate(user, !user.deactivated)
309 action = if user.deactivated, do: "activate", else: "deactivate"
311 ModerationLog.insert_log(%{
318 |> put_view(AccountView)
319 |> render("show.json", %{user: updated_user})
322 def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
323 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
324 {:ok, updated_users} = User.deactivate(users, false)
326 ModerationLog.insert_log(%{
333 |> put_view(AccountView)
334 |> render("index.json", %{users: Keyword.values(updated_users)})
337 def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
338 users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
339 {:ok, updated_users} = User.deactivate(users, true)
341 ModerationLog.insert_log(%{
348 |> put_view(AccountView)
349 |> render("index.json", %{users: Keyword.values(updated_users)})
352 def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
353 with {:ok, _} <- User.tag(nicknames, tags) do
354 ModerationLog.insert_log(%{
356 nicknames: nicknames,
361 json_response(conn, :no_content, "")
365 def untag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
366 with {:ok, _} <- User.untag(nicknames, tags) do
367 ModerationLog.insert_log(%{
369 nicknames: nicknames,
374 json_response(conn, :no_content, "")
378 def list_users(conn, params) do
379 {page, page_size} = page_params(params)
380 filters = maybe_parse_filters(params["filters"])
383 query: params["query"],
385 page_size: page_size,
386 tags: params["tags"],
387 name: params["name"],
388 email: params["email"]
391 with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
394 AccountView.render("index.json", users: users, count: count, page_size: page_size)
399 @filters ~w(local external active deactivated is_admin is_moderator)
401 @spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
402 defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
404 defp maybe_parse_filters(filters) do
407 |> Enum.filter(&Enum.member?(@filters, &1))
408 |> Enum.map(&String.to_atom(&1))
409 |> Enum.into(%{}, &{&1, true})
412 def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
413 "permission_group" => permission_group,
414 "nicknames" => nicknames
416 when permission_group in ["moderator", "admin"] do
417 update = %{:"is_#{permission_group}" => true}
419 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
421 for u <- users, do: User.admin_api_update(u, update)
423 ModerationLog.insert_log(%{
427 permission: permission_group
433 def right_add_multiple(conn, _) do
434 render_error(conn, :not_found, "No such permission_group")
437 def right_add(%{assigns: %{user: admin}} = conn, %{
438 "permission_group" => permission_group,
439 "nickname" => nickname
441 when permission_group in ["moderator", "admin"] do
442 fields = %{:"is_#{permission_group}" => true}
446 |> User.get_cached_by_nickname()
447 |> User.admin_api_update(fields)
449 ModerationLog.insert_log(%{
453 permission: permission_group
459 def right_add(conn, _) do
460 render_error(conn, :not_found, "No such permission_group")
463 def right_get(conn, %{"nickname" => nickname}) do
464 user = User.get_cached_by_nickname(nickname)
468 is_moderator: user.is_moderator,
469 is_admin: user.is_admin
473 def right_delete_multiple(
474 %{assigns: %{user: %{nickname: admin_nickname} = admin}} = conn,
476 "permission_group" => permission_group,
477 "nicknames" => nicknames
480 when permission_group in ["moderator", "admin"] do
481 with false <- Enum.member?(nicknames, admin_nickname) do
482 update = %{:"is_#{permission_group}" => false}
484 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
486 for u <- users, do: User.admin_api_update(u, update)
488 ModerationLog.insert_log(%{
492 permission: permission_group
497 _ -> render_error(conn, :forbidden, "You can't revoke your own admin/moderator status.")
501 def right_delete_multiple(conn, _) do
502 render_error(conn, :not_found, "No such permission_group")
506 %{assigns: %{user: admin}} = conn,
508 "permission_group" => permission_group,
509 "nickname" => nickname
512 when permission_group in ["moderator", "admin"] do
513 fields = %{:"is_#{permission_group}" => false}
517 |> User.get_cached_by_nickname()
518 |> User.admin_api_update(fields)
520 ModerationLog.insert_log(%{
524 permission: permission_group
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.")
534 def relay_list(conn, _params) do
535 with {:ok, list} <- Relay.list() do
536 json(conn, %{relays: list})
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",
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",
578 @doc "Sends registration invite via email"
579 def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
580 with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
581 {_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
582 {:ok, invite_token} <- UserInviteToken.create_invite(),
584 Pleroma.Emails.UserEmail.user_invitation_email(
590 {:ok, _} <- Pleroma.Emails.Mailer.deliver(email) do
591 json_response(conn, :no_content, "")
593 {:registrations_open, _} ->
594 {:error, "To send invites you need to set the `registrations_open` option to false."}
596 {:invites_enabled, _} ->
597 {:error, "To send invites you need to set the `invites_enabled` option to true."}
601 @doc "Create an account registration invite token"
602 def create_invite_token(conn, params) do
606 if params["max_use"],
607 do: Map.put(opts, :max_use, params["max_use"]),
611 if params["expires_at"],
612 do: Map.put(opts, :expires_at, params["expires_at"]),
615 {:ok, invite} = UserInviteToken.create_invite(opts)
617 json(conn, AccountView.render("invite.json", %{invite: invite}))
620 @doc "Get list of created invites"
621 def invites(conn, _params) do
622 invites = UserInviteToken.list_invites()
625 |> put_view(AccountView)
626 |> render("invites.json", %{invites: invites})
629 @doc "Revokes invite by token"
630 def revoke_invite(conn, %{"token" => token}) do
631 with {:ok, invite} <- UserInviteToken.find_by_token(token),
632 {:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
634 |> put_view(AccountView)
635 |> render("invite.json", %{invite: updated_invite})
637 nil -> {:error, :not_found}
641 @doc "Get a password reset token (base64 string) for given nickname"
642 def get_password_reset(conn, %{"nickname" => nickname}) do
643 (%User{local: true} = user) = User.get_cached_by_nickname(nickname)
644 {:ok, token} = Pleroma.PasswordResetToken.create_token(user)
649 link: Router.Helpers.reset_password_url(Endpoint, :reset, token.token)
653 @doc "Force password reset for a given user"
654 def force_password_reset(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
655 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
657 Enum.each(users, &User.force_password_reset_async/1)
659 ModerationLog.insert_log(%{
662 action: "force_password_reset"
665 json_response(conn, :no_content, "")
668 @doc "Disable mfa for user's account."
669 def disable_mfa(conn, %{"nickname" => nickname}) do
670 case User.get_by_nickname(nickname) do
680 @doc "Show a given user's credentials"
681 def show_user_credentials(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
682 with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
684 |> put_view(AccountView)
685 |> render("credentials.json", %{user: user, for: admin})
687 _ -> {:error, :not_found}
691 @doc "Updates a given user"
692 def update_user_credentials(
693 %{assigns: %{user: admin}} = conn,
694 %{"nickname" => nickname} = params
696 with {_, user} <- {:user, User.get_cached_by_nickname(nickname)},
698 User.update_as_admin(user, params) do
699 ModerationLog.insert_log(%{
702 action: "updated_users"
705 if params["password"] do
706 User.force_password_reset_async(user)
709 ModerationLog.insert_log(%{
712 action: "force_password_reset"
715 json(conn, %{status: "success"})
717 {:error, changeset} ->
718 {_, {error, _}} = Enum.at(changeset.errors, 0)
719 json(conn, %{error: "New password #{error}."})
722 json(conn, %{error: "Unable to change password."})
726 def list_reports(conn, params) do
727 {page, page_size} = page_params(params)
729 reports = Utils.get_reports(params, page, page_size)
732 |> put_view(ReportView)
733 |> render("index.json", %{reports: reports})
736 def report_show(conn, %{"id" => id}) do
737 with %Activity{} = report <- Activity.get_by_id(id) do
739 |> put_view(ReportView)
740 |> render("show.json", Report.extract_report_info(report))
742 _ -> {:error, :not_found}
746 def reports_update(%{assigns: %{user: admin}} = conn, %{"reports" => reports}) do
749 |> Enum.map(fn report ->
750 with {:ok, activity} <- CommonAPI.update_report_state(report["id"], report["state"]) do
751 ModerationLog.insert_log(%{
752 action: "report_update",
759 {:error, message} -> %{id: report["id"], error: message}
763 case Enum.any?(result, &Map.has_key?(&1, :error)) do
764 true -> json_response(conn, :bad_request, result)
765 false -> json_response(conn, :no_content, "")
769 def report_notes_create(%{assigns: %{user: user}} = conn, %{
773 with {:ok, _} <- ReportNote.create(user.id, report_id, content) do
774 ModerationLog.insert_log(%{
775 action: "report_note",
777 subject: Activity.get_by_id(report_id),
781 json_response(conn, :no_content, "")
783 _ -> json_response(conn, :bad_request, "")
787 def report_notes_delete(%{assigns: %{user: user}} = conn, %{
789 "report_id" => report_id
791 with {:ok, note} <- ReportNote.destroy(note_id) do
792 ModerationLog.insert_log(%{
793 action: "report_note_delete",
795 subject: Activity.get_by_id(report_id),
799 json_response(conn, :no_content, "")
801 _ -> json_response(conn, :bad_request, "")
805 def list_log(conn, params) do
806 {page, page_size} = page_params(params)
809 ModerationLog.get_all(%{
811 page_size: page_size,
812 start_date: params["start_date"],
813 end_date: params["end_date"],
814 user_id: params["user_id"],
815 search: params["search"]
819 |> put_view(ModerationLogView)
820 |> render("index.json", %{log: log})
823 def config_descriptions(conn, _params) do
824 descriptions = Enum.filter(@descriptions, &whitelisted_config?/1)
826 json(conn, descriptions)
829 def config_show(conn, %{"only_db" => true}) do
830 with :ok <- configurable_from_database() do
831 configs = Pleroma.Repo.all(ConfigDB)
834 |> put_view(ConfigView)
835 |> render("index.json", %{configs: configs})
839 def config_show(conn, _params) do
840 with :ok <- configurable_from_database() do
841 configs = ConfigDB.get_all_as_keyword()
844 Config.Holder.default_config()
845 |> ConfigDB.merge(configs)
846 |> Enum.map(fn {group, values} ->
847 Enum.map(values, fn {key, value} ->
849 if configs[group][key] do
850 ConfigDB.get_db_keys(configs[group][key], key)
853 db_value = configs[group][key]
856 if !is_nil(db_value) and Keyword.keyword?(db_value) and
857 ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do
858 ConfigDB.merge_group(group, key, value, db_value)
864 group: ConfigDB.convert(group),
865 key: ConfigDB.convert(key),
866 value: ConfigDB.convert(merged_value)
869 if db, do: Map.put(setting, :db, db), else: setting
874 json(conn, %{configs: merged, need_reboot: Restarter.Pleroma.need_reboot?()})
878 def config_update(conn, %{"configs" => configs}) do
879 with :ok <- configurable_from_database() do
882 |> Enum.filter(&whitelisted_config?/1)
884 %{"group" => group, "key" => key, "delete" => true} = params ->
885 ConfigDB.delete(%{group: group, key: key, subkeys: params["subkeys"]})
887 %{"group" => group, "key" => key, "value" => value} ->
888 ConfigDB.update_or_create(%{group: group, key: key, value: value})
890 |> Enum.split_with(fn result -> elem(result, 0) == :error end)
894 |> Enum.map(fn {:ok, config} ->
895 Map.put(config, :db, ConfigDB.get_db_keys(config))
897 |> Enum.split_with(fn config ->
898 Ecto.get_meta(config, :state) == :deleted
901 Config.TransferTask.load_and_update_env(deleted, false)
903 if !Restarter.Pleroma.need_reboot?() do
904 changed_reboot_settings? =
906 |> Enum.any?(fn config ->
907 group = ConfigDB.from_string(config.group)
908 key = ConfigDB.from_string(config.key)
909 value = ConfigDB.from_binary(config.value)
910 Config.TransferTask.pleroma_need_restart?(group, key, value)
913 if changed_reboot_settings?, do: Restarter.Pleroma.need_reboot()
917 |> put_view(ConfigView)
918 |> render("index.json", %{configs: updated, need_reboot: Restarter.Pleroma.need_reboot?()})
922 def restart(conn, _params) do
923 with :ok <- configurable_from_database() do
924 Restarter.Pleroma.restart(Config.get(:env), 50)
930 def need_reboot(conn, _params) do
931 json(conn, %{need_reboot: Restarter.Pleroma.need_reboot?()})
934 defp configurable_from_database do
935 if Config.get(:configurable_from_database) do
938 {:error, "To use this endpoint you need to enable configuration from database."}
942 defp whitelisted_config?(group, key) do
943 if whitelisted_configs = Config.get(:database_config_whitelist) do
944 Enum.any?(whitelisted_configs, fn
945 {whitelisted_group} ->
946 group == inspect(whitelisted_group)
948 {whitelisted_group, whitelisted_key} ->
949 group == inspect(whitelisted_group) && key == inspect(whitelisted_key)
956 defp whitelisted_config?(%{"group" => group, "key" => key}) do
957 whitelisted_config?(group, key)
960 defp whitelisted_config?(%{:group => group} = config) do
961 whitelisted_config?(group, config[:key])
964 def reload_emoji(conn, _params) do
965 Pleroma.Emoji.reload()
970 def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
971 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
973 User.toggle_confirmation(users)
975 ModerationLog.insert_log(%{
978 action: "confirm_email"
984 def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
985 users = nicknames |> Enum.map(&User.get_cached_by_nickname/1)
987 User.try_send_confirmation_email(users)
989 ModerationLog.insert_log(%{
992 action: "resend_confirmation_email"
998 def oauth_app_create(conn, params) do
1000 if params["name"] do
1001 Map.put(params, "client_name", params["name"])
1007 case App.create(params) do
1009 AppView.render("show.json", %{app: app, admin: true})
1011 {:error, changeset} ->
1012 App.errors(changeset)
1018 def oauth_app_update(conn, params) do
1020 if params["name"] do
1021 Map.put(params, "client_name", params["name"])
1026 with {:ok, app} <- App.update(params) do
1027 json(conn, AppView.render("show.json", %{app: app, admin: true}))
1029 {:error, changeset} ->
1030 json(conn, App.errors(changeset))
1033 json_response(conn, :bad_request, "")
1037 def oauth_app_list(conn, params) do
1038 {page, page_size} = page_params(params)
1041 client_name: params["name"],
1042 client_id: params["client_id"],
1044 page_size: page_size
1048 if Map.has_key?(params, "trusted") do
1049 Map.put(search_params, :trusted, params["trusted"])
1054 with {:ok, apps, count} <- App.search(search_params) do
1057 AppView.render("index.json",
1060 page_size: page_size,
1067 def oauth_app_delete(conn, params) do
1068 with {:ok, _app} <- App.destroy(params["id"]) do
1069 json_response(conn, :no_content, "")
1071 _ -> json_response(conn, :bad_request, "")
1075 def stats(conn, _) do
1076 count = Stats.get_status_visibility_count()
1079 |> json(%{"status_visibility" => count})
1082 defp page_params(params) do
1083 {get_page(params["page"]), get_page_size(params["page_size"])}
1086 defp get_page(page_string) when is_nil(page_string), do: 1
1088 defp get_page(page_string) do
1089 case Integer.parse(page_string) do
1095 defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
1097 defp get_page_size(page_size_string) do
1098 case Integer.parse(page_size_string) do
1099 {page_size, _} -> page_size
1100 :error -> @users_page_size