Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85806458
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/pleroma/web/mastodon_api/mastodon_socket.ex b/lib/pleroma/web/mastodon_api/mastodon_socket.ex
index 0f3d5ff7c..f3c13d1aa 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_socket.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_socket.ex
@@ -1,63 +1,80 @@
defmodule Pleroma.Web.MastodonAPI.MastodonSocket do
use Phoenix.Socket
alias Pleroma.Web.OAuth.Token
alias Pleroma.{User, Repo}
transport(
:streaming,
Phoenix.Transports.WebSocket.Raw,
# We never receive data.
timeout: :infinity
)
- def connect(params, socket) do
- with token when not is_nil(token) <- params["access_token"],
- %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
+ def connect(%{"access_token" => token} = params, socket) do
+ with %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
%User{} = user <- Repo.get(User, user_id),
stream
when stream in [
"public",
"public:local",
"public:media",
"public:local:media",
"user",
"direct",
"list",
"hashtag"
] <- params["stream"] do
topic =
case stream do
"hashtag" -> "hashtag:#{params["tag"]}"
"list" -> "list:#{params["list"]}"
_ -> stream
end
socket =
socket
|> assign(:topic, topic)
|> assign(:user, user)
Pleroma.Web.Streamer.add_socket(topic, socket)
{:ok, socket}
else
_e -> :error
end
end
+ def connect(%{"stream" => stream} = params, socket)
+ when stream in ["public", "public:local", "hashtag"] do
+ topic =
+ case stream do
+ "hashtag" -> "hashtag:#{params["tag"]}"
+ _ -> stream
+ end
+
+ with socket =
+ socket
+ |> assign(:topic, topic) do
+ Pleroma.Web.Streamer.add_socket(topic, socket)
+ {:ok, socket}
+ else
+ _e -> :error
+ end
+ end
+
def id(_), do: nil
def handle(:text, message, _state) do
# | :ok
# | state
# | {:text, message}
# | {:text, message, state}
# | {:close, "Goodbye!"}
{:text, message}
end
def handle(:closed, _, %{socket: socket}) do
topic = socket.assigns[:topic]
Pleroma.Web.Streamer.remove_socket(topic, socket)
end
end
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index 6b6d40346..5cab62c85 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -1,203 +1,224 @@
defmodule Pleroma.Web.Streamer do
use GenServer
require Logger
alias Pleroma.{User, Notification, Activity, Object, Repo}
alias Pleroma.Web.ActivityPub.ActivityPub
def init(args) do
{:ok, args}
end
def start_link do
spawn(fn ->
# 30 seconds
Process.sleep(1000 * 30)
GenServer.cast(__MODULE__, %{action: :ping})
end)
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
def add_socket(topic, socket) do
GenServer.cast(__MODULE__, %{action: :add, socket: socket, topic: topic})
end
def remove_socket(topic, socket) do
GenServer.cast(__MODULE__, %{action: :remove, socket: socket, topic: topic})
end
def stream(topic, item) do
GenServer.cast(__MODULE__, %{action: :stream, topic: topic, item: item})
end
def handle_cast(%{action: :ping}, topics) do
Map.values(topics)
|> List.flatten()
|> Enum.each(fn socket ->
Logger.debug("Sending keepalive ping")
send(socket.transport_pid, {:text, ""})
end)
spawn(fn ->
# 30 seconds
Process.sleep(1000 * 30)
GenServer.cast(__MODULE__, %{action: :ping})
end)
{:noreply, topics}
end
def handle_cast(%{action: :stream, topic: "direct", item: item}, topics) do
recipient_topics =
User.get_recipients_from_activity(item)
|> Enum.map(fn %{id: id} -> "direct:#{id}" end)
Enum.each(recipient_topics || [], fn user_topic ->
Logger.debug("Trying to push direct message to #{user_topic}\n\n")
push_to_socket(topics, user_topic, item)
end)
{:noreply, topics}
end
def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do
author = User.get_cached_by_ap_id(item.data["actor"])
# filter the recipient list if the activity is not public, see #270.
recipient_lists =
case ActivityPub.is_public?(item) do
true ->
Pleroma.List.get_lists_from_activity(item)
_ ->
Pleroma.List.get_lists_from_activity(item)
|> Enum.filter(fn list ->
owner = Repo.get(User, list.user_id)
author.follower_address in owner.following
end)
end
recipient_topics =
recipient_lists
|> Enum.map(fn %{id: id} -> "list:#{id}" end)
Enum.each(recipient_topics || [], fn list_topic ->
Logger.debug("Trying to push message to #{list_topic}\n\n")
push_to_socket(topics, list_topic, item)
end)
{:noreply, topics}
end
def handle_cast(%{action: :stream, topic: "user", item: %Notification{} = item}, topics) do
topic = "user:#{item.user_id}"
Enum.each(topics[topic] || [], fn socket ->
json =
%{
event: "notification",
payload:
Pleroma.Web.MastodonAPI.MastodonAPIController.render_notification(
socket.assigns["user"],
item
)
|> Jason.encode!()
}
|> Jason.encode!()
send(socket.transport_pid, {:text, json})
end)
{:noreply, topics}
end
def handle_cast(%{action: :stream, topic: "user", item: item}, topics) do
Logger.debug("Trying to push to users")
recipient_topics =
User.get_recipients_from_activity(item)
|> Enum.map(fn %{id: id} -> "user:#{id}" end)
Enum.each(recipient_topics, fn topic ->
push_to_socket(topics, topic, item)
end)
{:noreply, topics}
end
def handle_cast(%{action: :stream, topic: topic, item: item}, topics) do
Logger.debug("Trying to push to #{topic}")
Logger.debug("Pushing item to #{topic}")
push_to_socket(topics, topic, item)
{:noreply, topics}
end
def handle_cast(%{action: :add, topic: topic, socket: socket}, sockets) do
topic = internal_topic(topic, socket)
sockets_for_topic = sockets[topic] || []
sockets_for_topic = Enum.uniq([socket | sockets_for_topic])
sockets = Map.put(sockets, topic, sockets_for_topic)
Logger.debug("Got new conn for #{topic}")
{:noreply, sockets}
end
def handle_cast(%{action: :remove, topic: topic, socket: socket}, sockets) do
topic = internal_topic(topic, socket)
sockets_for_topic = sockets[topic] || []
sockets_for_topic = List.delete(sockets_for_topic, socket)
sockets = Map.put(sockets, topic, sockets_for_topic)
Logger.debug("Removed conn for #{topic}")
{:noreply, sockets}
end
def handle_cast(m, state) do
Logger.info("Unknown: #{inspect(m)}, #{inspect(state)}")
{:noreply, state}
end
defp represent_update(%Activity{} = activity, %User{} = user) do
%{
event: "update",
payload:
Pleroma.Web.MastodonAPI.StatusView.render(
"status.json",
activity: activity,
for: user
)
|> Jason.encode!()
}
|> Jason.encode!()
end
+ defp represent_update(%Activity{} = activity) do
+ %{
+ event: "update",
+ payload:
+ Pleroma.Web.MastodonAPI.StatusView.render(
+ "status.json",
+ activity: activity
+ )
+ |> Jason.encode!()
+ }
+ |> Jason.encode!()
+ end
+
def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do
Enum.each(topics[topic] || [], fn socket ->
# Get the current user so we have up-to-date blocks etc.
- user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
- blocks = user.info["blocks"] || []
+ if socket.assigns[:user] do
+ user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
+ blocks = user.info["blocks"] || []
- parent = Object.normalize(item.data["object"])
+ parent = Object.normalize(item.data["object"])
- unless is_nil(parent) or item.actor in blocks or parent.data["actor"] in blocks do
- send(socket.transport_pid, {:text, represent_update(item, user)})
+ unless is_nil(parent) or item.actor in blocks or parent.data["actor"] in blocks do
+ send(socket.transport_pid, {:text, represent_update(item, user)})
+ end
+ else
+ send(socket.transport_pid, {:text, represent_update(item)})
end
end)
end
def push_to_socket(topics, topic, item) do
Enum.each(topics[topic] || [], fn socket ->
# Get the current user so we have up-to-date blocks etc.
- user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
- blocks = user.info["blocks"] || []
-
- unless item.actor in blocks do
- send(socket.transport_pid, {:text, represent_update(item, user)})
+ if socket.assigns[:user] do
+ user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
+ blocks = user.info["blocks"] || []
+
+ unless item.actor in blocks do
+ send(socket.transport_pid, {:text, represent_update(item, user)})
+ end
+ else
+ send(socket.transport_pid, {:text, represent_update(item)})
end
end)
end
defp internal_topic(topic, socket) when topic in ~w[user direct] do
"#{topic}:#{socket.assigns[:user].id}"
end
defp internal_topic(topic, _), do: topic
end
diff --git a/test/web/mastodon_api/mastodon_socket_test.exs b/test/web/mastodon_api/mastodon_socket_test.exs
new file mode 100644
index 000000000..c7d71defc
--- /dev/null
+++ b/test/web/mastodon_api/mastodon_socket_test.exs
@@ -0,0 +1,33 @@
+defmodule Pleroma.Web.MastodonApi.MastodonSocketTest do
+ use Pleroma.DataCase
+
+ alias Pleroma.Web.MastodonApi.MastodonSocket
+ alias Pleroma.Web.{Streamer, CommonAPI}
+ alias Pleroma.User
+
+ import Pleroma.Factory
+
+ test "public is working when non-authenticated" do
+ user = insert(:user)
+
+ task =
+ Task.async(fn ->
+ assert_receive {:text, _}, 4_000
+ end)
+
+ fake_socket = %{
+ transport_pid: task.pid,
+ assigns: %{}
+ }
+
+ topics = %{
+ "public" => [fake_socket]
+ }
+
+ {:ok, activity} = CommonAPI.post(user, %{"status" => "Test"})
+
+ Streamer.push_to_socket(topics, "public", activity)
+
+ Task.await(task)
+ end
+end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Oct 13, 12:11 AM (1 d, 21 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1786528
Default Alt Text
(10 KB)
Attached To
Mode
rPUBE pleroma-upstream
Attached
Detach File
Event Timeline
Log In to Comment