Merge remote-tracking branch 'origin/develop' into link-verification
This commit is contained in:
commit
b7c625db0f
609 changed files with 6056 additions and 2607 deletions
|
|
@ -1,113 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.Benchmark do
|
||||
import Mix.Pleroma
|
||||
use Mix.Task
|
||||
|
||||
def run(["search"]) do
|
||||
start_pleroma()
|
||||
|
||||
Benchee.run(%{
|
||||
"search" => fn ->
|
||||
Pleroma.Activity.search(nil, "cofe")
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
def run(["tag"]) do
|
||||
start_pleroma()
|
||||
|
||||
Benchee.run(%{
|
||||
"tag" => fn ->
|
||||
%{"type" => "Create", "tag" => "cofe"}
|
||||
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
def run(["render_timeline", nickname | _] = args) do
|
||||
start_pleroma()
|
||||
user = Pleroma.User.get_by_nickname(nickname)
|
||||
|
||||
activities =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put("limit", 4096)
|
||||
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||
|> Enum.reverse()
|
||||
|
||||
inputs = %{
|
||||
"1 activity" => Enum.take_random(activities, 1),
|
||||
"10 activities" => Enum.take_random(activities, 10),
|
||||
"20 activities" => Enum.take_random(activities, 20),
|
||||
"40 activities" => Enum.take_random(activities, 40),
|
||||
"80 activities" => Enum.take_random(activities, 80)
|
||||
}
|
||||
|
||||
inputs =
|
||||
if Enum.at(args, 2) == "extended" do
|
||||
Map.merge(inputs, %{
|
||||
"200 activities" => Enum.take_random(activities, 200),
|
||||
"500 activities" => Enum.take_random(activities, 500),
|
||||
"2000 activities" => Enum.take_random(activities, 2000),
|
||||
"4096 activities" => Enum.take_random(activities, 4096)
|
||||
})
|
||||
else
|
||||
inputs
|
||||
end
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Standart rendering" => fn activities ->
|
||||
Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
|
||||
activities: activities,
|
||||
for: user,
|
||||
as: :activity
|
||||
})
|
||||
end
|
||||
},
|
||||
inputs: inputs
|
||||
)
|
||||
end
|
||||
|
||||
def run(["adapters"]) do
|
||||
start_pleroma()
|
||||
|
||||
:ok =
|
||||
Pleroma.Gun.Conn.open(
|
||||
"https://httpbin.org/stream-bytes/1500",
|
||||
:gun_connections
|
||||
)
|
||||
|
||||
Process.sleep(1_500)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Without conn and without pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
|
||||
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", [], receive_conn: false)
|
||||
end,
|
||||
"With reused conn and without pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
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")
|
||||
end
|
||||
},
|
||||
parallel: 10
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -193,7 +193,7 @@ defmodule Mix.Tasks.Pleroma.Database do
|
|||
"ALTER DATABASE #{db} SET default_text_search_config = '#{tsconfig}';"
|
||||
)
|
||||
|
||||
# non-exist config will not raise excpetion but only give >0 messages
|
||||
# non-exist config will not raise exception but only give >0 messages
|
||||
if length(msg) > 0 do
|
||||
shell_info("Error: #{inspect(msg, pretty: true)}")
|
||||
else
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ defmodule Mix.Tasks.Pleroma.Digest do
|
|||
shell_info("Digest email have been sent to #{nickname} (#{user.email})")
|
||||
else
|
||||
_ ->
|
||||
shell_info("Cound't find any mentions for #{nickname} since #{last_digest_emailed_at}")
|
||||
shell_info("Couldn't find any mentions for #{nickname} since #{last_digest_emailed_at}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ defmodule Mix.Tasks.Pleroma.Ecto.Rollback do
|
|||
Logger.configure(level: :info)
|
||||
|
||||
if opts[:env] == "test" do
|
||||
Logger.info("Rollback succesfully")
|
||||
Logger.info("Rollback successfully")
|
||||
else
|
||||
{:ok, _, _} =
|
||||
Ecto.Migrator.with_repo(Pleroma.Repo, &Ecto.Migrator.run(&1, path, :down, opts))
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do
|
|||
|
||||
{:ok, _} =
|
||||
:zip.unzip(binary_archive,
|
||||
cwd: pack_path,
|
||||
cwd: String.to_charlist(pack_path),
|
||||
file_list: files_to_unzip
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -292,7 +292,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
|
||||
if db_configurable? do
|
||||
shell_info(
|
||||
" Please transfer your config to the database after running database migrations. Refer to \"Transfering the config to/from the database\" section of the docs for more information."
|
||||
" Please transfer your config to the database after running database migrations. Refer to \"Transferring the config to/from the database\" section of the docs for more information."
|
||||
)
|
||||
end
|
||||
else
|
||||
|
|
@ -352,6 +352,4 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
|||
|
||||
enabled_filters
|
||||
end
|
||||
|
||||
defp upload_filters(_), do: []
|
||||
end
|
||||
|
|
|
|||
145
lib/mix/tasks/pleroma/search/meilisearch.ex
Normal file
145
lib/mix/tasks/pleroma/search/meilisearch.ex
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.Search.Meilisearch do
|
||||
require Pleroma.Constants
|
||||
|
||||
import Mix.Pleroma
|
||||
import Ecto.Query
|
||||
|
||||
import Pleroma.Search.Meilisearch,
|
||||
only: [meili_post: 2, meili_put: 2, meili_get: 1, meili_delete: 1]
|
||||
|
||||
def run(["index"]) do
|
||||
start_pleroma()
|
||||
Pleroma.HTML.compile_scrubbers()
|
||||
|
||||
meili_version =
|
||||
(
|
||||
{:ok, result} = meili_get("/version")
|
||||
|
||||
result["pkgVersion"]
|
||||
)
|
||||
|
||||
# The ranking rule syntax was changed but nothing about that is mentioned in the changelog
|
||||
if not Version.match?(meili_version, ">= 0.25.0") do
|
||||
raise "Meilisearch <0.24.0 not supported"
|
||||
end
|
||||
|
||||
{:ok, _} =
|
||||
meili_post(
|
||||
"/indexes/objects/settings/ranking-rules",
|
||||
[
|
||||
"published:desc",
|
||||
"words",
|
||||
"exactness",
|
||||
"proximity",
|
||||
"typo",
|
||||
"attribute",
|
||||
"sort"
|
||||
]
|
||||
)
|
||||
|
||||
{:ok, _} =
|
||||
meili_post(
|
||||
"/indexes/objects/settings/searchable-attributes",
|
||||
[
|
||||
"content"
|
||||
]
|
||||
)
|
||||
|
||||
IO.puts("Created indices. Starting to insert posts.")
|
||||
|
||||
chunk_size = Pleroma.Config.get([Pleroma.Search.Meilisearch, :initial_indexing_chunk_size])
|
||||
|
||||
Pleroma.Repo.transaction(
|
||||
fn ->
|
||||
query =
|
||||
from(Pleroma.Object,
|
||||
# Only index public and unlisted posts which are notes and have some text
|
||||
where:
|
||||
fragment("data->>'type' = 'Note'") and
|
||||
(fragment("data->'to' \\? ?", ^Pleroma.Constants.as_public()) or
|
||||
fragment("data->'cc' \\? ?", ^Pleroma.Constants.as_public())),
|
||||
order_by: [desc: fragment("data->'published'")]
|
||||
)
|
||||
|
||||
count = query |> Pleroma.Repo.aggregate(:count, :data)
|
||||
IO.puts("Entries to index: #{count}")
|
||||
|
||||
Pleroma.Repo.stream(
|
||||
query,
|
||||
timeout: :infinity
|
||||
)
|
||||
|> Stream.map(&Pleroma.Search.Meilisearch.object_to_search_data/1)
|
||||
|> Stream.filter(fn o -> not is_nil(o) end)
|
||||
|> Stream.chunk_every(chunk_size)
|
||||
|> Stream.transform(0, fn objects, acc ->
|
||||
new_acc = acc + Enum.count(objects)
|
||||
|
||||
# Reset to the beginning of the line and rewrite it
|
||||
IO.write("\r")
|
||||
IO.write("Indexed #{new_acc} entries")
|
||||
|
||||
{[objects], new_acc}
|
||||
end)
|
||||
|> Stream.each(fn objects ->
|
||||
result =
|
||||
meili_put(
|
||||
"/indexes/objects/documents",
|
||||
objects
|
||||
)
|
||||
|
||||
with {:ok, res} <- result do
|
||||
if not Map.has_key?(res, "uid") do
|
||||
IO.puts("\nFailed to index: #{inspect(result)}")
|
||||
end
|
||||
else
|
||||
e -> IO.puts("\nFailed to index due to network error: #{inspect(e)}")
|
||||
end
|
||||
end)
|
||||
|> Stream.run()
|
||||
end,
|
||||
timeout: :infinity
|
||||
)
|
||||
|
||||
IO.write("\n")
|
||||
end
|
||||
|
||||
def run(["clear"]) do
|
||||
start_pleroma()
|
||||
|
||||
meili_delete("/indexes/objects/documents")
|
||||
end
|
||||
|
||||
def run(["show-keys", master_key]) do
|
||||
start_pleroma()
|
||||
|
||||
endpoint = Pleroma.Config.get([Pleroma.Search.Meilisearch, :url])
|
||||
|
||||
{:ok, result} =
|
||||
Pleroma.HTTP.get(
|
||||
Path.join(endpoint, "/keys"),
|
||||
[{"Authorization", "Bearer #{master_key}"}]
|
||||
)
|
||||
|
||||
decoded = Jason.decode!(result.body)
|
||||
|
||||
if decoded["results"] do
|
||||
Enum.each(decoded["results"], fn %{"description" => desc, "key" => key} ->
|
||||
IO.puts("#{desc}: #{key}")
|
||||
end)
|
||||
else
|
||||
IO.puts("Error fetching the keys, check the master key is correct: #{inspect(decoded)}")
|
||||
end
|
||||
end
|
||||
|
||||
def run(["stats"]) do
|
||||
start_pleroma()
|
||||
|
||||
{:ok, result} = meili_get("/indexes/objects/stats")
|
||||
IO.puts("Number of entries: #{result["numberOfDocuments"]}")
|
||||
IO.puts("Indexing? #{result["isIndexing"]}")
|
||||
end
|
||||
end
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Phoenix.Transports.WebSocket.Raw do
|
||||
import Plug.Conn,
|
||||
only: [
|
||||
fetch_query_params: 1,
|
||||
send_resp: 3
|
||||
]
|
||||
|
||||
alias Phoenix.Socket.Transport
|
||||
|
||||
def default_config do
|
||||
[
|
||||
timeout: 60_000,
|
||||
transport_log: false,
|
||||
cowboy: Phoenix.Endpoint.CowboyWebSocket
|
||||
]
|
||||
end
|
||||
|
||||
def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do
|
||||
{_, opts} = handler.__transport__(transport)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> fetch_query_params
|
||||
|> Transport.transport_log(opts[:transport_log])
|
||||
|> Transport.force_ssl(handler, endpoint, opts)
|
||||
|> Transport.check_origin(handler, endpoint, opts)
|
||||
|
||||
case conn do
|
||||
%{halted: false} = conn ->
|
||||
case handler.connect(%{
|
||||
endpoint: endpoint,
|
||||
transport: transport,
|
||||
options: [serializer: nil],
|
||||
params: conn.params
|
||||
}) do
|
||||
{:ok, socket} ->
|
||||
{:ok, conn, {__MODULE__, {socket, opts}}}
|
||||
|
||||
:error ->
|
||||
send_resp(conn, :forbidden, "")
|
||||
{:error, conn}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, conn}
|
||||
end
|
||||
end
|
||||
|
||||
def init(conn, _) do
|
||||
send_resp(conn, :bad_request, "")
|
||||
{:error, conn}
|
||||
end
|
||||
|
||||
def ws_init({socket, config}) do
|
||||
Process.flag(:trap_exit, true)
|
||||
{:ok, %{socket: socket}, config[:timeout]}
|
||||
end
|
||||
|
||||
def ws_handle(op, data, state) do
|
||||
state.socket.handler
|
||||
|> apply(:handle, [op, data, state])
|
||||
|> case do
|
||||
{op, data} ->
|
||||
{:reply, {op, data}, state}
|
||||
|
||||
{op, data, state} ->
|
||||
{:reply, {op, data}, state}
|
||||
|
||||
%{} = state ->
|
||||
{:ok, state}
|
||||
|
||||
_ ->
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
|
||||
def ws_info({_, _} = tuple, state) do
|
||||
{:reply, tuple, state}
|
||||
end
|
||||
|
||||
def ws_info(_tuple, state), do: {:ok, state}
|
||||
|
||||
def ws_close(state) do
|
||||
ws_handle(:closed, :normal, state)
|
||||
end
|
||||
|
||||
def ws_terminate(reason, state) do
|
||||
ws_handle(:closed, reason, state)
|
||||
end
|
||||
end
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ defmodule Pleroma.Activity.HTML do
|
|||
end
|
||||
end
|
||||
|
||||
defp add_cache_key_for(activity_id, additional_key) do
|
||||
def add_cache_key_for(activity_id, additional_key) do
|
||||
current = get_cache_keys_for(activity_id)
|
||||
|
||||
unless additional_key in current do
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Activity.Queries do
|
|||
|
||||
import Ecto.Query, only: [from: 2, where: 3]
|
||||
|
||||
@type query :: Ecto.Queryable.t() | Activity.t()
|
||||
@type query :: Ecto.Queryable.t() | Pleroma.Activity.t()
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.User
|
||||
|
|
|
|||
|
|
@ -23,19 +23,21 @@ defmodule Pleroma.Announcement do
|
|||
timestamps(type: :utc_datetime)
|
||||
end
|
||||
|
||||
def change(struct, params \\ %{}) do
|
||||
struct
|
||||
|> cast(validate_params(struct, params), [:data, :starts_at, :ends_at, :rendered])
|
||||
@doc "Generates changeset for %Pleroma.Announcement{}"
|
||||
@spec changeset(%__MODULE__{}, map()) :: %Ecto.Changeset{}
|
||||
def changeset(announcement \\ %__MODULE__{}, params \\ %{data: %{}}) do
|
||||
announcement
|
||||
|> cast(validate_params(announcement, params), [:data, :starts_at, :ends_at, :rendered])
|
||||
|> validate_required([:data])
|
||||
end
|
||||
|
||||
defp validate_params(struct, params) do
|
||||
defp validate_params(announcement, params) do
|
||||
base_data =
|
||||
%{
|
||||
"content" => "",
|
||||
"all_day" => false
|
||||
}
|
||||
|> Map.merge((struct && struct.data) || %{})
|
||||
|> Map.merge((announcement && announcement.data) || %{})
|
||||
|
||||
merged_data =
|
||||
Map.merge(base_data, params.data)
|
||||
|
|
@ -61,13 +63,13 @@ defmodule Pleroma.Announcement do
|
|||
end
|
||||
|
||||
def add(params) do
|
||||
changeset = change(%__MODULE__{}, params)
|
||||
changeset = changeset(%__MODULE__{}, params)
|
||||
|
||||
Repo.insert(changeset)
|
||||
end
|
||||
|
||||
def update(announcement, params) do
|
||||
changeset = change(announcement, params)
|
||||
changeset = changeset(announcement, params)
|
||||
|
||||
Repo.update(changeset)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ defmodule Pleroma.Application do
|
|||
@name Mix.Project.config()[:name]
|
||||
@version Mix.Project.config()[:version]
|
||||
@repository Mix.Project.config()[:source_url]
|
||||
@mix_env Mix.env()
|
||||
|
||||
def name, do: @name
|
||||
def version, do: @version
|
||||
|
|
@ -54,7 +53,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 +89,7 @@ defmodule Pleroma.Application do
|
|||
# Define workers and child supervisors to be supervised
|
||||
children =
|
||||
[
|
||||
Pleroma.PromEx,
|
||||
Pleroma.Repo,
|
||||
Config.TransferTask,
|
||||
Pleroma.Emoji,
|
||||
|
|
@ -98,7 +97,7 @@ defmodule Pleroma.Application do
|
|||
{Task.Supervisor, name: Pleroma.TaskSupervisor}
|
||||
] ++
|
||||
cachex_children() ++
|
||||
http_children(adapter, @mix_env) ++
|
||||
http_children(adapter) ++
|
||||
[
|
||||
Pleroma.Stats,
|
||||
Pleroma.JobQueueMonitor,
|
||||
|
|
@ -106,8 +105,9 @@ defmodule Pleroma.Application do
|
|||
{Oban, Config.get(Oban)},
|
||||
Pleroma.Web.Endpoint
|
||||
] ++
|
||||
task_children(@mix_env) ++
|
||||
dont_run_in_test(@mix_env) ++
|
||||
task_children() ++
|
||||
streamer_registry() ++
|
||||
background_migrators() ++
|
||||
shout_child(shout_enabled?()) ++
|
||||
[Pleroma.Gopher.Server]
|
||||
|
||||
|
|
@ -116,12 +116,7 @@ defmodule Pleroma.Application do
|
|||
# If we have a lot of caches, default max_restarts can cause test
|
||||
# resets to fail.
|
||||
# Go for the default 3 unless we're in test
|
||||
max_restarts =
|
||||
if @mix_env == :test do
|
||||
100
|
||||
else
|
||||
3
|
||||
end
|
||||
max_restarts = Application.get_env(:pleroma, __MODULE__)[:max_restarts]
|
||||
|
||||
opts = [strategy: :one_for_one, name: Pleroma.Supervisor, max_restarts: max_restarts]
|
||||
result = Supervisor.start_link(children, opts)
|
||||
|
|
@ -138,7 +133,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"
|
||||
)
|
||||
|
||||
|
|
@ -159,7 +154,7 @@ defmodule Pleroma.Application do
|
|||
raise "Invalid custom modules"
|
||||
|
||||
{:ok, modules, _warnings} ->
|
||||
if @mix_env != :test do
|
||||
if Application.get_env(:pleroma, __MODULE__)[:load_custom_modules] do
|
||||
Enum.each(modules, fn mod ->
|
||||
Logger.info("Custom module loaded: #{inspect(mod)}")
|
||||
end)
|
||||
|
|
@ -170,29 +165,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()),
|
||||
|
|
@ -236,24 +208,30 @@ defmodule Pleroma.Application do
|
|||
|
||||
defp shout_enabled?, do: Config.get([:shout, :enabled])
|
||||
|
||||
defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
|
||||
|
||||
defp dont_run_in_test(_) do
|
||||
[
|
||||
{Registry,
|
||||
[
|
||||
name: Pleroma.Web.Streamer.registry(),
|
||||
keys: :duplicate,
|
||||
partitions: System.schedulers_online()
|
||||
]}
|
||||
] ++ background_migrators()
|
||||
defp streamer_registry do
|
||||
if Application.get_env(:pleroma, __MODULE__)[:streamer_registry] do
|
||||
[
|
||||
{Registry,
|
||||
[
|
||||
name: Pleroma.Web.Streamer.registry(),
|
||||
keys: :duplicate,
|
||||
partitions: System.schedulers_online()
|
||||
]}
|
||||
]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp background_migrators do
|
||||
[
|
||||
Pleroma.Migrators.HashtagsTableMigrator,
|
||||
Pleroma.Migrators.ContextObjectsDeletionMigrator
|
||||
]
|
||||
if Application.get_env(:pleroma, __MODULE__)[:background_migrators] do
|
||||
[
|
||||
Pleroma.Migrators.HashtagsTableMigrator,
|
||||
Pleroma.Migrators.ContextObjectsDeletionMigrator
|
||||
]
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp shout_child(true) do
|
||||
|
|
@ -265,37 +243,43 @@ defmodule Pleroma.Application do
|
|||
|
||||
defp shout_child(_), do: []
|
||||
|
||||
defp task_children(:test) do
|
||||
[
|
||||
defp task_children do
|
||||
children = [
|
||||
%{
|
||||
id: :web_push_init,
|
||||
start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
|
||||
restart: :temporary
|
||||
}
|
||||
]
|
||||
end
|
||||
|
||||
defp task_children(_) do
|
||||
[
|
||||
%{
|
||||
id: :web_push_init,
|
||||
start: {Task, :start_link, [&Pleroma.Web.Push.init/0]},
|
||||
restart: :temporary
|
||||
},
|
||||
%{
|
||||
id: :internal_fetch_init,
|
||||
start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]},
|
||||
restart: :temporary
|
||||
}
|
||||
]
|
||||
if Application.get_env(:pleroma, __MODULE__)[:internal_fetch] do
|
||||
children ++
|
||||
[
|
||||
%{
|
||||
id: :internal_fetch_init,
|
||||
start: {Task, :start_link, [&Pleroma.Web.ActivityPub.InternalFetchActor.init/0]},
|
||||
restart: :temporary
|
||||
}
|
||||
]
|
||||
else
|
||||
children
|
||||
end
|
||||
end
|
||||
|
||||
# start hackney and gun pools in tests
|
||||
defp http_children(_, :test) do
|
||||
http_children(Tesla.Adapter.Hackney, nil) ++ http_children(Tesla.Adapter.Gun, nil)
|
||||
defp http_children(adapter) do
|
||||
if Application.get_env(:pleroma, __MODULE__)[:test_http_pools] do
|
||||
http_children_hackney() ++ http_children_gun()
|
||||
else
|
||||
cond do
|
||||
match?(Tesla.Adapter.Hackney, adapter) -> http_children_hackney()
|
||||
match?(Tesla.Adapter.Gun, adapter) -> http_children_gun()
|
||||
true -> []
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp http_children(Tesla.Adapter.Hackney, _) do
|
||||
defp http_children_hackney do
|
||||
pools = [:federation, :media]
|
||||
|
||||
pools =
|
||||
|
|
@ -311,18 +295,20 @@ defmodule Pleroma.Application do
|
|||
end
|
||||
end
|
||||
|
||||
defp http_children(Tesla.Adapter.Gun, _) do
|
||||
defp http_children_gun do
|
||||
Pleroma.Gun.ConnectionPool.children() ++
|
||||
[{Task, &Pleroma.HTTP.AdapterHelper.Gun.limiter_setup/0}]
|
||||
end
|
||||
|
||||
defp http_children(_, _), do: []
|
||||
|
||||
@spec limiters_setup() :: :ok
|
||||
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, [])
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
The module represents the collection of validations to runs before start server.
|
||||
"""
|
||||
|
||||
defmodule VerifyError, do: defexception([:message])
|
||||
defmodule VerifyError do
|
||||
defexception([:message])
|
||||
@type t :: %__MODULE__{}
|
||||
end
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Helpers.MediaHelper
|
||||
|
|
@ -34,7 +37,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 +56,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 +171,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")
|
||||
]
|
||||
|
||||
|
|
@ -195,8 +196,6 @@ defmodule Pleroma.ApplicationRequirements do
|
|||
end
|
||||
end
|
||||
|
||||
defp check_system_commands!(result), do: result
|
||||
|
||||
defp check_repo_pool_size!(:ok) do
|
||||
if Pleroma.Config.get([Pleroma.Repo, :pool_size], 10) != 10 and
|
||||
not Pleroma.Config.get([:dangerzone, :override_repo_pool_size], false) do
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ defmodule Pleroma.Bookmark do
|
|||
timestamps()
|
||||
end
|
||||
|
||||
@spec create(FlakeId.Ecto.CompatType.t(), FlakeId.Ecto.CompatType.t()) ::
|
||||
{:ok, Bookmark.t()} | {:error, Changeset.t()}
|
||||
@spec create(Ecto.UUID.t(), Ecto.UUID.t()) ::
|
||||
{:ok, Bookmark.t()} | {:error, Ecto.Changeset.t()}
|
||||
def create(user_id, activity_id) do
|
||||
attrs = %{
|
||||
user_id: user_id,
|
||||
|
|
@ -37,7 +37,7 @@ defmodule Pleroma.Bookmark do
|
|||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@spec for_user_query(FlakeId.Ecto.CompatType.t()) :: Ecto.Query.t()
|
||||
@spec for_user_query(Ecto.UUID.t()) :: Ecto.Query.t()
|
||||
def for_user_query(user_id) do
|
||||
Bookmark
|
||||
|> where(user_id: ^user_id)
|
||||
|
|
@ -52,8 +52,8 @@ defmodule Pleroma.Bookmark do
|
|||
|> Repo.one()
|
||||
end
|
||||
|
||||
@spec destroy(FlakeId.Ecto.CompatType.t(), FlakeId.Ecto.CompatType.t()) ::
|
||||
{:ok, Bookmark.t()} | {:error, Changeset.t()}
|
||||
@spec destroy(Ecto.UUID.t(), Ecto.UUID.t()) ::
|
||||
{:ok, Bookmark.t()} | {:error, Ecto.Changeset.t()}
|
||||
def destroy(user_id, activity_id) do
|
||||
from(b in Bookmark,
|
||||
where: b.user_id == ^user_id,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,13 @@ defmodule Pleroma.Caching do
|
|||
@callback put(Cachex.cache(), any(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
|
||||
@callback put(Cachex.cache(), any(), any()) :: {Cachex.status(), boolean()}
|
||||
@callback fetch!(Cachex.cache(), any(), function() | nil) :: any()
|
||||
@callback fetch(Cachex.cache(), any(), function() | nil) ::
|
||||
{atom(), any()} | {atom(), any(), any()}
|
||||
# @callback del(Cachex.cache(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
|
||||
@callback del(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
|
||||
@callback stream!(Cachex.cache(), any()) :: Enumerable.t()
|
||||
@callback expire_at(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()}
|
||||
@callback expire(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()}
|
||||
@callback exists?(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
|
||||
@callback execute!(Cachex.cache(), function()) :: any()
|
||||
@callback get_and_update(Cachex.cache(), any(), function()) ::
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ defmodule Pleroma.Captcha.Kocaptcha do
|
|||
|
||||
@impl Service
|
||||
def validate(_token, captcha, answer_data) do
|
||||
# Here the token is unsed, because the unencrypted captcha answer is just passed to method
|
||||
# Here the token is unused, because the unencrypted captcha answer is just passed to method
|
||||
if not is_nil(captcha) and
|
||||
:crypto.hash(:md5, captcha) |> Base.encode16() == String.upcase(answer_data),
|
||||
do: :ok,
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ defmodule Pleroma.Chat do
|
|||
|> unique_constraint(:user_id, name: :chats_user_id_recipient_index)
|
||||
end
|
||||
|
||||
@spec get_by_user_and_id(User.t(), FlakeId.Ecto.CompatType.t()) ::
|
||||
@spec get_by_user_and_id(User.t(), Ecto.UUID.t()) ::
|
||||
{:ok, t()} | {:error, :not_found}
|
||||
def get_by_user_and_id(%User{id: user_id}, id) do
|
||||
from(c in __MODULE__,
|
||||
|
|
@ -52,17 +52,17 @@ defmodule Pleroma.Chat do
|
|||
|> Repo.find_resource()
|
||||
end
|
||||
|
||||
@spec get_by_id(FlakeId.Ecto.CompatType.t()) :: t() | nil
|
||||
@spec get_by_id(Ecto.UUID.t()) :: t() | nil
|
||||
def get_by_id(id) do
|
||||
Repo.get(__MODULE__, id)
|
||||
end
|
||||
|
||||
@spec get(FlakeId.Ecto.CompatType.t(), String.t()) :: t() | nil
|
||||
@spec get(Ecto.UUID.t(), String.t()) :: t() | nil
|
||||
def get(user_id, recipient) do
|
||||
Repo.get_by(__MODULE__, user_id: user_id, recipient: recipient)
|
||||
end
|
||||
|
||||
@spec get_or_create(FlakeId.Ecto.CompatType.t(), String.t()) ::
|
||||
@spec get_or_create(Ecto.UUID.t(), String.t()) ::
|
||||
{:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||
def get_or_create(user_id, recipient) do
|
||||
%__MODULE__{}
|
||||
|
|
@ -75,7 +75,7 @@ defmodule Pleroma.Chat do
|
|||
)
|
||||
end
|
||||
|
||||
@spec bump_or_create(FlakeId.Ecto.CompatType.t(), String.t()) ::
|
||||
@spec bump_or_create(Ecto.UUID.t(), String.t()) ::
|
||||
{:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||
def bump_or_create(user_id, recipient) do
|
||||
%__MODULE__{}
|
||||
|
|
@ -87,7 +87,7 @@ defmodule Pleroma.Chat do
|
|||
)
|
||||
end
|
||||
|
||||
@spec for_user_query(FlakeId.Ecto.CompatType.t()) :: Ecto.Query.t()
|
||||
@spec for_user_query(Ecto.UUID.t()) :: Ecto.Query.t()
|
||||
def for_user_query(user_id) do
|
||||
from(c in Chat,
|
||||
where: c.user_id == ^user_id,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
||||
|
|
@ -172,7 +172,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
|
||||
```
|
||||
config :pleroma, :mrf,
|
||||
transparency_exclusions: [{"instance.tld", "Reason to exlude transparency"}]
|
||||
transparency_exclusions: [{"instance.tld", "Reason to exclude transparency"}]
|
||||
```
|
||||
""")
|
||||
|
||||
|
|
@ -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.
|
||||
""")
|
||||
|
|
@ -213,7 +213,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
check_gun_pool_options(),
|
||||
check_activity_expiration_config(),
|
||||
check_remote_ip_plug_name(),
|
||||
check_uploders_s3_public_endpoint(),
|
||||
check_uploaders_s3_public_endpoint(),
|
||||
check_old_chat_shoutbox(),
|
||||
check_quarantined_instances_tuples(),
|
||||
check_transparency_exclusions_tuples(),
|
||||
|
|
@ -256,7 +256,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
move_namespace_and_warn(@mrf_config_map, warning_preface)
|
||||
end
|
||||
|
||||
@spec move_namespace_and_warn([config_map()], String.t()) :: :ok | nil
|
||||
@spec move_namespace_and_warn([config_map()], String.t()) :: :ok | :error
|
||||
def move_namespace_and_warn(config_map, warning_preface) do
|
||||
warning =
|
||||
Enum.reduce(config_map, "", fn
|
||||
|
|
@ -274,17 +274,17 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
if warning == "" do
|
||||
:ok
|
||||
else
|
||||
Logger.warn(warning_preface <> warning)
|
||||
Logger.warning(warning_preface <> warning)
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
@spec check_media_proxy_whitelist_config() :: :ok | nil
|
||||
@spec check_media_proxy_whitelist_config() :: :ok | :error
|
||||
def check_media_proxy_whitelist_config 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
|
||||
|
|
@ -340,7 +340,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
end
|
||||
end
|
||||
|
||||
@spec check_activity_expiration_config() :: :ok | nil
|
||||
@spec check_activity_expiration_config() :: :ok | :error
|
||||
def check_activity_expiration_config do
|
||||
warning_preface = """
|
||||
!!!DEPRECATION WARNING!!!
|
||||
|
|
@ -356,7 +356,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
)
|
||||
end
|
||||
|
||||
@spec check_remote_ip_plug_name() :: :ok | nil
|
||||
@spec check_remote_ip_plug_name() :: :ok | :error
|
||||
def check_remote_ip_plug_name do
|
||||
warning_preface = """
|
||||
!!!DEPRECATION WARNING!!!
|
||||
|
|
@ -372,8 +372,8 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
)
|
||||
end
|
||||
|
||||
@spec check_uploders_s3_public_endpoint() :: :ok | nil
|
||||
def check_uploders_s3_public_endpoint do
|
||||
@spec check_uploaders_s3_public_endpoint() :: :ok | :error
|
||||
def check_uploaders_s3_public_endpoint do
|
||||
s3_config = Pleroma.Config.get([Pleroma.Uploaders.S3])
|
||||
|
||||
use_old_config = Keyword.has_key?(s3_config, :public_endpoint)
|
||||
|
|
@ -393,7 +393,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
|
|||
end
|
||||
end
|
||||
|
||||
@spec check_old_chat_shoutbox() :: :ok | nil
|
||||
@spec check_old_chat_shoutbox() :: :ok | :error
|
||||
def check_old_chat_shoutbox do
|
||||
instance_config = Pleroma.Config.get([:instance])
|
||||
chat_config = Pleroma.Config.get([:chat]) || []
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do
|
|||
with_runtime_config =
|
||||
if File.exists?(config_path) do
|
||||
# <https://git.pleroma.social/pleroma/pleroma/-/issues/3135>
|
||||
%File.Stat{mode: mode} = File.lstat!(config_path)
|
||||
%File.Stat{mode: mode} = File.stat!(config_path)
|
||||
|
||||
if Bitwise.band(mode, 0o007) > 0 do
|
||||
raise "Configuration at #{config_path} has world-permissions, execute the following: chmod o= #{config_path}"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ defmodule Pleroma.ConfigDB do
|
|||
@spec get_by_params(map()) :: ConfigDB.t() | nil
|
||||
def get_by_params(%{group: _, key: _} = params), do: Repo.get_by(ConfigDB, params)
|
||||
|
||||
@spec changeset(ConfigDB.t(), map()) :: Changeset.t()
|
||||
@spec changeset(ConfigDB.t(), map()) :: Ecto.Changeset.t()
|
||||
def changeset(config, params \\ %{}) do
|
||||
config
|
||||
|> cast(params, [:key, :group, :value])
|
||||
|
|
@ -138,7 +138,7 @@ defmodule Pleroma.ConfigDB do
|
|||
end
|
||||
end
|
||||
|
||||
@spec update_or_create(map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()}
|
||||
@spec update_or_create(map()) :: {:ok, ConfigDB.t()} | {:error, Ecto.Changeset.t()}
|
||||
def update_or_create(params) do
|
||||
params = Map.put(params, :value, to_elixir_types(params[:value]))
|
||||
search_opts = Map.take(params, [:group, :key])
|
||||
|
|
@ -175,7 +175,7 @@ defmodule Pleroma.ConfigDB do
|
|||
end)
|
||||
end
|
||||
|
||||
@spec delete(ConfigDB.t() | map()) :: {:ok, ConfigDB.t()} | {:error, Changeset.t()}
|
||||
@spec delete(ConfigDB.t() | map()) :: {:ok, ConfigDB.t()} | {:error, Ecto.Changeset.t()}
|
||||
def delete(%ConfigDB{} = config), do: Repo.delete(config)
|
||||
|
||||
def delete(params) do
|
||||
|
|
|
|||
|
|
@ -76,6 +76,14 @@ defmodule Pleroma.Constants do
|
|||
]
|
||||
)
|
||||
|
||||
const(allowed_user_actor_types,
|
||||
do: [
|
||||
"Person",
|
||||
"Service",
|
||||
"Group"
|
||||
]
|
||||
)
|
||||
|
||||
# basic regex, just there to weed out potential mistakes
|
||||
# https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
|
||||
const(mime_regex,
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ defmodule Pleroma.Conversation do
|
|||
3. Bump all relevant participations to 'unread'
|
||||
"""
|
||||
def create_or_bump_for(activity, opts \\ []) do
|
||||
with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity),
|
||||
with true <- Pleroma.Web.ActivityPub.Visibility.direct?(activity),
|
||||
"Create" <- activity.data["type"],
|
||||
%Object{} = object <- Object.normalize(activity, fetch: false),
|
||||
true <- object.data["type"] in ["Note", "Question"],
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ defmodule Pleroma.DataMigration do
|
|||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "data_migrations" do
|
||||
field(:name, :string)
|
||||
field(:state, State, default: :pending)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ defmodule Pleroma.Docs.Generator do
|
|||
:code.all_loaded()
|
||||
|> Enum.filter(fn {module, _} ->
|
||||
# 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`
|
||||
# but in test environments 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)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ defmodule Pleroma.Docs.JSON do
|
|||
:persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(descriptions))
|
||||
end
|
||||
|
||||
@spec compiled_descriptions :: Map.t()
|
||||
@spec compiled_descriptions :: map()
|
||||
def compiled_descriptions do
|
||||
:persistent_term.get(@term)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,10 +8,12 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.BareUri do
|
|||
def type, do: :string
|
||||
|
||||
def cast(uri) when is_binary(uri) do
|
||||
case URI.parse(uri) do
|
||||
%URI{scheme: nil} -> :error
|
||||
%URI{} -> {:ok, uri}
|
||||
_ -> :error
|
||||
parsed = URI.parse(uri)
|
||||
|
||||
if is_nil(parsed.scheme) do
|
||||
:error
|
||||
else
|
||||
{:ok, uri}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ defmodule Pleroma.Emoji do
|
|||
|
||||
defstruct [:code, :file, :tags, :safe_code, :safe_file]
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
@doc "Build emoji struct"
|
||||
def build({code, file, tags}) do
|
||||
%__MODULE__{
|
||||
|
|
@ -49,12 +51,12 @@ defmodule Pleroma.Emoji do
|
|||
end
|
||||
|
||||
@doc "Returns the path of the emoji `name`."
|
||||
@spec get(String.t()) :: String.t() | nil
|
||||
@spec get(String.t()) :: Pleroma.Emoji.t() | nil
|
||||
def get(name) do
|
||||
name = maybe_strip_name(name)
|
||||
|
||||
case :ets.lookup(@ets, name) do
|
||||
[{_, path}] -> path
|
||||
[{_, emoji}] -> emoji
|
||||
_ -> nil
|
||||
end
|
||||
end
|
||||
|
|
@ -136,23 +138,23 @@ defmodule Pleroma.Emoji do
|
|||
emojis = emojis ++ regional_indicators
|
||||
|
||||
for emoji <- emojis do
|
||||
def is_unicode_emoji?(unquote(emoji)), do: true
|
||||
def unicode?(unquote(emoji)), do: true
|
||||
end
|
||||
|
||||
def is_unicode_emoji?(_), do: false
|
||||
def unicode?(_), do: false
|
||||
|
||||
@emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/
|
||||
|
||||
def is_custom_emoji?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s)
|
||||
def custom?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s)
|
||||
|
||||
def is_custom_emoji?(_), do: false
|
||||
def custom?(_), do: false
|
||||
|
||||
def maybe_strip_name(name) when is_binary(name), do: String.trim(name, ":")
|
||||
|
||||
def maybe_strip_name(name), do: name
|
||||
|
||||
def maybe_quote(name) when is_binary(name) do
|
||||
if is_unicode_emoji?(name) do
|
||||
if unicode?(name) do
|
||||
name
|
||||
else
|
||||
if String.starts_with?(name, ":") do
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ defmodule Pleroma.Emoji.Loader do
|
|||
|
||||
require Logger
|
||||
|
||||
@mix_env Mix.env()
|
||||
|
||||
@type pattern :: Regex.t() | module() | String.t()
|
||||
@type patterns :: pattern() | [pattern()]
|
||||
@type group_patterns :: keyword(patterns())
|
||||
|
|
@ -59,7 +57,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
|
||||
|
|
@ -79,7 +77,7 @@ defmodule Pleroma.Emoji.Loader do
|
|||
|
||||
# for testing emoji.txt entries we do not want exposed in normal operation
|
||||
test_emoji =
|
||||
if @mix_env == :test do
|
||||
if Application.get_env(:pleroma, __MODULE__)[:test_emoji] do
|
||||
load_from_file("test/config/emoji.txt", emoji_groups)
|
||||
else
|
||||
[]
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||
{:ok, _emoji_files} =
|
||||
:zip.unzip(
|
||||
to_charlist(file.path),
|
||||
[{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, tmp_dir}]
|
||||
[{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, String.to_charlist(tmp_dir)}]
|
||||
)
|
||||
|
||||
{_, updated_pack} =
|
||||
|
|
@ -209,7 +209,9 @@ defmodule Pleroma.Emoji.Pack do
|
|||
|
||||
with :ok <- validate_shareable_packs_available(uri) do
|
||||
uri
|
||||
|> URI.merge("/api/pleroma/emoji/packs?page=#{opts[:page]}&page_size=#{opts[:page_size]}")
|
||||
|> URI.merge(
|
||||
"/api/v1/pleroma/emoji/packs?page=#{opts[:page]}&page_size=#{opts[:page_size]}"
|
||||
)
|
||||
|> http_get()
|
||||
end
|
||||
end
|
||||
|
|
@ -249,8 +251,12 @@ defmodule Pleroma.Emoji.Pack do
|
|||
uri = url |> String.trim() |> URI.parse()
|
||||
|
||||
with :ok <- validate_shareable_packs_available(uri),
|
||||
{:ok, %{"files_count" => files_count}} <-
|
||||
uri |> URI.merge("/api/v1/pleroma/emoji/pack?name=#{name}&page_size=0") |> http_get(),
|
||||
{:ok, remote_pack} <-
|
||||
uri |> URI.merge("/api/pleroma/emoji/pack?name=#{name}") |> http_get(),
|
||||
uri
|
||||
|> URI.merge("/api/v1/pleroma/emoji/pack?name=#{name}&page_size=#{files_count}")
|
||||
|> http_get(),
|
||||
{:ok, %{sha: sha, url: url} = pack_info} <- fetch_pack_info(remote_pack, uri, name),
|
||||
{:ok, archive} <- download_archive(url, sha),
|
||||
pack <- copy_as(remote_pack, as || name),
|
||||
|
|
@ -592,7 +598,7 @@ defmodule Pleroma.Emoji.Pack do
|
|||
{:ok,
|
||||
%{
|
||||
sha: sha,
|
||||
url: URI.merge(uri, "/api/pleroma/emoji/packs/archive?name=#{name}") |> to_string()
|
||||
url: URI.merge(uri, "/api/v1/pleroma/emoji/packs/archive?name=#{name}") |> to_string()
|
||||
}}
|
||||
|
||||
%{"fallback-src" => src, "fallback-src-sha256" => sha} when is_binary(src) ->
|
||||
|
|
|
|||
|
|
@ -216,9 +216,6 @@ defmodule Pleroma.Filter do
|
|||
|
||||
:re ->
|
||||
~r/\b#{phrases}\b/i
|
||||
|
||||
_ ->
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
|
|||
|
||||
def response("/notices/" <> id) do
|
||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||
true <- Visibility.is_public?(activity) do
|
||||
true <- Visibility.public?(activity) do
|
||||
activities =
|
||||
ActivityPub.fetch_activities_for_context(activity.data["context"])
|
||||
|> render_activities
|
||||
|
|
|
|||
|
|
@ -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)}"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
|
|||
|
||||
def start_monitor do
|
||||
pid =
|
||||
case :gen_server.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
|
||||
case GenServer.start_link(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
|
||||
{:ok, pid} ->
|
||||
pid
|
||||
|
||||
|
|
|
|||
|
|
@ -18,10 +18,12 @@ defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do
|
|||
)
|
||||
end
|
||||
|
||||
def start_worker(opts, retry \\ false) do
|
||||
def start_worker(opts, last_attempt \\ false) do
|
||||
case DynamicSupervisor.start_child(__MODULE__, {Pleroma.Gun.ConnectionPool.Worker, opts}) do
|
||||
{:error, :max_children} ->
|
||||
if retry or free_pool() == :error do
|
||||
funs = [fn -> last_attempt end, fn -> match?(:error, free_pool()) end]
|
||||
|
||||
if Enum.any?(funs, fn fun -> fun.() end) do
|
||||
:telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts})
|
||||
{:error, :pool_full}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -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,141 +23,48 @@ 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"),
|
||||
{:ok, env} <- HTTP.get(url, [], pool: :media),
|
||||
{:ok, fifo_path} <- mkfifo(),
|
||||
args = [
|
||||
"-y",
|
||||
"-i",
|
||||
fifo_path,
|
||||
"-vframes",
|
||||
"1",
|
||||
"-f",
|
||||
"mjpeg",
|
||||
"-loglevel",
|
||||
"error",
|
||||
"-"
|
||||
] do
|
||||
run_fifo(fifo_path, env, executable, args)
|
||||
{:ok, pid} <- StringIO.open(env.body) do
|
||||
body_stream = IO.binstream(pid, 1)
|
||||
|
||||
Exile.stream!(
|
||||
[
|
||||
executable,
|
||||
"-i",
|
||||
"pipe:0",
|
||||
"-vframes",
|
||||
"1",
|
||||
"-f",
|
||||
"mjpeg",
|
||||
"pipe:1"
|
||||
],
|
||||
input: body_stream,
|
||||
ignore_epipe: true,
|
||||
stderr: :disable
|
||||
)
|
||||
|> Enum.into(<<>>)
|
||||
else
|
||||
nil -> {:error, {:ffmpeg, :command_not_found}}
|
||||
{:error, _} = error -> error
|
||||
end
|
||||
end
|
||||
|
||||
defp run_fifo(fifo_path, env, executable, args) do
|
||||
pid =
|
||||
Port.open({:spawn_executable, executable}, [
|
||||
:use_stdio,
|
||||
:stream,
|
||||
:exit_status,
|
||||
:binary,
|
||||
args: args
|
||||
])
|
||||
|
||||
fifo = Port.open(to_charlist(fifo_path), [:eof, :binary, :stream, :out])
|
||||
fix = Pleroma.Helpers.QtFastStart.fix(env.body)
|
||||
true = Port.command(fifo, fix)
|
||||
:erlang.port_close(fifo)
|
||||
loop_recv(pid)
|
||||
after
|
||||
File.rm(fifo_path)
|
||||
end
|
||||
|
||||
defp mkfifo do
|
||||
path = Path.join(System.tmp_dir!(), "pleroma-media-preview-pipe-#{Ecto.UUID.generate()}")
|
||||
|
||||
case System.cmd("mkfifo", [path]) do
|
||||
{_, 0} ->
|
||||
spawn(fifo_guard(path))
|
||||
{:ok, path}
|
||||
|
||||
{_, err} ->
|
||||
{:error, {:fifo_failed, err}}
|
||||
end
|
||||
end
|
||||
|
||||
defp fifo_guard(path) do
|
||||
pid = self()
|
||||
|
||||
fn ->
|
||||
ref = Process.monitor(pid)
|
||||
|
||||
receive do
|
||||
{:DOWN, ^ref, :process, ^pid, _} ->
|
||||
File.rm(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defp loop_recv(pid) do
|
||||
loop_recv(pid, <<>>)
|
||||
end
|
||||
|
||||
defp loop_recv(pid, acc) do
|
||||
receive do
|
||||
{^pid, {:data, data}} ->
|
||||
loop_recv(pid, acc <> data)
|
||||
|
||||
{^pid, {:exit_status, 0}} ->
|
||||
{:ok, acc}
|
||||
|
||||
{^pid, {:exit_status, status}} ->
|
||||
{:error, status}
|
||||
after
|
||||
5000 ->
|
||||
:erlang.port_close(pid)
|
||||
{:error, :timeout}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,16 +40,21 @@ defmodule Pleroma.Helpers.QtFastStart do
|
|||
got_mdat,
|
||||
acc
|
||||
) do
|
||||
full_size = (size - 8) * 8
|
||||
<<data::bits-size(full_size), rest::bits>> = rest
|
||||
try do
|
||||
full_size = (size - 8) * 8
|
||||
<<data::bits-size(full_size), rest::bits>> = rest
|
||||
|
||||
acc = [
|
||||
{fourcc, pos, pos + size, size,
|
||||
<<size::integer-big-size(32), fourcc::bits-size(32), data::bits>>}
|
||||
| acc
|
||||
]
|
||||
acc = [
|
||||
{fourcc, pos, pos + size, size,
|
||||
<<size::integer-big-size(32), fourcc::bits-size(32), data::bits>>}
|
||||
| acc
|
||||
]
|
||||
|
||||
fix(rest, pos + size, got_moov || fourcc == "moov", got_mdat || fourcc == "mdat", acc)
|
||||
fix(rest, pos + size, got_moov || fourcc == "moov", got_mdat || fourcc == "mdat", acc)
|
||||
rescue
|
||||
_ ->
|
||||
:abort
|
||||
end
|
||||
end
|
||||
|
||||
defp fix(<<>>, _pos, _, _, acc) do
|
||||
|
|
@ -121,9 +126,15 @@ defmodule Pleroma.Helpers.QtFastStart do
|
|||
<<pos::integer-big-size(unquote(size)), rest::bits>>,
|
||||
acc
|
||||
) do
|
||||
rewrite_entries(unquote(size), offset, rest, [
|
||||
acc | <<pos + offset::integer-big-size(unquote(size))>>
|
||||
])
|
||||
rewrite_entries(
|
||||
unquote(size),
|
||||
offset,
|
||||
rest,
|
||||
acc ++
|
||||
[
|
||||
<<pos + offset::integer-big-size(unquote(size))>>
|
||||
]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,6 @@ defmodule Pleroma.HTML do
|
|||
# Scrubbers are compiled on boot so they can be configured in OTP releases
|
||||
# @on_load :compile_scrubbers
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
|
||||
def compile_scrubbers do
|
||||
dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
|
||||
|
||||
|
|
@ -67,27 +65,20 @@ defmodule Pleroma.HTML do
|
|||
end
|
||||
end
|
||||
|
||||
def extract_first_external_url_from_object(%{data: %{"content" => content}} = object)
|
||||
@spec extract_first_external_url_from_object(Pleroma.Object.t()) ::
|
||||
{:ok, String.t()} | {:error, :no_content}
|
||||
def extract_first_external_url_from_object(%{data: %{"content" => content}})
|
||||
when is_binary(content) do
|
||||
unless object.data["fake"] do
|
||||
key = "URL|#{object.id}"
|
||||
url =
|
||||
content
|
||||
|> Floki.parse_fragment!()
|
||||
|> Floki.find("a:not(.mention,.hashtag,.attachment,[rel~=\"tag\"])")
|
||||
|> Enum.take(1)
|
||||
|> Floki.attribute("href")
|
||||
|> Enum.at(0)
|
||||
|
||||
@cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
{:commit, {:ok, extract_first_external_url(content)}}
|
||||
end)
|
||||
else
|
||||
{:ok, extract_first_external_url(content)}
|
||||
end
|
||||
{:ok, url}
|
||||
end
|
||||
|
||||
def extract_first_external_url_from_object(_), do: {:error, :no_content}
|
||||
|
||||
def extract_first_external_url(content) do
|
||||
content
|
||||
|> Floki.parse_fragment!()
|
||||
|> Floki.find("a:not(.mention,.hashtag,.attachment,[rel~=\"tag\"])")
|
||||
|> Enum.take(1)
|
||||
|> Floki.attribute("href")
|
||||
|> Enum.at(0)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -106,6 +106,10 @@ defmodule Pleroma.HTTP do
|
|||
[Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.ConnectionPool]
|
||||
end
|
||||
|
||||
defp adapter_middlewares({Tesla.Adapter.Finch, _}) do
|
||||
[Tesla.Middleware.FollowRedirects]
|
||||
end
|
||||
|
||||
defp adapter_middlewares(_) do
|
||||
if Pleroma.Config.get(:env) == :test do
|
||||
# Emulate redirects in test env, which are handled by adapters in other environments
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ defmodule Pleroma.HTTP.AdapterHelper do
|
|||
require Logger
|
||||
|
||||
@type proxy ::
|
||||
{Connection.host(), pos_integer()}
|
||||
| {Connection.proxy_type(), Connection.host(), pos_integer()}
|
||||
{host(), pos_integer()}
|
||||
| {proxy_type(), host(), pos_integer()}
|
||||
|
||||
@callback options(keyword(), URI.t()) :: keyword()
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -54,12 +54,12 @@ defmodule Pleroma.HTTP.RequestBuilder do
|
|||
@doc """
|
||||
Add optional parameters to the request
|
||||
"""
|
||||
@spec add_param(Request.t(), atom(), atom(), any()) :: Request.t()
|
||||
@spec add_param(Request.t(), atom(), atom() | String.t(), any()) :: Request.t()
|
||||
def add_param(request, :query, :query, values), do: %{request | query: values}
|
||||
|
||||
def add_param(request, :body, :body, value), do: %{request | body: value}
|
||||
|
||||
def add_param(request, :body, key, value) do
|
||||
def add_param(request, :body, key, value) when is_binary(key) do
|
||||
request
|
||||
|> Map.put(:body, Multipart.new())
|
||||
|> Map.update!(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -7,16 +7,15 @@ defmodule Pleroma.Instances do
|
|||
|
||||
alias Pleroma.Instances.Instance
|
||||
|
||||
def filter_reachable(urls_or_hosts), do: Instance.filter_reachable(urls_or_hosts)
|
||||
defdelegate filter_reachable(urls_or_hosts), to: Instance
|
||||
|
||||
def reachable?(url_or_host), do: Instance.reachable?(url_or_host)
|
||||
defdelegate reachable?(url_or_host), to: Instance
|
||||
|
||||
def set_reachable(url_or_host), do: Instance.set_reachable(url_or_host)
|
||||
defdelegate set_reachable(url_or_host), to: Instance
|
||||
|
||||
def set_unreachable(url_or_host, unreachable_since \\ nil),
|
||||
do: Instance.set_unreachable(url_or_host, unreachable_since)
|
||||
defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: Instance
|
||||
|
||||
def get_consistently_unreachable, do: Instance.get_consistently_unreachable()
|
||||
defdelegate get_consistently_unreachable, to: Instance
|
||||
|
||||
def set_consistently_unreachable(url_or_host),
|
||||
do: set_unreachable(url_or_host, reachability_datetime_threshold())
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Maps do
|
||||
|
|
@ -18,4 +18,17 @@ defmodule Pleroma.Maps do
|
|||
rescue
|
||||
_ -> data
|
||||
end
|
||||
|
||||
def filter_empty_values(data) do
|
||||
# TODO: Change to Map.filter in Elixir 1.13+
|
||||
data
|
||||
|> Enum.filter(fn
|
||||
{_k, nil} -> false
|
||||
{_k, ""} -> false
|
||||
{_k, []} -> false
|
||||
{_k, %{} = v} -> Map.keys(v) != []
|
||||
{_k, _v} -> true
|
||||
end)
|
||||
|> Map.new()
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ defmodule Pleroma.MFA do
|
|||
{:ok, codes}
|
||||
else
|
||||
{:error, msg} ->
|
||||
%{error: msg}
|
||||
{:error, msg}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.MFA.TOTP do
|
|||
@doc """
|
||||
https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||
"""
|
||||
@spec provisioning_uri(String.t(), String.t(), list()) :: String.t()
|
||||
def provisioning_uri(secret, label, opts \\ []) do
|
||||
query =
|
||||
%{
|
||||
|
|
@ -27,7 +28,7 @@ defmodule Pleroma.MFA.TOTP do
|
|||
|> URI.encode_query()
|
||||
|
||||
%URI{scheme: "otpauth", host: "totp", path: "/" <> label, query: query}
|
||||
|> URI.to_string()
|
||||
|> to_string()
|
||||
end
|
||||
|
||||
defp default_period, do: Config.get(@config_ns ++ [:period])
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ defmodule Pleroma.Migrators.HashtagsTableMigrator do
|
|||
|> where([_o, hashtags_objects], is_nil(hashtags_objects.object_id))
|
||||
end
|
||||
|
||||
@spec transfer_object_hashtags(Map.t()) :: {:noop | :ok | :error, integer()}
|
||||
@spec transfer_object_hashtags(map()) :: {:noop | :ok | :error, integer()}
|
||||
defp transfer_object_hashtags(object) do
|
||||
embedded_tags = if Map.has_key?(object, :tag), do: object.tag, else: object.data["tag"]
|
||||
hashtags = Object.object_data_hashtags(%{"tag" => embedded_tags})
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
)
|
||||
|
||||
|
|
@ -188,10 +188,11 @@ defmodule Pleroma.Migrators.Support.BaseMigrator do
|
|||
end
|
||||
|
||||
defp fault_rate do
|
||||
with failures_count when is_integer(failures_count) <- failures_count() do
|
||||
with failures_count when is_integer(failures_count) <- failures_count(),
|
||||
true <- failures_count > 0 do
|
||||
failures_count / Enum.max([get_stat(:affected_count, 0), 1])
|
||||
else
|
||||
_ -> :error
|
||||
_ -> 0
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ defmodule Pleroma.ModerationLog do
|
|||
|
||||
defp prepare_log_data(attrs), do: attrs
|
||||
|
||||
@spec insert_log(log_params()) :: {:ok, ModerationLog} | {:error, any}
|
||||
@spec insert_log(log_params()) :: {:ok, ModerationLog.t()} | {:error, any}
|
||||
def insert_log(%{actor: %User{}, subject: subjects, permission: permission} = attrs) do
|
||||
data =
|
||||
attrs
|
||||
|
|
@ -248,7 +248,8 @@ defmodule Pleroma.ModerationLog do
|
|||
|> insert_log_entry_with_message()
|
||||
end
|
||||
|
||||
@spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
|
||||
@spec insert_log_entry_with_message(ModerationLog.t()) ::
|
||||
{:ok, ModerationLog.t()} | {:error, any}
|
||||
defp insert_log_entry_with_message(entry) do
|
||||
entry.data["message"]
|
||||
|> put_in(get_log_entry_message(entry))
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ defmodule Pleroma.Notification do
|
|||
where: q.seen == true,
|
||||
select: type(q.id, :string),
|
||||
limit: 1,
|
||||
order_by: [desc: :id]
|
||||
order_by: fragment("? desc nulls last", q.id)
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -177,7 +177,10 @@ defmodule Pleroma.Object do
|
|||
ap_id
|
||||
|
||||
Keyword.get(options, :fetch) ->
|
||||
Fetcher.fetch_object_from_id!(ap_id, options)
|
||||
case Fetcher.fetch_object_from_id(ap_id, options) do
|
||||
{:ok, object} -> object
|
||||
_ -> nil
|
||||
end
|
||||
|
||||
true ->
|
||||
get_cached_by_ap_id(ap_id)
|
||||
|
|
@ -239,17 +242,17 @@ defmodule Pleroma.Object do
|
|||
{:ok, _} <- invalid_object_cache(object) do
|
||||
cleanup_attachments(
|
||||
Config.get([:instance, :cleanup_attachments]),
|
||||
%{"object" => object}
|
||||
object
|
||||
)
|
||||
|
||||
{:ok, object, deleted_activity}
|
||||
end
|
||||
end
|
||||
|
||||
@spec cleanup_attachments(boolean(), %{required(:object) => map()}) ::
|
||||
@spec cleanup_attachments(boolean(), Object.t()) ::
|
||||
{:ok, Oban.Job.t() | nil}
|
||||
def cleanup_attachments(true, %{"object" => _} = params) do
|
||||
AttachmentsCleanupWorker.enqueue("cleanup_attachments", params)
|
||||
def cleanup_attachments(true, %Object{} = object) do
|
||||
AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{"object" => object})
|
||||
end
|
||||
|
||||
def cleanup_attachments(_, _), do: {:ok, nil}
|
||||
|
|
@ -328,6 +331,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
|
||||
|
|
|
|||
|
|
@ -72,20 +72,25 @@ defmodule Pleroma.Object.Fetcher do
|
|||
{:object, data, Object.normalize(activity, fetch: false)} do
|
||||
{:ok, object}
|
||||
else
|
||||
{:allowed_depth, false} ->
|
||||
{:error, "Max thread distance exceeded."}
|
||||
{:allowed_depth, false} = e ->
|
||||
log_fetch_error(id, e)
|
||||
{:error, :allowed_depth}
|
||||
|
||||
{:containment, _} ->
|
||||
{:error, "Object containment failed."}
|
||||
{:containment, reason} = e ->
|
||||
log_fetch_error(id, e)
|
||||
{:error, reason}
|
||||
|
||||
{:transmogrifier, {:error, {:reject, e}}} ->
|
||||
{:reject, e}
|
||||
{:transmogrifier, {:error, {:reject, reason}}} = e ->
|
||||
log_fetch_error(id, e)
|
||||
{:reject, reason}
|
||||
|
||||
{:transmogrifier, {:reject, e}} ->
|
||||
{:reject, e}
|
||||
{:transmogrifier, {:reject, reason}} = e ->
|
||||
log_fetch_error(id, e)
|
||||
{:reject, reason}
|
||||
|
||||
{:transmogrifier, _} = e ->
|
||||
{:error, e}
|
||||
{:transmogrifier, reason} = e ->
|
||||
log_fetch_error(id, e)
|
||||
{:error, reason}
|
||||
|
||||
{:object, data, nil} ->
|
||||
reinject_object(%Object{}, data)
|
||||
|
|
@ -96,14 +101,21 @@ defmodule Pleroma.Object.Fetcher do
|
|||
{:fetch_object, %Object{} = object} ->
|
||||
{:ok, object}
|
||||
|
||||
{:fetch, {:error, error}} ->
|
||||
{:error, error}
|
||||
{:fetch, {:error, reason}} = e ->
|
||||
log_fetch_error(id, e)
|
||||
{:error, reason}
|
||||
|
||||
e ->
|
||||
e
|
||||
log_fetch_error(id, e)
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
defp log_fetch_error(id, error) do
|
||||
Logger.metadata(object: id)
|
||||
Logger.error("Object rejected while fetching #{id} #{inspect(error)}")
|
||||
end
|
||||
|
||||
defp prepare_activity_params(data) do
|
||||
%{
|
||||
"type" => "Create",
|
||||
|
|
@ -117,26 +129,6 @@ defmodule Pleroma.Object.Fetcher do
|
|||
|> Maps.put_if_present("bcc", data["bcc"])
|
||||
end
|
||||
|
||||
def fetch_object_from_id!(id, options \\ []) do
|
||||
with {:ok, object} <- fetch_object_from_id(id, options) do
|
||||
object
|
||||
else
|
||||
{:error, %Tesla.Mock.Error{}} ->
|
||||
nil
|
||||
|
||||
{:error, "Object has been deleted"} ->
|
||||
nil
|
||||
|
||||
{:reject, reason} ->
|
||||
Logger.info("Rejected #{id} while fetching: #{inspect(reason)}")
|
||||
nil
|
||||
|
||||
e ->
|
||||
Logger.error("Error while fetching #{id}: #{inspect(e)}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp make_signature(id, date) do
|
||||
uri = URI.parse(id)
|
||||
|
||||
|
|
@ -227,8 +219,11 @@ defmodule Pleroma.Object.Fetcher do
|
|||
{:error, {:content_type, nil}}
|
||||
end
|
||||
|
||||
{:ok, %{status: code}} when code in [401, 403] ->
|
||||
{:error, :forbidden}
|
||||
|
||||
{:ok, %{status: code}} when code in [404, 410] ->
|
||||
{:error, "Object has been deleted"}
|
||||
{:error, :not_found}
|
||||
|
||||
{:error, e} ->
|
||||
{:error, e}
|
||||
|
|
|
|||
|
|
@ -61,15 +61,16 @@ defmodule Pleroma.Pagination do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
||||
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
|
||||
|
||||
def paginate(list, options, _method, _table_binding) when is_list(list) do
|
||||
@spec paginate_list(list(), keyword()) :: list()
|
||||
def paginate_list(list, options) do
|
||||
offset = options[:offset] || 0
|
||||
limit = options[:limit] || 0
|
||||
Enum.slice(list, offset, limit)
|
||||
end
|
||||
|
||||
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
||||
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
|
||||
|
||||
def paginate(query, options, :keyset, table_binding) do
|
||||
query
|
||||
|> restrict(:min_id, options, table_binding)
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ defmodule Pleroma.Password.Pbkdf2 do
|
|||
|
||||
iterations = String.to_integer(iterations)
|
||||
|
||||
digest = String.to_atom(digest)
|
||||
digest = String.to_existing_atom(digest)
|
||||
|
||||
binary_hash =
|
||||
KeyGenerator.generate(password, salt, digest: digest, iterations: iterations, length: 64)
|
||||
|
|
|
|||
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
|
||||
|
|
@ -55,12 +55,6 @@ defmodule Pleroma.ReleaseTasks do
|
|||
|
||||
{:error, term} when is_binary(term) ->
|
||||
IO.puts(:stderr, "The database for #{inspect(@repo)} couldn't be created: #{term}")
|
||||
|
||||
{:error, term} ->
|
||||
IO.puts(
|
||||
:stderr,
|
||||
"The database for #{inspect(@repo)} couldn't be created: #{inspect(term)}"
|
||||
)
|
||||
end
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ defmodule Pleroma.ReportNote do
|
|||
timestamps()
|
||||
end
|
||||
|
||||
@spec create(FlakeId.Ecto.CompatType.t(), FlakeId.Ecto.CompatType.t(), String.t()) ::
|
||||
{:ok, ReportNote.t()} | {:error, Changeset.t()}
|
||||
@spec create(Ecto.UUID.t(), Ecto.UUID.t(), String.t()) ::
|
||||
{:ok, ReportNote.t()} | {:error, Ecto.Changeset.t()}
|
||||
def create(user_id, activity_id, content) do
|
||||
attrs = %{
|
||||
user_id: user_id,
|
||||
|
|
@ -38,8 +38,8 @@ defmodule Pleroma.ReportNote do
|
|||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@spec destroy(FlakeId.Ecto.CompatType.t()) ::
|
||||
{:ok, ReportNote.t()} | {:error, Changeset.t()}
|
||||
@spec destroy(Ecto.UUID.t()) ::
|
||||
{:ok, ReportNote.t()} | {:error, Ecto.Changeset.t()}
|
||||
def destroy(id) do
|
||||
from(r in ReportNote, where: r.id == ^id)
|
||||
|> Repo.one()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.ReverseProxy do
|
|||
~w(if-unmodified-since if-none-match) ++ @range_headers
|
||||
@resp_cache_headers ~w(etag date last-modified)
|
||||
@keep_resp_headers @resp_cache_headers ++
|
||||
~w(content-length content-type content-disposition content-encoding) ++
|
||||
~w(content-type content-disposition content-encoding) ++
|
||||
~w(content-range accept-ranges vary)
|
||||
@default_cache_control_header "public, max-age=1209600"
|
||||
@valid_resp_codes [200, 206, 304]
|
||||
|
|
@ -81,16 +81,16 @@ defmodule Pleroma.ReverseProxy do
|
|||
import Plug.Conn
|
||||
|
||||
@type option() ::
|
||||
{:max_read_duration, :timer.time() | :infinity}
|
||||
{:max_read_duration, non_neg_integer() | :infinity}
|
||||
| {:max_body_length, non_neg_integer() | :infinity}
|
||||
| {:failed_request_ttl, :timer.time() | :infinity}
|
||||
| {:http, []}
|
||||
| {:failed_request_ttl, non_neg_integer() | :infinity}
|
||||
| {:http, keyword()}
|
||||
| {:req_headers, [{String.t(), String.t()}]}
|
||||
| {:resp_headers, [{String.t(), String.t()}]}
|
||||
| {:inline_content_types, boolean() | [String.t()]}
|
||||
| {:inline_content_types, boolean() | list(String.t())}
|
||||
| {:redirect_on_failure, boolean()}
|
||||
|
||||
@spec call(Plug.Conn.t(), url :: String.t(), [option()]) :: Plug.Conn.t()
|
||||
@spec call(Plug.Conn.t(), String.t(), list(option())) :: Plug.Conn.t()
|
||||
def call(_conn, _url, _opts \\ [])
|
||||
|
||||
def call(conn = %{method: method}, url, opts) when method in @methods do
|
||||
|
|
@ -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)}"
|
||||
)
|
||||
|
||||
|
|
@ -388,8 +388,6 @@ defmodule Pleroma.ReverseProxy do
|
|||
|
||||
defp body_size_constraint(_, _), do: :ok
|
||||
|
||||
defp check_read_duration(nil = _duration, max), do: check_read_duration(@max_read_duration, max)
|
||||
|
||||
defp check_read_duration(duration, max)
|
||||
when is_integer(duration) and is_integer(max) and max > 0 do
|
||||
if duration > max do
|
||||
|
|
@ -407,10 +405,6 @@ defmodule Pleroma.ReverseProxy do
|
|||
{:ok, previous_duration + duration}
|
||||
end
|
||||
|
||||
defp increase_read_duration(_) do
|
||||
{:ok, :no_duration_limit, :no_duration_limit}
|
||||
end
|
||||
|
||||
defp client, do: Pleroma.ReverseProxy.Client.Wrapper
|
||||
|
||||
defp track_failed_url(url, error, opts) do
|
||||
|
|
|
|||
|
|
@ -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 filtering when removing
|
||||
from index.
|
||||
"""
|
||||
@callback remove_from_index(object :: Pleroma.Object.t()) :: :ok | {:error, any()}
|
||||
end
|
||||
|
|
@ -27,7 +27,7 @@ defmodule Pleroma.Signature do
|
|||
|
||||
_ ->
|
||||
case Pleroma.Web.WebFinger.finger(maybe_ap_id) do
|
||||
%{"ap_id" => ap_id} -> {:ok, ap_id}
|
||||
{:ok, %{"ap_id" => ap_id}} -> {:ok, ap_id}
|
||||
_ -> {:error, maybe_ap_id}
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
_,
|
||||
_
|
||||
) do
|
||||
Logger.error(fn ->
|
||||
Logger.debug(fn ->
|
||||
"Connection pool had to refuse opening a connection to #{key} due to connection limit exhaustion"
|
||||
end)
|
||||
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
|
||||
|
|
@ -81,7 +81,7 @@ defmodule Pleroma.Telemetry.Logger do
|
|||
%{key: key, protocol: :http},
|
||||
_
|
||||
) do
|
||||
Logger.info(fn ->
|
||||
Logger.debug(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
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ defmodule Pleroma.Upload do
|
|||
|
||||
"""
|
||||
alias Ecto.UUID
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
require Logger
|
||||
|
|
@ -52,6 +51,7 @@ defmodule Pleroma.Upload do
|
|||
| {:size_limit, nil | non_neg_integer()}
|
||||
| {:uploader, module()}
|
||||
| {:filters, [module()]}
|
||||
| {:actor, String.t()}
|
||||
|
||||
@type t :: %__MODULE__{
|
||||
id: String.t(),
|
||||
|
|
@ -76,6 +76,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
|
||||
|
|
@ -85,7 +87,7 @@ defmodule Pleroma.Upload do
|
|||
end
|
||||
end
|
||||
|
||||
@spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()}
|
||||
@spec store(source, options :: [option()]) :: {:ok, map()} | {:error, any()}
|
||||
@doc "Store a file. If using a `Plug.Upload{}` as the source, be sure to use `Majic.Plug` to ensure its content_type and filename is correct."
|
||||
def store(upload, opts \\ []) do
|
||||
opts = get_opts(opts)
|
||||
|
|
@ -174,7 +176,7 @@ defmodule Pleroma.Upload do
|
|||
defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do
|
||||
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
|
||||
data = Base.decode64!(parsed["data"], ignore: :whitespace)
|
||||
hash = Base.encode16(:crypto.hash(:sha256, data), lower: true)
|
||||
hash = Base.encode16(:crypto.hash(:sha256, data), case: :upper)
|
||||
|
||||
with :ok <- check_binary_size(data, opts.size_limit),
|
||||
tmp_path <- tempfile_for_image(data),
|
||||
|
|
@ -244,18 +246,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
|
||||
|
|
|
|||
|
|
@ -5,8 +5,6 @@
|
|||
defmodule Pleroma.Uploaders.Uploader do
|
||||
import Pleroma.Web.Gettext
|
||||
|
||||
@mix_env Mix.env()
|
||||
|
||||
@moduledoc """
|
||||
Defines the contract to put and get an uploaded file to any backend.
|
||||
"""
|
||||
|
|
@ -40,7 +38,7 @@ defmodule Pleroma.Uploaders.Uploader do
|
|||
|
||||
@callback delete_file(file :: String.t()) :: :ok | {:error, String.t()}
|
||||
|
||||
@callback http_callback(Plug.Conn.t(), Map.t()) ::
|
||||
@callback http_callback(Plug.Conn.t(), map()) ::
|
||||
{:ok, Plug.Conn.t()}
|
||||
| {:ok, Plug.Conn.t(), file_spec()}
|
||||
| {:error, Plug.Conn.t(), String.t()}
|
||||
|
|
@ -75,10 +73,5 @@ defmodule Pleroma.Uploaders.Uploader do
|
|||
end
|
||||
end
|
||||
|
||||
defp callback_timeout do
|
||||
case @mix_env do
|
||||
:test -> 1_000
|
||||
_ -> 30_000
|
||||
end
|
||||
end
|
||||
defp callback_timeout, do: Application.get_env(:pleroma, __MODULE__)[:timeout]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ defmodule Pleroma.User do
|
|||
alias Pleroma.Workers.BackgroundWorker
|
||||
|
||||
require Logger
|
||||
require Pleroma.Constants
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
@type account_status ::
|
||||
|
|
@ -580,7 +581,7 @@ defmodule Pleroma.User do
|
|||
|> validate_format(:nickname, local_nickname_regex())
|
||||
|> validate_length(:bio, max: bio_limit)
|
||||
|> validate_length(:name, min: 1, max: name_limit)
|
||||
|> validate_inclusion(:actor_type, ["Person", "Service"])
|
||||
|> validate_inclusion(:actor_type, Pleroma.Constants.allowed_user_actor_types())
|
||||
|> put_fields()
|
||||
|> put_emoji()
|
||||
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|
||||
|
|
@ -686,7 +687,7 @@ defmodule Pleroma.User do
|
|||
|> validate_inclusion(:actor_type, ["Person", "Service"])
|
||||
end
|
||||
|
||||
@spec update_as_admin(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t()}
|
||||
@spec update_as_admin(User.t(), map()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
def update_as_admin(user, params) do
|
||||
params = Map.put(params, "password_confirmation", params["password"])
|
||||
changeset = update_as_admin_changeset(user, params)
|
||||
|
|
@ -707,7 +708,7 @@ defmodule Pleroma.User do
|
|||
|> put_change(:password_reset_pending, false)
|
||||
end
|
||||
|
||||
@spec reset_password(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t()}
|
||||
@spec reset_password(User.t(), map()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
def reset_password(%User{} = user, params) do
|
||||
reset_password(user, user, params)
|
||||
end
|
||||
|
|
@ -1025,7 +1026,7 @@ defmodule Pleroma.User do
|
|||
|
||||
def maybe_send_confirmation_email(_), do: {:ok, :noop}
|
||||
|
||||
@spec send_confirmation_email(Uset.t()) :: User.t()
|
||||
@spec send_confirmation_email(User.t()) :: User.t()
|
||||
def send_confirmation_email(%User{} = user) do
|
||||
user
|
||||
|> Pleroma.Emails.UserEmail.account_confirmation_email()
|
||||
|
|
@ -1062,7 +1063,8 @@ defmodule Pleroma.User do
|
|||
|
||||
def needs_update?(_), do: true
|
||||
|
||||
@spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}
|
||||
@spec maybe_direct_follow(User.t(), User.t()) ::
|
||||
{:ok, User.t(), User.t()} | {:error, String.t()}
|
||||
|
||||
# "Locked" (self-locked) users demand explicit authorization of follow requests
|
||||
def maybe_direct_follow(%User{} = follower, %User{local: true, is_locked: true} = followed) do
|
||||
|
|
@ -1579,7 +1581,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}"
|
||||
)
|
||||
|
||||
|
|
@ -1801,14 +1803,17 @@ defmodule Pleroma.User do
|
|||
BackgroundWorker.enqueue("user_activation", %{"user_id" => user.id, "status" => status})
|
||||
end
|
||||
|
||||
@spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
|
||||
@spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
def set_activation(users, status) when is_list(users) do
|
||||
Repo.transaction(fn ->
|
||||
for user <- users, do: set_activation(user, status)
|
||||
for user <- users do
|
||||
{:ok, user} = set_activation(user, status)
|
||||
user
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
@spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
|
||||
@spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
def set_activation(%User{} = user, status) do
|
||||
with {:ok, user} <- set_activation_status(user, status) do
|
||||
user
|
||||
|
|
@ -1886,7 +1891,7 @@ defmodule Pleroma.User do
|
|||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
@spec purge_user_changeset(User.t()) :: Changeset.t()
|
||||
@spec purge_user_changeset(User.t()) :: Ecto.Changeset.t()
|
||||
def purge_user_changeset(user) do
|
||||
# "Right to be forgotten"
|
||||
# https://gdpr.eu/right-to-be-forgotten/
|
||||
|
|
@ -2192,7 +2197,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
|
||||
|
|
@ -2308,7 +2313,7 @@ defmodule Pleroma.User do
|
|||
if String.contains?(user.nickname, "@") do
|
||||
user.nickname
|
||||
else
|
||||
%{host: host} = URI.parse(user.ap_id)
|
||||
host = Pleroma.Web.WebFinger.host()
|
||||
user.nickname <> "@" <> host
|
||||
end
|
||||
end
|
||||
|
|
@ -2414,7 +2419,7 @@ defmodule Pleroma.User do
|
|||
updated_user
|
||||
end
|
||||
|
||||
@spec set_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
|
||||
@spec set_confirmation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
|
||||
def set_confirmation(%User{} = user, bool) do
|
||||
user
|
||||
|> confirmation_changeset(set_confirmation: bool)
|
||||
|
|
@ -2458,9 +2463,9 @@ defmodule Pleroma.User do
|
|||
|
||||
defp put_password_hash(changeset), do: changeset
|
||||
|
||||
def is_internal_user?(%User{nickname: nil}), do: true
|
||||
def is_internal_user?(%User{local: true, nickname: "internal." <> _}), do: true
|
||||
def is_internal_user?(_), do: false
|
||||
def internal?(%User{nickname: nil}), do: true
|
||||
def internal?(%User{local: true, nickname: "internal." <> _}), do: true
|
||||
def internal?(_), do: false
|
||||
|
||||
# A hack because user delete activities have a fake id for whatever reason
|
||||
# TODO: Get rid of this
|
||||
|
|
@ -2592,7 +2597,7 @@ defmodule Pleroma.User do
|
|||
|> update_and_set_cache()
|
||||
end
|
||||
|
||||
@spec confirmation_changeset(User.t(), keyword()) :: Changeset.t()
|
||||
@spec confirmation_changeset(User.t(), keyword()) :: Ecto.Changeset.t()
|
||||
def confirmation_changeset(user, set_confirmation: confirmed?) do
|
||||
params =
|
||||
if confirmed? do
|
||||
|
|
@ -2610,9 +2615,9 @@ defmodule Pleroma.User do
|
|||
cast(user, params, [:is_confirmed, :confirmation_token])
|
||||
end
|
||||
|
||||
@spec approval_changeset(User.t(), keyword()) :: Changeset.t()
|
||||
def approval_changeset(user, set_approval: approved?) do
|
||||
cast(user, %{is_approved: approved?}, [:is_approved])
|
||||
@spec approval_changeset(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()
|
||||
def approval_changeset(changeset, set_approval: approved?) do
|
||||
cast(changeset, %{is_approved: approved?}, [:is_approved])
|
||||
end
|
||||
|
||||
@spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()}
|
||||
|
|
@ -2738,6 +2743,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)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ defmodule Pleroma.User.Backup do
|
|||
alias Pleroma.Web.ActivityPub.UserView
|
||||
alias Pleroma.Workers.BackupWorker
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "backups" do
|
||||
field(:content_type, :string)
|
||||
field(:file_name, :string)
|
||||
|
|
@ -35,6 +37,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 +128,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 +139,7 @@ defmodule Pleroma.User.Backup do
|
|||
task =
|
||||
Task.Supervisor.async_nolink(
|
||||
Pleroma.TaskSupervisor,
|
||||
__MODULE__,
|
||||
processor_module,
|
||||
:do_process,
|
||||
[backup, current_pid]
|
||||
)
|
||||
|
|
@ -140,25 +147,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} ->
|
||||
|
|
@ -207,6 +197,7 @@ defmodule Pleroma.User.Backup do
|
|||
end
|
||||
|
||||
@files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json']
|
||||
@spec export(Pleroma.User.Backup.t(), pid()) :: {:ok, String.t()} | :error
|
||||
def export(%__MODULE__{} = backup, caller_pid) do
|
||||
backup = Repo.preload(backup, :user)
|
||||
dir = backup_tempdir(backup)
|
||||
|
|
@ -216,9 +207,11 @@ defmodule Pleroma.User.Backup do
|
|||
:ok <- statuses(dir, backup.user, caller_pid),
|
||||
:ok <- likes(dir, backup.user, caller_pid),
|
||||
:ok <- bookmarks(dir, backup.user, caller_pid),
|
||||
{:ok, zip_path} <- :zip.create(String.to_charlist(dir <> ".zip"), @files, cwd: dir),
|
||||
{:ok, zip_path} <- :zip.create(backup.file_name, @files, cwd: dir),
|
||||
{:ok, _} <- File.rm_rf(dir) do
|
||||
{:ok, to_string(zip_path)}
|
||||
{:ok, zip_path}
|
||||
else
|
||||
_ -> :error
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -305,7 +298,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 +358,37 @@ 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()
|
||||
else
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ defmodule Pleroma.User.Query do
|
|||
- pass non empty string
|
||||
- e.g. Pleroma.User.Query.build(%{email: "email@example.com"})
|
||||
- *contains criteria*
|
||||
- add field to @containns_criteria list
|
||||
- add field to @contains_criteria list
|
||||
- pass values list
|
||||
- e.g. Pleroma.User.Query.build(%{ap_id: ["http://ap_id1", "http://ap_id2"]})
|
||||
"""
|
||||
|
|
@ -71,7 +71,7 @@ defmodule Pleroma.User.Query do
|
|||
@equal_criteria [:email]
|
||||
@contains_criteria [:ap_id, :nickname]
|
||||
|
||||
@spec build(Query.t(), criteria()) :: Query.t()
|
||||
@spec build(Ecto.Query.t(), criteria()) :: Ecto.Query.t()
|
||||
def build(query \\ base_query(), criteria) do
|
||||
prepare_query(query, criteria)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ defmodule Pleroma.UserInviteToken do
|
|||
end
|
||||
|
||||
@spec update_invite(UserInviteToken.t(), map()) ::
|
||||
{:ok, UserInviteToken.t()} | {:error, Changeset.t()}
|
||||
{:ok, UserInviteToken.t()} | {:error, Ecto.Changeset.t()}
|
||||
def update_invite(invite, changes) do
|
||||
change(invite, changes) |> Repo.update()
|
||||
end
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ defmodule Pleroma.UserRelationship do
|
|||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
|
||||
@type t :: %__MODULE__{}
|
||||
|
||||
schema "user_relationships" do
|
||||
belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
|
||||
belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -74,28 +74,39 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
defp check_remote_limit(_), do: true
|
||||
|
||||
def increase_note_count_if_public(actor, object) do
|
||||
if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
|
||||
if public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
|
||||
end
|
||||
|
||||
def decrease_note_count_if_public(actor, object) do
|
||||
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
|
||||
if public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
|
||||
end
|
||||
|
||||
def update_last_status_at_if_public(actor, object) do
|
||||
if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor}
|
||||
if public?(object), do: User.update_last_status_at(actor), else: {:ok, actor}
|
||||
end
|
||||
|
||||
defp increase_replies_count_if_reply(%{
|
||||
"object" => %{"inReplyTo" => reply_ap_id} = object,
|
||||
"type" => "Create"
|
||||
}) do
|
||||
if is_public?(object) do
|
||||
if public?(object) do
|
||||
Object.increase_replies_count(reply_ap_id)
|
||||
end
|
||||
end
|
||||
|
||||
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 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,11 +313,13 @@ 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),
|
||||
_ <- notify_and_stream(activity),
|
||||
:ok <- maybe_schedule_poll_notifications(activity),
|
||||
:ok <- maybe_handle_group_posts(activity),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
|
|
@ -483,7 +499,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
end
|
||||
|
||||
@spec fetch_latest_direct_activity_id_for_context(String.t(), keyword() | map()) ::
|
||||
FlakeId.Ecto.CompatType.t() | nil
|
||||
Ecto.UUID.t() | nil
|
||||
def fetch_latest_direct_activity_id_for_context(context, opts \\ %{}) do
|
||||
context
|
||||
|> fetch_activities_for_context_query(Map.merge(%{skip_preload: true}, opts))
|
||||
|
|
@ -1237,6 +1253,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 +1423,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)
|
||||
|
|
@ -1673,9 +1698,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
Fetcher.fetch_and_contain_remote_object_from_id(first) do
|
||||
{:ok, false}
|
||||
else
|
||||
{:error, {:ok, %{status: code}}} when code in [401, 403] -> {:ok, true}
|
||||
{:error, _} = e -> e
|
||||
e -> {:error, e}
|
||||
{:error, _} -> {:ok, true}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
This module encodes our addressing policies and general shape of our objects.
|
||||
"""
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Emoji
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.User
|
||||
|
|
@ -131,7 +132,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
def emoji_react(actor, object, emoji) do
|
||||
with {:ok, data, meta} <- object_action(actor, object) do
|
||||
data =
|
||||
if Emoji.is_unicode_emoji?(emoji) do
|
||||
if Emoji.unicode?(emoji) do
|
||||
unicode_emoji_react(object, data, emoji)
|
||||
else
|
||||
custom_emoji_react(object, data, emoji)
|
||||
|
|
@ -347,7 +348,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
actor.ap_id == Relay.ap_id() ->
|
||||
[actor.follower_address]
|
||||
|
||||
public? and Visibility.is_local_public?(object) ->
|
||||
public? and Visibility.local_public?(object) ->
|
||||
[actor.follower_address, object.data["actor"], Utils.as_local_public()]
|
||||
|
||||
public? ->
|
||||
|
|
@ -375,7 +376,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
|||
|
||||
# Address the actor of the object, and our actor's follower collection if the post is public.
|
||||
to =
|
||||
if Visibility.is_public?(object) do
|
||||
if Visibility.public?(object) do
|
||||
[actor.follower_address, object.data["actor"]]
|
||||
else
|
||||
[object.data["actor"]]
|
||||
|
|
|
|||
|
|
@ -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.ActivityPub.MRF do
|
||||
|
|
@ -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()
|
||||
|
|
@ -137,7 +139,16 @@ defmodule Pleroma.Web.ActivityPub.MRF do
|
|||
|
||||
@spec subdomains_regex([String.t()]) :: [Regex.t()]
|
||||
def subdomains_regex(domains) when is_list(domains) do
|
||||
for domain <- domains, do: ~r(^#{String.replace(domain, "*.", "(.*\\.)*")}$)i
|
||||
for domain <- domains do
|
||||
try do
|
||||
target = String.replace(domain, "*.", "(.*\\.)*")
|
||||
~r<^#{target}$>i
|
||||
rescue
|
||||
e ->
|
||||
Logger.error("MRF: Invalid subdomain Regex: #{domain}")
|
||||
reraise e, __STACKTRACE__
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@spec subdomain_match?([Regex.t()], String.t()) :: boolean()
|
||||
|
|
@ -188,6 +199,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 +212,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)}"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -56,8 +56,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy do
|
|||
nick_score + name_score + actor_type_score
|
||||
end
|
||||
|
||||
defp determine_if_followbot(_), do: 0.0
|
||||
|
||||
defp bot_allowed?(%{"object" => target}, bot_actor) do
|
||||
%User{} = user = normalize_by_ap_id(target)
|
||||
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
|
|||
alias Pleroma.Object
|
||||
|
||||
@moduledoc """
|
||||
Reject, TWKN-remove or Set-Sensitive messsages with specific hashtags (without the leading #)
|
||||
Reject, TWKN-remove or Set-Sensitive messages with specific hashtags (without the leading #)
|
||||
|
||||
Note: This MRF Policy is always enabled, if you want to disable it you have to set empty lists.
|
||||
"""
|
||||
|
|
@ -84,7 +84,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
|
|||
if hashtags != [] do
|
||||
with {:ok, message} <- check_reject(message, hashtags),
|
||||
{:ok, message} <-
|
||||
(if "type" == "Create" do
|
||||
(if type == "Create" do
|
||||
check_ftl_removal(message, hashtags)
|
||||
else
|
||||
{:ok, message}
|
||||
|
|
|
|||
|
|
@ -62,7 +62,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
|
|||
key: :mrf_inline_quote,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy",
|
||||
label: "MRF Inline Quote Policy",
|
||||
type: :group,
|
||||
description: "Force quote url to appear in post content.",
|
||||
children: [
|
||||
%{
|
||||
|
|
|
|||
|
|
@ -10,15 +10,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
|||
@moduledoc "Reject or Word-Replace messages with a keyword or regex"
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
defp string_matches?(string, _) when not is_binary(string) do
|
||||
false
|
||||
end
|
||||
|
||||
defp string_matches?(string, pattern) when is_binary(pattern) do
|
||||
String.contains?(string, pattern)
|
||||
end
|
||||
|
||||
defp string_matches?(string, pattern) do
|
||||
defp string_matches?(string, %Regex{} = pattern) do
|
||||
String.match?(string, pattern)
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
|
|||
|
||||
@impl true
|
||||
def filter(%{"actor" => actor} = object) do
|
||||
with true <- is_local?(actor),
|
||||
true <- is_eligible_type?(object),
|
||||
true <- is_note?(object),
|
||||
with true <- local?(actor),
|
||||
true <- eligible_type?(object),
|
||||
true <- note?(object),
|
||||
false <- has_attachment?(object),
|
||||
true <- only_mentions?(object) do
|
||||
{:reject, "[NoEmptyPolicy]"}
|
||||
|
|
@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
|
|||
|
||||
def filter(object), do: {:ok, object}
|
||||
|
||||
defp is_local?(actor) do
|
||||
defp local?(actor) do
|
||||
if actor |> String.starts_with?("#{Endpoint.url()}") do
|
||||
true
|
||||
else
|
||||
|
|
@ -59,11 +59,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
|
|||
|
||||
defp only_mentions?(_), do: false
|
||||
|
||||
defp is_note?(%{"object" => %{"type" => "Note"}}), do: true
|
||||
defp is_note?(_), do: false
|
||||
defp note?(%{"object" => %{"type" => "Note"}}), do: true
|
||||
defp note?(_), do: false
|
||||
|
||||
defp is_eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true
|
||||
defp is_eligible_type?(_), do: false
|
||||
defp eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true
|
||||
defp eligible_type?(_), do: false
|
||||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.Policy do
|
||||
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||
@callback describe() :: {:ok | :error, Map.t()}
|
||||
@callback filter(map()) :: {:ok | :reject, map()}
|
||||
@callback describe() :: {:ok | :error, map()}
|
||||
@callback config_description() :: %{
|
||||
optional(:children) => [map()],
|
||||
key: atom(),
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy do
|
|||
tags = object["tag"] || []
|
||||
|
||||
if Enum.any?(tags, fn tag ->
|
||||
CommonFixes.is_object_link_tag(tag) and tag["href"] == quote_url
|
||||
CommonFixes.object_link_tag?(tag) and tag["href"] == quote_url
|
||||
end) do
|
||||
object
|
||||
else
|
||||
|
|
|
|||
|
|
@ -34,14 +34,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
|
|||
|> Path.basename()
|
||||
|> Path.extname()
|
||||
|
||||
file_path = Path.join(emoji_dir_path, shortcode <> (extension || ".png"))
|
||||
extension = if extension == "", do: ".png", else: extension
|
||||
|
||||
shortcode = Path.basename(shortcode)
|
||||
file_path = Path.join(emoji_dir_path, shortcode <> extension)
|
||||
|
||||
case File.write(file_path, response.body) do
|
||||
:ok ->
|
||||
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 +56,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
|
||||
|
|
@ -76,6 +79,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.StealEmojiPolicy do
|
|||
new_emojis =
|
||||
foreign_emojis
|
||||
|> Enum.reject(fn {shortcode, _url} -> shortcode in installed_emoji end)
|
||||
|> Enum.reject(fn {shortcode, _url} -> String.contains?(shortcode, ["/", "\\"]) end)
|
||||
|> Enum.filter(fn {shortcode, _url} ->
|
||||
reject_emoji? =
|
||||
[:mrf_steal_emoji, :rejected_shortcodes]
|
||||
|
|
|
|||
|
|
@ -173,6 +173,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
|
||||
{:object_validation, e} ->
|
||||
e
|
||||
|
||||
{:error, %Ecto.Changeset{} = e} ->
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
|
|||
object when is_binary(object) <- get_field(cng, :object),
|
||||
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
||||
%Object{} = object <- Object.get_cached_by_ap_id(object),
|
||||
false <- Visibility.is_public?(object) do
|
||||
false <- Visibility.public?(object) do
|
||||
same_actor = object.data["actor"] == actor.ap_id
|
||||
recipients = get_field(cng, :to) ++ get_field(cng, :cc)
|
||||
local_public = Utils.as_local_public()
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator do
|
|||
|> Map.put("attachment", attachment)
|
||||
end
|
||||
|
||||
def fix_attachment(%{"attachment" => attachment} = data) when attachment == [] do
|
||||
data
|
||||
|> Map.drop(["attachment"])
|
||||
end
|
||||
|
||||
def fix_attachment(data), do: data
|
||||
|
||||
def changeset(struct, data) do
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Maps
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Containment
|
||||
alias Pleroma.User
|
||||
|
|
@ -24,6 +25,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
|||
end
|
||||
|
||||
def fix_object_defaults(data) do
|
||||
data = Maps.filter_empty_values(data)
|
||||
|
||||
context =
|
||||
Utils.maybe_create_context(
|
||||
data["context"] || data["conversation"] || data["inReplyTo"] || data["id"]
|
||||
|
|
@ -99,7 +102,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
|||
end
|
||||
|
||||
def fix_quote_url(%{"tag" => [_ | _] = tags} = data) do
|
||||
tag = Enum.find(tags, &is_object_link_tag/1)
|
||||
tag = Enum.find(tags, &object_link_tag?/1)
|
||||
|
||||
if not is_nil(tag) do
|
||||
data
|
||||
|
|
@ -112,7 +115,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
|||
def fix_quote_url(data), do: data
|
||||
|
||||
# https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md
|
||||
def is_object_link_tag(%{
|
||||
def object_link_tag?(%{
|
||||
"type" => "Link",
|
||||
"mediaType" => media_type,
|
||||
"href" => href
|
||||
|
|
@ -121,5 +124,5 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
|||
true
|
||||
end
|
||||
|
||||
def is_object_link_tag(_), do: false
|
||||
def object_link_tag?(_), do: false
|
||||
end
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue