Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/develop' into fork
Browse files Browse the repository at this point in the history
Signed-off-by: marcin mikołajczak <[email protected]>
  • Loading branch information
mkljczk committed Jul 21, 2024
2 parents 9055a3b + 058f8ac commit e0d0992
Show file tree
Hide file tree
Showing 39 changed files with 450 additions and 91 deletions.
1 change: 1 addition & 0 deletions changelog.d/fix-mrfs.add
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added a Mix task "pleroma.config fix_mrf_policies" which will remove erroneous MRF policies from ConfigDB.
1 change: 1 addition & 0 deletions changelog.d/handle-non-validate-delete-errors.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Transmogrifier: handle non-validate errors on incoming Delete activities
1 change: 1 addition & 0 deletions changelog.d/metadata-provider-empty-post.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix OpenGraph and Twitter metadata providers when parsing objects with no content or summary fields.
1 change: 1 addition & 0 deletions changelog.d/oban-cancel-federation.add
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Deleting, Unfavoriting, Unrepeating, or Unreacting will cancel undelivered publishing jobs for the original activity.
1 change: 1 addition & 0 deletions changelog.d/oban-cancel.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Changed some jobs to return :cancel on unrecoverable errors that should not be retried
2 changes: 1 addition & 1 deletion changelog.d/oban-fetcher-rejected.change
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Discard Remote Fetcher jobs which errored due to an MRF rejection
Discard Remote Fetcher jobs which errored due to an MRF rejection.
1 change: 1 addition & 0 deletions changelog.d/oban-live_dashboard.add
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Oban jobs can now be viewed in the Live Dashboard
1 change: 1 addition & 0 deletions changelog.d/oban-rich-media-errors.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Prevent Rich Media backfill jobs from retrying in cases where it is likely they will fail again.
1 change: 1 addition & 0 deletions changelog.d/oban-timeouts.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure all Oban jobs have timeouts defined
4 changes: 2 additions & 2 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -600,9 +600,9 @@ config :pleroma, Oban,
web_push: 50,
transmogrifier: 20,
notifications: 20,
background: 5,
background: 20,
search_indexing: [limit: 10, paused: true],
slow: 1
slow: 5
],
plugins: [Oban.Plugins.Pruner],
crontab: [
Expand Down
15 changes: 15 additions & 0 deletions docs/administration/CLI_tasks/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,19 @@ This forcibly removes all saved values in the database.

```sh
mix pleroma.config [--force] reset

```

## Remove invalid MRF modules from the database

This forcibly removes any enabled MRF that does not exist and will fix the ability of the instance to start.

=== "OTP"
```sh
./bin/pleroma_ctl config fix_mrf_policies
```

=== "From Source"
```sh
mix pleroma.config fix_mrf_policies
```
29 changes: 29 additions & 0 deletions lib/mix/tasks/pleroma/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,35 @@ defmodule Mix.Tasks.Pleroma.Config do
end
end

# Removes any policies that are not a real module
# as they will prevent the server from starting
def run(["fix_mrf_policies"]) do
check_configdb(fn ->
start_pleroma()

group = :pleroma
key = :mrf

%{value: value} =
group
|> ConfigDB.get_by_group_and_key(key)

policies =
Keyword.get(value, :policies, [])
|> Enum.filter(&is_atom(&1))
|> Enum.filter(fn mrf ->
case Code.ensure_compiled(mrf) do
{:module, _} -> true
{:error, _} -> false
end
end)

value = Keyword.put(value, :policies, policies)

ConfigDB.update_or_create(%{group: group, key: key, value: value})
end)
end

@spec migrate_to_db(Path.t() | nil) :: any()
def migrate_to_db(file_path \\ nil) do
with :ok <- Pleroma.Config.DeprecationWarnings.warn() do
Expand Down
4 changes: 2 additions & 2 deletions lib/pleroma/instances/instance.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ defmodule Pleroma.Instances.Instance do
alias Pleroma.Maps
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Workers.BackgroundWorker
alias Pleroma.Workers.DeleteWorker

use Ecto.Schema

Expand Down Expand Up @@ -297,7 +297,7 @@ defmodule Pleroma.Instances.Instance do
all of those users' activities and notifications.
"""
def delete_users_and_activities(host) when is_binary(host) do
BackgroundWorker.enqueue("delete_instance", %{"host" => host})
DeleteWorker.enqueue("delete_instance", %{"host" => host})
end

def perform(:delete_instance, host) when is_binary(host) do
Expand Down
1 change: 1 addition & 0 deletions lib/pleroma/object/fetcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ defmodule Pleroma.Object.Fetcher do
end

# Note: will create a Create activity, which we need internally at the moment.
@spec fetch_object_from_id(String.t(), list()) :: {:ok, Object.t()} | {:error | :reject, any()}
def fetch_object_from_id(id, options \\ []) do
with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
{_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])},
Expand Down
3 changes: 2 additions & 1 deletion lib/pleroma/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ defmodule Pleroma.User do
alias Pleroma.Web.RelMe
alias Pleroma.Webhook.Notify
alias Pleroma.Workers.BackgroundWorker
alias Pleroma.Workers.DeleteWorker
alias Pleroma.Workers.UserRefreshWorker

require Logger
Expand Down Expand Up @@ -2032,7 +2033,7 @@ defmodule Pleroma.User do
def delete(%User{} = user) do
# Purge the user immediately
purge(user)
BackgroundWorker.enqueue("delete_user", %{"user_id" => user.id})
DeleteWorker.enqueue("delete_user", %{"user_id" => user.id})
end

# *Actually* delete the user from the DB
Expand Down
3 changes: 3 additions & 0 deletions lib/pleroma/web/activity_pub/transmogrifier.ex
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
else
_ -> e
end

e ->
{:error, e}
end
end

Expand Down
15 changes: 15 additions & 0 deletions lib/pleroma/web/common_api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ defmodule Pleroma.Web.CommonAPI do
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.CommonAPI.ActivityDraft

import Ecto.Query, only: [where: 3]
import Pleroma.Web.Gettext
import Pleroma.Web.CommonAPI.Utils

Expand Down Expand Up @@ -156,6 +157,7 @@ defmodule Pleroma.Web.CommonAPI do
def delete(activity_id, user) do
with {_, %Activity{data: %{"object" => _, "type" => "Create"}} = activity} <-
{:find_activity, Activity.get_by_id(activity_id, filter: [])},
{_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(activity)},
{_, %Object{} = object, _} <-
{:find_object, Object.normalize(activity, fetch: false), activity},
true <- User.privileged?(user, :messages_delete) || user.ap_id == object.data["actor"],
Expand Down Expand Up @@ -223,6 +225,7 @@ defmodule Pleroma.Web.CommonAPI do
{:find_activity, Activity.get_by_id(id)},
%Object{} = note <- Object.normalize(activity, fetch: false),
%Activity{} = announce <- Utils.get_existing_announce(user.ap_id, note),
{_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(announce)},
{:ok, undo, _} <- Builder.undo(user, announce),
{:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
{:ok, activity}
Expand Down Expand Up @@ -275,6 +278,7 @@ defmodule Pleroma.Web.CommonAPI do
{:find_activity, Activity.get_by_id(id)},
%Object{} = note <- Object.normalize(activity, fetch: false),
%Activity{} = like <- Utils.get_existing_like(user.ap_id, note),
{_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(like)},
{:ok, undo, _} <- Builder.undo(user, like),
{:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
{:ok, activity}
Expand All @@ -298,6 +302,7 @@ defmodule Pleroma.Web.CommonAPI do

def unreact_with_emoji(id, user, emoji) do
with %Activity{} = reaction_activity <- Utils.get_latest_reaction(id, user, emoji),
{_, {:ok, _}} <- {:cancel_jobs, maybe_cancel_jobs(reaction_activity)},
{:ok, undo, _} <- Builder.undo(user, reaction_activity),
{:ok, activity, _} <- Pipeline.common_pipeline(undo, local: true) do
{:ok, activity}
Expand Down Expand Up @@ -807,4 +812,14 @@ defmodule Pleroma.Web.CommonAPI do
_ -> {:error, nil}
end
end

defp maybe_cancel_jobs(%Activity{data: %{"id" => ap_id}}) do
Oban.Job
|> where([j], j.worker == "Pleroma.Workers.PublisherWorker")
|> where([j], j.args["op"] == "publish_one")
|> where([j], j.args["params"]["id"] == ^ap_id)
|> Oban.cancel_all_jobs()
end

defp maybe_cancel_jobs(_), do: {:ok, 0}
end
5 changes: 4 additions & 1 deletion lib/pleroma/web/metadata/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ defmodule Pleroma.Web.Metadata.Utils do
|> scrub_html_and_truncate_object_field(object)
end

def scrub_html_and_truncate(%{data: %{"content" => content}} = object) do
def scrub_html_and_truncate(%{data: %{"content" => content}} = object)
when is_binary(content) and content != "" do
content
|> scrub_html_and_truncate_object_field(object)
end

def scrub_html_and_truncate(%{}), do: ""

def scrub_html_and_truncate(content, max_length \\ 200, omission \\ "...")
when is_binary(content) do
content
Expand Down
24 changes: 9 additions & 15 deletions lib/pleroma/web/rich_media/backfill.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

defmodule Pleroma.Web.RichMedia.Backfill do
alias Pleroma.Web.RichMedia.Card
alias Pleroma.Web.RichMedia.Helpers
alias Pleroma.Web.RichMedia.Parser
alias Pleroma.Web.RichMedia.Parser.TTL
alias Pleroma.Workers.RichMediaWorker
Expand All @@ -16,8 +17,7 @@ defmodule Pleroma.Web.RichMedia.Backfill do
Pleroma.Web.ActivityPub.ActivityPub
)

@spec run(map()) ::
:ok | {:error, {:invalid_metadata, any()} | :body_too_large | {:content, any()} | any()}
@spec run(map()) :: :ok | Parser.parse_errors() | Helpers.get_errors()
def run(%{"url" => url} = args) do
url_hash = Card.url_to_hash(url)

Expand All @@ -33,22 +33,16 @@ defmodule Pleroma.Web.RichMedia.Backfill do
end

warm_cache(url_hash, card)
:ok

{:error, {:invalid_metadata, fields}} ->
Logger.debug("Rich media incomplete or invalid metadata for #{url}: #{inspect(fields)}")
negative_cache(url_hash)

{:error, :body_too_large} ->
Logger.error("Rich media error for #{url}: :body_too_large")
negative_cache(url_hash)

{:error, {:content_type, type}} ->
Logger.debug("Rich media error for #{url}: :content_type is #{type}")
{:error, type} = error
when type in [:invalid_metadata, :body_too_large, :content_type, :validate] ->
negative_cache(url_hash)
error

e ->
Logger.debug("Rich media error for #{url}: #{inspect(e)}")
{:error, e}
{:error, type} = error
when type in [:get, :head] ->
error
end
end

Expand Down
45 changes: 29 additions & 16 deletions lib/pleroma/web/rich_media/helpers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@
defmodule Pleroma.Web.RichMedia.Helpers do
alias Pleroma.Config

require Logger

@type get_errors :: {:error, :body_too_large | :content_type | :head | :get}

@spec rich_media_get(String.t()) :: {:ok, String.t()} | get_errors()

defp headers do
user_agent =
case Pleroma.Config.get([:rich_media, :user_agent], :default) do
Expand All @@ -21,31 +27,38 @@ defmodule Pleroma.Web.RichMedia.Helpers do
def rich_media_get(url) do
headers = headers()

head_check =
case Pleroma.HTTP.head(url, headers, http_options()) do
# If the HEAD request didn't reach the server for whatever reason,
# we assume the GET that comes right after won't either
{:error, _} = e ->
e
with {_, {:ok, %Tesla.Env{status: 200, headers: headers}}} <-
{:head, Pleroma.HTTP.head(url, headers, http_options())},
{_, :ok} <- {:content_type, check_content_type(headers)},
{_, :ok} <- {:content_length, check_content_length(headers)},
{_, {:ok, %Tesla.Env{status: 200, body: body}}} <-
{:get, Pleroma.HTTP.get(url, headers, http_options())} do
{:ok, body}
else
{:head, _} ->
Logger.debug("Rich media error for #{url}: HTTP HEAD failed")
{:error, :head}

{:ok, %Tesla.Env{status: 200, headers: headers}} ->
with :ok <- check_content_type(headers),
:ok <- check_content_length(headers),
do: :ok
{:content_type, {_, type}} ->
Logger.debug("Rich media error for #{url}: content-type is #{type}")
{:error, :content_type}

_ ->
:ok
end
{:content_length, {_, length}} ->
Logger.debug("Rich media error for #{url}: content-length is #{length}")
{:error, :body_too_large}

with :ok <- head_check, do: Pleroma.HTTP.get(url, headers, http_options())
{:get, _} ->
Logger.debug("Rich media error for #{url}: HTTP GET failed")
{:error, :get}
end
end

defp check_content_type(headers) do
case List.keyfind(headers, "content-type", 0) do
{_, content_type} ->
case Plug.Conn.Utils.media_type(content_type) do
{:ok, "text", "html", _} -> :ok
_ -> {:error, {:content_type, content_type}}
_ -> {:error, content_type}
end

_ ->
Expand All @@ -60,7 +73,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
{_, maybe_content_length} ->
case Integer.parse(maybe_content_length) do
{content_length, ""} when content_length <= max_body -> :ok
{_, ""} -> {:error, :body_too_large}
{_, ""} -> {:error, maybe_content_length}
_ -> :ok
end

Expand Down
23 changes: 13 additions & 10 deletions lib/pleroma/web/rich_media/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only

defmodule Pleroma.Web.RichMedia.Parser do
alias Pleroma.Web.RichMedia.Helpers
require Logger

@config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
Expand All @@ -11,24 +12,26 @@ defmodule Pleroma.Web.RichMedia.Parser do
Pleroma.Config.get([:rich_media, :parsers])
end

def parse(nil), do: nil
@type parse_errors :: {:error, :rich_media_disabled | :validate}

@spec parse(String.t()) :: {:ok, map()} | {:error, any()}
def parse(url) do
@spec parse(String.t()) ::
{:ok, map()} | parse_errors() | Helpers.get_errors()
def parse(url) when is_binary(url) do
with {_, true} <- {:config, @config_impl.get([:rich_media, :enabled])},
:ok <- validate_page_url(url),
{:ok, data} <- parse_url(url) do
{_, :ok} <- {:validate, validate_page_url(url)},
{_, {:ok, data}} <- {:parse, parse_url(url)} do
data = Map.put(data, "url", url)
{:ok, data}
else
{:config, _} -> {:error, :rich_media_disabled}
e -> e
{:validate, _} -> {:error, :validate}
{:parse, error} -> error
end
end

defp parse_url(url) do
with {:ok, %Tesla.Env{body: html}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url),
{:ok, html} <- Floki.parse_document(html) do
with {:ok, body} <- Helpers.rich_media_get(url),
{:ok, html} <- Floki.parse_document(body) do
html
|> maybe_parse()
|> clean_parsed_data()
Expand All @@ -50,8 +53,8 @@ defmodule Pleroma.Web.RichMedia.Parser do
{:ok, data}
end

defp check_parsed_data(data) do
{:error, {:invalid_metadata, data}}
defp check_parsed_data(_data) do
{:error, :invalid_metadata}
end

defp clean_parsed_data(data) do
Expand Down
2 changes: 1 addition & 1 deletion lib/pleroma/web/rich_media/parsers/o_embed.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
end

defp get_oembed_data(url) do
with {:ok, %Tesla.Env{body: json}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url) do
with {:ok, json} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url) do
Jason.decode(json)
end
end
Expand Down
Loading

0 comments on commit e0d0992

Please sign in to comment.