Commit 68cb152a authored by minibikini's avatar minibikini

Merge branch 'develop' into openapi/admin/relay

parents cbcd5923 879304dc
Pipeline #26703 passed with stages
in 11 minutes and 4 seconds
......@@ -44,6 +44,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix follower/blocks import when nicknames starts with @
- Filtering of push notifications on activities from blocked domains
- Resolving Peertube accounts with Webfinger
- `blob:` urls not being allowed by connect-src CSP
## [Unreleased (patch)]
......
This diff is collapsed.
......@@ -36,6 +36,7 @@ defp fetch_timelines(user) do
fetch_home_timeline(user)
fetch_direct_timeline(user)
fetch_public_timeline(user)
fetch_public_timeline(user, :with_blocks)
fetch_public_timeline(user, :local)
fetch_public_timeline(user, :tag)
fetch_notifications(user)
......@@ -227,6 +228,58 @@ defp fetch_public_timeline(user, :only_media) do
fetch_public_timeline(opts, "public timeline only media")
end
defp fetch_public_timeline(user, :with_blocks) do
opts = opts_for_public_timeline(user)
remote_non_friends = Agent.get(:non_friends_remote, & &1)
Benchee.run(%{
"public timeline without blocks" => fn ->
ActivityPub.fetch_public_activities(opts)
end
})
Enum.each(remote_non_friends, fn non_friend ->
{:ok, _} = User.block(user, non_friend)
end)
user = User.get_by_id(user.id)
opts = Map.put(opts, "blocking_user", user)
Benchee.run(
%{
"public timeline with user block" => fn ->
ActivityPub.fetch_public_activities(opts)
end
},
)
domains =
Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
{:ok, _user} = User.unblock(user, non_friend)
%{host: host} = URI.parse(non_friend.ap_id)
[host | domains]
end)
domains = Enum.uniq(domains)
Enum.each(domains, fn domain ->
{:ok, _} = User.block_domain(user, domain)
end)
user = User.get_by_id(user.id)
opts = Map.put(opts, "blocking_user", user)
Benchee.run(
%{
"public timeline with domain block" => fn opts ->
ActivityPub.fetch_public_activities(opts)
end
}
)
end
defp fetch_public_timeline(opts, title) when is_binary(title) do
first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
......
......@@ -27,7 +27,7 @@ def generate(opts \\ []) do
make_friends(main_user, opts[:friends])
Repo.get(User, main_user.id)
User.get_by_id(main_user.id)
end
def generate_users(max) do
......@@ -166,4 +166,24 @@ defp run_stream(users, main_user) do
)
|> Stream.run()
end
@spec prepare_users(User.t(), keyword()) :: map()
def prepare_users(user, opts) do
friends_limit = opts[:friends_used]
non_friends_limit = opts[:non_friends_used]
%{
user: user,
friends_local: fetch_users(user, friends_limit, :local, true),
friends_remote: fetch_users(user, friends_limit, :external, true),
non_friends_local: fetch_users(user, non_friends_limit, :local, false),
non_friends_remote: fetch_users(user, non_friends_limit, :external, false)
}
end
defp fetch_users(user, limit, local, friends?) do
user
|> get_users(limit: limit, local: local, friends?: friends?)
|> Enum.shuffle()
end
end
......@@ -5,7 +5,6 @@ defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
import Ecto.Query
alias Pleroma.Repo
alias Pleroma.Web.MastodonAPI.TimelineController
def run(_args) do
Mix.Pleroma.start_pleroma()
......@@ -37,7 +36,7 @@ def run(_args) do
Benchee.run(
%{
"Hashtag fetching, any" => fn tags ->
TimelineController.hashtag_fetching(
hashtag_fetching(
%{
"any" => tags
},
......@@ -47,7 +46,7 @@ def run(_args) do
end,
# Will always return zero results because no overlapping hashtags are generated.
"Hashtag fetching, all" => fn tags ->
TimelineController.hashtag_fetching(
hashtag_fetching(
%{
"all" => tags
},
......@@ -67,7 +66,7 @@ def run(_args) do
Benchee.run(
%{
"Hashtag fetching" => fn tag ->
TimelineController.hashtag_fetching(
hashtag_fetching(
%{
"tag" => tag
},
......@@ -80,4 +79,35 @@ def run(_args) do
time: 5
)
end
defp hashtag_fetching(params, user, local_only) do
tags =
[params["tag"], params["any"]]
|> List.flatten()
|> Enum.uniq()
|> Enum.filter(& &1)
|> Enum.map(&String.downcase(&1))
tag_all =
params
|> Map.get("all", [])
|> Enum.map(&String.downcase(&1))
tag_reject =
params
|> Map.get("none", [])
|> Enum.map(&String.downcase(&1))
_activities =
params
|> Map.put("type", "Create")
|> Map.put("local_only", local_only)
|> Map.put("blocking_user", user)
|> Map.put("muting_user", user)
|> Map.put("user", user)
|> Map.put("tag", tags)
|> Map.put("tag_all", tag_all)
|> Map.put("tag_reject", tag_reject)
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
end
end
......@@ -171,7 +171,8 @@
"application/ld+json" => ["activity+json"]
}
config :tesla, adapter: Tesla.Adapter.Gun
config :tesla, adapter: Tesla.Adapter.Hackney
# Configures http settings, upstream proxy etc.
config :pleroma, :http,
proxy_url: nil,
......@@ -183,7 +184,7 @@
name: "Pleroma",
email: "example@example.com",
notify_email: "noreply@example.com",
description: "A Pleroma instance, an alternative fediverse server",
description: "Pleroma: An efficient and flexible fediverse server",
background_image: "/images/city.jpg",
limit: 5_000,
chat_limit: 5_000,
......@@ -274,7 +275,7 @@
config :pleroma, :frontend_configurations,
pleroma_fe: %{
alwaysShowSubjectInput: true,
background: "/static/aurora_borealis.jpg",
background: "/images/city.jpg",
collapseMessageWithSubject: false,
disableChat: false,
greentext: false,
......
......@@ -511,7 +511,23 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- `discoverable`
- `actor_type`
- Response: none (code `200`)
- Response:
```json
{"status": "success"}
```
```json
{"errors":
{"actor_type": "is invalid"},
{"email": "has invalid format"},
...
}
```
```json
{"error": "Unable to update user."}
```
## `GET /api/pleroma/admin/reports`
......
......@@ -6,10 +6,6 @@ A Pleroma instance can be identified by "<Mastodon version> (compatible; Pleroma
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are lexically sortable strings
## Attachment cap
Some apps operate under the assumption that no more than 4 attachments can be returned or uploaded. Pleroma however does not enforce any limits on attachment count neither when returning the status object nor when posting.
## Timelines
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
......@@ -32,12 +28,20 @@ Has these additional fields under the `pleroma` object:
- `thread_muted`: true if the thread the post belongs to is muted
- `emoji_reactions`: A list with emoji / reaction maps. The format is `{name: "☕", count: 1, me: true}`. Contains no information about the reacting users, for that use the `/statuses/:id/reactions` endpoint.
## Attachments
## Media Attachments
Has these additional fields under the `pleroma` object:
- `mime_type`: mime type of the attachment.
### Attachment cap
Some apps operate under the assumption that no more than 4 attachments can be returned or uploaded. Pleroma however does not enforce any limits on attachment count neither when returning the status object nor when posting.
### Limitations
Pleroma does not process remote images and therefore cannot include fields such as `meta` and `blurhash`. It does not support focal points or aspect ratios. The frontend is expected to handle it.
## Accounts
The `id` parameter can also be the `nickname` of the user. This only works in these endpoints, not the deeper nested ones for following etc.
......
......@@ -69,3 +69,32 @@ mix pleroma.database update_users_following_followers_counts
```sh tab="From Source"
mix pleroma.database fix_likes_collections
```
## Vacuum the database
### Analyze
Running an `analyze` vacuum job can improve performance by updating statistics used by the query planner. **It is safe to cancel this.**
```sh tab="OTP"
./bin/pleroma_ctl database vacuum analyze
```
```sh tab="From Source"
mix pleroma.database vacuum analyze
```
### Full
Running a `full` vacuum job rebuilds your entire database by reading all of the data and rewriting it into smaller
and more compact files with an optimized layout. This process will take a long time and use additional disk space as
it builds the files side-by-side the existing database files. It can make your database faster and use less disk space,
but should only be run if necessary. **It is safe to cancel this.**
```sh tab="OTP"
./bin/pleroma_ctl database vacuum full
```
```sh tab="From Source"
mix pleroma.database vacuum full
```
\ No newline at end of file
# Optimizing your PostgreSQL performance
Pleroma performance depends to a large extent on good database performance. The default PostgreSQL settings are mostly fine, but often you can get better performance by changing a few settings.
You can use [PGTune](https://pgtune.leopard.in.ua) to get recommendations for your setup. If you do, set the "Number of Connections" field to 20, as Pleroma will only use 10 concurrent connections anyway. If you don't, it will give you advice that might even hurt your performance.
We also recommend not using the "Network Storage" option.
## Example configurations
Here are some configuration suggestions for PostgreSQL 10+.
### 1GB RAM, 1 CPU
```
shared_buffers = 256MB
effective_cache_size = 768MB
maintenance_work_mem = 64MB
work_mem = 13107kB
```
### 2GB RAM, 2 CPU
```
shared_buffers = 512MB
effective_cache_size = 1536MB
maintenance_work_mem = 128MB
work_mem = 26214kB
max_worker_processes = 2
max_parallel_workers_per_gather = 1
max_parallel_workers = 2
```
......@@ -38,8 +38,8 @@ sudo apt install git build-essential postgresql postgresql-contrib
* Download and add the Erlang repository:
```shell
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_1.0_all.deb
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_2.0_all.deb
```
* Install Elixir and Erlang:
......
......@@ -40,8 +40,8 @@ sudo apt install git build-essential postgresql postgresql-contrib
* Erlangのリポジトリをダウンロードおよびインストールします。
```
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_1.0_all.deb
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_2.0_all.deb
```
* ElixirとErlangをインストールします、
......
......@@ -63,7 +63,7 @@ apt install postgresql-11-rum
```
#### (Optional) Performance configuration
For optimal performance, you may use [PGTune](https://pgtune.leopard.in.ua), don't forget to restart postgresql after editing the configuration
It is encouraged to check [Optimizing your PostgreSQL performance](../configuration/postgresql.md) document, for tips on PostgreSQL tuning.
```sh tab="Alpine"
rc-service postgresql restart
......
......@@ -4,6 +4,7 @@
defmodule Mix.Tasks.Pleroma.Database do
alias Pleroma.Conversation
alias Pleroma.Maintenance
alias Pleroma.Object
alias Pleroma.Repo
alias Pleroma.User
......@@ -34,13 +35,7 @@ def run(["remove_embedded_objects" | args]) do
)
if Keyword.get(options, :vacuum) do
Logger.info("Runnning VACUUM FULL")
Repo.query!(
"vacuum full;",
[],
timeout: :infinity
)
Maintenance.vacuum("full")
end
end
......@@ -94,13 +89,7 @@ def run(["prune_objects" | args]) do
|> Repo.delete_all(timeout: :infinity)
if Keyword.get(options, :vacuum) do
Logger.info("Runnning VACUUM FULL")
Repo.query!(
"vacuum full;",
[],
timeout: :infinity
)
Maintenance.vacuum("full")
end
end
......@@ -135,4 +124,10 @@ def run(["fix_likes_collections"]) do
end)
|> Stream.run()
end
def run(["vacuum", args]) do
start_pleroma()
Maintenance.vacuum(args)
end
end
......@@ -15,7 +15,7 @@ def run(["ls-packs" | args]) do
{options, [], []} = parse_global_opts(args)
url_or_path = options[:manifest] || default_manifest()
manifest = fetch_manifest(url_or_path)
manifest = fetch_and_decode(url_or_path)
Enum.each(manifest, fn {name, info} ->
to_print = [
......@@ -42,12 +42,12 @@ def run(["get-packs" | args]) do
url_or_path = options[:manifest] || default_manifest()
manifest = fetch_manifest(url_or_path)
manifest = fetch_and_decode(url_or_path)
for pack_name <- pack_names do
if Map.has_key?(manifest, pack_name) do
pack = manifest[pack_name]
src_url = pack["src"]
src = pack["src"]
IO.puts(
IO.ANSI.format([
......@@ -57,11 +57,11 @@ def run(["get-packs" | args]) do
:normal,
" from ",
:underline,
src_url
src
])
)
binary_archive = Tesla.get!(client(), src_url).body
{:ok, binary_archive} = fetch(src)
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()
sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
......@@ -74,8 +74,8 @@ def run(["get-packs" | args]) do
raise "Bad SHA256 for #{pack_name}"
end
# The url specified in files should be in the same directory
files_url =
# The location specified in files should be in the same directory
files_loc =
url_or_path
|> Path.dirname()
|> Path.join(pack["files"])
......@@ -88,11 +88,11 @@ def run(["get-packs" | args]) do
:normal,
" from ",
:underline,
files_url
files_loc
])
)
files = Tesla.get!(client(), files_url).body |> Jason.decode!()
files = fetch_and_decode(files_loc)
IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))
......@@ -237,16 +237,20 @@ def run(["gen-pack" | args]) do
end
end
defp fetch_manifest(from) do
Jason.decode!(
if String.starts_with?(from, "http") do
Tesla.get!(client(), from).body
else
File.read!(from)
end
)
defp fetch_and_decode(from) do
with {:ok, json} <- fetch(from) do
Jason.decode!(json)
end
end
defp fetch("http" <> _ = from) do
with {:ok, %{body: body}} <- Tesla.get(client(), from) do
{:ok, body}
end
end
defp fetch(path), do: File.read(path)
defp parse_global_opts(args) do
OptionParser.parse(
args,
......
......@@ -24,10 +24,7 @@ def by_ap_id(query \\ Activity, ap_id) do
@spec by_actor(query, String.t()) :: query
def by_actor(query \\ Activity, actor) do
from(
activity in query,
where: fragment("(?)->>'actor' = ?", activity.data, ^actor)
)
from(a in query, where: a.actor == ^actor)
end
@spec by_author(query, User.t()) :: query
......
......@@ -24,6 +24,6 @@ defmodule Pleroma.Constants do
const(static_only_files,
do:
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc)
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css)
)
end
......@@ -63,7 +63,7 @@ def create_or_bump_for(activity, opts \\ []) do
ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do
{:ok, conversation} = create_for_ap_id(ap_id)
users = User.get_users_from_set(activity.recipients, false)
users = User.get_users_from_set(activity.recipients, local_only: false)
participations =
Enum.map(users, fn user ->
......
......@@ -499,7 +499,7 @@ defp download_archive(url, sha) do
if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do
{:ok, archive}
else
{:error, :imvalid_checksum}
{:error, :invalid_checksum}
end
end
end
......
......@@ -22,22 +22,7 @@ def options(connection_opts \\ [], %URI{} = uri) do
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
end
defp add_scheme_opts(opts, %URI{scheme: "http"}), do: opts
defp add_scheme_opts(opts, %URI{scheme: "https", host: host}) do
ssl_opts = [
ssl_options: [
# Workaround for remote server certificate chain issues
partial_chain: &:hackney_connect.partial_chain/1,
# We don't support TLS v1.3 yet
versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"],
server_name_indication: to_charlist(host)
]
]
Keyword.merge(opts, ssl_opts)
end
defp add_scheme_opts(opts, _), do: opts
def after_request(_), do: :ok
end
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Maintenance do
alias Pleroma.Repo
require Logger
def vacuum(args) do
case args do
"analyze" ->
Logger.info("Runnning VACUUM ANALYZE.")
Repo.query!(
"vacuum analyze;",
[],
timeout: :infinity
)
"full" ->
Logger.info("Runnning VACUUM FULL.")
Logger.warn(
"Re-packing your entire database may take a while and will consume extra disk space during the process."
)
Repo.query!(
"vacuum full;",
[],
timeout: :infinity
)
_ ->
Logger.error("Error: invalid vacuum argument.")
end
end
end
......@@ -92,8 +92,9 @@ def for_user_query(user, opts \\ %{}) do
|> join(:left, [n, a], object in Object,
on:
fragment(
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
object.data,
a.data,
a.data
)
)
......@@ -224,18 +225,8 @@ def set_read_up_to(%{id: user_id} = user, id) do
|> Marker.multi_set_last_read_id(user, "notifications")
|> Repo.transaction()
Notification
for_user_query(user)
|> where([n], n.id in ^notification_ids)
|> join(:inner, [n], activity in assoc(n, :activity))
|> join(:left, [n, a], object in Object,
on:
fragment(
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
object.data,
a.data
)
)
|> preload([n, a, o], activity: {a, object: o})
|> Repo.all()
end
......@@ -370,7 +361,8 @@ def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, lo
when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact"] do
potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity)
potential_receivers = User.get_users_from_set(potential_receiver_ap_ids, local_only)
potential_receivers =
User.get_users_from_set(potential_receiver_ap_ids, local_only: local_only)
notification_enabled_ap_ids =
potential_receiver_ap_ids
......
......@@ -31,7 +31,7 @@ defp headers do
{"x-content-type-options", "nosniff"},
{"referrer-policy", referrer_policy},
{"x-download-options", "noopen"},
{"content-security-policy", csp_string() <> ";"}
{"content-security-policy", csp_string()}
]
if report_uri do
......@@ -43,23 +43,46 @@ defp headers do
]
}
headers ++ [{"reply-to", Jason.encode!(report_group)}]
[{"reply-to", Jason.encode!(report_group)} | headers]
else
headers
end
end
static_csp_rules = [
"default-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",