- Simplified HTTP signature processing
- Rich media will now hard-exit after 5 seconds, to prevent timeline hangs
- HTTP Content Security Policy is now far more strict to prevent any potential XSS/CSS leakages
+- Follow requests are now paginated, matches mastodon API spec, so use the Link header to paginate.
### Fixed
- /api/v1/accounts/lookup will now respect restrict\_unauthenticated
|> Repo.aggregate(:count, :id)
end
- def get_follow_requests(%User{id: id}) do
+ def get_follow_requests_query(%User{id: id}) do
__MODULE__
- |> join(:inner, [r], f in assoc(r, :follower))
+ |> join(:inner, [r], f in assoc(r, :follower), as: :follower)
|> where([r], r.state == ^:follow_pending)
|> where([r], r.following_id == ^id)
- |> where([r, f], f.is_active == true)
- |> select([r, f], f)
- |> Repo.all()
+ |> where([r, follower: f], f.is_active == true)
+ |> select([r, follower: f], f)
end
def following?(%User{id: follower_id}, %User{id: followed_id}) do
defdelegate following(user), to: FollowingRelationship
defdelegate following?(follower, followed), to: FollowingRelationship
defdelegate following_ap_ids(user), to: FollowingRelationship
- defdelegate get_follow_requests(user), to: FollowingRelationship
+ defdelegate get_follow_requests_query(user), to: FollowingRelationship
+
+ def get_follow_requests(user) do
+ get_follow_requests_query(user)
+ |> Repo.all()
+ end
+
defdelegate search(query, opts \\ []), to: User.Search
@doc """
summary: "Retrieve follow requests",
security: [%{"oAuth" => ["read:follows", "follow"]}],
operationId: "FollowRequestController.index",
+ parameters: pagination_params(),
responses: %{
200 =>
Operation.response("Array of Account", "application/json", %Schema{
required: true
)
end
+
+ defp pagination_params do
+ [
+ Operation.parameter(:max_id, :query, :string, "Return items older than this ID"),
+ Operation.parameter(
+ :since_id,
+ :query,
+ :string,
+ "Return the oldest items newer than this ID"
+ ),
+ Operation.parameter(
+ :limit,
+ :query,
+ %Schema{type: :integer, default: 20},
+ "Maximum number of items to return. Will be ignored if it's more than 40"
+ )
+ ]
+ end
end
defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
use Pleroma.Web, :controller
+ import Pleroma.Web.ControllerHelper,
+ only: [add_link_headers: 2]
+
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug
+ alias Pleroma.Pagination
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(:assign_follower when action != :index)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FollowRequestOperation
@doc "GET /api/v1/follow_requests"
- def index(%{assigns: %{user: followed}} = conn, _params) do
- follow_requests = User.get_follow_requests(followed)
+ def index(%{assigns: %{user: followed}} = conn, params) do
+ follow_requests =
+ followed
+ |> User.get_follow_requests_query()
+ |> Pagination.fetch_paginated(params, :keyset, :follower)
- render(conn, "index.json", for: followed, users: follow_requests, as: :user)
+ conn
+ |> add_link_headers(follow_requests)
+ |> render("index.json", for: followed, users: follow_requests, as: :user)
end
@doc "POST /api/v1/follow_requests/:id/authorize"
%User{id: user_id}
) do
count =
- User.get_follow_requests(user)
+ user
+ |> User.get_follow_requests()
|> length()
data
describe "blocks" do
setup do
+ clear_config([:activitypub, :outgoing_blocks], true)
user = insert(:user, local: false)
blocked = insert(:user)
test "it blocks and federates", %{blocker: blocker, blocked: blocked} do
clear_config([:instance, :federating], true)
+ clear_config([:activitypub, :outgoing_blocks], true)
with_mock Pleroma.Web.Federator,
publish: fn _ -> nil end do
import Pleroma.Factory
+ defp extract_next_link_header(header) do
+ [_, next_link] = Regex.run(~r{<(?<next_link>.*)>; rel="next"}, header)
+ next_link
+ end
+
describe "locked accounts" do
setup do
user = insert(:user, is_locked: true)
assert to_string(other_user.id) == relationship["id"]
end
+ test "/api/v1/follow_requests paginates", %{user: user, conn: conn} do
+ for _ <- 1..21 do
+ other_user = insert(:user)
+ {:ok, _, _, _activity} = CommonAPI.follow(other_user, user)
+ {:ok, _, _} = User.follow(other_user, user, :follow_pending)
+ end
+
+ conn = get(conn, "/api/v1/follow_requests")
+ assert length(json_response_and_validate_schema(conn, 200)) == 20
+ assert [link_header] = get_resp_header(conn, "link")
+ assert link_header =~ "rel=\"next\""
+ next_link = extract_next_link_header(link_header)
+ assert next_link =~ "/api/v1/follow_requests"
+ conn = get(conn, next_link)
+ assert length(json_response_and_validate_schema(conn, 200)) == 1
+ end
+
test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do
other_user = insert(:user)