diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pleroma/application.ex | 3 | ||||
-rw-r--r-- | lib/pleroma/web/activity_pub/activity_pub.ex | 3 | ||||
-rw-r--r-- | lib/pleroma/web/endpoint.ex | 1 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_api_controller.ex | 51 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/mastodon_socket.ex | 34 | ||||
-rw-r--r-- | lib/pleroma/web/mastodon_api/views/status_view.ex | 2 | ||||
-rw-r--r-- | lib/pleroma/web/router.ex | 1 | ||||
-rw-r--r-- | lib/pleroma/web/streamer.ex | 76 | ||||
-rw-r--r-- | lib/transports.ex | 77 |
9 files changed, 246 insertions, 2 deletions
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 1f0a05568..5422cbc28 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -19,7 +19,8 @@ defmodule Pleroma.Application do ttl_interval: 1000, limit: 2500 ]]), - worker(Pleroma.Web.Federator, []) + worker(Pleroma.Web.Federator, []), + worker(Pleroma.Web.Streamer, []) ] # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 1624c6545..35536a1e4 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -22,6 +22,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do with create_data <- make_create_data(%{to: to, actor: actor, published: published, context: context, object: object}, additional), {:ok, activity} <- insert(create_data, local), :ok <- maybe_federate(activity) do + if activity.data["type"] == "Create" and Enum.member?(activity.data["to"], "https://www.w3.org/ns/activitystreams#Public") do + Pleroma.Web.Streamer.stream("public", activity) + end {:ok, activity} end end diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index a1b4108cd..dc1ba2a05 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -2,6 +2,7 @@ defmodule Pleroma.Web.Endpoint do use Phoenix.Endpoint, otp_app: :pleroma socket "/socket", Pleroma.Web.UserSocket + socket "/api/v1", Pleroma.Web.MastodonAPI.MastodonSocket # Serve at "/" the static files from "priv/static" directory. # diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index c28e20ed1..fb06d55c1 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -23,6 +23,57 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end + def update_credentials(%{assigns: %{user: user}} = conn, params) do + params = if bio = params["note"] do + Map.put(params, "bio", bio) + else + params + end + + params = if name = params["display_name"] do + Map.put(params, "name", name) + else + params + end + + user = if avatar = params["avatar"] do + with %Plug.Upload{} <- avatar, + {:ok, object} <- ActivityPub.upload(avatar), + change = Ecto.Changeset.change(user, %{avatar: object.data}), + {:ok, user} = Repo.update(change) do + user + else + _e -> user + end + else + user + end + + user = if banner = params["header"] do + with %Plug.Upload{} <- banner, + {:ok, object} <- ActivityPub.upload(banner), + new_info <- Map.put(user.info, "banner", object.data), + change <- User.info_changeset(user, %{info: new_info}), + {:ok, user} <- Repo.update(change) do + user + else + _e -> user + end + else + user + end + + with changeset <- User.update_changeset(user, params), + {:ok, user} <- Repo.update(changeset) do + json conn, AccountView.render("account.json", %{user: user}) + else + _e -> + conn + |> put_status(403) + |> json(%{error: "Invalid request"}) + end + end + def verify_credentials(%{assigns: %{user: user}} = conn, params) do account = AccountView.render("account.json", %{user: user}) json(conn, account) diff --git a/lib/pleroma/web/mastodon_api/mastodon_socket.ex b/lib/pleroma/web/mastodon_api/mastodon_socket.ex new file mode 100644 index 000000000..f9c8cec32 --- /dev/null +++ b/lib/pleroma/web/mastodon_api/mastodon_socket.ex @@ -0,0 +1,34 @@ +defmodule Pleroma.Web.MastodonAPI.MastodonSocket do + use Phoenix.Socket + + transport :streaming, Phoenix.Transports.WebSocket.Raw, + timeout: :infinity # We never receive data. + + def connect(params, socket) do + if params["stream"] == "public" do + socket = socket + |> assign(:topic, params["stream"]) + Pleroma.Web.Streamer.add_socket(params["stream"], socket) + {:ok, socket} + else + :error + end + end + + def id(socket), do: nil + + def handle(:text, message, state) do + IO.inspect message + #| :ok + #| state + #| {:text, message} + #| {:text, message, state} + #| {:close, "Goodbye!"} + {:text, message} + end + + def handle(:closed, reason, %{socket: socket}) do + topic = socket.assigns[:topic] + Pleroma.Web.Streamer.remove_socket(topic, socket) + end +end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index a0acdb0ac..d97b2acb4 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -46,7 +46,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do website: nil }, language: nil, - emoji: [] + emojis: [] } end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 0a0aea966..4c74736e2 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -53,6 +53,7 @@ defmodule Pleroma.Web.Router do scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through :authenticated_api + patch "/accounts/update_credentials", MastodonAPIController, :update_credentials get "/accounts/verify_credentials", MastodonAPIController, :verify_credentials get "/accounts/relationships", MastodonAPIController, :relationships get "/accounts/search", MastodonAPIController, :account_search diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex new file mode 100644 index 000000000..3a7b91743 --- /dev/null +++ b/lib/pleroma/web/streamer.ex @@ -0,0 +1,76 @@ +defmodule Pleroma.Web.Streamer do + use GenServer + require Logger + import Plug.Conn + + def start_link do + spawn(fn -> + Process.sleep(1000 * 30) # 30 seconds + 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 -> + Process.sleep(1000 * 30) # 30 seconds + GenServer.cast(__MODULE__, %{action: :ping}) + 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}") + Enum.each(topics[topic] || [], fn (socket) -> + json = %{ + event: "update", + payload: Pleroma.Web.MastodonAPI.StatusView.render("status.json", activity: item) |> Poison.encode! + } |> Poison.encode! + + send socket.transport_pid, {:text, json} + end) + {:noreply, topics} + end + + def handle_cast(%{action: :add, topic: topic, socket: socket}, sockets) do + 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}") + IO.inspect(sockets) + {:noreply, sockets} + end + + def handle_cast(%{action: :remove, topic: topic, socket: socket}, sockets) do + 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}") + IO.inspect(sockets) + {:noreply, sockets} + end + + def handle_cast(m, state) do + IO.inspect("Unknown: #{inspect(m)}, #{inspect(state)}") + {:noreply, state} + end +end diff --git a/lib/transports.ex b/lib/transports.ex new file mode 100644 index 000000000..5600a4fdd --- /dev/null +++ b/lib/transports.ex @@ -0,0 +1,77 @@ +defmodule Phoenix.Transports.WebSocket.Raw do + import Plug.Conn, only: [ + fetch_query_params: 1, + send_resp: 3 + ] + alias Phoenix.Socket.Transport + + def default_config do + [ + timeout: 60_000, + transport_log: false, + cowboy: Phoenix.Endpoint.CowboyWebSocket + ] + end + + def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do + {_, opts} = handler.__transport__(transport) + + conn = conn + |> fetch_query_params + |> Transport.transport_log(opts[:transport_log]) + |> Transport.force_ssl(handler, endpoint, opts) + |> Transport.check_origin(handler, endpoint, opts) + + case conn do + %{halted: false} = conn -> + case Transport.connect(endpoint, handler, transport, __MODULE__, nil, conn.params) do + {:ok, socket} -> + {:ok, conn, {__MODULE__, {socket, opts}}} + :error -> + send_resp(conn, :forbidden, "") + {:error, conn} + end + _ -> + {:error, conn} + end + end + + def init(conn, _) do + send_resp(conn, :bad_request, "") + {:error, conn} + end + + def ws_init({socket, config}) do + Process.flag(:trap_exit, true) + {:ok, %{socket: socket}, config[:timeout]} + end + + def ws_handle(op, data, state) do + state.socket.handler + |> apply(:handle, [op, data, state]) + |> case do + {op, data} -> + {:reply, {op, data}, state} + {op, data, state} -> + {:reply, {op, data}, state} + %{} = state -> + {:ok, state} + _ -> + {:ok, state} + end + end + + def ws_info({op, data} = tuple, state) do + {:reply, tuple, state} + end + + def ws_info(_tuple, state), do: {:ok, state} + + def ws_close(state) do + ws_handle(:closed, :normal, state) + end + + def ws_terminate(reason, state) do + ws_handle(:closed, reason, state) + end +end |