Merge branch 'develop' into 'post-languages'

Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
marcin mikołajczak 2024-02-02 17:13:15 +00:00 committed by marcin mikołajczak
commit e798be90ac
162 changed files with 1458 additions and 847 deletions

6
.dialyzer_ignore.exs Normal file
View file

@ -0,0 +1,6 @@
[
{"lib/cachex.ex", "Unknown type: Spec.cache/0."},
{"lib/pleroma/web/plugs/rate_limiter.ex", "The pattern can never match the type {:commit, _} | {:ignore, _}."},
{"lib/pleroma/web/plugs/rate_limiter.ex", "Function get_scale/2 will never be called."},
{"lib/pleroma/web/plugs/rate_limiter.ex", "Function initialize_buckets!/1 will never be called."}
]

View file

@ -28,6 +28,7 @@ cache: &global_cache_policy
stages:
- check-changelog
- build
- lint
- test
- benchmark
- deploy
@ -71,7 +72,7 @@ check-changelog:
tags:
- amd64
build:
build-1.12.3:
extends:
- .build_changes_policy
- .using-ci-base
@ -79,10 +80,20 @@ build:
script:
- mix compile --force
build-1.15.7-otp-25:
extends:
- .build_changes_policy
- .using-ci-base
stage: build
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.15
allow_failure: true
script:
- mix compile --force
spec-build:
extends:
- .using-ci-base
stage: test
stage: build
rules:
- changes:
- ".gitlab-ci.yml"
@ -110,7 +121,7 @@ benchmark:
- mix ecto.migrate
- mix pleroma.load_testing
unit-testing:
unit-testing-1.12.3:
extends:
- .build_changes_policy
- .using-ci-base
@ -118,12 +129,11 @@ unit-testing:
cache: &testing_cache_policy
<<: *global_cache_policy
policy: pull
services:
services: &testing_services
- name: postgres:13-alpine
alias: postgres
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
script:
script: &testing_script
- mix ecto.create
- mix ecto.migrate
- mix test --cover --preload-modules
@ -134,27 +144,32 @@ unit-testing:
coverage_format: cobertura
path: coverage.xml
unit-testing-erratic:
unit-testing-1.15.7-otp-25:
extends:
- .build_changes_policy
- .using-ci-base
stage: test
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.15-otp25
allow_failure: true
cache: *testing_cache_policy
services: *testing_services
script: *testing_script
unit-testing-1.12-erratic:
extends:
- .build_changes_policy
- .using-ci-base
stage: test
retry: 2
allow_failure: true
cache: &testing_cache_policy
<<: *global_cache_policy
policy: pull
services:
- name: postgres:13-alpine
alias: postgres
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
cache: *testing_cache_policy
services: *testing_services
script:
- mix ecto.create
- mix ecto.migrate
- mix test --only=erratic
unit-testing-rum:
unit-testing-1.12-rum:
extends:
- .build_changes_policy
- .using-ci-base
@ -173,10 +188,10 @@ unit-testing-rum:
- "mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/"
- mix test --preload-modules
lint:
formatting-1.13:
extends: .build_changes_policy
image: &current_elixir elixir:1.13-alpine
stage: test
image: &formatting_elixir elixir:1.13-alpine
stage: lint
cache: *testing_cache_policy
before_script: &current_bfr_script
- apk update
@ -187,25 +202,38 @@ lint:
script:
- mix format --check-formatted
analysis:
extends:
- .build_changes_policy
- .using-ci-base
stage: test
cache: *testing_cache_policy
script:
- mix credo --strict --only=warnings,todo,fixme,consistency,readability
cycles:
cycles-1.13:
extends: .build_changes_policy
image: *current_elixir
stage: test
image: *formatting_elixir
stage: lint
cache: {}
before_script: *current_bfr_script
script:
- mix compile
- mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != "No cycles found")}'
analysis:
extends:
- .build_changes_policy
- .using-ci-base
stage: lint
cache: *testing_cache_policy
script:
- mix credo --strict --only=warnings,todo,fixme,consistency,readability
dialyzer:
extends:
- .build_changes_policy
- .using-ci-base
stage: lint
allow_failure: true
when: manual
cache: *testing_cache_policy
tags:
- feld
script:
- mix dialyzer
docs-deploy:
stage: deploy
cache: *testing_cache_policy

View file

View file

View file

View file

View file

View file

0
changelog.d/exile.skip Normal file
View file

View file

@ -0,0 +1 @@
Remote object fetch failures will prevent the object fetch job from retrying if the object request returns 401, 403, 404, 410, or exceeds the maximum thread depth.

View file

@ -0,0 +1 @@
Mastodon API /api/v1/directory: Fix listing directory contents when not authenticated

View file

@ -0,0 +1 @@
Federated timeline removal of hashtags via MRF HashtagPolicy

View file

@ -1 +0,0 @@
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:latest --push .

View file

@ -0,0 +1 @@
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.12 --push .

View file

@ -0,0 +1,8 @@
FROM elixir:1.15.7-otp-25
# Single RUN statement, otherwise intermediate images are created
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
RUN apt-get update &&\
apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\
mix local.hex --force &&\
mix local.rebar --force

View file

@ -0,0 +1 @@
docker buildx build --platform linux/amd64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.15-otp25 --push .

View file

@ -79,6 +79,10 @@ IO.puts("RUM enabled: #{rum_enabled}")
config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock
config :pleroma, Pleroma.Application,
background_migrators: false,
streamer_registry: false
if File.exists?("./config/benchmark.secret.exs") do
import_config "benchmark.secret.exs"
else

View file

@ -904,6 +904,15 @@ config :pleroma, Pleroma.Search.Meilisearch,
private_key: nil,
initial_indexing_chunk_size: 100_000
config :pleroma, Pleroma.Application,
background_migrators: true,
internal_fetch: true,
load_custom_modules: true,
max_restarts: 3,
streamer_registry: true
config :pleroma, Pleroma.Uploaders.Uploader, timeout: 30_000
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"

View file

@ -162,6 +162,18 @@ peer_module =
config :pleroma, Pleroma.Cluster, peer_module: peer_module
config :pleroma, Pleroma.Application,
background_migrators: false,
internal_fetch: false,
load_custom_modules: false,
max_restarts: 100,
streamer_registry: false,
test_http_pools: true
config :pleroma, Pleroma.Uploaders.Uploader, timeout: 1_000
config :pleroma, Pleroma.Emoji.Loader, test_emoji: true
if File.exists?("./config/test.secret.exs") do
import_config "test.secret.exs"
else

View file

@ -352,6 +352,4 @@ defmodule Mix.Tasks.Pleroma.Instance do
enabled_filters
end
defp upload_filters(_), do: []
end

View file

@ -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

View file

@ -23,19 +23,21 @@ defmodule Pleroma.Announcement do
timestamps(type: :utc_datetime)
end
def change(struct, params \\ %{}) do
struct
|> cast(validate_params(struct, params), [:data, :starts_at, :ends_at, :rendered])
@doc "Generates changeset for %Pleroma.Announcement{}"
@spec changeset(%__MODULE__{}, map()) :: %Ecto.Changeset{}
def changeset(announcement \\ %__MODULE__{}, params \\ %{data: %{}}) do
announcement
|> cast(validate_params(announcement, params), [:data, :starts_at, :ends_at, :rendered])
|> validate_required([:data])
end
defp validate_params(struct, params) do
defp validate_params(announcement, params) do
base_data =
%{
"content" => "",
"all_day" => false
}
|> Map.merge((struct && struct.data) || %{})
|> Map.merge((announcement && announcement.data) || %{})
merged_data =
Map.merge(base_data, params.data)
@ -61,13 +63,13 @@ defmodule Pleroma.Announcement do
end
def add(params) do
changeset = change(%__MODULE__{}, params)
changeset = changeset(%__MODULE__{}, params)
Repo.insert(changeset)
end
def update(announcement, params) do
changeset = change(announcement, params)
changeset = changeset(announcement, params)
Repo.update(changeset)
end

