Merge remote-tracking branch 'upstream/develop' into chat-moderation
This commit is contained in:
commit
9733c9d065
168 changed files with 2652 additions and 8013 deletions
|
|
@ -18,6 +18,7 @@ defmodule Mix.Pleroma do
|
|||
@doc "Common functions to be reused in mix tasks"
|
||||
def start_pleroma do
|
||||
Pleroma.Config.Holder.save_default()
|
||||
Pleroma.Config.Oban.warn()
|
||||
Application.put_env(:phoenix, :serve_endpoints, false, persistent: true)
|
||||
|
||||
if Pleroma.Config.get(:env) != :test do
|
||||
|
|
|
|||
|
|
@ -91,20 +91,17 @@ defmodule Mix.Tasks.Pleroma.Benchmark do
|
|||
"Without conn and without pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
|
||||
adapter: [pool: :no_pool, receive_conn: false]
|
||||
pool: :no_pool,
|
||||
receive_conn: false
|
||||
)
|
||||
end,
|
||||
"Without conn and with pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
|
||||
adapter: [receive_conn: false]
|
||||
)
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [], receive_conn: false)
|
||||
end,
|
||||
"With reused conn and without pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
|
||||
adapter: [pool: :no_pool]
|
||||
)
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [], pool: :no_pool)
|
||||
end,
|
||||
"With reused conn and with pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} = Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500")
|
||||
|
|
|
|||
|
|
@ -133,8 +133,7 @@ defmodule Mix.Tasks.Pleroma.Database do
|
|||
days = Pleroma.Config.get([:mrf_activity_expiration, :days], 365)
|
||||
|
||||
Pleroma.Activity
|
||||
|> join(:left, [a], u in assoc(a, :expiration))
|
||||
|> join(:inner, [a, _u], o in Object,
|
||||
|> join(:inner, [a], o in Object,
|
||||
on:
|
||||
fragment(
|
||||
"(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')",
|
||||
|
|
@ -144,14 +143,20 @@ defmodule Mix.Tasks.Pleroma.Database do
|
|||
)
|
||||
)
|
||||
|> where(local: true)
|
||||
|> where([a, u], is_nil(u))
|
||||
|> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
|
||||
|> where([_a, _u, o], fragment("?->>'type' = 'Note'", o.data))
|
||||
|> where([_a, o], fragment("?->>'type' = 'Note'", o.data))
|
||||
|> Pleroma.RepoStreamer.chunk_stream(100)
|
||||
|> Stream.each(fn activities ->
|
||||
Enum.each(activities, fn activity ->
|
||||
expires_at = Timex.shift(activity.inserted_at, days: days)
|
||||
Pleroma.ActivityExpiration.create(activity, expires_at, false)
|
||||
expires_at =
|
||||
activity.inserted_at
|
||||
|> DateTime.from_naive!("Etc/UTC")
|
||||
|> Timex.shift(days: days)
|
||||
|
||||
Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
|
||||
activity_id: activity.id,
|
||||
expires_at: expires_at
|
||||
})
|
||||
end)
|
||||
end)
|
||||
|> Stream.run()
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do
|
|||
|
||||
IO.puts("Downloading the pack and generating SHA256")
|
||||
|
||||
binary_archive = Tesla.get!(client(), src).body
|
||||
{:ok, %{body: binary_archive}} = Pleroma.HTTP.get(src)
|
||||
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
|
||||
|
||||
IO.puts("SHA256 is #{archive_sha}")
|
||||
|
|
@ -252,7 +252,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do
|
|||
end
|
||||
|
||||
defp fetch("http" <> _ = from) do
|
||||
with {:ok, %{body: body}} <- Tesla.get(client(), from) do
|
||||
with {:ok, %{body: body}} <- Pleroma.HTTP.get(from) do
|
||||
{:ok, body}
|
||||
end
|
||||
end
|
||||
|
|
@ -271,13 +271,5 @@ defmodule Mix.Tasks.Pleroma.Emoji do
|
|||
)
|
||||
end
|
||||
|
||||
defp client do
|
||||
middleware = [
|
||||
{Tesla.Middleware.FollowRedirects, [max_redirects: 3]}
|
||||
]
|
||||
|
||||
Tesla.client(middleware)
|
||||
end
|
||||
|
||||
defp default_manifest, do: Pleroma.Config.get!([:emoji, :default_manifest])
|
||||
end
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ defmodule Mix.Tasks.Pleroma.Frontend do
|
|||
|
||||
fe_label = "#{frontend} (#{ref})"
|
||||
|
||||
tmp_dir = Path.join(dest, "tmp")
|
||||
tmp_dir = Path.join([instance_static_dir, "frontends", "tmp"])
|
||||
|
||||
with {_, :ok} <-
|
||||
{:download_or_unzip, download_or_unzip(frontend_info, tmp_dir, options[:file])},
|
||||
|
|
@ -124,9 +124,7 @@ defmodule Mix.Tasks.Pleroma.Frontend do
|
|||
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
|
||||
|
||||
with {:ok, %{status: 200, body: zip_body}} <-
|
||||
Pleroma.HTTP.get(url, [],
|
||||
adapter: [pool: :media, timeout: 120_000, recv_timeout: 120_000]
|
||||
) do
|
||||
Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do
|
||||
unzip(zip_body, dest)
|
||||
else
|
||||
e -> {:error, e}
|
||||
|
|
@ -135,6 +133,7 @@ defmodule Mix.Tasks.Pleroma.Frontend do
|
|||
|
||||
defp install_frontend(frontend_info, source, dest) do
|
||||
from = frontend_info["build_dir"] || "dist"
|
||||
File.rm_rf!(dest)
|
||||
File.mkdir_p!(dest)
|
||||
File.cp_r!(Path.join([source, from]), dest)
|
||||
:ok
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Activity do
|
|||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Activity.Queries
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Bookmark
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
|
|
@ -60,8 +59,6 @@ defmodule Pleroma.Activity do
|
|||
# typical case.
|
||||
has_one(:object, Object, on_delete: :nothing, foreign_key: :id)
|
||||
|
||||
has_one(:expiration, ActivityExpiration, on_delete: :delete_all)
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
|
@ -304,14 +301,14 @@ defmodule Pleroma.Activity do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
def follow_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do
|
||||
def follow_requests_for_actor(%User{ap_id: ap_id}) do
|
||||
ap_id
|
||||
|> Queries.by_object_id()
|
||||
|> Queries.by_type("Follow")
|
||||
|> where([a], fragment("? ->> 'state' = 'pending'", a.data))
|
||||
end
|
||||
|
||||
def following_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do
|
||||
def following_requests_for_actor(%User{ap_id: ap_id}) do
|
||||
Queries.by_type("Follow")
|
||||
|> where([a], fragment("?->>'state' = 'pending'", a.data))
|
||||
|> where([a], a.actor == ^ap_id)
|
||||
|
|
|
|||
|
|
@ -1,74 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ActivityExpiration do
|
||||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Repo
|
||||
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
@min_activity_lifetime :timer.hours(1)
|
||||
|
||||
schema "activity_expirations" do
|
||||
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
|
||||
field(:scheduled_at, :naive_datetime)
|
||||
end
|
||||
|
||||
def changeset(%ActivityExpiration{} = expiration, attrs, validate_scheduled_at) do
|
||||
expiration
|
||||
|> cast(attrs, [:scheduled_at])
|
||||
|> validate_required([:scheduled_at])
|
||||
|> validate_scheduled_at(validate_scheduled_at)
|
||||
end
|
||||
|
||||
def get_by_activity_id(activity_id) do
|
||||
ActivityExpiration
|
||||
|> where([exp], exp.activity_id == ^activity_id)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
def create(%Activity{} = activity, scheduled_at, validate_scheduled_at \\ true) do
|
||||
%ActivityExpiration{activity_id: activity.id}
|
||||
|> changeset(%{scheduled_at: scheduled_at}, validate_scheduled_at)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
def due_expirations(offset \\ 0) do
|
||||
naive_datetime =
|
||||
NaiveDateTime.utc_now()
|
||||
|> NaiveDateTime.add(offset, :millisecond)
|
||||
|
||||
ActivityExpiration
|
||||
|> where([exp], exp.scheduled_at < ^naive_datetime)
|
||||
|> limit(50)
|
||||
|> preload(:activity)
|
||||
|> Repo.all()
|
||||
|> Enum.reject(fn %{activity: activity} ->
|
||||
Activity.pinned_by_actor?(activity)
|
||||
end)
|
||||
end
|
||||
|
||||
def validate_scheduled_at(changeset, false), do: changeset
|
||||
|
||||
def validate_scheduled_at(changeset, true) do
|
||||
validate_change(changeset, :scheduled_at, fn _, scheduled_at ->
|
||||
if not expires_late_enough?(scheduled_at) do
|
||||
[scheduled_at: "an ephemeral activity must live for at least one hour"]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
def expires_late_enough?(scheduled_at) do
|
||||
now = NaiveDateTime.utc_now()
|
||||
diff = NaiveDateTime.diff(scheduled_at, now, :millisecond)
|
||||
diff > @min_activity_lifetime
|
||||
end
|
||||
end
|
||||
|
|
@ -22,13 +22,18 @@ defmodule Pleroma.Application do
|
|||
def repository, do: @repository
|
||||
|
||||
def user_agent do
|
||||
case Config.get([:http, :user_agent], :default) do
|
||||
:default ->
|
||||
info = "#{Pleroma.Web.base_url()} <#{Config.get([:instance, :email], "")}>"
|
||||
named_version() <> "; " <> info
|
||||
if Process.whereis(Pleroma.Web.Endpoint) do
|
||||
case Config.get([:http, :user_agent], :default) do
|
||||
:default ->
|
||||
info = "#{Pleroma.Web.base_url()} <#{Config.get([:instance, :email], "")}>"
|
||||
named_version() <> "; " <> info
|
||||
|
||||
custom ->
|
||||
custom
|
||||
custom ->
|
||||
custom
|
||||
end
|
||||
else
|
||||
# fallback, if endpoint is not started yet
|
||||
"Pleroma Data Loader"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -39,9 +44,13 @@ defmodule Pleroma.Application do
|
|||
# every time the application is restarted, so we disable module
|
||||
# conflicts at runtime
|
||||
Code.compiler_options(ignore_module_conflict: true)
|
||||
# Disable warnings_as_errors at runtime, it breaks Phoenix live reload
|
||||
# due to protocol consolidation warnings
|
||||
Code.compiler_options(warnings_as_errors: false)
|
||||
Pleroma.Telemetry.Logger.attach()
|
||||
Config.Holder.save_default()
|
||||
Pleroma.HTML.compile_scrubbers()
|
||||
Pleroma.Config.Oban.warn()
|
||||
Config.DeprecationWarnings.warn()
|
||||
Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()
|
||||
Pleroma.ApplicationRequirements.verify!()
|
||||
|
|
|
|||
|
|
@ -76,9 +76,7 @@ defmodule Pleroma.Chat do
|
|||
def for_user_query(user_id) do
|
||||
from(c in Chat,
|
||||
where: c.user_id == ^user_id,
|
||||
order_by: [desc: c.updated_at],
|
||||
inner_join: u in User,
|
||||
on: u.ap_id == c.recipient
|
||||
order_by: [desc: c.updated_at]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
require Logger
|
||||
alias Pleroma.Config
|
||||
|
||||
@type config_namespace() :: [atom()]
|
||||
@type config_namespace() :: atom() | [atom()]
|
||||
@type config_map() :: {config_namespace(), config_namespace(), String.t()}
|
||||
|
||||
@mrf_config_map [
|
||||
|
|
@ -56,6 +56,8 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
check_old_mrf_config()
|
||||
check_media_proxy_whitelist_config()
|
||||
check_welcome_message_config()
|
||||
check_gun_pool_options()
|
||||
check_activity_expiration_config()
|
||||
end
|
||||
|
||||
def check_welcome_message_config do
|
||||
|
|
@ -115,4 +117,62 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
""")
|
||||
end
|
||||
end
|
||||
|
||||
def check_gun_pool_options do
|
||||
pool_config = Config.get(:connections_pool)
|
||||
|
||||
if timeout = pool_config[:await_up_timeout] do
|
||||
Logger.warn("""
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using old setting name `await_up_timeout` instead of `connect_timeout`. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later.
|
||||
""")
|
||||
|
||||
Config.put(:connections_pool, Keyword.put_new(pool_config, :connect_timeout, timeout))
|
||||
end
|
||||
|
||||
pools_configs = Config.get(:pools)
|
||||
|
||||
warning_preface = """
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using old setting name `timeout` instead of `recv_timeout` in pool settings. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later.
|
||||
"""
|
||||
|
||||
updated_config =
|
||||
Enum.reduce(pools_configs, [], fn {pool_name, config}, acc ->
|
||||
if timeout = config[:timeout] do
|
||||
Keyword.put(acc, pool_name, Keyword.put_new(config, :recv_timeout, timeout))
|
||||
else
|
||||
acc
|
||||
end
|
||||
end)
|
||||
|
||||
if updated_config != [] do
|
||||
pool_warnings =
|
||||
updated_config
|
||||
|> Keyword.keys()
|
||||
|> Enum.map(fn pool_name ->
|
||||
"\n* `:timeout` options in #{pool_name} pool is now `:recv_timeout`"
|
||||
end)
|
||||
|
||||
Logger.warn(Enum.join([warning_preface | pool_warnings]))
|
||||
|
||||
Config.put(:pools, updated_config)
|
||||
end
|
||||
end
|
||||
|
||||
@spec check_activity_expiration_config() :: :ok | nil
|
||||
def check_activity_expiration_config do
|
||||
warning_preface = """
|
||||
!!!DEPRECATION WARNING!!!
|
||||
Your config is using old namespace for activity expiration configuration. Setting should work for now, but you are advised to change to new namespace to prevent possible issues later:
|
||||
"""
|
||||
|
||||
move_namespace_and_warn(
|
||||
[
|
||||
{Pleroma.ActivityExpiration, Pleroma.Workers.PurgeExpiredActivity,
|
||||
"\n* `config :pleroma, Pleroma.ActivityExpiration` is now `config :pleroma, Pleroma.Workers.PurgeExpiredActivity`"}
|
||||
],
|
||||
warning_preface
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
34
lib/pleroma/config/oban.ex
Normal file
34
lib/pleroma/config/oban.ex
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
defmodule Pleroma.Config.Oban do
|
||||
require Logger
|
||||
|
||||
def warn do
|
||||
oban_config = Pleroma.Config.get(Oban)
|
||||
|
||||
crontab =
|
||||
[
|
||||
Pleroma.Workers.Cron.StatsWorker,
|
||||
Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker,
|
||||
Pleroma.Workers.Cron.ClearOauthTokenWorker
|
||||
]
|
||||
|> Enum.reduce(oban_config[:crontab], fn removed_worker, acc ->
|
||||
with acc when is_list(acc) <- acc,
|
||||
setting when is_tuple(setting) <-
|
||||
Enum.find(acc, fn {_, worker} -> worker == removed_worker end) do
|
||||
"""
|
||||
!!!OBAN CONFIG WARNING!!!
|
||||
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()
|
||||
|
||||
List.delete(acc, setting)
|
||||
else
|
||||
_ -> acc
|
||||
end
|
||||
end)
|
||||
|
||||
Pleroma.Config.put(Oban, Keyword.put(oban_config, :crontab, crontab))
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.Emoji do
|
||||
use Ecto.Type
|
||||
|
||||
def type, do: :map
|
||||
|
||||
def cast(data) when is_map(data) do
|
||||
has_invalid_emoji? =
|
||||
Enum.find(data, fn
|
||||
{name, uri} when is_binary(name) and is_binary(uri) ->
|
||||
# based on ObjectValidators.Uri.cast()
|
||||
case URI.parse(uri) do
|
||||
%URI{host: nil} -> true
|
||||
%URI{host: ""} -> true
|
||||
%URI{scheme: scheme} when scheme in ["https", "http"] -> false
|
||||
_ -> true
|
||||
end
|
||||
|
||||
{_name, _uri} ->
|
||||
true
|
||||
end)
|
||||
|
||||
if has_invalid_emoji?, do: :error, else: {:ok, data}
|
||||
end
|
||||
|
||||
def cast(_data), do: :error
|
||||
|
||||
def dump(data), do: {:ok, data}
|
||||
|
||||
def load(data), do: {:ok, data}
|
||||
end
|
||||
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Gun.Conn do
|
|||
opts =
|
||||
opts
|
||||
|> Enum.into(%{})
|
||||
|> Map.put_new(:await_up_timeout, pool_opts[:await_up_timeout] || 5_000)
|
||||
|> Map.put_new(:connect_timeout, pool_opts[:connect_timeout] || 5_000)
|
||||
|> Map.put_new(:supervise, false)
|
||||
|> maybe_add_tls_opts(uri)
|
||||
|
||||
|
|
@ -50,10 +50,10 @@ defmodule Pleroma.Gun.Conn do
|
|||
|
||||
with open_opts <- Map.delete(opts, :tls_opts),
|
||||
{:ok, conn} <- Gun.open(proxy_host, proxy_port, open_opts),
|
||||
{:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]),
|
||||
{:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]),
|
||||
stream <- Gun.connect(conn, connect_opts),
|
||||
{:response, :fin, 200, _} <- Gun.await(conn, stream) do
|
||||
{:ok, conn}
|
||||
{:ok, conn, protocol}
|
||||
else
|
||||
error ->
|
||||
Logger.warn(
|
||||
|
|
@ -88,8 +88,8 @@ defmodule Pleroma.Gun.Conn do
|
|||
|> Map.put(:socks_opts, socks_opts)
|
||||
|
||||
with {:ok, conn} <- Gun.open(proxy_host, proxy_port, opts),
|
||||
{:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]) do
|
||||
{:ok, conn}
|
||||
{:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]) do
|
||||
{:ok, conn, protocol}
|
||||
else
|
||||
error ->
|
||||
Logger.warn(
|
||||
|
|
@ -106,8 +106,8 @@ defmodule Pleroma.Gun.Conn do
|
|||
host = Pleroma.HTTP.AdapterHelper.parse_host(host)
|
||||
|
||||
with {:ok, conn} <- Gun.open(host, port, opts),
|
||||
{:ok, _} <- Gun.await_up(conn, opts[:await_up_timeout]) do
|
||||
{:ok, conn}
|
||||
{:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]) do
|
||||
{:ok, conn, protocol}
|
||||
else
|
||||
error ->
|
||||
Logger.warn(
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
|
||||
@impl true
|
||||
def handle_continue({:connect, [key, uri, opts, client_pid]}, _) do
|
||||
with {:ok, conn_pid} <- Gun.Conn.open(uri, opts),
|
||||
with {:ok, conn_pid, protocol} <- Gun.Conn.open(uri, opts),
|
||||
Process.link(conn_pid) do
|
||||
time = :erlang.monotonic_time(:millisecond)
|
||||
|
||||
|
|
@ -27,8 +27,12 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
send(client_pid, {:conn_pid, conn_pid})
|
||||
|
||||
{:noreply,
|
||||
%{key: key, timer: nil, client_monitors: %{client_pid => Process.monitor(client_pid)}},
|
||||
:hibernate}
|
||||
%{
|
||||
key: key,
|
||||
timer: nil,
|
||||
client_monitors: %{client_pid => Process.monitor(client_pid)},
|
||||
protocol: protocol
|
||||
}, :hibernate}
|
||||
else
|
||||
err ->
|
||||
{:stop, {:shutdown, err}, nil}
|
||||
|
|
@ -53,14 +57,20 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:add_client, {client_pid, _}, %{key: key} = state) do
|
||||
def handle_call(:add_client, {client_pid, _}, %{key: key, protocol: protocol} = state) do
|
||||
time = :erlang.monotonic_time(:millisecond)
|
||||
|
||||
{{conn_pid, _, _, _}, _} =
|
||||
{{conn_pid, used_by, _, _}, _} =
|
||||
Registry.update_value(@registry, key, fn {conn_pid, used_by, crf, last_reference} ->
|
||||
{conn_pid, [client_pid | used_by], crf(time - last_reference, crf), time}
|
||||
end)
|
||||
|
||||
:telemetry.execute(
|
||||
[:pleroma, :connection_pool, :client, :add],
|
||||
%{client_pid: client_pid, clients: used_by},
|
||||
%{key: state.key, protocol: protocol}
|
||||
)
|
||||
|
||||
state =
|
||||
if state.timer != nil do
|
||||
Process.cancel_timer(state[:timer])
|
||||
|
|
@ -131,7 +141,7 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
|||
@impl true
|
||||
def handle_info({:DOWN, _ref, :process, pid, reason}, state) do
|
||||
:telemetry.execute(
|
||||
[:pleroma, :connection_pool, :client_death],
|
||||
[:pleroma, :connection_pool, :client, :dead],
|
||||
%{client_pid: pid, reason: reason},
|
||||
%{key: state.key}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.HTTP.AdapterHelper do
|
|||
@moduledoc """
|
||||
Configure Tesla.Client with default and customized adapter options.
|
||||
"""
|
||||
@defaults [pool: :federation]
|
||||
@defaults [pool: :federation, connect_timeout: 5_000, recv_timeout: 5_000]
|
||||
|
||||
@type proxy_type() :: :socks4 | :socks5
|
||||
@type host() :: charlist() | :inet.ip_address()
|
||||
|
|
@ -19,7 +19,6 @@ defmodule Pleroma.HTTP.AdapterHelper do
|
|||
| {Connection.proxy_type(), Connection.host(), pos_integer()}
|
||||
|
||||
@callback options(keyword(), URI.t()) :: keyword()
|
||||
@callback get_conn(URI.t(), keyword()) :: {:ok, term()} | {:error, term()}
|
||||
|
||||
@spec format_proxy(String.t() | tuple() | nil) :: proxy() | nil
|
||||
def format_proxy(nil), do: nil
|
||||
|
|
@ -47,9 +46,6 @@ defmodule Pleroma.HTTP.AdapterHelper do
|
|||
|> adapter_helper().options(uri)
|
||||
end
|
||||
|
||||
@spec get_conn(URI.t(), keyword()) :: {:ok, keyword()} | {:error, atom()}
|
||||
def get_conn(uri, opts), do: adapter_helper().get_conn(uri, opts)
|
||||
|
||||
defp adapter, do: Application.get_env(:tesla, :adapter)
|
||||
|
||||
defp adapter_helper do
|
||||
|
|
|
|||
|
|
@ -6,18 +6,13 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
|
|||
@behaviour Pleroma.HTTP.AdapterHelper
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Gun.ConnectionPool
|
||||
alias Pleroma.HTTP.AdapterHelper
|
||||
|
||||
require Logger
|
||||
|
||||
@defaults [
|
||||
connect_timeout: 5_000,
|
||||
domain_lookup_timeout: 5_000,
|
||||
tls_handshake_timeout: 5_000,
|
||||
retry: 1,
|
||||
retry_timeout: 1000,
|
||||
await_up_timeout: 5_000
|
||||
retry_timeout: 1_000
|
||||
]
|
||||
|
||||
@type pool() :: :federation | :upload | :media | :default
|
||||
|
|
@ -46,23 +41,17 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
|
|||
end
|
||||
|
||||
defp put_timeout(opts) do
|
||||
{recv_timeout, opts} = Keyword.pop(opts, :recv_timeout, pool_timeout(opts[:pool]))
|
||||
# this is the timeout to receive a message from Gun
|
||||
Keyword.put_new(opts, :timeout, pool_timeout(opts[:pool]))
|
||||
# `:timeout` key is used in Tesla
|
||||
Keyword.put(opts, :timeout, recv_timeout)
|
||||
end
|
||||
|
||||
@spec pool_timeout(pool()) :: non_neg_integer()
|
||||
def pool_timeout(pool) do
|
||||
default = Config.get([:pools, :default, :timeout], 5_000)
|
||||
default = Config.get([:pools, :default, :recv_timeout], 5_000)
|
||||
|
||||
Config.get([:pools, pool, :timeout], default)
|
||||
end
|
||||
|
||||
@spec get_conn(URI.t(), keyword()) :: {:ok, keyword()} | {:error, atom()}
|
||||
def get_conn(uri, opts) do
|
||||
case ConnectionPool.get_conn(uri, opts) do
|
||||
{:ok, conn_pid} -> {:ok, Keyword.merge(opts, conn: conn_pid, close_conn: false)}
|
||||
err -> err
|
||||
end
|
||||
Config.get([:pools, pool, :recv_timeout], default)
|
||||
end
|
||||
|
||||
@prefix Pleroma.Gun.ConnectionPool
|
||||
|
|
|
|||
|
|
@ -2,11 +2,8 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
|
|||
@behaviour Pleroma.HTTP.AdapterHelper
|
||||
|
||||
@defaults [
|
||||
connect_timeout: 10_000,
|
||||
recv_timeout: 20_000,
|
||||
follow_redirect: true,
|
||||
force_redirect: true,
|
||||
pool: :federation
|
||||
force_redirect: true
|
||||
]
|
||||
|
||||
@spec options(keyword(), URI.t()) :: keyword()
|
||||
|
|
@ -19,6 +16,7 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
|
|||
|> Keyword.merge(config_opts)
|
||||
|> Keyword.merge(connection_opts)
|
||||
|> add_scheme_opts(uri)
|
||||
|> maybe_add_with_body()
|
||||
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
|
||||
end
|
||||
|
||||
|
|
@ -28,6 +26,11 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
|
|||
|
||||
defp add_scheme_opts(opts, _), do: opts
|
||||
|
||||
@spec get_conn(URI.t(), keyword()) :: {:ok, keyword()}
|
||||
def get_conn(_uri, opts), do: {:ok, opts}
|
||||
defp maybe_add_with_body(opts) do
|
||||
if opts[:max_body] do
|
||||
Keyword.put(opts, :with_body, true)
|
||||
else
|
||||
opts
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.HTTP.ExAws do
|
|||
|
||||
@impl true
|
||||
def request(method, url, body \\ "", headers \\ [], http_opts \\ []) do
|
||||
http_opts = Keyword.put_new(http_opts, :adapter, pool: :upload)
|
||||
http_opts = Keyword.put_new(http_opts, :pool, :upload)
|
||||
|
||||
case HTTP.request(method, url, body, headers, http_opts) do
|
||||
{:ok, env} ->
|
||||
|
|
|
|||
|
|
@ -60,30 +60,23 @@ defmodule Pleroma.HTTP do
|
|||
{:ok, Env.t()} | {:error, any()}
|
||||
def request(method, url, body, headers, options) when is_binary(url) do
|
||||
uri = URI.parse(url)
|
||||
adapter_opts = AdapterHelper.options(uri, options[:adapter] || [])
|
||||
adapter_opts = AdapterHelper.options(uri, options || [])
|
||||
|
||||
case AdapterHelper.get_conn(uri, adapter_opts) do
|
||||
{:ok, adapter_opts} ->
|
||||
options = put_in(options[:adapter], adapter_opts)
|
||||
params = options[:params] || []
|
||||
request = build_request(method, headers, options, url, body, params)
|
||||
options = put_in(options[:adapter], adapter_opts)
|
||||
params = options[:params] || []
|
||||
request = build_request(method, headers, options, url, body, params)
|
||||
|
||||
adapter = Application.get_env(:tesla, :adapter)
|
||||
adapter = Application.get_env(:tesla, :adapter)
|
||||
|
||||
client = Tesla.client(adapter_middlewares(adapter), adapter)
|
||||
client = Tesla.client(adapter_middlewares(adapter), adapter)
|
||||
|
||||
maybe_limit(
|
||||
fn ->
|
||||
request(client, request)
|
||||
end,
|
||||
adapter,
|
||||
adapter_opts
|
||||
)
|
||||
|
||||
# Connection release is handled in a custom FollowRedirects middleware
|
||||
err ->
|
||||
err
|
||||
end
|
||||
maybe_limit(
|
||||
fn ->
|
||||
request(client, request)
|
||||
end,
|
||||
adapter,
|
||||
adapter_opts
|
||||
)
|
||||
end
|
||||
|
||||
@spec request(Client.t(), keyword()) :: {:ok, Env.t()} | {:error, any()}
|
||||
|
|
@ -110,7 +103,7 @@ defmodule Pleroma.HTTP do
|
|||
end
|
||||
|
||||
defp adapter_middlewares(Tesla.Adapter.Gun) do
|
||||
[Pleroma.HTTP.Middleware.FollowRedirects]
|
||||
[Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool]
|
||||
end
|
||||
|
||||
defp adapter_middlewares(_), do: []
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.HTTP.Tzdata do
|
|||
|
||||
@impl true
|
||||
def get(url, headers, options) do
|
||||
options = Keyword.put_new(options, :adapter, pool: :default)
|
||||
options = Keyword.put_new(options, :pool, :default)
|
||||
|
||||
with {:ok, %Tesla.Env{} = env} <- HTTP.get(url, headers, options) do
|
||||
{:ok, {env.status, env.headers, env.body}}
|
||||
|
|
@ -20,7 +20,7 @@ defmodule Pleroma.HTTP.Tzdata do
|
|||
|
||||
@impl true
|
||||
def head(url, headers, options) do
|
||||
options = Keyword.put_new(options, :adapter, pool: :default)
|
||||
options = Keyword.put_new(options, :pool, :default)
|
||||
|
||||
with {:ok, %Tesla.Env{} = env} <- HTTP.head(url, headers, options) do
|
||||
{:ok, {env.status, env.headers}}
|
||||
|
|
|
|||
|
|
@ -159,13 +159,11 @@ defmodule Pleroma.Instances.Instance do
|
|||
Pleroma.HTTP.get(to_string(instance_uri), [{"accept", "text/html"}],
|
||||
adapter: [pool: :media]
|
||||
),
|
||||
favicon_rel <-
|
||||
html
|
||||
|> Floki.parse_document!()
|
||||
|> Floki.attribute("link[rel=icon]", "href")
|
||||
|> List.first(),
|
||||
favicon <- URI.merge(instance_uri, favicon_rel) |> to_string(),
|
||||
true <- is_binary(favicon) do
|
||||
{_, [favicon_rel | _]} when is_binary(favicon_rel) <-
|
||||
{:parse,
|
||||
html |> Floki.parse_document!() |> Floki.attribute("link[rel=icon]", "href")},
|
||||
{_, favicon} when is_binary(favicon) <-
|
||||
{:merge, URI.merge(instance_uri, favicon_rel) |> to_string()} do
|
||||
favicon
|
||||
else
|
||||
_ -> nil
|
||||
|
|
|
|||
|
|
@ -10,10 +10,11 @@ defmodule Pleroma.MFA.Token do
|
|||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.OAuth.Authorization
|
||||
alias Pleroma.Web.OAuth.Token, as: OAuthToken
|
||||
|
||||
@expires 300
|
||||
|
||||
@type t() :: %__MODULE__{}
|
||||
|
||||
schema "mfa_tokens" do
|
||||
field(:token, :string)
|
||||
field(:valid_until, :naive_datetime_usec)
|
||||
|
|
@ -24,6 +25,7 @@ defmodule Pleroma.MFA.Token do
|
|||
timestamps()
|
||||
end
|
||||
|
||||
@spec get_by_token(String.t()) :: {:ok, t()} | {:error, :not_found}
|
||||
def get_by_token(token) do
|
||||
from(
|
||||
t in __MODULE__,
|
||||
|
|
@ -33,33 +35,40 @@ defmodule Pleroma.MFA.Token do
|
|||
|> Repo.find_resource()
|
||||
end
|
||||
|
||||
def validate(token) do
|
||||
with {:fetch_token, {:ok, token}} <- {:fetch_token, get_by_token(token)},
|
||||
{:expired, false} <- {:expired, is_expired?(token)} do
|
||||
@spec validate(String.t()) :: {:ok, t()} | {:error, :not_found} | {:error, :expired_token}
|
||||
def validate(token_str) do
|
||||
with {:ok, token} <- get_by_token(token_str),
|
||||
false <- expired?(token) do
|
||||
{:ok, token}
|
||||
else
|
||||
{:expired, _} -> {:error, :expired_token}
|
||||
{:fetch_token, _} -> {:error, :not_found}
|
||||
error -> {:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
def create_token(%User{} = user) do
|
||||
%__MODULE__{}
|
||||
|> change
|
||||
|> assign_user(user)
|
||||
|> put_token
|
||||
|> put_valid_until
|
||||
|> Repo.insert()
|
||||
defp expired?(%__MODULE__{valid_until: valid_until}) do
|
||||
with true <- NaiveDateTime.diff(NaiveDateTime.utc_now(), valid_until) > 0 do
|
||||
{:error, :expired_token}
|
||||
end
|
||||
end
|
||||
|
||||
def create_token(user, authorization) do
|
||||
@spec create(User.t(), Authorization.t() | nil) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||
def create(user, authorization \\ nil) do
|
||||
with {:ok, token} <- do_create(user, authorization) do
|
||||
Pleroma.Workers.PurgeExpiredToken.enqueue(%{
|
||||
token_id: token.id,
|
||||
valid_until: DateTime.from_naive!(token.valid_until, "Etc/UTC"),
|
||||
mod: __MODULE__
|
||||
})
|
||||
|
||||
{:ok, token}
|
||||
end
|
||||
end
|
||||
|
||||
defp do_create(user, authorization) do
|
||||
%__MODULE__{}
|
||||
|> change
|
||||
|> change()
|
||||
|> assign_user(user)
|
||||
|> assign_authorization(authorization)
|
||||
|> put_token
|
||||
|> put_valid_until
|
||||
|> maybe_assign_authorization(authorization)
|
||||
|> put_token()
|
||||
|> put_valid_until()
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
|
|
@ -69,15 +78,19 @@ defmodule Pleroma.MFA.Token do
|
|||
|> validate_required([:user])
|
||||
end
|
||||
|
||||
defp assign_authorization(changeset, authorization) do
|
||||
defp maybe_assign_authorization(changeset, %Authorization{} = authorization) do
|
||||
changeset
|
||||
|> put_assoc(:authorization, authorization)
|
||||
|> validate_required([:authorization])
|
||||
end
|
||||
|
||||
defp maybe_assign_authorization(changeset, _), do: changeset
|
||||
|
||||
defp put_token(changeset) do
|
||||
token = Pleroma.Web.OAuth.Token.Utils.generate_token()
|
||||
|
||||
changeset
|
||||
|> change(%{token: OAuthToken.Utils.generate_token()})
|
||||
|> change(%{token: token})
|
||||
|> validate_required([:token])
|
||||
|> unique_constraint(:token)
|
||||
end
|
||||
|
|
@ -89,18 +102,4 @@ defmodule Pleroma.MFA.Token do
|
|||
|> change(%{valid_until: expires_in})
|
||||
|> validate_required([:valid_until])
|
||||
end
|
||||
|
||||
def is_expired?(%__MODULE__{valid_until: valid_until}) do
|
||||
NaiveDateTime.diff(NaiveDateTime.utc_now(), valid_until) > 0
|
||||
end
|
||||
|
||||
def is_expired?(_), do: false
|
||||
|
||||
def delete_expired_tokens do
|
||||
from(
|
||||
q in __MODULE__,
|
||||
where: fragment("?", q.valid_until) < ^Timex.now()
|
||||
)
|
||||
|> Repo.delete_all()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -648,4 +648,16 @@ defmodule Pleroma.Notification do
|
|||
)
|
||||
|> Repo.one()
|
||||
end
|
||||
|
||||
@spec mark_context_as_read(User.t(), String.t()) :: {integer(), nil | [term()]}
|
||||
def mark_context_as_read(%User{id: id}, context) do
|
||||
from(
|
||||
n in Notification,
|
||||
join: a in assoc(n, :activity),
|
||||
where: n.user_id == ^id,
|
||||
where: n.seen == false,
|
||||
where: fragment("?->>'context'", a.data) == ^context
|
||||
)
|
||||
|> Repo.update_all(set: [seen: true])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -44,13 +44,6 @@ defmodule Pleroma.Object.Containment do
|
|||
nil
|
||||
end
|
||||
|
||||
# TODO: We explicitly allow 'tag' URIs through, due to references to legacy OStatus
|
||||
# objects being present in the test suite environment. Once these objects are
|
||||
# removed, please also remove this.
|
||||
if Mix.env() == :test do
|
||||
defp compare_uris(_, %URI{scheme: "tag"}), do: :ok
|
||||
end
|
||||
|
||||
defp compare_uris(%URI{host: host} = _id_uri, %URI{host: host} = _other_uri), do: :ok
|
||||
defp compare_uris(_id_uri, _other_uri), do: :error
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@ defmodule Pleroma.Object.Fetcher do
|
|||
defp reinject_object(%Object{data: %{"type" => "Question"}} = object, new_data) do
|
||||
Logger.debug("Reinjecting object #{new_data["id"]}")
|
||||
|
||||
with new_data <- Transmogrifier.fix_object(new_data),
|
||||
data <- maybe_reinject_internal_fields(object, new_data),
|
||||
with data <- maybe_reinject_internal_fields(object, new_data),
|
||||
{:ok, data, _} <- ObjectValidator.validate(data, %{}),
|
||||
changeset <- Object.change(object, %{data: data}),
|
||||
changeset <- touch_changeset(changeset),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ defmodule Pleroma.ReverseProxy.Client.Tesla do
|
|||
url,
|
||||
body,
|
||||
headers,
|
||||
Keyword.put(opts, :adapter, opts)
|
||||
opts
|
||||
) do
|
||||
if is_map(response.body) and method != :head do
|
||||
{:ok, response.status, response.headers, response.body}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Stats do
|
||||
use GenServer
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.CounterCache
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
use GenServer
|
||||
@interval :timer.seconds(60)
|
||||
|
||||
def start_link(_) do
|
||||
GenServer.start_link(
|
||||
|
|
@ -18,6 +21,12 @@ defmodule Pleroma.Stats do
|
|||
)
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(_args) do
|
||||
if Pleroma.Config.get(:env) == :test, do: :ok = Ecto.Adapters.SQL.Sandbox.checkout(Repo)
|
||||
{:ok, nil, {:continue, :calculate_stats}}
|
||||
end
|
||||
|
||||
@doc "Performs update stats"
|
||||
def force_update do
|
||||
GenServer.call(__MODULE__, :force_update)
|
||||
|
|
@ -29,7 +38,11 @@ defmodule Pleroma.Stats do
|
|||
end
|
||||
|
||||
@doc "Returns stats data"
|
||||
@spec get_stats() :: %{domain_count: integer(), status_count: integer(), user_count: integer()}
|
||||
@spec get_stats() :: %{
|
||||
domain_count: non_neg_integer(),
|
||||
status_count: non_neg_integer(),
|
||||
user_count: non_neg_integer()
|
||||
}
|
||||
def get_stats do
|
||||
%{stats: stats} = GenServer.call(__MODULE__, :get_state)
|
||||
|
||||
|
|
@ -44,25 +57,14 @@ defmodule Pleroma.Stats do
|
|||
peers
|
||||
end
|
||||
|
||||
def init(_args) do
|
||||
{:ok, calculate_stat_data()}
|
||||
end
|
||||
|
||||
def handle_call(:force_update, _from, _state) do
|
||||
new_stats = calculate_stat_data()
|
||||
{:reply, new_stats, new_stats}
|
||||
end
|
||||
|
||||
def handle_call(:get_state, _from, state) do
|
||||
{:reply, state, state}
|
||||
end
|
||||
|
||||
def handle_cast(:run_update, _state) do
|
||||
new_stats = calculate_stat_data()
|
||||
|
||||
{:noreply, new_stats}
|
||||
end
|
||||
|
||||
@spec calculate_stat_data() :: %{
|
||||
peers: list(),
|
||||
stats: %{
|
||||
domain_count: non_neg_integer(),
|
||||
status_count: non_neg_integer(),
|
||||
user_count: non_neg_integer()
|
||||
}
|
||||
}
|
||||
def calculate_stat_data do
|
||||
peers =
|
||||
from(
|
||||
|
|
@ -97,6 +99,7 @@ defmodule Pleroma.Stats do
|
|||
}
|
||||
end
|
||||
|
||||
@spec get_status_visibility_count(String.t() | nil) :: map()
|
||||
def get_status_visibility_count(instance \\ nil) do
|
||||
if is_nil(instance) do
|
||||
CounterCache.get_sum()
|
||||
|
|
@ -104,4 +107,36 @@ defmodule Pleroma.Stats do
|
|||
CounterCache.get_by_instance(instance)
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_continue(:calculate_stats, _) do
|
||||
stats = calculate_stat_data()
|
||||
Process.send_after(self(), :run_update, @interval)
|
||||
{:noreply, stats}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:force_update, _from, _state) do
|
||||
new_stats = calculate_stat_data()
|
||||
{:reply, new_stats, new_stats}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call(:get_state, _from, state) do
|
||||
{:reply, state, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_cast(:run_update, _state) do
|
||||
new_stats = calculate_stat_data()
|
||||
|
||||
{:noreply, new_stats}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_info(:run_update, _) do
|
||||
new_stats = calculate_stat_data()
|
||||
Process.send_after(self(), :run_update, @interval)
|
||||
{:noreply, new_stats}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
[:pleroma, :connection_pool, :reclaim, :start],
|
||||
[:pleroma, :connection_pool, :reclaim, :stop],
|
||||
[:pleroma, :connection_pool, :provision_failure],
|
||||
[:pleroma, :connection_pool, :client_death]
|
||||
[:pleroma, :connection_pool, :client, :dead],
|
||||
[:pleroma, :connection_pool, :client, :add]
|
||||
]
|
||||
def attach do
|
||||
:telemetry.attach_many("pleroma-logger", @events, &handle_event/4, [])
|
||||
|
|
@ -62,7 +63,7 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :connection_pool, :client_death],
|
||||
[:pleroma, :connection_pool, :client, :dead],
|
||||
%{client_pid: client_pid, reason: reason},
|
||||
%{key: key},
|
||||
_
|
||||
|
|
@ -73,4 +74,17 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
}"
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event(
|
||||
[:pleroma, :connection_pool, :client, :add],
|
||||
%{clients: [_, _ | _] = clients},
|
||||
%{key: key, protocol: :http},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
"Pool worker for #{key}: #{length(clients)} clients are using an HTTP1 connection at the same time, head-of-line blocking might occur."
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_event([:pleroma, :connection_pool, :client, :add], _, _, _), do: :ok
|
||||
end
|
||||
|
|
|
|||
50
lib/pleroma/tesla/middleware/connection_pool.ex
Normal file
50
lib/pleroma/tesla/middleware/connection_pool.ex
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Tesla.Middleware.ConnectionPool do
|
||||
@moduledoc """
|
||||
Middleware to get/release connections from `Pleroma.Gun.ConnectionPool`
|
||||
"""
|
||||
|
||||
@behaviour Tesla.Middleware
|
||||
|
||||
alias Pleroma.Gun.ConnectionPool
|
||||
|
||||
@impl Tesla.Middleware
|
||||
def call(%Tesla.Env{url: url, opts: opts} = env, next, _) do
|
||||
uri = URI.parse(url)
|
||||
|
||||
# Avoid leaking connections when the middleware is called twice
|
||||
# with body_as: :chunks. We assume only the middleware can set
|
||||
# opts[:adapter][:conn]
|
||||
if opts[:adapter][:conn] do
|
||||
ConnectionPool.release_conn(opts[:adapter][:conn])
|
||||
end
|
||||
|
||||
case ConnectionPool.get_conn(uri, opts[:adapter]) do
|
||||
{:ok, conn_pid} ->
|
||||
adapter_opts = Keyword.merge(opts[:adapter], conn: conn_pid, close_conn: false)
|
||||
opts = Keyword.put(opts, :adapter, adapter_opts)
|
||||
env = %{env | opts: opts}
|
||||
|
||||
case Tesla.run(env, next) do
|
||||
{:ok, env} ->
|
||||
unless opts[:adapter][:body_as] == :chunks do
|
||||
ConnectionPool.release_conn(conn_pid)
|
||||
{_, res} = pop_in(env.opts[:adapter][:conn])
|
||||
{:ok, res}
|
||||
else
|
||||
{:ok, env}
|
||||
end
|
||||
|
||||
err ->
|
||||
ConnectionPool.release_conn(conn_pid)
|
||||
err
|
||||
end
|
||||
|
||||
err ->
|
||||
err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2015-2020 Tymon Tobolski <https://github.com/teamon/tesla/blob/master/lib/tesla/middleware/follow_redirects.ex>
|
||||
# Copyright © 2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTTP.Middleware.FollowRedirects do
|
||||
@moduledoc """
|
||||
Pool-aware version of https://github.com/teamon/tesla/blob/master/lib/tesla/middleware/follow_redirects.ex
|
||||
|
||||
Follow 3xx redirects
|
||||
## Options
|
||||
- `:max_redirects` - limit number of redirects (default: `5`)
|
||||
"""
|
||||
|
||||
alias Pleroma.Gun.ConnectionPool
|
||||
|
||||
@behaviour Tesla.Middleware
|
||||
|
||||
@max_redirects 5
|
||||
@redirect_statuses [301, 302, 303, 307, 308]
|
||||
|
||||
@impl Tesla.Middleware
|
||||
def call(env, next, opts \\ []) do
|
||||
max = Keyword.get(opts, :max_redirects, @max_redirects)
|
||||
|
||||
redirect(env, next, max)
|
||||
end
|
||||
|
||||
defp redirect(env, next, left) do
|
||||
opts = env.opts[:adapter]
|
||||
|
||||
case Tesla.run(env, next) do
|
||||
{:ok, %{status: status} = res} when status in @redirect_statuses and left > 0 ->
|
||||
release_conn(opts)
|
||||
|
||||
case Tesla.get_header(res, "location") do
|
||||
nil ->
|
||||
{:ok, res}
|
||||
|
||||
location ->
|
||||
location = parse_location(location, res)
|
||||
|
||||
case get_conn(location, opts) do
|
||||
{:ok, opts} ->
|
||||
%{env | opts: Keyword.put(env.opts, :adapter, opts)}
|
||||
|> new_request(res.status, location)
|
||||
|> redirect(next, left - 1)
|
||||
|
||||
e ->
|
||||
e
|
||||
end
|
||||
end
|
||||
|
||||
{:ok, %{status: status}} when status in @redirect_statuses ->
|
||||
release_conn(opts)
|
||||
{:error, {__MODULE__, :too_many_redirects}}
|
||||
|
||||
{:error, _} = e ->
|
||||
release_conn(opts)
|
||||
e
|
||||
|
||||
other ->
|
||||
unless opts[:body_as] == :chunks do
|
||||
release_conn(opts)
|
||||
end
|
||||
|
||||
other
|
||||
end
|
||||
end
|
||||
|
||||
defp get_conn(location, opts) do
|
||||
uri = URI.parse(location)
|
||||
|
||||
case ConnectionPool.get_conn(uri, opts) do
|
||||
{:ok, conn} ->
|
||||
{:ok, Keyword.merge(opts, conn: conn)}
|
||||
|
||||
e ->
|
||||
e
|
||||
end
|
||||
end
|
||||
|
||||
defp release_conn(opts) do
|
||||
ConnectionPool.release_conn(opts[:conn])
|
||||
end
|
||||
|
||||
# The 303 (See Other) redirect was added in HTTP/1.1 to indicate that the originally
|
||||
# requested resource is not available, however a related resource (or another redirect)
|
||||
# available via GET is available at the specified location.
|
||||
# https://tools.ietf.org/html/rfc7231#section-6.4.4
|
||||
defp new_request(env, 303, location), do: %{env | url: location, method: :get, query: []}
|
||||
|
||||
# The 307 (Temporary Redirect) status code indicates that the target
|
||||
# resource resides temporarily under a different URI and the user agent
|
||||
# MUST NOT change the request method (...)
|
||||
# https://tools.ietf.org/html/rfc7231#section-6.4.7
|
||||
defp new_request(env, 307, location), do: %{env | url: location}
|
||||
|
||||
defp new_request(env, _, location), do: %{env | url: location, query: []}
|
||||
|
||||
defp parse_location("https://" <> _rest = location, _env), do: location
|
||||
defp parse_location("http://" <> _rest = location, _env), do: location
|
||||
|
||||
defp parse_location(location, env) do
|
||||
env.url
|
||||
|> URI.parse()
|
||||
|> URI.merge(location)
|
||||
|> URI.to_string()
|
||||
end
|
||||
end
|
||||
|
|
@ -83,7 +83,7 @@ defmodule Pleroma.User do
|
|||
]
|
||||
|
||||
schema "users" do
|
||||
field(:bio, :string)
|
||||
field(:bio, :string, default: "")
|
||||
field(:raw_bio, :string)
|
||||
field(:email, :string)
|
||||
field(:name, :string)
|
||||
|
|
@ -1587,7 +1587,7 @@ defmodule Pleroma.User do
|
|||
# "Right to be forgotten"
|
||||
# https://gdpr.eu/right-to-be-forgotten/
|
||||
change(user, %{
|
||||
bio: nil,
|
||||
bio: "",
|
||||
raw_bio: nil,
|
||||
email: nil,
|
||||
name: nil,
|
||||
|
|
@ -2315,6 +2315,11 @@ defmodule Pleroma.User do
|
|||
max_pinned_statuses = Config.get([:instance, :max_pinned_statuses], 0)
|
||||
params = %{pinned_activities: user.pinned_activities ++ [id]}
|
||||
|
||||
# if pinned activity was scheduled for deletion, we remove job
|
||||
if expiration = Pleroma.Workers.PurgeExpiredActivity.get_expiration(id) do
|
||||
Oban.cancel_job(expiration.id)
|
||||
end
|
||||
|
||||
user
|
||||
|> cast(params, [:pinned_activities])
|
||||
|> validate_length(:pinned_activities,
|
||||
|
|
@ -2327,9 +2332,19 @@ defmodule Pleroma.User do
|
|||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
def remove_pinnned_activity(user, %Pleroma.Activity{id: id}) do
|
||||
def remove_pinnned_activity(user, %Pleroma.Activity{id: id, data: data}) do
|
||||
params = %{pinned_activities: List.delete(user.pinned_activities, id)}
|
||||
|
||||
# if pinned activity was scheduled for deletion, we reschedule it for deletion
|
||||
if data["expires_at"] do
|
||||
{:ok, expires_at, _} = DateTime.from_iso8601(data["expires_at"])
|
||||
|
||||
Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
|
||||
activity_id: id,
|
||||
expires_at: expires_at
|
||||
})
|
||||
end
|
||||
|
||||
user
|
||||
|> cast(params, [:pinned_activities])
|
||||
|> update_and_set_cache()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Activity.Ir.Topics
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Constants
|
||||
alias Pleroma.Conversation
|
||||
|
|
@ -102,7 +101,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
local: local,
|
||||
recipients: recipients,
|
||||
actor: object["actor"]
|
||||
}) do
|
||||
}),
|
||||
# TODO: add tests for expired activities, when Note type will be supported in new pipeline
|
||||
{:ok, _} <- maybe_create_activity_expiration(activity) do
|
||||
{:ok, activity, meta}
|
||||
end
|
||||
end
|
||||
|
|
@ -111,23 +112,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
|
||||
with nil <- Activity.normalize(map),
|
||||
map <- lazy_put_activity_defaults(map, fake),
|
||||
true <- bypass_actor_check || check_actor_is_active(map["actor"]),
|
||||
{_, true} <- {:remote_limit_error, check_remote_limit(map)},
|
||||
{_, true} <- {:actor_check, bypass_actor_check || check_actor_is_active(map["actor"])},
|
||||
{_, true} <- {:remote_limit_pass, check_remote_limit(map)},
|
||||
{:ok, map} <- MRF.filter(map),
|
||||
{recipients, _, _} = get_recipients(map),
|
||||
{:fake, false, map, recipients} <- {:fake, fake, map, recipients},
|
||||
{:containment, :ok} <- {:containment, Containment.contain_child(map)},
|
||||
{:ok, map, object} <- insert_full_object(map) do
|
||||
{:ok, activity} =
|
||||
%Activity{
|
||||
data: map,
|
||||
local: local,
|
||||
actor: map["actor"],
|
||||
recipients: recipients
|
||||
}
|
||||
|> Repo.insert()
|
||||
|> maybe_create_activity_expiration()
|
||||
|
||||
{:ok, map, object} <- insert_full_object(map),
|
||||
{:ok, activity} <- insert_activity_with_expiration(map, local, recipients) do
|
||||
# Splice in the child object if we have one.
|
||||
activity = Maps.put_if_present(activity, :object, object)
|
||||
|
||||
|
|
@ -138,6 +130,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
%Activity{} = activity ->
|
||||
{:ok, activity}
|
||||
|
||||
{:actor_check, _} ->
|
||||
{:error, false}
|
||||
|
||||
{:containment, _} = error ->
|
||||
error
|
||||
|
||||
{:error, _} = error ->
|
||||
error
|
||||
|
||||
{:fake, true, map, recipients} ->
|
||||
activity = %Activity{
|
||||
data: map,
|
||||
|
|
@ -150,8 +151,24 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity)
|
||||
{:ok, activity}
|
||||
|
||||
error ->
|
||||
{:error, error}
|
||||
{:remote_limit_pass, _} ->
|
||||
{:error, :remote_limit}
|
||||
|
||||
{:reject, reason} ->
|
||||
{:error, reason}
|
||||
end
|
||||
end
|
||||
|
||||
defp insert_activity_with_expiration(data, local, recipients) do
|
||||
struct = %Activity{
|
||||
data: data,
|
||||
local: local,
|
||||
actor: data["actor"],
|
||||
recipients: recipients
|
||||
}
|
||||
|
||||
with {:ok, activity} <- Repo.insert(struct) do
|
||||
maybe_create_activity_expiration(activity)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -164,13 +181,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
stream_out_participations(participations)
|
||||
end
|
||||
|
||||
defp maybe_create_activity_expiration({:ok, %{data: %{"expires_at" => expires_at}} = activity}) do
|
||||
with {:ok, _} <- ActivityExpiration.create(activity, expires_at) do
|
||||
defp maybe_create_activity_expiration(
|
||||
%{data: %{"expires_at" => %DateTime{} = expires_at}} = activity
|
||||
) do
|
||||
with {:ok, _job} <-
|
||||
Pleroma.Workers.PurgeExpiredActivity.enqueue(%{
|
||||
activity_id: activity.id,
|
||||
expires_at: expires_at
|
||||
}) do
|
||||
{:ok, activity}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_create_activity_expiration(result), do: result
|
||||
defp maybe_create_activity_expiration(activity), do: {:ok, activity}
|
||||
|
||||
defp create_or_bump_conversation(activity, actor) do
|
||||
with {:ok, conversation} <- Conversation.create_or_bump_for(activity),
|
||||
|
|
@ -1224,7 +1247,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
name: data["name"],
|
||||
follower_address: data["followers"],
|
||||
following_address: data["following"],
|
||||
bio: data["summary"],
|
||||
bio: data["summary"] || "",
|
||||
actor_type: actor_type,
|
||||
also_known_as: Map.get(data, "alsoKnownAs", []),
|
||||
public_key: public_key,
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy do
|
|||
|
||||
defp maybe_add_expiration(activity) do
|
||||
days = Pleroma.Config.get([:mrf_activity_expiration, :days], 365)
|
||||
expires_at = NaiveDateTime.utc_now() |> Timex.shift(days: days)
|
||||
expires_at = DateTime.utc_now() |> Timex.shift(days: days)
|
||||
|
||||
with %{"expires_at" => existing_expires_at} <- activity,
|
||||
:lt <- NaiveDateTime.compare(existing_expires_at, expires_at) do
|
||||
:lt <- DateTime.compare(existing_expires_at, expires_at) do
|
||||
activity
|
||||
else
|
||||
_ -> Map.put(activity, "expires_at", expires_at)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy do
|
||||
alias Pleroma.User
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
@moduledoc "Remove bot posts from federated timeline"
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
defp check_by_actor_type(user), do: user.actor_type in ["Application", "Service"]
|
||||
defp check_by_nickname(user), do: Regex.match?(~r/bot@|ebooks@/i, user.nickname)
|
||||
|
||||
defp check_if_bot(user), do: check_by_actor_type(user) or check_by_nickname(user)
|
||||
|
||||
@impl true
|
||||
def filter(
|
||||
%{
|
||||
"type" => "Create",
|
||||
"to" => to,
|
||||
"cc" => cc,
|
||||
"actor" => actor,
|
||||
"object" => object
|
||||
} = message
|
||||
) do
|
||||
user = User.get_cached_by_ap_id(actor)
|
||||
isbot = check_if_bot(user)
|
||||
|
||||
if isbot and Enum.member?(to, Pleroma.Constants.as_public()) do
|
||||
to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address]
|
||||
cc = List.delete(cc, user.follower_address) ++ [Pleroma.Constants.as_public()]
|
||||
|
||||
object =
|
||||
object
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", cc)
|
||||
|
||||
message =
|
||||
message
|
||||
|> Map.put("to", to)
|
||||
|> Map.put("cc", cc)
|
||||
|> Map.put("object", object)
|
||||
|
||||
{:ok, message}
|
||||
else
|
||||
{:ok, message}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def filter(message), do: {:ok, message}
|
||||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
end
|
||||
|
|
@ -13,22 +13,16 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
|
|||
require Logger
|
||||
|
||||
@options [
|
||||
pool: :media
|
||||
pool: :media,
|
||||
recv_timeout: 10_000
|
||||
]
|
||||
|
||||
def perform(:prefetch, url) do
|
||||
Logger.debug("Prefetching #{inspect(url)}")
|
||||
|
||||
opts =
|
||||
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
|
||||
Keyword.put(@options, :recv_timeout, 10_000)
|
||||
else
|
||||
@options
|
||||
end
|
||||
|
||||
url
|
||||
|> MediaProxy.url()
|
||||
|> HTTP.get([], adapter: opts)
|
||||
|> HTTP.get([], @options)
|
||||
end
|
||||
|
||||
def perform(:preload, %{"object" => %{"attachment" => attachments}} = _message) do
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do
|
|||
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
|
|
@ -33,8 +34,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do
|
|||
field(:attributedTo, ObjectValidators.ObjectID)
|
||||
field(:summary, :string)
|
||||
field(:published, ObjectValidators.DateTime)
|
||||
# TODO: Write type
|
||||
field(:emoji, :map, default: %{})
|
||||
field(:emoji, ObjectValidators.Emoji, default: %{})
|
||||
field(:sensitive, :boolean, default: false)
|
||||
embeds_many(:attachment, AttachmentValidator)
|
||||
field(:replies_count, :integer, default: 0)
|
||||
|
|
@ -83,6 +83,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do
|
|||
data
|
||||
|> CommonFixes.fix_defaults()
|
||||
|> CommonFixes.fix_attribution()
|
||||
|> Transmogrifier.fix_emoji()
|
||||
|> fix_url()
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do
|
|||
field(:content, ObjectValidators.SafeText)
|
||||
field(:actor, ObjectValidators.ObjectID)
|
||||
field(:published, ObjectValidators.DateTime)
|
||||
field(:emoji, :map, default: %{})
|
||||
field(:emoji, ObjectValidators.Emoji, default: %{})
|
||||
|
||||
embeds_one(:attachment, AttachmentValidator)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
|||
Utils.create_context(data["context"] || data["conversation"])
|
||||
|
||||
data
|
||||
|> Map.put_new("context", context)
|
||||
|> Map.put_new("context_id", context_id)
|
||||
|> Map.put("context", context)
|
||||
|> Map.put("context_id", context_id)
|
||||
end
|
||||
|
||||
def fix_attribution(data) do
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
|
|||
alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
|
|
@ -39,8 +40,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
|
|||
|
||||
field(:attributedTo, ObjectValidators.ObjectID)
|
||||
field(:published, ObjectValidators.DateTime)
|
||||
# TODO: Write type
|
||||
field(:emoji, :map, default: %{})
|
||||
field(:emoji, ObjectValidators.Emoji, default: %{})
|
||||
field(:sensitive, :boolean, default: false)
|
||||
embeds_many(:attachment, AttachmentValidator)
|
||||
field(:replies_count, :integer, default: 0)
|
||||
|
|
@ -74,6 +74,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EventValidator do
|
|||
data
|
||||
|> CommonFixes.fix_defaults()
|
||||
|> CommonFixes.fix_attribution()
|
||||
|> Transmogrifier.fix_emoji()
|
||||
end
|
||||
|
||||
def changeset(struct, data) do
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
|
|||
use Ecto.Schema
|
||||
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
|
|
@ -32,8 +33,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
|
|||
field(:actor, ObjectValidators.ObjectID)
|
||||
field(:attributedTo, ObjectValidators.ObjectID)
|
||||
field(:published, ObjectValidators.DateTime)
|
||||
# TODO: Write type
|
||||
field(:emoji, :map, default: %{})
|
||||
field(:emoji, ObjectValidators.Emoji, default: %{})
|
||||
field(:sensitive, :boolean, default: false)
|
||||
# TODO: Write type
|
||||
field(:attachment, {:array, :map}, default: [])
|
||||
|
|
@ -53,7 +53,14 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
|
|||
|> validate_data()
|
||||
end
|
||||
|
||||
defp fix(data) do
|
||||
data
|
||||
|> Transmogrifier.fix_emoji()
|
||||
end
|
||||
|
||||
def cast_data(data) do
|
||||
data = fix(data)
|
||||
|
||||
%__MODULE__{}
|
||||
|> cast(data, __schema__(:fields))
|
||||
end
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
|
|||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||
alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
|
|
@ -35,8 +36,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
|
|||
field(:attributedTo, ObjectValidators.ObjectID)
|
||||
field(:summary, :string)
|
||||
field(:published, ObjectValidators.DateTime)
|
||||
# TODO: Write type
|
||||
field(:emoji, :map, default: %{})
|
||||
field(:emoji, ObjectValidators.Emoji, default: %{})
|
||||
field(:sensitive, :boolean, default: false)
|
||||
embeds_many(:attachment, AttachmentValidator)
|
||||
field(:replies_count, :integer, default: 0)
|
||||
|
|
@ -85,6 +85,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
|
|||
data
|
||||
|> CommonFixes.fix_defaults()
|
||||
|> CommonFixes.fix_attribution()
|
||||
|> Transmogrifier.fix_emoji()
|
||||
|> fix_closed()
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
"""
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Activity.Ir.Topics
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Chat
|
||||
alias Pleroma.Chat.MessageReference
|
||||
alias Pleroma.FollowingRelationship
|
||||
|
|
@ -188,10 +187,6 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
|||
Object.increase_replies_count(in_reply_to)
|
||||
end
|
||||
|
||||
if expires_at = activity.data["expires_at"] do
|
||||
ActivityExpiration.create(activity, expires_at)
|
||||
end
|
||||
|
||||
BackgroundWorker.enqueue("fetch_data_for_activity", %{"activity_id" => activity.id})
|
||||
|
||||
meta =
|
||||
|
|
|
|||
|
|
@ -168,7 +168,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options)
|
||||
when not is_nil(in_reply_to) do
|
||||
in_reply_to_id = prepare_in_reply_to(in_reply_to)
|
||||
object = Map.put(object, "inReplyToAtomUri", in_reply_to_id)
|
||||
depth = (options[:depth] || 0) + 1
|
||||
|
||||
if Federator.allowed_thread_distance?(depth) do
|
||||
|
|
@ -176,9 +175,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
%Activity{} <- Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
|
||||
object
|
||||
|> Map.put("inReplyTo", replied_object.data["id"])
|
||||
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
|
||||
|> Map.put("context", replied_object.data["context"] || object["conversation"])
|
||||
|> Map.drop(["conversation"])
|
||||
|> Map.drop(["conversation", "inReplyToAtomUri"])
|
||||
else
|
||||
e ->
|
||||
Logger.warn("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
|
||||
|
|
@ -318,9 +316,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
|||
Map.put(mapping, name, data["icon"]["url"])
|
||||
end)
|
||||
|
||||
# we merge mastodon and pleroma emoji into a single mapping, to allow for both wire formats
|
||||
emoji = Map.merge(object["emoji"] || %{}, emoji)
|
||||
|
||||
Map.put(object, "emoji", emoji)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
description: "Add accounts to the given list.",
|
||||
operationId: "ListController.add_to_list",
|
||||
parameters: [id_param()],
|
||||
requestBody: add_remove_accounts_request(),
|
||||
requestBody: add_remove_accounts_request(true),
|
||||
security: [%{"oAuth" => ["write:lists"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
|
||||
|
|
@ -127,8 +127,16 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
tags: ["Lists"],
|
||||
summary: "Remove accounts from list",
|
||||
operationId: "ListController.remove_from_list",
|
||||
parameters: [id_param()],
|
||||
requestBody: add_remove_accounts_request(),
|
||||
parameters: [
|
||||
id_param(),
|
||||
Operation.parameter(
|
||||
:account_ids,
|
||||
:query,
|
||||
%Schema{type: :array, items: %Schema{type: :string}},
|
||||
"Array of account IDs"
|
||||
)
|
||||
],
|
||||
requestBody: add_remove_accounts_request(false),
|
||||
security: [%{"oAuth" => ["write:lists"]}],
|
||||
responses: %{
|
||||
200 => Operation.response("Empty object", "application/json", %Schema{type: :object})
|
||||
|
|
@ -171,7 +179,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
)
|
||||
end
|
||||
|
||||
defp add_remove_accounts_request do
|
||||
defp add_remove_accounts_request(required) when is_boolean(required) do
|
||||
request_body(
|
||||
"Parameters",
|
||||
%Schema{
|
||||
|
|
@ -180,9 +188,9 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
|
|||
properties: %{
|
||||
account_ids: %Schema{type: :array, description: "Array of account IDs", items: FlakeID}
|
||||
},
|
||||
required: [:account_ids]
|
||||
required: required && [:account_ids]
|
||||
},
|
||||
required: true
|
||||
required: required
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
nickname = value([registration_attrs["nickname"], Registration.nickname(registration)])
|
||||
email = value([registration_attrs["email"], Registration.email(registration)])
|
||||
name = value([registration_attrs["name"], Registration.name(registration)]) || nickname
|
||||
bio = value([registration_attrs["bio"], Registration.description(registration)])
|
||||
bio = value([registration_attrs["bio"], Registration.description(registration)]) || ""
|
||||
|
||||
random_password = :crypto.strong_rand_bytes(64) |> Base.encode64()
|
||||
|
||||
|
|
|
|||
|
|
@ -202,7 +202,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|
|||
|
||||
additional =
|
||||
case draft.expires_at do
|
||||
%NaiveDateTime{} = expires_at -> Map.put(additional, "expires_at", expires_at)
|
||||
%DateTime{} = expires_at -> Map.put(additional, "expires_at", expires_at)
|
||||
_ -> additional
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
defmodule Pleroma.Web.CommonAPI do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.Object
|
||||
|
|
@ -381,9 +380,9 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
def check_expiry_date({:ok, nil} = res), do: res
|
||||
|
||||
def check_expiry_date({:ok, in_seconds}) do
|
||||
expiry = NaiveDateTime.utc_now() |> NaiveDateTime.add(in_seconds)
|
||||
expiry = DateTime.add(DateTime.utc_now(), in_seconds)
|
||||
|
||||
if ActivityExpiration.expires_late_enough?(expiry) do
|
||||
if Pleroma.Workers.PurgeExpiredActivity.expires_late_enough?(expiry) do
|
||||
{:ok, expiry}
|
||||
else
|
||||
{:error, "Expiry date is too soon"}
|
||||
|
|
@ -452,7 +451,8 @@ defmodule Pleroma.Web.CommonAPI do
|
|||
end
|
||||
|
||||
def add_mute(user, activity) do
|
||||
with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]) do
|
||||
with {:ok, _} <- ThreadMute.add_mute(user.id, activity.data["context"]),
|
||||
_ <- Pleroma.Notification.mark_context_as_read(user, activity.data["context"]) do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:error, _} -> {:error, dgettext("errors", "conversation is already muted")}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.ListController do
|
|||
|
||||
# DELETE /api/v1/lists/:id/accounts
|
||||
def remove_from_list(
|
||||
%{assigns: %{list: list}, body_params: %{account_ids: account_ids}} = conn,
|
||||
%{assigns: %{list: list}, params: %{account_ids: account_ids}} = conn,
|
||||
_
|
||||
) do
|
||||
Enum.each(account_ids, fn account_id ->
|
||||
|
|
@ -86,6 +86,10 @@ defmodule Pleroma.Web.MastodonAPI.ListController do
|
|||
json(conn, %{})
|
||||
end
|
||||
|
||||
def remove_from_list(%{body_params: params} = conn, _) do
|
||||
remove_from_list(%{conn | params: params}, %{})
|
||||
end
|
||||
|
||||
defp list_by_id_and_user(%{assigns: %{user: user}, params: %{id: id}} = conn, _) do
|
||||
case Pleroma.List.get(id, user) do
|
||||
%Pleroma.List{} = list -> assign(conn, :list, list)
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
|||
followers_count: followers_count,
|
||||
following_count: following_count,
|
||||
statuses_count: user.note_count,
|
||||
note: user.bio || "",
|
||||
note: user.bio,
|
||||
url: user.uri || user.ap_id,
|
||||
avatar: image,
|
||||
avatar_static: image,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
require Pleroma.Constants
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
|
|
@ -228,8 +227,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
|
|||
|
||||
expires_at =
|
||||
with true <- client_posted_this_activity,
|
||||
%ActivityExpiration{scheduled_at: scheduled_at} <-
|
||||
ActivityExpiration.get_by_activity_id(activity.id) do
|
||||
%Oban.Job{scheduled_at: scheduled_at} <-
|
||||
Pleroma.Workers.PurgeExpiredActivity.get_expiration(activity.id) do
|
||||
scheduled_at
|
||||
else
|
||||
_ -> nil
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
else
|
||||
{:error, :bad_topic} ->
|
||||
Logger.debug("#{__MODULE__} bad topic #{inspect(req)}")
|
||||
{:ok, req} = :cowboy_req.reply(404, req)
|
||||
req = :cowboy_req.reply(404, req)
|
||||
{:ok, req, state}
|
||||
|
||||
{:error, :unauthorized} ->
|
||||
Logger.debug("#{__MODULE__} authentication error: #{inspect(req)}")
|
||||
{:ok, req} = :cowboy_req.reply(401, req)
|
||||
req = :cowboy_req.reply(401, req)
|
||||
{:ok, req, state}
|
||||
end
|
||||
end
|
||||
|
|
@ -64,7 +64,9 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
{:ok, %{state | timer: timer()}}
|
||||
end
|
||||
|
||||
# We never receive messages.
|
||||
# We only receive pings for now
|
||||
def websocket_handle(:ping, state), do: {:ok, state}
|
||||
|
||||
def websocket_handle(frame, state) do
|
||||
Logger.error("#{__MODULE__} received frame: #{inspect(frame)}")
|
||||
{:ok, state}
|
||||
|
|
@ -98,6 +100,10 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
|||
{:reply, :ping, %{state | timer: nil, count: 0}, :hibernate}
|
||||
end
|
||||
|
||||
# State can be `[]` only in case we terminate before switching to websocket,
|
||||
# we already log errors for these cases in `init/1`, so just do nothing here
|
||||
def terminate(_reason, _req, []), do: :ok
|
||||
|
||||
def terminate(reason, _req, state) do
|
||||
Logger.debug(
|
||||
"#{__MODULE__} terminating websocket connection for user #{
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ defmodule Pleroma.Web.Metadata.Providers.OpenGraph do
|
|||
|
||||
@impl Provider
|
||||
def build_tags(%{user: user}) do
|
||||
with truncated_bio = Utils.scrub_html_and_truncate(user.bio || "") do
|
||||
with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do
|
||||
[
|
||||
{:meta,
|
||||
[
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ defmodule Pleroma.Web.Metadata.Providers.TwitterCard do
|
|||
|
||||
@impl Provider
|
||||
def build_tags(%{user: user}) do
|
||||
with truncated_bio = Utils.scrub_html_and_truncate(user.bio || "") do
|
||||
with truncated_bio = Utils.scrub_html_and_truncate(user.bio) do
|
||||
[
|
||||
title_tag(user),
|
||||
{:meta, [property: "twitter:description", content: truncated_bio], []},
|
||||
|
|
|
|||
|
|
@ -145,7 +145,10 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
|
||||
"authorization" => %{"redirect_uri" => @oob_token_redirect_uri}
|
||||
}) do
|
||||
render(conn, "oob_authorization_created.html", %{auth: auth})
|
||||
# Enforcing the view to reuse the template when calling from other controllers
|
||||
conn
|
||||
|> put_view(OAuthView)
|
||||
|> render("oob_authorization_created.html", %{auth: auth})
|
||||
end
|
||||
|
||||
def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
|
||||
|
|
@ -197,7 +200,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
{:mfa_required, user, auth, _},
|
||||
params
|
||||
) do
|
||||
{:ok, token} = MFA.Token.create_token(user, auth)
|
||||
{:ok, token} = MFA.Token.create(user, auth)
|
||||
|
||||
data = %{
|
||||
"mfa_token" => token.token,
|
||||
|
|
@ -579,7 +582,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
|||
do: put_session(conn, :registration_id, registration_id)
|
||||
|
||||
defp build_and_response_mfa_token(user, auth) do
|
||||
with {:ok, token} <- MFA.Token.create_token(user, auth) do
|
||||
with {:ok, token} <- MFA.Token.create(user, auth) do
|
||||
MFAView.render("mfa_response.json", %{token: token, user: user})
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
true <- auth.app_id == app.id do
|
||||
user = if auth.user_id, do: User.get_cached_by_id(auth.user_id), else: %User{}
|
||||
|
||||
create_token(
|
||||
create(
|
||||
app,
|
||||
user,
|
||||
%{scopes: auth.scopes}
|
||||
|
|
@ -83,8 +83,22 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
|> validate_required([:valid_until])
|
||||
end
|
||||
|
||||
@spec create_token(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Changeset.t()}
|
||||
def create_token(%App{} = app, %User{} = user, attrs \\ %{}) do
|
||||
@spec create(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Changeset.t()}
|
||||
def create(%App{} = app, %User{} = user, attrs \\ %{}) do
|
||||
with {:ok, token} <- do_create(app, user, attrs) do
|
||||
if Pleroma.Config.get([:oauth2, :clean_expired_tokens]) do
|
||||
Pleroma.Workers.PurgeExpiredToken.enqueue(%{
|
||||
token_id: token.id,
|
||||
valid_until: DateTime.from_naive!(token.valid_until, "Etc/UTC"),
|
||||
mod: __MODULE__
|
||||
})
|
||||
end
|
||||
|
||||
{:ok, token}
|
||||
end
|
||||
end
|
||||
|
||||
defp do_create(app, user, attrs) do
|
||||
%__MODULE__{user_id: user.id, app_id: app.id}
|
||||
|> cast(%{scopes: attrs[:scopes] || app.scopes}, [:scopes])
|
||||
|> validate_required([:scopes, :app_id])
|
||||
|
|
@ -105,11 +119,6 @@ defmodule Pleroma.Web.OAuth.Token do
|
|||
|> Repo.delete_all()
|
||||
end
|
||||
|
||||
def delete_expired_tokens do
|
||||
Query.get_expired_tokens()
|
||||
|> Repo.delete_all()
|
||||
end
|
||||
|
||||
def get_user_tokens(%User{id: user_id}) do
|
||||
Query.get_by_user(user_id)
|
||||
|> Query.preload([:app])
|
||||
|
|
|
|||
|
|
@ -1,38 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.OAuth.Token.CleanWorker do
|
||||
@moduledoc """
|
||||
The module represents functions to clean an expired OAuth and MFA tokens.
|
||||
"""
|
||||
use GenServer
|
||||
|
||||
@ten_seconds 10_000
|
||||
@one_day 86_400_000
|
||||
|
||||
alias Pleroma.MFA
|
||||
alias Pleroma.Web.OAuth
|
||||
alias Pleroma.Workers.BackgroundWorker
|
||||
|
||||
def start_link(_), do: GenServer.start_link(__MODULE__, %{})
|
||||
|
||||
def init(_) do
|
||||
Process.send_after(self(), :perform, @ten_seconds)
|
||||
{:ok, nil}
|
||||
end
|
||||
|
||||
@doc false
|
||||
def handle_info(:perform, state) do
|
||||
BackgroundWorker.enqueue("clean_expired_tokens", %{})
|
||||
interval = Pleroma.Config.get([:oauth2, :clean_expired_tokens_interval], @one_day)
|
||||
|
||||
Process.send_after(self(), :perform, interval)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def perform(:clean) do
|
||||
OAuth.Token.delete_expired_tokens()
|
||||
MFA.Token.delete_expired_tokens()
|
||||
end
|
||||
end
|
||||
|
|
@ -33,12 +33,6 @@ defmodule Pleroma.Web.OAuth.Token.Query do
|
|||
from(q in query, where: q.id == ^id)
|
||||
end
|
||||
|
||||
@spec get_expired_tokens(query, DateTime.t() | nil) :: query
|
||||
def get_expired_tokens(query \\ Token, date \\ nil) do
|
||||
expired_date = date || Timex.now()
|
||||
from(q in query, where: fragment("?", q.valid_until) < ^expired_date)
|
||||
end
|
||||
|
||||
@spec get_by_user(query, String.t()) :: query
|
||||
def get_by_user(query \\ Token, user_id) do
|
||||
from(q in query, where: q.user_id == ^user_id)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ defmodule Pleroma.Web.OAuth.Token.Strategy.RefreshToken do
|
|||
defp create_access_token({:error, error}, _), do: {:error, error}
|
||||
|
||||
defp create_access_token({:ok, token}, %{app: app, user: user} = token_params) do
|
||||
Token.create_token(app, user, add_refresh_token(token_params, token.refresh_token))
|
||||
Token.create(app, user, add_refresh_token(token_params, token.refresh_token))
|
||||
end
|
||||
|
||||
defp add_refresh_token(params, token) do
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
defmodule Pleroma.Web.RelMe do
|
||||
@options [
|
||||
pool: :media,
|
||||
max_body: 2_000_000
|
||||
max_body: 2_000_000,
|
||||
recv_timeout: 2_000
|
||||
]
|
||||
|
||||
if Pleroma.Config.get(:env) == :test do
|
||||
|
|
@ -23,18 +24,8 @@ defmodule Pleroma.Web.RelMe do
|
|||
def parse(_), do: {:error, "No URL provided"}
|
||||
|
||||
defp parse_url(url) do
|
||||
opts =
|
||||
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
|
||||
Keyword.merge(@options,
|
||||
recv_timeout: 2_000,
|
||||
with_body: true
|
||||
)
|
||||
else
|
||||
@options
|
||||
end
|
||||
|
||||
with {:ok, %Tesla.Env{body: html, status: status}} when status in 200..299 <-
|
||||
Pleroma.HTTP.get(url, [], adapter: opts),
|
||||
Pleroma.HTTP.get(url, [], @options),
|
||||
{:ok, html_tree} <- Floki.parse_document(html),
|
||||
data <-
|
||||
Floki.attribute(html_tree, "link[rel~=me]", "href") ++
|
||||
|
|
|
|||
|
|
@ -9,14 +9,15 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
|||
alias Pleroma.Object
|
||||
alias Pleroma.Web.RichMedia.Parser
|
||||
|
||||
@rich_media_options [
|
||||
@options [
|
||||
pool: :media,
|
||||
max_body: 2_000_000
|
||||
max_body: 2_000_000,
|
||||
recv_timeout: 2_000
|
||||
]
|
||||
|
||||
@spec validate_page_url(URI.t() | binary()) :: :ok | :error
|
||||
defp validate_page_url(page_url) when is_binary(page_url) do
|
||||
validate_tld = Pleroma.Config.get([Pleroma.Formatter, :validate_tld])
|
||||
validate_tld = Config.get([Pleroma.Formatter, :validate_tld])
|
||||
|
||||
page_url
|
||||
|> Linkify.Parser.url?(validate_tld: validate_tld)
|
||||
|
|
@ -86,16 +87,6 @@ defmodule Pleroma.Web.RichMedia.Helpers do
|
|||
def rich_media_get(url) do
|
||||
headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}]
|
||||
|
||||
options =
|
||||
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
|
||||
Keyword.merge(@rich_media_options,
|
||||
recv_timeout: 2_000,
|
||||
with_body: true
|
||||
)
|
||||
else
|
||||
@rich_media_options
|
||||
end
|
||||
|
||||
Pleroma.HTTP.get(url, headers, adapter: options)
|
||||
Pleroma.HTTP.get(url, headers, @options)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,8 +21,13 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
|||
{:ok, _} <- set_ttl_based_on_image(data, url) do
|
||||
{:ok, data}
|
||||
else
|
||||
{:error, {:invalid_metadata, data}} = e ->
|
||||
Logger.debug(fn -> "Incomplete or invalid metadata for #{url}: #{inspect(data)}" end)
|
||||
e
|
||||
|
||||
error ->
|
||||
Logger.error(fn -> "Rich media error: #{inspect(error)}" end)
|
||||
Logger.error(fn -> "Rich media error for #{url}: #{inspect(error)}" end)
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -90,7 +95,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
|||
end)
|
||||
end
|
||||
|
||||
defp parse_url(url) do
|
||||
def parse_url(url) do
|
||||
with {:ok, %Tesla.Env{body: html}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url),
|
||||
{:ok, html} <- Floki.parse_document(html) do
|
||||
html
|
||||
|
|
@ -116,7 +121,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
|
|||
end
|
||||
|
||||
defp check_parsed_data(data) do
|
||||
{:error, "Found metadata was invalid or incomplete: #{inspect(data)}"}
|
||||
{:error, {:invalid_metadata, data}}
|
||||
end
|
||||
|
||||
defp clean_parsed_data(data) do
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
<h1>Successfully authorized</h1>
|
||||
<h2>Token code is <%= @auth.token %></h2>
|
||||
<h2>Token code is <br><%= @auth.token %></h2>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
<h1>Authorization exists</h1>
|
||||
<h2>Access token is <%= @token.token %></h2>
|
||||
<h2>Access token is <br><%= @token.token %></h2>
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
|
|||
end
|
||||
|
||||
defp handle_follow_error(conn, {:mfa_required, followee, user, _} = _) do
|
||||
{:ok, %{token: token}} = MFA.Token.create_token(user)
|
||||
{:ok, %{token: token}} = MFA.Token.create(user)
|
||||
render(conn, "follow_mfa.html", %{followee: followee, mfa_token: token, error: false})
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.Cron.ClearOauthTokenWorker do
|
||||
@moduledoc """
|
||||
The worker to cleanup expired oAuth tokens.
|
||||
"""
|
||||
|
||||
use Oban.Worker, queue: "background"
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(_job) do
|
||||
if Config.get([:oauth2, :clean_expired_tokens], false) do
|
||||
Token.delete_expired_tokens()
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker do
|
||||
@moduledoc """
|
||||
The worker to purge expired activities.
|
||||
"""
|
||||
|
||||
use Oban.Worker, queue: "activity_expiration"
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
require Logger
|
||||
|
||||
@interval :timer.minutes(1)
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(_job) do
|
||||
if Config.get([ActivityExpiration, :enabled]) do
|
||||
Enum.each(ActivityExpiration.due_expirations(@interval), &delete_activity/1)
|
||||
end
|
||||
after
|
||||
:ok
|
||||
end
|
||||
|
||||
def delete_activity(%ActivityExpiration{activity_id: activity_id}) do
|
||||
with {:activity, %Activity{} = activity} <-
|
||||
{:activity, Activity.get_by_id_with_object(activity_id)},
|
||||
{:user, %User{} = user} <- {:user, User.get_by_ap_id(activity.object.data["actor"])} do
|
||||
CommonAPI.delete(activity.id, user)
|
||||
else
|
||||
{:activity, _} ->
|
||||
Logger.error(
|
||||
"#{__MODULE__} Couldn't delete expired activity: not found activity ##{activity_id}"
|
||||
)
|
||||
|
||||
{:user, _} ->
|
||||
Logger.error(
|
||||
"#{__MODULE__} Couldn't delete expired activity: not found actor of ##{activity_id}"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.Cron.StatsWorker do
|
||||
@moduledoc """
|
||||
The worker to update peers statistics.
|
||||
"""
|
||||
|
||||
use Oban.Worker, queue: "background"
|
||||
|
||||
@impl Oban.Worker
|
||||
def perform(_job) do
|
||||
Pleroma.Stats.do_collect()
|
||||
:ok
|
||||
end
|
||||
end
|
||||
72
lib/pleroma/workers/purge_expired_activity.ex
Normal file
72
lib/pleroma/workers/purge_expired_activity.ex
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.PurgeExpiredActivity do
|
||||
@moduledoc """
|
||||
Worker which purges expired activity.
|
||||
"""
|
||||
|
||||
use Oban.Worker, queue: :activity_expiration, max_attempts: 1
|
||||
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.Activity
|
||||
|
||||
@spec enqueue(map()) ::
|
||||
{:ok, Oban.Job.t()}
|
||||
| {:error, :expired_activities_disabled}
|
||||
| {:error, :expiration_too_close}
|
||||
def enqueue(args) do
|
||||
with true <- enabled?() do
|
||||
{scheduled_at, args} = Map.pop(args, :expires_at)
|
||||
|
||||
args
|
||||
|> new(scheduled_at: scheduled_at)
|
||||
|> Oban.insert()
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def perform(%Oban.Job{args: %{"activity_id" => id}}) do
|
||||
with %Activity{} = activity <- find_activity(id),
|
||||
%Pleroma.User{} = user <- find_user(activity.object.data["actor"]) do
|
||||
Pleroma.Web.CommonAPI.delete(activity.id, user)
|
||||
end
|
||||
end
|
||||
|
||||
defp enabled? do
|
||||
with false <- Pleroma.Config.get([__MODULE__, :enabled], false) do
|
||||
{:error, :expired_activities_disabled}
|
||||
end
|
||||
end
|
||||
|
||||
defp find_activity(id) do
|
||||
with nil <- Activity.get_by_id_with_object(id) do
|
||||
{:error, :activity_not_found}
|
||||
end
|
||||
end
|
||||
|
||||
defp find_user(ap_id) do
|
||||
with nil <- Pleroma.User.get_by_ap_id(ap_id) do
|
||||
{:error, :user_not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def get_expiration(id) do
|
||||
from(j in Oban.Job,
|
||||
where: j.state == "scheduled",
|
||||
where: j.queue == "activity_expiration",
|
||||
where: fragment("?->>'activity_id' = ?", j.args, ^id)
|
||||
)
|
||||
|> Pleroma.Repo.one()
|
||||
end
|
||||
|
||||
@spec expires_late_enough?(DateTime.t()) :: boolean()
|
||||
def expires_late_enough?(scheduled_at) do
|
||||
now = DateTime.utc_now()
|
||||
diff = DateTime.diff(scheduled_at, now, :millisecond)
|
||||
min_lifetime = Pleroma.Config.get([__MODULE__, :min_lifetime], 600)
|
||||
diff > :timer.seconds(min_lifetime)
|
||||
end
|
||||
end
|
||||
29
lib/pleroma/workers/purge_expired_token.ex
Normal file
29
lib/pleroma/workers/purge_expired_token.ex
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Workers.PurgeExpiredToken do
|
||||
@moduledoc """
|
||||
Worker which purges expired OAuth tokens
|
||||
"""
|
||||
|
||||
use Oban.Worker, queue: :token_expiration, max_attempts: 1
|
||||
|
||||
@spec enqueue(%{token_id: integer(), valid_until: DateTime.t(), mod: module()}) ::
|
||||
{:ok, Oban.Job.t()} | {:error, Ecto.Changeset.t()}
|
||||
def enqueue(args) do
|
||||
{scheduled_at, args} = Map.pop(args, :valid_until)
|
||||
|
||||
args
|
||||
|> __MODULE__.new(scheduled_at: scheduled_at)
|
||||
|> Oban.insert()
|
||||
end
|
||||
|
||||
@impl true
|
||||
def perform(%Oban.Job{args: %{"token_id" => id, "mod" => module}}) do
|
||||
module
|
||||
|> String.to_existing_atom()
|
||||
|> Pleroma.Repo.get(id)
|
||||
|> Pleroma.Repo.delete()
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue