Merge branch 'restrict-domain' into 'develop'
View a remote server's timeline See merge request pleroma/pleroma!2713
This commit is contained in:
commit
5db4c823b2
|
@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- App metrics: ability to restrict access to specified IP whitelist.
|
- App metrics: ability to restrict access to specified IP whitelist.
|
||||||
- Account backup
|
- Account backup
|
||||||
- Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
|
- Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
|
||||||
|
- Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,13 @@ Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mas
|
||||||
## Timelines
|
## Timelines
|
||||||
|
|
||||||
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
||||||
|
|
||||||
Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||||
|
|
||||||
Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
|
Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
|
||||||
|
|
||||||
|
Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance).
|
||||||
|
|
||||||
## Statuses
|
## Statuses
|
||||||
|
|
||||||
- `visibility`: has an additional possible value `list`
|
- `visibility`: has an additional possible value `list`
|
||||||
|
@ -249,6 +253,8 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
|
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
|
||||||
|
|
||||||
|
For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
|
||||||
|
|
||||||
## Not implemented
|
## Not implemented
|
||||||
|
|
||||||
Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.
|
Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.
|
||||||
|
|
|
@ -40,7 +40,8 @@ defp visibility_tags(object, activity) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
|
defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
|
||||||
tags ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
|
tags ++
|
||||||
|
remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp item_creation_tags(tags, _, _) do
|
defp item_creation_tags(tags, _, _) do
|
||||||
|
@ -55,9 +56,19 @@ defp hashtags_to_topics(%{data: %{"tag" => tags}}) do
|
||||||
|
|
||||||
defp hashtags_to_topics(_), do: []
|
defp hashtags_to_topics(_), do: []
|
||||||
|
|
||||||
|
defp remote_topics(%{local: true}), do: []
|
||||||
|
|
||||||
|
defp remote_topics(%{actor: actor}) when is_binary(actor),
|
||||||
|
do: ["public:remote:" <> URI.parse(actor).host]
|
||||||
|
|
||||||
|
defp remote_topics(_), do: []
|
||||||
|
|
||||||
defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: []
|
defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: []
|
||||||
|
|
||||||
defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"]
|
defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"]
|
||||||
|
|
||||||
|
defp attachment_topics(_object, %{actor: actor}) when is_binary(actor),
|
||||||
|
do: ["public:media", "public:remote:media:" <> URI.parse(actor).host]
|
||||||
|
|
||||||
defp attachment_topics(_object, _act), do: ["public:media"]
|
defp attachment_topics(_object, _act), do: ["public:media"]
|
||||||
end
|
end
|
||||||
|
|
|
@ -937,16 +937,11 @@ defp restrict_muted_reblogs(query, %{muting_user: %User{} = user} = opts) do
|
||||||
|
|
||||||
defp restrict_muted_reblogs(query, _), do: query
|
defp restrict_muted_reblogs(query, _), do: query
|
||||||
|
|
||||||
defp restrict_instance(query, %{instance: instance}) do
|
defp restrict_instance(query, %{instance: instance}) when is_binary(instance) do
|
||||||
users =
|
|
||||||
from(
|
from(
|
||||||
u in User,
|
activity in query,
|
||||||
select: u.ap_id,
|
where: fragment("split_part(actor::text, '/'::text, 3) = ?", ^instance)
|
||||||
where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
|
|
||||||
)
|
)
|
||||||
|> Repo.all()
|
|
||||||
|
|
||||||
from(activity in query, where: activity.actor in ^users)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_instance(query, _), do: query
|
defp restrict_instance(query, _), do: query
|
||||||
|
|
|
@ -59,6 +59,7 @@ def public_operation do
|
||||||
security: [%{"oAuth" => ["read:statuses"]}],
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
parameters: [
|
parameters: [
|
||||||
local_param(),
|
local_param(),
|
||||||
|
instance_param(),
|
||||||
only_media_param(),
|
only_media_param(),
|
||||||
with_muted_param(),
|
with_muted_param(),
|
||||||
exclude_visibilities_param(),
|
exclude_visibilities_param(),
|
||||||
|
@ -158,6 +159,15 @@ defp local_param do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp instance_param do
|
||||||
|
Operation.parameter(
|
||||||
|
:instance,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :string},
|
||||||
|
"Show only statuses from the given domain"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp with_muted_param do
|
defp with_muted_param do
|
||||||
Operation.parameter(:with_muted, :query, BooleanLike, "Include activities by muted users")
|
Operation.parameter(:with_muted, :query, BooleanLike, "Include activities by muted users")
|
||||||
end
|
end
|
||||||
|
|
|
@ -111,6 +111,7 @@ def public(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> Map.put(:blocking_user, user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put(:muting_user, user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put(:reply_filtering_user, user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|
|> Map.put(:instance, params[:instance])
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -57,6 +57,15 @@ def get_topic("hashtag", _user, _oauth_token, %{"tag" => tag} = _params) do
|
||||||
{:ok, "hashtag:" <> tag}
|
{:ok, "hashtag:" <> tag}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Allow remote instance streams.
|
||||||
|
def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||||
|
{:ok, "public:remote:" <> instance}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||||
|
{:ok, "public:remote:media:" <> instance}
|
||||||
|
end
|
||||||
|
|
||||||
# Expand user streams.
|
# Expand user streams.
|
||||||
def get_topic(
|
def get_topic(
|
||||||
stream,
|
stream,
|
||||||
|
|
|
@ -97,6 +97,20 @@ test "only converts strings to hash tags", %{
|
||||||
|
|
||||||
refute Enum.member?(topics, "hashtag:2")
|
refute Enum.member?(topics, "hashtag:2")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "non-local action produces public:remote topic", %{activity: activity} do
|
||||||
|
activity = %{activity | local: false, actor: "https://lain.com/users/lain"}
|
||||||
|
topics = Topics.get_activity_topics(activity)
|
||||||
|
|
||||||
|
assert Enum.member?(topics, "public:remote:lain.com")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "local action doesn't produce public:remote topic", %{activity: activity} do
|
||||||
|
activity = %{activity | local: true, actor: "https://lain.com/users/lain"}
|
||||||
|
topics = Topics.get_activity_topics(activity)
|
||||||
|
|
||||||
|
refute Enum.member?(topics, "public:remote:lain.com")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "public visibility create events with attachments" do
|
describe "public visibility create events with attachments" do
|
||||||
|
@ -128,6 +142,13 @@ test "non-local doesn't produce public:local:media topics", %{activity: activity
|
||||||
|
|
||||||
refute Enum.member?(topics, "public:local:media")
|
refute Enum.member?(topics, "public:local:media")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "non-local action produces public:remote:media topic", %{activity: activity} do
|
||||||
|
activity = %{activity | local: false, actor: "https://lain.com/users/lain"}
|
||||||
|
topics = Topics.get_activity_topics(activity)
|
||||||
|
|
||||||
|
assert Enum.member?(topics, "public:remote:media:lain.com")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "non-public visibility" do
|
describe "non-public visibility" do
|
||||||
|
|
|
@ -49,6 +49,7 @@ test "requires authentication and a valid token for protected streams" do
|
||||||
test "allows public streams without authentication" do
|
test "allows public streams without authentication" do
|
||||||
assert {:ok, _} = start_socket("?stream=public")
|
assert {:ok, _} = start_socket("?stream=public")
|
||||||
assert {:ok, _} = start_socket("?stream=public:local")
|
assert {:ok, _} = start_socket("?stream=public:local")
|
||||||
|
assert {:ok, _} = start_socket("?stream=public:remote&instance=lain.com")
|
||||||
assert {:ok, _} = start_socket("?stream=hashtag&tag=lain")
|
assert {:ok, _} = start_socket("?stream=hashtag&tag=lain")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -844,8 +844,8 @@ test "sets password_reset_pending to true", %{conn: conn} do
|
||||||
|
|
||||||
describe "instances" do
|
describe "instances" do
|
||||||
test "GET /instances/:instance/statuses", %{conn: conn} do
|
test "GET /instances/:instance/statuses", %{conn: conn} do
|
||||||
user = insert(:user, local: false, nickname: "archaeme@archae.me")
|
user = insert(:user, local: false, ap_id: "https://archae.me/users/archaeme")
|
||||||
user2 = insert(:user, local: false, nickname: "test@test.com")
|
user2 = insert(:user, local: false, ap_id: "https://test.com/users/test")
|
||||||
insert_pair(:note_activity, user: user)
|
insert_pair(:note_activity, user: user)
|
||||||
activity = insert(:note_activity, user: user2)
|
activity = insert(:note_activity, user: user2)
|
||||||
|
|
||||||
|
|
|
@ -147,6 +147,18 @@ test "doesn't return replies if follow is posting with users from blocked domain
|
||||||
activities = json_response_and_validate_schema(res_conn, 200)
|
activities = json_response_and_validate_schema(res_conn, 200)
|
||||||
[%{"id" => ^activity_id}] = activities
|
[%{"id" => ^activity_id}] = activities
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "can be filtered by instance", %{conn: conn} do
|
||||||
|
user = insert(:user, ap_id: "https://lain.com/users/lain")
|
||||||
|
insert(:note_activity, local: false)
|
||||||
|
insert(:note_activity, local: false)
|
||||||
|
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: "test"})
|
||||||
|
|
||||||
|
conn = get(conn, "/api/v1/timelines/public?instance=lain.com")
|
||||||
|
|
||||||
|
assert length(json_response_and_validate_schema(conn, :ok)) == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_and_remote_activities do
|
defp local_and_remote_activities do
|
||||||
|
|
|
@ -29,6 +29,14 @@ test "allows public" do
|
||||||
assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil)
|
assert {:ok, "public:local:media"} = Streamer.get_topic("public:local:media", nil, nil)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "allows instance streams" do
|
||||||
|
assert {:ok, "public:remote:lain.com"} =
|
||||||
|
Streamer.get_topic("public:remote", nil, nil, %{"instance" => "lain.com"})
|
||||||
|
|
||||||
|
assert {:ok, "public:remote:media:lain.com"} =
|
||||||
|
Streamer.get_topic("public:remote:media", nil, nil, %{"instance" => "lain.com"})
|
||||||
|
end
|
||||||
|
|
||||||
test "allows hashtag streams" do
|
test "allows hashtag streams" do
|
||||||
assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"})
|
assert {:ok, "hashtag:cofe"} = Streamer.get_topic("hashtag", nil, nil, %{"tag" => "cofe"})
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue