Merge remote-tracking branch 'origin/develop' into HEAD
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
c592a0e58d
|
@ -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."}
|
||||||
|
]
|
|
@ -57,5 +57,6 @@ pleroma.iml
|
||||||
.tool-versions
|
.tool-versions
|
||||||
|
|
||||||
# Editor temp files
|
# Editor temp files
|
||||||
/*~
|
*~
|
||||||
/*#
|
*#
|
||||||
|
*.swp
|
||||||
|
|
|
@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## 2.6.2
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- MRF StealEmojiPolicy: Sanitize shortcodes (thanks to Hazel K for the report
|
||||||
|
|
||||||
## 2.6.1
|
## 2.6.1
|
||||||
### Changed
|
### Changed
|
||||||
- - Document maximum supported version of Erlang & Elixir
|
- - Document maximum supported version of Erlang & Elixir
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Support Bandit as an alternative to Cowboy for the HTTP server.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix federation with Convergence AP Bridge
|
|
@ -0,0 +1 @@
|
||||||
|
- Config: Check the permissions of the linked file instead of the symlink
|
|
@ -0,0 +1 @@
|
||||||
|
MediaProxy was setting the content-length header which is not permitted by RFC9112§6.2 when we are chunking the reply as it conflicts with the existence of the transfer-encoding header.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix logic error in Gun connection pooling which prevented retries even when the worker was launched with retry = true
|
|
@ -0,0 +1 @@
|
||||||
|
Mastodon API /api/v1/directory: Fix listing directory contents when not authenticated
|
|
@ -0,0 +1 @@
|
||||||
|
Fix a memory leak caused by Websocket connections that would not enter a state where a full garbage collection run could be triggered.
|
|
@ -0,0 +1 @@
|
||||||
|
Federated timeline removal of hashtags via MRF HashtagPolicy
|
|
@ -0,0 +1 @@
|
||||||
|
Fix notifications query which was not using the index properly
|
|
@ -0,0 +1 @@
|
||||||
|
Use User.full_nickname/1 in oauth html template
|
|
@ -0,0 +1 @@
|
||||||
|
Rich Media Preview cache eviction when the activity is updated.
|
|
@ -0,0 +1 @@
|
||||||
|
Update Tesla HTTP client middleware to 1.8.0
|
|
@ -0,0 +1 @@
|
||||||
|
Refactor the Mastodon /api/v1/streaming websocket handler to use Phoenix.Socket.Transport
|
|
@ -1,4 +1,4 @@
|
||||||
FROM elixir:1.12.3
|
FROM elixir:1.15.7-otp-25
|
||||||
|
|
||||||
# Single RUN statement, otherwise intermediate images are created
|
# Single RUN statement, otherwise intermediate images are created
|
||||||
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
|
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
|
||||||
|
|
|
@ -114,14 +114,7 @@
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
url: [host: "localhost"],
|
url: [host: "localhost"],
|
||||||
http: [
|
http: [
|
||||||
ip: {127, 0, 0, 1},
|
ip: {127, 0, 0, 1}
|
||||||
dispatch: [
|
|
||||||
{:_,
|
|
||||||
[
|
|
||||||
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
|
|
||||||
{:_, Plug.Cowboy.Handler, {Pleroma.Web.Endpoint, []}}
|
|
||||||
]}
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
|
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
|
||||||
|
@ -911,6 +904,8 @@
|
||||||
max_restarts: 3,
|
max_restarts: 3,
|
||||||
streamer_registry: true
|
streamer_registry: true
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Uploaders.Uploader, timeout: 30_000
|
||||||
|
|
||||||
# Import environment specific config. This must remain at the bottom
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
import_config "#{Mix.env()}.exs"
|
import_config "#{Mix.env()}.exs"
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
# with brunch.io to recompile .js and .css sources.
|
# with brunch.io to recompile .js and .css sources.
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
http: [
|
http: [
|
||||||
port: 4000,
|
port: 4000
|
||||||
protocol_options: [max_request_line_length: 8192, max_header_value_length: 8192]
|
|
||||||
],
|
],
|
||||||
protocol: "http",
|
protocol: "http",
|
||||||
debug_errors: true,
|
debug_errors: true,
|
||||||
|
|
|
@ -170,6 +170,10 @@
|
||||||
streamer_registry: false,
|
streamer_registry: false,
|
||||||
test_http_pools: true
|
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
|
if File.exists?("./config/test.secret.exs") do
|
||||||
import_config "test.secret.exs"
|
import_config "test.secret.exs"
|
||||||
else
|
else
|
||||||
|
|
|
@ -111,7 +111,7 @@ def run(["get-packs" | args]) do
|
||||||
|
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
:zip.unzip(binary_archive,
|
:zip.unzip(binary_archive,
|
||||||
cwd: pack_path,
|
cwd: String.to_charlist(pack_path),
|
||||||
file_list: files_to_unzip
|
file_list: files_to_unzip
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,93 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Phoenix.Transports.WebSocket.Raw do
|
|
||||||
import Plug.Conn,
|
|
||||||
only: [
|
|
||||||
fetch_query_params: 1,
|
|
||||||
send_resp: 3
|
|
||||||
]
|
|
||||||
|
|
||||||
alias Phoenix.Socket.Transport
|
|
||||||
|
|
||||||
def default_config do
|
|
||||||
[
|
|
||||||
timeout: 60_000,
|
|
||||||
transport_log: false,
|
|
||||||
cowboy: Phoenix.Endpoint.CowboyWebSocket
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do
|
|
||||||
{_, opts} = handler.__transport__(transport)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> fetch_query_params
|
|
||||||
|> Transport.transport_log(opts[:transport_log])
|
|
||||||
|> Transport.check_origin(handler, endpoint, opts)
|
|
||||||
|
|
||||||
case conn do
|
|
||||||
%{halted: false} = conn ->
|
|
||||||
case handler.connect(%{
|
|
||||||
endpoint: endpoint,
|
|
||||||
transport: transport,
|
|
||||||
options: [serializer: nil],
|
|
||||||
params: conn.params
|
|
||||||
}) do
|
|
||||||
{:ok, socket} ->
|
|
||||||
{:ok, conn, {__MODULE__, {socket, opts}}}
|
|
||||||
|
|
||||||
:error ->
|
|
||||||
send_resp(conn, :forbidden, "")
|
|
||||||
{:error, conn}
|
|
||||||
end
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
{:error, conn}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def init(conn, _) do
|
|
||||||
send_resp(conn, :bad_request, "")
|
|
||||||
{:error, conn}
|
|
||||||
end
|
|
||||||
|
|
||||||
def ws_init({socket, config}) do
|
|
||||||
Process.flag(:trap_exit, true)
|
|
||||||
{:ok, %{socket: socket}, config[:timeout]}
|
|
||||||
end
|
|
||||||
|
|
||||||
def ws_handle(op, data, state) do
|
|
||||||
state.socket.handler
|
|
||||||
|> apply(:handle, [op, data, state])
|
|
||||||
|> case do
|
|
||||||
{op, data} ->
|
|
||||||
{:reply, {op, data}, state}
|
|
||||||
|
|
||||||
{op, data, state} ->
|
|
||||||
{:reply, {op, data}, state}
|
|
||||||
|
|
||||||
%{} = state ->
|
|
||||||
{:ok, state}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
{:ok, state}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def ws_info({_, _} = tuple, state) do
|
|
||||||
{:reply, tuple, state}
|
|
||||||
end
|
|
||||||
|
|
||||||
def ws_info(_tuple, state), do: {:ok, state}
|
|
||||||
|
|
||||||
def ws_close(state) do
|
|
||||||
ws_handle(:closed, :normal, state)
|
|
||||||
end
|
|
||||||
|
|
||||||
def ws_terminate(reason, state) do
|
|
||||||
ws_handle(:closed, reason, state)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -28,7 +28,7 @@ defp get_cache_keys_for(activity_id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp add_cache_key_for(activity_id, additional_key) do
|
def add_cache_key_for(activity_id, additional_key) do
|
||||||
current = get_cache_keys_for(activity_id)
|
current = get_cache_keys_for(activity_id)
|
||||||
|
|
||||||
unless additional_key in current do
|
unless additional_key in current do
|
||||||
|
|
|
@ -23,19 +23,21 @@ defmodule Pleroma.Announcement do
|
||||||
timestamps(type: :utc_datetime)
|
timestamps(type: :utc_datetime)
|
||||||
end
|
end
|
||||||
|
|
||||||
def change(struct, params \\ %{}) do
|
@doc "Generates changeset for %Pleroma.Announcement{}"
|
||||||
struct
|
@spec changeset(%__MODULE__{}, map()) :: %Ecto.Changeset{}
|
||||||
|> cast(validate_params(struct, params), [:data, :starts_at, :ends_at, :rendered])
|
def changeset(announcement \\ %__MODULE__{}, params \\ %{data: %{}}) do
|
||||||
|
announcement
|
||||||
|
|> cast(validate_params(announcement, params), [:data, :starts_at, :ends_at, :rendered])
|
||||||
|> validate_required([:data])
|
|> validate_required([:data])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_params(struct, params) do
|
defp validate_params(announcement, params) do
|
||||||
base_data =
|
base_data =
|
||||||
%{
|
%{
|
||||||
"content" => "",
|
"content" => "",
|
||||||
"all_day" => false
|
"all_day" => false
|
||||||
}
|
}
|
||||||
|> Map.merge((struct && struct.data) || %{})
|
|> Map.merge((announcement && announcement.data) || %{})
|
||||||
|
|
||||||
merged_data =
|
merged_data =
|
||||||
Map.merge(base_data, params.data)
|
Map.merge(base_data, params.data)
|
||||||
|
@ -61,13 +63,13 @@ def add_rendered_properties(params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def add(params) do
|
def add(params) do
|
||||||
changeset = change(%__MODULE__{}, params)
|
changeset = changeset(%__MODULE__{}, params)
|
||||||
|
|
||||||
Repo.insert(changeset)
|
Repo.insert(changeset)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(announcement, params) do
|
def update(announcement, params) do
|
||||||
changeset = change(announcement, params)
|
changeset = changeset(announcement, params)
|
||||||
|
|
||||||
Repo.update(changeset)
|
Repo.update(changeset)
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,10 +8,13 @@ defmodule Pleroma.Caching do
|
||||||
@callback put(Cachex.cache(), any(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
|
@callback put(Cachex.cache(), any(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
|
||||||
@callback put(Cachex.cache(), any(), any()) :: {Cachex.status(), boolean()}
|
@callback put(Cachex.cache(), any(), any()) :: {Cachex.status(), boolean()}
|
||||||
@callback fetch!(Cachex.cache(), any(), function() | nil) :: any()
|
@callback fetch!(Cachex.cache(), any(), function() | nil) :: any()
|
||||||
|
@callback fetch(Cachex.cache(), any(), function() | nil) ::
|
||||||
|
{atom(), any()} | {atom(), any(), any()}
|
||||||
# @callback del(Cachex.cache(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
|
# @callback del(Cachex.cache(), any(), Keyword.t()) :: {Cachex.status(), boolean()}
|
||||||
@callback del(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
|
@callback del(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
|
||||||
@callback stream!(Cachex.cache(), any()) :: Enumerable.t()
|
@callback stream!(Cachex.cache(), any()) :: Enumerable.t()
|
||||||
@callback expire_at(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()}
|
@callback expire_at(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()}
|
||||||
|
@callback expire(Cachex.cache(), binary(), number()) :: {Cachex.status(), boolean()}
|
||||||
@callback exists?(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
|
@callback exists?(Cachex.cache(), any()) :: {Cachex.status(), boolean()}
|
||||||
@callback execute!(Cachex.cache(), function()) :: any()
|
@callback execute!(Cachex.cache(), function()) :: any()
|
||||||
@callback get_and_update(Cachex.cache(), any(), function()) ::
|
@callback get_and_update(Cachex.cache(), any(), function()) ::
|
||||||
|
|
|
@ -256,7 +256,7 @@ def check_old_mrf_config do
|
||||||
move_namespace_and_warn(@mrf_config_map, warning_preface)
|
move_namespace_and_warn(@mrf_config_map, warning_preface)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec move_namespace_and_warn([config_map()], String.t()) :: :ok | nil
|
@spec move_namespace_and_warn([config_map()], String.t()) :: :ok | :error
|
||||||
def move_namespace_and_warn(config_map, warning_preface) do
|
def move_namespace_and_warn(config_map, warning_preface) do
|
||||||
warning =
|
warning =
|
||||||
Enum.reduce(config_map, "", fn
|
Enum.reduce(config_map, "", fn
|
||||||
|
@ -279,7 +279,7 @@ def move_namespace_and_warn(config_map, warning_preface) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec check_media_proxy_whitelist_config() :: :ok | nil
|
@spec check_media_proxy_whitelist_config() :: :ok | :error
|
||||||
def check_media_proxy_whitelist_config do
|
def check_media_proxy_whitelist_config do
|
||||||
whitelist = Config.get([:media_proxy, :whitelist])
|
whitelist = Config.get([:media_proxy, :whitelist])
|
||||||
|
|
||||||
|
@ -340,7 +340,7 @@ def check_gun_pool_options do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec check_activity_expiration_config() :: :ok | nil
|
@spec check_activity_expiration_config() :: :ok | :error
|
||||||
def check_activity_expiration_config do
|
def check_activity_expiration_config do
|
||||||
warning_preface = """
|
warning_preface = """
|
||||||
!!!DEPRECATION WARNING!!!
|
!!!DEPRECATION WARNING!!!
|
||||||
|
@ -356,7 +356,7 @@ def check_activity_expiration_config do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec check_remote_ip_plug_name() :: :ok | nil
|
@spec check_remote_ip_plug_name() :: :ok | :error
|
||||||
def check_remote_ip_plug_name do
|
def check_remote_ip_plug_name do
|
||||||
warning_preface = """
|
warning_preface = """
|
||||||
!!!DEPRECATION WARNING!!!
|
!!!DEPRECATION WARNING!!!
|
||||||
|
@ -372,7 +372,7 @@ def check_remote_ip_plug_name do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec check_uploaders_s3_public_endpoint() :: :ok | nil
|
@spec check_uploaders_s3_public_endpoint() :: :ok | :error
|
||||||
def check_uploaders_s3_public_endpoint do
|
def check_uploaders_s3_public_endpoint do
|
||||||
s3_config = Pleroma.Config.get([Pleroma.Uploaders.S3])
|
s3_config = Pleroma.Config.get([Pleroma.Uploaders.S3])
|
||||||
|
|
||||||
|
@ -393,7 +393,7 @@ def check_uploaders_s3_public_endpoint do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec check_old_chat_shoutbox() :: :ok | nil
|
@spec check_old_chat_shoutbox() :: :ok | :error
|
||||||
def check_old_chat_shoutbox do
|
def check_old_chat_shoutbox do
|
||||||
instance_config = Pleroma.Config.get([:instance])
|
instance_config = Pleroma.Config.get([:instance])
|
||||||
chat_config = Pleroma.Config.get([:chat]) || []
|
chat_config = Pleroma.Config.get([:chat]) || []
|
||||||
|
|
|
@ -21,7 +21,7 @@ def load(config, opts) do
|
||||||
with_runtime_config =
|
with_runtime_config =
|
||||||
if File.exists?(config_path) do
|
if File.exists?(config_path) do
|
||||||
# <https://git.pleroma.social/pleroma/pleroma/-/issues/3135>
|
# <https://git.pleroma.social/pleroma/pleroma/-/issues/3135>
|
||||||
%File.Stat{mode: mode} = File.lstat!(config_path)
|
%File.Stat{mode: mode} = File.stat!(config_path)
|
||||||
|
|
||||||
if Bitwise.band(mode, 0o007) > 0 do
|
if Bitwise.band(mode, 0o007) > 0 do
|
||||||
raise "Configuration at #{config_path} has world-permissions, execute the following: chmod o= #{config_path}"
|
raise "Configuration at #{config_path} has world-permissions, execute the following: chmod o= #{config_path}"
|
||||||
|
|
|
@ -57,7 +57,7 @@ def maybe_create_recipientships(participation, activity) do
|
||||||
3. Bump all relevant participations to 'unread'
|
3. Bump all relevant participations to 'unread'
|
||||||
"""
|
"""
|
||||||
def create_or_bump_for(activity, opts \\ []) do
|
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"],
|
"Create" <- activity.data["type"],
|
||||||
%Object{} = object <- Object.normalize(activity, fetch: false),
|
%Object{} = object <- Object.normalize(activity, fetch: false),
|
||||||
true <- object.data["type"] in ["Note", "Question"],
|
true <- object.data["type"] in ["Note", "Question"],
|
||||||
|
|
|
@ -12,6 +12,8 @@ defmodule Pleroma.DataMigration do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
schema "data_migrations" do
|
schema "data_migrations" do
|
||||||
field(:name, :string)
|
field(:name, :string)
|
||||||
field(:state, State, default: :pending)
|
field(:state, State, default: :pending)
|
||||||
|
|
|
@ -51,12 +51,12 @@ def reload do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Returns the path of the emoji `name`."
|
@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
|
def get(name) do
|
||||||
name = maybe_strip_name(name)
|
name = maybe_strip_name(name)
|
||||||
|
|
||||||
case :ets.lookup(@ets, name) do
|
case :ets.lookup(@ets, name) do
|
||||||
[{_, path}] -> path
|
[{_, emoji}] -> emoji
|
||||||
_ -> nil
|
_ -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -138,23 +138,23 @@ defp update_emojis(emojis) do
|
||||||
emojis = emojis ++ regional_indicators
|
emojis = emojis ++ regional_indicators
|
||||||
|
|
||||||
for emoji <- emojis do
|
for emoji <- emojis do
|
||||||
def is_unicode_emoji?(unquote(emoji)), do: true
|
def unicode?(unquote(emoji)), do: true
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_unicode_emoji?(_), do: false
|
def unicode?(_), do: false
|
||||||
|
|
||||||
@emoji_regex ~r/:[A-Za-z0-9_-]+(@.+)?:/
|
@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) when is_binary(name), do: String.trim(name, ":")
|
||||||
|
|
||||||
def maybe_strip_name(name), do: name
|
def maybe_strip_name(name), do: name
|
||||||
|
|
||||||
def maybe_quote(name) when is_binary(name) do
|
def maybe_quote(name) when is_binary(name) do
|
||||||
if is_unicode_emoji?(name) do
|
if unicode?(name) do
|
||||||
name
|
name
|
||||||
else
|
else
|
||||||
if String.starts_with?(name, ":") do
|
if String.starts_with?(name, ":") do
|
||||||
|
|
|
@ -15,8 +15,6 @@ defmodule Pleroma.Emoji.Loader do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@mix_env Mix.env()
|
|
||||||
|
|
||||||
@type pattern :: Regex.t() | module() | String.t()
|
@type pattern :: Regex.t() | module() | String.t()
|
||||||
@type patterns :: pattern() | [pattern()]
|
@type patterns :: pattern() | [pattern()]
|
||||||
@type group_patterns :: keyword(patterns())
|
@type group_patterns :: keyword(patterns())
|
||||||
|
@ -79,7 +77,7 @@ def load do
|
||||||
|
|
||||||
# for testing emoji.txt entries we do not want exposed in normal operation
|
# for testing emoji.txt entries we do not want exposed in normal operation
|
||||||
test_emoji =
|
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)
|
load_from_file("test/config/emoji.txt", emoji_groups)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
|
|
|
@ -100,7 +100,7 @@ def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"}
|
||||||
{:ok, _emoji_files} =
|
{:ok, _emoji_files} =
|
||||||
:zip.unzip(
|
:zip.unzip(
|
||||||
to_charlist(file.path),
|
to_charlist(file.path),
|
||||||
[{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, tmp_dir}]
|
[{:file_list, Enum.map(emojies, & &1[:path])}, {:cwd, String.to_charlist(tmp_dir)}]
|
||||||
)
|
)
|
||||||
|
|
||||||
{_, updated_pack} =
|
{_, updated_pack} =
|
||||||
|
|
|
@ -216,9 +216,6 @@ def compose_regex([_ | _] = filters, format) do
|
||||||
|
|
||||||
:re ->
|
:re ->
|
||||||
~r/\b#{phrases}\b/i
|
~r/\b#{phrases}\b/i
|
||||||
|
|
||||||
_ ->
|
|
||||||
nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ def response("/main/all") do
|
||||||
|
|
||||||
def response("/notices/" <> id) do
|
def response("/notices/" <> id) do
|
||||||
with %Activity{} = activity <- Activity.get_by_id(id),
|
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||||
true <- Visibility.is_public?(activity) do
|
true <- Visibility.public?(activity) do
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities_for_context(activity.data["context"])
|
ActivityPub.fetch_activities_for_context(activity.data["context"])
|
||||||
|> render_activities
|
|> render_activities
|
||||||
|
|
|
@ -9,7 +9,7 @@ defp registry, do: Pleroma.Gun.ConnectionPool
|
||||||
|
|
||||||
def start_monitor do
|
def start_monitor do
|
||||||
pid =
|
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} ->
|
{:ok, pid} ->
|
||||||
pid
|
pid
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,9 @@ def init(_opts) do
|
||||||
def start_worker(opts, retry \\ false) do
|
def start_worker(opts, retry \\ false) do
|
||||||
case DynamicSupervisor.start_child(__MODULE__, {Pleroma.Gun.ConnectionPool.Worker, opts}) do
|
case DynamicSupervisor.start_child(__MODULE__, {Pleroma.Gun.ConnectionPool.Worker, opts}) do
|
||||||
{:error, :max_children} ->
|
{:error, :max_children} ->
|
||||||
if retry or free_pool() == :error do
|
funs = [fn -> !retry end, fn -> match?(:error, free_pool()) end]
|
||||||
|
|
||||||
|
if Enum.any?(funs, fn fun -> fun.() end) do
|
||||||
:telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts})
|
:telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts})
|
||||||
{:error, :pool_full}
|
{:error, :pool_full}
|
||||||
else
|
else
|
||||||
|
|
|
@ -43,89 +43,28 @@ def image_resize(url, options) do
|
||||||
def video_framegrab(url) do
|
def video_framegrab(url) do
|
||||||
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
|
with executable when is_binary(executable) <- System.find_executable("ffmpeg"),
|
||||||
{:ok, env} <- HTTP.get(url, [], pool: :media),
|
{:ok, env} <- HTTP.get(url, [], pool: :media),
|
||||||
{:ok, fifo_path} <- mkfifo(),
|
{:ok, pid} <- StringIO.open(env.body) do
|
||||||
args = [
|
body_stream = IO.binstream(pid, 1)
|
||||||
"-y",
|
|
||||||
|
Exile.stream!(
|
||||||
|
[
|
||||||
|
executable,
|
||||||
"-i",
|
"-i",
|
||||||
fifo_path,
|
"pipe:0",
|
||||||
"-vframes",
|
"-vframes",
|
||||||
"1",
|
"1",
|
||||||
"-f",
|
"-f",
|
||||||
"mjpeg",
|
"mjpeg",
|
||||||
"-loglevel",
|
"pipe:1"
|
||||||
"error",
|
],
|
||||||
"-"
|
input: body_stream,
|
||||||
] do
|
ignore_epipe: true,
|
||||||
run_fifo(fifo_path, env, executable, args)
|
stderr: :disable
|
||||||
|
)
|
||||||
|
|> Enum.into(<<>>)
|
||||||
else
|
else
|
||||||
nil -> {:error, {:ffmpeg, :command_not_found}}
|
nil -> {:error, {:ffmpeg, :command_not_found}}
|
||||||
{:error, _} = error -> error
|
{:error, _} = error -> error
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -126,9 +126,15 @@ defp rewrite_entries(
|
||||||
<<pos::integer-big-size(unquote(size)), rest::bits>>,
|
<<pos::integer-big-size(unquote(size)), rest::bits>>,
|
||||||
acc
|
acc
|
||||||
) do
|
) do
|
||||||
rewrite_entries(unquote(size), offset, rest, [
|
rewrite_entries(
|
||||||
acc | <<pos + offset::integer-big-size(unquote(size))>>
|
unquote(size),
|
||||||
])
|
offset,
|
||||||
|
rest,
|
||||||
|
acc ++
|
||||||
|
[
|
||||||
|
<<pos + offset::integer-big-size(unquote(size))>>
|
||||||
|
]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,6 @@ defmodule Pleroma.HTML do
|
||||||
# Scrubbers are compiled on boot so they can be configured in OTP releases
|
# Scrubbers are compiled on boot so they can be configured in OTP releases
|
||||||
# @on_load :compile_scrubbers
|
# @on_load :compile_scrubbers
|
||||||
|
|
||||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
|
||||||
|
|
||||||
def compile_scrubbers do
|
def compile_scrubbers do
|
||||||
dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
|
dir = Path.join(:code.priv_dir(:pleroma), "scrubbers")
|
||||||
|
|
||||||
|
@ -67,27 +65,20 @@ def ensure_scrubbed_html(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_first_external_url_from_object(%{data: %{"content" => content}} = object)
|
@spec extract_first_external_url_from_object(Pleroma.Object.t()) ::
|
||||||
|
{:ok, String.t()} | {:error, :no_content}
|
||||||
|
def extract_first_external_url_from_object(%{data: %{"content" => content}})
|
||||||
when is_binary(content) do
|
when is_binary(content) do
|
||||||
unless object.data["fake"] do
|
url =
|
||||||
key = "URL|#{object.id}"
|
|
||||||
|
|
||||||
@cachex.fetch!(:scrubber_cache, key, fn _key ->
|
|
||||||
{:commit, {:ok, extract_first_external_url(content)}}
|
|
||||||
end)
|
|
||||||
else
|
|
||||||
{:ok, extract_first_external_url(content)}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def extract_first_external_url_from_object(_), do: {:error, :no_content}
|
|
||||||
|
|
||||||
def extract_first_external_url(content) do
|
|
||||||
content
|
content
|
||||||
|> Floki.parse_fragment!()
|
|> Floki.parse_fragment!()
|
||||||
|> Floki.find("a:not(.mention,.hashtag,.attachment,[rel~=\"tag\"])")
|
|> Floki.find("a:not(.mention,.hashtag,.attachment,[rel~=\"tag\"])")
|
||||||
|> Enum.take(1)
|
|> Enum.take(1)
|
||||||
|> Floki.attribute("href")
|
|> Floki.attribute("href")
|
||||||
|> Enum.at(0)
|
|> Enum.at(0)
|
||||||
|
|
||||||
|
{:ok, url}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def extract_first_external_url_from_object(_), do: {:error, :no_content}
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,8 +15,8 @@ defmodule Pleroma.HTTP.AdapterHelper do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@type proxy ::
|
@type proxy ::
|
||||||
{Connection.host(), pos_integer()}
|
{host(), pos_integer()}
|
||||||
| {Connection.proxy_type(), Connection.host(), pos_integer()}
|
| {proxy_type(), host(), pos_integer()}
|
||||||
|
|
||||||
@callback options(keyword(), URI.t()) :: keyword()
|
@callback options(keyword(), URI.t()) :: keyword()
|
||||||
|
|
||||||
|
|
|
@ -54,12 +54,12 @@ def opts(request, options), do: %{request | opts: options}
|
||||||
@doc """
|
@doc """
|
||||||
Add optional parameters to the request
|
Add optional parameters to the request
|
||||||
"""
|
"""
|
||||||
@spec add_param(Request.t(), atom(), atom(), any()) :: Request.t()
|
@spec add_param(Request.t(), atom(), atom() | String.t(), any()) :: Request.t()
|
||||||
def add_param(request, :query, :query, values), do: %{request | query: values}
|
def add_param(request, :query, :query, values), do: %{request | query: values}
|
||||||
|
|
||||||
def add_param(request, :body, :body, value), do: %{request | body: value}
|
def add_param(request, :body, :body, value), do: %{request | body: value}
|
||||||
|
|
||||||
def add_param(request, :body, key, value) do
|
def add_param(request, :body, key, value) when is_binary(key) do
|
||||||
request
|
request
|
||||||
|> Map.put(:body, Multipart.new())
|
|> Map.put(:body, Multipart.new())
|
||||||
|> Map.update!(
|
|> Map.update!(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# Pleroma: A lightweight social networking server
|
||||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Maps do
|
defmodule Pleroma.Maps do
|
||||||
|
@ -18,4 +18,17 @@ def safe_put_in(data, keys, value) when is_map(data) and is_list(keys) do
|
||||||
rescue
|
rescue
|
||||||
_ -> data
|
_ -> data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter_empty_values(data) do
|
||||||
|
# TODO: Change to Map.filter in Elixir 1.13+
|
||||||
|
data
|
||||||
|
|> Enum.filter(fn
|
||||||
|
{_k, nil} -> false
|
||||||
|
{_k, ""} -> false
|
||||||
|
{_k, []} -> false
|
||||||
|
{_k, %{} = v} -> Map.keys(v) != []
|
||||||
|
{_k, _v} -> true
|
||||||
|
end)
|
||||||
|
|> Map.new()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -77,7 +77,7 @@ def generate_backup_codes(%User{} = user) do
|
||||||
{:ok, codes}
|
{:ok, codes}
|
||||||
else
|
else
|
||||||
{:error, msg} ->
|
{:error, msg} ->
|
||||||
%{error: msg}
|
{:error, msg}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.MFA.TOTP do
|
||||||
@doc """
|
@doc """
|
||||||
https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
||||||
"""
|
"""
|
||||||
|
@spec provisioning_uri(String.t(), String.t(), list()) :: String.t()
|
||||||
def provisioning_uri(secret, label, opts \\ []) do
|
def provisioning_uri(secret, label, opts \\ []) do
|
||||||
query =
|
query =
|
||||||
%{
|
%{
|
||||||
|
@ -27,7 +28,7 @@ def provisioning_uri(secret, label, opts \\ []) do
|
||||||
|> URI.encode_query()
|
|> URI.encode_query()
|
||||||
|
|
||||||
%URI{scheme: "otpauth", host: "totp", path: "/" <> label, query: query}
|
%URI{scheme: "otpauth", host: "totp", path: "/" <> label, query: query}
|
||||||
|> URI.to_string()
|
|> to_string()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp default_period, do: Config.get(@config_ns ++ [:period])
|
defp default_period, do: Config.get(@config_ns ++ [:period])
|
||||||
|
|
|
@ -188,10 +188,11 @@ defp update_status(status, message \\ nil) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fault_rate do
|
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])
|
failures_count / Enum.max([get_stat(:affected_count, 0), 1])
|
||||||
else
|
else
|
||||||
_ -> :error
|
_ -> 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -121,7 +121,7 @@ defp prepare_log_data(%{actor: actor, action: action} = attrs) do
|
||||||
|
|
||||||
defp prepare_log_data(attrs), do: attrs
|
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
|
def insert_log(%{actor: %User{}, subject: subjects, permission: permission} = attrs) do
|
||||||
data =
|
data =
|
||||||
attrs
|
attrs
|
||||||
|
@ -248,7 +248,8 @@ def insert_log(%{actor: %User{} = actor, action: "chat_message_delete", subject_
|
||||||
|> insert_log_entry_with_message()
|
|> insert_log_entry_with_message()
|
||||||
end
|
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
|
defp insert_log_entry_with_message(entry) do
|
||||||
entry.data["message"]
|
entry.data["message"]
|
||||||
|> put_in(get_log_entry_message(entry))
|
|> put_in(get_log_entry_message(entry))
|
||||||
|
|
|
@ -88,7 +88,7 @@ def last_read_query(user) do
|
||||||
where: q.seen == true,
|
where: q.seen == true,
|
||||||
select: type(q.id, :string),
|
select: type(q.id, :string),
|
||||||
limit: 1,
|
limit: 1,
|
||||||
order_by: [desc: :id]
|
order_by: fragment("? desc nulls last", q.id)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -242,17 +242,17 @@ def delete(%Object{data: %{"id" => id}} = object) do
|
||||||
{:ok, _} <- invalid_object_cache(object) do
|
{:ok, _} <- invalid_object_cache(object) do
|
||||||
cleanup_attachments(
|
cleanup_attachments(
|
||||||
Config.get([:instance, :cleanup_attachments]),
|
Config.get([:instance, :cleanup_attachments]),
|
||||||
%{"object" => object}
|
object
|
||||||
)
|
)
|
||||||
|
|
||||||
{:ok, object, deleted_activity}
|
{:ok, object, deleted_activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec cleanup_attachments(boolean(), %{required(:object) => map()}) ::
|
@spec cleanup_attachments(boolean(), Object.t()) ::
|
||||||
{:ok, Oban.Job.t() | nil}
|
{:ok, Oban.Job.t() | nil}
|
||||||
def cleanup_attachments(true, %{"object" => _} = params) do
|
def cleanup_attachments(true, %Object{} = object) do
|
||||||
AttachmentsCleanupWorker.enqueue("cleanup_attachments", params)
|
AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{"object" => object})
|
||||||
end
|
end
|
||||||
|
|
||||||
def cleanup_attachments(_, _), do: {:ok, nil}
|
def cleanup_attachments(_, _), do: {:ok, nil}
|
||||||
|
|
|
@ -61,15 +61,16 @@ def fetch_paginated(query, params, :offset, table_binding) do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
@spec paginate_list(list(), keyword()) :: list()
|
||||||
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
|
def paginate_list(list, options) do
|
||||||
|
|
||||||
def paginate(list, options, _method, _table_binding) when is_list(list) do
|
|
||||||
offset = options[:offset] || 0
|
offset = options[:offset] || 0
|
||||||
limit = options[:limit] || 0
|
limit = options[:limit] || 0
|
||||||
Enum.slice(list, offset, limit)
|
Enum.slice(list, offset, limit)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
||||||
|
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
|
||||||
|
|
||||||
def paginate(query, options, :keyset, table_binding) do
|
def paginate(query, options, :keyset, table_binding) do
|
||||||
query
|
query
|
||||||
|> restrict(:min_id, options, table_binding)
|
|> restrict(:min_id, options, table_binding)
|
||||||
|
|
|
@ -28,7 +28,7 @@ def verify_pass(password, hash) do
|
||||||
|
|
||||||
iterations = String.to_integer(iterations)
|
iterations = String.to_integer(iterations)
|
||||||
|
|
||||||
digest = String.to_atom(digest)
|
digest = String.to_existing_atom(digest)
|
||||||
|
|
||||||
binary_hash =
|
binary_hash =
|
||||||
KeyGenerator.generate(password, salt, digest: digest, iterations: iterations, length: 64)
|
KeyGenerator.generate(password, salt, digest: digest, iterations: iterations, length: 64)
|
||||||
|
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.ReverseProxy do
|
||||||
~w(if-unmodified-since if-none-match) ++ @range_headers
|
~w(if-unmodified-since if-none-match) ++ @range_headers
|
||||||
@resp_cache_headers ~w(etag date last-modified)
|
@resp_cache_headers ~w(etag date last-modified)
|
||||||
@keep_resp_headers @resp_cache_headers ++
|
@keep_resp_headers @resp_cache_headers ++
|
||||||
~w(content-length content-type content-disposition content-encoding) ++
|
~w(content-type content-disposition content-encoding) ++
|
||||||
~w(content-range accept-ranges vary)
|
~w(content-range accept-ranges vary)
|
||||||
@default_cache_control_header "public, max-age=1209600"
|
@default_cache_control_header "public, max-age=1209600"
|
||||||
@valid_resp_codes [200, 206, 304]
|
@valid_resp_codes [200, 206, 304]
|
||||||
|
@ -84,13 +84,13 @@ def default_cache_control_header, do: @default_cache_control_header
|
||||||
{:max_read_duration, non_neg_integer() | :infinity}
|
{:max_read_duration, non_neg_integer() | :infinity}
|
||||||
| {:max_body_length, non_neg_integer() | :infinity}
|
| {:max_body_length, non_neg_integer() | :infinity}
|
||||||
| {:failed_request_ttl, non_neg_integer() | :infinity}
|
| {:failed_request_ttl, non_neg_integer() | :infinity}
|
||||||
| {:http, []}
|
| {:http, keyword()}
|
||||||
| {:req_headers, [{String.t(), String.t()}]}
|
| {:req_headers, [{String.t(), String.t()}]}
|
||||||
| {:resp_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()}
|
| {: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, _url, _opts \\ [])
|
||||||
|
|
||||||
def call(conn = %{method: method}, url, opts) when method in @methods do
|
def call(conn = %{method: method}, url, opts) when method in @methods do
|
||||||
|
@ -388,8 +388,6 @@ defp body_size_constraint(size, limit) when is_integer(limit) and limit > 0 and
|
||||||
|
|
||||||
defp body_size_constraint(_, _), do: :ok
|
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)
|
defp check_read_duration(duration, max)
|
||||||
when is_integer(duration) and is_integer(max) and max > 0 do
|
when is_integer(duration) and is_integer(max) and max > 0 do
|
||||||
if duration > max do
|
if duration > max do
|
||||||
|
@ -407,10 +405,6 @@ defp increase_read_duration({previous_duration, started})
|
||||||
{:ok, previous_duration + duration}
|
{:ok, previous_duration + duration}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp increase_read_duration(_) do
|
|
||||||
{:ok, :no_duration_limit, :no_duration_limit}
|
|
||||||
end
|
|
||||||
|
|
||||||
defp client, do: Pleroma.ReverseProxy.Client.Wrapper
|
defp client, do: Pleroma.ReverseProxy.Client.Wrapper
|
||||||
|
|
||||||
defp track_failed_url(url, error, opts) do
|
defp track_failed_url(url, error, opts) do
|
||||||
|
|
|
@ -20,5 +20,5 @@ defmodule Pleroma.Search.SearchBackend do
|
||||||
is what contains the actual content and there is no need for filtering when removing
|
is what contains the actual content and there is no need for filtering when removing
|
||||||
from index.
|
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
|
end
|
||||||
|
|
|
@ -27,7 +27,7 @@ def key_id_to_actor_id(key_id) do
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
case Pleroma.Web.WebFinger.finger(maybe_ap_id) 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}
|
_ -> {:error, maybe_ap_id}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -59,7 +59,7 @@ def handle_event(
|
||||||
_,
|
_,
|
||||||
_
|
_
|
||||||
) do
|
) do
|
||||||
Logger.error(fn ->
|
Logger.debug(fn ->
|
||||||
"Connection pool had to refuse opening a connection to #{key} due to connection limit exhaustion"
|
"Connection pool had to refuse opening a connection to #{key} due to connection limit exhaustion"
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
@ -81,7 +81,7 @@ def handle_event(
|
||||||
%{key: key, protocol: :http},
|
%{key: key, protocol: :http},
|
||||||
_
|
_
|
||||||
) do
|
) do
|
||||||
Logger.info(fn ->
|
Logger.debug(fn ->
|
||||||
"Pool worker for #{key}: #{length(clients)} clients are using an HTTP1 connection at the same time, head-of-line blocking might occur."
|
"Pool worker for #{key}: #{length(clients)} clients are using an HTTP1 connection at the same time, head-of-line blocking might occur."
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -51,6 +51,7 @@ defmodule Pleroma.Upload do
|
||||||
| {:size_limit, nil | non_neg_integer()}
|
| {:size_limit, nil | non_neg_integer()}
|
||||||
| {:uploader, module()}
|
| {:uploader, module()}
|
||||||
| {:filters, [module()]}
|
| {:filters, [module()]}
|
||||||
|
| {:actor, String.t()}
|
||||||
|
|
||||||
@type t :: %__MODULE__{
|
@type t :: %__MODULE__{
|
||||||
id: String.t(),
|
id: String.t(),
|
||||||
|
@ -175,7 +176,7 @@ defp prepare_upload(%Plug.Upload{} = file, opts) do
|
||||||
defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do
|
defp prepare_upload(%{img: "data:image/" <> image_data}, opts) do
|
||||||
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
|
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
|
||||||
data = Base.decode64!(parsed["data"], ignore: :whitespace)
|
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),
|
with :ok <- check_binary_size(data, opts.size_limit),
|
||||||
tmp_path <- tempfile_for_image(data),
|
tmp_path <- tempfile_for_image(data),
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
defmodule Pleroma.Uploaders.Uploader do
|
defmodule Pleroma.Uploaders.Uploader do
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
|
|
||||||
@mix_env Mix.env()
|
|
||||||
|
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
Defines the contract to put and get an uploaded file to any backend.
|
Defines the contract to put and get an uploaded file to any backend.
|
||||||
"""
|
"""
|
||||||
|
@ -75,10 +73,5 @@ defp handle_callback(uploader, upload) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp callback_timeout do
|
defp callback_timeout, do: Application.get_env(:pleroma, __MODULE__)[:timeout]
|
||||||
case @mix_env do
|
|
||||||
:test -> 1_000
|
|
||||||
_ -> 30_000
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1787,7 +1787,10 @@ def set_activation_async(user, status \\ true) do
|
||||||
@spec set_activation([User.t()], boolean()) :: {:ok, User.t()} | {:error, Ecto.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
|
def set_activation(users, status) when is_list(users) do
|
||||||
Repo.transaction(fn ->
|
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)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2404,9 +2407,9 @@ defp put_password_hash(
|
||||||
|
|
||||||
defp put_password_hash(changeset), do: changeset
|
defp put_password_hash(changeset), do: changeset
|
||||||
|
|
||||||
def is_internal_user?(%User{nickname: nil}), do: true
|
def internal?(%User{nickname: nil}), do: true
|
||||||
def is_internal_user?(%User{local: true, nickname: "internal." <> _}), do: true
|
def internal?(%User{local: true, nickname: "internal." <> _}), do: true
|
||||||
def is_internal_user?(_), do: false
|
def internal?(_), do: false
|
||||||
|
|
||||||
# A hack because user delete activities have a fake id for whatever reason
|
# A hack because user delete activities have a fake id for whatever reason
|
||||||
# TODO: Get rid of this
|
# TODO: Get rid of this
|
||||||
|
@ -2556,9 +2559,9 @@ def confirmation_changeset(user, set_confirmation: confirmed?) do
|
||||||
cast(user, params, [:is_confirmed, :confirmation_token])
|
cast(user, params, [:is_confirmed, :confirmation_token])
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec approval_changeset(User.t(), keyword()) :: Ecto.Changeset.t()
|
@spec approval_changeset(Ecto.Changeset.t(), keyword()) :: Ecto.Changeset.t()
|
||||||
def approval_changeset(user, set_approval: approved?) do
|
def approval_changeset(changeset, set_approval: approved?) do
|
||||||
cast(user, %{is_approved: approved?}, [:is_approved])
|
cast(changeset, %{is_approved: approved?}, [:is_approved])
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()}
|
@spec add_pinned_object_id(User.t(), String.t()) :: {:ok, User.t()} | {:error, term()}
|
||||||
|
|
|
@ -22,6 +22,8 @@ defmodule Pleroma.User.Backup do
|
||||||
alias Pleroma.Web.ActivityPub.UserView
|
alias Pleroma.Web.ActivityPub.UserView
|
||||||
alias Pleroma.Workers.BackupWorker
|
alias Pleroma.Workers.BackupWorker
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
schema "backups" do
|
schema "backups" do
|
||||||
field(:content_type, :string)
|
field(:content_type, :string)
|
||||||
field(:file_name, :string)
|
field(:file_name, :string)
|
||||||
|
@ -195,6 +197,7 @@ defp wait_backup(backup, current_processed, task) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json']
|
@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
|
def export(%__MODULE__{} = backup, caller_pid) do
|
||||||
backup = Repo.preload(backup, :user)
|
backup = Repo.preload(backup, :user)
|
||||||
dir = backup_tempdir(backup)
|
dir = backup_tempdir(backup)
|
||||||
|
@ -204,9 +207,11 @@ def export(%__MODULE__{} = backup, caller_pid) do
|
||||||
:ok <- statuses(dir, backup.user, caller_pid),
|
:ok <- statuses(dir, backup.user, caller_pid),
|
||||||
:ok <- likes(dir, backup.user, caller_pid),
|
:ok <- likes(dir, backup.user, caller_pid),
|
||||||
:ok <- bookmarks(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, _} <- File.rm_rf(dir) do
|
||||||
{:ok, to_string(zip_path)}
|
{:ok, zip_path}
|
||||||
|
else
|
||||||
|
_ -> :error
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -382,6 +387,8 @@ def do_process(backup, current_pid) do
|
||||||
[:file_size, :processed, :state]
|
[:file_size, :processed, :state]
|
||||||
)
|
)
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
|
else
|
||||||
|
e -> {:error, e}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -71,7 +71,7 @@ defmodule Pleroma.User.Query do
|
||||||
@equal_criteria [:email]
|
@equal_criteria [:email]
|
||||||
@contains_criteria [:ap_id, :nickname]
|
@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
|
def build(query \\ base_query(), criteria) do
|
||||||
prepare_query(query, criteria)
|
prepare_query(query, criteria)
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,6 +14,8 @@ defmodule Pleroma.UserRelationship do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserRelationship
|
alias Pleroma.UserRelationship
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
schema "user_relationships" do
|
schema "user_relationships" do
|
||||||
belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
|
belongs_to(:source, User, type: FlakeId.Ecto.CompatType)
|
||||||
belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
|
belongs_to(:target, User, type: FlakeId.Ecto.CompatType)
|
||||||
|
|
|
@ -74,22 +74,22 @@ defp check_remote_limit(%{"object" => %{"content" => content}}) when not is_nil(
|
||||||
defp check_remote_limit(_), do: true
|
defp check_remote_limit(_), do: true
|
||||||
|
|
||||||
def increase_note_count_if_public(actor, object) do
|
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
|
end
|
||||||
|
|
||||||
def decrease_note_count_if_public(actor, object) do
|
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
|
end
|
||||||
|
|
||||||
def update_last_status_at_if_public(actor, object) do
|
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
|
end
|
||||||
|
|
||||||
defp increase_replies_count_if_reply(%{
|
defp increase_replies_count_if_reply(%{
|
||||||
"object" => %{"inReplyTo" => reply_ap_id} = object,
|
"object" => %{"inReplyTo" => reply_ap_id} = object,
|
||||||
"type" => "Create"
|
"type" => "Create"
|
||||||
}) do
|
}) do
|
||||||
if is_public?(object) do
|
if public?(object) do
|
||||||
Object.increase_replies_count(reply_ap_id)
|
Object.increase_replies_count(reply_ap_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -100,7 +100,7 @@ defp increase_quotes_count_if_quote(%{
|
||||||
"object" => %{"quoteUrl" => quote_ap_id} = object,
|
"object" => %{"quoteUrl" => quote_ap_id} = object,
|
||||||
"type" => "Create"
|
"type" => "Create"
|
||||||
}) do
|
}) do
|
||||||
if is_public?(object) do
|
if public?(object) do
|
||||||
Object.increase_quotes_count(quote_ap_id)
|
Object.increase_quotes_count(quote_ap_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
This module encodes our addressing policies and general shape of our objects.
|
This module encodes our addressing policies and general shape of our objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -131,7 +132,7 @@ defp custom_emoji_react(object, data, emoji) do
|
||||||
def emoji_react(actor, object, emoji) do
|
def emoji_react(actor, object, emoji) do
|
||||||
with {:ok, data, meta} <- object_action(actor, object) do
|
with {:ok, data, meta} <- object_action(actor, object) do
|
||||||
data =
|
data =
|
||||||
if Emoji.is_unicode_emoji?(emoji) do
|
if Emoji.unicode?(emoji) do
|
||||||
unicode_emoji_react(object, data, emoji)
|
unicode_emoji_react(object, data, emoji)
|
||||||
else
|
else
|
||||||
custom_emoji_react(object, data, emoji)
|
custom_emoji_react(object, data, emoji)
|
||||||
|
@ -347,7 +348,7 @@ def announce(actor, object, options \\ []) do
|
||||||
actor.ap_id == Relay.ap_id() ->
|
actor.ap_id == Relay.ap_id() ->
|
||||||
[actor.follower_address]
|
[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()]
|
[actor.follower_address, object.data["actor"], Utils.as_local_public()]
|
||||||
|
|
||||||
public? ->
|
public? ->
|
||||||
|
@ -375,7 +376,7 @@ defp object_action(actor, object) do
|
||||||
|
|
||||||
# Address the actor of the object, and our actor's follower collection if the post is public.
|
# Address the actor of the object, and our actor's follower collection if the post is public.
|
||||||
to =
|
to =
|
||||||
if Visibility.is_public?(object) do
|
if Visibility.public?(object) do
|
||||||
[actor.follower_address, object.data["actor"]]
|
[actor.follower_address, object.data["actor"]]
|
||||||
else
|
else
|
||||||
[object.data["actor"]]
|
[object.data["actor"]]
|
||||||
|
|
|
@ -84,7 +84,7 @@ def filter(%{"type" => type, "object" => object} = message) when type in ["Creat
|
||||||
if hashtags != [] do
|
if hashtags != [] do
|
||||||
with {:ok, message} <- check_reject(message, hashtags),
|
with {:ok, message} <- check_reject(message, hashtags),
|
||||||
{:ok, message} <-
|
{:ok, message} <-
|
||||||
(if "type" == "Create" do
|
(if type == "Create" do
|
||||||
check_ftl_removal(message, hashtags)
|
check_ftl_removal(message, hashtags)
|
||||||
else
|
else
|
||||||
{:ok, message}
|
{:ok, message}
|
||||||
|
|
|
@ -62,7 +62,6 @@ def config_description do
|
||||||
key: :mrf_inline_quote,
|
key: :mrf_inline_quote,
|
||||||
related_policy: "Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy",
|
related_policy: "Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy",
|
||||||
label: "MRF Inline Quote Policy",
|
label: "MRF Inline Quote Policy",
|
||||||
type: :group,
|
|
||||||
description: "Force quote url to appear in post content.",
|
description: "Force quote url to appear in post content.",
|
||||||
children: [
|
children: [
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -10,15 +10,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do
|
||||||
@moduledoc "Reject or Word-Replace messages with a keyword or regex"
|
@moduledoc "Reject or Word-Replace messages with a keyword or regex"
|
||||||
|
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
@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
|
defp string_matches?(string, pattern) when is_binary(pattern) do
|
||||||
String.contains?(string, pattern)
|
String.contains?(string, pattern)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp string_matches?(string, pattern) do
|
defp string_matches?(string, %Regex{} = pattern) do
|
||||||
String.match?(string, pattern)
|
String.match?(string, pattern)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def filter(%{"actor" => actor} = object) do
|
def filter(%{"actor" => actor} = object) do
|
||||||
with true <- is_local?(actor),
|
with true <- local?(actor),
|
||||||
true <- is_eligible_type?(object),
|
true <- eligible_type?(object),
|
||||||
true <- is_note?(object),
|
true <- note?(object),
|
||||||
false <- has_attachment?(object),
|
false <- has_attachment?(object),
|
||||||
true <- only_mentions?(object) do
|
true <- only_mentions?(object) do
|
||||||
{:reject, "[NoEmptyPolicy]"}
|
{:reject, "[NoEmptyPolicy]"}
|
||||||
|
@ -24,7 +24,7 @@ def filter(%{"actor" => actor} = object) do
|
||||||
|
|
||||||
def filter(object), do: {:ok, object}
|
def filter(object), do: {:ok, object}
|
||||||
|
|
||||||
defp is_local?(actor) do
|
defp local?(actor) do
|
||||||
if actor |> String.starts_with?("#{Endpoint.url()}") do
|
if actor |> String.starts_with?("#{Endpoint.url()}") do
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
|
@ -59,11 +59,11 @@ defp only_mentions?(%{"object" => %{"type" => "Note", "source" => source}}) do
|
||||||
|
|
||||||
defp only_mentions?(_), do: false
|
defp only_mentions?(_), do: false
|
||||||
|
|
||||||
defp is_note?(%{"object" => %{"type" => "Note"}}), do: true
|
defp note?(%{"object" => %{"type" => "Note"}}), do: true
|
||||||
defp is_note?(_), do: false
|
defp note?(_), do: false
|
||||||
|
|
||||||
defp is_eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true
|
defp eligible_type?(%{"type" => type}) when type in ["Create", "Update"], do: true
|
||||||
defp is_eligible_type?(_), do: false
|
defp eligible_type?(_), do: false
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def describe, do: {:ok, %{}}
|
def describe, do: {:ok, %{}}
|
||||||
|
|
|
@ -28,7 +28,7 @@ defp filter_object(%{"quoteUrl" => quote_url} = object) do
|
||||||
tags = object["tag"] || []
|
tags = object["tag"] || []
|
||||||
|
|
||||||
if Enum.any?(tags, fn 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
|
end) do
|
||||||
object
|
object
|
||||||
else
|
else
|
||||||
|
|
|
@ -36,6 +36,7 @@ defp steal_emoji({shortcode, url}, emoji_dir_path) do
|
||||||
|
|
||||||
extension = if extension == "", do: ".png", else: extension
|
extension = if extension == "", do: ".png", else: extension
|
||||||
|
|
||||||
|
shortcode = Path.basename(shortcode)
|
||||||
file_path = Path.join(emoji_dir_path, shortcode <> extension)
|
file_path = Path.join(emoji_dir_path, shortcode <> extension)
|
||||||
|
|
||||||
case File.write(file_path, response.body) do
|
case File.write(file_path, response.body) do
|
||||||
|
@ -78,6 +79,7 @@ def filter(%{"object" => %{"emoji" => foreign_emojis, "actor" => actor}} = messa
|
||||||
new_emojis =
|
new_emojis =
|
||||||
foreign_emojis
|
foreign_emojis
|
||||||
|> Enum.reject(fn {shortcode, _url} -> shortcode in installed_emoji end)
|
|> Enum.reject(fn {shortcode, _url} -> shortcode in installed_emoji end)
|
||||||
|
|> Enum.reject(fn {shortcode, _url} -> String.contains?(shortcode, ["/", "\\"]) end)
|
||||||
|> Enum.filter(fn {shortcode, _url} ->
|
|> Enum.filter(fn {shortcode, _url} ->
|
||||||
reject_emoji? =
|
reject_emoji? =
|
||||||
[:mrf_steal_emoji, :rejected_shortcodes]
|
[:mrf_steal_emoji, :rejected_shortcodes]
|
||||||
|
|
|
@ -173,6 +173,9 @@ def validate(
|
||||||
|
|
||||||
{:object_validation, e} ->
|
{:object_validation, e} ->
|
||||||
e
|
e
|
||||||
|
|
||||||
|
{:error, %Ecto.Changeset{} = e} ->
|
||||||
|
{:error, e}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ defp validate_announcable(cng) do
|
||||||
object when is_binary(object) <- get_field(cng, :object),
|
object when is_binary(object) <- get_field(cng, :object),
|
||||||
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
||||||
%Object{} = object <- Object.get_cached_by_ap_id(object),
|
%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
|
same_actor = object.data["actor"] == actor.ap_id
|
||||||
recipients = get_field(cng, :to) ++ get_field(cng, :cc)
|
recipients = get_field(cng, :to) ++ get_field(cng, :cc)
|
||||||
local_public = Utils.as_local_public()
|
local_public = Utils.as_local_public()
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -24,6 +25,8 @@ def cast_and_filter_recipients(message, field, follower_collection, field_fallba
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_object_defaults(data) do
|
def fix_object_defaults(data) do
|
||||||
|
data = Maps.filter_empty_values(data)
|
||||||
|
|
||||||
context =
|
context =
|
||||||
Utils.maybe_create_context(
|
Utils.maybe_create_context(
|
||||||
data["context"] || data["conversation"] || data["inReplyTo"] || data["id"]
|
data["context"] || data["conversation"] || data["inReplyTo"] || data["id"]
|
||||||
|
@ -99,7 +102,7 @@ def fix_quote_url(%{"_misskey_quote" => quote_url} = data) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_quote_url(%{"tag" => [_ | _] = tags} = data) do
|
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
|
if not is_nil(tag) do
|
||||||
data
|
data
|
||||||
|
@ -112,7 +115,7 @@ def fix_quote_url(%{"tag" => [_ | _] = tags} = data) do
|
||||||
def fix_quote_url(data), do: data
|
def fix_quote_url(data), do: data
|
||||||
|
|
||||||
# https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md
|
# https://codeberg.org/fediverse/fep/src/branch/main/fep/e232/fep-e232.md
|
||||||
def is_object_link_tag(%{
|
def object_link_tag?(%{
|
||||||
"type" => "Link",
|
"type" => "Link",
|
||||||
"mediaType" => media_type,
|
"mediaType" => media_type,
|
||||||
"href" => href
|
"href" => href
|
||||||
|
@ -121,5 +124,5 @@ def is_object_link_tag(%{
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_object_link_tag(_), do: false
|
def object_link_tag?(_), do: false
|
||||||
end
|
end
|
||||||
|
|
|
@ -74,10 +74,10 @@ defp fix_emoji_qualification(%{"content" => emoji} = data) do
|
||||||
new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji)
|
new_emoji = Pleroma.Emoji.fully_qualify_emoji(emoji)
|
||||||
|
|
||||||
cond do
|
cond do
|
||||||
Pleroma.Emoji.is_unicode_emoji?(emoji) ->
|
Pleroma.Emoji.unicode?(emoji) ->
|
||||||
data
|
data
|
||||||
|
|
||||||
Pleroma.Emoji.is_unicode_emoji?(new_emoji) ->
|
Pleroma.Emoji.unicode?(new_emoji) ->
|
||||||
data |> Map.put("content", new_emoji)
|
data |> Map.put("content", new_emoji)
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
|
@ -90,7 +90,7 @@ defp fix_emoji_qualification(data), do: data
|
||||||
defp validate_emoji(cng) do
|
defp validate_emoji(cng) do
|
||||||
content = get_field(cng, :content)
|
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
|
cng
|
||||||
else
|
else
|
||||||
cng
|
cng
|
||||||
|
@ -101,7 +101,7 @@ defp validate_emoji(cng) do
|
||||||
defp maybe_validate_tag_presence(cng) do
|
defp maybe_validate_tag_presence(cng) do
|
||||||
content = get_field(cng, :content)
|
content = get_field(cng, :content)
|
||||||
|
|
||||||
if Emoji.is_unicode_emoji?(content) do
|
if Emoji.unicode?(content) do
|
||||||
cng
|
cng
|
||||||
else
|
else
|
||||||
tag = get_field(cng, :tag)
|
tag = get_field(cng, :tag)
|
||||||
|
|
|
@ -62,7 +62,7 @@ defp maybe_federate(%Activity{} = activity, meta) do
|
||||||
with {:ok, local} <- Keyword.fetch(meta, :local) do
|
with {:ok, local} <- Keyword.fetch(meta, :local) do
|
||||||
do_not_federate = meta[:do_not_federate] || !config().get([:instance, :federating])
|
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 =
|
activity =
|
||||||
if object = Keyword.get(meta, :object_data) do
|
if object = Keyword.get(meta, :object_data) do
|
||||||
%{activity | data: Map.put(activity.data, "object", object)}
|
%{activity | data: Map.put(activity.data, "object", object)}
|
||||||
|
|
|
@ -66,7 +66,7 @@ def remote_users(%User{id: user_id}, %{data: %{"to" => to} = data}) do
|
||||||
@doc """
|
@doc """
|
||||||
Determine if an activity can be represented by running it through Transmogrifier.
|
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
|
with {:ok, _data} <- Transmogrifier.prepare_outgoing(activity.data) do
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
|
@ -246,7 +246,7 @@ def determine_inbox(
|
||||||
|
|
||||||
def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity)
|
def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity)
|
||||||
when is_list(bcc) and bcc != [] do
|
when is_list(bcc) and bcc != [] do
|
||||||
public = is_public?(activity)
|
public = public?(activity)
|
||||||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
||||||
|
|
||||||
[priority_recipients, recipients] = recipients(actor, activity)
|
[priority_recipients, recipients] = recipients(actor, activity)
|
||||||
|
@ -291,7 +291,7 @@ def publish(%User{} = actor, %{data: %{"bcc" => bcc}} = activity)
|
||||||
|
|
||||||
# Publishes an activity to all relevant peers.
|
# Publishes an activity to all relevant peers.
|
||||||
def publish(%User{} = actor, %Activity{} = activity) do
|
def publish(%User{} = actor, %Activity{} = activity) do
|
||||||
public = is_public?(activity)
|
public = public?(activity)
|
||||||
|
|
||||||
if public && Config.get([:instance, :allow_relay]) do
|
if public && Config.get([:instance, :allow_relay]) do
|
||||||
Logger.debug(fn -> "Relaying #{activity.data["id"]} out" end)
|
Logger.debug(fn -> "Relaying #{activity.data["id"]} out" end)
|
||||||
|
|
|
@ -58,7 +58,7 @@ defp fetch_target_user(ap_id, opts) do
|
||||||
@spec publish(any()) :: {:ok, Activity.t()} | {:error, any()}
|
@spec publish(any()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
def publish(%Activity{data: %{"type" => "Create"}} = activity) do
|
||||||
with %User{} = user <- get_actor(),
|
with %User{} = user <- get_actor(),
|
||||||
true <- Visibility.is_public?(activity) do
|
true <- Visibility.public?(activity) do
|
||||||
CommonAPI.repeat(activity.id, user)
|
CommonAPI.repeat(activity.id, user)
|
||||||
else
|
else
|
||||||
error -> format_error(error)
|
error -> format_error(error)
|
||||||
|
|
|
@ -258,7 +258,7 @@ def handle(%{data: %{"type" => "Announce"}} = object, meta) do
|
||||||
|
|
||||||
Utils.add_announce_to_object(object, announced_object)
|
Utils.add_announce_to_object(object, announced_object)
|
||||||
|
|
||||||
if !User.is_internal_user?(user) do
|
if !User.internal?(user) do
|
||||||
Notification.create_notifications(object)
|
Notification.create_notifications(object)
|
||||||
|
|
||||||
ap_streamer().stream_out(object)
|
ap_streamer().stream_out(object)
|
||||||
|
@ -304,9 +304,9 @@ def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object,
|
||||||
result =
|
result =
|
||||||
case deleted_object do
|
case deleted_object do
|
||||||
%Object{} ->
|
%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"]},
|
{_, 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"])
|
User.remove_pinned_object_id(user, deleted_object.data["id"])
|
||||||
|
|
||||||
{:ok, user} = ActivityPub.decrease_note_count_if_public(user, deleted_object)
|
{:ok, user} = ActivityPub.decrease_note_count_if_public(user, deleted_object)
|
||||||
|
@ -328,6 +328,17 @@ def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object,
|
||||||
{:actor, _} ->
|
{:actor, _} ->
|
||||||
@logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
|
@logger.error("The object doesn't have an actor: #{inspect(deleted_object)}")
|
||||||
:no_object_actor
|
: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
|
end
|
||||||
|
|
||||||
%User{} ->
|
%User{} ->
|
||||||
|
@ -569,7 +580,7 @@ def handle_undoing(
|
||||||
|
|
||||||
def handle_undoing(object), do: {:error, ["don't know how to handle", object]}
|
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
|
defp delete_object(object) do
|
||||||
with {:ok, _} <- Repo.delete(object), do: :ok
|
with {:ok, _} <- Repo.delete(object), do: :ok
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,5 +4,5 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do
|
defmodule Pleroma.Web.ActivityPub.SideEffects.Handling do
|
||||||
@callback handle(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
|
@callback handle(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()}
|
||||||
@callback handle_after_transaction(map()) :: map()
|
@callback handle_after_transaction(keyword()) :: keyword()
|
||||||
end
|
end
|
||||||
|
|
|
@ -336,10 +336,6 @@ def fix_tag(%{"tag" => %{} = tag} = object) do
|
||||||
|
|
||||||
def fix_tag(object), do: object
|
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.
|
# content map usually only has one language so this will do for now.
|
||||||
def fix_content_map(%{"contentMap" => content_map} = object) do
|
def fix_content_map(%{"contentMap" => content_map} = object) do
|
||||||
content_groups = Map.to_list(content_map)
|
content_groups = Map.to_list(content_map)
|
||||||
|
@ -783,7 +779,7 @@ def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => objec
|
||||||
|> Object.normalize(fetch: false)
|
|> Object.normalize(fetch: false)
|
||||||
|
|
||||||
data =
|
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)
|
data |> Map.put("object", object |> Map.get(:data) |> prepare_object)
|
||||||
else
|
else
|
||||||
data |> maybe_fix_object_url
|
data |> maybe_fix_object_url
|
||||||
|
|
|
@ -167,7 +167,7 @@ def maybe_federate(%Activity{local: true, data: %{"type" => type}} = activity) d
|
||||||
|
|
||||||
with true <- Config.get!([:instance, :federating]),
|
with true <- Config.get!([:instance, :federating]),
|
||||||
true <- type != "Block" || outgoing_blocks,
|
true <- type != "Block" || outgoing_blocks,
|
||||||
false <- Visibility.is_local_public?(activity) do
|
false <- Visibility.local_public?(activity) do
|
||||||
Pleroma.Web.Federator.publish(activity)
|
Pleroma.Web.Federator.publish(activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -277,7 +277,7 @@ def make_like_data(
|
||||||
object_actor = User.get_cached_by_ap_id(object_actor_id)
|
object_actor = User.get_cached_by_ap_id(object_actor_id)
|
||||||
|
|
||||||
to =
|
to =
|
||||||
if Visibility.is_public?(object) do
|
if Visibility.public?(object) do
|
||||||
[actor.follower_address, object.data["actor"]]
|
[actor.follower_address, object.data["actor"]]
|
||||||
else
|
else
|
||||||
[object.data["actor"]]
|
[object.data["actor"]]
|
||||||
|
@ -776,10 +776,9 @@ defp build_flag_object(act) when is_map(act) or is_binary(act) do
|
||||||
build_flag_object(object)
|
build_flag_object(object)
|
||||||
|
|
||||||
nil ->
|
nil ->
|
||||||
if %Object{} = object = Object.get_by_ap_id(id) do
|
case Object.get_by_ap_id(id) do
|
||||||
build_flag_object(object)
|
%Object{} = object -> build_flag_object(object)
|
||||||
else
|
_ -> %{"id" => id, "deleted" => true}
|
||||||
%{"id" => id, "deleted" => true}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,28 +11,28 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||||
|
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
@spec is_public?(Object.t() | Activity.t() | map()) :: boolean()
|
@spec public?(Object.t() | Activity.t() | map()) :: boolean()
|
||||||
def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
def public?(%Object{data: %{"type" => "Tombstone"}}), do: false
|
||||||
def is_public?(%Object{data: data}), do: is_public?(data)
|
def public?(%Object{data: data}), do: public?(data)
|
||||||
def is_public?(%Activity{data: %{"type" => "Move"}}), do: true
|
def public?(%Activity{data: %{"type" => "Move"}}), do: true
|
||||||
def is_public?(%Activity{data: data}), do: is_public?(data)
|
def public?(%Activity{data: data}), do: public?(data)
|
||||||
def is_public?(%{"directMessage" => true}), do: false
|
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?(Pleroma.Constants.as_public(), data) or
|
||||||
Utils.label_in_message?(Utils.as_local_public(), data)
|
Utils.label_in_message?(Utils.as_local_public(), data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_local_public?(%Object{data: data}), do: is_local_public?(data)
|
def local_public?(%Object{data: data}), do: local_public?(data)
|
||||||
def is_local_public?(%Activity{data: data}), do: is_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
|
Utils.label_in_message?(Utils.as_local_public(), data) and
|
||||||
not Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
not Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_private?(activity) do
|
def private?(activity) do
|
||||||
with false <- is_public?(activity),
|
with false <- public?(activity),
|
||||||
%User{follower_address: follower_address} <-
|
%User{follower_address: follower_address} <-
|
||||||
User.get_cached_by_ap_id(activity.data["actor"]) do
|
User.get_cached_by_ap_id(activity.data["actor"]) do
|
||||||
follower_address in activity.data["to"]
|
follower_address in activity.data["to"]
|
||||||
|
@ -41,20 +41,20 @@ def is_private?(activity) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_announceable?(activity, user, public \\ true) do
|
def announceable?(activity, user, public \\ true) do
|
||||||
is_public?(activity) ||
|
public?(activity) ||
|
||||||
(!public && is_private?(activity) && activity.data["actor"] == user.ap_id)
|
(!public && private?(activity) && activity.data["actor"] == user.ap_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_direct?(%Activity{data: %{"directMessage" => true}}), do: true
|
def direct?(%Activity{data: %{"directMessage" => true}}), do: true
|
||||||
def is_direct?(%Object{data: %{"directMessage" => true}}), do: true
|
def direct?(%Object{data: %{"directMessage" => true}}), do: true
|
||||||
|
|
||||||
def is_direct?(activity) do
|
def direct?(activity) do
|
||||||
!is_public?(activity) && !is_private?(activity)
|
!public?(activity) && !private?(activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_list?(%{data: %{"listMessage" => _}}), do: true
|
def list?(%{data: %{"listMessage" => _}}), do: true
|
||||||
def is_list?(_), do: false
|
def list?(_), do: false
|
||||||
|
|
||||||
@spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean()
|
@spec visible_for_user?(Object.t() | Activity.t() | nil, User.t() | nil) :: boolean()
|
||||||
def visible_for_user?(%Object{data: %{"type" => "Tombstone"}}, _), do: false
|
def visible_for_user?(%Object{data: %{"type" => "Tombstone"}}, _), do: false
|
||||||
|
@ -77,7 +77,7 @@ def visible_for_user?(%{__struct__: module} = message, nil)
|
||||||
when module in [Activity, Object] do
|
when module in [Activity, Object] do
|
||||||
if restrict_unauthenticated_access?(message),
|
if restrict_unauthenticated_access?(message),
|
||||||
do: false,
|
do: false,
|
||||||
else: is_public?(message) and not is_local_public?(message)
|
else: public?(message) and not local_public?(message)
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_for_user?(%{__struct__: module} = message, user)
|
def visible_for_user?(%{__struct__: module} = message, user)
|
||||||
|
@ -86,8 +86,8 @@ def visible_for_user?(%{__struct__: module} = message, user)
|
||||||
y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || [])
|
y = [message.data["actor"]] ++ message.data["to"] ++ (message.data["cc"] || [])
|
||||||
|
|
||||||
user_is_local = user.local
|
user_is_local = user.local
|
||||||
federatable = not is_local_public?(message)
|
federatable = not local_public?(message)
|
||||||
(is_public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable)
|
(public?(message) || Enum.any?(x, &(&1 in y))) and (user_is_local || federatable)
|
||||||
end
|
end
|
||||||
|
|
||||||
def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
|
def entire_thread_visible_for_user?(%Activity{} = activity, %User{} = user) do
|
||||||
|
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
|
||||||
alias Pleroma.ConfigDB
|
alias Pleroma.ConfigDB
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
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(OAuthScopesPlug, %{scopes: ["admin:write"]} when action == :update)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -76,7 +76,7 @@ def descriptions(conn, _params) do
|
||||||
json(conn, translate_descriptions(descriptions))
|
json(conn, translate_descriptions(descriptions))
|
||||||
end
|
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
|
with :ok <- configurable_from_database() do
|
||||||
configs = Pleroma.Repo.all(ConfigDB)
|
configs = Pleroma.Repo.all(ConfigDB)
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ def show(conn, _params) do
|
||||||
end
|
end
|
||||||
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
|
with :ok <- configurable_from_database() do
|
||||||
results =
|
results =
|
||||||
configs
|
configs
|
||||||
|
|
|
@ -9,7 +9,7 @@ defmodule Pleroma.Web.AdminAPI.InstanceDocumentController do
|
||||||
alias Pleroma.Web.Plugs.InstanceStatic
|
alias Pleroma.Web.Plugs.InstanceStatic
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
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:read"]} when action == :show)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["admin:write"]} when action in [:update, :delete])
|
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),
|
with {:ok, url} <- InstanceDocument.get(document_name),
|
||||||
{:ok, content} <- File.read(InstanceStatic.file_path(url)) do
|
{:ok, content} <- File.read(InstanceStatic.file_path(url)) do
|
||||||
conn
|
conn
|
||||||
|
@ -27,13 +27,18 @@ def show(conn, %{name: document_name}) do
|
||||||
end
|
end
|
||||||
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
|
with {:ok, url} <- InstanceDocument.put(document_name, file.path) do
|
||||||
json(conn, %{"url" => url})
|
json(conn, %{"url" => url})
|
||||||
end
|
end
|
||||||
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
|
with :ok <- InstanceDocument.delete(document_name) do
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.AdminAPI.InviteController do
|
||||||
|
|
||||||
require Logger
|
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(OAuthScopesPlug, %{scopes: ["admin:read:invites"]} when action == :index)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -33,14 +33,14 @@ def index(conn, _params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Create an account registration invite token"
|
@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)
|
{:ok, invite} = UserInviteToken.create_invite(params)
|
||||||
|
|
||||||
render(conn, "show.json", invite: invite)
|
render(conn, "show.json", invite: invite)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Revokes invite by token"
|
@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),
|
with {:ok, invite} <- UserInviteToken.find_by_token(token),
|
||||||
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
|
{:ok, updated_invite} = UserInviteToken.update_invite(invite, %{used: true}) do
|
||||||
render(conn, "show.json", invite: updated_invite)
|
render(conn, "show.json", invite: updated_invite)
|
||||||
|
@ -51,7 +51,13 @@ def revoke(%{body_params: %{token: token}} = conn, _) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Sends registration invite via email"
|
@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])},
|
with {_, false} <- {:registrations_open, Config.get([:instance, :registrations_open])},
|
||||||
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
|
{_, true} <- {:invites_enabled, Config.get([:instance, :invites_enabled])},
|
||||||
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
||||||
|
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
|
||||||
|
|
||||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
|
@ -27,7 +27,7 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Spec.MediaProxyCacheOperation
|
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)
|
entries = fetch_entries(params)
|
||||||
urls = paginate_entries(entries, params.page, params.page_size)
|
urls = paginate_entries(entries, params.page, params.page_size)
|
||||||
|
|
||||||
|
@ -59,12 +59,19 @@ defp paginate_entries(entries, page, page_size) do
|
||||||
Enum.slice(entries, offset, page_size)
|
Enum.slice(entries, offset, page_size)
|
||||||
end
|
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)
|
MediaProxy.remove_from_banned_urls(urls)
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
end
|
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)
|
MediaProxy.Invalidation.purge(urls)
|
||||||
|
|
||||||
if ban do
|
if ban do
|
||||||
|
|
|
@ -11,7 +11,7 @@ defmodule Pleroma.Web.AdminAPI.RelayController do
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
|
@ -31,7 +31,13 @@ def index(conn, _params) do
|
||||||
end
|
end
|
||||||
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
|
with {:ok, _message} <- Relay.follow(target) do
|
||||||
ModerationLog.insert_log(%{action: "relay_follow", actor: admin, target: target})
|
ModerationLog.insert_log(%{action: "relay_follow", actor: admin, target: target})
|
||||||
|
|
||||||
|
@ -44,7 +50,13 @@ def follow(%{assigns: %{user: admin}, body_params: %{relay_url: target}} = conn,
|
||||||
end
|
end
|
||||||
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
|
with {:ok, _message} <- Relay.unfollow(target, %{force: params[:force]}) do
|
||||||
ModerationLog.insert_log(%{action: "relay_unfollow", actor: admin, target: target})
|
ModerationLog.insert_log(%{action: "relay_unfollow", actor: admin, target: target})
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
|
||||||
|
|
||||||
require Logger
|
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(OAuthScopesPlug, %{scopes: ["admin:read:reports"]} when action in [:index, :show])
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -31,13 +31,13 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.ReportOperation
|
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)
|
reports = Utils.get_reports(params, params.page, params.page_size)
|
||||||
|
|
||||||
render(conn, "index.json", reports: reports)
|
render(conn, "index.json", reports: reports)
|
||||||
end
|
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
|
with %Activity{} = report <- Activity.get_report(id) do
|
||||||
render(conn, "show.json", Report.extract_report_info(report))
|
render(conn, "show.json", Report.extract_report_info(report))
|
||||||
else
|
else
|
||||||
|
@ -45,7 +45,13 @@ def show(conn, %{id: id}) do
|
||||||
end
|
end
|
||||||
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 =
|
result =
|
||||||
Enum.map(reports, fn report ->
|
Enum.map(reports, fn report ->
|
||||||
case CommonAPI.update_report_state(report.id, report.state) do
|
case CommonAPI.update_report_state(report.id, report.state) do
|
||||||
|
@ -73,9 +79,13 @@ def update(%{assigns: %{user: admin}, body_params: %{reports: reports}} = conn,
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = conn, %{
|
def notes_create(
|
||||||
id: report_id
|
%{
|
||||||
}) do
|
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),
|
with {:ok, _} <- ReportNote.create(user.id, report_id, content),
|
||||||
report <- Activity.get_by_id_with_user_actor(report_id) do
|
report <- Activity.get_by_id_with_user_actor(report_id) do
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
|
@ -92,10 +102,20 @@ def notes_create(%{assigns: %{user: user}, body_params: %{content: content}} = c
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def notes_delete(%{assigns: %{user: user}} = conn, %{
|
def notes_delete(
|
||||||
|
%{
|
||||||
|
assigns: %{user: user},
|
||||||
|
private: %{
|
||||||
|
open_api_spex: %{
|
||||||
|
params: %{
|
||||||
id: note_id,
|
id: note_id,
|
||||||
report_id: report_id
|
report_id: report_id
|
||||||
}) do
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} = conn,
|
||||||
|
_
|
||||||
|
) do
|
||||||
with {:ok, note} <- ReportNote.destroy(note_id),
|
with {:ok, note} <- ReportNote.destroy(note_id),
|
||||||
report <- Activity.get_by_id_with_user_actor(report_id) do
|
report <- Activity.get_by_id_with_user_actor(report_id) do
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
|
|
|
@ -18,7 +18,7 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
||||||
|
|
||||||
@users_page_size 50
|
@users_page_size 50
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
|
@ -51,13 +51,22 @@ defmodule Pleroma.Web.AdminAPI.UserController do
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.Admin.UserOperation
|
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
|
conn
|
||||||
|> Map.put(:body_params, %{nicknames: [nickname]})
|
|> do_deletes([nickname])
|
||||||
|> delete(%{})
|
|
||||||
end
|
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)
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
|
||||||
Enum.each(users, fn user ->
|
Enum.each(users, fn user ->
|
||||||
|
@ -77,10 +86,14 @@ def delete(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = co
|
||||||
def follow(
|
def follow(
|
||||||
%{
|
%{
|
||||||
assigns: %{user: admin},
|
assigns: %{user: admin},
|
||||||
|
private: %{
|
||||||
|
open_api_spex: %{
|
||||||
body_params: %{
|
body_params: %{
|
||||||
follower: follower_nick,
|
follower: follower_nick,
|
||||||
followed: followed_nick
|
followed: followed_nick
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} = conn,
|
} = conn,
|
||||||
_
|
_
|
||||||
) do
|
) do
|
||||||
|
@ -102,10 +115,14 @@ def follow(
|
||||||
def unfollow(
|
def unfollow(
|
||||||
%{
|
%{
|
||||||
assigns: %{user: admin},
|
assigns: %{user: admin},
|
||||||
|
private: %{
|
||||||
|
open_api_spex: %{
|
||||||
body_params: %{
|
body_params: %{
|
||||||
follower: follower_nick,
|
follower: follower_nick,
|
||||||
followed: followed_nick
|
followed: followed_nick
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} = conn,
|
} = conn,
|
||||||
_
|
_
|
||||||
) do
|
) do
|
||||||
|
@ -124,7 +141,13 @@ def unfollow(
|
||||||
json(conn, "ok")
|
json(conn, "ok")
|
||||||
end
|
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 =
|
changesets =
|
||||||
users
|
users
|
||||||
|> Enum.map(fn %{nickname: nickname, email: email, password: password} ->
|
|> Enum.map(fn %{nickname: nickname, email: email, password: password} ->
|
||||||
|
@ -178,7 +201,13 @@ def create(%{assigns: %{user: admin}, body_params: %{users: users}} = conn, _) d
|
||||||
end
|
end
|
||||||
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
|
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
|
||||||
render(conn, "show.json", %{user: user})
|
render(conn, "show.json", %{user: user})
|
||||||
else
|
else
|
||||||
|
@ -186,7 +215,11 @@ def show(%{assigns: %{user: admin}} = conn, %{nickname: nickname}) do
|
||||||
end
|
end
|
||||||
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)
|
user = User.get_cached_by_nickname(nickname)
|
||||||
|
|
||||||
{:ok, updated_user} = User.set_activation(user, !user.is_active)
|
{:ok, updated_user} = User.set_activation(user, !user.is_active)
|
||||||
|
@ -202,7 +235,13 @@ def toggle_activation(%{assigns: %{user: admin}} = conn, %{nickname: nickname})
|
||||||
render(conn, "show.json", user: updated_user)
|
render(conn, "show.json", user: updated_user)
|
||||||
end
|
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)
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
{:ok, updated_users} = User.set_activation(users, true)
|
{:ok, updated_users} = User.set_activation(users, true)
|
||||||
|
|
||||||
|
@ -212,10 +251,16 @@ def activate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} =
|
||||||
action: "activate"
|
action: "activate"
|
||||||
})
|
})
|
||||||
|
|
||||||
render(conn, "index.json", users: Keyword.values(updated_users))
|
render(conn, "index.json", users: updated_users)
|
||||||
end
|
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)
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
{:ok, updated_users} = User.set_activation(users, false)
|
{:ok, updated_users} = User.set_activation(users, false)
|
||||||
|
|
||||||
|
@ -225,10 +270,16 @@ def deactivate(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}}
|
||||||
action: "deactivate"
|
action: "deactivate"
|
||||||
})
|
})
|
||||||
|
|
||||||
render(conn, "index.json", users: Keyword.values(updated_users))
|
render(conn, "index.json", users: updated_users)
|
||||||
end
|
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)
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
{:ok, updated_users} = User.approve(users)
|
{:ok, updated_users} = User.approve(users)
|
||||||
|
|
||||||
|
@ -241,7 +292,13 @@ def approve(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = c
|
||||||
render(conn, "index.json", users: updated_users)
|
render(conn, "index.json", users: updated_users)
|
||||||
end
|
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)
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
{:ok, updated_users} = User.set_suggestion(users, true)
|
{:ok, updated_users} = User.set_suggestion(users, true)
|
||||||
|
|
||||||
|
@ -254,7 +311,13 @@ def suggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} = c
|
||||||
render(conn, "index.json", users: updated_users)
|
render(conn, "index.json", users: updated_users)
|
||||||
end
|
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)
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
{:ok, updated_users} = User.set_suggestion(users, false)
|
{:ok, updated_users} = User.set_suggestion(users, false)
|
||||||
|
|
||||||
|
@ -267,7 +330,7 @@ def unsuggest(%{assigns: %{user: admin}, body_params: %{nicknames: nicknames}} =
|
||||||
render(conn, "index.json", users: updated_users)
|
render(conn, "index.json", users: updated_users)
|
||||||
end
|
end
|
||||||
|
|
||||||
def index(conn, params) do
|
def index(%{private: %{open_api_spex: %{params: params}}} = conn, _) do
|
||||||
{page, page_size} = page_params(params)
|
{page, page_size} = page_params(params)
|
||||||
filters = maybe_parse_filters(params[:filters])
|
filters = maybe_parse_filters(params[:filters])
|
||||||
|
|
||||||
|
|
|
@ -27,10 +27,12 @@ def init(opts) do
|
||||||
|
|
||||||
@impl Plug
|
@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)
|
{spec, operation_lookup} = PutApiSpec.get_spec_and_operation_lookup(conn)
|
||||||
operation = operation_lookup[operation_id]
|
operation = operation_lookup[operation_id]
|
||||||
|
|
||||||
|
cast_opts = opts |> Map.take([:replace_params]) |> Map.to_list()
|
||||||
|
|
||||||
content_type =
|
content_type =
|
||||||
case Conn.get_req_header(conn, "content-type") do
|
case Conn.get_req_header(conn, "content-type") do
|
||||||
[header_value | _] ->
|
[header_value | _] ->
|
||||||
|
@ -44,7 +46,7 @@ def call(conn, %{operation_id: operation_id, render_error: render_error}) do
|
||||||
|
|
||||||
conn = Conn.put_private(conn, :operation_id, operation_id)
|
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} ->
|
{:ok, conn} ->
|
||||||
conn
|
conn
|
||||||
|
|
||||||
|
@ -94,11 +96,11 @@ def call(
|
||||||
|
|
||||||
def call(conn, opts), do: OpenApiSpex.Plug.CastAndValidate.call(conn, opts)
|
def call(conn, opts), do: OpenApiSpex.Plug.CastAndValidate.call(conn, opts)
|
||||||
|
|
||||||
defp cast_and_validate(spec, operation, conn, content_type, true = _strict) do
|
defp cast_and_validate(spec, operation, conn, content_type, true = _strict, cast_opts) do
|
||||||
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type)
|
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
|
||||||
end
|
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
|
case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
|
||||||
{:ok, conn} ->
|
{:ok, conn} ->
|
||||||
{:ok, conn}
|
{:ok, conn}
|
||||||
|
@ -123,7 +125,7 @@ defp cast_and_validate(spec, operation, conn, content_type, false = _strict) do
|
||||||
end)
|
end)
|
||||||
|
|
||||||
conn = %Conn{conn | query_params: query_params}
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ def with_relationships_param do
|
||||||
Operation.parameter(
|
Operation.parameter(
|
||||||
:with_relationships,
|
:with_relationships,
|
||||||
:query,
|
:query,
|
||||||
BooleanLike,
|
BooleanLike.schema(),
|
||||||
"Embed relationships into accounts. **If this parameter is not set account's `pleroma.relationship` is going to be `null`.**"
|
"Embed relationships into accounts. **If this parameter is not set account's `pleroma.relationship` is going to be `null`.**"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue