Merge remote-tracking branch 'origin/develop' into webfinger-fix
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
28e5e65676
365 changed files with 3208 additions and 872 deletions
|
|
@ -368,7 +368,7 @@ defmodule Pleroma.Activity do
|
|||
)
|
||||
end
|
||||
|
||||
defdelegate search(user, query, options \\ []), to: Pleroma.Activity.Search
|
||||
defdelegate search(user, query, options \\ []), to: Pleroma.Search.DatabaseSearch
|
||||
|
||||
def direct_conversation_id(activity, for_user) do
|
||||
alias Pleroma.Conversation.Participation
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ defmodule Pleroma.Application do
|
|||
Config.DeprecationWarnings.warn()
|
||||
Pleroma.Web.Plugs.HTTPSecurityPlug.warn_if_disabled()
|
||||
Pleroma.ApplicationRequirements.verify!()
|
||||
setup_instrumenters()
|
||||
load_custom_modules()
|
||||
Pleroma.Docs.JSON.compile()
|
||||
limiters_setup()
|
||||
|
|
@ -91,6 +90,7 @@ defmodule Pleroma.Application do
|
|||
# Define workers and child supervisors to be supervised
|
||||
children =
|
||||
[
|
||||
Pleroma.PromEx,
|
||||
Pleroma.Repo,
|
||||
Config.TransferTask,
|
||||
Pleroma.Emoji,
|
||||
|
|
@ -138,7 +138,7 @@ defmodule Pleroma.Application do
|
|||
num
|
||||
else
|
||||
e ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Could not get the postgres version: #{inspect(e)}.\nSetting the default value of 9.6"
|
||||
)
|
||||
|
||||
|
|
@ -170,29 +170,6 @@ defmodule Pleroma.Application do
|
|||
end
|
||||
end
|
||||
|
||||
defp setup_instrumenters do
|
||||
require Prometheus.Registry
|
||||
|
||||
if Application.get_env(:prometheus, Pleroma.Repo.Instrumenter) do
|
||||
:ok =
|
||||
:telemetry.attach(
|
||||
"prometheus-ecto",
|
||||
[:pleroma, :repo, :query],
|
||||
&Pleroma.Repo.Instrumenter.handle_event/4,
|
||||
%{}
|
||||
)
|
||||
|
||||
Pleroma.Repo.Instrumenter.setup()
|
||||
end
|
||||
|
||||
Pleroma.Web.Endpoint.MetricsExporter.setup()
|
||||
Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
|
||||
|
||||
# Note: disabled until prometheus-phx is integrated into prometheus-phoenix:
|
||||
# Pleroma.Web.Endpoint.Instrumenter.setup()
|
||||
PrometheusPhx.setup()
|
||||
end
|
||||
|
||||
defp cachex_children do
|
||||
[
|
||||
build_cachex("used_captcha", ttl_interval: seconds_valid_interval()),
|
||||
|
|
@ -322,7 +299,11 @@ defmodule Pleroma.Application do
|
|||
def limiters_setup do
|
||||
config = Config.get(ConcurrentLimiter, [])
|
||||
|
||||
[Pleroma.Web.RichMedia.Helpers, Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy]
|
||||
[
|
||||
Pleroma.Web.RichMedia.Helpers,
|
||||
Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy,
|
||||
Pleroma.Search
|
||||
]
|
||||
|> Enum.each(fn module ->
|
||||
mod_config = Keyword.get(config, module, [])
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
defp check_welcome_message_config!(:ok) do
|
||||
if Pleroma.Config.get([:welcome, :email, :enabled], false) and
|
||||
not Pleroma.Emails.Mailer.enabled?() do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
To send welcome emails, you need to enable the mailer.
|
||||
Welcome emails will NOT be sent with the current config.
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
def check_confirmation_accounts!(:ok) do
|
||||
if Pleroma.Config.get([:instance, :account_activation_required]) &&
|
||||
not Pleroma.Emails.Mailer.enabled?() do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
Account activation is required, but the mailer is disabled.
|
||||
Users will NOT be able to confirm their accounts with this config.
|
||||
Either disable account activation or enable the mailer.
|
||||
|
|
@ -168,8 +168,6 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"),
|
||||
check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "convert"),
|
||||
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "ffprobe")
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, [])
|
||||
|
||||
if Pleroma.Upload.Filter.Exiftool in filters do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later:
|
||||
|
||||
|
|
@ -63,7 +63,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
|> Enum.any?(fn {_, v} -> Enum.any?(v, &is_binary/1) end)
|
||||
|
||||
if has_strings do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using strings in the SimplePolicy configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
has_strings = Config.get([:instance, :quarantined_instances]) |> Enum.any?(&is_binary/1)
|
||||
|
||||
if has_strings do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using strings in the quarantined_instances configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
|
||||
|
||||
|
|
@ -158,7 +158,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
has_strings = Config.get([:mrf, :transparency_exclusions]) |> Enum.any?(&is_binary/1)
|
||||
|
||||
if has_strings do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using strings in the transparency_exclusions configuration instead of tuples. They should work for now, but you are advised to change to the new configuration to prevent possible issues later:
|
||||
|
||||
|
|
@ -193,7 +193,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
|
||||
def check_hellthread_threshold do
|
||||
if Config.get([:mrf_hellthread, :threshold]) do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
You are using the old configuration mechanism for the hellthread filter. Please check config.md.
|
||||
""")
|
||||
|
|
@ -274,7 +274,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
if warning == "" do
|
||||
:ok
|
||||
else
|
||||
Logger.warn(warning_preface <> warning)
|
||||
Logger.warning(warning_preface <> warning)
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
|
@ -284,7 +284,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
whitelist = Config.get([:media_proxy, :whitelist])
|
||||
|
||||
if Enum.any?(whitelist, &(not String.starts_with?(&1, "http"))) do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using old format (only domain) for MediaProxy whitelist option. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later.
|
||||
""")
|
||||
|
|
@ -299,7 +299,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
pool_config = Config.get(:connections_pool)
|
||||
|
||||
if timeout = pool_config[:await_up_timeout] do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using old setting `config :pleroma, :connections_pool, await_up_timeout`. Please change to `config :pleroma, :connections_pool, connect_timeout` to ensure compatibility with future releases.
|
||||
""")
|
||||
|
|
@ -331,7 +331,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
"\n* `:timeout` options in #{pool_name} pool is now `:recv_timeout`"
|
||||
end)
|
||||
|
||||
Logger.warn(Enum.join([warning_preface | pool_warnings]))
|
||||
Logger.warning(Enum.join([warning_preface | pool_warnings]))
|
||||
|
||||
Config.put(:pools, updated_config)
|
||||
:error
|
||||
|
|
|
|||
|
|
@ -5,4 +5,11 @@
|
|||
defmodule Pleroma.Config.Getting do
|
||||
@callback get(any()) :: any()
|
||||
@callback get(any(), any()) :: any()
|
||||
|
||||
def get(key), do: get(key, nil)
|
||||
def get(key, default), do: impl().get(key, default)
|
||||
|
||||
def impl do
|
||||
Application.get_env(:pleroma, :config_impl, Pleroma.Config)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ defmodule Pleroma.Config.Oban do
|
|||
You are using old workers in Oban crontab settings, which were removed.
|
||||
Please, remove setting from crontab in your config file (prod.secret.exs): #{inspect(setting)}
|
||||
"""
|
||||
|> Logger.warn()
|
||||
|> Logger.warning()
|
||||
|
||||
List.delete(acc, setting)
|
||||
else
|
||||
|
|
|
|||
|
|
@ -55,8 +55,7 @@ defmodule Pleroma.Config.TransferTask do
|
|||
|
||||
started_applications = Application.started_applications()
|
||||
|
||||
# TODO: some problem with prometheus after restart!
|
||||
reject = [nil, :prometheus, :postgrex]
|
||||
reject = [nil, :postgrex]
|
||||
|
||||
reject =
|
||||
if restart_pleroma? do
|
||||
|
|
@ -145,7 +144,7 @@ defmodule Pleroma.Config.TransferTask do
|
|||
error_msg =
|
||||
"updating env causes error, group: #{inspect(group)}, key: #{inspect(key)}, value: #{inspect(value)} error: #{inspect(error)}"
|
||||
|
||||
Logger.warn(error_msg)
|
||||
Logger.warning(error_msg)
|
||||
|
||||
nil
|
||||
end
|
||||
|
|
@ -179,12 +178,12 @@ defmodule Pleroma.Config.TransferTask do
|
|||
:ok = Application.start(app)
|
||||
else
|
||||
nil ->
|
||||
Logger.warn("#{app} is not started.")
|
||||
Logger.warning("#{app} is not started.")
|
||||
|
||||
error ->
|
||||
error
|
||||
|> inspect()
|
||||
|> Logger.warn()
|
||||
|> Logger.warning()
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ defmodule Pleroma.Docs.Generator do
|
|||
# This shouldn't be needed as all modules are expected to have module_info/1,
|
||||
# but in test enviroments some transient modules `:elixir_compiler_XX`
|
||||
# are loaded for some reason (where XX is a random integer).
|
||||
Code.ensure_loaded(module)
|
||||
|
||||
if function_exported?(module, :module_info, 1) do
|
||||
module.module_info(:attributes)
|
||||
|> Keyword.get_values(:behaviour)
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ defmodule Pleroma.Emoji.Loader do
|
|||
Logger.info("Found emoji packs: #{Enum.join(packs, ", ")}")
|
||||
|
||||
if not Enum.empty?(files) do
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Found files in the emoji folder. These will be ignored, please move them to a subdirectory\nFound files: #{Enum.join(files, ", ")}"
|
||||
)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ defmodule Pleroma.Gun.Conn do
|
|||
{:ok, conn, protocol}
|
||||
else
|
||||
error ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Opening proxied connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
|
||||
)
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ defmodule Pleroma.Gun.Conn do
|
|||
{:ok, conn, protocol}
|
||||
else
|
||||
error ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Opening socks proxied connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
|
||||
)
|
||||
|
||||
|
|
@ -106,7 +106,7 @@ defmodule Pleroma.Gun.Conn do
|
|||
{:ok, conn, protocol}
|
||||
else
|
||||
error ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Opening connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,12 @@ defmodule Pleroma.Helpers.MediaHelper do
|
|||
"""
|
||||
|
||||
alias Pleroma.HTTP
|
||||
alias Vix.Vips.Operation
|
||||
|
||||
require Logger
|
||||
|
||||
def missing_dependencies do
|
||||
Enum.reduce([imagemagick: "convert", ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc ->
|
||||
Enum.reduce([ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc ->
|
||||
if Pleroma.Utils.command_available?(executable) do
|
||||
acc
|
||||
else
|
||||
|
|
@ -22,54 +23,22 @@ defmodule Pleroma.Helpers.MediaHelper do
|
|||
end
|
||||
|
||||
def image_resize(url, options) do
|
||||
with executable when is_binary(executable) <- System.find_executable("convert"),
|
||||
{:ok, args} <- prepare_image_resize_args(options),
|
||||
{:ok, env} <- HTTP.get(url, [], pool: :media),
|
||||
{:ok, fifo_path} <- mkfifo() do
|
||||
args = List.flatten([fifo_path, args])
|
||||
run_fifo(fifo_path, env, executable, args)
|
||||
with {:ok, env} <- HTTP.get(url, [], pool: :media),
|
||||
{:ok, resized} <-
|
||||
Operation.thumbnail_buffer(env.body, options.max_width,
|
||||
height: options.max_height,
|
||||
size: :VIPS_SIZE_DOWN
|
||||
) do
|
||||
if options[:format] == "png" do
|
||||
Operation.pngsave_buffer(resized, Q: options[:quality])
|
||||
else
|
||||
Operation.jpegsave_buffer(resized, Q: options[:quality], interlace: true)
|
||||
end
|
||||
else
|
||||
nil -> {:error, {:convert, :command_not_found}}
|
||||
{:error, _} = error -> error
|
||||
end
|
||||
end
|
||||
|
||||
defp prepare_image_resize_args(
|
||||
%{max_width: max_width, max_height: max_height, format: "png"} = options
|
||||
) do
|
||||
quality = options[:quality] || 85
|
||||
resize = Enum.join([max_width, "x", max_height, ">"])
|
||||
|
||||
args = [
|
||||
"-resize",
|
||||
resize,
|
||||
"-quality",
|
||||
to_string(quality),
|
||||
"png:-"
|
||||
]
|
||||
|
||||
{:ok, args}
|
||||
end
|
||||
|
||||
defp prepare_image_resize_args(%{max_width: max_width, max_height: max_height} = options) do
|
||||
quality = options[:quality] || 85
|
||||
resize = Enum.join([max_width, "x", max_height, ">"])
|
||||
|
||||
args = [
|
||||
"-interlace",
|
||||
"Plane",
|
||||
"-resize",
|
||||
resize,
|
||||
"-quality",
|
||||
to_string(quality),
|
||||
"jpg:-"
|
||||
]
|
||||
|
||||
{:ok, args}
|
||||
end
|
||||
|
||||
defp prepare_image_resize_args(_), do: {:error, :missing_options}
|
||||
|
||||
# Note: video thumbnail is intentionally not resized (always has original dimensions)
|
||||
def video_framegrab(url) do
|
||||
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
|
||||
|
|
|
|||
|
|
@ -70,15 +70,15 @@ defmodule Pleroma.HTTP.AdapterHelper do
|
|||
{:ok, parse_host(host), port}
|
||||
else
|
||||
{_, _} ->
|
||||
Logger.warn("Parsing port failed #{inspect(proxy)}")
|
||||
Logger.warning("Parsing port failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy_port}
|
||||
|
||||
:error ->
|
||||
Logger.warn("Parsing port failed #{inspect(proxy)}")
|
||||
Logger.warning("Parsing port failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy_port}
|
||||
|
||||
_ ->
|
||||
Logger.warn("Parsing proxy failed #{inspect(proxy)}")
|
||||
Logger.warning("Parsing proxy failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy}
|
||||
end
|
||||
end
|
||||
|
|
@ -88,7 +88,7 @@ defmodule Pleroma.HTTP.AdapterHelper do
|
|||
{:ok, type, parse_host(host), port}
|
||||
else
|
||||
_ ->
|
||||
Logger.warn("Parsing proxy failed #{inspect(proxy)}")
|
||||
Logger.warning("Parsing proxy failed #{inspect(proxy)}")
|
||||
{:error, :invalid_proxy}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ defmodule Pleroma.HTTP.WebPush do
|
|||
@moduledoc false
|
||||
|
||||
def post(url, payload, headers, options \\ []) do
|
||||
list_headers = Map.to_list(headers)
|
||||
list_headers =
|
||||
headers
|
||||
|> Map.to_list()
|
||||
|> Kernel.++([{"content-type", "octet-stream"}])
|
||||
|
||||
Pleroma.HTTP.post(url, payload, list_headers, options)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -97,13 +97,9 @@ defmodule Pleroma.Instances.Instance do
|
|||
def reachable?(url_or_host) when is_binary(url_or_host), do: true
|
||||
|
||||
def set_reachable(url_or_host) when is_binary(url_or_host) do
|
||||
with host <- host(url_or_host),
|
||||
%Instance{} = existing_record <- Repo.get_by(Instance, %{host: host}) do
|
||||
{:ok, _instance} =
|
||||
existing_record
|
||||
|> changeset(%{unreachable_since: nil})
|
||||
|> Repo.update()
|
||||
end
|
||||
%Instance{host: host(url_or_host)}
|
||||
|> changeset(%{unreachable_since: nil})
|
||||
|> Repo.insert(on_conflict: {:replace, [:unreachable_since]}, conflict_target: :host)
|
||||
end
|
||||
|
||||
def set_reachable(_), do: {:error, nil}
|
||||
|
|
@ -177,7 +173,7 @@ defmodule Pleroma.Instances.Instance do
|
|||
end
|
||||
rescue
|
||||
e ->
|
||||
Logger.warn("Instance.get_or_update_favicon(\"#{host}\") error: #{inspect(e)}")
|
||||
Logger.warning("Instance.get_or_update_favicon(\"#{host}\") error: #{inspect(e)}")
|
||||
nil
|
||||
end
|
||||
|
||||
|
|
@ -205,7 +201,7 @@ defmodule Pleroma.Instances.Instance do
|
|||
end
|
||||
rescue
|
||||
e ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Instance.scrape_favicon(\"#{to_string(instance_uri)}\") error: #{inspect(e)}"
|
||||
)
|
||||
|
||||
|
|
@ -288,7 +284,7 @@ defmodule Pleroma.Instances.Instance do
|
|||
end
|
||||
rescue
|
||||
e ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Instance.scrape_metadata(\"#{to_string(instance_uri)}\") error: #{inspect(e)}"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ defmodule Pleroma.Maintenance do
|
|||
"full" ->
|
||||
Logger.info("Running VACUUM FULL.")
|
||||
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Re-packing your entire database may take a while and will consume extra disk space during the process."
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ defmodule Pleroma.Migrators.Support.BaseMigrator do
|
|||
data_migration.state == :manual or data_migration.name in manual_migrations ->
|
||||
message = "Data migration is in manual execution or manual fix mode."
|
||||
update_status(:manual, message)
|
||||
Logger.warn("#{__MODULE__}: #{message}")
|
||||
Logger.warning("#{__MODULE__}: #{message}")
|
||||
|
||||
data_migration.state == :complete ->
|
||||
on_complete(data_migration)
|
||||
|
|
@ -109,7 +109,7 @@ defmodule Pleroma.Migrators.Support.BaseMigrator do
|
|||
Putting data migration to manual fix mode. Try running `#{__MODULE__}.retry_failed/0`.
|
||||
"""
|
||||
|
||||
Logger.warn("#{__MODULE__}: #{message}")
|
||||
Logger.warning("#{__MODULE__}: #{message}")
|
||||
update_status(:manual, message)
|
||||
on_complete(data_migration())
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ defmodule Pleroma.Migrators.Support.BaseMigrator do
|
|||
|
||||
defp on_complete(data_migration) do
|
||||
if data_migration.feature_lock || feature_state() == :disabled do
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"#{__MODULE__}: migration complete but feature is locked; consider enabling."
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -328,6 +328,52 @@ defmodule Pleroma.Object do
|
|||
end
|
||||
end
|
||||
|
||||
def increase_quotes_count(ap_id) do
|
||||
Object
|
||||
|> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id)))
|
||||
|> update([o],
|
||||
set: [
|
||||
data:
|
||||
fragment(
|
||||
"""
|
||||
safe_jsonb_set(?, '{quotesCount}',
|
||||
(coalesce((?->>'quotesCount')::int, 0) + 1)::varchar::jsonb, true)
|
||||
""",
|
||||
o.data,
|
||||
o.data
|
||||
)
|
||||
]
|
||||
)
|
||||
|> Repo.update_all([])
|
||||
|> case do
|
||||
{1, [object]} -> set_cache(object)
|
||||
_ -> {:error, "Not found"}
|
||||
end
|
||||
end
|
||||
|
||||
def decrease_quotes_count(ap_id) do
|
||||
Object
|
||||
|> where([o], fragment("?->>'id' = ?::text", o.data, ^to_string(ap_id)))
|
||||
|> update([o],
|
||||
set: [
|
||||
data:
|
||||
fragment(
|
||||
"""
|
||||
safe_jsonb_set(?, '{quotesCount}',
|
||||
(greatest(0, (?->>'quotesCount')::int - 1))::varchar::jsonb, true)
|
||||
""",
|
||||
o.data,
|
||||
o.data
|
||||
)
|
||||
]
|
||||
)
|
||||
|> Repo.update_all([])
|
||||
|> case do
|
||||
{1, [object]} -> set_cache(object)
|
||||
_ -> {:error, "Not found"}
|
||||
end
|
||||
end
|
||||
|
||||
def increase_vote_count(ap_id, name, actor) do
|
||||
with %Object{} = object <- Object.normalize(ap_id, fetch: false),
|
||||
"Question" <- object.data["type"] do
|
||||
|
|
|
|||
49
lib/pleroma/prom_ex.ex
Normal file
49
lib/pleroma/prom_ex.ex
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
defmodule Pleroma.PromEx do
|
||||
use PromEx, otp_app: :pleroma
|
||||
|
||||
alias PromEx.Plugins
|
||||
|
||||
@impl true
|
||||
def plugins do
|
||||
[
|
||||
# PromEx built in plugins
|
||||
Plugins.Application,
|
||||
Plugins.Beam,
|
||||
{Plugins.Phoenix, router: Pleroma.Web.Router, endpoint: Pleroma.Web.Endpoint},
|
||||
Plugins.Ecto,
|
||||
Plugins.Oban
|
||||
# Plugins.PhoenixLiveView,
|
||||
# Plugins.Absinthe,
|
||||
# Plugins.Broadway,
|
||||
|
||||
# Add your own PromEx metrics plugins
|
||||
# Pleroma.Users.PromExPlugin
|
||||
]
|
||||
end
|
||||
|
||||
@impl true
|
||||
def dashboard_assigns do
|
||||
[
|
||||
datasource_id: Pleroma.Config.get([Pleroma.PromEx, :datasource]),
|
||||
default_selected_interval: "30s"
|
||||
]
|
||||
end
|
||||
|
||||
@impl true
|
||||
def dashboards do
|
||||
[
|
||||
# PromEx built in Grafana dashboards
|
||||
{:prom_ex, "application.json"},
|
||||
{:prom_ex, "beam.json"},
|
||||
{:prom_ex, "phoenix.json"},
|
||||
{:prom_ex, "ecto.json"},
|
||||
{:prom_ex, "oban.json"}
|
||||
# {:prom_ex, "phoenix_live_view.json"},
|
||||
# {:prom_ex, "absinthe.json"},
|
||||
# {:prom_ex, "broadway.json"},
|
||||
|
||||
# Add your dashboard definitions here with the format: {:otp_app, "path_in_priv"}
|
||||
# {:pleroma, "/grafana_dashboards/user_metrics.json"}
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
@ -11,8 +11,6 @@ defmodule Pleroma.Repo do
|
|||
import Ecto.Query
|
||||
require Logger
|
||||
|
||||
defmodule Instrumenter, do: use(Prometheus.EctoInstrumenter)
|
||||
|
||||
@doc """
|
||||
Dynamically loads the repository url from the
|
||||
DATABASE_URL environment variable.
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ defmodule Pleroma.ReverseProxy do
|
|||
halt(conn)
|
||||
|
||||
{:error, error, conn} ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"#{__MODULE__} request to #{url} failed while reading/chunking: #{inspect(error)}"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.ScheduledActivity do
|
|||
use Ecto.Schema
|
||||
|
||||
alias Ecto.Multi
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.ScheduledActivity
|
||||
alias Pleroma.User
|
||||
|
|
@ -20,6 +19,8 @@ defmodule Pleroma.ScheduledActivity do
|
|||
|
||||
@min_offset :timer.minutes(5)
|
||||
|
||||
@config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
|
||||
|
||||
schema "scheduled_activities" do
|
||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||
field(:scheduled_at, :naive_datetime)
|
||||
|
|
@ -87,7 +88,7 @@ defmodule Pleroma.ScheduledActivity do
|
|||
|> where([sa], type(sa.scheduled_at, :date) == type(^scheduled_at, :date))
|
||||
|> select([sa], count(sa.id))
|
||||
|> Repo.one()
|
||||
|> Kernel.>=(Config.get([ScheduledActivity, :daily_user_limit]))
|
||||
|> Kernel.>=(@config_impl.get([ScheduledActivity, :daily_user_limit]))
|
||||
end
|
||||
|
||||
def exceeds_total_user_limit?(user_id) do
|
||||
|
|
@ -95,7 +96,7 @@ defmodule Pleroma.ScheduledActivity do
|
|||
|> where(user_id: ^user_id)
|
||||
|> select([sa], count(sa.id))
|
||||
|> Repo.one()
|
||||
|> Kernel.>=(Config.get([ScheduledActivity, :total_user_limit]))
|
||||
|> Kernel.>=(@config_impl.get([ScheduledActivity, :total_user_limit]))
|
||||
end
|
||||
|
||||
def far_enough?(scheduled_at) when is_binary(scheduled_at) do
|
||||
|
|
@ -123,7 +124,7 @@ defmodule Pleroma.ScheduledActivity do
|
|||
def create(%User{} = user, attrs) do
|
||||
Multi.new()
|
||||
|> Multi.insert(:scheduled_activity, new(user, attrs))
|
||||
|> maybe_add_jobs(Config.get([ScheduledActivity, :enabled]))
|
||||
|> maybe_add_jobs(@config_impl.get([ScheduledActivity, :enabled]))
|
||||
|> Repo.transaction()
|
||||
|> transaction_response
|
||||
end
|
||||
|
|
|
|||
17
lib/pleroma/search.ex
Normal file
17
lib/pleroma/search.ex
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
defmodule Pleroma.Search do
|
||||
alias Pleroma.Workers.SearchIndexingWorker
|
||||
|
||||
def add_to_index(%Pleroma.Activity{id: activity_id}) do
|
||||
SearchIndexingWorker.enqueue("add_to_index", %{"activity" => activity_id})
|
||||
end
|
||||
|
||||
def remove_from_index(%Pleroma.Object{id: object_id}) do
|
||||
SearchIndexingWorker.enqueue("remove_from_index", %{"object" => object_id})
|
||||
end
|
||||
|
||||
def search(query, options) do
|
||||
search_module = Pleroma.Config.get([Pleroma.Search, :module], Pleroma.Activity)
|
||||
|
||||
search_module.search(options[:for_user], query, options)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Activity.Search do
|
||||
defmodule Pleroma.Search.DatabaseSearch do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Object.Fetcher
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.User
|
||||
|
|
@ -13,8 +14,11 @@ defmodule Pleroma.Activity.Search do
|
|||
|
||||
import Ecto.Query
|
||||
|
||||
@behaviour Pleroma.Search.SearchBackend
|
||||
|
||||
@impl true
|
||||
def search(user, search_query, options \\ []) do
|
||||
index_type = if Pleroma.Config.get([:database, :rum_enabled]), do: :rum, else: :gin
|
||||
index_type = if Config.get([:database, :rum_enabled]), do: :rum, else: :gin
|
||||
limit = Enum.min([Keyword.get(options, :limit), 40])
|
||||
offset = Keyword.get(options, :offset, 0)
|
||||
author = Keyword.get(options, :author)
|
||||
|
|
@ -45,6 +49,12 @@ defmodule Pleroma.Activity.Search do
|
|||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def add_to_index(_activity), do: :ok
|
||||
|
||||
@impl true
|
||||
def remove_from_index(_object), do: :ok
|
||||
|
||||
def maybe_restrict_author(query, %User{} = author) do
|
||||
Activity.Queries.by_author(query, author)
|
||||
end
|
||||
|
|
@ -136,8 +146,8 @@ defmodule Pleroma.Activity.Search do
|
|||
)
|
||||
end
|
||||
|
||||
defp maybe_restrict_local(q, user) do
|
||||
limit = Pleroma.Config.get([:instance, :limit_to_local_content], :unauthenticated)
|
||||
def maybe_restrict_local(q, user) do
|
||||
limit = Config.get([:instance, :limit_to_local_content], :unauthenticated)
|
||||
|
||||
case {limit, user} do
|
||||
{:all, _} -> restrict_local(q)
|
||||
|
|
@ -149,7 +159,7 @@ defmodule Pleroma.Activity.Search do
|
|||
|
||||
defp restrict_local(q), do: where(q, local: true)
|
||||
|
||||
defp maybe_fetch(activities, user, search_query) do
|
||||
def maybe_fetch(activities, user, search_query) do
|
||||
with true <- Regex.match?(~r/https?:/, search_query),
|
||||
{:ok, object} <- Fetcher.fetch_object_from_id(search_query),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
181
lib/pleroma/search/meilisearch.ex
Normal file
181
lib/pleroma/search/meilisearch.ex
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
defmodule Pleroma.Search.Meilisearch do
|
||||
require Logger
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config.Getting, as: Config
|
||||
|
||||
import Pleroma.Search.DatabaseSearch
|
||||
import Ecto.Query
|
||||
|
||||
@behaviour Pleroma.Search.SearchBackend
|
||||
|
||||
defp meili_headers do
|
||||
private_key = Config.get([Pleroma.Search.Meilisearch, :private_key])
|
||||
|
||||
[{"Content-Type", "application/json"}] ++
|
||||
if is_nil(private_key), do: [], else: [{"Authorization", "Bearer #{private_key}"}]
|
||||
end
|
||||
|
||||
def meili_get(path) do
|
||||
endpoint = Config.get([Pleroma.Search.Meilisearch, :url])
|
||||
|
||||
result =
|
||||
Pleroma.HTTP.get(
|
||||
Path.join(endpoint, path),
|
||||
meili_headers()
|
||||
)
|
||||
|
||||
with {:ok, res} <- result do
|
||||
{:ok, Jason.decode!(res.body)}
|
||||
end
|
||||
end
|
||||
|
||||
def meili_post(path, params) do
|
||||
endpoint = Config.get([Pleroma.Search.Meilisearch, :url])
|
||||
|
||||
result =
|
||||
Pleroma.HTTP.post(
|
||||
Path.join(endpoint, path),
|
||||
Jason.encode!(params),
|
||||
meili_headers()
|
||||
)
|
||||
|
||||
with {:ok, res} <- result do
|
||||
{:ok, Jason.decode!(res.body)}
|
||||
end
|
||||
end
|
||||
|
||||
def meili_put(path, params) do
|
||||
endpoint = Config.get([Pleroma.Search.Meilisearch, :url])
|
||||
|
||||
result =
|
||||
Pleroma.HTTP.request(
|
||||
:put,
|
||||
Path.join(endpoint, path),
|
||||
Jason.encode!(params),
|
||||
meili_headers(),
|
||||
[]
|
||||
)
|
||||
|
||||
with {:ok, res} <- result do
|
||||
{:ok, Jason.decode!(res.body)}
|
||||
end
|
||||
end
|
||||
|
||||
def meili_delete(path) do
|
||||
endpoint = Config.get([Pleroma.Search.Meilisearch, :url])
|
||||
|
||||
with {:ok, _} <-
|
||||
Pleroma.HTTP.request(
|
||||
:delete,
|
||||
Path.join(endpoint, path),
|
||||
"",
|
||||
meili_headers(),
|
||||
[]
|
||||
) do
|
||||
:ok
|
||||
else
|
||||
_ -> {:error, "Could not remove from index"}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def search(user, query, options \\ []) do
|
||||
limit = Enum.min([Keyword.get(options, :limit), 40])
|
||||
offset = Keyword.get(options, :offset, 0)
|
||||
author = Keyword.get(options, :author)
|
||||
|
||||
res =
|
||||
meili_post(
|
||||
"/indexes/objects/search",
|
||||
%{q: query, offset: offset, limit: limit}
|
||||
)
|
||||
|
||||
with {:ok, result} <- res do
|
||||
hits = result["hits"] |> Enum.map(& &1["ap"])
|
||||
|
||||
try do
|
||||
hits
|
||||
|> Activity.create_by_object_ap_id()
|
||||
|> Activity.with_preloaded_object()
|
||||
|> Activity.restrict_deactivated_users()
|
||||
|> maybe_restrict_local(user)
|
||||
|> maybe_restrict_author(author)
|
||||
|> maybe_restrict_blocked(user)
|
||||
|> maybe_fetch(user, query)
|
||||
|> order_by([object: obj], desc: obj.data["published"])
|
||||
|> Pleroma.Repo.all()
|
||||
rescue
|
||||
_ -> maybe_fetch([], user, query)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def object_to_search_data(object) do
|
||||
# Only index public or unlisted Notes
|
||||
if not is_nil(object) and object.data["type"] == "Note" and
|
||||
not is_nil(object.data["content"]) and
|
||||
(Pleroma.Constants.as_public() in object.data["to"] or
|
||||
Pleroma.Constants.as_public() in object.data["cc"]) and
|
||||
object.data["content"] not in ["", "."] do
|
||||
data = object.data
|
||||
|
||||
content_str =
|
||||
case data["content"] do
|
||||
[nil | rest] -> to_string(rest)
|
||||
str -> str
|
||||
end
|
||||
|
||||
content =
|
||||
with {:ok, scrubbed} <-
|
||||
FastSanitize.Sanitizer.scrub(content_str, Pleroma.HTML.Scrubber.SearchIndexing),
|
||||
trimmed <- String.trim(scrubbed) do
|
||||
trimmed
|
||||
end
|
||||
|
||||
# Make sure we have a non-empty string
|
||||
if content != "" do
|
||||
{:ok, published, _} = DateTime.from_iso8601(data["published"])
|
||||
|
||||
%{
|
||||
id: object.id,
|
||||
content: content,
|
||||
ap: data["id"],
|
||||
published: published |> DateTime.to_unix()
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def add_to_index(activity) do
|
||||
maybe_search_data = object_to_search_data(activity.object)
|
||||
|
||||
if activity.data["type"] == "Create" and maybe_search_data do
|
||||
result =
|
||||
meili_put(
|
||||
"/indexes/objects/documents",
|
||||
[maybe_search_data]
|
||||
)
|
||||
|
||||
with {:ok, %{"status" => "enqueued"}} <- result do
|
||||
# Added successfully
|
||||
:ok
|
||||
else
|
||||
_ ->
|
||||
# There was an error, report it
|
||||
Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}")
|
||||
{:error, result}
|
||||
end
|
||||
else
|
||||
# The post isn't something we can search, that's ok
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def remove_from_index(object) do
|
||||
meili_delete("/indexes/objects/documents/#{object.id}")
|
||||
end
|
||||
end
|
||||
24
lib/pleroma/search/search_backend.ex
Normal file
24
lib/pleroma/search/search_backend.ex
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
defmodule Pleroma.Search.SearchBackend do
|
||||
@doc """
|
||||
Search statuses with a query, restricting to only those the user should have access to.
|
||||
"""
|
||||
@callback search(user :: Pleroma.User.t(), query :: String.t(), options :: [any()]) :: [
|
||||
Pleroma.Activity.t()
|
||||
]
|
||||
|
||||
@doc """
|
||||
Add the object associated with the activity to the search index.
|
||||
|
||||
The whole activity is passed, to allow filtering on things such as scope.
|
||||
"""
|
||||
@callback add_to_index(activity :: Pleroma.Activity.t()) :: :ok | {:error, any()}
|
||||
|
||||
@doc """
|
||||
Remove the object from the index.
|
||||
|
||||
Just the object, as opposed to the whole activity, is passed, since the object
|
||||
is what contains the actual content and there is no need for fitlering when removing
|
||||
from index.
|
||||
"""
|
||||
@callback remove_from_index(object :: Pleroma.Object.t()) :: {:ok, any()} | {:error, any()}
|
||||
end
|
||||
|
|
@ -70,7 +70,7 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
%{key: key},
|
||||
_
|
||||
) do
|
||||
Logger.warn(fn ->
|
||||
Logger.warning(fn ->
|
||||
"Pool worker for #{key}: Client #{inspect(client_pid)} died before releasing the connection with #{inspect(reason)}"
|
||||
end)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ defmodule Pleroma.Upload do
|
|||
|
||||
"""
|
||||
alias Ecto.UUID
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
require Logger
|
||||
|
|
@ -76,6 +75,8 @@ defmodule Pleroma.Upload do
|
|||
:path
|
||||
]
|
||||
|
||||
@config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
|
||||
|
||||
defp get_description(upload) do
|
||||
case {upload.description, Pleroma.Config.get([Pleroma.Upload, :default_description])} do
|
||||
{description, _} when is_binary(description) -> description
|
||||
|
|
@ -244,18 +245,18 @@ defmodule Pleroma.Upload do
|
|||
defp url_from_spec(_upload, _base_url, {:url, url}), do: url
|
||||
|
||||
def base_url do
|
||||
uploader = Config.get([Pleroma.Upload, :uploader])
|
||||
upload_base_url = Config.get([Pleroma.Upload, :base_url])
|
||||
public_endpoint = Config.get([uploader, :public_endpoint])
|
||||
uploader = @config_impl.get([Pleroma.Upload, :uploader])
|
||||
upload_base_url = @config_impl.get([Pleroma.Upload, :base_url])
|
||||
public_endpoint = @config_impl.get([uploader, :public_endpoint])
|
||||
|
||||
case uploader do
|
||||
Pleroma.Uploaders.Local ->
|
||||
upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/"
|
||||
|
||||
Pleroma.Uploaders.S3 ->
|
||||
bucket = Config.get([Pleroma.Uploaders.S3, :bucket])
|
||||
truncated_namespace = Config.get([Pleroma.Uploaders.S3, :truncated_namespace])
|
||||
namespace = Config.get([Pleroma.Uploaders.S3, :bucket_namespace])
|
||||
bucket = @config_impl.get([Pleroma.Uploaders.S3, :bucket])
|
||||
truncated_namespace = @config_impl.get([Pleroma.Uploaders.S3, :truncated_namespace])
|
||||
namespace = @config_impl.get([Pleroma.Uploaders.S3, :bucket_namespace])
|
||||
|
||||
bucket_with_namespace =
|
||||
cond do
|
||||
|
|
|
|||
|
|
@ -8,27 +8,28 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadata do
|
|||
"""
|
||||
require Logger
|
||||
|
||||
alias Vix.Vips.Image
|
||||
alias Vix.Vips.Operation
|
||||
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
@spec filter(Pleroma.Upload.t()) ::
|
||||
{:ok, :filtered, Pleroma.Upload.t()} | {:ok, :noop} | {:error, String.t()}
|
||||
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _} = upload) do
|
||||
try do
|
||||
image =
|
||||
file
|
||||
|> Mogrify.open()
|
||||
|> Mogrify.verbose()
|
||||
{:ok, image} = Image.new_from_file(file)
|
||||
{width, height} = {Image.width(image), Image.height(image)}
|
||||
|
||||
upload =
|
||||
upload
|
||||
|> Map.put(:width, image.width)
|
||||
|> Map.put(:height, image.height)
|
||||
|> Map.put(:blurhash, get_blurhash(file))
|
||||
|> Map.put(:width, width)
|
||||
|> Map.put(:height, height)
|
||||
|> Map.put(:blurhash, get_blurhash(image))
|
||||
|
||||
{:ok, :filtered, upload}
|
||||
rescue
|
||||
e in ErlangError ->
|
||||
Logger.warn("#{__MODULE__}: #{inspect(e)}")
|
||||
Logger.warning("#{__MODULE__}: #{inspect(e)}")
|
||||
{:ok, :noop}
|
||||
end
|
||||
end
|
||||
|
|
@ -45,7 +46,7 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadata do
|
|||
{:ok, :filtered, upload}
|
||||
rescue
|
||||
e in ErlangError ->
|
||||
Logger.warn("#{__MODULE__}: #{inspect(e)}")
|
||||
Logger.warning("#{__MODULE__}: #{inspect(e)}")
|
||||
{:ok, :noop}
|
||||
end
|
||||
end
|
||||
|
|
@ -53,7 +54,7 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadata do
|
|||
def filter(_), do: {:ok, :noop}
|
||||
|
||||
defp get_blurhash(file) do
|
||||
with {:ok, blurhash} <- :eblurhash.magick(file) do
|
||||
with {:ok, blurhash} <- vips_blurhash(file) do
|
||||
blurhash
|
||||
else
|
||||
_ -> nil
|
||||
|
|
@ -77,7 +78,28 @@ defmodule Pleroma.Upload.Filter.AnalyzeMetadata do
|
|||
%{width: width, height: height}
|
||||
else
|
||||
nil -> {:error, {:ffprobe, :command_not_found}}
|
||||
{:error, _} = error -> error
|
||||
error -> {:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
defp vips_blurhash(%Vix.Vips.Image{} = image) do
|
||||
with {:ok, resized_image} <- Operation.thumbnail_image(image, 100),
|
||||
{height, width} <- {Image.height(resized_image), Image.width(resized_image)},
|
||||
max <- max(height, width),
|
||||
{x, y} <- {max(round(width * 5 / max), 1), max(round(height * 5 / max), 1)} do
|
||||
{:ok, rgb} =
|
||||
if Image.has_alpha?(resized_image) do
|
||||
# remove alpha channel
|
||||
resized_image
|
||||
|> Operation.extract_band!(0, n: 3)
|
||||
|> Image.write_to_binary()
|
||||
else
|
||||
Image.write_to_binary(resized_image)
|
||||
end
|
||||
|
||||
Blurhash.encode(rgb, width, height, x, y)
|
||||
else
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do
|
|||
"""
|
||||
@behaviour Pleroma.Upload.Filter
|
||||
|
||||
@spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()}
|
||||
|
||||
def filter(%Pleroma.Upload{description: description})
|
||||
when is_binary(description),
|
||||
do: {:ok, :noop}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ defmodule Pleroma.Uploaders.S3 do
|
|||
@behaviour Pleroma.Uploaders.Uploader
|
||||
require Logger
|
||||
|
||||
alias Pleroma.Config
|
||||
@ex_aws_impl Application.compile_env(:pleroma, [__MODULE__, :ex_aws_impl], ExAws)
|
||||
@config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
|
||||
|
||||
# The file name is re-encoded with S3's constraints here to comply with previous
|
||||
# links with less strict filenames
|
||||
|
|
@ -22,7 +23,7 @@ defmodule Pleroma.Uploaders.S3 do
|
|||
|
||||
@impl true
|
||||
def put_file(%Pleroma.Upload{} = upload) do
|
||||
config = Config.get([__MODULE__])
|
||||
config = @config_impl.get([__MODULE__])
|
||||
bucket = Keyword.get(config, :bucket)
|
||||
streaming = Keyword.get(config, :streaming_enabled)
|
||||
|
||||
|
|
@ -56,7 +57,7 @@ defmodule Pleroma.Uploaders.S3 do
|
|||
])
|
||||
end
|
||||
|
||||
case ExAws.request(op) do
|
||||
case @ex_aws_impl.request(op) do
|
||||
{:ok, _} ->
|
||||
{:ok, {:file, s3_name}}
|
||||
|
||||
|
|
@ -69,9 +70,9 @@ defmodule Pleroma.Uploaders.S3 do
|
|||
@impl true
|
||||
def delete_file(file) do
|
||||
[__MODULE__, :bucket]
|
||||
|> Config.get()
|
||||
|> @config_impl.get()
|
||||
|> ExAws.S3.delete_object(file)
|
||||
|> ExAws.request()
|
||||
|> @ex_aws_impl.request()
|
||||
|> case do
|
||||
{:ok, %{status_code: 204}} -> :ok
|
||||
error -> {:error, inspect(error)}
|
||||
|
|
@ -83,3 +84,7 @@ defmodule Pleroma.Uploaders.S3 do
|
|||
String.replace(name, @regex, "-")
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Pleroma.Uploaders.S3.ExAwsAPI do
|
||||
@callback request(op :: ExAws.Operation.t()) :: {:ok, ExAws.Operation.t()} | {:error, term()}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1560,7 +1560,7 @@ defmodule Pleroma.User do
|
|||
unmute(muter, mutee)
|
||||
else
|
||||
{who, result} = error ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"User.unmute/2 failed. #{who}: #{result}, muter_id: #{muter_id}, mutee_id: #{mutee_id}"
|
||||
)
|
||||
|
||||
|
|
@ -2136,7 +2136,7 @@ defmodule Pleroma.User do
|
|||
def public_key(_), do: {:error, "key not found"}
|
||||
|
||||
def get_public_key_for_ap_id(ap_id) do
|
||||
with {:ok, %User{} = user} <- get_or_fetch_by_ap_id(ap_id),
|
||||
with %User{} = user <- get_cached_by_ap_id(ap_id),
|
||||
{:ok, public_key} <- public_key(user) do
|
||||
{:ok, public_key}
|
||||
else
|
||||
|
|
@ -2681,6 +2681,8 @@ defmodule Pleroma.User do
|
|||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def update_last_active_at(user), do: user
|
||||
|
||||
def active_user_count(days \\ 30) do
|
||||
active_after = Timex.shift(NaiveDateTime.utc_now(), days: -days)
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ defmodule Pleroma.User.Backup do
|
|||
timestamps()
|
||||
end
|
||||
|
||||
@config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
|
||||
|
||||
def create(user, admin_id \\ nil) do
|
||||
with :ok <- validate_limit(user, admin_id),
|
||||
{:ok, backup} <- user |> new() |> Repo.insert() do
|
||||
|
|
@ -124,7 +126,10 @@ defmodule Pleroma.User.Backup do
|
|||
|> Repo.update()
|
||||
end
|
||||
|
||||
def process(%__MODULE__{} = backup) do
|
||||
def process(
|
||||
%__MODULE__{} = backup,
|
||||
processor_module \\ __MODULE__.Processor
|
||||
) do
|
||||
set_state(backup, :running, 0)
|
||||
|
||||
current_pid = self()
|
||||
|
|
@ -132,7 +137,7 @@ defmodule Pleroma.User.Backup do
|
|||
task =
|
||||
Task.Supervisor.async_nolink(
|
||||
Pleroma.TaskSupervisor,
|
||||
__MODULE__,
|
||||
processor_module,
|
||||
:do_process,
|
||||
[backup, current_pid]
|
||||
)
|
||||
|
|
@ -140,25 +145,8 @@ defmodule Pleroma.User.Backup do
|
|||
wait_backup(backup, backup.processed_number, task)
|
||||
end
|
||||
|
||||
def do_process(backup, current_pid) do
|
||||
with {:ok, zip_file} <- export(backup, current_pid),
|
||||
{:ok, %{size: size}} <- File.stat(zip_file),
|
||||
{:ok, _upload} <- upload(backup, zip_file) do
|
||||
backup
|
||||
|> cast(
|
||||
%{
|
||||
file_size: size,
|
||||
processed: true,
|
||||
state: :complete
|
||||
},
|
||||
[:file_size, :processed, :state]
|
||||
)
|
||||
|> Repo.update()
|
||||
end
|
||||
end
|
||||
|
||||
defp wait_backup(backup, current_processed, task) do
|
||||
wait_time = Pleroma.Config.get([__MODULE__, :process_wait_time])
|
||||
wait_time = @config_impl.get([__MODULE__, :process_wait_time])
|
||||
|
||||
receive do
|
||||
{:progress, new_processed} ->
|
||||
|
|
@ -305,7 +293,7 @@ defmodule Pleroma.User.Backup do
|
|||
acc + 1
|
||||
else
|
||||
{:error, e} ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Error processing backup item: #{inspect(e)}\n The item is: #{inspect(i)}"
|
||||
)
|
||||
|
||||
|
|
@ -365,3 +353,35 @@ defmodule Pleroma.User.Backup do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Pleroma.User.Backup.ProcessorAPI do
|
||||
@callback do_process(%Pleroma.User.Backup{}, pid()) ::
|
||||
{:ok, %Pleroma.User.Backup{}} | {:error, any()}
|
||||
end
|
||||
|
||||
defmodule Pleroma.User.Backup.Processor do
|
||||
@behaviour Pleroma.User.Backup.ProcessorAPI
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User.Backup
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
@impl true
|
||||
def do_process(backup, current_pid) do
|
||||
with {:ok, zip_file} <- Backup.export(backup, current_pid),
|
||||
{:ok, %{size: size}} <- File.stat(zip_file),
|
||||
{:ok, _upload} <- Backup.upload(backup, zip_file) do
|
||||
backup
|
||||
|> cast(
|
||||
%{
|
||||
file_size: size,
|
||||
processed: true,
|
||||
state: :complete
|
||||
},
|
||||
[:file_size, :processed, :state]
|
||||
)
|
||||
|> Repo.update()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ defmodule Pleroma.Web do
|
|||
namespace: Pleroma.Web
|
||||
|
||||
# Import convenience functions from controllers
|
||||
import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1]
|
||||
import Phoenix.Controller, only: [get_csrf_token: 0, view_module: 1]
|
||||
|
||||
import Pleroma.Web.ErrorHelpers
|
||||
import Pleroma.Web.Gettext
|
||||
|
|
|
|||
|
|
@ -96,6 +96,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp increase_replies_count_if_reply(_create_data), do: :noop
|
||||
|
||||
defp increase_quotes_count_if_quote(%{
|
||||
"object" => %{"quoteUrl" => quote_ap_id} = object,
|
||||
"type" => "Create"
|
||||
}) do
|
||||
if is_public?(object) do
|
||||
Object.increase_quotes_count(quote_ap_id)
|
||||
end
|
||||
end
|
||||
|
||||
defp increase_quotes_count_if_quote(_create_data), do: :noop
|
||||
|
||||
@object_types ~w[ChatMessage Question Answer Audio Video Image Event Article Note Page]
|
||||
@impl true
|
||||
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
||||
|
|
@ -140,6 +151,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
|
||||
end)
|
||||
|
||||
# Add local posts to search index
|
||||
if local, do: Pleroma.Search.add_to_index(activity)
|
||||
|
||||
{:ok, activity}
|
||||
else
|
||||
%Activity{} = activity ->
|
||||
|
|
@ -299,6 +313,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
with {:ok, activity} <- insert(create_data, local, fake),
|
||||
{:fake, false, activity} <- {:fake, fake, activity},
|
||||
_ <- increase_replies_count_if_reply(create_data),
|
||||
_ <- increase_quotes_count_if_quote(create_data),
|
||||
{:quick_insert, false, activity} <- {:quick_insert, quick_insert?, activity},
|
||||
{:ok, _actor} <- increase_note_count_if_public(actor, activity),
|
||||
{:ok, _actor} <- update_last_status_at_if_public(actor, activity),
|
||||
|
|
@ -1237,6 +1252,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|
||||
defp restrict_unauthenticated(query, _), do: query
|
||||
|
||||
defp restrict_quote_url(query, %{quote_url: quote_url}) do
|
||||
from([_activity, object] in query,
|
||||
where: fragment("(?)->'quoteUrl' = ?", object.data, ^quote_url)
|
||||
)
|
||||
end
|
||||
|
||||
defp restrict_quote_url(query, _), do: query
|
||||
|
||||
defp exclude_poll_votes(query, %{include_poll_votes: true}), do: query
|
||||
|
||||
defp exclude_poll_votes(query, _) do
|
||||
|
|
@ -1399,6 +1422,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
|> restrict_instance(opts)
|
||||
|> restrict_announce_object_actor(opts)
|
||||
|> restrict_filtered(opts)
|
||||
|> restrict_quote_url(opts)
|
||||
|> maybe_restrict_deactivated_users(opts)
|
||||
|> exclude_poll_votes(opts)
|
||||
|> exclude_chat_messages(opts)
|
||||
|
|
|
|||
|
|
@ -273,12 +273,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
end
|
||||
|
||||
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
|
||||
with %User{} = recipient <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, %User{} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]),
|
||||
with %User{is_active: true} = recipient <- User.get_cached_by_nickname(nickname),
|
||||
{:ok, %User{is_active: true} = actor} <- User.get_or_fetch_by_ap_id(params["actor"]),
|
||||
true <- Utils.recipient_in_message(recipient, actor, params),
|
||||
params <- Utils.maybe_splice_recipient(recipient.ap_id, params) do
|
||||
Federator.incoming_ap_doc(params)
|
||||
json(conn, "ok")
|
||||
else
|
||||
_ ->
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json("Invalid request.")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -287,10 +292,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
json(conn, "ok")
|
||||
end
|
||||
|
||||
def inbox(%{assigns: %{valid_signature: false}} = conn, _params) do
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|> json("Invalid HTTP Signature")
|
||||
def inbox(%{assigns: %{valid_signature: false}, req_headers: req_headers} = conn, params) do
|
||||
Federator.incoming_ap_doc(%{req_headers: req_headers, params: params})
|
||||
json(conn, "ok")
|
||||
end
|
||||
|
||||
# POST /relay/inbox -or- POST /internal/fetch/inbox
|
||||
|
|
@ -476,7 +480,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
|||
|> json(message)
|
||||
|
||||
e ->
|
||||
Logger.warn(fn -> "AP C2S: #{inspect(e)}" end)
|
||||
Logger.warning(fn -> "AP C2S: #{inspect(e)}" end)
|
||||
|
||||
conn
|
||||
|> put_status(:bad_request)
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
@required_description_keys [:key, :related_policy]
|
||||
|
||||
def filter_one(policy, message) do
|
||||
Code.ensure_loaded(policy)
|
||||
|
||||
should_plug_history? =
|
||||
if function_exported?(policy, :history_awareness, 0) do
|
||||
policy.history_awareness()
|
||||
|
|
@ -188,6 +190,8 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
|
||||
def config_descriptions(policies) do
|
||||
Enum.reduce(policies, @mrf_config_descriptions, fn policy, acc ->
|
||||
Code.ensure_loaded(policy)
|
||||
|
||||
if function_exported?(policy, :config_description, 0) do
|
||||
description =
|
||||
@default_description
|
||||
|
|
@ -199,7 +203,7 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
if Enum.all?(@required_description_keys, &Map.has_key?(description, &1)) do
|
||||
[description | acc]
|
||||
else
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"#{policy} config description doesn't have one or all required keys #{inspect(@required_description_keys)}"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do
|
|||
try_follow(follower, message)
|
||||
else
|
||||
nil ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"#{__MODULE__} skipped because of missing `:mrf_follow_bot, :follower_nickname` configuration, the :follower_nickname
|
||||
account does not exist, or the account is not correctly configured as a bot."
|
||||
)
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
|
|||
shortcode
|
||||
|
||||
e ->
|
||||
Logger.warn("MRF.StealEmojiPolicy: Failed to write to #{file_path}: #{inspect(e)}")
|
||||
Logger.warning("MRF.StealEmojiPolicy: Failed to write to #{file_path}: #{inspect(e)}")
|
||||
nil
|
||||
end
|
||||
else
|
||||
|
|
@ -53,7 +53,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
|
|||
end
|
||||
else
|
||||
e ->
|
||||
Logger.warn("MRF.StealEmojiPolicy: Failed to fetch #{url}: #{inspect(e)}")
|
||||
Logger.warning("MRF.StealEmojiPolicy: Failed to fetch #{url}: #{inspect(e)}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do
|
|||
field(:replies_count, :integer, default: 0)
|
||||
field(:like_count, :integer, default: 0)
|
||||
field(:announcement_count, :integer, default: 0)
|
||||
field(:quotes_count, :integer, default: 0)
|
||||
field(:inReplyTo, ObjectValidators.ObjectID)
|
||||
field(:quoteUrl, ObjectValidators.ObjectID)
|
||||
field(:url, ObjectValidators.BareUri)
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
end
|
||||
end
|
||||
|
||||
@spec recipients(User.t(), Activity.t()) :: list(User.t()) | []
|
||||
@spec recipients(User.t(), Activity.t()) :: [[User.t()]]
|
||||
defp recipients(actor, activity) do
|
||||
followers =
|
||||
if actor.follower_address in activity.recipients do
|
||||
|
|
@ -138,7 +138,10 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
[]
|
||||
end
|
||||
|
||||
Pleroma.Web.Federator.Publisher.remote_users(actor, activity) ++ followers ++ fetchers
|
||||
mentioned = Pleroma.Web.Federator.Publisher.remote_users(actor, activity)
|
||||
non_mentioned = (followers ++ fetchers) -- mentioned
|
||||
|
||||
[mentioned, non_mentioned]
|
||||
end
|
||||
|
||||
defp get_cc_ap_ids(ap_id, recipients) do
|
||||
|
|
@ -195,34 +198,39 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
public = is_public?(activity)
|
||||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
|
||||
recipients = recipients(actor, activity)
|
||||
[priority_recipients, recipients] = recipients(actor, activity)
|
||||
|
||||
inboxes =
|
||||
recipients
|
||||
|> Enum.map(fn actor -> actor.inbox end)
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Instances.filter_reachable()
|
||||
[priority_recipients, recipients]
|
||||
|> Enum.map(fn recipients ->
|
||||
recipients
|
||||
|> Enum.map(fn actor -> actor.inbox end)
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Instances.filter_reachable()
|
||||
end)
|
||||
|
||||
Repo.checkout(fn ->
|
||||
Enum.each(inboxes, fn {inbox, unreachable_since} ->
|
||||
%User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end)
|
||||
Enum.each(inboxes, fn inboxes ->
|
||||
Enum.each(inboxes, fn {inbox, unreachable_since} ->
|
||||
%User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end)
|
||||
|
||||
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
|
||||
# instance would only accept a first message for the first recipient and ignore the rest.
|
||||
cc = get_cc_ap_ids(ap_id, recipients)
|
||||
# Get all the recipients on the same host and add them to cc. Otherwise, a remote
|
||||
# instance would only accept a first message for the first recipient and ignore the rest.
|
||||
cc = get_cc_ap_ids(ap_id, recipients)
|
||||
|
||||
json =
|
||||
data
|
||||
|> Map.put("cc", cc)
|
||||
|> Jason.encode!()
|
||||
json =
|
||||
data
|
||||
|> Map.put("cc", cc)
|
||||
|> Jason.encode!()
|
||||
|
||||
Pleroma.Web.Federator.Publisher.enqueue_one(__MODULE__, %{
|
||||
inbox: inbox,
|
||||
json: json,
|
||||
actor_id: actor.id,
|
||||
id: activity.data["id"],
|
||||
unreachable_since: unreachable_since
|
||||
})
|
||||
Pleroma.Web.Federator.Publisher.enqueue_one(__MODULE__, %{
|
||||
inbox: inbox,
|
||||
json: json,
|
||||
actor_id: actor.id,
|
||||
id: activity.data["id"],
|
||||
unreachable_since: unreachable_since
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
|
@ -239,25 +247,36 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
|||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||
json = Jason.encode!(data)
|
||||
|
||||
recipients(actor, activity)
|
||||
|> Enum.map(fn %User{} = user ->
|
||||
determine_inbox(activity, user)
|
||||
end)
|
||||
|> Enum.uniq()
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
|> Instances.filter_reachable()
|
||||
|> Enum.each(fn {inbox, unreachable_since} ->
|
||||
Pleroma.Web.Federator.Publisher.enqueue_one(
|
||||
__MODULE__,
|
||||
%{
|
||||
inbox: inbox,
|
||||
json: json,
|
||||
actor_id: actor.id,
|
||||
id: activity.data["id"],
|
||||
unreachable_since: unreachable_since
|
||||
}
|
||||
)
|
||||
[priority_inboxes, inboxes] =
|
||||
recipients(actor, activity)
|
||||
|> Enum.map(fn recipients ->
|
||||
recipients
|
||||
|> Enum.map(fn actor -> actor.inbox end)
|
||||
|> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
|
||||
end)
|
||||
|
||||
inboxes = inboxes -- priority_inboxes
|
||||
|
||||
[{priority_inboxes, 0}, {inboxes, 1}]
|
||||
|> Enum.each(fn {inboxes, priority} ->
|
||||
inboxes
|
||||
|> Instances.filter_reachable()
|
||||
|> Enum.each(fn {inbox, unreachable_since} ->
|
||||
Pleroma.Web.Federator.Publisher.enqueue_one(
|
||||
__MODULE__,
|
||||
%{
|
||||
inbox: inbox,
|
||||
json: json,
|
||||
actor_id: actor.id,
|
||||
id: activity.data["id"],
|
||||
unreachable_since: unreachable_since
|
||||
},
|
||||
priority: priority
|
||||
)
|
||||
end)
|
||||
end)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
def gather_webfinger_links(%User{} = user) do
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
# - Increase replies count
|
||||
# - Set up ActivityExpiration
|
||||
# - Set up notifications
|
||||
# - Index incoming posts for search (if needed)
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Create"}} = activity, meta) do
|
||||
with {:ok, object, meta} <- handle_object_creation(meta[:object_data], activity, meta),
|
||||
|
|
@ -209,6 +210,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
Object.increase_replies_count(in_reply_to)
|
||||
end
|
||||
|
||||
if quote_url = object.data["quoteUrl"] do
|
||||
Object.increase_quotes_count(quote_url)
|
||||
end
|
||||
|
||||
reply_depth = (meta[:depth] || 0) + 1
|
||||
|
||||
# FIXME: Force inReplyTo to replies
|
||||
|
|
@ -226,6 +231,8 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
|
||||
end)
|
||||
|
||||
Pleroma.Search.add_to_index(Map.put(activity, :object, object))
|
||||
|
||||
meta =
|
||||
meta
|
||||
|> add_notifications(notifications)
|
||||
|
|
@ -285,6 +292,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
# - Reduce the user note count
|
||||
# - Reduce the reply count
|
||||
# - Stream out the activity
|
||||
# - Removes posts from search index (if needed)
|
||||
@impl true
|
||||
def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, meta) do
|
||||
deleted_object =
|
||||
|
|
@ -305,6 +313,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
Object.decrease_replies_count(in_reply_to)
|
||||
end
|
||||
|
||||
if quote_url = deleted_object.data["quoteUrl"] do
|
||||
Object.decrease_quotes_count(quote_url)
|
||||
end
|
||||
|
||||
MessageReference.delete_for_object(deleted_object)
|
||||
|
||||
ap_streamer().stream_out(object)
|
||||
|
|
@ -323,6 +335,11 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
end
|
||||
|
||||
if result == :ok do
|
||||
# Only remove from index when deleting actual objects, not users or anything else
|
||||
with %Pleroma.Object{} <- deleted_object do
|
||||
Pleroma.Search.remove_from_index(deleted_object)
|
||||
end
|
||||
|
||||
{:ok, object, meta}
|
||||
else
|
||||
{:error, result}
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
|> Map.drop(["conversation", "inReplyToAtomUri"])
|
||||
else
|
||||
e ->
|
||||
Logger.warn("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
|
||||
Logger.warning("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
|
||||
object
|
||||
end
|
||||
else
|
||||
|
|
@ -182,7 +182,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
object
|
||||
|
||||
e ->
|
||||
Logger.warn("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}")
|
||||
Logger.warning("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}")
|
||||
object
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
alias Ecto.UUID
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
|
|
@ -852,9 +853,11 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
|||
[actor | reported_activities] = activity.data["object"]
|
||||
|
||||
stripped_activities =
|
||||
Enum.map(reported_activities, fn
|
||||
act when is_map(act) -> act["id"]
|
||||
act when is_binary(act) -> act
|
||||
Enum.reduce(reported_activities, [], fn act, acc ->
|
||||
case ObjectID.cast(act) do
|
||||
{:ok, act} -> [act | acc]
|
||||
_ -> acc
|
||||
end
|
||||
end)
|
||||
|
||||
new_data = put_in(activity.data, ["object"], [actor | stripped_activities])
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
|||
"following" => "#{user.ap_id}/following",
|
||||
"followers" => "#{user.ap_id}/followers",
|
||||
"inbox" => "#{user.ap_id}/inbox",
|
||||
"outbox" => "#{user.ap_id}/outbox",
|
||||
"name" => "Pleroma",
|
||||
"summary" =>
|
||||
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,18 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
|
|||
}
|
||||
end
|
||||
|
||||
def show2_operation do
|
||||
%Operation{
|
||||
tags: ["Instance misc"],
|
||||
summary: "Retrieve instance information",
|
||||
description: "Information about the server",
|
||||
operationId: "InstanceController.show2",
|
||||
responses: %{
|
||||
200 => Operation.response("Instance", "application/json", instance2())
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def peers_operation do
|
||||
%Operation{
|
||||
tags: ["Instance misc"],
|
||||
|
|
@ -165,6 +177,166 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
|
|||
}
|
||||
end
|
||||
|
||||
defp instance2 do
|
||||
%Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
domain: %Schema{type: :string, description: "The domain name of the instance"},
|
||||
title: %Schema{type: :string, description: "The title of the website"},
|
||||
version: %Schema{
|
||||
type: :string,
|
||||
description: "The version of Pleroma installed on the instance"
|
||||
},
|
||||
source_url: %Schema{
|
||||
type: :string,
|
||||
description: "The version of Pleroma installed on the instance"
|
||||
},
|
||||
description: %Schema{
|
||||
type: :string,
|
||||
description: "Admin-defined description of the Pleroma site"
|
||||
},
|
||||
usage: %Schema{
|
||||
type: :object,
|
||||
description: "Instance usage statistics",
|
||||
properties: %{
|
||||
users: %Schema{
|
||||
type: :object,
|
||||
description: "User count statistics",
|
||||
properties: %{
|
||||
active_month: %Schema{
|
||||
type: :integer,
|
||||
description: "Monthly active users"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
email: %Schema{
|
||||
type: :string,
|
||||
description: "An email that may be contacted for any inquiries",
|
||||
format: :email
|
||||
},
|
||||
urls: %Schema{
|
||||
type: :object,
|
||||
description: "URLs of interest for clients apps",
|
||||
properties: %{}
|
||||
},
|
||||
stats: %Schema{
|
||||
type: :object,
|
||||
description: "Statistics about how much information the instance contains",
|
||||
properties: %{
|
||||
user_count: %Schema{
|
||||
type: :integer,
|
||||
description: "Users registered on this instance"
|
||||
},
|
||||
status_count: %Schema{
|
||||
type: :integer,
|
||||
description: "Statuses authored by users on instance"
|
||||
},
|
||||
domain_count: %Schema{
|
||||
type: :integer,
|
||||
description: "Domains federated with this instance"
|
||||
}
|
||||
}
|
||||
},
|
||||
thumbnail: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
url: %Schema{
|
||||
type: :string,
|
||||
description: "Banner image for the website",
|
||||
nullable: true
|
||||
}
|
||||
}
|
||||
},
|
||||
languages: %Schema{
|
||||
type: :array,
|
||||
items: %Schema{type: :string},
|
||||
description: "Primary langauges of the website and its staff"
|
||||
},
|
||||
registrations: %Schema{
|
||||
type: :object,
|
||||
description: "Registrations-related configuration",
|
||||
properties: %{
|
||||
enabled: %Schema{
|
||||
type: :boolean,
|
||||
description: "Whether registrations are enabled"
|
||||
},
|
||||
approval_required: %Schema{
|
||||
type: :boolean,
|
||||
description: "Whether users need to be manually approved by admin"
|
||||
}
|
||||
}
|
||||
},
|
||||
configuration: %Schema{
|
||||
type: :object,
|
||||
description: "Instance configuration",
|
||||
properties: %{
|
||||
urls: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
streaming: %Schema{
|
||||
type: :string,
|
||||
description: "Websockets address for push streaming"
|
||||
}
|
||||
}
|
||||
},
|
||||
statuses: %Schema{
|
||||
type: :object,
|
||||
description: "A map with poll limits for local statuses",
|
||||
properties: %{
|
||||
max_characters: %Schema{
|
||||
type: :integer,
|
||||
description: "Posts character limit (CW/Subject included in the counter)"
|
||||
},
|
||||
max_media_attachments: %Schema{
|
||||
type: :integer,
|
||||
description: "Media attachment limit"
|
||||
}
|
||||
}
|
||||
},
|
||||
media_attachments: %Schema{
|
||||
type: :object,
|
||||
description: "A map with poll limits for media attachments",
|
||||
properties: %{
|
||||
image_size_limit: %Schema{
|
||||
type: :integer,
|
||||
description: "File size limit of uploaded images"
|
||||
},
|
||||
video_size_limit: %Schema{
|
||||
type: :integer,
|
||||
description: "File size limit of uploaded videos"
|
||||
}
|
||||
}
|
||||
},
|
||||
polls: %Schema{
|
||||
type: :object,
|
||||
description: "A map with poll limits for local polls",
|
||||
properties: %{
|
||||
max_options: %Schema{
|
||||
type: :integer,
|
||||
description: "Maximum number of options."
|
||||
},
|
||||
max_characters_per_option: %Schema{
|
||||
type: :integer,
|
||||
description: "Maximum number of characters per option."
|
||||
},
|
||||
min_expiration: %Schema{
|
||||
type: :integer,
|
||||
description: "Minimum expiration time (in seconds)."
|
||||
},
|
||||
max_expiration: %Schema{
|
||||
type: :integer,
|
||||
description: "Maximum expiration time (in seconds)."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp array_of_domains do
|
||||
%Schema{
|
||||
type: :array,
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
|
|||
album: %Schema{type: :string, description: "The album of the media playing"},
|
||||
artist: %Schema{type: :string, description: "The artist of the media playing"},
|
||||
length: %Schema{type: :integer, description: "The length of the media playing"},
|
||||
externalLink: %Schema{type: :string, description: "A URL referencing the media playing"},
|
||||
visibility: %Schema{
|
||||
allOf: [VisibilityScope],
|
||||
default: "public",
|
||||
|
|
@ -69,7 +70,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
|
|||
"title" => "Some Title",
|
||||
"artist" => "Some Artist",
|
||||
"album" => "Some Album",
|
||||
"length" => 180_000
|
||||
"length" => 180_000,
|
||||
"externalLink" => "https://www.last.fm/music/Some+Artist/_/Some+Title"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
@ -83,6 +85,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
|
|||
title: %Schema{type: :string, description: "The title of the media playing"},
|
||||
album: %Schema{type: :string, description: "The album of the media playing"},
|
||||
artist: %Schema{type: :string, description: "The artist of the media playing"},
|
||||
externalLink: %Schema{type: :string, description: "A URL referencing the media playing"},
|
||||
length: %Schema{
|
||||
type: :integer,
|
||||
description: "The length of the media playing",
|
||||
|
|
@ -97,6 +100,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
|
|||
"artist" => "Some Artist",
|
||||
"album" => "Some Album",
|
||||
"length" => 180_000,
|
||||
"externalLink" => "https://www.last.fm/music/Some+Artist/_/Some+Title",
|
||||
"created_at" => "2019-09-28T12:40:45.000Z"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.PleromaStatusOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
alias Pleroma.Web.ApiSpec.StatusOperation
|
||||
|
||||
import Pleroma.Web.ApiSpec.Helpers
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
def quotes_operation do
|
||||
%Operation{
|
||||
tags: ["Retrieve status information"],
|
||||
summary: "Quoted by",
|
||||
description: "View quotes for a given status",
|
||||
operationId: "PleromaAPI.StatusController.quotes",
|
||||
parameters: [id_param() | pagination_params()],
|
||||
security: [%{"oAuth" => ["read:statuses"]}],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"Array of Status",
|
||||
"application/json",
|
||||
StatusOperation.array_of_statuses()
|
||||
),
|
||||
403 => Operation.response("Forbidden", "application/json", ApiError),
|
||||
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def id_param do
|
||||
Operation.parameter(:id, :path, FlakeID, "Status ID",
|
||||
example: "9umDrYheeY451cQnEe",
|
||||
required: true
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -213,6 +213,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
|||
type: :boolean,
|
||||
description: "`true` if the quoted post is visible to the user"
|
||||
},
|
||||
quotes_count: %Schema{
|
||||
type: :integer,
|
||||
description: "How many statuses quoted this status"
|
||||
},
|
||||
local: %Schema{
|
||||
type: :boolean,
|
||||
description: "`true` if the post was made on the local instance"
|
||||
|
|
@ -367,7 +371,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
|
|||
"in_reply_to_account_acct" => nil,
|
||||
"local" => true,
|
||||
"spoiler_text" => %{"text/plain" => ""},
|
||||
"thread_muted" => false
|
||||
"thread_muted" => false,
|
||||
"quotes_count" => 0
|
||||
},
|
||||
"poll" => nil,
|
||||
"reblog" => nil,
|
||||
|
|
|
|||
|
|
@ -550,7 +550,7 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
remove_mute(user, activity)
|
||||
else
|
||||
{what, result} = error ->
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"CommonAPI.remove_mute/2 failed. #{what}: #{result}, user_id: #{user_id}, activity_id: #{activity_id}"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
defp listen_object(draft) do
|
||||
object =
|
||||
draft.params
|
||||
|> Map.take([:album, :artist, :title, :length])
|
||||
|> Map.take([:album, :artist, :title, :length, :externalLink])
|
||||
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|
||||
|> Map.put("type", "Audio")
|
||||
|> Map.put("to", draft.to)
|
||||
|
|
|
|||
|
|
@ -321,13 +321,13 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|
|||
format_asctime(date)
|
||||
else
|
||||
_e ->
|
||||
Logger.warn("Date #{date} in wrong format, must be ISO 8601")
|
||||
Logger.warning("Date #{date} in wrong format, must be ISO 8601")
|
||||
""
|
||||
end
|
||||
end
|
||||
|
||||
def date_to_asctime(date) do
|
||||
Logger.warn("Date #{date} in wrong format, must be ISO 8601")
|
||||
Logger.warning("Date #{date} in wrong format, must be ISO 8601")
|
||||
""
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,20 @@ defmodule Pleroma.Web.Endpoint do
|
|||
|
||||
alias Pleroma.Config
|
||||
|
||||
socket("/socket", Pleroma.Web.UserSocket)
|
||||
socket("/socket", Pleroma.Web.UserSocket,
|
||||
websocket: [
|
||||
path: "/websocket",
|
||||
serializer: [
|
||||
{Phoenix.Socket.V1.JSONSerializer, "~> 1.0.0"},
|
||||
{Phoenix.Socket.V2.JSONSerializer, "~> 2.0.0"}
|
||||
],
|
||||
timeout: 60_000,
|
||||
transport_log: false,
|
||||
compress: false
|
||||
],
|
||||
longpoll: false
|
||||
)
|
||||
|
||||
socket("/live", Phoenix.LiveView.Socket)
|
||||
|
||||
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
|
||||
|
|
@ -138,47 +151,6 @@ defmodule Pleroma.Web.Endpoint do
|
|||
|
||||
plug(Pleroma.Web.Plugs.RemoteIp)
|
||||
|
||||
defmodule Instrumenter do
|
||||
use Prometheus.PhoenixInstrumenter
|
||||
end
|
||||
|
||||
defmodule PipelineInstrumenter do
|
||||
use Prometheus.PlugPipelineInstrumenter
|
||||
end
|
||||
|
||||
defmodule MetricsExporter do
|
||||
use Prometheus.PlugExporter
|
||||
end
|
||||
|
||||
defmodule MetricsExporterCaller do
|
||||
@behaviour Plug
|
||||
|
||||
def init(opts), do: opts
|
||||
|
||||
def call(conn, opts) do
|
||||
prometheus_config = Application.get_env(:prometheus, MetricsExporter, [])
|
||||
ip_whitelist = List.wrap(prometheus_config[:ip_whitelist])
|
||||
|
||||
cond do
|
||||
!prometheus_config[:enabled] ->
|
||||
conn
|
||||
|
||||
ip_whitelist != [] and
|
||||
!Enum.find(ip_whitelist, fn ip ->
|
||||
Pleroma.Helpers.InetHelper.parse_address(ip) == {:ok, conn.remote_ip}
|
||||
end) ->
|
||||
conn
|
||||
|
||||
true ->
|
||||
MetricsExporter.call(conn, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
plug(PipelineInstrumenter)
|
||||
|
||||
plug(MetricsExporterCaller)
|
||||
|
||||
plug(Pleroma.Web.Router)
|
||||
|
||||
@doc """
|
||||
|
|
|
|||
|
|
@ -17,10 +17,28 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
|> json(%{error: "Not implemented"})
|
||||
end
|
||||
|
||||
def add_generated_metadata(page_content, extra \\ "") do
|
||||
title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
|
||||
favicon = "<link rel='icon' href='#{Pleroma.Config.get([:instance, :favicon])}'>"
|
||||
manifest = "<link rel='manifest' href='/manifest.json'>"
|
||||
|
||||
page_content
|
||||
|> String.replace(
|
||||
"<!--server-generated-meta-->",
|
||||
title <> favicon <> manifest <> extra
|
||||
)
|
||||
end
|
||||
|
||||
def redirector(conn, _params, code \\ 200) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
|
||||
response =
|
||||
index_content
|
||||
|> add_generated_metadata()
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|> send_file(code, index_file_path())
|
||||
|> send_resp(code, response)
|
||||
end
|
||||
|
||||
def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do
|
||||
|
|
@ -34,14 +52,12 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
|
||||
def redirector_with_meta(conn, params) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
|
||||
tags = build_tags(conn, params)
|
||||
preloads = preload_data(conn, params)
|
||||
title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
|
||||
|
||||
response =
|
||||
index_content
|
||||
|> String.replace("<!--server-generated-meta-->", tags <> preloads <> title)
|
||||
|> add_generated_metadata(tags <> preloads)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|
|
@ -55,11 +71,10 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|
|||
def redirector_with_preload(conn, params) do
|
||||
{:ok, index_content} = File.read(index_file_path())
|
||||
preloads = preload_data(conn, params)
|
||||
title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
|
||||
|
||||
response =
|
||||
index_content
|
||||
|> String.replace("<!--server-generated-meta-->", preloads <> title)
|
||||
|> add_generated_metadata(preloads)
|
||||
|
||||
conn
|
||||
|> put_resp_content_type("text/html")
|
||||
|
|
|
|||
|
|
@ -35,6 +35,17 @@ defmodule Pleroma.Web.Federator do
|
|||
end
|
||||
|
||||
# Client API
|
||||
def incoming_ap_doc(%{params: params, req_headers: req_headers}) do
|
||||
ReceiverWorker.enqueue(
|
||||
"incoming_ap_doc",
|
||||
%{"req_headers" => req_headers, "params" => params, "timeout" => :timer.seconds(20)},
|
||||
priority: 2
|
||||
)
|
||||
end
|
||||
|
||||
def incoming_ap_doc(%{"type" => "Delete"} = params) do
|
||||
ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params}, priority: 3)
|
||||
end
|
||||
|
||||
def incoming_ap_doc(params) do
|
||||
ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params})
|
||||
|
|
|
|||
|
|
@ -29,11 +29,12 @@ defmodule Pleroma.Web.Federator.Publisher do
|
|||
@doc """
|
||||
Enqueue publishing a single activity.
|
||||
"""
|
||||
@spec enqueue_one(module(), Map.t()) :: :ok
|
||||
def enqueue_one(module, %{} = params) do
|
||||
@spec enqueue_one(module(), Map.t(), Keyword.t()) :: {:ok, %Oban.Job{}}
|
||||
def enqueue_one(module, %{} = params, worker_args \\ []) do
|
||||
PublisherWorker.enqueue(
|
||||
"publish_one",
|
||||
%{"module" => to_string(module), "params" => params}
|
||||
%{"module" => to_string(module), "params" => params},
|
||||
worker_args
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do
|
|||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
plug(:skip_auth when action in [:show, :peers])
|
||||
plug(:skip_auth when action in [:show, :show2, :peers])
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.InstanceOperation
|
||||
|
||||
|
|
@ -16,6 +16,11 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do
|
|||
render(conn, "show.json")
|
||||
end
|
||||
|
||||
@doc "GET /api/v2/instance"
|
||||
def show2(conn, _params) do
|
||||
render(conn, "show2.json")
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/instance/peers"
|
||||
def peers(conn, _params) do
|
||||
json(conn, Pleroma.Stats.get_peers())
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ControllerHelper
|
||||
|
|
@ -100,7 +99,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
|||
end
|
||||
|
||||
defp resource_search(_, "statuses", query, options) do
|
||||
statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end)
|
||||
statuses = with_fallback(fn -> Pleroma.Search.search(query, options) end)
|
||||
|
||||
StatusView.render("index.json",
|
||||
activities: statuses,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
|
|
@ -249,6 +249,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
nil
|
||||
end
|
||||
|
||||
last_status_at =
|
||||
user.last_status_at &&
|
||||
user.last_status_at |> NaiveDateTime.to_date() |> Date.to_iso8601()
|
||||
|
||||
%{
|
||||
id: to_string(user.id),
|
||||
username: username_from_nickname(user.nickname),
|
||||
|
|
@ -277,7 +281,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
actor_type: user.actor_type
|
||||
}
|
||||
},
|
||||
last_status_at: user.last_status_at,
|
||||
last_status_at: last_status_at,
|
||||
|
||||
# Pleroma extensions
|
||||
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
|
||||
|
|
|
|||
|
|
@ -13,12 +13,11 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
def render("show.json", _) do
|
||||
instance = Config.get(:instance)
|
||||
|
||||
%{
|
||||
common_information(instance)
|
||||
|> Map.merge(%{
|
||||
uri: Pleroma.Web.WebFinger.host(),
|
||||
title: Keyword.get(instance, :name),
|
||||
description: Keyword.get(instance, :description),
|
||||
short_description: Keyword.get(instance, :short_description),
|
||||
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
|
||||
email: Keyword.get(instance, :email),
|
||||
urls: %{
|
||||
streaming_api: Pleroma.Web.Endpoint.websocket_url()
|
||||
|
|
@ -27,9 +26,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
thumbnail:
|
||||
URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
|
||||
|> to_string,
|
||||
languages: Keyword.get(instance, :languages, ["en"]),
|
||||
registrations: Keyword.get(instance, :registrations_open),
|
||||
approval_required: Keyword.get(instance, :account_approval_required),
|
||||
configuration: configuration(),
|
||||
# Extra (not present in Mastodon):
|
||||
max_toot_chars: Keyword.get(instance, :limit),
|
||||
max_media_attachments: Keyword.get(instance, :max_media_attachments),
|
||||
|
|
@ -41,19 +40,44 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
|
||||
shout_limit: Config.get([:shout, :limit]),
|
||||
description_limit: Keyword.get(instance, :description_limit),
|
||||
pleroma: %{
|
||||
metadata: %{
|
||||
account_activation_required: Keyword.get(instance, :account_activation_required),
|
||||
features: features(),
|
||||
federation: federation(),
|
||||
fields_limits: fields_limits(),
|
||||
post_formats: Config.get([:instance, :allowed_post_formats]),
|
||||
birthday_required: Config.get([:instance, :birthday_required]),
|
||||
birthday_min_age: Config.get([:instance, :birthday_min_age])
|
||||
},
|
||||
stats: %{mau: Pleroma.User.active_user_count()},
|
||||
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
||||
}
|
||||
pleroma: pleroma_configuration(instance)
|
||||
})
|
||||
end
|
||||
|
||||
def render("show2.json", _) do
|
||||
instance = Config.get(:instance)
|
||||
|
||||
common_information(instance)
|
||||
|> Map.merge(%{
|
||||
domain: Pleroma.Web.WebFinger.host(),
|
||||
source_url: Pleroma.Application.repository(),
|
||||
description: Keyword.get(instance, :short_description),
|
||||
usage: %{users: %{active_month: Pleroma.User.active_user_count()}},
|
||||
thumbnail: %{
|
||||
url:
|
||||
URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
|
||||
|> to_string
|
||||
},
|
||||
configuration: configuration2(),
|
||||
registrations: %{
|
||||
enabled: Keyword.get(instance, :registrations_open),
|
||||
approval_required: Keyword.get(instance, :account_approval_required),
|
||||
message: nil
|
||||
},
|
||||
contact: %{
|
||||
email: Keyword.get(instance, :email),
|
||||
account: nil
|
||||
},
|
||||
# Extra (not present in Mastodon):
|
||||
pleroma: pleroma_configuration2(instance)
|
||||
})
|
||||
end
|
||||
|
||||
defp common_information(instance) do
|
||||
%{
|
||||
title: Keyword.get(instance, :name),
|
||||
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
|
||||
languages: Keyword.get(instance, :languages, ["en"])
|
||||
}
|
||||
end
|
||||
|
||||
|
|
@ -133,7 +157,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
|> Map.put(:enabled, Config.get([:instance, :federating]))
|
||||
end
|
||||
|
||||
def fields_limits do
|
||||
defp fields_limits do
|
||||
%{
|
||||
max_fields: Config.get([:instance, :max_account_fields]),
|
||||
max_remote_fields: Config.get([:instance, :max_remote_account_fields]),
|
||||
|
|
@ -141,4 +165,65 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|
|||
value_length: Config.get([:instance, :account_field_value_length])
|
||||
}
|
||||
end
|
||||
|
||||
defp configuration do
|
||||
%{
|
||||
statuses: %{
|
||||
max_characters: Config.get([:instance, :limit]),
|
||||
max_media_attachments: Config.get([:instance, :max_media_attachments])
|
||||
},
|
||||
media_attachments: %{
|
||||
image_size_limit: Config.get([:instance, :upload_limit]),
|
||||
video_size_limit: Config.get([:instance, :upload_limit])
|
||||
},
|
||||
polls: %{
|
||||
max_options: Config.get([:instance, :poll_limits, :max_options]),
|
||||
max_characters_per_option: Config.get([:instance, :poll_limits, :max_option_chars]),
|
||||
min_expiration: Config.get([:instance, :poll_limits, :min_expiration]),
|
||||
max_expiration: Config.get([:instance, :poll_limits, :max_expiration])
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp configuration2 do
|
||||
configuration()
|
||||
|> Map.merge(%{
|
||||
urls: %{streaming: Pleroma.Web.Endpoint.websocket_url()}
|
||||
})
|
||||
end
|
||||
|
||||
defp pleroma_configuration(instance) do
|
||||
%{
|
||||
metadata: %{
|
||||
account_activation_required: Keyword.get(instance, :account_activation_required),
|
||||
features: features(),
|
||||
federation: federation(),
|
||||
fields_limits: fields_limits(),
|
||||
post_formats: Config.get([:instance, :allowed_post_formats]),
|
||||
birthday_required: Config.get([:instance, :birthday_required]),
|
||||
birthday_min_age: Config.get([:instance, :birthday_min_age])
|
||||
},
|
||||
stats: %{mau: Pleroma.User.active_user_count()},
|
||||
vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
|
||||
}
|
||||
end
|
||||
|
||||
defp pleroma_configuration2(instance) do
|
||||
configuration = pleroma_configuration(instance)
|
||||
|
||||
configuration
|
||||
|> Map.merge(%{
|
||||
metadata:
|
||||
configuration.metadata
|
||||
|> Map.merge(%{
|
||||
avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
|
||||
background_upload_limit: Keyword.get(instance, :background_upload_limit),
|
||||
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
||||
background_image:
|
||||
Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
|
||||
description_limit: Keyword.get(instance, :description_limit),
|
||||
shout_limit: Config.get([:shout, :limit])
|
||||
})
|
||||
})
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -447,7 +447,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
thread_muted: thread_muted?,
|
||||
emoji_reactions: emoji_reactions,
|
||||
parent_visible: visible_for_user?(reply_to, opts[:for]),
|
||||
pinned_at: pinned_at
|
||||
pinned_at: pinned_at,
|
||||
quotes_count: object.data["quotesCount"] || 0
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
@ -562,25 +563,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|
||||
page_url = page_url_data |> to_string
|
||||
|
||||
image_url_data =
|
||||
if is_binary(rich_media["image"]) do
|
||||
URI.parse(rich_media["image"])
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
image_url = build_image_url(image_url_data, page_url_data)
|
||||
image_url = proxied_url(rich_media["image"], page_url_data)
|
||||
audio_url = proxied_url(rich_media["audio"], page_url_data)
|
||||
video_url = proxied_url(rich_media["video"], page_url_data)
|
||||
|
||||
%{
|
||||
type: "link",
|
||||
provider_name: page_url_data.host,
|
||||
provider_url: page_url_data.scheme <> "://" <> page_url_data.host,
|
||||
url: page_url,
|
||||
image: image_url |> MediaProxy.url(),
|
||||
image: image_url,
|
||||
title: rich_media["title"] || "",
|
||||
description: rich_media["description"] || "",
|
||||
pleroma: %{
|
||||
opengraph: rich_media
|
||||
opengraph:
|
||||
rich_media
|
||||
|> Maps.put_if_present("image", image_url)
|
||||
|> Maps.put_if_present("audio", audio_url)
|
||||
|> Maps.put_if_present("video", video_url)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
@ -817,4 +817,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
defp get_source_content_type(_source) do
|
||||
Utils.get_content_type(nil)
|
||||
end
|
||||
|
||||
defp proxied_url(url, page_url_data) do
|
||||
if is_binary(url) do
|
||||
build_image_url(URI.parse(url), page_url_data) |> MediaProxy.url()
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
66
lib/pleroma/web/pleroma_api/controllers/status_controller.ex
Normal file
66
lib/pleroma/web/pleroma_api/controllers/status_controller.ex
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.PleromaAPI.StatusController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
|
||||
|
||||
require Ecto.Query
|
||||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} when action == :quotes
|
||||
)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaStatusOperation
|
||||
|
||||
@doc "GET /api/v1/pleroma/statuses/:id/quotes"
|
||||
def quotes(%{assigns: %{user: user}} = conn, %{id: id} = params) do
|
||||
with %Activity{object: object} = activity <- Activity.get_by_id_with_object(id),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
params =
|
||||
params
|
||||
|> Map.put(:type, "Create")
|
||||
|> Map.put(:blocking_user, user)
|
||||
|> Map.put(:quote_url, object.data["id"])
|
||||
|
||||
recipients =
|
||||
if user do
|
||||
[Pleroma.Constants.as_public()] ++ [user.ap_id | User.following(user)]
|
||||
else
|
||||
[Pleroma.Constants.as_public()]
|
||||
end
|
||||
|
||||
activities =
|
||||
recipients
|
||||
|> ActivityPub.fetch_activities(params)
|
||||
|> Enum.reverse()
|
||||
|
||||
conn
|
||||
|> add_link_headers(activities)
|
||||
|> put_view(StatusView)
|
||||
|> render("index.json",
|
||||
activities: activities,
|
||||
for: user,
|
||||
as: :activity
|
||||
)
|
||||
else
|
||||
nil -> {:error, :not_found}
|
||||
false -> {:error, :not_found}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -27,6 +27,7 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleView do
|
|||
title: object.data["title"] |> HTML.strip_tags(),
|
||||
artist: object.data["artist"] |> HTML.strip_tags(),
|
||||
album: object.data["album"] |> HTML.strip_tags(),
|
||||
externalLink: object.data["externalLink"],
|
||||
length: object.data["length"]
|
||||
}
|
||||
end
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do
|
|||
|
||||
def warn_if_disabled do
|
||||
unless Config.get([:http_security, :enabled]) do
|
||||
Logger.warn("
|
||||
Logger.warning("
|
||||
.i;;;;i.
|
||||
iYcviii;vXY:
|
||||
.YXi .i1c.
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
|
|||
end
|
||||
|
||||
defp handle_disabled(conn) do
|
||||
Logger.warn(
|
||||
Logger.warning(
|
||||
"Rate limiter disabled due to forwarded IP not being found. Please ensure your reverse proxy is providing the X-Forwarded-For header or disable the RemoteIP plug/rate limiter."
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.Push do
|
|||
|
||||
def init do
|
||||
unless enabled() do
|
||||
Logger.warn("""
|
||||
Logger.warning("""
|
||||
VAPID key pair is not found. If you wish to enabled web push, please run
|
||||
|
||||
mix web_push.gen.keypair
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ defmodule Pleroma.Web.Push.Impl do
|
|||
end
|
||||
|
||||
def perform(_) do
|
||||
Logger.warn("Unknown notification type")
|
||||
Logger.warning("Unknown notification type")
|
||||
{:error, :unknown_type}
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,12 @@
|
|||
|
||||
defmodule Pleroma.Web.RichMedia.Helpers do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.RichMedia.Parser
|
||||
|
||||
@config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
|
||||
|
||||
@options [
|
||||
pool: :media,
|
||||
max_body: 2_000_000,
|
||||
|
|
@ -17,7 +18,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
|||
|
||||
@spec validate_page_url(URI.t() | binary()) :: :ok | :error
|
||||
defp validate_page_url(page_url) when is_binary(page_url) do
|
||||
validate_tld = Config.get([Pleroma.Formatter, :validate_tld])
|
||||
validate_tld = @config_impl.get([Pleroma.Formatter, :validate_tld])
|
||||
|
||||
page_url
|
||||
|> Linkify.Parser.url?(validate_tld: validate_tld)
|
||||
|
|
@ -27,10 +28,10 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
|||
defp validate_page_url(%URI{host: host, scheme: "https", authority: authority})
|
||||
when is_binary(authority) do
|
||||
cond do
|
||||
host in Config.get([:rich_media, :ignore_hosts], []) ->
|
||||
host in @config_impl.get([:rich_media, :ignore_hosts], []) ->
|
||||
:error
|
||||
|
||||
get_tld(host) in Config.get([:rich_media, :ignore_tld], []) ->
|
||||
get_tld(host) in @config_impl.get([:rich_media, :ignore_tld], []) ->
|
||||
:error
|
||||
|
||||
true ->
|
||||
|
|
@ -56,7 +57,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
|||
end
|
||||
|
||||
def fetch_data_for_object(object) do
|
||||
with true <- Config.get([:rich_media, :enabled]),
|
||||
with true <- @config_impl.get([:rich_media, :enabled]),
|
||||
{:ok, page_url} <-
|
||||
HTML.extract_first_external_url_from_object(object),
|
||||
:ok <- validate_page_url(page_url),
|
||||
|
|
@ -68,7 +69,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
|||
end
|
||||
|
||||
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||
with true <- Config.get([:rich_media, :enabled]),
|
||||
with true <- @config_impl.get([:rich_media, :enabled]),
|
||||
%Object{} = object <- Object.normalize(activity, fetch: false) do
|
||||
fetch_data_for_object(object)
|
||||
else
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
|||
end
|
||||
|
||||
defp log_error(url, reason) do
|
||||
Logger.warn(fn -> "Rich media error for #{url}: #{inspect(reason)}" end)
|
||||
Logger.warning(fn -> "Rich media error for #{url}: #{inspect(reason)}" end)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -224,6 +224,12 @@ defmodule Pleroma.Web.Router do
|
|||
post("/remote_interaction", UtilController, :remote_interaction)
|
||||
end
|
||||
|
||||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||
pipe_through(:pleroma_api)
|
||||
|
||||
get("/federation_status", InstancesController, :show)
|
||||
end
|
||||
|
||||
scope "/api/v1/pleroma", Pleroma.Web do
|
||||
pipe_through(:pleroma_api)
|
||||
post("/uploader_callback/:upload_path", UploaderController, :callback)
|
||||
|
|
@ -465,6 +471,8 @@ defmodule Pleroma.Web.Router do
|
|||
get("/main/ostatus", UtilController, :show_subscribe_form)
|
||||
get("/ostatus_subscribe", RemoteFollowController, :follow)
|
||||
post("/ostatus_subscribe", RemoteFollowController, :do_follow)
|
||||
|
||||
get("/authorize_interaction", RemoteFollowController, :authorize_interaction)
|
||||
end
|
||||
|
||||
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
||||
|
|
@ -578,6 +586,8 @@ defmodule Pleroma.Web.Router do
|
|||
pipe_through(:api)
|
||||
get("/accounts/:id/favourites", AccountController, :favourites)
|
||||
get("/accounts/:id/endorsements", AccountController, :endorsements)
|
||||
|
||||
get("/statuses/:id/quotes", StatusController, :quotes)
|
||||
end
|
||||
|
||||
scope [] do
|
||||
|
|
@ -602,7 +612,6 @@ defmodule Pleroma.Web.Router do
|
|||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||
pipe_through(:api)
|
||||
get("/accounts/:id/scrobbles", ScrobbleController, :index)
|
||||
get("/federation_status", InstancesController, :show)
|
||||
end
|
||||
|
||||
scope "/api/v2/pleroma", Pleroma.Web.PleromaAPI do
|
||||
|
|
@ -774,11 +783,14 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
||||
pipe_through(:api)
|
||||
|
||||
get("/search", SearchController, :search2)
|
||||
|
||||
post("/media", MediaController, :create2)
|
||||
|
||||
get("/suggestions", SuggestionController, :index2)
|
||||
|
||||
get("/instance", InstanceController, :show2)
|
||||
end
|
||||
|
||||
scope "/api", Pleroma.Web do
|
||||
|
|
@ -1003,9 +1015,8 @@ defmodule Pleroma.Web.Router do
|
|||
options("/*path", RedirectController, :empty)
|
||||
end
|
||||
|
||||
# TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
|
||||
def get_api_routes do
|
||||
__MODULE__.__routes__()
|
||||
Phoenix.Router.routes(__MODULE__)
|
||||
|> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
|
||||
|> Enum.map(fn r ->
|
||||
r.path
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<%= if get_flash(@conn, :info) do %>
|
||||
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
||||
<%= if Phoenix.Flash.get(@flash, :info) do %>
|
||||
<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
|
||||
<% end %>
|
||||
<%= if get_flash(@conn, :error) do %>
|
||||
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||
<%= if Phoenix.Flash.get(@flash, :error) do %>
|
||||
<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
|
||||
<% end %>
|
||||
|
||||
<h2><%= Gettext.dpgettext("static_pages", "mfa recover page title", "Two-factor recovery") %></h2>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<%= if get_flash(@conn, :info) do %>
|
||||
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
||||
<%= if Phoenix.Flash.get(@flash, :info) do %>
|
||||
<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
|
||||
<% end %>
|
||||
<%= if get_flash(@conn, :error) do %>
|
||||
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||
<%= if Phoenix.Flash.get(@flash, :error) do %>
|
||||
<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
|
||||
<% end %>
|
||||
|
||||
<h2><%= Gettext.dpgettext("static_pages", "mfa auth page title", "Two-factor authentication") %></h2>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<%= if get_flash(@conn, :info) do %>
|
||||
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
||||
<%= if Phoenix.Flash.get(@flash, :info) do %>
|
||||
<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
|
||||
<% end %>
|
||||
<%= if get_flash(@conn, :error) do %>
|
||||
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||
<%= if Phoenix.Flash.get(@flash, :error) do %>
|
||||
<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
|
||||
<% end %>
|
||||
|
||||
<h2><%= Gettext.dpgettext("static_pages", "oauth register page title", "Registration Details") %></h2>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<%= if get_flash(@conn, :info) do %>
|
||||
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
||||
<%= if Phoenix.Flash.get(@flash, :info) do %>
|
||||
<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
|
||||
<% end %>
|
||||
<%= if get_flash(@conn, :error) do %>
|
||||
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||
<%= if Phoenix.Flash.get(@flash, :error) do %>
|
||||
<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
|
||||
<% end %>
|
||||
|
||||
<%= form_for @conn, Routes.o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
|
||||
|
|
|
|||
|
|
@ -121,6 +121,13 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
|||
render(conn, "followed.html", %{error: "Insufficient permissions: follow | write:follows."})
|
||||
end
|
||||
|
||||
# GET /authorize_interaction
|
||||
#
|
||||
def authorize_interaction(conn, %{"uri" => uri}) do
|
||||
conn
|
||||
|> redirect(to: Routes.remote_follow_path(conn, :follow, %{acct: uri}))
|
||||
end
|
||||
|
||||
defp handle_follow_error(conn, {:mfa_token, followee, _} = _) do
|
||||
render(conn, "follow_login.html", %{error: "Wrong username or password", followee: followee})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -345,13 +345,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
end
|
||||
|
||||
def healthcheck(conn, _params) do
|
||||
with true <- Config.get([:instance, :healthcheck]),
|
||||
with {:cfg, true} <- {:cfg, Config.get([:instance, :healthcheck])},
|
||||
%{healthy: true} = info <- Healthcheck.system_info() do
|
||||
json(conn, info)
|
||||
else
|
||||
%{healthy: false} = info ->
|
||||
service_unavailable(conn, info)
|
||||
|
||||
{:cfg, false} ->
|
||||
service_unavailable(conn, %{"error" => "Healthcheck disabled"})
|
||||
|
||||
_ ->
|
||||
service_unavailable(conn, %{})
|
||||
end
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ defmodule Pleroma.Web.WebFinger do
|
|||
get_template_from_xml(body)
|
||||
else
|
||||
error ->
|
||||
Logger.warn("Can't find LRDD template in #{inspect(meta_url)}: #{inspect(error)}")
|
||||
Logger.warning("Can't find LRDD template in #{inspect(meta_url)}: #{inspect(error)}")
|
||||
{:error, :lrdd_not_found}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorker do
|
|||
The worker to send digest emails.
|
||||
"""
|
||||
|
||||
use Oban.Worker, queue: "digest_emails"
|
||||
use Oban.Worker, queue: "mailer"
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Emails
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do
|
|||
|
||||
import Ecto.Query
|
||||
|
||||
use Pleroma.Workers.WorkerHelper, queue: "new_users_digest"
|
||||
use Pleroma.Workers.WorkerHelper, queue: "mailer"
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(_job) do
|
||||
|
|
|
|||
|
|
@ -3,24 +3,56 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.ReceiverWorker do
|
||||
alias Pleroma.Signature
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.Federator
|
||||
|
||||
use Pleroma.Workers.WorkerHelper, queue: "federator_incoming"
|
||||
|
||||
@impl Oban.Worker
|
||||
|
||||
def perform(%Job{
|
||||
args: %{"op" => "incoming_ap_doc", "req_headers" => req_headers, "params" => params}
|
||||
}) do
|
||||
# Oban's serialization converts our tuple headers to lists.
|
||||
# Revert it for the signature validation.
|
||||
req_headers = Enum.into(req_headers, [], &List.to_tuple(&1))
|
||||
|
||||
conn_data = %{params: params, req_headers: req_headers}
|
||||
|
||||
with {:ok, %User{} = _actor} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]),
|
||||
{:ok, _public_key} <- Signature.refetch_public_key(conn_data),
|
||||
{:signature, true} <- {:signature, HTTPSignatures.validate_conn(conn_data)},
|
||||
{:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
|
||||
{:ok, res}
|
||||
else
|
||||
e -> process_errors(e)
|
||||
end
|
||||
end
|
||||
|
||||
def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params}}) do
|
||||
with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
|
||||
{:ok, res}
|
||||
else
|
||||
e -> process_errors(e)
|
||||
end
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def timeout(%_{args: %{"timeout" => timeout}}), do: timeout
|
||||
|
||||
def timeout(_job), do: :timer.seconds(5)
|
||||
|
||||
defp process_errors(errors) do
|
||||
case errors do
|
||||
{:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed}
|
||||
{:error, :already_present} -> {:cancel, :already_present}
|
||||
{:error, {:validate_object, reason}} -> {:cancel, reason}
|
||||
{:error, {:error, {:validate, reason}}} -> {:cancel, reason}
|
||||
{:error, {:reject, reason}} -> {:cancel, reason}
|
||||
{:signature, false} -> {:cancel, :invalid_signature}
|
||||
{:error, {:error, reason = "Object has been deleted"}} -> {:cancel, reason}
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
@impl Oban.Worker
|
||||
def timeout(_job), do: :timer.seconds(5)
|
||||
end
|
||||
|
|
|
|||
23
lib/pleroma/workers/search_indexing_worker.ex
Normal file
23
lib/pleroma/workers/search_indexing_worker.ex
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
defmodule Pleroma.Workers.SearchIndexingWorker do
|
||||
use Pleroma.Workers.WorkerHelper, queue: "search_indexing"
|
||||
|
||||
@impl Oban.Worker
|
||||
|
||||
alias Pleroma.Config.Getting, as: Config
|
||||
|
||||
def perform(%Job{args: %{"op" => "add_to_index", "activity" => activity_id}}) do
|
||||
activity = Pleroma.Activity.get_by_id_with_object(activity_id)
|
||||
|
||||
search_module = Config.get([Pleroma.Search, :module])
|
||||
|
||||
search_module.add_to_index(activity)
|
||||
end
|
||||
|
||||
def perform(%Job{args: %{"op" => "remove_from_index", "object" => object_id}}) do
|
||||
object = Pleroma.Object.get_by_id(object_id)
|
||||
|
||||
search_module = Config.get([Pleroma.Search, :module])
|
||||
|
||||
search_module.remove_from_index(object)
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue