alias Pleroma.Web.Plugs.RateLimiter
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(:skip_public_check when action in [:public, :hashtag])
+ plug(:skip_public_check when action in [:public, :hashtag, :bubble])
# TODO: Replace with a macro when there is a Phoenix release with the following commit in it:
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
plug(RateLimiter, [name: :timeline, bucket_name: :home_timeline] when action == :home)
plug(RateLimiter, [name: :timeline, bucket_name: :hashtag_timeline] when action == :hashtag)
plug(RateLimiter, [name: :timeline, bucket_name: :list_timeline] when action == :list)
+ plug(RateLimiter, [name: :timeline, bucket_name: :bubble_timeline] when action == :bubble)
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct])
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)
plug(
OAuthScopesPlug,
%{scopes: ["read:statuses"], fallback: :proceed_unauthenticated}
- when action in [:public, :hashtag]
+ when action in [:public, :hashtag, :bubble]
)
+ require Logger
+
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TimelineOperation
# GET /api/v1/timelines/home
def home(%{assigns: %{user: user}} = conn, params) do
+ %{nickname: nickname} = user
+
+ Logger.debug("TimelineController.home: #{nickname}")
+
+ followed_hashtags =
+ user
+ |> User.followed_hashtags()
+ |> Enum.map(& &1.id)
+
params =
params
|> Map.put(:type, ["Create", "Announce"])
|> Map.put(:announce_filtering_user, user)
|> Map.put(:user, user)
|> Map.put(:local_only, params[:local])
+ |> Map.put(:followed_hashtags, followed_hashtags)
|> Map.delete(:local)
+ Logger.debug("TimelineController.home: #{nickname} - fetching activities")
+
activities =
[user.ap_id | User.following(user)]
|> ActivityPub.fetch_activities(params)
|> Enum.reverse()
+ Logger.debug("TimelineController.home: #{nickname} - rendering")
+
conn
|> add_link_headers(activities)
|> render("index.json",
# GET /api/v1/timelines/direct
def direct(%{assigns: %{user: user}} = conn, params) do
+ Logger.debug("TimelineController.direct: #{user.nickname}")
+
params =
params
|> Map.put(:type, "Create")
|> Map.put(:user, user)
|> Map.put(:visibility, "direct")
+ Logger.debug("TimelineController.direct: #{user.nickname} - fetching activities")
+
activities =
[user.ap_id]
|> ActivityPub.fetch_activities_query(params)
|> Pagination.fetch_paginated(params)
+ Logger.debug("TimelineController.direct: #{user.nickname} - rendering")
+
conn
|> add_link_headers(activities)
|> render("index.json",
)
end
- defp restrict_unauthenticated?(true = _local_only) do
- Config.restrict_unauthenticated_access?(:timelines, :local)
- end
-
- defp restrict_unauthenticated?(_) do
- Config.restrict_unauthenticated_access?(:timelines, :federated)
+ defp restrict_unauthenticated?(type) do
+ Config.restrict_unauthenticated_access?(:timelines, type)
end
# GET /api/v1/timelines/public
def public(%{assigns: %{user: user}} = conn, params) do
+ Logger.debug("TimelineController.public")
local_only = params[:local]
+ timeline_type = if local_only, do: :local, else: :federated
+
+ with {:enabled, true} <-
+ {:enabled, local_only || Config.get([:instance, :federated_timeline_available], true)},
+ {:authenticated, true} <-
+ {:authenticated, !(is_nil(user) and restrict_unauthenticated?(timeline_type))} do
+ Logger.debug("TimelineController.public: fetching activities")
- if is_nil(user) and restrict_unauthenticated?(local_only) do
- fail_on_bad_auth(conn)
- else
activities =
params
|> Map.put(:type, ["Create"])
|> Map.put(:muting_user, user)
|> Map.put(:reply_filtering_user, user)
|> Map.put(:instance, params[:instance])
+ # Restricts unfederated content to authenticated users
+ |> Map.put(:includes_local_public, not is_nil(user))
|> ActivityPub.fetch_public_activities()
+ Logger.debug("TimelineController.public: rendering")
+
conn
|> add_link_headers(activities, %{"local" => local_only})
|> render("index.json",
as: :activity,
with_muted: Map.get(params, :with_muted, false)
)
+ else
+ {:enabled, false} ->
+ conn
+ |> put_status(404)
+ |> json(%{error: "Federated timeline is disabled"})
+
+ {:authenticated, false} ->
+ fail_on_bad_auth(conn)
+ end
+ end
+
+ # GET /api/v1/timelines/bubble
+ def bubble(%{assigns: %{user: user}} = conn, params) do
+ Logger.debug("TimelineController.bubble")
+
+ if is_nil(user) and restrict_unauthenticated?(:bubble) do
+ fail_on_bad_auth(conn)
+ else
+ bubble_instances =
+ Enum.uniq(
+ Config.get([:instance, :local_bubble], []) ++
+ [Pleroma.Web.Endpoint.host()]
+ )
+
+ Logger.debug("TimelineController.bubble: fetching activities")
+
+ activities =
+ params
+ |> Map.put(:type, ["Create"])
+ |> Map.put(:blocking_user, user)
+ |> Map.put(:muting_user, user)
+ |> Map.put(:reply_filtering_user, user)
+ |> Map.put(:instance, bubble_instances)
+ |> ActivityPub.fetch_public_activities()
+
+ Logger.debug("TimelineController.bubble: rendering")
+
+ conn
+ |> add_link_headers(activities)
+ |> render("index.json",
+ activities: activities,
+ for: user,
+ as: :activity,
+ with_muted: Map.get(params, :with_muted, false)
+ )
end
end
def hashtag(%{assigns: %{user: user}} = conn, params) do
local_only = params[:local]
- if is_nil(user) and restrict_unauthenticated?(local_only) do
+ if is_nil(user) and restrict_unauthenticated?(if local_only, do: :local, else: :federated) do
fail_on_bad_auth(conn)
else
activities = hashtag_fetching(params, user, local_only)
|> ActivityPub.fetch_activities_bounded(following, params)
|> Enum.reverse()
- render(conn, "index.json",
+ conn
+ |> add_link_headers(activities)
+ |> render("index.json",
activities: activities,
for: user,
as: :activity,