View file

@ -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
@ -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)
@ -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)
@ -213,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
@ -242,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 =
@ -288,13 +295,11 @@ 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, [])

View file

@ -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
@ -193,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

View file

@ -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,

View file

@ -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,

View file

@ -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

View file

@ -57,7 +57,7 @@ defmodule Pleroma.Conversation do
3. Bump all relevant participations to 'unread'
"""
def create_or_bump_for(activity, opts \\ []) do
with true <- Pleroma.Web.ActivityPub.Visibility.is_direct?(activity),
with true <- Pleroma.Web.ActivityPub.Visibility.direct?(activity),
"Create" <- activity.data["type"],
%Object{} = object <- Object.normalize(activity, fetch: false),
true <- object.data["type"] in ["Note", "Question"],

View file

@ -12,6 +12,8 @@ defmodule Pleroma.DataMigration do
import Ecto.Changeset
import Ecto.Query
@type t :: %__MODULE__{}
schema "data_migrations" do
field(:name, :string)
field(:state, State, default: :pending)

View file

@ -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

View file

@ -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

View file

@ -6,7 +6,7 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ContentLanguageMap do
use Ecto.Type
import Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode,
only: [is_good_locale_code?: 1]
only: [good_locale_code?: 1]
def type, do: :map
@ -30,7 +30,7 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.ContentLanguageMap do
object
|> Enum.reduce({:ok, %{}}, fn
{lang, value}, {status, acc} when is_binary(lang) and is_binary(value) ->
if is_good_locale_code?(lang) do
if good_locale_code?(lang) do
{status, Map.put(acc, lang, value)}
else
{:modified, acc}

View file

@ -8,7 +8,7 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode do
def type, do: :string
def cast(language) when is_binary(language) do
if is_good_locale_code?(language) do
if good_locale_code?(language) do
{:ok, language}
else
{:error, :invalid_language}
@ -21,7 +21,7 @@ defmodule Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode do
def load(data), do: {:ok, data}
def is_good_locale_code?(code) when is_binary(code), do: code =~ ~r<^[a-zA-Z0-9\-]+$>
def good_locale_code?(code) when is_binary(code), do: code =~ ~r<^[a-zA-Z0-9\-]+$>
def is_good_locale_code?(_code), do: false
def good_locale_code?(_code), do: false
end

View file

@ -24,6 +24,8 @@ defmodule Pleroma.Emoji do
defstruct [:code, :file, :tags, :safe_code, :safe_file]
@type t :: %__MODULE__{}
@doc "Build emoji struct"
def build({code, file, tags}) do
%__MODULE__{
@ -49,12 +51,12 @@ defmodule Pleroma.Emoji do
end
@doc "Returns the path of the emoji `name`."
@spec get(String.t()) :: String.t() | nil
@spec get(String.t()) :: Pleroma.Emoji.t() | nil
def get(name) do
name = maybe_strip_name(name)
case :ets.lookup(@ets, name) do
[{_, path}] -> path
[{_, emoji}] -> emoji
_ -> nil
end
end
@ -136,23 +138,23 @@ defmodule Pleroma.Emoji do
emojis = emojis ++ regional_indicators
for emoji <- emojis do
def is_unicode_emoji?(unquote(emoji)), do: true
def unicode?(unquote(emoji)), do: true
end
def is_unicode_emoji?(_), do: false
def unicode?(_), do: false
@emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/
def is_custom_emoji?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s)
def custom?(s) when is_binary(s), do: Regex.match?(@emoji_regex, s)
def is_custom_emoji?(_), do: false
def custom?(_), do: false
def maybe_strip_name(name) when is_binary(name), do: String.trim(name, ":")
def maybe_strip_name(name), do: name
def maybe_quote(name) when is_binary(name) do
if is_unicode_emoji?(name) do
if unicode?(name) do
name
else
if String.starts_with?(name, ":") do

View file

@ -15,8 +15,6 @@ defmodule Pleroma.Emoji.Loader do
require Logger
@mix_env Mix.env()
@type pattern :: Regex.t() | module() | String.t()
@type patterns :: pattern() | [pattern()]
@type group_patterns :: keyword(patterns())
@ -79,7 +77,7 @@ defmodule Pleroma.Emoji.Loader do
# for testing emoji.txt entries we do not want exposed in normal operation
test_emoji =
if @mix_env == :test do
if Application.get_env(:pleroma, __MODULE__)[:test_emoji] do
load_from_file("test/config/emoji.txt", emoji_groups)
else
[]

View file

@ -114,7 +114,7 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
def response("/notices/" <> id) do
with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.is_public?(activity) do
true <- Visibility.public?(activity) do
activities =
ActivityPub.fetch_activities_for_context(activity.data["context"])
|> render_activities

View file

@ -9,7 +9,7 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
def start_monitor do
pid =
case :gen_server.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
case GenServer.start_link(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
{:ok, pid} ->
pid

View file

@ -21,7 +21,7 @@ defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do
def start_worker(opts, retry \\ false) do
case DynamicSupervisor.start_child(__MODULE__, {Pleroma.Gun.ConnectionPool.Worker, opts}) do
{:error, :max_children} ->
if retry or free_pool() == :error do
if Enum.any?([retry, free_pool()], &match?(&1, :error)) do
:telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts})
{:error, :pool_full}
else

View file

@ -43,89 +43,28 @@ defmodule Pleroma.Helpers.MediaHelper do
def video_framegrab(url) do
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
{:ok, env} <- HTTP.get(url, [], pool: :media),
{:ok, fifo_path} <- mkfifo(),
args = [
"-y",
"-i",
fifo_path,
"-vframes",
"1",
"-f",
"mjpeg",
"-loglevel",
"error",
"-"
] do
run_fifo(fifo_path, env, executable, args)
{:ok, pid} <- StringIO.open(env.body) do
body_stream = IO.binstream(pid, 1)
Exile.stream!(
[
executable,
"-i",
"pipe:0",
"-vframes",
"1",
"-f",
"mjpeg",
"pipe:1"
],
input: body_stream,
ignore_epipe: true,
stderr: :disable
)
|> Enum.into(<<>>)
else
nil -> {:error, {:ffmpeg, :command_not_found}}
{:error, _} = error -> error
end
end
defp run_fifo(fifo_path, env, executable, args) do
pid =
Port.open({:spawn_executable, executable}, [
:use_stdio,
:stream,
:exit_status,
:binary,
args: args
])
fifo = Port.open(to_charlist(fifo_path), [:eof, :binary, :stream, :out])
fix = Pleroma.Helpers.QtFastStart.fix(env.body)
true = Port.command(fifo, fix)
:erlang.port_close(fifo)
loop_recv(pid)
after
File.rm(fifo_path)
end
defp mkfifo do
path = Path.join(System.tmp_dir!(), "pleroma-media-preview-pipe-#{Ecto.UUID.generate()}")
case System.cmd("mkfifo", [path]) do
{_, 0} ->
spawn(fifo_guard(path))
{:ok, path}
{_, err} ->
{:error, {:fifo_failed, err}}
end
end
defp fifo_guard(path) do
pid = self()
fn ->
ref = Process.monitor(pid)
receive do
{:DOWN, ^ref, :process, ^pid, _} ->
File.rm(path)
end
end
end
defp loop_recv(pid) do
loop_recv(pid, <<>>)
end
defp loop_recv(pid, acc) do
receive do
{^pid, {:data, data}} ->
loop_recv(pid, acc <> data)
{^pid, {:exit_status, 0}} ->
{:ok, acc}
{^pid, {:exit_status, status}} ->
{:error, status}
after
5000 ->
:erlang.port_close(pid)
{:error, :timeout}
end
end
end

View file

@ -126,9 +126,15 @@ defmodule Pleroma.Helpers.QtFastStart do
<<pos::integer-big-size(unquote(size)), rest::bits>>,
acc
) do
rewrite_entries(unquote(size), offset, rest, [
acc | <<pos + offset::integer-big-size(unquote(size))>>
])
rewrite_entries(
unquote(size),
offset,
rest,
acc ++
[
<<pos + offset::integer-big-size(unquote(size))>>
]
)
end
end

View file

@ -15,8 +15,8 @@ defmodule Pleroma.HTTP.AdapterHelper do
require Logger
@type proxy ::
{Connection.host(), pos_integer()}
| {Connection.proxy_type(), Connection.host(), pos_integer()}
{host(), pos_integer()}
| {proxy_type(), host(), pos_integer()}
@callback options(keyword(), URI.t()) :: keyword()

View file

@ -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})

View file

@ -188,10 +188,11 @@ defmodule Pleroma.Migrators.Support.BaseMigrator do
end
defp fault_rate do
with failures_count when is_integer(failures_count) <- failures_count() do
with failures_count when is_integer(failures_count) <- failures_count(),
true <- failures_count > 0 do
failures_count / Enum.max([get_stat(:affected_count, 0), 1])
else
_ -> :error
_ -> 0
end
end

View file

@ -121,7 +121,7 @@ defmodule Pleroma.ModerationLog do
defp prepare_log_data(attrs), do: attrs
@spec insert_log(log_params()) :: {:ok, ModerationLog} | {:error, any}
@spec insert_log(log_params()) :: {:ok, ModerationLog.t()} | {:error, any}
def insert_log(%{actor: %User{}, subject: subjects, permission: permission} = attrs) do
data =
attrs
@ -248,7 +248,8 @@ defmodule Pleroma.ModerationLog do
|> insert_log_entry_with_message()
end
@spec insert_log_entry_with_message(ModerationLog) :: {:ok, ModerationLog} | {:error, any}
@spec insert_log_entry_with_message(ModerationLog.t()) ::
{:ok, ModerationLog.t()} | {:error, any}
defp insert_log_entry_with_message(entry) do
entry.data["message"]
|> put_in(get_log_entry_message(entry))

View file

@ -177,7 +177,10 @@ defmodule Pleroma.Object do
ap_id
Keyword.get(options, :fetch) ->
Fetcher.fetch_object_from_id!(ap_id, options)
case Fetcher.fetch_object_from_id(ap_id, options) do
{:ok, object} -> object
_ -> nil
end
true ->
get_cached_by_ap_id(ap_id)
@ -239,17 +242,17 @@ defmodule Pleroma.Object do
{:ok, _} <- invalid_object_cache(object) do
cleanup_attachments(
Config.get([:instance, :cleanup_attachments]),
%{"object" => object}
object
)
{:ok, object, deleted_activity}
end
end
@spec cleanup_attachments(boolean(), %{required(:object) => map()}) ::
@spec cleanup_attachments(boolean(), Object.t()) ::
{:ok, Oban.Job.t() | nil}
def cleanup_attachments(true, %{"object" => _} = params) do
AttachmentsCleanupWorker.enqueue("cleanup_attachments", params)
def cleanup_attachments(true, %Object{} = object) do
AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{"object" => object})
end
def cleanup_attachments(_, _), do: {:ok, nil}

View file

@ -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}

View file

@ -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

View file

@ -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()

View file

@ -81,16 +81,16 @@ defmodule Pleroma.ReverseProxy do
import Plug.Conn
@type option() ::
{:max_read_duration, :timer.time() | :infinity}
{:max_read_duration, non_neg_integer() | :infinity}
| {:max_body_length, non_neg_integer() | :infinity}
| {:failed_request_ttl, :timer.time() | :infinity}
| {:http, []}
| {:failed_request_ttl, non_neg_integer() | :infinity}
| {:http, keyword()}
| {:req_headers, [{String.t(), String.t()}]}
| {:resp_headers, [{String.t(), String.t()}]}
| {:inline_content_types, boolean() | [String.t()]}
| {:inline_content_types, boolean() | list(String.t())}
| {:redirect_on_failure, boolean()}
@spec call(Plug.Conn.t(), url :: String.t(), [option()]) :: Plug.Conn.t()
@spec call(Plug.Conn.t(), String.t(), list(option())) :: Plug.Conn.t()
def call(_conn, _url, _opts \\ [])
def call(conn = %{method: method}, url, opts) when method in @methods do
@ -388,8 +388,6 @@ defmodule Pleroma.ReverseProxy do
defp body_size_constraint(_, _), do: :ok
defp check_read_duration(nil = _duration, max), do: check_read_duration(@max_read_duration, max)
defp check_read_duration(duration, max)
when is_integer(duration) and is_integer(max) and max > 0 do
if duration > max do
@ -407,10 +405,6 @@ defmodule Pleroma.ReverseProxy do
{:ok, previous_duration + duration}
end
defp increase_read_duration(_) do
{:ok, :no_duration_limit, :no_duration_limit}
end
defp client, do: Pleroma.ReverseProxy.Client.Wrapper
defp track_failed_url(url, error, opts) do

View file

@ -20,5 +20,5 @@ defmodule Pleroma.Search.SearchBackend do
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()}
@callback remove_from_index(object :: Pleroma.Object.t()) :: :ok | {:error, any()}
end

View file

@ -27,7 +27,7 @@ defmodule Pleroma.Signature do
_ ->
case Pleroma.Web.WebFinger.finger(maybe_ap_id) do
%{"ap_id" => ap_id} -> {:ok, ap_id}
{:ok, %{"ap_id" => ap_id}} -> {:ok, ap_id}
_ -> {:error, maybe_ap_id}
end
end

View file

@ -51,6 +51,7 @@ defmodule Pleroma.Upload do
| {:size_limit, nil | non_neg_integer()}
| {:uploader, module()}
| {:filters, [module()]}
| {:actor, String.t()}
@type t :: %__MODULE__{
id: String.t(),
@ -86,7 +87,7 @@ defmodule Pleroma.Upload do
end
end
@spec store(source, options :: [option()]) :: {:ok, Map.t()} | {:error, any()}
@spec store(source, options :: [option()]) :: {:ok, map()} | {:error, any()}
@doc "Store a file. If using a `Plug.Upload{}` as the source, be sure to use `Majic.Plug` to ensure its content_type and filename is correct."
def store(upload, opts \\ []) do
opts = get_opts(opts)
@ -175,7 +176,7 @@ defmodule Pleroma.Upload do
defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
data = Base.decode64!(parsed["data"], ignore: :whitespace)
hash = Base.encode16(:crypto.hash(:sha256, data), lower: true)
hash = Base.encode16(:crypto.hash(:sha256, data), case: :upper)
with :ok <- check_binary_size(data, opts.size_limit),
tmp_path <- tempfile_for_image(data),

View file

@ -5,8 +5,6 @@
defmodule Pleroma.Uploaders.Uploader do
import Pleroma.Web.Gettext
@mix_env Mix.env()
@moduledoc """
Defines the contract to put and get an uploaded file to any backend.
"""
@ -40,7 +38,7 @@ defmodule Pleroma.Uploaders.Uploader do
@callback delete_file(file :: String.t()) :: :ok | {:error, String.t()}
@callback http_callback(Plug.Conn.t(), Map.t()) ::
@callback http_callback(Plug.Conn.t(), map()) ::
{:ok, Plug.Conn.t()}
| {:ok, Plug.Conn.t(), file_spec()}
| {:error, Plug.Conn.t(), String.t()}
@ -75,10 +73,5 @@ defmodule Pleroma.Uploaders.Uploader do
end
end
defp callback_timeout do
case @mix_env do
:test -> 1_000
_ -> 30_000
end
end
defp callback_timeout, do: Application.get_env(:pleroma, __MODULE__)[:timeout]
end

View file

@ -672,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)
@ -693,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
@ -1011,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()
@ -1048,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
@ -1783,14 +1784,17 @@ defmodule Pleroma.User do
BackgroundWorker.enqueue("user_activation", %{"user_id" => user.id, "status" => status})
end
@spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
@spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
def set_activation(users, status) when is_list(users) do
Repo.transaction(fn ->
for user <- users, do: set_activation(user, status)
for user <- users do
{:ok, user} = set_activation(user, status)
user
end
end)
end
@spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Changeset.t()}
@spec set_activation(User.t(), boolean()) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
def set_activation(%User{} = user, status) do
with {:ok, user} <- set_activation_status(user, status) do
user
@ -1868,7 +1872,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/
@ -2359,7 +2363,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)
@ -2403,9 +2407,9 @@ defmodule Pleroma.User do
defp put_password_hash(changeset), do: changeset
def is_internal_user?(%User{nickname: nil}), do: true
def is_internal_user?(%User{local: true, nickname: "internal." <> _}), do: true
def is_internal_user?(_), do: false
def internal?(%User{nickname: nil}), do: true
def internal?(%User{local: true, nickname: "internal." <> _}), do: true
def internal?(_), do: false
# A hack because user delete activities have a fake id for whatever reason
# TODO: Get rid of this
@ -2537,7 +2541,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
@ -2555,9 +2559,9 @@ defmodule Pleroma.User do
cast(user, params, [:is_confirmed, :confirmation_token])
end
@spec approval_changeset(User.t(), keyword()) :: Changeset.t()
def approval_changeset(user, set_approval: approved?) do
cast(user, %{is_approved: approved?}, [:is_approved])
@spec approval_changeset(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()
def approval_changeset(changeset, set_approval: approved?) do
cast(changeset, %{is_approved: approved?}, [:is_approved])
end
@spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()}

View file

@ -22,6 +22,8 @@ defmodule Pleroma.User.Backup do
alias Pleroma.Web.ActivityPub.UserView
alias Pleroma.Workers.BackupWorker
@type t :: %__MODULE__{}
schema "backups" do
field(:content_type, :string)
field(:file_name, :string)
@ -195,6 +197,7 @@ defmodule Pleroma.User.Backup do
end
@files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json']
@spec export(Pleroma.User.Backup.t(), pid()) :: {:ok, String.t()} | :error
def export(%__MODULE__{} = backup, caller_pid) do
backup = Repo.preload(backup, :user)
dir = backup_tempdir(backup)
@ -204,9 +207,11 @@ defmodule Pleroma.User.Backup do
:ok <- statuses(dir, backup.user, caller_pid),
:ok <- likes(dir, backup.user, caller_pid),
:ok <- bookmarks(dir, backup.user, caller_pid),
{:ok, zip_path} <- :zip.create(String.to_charlist(dir <> ".zip"), @files, cwd: dir),
{:ok, zip_path} <- :zip.create(backup.file_name, @files, cwd: dir),
{:ok, _} <- File.rm_rf(dir) do
{:ok, to_string(zip_path)}
{:ok, zip_path}
else
_ -> :error
end
end
@ -382,6 +387,8 @@ defmodule Pleroma.User.Backup.Processor do
[:file_size, :processed, :state]
)
|> Repo.update()
else
e -> {:error, e}
end
end
end

View file

@ -71,7 +71,7 @@ defmodule Pleroma.User.Query do
@equal_criteria [:email]
@contains_criteria [:ap_id, :nickname]
@spec build(Query.t(), criteria()) :: Query.t()
@spec build(Ecto.Query.t(), criteria()) :: Ecto.Query.t()
def build(query \\ base_query(), criteria) do
prepare_query(query, criteria)
end

View file

@ -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

View file

@ -14,6 +14,8 @@ defmodule Pleroma.UserRelationship do
alias Pleroma.User
alias Pleroma.UserRelationship
@type t :: %__MODULE__{}
schema "user_relationships" do
belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
belongs_to(:target, User, type: FlakeId.Ecto.CompatType)

View file

@ -74,22 +74,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp check_remote_limit(_), do: true
def increase_note_count_if_public(actor, object) do
if is_public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
if public?(object), do: User.increase_note_count(actor), else: {:ok, actor}
end
def decrease_note_count_if_public(actor, object) do
if is_public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
if public?(object), do: User.decrease_note_count(actor), else: {:ok, actor}
end
def update_last_status_at_if_public(actor, object) do
if is_public?(object), do: User.update_last_status_at(actor), else: {:ok, actor}
if public?(object), do: User.update_last_status_at(actor), else: {:ok, actor}
end
defp increase_replies_count_if_reply(%{
"object" => %{"inReplyTo" => reply_ap_id} = object,
"type" => "Create"
}) do
if is_public?(object) do
if public?(object) do
Object.increase_replies_count(reply_ap_id)
end
end
@ -100,7 +100,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
"object" => %{"quoteUrl" => quote_ap_id} = object,
"type" => "Create"
}) do
if is_public?(object) do
if public?(object) do
Object.increase_quotes_count(quote_ap_id)
end
end
@ -499,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))
@ -1698,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

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
This module encodes our addressing policies and general shape of our objects.
"""
alias Pleroma.Activity
alias Pleroma.Emoji
alias Pleroma.Object
alias Pleroma.User
@ -131,7 +132,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
def emoji_react(actor, object, emoji) do
with {:ok, data, meta} <- object_action(actor, object) do
data =
if Emoji.is_unicode_emoji?(emoji) do
if Emoji.unicode?(emoji) do
unicode_emoji_react(object, data, emoji)
else
custom_emoji_react(object, data, emoji)
@ -347,7 +348,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
actor.ap_id == Relay.ap_id() ->
[actor.follower_address]
public? and Visibility.is_local_public?(object) ->
public? and Visibility.local_public?(object) ->
[actor.follower_address, object.data["actor"], Utils.as_local_public()]
public? ->
@ -375,7 +376,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
# Address the actor of the object, and our actor's follower collection if the post is public.
to =
if Visibility.is_public?(object) do
if Visibility.public?(object) do
[actor.follower_address, object.data["actor"]]
else
[object.data["actor"]]

View file

@ -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)

View file

@ -84,7 +84,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.HashtagPolicy do
if hashtags != [] do
with {:ok, message} <- check_reject(message, hashtags),
{:ok, message} <-
(if "type" == "Create" do
(if type == "Create" do
check_ftl_removal(message, hashtags)
else
{:ok, message}

View file

@ -62,7 +62,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy do
key: :mrf_inline_quote,
related_policy: "Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy",
label: "MRF Inline Quote Policy",
type: :group,
description: "Force quote url to appear in post content.",
children: [
%{

View file

@ -10,15 +10,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
@moduledoc "Reject or Word-Replace messages with a keyword or regex"
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
defp string_matches?(string, _) when not is_binary(string) do
false
end
defp string_matches?(string, pattern) when is_binary(pattern) do
String.contains?(string, pattern)
end
defp string_matches?(string, pattern) do
defp string_matches?(string, %Regex{} = pattern) do
String.match?(string, pattern)
end

View file

@ -10,9 +10,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
@impl true
def filter(%{"actor" => actor} = object) do
with true <- is_local?(actor),
true <- is_eligible_type?(object),
true <- is_note?(object),
with true <- local?(actor),
true <- eligible_type?(object),
true <- note?(object),
false <- has_attachment?(object),
true <- only_mentions?(object) do
{:reject, "[NoEmptyPolicy]"}
@ -24,7 +24,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
def filter(object), do: {:ok, object}
defp is_local?(actor) do
defp local?(actor) do
if actor |> String.starts_with?("#{Endpoint.url()}") do
true
else
@ -59,11 +59,11 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
defp only_mentions?(_), do: false
defp is_note?(%{"object" => %{"type" => "Note"}}), do: true
defp is_note?(_), do: false
defp note?(%{"object" => %{"type" => "Note"}}), do: true
defp note?(_), do: false
defp is_eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true
defp is_eligible_type?(_), do: false
defp eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true
defp eligible_type?(_), do: false
@impl true
def describe, do: {:ok, %{}}

View file

@ -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(),

View file

@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.QuoteToLinkTagPolicy do
tags = object["tag"] || []
if Enum.any?(tags, fn tag ->
CommonFixes.is_object_link_tag(tag) and tag["href"] == quote_url
CommonFixes.object_link_tag?(tag) and tag["href"] == quote_url
end) do
object
else

View file

@ -181,6 +181,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
{:object_validation, e} ->
e
{:error, %Ecto.Changeset{} = e} ->
{:error, e}
end
end

View file

@ -82,7 +82,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator do
object when is_binary(object) <- get_field(cng, :object),
%User{} = actor <- User.get_cached_by_ap_id(actor),
%Object{} = object <- Object.get_cached_by_ap_id(object),
false <- Visibility.is_public?(object) do
false <- Visibility.public?(object) do
same_actor = object.data["actor"] == actor.ap_id
recipients = get_field(cng, :to) ++ get_field(cng, :cc)
local_public = Utils.as_local_public()

View file

@ -11,7 +11,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
alias Pleroma.Web.ActivityPub.Utils
import Pleroma.EctoType.ActivityPub.ObjectValidators.LanguageCode,
only: [is_good_locale_code?: 1]
only: [good_locale_code?: 1]
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
@ -104,7 +104,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
end
def fix_quote_url(%{"tag" => [_ | _] = tags} = data) do
tag = Enum.find(tags, &is_object_link_tag/1)
tag = Enum.find(tags, &object_link_tag?/1)
if not is_nil(tag) do
data
@ -117,7 +117,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
def fix_quote_url(data), do: data
# https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md
def is_object_link_tag(%{
def object_link_tag?(%{
"type" => "Link",
"mediaType" => media_type,
"href" => href
@ -126,7 +126,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
true
end
def is_object_link_tag(_), do: false
def object_link_tag?(_), do: false
def maybe_add_language_from_activity(object, activity) do
language = get_language_from_context(activity)
@ -144,7 +144,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
get_language_from_context(object),
get_language_from_content_map(object)
]
|> Enum.find(&is_good_locale_code?(&1))
|> Enum.find(&good_locale_code?(&1))
if language do
Map.put(object, "language", language)

View file

@ -74,10 +74,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji)
cond do
Pleroma.Emoji.is_unicode_emoji?(emoji) ->
Pleroma.Emoji.unicode?(emoji) ->
data
Pleroma.Emoji.is_unicode_emoji?(new_emoji) ->
Pleroma.Emoji.unicode?(new_emoji) ->
data |> Map.put("content", new_emoji)
true ->
@ -90,7 +90,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
defp validate_emoji(cng) do
content = get_field(cng, :content)
if Emoji.is_unicode_emoji?(content) || Emoji.is_custom_emoji?(content) do
if Emoji.unicode?(content) || Emoji.custom?(content) do
cng
else
cng
@ -101,7 +101,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do
defp maybe_validate_tag_presence(cng) do
content = get_field(cng, :content)
if Emoji.is_unicode_emoji?(content) do
if Emoji.unicode?(content) do
cng
else
tag = get_field(cng, :tag)

View file

@ -62,7 +62,7 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
with {:ok, local} <- Keyword.fetch(meta, :local) do
do_not_federate = meta[:do_not_federate] || !config().get([:instance, :federating])
if !do_not_federate and local and not Visibility.is_local_public?(activity) do
if !do_not_federate and local and not Visibility.local_public?(activity) do
activity =
if object = Keyword.get(meta, :object_data) do
%{activity | data: Map.put(activity.data, "object", object)}

View file

@ -28,7 +28,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
@doc """
Enqueue publishing a single activity.
"""
@spec enqueue_one(Map.t(), Keyword.t()) :: {:ok, %Oban.Job{}}
@spec enqueue_one(map(), Keyword.t()) :: {:ok, %Oban.Job{}}
def enqueue_one(%{} = params, worker_args \\ []) do
PublisherWorker.enqueue(
"publish_one",
@ -66,7 +66,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
@doc """
Determine if an activity can be represented by running it through Transmogrifier.
"""
def is_representable?(%Activity{} = activity) do
def representable?(%Activity{} = activity) do
with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do
true
else
@ -246,7 +246,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity)
when is_list(bcc) and bcc != [] do
public = is_public?(activity)
public = public?(activity)
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
[priority_recipients, recipients] = recipients(actor, activity)
@ -291,7 +291,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
# Publishes an activity to all relevant peers.
def publish(%User{} = actor, %Activity{} = activity) do
public = is_public?(activity)
public = public?(activity)
if public && Config.get([:instance, :allow_relay]) do
Logger.debug(fn -> "Relaying #{activity.data["id"]} out" end)

View file

@ -58,7 +58,7 @@ defmodule Pleroma.Web.ActivityPub.Relay do
@spec publish(any()) :: {:ok, Activity.t()} | {:error, any()}
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
with %User{} = user <- get_actor(),
true <- Visibility.is_public?(activity) do
true <- Visibility.public?(activity) do
CommonAPI.repeat(activity.id, user)
else
error -> format_error(error)

View file

@ -258,7 +258,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
Utils.add_announce_to_object(object, announced_object)
if !User.is_internal_user?(user) do
if !User.internal?(user) do
Notification.create_notifications(object)
ap_streamer().stream_out(object)
@ -304,9 +304,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
result =
case deleted_object do
%Object{} ->
with {:ok, deleted_object, _activity} <- Object.delete(deleted_object),
with {_, {:ok, deleted_object, _activity}} <- {:object, Object.delete(deleted_object)},
{_, actor} when is_binary(actor) <- {:actor, deleted_object.data["actor"]},
%User{} = user <- User.get_cached_by_ap_id(actor) do
{_, %User{} = user} <- {:user, User.get_cached_by_ap_id(actor)} do
User.remove_pinned_object_id(user, deleted_object.data["id"])
{:ok, user} = ActivityPub.decrease_note_count_if_public(user, deleted_object)
@ -328,6 +328,17 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
{:actor, _} ->
@logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
:no_object_actor
{:user, _} ->
@logger.error(
"The object's actor could not be resolved to a user: #{inspect(deleted_object)}"
)
:no_object_user
{:object, _} ->
@logger.error("The object could not be deleted: #{inspect(deleted_object)}")
{:error, object}
end
%User{} ->
@ -569,7 +580,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
def handle_undoing(object), do: {:error, ["don't know how to handle", object]}
@spec delete_object(Object.t()) :: :ok | {:error, Ecto.Changeset.t()}
@spec delete_object(Activity.t()) :: :ok | {:error, Ecto.Changeset.t()}
defp delete_object(object) do
with {:ok, _} <- Repo.delete(object), do: :ok
end

View file

@ -4,5 +4,5 @@
defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do
@callback handle(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
@callback handle_after_transaction(map()) :: map()
@callback handle_after_transaction(keyword()) :: keyword()
end

View file

@ -24,7 +24,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
import Ecto.Query
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
require Logger
require Pleroma.Constants
@doc """
@ -156,8 +155,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("context", replied_object.data["context"] || object["conversation"])
|> Map.drop(["conversation", "inReplyToAtomUri"])
else
e ->
Logger.warning("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
_ ->
object
end
else
@ -182,8 +180,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:quoting?, _} ->
object
e ->
Logger.warning("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}")
_ ->
object
end
end
@ -785,7 +782,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Object.normalize(fetch: false)
data =
if Visibility.is_private?(object) && object.data["actor"] == ap_id do
if Visibility.private?(object) && object.data["actor"] == ap_id do
data |> Map.put("object", object |> Map.get(:data) |> prepare_object)
else
data |> maybe_fix_object_url
@ -855,8 +852,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
_ ->

View file

@ -174,7 +174,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
with true <- Config.get!([:instance, :federating]),
true <- type != "Block" || outgoing_blocks,
false <- Visibility.is_local_public?(activity) do
false <- Visibility.local_public?(activity) do
Pleroma.Web.Federator.publish(activity)
end
@ -284,7 +284,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
object_actor = User.get_cached_by_ap_id(object_actor_id)
to =
if Visibility.is_public?(object) do
if Visibility.public?(object) do
[actor.follower_address, object.data["actor"]]
else
[object.data["actor"]]
@ -783,10 +783,9 @@ defmodule Pleroma.Web.ActivityPub.Utils do
build_flag_object(object)
nil ->
if %Object{} = object = Object.get_by_ap_id(id) do
build_flag_object(object)
else
%{"id" => id, "deleted" => true}
case Object.get_by_ap_id(id) do
%Object{} = object -> build_flag_object(object)
_ -> %{"id" => id, "deleted" => true}
end
end
end

View file

@ -11,28 +11,28 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
require Pleroma.Constants
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
def is_public?(%Object{data: data}), do: is_public?(data)
def is_public?(%Activity{data: %{"type" => "Move"}}), do: true
def is_public?(%Activity{data: data}), do: is_public?(data)
def is_public?(%{"directMessage" => true}), do: false
@spec public?(Object.t() | Activity.t() | map()) :: boolean()
def public?(%Object{data: %{"type" => "Tombstone"}}), do: false
def public?(%Object{data: data}), do: public?(data)
def public?(%Activity{data: %{"type" => "Move"}}), do: true
def public?(%Activity{data: data}), do: public?(data)
def public?(%{"directMessage" => true}), do: false
def is_public?(data) do
def public?(data) do
Utils.label_in_message?(Pleroma.Constants.as_public(), data) or
Utils.label_in_message?(Utils.as_local_public(), data)
end
def is_local_public?(%Object{data: data}), do: is_local_public?(data)
def is_local_public?(%Activity{data: data}), do: is_local_public?(data)
def local_public?(%Object{data: data}), do: local_public?(data)
def local_public?(%Activity{data: data}), do: local_public?(data)
def is_local_public?(data) do
def local_public?(data) do
Utils.label_in_message?(Utils.as_local_public(), data) and
not Utils.label_in_message?(Pleroma.Constants.as_public(), data)
end
def is_private?(activity) do
with false <- is_public?(activity),
def private?(activity) do
with false <- public?(activity),
%User{follower_address: follower_address} <-
User.get_cached_by_ap_id(activity.data["actor"]) do
follower_address in activity.data["to"]
@ -41,20 +41,20 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
end
end
def is_announceable?(activity, user, public \\ true) do
is_public?(activity) ||
(!public && is_private?(activity) && activity.data["actor"] == user.ap_id)
def announceable?(activity, user, public \\ true) do
public?(activity) ||
(!public && private?(activity) && activity.data["actor"] == user.ap_id)
end
def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true
def is_direct?(%Object{data: %{"directMessage" => true}}), do: true
def direct?(%Activity{data: %{"directMessage" => true}}), do: true
def direct?(%Object{data: %{"directMessage" => true}}), do: true
def is_direct?(activity) do
!is_public?(activity) && !is_private?(activity)
def direct?(activity) do
!public?(activity) && !private?(activity)
end
def is_list?(%{data: %{"listMessage" => _}}), do: true
def is_list?(_), do: false
def list?(%{data: %{"listMessage" => _}}), do: true
def list?(_), do: false
@spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean()
def visible_for_user?(%Object{data: %{"type" => "Tombstone"}}, _), do: false
@ -77,7 +77,7 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
when module in [Activity, Object] do
if restrict_unauthenticated_access?(message),
do: false,
else: is_public?(message) and not is_local_public?(message)
else: public?(message) and not local_public?(message)
end
def visible_for_user?(%{__struct__: module} = message, user)
@ -86,8 +86,8 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || [])
user_is_local = user.local
federatable = not is_local_public?(message)
(is_public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable)
federatable = not local_public?(message)
(public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable)
end
def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do

View file

@ -9,7 +9,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
alias Pleroma.ConfigDB
alias Pleroma.Web.Plugs.OAuthScopesPlug
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action == :update)
plug(
@ -76,7 +76,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
json(conn, translate_descriptions(descriptions))
end
def show(conn, %{only_db: true}) do
def show(%{private: %{open_api_spex: %{params: %{only_db: true}}}} = conn, _) do
with :ok <- configurable_from_database() do
configs = Pleroma.Repo.all(ConfigDB)
@ -128,7 +128,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
end
end
def update(%{body_params: %{configs: configs}} = conn, _) do
def update(%{private: %{open_api_spex: %{body_params: %{configs: configs}}}} = conn, _) do
with :ok <- configurable_from_database() do
results =
configs

View file

@ -9,7 +9,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do
alias Pleroma.Web.Plugs.InstanceStatic
alias Pleroma.Web.Plugs.OAuthScopesPlug
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do
plug(OAuthScopesPlug, %{scopes: ["admin:read"]} when action == :show)
plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:update, :delete])
def show(conn, %{name: document_name}) do
def show(%{private: %{open_api_spex: %{params: %{name: document_name}}}} = conn, _) do
with {:ok, url} <- InstanceDocument.get(document_name),
{:ok, content} <- File.read(InstanceStatic.file_path(url)) do
conn
@ -27,13 +27,18 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do
end
end
def update(%{body_params: %{file: file}} = conn, %{name: document_name}) do
def update(
%{
private: %{open_api_spex: %{body_params: %{file: file}, params: %{name: document_name}}}
} = conn,
_
) do
with {:ok, url} <- InstanceDocument.put(document_name, file.path) do
json(conn, %{"url" => url})
end
end
def delete(conn, %{name: document_name}) do
def delete(%{private: %{open_api_spex: %{params: %{name: document_name}}}} = conn, _) do
with :ok <- InstanceDocument.delete(document_name) do
json(conn, %{})
end

View file

@ -13,7 +13,7 @@ defmodule Pleroma.Web.AdminAPI.InviteController do
require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug(OAuthScopesPlug, %{scopes: ["admin:read:invites"]} when action == :index)
plug(
@ -33,14 +33,14 @@ defmodule Pleroma.Web.AdminAPI.InviteController do
end
@doc "Create an account registration invite token"
def create(%{body_params: params} = conn, _) do
def create(%{private: %{open_api_spex: %{body_params: params}}} = conn, _) do
{:ok, invite} = UserInviteToken.create_invite(params)
render(conn, "show.json", invite: invite)
end
@doc "Revokes invite by token"
def revoke(%{body_params: %{token: token}} = conn, _) do
def revoke(%{private: %{open_api_spex: %{body_params: %{token: token}}}} = conn, _) do
with {:ok, invite} <- UserInviteToken.find_by_token(token),
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
render(conn, "show.json", invite: updated_invite)
@ -51,7 +51,13 @@ defmodule Pleroma.Web.AdminAPI.InviteController do
end
@doc "Sends registration invite via email"
def email(%{assigns: %{user: user}, body_params: %{email: email} = params} = conn, _) do
def email(
%{
assigns: %{user: user},
private: %{open_api_spex: %{body_params: %{email: email} = params}}
} = conn,
_
) do
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
{:ok, invite_token} <- UserInviteToken.create_invite(),

View file

@ -11,7 +11,7 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug(
OAuthScopesPlug,
@ -27,7 +27,7 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
defdelegate open_api_operation(action), to: Spec.MediaProxyCacheOperation
def index(%{assigns: %{user: _}} = conn, params) do
def index(%{assigns: %{user: _}, private: %{open_api_spex: %{params: params}}} = conn, _) do
entries = fetch_entries(params)
urls = paginate_entries(entries, params.page, params.page_size)
@ -59,12 +59,19 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
Enum.slice(entries, offset, page_size)
end
def delete(%{assigns: %{user: _}, body_params: %{urls: urls}} = conn, _) do
def delete(
%{assigns: %{user: _}, private: %{open_api_spex: %{body_params: %{urls: urls}}}} = conn,
_
) do
MediaProxy.remove_from_banned_urls(urls)
json(conn, %{})
end
def purge(%{assigns: %{user: _}, body_params: %{urls: urls, ban: ban}} = conn, _) do
def purge(
%{assigns: %{user: _}, private: %{open_api_spex: %{body_params: %{urls: urls, ban: ban}}}} =
conn,
_
) do
MediaProxy.Invalidation.purge(urls)
if ban do

View file

@ -11,7 +11,7 @@ defmodule Pleroma.Web.AdminAPI.RelayController do
require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug(
OAuthScopesPlug,
@ -31,7 +31,13 @@ defmodule Pleroma.Web.AdminAPI.RelayController do
end
end
def follow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn, _) do
def follow(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{relay_url: target}}}
} = conn,
_
) do
with {:ok, _message} <- Relay.follow(target) do
ModerationLog.insert_log(%{action: "relay_follow", actor: admin, target: target})
@ -44,7 +50,13 @@ defmodule Pleroma.Web.AdminAPI.RelayController do
end
end
def unfollow(%{assigns: %{user: admin}, body_params: %{relay_url: target} = params} = conn, _) do
def unfollow(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{relay_url: target} = params}}
} = conn,
_
) do
with {:ok, _message} <- Relay.unfollow(target, %{force: params[:force]}) do
ModerationLog.insert_log(%{action: "relay_unfollow", actor: admin, target: target})

View file

@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
require Logger
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug(OAuthScopesPlug, %{scopes: ["admin:read:reports"]} when action in [:index, :show])
plug(
@ -31,13 +31,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ReportOperation
def index(conn, params) do
def index(%{private: %{open_api_spex: %{params: params}}} = conn, _) do
reports = Utils.get_reports(params, params.page, params.page_size)
render(conn, "index.json", reports: reports)
end
def show(conn, %{id: id}) do
def show(%{private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do
with %Activity{} = report <- Activity.get_report(id) do
render(conn, "show.json", Report.extract_report_info(report))
else
@ -45,7 +45,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
end
end
def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn, _) do
def update(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{reports: reports}}}
} = conn,
_
) do
result =
Enum.map(reports, fn report ->
case CommonAPI.update_report_state(report.id, report.state) do
@ -73,9 +79,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
end
end
def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{
id: report_id
}) do
def notes_create(
%{
assigns: %{user: user},
private: %{open_api_spex: %{body_params: %{content: content}, params: %{id: report_id}}}
} = conn,
_
) do
with {:ok, _} <- ReportNote.create(user.id, report_id, content),
report <- Activity.get_by_id_with_user_actor(report_id) do
ModerationLog.insert_log(%{
@ -92,10 +102,20 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
end
end
def notes_delete(%{assigns: %{user: user}} = conn, %{
id: note_id,
report_id: report_id
}) do
def notes_delete(
%{
assigns: %{user: user},
private: %{
open_api_spex: %{
params: %{
id: note_id,
report_id: report_id
}
}
}
} = conn,
_
) do
with {:ok, note} <- ReportNote.destroy(note_id),
report <- Activity.get_by_id_with_user_actor(report_id) do
ModerationLog.insert_log(%{

View file

@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.UserController do
@users_page_size 50
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
plug(
OAuthScopesPlug,
@ -51,13 +51,22 @@ defmodule Pleroma.Web.AdminAPI.UserController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.UserOperation
def delete(conn, %{nickname: nickname}) do
def delete(%{private: %{open_api_spex: %{params: %{nickname: nickname}}}} = conn, _) do
conn
|> Map.put(:body_params, %{nicknames: [nickname]})
|> delete(%{})
|> do_deletes([nickname])
end
def delete(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
def delete(
%{
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
conn
|> do_deletes(nicknames)
end
defp do_deletes(%{assigns: %{user: admin}} = conn, nicknames) when is_list(nicknames) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
Enum.each(users, fn user ->
@ -77,9 +86,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
def follow(
%{
assigns: %{user: admin},
body_params: %{
follower: follower_nick,
followed: followed_nick
private: %{
open_api_spex: %{
body_params: %{
follower: follower_nick,
followed: followed_nick
}
}
}
} = conn,
_
@ -102,9 +115,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
def unfollow(
%{
assigns: %{user: admin},
body_params: %{
follower: follower_nick,
followed: followed_nick
private: %{
open_api_spex: %{
body_params: %{
follower: follower_nick,
followed: followed_nick
}
}
}
} = conn,
_
@ -124,7 +141,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
json(conn, "ok")
end
def create(%{assigns: %{user: admin}, body_params: %{users: users}} = conn, _) do
def create(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{users: users}}}
} = conn,
_
) do
changesets =
users
|> Enum.map(fn %{nickname: nickname, email: email, password: password} ->
@ -178,7 +201,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
end
end
def show(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do
def show(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{params: %{nickname: nickname}}}
} = conn,
_
) do
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
render(conn, "show.json", %{user: user})
else
@ -186,7 +215,11 @@ defmodule Pleroma.Web.AdminAPI.UserController do
end
end
def toggle_activation(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do
def toggle_activation(
%{assigns: %{user: admin}, private: %{open_api_spex: %{params: %{nickname: nickname}}}} =
conn,
_
) do
user = User.get_cached_by_nickname(nickname)
{:ok, updated_user} = User.set_activation(user, !user.is_active)
@ -202,7 +235,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
render(conn, "show.json", user: updated_user)
end
def activate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
def activate(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.set_activation(users, true)
@ -212,10 +251,16 @@ defmodule Pleroma.Web.AdminAPI.UserController do
action: "activate"
})
render(conn, "index.json", users: Keyword.values(updated_users))
render(conn, "index.json", users: updated_users)
end
def deactivate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
def deactivate(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.set_activation(users, false)
@ -225,10 +270,16 @@ defmodule Pleroma.Web.AdminAPI.UserController do
action: "deactivate"
})
render(conn, "index.json", users: Keyword.values(updated_users))
render(conn, "index.json", users: updated_users)
end
def approve(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
def approve(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.approve(users)
@ -241,7 +292,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
render(conn, "index.json", users: updated_users)
end
def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
def suggest(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.set_suggestion(users, true)
@ -254,7 +311,13 @@ defmodule Pleroma.Web.AdminAPI.UserController do
render(conn, "index.json", users: updated_users)
end
def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = conn, _) do
def unsuggest(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{nicknames: nicknames}}}
} = conn,
_
) do
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
{:ok, updated_users} = User.set_suggestion(users, false)
@ -267,7 +330,7 @@ defmodule Pleroma.Web.AdminAPI.UserController do
render(conn, "index.json", users: updated_users)
end
def index(conn, params) do
def index(%{private: %{open_api_spex: %{params: params}}} = conn, _) do
{page, page_size} = page_params(params)
filters = maybe_parse_filters(params[:filters])

View file

@ -27,10 +27,12 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
@impl Plug
def call(conn, %{operation_id: operation_id, render_error: render_error}) do
def call(conn, %{operation_id: operation_id, render_error: render_error} = opts) do
{spec, operation_lookup} = PutApiSpec.get_spec_and_operation_lookup(conn)
operation = operation_lookup[operation_id]
cast_opts = opts |> Map.take([:replace_params]) |> Map.to_list()
content_type =
case Conn.get_req_header(conn, "content-type") do
[header_value | _] ->
@ -44,7 +46,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
conn = Conn.put_private(conn, :operation_id, operation_id)
case cast_and_validate(spec, operation, conn, content_type, strict?()) do
case cast_and_validate(spec, operation, conn, content_type, strict?(), cast_opts) do
{:ok, conn} ->
conn
@ -94,11 +96,11 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
def call(conn, opts), do: OpenApiSpex.Plug.CastAndValidate.call(conn, opts)
defp cast_and_validate(spec, operation, conn, content_type, true = _strict) do
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type)
defp cast_and_validate(spec, operation, conn, content_type, true = _strict, cast_opts) do
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
end
defp cast_and_validate(spec, operation, conn, content_type, false = _strict) do
defp cast_and_validate(spec, operation, conn, content_type, false = _strict, cast_opts) do
case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
{:ok, conn} ->
{:ok, conn}
@ -123,7 +125,7 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
end)
conn = %Conn{conn | query_params: query_params}
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type)
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
end
end

View file

@ -62,7 +62,7 @@ defmodule Pleroma.Web.ApiSpec.Helpers do
Operation.parameter(
:with_relationships,
:query,
BooleanLike,
BooleanLike.schema(),
"Embed relationships into accounts. **If this parameter is not set account's `pleroma.relationship` is going to be `null`.**"
)
end

View file

@ -122,22 +122,27 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
parameters:
[
%Reference{"$ref": "#/components/parameters/accountIdOrNickname"},
Operation.parameter(:pinned, :query, BooleanLike, "Include only pinned statuses"),
Operation.parameter(
:pinned,
:query,
BooleanLike.schema(),
"Include only pinned statuses"
),
Operation.parameter(:tagged, :query, :string, "With tag"),
Operation.parameter(
:only_media,
:query,
BooleanLike,
BooleanLike.schema(),
"Include only statuses with media attached"
),
Operation.parameter(
:with_muted,
:query,
BooleanLike,
BooleanLike.schema(),
"Include statuses from muted accounts."
),
Operation.parameter(:exclude_reblogs, :query, BooleanLike, "Exclude reblogs"),
Operation.parameter(:exclude_replies, :query, BooleanLike, "Exclude replies"),
Operation.parameter(:exclude_reblogs, :query, BooleanLike.schema(), "Exclude reblogs"),
Operation.parameter(:exclude_replies, :query, BooleanLike.schema(), "Exclude replies"),
Operation.parameter(
:exclude_visibilities,
:query,
@ -147,7 +152,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
Operation.parameter(
:with_muted,
:query,
BooleanLike,
BooleanLike.schema(),
"Include reactions from muted accounts."
)
] ++ pagination_params(),

View file

@ -141,7 +141,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
end
def id_param do
Operation.parameter(:id, :path, FlakeID, "Report ID",
Operation.parameter(:id, :path, FlakeID.schema(), "Report ID",
example: "9umDrYheeY451cQnEe",
required: true
)

View file

@ -137,7 +137,12 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
"Deprecated due to no support for pagination. Using [/api/v2/pleroma/chats](#operation/ChatController.index2) instead is recommended.",
operationId: "ChatController.index",
parameters: [
Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users")
Operation.parameter(
:with_muted,
:query,
BooleanLike.schema(),
"Include chats from muted users"
)
],
responses: %{
200 => Operation.response("The chats of the user", "application/json", chats_response())
@ -156,7 +161,12 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
summary: "Retrieve list of chats",
operationId: "ChatController.index2",
parameters: [
Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users")
Operation.parameter(
:with_muted,
:query,
BooleanLike.schema(),
"Include chats from muted users"
)
| pagination_params()
],
responses: %{

View file

@ -29,7 +29,7 @@ defmodule Pleroma.Web.ApiSpec.DirectoryOperation do
"Order by recent activity or account creation",
required: nil
),
Operation.parameter(:local, :query, BooleanLike, "Include local users only")
Operation.parameter(:local, :query, BooleanLike.schema(), "Include local users only")
] ++ pagination_params(),
responses: %{
200 =>

View file

@ -21,7 +21,7 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do
summary:
"Get an object of emoji to account mappings with accounts that reacted to the post",
parameters: [
Operation.parameter(:id, :path, FlakeID, "Status ID", required: true),
Operation.parameter(:id, :path, FlakeID.schema(), "Status ID", required: true),
Operation.parameter(:emoji, :path, :string, "Filter by a single unicode emoji",
required: nil
),
@ -45,7 +45,7 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do
tags: ["Emoji reactions"],
summary: "React to a post with a unicode emoji",
parameters: [
Operation.parameter(:id, :path, FlakeID, "Status ID", required: true),
Operation.parameter(:id, :path, FlakeID.schema(), "Status ID", required: true),
Operation.parameter(:emoji, :path, :string, "A single character unicode emoji",
required: true
)
@ -64,7 +64,7 @@ defmodule Pleroma.Web.ApiSpec.EmojiReactionOperation do
tags: ["Emoji reactions"],
summary: "Remove a reaction to a post with a unicode emoji",
parameters: [
Operation.parameter(:id, :path, FlakeID, "Status ID", required: true),
Operation.parameter(:id, :path, FlakeID.schema(), "Status ID", required: true),
Operation.parameter(:emoji, :path, :string, "A single character unicode emoji",
required: true
)

View file

@ -62,7 +62,7 @@ defmodule Pleroma.Web.ApiSpec.NotificationOperation do
Operation.parameter(
:with_muted,
:query,
BooleanLike,
BooleanLike.schema(),
"Include the notifications from muted users"
)
] ++ pagination_params(),

View file

@ -142,7 +142,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
end
defp id_param do
Operation.parameter(:id, :path, FlakeID, "Account ID",
Operation.parameter(:id, :path, FlakeID.schema(), "Account ID",
example: "9umDrYheeY451cQnEe",
required: true
)

View file

@ -37,7 +37,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaStatusOperation do
end
def id_param do
Operation.parameter(:id, :path, FlakeID, "Status ID",
Operation.parameter(:id, :path, FlakeID.schema(), "Status ID",
example: "9umDrYheeY451cQnEe",
required: true
)

View file

@ -47,7 +47,7 @@ defmodule Pleroma.Web.ApiSpec.PollOperation do
end
defp id_param do
Operation.parameter(:id, :path, FlakeID, "Poll ID",
Operation.parameter(:id, :path, FlakeID.schema(), "Poll ID",
example: "123",
required: true
)

Some files were not shown because too many files have changed in this diff Show more