aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/pleroma/benchmark.ex44
-rw-r--r--lib/mix/tasks/pleroma/relay.ex12
-rw-r--r--lib/pleroma/activity.ex1
-rw-r--r--lib/pleroma/application.ex191
-rw-r--r--lib/pleroma/captcha/captcha.ex2
-rw-r--r--lib/pleroma/config/transfer_task.ex2
-rw-r--r--lib/pleroma/conversation.ex11
-rw-r--r--lib/pleroma/conversation/participation.ex54
-rw-r--r--lib/pleroma/conversation/participation_recipient_ship.ex34
-rw-r--r--lib/pleroma/digest_email_worker.ex4
-rw-r--r--lib/pleroma/emoji.ex2
-rw-r--r--lib/pleroma/flake_id.ex2
-rw-r--r--lib/pleroma/gopher/server.ex2
-rw-r--r--lib/pleroma/html.ex2
-rw-r--r--lib/pleroma/reverse_proxy/reverse_proxy.ex12
-rw-r--r--lib/pleroma/scheduled_activity_worker.ex2
-rw-r--r--lib/pleroma/stats.ex60
-rw-r--r--lib/pleroma/user.ex41
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex23
-rw-r--r--lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex3
-rw-r--r--lib/pleroma/web/activity_pub/mrf/reject_non_public.ex3
-rw-r--r--lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex3
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex3
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex9
-rw-r--r--lib/pleroma/web/chat_channel.ex4
-rw-r--r--lib/pleroma/web/common_api/common_api.ex22
-rw-r--r--lib/pleroma/web/common_api/utils.ex35
-rw-r--r--lib/pleroma/web/controller_helper.ex76
-rw-r--r--lib/pleroma/web/federator/retry_queue.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex72
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/views/conversation_view.ex6
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex22
-rw-r--r--lib/pleroma/web/oauth/token/clean_worker.ex26
-rw-r--r--lib/pleroma/web/pleroma_api/pleroma_api_controller.ex73
-rw-r--r--lib/pleroma/web/router.ex15
-rw-r--r--lib/pleroma/web/streamer.ex27
-rw-r--r--lib/pleroma/web/web.ex18
38 files changed, 621 insertions, 305 deletions
diff --git a/lib/mix/tasks/pleroma/benchmark.ex b/lib/mix/tasks/pleroma/benchmark.ex
index 5222cce80..4cc634727 100644
--- a/lib/mix/tasks/pleroma/benchmark.ex
+++ b/lib/mix/tasks/pleroma/benchmark.ex
@@ -26,4 +26,48 @@ defmodule Mix.Tasks.Pleroma.Benchmark do
end
})
end
+
+ def run(["render_timeline", nickname]) do
+ start_pleroma()
+ user = Pleroma.User.get_by_nickname(nickname)
+
+ activities =
+ %{}
+ |> Map.put("type", ["Create", "Announce"])
+ |> Map.put("blocking_user", user)
+ |> Map.put("muting_user", user)
+ |> Map.put("user", user)
+ |> Map.put("limit", 80)
+ |> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
+ |> Enum.reverse()
+
+ inputs = %{
+ "One activity" => Enum.take_random(activities, 1),
+ "Ten activities" => Enum.take_random(activities, 10),
+ "Twenty activities" => Enum.take_random(activities, 20),
+ "Forty activities" => Enum.take_random(activities, 40),
+ "Eighty activities" => Enum.take_random(activities, 80)
+ }
+
+ Benchee.run(
+ %{
+ "Parallel rendering" => fn activities ->
+ Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+ activities: activities,
+ for: user,
+ as: :activity
+ })
+ end,
+ "Standart rendering" => fn activities ->
+ Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
+ activities: activities,
+ for: user,
+ as: :activity,
+ parallel: false
+ })
+ end
+ },
+ inputs: inputs
+ )
+ end
end
diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex
index c7324fff6..a738fae75 100644
--- a/lib/mix/tasks/pleroma/relay.ex
+++ b/lib/mix/tasks/pleroma/relay.ex
@@ -53,13 +53,11 @@ defmodule Mix.Tasks.Pleroma.Relay do
def run(["list"]) do
start_pleroma()
- with %User{} = user <- Relay.get_actor() do
- user.following
- |> Enum.each(fn entry ->
- URI.parse(entry)
- |> Map.get(:host)
- |> shell_info()
- end)
+ with %User{following: following} = _user <- Relay.get_actor() do
+ following
+ |> Enum.map(fn entry -> URI.parse(entry).host end)
+ |> Enum.uniq()
+ |> Enum.each(&shell_info(&1))
else
e -> shell_error("Error while fetching relay subscription list: #{inspect(e)}")
end
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index baf1e7722..35612c882 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -96,6 +96,7 @@ defmodule Pleroma.Activity do
from([a] in query,
left_join: tm in ThreadMute,
on: tm.user_id == ^user.id and tm.context == fragment("?->>'context'", a.data),
+ as: :thread_mute,
select: %Activity{a | thread_muted?: not is_nil(tm.id)}
)
end
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 00b06f723..25e56b9e2 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -3,11 +3,14 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Application do
+ import Cachex.Spec
use Application
@name Mix.Project.config()[:name]
@version Mix.Project.config()[:version]
@repository Mix.Project.config()[:source_url]
+ @env Mix.env()
+
def name, do: @name
def version, do: @version
def named_version, do: @name <> " " <> @version
@@ -21,116 +24,24 @@ defmodule Pleroma.Application do
# See http://elixir-lang.org/docs/stable/elixir/Application.html
# for more information on OTP Applications
def start(_type, _args) do
- import Cachex.Spec
-
Pleroma.Config.DeprecationWarnings.warn()
setup_instrumenters()
# Define workers and child supervisors to be supervised
children =
[
- # Start the Ecto repository
- %{id: Pleroma.Repo, start: {Pleroma.Repo, :start_link, []}, type: :supervisor},
- %{id: Pleroma.Config.TransferTask, start: {Pleroma.Config.TransferTask, :start_link, []}},
- %{id: Pleroma.Emoji, start: {Pleroma.Emoji, :start_link, []}},
- %{id: Pleroma.Captcha, start: {Pleroma.Captcha, :start_link, []}},
- %{
- id: :cachex_used_captcha_cache,
- start:
- {Cachex, :start_link,
- [
- :used_captcha_cache,
- [
- ttl_interval:
- :timer.seconds(Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid]))
- ]
- ]}
- },
- %{
- id: :cachex_user,
- start:
- {Cachex, :start_link,
- [
- :user_cache,
- [
- default_ttl: 25_000,
- ttl_interval: 1000,
- limit: 2500
- ]
- ]}
- },
- %{
- id: :cachex_object,
- start:
- {Cachex, :start_link,
- [
- :object_cache,
- [
- default_ttl: 25_000,
- ttl_interval: 1000,
- limit: 2500
- ]
- ]}
- },
- %{
- id: :cachex_rich_media,
- start:
- {Cachex, :start_link,
- [
- :rich_media_cache,
- [
- default_ttl: :timer.minutes(120),
- limit: 5000
- ]
- ]}
- },
- %{
- id: :cachex_scrubber,
- start:
- {Cachex, :start_link,
- [
- :scrubber_cache,
- [
- limit: 2500
- ]
- ]}
- },
- %{
- id: :cachex_idem,
- start:
- {Cachex, :start_link,
- [
- :idempotency_cache,
- [
- expiration:
- expiration(
- default: :timer.seconds(6 * 60 * 60),
- interval: :timer.seconds(60)
- ),
- limit: 2500
- ]
- ]}
- },
- %{id: Pleroma.FlakeId, start: {Pleroma.FlakeId, :start_link, []}},
- %{
- id: Pleroma.ScheduledActivityWorker,
- start: {Pleroma.ScheduledActivityWorker, :start_link, []}
- }
+ Pleroma.Repo,
+ Pleroma.Config.TransferTask,
+ Pleroma.Emoji,
+ Pleroma.Captcha,
+ Pleroma.FlakeId,
+ Pleroma.ScheduledActivityWorker
] ++
+ cachex_children() ++
hackney_pool_children() ++
[
- %{
- id: Pleroma.Web.Federator.RetryQueue,
- start: {Pleroma.Web.Federator.RetryQueue, :start_link, []}
- },
- %{
- id: Pleroma.Web.OAuth.Token.CleanWorker,
- start: {Pleroma.Web.OAuth.Token.CleanWorker, :start_link, []}
- },
- %{
- id: Pleroma.Stats,
- start: {Pleroma.Stats, :start_link, []}
- },
+ Pleroma.Web.Federator.RetryQueue,
+ Pleroma.Stats,
%{
id: :web_push_init,
start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
@@ -147,16 +58,12 @@ defmodule Pleroma.Application do
restart: :temporary
}
] ++
- streamer_child() ++
- chat_child() ++
+ oauth_cleanup_child(oauth_cleanup_enabled?()) ++
+ streamer_child(@env) ++
+ chat_child(@env, chat_enabled?()) ++
[
- # Start the endpoint when the application starts
- %{
- id: Pleroma.Web.Endpoint,
- start: {Pleroma.Web.Endpoint, :start_link, []},
- type: :supervisor
- },
- %{id: Pleroma.Gopher.Server, start: {Pleroma.Gopher.Server, :start_link, []}}
+ Pleroma.Web.Endpoint,
+ Pleroma.Gopher.Server
]
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
@@ -201,28 +108,54 @@ defmodule Pleroma.Application do
end
end
- if Pleroma.Config.get(:env) == :test do
- defp streamer_child, do: []
- defp chat_child, do: []
- else
- defp streamer_child do
- [%{id: Pleroma.Web.Streamer, start: {Pleroma.Web.Streamer, :start_link, []}}]
- end
+ defp cachex_children do
+ [
+ build_cachex("used_captcha", ttl_interval: seconds_valid_interval()),
+ build_cachex("user", default_ttl: 25_000, ttl_interval: 1000, limit: 2500),
+ build_cachex("object", default_ttl: 25_000, ttl_interval: 1000, limit: 2500),
+ build_cachex("rich_media", default_ttl: :timer.minutes(120), limit: 5000),
+ build_cachex("scrubber", limit: 2500),
+ build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500)
+ ]
+ end
- defp chat_child do
- if Pleroma.Config.get([:chat, :enabled]) do
- [
- %{
- id: Pleroma.Web.ChatChannel.ChatChannelState,
- start: {Pleroma.Web.ChatChannel.ChatChannelState, :start_link, []}
- }
- ]
- else
- []
- end
- end
+ defp idempotency_expiration,
+ do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))
+
+ defp seconds_valid_interval,
+ do: :timer.seconds(Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid]))
+
+ defp build_cachex(type, opts),
+ do: %{
+ id: String.to_atom("cachex_" <> type),
+ start: {Cachex, :start_link, [String.to_atom(type <> "_cache"), opts]},
+ type: :worker
+ }
+
+ defp chat_enabled?, do: Pleroma.Config.get([:chat, :enabled])
+
+ defp oauth_cleanup_enabled?,
+ do: Pleroma.Config.get([:oauth2, :clean_expired_tokens], false)
+
+ defp streamer_child(:test), do: []
+
+ defp streamer_child(_) do
+ [Pleroma.Web.Streamer]
+ end
+
+ defp oauth_cleanup_child(true),
+ do: [Pleroma.Web.OAuth.Token.CleanWorker]
+
+ defp oauth_cleanup_child(_), do: []
+
+ defp chat_child(:test, _), do: []
+
+ defp chat_child(_env, true) do
+ [Pleroma.Web.ChatChannel.ChatChannelState]
end
+ defp chat_child(_, _), do: []
+
defp hackney_pool_children do
for pool <- enabled_hackney_pools() do
options = Pleroma.Config.get([:hackney_pools, pool])
diff --git a/lib/pleroma/captcha/captcha.ex b/lib/pleroma/captcha/captcha.ex
index a73b87251..c2765a5b8 100644
--- a/lib/pleroma/captcha/captcha.ex
+++ b/lib/pleroma/captcha/captcha.ex
@@ -12,7 +12,7 @@ defmodule Pleroma.Captcha do
use GenServer
@doc false
- def start_link do
+ def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex
index 7799b2a78..3214c9951 100644
--- a/lib/pleroma/config/transfer_task.ex
+++ b/lib/pleroma/config/transfer_task.ex
@@ -6,7 +6,7 @@ defmodule Pleroma.Config.TransferTask do
use Task
alias Pleroma.Web.AdminAPI.Config
- def start_link do
+ def start_link(_) do
load_and_update_env()
if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Pleroma.Repo)
:ignore
diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex
index bc97b39ca..be5821ad7 100644
--- a/lib/pleroma/conversation.ex
+++ b/lib/pleroma/conversation.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Conversation do
alias Pleroma.Conversation.Participation
+ alias Pleroma.Conversation.Participation.RecipientShip
alias Pleroma.Repo
alias Pleroma.User
use Ecto.Schema
@@ -39,6 +40,15 @@ defmodule Pleroma.Conversation do
Repo.get_by(__MODULE__, ap_id: ap_id)
end
+ def maybe_create_recipientships(participation, activity) do
+ participation = Repo.preload(participation, :recipients)
+
+ if participation.recipients |> Enum.empty?() do
+ recipients = User.get_all_by_ap_id(activity.recipients)
+ RecipientShip.create(recipients, participation)
+ end
+ end
+
@doc """
This will
1. Create a conversation if there isn't one already
@@ -60,6 +70,7 @@ defmodule Pleroma.Conversation do
{:ok, participation} =
Participation.create_for_user_and_conversation(user, conversation, opts)
+ maybe_create_recipientships(participation, activity)
participation
end)
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index 5883e4183..ea5b9fe17 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -5,6 +5,7 @@
defmodule Pleroma.Conversation.Participation do
use Ecto.Schema
alias Pleroma.Conversation
+ alias Pleroma.Conversation.Participation.RecipientShip
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
@@ -17,6 +18,9 @@ defmodule Pleroma.Conversation.Participation do
field(:read, :boolean, default: false)
field(:last_activity_id, Pleroma.FlakeId, virtual: true)
+ has_many(:recipient_ships, RecipientShip)
+ has_many(:recipients, through: [:recipient_ships, :user])
+
timestamps()
end
@@ -65,6 +69,14 @@ defmodule Pleroma.Conversation.Participation do
|> Pleroma.Pagination.fetch_paginated(params)
end
+ def for_user_and_conversation(user, conversation) do
+ from(p in __MODULE__,
+ where: p.user_id == ^user.id,
+ where: p.conversation_id == ^conversation.id
+ )
+ |> Repo.one()
+ end
+
def for_user_with_last_activity_id(user, params \\ %{}) do
for_user(user, params)
|> Enum.map(fn participation ->
@@ -81,4 +93,46 @@ defmodule Pleroma.Conversation.Participation do
end)
|> Enum.filter(& &1.last_activity_id)
end
+
+ def get(_, _ \\ [])
+ def get(nil, _), do: nil
+
+ def get(id, params) do
+ query =
+ if preload = params[:preload] do
+ from(p in __MODULE__,
+ preload: ^preload
+ )
+ else
+ __MODULE__
+ end
+
+ Repo.get(query, id)
+ end
+
+ def set_recipients(participation, user_ids) do
+ user_ids =
+ [participation.user_id | user_ids]
+ |> Enum.uniq()
+
+ Repo.transaction(fn ->
+ query =
+ from(r in RecipientShip,
+ where: r.participation_id == ^participation.id
+ )
+
+ Repo.delete_all(query)
+
+ users =
+ from(u in User,
+ where: u.id in ^user_ids
+ )
+ |> Repo.all()
+
+ RecipientShip.create(users, participation)
+ :ok
+ end)
+
+ {:ok, Repo.preload(participation, :recipients, force: true)}
+ end
end
diff --git a/lib/pleroma/conversation/participation_recipient_ship.ex b/lib/pleroma/conversation/participation_recipient_ship.ex
new file mode 100644
index 000000000..932cbd04c
--- /dev/null
+++ b/lib/pleroma/conversation/participation_recipient_ship.ex
@@ -0,0 +1,34 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Conversation.Participation.RecipientShip do
+ use Ecto.Schema
+
+ alias Pleroma.Conversation.Participation
+ alias Pleroma.Repo
+ alias Pleroma.User
+
+ import Ecto.Changeset
+
+ schema "conversation_participation_recipient_ships" do
+ belongs_to(:user, User, type: Pleroma.FlakeId)
+ belongs_to(:participation, Participation)
+ end
+
+ def creation_cng(struct, params) do
+ struct
+ |> cast(params, [:user_id, :participation_id])
+ |> validate_required([:user_id, :participation_id])
+ end
+
+ def create(%User{} = user, participation), do: create([user], participation)
+
+ def create(users, participation) do
+ Enum.each(users, fn user ->
+ %__MODULE__{}
+ |> creation_cng(%{user_id: user.id, participation_id: participation.id})
+ |> Repo.insert!()
+ end)
+ end
+end
diff --git a/lib/pleroma/digest_email_worker.ex b/lib/pleroma/digest_email_worker.ex
index 18e67d39b..5644d6a67 100644
--- a/lib/pleroma/digest_email_worker.ex
+++ b/lib/pleroma/digest_email_worker.ex
@@ -1,3 +1,7 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
defmodule Pleroma.DigestEmailWorker do
import Ecto.Query
diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex
index 052501642..66e20f0e4 100644
--- a/lib/pleroma/emoji.ex
+++ b/lib/pleroma/emoji.ex
@@ -24,7 +24,7 @@ defmodule Pleroma.Emoji do
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
@doc false
- def start_link do
+ def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
diff --git a/lib/pleroma/flake_id.ex b/lib/pleroma/flake_id.ex
index ca0610abc..47d61ca5f 100644
--- a/lib/pleroma/flake_id.ex
+++ b/lib/pleroma/flake_id.ex
@@ -98,7 +98,7 @@ defmodule Pleroma.FlakeId do
def autogenerate, do: get()
# -- GenServer API
- def start_link do
+ def start_link(_) do
:gen_server.start_link({:local, :flake}, __MODULE__, [], [])
end
diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex
index b3319e137..d4e4f3e55 100644
--- a/lib/pleroma/gopher/server.ex
+++ b/lib/pleroma/gopher/server.ex
@@ -6,7 +6,7 @@ defmodule Pleroma.Gopher.Server do
use GenServer
require Logger
- def start_link do
+ def start_link(_) do
config = Pleroma.Config.get(:gopher, [])
ip = Keyword.get(config, :ip, {0, 0, 0, 0})
port = Keyword.get(config, :port, 1234)
diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex
index bf2000d90..3951f0f51 100644
--- a/lib/pleroma/html.ex
+++ b/lib/pleroma/html.ex
@@ -203,6 +203,8 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes("p", [])
Meta.allow_tag_with_these_attributes("pre", [])
Meta.allow_tag_with_these_attributes("strong", [])
+ Meta.allow_tag_with_these_attributes("sub", [])
+ Meta.allow_tag_with_these_attributes("sup", [])
Meta.allow_tag_with_these_attributes("u", [])
Meta.allow_tag_with_these_attributes("ul", [])
diff --git a/lib/pleroma/reverse_proxy/reverse_proxy.ex b/lib/pleroma/reverse_proxy/reverse_proxy.ex
index 1f98f215c..03efad30a 100644
--- a/lib/pleroma/reverse_proxy/reverse_proxy.ex
+++ b/lib/pleroma/reverse_proxy/reverse_proxy.ex
@@ -109,7 +109,11 @@ defmodule Pleroma.ReverseProxy do
end
with {:ok, code, headers, client} <- request(method, url, req_headers, hackney_opts),
- :ok <- header_length_constraint(headers, Keyword.get(opts, :max_body_length)) do
+ :ok <-
+ header_length_constraint(
+ headers,
+ Keyword.get(opts, :max_body_length, @max_body_length)
+ ) do
response(conn, client, url, code, headers, opts)
else
{:ok, code, headers} ->
@@ -200,7 +204,11 @@ defmodule Pleroma.ReverseProxy do
{:ok, data} <- client().stream_body(client),
{:ok, duration} <- increase_read_duration(duration),
sent_so_far = sent_so_far + byte_size(data),
- :ok <- body_size_constraint(sent_so_far, Keyword.get(opts, :max_body_size)),
+ :ok <-
+ body_size_constraint(
+ sent_so_far,
+ Keyword.get(opts, :max_body_length, @max_body_length)
+ ),
{:ok, conn} <- chunk(conn, data) do
chunk_reply(conn, client, opts, sent_so_far, duration)
else
diff --git a/lib/pleroma/scheduled_activity_worker.ex b/lib/pleroma/scheduled_activity_worker.ex
index 65b38622f..8578cab5e 100644
--- a/lib/pleroma/scheduled_activity_worker.ex
+++ b/lib/pleroma/scheduled_activity_worker.ex
@@ -16,7 +16,7 @@ defmodule Pleroma.ScheduledActivityWorker do
@schedule_interval :timer.minutes(1)
- def start_link do
+ def start_link(_) do
GenServer.start_link(__MODULE__, nil)
end
diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex
index 5b242927b..df80fbaa4 100644
--- a/lib/pleroma/stats.ex
+++ b/lib/pleroma/stats.ex
@@ -7,31 +7,56 @@ defmodule Pleroma.Stats do
alias Pleroma.Repo
alias Pleroma.User
- def start_link do
- agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__)
- spawn(fn -> schedule_update() end)
- agent
+ use GenServer
+
+ @interval 1000 * 60 * 60
+
+ def start_link(_) do
+ GenServer.start_link(__MODULE__, initial_data(), name: __MODULE__)
+ end
+
+ def force_update do
+ GenServer.call(__MODULE__, :force_update)
end
def get_stats do
- Agent.get(__MODULE__, fn {_, stats} -> stats end)
+ %{stats: stats} = GenServer.call(__MODULE__, :get_state)
+
+ stats
end
def get_peers do
- Agent.get(__MODULE__, fn {peers, _} -> peers end)
+ %{peers: peers} = GenServer.call(__MODULE__, :get_state)
+
+ peers
+ end
+
+ def init(args) do
+ Process.send(self(), :run_update, [])
+ {:ok, args}
+ end
+
+ def handle_call(:force_update, _from, _state) do
+ new_stats = get_stat_data()
+ {:reply, new_stats, new_stats}
end
- def schedule_update do
- spawn(fn ->
- # 1 hour
- Process.sleep(1000 * 60 * 60)
- schedule_update()
- end)
+ def handle_call(:get_state, _from, state) do
+ {:reply, state, state}
+ end
+
+ def handle_info(:run_update, _state) do
+ new_stats = get_stat_data()
+
+ Process.send_after(self(), :run_update, @interval)
+ {:noreply, new_stats}
+ end
- update_stats()
+ defp initial_data do
+ %{peers: [], stats: %{}}
end
- def update_stats do
+ defp get_stat_data do
peers =
from(
u in User,
@@ -52,8 +77,9 @@ defmodule Pleroma.Stats do
user_count = Repo.aggregate(User.Query.build(%{local: true, active: true}), :count, :id)
- Agent.update(__MODULE__, fn _ ->
- {peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}}
- end)
+ %{
+ peers: peers,
+ stats: %{domain_count: domain_count, status_count: status_count, user_count: user_count}
+ }
end
end
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index faa1e3d50..134b8bb6c 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -21,6 +21,7 @@ defmodule Pleroma.User do
alias Pleroma.Web
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
+ alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
alias Pleroma.Web.OAuth
alias Pleroma.Web.OStatus
@@ -132,6 +133,28 @@ defmodule Pleroma.User do
|> Map.put(:follower_count, follower_count)
end
+ def follow_state(%User{} = user, %User{} = target) do
+ follow_activity = Utils.fetch_latest_follow(user, target)
+
+ if follow_activity,
+ do: follow_activity.data["state"],
+ # Ideally this would be nil, but then Cachex does not commit the value
+ else: false
+ end
+
+ def get_cached_follow_state(user, target) do
+ key = "follow_state:#{user.ap_id}|#{target.ap_id}"
+ Cachex.fetch!(:user_cache, key, fn _ -> {:commit, follow_state(user, target)} end)
+ end
+
+ def set_follow_state_cache(user_ap_id, target_ap_id, state) do
+ Cachex.put(
+ :user_cache,
+ "follow_state:#{user_ap_id}|#{target_ap_id}",
+ state
+ )
+ end
+
def set_info_cache(user, args) do
Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user, args))
end
@@ -463,6 +486,13 @@ defmodule Pleroma.User do
Repo.get_by(User, ap_id: ap_id)
end
+ def get_all_by_ap_id(ap_ids) do
+ from(u in __MODULE__,
+ where: u.ap_id in ^ap_ids
+ )
+ |> Repo.all()
+ end
+
# This is mostly an SPC migration fix. This guesses the user nickname by taking the last part
# of the ap_id and the domain and tries to get that user
def get_by_guessed_nickname(ap_id) do
@@ -720,6 +750,7 @@ defmodule Pleroma.User do
|> update_and_set_cache()
end
+ @spec maybe_fetch_follow_information(User.t()) :: User.t()
def maybe_fetch_follow_information(user) do
with {:ok, user} <- fetch_follow_information(user) do
user
@@ -777,9 +808,10 @@ defmodule Pleroma.User do
end
end
+ @spec maybe_update_following_count(User.t()) :: User.t()
def maybe_update_following_count(%User{local: false} = user) do
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
- {:ok, maybe_fetch_follow_information(user)}
+ maybe_fetch_follow_information(user)
else
user
end
@@ -885,6 +917,13 @@ defmodule Pleroma.User do
blocker
end
+ # clear any requested follows as well
+ blocked =
+ case CommonAPI.reject_follow_request(blocked, blocker) do
+ {:ok, %User{} = updated_blocked} -> updated_blocked
+ nil -> blocked
+ end
+
blocker =
if subscribed_to?(blocked, blocker) do
{:ok, blocker} = unsubscribe(blocked, blocker)
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 7bb7740bf..af9749d13 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -388,7 +388,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def follow(follower, followed, activity_id \\ nil, local \\ true) do
with data <- make_follow_data(follower, followed, activity_id),
{:ok, activity} <- insert(data, local),
- :ok <- maybe_federate(activity) do
+ :ok <- maybe_federate(activity),
+ _ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
{:ok, activity}
end
end
@@ -790,14 +791,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_muted(query, %{"with_muted" => val}) when val in [true, "true", "1"], do: query
- defp restrict_muted(query, %{"muting_user" => %User{info: info}}) do
+ defp restrict_muted(query, %{"muting_user" => %User{info: info}} = opts) do
mutes = info.mutes
- from(
- activity in query,
- where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
- where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
- )
+ query =
+ from([activity] in query,
+ where: fragment("not (? = ANY(?))", activity.actor, ^mutes),
+ where: fragment("not (?->'to' \\?| ?)", activity.data, ^mutes)
+ )
+
+ unless opts["skip_preload"] do
+ from([thread_mute: tm] in query, where: is_nil(tm))
+ else
+ query
+ end
end
defp restrict_muted(query, _), do: query
@@ -898,7 +905,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp maybe_set_thread_muted_field(query, opts) do
query
- |> Activity.with_set_thread_muted_field(opts["user"])
+ |> Activity.with_set_thread_muted_field(opts["muting_user"] || opts["user"])
end
defp maybe_order(query, %{order: :desc}) do
diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
index 9863454fa..b3c742954 100644
--- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex
@@ -92,5 +92,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
def filter(message), do: {:ok, message}
@impl true
- def describe, do: {:ok, %{mrf_hellthread: Pleroma.Config.get([:mrf_hellthread])}}
+ def describe,
+ do: {:ok, %{mrf_hellthread: Pleroma.Config.get(:mrf_hellthread) |> Enum.into(%{})}}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
index 0ae9397ed..5a809a321 100644
--- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
+++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex
@@ -46,5 +46,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do
def filter(object), do: {:ok, object}
@impl true
- def describe, do: {:ok, %{mrf_rejectnonpublic: Pleroma.Config.get([:mrf_rejectnonpublic])}}
+ def describe,
+ do: {:ok, %{mrf_rejectnonpublic: Pleroma.Config.get(:mrf_rejectnonpublic) |> Enum.into(%{})}}
end
diff --git a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
index 74da8d57e..4eaea00d8 100644
--- a/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
@@ -32,5 +32,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
def filter(message), do: {:ok, message}
- def describe, do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary)}}
+ def describe,
+ do: {:ok, %{mrf_vocabulary: Pleroma.Config.get(:mrf_vocabulary) |> Enum.into(%{})}}
end
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index 46edab0bd..262529b84 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -46,7 +46,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
"""
def publish_one(%{inbox: inbox, json: json, actor: %User{} = actor, id: id} = params) do
Logger.info("Federating #{id} to #{inbox}")
- host = URI.parse(inbox).host
+ %{host: host, path: path} = URI.parse(inbox)
digest = "SHA-256=" <> (:crypto.hash(:sha256, json) |> Base.encode64())
@@ -56,6 +56,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
signature =
Pleroma.Signature.sign(actor, %{
+ "(request-target)": "post #{path}",
host: host,
"content-length": byte_size(json),
digest: digest,
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index fc5305c58..1c3058658 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -374,6 +374,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
[state, actor, object]
)
+ User.set_follow_state_cache(actor, object, state)
activity = Activity.get_by_id(activity.id)
{:ok, activity}
rescue
@@ -382,12 +383,16 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end
end
- def update_follow_state(%Activity{} = activity, state) do
+ def update_follow_state(
+ %Activity{data: %{"actor" => actor, "object" => object}} = activity,
+ state
+ ) do
with new_data <-
activity.data
|> Map.put("state", state),
changeset <- Changeset.change(activity, data: new_data),
- {:ok, activity} <- Repo.update(changeset) do
+ {:ok, activity} <- Repo.update(changeset),
+ _ <- User.set_follow_state_cache(actor, object, state) do
{:ok, activity}
end
end
diff --git a/lib/pleroma/web/chat_channel.ex b/lib/pleroma/web/chat_channel.ex
index f63f4bda1..b543909f1 100644
--- a/lib/pleroma/web/chat_channel.ex
+++ b/lib/pleroma/web/chat_channel.ex
@@ -33,9 +33,11 @@ defmodule Pleroma.Web.ChatChannel do
end
defmodule Pleroma.Web.ChatChannel.ChatChannelState do
+ use Agent
+
@max_messages 20
- def start_link do
+ def start_link(_) do
Agent.start_link(fn -> %{max_id: 1, messages: []} end, name: __MODULE__)
end
diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex
index 2db58324b..72da46263 100644
--- a/lib/pleroma/web/common_api/common_api.ex
+++ b/lib/pleroma/web/common_api/common_api.ex
@@ -4,6 +4,7 @@
defmodule Pleroma.Web.CommonAPI do
alias Pleroma.Activity
+ alias Pleroma.Conversation.Participation
alias Pleroma.Formatter
alias Pleroma.Object
alias Pleroma.ThreadMute
@@ -171,21 +172,25 @@ defmodule Pleroma.Web.CommonAPI do
end)
end
- def get_visibility(%{"visibility" => visibility}, in_reply_to)
+ def get_visibility(_, _, %Participation{}) do
+ {"direct", "direct"}
+ end
+
+ def get_visibility(%{"visibility" => visibility}, in_reply_to, _)
when visibility in ~w{public unlisted private direct},
do: {visibility, get_replied_to_visibility(in_reply_to)}
- def get_visibility(%{"visibility" => "list:" <> list_id}, in_reply_to) do
+ def get_visibility(%{"visibility" => "list:" <> list_id}, in_reply_to, _) do
visibility = {:list, String.to_integer(list_id)}
{visibility, get_replied_to_visibility(in_reply_to)}
end
- def get_visibility(_, in_reply_to) when not is_nil(in_reply_to) do
+ def get_visibility(_, in_reply_to, _) when not is_nil(in_reply_to) do
visibility = get_replied_to_visibility(in_reply_to)
{visibility, visibility}
end
- def get_visibility(_, in_reply_to), do: {"public", get_replied_to_visibility(in_reply_to)}
+ def get_visibility(_, in_reply_to, _), do: {"public", get_replied_to_visibility(in_reply_to)}
def get_replied_to_visibility(nil), do: nil
@@ -201,7 +206,9 @@ defmodule Pleroma.Web.CommonAPI do
with status <- String.trim(status),
attachments <- attachments_from_ids(data),
in_reply_to <- get_replied_to_activity(data["in_reply_to_status_id"]),
- {visibility, in_reply_to_visibility} <- get_visibility(data, in_reply_to),
+ in_reply_to_conversation <- Participation.get(data["in_reply_to_conversation_id"]),
+ {visibility, in_reply_to_visibility} <-
+ get_visibility(data, in_reply_to, in_reply_to_conversation),
{_, false} <-
{:private_to_public, in_reply_to_visibility == "direct" && visibility != "direct"},
{content_html, mentions, tags} <-
@@ -214,8 +221,9 @@ defmodule Pleroma.Web.CommonAPI do
mentioned_users <- for({_, mentioned_user} <- mentions, do: mentioned_user.ap_id),
addressed_users <- get_addressed_users(mentioned_users, data["to"]),
{poll, poll_emoji} <- make_poll_data(data),
- {to, cc} <- get_to_and_cc(user, addressed_users, in_reply_to, visibility),
- context <- make_context(in_reply_to),
+ {to, cc} <-
+ get_to_and_cc(user, addressed_users, in_reply_to, visibility, in_reply_to_conversation),
+ context <- make_context(in_reply_to, in_reply_to_conversation),
cw <- data["spoiler_text"] || "",
sensitive <- data["sensitive"] || Enum.member?(tags, {"#nsfw", "nsfw"}),
full_payload <- String.trim(status <> cw),
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 22c44a0a3..61b96aba9 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -8,6 +8,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
alias Calendar.Strftime
alias Pleroma.Activity
alias Pleroma.Config
+ alias Pleroma.Conversation.Participation
alias Pleroma.Formatter
alias Pleroma.Object
alias Pleroma.Plugs.AuthenticationPlug
@@ -86,9 +87,21 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> Enum.filter(& &1)
end
- @spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) ::
+ @spec get_to_and_cc(
+ User.t(),
+ list(String.t()),
+ Activity.t() | nil,
+ String.t(),
+ Participation.t() | nil
+ ) ::
{list(String.t()), list(String.t())}
- def get_to_and_cc(user, mentioned_users, inReplyTo, "public") do
+
+ def get_to_and_cc(_, _, _, _, %Participation{} = participation) do
+ participation = Repo.preload(participation, :recipients)
+ {Enum.map(participation.recipients, & &1.ap_id), []}
+ end
+
+ def get_to_and_cc(user, mentioned_users, inReplyTo, "public", _) do
to = [Pleroma.Constants.as_public() | mentioned_users]
cc = [user.follower_address]
@@ -99,7 +112,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end
end
- def get_to_and_cc(user, mentioned_users, inReplyTo, "unlisted") do
+ def get_to_and_cc(user, mentioned_users, inReplyTo, "unlisted", _) do
to = [user.follower_address | mentioned_users]
cc = [Pleroma.Constants.as_public()]
@@ -110,12 +123,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end
end
- def get_to_and_cc(user, mentioned_users, inReplyTo, "private") do
- {to, cc} = get_to_and_cc(user, mentioned_users, inReplyTo, "direct")
+ def get_to_and_cc(user, mentioned_users, inReplyTo, "private", _) do
+ {to, cc} = get_to_and_cc(user, mentioned_users, inReplyTo, "direct", nil)
{[user.follower_address | to], cc}
end
- def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct") do
+ def get_to_and_cc(_user, mentioned_users, inReplyTo, "direct", _) do
if inReplyTo do
{Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}
else
@@ -123,7 +136,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end
end
- def get_to_and_cc(_user, mentions, _inReplyTo, {:list, _}), do: {mentions, []}
+ def get_to_and_cc(_user, mentions, _inReplyTo, {:list, _}, _), do: {mentions, []}
def get_addressed_users(_, to) when is_list(to) do
User.get_ap_ids_by_nicknames(to)
@@ -253,8 +266,12 @@ defmodule Pleroma.Web.CommonAPI.Utils do
defp maybe_add_nsfw_tag(data, _), do: data
- def make_context(%Activity{data: %{"context" => context}}), do: context
- def make_context(_), do: Utils.generate_context_id()
+ def make_context(_, %Participation{} = participation) do
+ Repo.preload(participation, :conversation).conversation.ap_id
+ end
+
+ def make_context(%Activity{data: %{"context" => context}}, _), do: context
+ def make_context(_, _), do: Utils.generate_context_id()
def maybe_add_attachments(parsed, _attachments, true = _no_links), do: parsed
diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex
index 8a753bb4f..eeac9f503 100644
--- a/lib/pleroma/web/controller_helper.ex
+++ b/lib/pleroma/web/controller_helper.ex
@@ -33,4 +33,80 @@ defmodule Pleroma.Web.ControllerHelper do
end
defp param_to_integer(_, default), do: default
+
+ def add_link_headers(
+ conn,
+ method,
+ activities,
+ param \\ nil,
+ params \\ %{},
+ func3 \\ nil,
+ func4 \\ nil
+ ) do
+ params =
+ conn.params
+ |> Map.drop(["since_id", "max_id", "min_id"])
+ |> Map.merge(params)
+
+ last = List.last(activities)
+
+ func3 = func3 || (&mastodon_api_url/3)
+ func4 = func4 || (&mastodon_api_url/4)
+
+ if last do
+ max_id = last.id
+
+ limit =
+ params
+ |> Map.get("limit", "20")
+ |> String.to_integer()
+
+ min_id =
+ if length(activities) <= limit do
+ activities
+ |> List.first()
+ |> Map.get(:id)
+ else
+ activities
+ |> Enum.at(limit * -1)
+ |> Map.get(:id)
+ end
+
+ {next_url, prev_url} =
+ if param do
+ {
+ func4.(
+ Pleroma.Web.Endpoint,
+ method,
+ param,
+ Map.merge(params, %{max_id: max_id})
+ ),
+ func4.(
+ Pleroma.Web.Endpoint,
+ method,
+ param,
+ Map.merge(params, %{min_id: min_id})
+ )
+ }
+ else
+ {
+ func3.(
+ Pleroma.Web.Endpoint,
+ method,
+ Map.merge(params, %{max_id: max_id})
+ ),
+ func3.(
+ Pleroma.Web.Endpoint,
+ method,
+ Map.merge(params, %{min_id: min_id})
+ )
+ }
+ end
+
+ conn
+ |> put_resp_header("link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
+ else
+ conn
+ end
+ end
end
diff --git a/lib/pleroma/web/federator/retry_queue.ex b/lib/pleroma/web/federator/retry_queue.ex
index 3db948c2e..9eab8c218 100644
--- a/lib/pleroma/web/federator/retry_queue.ex
+++ b/lib/pleroma/web/federator/retry_queue.ex
@@ -13,7 +13,7 @@ defmodule Pleroma.Web.Federator.RetryQueue do
{:ok, %{args | queue_table: queue_table, running_jobs: :sets.new()}}
end
- def start_link do
+ def start_link(_) do
enabled =
if Pleroma.Config.get(:env) == :test,
do: true,
diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
index e8fac8880..53cf95fbb 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -5,7 +5,8 @@
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
use Pleroma.Web, :controller
- import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+ import Pleroma.Web.ControllerHelper,
+ only: [json_response: 3, add_link_headers: 5, add_link_headers: 4, add_link_headers: 3]
alias Ecto.Changeset
alias Pleroma.Activity
@@ -350,71 +351,6 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
json(conn, mastodon_emoji)
end
- defp add_link_headers(conn, method, activities, param \\ nil, params \\ %{}) do
- params =
- conn.params
- |> Map.drop(["since_id", "max_id", "min_id"])
- |> Map.merge(params)
-
- last = List.last(activities)
-
- if last do
- max_id = last.id
-
- limit =
- params
- |> Map.get("limit", "20")
- |> String.to_integer()
-
- min_id =
- if length(activities) <= limit do
- activities
- |> List.first()
- |> Map.get(:id)
- else
- activities
- |> Enum.at(limit * -1)
- |> Map.get(:id)
- end
-
- {next_url, prev_url} =
- if param do
- {
- mastodon_api_url(
- Pleroma.Web.Endpoint,
- method,
- param,
- Map.merge(params, %{max_id: max_id})
- ),
- mastodon_api_url(
- Pleroma.Web.Endpoint,
- method,
- param,
- Map.merge(params, %{min_id: min_id})
- )
- }
- else
- {
- mastodon_api_url(
- Pleroma.Web.Endpoint,
- method,
- Map.merge(params, %{max_id: max_id})
- ),
- mastodon_api_url(
- Pleroma.Web.Endpoint,
- method,
- Map.merge(params, %{min_id: min_id})
- )
- }
- end
-
- conn
- |> put_resp_header("link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
- else
- conn
- end
- end
-
def home_timeline(%{assigns: %{user: user}} = conn, params) do
params =
params
@@ -1805,7 +1741,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
conversations =
Enum.map(participations, fn participation ->
- ConversationView.render("participation.json", %{participation: participation, user: user})
+ ConversationView.render("participation.json", %{participation: participation, for: user})
end)
conn
@@ -1818,7 +1754,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
Repo.get_by(Participation, id: participation_id, user_id: user.id),
{:ok, participation} <- Participation.mark_as_read(participation) do
participation_view =
- ConversationView.render("participation.json", %{participation: participation, user: user})
+ ConversationView.render("participation.json", %{participation: participation, for: user})
conn
|> json(participation_view)
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index a2297a8e8..169116d0d 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -37,11 +37,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end
def render("relationship.json", %{user: %User{} = user, target: %User{} = target}) do
- follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target)
+ follow_state = User.get_cached_follow_state(user, target)
requested =
- if follow_activity && !User.following?(target, user) do
- follow_activity.data["state"] == "pending"
+ if follow_state && !User.following?(user, target) do
+ follow_state == "pending"
else
false
end
diff --git a/lib/pleroma/web/mastodon_api/views/conversation_view.ex b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
index 38bdec737..40acc07b3 100644
--- a/lib/pleroma/web/mastodon_api/views/conversation_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/conversation_view.ex
@@ -11,8 +11,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MastodonAPI.StatusView
- def render("participation.json", %{participation: participation, user: user}) do
- participation = Repo.preload(participation, conversation: :users)
+ def render("participation.json", %{participation: participation, for: user}) do
+ participation = Repo.preload(participation, conversation: [], recipients: [])
last_activity_id =
with nil <- participation.last_activity_id do
@@ -28,7 +28,7 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
# Conversations return all users except the current user.
users =
- participation.conversation.users
+ participation.recipients
|> Enum.reject(&(&1.id == user.id))
accounts =
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index 492af1702..42fbdf51b 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -8,6 +8,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
require Pleroma.Constants
alias Pleroma.Activity
+ alias Pleroma.Conversation
+ alias Pleroma.Conversation.Participation
alias Pleroma.HTML
alias Pleroma.Object
alias Pleroma.Repo
@@ -70,12 +72,14 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
def render("index.json", opts) do
replied_to_activities = get_replied_to_activities(opts.activities)
+ parallel = unless is_nil(opts[:parallel]), do: opts[:parallel], else: true
opts.activities
|> safe_render_many(
StatusView,
"status.json",
- Map.put(opts, :replied_to_activities, replied_to_activities)
+ Map.put(opts, :replied_to_activities, replied_to_activities),
+ parallel
)
end
@@ -233,6 +237,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
object.data["url"] || object.data["external_url"] || object.data["id"]
end
+ direct_conversation_id =
+ with {_, true} <- {:include_id, opts[:with_direct_conversation_id]},
+ {_, %User{} = for_user} <- {:for_user, opts[:for]},
+ %{data: %{"context" => context}} when is_binary(context) <- activity,
+ %Conversation{} = conversation <- Conversation.get_for_ap_id(context),
+ %Participation{id: participation_id} <-
+ Participation.for_user_and_conversation(for_user, conversation) do
+ participation_id
+ else
+ _e ->
+ nil
+ end
+
%{
id: to_string(activity.id),
uri: object.data["id"],
@@ -270,7 +287,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
conversation_id: get_context_id(activity),
in_reply_to_account_acct: reply_to_user && reply_to_user.nickname,
content: %{"text/plain" => content_plaintext},
- spoiler_text: %{"text/plain" => summary_plaintext}
+ spoiler_text: %{"text/plain" => summary_plaintext},
+ direct_conversation_id: direct_conversation_id
}
}
end
diff --git a/lib/pleroma/web/oauth/token/clean_worker.ex b/lib/pleroma/web/oauth/token/clean_worker.ex
index dca852449..f50098302 100644
--- a/lib/pleroma/web/oauth/token/clean_worker.ex
+++ b/lib/pleroma/web/oauth/token/clean_worker.ex
@@ -6,36 +6,30 @@ defmodule Pleroma.Web.OAuth.Token.CleanWorker do
@moduledoc """
The module represents functions to clean an expired oauth tokens.
"""
+ use GenServer
+
+ @ten_seconds 10_000
+ @one_day 86_400_000
- # 10 seconds
- @start_interval 10_000
@interval Pleroma.Config.get(
- # 24 hours
[:oauth2, :clean_expired_tokens_interval],
- 86_400_000
+ @one_day
)
- @queue :background
alias Pleroma.Web.OAuth.Token
- def start_link, do: GenServer.start_link(__MODULE__, nil)
+ def start_link(_), do: GenServer.start_link(__MODULE__, %{})
def init(_) do
- if Pleroma.Config.get([:oauth2, :clean_expired_tokens], false) do
- Process.send_after(self(), :perform, @start_interval)
- {:ok, nil}
- else
- :ignore
- end
+ Process.send_after(self(), :perform, @ten_seconds)
+ {:ok, nil}
end
@doc false
def handle_info(:perform, state) do
+ Token.delete_expired_tokens()
+
Process.send_after(self(), :perform, @interval)
- PleromaJobQueue.enqueue(@queue, __MODULE__, [:clean])
{:noreply, state}
end
-
- # Job Worker Callbacks
- def perform(:clean), do: Token.delete_expired_tokens()
end
diff --git a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex
new file mode 100644
index 000000000..b6d2bf86b
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex
@@ -0,0 +1,73 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do
+ use Pleroma.Web, :controller
+
+ import Pleroma.Web.ControllerHelper, only: [add_link_headers: 7]
+
+ alias Pleroma.Conversation.Participation
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.MastodonAPI.ConversationView
+ alias Pleroma.Web.MastodonAPI.StatusView
+
+ def conversation(%{assigns: %{user: user}} = conn, %{"id" => participation_id}) do
+ with %Participation{} = participation <- Participation.get(participation_id),
+ true <- user.id == participation.user_id do
+ conn
+ |> put_view(ConversationView)
+ |> render("participation.json", %{participation: participation, for: user})
+ end
+ end
+
+ def conversation_statuses(
+ %{assigns: %{user: user}} = conn,
+ %{"id" => participation_id} = params
+ ) do
+ params =
+ params
+ |> Map.put("blocking_user", user)
+ |> Map.put("muting_user", user)
+ |> Map.put("user", user)
+
+ participation =
+ participation_id
+ |> Participation.get(preload: [:conversation])
+
+ if user.id == participation.user_id do
+ activities =
+ participation.conversation.ap_id
+ |> ActivityPub.fetch_activities_for_context(params)
+ |> Enum.reverse()
+
+ conn
+ |> add_link_headers(
+ :conversation_statuses,
+ activities,
+ participation_id,
+ params,
+ nil,
+ &pleroma_api_url/4
+ )
+ |> put_view(StatusView)
+ |> render("index.json", %{activities: activities, for: user, as: :activity})
+ end
+ end
+
+ def update_conversation(
+ %{assigns: %{user: user}} = conn,
+ %{"id" => participation_id, "recipients" => recipients}
+ ) do
+ participation =
+ participation_id
+ |> Participation.get()
+
+ with true <- user.id == participation.user_id,
+ {:ok, participation} <- Participation.set_recipients(participation, recipients) do
+ conn
+ |> put_view(ConversationView)
+ |> render("participation.json", %{participation: participation, for: user})
+ end
+ end
+end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index c8c1c22dd..1eb6f7b9d 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -259,6 +259,21 @@ defmodule Pleroma.Web.Router do
end
end
+ scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
+ pipe_through(:authenticated_api)
+
+ scope [] do
+ pipe_through(:oauth_read)
+ get("/conversations/:id/statuses", PleromaAPIController, :conversation_statuses)
+ get("/conversations/:id", PleromaAPIController, :conversation)
+ end
+
+ scope [] do
+ pipe_through(:oauth_write)
+ patch("/conversations/:id", PleromaAPIController, :update_conversation)
+ end
+ end
+
scope "/api/v1", Pleroma.Web.MastodonAPI do
pipe_through(:authenticated_api)
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index 9ee331030..587c43f40 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -18,7 +18,7 @@ defmodule Pleroma.Web.Streamer do
@keepalive_interval :timer.seconds(30)
- def start_link do
+ def start_link(_) do
GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end
@@ -35,28 +35,21 @@ defmodule Pleroma.Web.Streamer do
end
def init(args) do
- spawn(fn ->
- # 30 seconds
- Process.sleep(@keepalive_interval)
- GenServer.cast(__MODULE__, %{action: :ping})
- end)
+ Process.send_after(self(), %{action: :ping}, @keepalive_interval)
{:ok, args}
end
- def handle_cast(%{action: :ping}, topics) do
- Map.values(topics)
+ def handle_info(%{action: :ping}, topics) do
+ topics
+ |> Map.values()
|> List.flatten()
|> Enum.each(fn socket ->
Logger.debug("Sending keepalive ping")
send(socket.transport_pid, {:text, ""})
end)
- spawn(fn ->
- # 30 seconds
- Process.sleep(@keepalive_interval)
- GenServer.cast(__MODULE__, %{action: :ping})
- end)
+ Process.send_after(self(), %{action: :ping}, @keepalive_interval)
{:noreply, topics}
end
@@ -120,8 +113,7 @@ defmodule Pleroma.Web.Streamer do
|> Map.get("#{topic}:#{item.user_id}", [])
|> Enum.each(fn socket ->
with %User{} = user <- User.get_cached_by_ap_id(socket.assigns[:user].ap_id),
- true <- should_send?(user, item),
- false <- CommonAPI.thread_muted?(user, item.activity) do
+ true <- should_send?(user, item) do
send(
socket.transport_pid,
{:text, represent_notification(socket.assigns[:user], item)}
@@ -209,7 +201,7 @@ defmodule Pleroma.Web.Streamer do
payload:
Pleroma.Web.MastodonAPI.ConversationView.render("participation.json", %{
participation: participation,
- user: participation.user
+ for: participation.user
})
|> Jason.encode!()
}
@@ -243,7 +235,8 @@ defmodule Pleroma.Web.Streamer do
%{host: parent_host} <- URI.parse(parent.data["actor"]),
false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, item_host),
false <- Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, parent_host),
- true <- thread_containment(item, user) do
+ true <- thread_containment(item, user),
+ false <- CommonAPI.thread_muted?(user, item) do
true
else
_ -> false
diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex
index 687346554..bfb6c7287 100644
--- a/lib/pleroma/web/web.ex
+++ b/lib/pleroma/web/web.ex
@@ -66,9 +66,23 @@ defmodule Pleroma.Web do
end
@doc """
- Same as `render_many/4` but wrapped in rescue block.
+ Same as `render_many/4` but wrapped in rescue block and parallelized (unless disabled by passing false as a fifth argument).
"""
- def safe_render_many(collection, view, template, assigns \\ %{}) do
+ def safe_render_many(collection, view, template, assigns \\ %{}, parallel \\ true)
+
+ def safe_render_many(collection, view, template, assigns, true) do
+ Enum.map(collection, fn resource ->
+ Task.async(fn ->
+ as = Map.get(assigns, :as) || view.__resource__
+ assigns = Map.put(assigns, as, resource)
+ safe_render(view, template, assigns)
+ end)
+ end)
+ |> Enum.map(&Task.await(&1, :infinity))
+ |> Enum.filter(& &1)
+ end
+
+ def safe_render_many(collection, view, template, assigns, false) do
Enum.map(collection, fn resource ->
as = Map.get(assigns, :as) || view.__resource__
assigns = Map.put(assigns, as, resource)