Merge develop :)

Signed-off-by: Sam Therapy <sam@samtherapy.net>
This commit is contained in:
Sam Therapy 2022-12-13 23:01:02 +01:00
commit 0cfd42a7d4
Signed by: sam
GPG key ID: 4D8B07C18F31ACBD
64 changed files with 5284 additions and 4014 deletions

View file

@ -1,6 +1,8 @@
name: "Bug report"
about: "Something isn't working as expected"
title: "[bug] "
labels:
- bug
body:
- type: markdown
attributes:

View file

@ -1,6 +1,9 @@
name: "Feature request"
about: "I'd like something to be added to Akkoma"
title: "[feat] "
labels:
- "feature request"
body:
- type: markdown
attributes:

View file

@ -6,6 +6,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
## Removed
- Non-finch HTTP adapters
## Upgrade notes
- Ensure `config :tesla, :adapter` is either unset, or set to `{Tesla.Adapter.Finch, name: MyFinch}` in your .exs config
## 2022.12
## Added
- Config: HTTP timeout options, :pool\_timeout and :receive\_timeout
- Added statistic gathering about instances which do/don't have signed fetches when they request from us
@ -13,6 +21,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Regular task to prune local transient activities
- Task to manually run the transient prune job (pleroma.database prune\_task)
- Ability to follow hashtags
- Option to extend `reject` in MRF-Simple to apply to entire threads, where the originating instance is rejected
- Extra information to failed HTTP requests
## Changed
- MastoAPI: Accept BooleanLike input on `/api/v1/accounts/:id/follow` (fixes follows with mastodon.py)
@ -21,6 +31,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Follow/Block/Mute imports now spin off into *n* tasks to avoid the oban timeout
- Transient activities recieved from remote servers are no longer persisted in the database
- Overhauled static-fe view for logged-out users
- Blocked instances will now not be sent _any_ requests, even fetch ones that would get rejected by MRF anyhow
## Removed
- FollowBotPolicy
- Passing of undo/block into MRF
## Upgrade Notes
- If you have an old instance, you will probably want to run `mix pleroma.database prune_task` in the foreground to catch it up with the history of your instance.

View file

@ -1,6 +1,7 @@
FROM hexpm/elixir:1.13.4-erlang-24.3.4.5-alpine-3.15.6
ENV MIX_ENV=prod
ENV ERL_EPMD_ADDRESS=127.0.0.1
ARG HOME=/opt/akkoma

View file

@ -163,11 +163,6 @@
format: "$metadata[$level] $message",
metadata: [:request_id]
config :quack,
level: :warn,
meta: [:all],
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
config :mime, :types, %{
"application/xml" => ["xml"],
"application/xrd+xml" => ["xrd+xml"],
@ -396,7 +391,8 @@
accept: [],
avatar_removal: [],
banner_removal: [],
reject_deletes: []
reject_deletes: [],
handle_threads: true
config :pleroma, :mrf_keyword,
reject: [],
@ -659,6 +655,10 @@
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false
config :swoosh,
api_client: Swoosh.ApiClient.Finch,
finch_name: MyFinch
config :pleroma, Pleroma.Emails.UserEmail,
logo: nil,
styling: %{

View file

@ -1147,45 +1147,6 @@
}
]
},
%{
group: :quack,
type: :group,
label: "Quack Logger",
description: "Quack-related settings",
children: [
%{
key: :level,
type: {:dropdown, :atom},
description: "Log level",
suggestions: [:debug, :info, :warn, :error]
},
%{
key: :meta,
type: {:list, :atom},
description: "Configure which metadata you want to report on",
suggestions: [
:application,
:module,
:file,
:function,
:line,
:pid,
:crash_reason,
:initial_call,
:registered_name,
:all,
:none
]
},
%{
key: :webhook_url,
label: "Webhook URL",
type: :string,
description: "Configure the Slack incoming webhook",
suggestions: ["https://hooks.slack.com/services/YOUR-KEY-HERE"]
}
]
},
%{
group: :pleroma,
key: :frontend_configurations,

View file

@ -1,4 +1,5 @@
MIX_ENV=prod
ERL_EPMD_ADDRESS=127.0.0.1
DB_NAME=akkoma
DB_USER=akkoma
DB_PASS=akkoma

View file

@ -230,11 +230,6 @@ Notes:
- The hashtags in the configuration do not have a leading `#`.
- This MRF Policy is always enabled, if you want to disable it you have to set empty lists
#### :mrf_follow_bot
* `follower_nickname`: The name of the bot account to use for following newly discovered users. Using `followbot` or similar is strongly suggested.
### :activitypub
* `unfollow_blocked`: Whether blocks result in people getting unfollowed
* `outgoing_blocks`: Whether to federate blocks to other instances
@ -542,54 +537,6 @@ Available caches:
* `user_agent`: what user agent should we use? (default: `:default`), must be string or `:default`
* `adapter`: array of adapter options
### :hackney_pools
Advanced. Tweaks Hackney (http client) connections pools.
There's three pools used:
* `:federation` for the federation jobs.
You may want this pool max_connections to be at least equal to the number of federator jobs + retry queue jobs.
* `:media` for rich media, media proxy
* `:upload` for uploaded media (if using a remote uploader and `proxy_remote: true`)
For each pool, the options are:
* `max_connections` - how much connections a pool can hold
* `timeout` - retention duration for connections
### :connections_pool
*For `gun` adapter*
Settings for HTTP connection pool.
* `:connection_acquisition_wait` - Timeout to acquire a connection from pool.The total max time is this value multiplied by the number of retries.
* `connection_acquisition_retries` - Number of attempts to acquire the connection from the pool if it is overloaded. Each attempt is timed `:connection_acquisition_wait` apart.
* `:max_connections` - Maximum number of connections in the pool.
* `:connect_timeout` - Timeout to connect to the host.
* `:reclaim_multiplier` - Multiplied by `:max_connections` this will be the maximum number of idle connections that will be reclaimed in case the pool is overloaded.
### :pools
*For `gun` adapter*
Settings for request pools. These pools are limited on top of `:connections_pool`.
There are four pools used:
* `:federation` for the federation jobs. You may want this pool's max_connections to be at least equal to the number of federator jobs + retry queue jobs.
* `:media` - for rich media, media proxy.
* `:upload` - for proxying media when a remote uploader is used and `proxy_remote: true`.
* `:default` - for other requests.
For each pool, the options are:
* `:size` - limit to how much requests can be concurrently executed.
* `:recv_timeout` - timeout while `gun` will wait for response
* `:max_waiting` - limit to how much requests can be waiting for others to finish, after this is reached, subsequent requests will be dropped.
## Captcha
### Pleroma.Captcha
@ -847,17 +794,8 @@ config :logger, :ex_syslogger,
level: :info,
ident: "pleroma",
format: "$metadata[$level] $message"
config :quack,
level: :warn,
meta: [:all],
webhook_url: "https://hooks.slack.com/services/YOUR-API-KEY-HERE"
```
See the [Quack Github](https://github.com/azohra/quack) for more details
## Database options
### RUM indexing for full text search

View file

@ -1057,14 +1057,13 @@ Most of the settings will be applied in `runtime`, this means that you don't nee
Example of setting without keyword in value:
```elixir
config :tesla, :adapter, Tesla.Adapter.Hackney
config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}
```
List of settings which support only full update by key:
```elixir
@full_key_update [
{:pleroma, :ecto_repos},
{:quack, :meta},
{:mime, :types},
{:cors_plug, [:max_age, :methods, :expose, :headers]},
{:auto_linker, :opts},
@ -1084,22 +1083,6 @@ List of settings which support only full update by subkey:
]
```
*Settings without explicit key must be sended in separate config object params.*
```elixir
config :quack,
level: :debug,
meta: [:all],
...
```
```json
{
"configs": [
{"group": ":quack", "key": ":level", "value": ":debug"},
{"group": ":quack", "key": ":meta", "value": [":all"]},
...
]
}
```
- Request:
```json

View file

@ -12,8 +12,21 @@ theme:
- navigation.instant
- navigation.sections
palette:
primary: 'deep purple'
accent: 'blue grey'
- media: "(prefers-color-scheme: light)"
scheme: default
toggle:
icon: material/brightness-7
name: Switch to dark mode
primary: 'deep purple'
accent: 'blue grey'
- media: "(prefers-color-scheme: dark)"
scheme: slate
toggle:
icon: material/brightness-4
name: Switch to light mode
primary: 'deep purple'
accent: 'blue grey'
extra_css:
- css/extra.css

View file

@ -4,7 +4,6 @@ After=network.target postgresql.service
[Service]
ExecReload=/bin/kill $MAINPID
KillMode=process
Restart=on-failure
; Uncomment this if you're on Arch Linux
@ -15,6 +14,9 @@ User=akkoma
; Declares that Akkoma runs in production mode.
Environment="MIX_ENV=prod"
; Don't listen epmd on 0.0.0.0
Environment="ERL_EPMD_ADDRESS=127.0.0.1"
; Make sure that all paths fit your installation.
; Path to the home directory of the user running the Akkoma service.
Environment="HOME=/var/lib/akkoma"

View file

@ -12,7 +12,8 @@ environment =
HOME=/home/akkoma,
USER=akkoma,
PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/akkoma/bin:%(ENV_PATH)s",
PWD=/home/akkoma/akkoma
PWD=/home/akkoma/akkoma,
ERL_EPMD_ADDRESS=127.0.0.1
stdout_logfile=/home/akkoma/logs/stdout.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10

View file

@ -18,7 +18,8 @@ load_rc_config ${name}
: ${akkoma_user:=akkoma}
: ${akkoma_home:=$(getent passwd ${akkoma_user} | awk -F: '{print $6}')}
: ${akkoma_chdir:="${akkoma_home}/akkoma"}
: ${akkoma_env:="HOME=${akkoma_home} MIX_ENV=prod"}
: ${akkoma_env:="HOME=${akkoma_home} MIX_ENV=prod ERL_EPMD_ADDRESS=127.0.0.1"}
command=/usr/local/bin/elixir
command_args="--erl \"-detached\" -S /usr/local/bin/mix phx.server"

View file

@ -31,6 +31,7 @@ else
fi
export MIX_ENV=prod
export ERL_EPMD_ADDRESS=127.0.0.1
depend() {
need nginx postgresql

View file

@ -14,7 +14,7 @@ start_precmd="ulimit -n unlimited"
pidfile="/dev/null"
akkoma_chdir="${akkoma_home}/akkoma"
akkoma_env="HOME=${akkoma_home} MIX_ENV=prod"
akkoma_env="HOME=${akkoma_home} MIX_ENV=prod ERL_EPMD_ADDRESS=127.0.0.1"
check_pidfile()
{

View file

@ -9,6 +9,13 @@ defmodule Mix.Tasks.Pleroma.Diagnostics do
import Ecto.Query
use Mix.Task
def run(["http", url]) do
start_pleroma()
Pleroma.HTTP.get(url)
|> IO.inspect()
end
def run(["home_timeline", nickname]) do
start_pleroma()
user = Repo.get_by!(User, nickname: nickname)

View file

@ -68,7 +68,7 @@ defp fetch_page_items(id, items \\ []) do
items
end
else
{:error, "Object has been deleted"} ->
{:error, {"Object has been deleted", _, _}} ->
items
{:error, error} ->

View file

@ -25,7 +25,7 @@ defmodule Pleroma.Config.DeprecationWarnings do
def check_simple_policy_tuples do
has_strings =
Config.get([:mrf_simple])
|> Enum.any?(fn {_, v} -> Enum.any?(v, &is_binary/1) end)
|> Enum.any?(fn {_, v} -> is_list(v) and Enum.any?(v, &is_binary/1) end)
if has_strings do
Logger.warn("""
@ -66,6 +66,7 @@ def check_simple_policy_tuples do
new_config =
Config.get([:mrf_simple])
|> Enum.filter(fn {_k, v} -> not is_atom(v) end)
|> Enum.map(fn {k, v} ->
{k,
Enum.map(v, fn
@ -181,7 +182,8 @@ def warn do
check_old_chat_shoutbox(),
check_quarantined_instances_tuples(),
check_transparency_exclusions_tuples(),
check_simple_policy_tuples()
check_simple_policy_tuples(),
check_http_adapter()
]
|> Enum.reduce(:ok, fn
:ok, :ok -> :ok
@ -210,6 +212,32 @@ def check_welcome_message_config do
end
end
def check_http_adapter do
http_adapter = Application.get_env(:tesla, :adapter)
case http_adapter do
{Tesla.Adapter.Finch, _} ->
:ok
Tesla.Mock ->
# tests do be testing
:ok
_anything_else ->
Logger.error("""
!!!CONFIG ERROR!!!
Your config is using a custom tesla adapter, this was standardised
to finch in 2022.06, and alternate adapters were removed in 2023.02.
Please ensure you either:
\n* do not have any custom value for `:tesla, :adapter`, or
\n* have `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}`
(your current value is #{inspect(http_adapter)})
""")
:error
end
end
def check_old_mrf_config do
warning_preface = """
!!!DEPRECATION WARNING!!!

View file

@ -42,7 +42,7 @@ def load_and_update_env(deleted_settings \\ [], restart_pleroma? \\ true) do
{logger, other} =
(Repo.all(ConfigDB) ++ deleted_settings)
|> Enum.map(&merge_with_default/1)
|> Enum.split_with(fn {group, _, _, _} -> group in [:logger, :quack] end)
|> Enum.split_with(fn {group, _, _, _} -> group == :logger end)
logger
|> Enum.sort()
@ -102,12 +102,6 @@ defp merge_with_default(%{group: group, key: key, value: value} = setting) do
{group, key, value, merged}
end
# change logger configuration in runtime, without restart
defp configure({:quack, key, _, merged}) do
Logger.configure_backend(Quack.Logger, [{key, merged}])
:ok = update_env(:quack, key, merged)
end
defp configure({_, :backends, _, merged}) do
# removing current backends
Enum.each(Application.get_env(:logger, :backends), &Logger.remove_backend/1)

View file

@ -163,7 +163,6 @@ defp can_be_partially_updated?(%ConfigDB{} = config), do: not only_full_update?(
defp only_full_update?(%ConfigDB{group: group, key: key}) do
full_key_update = [
{:pleroma, :ecto_repos},
{:quack, :meta},
{:mime, :types},
{:cors_plug, [:max_age, :methods, :expose, :headers]},
{:swarm, :node_blacklist},

View file

@ -35,11 +35,6 @@ def perform(:deliver_async, email, config), do: deliver(email, config)
def deliver(email, config \\ [])
def deliver(email, config) do
# temporary hackney fix until hackney max_connections bug is fixed
# https://git.pleroma.social/pleroma/pleroma/-/issues/2101
email =
Swoosh.Email.put_private(email, :hackney_options, ssl_options: [versions: [:"tlsv1.2"]])
case enabled?() do
true -> Swoosh.Mailer.deliver(email, parse_config(config))
false -> {:error, :deliveries_disabled}

View file

@ -93,7 +93,7 @@ defp download_build(frontend_info, dest) do
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
with {:ok, %{status: 200, body: zip_body}} <-
Pleroma.HTTP.get(url, [], recv_timeout: 120_000) do
Pleroma.HTTP.get(url, [], receive_timeout: 120_000) do
unzip(zip_body, dest)
else
{:error, e} -> {:error, e}

View file

@ -1,29 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun do
@callback open(charlist(), pos_integer(), map()) :: {:ok, pid()}
@callback info(pid()) :: map()
@callback close(pid()) :: :ok
@callback await_up(pid, pos_integer()) :: {:ok, atom()} | {:error, atom()}
@callback connect(pid(), map()) :: reference()
@callback await(pid(), reference()) :: {:response, :fin, 200, []}
@callback set_owner(pid(), pid()) :: :ok
defp api, do: Pleroma.Config.get([Pleroma.Gun], Pleroma.Gun.API)
def open(host, port, opts), do: api().open(host, port, opts)
def info(pid), do: api().info(pid)
def close(pid), do: api().close(pid)
def await_up(pid, timeout \\ 5_000), do: api().await_up(pid, timeout)
def connect(pid, opts), do: api().connect(pid, opts)
def await(pid, ref), do: api().await(pid, ref)
def set_owner(pid, owner), do: api().set_owner(pid, owner)
end

View file

@ -1,46 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.API do
@behaviour Pleroma.Gun
alias Pleroma.Gun
@gun_keys [
:connect_timeout,
:http_opts,
:http2_opts,
:protocols,
:retry,
:retry_timeout,
:trace,
:transport,
:tls_opts,
:tcp_opts,
:socks_opts,
:ws_opts,
:supervise
]
@impl Gun
def open(host, port, opts \\ %{}), do: :gun.open(host, port, Map.take(opts, @gun_keys))
@impl Gun
defdelegate info(pid), to: :gun
@impl Gun
defdelegate close(pid), to: :gun
@impl Gun
defdelegate await_up(pid, timeout \\ 5_000), to: :gun
@impl Gun
defdelegate connect(pid, opts), to: :gun
@impl Gun
defdelegate await(pid, ref), to: :gun
@impl Gun
defdelegate set_owner(pid, owner), to: :gun
end

View file

@ -1,131 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.Conn do
alias Pleroma.Gun
require Logger
def open(%URI{} = uri, opts) do
pool_opts = Pleroma.Config.get([:connections_pool], [])
opts =
opts
|> Enum.into(%{})
|> Map.put_new(:connect_timeout, pool_opts[:connect_timeout] || 5_000)
|> Map.put_new(:supervise, false)
|> maybe_add_tls_opts(uri)
do_open(uri, opts)
end
defp maybe_add_tls_opts(opts, %URI{scheme: "http"}), do: opts
defp maybe_add_tls_opts(opts, %URI{scheme: "https"}) do
tls_opts = [
verify: :verify_peer,
cacertfile: CAStore.file_path(),
depth: 20,
reuse_sessions: false,
log_level: :warning,
customize_hostname_check: [match_fun: :public_key.pkix_verify_hostname_match_fun(:https)]
]
tls_opts =
if Keyword.keyword?(opts[:tls_opts]) do
Keyword.merge(tls_opts, opts[:tls_opts])
else
tls_opts
end
Map.put(opts, :tls_opts, tls_opts)
end
defp do_open(uri, %{proxy: {proxy_host, proxy_port}} = opts) do
connect_opts =
uri
|> destination_opts()
|> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, []))
with open_opts <- Map.delete(opts, :tls_opts),
{:ok, conn} <- Gun.open(proxy_host, proxy_port, open_opts),
{:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]),
stream <- Gun.connect(conn, connect_opts),
{:response, :fin, 200, _} <- Gun.await(conn, stream) do
{:ok, conn, protocol}
else
error ->
Logger.warn(
"Opening proxied connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
)
error
end
end
defp do_open(uri, %{proxy: {proxy_type, proxy_host, proxy_port}} = opts) do
version =
proxy_type
|> to_string()
|> String.last()
|> case do
"4" -> 4
_ -> 5
end
socks_opts =
uri
|> destination_opts()
|> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, []))
|> Map.put(:version, version)
opts =
opts
|> Map.put(:protocols, [:socks])
|> Map.put(:socks_opts, socks_opts)
with {:ok, conn} <- Gun.open(proxy_host, proxy_port, opts),
{:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]) do
{:ok, conn, protocol}
else
error ->
Logger.warn(
"Opening socks proxied connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
)
error
end
end
defp do_open(%URI{host: host, port: port} = uri, opts) do
host = Pleroma.HTTP.AdapterHelper.parse_host(host)
with {:ok, conn} <- Gun.open(host, port, opts),
{:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]) do
{:ok, conn, protocol}
else
error ->
Logger.warn(
"Opening connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
)
error
end
end
defp destination_opts(%URI{host: host, port: port}) do
host = Pleroma.HTTP.AdapterHelper.parse_host(host)
%{host: host, port: port}
end
defp add_http2_opts(opts, "https", tls_opts) do
Map.merge(opts, %{protocols: [:http2], transport: :tls, tls_opts: tls_opts})
end
defp add_http2_opts(opts, _, _), do: opts
def compose_uri_log(%URI{scheme: scheme, host: host, path: path}) do
"#{scheme}://#{host}#{path}"
end
end

View file

@ -1,86 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.ConnectionPool do
@registry __MODULE__
alias Pleroma.Gun.ConnectionPool.WorkerSupervisor
def children do
[
{Registry, keys: :unique, name: @registry},
Pleroma.Gun.ConnectionPool.WorkerSupervisor
]
end
@spec get_conn(URI.t(), keyword()) :: {:ok, pid()} | {:error, term()}
def get_conn(uri, opts) do
key = "#{uri.scheme}:#{uri.host}:#{uri.port}"
case Registry.lookup(@registry, key) do
# The key has already been registered, but connection is not up yet
[{worker_pid, nil}] ->
get_gun_pid_from_worker(worker_pid, true)
[{worker_pid, {gun_pid, _used_by, _crf, _last_reference}}] ->
GenServer.call(worker_pid, :add_client)
{:ok, gun_pid}
[] ->
# :gun.set_owner fails in :connected state for whatevever reason,
# so we open the connection in the process directly and send it's pid back
# We trust gun to handle timeouts by itself
case WorkerSupervisor.start_worker([key, uri, opts, self()]) do
{:ok, worker_pid} ->
get_gun_pid_from_worker(worker_pid, false)
{:error, {:already_started, worker_pid}} ->
get_gun_pid_from_worker(worker_pid, true)
err ->
err
end
end
end
defp get_gun_pid_from_worker(worker_pid, register) do
# GenServer.call will block the process for timeout length if
# the server crashes on startup (which will happen if gun fails to connect)
# so instead we use cast + monitor
ref = Process.monitor(worker_pid)
if register, do: GenServer.cast(worker_pid, {:add_client, self()})
receive do
{:conn_pid, pid} ->
Process.demonitor(ref)
{:ok, pid}
{:DOWN, ^ref, :process, ^worker_pid, reason} ->
case reason do
{:shutdown, {:error, _} = error} -> error
{:shutdown, error} -> {:error, error}
_ -> {:error, reason}
end
end
end
@spec release_conn(pid()) :: :ok
def release_conn(conn_pid) do
# :ets.fun2ms(fn {_, {worker_pid, {gun_pid, _, _, _}}} when gun_pid == conn_pid ->
# worker_pid end)
query_result =
Registry.select(@registry, [
{{:_, :"$1", {:"$2", :_, :_, :_}}, [{:==, :"$2", conn_pid}], [:"$1"]}
])
case query_result do
[worker_pid] ->
GenServer.call(worker_pid, :remove_client)
[] ->
:ok
end
end
end

View file

@ -1,89 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
use GenServer, restart: :temporary
defp registry, do: Pleroma.Gun.ConnectionPool
def start_monitor do
pid =
case :gen_server.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
{:ok, pid} ->
pid
{:error, {:already_registered, pid}} ->
pid
end
{pid, Process.monitor(pid)}
end
@impl true
def init(_) do
{:ok, nil, {:continue, :reclaim}}
end
@impl true
def handle_continue(:reclaim, _) do
max_connections = Pleroma.Config.get([:connections_pool, :max_connections])
reclaim_max =
[:connections_pool, :reclaim_multiplier]
|> Pleroma.Config.get()
|> Kernel.*(max_connections)
|> round
|> max(1)
:telemetry.execute([:pleroma, :connection_pool, :reclaim, :start], %{}, %{
max_connections: max_connections,
reclaim_max: reclaim_max
})
# :ets.fun2ms(
# fn {_, {worker_pid, {_, used_by, crf, last_reference}}} when used_by == [] ->
# {worker_pid, crf, last_reference} end)
unused_conns =
Registry.select(
registry(),
[
{{:_, :"$1", {:_, :"$2", :"$3", :"$4"}}, [{:==, :"$2", []}], [{{:"$1", :"$3", :"$4"}}]}
]
)
case unused_conns do
[] ->
:telemetry.execute(
[:pleroma, :connection_pool, :reclaim, :stop],
%{reclaimed_count: 0},
%{
max_connections: max_connections
}
)
{:stop, :no_unused_conns, nil}
unused_conns ->
reclaimed =
unused_conns
|> Enum.sort(fn {_pid1, crf1, last_reference1}, {_pid2, crf2, last_reference2} ->
crf1 <= crf2 and last_reference1 <= last_reference2
end)
|> Enum.take(reclaim_max)
reclaimed
|> Enum.each(fn {pid, _, _} ->
DynamicSupervisor.terminate_child(Pleroma.Gun.ConnectionPool.WorkerSupervisor, pid)
end)
:telemetry.execute(
[:pleroma, :connection_pool, :reclaim, :stop],
%{reclaimed_count: Enum.count(reclaimed)},
%{max_connections: max_connections}
)
{:stop, :normal, nil}
end
end
end

View file

@ -1,153 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.ConnectionPool.Worker do
alias Pleroma.Gun
use GenServer, restart: :temporary
defp registry, do: Pleroma.Gun.ConnectionPool
def start_link([key | _] = opts) do
GenServer.start_link(__MODULE__, opts, name: {:via, Registry, {registry(), key}})
end
@impl true
def init([_key, _uri, _opts, _client_pid] = opts) do
{:ok, nil, {:continue, {:connect, opts}}}
end
@impl true
def handle_continue({:connect, [key, uri, opts, client_pid]}, _) do
with {:ok, conn_pid, protocol} <- Gun.Conn.open(uri, opts),
Process.link(conn_pid) do
time = :erlang.monotonic_time(:millisecond)
{_, _} =
Registry.update_value(registry(), key, fn _ ->
{conn_pid, [client_pid], 1, time}
end)
send(client_pid, {:conn_pid, conn_pid})
{:noreply,
%{
key: key,
timer: nil,
client_monitors: %{client_pid => Process.monitor(client_pid)},
protocol: protocol
}, :hibernate}
else
err ->
{:stop, {:shutdown, err}, nil}
end
end
@impl true
def handle_cast({:add_client, client_pid}, state) do
case handle_call(:add_client, {client_pid, nil}, state) do
{:reply, conn_pid, state, :hibernate} ->
send(client_pid, {:conn_pid, conn_pid})
{:noreply, state, :hibernate}
end
end
@impl true
def handle_cast({:remove_client, client_pid}, state) do
case handle_call(:remove_client, {client_pid, nil}, state) do
{:reply, _, state, :hibernate} ->
{:noreply, state, :hibernate}
end
end
@impl true
def handle_call(:add_client, {client_pid, _}, %{key: key, protocol: protocol} = state) do
time = :erlang.monotonic_time(:millisecond)
{{conn_pid, used_by, _, _}, _} =
Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} ->
{conn_pid, [client_pid | used_by], crf(time - last_reference, crf), time}
end)
:telemetry.execute(
[:pleroma, :connection_pool, :client, :add],
%{client_pid: client_pid, clients: used_by},
%{key: state.key, protocol: protocol}
)
state =
if state.timer != nil do
Process.cancel_timer(state[:timer])
%{state | timer: nil}
else
state
end
ref = Process.monitor(client_pid)
state = put_in(state.client_monitors[client_pid], ref)
{:reply, conn_pid, state, :hibernate}
end
@impl true
def handle_call(:remove_client, {client_pid, _}, %{key: key} = state) do
{{_conn_pid, used_by, _crf, _last_reference}, _} =
Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} ->
{conn_pid, List.delete(used_by, client_pid), crf, last_reference}
end)
{ref, state} = pop_in(state.client_monitors[client_pid])
Process.demonitor(ref, [:flush])
timer =
if used_by == [] do
max_idle = Pleroma.Config.get([:connections_pool, :max_idle_time], 30_000)
Process.send_after(self(), :idle_close, max_idle)
else
nil
end
{:reply, :ok, %{state | timer: timer}, :hibernate}
end
@impl true
def handle_info(:idle_close, state) do
# Gun monitors the owner process, and will close the connection automatically
# when it's terminated
{:stop, :normal, state}
end
@impl true
def handle_info({:gun_up, _pid, _protocol}, state) do
{:noreply, state, :hibernate}
end
# Gracefully shutdown if the connection got closed without any streams left
@impl true
def handle_info({:gun_down, _pid, _protocol, _reason, []}, state) do
{:stop, :normal, state}
end
# Otherwise, wait for retry
@impl true
def handle_info({:gun_down, _pid, _protocol, _reason, _killed_streams}, state) do
{:noreply, state, :hibernate}
end
@impl true
def handle_info({:DOWN, _ref, :process, pid, reason}, state) do
:telemetry.execute(
[:pleroma, :connection_pool, :client, :dead],
%{client_pid: pid, reason: reason},
%{key: state.key}
)
handle_cast({:remove_client, pid}, state)
end
# LRFU policy: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.55.1478
defp crf(time_delta, prev_crf) do
1 + :math.pow(0.5, 0.0001 * time_delta) * prev_crf
end
end

View file

@ -1,49 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do
@moduledoc "Supervisor for pool workers. Does not do anything except enforce max connection limit"
use DynamicSupervisor
def start_link(opts) do
DynamicSupervisor.start_link(__MODULE__, opts, name: __MODULE__)
end
def init(_opts) do
DynamicSupervisor.init(
strategy: :one_for_one,
max_children: Pleroma.Config.get([:connections_pool, :max_connections])
)
end
def start_worker(opts, retry \\ false) do
case DynamicSupervisor.start_child(__MODULE__, {Pleroma.Gun.ConnectionPool.Worker, opts}) do
{:error, :max_children} ->
if retry or free_pool() == :error do
:telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts})
{:error, :pool_full}
else
start_worker(opts, true)
end
res ->
res
end
end
defp free_pool do
wait_for_reclaimer_finish(Pleroma.Gun.ConnectionPool.Reclaimer.start_monitor())
end
defp wait_for_reclaimer_finish({pid, mon}) do
receive do
{:DOWN, ^mon, :process, ^pid, :no_unused_conns} ->
:error
{:DOWN, ^mon, :process, ^pid, :normal} ->
:ok
end
end
end

View file

@ -116,7 +116,11 @@ def refetch_object(%Object{data: %{"id" => id}} = object) do
# Note: will create a Create activity, which we need internally at the moment.
def fetch_object_from_id(id, options \\ []) do
with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
with %URI{} = uri <- URI.parse(id),
# If we have instance restrictions, apply them here to prevent fetching from unwanted instances
{:ok, nil} <- Pleroma.Web.ActivityPub.MRF.SimplePolicy.check_reject(uri),
{:ok, _} <- Pleroma.Web.ActivityPub.MRF.SimplePolicy.check_accept(uri),
{_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
{_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])},
{_, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
{_, nil} <- {:normalize, Object.normalize(data, fetch: false)},
@ -155,6 +159,9 @@ def fetch_object_from_id(id, options \\ []) do
{:fetch, {:error, error}} ->
{:error, error}
{:reject, reason} ->
{:reject, reason}
e ->
e
end
@ -180,7 +187,7 @@ def fetch_object_from_id!(id, options \\ []) do
{:error, %Tesla.Mock.Error{}} ->
nil
{:error, "Object has been deleted"} ->
{:error, {"Object has been deleted", _id, _code}} ->
nil
{:reject, reason} ->
@ -255,7 +262,7 @@ def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
def fetch_and_contain_remote_object_from_id(_id),
do: {:error, "id must be a string"}
defp get_object(id) do
def get_object(id) do
date = Pleroma.Signature.signed_date()
headers =
@ -275,6 +282,11 @@ defp get_object(id) do
%{"profile" => "https://www.w3.org/ns/activitystreams"}} ->
{:ok, body}
# pixelfed sometimes (and only sometimes) responds with http instead of https
{:ok, "application", "ld+json",
%{"profile" => "http://www.w3.org/ns/activitystreams"}} ->
{:ok, body}
_ ->
{:error, {:content_type, content_type}}
end
@ -284,7 +296,7 @@ defp get_object(id) do
end
{:ok, %{status: code}} when code in [404, 410] ->
{:error, "Object has been deleted"}
{:error, {"Object has been deleted", id, code}}
{:error, e} ->
{:error, e}

View file

@ -1,25 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ReverseProxy.Client.Hackney do
@behaviour Pleroma.ReverseProxy.Client
@impl true
def request(method, url, headers, body, opts \\ []) do
opts = Keyword.put(opts, :ssl_options, versions: [:"tlsv1.2", :"tlsv1.1", :tlsv1])
:hackney.request(method, url, headers, body, opts)
end
@impl true
def stream_body(ref) do
case :hackney.stream_body(ref) do
:done -> :done
{:ok, data} -> {:ok, data, ref}
{:error, error} -> {:error, error}
end
end
@impl true
def close(ref), do: :hackney.close(ref)
end

View file

@ -5,8 +5,6 @@
defmodule Pleroma.ReverseProxy.Client.Tesla do
@behaviour Pleroma.ReverseProxy.Client
alias Pleroma.Gun.ConnectionPool
@type headers() :: [{String.t(), String.t()}]
@type status() :: pos_integer()
@ -33,8 +31,6 @@ def request(method, url, headers, body, opts \\ []) do
if is_map(response.body) and method != :head do
{:ok, response.status, response.headers, response.body}
else
conn_pid = response.opts[:adapter][:conn]
ConnectionPool.release_conn(conn_pid)
{:ok, response.status, response.headers}
end
else
@ -45,8 +41,7 @@ def request(method, url, headers, body, opts \\ []) do
@impl true
@spec stream_body(map()) ::
{:ok, binary(), map()} | {:error, atom() | String.t()} | :done | no_return()
def stream_body(%{pid: pid, fin: true}) do
ConnectionPool.release_conn(pid)
def stream_body(%{pid: _pid, fin: true}) do
:done
end
@ -70,17 +65,12 @@ defp read_chunk!(%{pid: pid, stream: stream, opts: opts}) do
@impl true
@spec close(map) :: :ok | no_return()
def close(%{pid: pid}) do
ConnectionPool.release_conn(pid)
def close(%{pid: _pid}) do
end
defp check_adapter do
adapter = Application.get_env(:tesla, :adapter)
unless adapter == Tesla.Adapter.Gun do
raise "#{adapter} doesn't support reading body in chunks"
end
adapter
end
end

View file

@ -23,8 +23,6 @@ defp client do
|> client()
end
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
defp client({Tesla.Adapter.Finch, _}), do: Pleroma.ReverseProxy.Client.Hackney
defp client({Tesla.Adapter.Finch, _}), do: Pleroma.ReverseProxy.Client.Tesla
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
end

View file

@ -30,7 +30,7 @@ def object_to_search_data(object) do
trimmed
end
if String.length(content) > 1 do
if String.length(content) > 1 and not is_nil(data["published"]) do
{:ok, published, _} = DateTime.from_iso8601(data["published"])
%{

View file

@ -128,7 +128,7 @@ def object_to_search_data(object) do
trimmed
end
if String.length(content) > 1 do
if String.length(content) > 1 and not is_nil(data["published"]) do
{:ok, published, _} = DateTime.from_iso8601(data["published"])
%{

View file

@ -1,50 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Tesla.Middleware.ConnectionPool do
@moduledoc """
Middleware to get/release connections from `Pleroma.Gun.ConnectionPool`
"""
@behaviour Tesla.Middleware
alias Pleroma.Gun.ConnectionPool
@impl Tesla.Middleware
def call(%Tesla.Env{url: url, opts: opts} = env, next, _) do
uri = URI.parse(url)
# Avoid leaking connections when the middleware is called twice
# with body_as: :chunks. We assume only the middleware can set
# opts[:adapter][:conn]
if opts[:adapter][:conn] do
ConnectionPool.release_conn(opts[:adapter][:conn])
end
case ConnectionPool.get_conn(uri, opts[:adapter]) do
{:ok, conn_pid} ->
adapter_opts = Keyword.merge(opts[:adapter], conn: conn_pid, close_conn: false)
opts = Keyword.put(opts, :adapter, adapter_opts)
env = %{env | opts: opts}
case Tesla.run(env, next) do
{:ok, env} ->
unless opts[:adapter][:body_as] == :chunks do
ConnectionPool.release_conn(conn_pid)
{_, res} = pop_in(env.opts[:adapter][:conn])
{:ok, res}
else
{:ok, env}
end
err ->
ConnectionPool.release_conn(conn_pid)
err
end
err ->
err
end
end
end

View file

@ -1948,7 +1948,7 @@ def get_or_fetch_by_ap_id(ap_id) do
{:ok, user}
e ->
Logger.error("Could not fetch user, #{inspect(e)}")
Logger.error("Could not fetch user #{ap_id}, #{inspect(e)}")
{:error, :not_found}
end
end

View file

@ -1740,7 +1740,7 @@ def fetch_and_prepare_user_from_ap_id(ap_id, additional \\ []) do
{:ok, maybe_update_follow_information(data)}
else
# If this has been deleted, only log a debug and not an error
{:error, "Object has been deleted" = e} ->
{:error, {"Object has been deleted", _, _} = e} ->
Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
{:error, e}

View file

@ -63,6 +63,12 @@ defmodule Pleroma.Web.ActivityPub.MRF do
@required_description_keys [:key, :related_policy]
def filter_one(policy, %{"type" => type} = message)
when type in ["Undo", "Block", "Delete"] and
policy != Pleroma.Web.ActivityPub.MRF.SimplePolicy do
{:ok, message}
end
def filter_one(policy, message) do
should_plug_history? =
if function_exported?(policy, :history_awareness, 0) do

View file

@ -1,59 +0,0 @@
defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
alias Pleroma.Config
alias Pleroma.User
alias Pleroma.Web.CommonAPI
require Logger
@impl true
def filter(message) do
with follower_nickname <- Config.get([:mrf_follow_bot, :follower_nickname]),
%User{actor_type: "Service"} = follower <-
User.get_cached_by_nickname(follower_nickname),
%{"type" => "Create", "object" => %{"type" => "Note"}} <- message do
try_follow(follower, message)
else
nil ->
Logger.warn(
"#{__MODULE__} skipped because of missing `:mrf_follow_bot, :follower_nickname` configuration, the :follower_nickname
account does not exist, or the account is not correctly configured as a bot."
)
{:ok, message}
_ ->
{:ok, message}
end
end
defp try_follow(follower, message) do
to = Map.get(message, "to", [])
cc = Map.get(message, "cc", [])
actor = [message["actor"]]
Enum.concat([to, cc, actor])
|> List.flatten()
|> Enum.uniq()
|> User.get_all_by_ap_id()
|> Enum.each(fn user ->
with false <- user.local,
false <- User.following?(follower, user),
false <- User.locked?(user),
false <- (user.bio || "") |> String.downcase() |> String.contains?("nobot") do
Logger.debug(
"#{__MODULE__}: Follow request from #{follower.nickname} to #{user.nickname}"
)
CommonAPI.follow(follower, user)
end
end)
{:ok, message}
end
@impl true
def describe do
{:ok, %{}}
end
end

View file

@ -12,7 +12,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
require Logger
@adapter_options [
recv_timeout: 10_000
receive_timeout: 10_000
]
@impl true

View file

@ -13,20 +13,20 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
require Pleroma.Constants
defp check_accept(%{host: actor_host} = _actor_info, object) do
def check_accept(%{host: actor_host} = _actor_info) do
accepts =
instance_list(:accept)
|> MRF.subdomains_regex()
cond do
accepts == [] -> {:ok, object}
actor_host == Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, object}
MRF.subdomain_match?(accepts, actor_host) -> {:ok, object}
accepts == [] -> {:ok, nil}
actor_host == Config.get([Pleroma.Web.Endpoint, :url, :host]) -> {:ok, nil}
MRF.subdomain_match?(accepts, actor_host) -> {:ok, nil}
true -> {:reject, "[SimplePolicy] host not in accept list"}
end
end
defp check_reject(%{host: actor_host} = _actor_info, object) do
def check_reject(%{host: actor_host} = _actor_info) do
rejects =
instance_list(:reject)
|> MRF.subdomains_regex()
@ -34,7 +34,7 @@ defp check_reject(%{host: actor_host} = _actor_info, object) do
if MRF.subdomain_match?(rejects, actor_host) do
{:reject, "[SimplePolicy] host in reject list"}
else
{:ok, object}
{:ok, nil}
end
end
@ -178,6 +178,55 @@ defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image
defp check_banner_removal(_actor_info, object), do: {:ok, object}
defp extract_context_uri(%{"conversation" => "tag:" <> rest}) do
rest
|> String.split(",", parts: 2, trim: true)
|> hd()
|> case do
nil -> nil
hostname -> URI.parse("//" <> hostname)
end
end
defp extract_context_uri(%{"context" => "http" <> _ = context}), do: URI.parse(context)
defp extract_context_uri(_), do: nil
defp check_context(activity) do
uri = extract_context_uri(activity)
with {:uri, true} <- {:uri, Kernel.match?(%URI{}, uri)},
{:ok, _} <- check_accept(uri),
{:ok, _} <- check_reject(uri) do
{:ok, activity}
else
# Can't check.
{:uri, false} -> {:ok, activity}
{:reject, nil} -> {:reject, "[SimplePolicy]"}
{:reject, _} = e -> e
_ -> {:reject, "[SimplePolicy]"}
end
end
defp check_reply_to(%{"object" => %{"inReplyTo" => in_reply_to}} = activity) do
with {:ok, _} <- filter(in_reply_to) do
{:ok, activity}
end
end
defp check_reply_to(activity), do: {:ok, activity}
defp maybe_check_thread(activity) do
if Config.get([:mrf_simple, :handle_threads], true) do
with {:ok, _} <- check_context(activity),
{:ok, _} <- check_reply_to(activity) do
{:ok, activity}
end
else
{:ok, activity}
end
end
defp check_object(%{"object" => object} = activity) do
with {:ok, _object} <- filter(object) do
{:ok, activity}
@ -210,13 +259,14 @@ def filter(%{"type" => "Delete", "actor" => actor} = object) do
def filter(%{"actor" => actor} = object) do
actor_info = URI.parse(actor)
with {:ok, object} <- check_accept(actor_info, object),
{:ok, object} <- check_reject(actor_info, object),
with {:ok, _} <- check_accept(actor_info),
{:ok, _} <- check_reject(actor_info),
{:ok, object} <- check_media_removal(actor_info, object),
{:ok, object} <- check_media_nsfw(actor_info, object),
{:ok, object} <- check_ftl_removal(actor_info, object),
{:ok, object} <- check_followers_only(actor_info, object),
{:ok, object} <- check_report_removal(actor_info, object),
{:ok, object} <- maybe_check_thread(object),
{:ok, object} <- check_object(object) do
{:ok, object}
else
@ -230,8 +280,8 @@ def filter(%{"id" => actor, "type" => obj_type} = object)
when obj_type in ["Application", "Group", "Organization", "Person", "Service"] do
actor_info = URI.parse(actor)
with {:ok, object} <- check_accept(actor_info, object),
{:ok, object} <- check_reject(actor_info, object),
with {:ok, _} <- check_accept(actor_info),
{:ok, _} <- check_reject(actor_info),
{:ok, object} <- check_avatar_removal(actor_info, object),
{:ok, object} <- check_banner_removal(actor_info, object) do
{:ok, object}
@ -242,11 +292,17 @@ def filter(%{"id" => actor, "type" => obj_type} = object)
end
end
def filter(%{"id" => id} = object) do
with {:ok, _} <- filter(id) do
{:ok, object}
end
end
def filter(object) when is_binary(object) do
uri = URI.parse(object)
with {:ok, object} <- check_accept(uri, object),
{:ok, object} <- check_reject(uri, object) do
with {:ok, _} <- check_accept(uri),
{:ok, _} <- check_reject(uri) do
{:ok, object}
else
{:reject, nil} -> {:reject, "[SimplePolicy]"}
@ -288,6 +344,7 @@ def describe do
mrf_simple_excluded =
Config.get(:mrf_simple)
|> Enum.filter(fn {_, v} -> is_list(v) end)
|> Enum.map(fn {rule, instances} ->
{rule, Enum.reject(instances, fn {host, _} -> host in exclusions end)}
end)
@ -332,66 +389,78 @@ def config_description do
label: "MRF Simple",
description: "Simple ingress policies",
children:
[
%{
key: :media_removal,
description:
"List of instances to strip media attachments from and the reason for doing so"
},
%{
key: :media_nsfw,
label: "Media NSFW",
description:
"List of instances to tag all media as NSFW (sensitive) from and the reason for doing so"
},
%{
key: :federated_timeline_removal,
description:
"List of instances to remove from the Federated (aka The Whole Known Network) Timeline and the reason for doing so"
},
%{
key: :reject,
description:
"List of instances to reject activities from (except deletes) and the reason for doing so"
},
%{
key: :accept,
description:
"List of instances to only accept activities from (except deletes) and the reason for doing so"
},
%{
key: :followers_only,
description:
"Force posts from the given instances to be visible by followers only and the reason for doing so"
},
%{
key: :report_removal,
description: "List of instances to reject reports from and the reason for doing so"
},
%{
key: :avatar_removal,
description: "List of instances to strip avatars from and the reason for doing so"
},
%{
key: :banner_removal,
description: "List of instances to strip banners from and the reason for doing so"
},
%{
key: :reject_deletes,
description: "List of instances to reject deletions from and the reason for doing so"
}
]
|> Enum.map(fn setting ->
Map.merge(
setting,
([
%{
key: :media_removal,
description:
"List of instances to strip media attachments from and the reason for doing so"
},
%{
key: :media_nsfw,
label: "Media NSFW",
description:
"List of instances to tag all media as NSFW (sensitive) from and the reason for doing so"
},
%{
key: :federated_timeline_removal,
description:
"List of instances to remove from the Federated (aka The Whole Known Network) Timeline and the reason for doing so"
},
%{
key: :reject,
description:
"List of instances to reject activities from (except deletes) and the reason for doing so"
},
%{
key: :accept,
description:
"List of instances to only accept activities from (except deletes) and the reason for doing so"
},
%{
key: :followers_only,
description:
"Force posts from the given instances to be visible by followers only and the reason for doing so"
},
%{
key: :report_removal,
description: "List of instances to reject reports from and the reason for doing so"
},
%{
key: :avatar_removal,
description: "List of instances to strip avatars from and the reason for doing so"
},
%{
key: :banner_removal,
description: "List of instances to strip banners from and the reason for doing so"
},
%{
key: :reject_deletes,
description: "List of instances to reject deletions from and the reason for doing so"
}
]
|> Enum.map(fn setting ->
Map.merge(
setting,
%{
type: {:list, :tuple},
key_placeholder: "instance",
value_placeholder: "reason",
suggestions: [
{"example.com", "Some reason"},
{"*.example.com", "Another reason"}
]
}
)
end)) ++
[
%{
type: {:list, :tuple},
key_placeholder: "instance",
value_placeholder: "reason",
suggestions: [{"example.com", "Some reason"}, {"*.example.com", "Another reason"}]
key: :handle_threads,
label: "Apply to entire threads",
type: :boolean,
description:
"Enable to filter replies to threads based from their originating instance, using the reject and accept rules"
}
)
end)
]
}
end
end

View file

@ -136,7 +136,7 @@ def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options)
|> Map.drop(["conversation", "inReplyToAtomUri"])
else
e ->
Logger.warn("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
Logger.warn("Couldn't fetch reply@#{inspect(in_reply_to_id)}, error: #{inspect(e)}")
object
end
else
@ -159,7 +159,7 @@ def fix_quote_url(%{"quoteUri" => quote_url} = object, options)
|> Map.put("quoteUri", quoted_object.data["id"])
else
e ->
Logger.warn("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}")
Logger.warn("Couldn't fetch quote@#{inspect(quote_url)}, error: #{inspect(e)}")
object
end
else
@ -833,7 +833,7 @@ def maybe_fix_object_url(%{"object" => object} = data) when is_binary(object) do
Map.put(data, "object", external_url)
else
{:fetch, e} ->
Logger.error("Couldn't fetch #{object} #{inspect(e)}")
Logger.error("Couldn't fetch fixed_object@#{object} #{inspect(e)}")
data
_ ->

View file

@ -5,7 +5,7 @@
defmodule Pleroma.Web.RelMe do
@options [
max_body: 2_000_000,
recv_timeout: 2_000
receive_timeout: 2_000
]
if Pleroma.Config.get(:env) == :test do

View file

@ -11,7 +11,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do
@options [
max_body: 2_000_000,
recv_timeout: 2_000
receive_timeout: 2_000
]
@spec validate_page_url(URI.t() | binary()) :: :ok | :error

12
mix.exs
View file

@ -4,7 +4,7 @@ defmodule Pleroma.Mixfile do
def project do
[
app: :pleroma,
version: version("3.4.0"),
version: version("3.5.0"),
elixir: "~> 1.12",
elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(),
@ -76,7 +76,6 @@ def application do
:logger,
:runtime_tools,
:comeonin,
:quack,
:fast_sanitize,
:os_mon,
:ssl
@ -138,8 +137,7 @@ defp deps do
{:tesla, "~> 1.4.4", override: true},
{:castore, "~> 0.1"},
{:cowlib, "~> 2.9", override: true},
{:gun, "~> 2.0.0-rc.1", override: true},
{:finch, "~> 0.13.0"},
{:finch, "~> 0.14.0"},
{:jason, "~> 1.2"},
{:mogrify, "~> 0.9.1"},
{:ex_aws, "~> 2.1.6"},
@ -153,6 +151,8 @@ defp deps do
{:cors_plug, "~> 2.0"},
{:web_push_encryption, "~> 0.3.1"},
{:swoosh, "~> 1.0"},
# for gmail adapter in swoosh
{:mail, ">= 0.0.0"},
{:phoenix_swoosh, "~> 0.3"},
{:gen_smtp, "~> 0.13"},
{:ex_syslogger, "~> 1.4"},
@ -165,7 +165,6 @@ defp deps do
{:telemetry, "~> 0.3"},
{:poolboy, "~> 1.5"},
{:recon, "~> 2.5"},
{:quack, "~> 0.1.1"},
{:joken, "~> 2.0"},
{:benchee, "~> 1.0"},
{:pot, "~> 1.0"},
@ -203,8 +202,7 @@ defp deps do
{:ex_machina, "~> 2.4", only: :test},
{:credo, "~> 1.6", only: [:dev, :test], runtime: false},
{:mock, "~> 0.3.5", only: :test},
# temporary downgrade for excoveralls, hackney until hackney max_connections bug will be fixed
{:excoveralls, "0.12.3", only: :test},
{:excoveralls, "0.15.1", only: :test},
{:mox, "~> 1.0", only: :test},
{:websockex, "~> 0.4.3", only: :test}
] ++ oauth_deps()

View file

@ -7,7 +7,7 @@
"cachex": {:hex, :cachex, "3.4.0", "868b2959ea4aeb328c6b60ff66c8d5123c083466ad3c33d3d8b5f142e13101fb", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "370123b1ab4fba4d2965fb18f87fd758325709787c8c5fce35b3fe80645ccbe5"},
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.1.201603 or ~> 0.5.20 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e0f16822d578866e186a0974d65ad58cddc1e2ab", [ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"]},
"castore": {:hex, :castore, "0.1.19", "a2c3e46d62b7f3aa2e6f88541c21d7400381e53704394462b9fd4f06f6d42bb6", [:mix], [], "hexpm", "e96e0161a5dc82ef441da24d5fa74aefc40d920f3a6645d15e1f9f3e66bb2109"},
"castore": {:hex, :castore, "0.1.20", "62a0126cbb7cb3e259257827b9190f88316eb7aa3fdac01fd6f2dfd64e7f46e9", [:mix], [], "hexpm", "a020b7650529c986c454a4035b6b13a328e288466986307bea3aadb4c95ac98a"},
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"comeonin": {:hex, :comeonin, "5.3.3", "2c564dac95a35650e9b6acfe6d2952083d8a08e4a89b93a481acb552b325892e", [:mix], [], "hexpm", "3e38c9c2cb080828116597ca8807bb482618a315bfafd98c90bc22a821cc84df"},
@ -39,16 +39,15 @@
"ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"},
"ex_machina": {:hex, :ex_machina, "2.7.0", "b792cc3127fd0680fecdb6299235b4727a4944a09ff0fa904cc639272cd92dc7", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "419aa7a39bde11894c87a615c4ecaa52d8f107bbdd81d810465186f783245bf8"},
"ex_syslogger": {:hex, :ex_syslogger, "1.5.2", "72b6aa2d47a236e999171f2e1ec18698740f40af0bd02c8c650bf5f1fd1bac79", [:mix], [{:poison, ">= 1.5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:syslog, "~> 1.1.0", [hex: :syslog, repo: "hexpm", optional: false]}], "hexpm", "ab9fab4136dbc62651ec6f16fa4842f10cf02ab4433fa3d0976c01be99398399"},
"excoveralls": {:hex, :excoveralls, "0.12.3", "2142be7cb978a3ae78385487edda6d1aff0e482ffc6123877bb7270a8ffbcfe0", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "568a3e616c264283f5dea5b020783ae40eef3f7ee2163f7a67cbd7b35bcadada"},
"excoveralls": {:hex, :excoveralls, "0.15.1", "83c8cf7973dd9d1d853dce37a2fb98aaf29b564bf7d01866e409abf59dac2c0e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f8416bd90c0082d56a2178cf46c837595a06575f70a5624f164a1ffe37de07e7"},
"fast_html": {:hex, :fast_html, "2.0.5", "c61760340606c1077ff1f196f17834056cb1dd3d5cb92a9f2cabf28bc6221c3c", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}], "hexpm", "605f4f4829443c14127694ebabb681778712ceecb4470ec32aa31012330e6506"},
"fast_sanitize": {:hex, :fast_sanitize, "0.2.3", "67b93dfb34e302bef49fec3aaab74951e0f0602fd9fa99085987af05bd91c7a5", [:mix], [{:fast_html, "~> 2.0", [hex: :fast_html, repo: "hexpm", optional: false]}, {:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "e8ad286d10d0386e15d67d0ee125245ebcfbc7d7290b08712ba9013c8c5e56e2"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"finch": {:hex, :finch, "0.13.0", "c881e5460ec563bf02d4f4584079e62201db676ed4c0ef3e59189331c4eddf7b", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "49957dcde10dcdc042a123a507a9c5ec5a803f53646d451db2f7dea696fba6cc"},
"finch": {:hex, :finch, "0.14.0", "619bfdee18fc135190bf590356c4bf5d5f71f916adb12aec94caa3fa9267a4bc", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5459acaf18c4fdb47a8c22fb3baff5d8173106217c8e56c5ba0b93e66501a8dd"},
"flake_id": {:hex, :flake_id, "0.1.0", "7716b086d2e405d09b647121a166498a0d93d1a623bead243e1f74216079ccb3", [:mix], [{:base62, "~> 1.2", [hex: :base62, repo: "hexpm", optional: false]}, {:ecto, ">= 2.0.0", [hex: :ecto, repo: "hexpm", optional: true]}], "hexpm", "31fc8090fde1acd267c07c36ea7365b8604055f897d3a53dd967658c691bd827"},
"floki": {:hex, :floki, "0.34.0", "002d0cc194b48794d74711731db004fafeb328fe676976f160685262d43706a8", [:mix], [], "hexpm", "9c3a9f43f40dde00332a589bd9d389b90c1f518aef500364d00636acc5ebc99c"},
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
"gettext": {:git, "https://github.com/tusooa/gettext.git", "72fb2496b6c5280ed911bdc3756890e7f38a4808", [ref: "72fb2496b6c5280ed911bdc3756890e7f38a4808"]},
"gun": {:hex, :gun, "2.0.0-rc.2", "7c489a32dedccb77b6e82d1f3c5a7dadfbfa004ec14e322cdb5e579c438632d2", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "6b9d1eae146410d727140dbf8b404b9631302ecc2066d1d12f22097ad7d254fc"},
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
@ -61,6 +60,7 @@
"jose": {:hex, :jose, "1.11.2", "f4c018ccf4fdce22c71e44d471f15f723cb3efab5d909ab2ba202b5bf35557b3", [:mix, :rebar3], [], "hexpm", "98143fbc48d55f3a18daba82d34fe48959d44538e9697c08f34200fa5f0947d2"},
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
"linkify": {:git, "https://akkoma.dev/AkkomaGang/linkify.git", "2567e2c1073fa371fd26fd66dfa5bc77b6919c16", [branch: "bugfix/line-ending-buffer"]},
"mail": {:hex, :mail, "0.2.3", "2c6bb5f8a5f74845fa50ecd0fb45ea16b164026f285f45104f1c4c078cd616d4", [:mix], [], "hexpm", "932b398fa9c69fdf290d7ff63175826e0f1e24414d5b0763bb00a2acfc6c6bf5"},
"majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
@ -74,7 +74,7 @@
"mock": {:hex, :mock, "0.3.7", "75b3bbf1466d7e486ea2052a73c6e062c6256fb429d6797999ab02fa32f29e03", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "4da49a4609e41fd99b7836945c26f373623ea968cfb6282742bcb94440cf7e5c"},
"mogrify": {:hex, :mogrify, "0.9.2", "b360984adea7dd6a55f18028e6327973c58de7f548fdb86c9859848aa904d5b0", [:mix], [], "hexpm", "c18d10fd70ca20e2585301616c89f6e4f7159d92efc9cc8ee579e00c886f699d"},
"mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"},
"nimble_options": {:hex, :nimble_options, "0.4.0", "c89babbab52221a24b8d1ff9e7d838be70f0d871be823165c94dd3418eea728f", [:mix], [], "hexpm", "e6701c1af326a11eea9634a3b1c62b475339ace9456c1a23ec3bc9a847bca02d"},
"nimble_options": {:hex, :nimble_options, "0.5.1", "5c166f7669e40333191bea38e3bd3811cc13f459f1e4be49e89128a21b5d8c4d", [:mix], [], "hexpm", "d176cf7baa4fef0ceb301ca3eb8b55bd7de3e45f489c4f8b4f2849f1f114ef3e"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"},
"oban": {:hex, :oban, "2.12.1", "f604d7e6a8be9fda4a9b0f6cebbd633deba569f85dbff70c4d25d99a6f023177", [:mix], [{:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9b1844c2b74e0d788b73e5144b0c9d5674cb775eae29d88a36f3c3b48d42d058"},

View file

@ -5,8 +5,8 @@ msgstr ""
"POT-Creation-Date: 2022-07-28 09:35+0000\n"
"PO-Revision-Date: 2022-08-08 15:48+0000\n"
"Last-Translator: sola <spla@mastodont.cat>\n"
"Language-Team: Catalan <http://translate.akkoma.dev/projects/akkoma/"
"akkoma-backend-config-descriptions/ca/>\n"
"Language-Team: Catalan <http://translate.akkoma.dev/projects/akkoma/akkoma-"
"backend-config-descriptions/ca/>\n"
"Language: ca\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,526 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-12-07 13:44+0000\n"
"PO-Revision-Date: 2022-12-07 15:39+0000\n"
"Last-Translator: t1 <taaa@fedora.email>\n"
"Language-Team: Indonesian <http://translate.akkoma.dev/projects/akkoma/"
"akkoma-backend-static-pages/id/>\n"
"Language: id\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 4.14\n"
## This file is a PO Template file.
##
## "msgid"s here are often extracted from source code.
## Add new translations manually only if they're dynamic
## translations that can't be statically extracted.
##
## Run "mix gettext.extract" to bring this file up to
## date. Leave "msgstr"s empty as changing them here as no
## effect: edit them in PO (.po) files instead.
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:9
#, elixir-autogen, elixir-format
msgctxt "remote follow authorization button"
msgid "Authorize"
msgstr ""
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "remote follow error"
msgid "Error fetching user"
msgstr "Gagal memuat pengguna"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow header"
msgid "Remote follow"
msgstr ""
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "placeholder text for auth code entry"
msgid "Authentication code"
msgstr "Kode autentikasi"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:10
#, elixir-autogen, elixir-format
msgctxt "placeholder text for password entry"
msgid "Password"
msgstr "Kata sandi"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "placeholder text for username entry"
msgid "Username"
msgstr "Nama pengguna"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:13
#, elixir-autogen, elixir-format
msgctxt "remote follow authorization button for login"
msgid "Authorize"
msgstr ""
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:12
#, elixir-autogen, elixir-format
msgctxt "remote follow authorization button for mfa"
msgid "Authorize"
msgstr ""
#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "remote follow error"
msgid "Error following account"
msgstr "Gagal mengikuti akun"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_login.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow header, need login"
msgid "Log in to follow"
msgstr "Masuk untuk mengikuti"
#: lib/pleroma/web/templates/twitter_api/remote_follow/follow_mfa.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow mfa header"
msgid "Two-factor authentication"
msgstr "Autentikasi dua faktor"
#: lib/pleroma/web/templates/twitter_api/remote_follow/followed.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow success"
msgid "Account followed!"
msgstr "Akun diikuti!"
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:7
#, elixir-autogen, elixir-format
msgctxt "placeholder text for account id"
msgid "Your account ID, e.g. lain@quitter.se"
msgstr "ID akunmu, cth. lain@quitter.se"
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "remote follow authorization button for following with a remote account"
msgid "Follow"
msgstr "Ikuti"
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "remote follow error"
msgid "Error: %{error}"
msgstr "Kesalahan: %{error}"
#: lib/pleroma/web/templates/twitter_api/util/subscribe.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "remote follow header"
msgid "Remotely follow %{nickname}"
msgstr ""
#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:12
#, elixir-autogen, elixir-format
msgctxt "password reset button"
msgid "Reset"
msgstr ""
#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "password reset failed homepage link"
msgid "Homepage"
msgstr "Beranda"
#: lib/pleroma/web/templates/twitter_api/password/reset_failed.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "password reset failed message"
msgid "Password reset failed"
msgstr "Gagal mengatur ulang kata sandi"
#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "password reset form confirm password prompt"
msgid "Confirmation"
msgstr "Konfirmasi"
#: lib/pleroma/web/templates/twitter_api/password/reset.html.eex:4
#, elixir-autogen, elixir-format
msgctxt "password reset form password prompt"
msgid "Password"
msgstr "Kata sandi"
#: lib/pleroma/web/templates/twitter_api/password/invalid_token.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "password reset invalid token message"
msgid "Invalid Token"
msgstr "Token Tidak Sah"
#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "password reset successful homepage link"
msgid "Homepage"
msgstr "Beranda"
#: lib/pleroma/web/templates/twitter_api/password/reset_success.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "password reset successful message"
msgid "Password changed!"
msgstr "Kata sandi diubah!"
#: lib/pleroma/web/templates/feed/feed/tag.atom.eex:15
#: lib/pleroma/web/templates/feed/feed/tag.rss.eex:7
#, elixir-autogen, elixir-format
msgctxt "tag feed description"
msgid "These are public toots tagged with #%{tag}. You can interact with them if you have an account anywhere in the fediverse."
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "oauth authorization exists page title"
msgid "Authorization exists"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:32
#, elixir-autogen, elixir-format
msgctxt "oauth authorize approve button"
msgid "Approve"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:30
#, elixir-autogen, elixir-format
msgctxt "oauth authorize cancel button"
msgid "Cancel"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:23
#, elixir-autogen, elixir-format
msgctxt "oauth authorize message"
msgid "Application <strong>%{client_name}</strong> is requesting access to your account."
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "oauth authorized page title"
msgid "Successfully authorized"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "oauth external provider page title"
msgid "Sign in with external provider"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex:13
#, elixir-autogen, elixir-format
msgctxt "oauth external provider sign in button"
msgid "Sign in with %{strategy}"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:54
#, elixir-autogen, elixir-format
msgctxt "oauth login button"
msgid "Log In"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:51
#, elixir-autogen, elixir-format
msgctxt "oauth login password prompt"
msgid "Password"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:47
#, elixir-autogen, elixir-format
msgctxt "oauth login username prompt"
msgid "Username"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:39
#, elixir-autogen, elixir-format
msgctxt "oauth register nickname prompt"
msgid "Pleroma Handle"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:37
#, elixir-autogen, elixir-format
msgctxt "oauth register nickname unchangeable warning"
msgid "Choose carefully! You won't be able to change this later. You will be able to change your display name, though."
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:18
#, elixir-autogen, elixir-format
msgctxt "oauth register page email prompt"
msgid "Email"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:10
#, elixir-autogen, elixir-format
msgctxt "oauth register page fill form prompt"
msgid "If you'd like to register a new account, please provide the details below."
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:35
#, elixir-autogen, elixir-format
msgctxt "oauth register page login button"
msgid "Proceed as existing user"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:31
#, elixir-autogen, elixir-format
msgctxt "oauth register page login password prompt"
msgid "Password"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:24
#, elixir-autogen, elixir-format
msgctxt "oauth register page login prompt"
msgid "Alternatively, sign in to connect to existing account."
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:27
#, elixir-autogen, elixir-format
msgctxt "oauth register page login username prompt"
msgid "Name or email"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:14
#, elixir-autogen, elixir-format
msgctxt "oauth register page nickname prompt"
msgid "Nickname"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:22
#, elixir-autogen, elixir-format
msgctxt "oauth register page register button"
msgid "Proceed as new user"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/register.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "oauth register page title"
msgid "Registration Details"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/show.html.eex:36
#, elixir-autogen, elixir-format
msgctxt "oauth register page title"
msgid "This is the first time you visit! Please enter your Pleroma handle."
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/_scopes.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "oauth scopes message"
msgid "The following permissions will be granted"
msgstr ""
#: lib/pleroma/web/templates/o_auth/o_auth/oob_authorization_created.html.eex:2
#: lib/pleroma/web/templates/o_auth/o_auth/oob_token_exists.html.eex:2
#, elixir-autogen, elixir-format
msgctxt "oauth token code message"
msgid "Token code is <br>%{token}"
msgstr ""
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:12
#, elixir-autogen, elixir-format
msgctxt "mfa auth code prompt"
msgid "Authentication code"
msgstr ""
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "mfa auth page title"
msgid "Two-factor authentication"
msgstr ""
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:23
#, elixir-autogen, elixir-format
msgctxt "mfa auth page use recovery code link"
msgid "Enter a two-factor recovery code"
msgstr ""
#: lib/pleroma/web/templates/o_auth/mfa/totp.html.eex:20
#, elixir-autogen, elixir-format
msgctxt "mfa auth verify code button"
msgid "Verify"
msgstr ""
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "mfa recover page title"
msgid "Two-factor recovery"
msgstr ""
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:12
#, elixir-autogen, elixir-format
msgctxt "mfa recover recovery code prompt"
msgid "Recovery code"
msgstr ""
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:23
#, elixir-autogen, elixir-format
msgctxt "mfa recover use 2fa code link"
msgid "Enter a two-factor code"
msgstr ""
#: lib/pleroma/web/templates/o_auth/mfa/recovery.html.eex:20
#, elixir-autogen, elixir-format
msgctxt "mfa recover verify recovery code button"
msgid "Verify"
msgstr ""
#: lib/pleroma/web/templates/static_fe/static_fe/profile.html.eex:8
#, elixir-autogen, elixir-format
msgctxt "static fe profile page remote follow button"
msgid "Remote follow"
msgstr ""
#: lib/pleroma/web/templates/email/digest.html.eex:163
#, elixir-autogen, elixir-format
msgctxt "digest email header line"
msgid "Hey %{nickname}, here is what you've missed!"
msgstr ""
#: lib/pleroma/web/templates/email/digest.html.eex:544
#, elixir-autogen, elixir-format
msgctxt "digest email receiver address"
msgid "The email address you are subscribed as is <a href='mailto:%{@user.email}' style='color: %{color};text-decoration: none;'>%{email}</a>. "
msgstr ""
#: lib/pleroma/web/templates/email/digest.html.eex:538
#, elixir-autogen, elixir-format
msgctxt "digest email sending reason"
msgid "You have received this email because you have signed up to receive digest emails from <b>%{instance}</b> Pleroma instance."
msgstr ""
#: lib/pleroma/web/templates/email/digest.html.eex:547
#, elixir-autogen, elixir-format
msgctxt "digest email unsubscribe action"
msgid "To unsubscribe, please go %{here}."
msgstr ""
#: lib/pleroma/web/templates/email/digest.html.eex:547
#, elixir-autogen, elixir-format
msgctxt "digest email unsubscribe action link text"
msgid "here"
msgstr ""
#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_failure.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "mailer unsubscribe failed message"
msgid "UNSUBSCRIBE FAILURE"
msgstr ""
#: lib/pleroma/web/templates/mailer/subscription/unsubscribe_success.html.eex:1
#, elixir-autogen, elixir-format
msgctxt "mailer unsubscribe successful message"
msgid "UNSUBSCRIBE SUCCESSFUL"
msgstr ""
#: lib/pleroma/web/templates/email/digest.html.eex:385
#, elixir-format
msgctxt "new followers count header"
msgid "%{count} New Follower"
msgid_plural "%{count} New Followers"
msgstr[0] ""
msgstr[1] ""
#: lib/pleroma/emails/user_email.ex:356
#, elixir-autogen, elixir-format
msgctxt "account archive email body - self-requested"
msgid "<p>You requested a full backup of your Pleroma account. It's ready for download:</p>\n<p><a href=\"%{download_url}\">%{download_url}</a></p>\n"
msgstr ""
#: lib/pleroma/emails/user_email.ex:384
#, elixir-autogen, elixir-format
msgctxt "account archive email subject"
msgid "Your account archive is ready"
msgstr ""
#: lib/pleroma/emails/user_email.ex:188
#, elixir-autogen, elixir-format
msgctxt "approval pending email body"
msgid "<h3>Awaiting Approval</h3>\n<p>Your account at %{instance_name} is being reviewed by staff. You will receive another email once your account is approved.</p>\n"
msgstr ""
#: lib/pleroma/emails/user_email.ex:202
#, elixir-autogen, elixir-format
msgctxt "approval pending email subject"
msgid "Your account is awaiting approval"
msgstr ""
#: lib/pleroma/emails/user_email.ex:158
#, elixir-autogen, elixir-format
msgctxt "confirmation email body"
msgid "<h3>Thank you for registering on %{instance_name}</h3>\n<p>Email confirmation is required to activate the account.</p>\n<p>Please click the following link to <a href=\"%{confirmation_url}\">activate your account</a>.</p>\n"
msgstr ""
#: lib/pleroma/emails/user_email.ex:174
#, elixir-autogen, elixir-format
msgctxt "confirmation email subject"
msgid "%{instance_name} account confirmation"
msgstr ""
#: lib/pleroma/emails/user_email.ex:310
#, elixir-autogen, elixir-format
msgctxt "digest email subject"
msgid "Your digest from %{instance_name}"
msgstr ""
#: lib/pleroma/emails/user_email.ex:81
#, elixir-autogen, elixir-format
msgctxt "password reset email body"
msgid "<h3>Reset your password at %{instance_name}</h3>\n<p>Someone has requested password change for your account at %{instance_name}.</p>\n<p>If it was you, visit the following link to proceed: <a href=\"%{password_reset_url}\">reset password</a>.</p>\n<p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>\n"
msgstr ""
#: lib/pleroma/emails/user_email.ex:98
#, elixir-autogen, elixir-format
msgctxt "password reset email subject"
msgid "Password reset"
msgstr ""
#: lib/pleroma/emails/user_email.ex:215
#, elixir-autogen, elixir-format
msgctxt "successful registration email body"
msgid "<h3>Hello @%{nickname},</h3>\n<p>Your account at %{instance_name} has been registered successfully.</p>\n<p>No further action is required to activate your account.</p>\n"
msgstr ""
#: lib/pleroma/emails/user_email.ex:231
#, elixir-autogen, elixir-format
msgctxt "successful registration email subject"
msgid "Account registered on %{instance_name}"
msgstr ""
#: lib/pleroma/emails/user_email.ex:119
#, elixir-autogen, elixir-format
msgctxt "user invitation email body"
msgid "<h3>You are invited to %{instance_name}</h3>\n<p>%{inviter_name} invites you to join %{instance_name}, an instance of Pleroma federated social networking platform.</p>\n<p>Click the following link to register: <a href=\"%{registration_url}\">accept invitation</a>.</p>\n"
msgstr ""
#: lib/pleroma/emails/user_email.ex:136
#, elixir-autogen, elixir-format
msgctxt "user invitation email subject"
msgid "Invitation to %{instance_name}"
msgstr ""
#: lib/pleroma/emails/user_email.ex:53
#, elixir-autogen, elixir-format
msgctxt "welcome email html body"
msgid "Welcome to %{instance_name}!"
msgstr ""
#: lib/pleroma/emails/user_email.ex:41
#, elixir-autogen, elixir-format
msgctxt "welcome email subject"
msgid "Welcome to %{instance_name}!"
msgstr ""
#: lib/pleroma/emails/user_email.ex:65
#, elixir-autogen, elixir-format
msgctxt "welcome email text body"
msgid "Welcome to %{instance_name}!"
msgstr ""
#: lib/pleroma/emails/user_email.ex:368
#, elixir-autogen, elixir-format
msgctxt "account archive email body - admin requested"
msgid "<p>Admin @%{admin_nickname} requested a full backup of your Pleroma account. It's ready for download:</p>\n<p><a href=\"%{download_url}\">%{download_url}</a></p>\n"
msgstr ""

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
defmodule Pleroma.Repo.Migrations.RemoveUnusedIndices do
use Ecto.Migration
def change do
drop_if_exists(
index(:activities, ["(data->>'actor')", "inserted_at desc"], name: :activities_actor_index)
)
drop_if_exists(index(:objects, ["(data->'tag')"], using: :gin, name: :objects_tags))
end
end

View file

@ -8,8 +8,6 @@
config :pleroma, :second_setting, key: "value2", key2: ["Activity"]
config :quack, level: :info
config :pleroma, Pleroma.Repo, pool: Ecto.Adapters.SQL.Sandbox
config :postgrex, :json_library, Poison

View file

@ -49,7 +49,6 @@ test "error if file with custom settings doesn't exist" do
describe "migrate_to_db/1" do
setup do
clear_config(:configurable_from_database, true)
clear_config([:quack, :level])
end
@tag capture_log: true
@ -72,14 +71,12 @@ test "filtered settings are migrated to db" do
config1 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":first_setting"})
config2 = ConfigDB.get_by_params(%{group: ":pleroma", key: ":second_setting"})
config3 = ConfigDB.get_by_params(%{group: ":quack", key: ":level"})
refute ConfigDB.get_by_params(%{group: ":pleroma", key: "Pleroma.Repo"})
refute ConfigDB.get_by_params(%{group: ":postgrex", key: ":json_library"})
refute ConfigDB.get_by_params(%{group: ":pleroma", key: ":database"})
assert config1.value == [key: "value", key2: [Repo]]
assert config2.value == [key: "value2", key2: ["Activity"]]
assert config3.value == :info
end
test "config table is truncated before migration" do
@ -108,7 +105,6 @@ test "config table is truncated before migration" do
test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do
insert_config_record(:pleroma, :setting_first, key: "value", key2: ["Activity"])
insert_config_record(:pleroma, :setting_second, key: "value2", key2: [Repo])
insert_config_record(:quack, :level, :info)
MixTask.run(["migrate_from_db", "--env", "temp", "-d"])
@ -117,7 +113,6 @@ test "settings are migrated to file and deleted from db", %{temp_file: temp_file
file = File.read!(temp_file)
assert file =~ "config :pleroma, :setting_first,"
assert file =~ "config :pleroma, :setting_second,"
assert file =~ "config :quack, :level, :info"
end
test "load a settings with large values and pass to file", %{temp_file: temp_file} do
@ -199,7 +194,6 @@ test "load a settings with large values and pass to file", %{temp_file: temp_fil
setup do
insert_config_record(:pleroma, :setting_first, key: "value", key2: ["Activity"])
insert_config_record(:pleroma, :setting_second, key: "value2", key2: [Repo])
insert_config_record(:quack, :level, :info)
path = "test/instance_static"
file_path = Path.join(path, "temp.exported_from_db.secret.exs")
@ -215,7 +209,6 @@ test "with path parameter", %{file_path: file_path} do
file = File.read!(file_path)
assert file =~ "config :pleroma, :setting_first,"
assert file =~ "config :pleroma, :setting_second,"
assert file =~ "config :quack, :level, :info"
end
test "release", %{file_path: file_path} do
@ -227,7 +220,6 @@ test "release", %{file_path: file_path} do
file = File.read!(file_path)
assert file =~ "config :pleroma, :setting_first,"
assert file =~ "config :pleroma, :setting_second,"
assert file =~ "config :quack, :level, :info"
end
end

View file

@ -280,6 +280,16 @@ test "check_uploders_s3_public_endpoint/0" do
"Your config is using the old setting for controlling the URL of media uploaded to your S3 bucket."
end
test "check_http_adapter/0" do
Application.put_env(:tesla, :adapter, Gun)
assert capture_log(fn ->
DeprecationWarnings.check_http_adapter()
end) =~ "Your config is using a custom tesla adapter"
Application.put_env(:tesla, :adapter, Tesla.Mock)
end
test "check_old_chat_shoutbox/0" do
clear_config([:instance, :chat_limit], 1_000)
clear_config([:chat, :enabled], true)

View file

@ -11,7 +11,6 @@ test "read/1" do
config = Loader.read("test/fixtures/config/temp.secret.exs")
assert config[:pleroma][:first_setting][:key] == "value"
assert config[:pleroma][:first_setting][:key2] == [Pleroma.Repo]
assert config[:quack][:level] == :info
end
test "filter_group/2" do

View file

@ -17,14 +17,12 @@ defmodule Pleroma.Config.TransferTaskTest do
test "transfer config values from db to env" do
refute Application.get_env(:pleroma, :test_key)
refute Application.get_env(:idna, :test_key)
refute Application.get_env(:quack, :test_key)
refute Application.get_env(:postgrex, :test_key)
initial = Application.get_env(:logger, :level)
insert(:config, key: :test_key, value: [live: 2, com: 3])
insert(:config, group: :idna, key: :test_key, value: [live: 15, com: 35])
insert(:config, group: :quack, key: :test_key, value: [:test_value1, :test_value2])
insert(:config, group: :postgrex, key: :test_key, value: :value)
insert(:config, group: :logger, key: :level, value: :debug)
insert(:config, group: :pleroma, key: :instance, value: [static_dir: "static_dir_from_db"])
@ -32,7 +30,6 @@ test "transfer config values from db to env" do
assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]
assert Application.get_env(:idna, :test_key) == [live: 15, com: 35]
assert Application.get_env(:quack, :test_key) == [:test_value1, :test_value2]
assert Application.get_env(:logger, :level) == :debug
assert Application.get_env(:postgrex, :test_key) == :value
assert Application.get_env(:pleroma, :instance)[:static_dir] == "static_dir_from_db"
@ -40,7 +37,6 @@ test "transfer config values from db to env" do
on_exit(fn ->
Application.delete_env(:pleroma, :test_key)
Application.delete_env(:idna, :test_key)
Application.delete_env(:quack, :test_key)
Application.delete_env(:postgrex, :test_key)
Application.put_env(:logger, :level, initial)
System.delete_env("RELEASE_NAME")
@ -82,26 +78,6 @@ test "transfer task falls back to release defaults if no other values found" do
end)
end
test "transfer config values for 1 group and some keys" do
level = Application.get_env(:quack, :level)
meta = Application.get_env(:quack, :meta)
insert(:config, group: :quack, key: :level, value: :info)
insert(:config, group: :quack, key: :meta, value: [:none])
TransferTask.start_link([])
assert Application.get_env(:quack, :level) == :info
assert Application.get_env(:quack, :meta) == [:none]
default = Pleroma.Config.Holder.default_config(:quack, :webhook_url)
assert Application.get_env(:quack, :webhook_url) == default
on_exit(fn ->
Application.put_env(:quack, :level, level)
Application.put_env(:quack, :meta, meta)
end)
end
test "transfer config values with full subkey update" do
clear_config(:emoji)
clear_config(:assets)

View file

@ -14,28 +14,6 @@ test "get_by_params/1" do
assert config == ConfigDB.get_by_params(%{group: config.group, key: config.key})
end
test "get_all_as_keyword/0" do
saved = insert(:config)
insert(:config, group: ":quack", key: ":level", value: :info)
insert(:config, group: ":quack", key: ":meta", value: [:none])
insert(:config,
group: ":quack",
key: ":webhook_url",
value: "https://hooks.slack.com/services/KEY/some_val"
)
config = ConfigDB.get_all_as_keyword()
assert config[:pleroma] == [
{saved.key, saved.value}
]
assert config[:quack][:level] == :info
assert config[:quack][:meta] == [:none]
assert config[:quack][:webhook_url] == "https://hooks.slack.com/services/KEY/some_val"
end
describe "update_or_create/1" do
test "common" do
config = insert(:config)

View file

@ -161,6 +161,28 @@ test "it does not fetch a spoofed object uploaded on an instance as an attachmen
)
end
test "does not fetch anything from a rejected instance" do
clear_config([:mrf_simple, :reject], [{"evil.example.org", "i said so"}])
assert {:reject, _} =
Fetcher.fetch_object_from_id("http://evil.example.org/@admin/99541947525187367")
end
test "does not fetch anything if mrf_simple accept is on" do
clear_config([:mrf_simple, :accept], [{"mastodon.example.org", "i said so"}])
clear_config([:mrf_simple, :reject], [])
assert {:reject, _} =
Fetcher.fetch_object_from_id(
"http://notlisted.example.org/@admin/99541947525187367"
)
assert {:ok, _object} =
Fetcher.fetch_object_from_id(
"http://mastodon.example.org/@admin/99541947525187367"
)
end
test "it resets instance reachability on successful fetch" do
id = "http://mastodon.example.org/@admin/99541947525187367"
Instances.set_consistently_unreachable(id)
@ -216,14 +238,16 @@ test "all objects with fake directions are rejected by the object fetcher" do
end
test "handle HTTP 410 Gone response" do
assert {:error, "Object has been deleted"} ==
assert {:error,
{"Object has been deleted", "https://mastodon.example.org/users/userisgone", 410}} ==
Fetcher.fetch_and_contain_remote_object_from_id(
"https://mastodon.example.org/users/userisgone"
)
end
test "handle HTTP 404 response" do
assert {:error, "Object has been deleted"} ==
assert {:error,
{"Object has been deleted", "https://mastodon.example.org/users/userisgone404", 404}} ==
Fetcher.fetch_and_contain_remote_object_from_id(
"https://mastodon.example.org/users/userisgone404"
)
@ -548,4 +572,75 @@ test "it gets history", %{object2: object2} do
} = object.data
end
end
describe "get_object/1" do
test "should return ok if the content type is application/activity+json" do
Tesla.Mock.mock(fn
%{
method: :get,
url: "https://mastodon.social/2"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/activity+json"}],
body: "{}"
}
end)
assert {:ok, "{}"} = Fetcher.get_object("https://mastodon.social/2")
end
test "should return ok if the content type is application/ld+json with a profile" do
Tesla.Mock.mock(fn
%{
method: :get,
url: "https://mastodon.social/2"
} ->
%Tesla.Env{
status: 200,
headers: [
{"content-type",
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""}
],
body: "{}"
}
end)
assert {:ok, "{}"} = Fetcher.get_object("https://mastodon.social/2")
Tesla.Mock.mock(fn
%{
method: :get,
url: "https://mastodon.social/2"
} ->
%Tesla.Env{
status: 200,
headers: [
{"content-type",
"application/ld+json; profile=\"http://www.w3.org/ns/activitystreams\""}
],
body: "{}"
}
end)
assert {:ok, "{}"} = Fetcher.get_object("https://mastodon.social/2")
end
test "should not return ok with other content types" do
Tesla.Mock.mock(fn
%{
method: :get,
url: "https://mastodon.social/2"
} ->
%Tesla.Env{
status: 200,
headers: [{"content-type", "application/json"}],
body: "{}"
}
end)
assert {:error, {:content_type, "application/json"}} =
Fetcher.get_object("https://mastodon.social/2")
end
end
end

View file

@ -533,9 +533,6 @@ test "it sends a confirm email" do
ObanHelpers.perform_all()
Pleroma.Emails.UserEmail.account_confirmation_email(registered_user)
# temporary hackney fix until hackney max_connections bug is fixed
# https://git.pleroma.social/pleroma/pleroma/-/issues/2101
|> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]])
|> assert_email_sent()
end

View file

@ -1,126 +0,0 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicyTest do
use Pleroma.DataCase, async: true
alias Pleroma.User
alias Pleroma.Web.ActivityPub.MRF.FollowBotPolicy
import Pleroma.Factory
describe "FollowBotPolicy" do
test "follows remote users" do
bot = insert(:user, actor_type: "Service")
remote_user = insert(:user, local: false)
clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname)
message = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"to" => [remote_user.follower_address],
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
"type" => "Create",
"object" => %{
"content" => "Test post",
"type" => "Note",
"attributedTo" => remote_user.ap_id,
"inReplyTo" => nil
},
"actor" => remote_user.ap_id
}
refute User.following?(bot, remote_user)
assert User.get_follow_requests(remote_user) |> length == 0
FollowBotPolicy.filter(message)
assert User.get_follow_requests(remote_user) |> length == 1
end
test "does not follow users with #nobot in bio" do
bot = insert(:user, actor_type: "Service")
remote_user = insert(:user, %{local: false, bio: "go away bots! #nobot"})
clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname)
message = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"to" => [remote_user.follower_address],
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
"type" => "Create",
"object" => %{
"content" => "I don't like follow bots",
"type" => "Note",
"attributedTo" => remote_user.ap_id,
"inReplyTo" => nil
},
"actor" => remote_user.ap_id
}
refute User.following?(bot, remote_user)
assert User.get_follow_requests(remote_user) |> length == 0
FollowBotPolicy.filter(message)
assert User.get_follow_requests(remote_user) |> length == 0
end
test "does not follow local users" do
bot = insert(:user, actor_type: "Service")
local_user = insert(:user, local: true)
clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname)
message = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"to" => [local_user.follower_address],
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
"type" => "Create",
"object" => %{
"content" => "Hi I'm a local user",
"type" => "Note",
"attributedTo" => local_user.ap_id,
"inReplyTo" => nil
},
"actor" => local_user.ap_id
}
refute User.following?(bot, local_user)
assert User.get_follow_requests(local_user) |> length == 0
FollowBotPolicy.filter(message)
assert User.get_follow_requests(local_user) |> length == 0
end
test "does not follow users requiring follower approval" do
bot = insert(:user, actor_type: "Service")
remote_user = insert(:user, %{local: false, is_locked: true})
clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname)
message = %{
"@context" => "https://www.w3.org/ns/activitystreams",
"to" => [remote_user.follower_address],
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
"type" => "Create",
"object" => %{
"content" => "I don't like randos following me",
"type" => "Note",
"attributedTo" => remote_user.ap_id,
"inReplyTo" => nil
},
"actor" => remote_user.ap_id
}
refute User.following?(bot, remote_user)
assert User.get_follow_requests(remote_user) |> length == 0
FollowBotPolicy.filter(message)
assert User.get_follow_requests(remote_user) |> length == 0
end
end
end

View file

@ -356,6 +356,86 @@ test "reject by URI object" do
assert {:reject, _} = SimplePolicy.filter(announce)
end
test "accept by matching context URI if :handle_threads is disabled" do
clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
clear_config([:mrf_simple, :handle_threads], false)
remote_message =
build_remote_message()
|> Map.put("context", "https://blocked.tld/contexts/abc")
assert {:ok, _} = SimplePolicy.filter(remote_message)
end
test "accept by matching conversation field if :handle_threads is disabled" do
clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
clear_config([:mrf_simple, :handle_threads], false)
remote_message =
build_remote_message()
|> Map.put(
"conversation",
"tag:blocked.tld,1997-06-25:objectId=12345:objectType=Conversation"
)
assert {:ok, _} = SimplePolicy.filter(remote_message)
end
test "accept by matching reply ID if :handle_threads is disabled" do
clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
clear_config([:mrf_simple, :handle_threads], false)
remote_message =
build_remote_message()
|> Map.put("type", "Create")
|> Map.put("object", %{
"type" => "Note",
"inReplyTo" => "https://blocked.tld/objects/1"
})
assert {:ok, _} = SimplePolicy.filter(remote_message)
end
test "reject by matching context URI if :handle_threads is enabled" do
clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
clear_config([:mrf_simple, :handle_threads], true)
remote_message =
build_remote_message()
|> Map.put("context", "https://blocked.tld/contexts/abc")
assert {:reject, _} = SimplePolicy.filter(remote_message)
end
test "reject by matching conversation field if :handle_threads is enabled" do
clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
clear_config([:mrf_simple, :handle_threads], true)
remote_message =
build_remote_message()
|> Map.put(
"conversation",
"tag:blocked.tld,1997-06-25:objectId=12345:objectType=Conversation"
)
assert {:reject, _} = SimplePolicy.filter(remote_message)
end
test "reject by matching reply ID if :handle_threads is enabled" do
clear_config([:mrf_simple, :reject], [{"blocked.tld", ""}])
clear_config([:mrf_simple, :handle_threads], true)
remote_message =
build_remote_message()
|> Map.put("type", "Create")
|> Map.put("object", %{
"type" => "Note",
"inReplyTo" => "https://blocked.tld/objects/1"
})
assert {:reject, _} = SimplePolicy.filter(remote_message)
end
end
describe "when :followers_only" do

View file

@ -850,9 +850,6 @@ test "it resend emails for two users", %{conn: conn, admin: admin} do
ObanHelpers.perform_all()
Pleroma.Emails.UserEmail.account_confirmation_email(first_user)
# temporary hackney fix until hackney max_connections bug is fixed
# https://git.pleroma.social/pleroma/pleroma/-/issues/2101
|> Swoosh.Email.put_private(:hackney_options, ssl_options: [versions: [:"tlsv1.2"]])
|> assert_email_sent()
end
end

View file

@ -96,7 +96,7 @@ def spawn_cluster(node_configs) do
silence_logger_warnings(fn ->
node_configs
|> Enum.map(&Task.async(fn -> start_slave(&1) end))
|> Enum.map(&Task.async(fn -> start_peer(&1) end))
|> Enum.map(&Task.await(&1, 90_000))
end)
end
@ -121,9 +121,9 @@ def eval_quoted(block, binding) do
result
end
defp start_slave({node_host, override_configs}) do
defp start_peer({node_host, override_configs}) do
log(node_host, "booting federated VM")
{:ok, node} = :slave.start(~c"127.0.0.1", node_name(node_host), vm_args())
{:ok, node} = :peer.start(%{host: ~c"127.0.0.1", name: node_name(node_host), args: vm_args()})
add_code_paths(node)
load_apps_and_transfer_configuration(node, override_configs)
ensure_apps_started(node)