diff options
29 files changed, 246 insertions, 202 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d6eac4c5..fa7818137 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,11 +37,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).  - Rich Media: The crawled URL is now spliced into the rich media data.  - ActivityPub S2S: sharedInbox usage has been mostly aligned with the rules in the AP specification.  - ActivityPub S2S: remote user deletions now work the same as local user deletions. +- ActivityPub S2S: POST requests are now signed with `(request-target)` pseudo-header.  - Not being able to access the Mastodon FE login page on private instances  - Invalid SemVer version generation, when the current branch does not have commits ahead of tag/checked out on a tag  - Pleroma.Upload base_url was not automatically whitelisted by MediaProxy. Now your custom CDN or file hosting will be accessed directly as expected.  - Report email not being sent to admins when the reporter is a remote user  - MRF: ensure that subdomain_match calls are case-insensitive +- MRF: fix use of unserializable keyword lists in describe() implementations  ### Added  - Conversations: Add Pleroma-specific conversation endpoints and status posting extensions. Run the `bump_all_conversations` task again to create the necessary data. 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/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/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 2fae7281c..06e60cba3 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/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 29712f257..5c3c8a8a2 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -132,6 +132,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 diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index cf55c9520..01052846f 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 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/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/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index 72c092f25..0ef568f0f 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/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index d3aea2aaf..42fbdf51b 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -72,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 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/streamer.ex b/lib/pleroma/web/streamer.ex index a0bb10895..66c8cd2c4 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 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) @@ -95,7 +95,7 @@ defmodule Pleroma.Mixfile do    defp deps do      [        {:phoenix, "~> 1.4.8"}, -      {:tzdata, "~> 1.0"}, +      {:tzdata, "~> 0.5.21"},        {:plug_cowboy, "~> 2.0"},        {:phoenix_pubsub, "~> 1.1"},        {:phoenix_ecto, "~> 4.0"}, @@ -87,7 +87,7 @@    "tesla": {:hex, :tesla, "1.2.1", "864783cc27f71dd8c8969163704752476cec0f3a51eb3b06393b3971dc9733ff", [:mix], [{:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "~> 4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm"},    "timex": {:hex, :timex, "3.6.1", "efdf56d0e67a6b956cc57774353b0329c8ab7726766a11547e529357ffdc1d56", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},    "trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"}, -  "tzdata": {:hex, :tzdata, "1.0.1", "f6027a331af7d837471248e62733c6ebee86a72e57c613aa071ebb1f750fc71a", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"}, +  "tzdata": {:hex, :tzdata, "0.5.21", "8cbf3607fcce69636c672d5be2bbb08687fe26639a62bdcc283d267277db7cf0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},    "ueberauth": {:hex, :ueberauth, "0.6.1", "9e90d3337dddf38b1ca2753aca9b1e53d8a52b890191cdc55240247c89230412", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},    "unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},    "unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm"}, diff --git a/test/config/transfer_task_test.exs b/test/config/transfer_task_test.exs index dbeadbe87..4455a4d47 100644 --- a/test/config/transfer_task_test.exs +++ b/test/config/transfer_task_test.exs @@ -31,7 +31,7 @@ defmodule Pleroma.Config.TransferTaskTest do        value: [live: 15, com: 35]      }) -    Pleroma.Config.TransferTask.start_link() +    Pleroma.Config.TransferTask.start_link([])      assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]      assert Application.get_env(:idna, :test_key) == [live: 15, com: 35] @@ -50,7 +50,7 @@ defmodule Pleroma.Config.TransferTaskTest do      })      assert ExUnit.CaptureLog.capture_log(fn -> -             Pleroma.Config.TransferTask.start_link() +             Pleroma.Config.TransferTask.start_link([])             end) =~               "updating env causes error, key: \"undefined_atom_key\", error: %ArgumentError{message: \"argument error\"}"    end diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index 2febe8b3a..112e272f9 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -2624,7 +2624,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do        |> Changeset.put_embed(:info, info_change)        |> User.update_and_set_cache() -    Pleroma.Stats.update_stats() +    Pleroma.Stats.force_update()      conn = get(conn, "/api/v1/instance") @@ -2642,7 +2642,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do      insert(:user, %{local: false, nickname: "u@peer1.com"})      insert(:user, %{local: false, nickname: "u@peer2.com"}) -    Pleroma.Stats.update_stats() +    Pleroma.Stats.force_update()      conn = get(conn, "/api/v1/instance/peers")  | 
