summaryrefslogtreecommitdiff
path: root/lib/pleroma
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pleroma')
-rw-r--r--lib/pleroma/activity.ex2
-rw-r--r--lib/pleroma/activity/queries.ex2
-rw-r--r--lib/pleroma/application.ex134
-rw-r--r--lib/pleroma/application_requirements.ex13
-rw-r--r--lib/pleroma/bookmark.ex10
-rw-r--r--lib/pleroma/captcha/kocaptcha.ex2
-rw-r--r--lib/pleroma/chat.ex12
-rw-r--r--lib/pleroma/config/deprecation_warnings.ex26
-rw-r--r--lib/pleroma/config/getting.ex7
-rw-r--r--lib/pleroma/config/oban.ex2
-rw-r--r--lib/pleroma/config/transfer_task.ex9
-rw-r--r--lib/pleroma/config_db.ex6
-rw-r--r--lib/pleroma/constants.ex8
-rw-r--r--lib/pleroma/docs/generator.ex4
-rw-r--r--lib/pleroma/docs/json.ex2
-rw-r--r--lib/pleroma/ecto_type/activity_pub/object_validators/bare_uri.ex10
-rw-r--r--lib/pleroma/emoji.ex2
-rw-r--r--lib/pleroma/emoji/loader.ex2
-rw-r--r--lib/pleroma/emoji/pack.ex12
-rw-r--r--lib/pleroma/gun/conn.ex6
-rw-r--r--lib/pleroma/helpers/media_helper.ex57
-rw-r--r--lib/pleroma/helpers/qt_fast_start.ex25
-rw-r--r--lib/pleroma/http.ex4
-rw-r--r--lib/pleroma/http/adapter_helper.ex8
-rw-r--r--lib/pleroma/http/web_push.ex6
-rw-r--r--lib/pleroma/instances.ex11
-rw-r--r--lib/pleroma/instances/instance.ex16
-rw-r--r--lib/pleroma/maintenance.ex2
-rw-r--r--lib/pleroma/migrators/hashtags_table_migrator.ex2
-rw-r--r--lib/pleroma/migrators/support/base_migrator.ex6
-rw-r--r--lib/pleroma/object.ex51
-rw-r--r--lib/pleroma/object/fetcher.ex63
-rw-r--r--lib/pleroma/prom_ex.ex49
-rw-r--r--lib/pleroma/release_tasks.ex6
-rw-r--r--lib/pleroma/repo.ex2
-rw-r--r--lib/pleroma/report_note.ex8
-rw-r--r--lib/pleroma/reverse_proxy.ex6
-rw-r--r--lib/pleroma/scheduled_activity.ex9
-rw-r--r--lib/pleroma/search.ex17
-rw-r--r--lib/pleroma/search/database_search.ex (renamed from lib/pleroma/activity/search.ex)22
-rw-r--r--lib/pleroma/search/meilisearch.ex181
-rw-r--r--lib/pleroma/search/search_backend.ex24
-rw-r--r--lib/pleroma/telemetry/logger.ex2
-rw-r--r--lib/pleroma/upload.ex17
-rw-r--r--lib/pleroma/upload/filter/analyze_metadata.ex44
-rw-r--r--lib/pleroma/upload/filter/exiftool/read_description.ex2
-rw-r--r--lib/pleroma/uploaders/s3.ex15
-rw-r--r--lib/pleroma/uploaders/uploader.ex2
-rw-r--r--lib/pleroma/user.ex32
-rw-r--r--lib/pleroma/user/backup.ex62
-rw-r--r--lib/pleroma/user/query.ex2
-rw-r--r--lib/pleroma/user_invite_token.ex2
-rw-r--r--lib/pleroma/web.ex2
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex31
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex18
-rw-r--r--lib/pleroma/web/activity_pub/mrf.ex19
-rw-r--r--lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/policy.ex4
-rw-r--r--lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex8
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex5
-rw-r--r--lib/pleroma/web/activity_pub/object_validators/common_fields.ex1
-rw-r--r--lib/pleroma/web/activity_pub/publisher.ex171
-rw-r--r--lib/pleroma/web/activity_pub/side_effects.ex19
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex14
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex32
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex1
-rw-r--r--lib/pleroma/web/api_spec.ex8
-rw-r--r--lib/pleroma/web/api_spec/operations/account_operation.ex2
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex4
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex8
-rw-r--r--lib/pleroma/web/api_spec/operations/admin/report_operation.ex10
-rw-r--r--lib/pleroma/web/api_spec/operations/instance_operation.ex174
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex8
-rw-r--r--lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex45
-rw-r--r--lib/pleroma/web/api_spec/operations/status_operation.ex4
-rw-r--r--lib/pleroma/web/api_spec/operations/twitter_util_operation.ex6
-rw-r--r--lib/pleroma/web/api_spec/schemas/attachment.ex2
-rw-r--r--lib/pleroma/web/api_spec/schemas/status.ex7
-rw-r--r--lib/pleroma/web/auth/authenticator.ex2
-rw-r--r--lib/pleroma/web/common_api.ex2
-rw-r--r--lib/pleroma/web/common_api/activity_draft.ex2
-rw-r--r--lib/pleroma/web/common_api/utils.ex4
-rw-r--r--lib/pleroma/web/endpoint.ex56
-rw-r--r--lib/pleroma/web/fallback/redirect_controller.ex27
-rw-r--r--lib/pleroma/web/federator.ex19
-rw-r--r--lib/pleroma/web/federator/publisher.ex109
-rw-r--r--lib/pleroma/web/feed/feed_view.ex4
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/account_controller.ex2
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/instance_controller.ex7
-rw-r--r--lib/pleroma/web/mastodon_api/controllers/search_controller.ex3
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex24
-rw-r--r--lib/pleroma/web/mastodon_api/views/instance_view.ex128
-rw-r--r--lib/pleroma/web/mastodon_api/views/status_view.ex30
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo.ex2
-rw-r--r--lib/pleroma/web/o_auth/authorization.ex8
-rw-r--r--lib/pleroma/web/o_auth/o_auth_controller.ex2
-rw-r--r--lib/pleroma/web/o_auth/token.ex5
-rw-r--r--lib/pleroma/web/o_auth/token/query.ex4
-rw-r--r--lib/pleroma/web/pleroma_api/controllers/status_controller.ex66
-rw-r--r--lib/pleroma/web/pleroma_api/views/scrobble_view.ex1
-rw-r--r--lib/pleroma/web/plugs/cache.ex2
-rw-r--r--lib/pleroma/web/plugs/http_security_plug.ex2
-rw-r--r--lib/pleroma/web/plugs/rate_limiter.ex2
-rw-r--r--lib/pleroma/web/plugs/uploaded_media.ex2
-rw-r--r--lib/pleroma/web/push.ex2
-rw-r--r--lib/pleroma/web/push/impl.ex2
-rw-r--r--lib/pleroma/web/rich_media/helpers.ex13
-rw-r--r--lib/pleroma/web/rich_media/parser.ex4
-rw-r--r--lib/pleroma/web/rich_media/parser/ttl.ex2
-rw-r--r--lib/pleroma/web/router.ex21
-rw-r--r--lib/pleroma/web/streamer.ex6
-rw-r--r--lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex8
-rw-r--r--lib/pleroma/web/templates/o_auth/mfa/totp.html.eex8
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/register.html.eex8
-rw-r--r--lib/pleroma/web/templates/o_auth/o_auth/show.html.eex8
-rw-r--r--lib/pleroma/web/twitter_api/controllers/password_controller.ex2
-rw-r--r--lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex7
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex9
-rw-r--r--lib/pleroma/web/web_finger.ex10
-rw-r--r--lib/pleroma/web/web_finger/web_finger_controller.ex2
-rw-r--r--lib/pleroma/workers/cron/digest_emails_worker.ex2
-rw-r--r--lib/pleroma/workers/cron/new_users_digest_worker.ex2
-rw-r--r--lib/pleroma/workers/publisher_worker.ex4
-rw-r--r--lib/pleroma/workers/receiver_worker.ex38
-rw-r--r--lib/pleroma/workers/remote_fetcher_worker.ex17
-rw-r--r--lib/pleroma/workers/search_indexing_worker.ex23
128 files changed, 1629 insertions, 719 deletions
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 3556aaf9e..8a512dc57 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -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
diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex
index 81c44ac05..d770b9ff3 100644
--- a/lib/pleroma/activity/queries.ex
+++ b/lib/pleroma/activity/queries.ex
@@ -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
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index e68a3c57e..de668052f 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -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, [])
diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex
index 44b1c1705..819245481 100644
--- a/lib/pleroma/application_requirements.ex
+++ b/lib/pleroma/application_requirements.ex
@@ -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
diff --git a/lib/pleroma/bookmark.ex b/lib/pleroma/bookmark.ex
index 187749e86..b83d72446 100644
--- a/lib/pleroma/bookmark.ex
+++ b/lib/pleroma/bookmark.ex
@@ -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,
diff --git a/lib/pleroma/captcha/kocaptcha.ex b/lib/pleroma/captcha/kocaptcha.ex
index e786e28b9..c4987d4fd 100644
--- a/lib/pleroma/captcha/kocaptcha.ex
+++ b/lib/pleroma/captcha/kocaptcha.ex
@@ -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,
diff --git a/lib/pleroma/chat.ex b/lib/pleroma/chat.ex
index fe32ec08c..5c4dbc1ff 100644
--- a/lib/pleroma/chat.ex
+++ b/lib/pleroma/chat.ex
@@ -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,
diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex
index b53b15d95..a77923264 100644
--- a/lib/pleroma/config/deprecation_warnings.ex
+++ b/lib/pleroma/config/deprecation_warnings.ex
@@ -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(),
@@ -274,7 +274,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
if warning == "" do
:ok
else
- Logger.warn(warning_preface <> warning)
+ Logger.warning(warning_preface <> warning)
:error
end
end
@@ -284,7 +284,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
whitelist = Config.get([:media_proxy, :whitelist])
if Enum.any?(whitelist, &(not String.starts_with?(&1, "http"))) do
- Logger.warn("""
+ Logger.warning("""
!!!DEPRECATION WARNING!!!
Your config is using old format (only domain) for MediaProxy whitelist option. Setting should work for now, but you are advised to change format to scheme with port to prevent possible issues later.
""")
@@ -299,7 +299,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
pool_config = Config.get(:connections_pool)
if timeout = pool_config[:await_up_timeout] do
- Logger.warn("""
+ Logger.warning("""
!!!DEPRECATION WARNING!!!
Your config is using old setting `config :pleroma, :connections_pool, await_up_timeout`. Please change to `config :pleroma, :connections_pool, connect_timeout` to ensure compatibility with future releases.
""")
@@ -331,7 +331,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
"\n* `:timeout` options in #{pool_name} pool is now `:recv_timeout`"
end)
- Logger.warn(Enum.join([warning_preface | pool_warnings]))
+ Logger.warning(Enum.join([warning_preface | pool_warnings]))
Config.put(:pools, updated_config)
:error
@@ -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 | nil
+ 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)
diff --git a/lib/pleroma/config/getting.ex b/lib/pleroma/config/getting.ex
index f9b66bba6..ec93fd02a 100644
--- a/lib/pleroma/config/getting.ex
+++ b/lib/pleroma/config/getting.ex
@@ -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
diff --git a/lib/pleroma/config/oban.ex b/lib/pleroma/config/oban.ex
index 483d2bb79..836f0c1a7 100644
--- a/lib/pleroma/config/oban.ex
+++ b/lib/pleroma/config/oban.ex
@@ -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
diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex
index 44a984019..91885347f 100644
--- a/lib/pleroma/config/transfer_task.ex
+++ b/lib/pleroma/config/transfer_task.ex
@@ -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
diff --git a/lib/pleroma/config_db.ex b/lib/pleroma/config_db.ex
index 846cede04..e28fcb124 100644
--- a/lib/pleroma/config_db.ex
+++ b/lib/pleroma/config_db.ex
@@ -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
diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex
index 77bc4bfac..d814b4931 100644
--- a/lib/pleroma/constants.ex
+++ b/lib/pleroma/constants.ex
@@ -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,
diff --git a/lib/pleroma/docs/generator.ex b/lib/pleroma/docs/generator.ex
index 6508f1947..93b19484f 100644
--- a/lib/pleroma/docs/generator.ex
+++ b/lib/pleroma/docs/generator.ex
@@ -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)
diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex
index 05f46f39b..f69854935 100644
--- a/lib/pleroma/docs/json.ex
+++ b/lib/pleroma/docs/json.ex
@@ -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
diff --git a/lib/pleroma/ecto_type/activity_pub/object_validators/bare_uri.ex b/lib/pleroma/ecto_type/activity_pub/object_validators/bare_uri.ex
index 1038296e7..a1af8faa1 100644
--- a/lib/pleroma/ecto_type/activity_pub/object_validators/bare_uri.ex
+++ b/lib/pleroma/ecto_type/activity_pub/object_validators/bare_uri.ex
@@ -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
diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex
index 43a3447c3..48302f396 100644
--- a/lib/pleroma/emoji.ex
+++ b/lib/pleroma/emoji.ex
@@ -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__{
diff --git a/lib/pleroma/emoji/loader.ex b/lib/pleroma/emoji/loader.ex
index 97d4b8f70..eb6f6816b 100644
--- a/lib/pleroma/emoji/loader.ex
+++ b/lib/pleroma/emoji/loader.ex
@@ -59,7 +59,7 @@ defmodule Pleroma.Emoji.Loader do
Logger.info("Found emoji packs: #{Enum.join(packs, ", ")}")
if not Enum.empty?(files) do
- Logger.warn(
+ Logger.warning(
"Found files in the emoji folder. These will be ignored, please move them to a subdirectory\nFound files: #{Enum.join(files, ", ")}"
)
end
diff --git a/lib/pleroma/emoji/pack.ex b/lib/pleroma/emoji/pack.ex
index 6e58f8898..85f7e1877 100644
--- a/lib/pleroma/emoji/pack.ex
+++ b/lib/pleroma/emoji/pack.ex
@@ -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) ->
diff --git a/lib/pleroma/gun/conn.ex b/lib/pleroma/gun/conn.ex
index 7c5785def..804cd11c7 100644
--- a/lib/pleroma/gun/conn.ex
+++ b/lib/pleroma/gun/conn.ex
@@ -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)}"
)
diff --git a/lib/pleroma/helpers/media_helper.ex b/lib/pleroma/helpers/media_helper.ex
index 24c845fcd..07dfea55b 100644
--- a/lib/pleroma/helpers/media_helper.ex
+++ b/lib/pleroma/helpers/media_helper.ex
@@ -8,11 +8,12 @@ defmodule Pleroma.Helpers.MediaHelper do
"""
alias Pleroma.HTTP
+ alias Vix.Vips.Operation
require Logger
def missing_dependencies do
- Enum.reduce([imagemagick: "convert", ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc ->
+ Enum.reduce([ffmpeg: "ffmpeg"], [], fn {sym, executable}, acc ->
if Pleroma.Utils.command_available?(executable) do
acc
else
@@ -22,54 +23,22 @@ defmodule Pleroma.Helpers.MediaHelper do
end
def image_resize(url, options) do
- with executable when is_binary(executable) <- System.find_executable("convert"),
- {:ok, args} <- prepare_image_resize_args(options),
- {:ok, env} <- HTTP.get(url, [], pool: :media),
- {:ok, fifo_path} <- mkfifo() do
- args = List.flatten([fifo_path, args])
- run_fifo(fifo_path, env, executable, args)
+ with {:ok, env} <- HTTP.get(url, [], pool: :media),
+ {:ok, resized} <-
+ Operation.thumbnail_buffer(env.body, options.max_width,
+ height: options.max_height,
+ size: :VIPS_SIZE_DOWN
+ ) do
+ if options[:format] == "png" do
+ Operation.pngsave_buffer(resized, Q: options[:quality])
+ else
+ Operation.jpegsave_buffer(resized, Q: options[:quality], interlace: true)
+ end
else
- nil -> {:error, {:convert, :command_not_found}}
{:error, _} = error -> error
end
end
- defp prepare_image_resize_args(
- %{max_width: max_width, max_height: max_height, format: "png"} = options
- ) do
- quality = options[:quality] || 85
- resize = Enum.join([max_width, "x", max_height, ">"])
-
- args = [
- "-resize",
- resize,
- "-quality",
- to_string(quality),
- "png:-"
- ]
-
- {:ok, args}
- end
-
- defp prepare_image_resize_args(%{max_width: max_width, max_height: max_height} = options) do
- quality = options[:quality] || 85
- resize = Enum.join([max_width, "x", max_height, ">"])
-
- args = [
- "-interlace",
- "Plane",
- "-resize",
- resize,
- "-quality",
- to_string(quality),
- "jpg:-"
- ]
-
- {:ok, args}
- end
-
- defp prepare_image_resize_args(_), do: {:error, :missing_options}
-
# Note: video thumbnail is intentionally not resized (always has original dimensions)
def video_framegrab(url) do
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
diff --git a/lib/pleroma/helpers/qt_fast_start.ex b/lib/pleroma/helpers/qt_fast_start.ex
index 5711c7162..919fec84a 100644
--- a/lib/pleroma/helpers/qt_fast_start.ex
+++ b/lib/pleroma/helpers/qt_fast_start.ex
@@ -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
-
- 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)
+ 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
+ ]
+
+ fix(rest, pos + size, got_moov || fourcc == "moov", got_mdat || fourcc == "mdat", acc)
+ rescue
+ _ ->
+ :abort
+ end
end
defp fix(<<>>, _pos, _, _, acc) do
diff --git a/lib/pleroma/http.ex b/lib/pleroma/http.ex
index d41061538..eec61cf14 100644
--- a/lib/pleroma/http.ex
+++ b/lib/pleroma/http.ex
@@ -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
diff --git a/lib/pleroma/http/adapter_helper.ex b/lib/pleroma/http/adapter_helper.ex
index 252a6aba5..e9bb2023a 100644
--- a/lib/pleroma/http/adapter_helper.ex
+++ b/lib/pleroma/http/adapter_helper.ex
@@ -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
diff --git a/lib/pleroma/http/web_push.ex b/lib/pleroma/http/web_push.ex
index ca399b6c8..888079c1e 100644
--- a/lib/pleroma/http/web_push.ex
+++ b/lib/pleroma/http/web_push.ex
@@ -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
diff --git a/lib/pleroma/instances.ex b/lib/pleroma/instances.ex
index 782948f83..b6d83f591 100644
--- a/lib/pleroma/instances.ex
+++ b/lib/pleroma/instances.ex
@@ -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())
diff --git a/lib/pleroma/instances/instance.ex b/lib/pleroma/instances/instance.ex
index 9756c66dc..c497a4fb7 100644
--- a/lib/pleroma/instances/instance.ex
+++ b/lib/pleroma/instances/instance.ex
@@ -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)}"
)
diff --git a/lib/pleroma/maintenance.ex b/lib/pleroma/maintenance.ex
index eb5a6ef42..1e39b03e6 100644
--- a/lib/pleroma/maintenance.ex
+++ b/lib/pleroma/maintenance.ex
@@ -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."
)
diff --git a/lib/pleroma/migrators/hashtags_table_migrator.ex b/lib/pleroma/migrators/hashtags_table_migrator.ex
index dca4bfa6f..bd4dd2f1d 100644
--- a/lib/pleroma/migrators/hashtags_table_migrator.ex
+++ b/lib/pleroma/migrators/hashtags_table_migrator.ex
@@ -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})
diff --git a/lib/pleroma/migrators/support/base_migrator.ex b/lib/pleroma/migrators/support/base_migrator.ex
index 3bcd59fd0..ce88caac7 100644
--- a/lib/pleroma/migrators/support/base_migrator.ex
+++ b/lib/pleroma/migrators/support/base_migrator.ex
@@ -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."
)
diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex
index aa137d250..f2ba24abf 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -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)
@@ -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
diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex
index cc3772563..af5642af4 100644
--- a/lib/pleroma/object/fetcher.ex
+++ b/lib/pleroma/object/fetcher.ex
@@ -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}
diff --git a/lib/pleroma/prom_ex.ex b/lib/pleroma/prom_ex.ex
new file mode 100644
index 000000000..6608708b7
--- /dev/null
+++ b/lib/pleroma/prom_ex.ex
@@ -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
diff --git a/lib/pleroma/release_tasks.ex b/lib/pleroma/release_tasks.ex
index f9e8d1948..bcfcd1243 100644
--- a/lib/pleroma/release_tasks.ex
+++ b/lib/pleroma/release_tasks.ex
@@ -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
diff --git a/lib/pleroma/repo.ex b/lib/pleroma/repo.ex
index 515b0c1ff..a50a59b3b 100644
--- a/lib/pleroma/repo.ex
+++ b/lib/pleroma/repo.ex
@@ -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.
diff --git a/lib/pleroma/report_note.ex b/lib/pleroma/report_note.ex
index f2ad76fa8..f59e5451b 100644
--- a/lib/pleroma/report_note.ex
+++ b/lib/pleroma/report_note.ex
@@ -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()
diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex
index 2248c2713..ec7732946 100644
--- a/lib/pleroma/reverse_proxy.ex
+++ b/lib/pleroma/reverse_proxy.ex
@@ -81,9 +81,9 @@ 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}
+ | {:failed_request_ttl, non_neg_integer() | :infinity}
| {:http, []}
| {:req_headers, [{String.t(), String.t()}]}
| {:resp_headers, [{String.t(), String.t()}]}
@@ -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)}"
)
diff --git a/lib/pleroma/scheduled_activity.ex b/lib/pleroma/scheduled_activity.ex
index 0ed51ad07..63c6cb45b 100644
--- a/lib/pleroma/scheduled_activity.ex
+++ b/lib/pleroma/scheduled_activity.ex
@@ -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
diff --git a/lib/pleroma/search.ex b/lib/pleroma/search.ex
new file mode 100644
index 000000000..3b266e59b
--- /dev/null
+++ b/lib/pleroma/search.ex
@@ -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
diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/search/database_search.ex
index 0b9b24aa4..c6311e0c7 100644
--- a/lib/pleroma/activity/search.ex
+++ b/lib/pleroma/search/database_search.ex
@@ -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"]),
diff --git a/lib/pleroma/search/meilisearch.ex b/lib/pleroma/search/meilisearch.ex
new file mode 100644
index 000000000..2bff663e8
--- /dev/null
+++ b/lib/pleroma/search/meilisearch.ex
@@ -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
diff --git a/lib/pleroma/search/search_backend.ex b/lib/pleroma/search/search_backend.ex
new file mode 100644
index 000000000..46be84551
--- /dev/null
+++ b/lib/pleroma/search/search_backend.ex
@@ -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, any()} | {:error, any()}
+end
diff --git a/lib/pleroma/telemetry/logger.ex b/lib/pleroma/telemetry/logger.ex
index 384c70fbc..92d395394 100644
--- a/lib/pleroma/telemetry/logger.ex
+++ b/lib/pleroma/telemetry/logger.ex
@@ -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
diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex
index 4aee9326f..3c355e64d 100644
--- a/lib/pleroma/upload.ex
+++ b/lib/pleroma/upload.ex
@@ -34,7 +34,6 @@ defmodule Pleroma.Upload do
"""
alias Ecto.UUID
- alias Pleroma.Config
alias Pleroma.Maps
alias Pleroma.Web.ActivityPub.Utils
require Logger
@@ -76,6 +75,8 @@ defmodule Pleroma.Upload do
:path
]
+ @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
+
defp get_description(upload) do
case {upload.description, Pleroma.Config.get([Pleroma.Upload, :default_description])} do
{description, _} when is_binary(description) -> description
@@ -85,7 +86,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)
@@ -244,18 +245,18 @@ defmodule Pleroma.Upload do
defp url_from_spec(_upload, _base_url, {:url, url}), do: url
def base_url do
- uploader = Config.get([Pleroma.Upload, :uploader])
- upload_base_url = Config.get([Pleroma.Upload, :base_url])
- public_endpoint = Config.get([uploader, :public_endpoint])
+ uploader = @config_impl.get([Pleroma.Upload, :uploader])
+ upload_base_url = @config_impl.get([Pleroma.Upload, :base_url])
+ public_endpoint = @config_impl.get([uploader, :public_endpoint])
case uploader do
Pleroma.Uploaders.Local ->
upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/"
Pleroma.Uploaders.S3 ->
- bucket = Config.get([Pleroma.Uploaders.S3, :bucket])
- truncated_namespace = Config.get([Pleroma.Uploaders.S3, :truncated_namespace])
- namespace = Config.get([Pleroma.Uploaders.S3, :bucket_namespace])
+ bucket = @config_impl.get([Pleroma.Uploaders.S3, :bucket])
+ truncated_namespace = @config_impl.get([Pleroma.Uploaders.S3, :truncated_namespace])
+ namespace = @config_impl.get([Pleroma.Uploaders.S3, :bucket_namespace])
bucket_with_namespace =
cond do
diff --git a/lib/pleroma/upload/filter/analyze_metadata.ex b/lib/pleroma/upload/filter/analyze_metadata.ex
index 9a76a998b..7ee643277 100644
--- a/lib/pleroma/upload/filter/analyze_metadata.ex
+++ b/lib/pleroma/upload/filter/analyze_metadata.ex
@@ -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
diff --git a/lib/pleroma/upload/filter/exiftool/read_description.ex b/lib/pleroma/upload/filter/exiftool/read_description.ex
index 543b22031..8c1ed82f8 100644
--- a/lib/pleroma/upload/filter/exiftool/read_description.ex
+++ b/lib/pleroma/upload/filter/exiftool/read_description.ex
@@ -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}
diff --git a/lib/pleroma/uploaders/s3.ex b/lib/pleroma/uploaders/s3.ex
index 19287c532..7b32bd8a5 100644
--- a/lib/pleroma/uploaders/s3.ex
+++ b/lib/pleroma/uploaders/s3.ex
@@ -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
diff --git a/lib/pleroma/uploaders/uploader.ex b/lib/pleroma/uploaders/uploader.ex
index 77f6f02dd..23caaff1a 100644
--- a/lib/pleroma/uploaders/uploader.ex
+++ b/lib/pleroma/uploaders/uploader.ex
@@ -40,7 +40,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()}
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index ce125d608..89a95c435 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -39,6 +39,7 @@ defmodule Pleroma.User do
alias Pleroma.Workers.BackgroundWorker
require Logger
+ require Pleroma.Constants
@type t :: %__MODULE__{}
@type account_status ::
@@ -579,7 +580,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)})
@@ -671,7 +672,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)
@@ -692,7 +693,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
@@ -1010,7 +1011,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()
@@ -1047,7 +1048,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
@@ -1560,7 +1562,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}"
)
@@ -1782,14 +1784,14 @@ 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)
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
@@ -1867,7 +1869,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/
@@ -2136,7 +2138,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
@@ -2252,7 +2254,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
@@ -2358,7 +2360,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)
@@ -2536,7 +2538,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
@@ -2554,7 +2556,7 @@ defmodule Pleroma.User do
cast(user, params, [:is_confirmed, :confirmation_token])
end
- @spec approval_changeset(User.t(), keyword()) :: Changeset.t()
+ @spec approval_changeset(User.t(), keyword()) :: Ecto.Changeset.t()
def approval_changeset(user, set_approval: approved?) do
cast(user, %{is_approved: approved?}, [:is_approved])
end
@@ -2681,6 +2683,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)
diff --git a/lib/pleroma/user/backup.ex b/lib/pleroma/user/backup.ex
index 447fca2a1..74e0ec073 100644
--- a/lib/pleroma/user/backup.ex
+++ b/lib/pleroma/user/backup.ex
@@ -35,6 +35,8 @@ defmodule Pleroma.User.Backup do
timestamps()
end
+ @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
+
def create(user, admin_id \\ nil) do
with :ok <- validate_limit(user, admin_id),
{:ok, backup} <- user |> new() |> Repo.insert() do
@@ -124,7 +126,10 @@ defmodule Pleroma.User.Backup do
|> Repo.update()
end
- def process(%__MODULE__{} = backup) do
+ def process(
+ %__MODULE__{} = backup,
+ processor_module \\ __MODULE__.Processor
+ ) do
set_state(backup, :running, 0)
current_pid = self()
@@ -132,7 +137,7 @@ defmodule Pleroma.User.Backup do
task =
Task.Supervisor.async_nolink(
Pleroma.TaskSupervisor,
- __MODULE__,
+ processor_module,
:do_process,
[backup, current_pid]
)
@@ -140,25 +145,8 @@ defmodule Pleroma.User.Backup do
wait_backup(backup, backup.processed_number, task)
end
- def do_process(backup, current_pid) do
- with {:ok, zip_file} <- export(backup, current_pid),
- {:ok, %{size: size}} <- File.stat(zip_file),
- {:ok, _upload} <- upload(backup, zip_file) do
- backup
- |> cast(
- %{
- file_size: size,
- processed: true,
- state: :complete
- },
- [:file_size, :processed, :state]
- )
- |> Repo.update()
- end
- end
-
defp wait_backup(backup, current_processed, task) do
- wait_time = Pleroma.Config.get([__MODULE__, :process_wait_time])
+ wait_time = @config_impl.get([__MODULE__, :process_wait_time])
receive do
{:progress, new_processed} ->
@@ -305,7 +293,7 @@ defmodule Pleroma.User.Backup do
acc + 1
else
{:error, e} ->
- Logger.warn(
+ Logger.warning(
"Error processing backup item: #{inspect(e)}\n The item is: #{inspect(i)}"
)
@@ -365,3 +353,35 @@ defmodule Pleroma.User.Backup do
)
end
end
+
+defmodule Pleroma.User.Backup.ProcessorAPI do
+ @callback do_process(%Pleroma.User.Backup{}, pid()) ::
+ {:ok, %Pleroma.User.Backup{}} | {:error, any()}
+end
+
+defmodule Pleroma.User.Backup.Processor do
+ @behaviour Pleroma.User.Backup.ProcessorAPI
+
+ alias Pleroma.Repo
+ alias Pleroma.User.Backup
+
+ import Ecto.Changeset
+
+ @impl true
+ def do_process(backup, current_pid) do
+ with {:ok, zip_file} <- Backup.export(backup, current_pid),
+ {:ok, %{size: size}} <- File.stat(zip_file),
+ {:ok, _upload} <- Backup.upload(backup, zip_file) do
+ backup
+ |> cast(
+ %{
+ file_size: size,
+ processed: true,
+ state: :complete
+ },
+ [:file_size, :processed, :state]
+ )
+ |> Repo.update()
+ end
+ end
+end
diff --git a/lib/pleroma/user/query.ex b/lib/pleroma/user/query.ex
index 3e090cac0..874e8ec2b 100644
--- a/lib/pleroma/user/query.ex
+++ b/lib/pleroma/user/query.ex
@@ -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"]})
"""
diff --git a/lib/pleroma/user_invite_token.ex b/lib/pleroma/user_invite_token.ex
index b242a8848..4bfb3a6a7 100644
--- a/lib/pleroma/user_invite_token.ex
+++ b/lib/pleroma/user_invite_token.ex
@@ -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
diff --git a/lib/pleroma/web.ex b/lib/pleroma/web.ex
index aee41b0fe..7a8b176cd 100644
--- a/lib/pleroma/web.ex
+++ b/lib/pleroma/web.ex
@@ -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
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex
index 3979d418e..a12438f56 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -96,6 +96,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp increase_replies_count_if_reply(_create_data), do: :noop
+ defp increase_quotes_count_if_quote(%{
+ "object" => %{"quoteUrl" => quote_ap_id} = object,
+ "type" => "Create"
+ }) do
+ if is_public?(object) do
+ Object.increase_quotes_count(quote_ap_id)
+ end
+ end
+
+ defp increase_quotes_count_if_quote(_create_data), do: :noop
+
@object_types ~w[ChatMessage Question Answer Audio Video Image Event Article Note Page]
@impl true
def persist(%{"type" => type} = object, meta) when type in @object_types do
@@ -140,6 +151,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
end)
+ # Add local posts to search index
+ if local, do: Pleroma.Search.add_to_index(activity)
+
{:ok, activity}
else
%Activity{} = activity ->
@@ -299,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
diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
index 1357c379c..e38a94966 100644
--- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex
@@ -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)
diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex
index ff9f84497..1071f8e6e 100644
--- a/lib/pleroma/web/activity_pub/mrf.ex
+++ b/lib/pleroma/web/activity_pub/mrf.ex
@@ -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)}"
)
diff --git a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
index 97d75ecf2..df4ba819c 100644
--- a/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/anti_followbot_policy.ex
@@ -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)
diff --git a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex
index 5b6adbb4b..5a4a97626 100644
--- a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex
@@ -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."
)
diff --git a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex
index b73fd974c..d13d980cc 100644
--- a/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/hashtag_policy.ex
@@ -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.
"""
diff --git a/lib/pleroma/web/activity_pub/mrf/policy.ex b/lib/pleroma/web/activity_pub/mrf/policy.ex
index 0234de4d5..1f34883e7 100644
--- a/lib/pleroma/web/activity_pub/mrf/policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/policy.ex
@@ -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(),
diff --git a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex
index f66c379b5..237dfefa5 100644
--- a/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/steal_emoji_policy.ex
@@ -34,14 +34,16 @@ 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
+
+ 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 +55,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
diff --git a/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
index efae48cae..09e25be89 100644
--- a/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/chat_message_validator.ex
@@ -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
diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex
index 835ed97b7..1a5d02601 100644
--- a/lib/pleroma/web/activity_pub/object_validators/common_fields.ex
+++ b/lib/pleroma/web/activity_pub/object_validators/common_fields.ex
@@ -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)
diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex
index af6aa0781..2449a3a3e 100644
--- a/lib/pleroma/web/activity_pub/publisher.ex
+++ b/lib/pleroma/web/activity_pub/publisher.ex
@@ -13,13 +13,12 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Relay
alias Pleroma.Web.ActivityPub.Transmogrifier
+ alias Pleroma.Workers.PublisherWorker
require Pleroma.Constants
import Pleroma.Web.ActivityPub.Visibility
- @behaviour Pleroma.Web.Federator.Publisher
-
require Logger
@moduledoc """
@@ -27,6 +26,44 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
"""
@doc """
+ Enqueue publishing a single activity.
+ """
+ @spec enqueue_one(map(), Keyword.t()) :: {:ok, %Oban.Job{}}
+ def enqueue_one(%{} = params, worker_args \\ []) do
+ PublisherWorker.enqueue(
+ "publish_one",
+ %{"params" => params},
+ worker_args
+ )
+ end
+
+ @doc """
+ Gathers a set of remote users given an IR envelope.
+ """
+ def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do
+ cc = Map.get(data, "cc", [])
+
+ bcc =
+ data
+ |> Map.get("bcc", [])
+ |> Enum.reduce([], fn ap_id, bcc ->
+ case Pleroma.List.get_by_ap_id(ap_id) do
+ %Pleroma.List{user_id: ^user_id} = list ->
+ {:ok, following} = Pleroma.List.get_following(list)
+ bcc ++ Enum.map(following, & &1.ap_id)
+
+ _ ->
+ bcc
+ end
+ end)
+
+ [to, cc, bcc]
+ |> Enum.concat()
+ |> Enum.map(&User.get_cached_by_ap_id/1)
+ |> Enum.filter(fn user -> user && !user.local end)
+ end
+
+ @doc """
Determine if an activity can be represented by running it through Transmogrifier.
"""
def is_representable?(%Activity{} = activity) do
@@ -80,9 +117,23 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
result
else
- {_post_result, response} ->
+ {_post_result, %{status: code} = response} = e ->
+ unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
+ Logger.metadata(activity: id, inbox: inbox, status: code)
+ Logger.error("Publisher failed to inbox #{inbox} with status #{code}")
+
+ case response do
+ %{status: 403} -> {:discard, :forbidden}
+ %{status: 404} -> {:discard, :not_found}
+ %{status: 410} -> {:discard, :not_found}
+ _ -> {:error, e}
+ end
+
+ e ->
unless params[:unreachable_since], do: Instances.set_unreachable(inbox)
- {:error, response}
+ Logger.metadata(activity: id, inbox: inbox)
+ Logger.error("Publisher failed to inbox #{inbox} #{inspect(e)}")
+ {:error, e}
end
end
@@ -118,7 +169,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
end
end
- @spec recipients(User.t(), Activity.t()) :: list(User.t()) | []
+ @spec recipients(User.t(), Activity.t()) :: [[User.t()]]
defp recipients(actor, activity) do
followers =
if actor.follower_address in activity.recipients do
@@ -138,7 +189,10 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
[]
end
- Pleroma.Web.Federator.Publisher.remote_users(actor, activity) ++ followers ++ fetchers
+ mentioned = remote_users(actor, activity)
+ non_mentioned = (followers ++ fetchers) -- mentioned
+
+ [mentioned, non_mentioned]
end
defp get_cc_ap_ids(ap_id, recipients) do
@@ -195,34 +249,42 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
public = is_public?(activity)
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
- recipients = recipients(actor, activity)
+ [priority_recipients, recipients] = recipients(actor, activity)
inboxes =
- recipients
- |> Enum.map(fn actor -> actor.inbox end)
- |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
- |> Instances.filter_reachable()
+ [priority_recipients, recipients]
+ |> Enum.map(fn recipients ->
+ recipients
+ |> Enum.map(fn %User{} = user ->
+ determine_inbox(activity, user)
+ end)
+ |> Enum.uniq()
+ |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
+ |> Instances.filter_reachable()
+ end)
Repo.checkout(fn ->
- Enum.each(inboxes, fn {inbox, unreachable_since} ->
- %User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end)
-
- # Get all the recipients on the same host and add them to cc. Otherwise, a remote
- # instance would only accept a first message for the first recipient and ignore the rest.
- cc = get_cc_ap_ids(ap_id, recipients)
-
- json =
- data
- |> Map.put("cc", cc)
- |> Jason.encode!()
-
- Pleroma.Web.Federator.Publisher.enqueue_one(__MODULE__, %{
- inbox: inbox,
- json: json,
- actor_id: actor.id,
- id: activity.data["id"],
- unreachable_since: unreachable_since
- })
+ Enum.each(inboxes, fn inboxes ->
+ Enum.each(inboxes, fn {inbox, unreachable_since} ->
+ %User{ap_id: ap_id} = Enum.find(recipients, fn actor -> actor.inbox == inbox end)
+
+ # Get all the recipients on the same host and add them to cc. Otherwise, a remote
+ # instance would only accept a first message for the first recipient and ignore the rest.
+ cc = get_cc_ap_ids(ap_id, recipients)
+
+ json =
+ data
+ |> Map.put("cc", cc)
+ |> Jason.encode!()
+
+ __MODULE__.enqueue_one(%{
+ inbox: inbox,
+ json: json,
+ actor_id: actor.id,
+ id: activity.data["id"],
+ unreachable_since: unreachable_since
+ })
+ end)
end)
end)
end
@@ -239,25 +301,38 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
json = Jason.encode!(data)
- recipients(actor, activity)
- |> Enum.map(fn %User{} = user ->
- determine_inbox(activity, user)
- end)
- |> Enum.uniq()
- |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
- |> Instances.filter_reachable()
- |> Enum.each(fn {inbox, unreachable_since} ->
- Pleroma.Web.Federator.Publisher.enqueue_one(
- __MODULE__,
- %{
- inbox: inbox,
- json: json,
- actor_id: actor.id,
- id: activity.data["id"],
- unreachable_since: unreachable_since
- }
- )
+ [priority_inboxes, inboxes] =
+ recipients(actor, activity)
+ |> Enum.map(fn recipients ->
+ recipients
+ |> Enum.map(fn %User{} = user ->
+ determine_inbox(activity, user)
+ end)
+ |> Enum.uniq()
+ |> Enum.filter(fn inbox -> should_federate?(inbox, public) end)
+ end)
+
+ inboxes = inboxes -- priority_inboxes
+
+ [{priority_inboxes, 0}, {inboxes, 1}]
+ |> Enum.each(fn {inboxes, priority} ->
+ inboxes
+ |> Instances.filter_reachable()
+ |> Enum.each(fn {inbox, unreachable_since} ->
+ __MODULE__.enqueue_one(
+ %{
+ inbox: inbox,
+ json: json,
+ actor_id: actor.id,
+ id: activity.data["id"],
+ unreachable_since: unreachable_since
+ },
+ priority: priority
+ )
+ end)
end)
+
+ :ok
end
def gather_webfinger_links(%User{} = user) do
diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex
index 098c177c7..59b19180d 100644
--- a/lib/pleroma/web/activity_pub/side_effects.ex
+++ b/lib/pleroma/web/activity_pub/side_effects.ex
@@ -197,6 +197,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# - Increase replies count
# - Set up ActivityExpiration
# - Set up notifications
+ # - Index incoming posts for search (if needed)
@impl true
def handle(%{data: %{"type" => "Create"}} = activity, meta) do
with {:ok, object, meta} <- handle_object_creation(meta[:object_data], activity, meta),
@@ -209,6 +210,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
Object.increase_replies_count(in_reply_to)
end
+ if quote_url = object.data["quoteUrl"] do
+ Object.increase_quotes_count(quote_url)
+ end
+
reply_depth = (meta[:depth] || 0) + 1
# FIXME: Force inReplyTo to replies
@@ -226,6 +231,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
end)
+ Pleroma.Search.add_to_index(Map.put(activity, :object, object))
+
+ Utils.maybe_handle_group_posts(activity)
+
meta =
meta
|> add_notifications(notifications)
@@ -285,6 +294,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
# - Reduce the user note count
# - Reduce the reply count
# - Stream out the activity
+ # - Removes posts from search index (if needed)
@impl true
def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, meta) do
deleted_object =
@@ -305,6 +315,10 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
Object.decrease_replies_count(in_reply_to)
end
+ if quote_url = deleted_object.data["quoteUrl"] do
+ Object.decrease_quotes_count(quote_url)
+ end
+
MessageReference.delete_for_object(deleted_object)
ap_streamer().stream_out(object)
@@ -323,6 +337,11 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
end
if result == :ok do
+ # Only remove from index when deleting actual objects, not users or anything else
+ with %Pleroma.Object{} <- deleted_object do
+ Pleroma.Search.remove_from_index(deleted_object)
+ end
+
{:ok, object, meta}
else
{:error, result}
diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex
index 86d3ac60f..a4cf395f2 100644
--- a/lib/pleroma/web/activity_pub/transmogrifier.ex
+++ b/lib/pleroma/web/activity_pub/transmogrifier.ex
@@ -23,7 +23,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
import Ecto.Query
- require Logger
require Pleroma.Constants
@doc """
@@ -155,8 +154,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("context", replied_object.data["context"] || object["conversation"])
|> Map.drop(["conversation", "inReplyToAtomUri"])
else
- e ->
- Logger.warn("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
+ _ ->
object
end
else
@@ -181,8 +179,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:quoting?, _} ->
object
- e ->
- Logger.warn("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}")
+ _ ->
object
end
end
@@ -339,6 +336,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def fix_tag(object), do: object
+ def fix_content_map(%{"contentMap" => nil} = object) do
+ Map.drop(object, ["contentMap"])
+ end
+
# content map usually only has one language so this will do for now.
def fix_content_map(%{"contentMap" => content_map} = object) do
content_groups = Map.to_list(content_map)
@@ -852,8 +853,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
relative_object do
Map.put(data, "object", external_url)
else
- {:fetch, e} ->
- Logger.error("Couldn't fetch #{object} #{inspect(e)}")
+ {:fetch, _} ->
data
_ ->
diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex
index 437220077..e2fc2640d 100644
--- a/lib/pleroma/web/activity_pub/utils.ex
+++ b/lib/pleroma/web/activity_pub/utils.ex
@@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
alias Ecto.UUID
alias Pleroma.Activity
alias Pleroma.Config
+ alias Pleroma.EctoType.ActivityPub.ObjectValidators.ObjectID
alias Pleroma.Maps
alias Pleroma.Notification
alias Pleroma.Object
@@ -852,9 +853,11 @@ defmodule Pleroma.Web.ActivityPub.Utils do
[actor | reported_activities] = activity.data["object"]
stripped_activities =
- Enum.map(reported_activities, fn
- act when is_map(act) -> act["id"]
- act when is_binary(act) -> act
+ Enum.reduce(reported_activities, [], fn act, acc ->
+ case ObjectID.cast(act) do
+ {:ok, act} -> [act | acc]
+ _ -> acc
+ end
end)
new_data = put_in(activity.data, ["object"], [actor | stripped_activities])
@@ -932,4 +935,27 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|> where([a, object: o], fragment("(?)->>'type' = 'Answer'", o.data))
|> Repo.all()
end
+
+ def maybe_handle_group_posts(activity) do
+ poster = User.get_cached_by_ap_id(activity.actor)
+
+ mentions =
+ activity.data["to"]
+ |> Enum.filter(&(&1 != activity.actor))
+
+ mentioned_local_groups =
+ User.get_all_by_ap_id(mentions)
+ |> Enum.filter(fn user ->
+ user.actor_type == "Group" and
+ user.local and
+ not User.blocks?(user, poster)
+ end)
+
+ mentioned_local_groups
+ |> Enum.each(fn group ->
+ Pleroma.Web.CommonAPI.repeat(activity.id, group)
+ end)
+
+ :ok
+ end
end
diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index f69fca075..24ee683ae 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -46,6 +46,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"following" => "#{user.ap_id}/following",
"followers" => "#{user.ap_id}/followers",
"inbox" => "#{user.ap_id}/inbox",
+ "outbox" => "#{user.ap_id}/outbox",
"name" => "Pleroma",
"summary" =>
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
diff --git a/lib/pleroma/web/api_spec.ex b/lib/pleroma/web/api_spec.ex
index 163226ce5..3588608f2 100644
--- a/lib/pleroma/web/api_spec.ex
+++ b/lib/pleroma/web/api_spec.ex
@@ -43,7 +43,7 @@ defmodule Pleroma.Web.ApiSpec do
- [Mastodon API documentation](https://docs.joinmastodon.org/client/intro/)
- [Differences in Mastodon API responses from vanilla Mastodon](https://docs-develop.pleroma.social/backend/development/API/differences_in_mastoapi_responses/)
- Please report such occurences on our [issue tracker](https://git.pleroma.social/pleroma/pleroma/-/issues). Feel free to submit API questions or proposals there too!
+ Please report such occurrences on our [issue tracker](https://git.pleroma.social/pleroma/pleroma/-/issues). Feel free to submit API questions or proposals there too!
""",
# Strip environment from the version
version: Application.spec(:pleroma, :vsn) |> to_string() |> String.replace(~r/\+.*$/, ""),
@@ -94,14 +94,14 @@ defmodule Pleroma.Web.ApiSpec do
"tags" => [
"Chat administration",
"Emoji pack administration",
- "Frontend managment",
+ "Frontend management",
"Instance configuration",
"Instance documents",
"Invites",
"MediaProxy cache",
- "OAuth application managment",
+ "OAuth application management",
"Relays",
- "Report managment",
+ "Report management",
"Status administration",
"User administration",
"Announcement management"
diff --git a/lib/pleroma/web/api_spec/operations/account_operation.ex b/lib/pleroma/web/api_spec/operations/account_operation.ex
index f2897a3a3..75cea2184 100644
--- a/lib/pleroma/web/api_spec/operations/account_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/account_operation.ex
@@ -347,7 +347,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
summary: "Endorse",
operationId: "AccountController.endorse",
security: [%{"oAuth" => ["follow", "write:accounts"]}],
- description: "Addds the given account to endorsed accounts list.",
+ description: "Adds the given account to endorsed accounts list.",
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
responses: %{
200 => Operation.response("Relationship", "application/json", AccountRelationship),
diff --git a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex
index 3e85c44d2..e17881b49 100644
--- a/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/frontend_operation.ex
@@ -16,7 +16,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.FrontendOperation do
def index_operation do
%Operation{
- tags: ["Frontend managment"],
+ tags: ["Frontend management"],
summary: "Retrieve a list of available frontends",
operationId: "AdminAPI.FrontendController.index",
security: [%{"oAuth" => ["admin:read"]}],
@@ -29,7 +29,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.FrontendOperation do
def install_operation do
%Operation{
- tags: ["Frontend managment"],
+ tags: ["Frontend management"],
summary: "Install a frontend",
operationId: "AdminAPI.FrontendController.install",
security: [%{"oAuth" => ["admin:read"]}],
diff --git a/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex b/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex
index 1a05aff6a..2b2496c26 100644
--- a/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/o_auth_app_operation.ex
@@ -17,7 +17,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
def index_operation do
%Operation{
summary: "Retrieve a list of OAuth applications",
- tags: ["OAuth application managment"],
+ tags: ["OAuth application management"],
operationId: "AdminAPI.OAuthAppController.index",
security: [%{"oAuth" => ["admin:write"]}],
parameters: [
@@ -69,7 +69,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
def create_operation do
%Operation{
- tags: ["OAuth application managment"],
+ tags: ["OAuth application management"],
summary: "Create an OAuth application",
operationId: "AdminAPI.OAuthAppController.create",
requestBody: request_body("Parameters", create_request()),
@@ -84,7 +84,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
def update_operation do
%Operation{
- tags: ["OAuth application managment"],
+ tags: ["OAuth application management"],
summary: "Update OAuth application",
operationId: "AdminAPI.OAuthAppController.update",
parameters: [id_param() | admin_api_params()],
@@ -102,7 +102,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
def delete_operation do
%Operation{
- tags: ["OAuth application managment"],
+ tags: ["OAuth application management"],
summary: "Delete OAuth application",
operationId: "AdminAPI.OAuthAppController.delete",
parameters: [id_param() | admin_api_params()],
diff --git a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex
index 312e091a5..d7a74f665 100644
--- a/lib/pleroma/web/api_spec/operations/admin/report_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/admin/report_operation.ex
@@ -19,7 +19,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
def index_operation do
%Operation{
- tags: ["Report managment"],
+ tags: ["Report management"],
summary: "Retrieve a list of reports",
operationId: "AdminAPI.ReportController.index",
security: [%{"oAuth" => ["admin:read:reports"]}],
@@ -69,7 +69,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
def show_operation do
%Operation{
- tags: ["Report managment"],
+ tags: ["Report management"],
summary: "Retrieve a report",
operationId: "AdminAPI.ReportController.show",
parameters: [id_param() | admin_api_params()],
@@ -83,7 +83,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
def update_operation do
%Operation{
- tags: ["Report managment"],
+ tags: ["Report management"],
summary: "Change state of specified reports",
operationId: "AdminAPI.ReportController.update",
security: [%{"oAuth" => ["admin:write:reports"]}],
@@ -99,7 +99,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
def notes_create_operation do
%Operation{
- tags: ["Report managment"],
+ tags: ["Report management"],
summary: "Add a note to the report",
operationId: "AdminAPI.ReportController.notes_create",
parameters: [id_param() | admin_api_params()],
@@ -120,7 +120,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
def notes_delete_operation do
%Operation{
- tags: ["Report managment"],
+ tags: ["Report management"],
summary: "Delete note attached to the report",
operationId: "AdminAPI.ReportController.notes_delete",
parameters: [
diff --git a/lib/pleroma/web/api_spec/operations/instance_operation.ex b/lib/pleroma/web/api_spec/operations/instance_operation.ex
index a07be7e40..708b74b12 100644
--- a/lib/pleroma/web/api_spec/operations/instance_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/instance_operation.ex
@@ -23,6 +23,18 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
}
end
+ def show2_operation do
+ %Operation{
+ tags: ["Instance misc"],
+ summary: "Retrieve instance information",
+ description: "Information about the server",
+ operationId: "InstanceController.show2",
+ responses: %{
+ 200 => Operation.response("Instance", "application/json", instance2())
+ }
+ }
+ end
+
def peers_operation do
%Operation{
tags: ["Instance misc"],
@@ -89,7 +101,7 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
languages: %Schema{
type: :array,
items: %Schema{type: :string},
- description: "Primary langauges of the website and its staff"
+ description: "Primary languages of the website and its staff"
},
registrations: %Schema{type: :boolean, description: "Whether registrations are enabled"},
# Extra (not present in Mastodon):
@@ -165,6 +177,166 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
}
end
+ defp instance2 do
+ %Schema{
+ type: :object,
+ properties: %{
+ domain: %Schema{type: :string, description: "The domain name of the instance"},
+ title: %Schema{type: :string, description: "The title of the website"},
+ version: %Schema{
+ type: :string,
+ description: "The version of Pleroma installed on the instance"
+ },
+ source_url: %Schema{
+ type: :string,
+ description: "The version of Pleroma installed on the instance"
+ },
+ description: %Schema{
+ type: :string,
+ description: "Admin-defined description of the Pleroma site"
+ },
+ usage: %Schema{
+ type: :object,
+ description: "Instance usage statistics",
+ properties: %{
+ users: %Schema{
+ type: :object,
+ description: "User count statistics",
+ properties: %{
+ active_month: %Schema{
+ type: :integer,
+ description: "Monthly active users"
+ }
+ }
+ }
+ }
+ },
+ email: %Schema{
+ type: :string,
+ description: "An email that may be contacted for any inquiries",
+ format: :email
+ },
+ urls: %Schema{
+ type: :object,
+ description: "URLs of interest for clients apps",
+ properties: %{}
+ },
+ stats: %Schema{
+ type: :object,
+ description: "Statistics about how much information the instance contains",
+ properties: %{
+ user_count: %Schema{
+ type: :integer,
+ description: "Users registered on this instance"
+ },
+ status_count: %Schema{
+ type: :integer,
+ description: "Statuses authored by users on instance"
+ },
+ domain_count: %Schema{
+ type: :integer,
+ description: "Domains federated with this instance"
+ }
+ }
+ },
+ thumbnail: %Schema{
+ type: :object,
+ properties: %{
+ url: %Schema{
+ type: :string,
+ description: "Banner image for the website",
+ nullable: true
+ }
+ }
+ },
+ languages: %Schema{
+ type: :array,
+ items: %Schema{type: :string},
+ description: "Primary languages of the website and its staff"
+ },
+ registrations: %Schema{
+ type: :object,
+ description: "Registrations-related configuration",
+ properties: %{
+ enabled: %Schema{
+ type: :boolean,
+ description: "Whether registrations are enabled"
+ },
+ approval_required: %Schema{
+ type: :boolean,
+ description: "Whether users need to be manually approved by admin"
+ }
+ }
+ },
+ configuration: %Schema{
+ type: :object,
+ description: "Instance configuration",
+ properties: %{
+ urls: %Schema{
+ type: :object,
+ properties: %{
+ streaming: %Schema{
+ type: :string,
+ description: "Websockets address for push streaming"
+ }
+ }
+ },
+ statuses: %Schema{
+ type: :object,
+ description: "A map with poll limits for local statuses",
+ properties: %{
+ max_characters: %Schema{
+ type: :integer,
+ description: "Posts character limit (CW/Subject included in the counter)"
+ },
+ max_media_attachments: %Schema{
+ type: :integer,
+ description: "Media attachment limit"
+ }
+ }
+ },
+ media_attachments: %Schema{
+ type: :object,
+ description: "A map with poll limits for media attachments",
+ properties: %{
+ image_size_limit: %Schema{
+ type: :integer,
+ description: "File size limit of uploaded images"
+ },
+ video_size_limit: %Schema{
+ type: :integer,
+ description: "File size limit of uploaded videos"
+ }
+ }
+ },
+ polls: %Schema{
+ type: :object,
+ description: "A map with poll limits for local polls",
+ properties: %{
+ max_options: %Schema{
+ type: :integer,
+ description: "Maximum number of options."
+ },
+ max_characters_per_option: %Schema{
+ type: :integer,
+ description: "Maximum number of characters per option."
+ },
+ min_expiration: %Schema{
+ type: :integer,
+ description: "Minimum expiration time (in seconds)."
+ },
+ max_expiration: %Schema{
+ type: :integer,
+ description: "Maximum expiration time (in seconds)."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ end
+
defp array_of_domains do
%Schema{
type: :array,
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex
index ca40da930..f595583b6 100644
--- a/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/pleroma_scrobble_operation.ex
@@ -23,7 +23,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
security: [%{"oAuth" => ["write"]}],
operationId: "PleromaAPI.ScrobbleController.create",
deprecated: true,
- requestBody: request_body("Parameters", create_request(), requried: true),
+ requestBody: request_body("Parameters", create_request(), required: true),
responses: %{
200 => Operation.response("Scrobble", "application/json", scrobble())
}
@@ -59,6 +59,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
album: %Schema{type: :string, description: "The album of the media playing"},
artist: %Schema{type: :string, description: "The artist of the media playing"},
length: %Schema{type: :integer, description: "The length of the media playing"},
+ externalLink: %Schema{type: :string, description: "A URL referencing the media playing"},
visibility: %Schema{
allOf: [VisibilityScope],
default: "public",
@@ -69,7 +70,8 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
"title" => "Some Title",
"artist" => "Some Artist",
"album" => "Some Album",
- "length" => 180_000
+ "length" => 180_000,
+ "externalLink" => "https://www.last.fm/music/Some+Artist/_/Some+Title"
}
}
end
@@ -83,6 +85,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
title: %Schema{type: :string, description: "The title of the media playing"},
album: %Schema{type: :string, description: "The album of the media playing"},
artist: %Schema{type: :string, description: "The artist of the media playing"},
+ externalLink: %Schema{type: :string, description: "A URL referencing the media playing"},
length: %Schema{
type: :integer,
description: "The length of the media playing",
@@ -97,6 +100,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaScrobbleOperation do
"artist" => "Some Artist",
"album" => "Some Album",
"length" => 180_000,
+ "externalLink" => "https://www.last.fm/music/Some+Artist/_/Some+Title",
"created_at" => "2019-09-28T12:40:45.000Z"
}
}
diff --git a/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex
new file mode 100644
index 000000000..6e69c5269
--- /dev/null
+++ b/lib/pleroma/web/api_spec/operations/pleroma_status_operation.ex
@@ -0,0 +1,45 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.ApiSpec.PleromaStatusOperation do
+ alias OpenApiSpex.Operation
+ alias Pleroma.Web.ApiSpec.Schemas.ApiError
+ alias Pleroma.Web.ApiSpec.Schemas.FlakeID
+ alias Pleroma.Web.ApiSpec.StatusOperation
+
+ import Pleroma.Web.ApiSpec.Helpers
+
+ def open_api_operation(action) do
+ operation = String.to_existing_atom("#{action}_operation")
+ apply(__MODULE__, operation, [])
+ end
+
+ def quotes_operation do
+ %Operation{
+ tags: ["Retrieve status information"],
+ summary: "Quoted by",
+ description: "View quotes for a given status",
+ operationId: "PleromaAPI.StatusController.quotes",
+ parameters: [id_param() | pagination_params()],
+ security: [%{"oAuth" => ["read:statuses"]}],
+ responses: %{
+ 200 =>
+ Operation.response(
+ "Array of Status",
+ "application/json",
+ StatusOperation.array_of_statuses()
+ ),
+ 403 => Operation.response("Forbidden", "application/json", ApiError),
+ 404 => Operation.response("Not Found", "application/json", ApiError)
+ }
+ }
+ end
+
+ def id_param do
+ Operation.parameter(:id, :path, FlakeID, "Status ID",
+ example: "9umDrYheeY451cQnEe",
+ required: true
+ )
+ end
+end
diff --git a/lib/pleroma/web/api_spec/operations/status_operation.ex b/lib/pleroma/web/api_spec/operations/status_operation.ex
index c133a3aac..f52372c18 100644
--- a/lib/pleroma/web/api_spec/operations/status_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/status_operation.ex
@@ -534,7 +534,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
format: :"date-time",
nullable: true,
description:
- "ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future."
+ "ISO 8601 Datetime at which to schedule a status. Providing this parameter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future."
},
language: %Schema{
type: :string,
@@ -546,7 +546,7 @@ defmodule Pleroma.Web.ApiSpec.StatusOperation do
allOf: [BooleanLike],
nullable: true,
description:
- "If set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example"
+ "If set to `true` the post won't be actually posted, but the status entity would still be rendered back. This could be useful for previewing rich text/custom emoji, for example"
},
content_type: %Schema{
type: :string,
diff --git a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex
index 084329ad7..87af0d526 100644
--- a/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex
+++ b/lib/pleroma/web/api_spec/operations/twitter_util_operation.ex
@@ -87,7 +87,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
defp change_password_request do
%Schema{
title: "ChangePasswordRequest",
- description: "POST body for changing the account's passowrd",
+ description: "POST body for changing the account's password",
type: :object,
required: [:password, :new_password, :new_password_confirmation],
properties: %{
@@ -136,12 +136,12 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
}
end
- def update_notificaton_settings_operation do
+ def update_notification_settings_operation do
%Operation{
tags: ["Settings"],
summary: "Update Notification Settings",
security: [%{"oAuth" => ["write:accounts"]}],
- operationId: "UtilController.update_notificaton_settings",
+ operationId: "UtilController.update_notification_settings",
parameters: [
Operation.parameter(
:block_from_strangers,
diff --git a/lib/pleroma/web/api_spec/schemas/attachment.ex b/lib/pleroma/web/api_spec/schemas/attachment.ex
index 48634a14f..2871b5f99 100644
--- a/lib/pleroma/web/api_spec/schemas/attachment.ex
+++ b/lib/pleroma/web/api_spec/schemas/attachment.ex
@@ -11,7 +11,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Attachment do
title: "Attachment",
description: "Represents a file or media attachment that can be added to a status.",
type: :object,
- requried: [:id, :url, :preview_url],
+ required: [:id, :url, :preview_url],
properties: %{
id: %Schema{type: :string, description: "The ID of the attachment in the database."},
url: %Schema{
diff --git a/lib/pleroma/web/api_spec/schemas/status.ex b/lib/pleroma/web/api_spec/schemas/status.ex
index 07f03134a..a4052803b 100644
--- a/lib/pleroma/web/api_spec/schemas/status.ex
+++ b/lib/pleroma/web/api_spec/schemas/status.ex
@@ -213,6 +213,10 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
type: :boolean,
description: "`true` if the quoted post is visible to the user"
},
+ quotes_count: %Schema{
+ type: :integer,
+ description: "How many statuses quoted this status"
+ },
local: %Schema{
type: :boolean,
description: "`true` if the post was made on the local instance"
@@ -367,7 +371,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Status do
"in_reply_to_account_acct" => nil,
"local" => true,
"spoiler_text" => %{"text/plain" => ""},
- "thread_muted" => false
+ "thread_muted" => false,
+ "quotes_count" => 0
},
"poll" => nil,
"reblog" => nil,
diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex
index a0bd154db..01bf1575c 100644
--- a/lib/pleroma/web/auth/authenticator.ex
+++ b/lib/pleroma/web/auth/authenticator.ex
@@ -5,7 +5,7 @@
defmodule Pleroma.Web.Auth.Authenticator do
@callback get_user(Plug.Conn.t()) :: {:ok, user :: struct()} | {:error, any()}
@callback create_from_registration(Plug.Conn.t(), registration :: struct()) ::
- {:ok, User.t()} | {:error, any()}
+ {:ok, Pleroma.User.t()} | {:error, any()}
@callback get_registration(Plug.Conn.t()) :: {:ok, registration :: struct()} | {:error, any()}
@callback handle_error(Plug.Conn.t(), any()) :: any()
@callback auth_template() :: String.t() | nil
diff --git a/lib/pleroma/web/common_api.ex b/lib/pleroma/web/common_api.ex
index 82c7f70d2..dfc0a625d 100644
--- a/lib/pleroma/web/common_api.ex
+++ b/lib/pleroma/web/common_api.ex
@@ -550,7 +550,7 @@ defmodule Pleroma.Web.CommonAPI do
remove_mute(user, activity)
else
{what, result} = error ->
- Logger.warn(
+ Logger.warning(
"CommonAPI.remove_mute/2 failed. #{what}: #{result}, user_id: #{user_id}, activity_id: #{activity_id}"
)
diff --git a/lib/pleroma/web/common_api/activity_draft.ex b/lib/pleroma/web/common_api/activity_draft.ex
index ca1329284..8910ad5b8 100644
--- a/lib/pleroma/web/common_api/activity_draft.ex
+++ b/lib/pleroma/web/common_api/activity_draft.ex
@@ -83,7 +83,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
defp listen_object(draft) do
object =
draft.params
- |> Map.take([:album, :artist, :title, :length])
+ |> Map.take([:album, :artist, :title, :length, :externalLink])
|> Map.new(fn {key, value} -> {to_string(key), value} end)
|> Map.put("type", "Audio")
|> Map.put("to", draft.to)
diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 0f394e951..dcda3e0e8 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -321,13 +321,13 @@ defmodule Pleroma.Web.CommonAPI.Utils do
format_asctime(date)
else
_e ->
- Logger.warn("Date #{date} in wrong format, must be ISO 8601")
+ Logger.warning("Date #{date} in wrong format, must be ISO 8601")
""
end
end
def date_to_asctime(date) do
- Logger.warn("Date #{date} in wrong format, must be ISO 8601")
+ Logger.warning("Date #{date} in wrong format, must be ISO 8601")
""
end
diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex
index 574f3ab63..307fa069e 100644
--- a/lib/pleroma/web/endpoint.ex
+++ b/lib/pleroma/web/endpoint.ex
@@ -9,7 +9,20 @@ defmodule Pleroma.Web.Endpoint do
alias Pleroma.Config
- socket("/socket", Pleroma.Web.UserSocket)
+ socket("/socket", Pleroma.Web.UserSocket,
+ websocket: [
+ path: "/websocket",
+ serializer: [
+ {Phoenix.Socket.V1.JSONSerializer, "~> 1.0.0"},
+ {Phoenix.Socket.V2.JSONSerializer, "~> 2.0.0"}
+ ],
+ timeout: 60_000,
+ transport_log: false,
+ compress: false
+ ],
+ longpoll: false
+ )
+
socket("/live", Phoenix.LiveView.Socket)
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
@@ -138,47 +151,6 @@ defmodule Pleroma.Web.Endpoint do
plug(Pleroma.Web.Plugs.RemoteIp)
- defmodule Instrumenter do
- use Prometheus.PhoenixInstrumenter
- end
-
- defmodule PipelineInstrumenter do
- use Prometheus.PlugPipelineInstrumenter
- end
-
- defmodule MetricsExporter do
- use Prometheus.PlugExporter
- end
-
- defmodule MetricsExporterCaller do
- @behaviour Plug
-
- def init(opts), do: opts
-
- def call(conn, opts) do
- prometheus_config = Application.get_env(:prometheus, MetricsExporter, [])
- ip_whitelist = List.wrap(prometheus_config[:ip_whitelist])
-
- cond do
- !prometheus_config[:enabled] ->
- conn
-
- ip_whitelist != [] and
- !Enum.find(ip_whitelist, fn ip ->
- Pleroma.Helpers.InetHelper.parse_address(ip) == {:ok, conn.remote_ip}
- end) ->
- conn
-
- true ->
- MetricsExporter.call(conn, opts)
- end
- end
- end
-
- plug(PipelineInstrumenter)
-
- plug(MetricsExporterCaller)
-
plug(Pleroma.Web.Router)
@doc """
diff --git a/lib/pleroma/web/fallback/redirect_controller.ex b/lib/pleroma/web/fallback/redirect_controller.ex
index 1a86f7a53..4a0885fab 100644
--- a/lib/pleroma/web/fallback/redirect_controller.ex
+++ b/lib/pleroma/web/fallback/redirect_controller.ex
@@ -17,10 +17,28 @@ defmodule Pleroma.Web.Fallback.RedirectController do
|> json(%{error: "Not implemented"})
end
+ def add_generated_metadata(page_content, extra \\ "") do
+ title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
+ favicon = "<link rel='icon' href='#{Pleroma.Config.get([:instance, :favicon])}'>"
+ manifest = "<link rel='manifest' href='/manifest.json'>"
+
+ page_content
+ |> String.replace(
+ "<!--server-generated-meta-->",
+ title <> favicon <> manifest <> extra
+ )
+ end
+
def redirector(conn, _params, code \\ 200) do
+ {:ok, index_content} = File.read(index_file_path())
+
+ response =
+ index_content
+ |> add_generated_metadata()
+
conn
|> put_resp_content_type("text/html")
- |> send_file(code, index_file_path())
+ |> send_resp(code, response)
end
def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do
@@ -34,14 +52,12 @@ defmodule Pleroma.Web.Fallback.RedirectController do
def redirector_with_meta(conn, params) do
{:ok, index_content} = File.read(index_file_path())
-
tags = build_tags(conn, params)
preloads = preload_data(conn, params)
- title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
response =
index_content
- |> String.replace("<!--server-generated-meta-->", tags <> preloads <> title)
+ |> add_generated_metadata(tags <> preloads)
conn
|> put_resp_content_type("text/html")
@@ -55,11 +71,10 @@ defmodule Pleroma.Web.Fallback.RedirectController do
def redirector_with_preload(conn, params) do
{:ok, index_content} = File.read(index_file_path())
preloads = preload_data(conn, params)
- title = "<title>#{Pleroma.Config.get([:instance, :name])}</title>"
response =
index_content
- |> String.replace("<!--server-generated-meta-->", preloads <> title)
+ |> add_generated_metadata(preloads)
conn
|> put_resp_content_type("text/html")
diff --git a/lib/pleroma/web/federator.ex b/lib/pleroma/web/federator.ex
index 84b77cda1..1f2c3835a 100644
--- a/lib/pleroma/web/federator.ex
+++ b/lib/pleroma/web/federator.ex
@@ -6,9 +6,9 @@ defmodule Pleroma.Web.Federator do
alias Pleroma.Activity
alias Pleroma.Object.Containment
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.Publisher
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.Utils
- alias Pleroma.Web.Federator.Publisher
alias Pleroma.Workers.PublisherWorker
alias Pleroma.Workers.ReceiverWorker
@@ -35,6 +35,17 @@ defmodule Pleroma.Web.Federator do
end
# Client API
+ def incoming_ap_doc(%{params: params, req_headers: req_headers}) do
+ ReceiverWorker.enqueue(
+ "incoming_ap_doc",
+ %{"req_headers" => req_headers, "params" => params, "timeout" => :timer.seconds(20)},
+ priority: 2
+ )
+ end
+
+ def incoming_ap_doc(%{"type" => "Delete"} = params) do
+ ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params}, priority: 3)
+ end
def incoming_ap_doc(params) do
ReceiverWorker.enqueue("incoming_ap_doc", %{"params" => params})
@@ -57,10 +68,8 @@ defmodule Pleroma.Web.Federator do
# Job Worker Callbacks
- @spec perform(atom(), module(), any()) :: {:ok, any()} | {:error, any()}
- def perform(:publish_one, module, params) do
- apply(module, :publish_one, [params])
- end
+ @spec perform(atom(), any()) :: {:ok, any()} | {:error, any()}
+ def perform(:publish_one, params), do: Publisher.publish_one(params)
def perform(:publish, activity) do
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end)
diff --git a/lib/pleroma/web/federator/publisher.ex b/lib/pleroma/web/federator/publisher.ex
deleted file mode 100644
index a45796e9d..000000000
--- a/lib/pleroma/web/federator/publisher.ex
+++ /dev/null
@@ -1,109 +0,0 @@
-# Pleroma: A lightweight social networking server
-# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
-# SPDX-License-Identifier: AGPL-3.0-only
-
-defmodule Pleroma.Web.Federator.Publisher do
- alias Pleroma.Activity
- alias Pleroma.Config
- alias Pleroma.User
- alias Pleroma.Workers.PublisherWorker
-
- require Logger
-
- @moduledoc """
- Defines the contract used by federation implementations to publish messages to
- their peers.
- """
-
- @doc """
- Determine whether an activity can be relayed using the federation module.
- """
- @callback is_representable?(Pleroma.Activity.t()) :: boolean()
-
- @doc """
- Relays an activity to a specified peer, determined by the parameters. The
- parameters used are controlled by the federation module.
- """
- @callback publish_one(Map.t()) :: {:ok, Map.t()} | {:error, any()}
-
- @doc """
- Enqueue publishing a single activity.
- """
- @spec enqueue_one(module(), Map.t()) :: :ok
- def enqueue_one(module, %{} = params) do
- PublisherWorker.enqueue(
- "publish_one",
- %{"module" => to_string(module), "params" => params}
- )
- end
-
- @doc """
- Relays an activity to all specified peers.
- """
- @callback publish(User.t(), Activity.t()) :: :ok | {:error, any()}
-
- @spec publish(User.t(), Activity.t()) :: :ok
- def publish(%User{} = user, %Activity{} = activity) do
- Config.get([:instance, :federation_publisher_modules])
- |> Enum.each(fn module ->
- if module.is_representable?(activity) do
- Logger.debug("Publishing #{activity.data["id"]} using #{inspect(module)}")
- module.publish(user, activity)
- end
- end)
-
- :ok
- end
-
- @doc """
- Gathers links used by an outgoing federation module for WebFinger output.
- """
- @callback gather_webfinger_links(User.t()) :: list()
-
- @spec gather_webfinger_links(User.t()) :: list()
- def gather_webfinger_links(%User{} = user) do
- Config.get([:instance, :federation_publisher_modules])
- |> Enum.reduce([], fn module, links ->
- links ++ module.gather_webfinger_links(user)
- end)
- end
-
- @doc """
- Gathers nodeinfo protocol names supported by the federation module.
- """
- @callback gather_nodeinfo_protocol_names() :: list()
-
- @spec gather_nodeinfo_protocol_names() :: list()
- def gather_nodeinfo_protocol_names do
- Config.get([:instance, :federation_publisher_modules])
- |> Enum.reduce([], fn module, links ->
- links ++ module.gather_nodeinfo_protocol_names()
- end)
- end
-
- @doc """
- Gathers a set of remote users given an IR envelope.
- """
- def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do
- cc = Map.get(data, "cc", [])
-
- bcc =
- data
- |> Map.get("bcc", [])
- |> Enum.reduce([], fn ap_id, bcc ->
- case Pleroma.List.get_by_ap_id(ap_id) do
- %Pleroma.List{user_id: ^user_id} = list ->
- {:ok, following} = Pleroma.List.get_following(list)
- bcc ++ Enum.map(following, & &1.ap_id)
-
- _ ->
- bcc
- end
- end)
-
- [to, cc, bcc]
- |> Enum.concat()
- |> Enum.map(&User.get_cached_by_ap_id/1)
- |> Enum.filter(fn user -> user && !user.local end)
- end
-end
diff --git a/lib/pleroma/web/feed/feed_view.ex b/lib/pleroma/web/feed/feed_view.ex
index 034722eb2..e1ee33d62 100644
--- a/lib/pleroma/web/feed/feed_view.ex
+++ b/lib/pleroma/web/feed/feed_view.ex
@@ -132,7 +132,7 @@ defmodule Pleroma.Web.Feed.FeedView do
|> safe_to_string()
end
- @spec to_rfc3339(String.t() | NativeDateTime.t()) :: String.t()
+ @spec to_rfc3339(String.t() | NaiveDateTime.t()) :: String.t()
def to_rfc3339(date) when is_binary(date) do
date
|> Timex.parse!("{ISO:Extended}")
@@ -145,7 +145,7 @@ defmodule Pleroma.Web.Feed.FeedView do
|> Timex.format!("{RFC3339}")
end
- @spec to_rfc2822(String.t() | DateTime.t() | NativeDateTime.t()) :: String.t()
+ @spec to_rfc2822(String.t() | DateTime.t() | NaiveDateTime.t()) :: String.t()
def to_rfc2822(datestr) when is_binary(datestr) do
datestr
|> Timex.parse!("{ISO:Extended}")
diff --git a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
index 9a4b56301..1b5de4b45 100644
--- a/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/account_controller.ex
@@ -235,7 +235,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
# So we first build the normal local changeset, then apply it to the
# user data, but don't persist it. With this, we generate the object
# data for our update activity. We feed this and the changeset as meta
- # inforation into the pipeline, where they will be properly updated and
+ # information into the pipeline, where they will be properly updated and
# federated.
with changeset <- User.update_changeset(user, user_params),
{:ok, unpersisted_user} <- Ecto.Changeset.apply_action(changeset, :update),
diff --git a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex
index 6410e872c..3e664903a 100644
--- a/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/instance_controller.ex
@@ -7,7 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do
plug(Pleroma.Web.ApiSpec.CastAndValidate)
- plug(:skip_auth when action in [:show, :peers])
+ plug(:skip_auth when action in [:show, :show2, :peers])
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.InstanceOperation
@@ -16,6 +16,11 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do
render(conn, "show.json")
end
+ @doc "GET /api/v2/instance"
+ def show2(conn, _params) do
+ render(conn, "show2.json")
+ end
+
@doc "GET /api/v1/instance/peers"
def peers(conn, _params) do
json(conn, Pleroma.Stats.get_peers())
diff --git a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex
index 5e6e04734..e4acba226 100644
--- a/lib/pleroma/web/mastodon_api/controllers/search_controller.ex
+++ b/lib/pleroma/web/mastodon_api/controllers/search_controller.ex
@@ -5,7 +5,6 @@
defmodule Pleroma.Web.MastodonAPI.SearchController do
use Pleroma.Web, :controller
- alias Pleroma.Activity
alias Pleroma.Repo
alias Pleroma.User
alias Pleroma.Web.ControllerHelper
@@ -100,7 +99,7 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
end
defp resource_search(_, "statuses", query, options) do
- statuses = with_fallback(fn -> Activity.search(options[:for_user], query, options) end)
+ statuses = with_fallback(fn -> Pleroma.Search.search(query, options) end)
StatusView.render("index.json",
activities: statuses,
diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex
index cc3e3582f..df8fdc8b8 100644
--- a/lib/pleroma/web/mastodon_api/views/account_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/account_view.ex
@@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# Copyright © 2017-2023 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AccountView do
@@ -194,6 +194,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
end
defp do_render("show.json", %{user: user} = opts) do
+ self = opts[:for] == user
+
user = User.sanitize_html(user, User.html_filter_policy(opts[:for]))
display_name = user.name || user.nickname
@@ -203,16 +205,16 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
header_static = User.banner_url(user) |> MediaProxy.preview_url(static: true)
following_count =
- if !user.hide_follows_count or !user.hide_follows or opts[:for] == user,
+ if !user.hide_follows_count or !user.hide_follows or self,
do: user.following_count,
else: 0
followers_count =
- if !user.hide_followers_count or !user.hide_followers or opts[:for] == user,
+ if !user.hide_followers_count or !user.hide_followers or self,
do: user.follower_count,
else: 0
- bot = user.actor_type == "Service"
+ bot = is_bot?(user)
emojis =
Enum.map(user.emoji, fn {shortcode, raw_url} ->
@@ -249,6 +251,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
nil
end
+ last_status_at =
+ user.last_status_at &&
+ user.last_status_at |> NaiveDateTime.to_date() |> Date.to_iso8601()
+
%{
id: to_string(user.id),
username: username_from_nickname(user.nickname),
@@ -277,7 +283,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
actor_type: user.actor_type
}
},
- last_status_at: user.last_status_at,
+ last_status_at: last_status_at,
# Pleroma extensions
# Note: it's insecure to output :email but fully-qualified nickname may serve as safe stub
@@ -464,4 +470,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
defp image_url(_), do: nil
+
+ defp is_bot?(user) do
+ # Because older and/or Mastodon clients may not recognize a Group actor properly,
+ # and currently the group actor can only boost things, we should let these clients
+ # think groups are bots.
+ # See https://git.pleroma.social/pleroma/pleroma-meta/-/issues/14
+ user.actor_type == "Service" || user.actor_type == "Group"
+ end
end
diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex
index 1b01d7371..7f73917d6 100644
--- a/lib/pleroma/web/mastodon_api/views/instance_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex
@@ -13,12 +13,11 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
def render("show.json", _) do
instance = Config.get(:instance)
- %{
- uri: Pleroma.Web.Endpoint.url(),
- title: Keyword.get(instance, :name),
+ common_information(instance)
+ |> Map.merge(%{
+ uri: Pleroma.Web.WebFinger.host(),
description: Keyword.get(instance, :description),
short_description: Keyword.get(instance, :short_description),
- version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
email: Keyword.get(instance, :email),
urls: %{
streaming_api: Pleroma.Web.Endpoint.websocket_url()
@@ -27,9 +26,9 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
thumbnail:
URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
|> to_string,
- languages: Keyword.get(instance, :languages, ["en"]),
registrations: Keyword.get(instance, :registrations_open),
approval_required: Keyword.get(instance, :account_approval_required),
+ configuration: configuration(),
# Extra (not present in Mastodon):
max_toot_chars: Keyword.get(instance, :limit),
max_media_attachments: Keyword.get(instance, :max_media_attachments),
@@ -41,19 +40,45 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
shout_limit: Config.get([:shout, :limit]),
description_limit: Keyword.get(instance, :description_limit),
- pleroma: %{
- metadata: %{
- account_activation_required: Keyword.get(instance, :account_activation_required),
- features: features(),
- federation: federation(),
- fields_limits: fields_limits(),
- post_formats: Config.get([:instance, :allowed_post_formats]),
- birthday_required: Config.get([:instance, :birthday_required]),
- birthday_min_age: Config.get([:instance, :birthday_min_age])
- },
- stats: %{mau: Pleroma.User.active_user_count()},
- vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
- }
+ chat_limit: Keyword.get(instance, :chat_limit),
+ pleroma: pleroma_configuration(instance)
+ })
+ end
+
+ def render("show2.json", _) do
+ instance = Config.get(:instance)
+
+ common_information(instance)
+ |> Map.merge(%{
+ domain: Pleroma.Web.WebFinger.host(),
+ source_url: Pleroma.Application.repository(),
+ description: Keyword.get(instance, :short_description),
+ usage: %{users: %{active_month: Pleroma.User.active_user_count()}},
+ thumbnail: %{
+ url:
+ URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
+ |> to_string
+ },
+ configuration: configuration2(),
+ registrations: %{
+ enabled: Keyword.get(instance, :registrations_open),
+ approval_required: Keyword.get(instance, :account_approval_required),
+ message: nil
+ },
+ contact: %{
+ email: Keyword.get(instance, :email),
+ account: nil
+ },
+ # Extra (not present in Mastodon):
+ pleroma: pleroma_configuration2(instance)
+ })
+ end
+
+ defp common_information(instance) do
+ %{
+ title: Keyword.get(instance, :name),
+ version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
+ languages: Keyword.get(instance, :languages, ["en"])
}
end
@@ -101,7 +126,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
if Config.get([:instance, :profile_directory]) do
"profile_directory"
end,
- "pleroma:get:main/ostatus"
+ "pleroma:get:main/ostatus",
+ "pleroma:group_actors"
]
|> Enum.filter(& &1)
end
@@ -133,7 +159,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
|> Map.put(:enabled, Config.get([:instance, :federating]))
end
- def fields_limits do
+ defp fields_limits do
%{
max_fields: Config.get([:instance, :max_account_fields]),
max_remote_fields: Config.get([:instance, :max_remote_account_fields]),
@@ -141,4 +167,66 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
value_length: Config.get([:instance, :account_field_value_length])
}
end
+
+ defp configuration do
+ %{
+ statuses: %{
+ max_characters: Config.get([:instance, :limit]),
+ max_media_attachments: Config.get([:instance, :max_media_attachments])
+ },
+ media_attachments: %{
+ image_size_limit: Config.get([:instance, :upload_limit]),
+ video_size_limit: Config.get([:instance, :upload_limit])
+ },
+ polls: %{
+ max_options: Config.get([:instance, :poll_limits, :max_options]),
+ max_characters_per_option: Config.get([:instance, :poll_limits, :max_option_chars]),
+ min_expiration: Config.get([:instance, :poll_limits, :min_expiration]),
+ max_expiration: Config.get([:instance, :poll_limits, :max_expiration])
+ }
+ }
+ end
+
+ defp configuration2 do
+ configuration()
+ |> Map.merge(%{
+ urls: %{streaming: Pleroma.Web.Endpoint.websocket_url()}
+ })
+ end
+
+ defp pleroma_configuration(instance) do
+ %{
+ metadata: %{
+ account_activation_required: Keyword.get(instance, :account_activation_required),
+ features: features(),
+ federation: federation(),
+ fields_limits: fields_limits(),
+ post_formats: Config.get([:instance, :allowed_post_formats]),
+ birthday_required: Config.get([:instance, :birthday_required]),
+ birthday_min_age: Config.get([:instance, :birthday_min_age])
+ },
+ stats: %{mau: Pleroma.User.active_user_count()},
+ vapid_public_key: Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key)
+ }
+ end
+
+ defp pleroma_configuration2(instance) do
+ configuration = pleroma_configuration(instance)
+
+ configuration
+ |> Map.merge(%{
+ metadata:
+ configuration.metadata
+ |> Map.merge(%{
+ avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
+ background_upload_limit: Keyword.get(instance, :background_upload_limit),
+ banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
+ background_image:
+ Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
+ chat_limit: Keyword.get(instance, :chat_limit),
+ description_limit: Keyword.get(instance, :description_limit),
+ shout_limit: Config.get([:shout, :limit])
+ })
+ })
+ end
end
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex
index d070262cc..0e2e604f5 100644
--- a/lib/pleroma/web/mastodon_api/views/status_view.ex
+++ b/lib/pleroma/web/mastodon_api/views/status_view.ex
@@ -447,7 +447,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
thread_muted: thread_muted?,
emoji_reactions: emoji_reactions,
parent_visible: visible_for_user?(reply_to, opts[:for]),
- pinned_at: pinned_at
+ pinned_at: pinned_at,
+ quotes_count: object.data["quotesCount"] || 0
}
}
end
@@ -562,25 +563,24 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
page_url = page_url_data |> to_string
- image_url_data =
- if is_binary(rich_media["image"]) do
- URI.parse(rich_media["image"])
- else
- nil
- end
-
- image_url = build_image_url(image_url_data, page_url_data)
+ image_url = proxied_url(rich_media["image"], page_url_data)
+ audio_url = proxied_url(rich_media["audio"], page_url_data)
+ video_url = proxied_url(rich_media["video"], page_url_data)
%{
type: "link",
provider_name: page_url_data.host,
provider_url: page_url_data.scheme <> "://" <> page_url_data.host,
url: page_url,
- image: image_url |> MediaProxy.url(),
+ image: image_url,
title: rich_media["title"] || "",
description: rich_media["description"] || "",
pleroma: %{
- opengraph: rich_media
+ opengraph:
+ rich_media
+ |> Maps.put_if_present("image", image_url)
+ |> Maps.put_if_present("audio", audio_url)
+ |> Maps.put_if_present("video", video_url)
}
}
end
@@ -817,4 +817,12 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
defp get_source_content_type(_source) do
Utils.get_content_type(nil)
end
+
+ defp proxied_url(url, page_url_data) do
+ if is_binary(url) do
+ build_image_url(URI.parse(url), page_url_data) |> MediaProxy.url()
+ else
+ nil
+ end
+ end
end
diff --git a/lib/pleroma/web/nodeinfo/nodeinfo.ex b/lib/pleroma/web/nodeinfo/nodeinfo.ex
index 9e27ac26c..4d5a9a57f 100644
--- a/lib/pleroma/web/nodeinfo/nodeinfo.ex
+++ b/lib/pleroma/web/nodeinfo/nodeinfo.ex
@@ -6,7 +6,7 @@ defmodule Pleroma.Web.Nodeinfo.Nodeinfo do
alias Pleroma.Config
alias Pleroma.Stats
alias Pleroma.User
- alias Pleroma.Web.Federator.Publisher
+ alias Pleroma.Web.ActivityPub.Publisher
alias Pleroma.Web.MastodonAPI.InstanceView
# returns a nodeinfo 2.0 map, since 2.1 just adds a repository field
diff --git a/lib/pleroma/web/o_auth/authorization.ex b/lib/pleroma/web/o_auth/authorization.ex
index 593d2d66f..22e5bfc53 100644
--- a/lib/pleroma/web/o_auth/authorization.ex
+++ b/lib/pleroma/web/o_auth/authorization.ex
@@ -28,7 +28,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
end
@spec create_authorization(App.t(), User.t() | %{}, [String.t()] | nil) ::
- {:ok, Authorization.t()} | {:error, Changeset.t()}
+ {:ok, Authorization.t()} | {:error, Ecto.Changeset.t()}
def create_authorization(%App{} = app, %User{} = user, scopes \\ nil) do
%{
scopes: scopes || app.scopes,
@@ -39,7 +39,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
|> Repo.insert()
end
- @spec create_changeset(map()) :: Changeset.t()
+ @spec create_changeset(map()) :: Ecto.Changeset.t()
def create_changeset(attrs \\ %{}) do
%Authorization{}
|> cast(attrs, [:user_id, :app_id, :scopes, :valid_until])
@@ -58,7 +58,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
put_change(changeset, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), lifespan))
end
- @spec use_changeset(Authtorizatiton.t(), map()) :: Changeset.t()
+ @spec use_changeset(Authorization.t(), map()) :: Ecto.Changeset.t()
def use_changeset(%Authorization{} = auth, params) do
auth
|> cast(params, [:used])
@@ -66,7 +66,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
end
@spec use_token(Authorization.t()) ::
- {:ok, Authorization.t()} | {:error, Changeset.t()} | {:error, String.t()}
+ {:ok, Authorization.t()} | {:error, Ecto.Changeset.t()} | {:error, String.t()}
def use_token(%Authorization{used: false, valid_until: valid_until} = auth) do
if NaiveDateTime.diff(NaiveDateTime.utc_now(), valid_until) < 0 do
Repo.update(use_changeset(auth, %{used: true}))
diff --git a/lib/pleroma/web/o_auth/o_auth_controller.ex b/lib/pleroma/web/o_auth/o_auth_controller.ex
index c1fb4f378..2bbe5d5fa 100644
--- a/lib/pleroma/web/o_auth/o_auth_controller.ex
+++ b/lib/pleroma/web/o_auth/o_auth_controller.ex
@@ -310,7 +310,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
after_token_exchange(conn, %{token: token})
else
_error ->
- handle_token_exchange_error(conn, :invalid_credentails)
+ handle_token_exchange_error(conn, :invalid_credentials)
end
end
diff --git a/lib/pleroma/web/o_auth/token.ex b/lib/pleroma/web/o_auth/token.ex
index 26de7bb10..d4a7e8999 100644
--- a/lib/pleroma/web/o_auth/token.ex
+++ b/lib/pleroma/web/o_auth/token.ex
@@ -56,7 +56,8 @@ defmodule Pleroma.Web.OAuth.Token do
|> Repo.find_resource()
end
- @spec exchange_token(App.t(), Authorization.t()) :: {:ok, Token.t()} | {:error, Changeset.t()}
+ @spec exchange_token(App.t(), Authorization.t()) ::
+ {:ok, Token.t()} | {:error, Ecto.Changeset.t()}
def exchange_token(app, auth) do
with {:ok, auth} <- Authorization.use_token(auth),
true <- auth.app_id == app.id do
@@ -95,7 +96,7 @@ defmodule Pleroma.Web.OAuth.Token do
|> validate_required([:valid_until])
end
- @spec create(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Changeset.t()}
+ @spec create(App.t(), User.t(), map()) :: {:ok, Token} | {:error, Ecto.Changeset.t()}
def create(%App{} = app, %User{} = user, attrs \\ %{}) do
with {:ok, token} <- do_create(app, user, attrs) do
if Pleroma.Config.get([:oauth2, :clean_expired_tokens]) do
diff --git a/lib/pleroma/web/o_auth/token/query.ex b/lib/pleroma/web/o_auth/token/query.ex
index 4a4d2d3ef..6853ec8dd 100644
--- a/lib/pleroma/web/o_auth/token/query.ex
+++ b/lib/pleroma/web/o_auth/token/query.ex
@@ -9,10 +9,10 @@ defmodule Pleroma.Web.OAuth.Token.Query do
import Ecto.Query, only: [from: 2]
- @type query :: Ecto.Queryable.t() | Token.t()
-
alias Pleroma.Web.OAuth.Token
+ @type query :: Ecto.Queryable.t() | Token.t()
+
@spec get_by_refresh_token(query, String.t()) :: query
def get_by_refresh_token(query \\ Token, refresh_token) do
from(q in query, where: q.refresh_token == ^refresh_token)
diff --git a/lib/pleroma/web/pleroma_api/controllers/status_controller.ex b/lib/pleroma/web/pleroma_api/controllers/status_controller.ex
new file mode 100644
index 000000000..482662fdd
--- /dev/null
+++ b/lib/pleroma/web/pleroma_api/controllers/status_controller.ex
@@ -0,0 +1,66 @@
+# Pleroma: A lightweight social networking server
+# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
+# SPDX-License-Identifier: AGPL-3.0-only
+
+defmodule Pleroma.Web.PleromaAPI.StatusController do
+ use Pleroma.Web, :controller
+
+ import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2]
+
+ require Ecto.Query
+ require Pleroma.Constants
+
+ alias Pleroma.Activity
+ alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
+ alias Pleroma.Web.ActivityPub.Visibility
+ alias Pleroma.Web.MastodonAPI.StatusView
+ alias Pleroma.Web.Plugs.OAuthScopesPlug
+
+ plug(Pleroma.Web.ApiSpec.CastAndValidate)
+
+ action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
+
+ plug(
+ OAuthScopesPlug,
+ %{scopes: ["read:statuses"], fallback: :proceed_unauthenticated} when action == :quotes
+ )
+
+ defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaStatusOperation
+
+ @doc "GET /api/v1/pleroma/statuses/:id/quotes"
+ def quotes(%{assigns: %{user: user}} = conn, %{id: id} = params) do
+ with %Activity{object: object} = activity <- Activity.get_by_id_with_object(id),
+ true <- Visibility.visible_for_user?(activity, user) do
+ params =
+ params
+ |> Map.put(:type, "Create")
+ |> Map.put(:blocking_user, user)
+ |> Map.put(:quote_url, object.data["id"])
+
+ recipients =
+ if user do
+ [Pleroma.Constants.as_public()] ++ [user.ap_id | User.following(user)]
+ else
+ [Pleroma.Constants.as_public()]
+ end
+
+ activities =
+ recipients
+ |> ActivityPub.fetch_activities(params)
+ |> Enum.reverse()
+
+ conn
+ |> add_link_headers(activities)
+ |> put_view(StatusView)
+ |> render("index.json",
+ activities: activities,
+ for: user,
+ as: :activity
+ )
+ else
+ nil -> {:error, :not_found}
+ false -> {:error, :not_found}
+ end
+ end
+end
diff --git a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex
index a5985fb2a..edf0a2390 100644
--- a/lib/pleroma/web/pleroma_api/views/scrobble_view.ex
+++ b/lib/pleroma/web/pleroma_api/views/scrobble_view.ex
@@ -27,6 +27,7 @@ defmodule Pleroma.Web.PleromaAPI.ScrobbleView do
title: object.data["title"] |> HTML.strip_tags(),
artist: object.data["artist"] |> HTML.strip_tags(),
album: object.data["album"] |> HTML.strip_tags(),
+ externalLink: object.data["externalLink"],
length: object.data["length"]
}
end
diff --git a/lib/pleroma/web/plugs/cache.ex b/lib/pleroma/web/plugs/cache.ex
index 667477857..5a7e86ef7 100644
--- a/lib/pleroma/web/plugs/cache.ex
+++ b/lib/pleroma/web/plugs/cache.ex
@@ -20,7 +20,7 @@ defmodule Pleroma.Web.Plugs.Cache do
- `ttl`: An expiration time (time-to-live). This value should be in milliseconds or `nil` to disable expiration. Defaults to `nil`.
- `query_params`: Take URL query string into account (`true`), ignore it (`false`) or limit to specific params only (list). Defaults to `true`.
- - `tracking_fun`: A function that is called on successfull responses, no matter if the request is cached or not. It should accept a conn as the first argument and the value assigned to `tracking_fun_data` as the second.
+ - `tracking_fun`: A function that is called on successful responses, no matter if the request is cached or not. It should accept a conn as the first argument and the value assigned to `tracking_fun_data` as the second.
Additionally, you can overwrite the TTL inside a controller action by assigning `cache_ttl` to the connection struct:
diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex
index 5093414c4..a27dcd0ab 100644
--- a/lib/pleroma/web/plugs/http_security_plug.ex
+++ b/lib/pleroma/web/plugs/http_security_plug.ex
@@ -201,7 +201,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do
def warn_if_disabled do
unless Config.get([:http_security, :enabled]) do
- Logger.warn("
+ Logger.warning("
.i;;;;i.
iYcviii;vXY:
.YXi .i1c.
diff --git a/lib/pleroma/web/plugs/rate_limiter.ex b/lib/pleroma/web/plugs/rate_limiter.ex
index 2080b06bd..aa79dbf6b 100644
--- a/lib/pleroma/web/plugs/rate_limiter.ex
+++ b/lib/pleroma/web/plugs/rate_limiter.ex
@@ -89,7 +89,7 @@ defmodule Pleroma.Web.Plugs.RateLimiter do
end
defp handle_disabled(conn) do
- Logger.warn(
+ Logger.warning(
"Rate limiter disabled due to forwarded IP not being found. Please ensure your reverse proxy is providing the X-Forwarded-For header or disable the RemoteIP plug/rate limiter."
)
diff --git a/lib/pleroma/web/plugs/uploaded_media.ex b/lib/pleroma/web/plugs/uploaded_media.ex
index 8b3bc9acb..f1076da1b 100644
--- a/lib/pleroma/web/plugs/uploaded_media.ex
+++ b/lib/pleroma/web/plugs/uploaded_media.ex
@@ -105,7 +105,7 @@ defmodule Pleroma.Web.Plugs.UploadedMedia do
end
defp get_media(conn, unknown, _, _) do
- Logger.error("#{__MODULE__}: Unknown get startegy: #{inspect(unknown)}")
+ Logger.error("#{__MODULE__}: Unknown get strategy: #{inspect(unknown)}")
conn
|> send_resp(:internal_server_error, dgettext("errors", "Internal Error"))
diff --git a/lib/pleroma/web/push.ex b/lib/pleroma/web/push.ex
index 9665b0b4a..0d43f402e 100644
--- a/lib/pleroma/web/push.ex
+++ b/lib/pleroma/web/push.ex
@@ -9,7 +9,7 @@ defmodule Pleroma.Web.Push do
def init do
unless enabled() do
- Logger.warn("""
+ Logger.warning("""
VAPID key pair is not found. If you wish to enabled web push, please run
mix web_push.gen.keypair
diff --git a/lib/pleroma/web/push/impl.ex b/lib/pleroma/web/push/impl.ex
index 3c5f00764..36f44d8e8 100644
--- a/lib/pleroma/web/push/impl.ex
+++ b/lib/pleroma/web/push/impl.ex
@@ -57,7 +57,7 @@ defmodule Pleroma.Web.Push.Impl do
end
def perform(_) do
- Logger.warn("Unknown notification type")
+ Logger.warning("Unknown notification type")
{:error, :unknown_type}
end
diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex
index 0488df30e..61000bb9b 100644
--- a/lib/pleroma/web/rich_media/helpers.ex
+++ b/lib/pleroma/web/rich_media/helpers.ex
@@ -4,11 +4,12 @@
defmodule Pleroma.Web.RichMedia.Helpers do
alias Pleroma.Activity
- alias Pleroma.Config
alias Pleroma.HTML
alias Pleroma.Object
alias Pleroma.Web.RichMedia.Parser
+ @config_impl Application.compile_env(:pleroma, [__MODULE__, :config_impl], Pleroma.Config)
+
@options [
pool: :media,
max_body: 2_000_000,
@@ -17,7 +18,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
@spec validate_page_url(URI.t() | binary()) :: :ok | :error
defp validate_page_url(page_url) when is_binary(page_url) do
- validate_tld = Config.get([Pleroma.Formatter, :validate_tld])
+ validate_tld = @config_impl.get([Pleroma.Formatter, :validate_tld])
page_url
|> Linkify.Parser.url?(validate_tld: validate_tld)
@@ -27,10 +28,10 @@ defmodule Pleroma.Web.RichMedia.Helpers do
defp validate_page_url(%URI{host: host, scheme: "https", authority: authority})
when is_binary(authority) do
cond do
- host in Config.get([:rich_media, :ignore_hosts], []) ->
+ host in @config_impl.get([:rich_media, :ignore_hosts], []) ->
:error
- get_tld(host) in Config.get([:rich_media, :ignore_tld], []) ->
+ get_tld(host) in @config_impl.get([:rich_media, :ignore_tld], []) ->
:error
true ->
@@ -56,7 +57,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
end
def fetch_data_for_object(object) do
- with true <- Config.get([:rich_media, :enabled]),
+ with true <- @config_impl.get([:rich_media, :enabled]),
{:ok, page_url} <-
HTML.extract_first_external_url_from_object(object),
:ok <- validate_page_url(page_url),
@@ -68,7 +69,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
end
def fetch_data_for_activity(%Activity{data: %{"type" => "Create"}} = activity) do
- with true <- Config.get([:rich_media, :enabled]),
+ with true <- @config_impl.get([:rich_media, :enabled]),
%Object{} = object <- Object.normalize(activity, fetch: false) do
fetch_data_for_object(object)
else
diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex
index dbe81eabb..17a6ad617 100644
--- a/lib/pleroma/web/rich_media/parser.ex
+++ b/lib/pleroma/web/rich_media/parser.ex
@@ -75,7 +75,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
end
defp log_error(url, reason) do
- Logger.warn(fn -> "Rich media error for #{url}: #{inspect(reason)}" end)
+ Logger.warning(fn -> "Rich media error for #{url}: #{inspect(reason)}" end)
end
end
@@ -102,7 +102,7 @@ defmodule Pleroma.Web.RichMedia.Parser do
ttl_setters: [MyModule]
"""
@spec set_ttl_based_on_image(map(), String.t()) ::
- {:ok, Integer.t() | :noop} | {:error, :no_key}
+ {:ok, integer() | :noop} | {:error, :no_key}
def set_ttl_based_on_image(data, url) do
case get_ttl_from_image(data, url) do
{:ok, ttl} when is_number(ttl) ->
diff --git a/lib/pleroma/web/rich_media/parser/ttl.ex b/lib/pleroma/web/rich_media/parser/ttl.ex
index 59d7f87ab..b51298bd8 100644
--- a/lib/pleroma/web/rich_media/parser/ttl.ex
+++ b/lib/pleroma/web/rich_media/parser/ttl.ex
@@ -3,5 +3,5 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.RichMedia.Parser.TTL do
- @callback ttl(Map.t(), String.t()) :: Integer.t() | nil
+ @callback ttl(map(), String.t()) :: integer() | nil
end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index 6b9e158a3..8ba845364 100644
--- a/lib/pleroma/web/router.ex
+++ b/lib/pleroma/web/router.ex
@@ -182,7 +182,7 @@ defmodule Pleroma.Web.Router do
end
pipeline :well_known do
- plug(:accepts, ["json", "jrd+json", "xml", "xrd+xml"])
+ plug(:accepts, ["json", "jrd", "jrd+json", "xml", "xrd+xml"])
end
pipeline :config do
@@ -224,6 +224,12 @@ defmodule Pleroma.Web.Router do
post("/remote_interaction", UtilController, :remote_interaction)
end
+ scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
+ pipe_through(:pleroma_api)
+
+ get("/federation_status", InstancesController, :show)
+ end
+
scope "/api/v1/pleroma", Pleroma.Web do
pipe_through(:pleroma_api)
post("/uploader_callback/:upload_path", UploaderController, :callback)
@@ -465,6 +471,8 @@ defmodule Pleroma.Web.Router do
get("/main/ostatus", UtilController, :show_subscribe_form)
get("/ostatus_subscribe", RemoteFollowController, :follow)
post("/ostatus_subscribe", RemoteFollowController, :do_follow)
+
+ get("/authorize_interaction", RemoteFollowController, :authorize_interaction)
end
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
@@ -473,7 +481,7 @@ defmodule Pleroma.Web.Router do
post("/change_email", UtilController, :change_email)
post("/change_password", UtilController, :change_password)
post("/delete_account", UtilController, :delete_account)
- put("/notification_settings", UtilController, :update_notificaton_settings)
+ put("/notification_settings", UtilController, :update_notification_settings)
post("/disable_account", UtilController, :disable_account)
post("/move_account", UtilController, :move_account)
@@ -578,6 +586,8 @@ defmodule Pleroma.Web.Router do
pipe_through(:api)
get("/accounts/:id/favourites", AccountController, :favourites)
get("/accounts/:id/endorsements", AccountController, :endorsements)
+
+ get("/statuses/:id/quotes", StatusController, :quotes)
end
scope [] do
@@ -602,7 +612,6 @@ defmodule Pleroma.Web.Router do
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
pipe_through(:api)
get("/accounts/:id/scrobbles", ScrobbleController, :index)
- get("/federation_status", InstancesController, :show)
end
scope "/api/v2/pleroma", Pleroma.Web.PleromaAPI do
@@ -774,11 +783,14 @@ defmodule Pleroma.Web.Router do
scope "/api/v2", Pleroma.Web.MastodonAPI do
pipe_through(:api)
+
get("/search", SearchController, :search2)
post("/media", MediaController, :create2)
get("/suggestions", SuggestionController, :index2)
+
+ get("/instance", InstanceController, :show2)
end
scope "/api", Pleroma.Web do
@@ -1003,9 +1015,8 @@ defmodule Pleroma.Web.Router do
options("/*path", RedirectController, :empty)
end
- # TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
def get_api_routes do
- __MODULE__.__routes__()
+ Phoenix.Router.routes(__MODULE__)
|> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
|> Enum.map(fn r ->
r.path
diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex
index 48ca82421..0c9f04f82 100644
--- a/lib/pleroma/web/streamer.ex
+++ b/lib/pleroma/web/streamer.ex
@@ -34,7 +34,7 @@ defmodule Pleroma.Web.Streamer do
stream :: String.t(),
User.t() | nil,
Token.t() | nil,
- Map.t() | nil
+ map() | nil
) ::
{:ok, topic :: String.t()} | {:error, :bad_topic} | {:error, :unauthorized}
def get_topic_and_add_socket(stream, user, oauth_token, params \\ %{}) do
@@ -60,7 +60,7 @@ defmodule Pleroma.Web.Streamer do
end
@doc "Expand and authorizes a stream"
- @spec get_topic(stream :: String.t() | nil, User.t() | nil, Token.t() | nil, Map.t()) ::
+ @spec get_topic(stream :: String.t() | nil, User.t() | nil, Token.t() | nil, map()) ::
{:ok, topic :: String.t() | nil} | {:error, :bad_topic}
def get_topic(stream, user, oauth_token, params \\ %{})
@@ -396,7 +396,7 @@ defmodule Pleroma.Web.Streamer do
end
end
- # In test environement, only return true if the registry is started.
+ # In test environment, only return true if the registry is started.
# In benchmark environment, returns false.
# In any other environment, always returns true.
cond do
diff --git a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex
index e45d13bdf..e3639aae7 100644
--- a/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex
+++ b/lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex
@@ -1,8 +1,8 @@
-<%= if get_flash(@conn, :info) do %>
-<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
+<%= if Phoenix.Flash.get(@flash, :info) do %>
+<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
<% end %>
-<%= if get_flash(@conn, :error) do %>
-<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
+<%= if Phoenix.Flash.get(@flash, :error) do %>
+<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
<% end %>
<h2><%= Gettext.dpgettext("static_pages", "mfa recover page title", "Two-factor recovery") %></h2>
diff --git a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex
index 50e6c04b6..f995b8805 100644
--- a/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex
+++ b/lib/pleroma/web/templates/o_auth/mfa/totp.html.eex
@@ -1,8 +1,8 @@
-<%= if get_flash(@conn, :info) do %>
-<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
+<%= if Phoenix.Flash.get(@flash, :info) do %>
+<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
<% end %>
-<%= if get_flash(@conn, :error) do %>
-<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
+<%= if Phoenix.Flash.get(@flash, :error) do %>
+<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
<% end %>
<h2><%= Gettext.dpgettext("static_pages", "mfa auth page title", "Two-factor authentication") %></h2>
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex
index 1f661efb2..e7f65266f 100644
--- a/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex
+++ b/lib/pleroma/web/templates/o_auth/o_auth/register.html.eex
@@ -1,8 +1,8 @@
-<%= if get_flash(@conn, :info) do %>
- <p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
+<%= if Phoenix.Flash.get(@flash, :info) do %>
+ <p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
<% end %>
-<%= if get_flash(@conn, :error) do %>
- <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
+<%= if Phoenix.Flash.get(@flash, :error) do %>
+ <p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
<% end %>
<h2><%= Gettext.dpgettext("static_pages", "oauth register page title", "Registration Details") %></h2>
diff --git a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex
index b3654f3eb..5b38f7142 100644
--- a/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex
+++ b/lib/pleroma/web/templates/o_auth/o_auth/show.html.eex
@@ -1,8 +1,8 @@
-<%= if get_flash(@conn, :info) do %>
-<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
+<%= if Phoenix.Flash.get(@flash, :info) do %>
+<p class="alert alert-info" role="alert"><%= Phoenix.Flash.get(@flash, :info) %></p>
<% end %>
-<%= if get_flash(@conn, :error) do %>
-<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
+<%= if Phoenix.Flash.get(@flash, :error) do %>
+<p class="alert alert-danger" role="alert"><%= Phoenix.Flash.get(@flash, :error) %></p>
<% end %>
<%= form_for @conn, Routes.o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
diff --git a/lib/pleroma/web/twitter_api/controllers/password_controller.ex b/lib/pleroma/web/twitter_api/controllers/password_controller.ex
index 31b7dd728..e5482de9d 100644
--- a/lib/pleroma/web/twitter_api/controllers/password_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/password_controller.ex
@@ -4,7 +4,7 @@
defmodule Pleroma.Web.TwitterAPI.PasswordController do
@moduledoc """
- The module containts functions for reset password.
+ The module contains functions for password reset.
"""
use Pleroma.Web, :controller
diff --git a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex
index 6229d5d05..178ad2b43 100644
--- a/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex
@@ -121,6 +121,13 @@ defmodule Pleroma.Web.TwitterAPI.RemoteFollowController do
render(conn, "followed.html", %{error: "Insufficient permissions: follow | write:follows."})
end
+ # GET /authorize_interaction
+ #
+ def authorize_interaction(conn, %{"uri" => uri}) do
+ conn
+ |> redirect(to: Routes.remote_follow_path(conn, :follow, %{acct: uri}))
+ end
+
defp handle_follow_error(conn, {:mfa_token, followee, _} = _) do
render(conn, "follow_login.html", %{error: "Wrong username or password", followee: followee})
end
diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index d5a24ae6c..50f8f803f 100644
--- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex
+++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
@@ -35,7 +35,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
:change_email,
:change_password,
:delete_account,
- :update_notificaton_settings,
+ :update_notification_settings,
:disable_account,
:move_account,
:add_alias,
@@ -181,7 +181,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
json(conn, emoji)
end
- def update_notificaton_settings(%{assigns: %{user: user}} = conn, params) do
+ def update_notification_settings(%{assigns: %{user: user}} = conn, params) do
with {:ok, _} <- User.update_notification_settings(user, params) do
json(conn, %{status: "success"})
end
@@ -345,13 +345,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end
def healthcheck(conn, _params) do
- with true <- Config.get([:instance, :healthcheck]),
+ with {:cfg, true} <- {:cfg, Config.get([:instance, :healthcheck])},
%{healthy: true} = info <- Healthcheck.system_info() do
json(conn, info)
else
%{healthy: false} = info ->
service_unavailable(conn, info)
+ {:cfg, false} ->
+ service_unavailable(conn, %{"error" => "Healthcheck disabled"})
+
_ ->
service_unavailable(conn, %{})
end
diff --git a/lib/pleroma/web/web_finger.ex b/lib/pleroma/web/web_finger.ex
index f95dc2458..26fb8af84 100644
--- a/lib/pleroma/web/web_finger.ex
+++ b/lib/pleroma/web/web_finger.ex
@@ -5,8 +5,8 @@
defmodule Pleroma.Web.WebFinger do
alias Pleroma.HTTP
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.Publisher
alias Pleroma.Web.Endpoint
- alias Pleroma.Web.Federator.Publisher
alias Pleroma.Web.XML
alias Pleroma.XmlBuilder
require Jason
@@ -70,7 +70,7 @@ defmodule Pleroma.Web.WebFinger do
def represent_user(user, "JSON") do
%{
- "subject" => "acct:#{user.nickname}@#{domain()}",
+ "subject" => "acct:#{user.nickname}@#{host()}",
"aliases" => gather_aliases(user),
"links" => gather_links(user)
}
@@ -90,13 +90,13 @@ defmodule Pleroma.Web.WebFinger do
:XRD,
%{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"},
[
- {:Subject, "acct:#{user.nickname}@#{domain()}"}
+ {:Subject, "acct:#{user.nickname}@#{host()}"}
] ++ aliases ++ links
}
|> XmlBuilder.to_doc()
end
- defp domain do
+ def host do
Pleroma.Config.get([__MODULE__, :domain]) || Pleroma.Web.Endpoint.host()
end
@@ -163,7 +163,7 @@ defmodule Pleroma.Web.WebFinger do
get_template_from_xml(body)
else
error ->
- Logger.warn("Can't find LRDD template in #{inspect(meta_url)}: #{inspect(error)}")
+ Logger.warning("Can't find LRDD template in #{inspect(meta_url)}: #{inspect(error)}")
{:error, :lrdd_not_found}
end
end
diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex
index 9e5efb77f..021df9bc5 100644
--- a/lib/pleroma/web/web_finger/web_finger_controller.ex
+++ b/lib/pleroma/web/web_finger/web_finger_controller.ex
@@ -30,7 +30,7 @@ defmodule Pleroma.Web.WebFinger.WebFingerController do
end
def webfinger(%{assigns: %{format: format}} = conn, %{"resource" => resource})
- when format in ["json", "jrd+json"] do
+ when format in ["jrd", "json", "jrd+json"] do
with {:ok, response} <- WebFinger.webfinger(resource, "JSON") do
json(conn, response)
else
diff --git a/lib/pleroma/workers/cron/digest_emails_worker.ex b/lib/pleroma/workers/cron/digest_emails_worker.ex
index 1540c1605..0292bbb3b 100644
--- a/lib/pleroma/workers/cron/digest_emails_worker.ex
+++ b/lib/pleroma/workers/cron/digest_emails_worker.ex
@@ -7,7 +7,7 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorker do
The worker to send digest emails.
"""
- use Oban.Worker, queue: "digest_emails"
+ use Oban.Worker, queue: "mailer"
alias Pleroma.Config
alias Pleroma.Emails
diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex
index 267fe2837..1c3e445aa 100644
--- a/lib/pleroma/workers/cron/new_users_digest_worker.ex
+++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex
@@ -9,7 +9,7 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do
import Ecto.Query
- use Pleroma.Workers.WorkerHelper, queue: "new_users_digest"
+ use Pleroma.Workers.WorkerHelper, queue: "mailer"
@impl Oban.Worker
def perform(_job) do
diff --git a/lib/pleroma/workers/publisher_worker.ex b/lib/pleroma/workers/publisher_worker.ex
index 598ae3779..63fcf4ac2 100644
--- a/lib/pleroma/workers/publisher_worker.ex
+++ b/lib/pleroma/workers/publisher_worker.ex
@@ -18,9 +18,9 @@ defmodule Pleroma.Workers.PublisherWorker do
Federator.perform(:publish, activity)
end
- def perform(%Job{args: %{"op" => "publish_one", "module" => module_name, "params" => params}}) do
+ def perform(%Job{args: %{"op" => "publish_one", "params" => params}}) do
params = Map.new(params, fn {k, v} -> {String.to_atom(k), v} end)
- Federator.perform(:publish_one, String.to_atom(module_name), params)
+ Federator.perform(:publish_one, params)
end
@impl Oban.Worker
diff --git a/lib/pleroma/workers/receiver_worker.ex b/lib/pleroma/workers/receiver_worker.ex
index cf1bb62b4..1dddd8d2e 100644
--- a/lib/pleroma/workers/receiver_worker.ex
+++ b/lib/pleroma/workers/receiver_worker.ex
@@ -3,24 +3,56 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.ReceiverWorker do
+ alias Pleroma.Signature
+ alias Pleroma.User
alias Pleroma.Web.Federator
use Pleroma.Workers.WorkerHelper, queue: "federator_incoming"
@impl Oban.Worker
+
+ def perform(%Job{
+ args: %{"op" => "incoming_ap_doc", "req_headers" => req_headers, "params" => params}
+ }) do
+ # Oban's serialization converts our tuple headers to lists.
+ # Revert it for the signature validation.
+ req_headers = Enum.into(req_headers, [], &List.to_tuple(&1))
+
+ conn_data = %{params: params, req_headers: req_headers}
+
+ with {:ok, %User{} = _actor} <- User.get_or_fetch_by_ap_id(conn_data.params["actor"]),
+ {:ok, _public_key} <- Signature.refetch_public_key(conn_data),
+ {:signature, true} <- {:signature, HTTPSignatures.validate_conn(conn_data)},
+ {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
+ {:ok, res}
+ else
+ e -> process_errors(e)
+ end
+ end
+
def perform(%Job{args: %{"op" => "incoming_ap_doc", "params" => params}}) do
with {:ok, res} <- Federator.perform(:incoming_ap_doc, params) do
{:ok, res}
else
+ e -> process_errors(e)
+ end
+ end
+
+ @impl Oban.Worker
+ def timeout(%_{args: %{"timeout" => timeout}}), do: timeout
+
+ def timeout(_job), do: :timer.seconds(5)
+
+ defp process_errors(errors) do
+ case errors do
{:error, :origin_containment_failed} -> {:cancel, :origin_containment_failed}
{:error, :already_present} -> {:cancel, :already_present}
{:error, {:validate_object, reason}} -> {:cancel, reason}
{:error, {:error, {:validate, reason}}} -> {:cancel, reason}
{:error, {:reject, reason}} -> {:cancel, reason}
+ {:signature, false} -> {:cancel, :invalid_signature}
+ {:error, {:error, reason = "Object has been deleted"}} -> {:cancel, reason}
e -> e
end
end
-
- @impl Oban.Worker
- def timeout(_job), do: :timer.seconds(5)
end
diff --git a/lib/pleroma/workers/remote_fetcher_worker.ex b/lib/pleroma/workers/remote_fetcher_worker.ex
index d2a77aa17..d526a99cb 100644
--- a/lib/pleroma/workers/remote_fetcher_worker.ex
+++ b/lib/pleroma/workers/remote_fetcher_worker.ex
@@ -9,7 +9,22 @@ defmodule Pleroma.Workers.RemoteFetcherWorker do
@impl Oban.Worker
def perform(%Job{args: %{"op" => "fetch_remote", "id" => id} = args}) do
- {:ok, _object} = Fetcher.fetch_object_from_id(id, depth: args["depth"])
+ case Fetcher.fetch_object_from_id(id, depth: args["depth"]) do
+ {:ok, _object} ->
+ :ok
+
+ {:error, :forbidden} ->
+ {:discard, :forbidden}
+
+ {:error, :not_found} ->
+ {:discard, :not_found}
+
+ {:error, :allowed_depth} ->
+ {:discard, :allowed_depth}
+
+ _ ->
+ :error
+ end
end
@impl Oban.Worker
diff --git a/lib/pleroma/workers/search_indexing_worker.ex b/lib/pleroma/workers/search_indexing_worker.ex
new file mode 100644
index 000000000..8476a2be5
--- /dev/null
+++ b/lib/pleroma/workers/search_indexing_worker.ex
@@ -0,0 +1,23 @@
+defmodule Pleroma.Workers.SearchIndexingWorker do
+ use Pleroma.Workers.WorkerHelper, queue: "search_indexing"
+
+ @impl Oban.Worker
+
+ alias Pleroma.Config.Getting, as: Config
+
+ def perform(%Job{args: %{"op" => "add_to_index", "activity" => activity_id}}) do
+ activity = Pleroma.Activity.get_by_id_with_object(activity_id)
+
+ search_module = Config.get([Pleroma.Search, :module])
+
+ search_module.add_to_index(activity)
+ end
+
+ def perform(%Job{args: %{"op" => "remove_from_index", "object" => object_id}}) do
+ object = Pleroma.Object.get_by_id(object_id)
+
+ search_module = Config.get([Pleroma.Search, :module])
+
+ search_module.remove_from_index(object)
+ end
+end