Compare commits

...

42 commits

Author SHA1 Message Date
Phantasm
521fc70e48 MastoAPI AccountView AccountController: Add more block/mute expiry tests 2026-01-26 20:51:36 +02:00
Phantasm
f4a8c426df MastoAPI AccountView: Readd block/mute expiry outside the relationship 2026-01-26 20:51:36 +02:00
Phantasm
6439a5b334 MastoAPI AccountView: Add mute/block expiry to the relationship key 2026-01-26 20:51:36 +02:00
Lain Soykaf
0b813b9c4a mrf(media_proxy_warming): avoid adapter-level redirects
Drop follow_redirect/force_redirect from the HTTP options used when warming MediaProxy, relying on Tesla middleware instead (Hackney redirect handling can crash behind CONNECT proxies).

Also add a regression assertion in the policy test and document the upstream Hackney issues in ReverseProxy redirect handling.
2026-01-26 20:51:36 +02:00
Lain Soykaf
029994aa75 InstanceView: Omit comment if it's empty 2026-01-26 20:51:36 +02:00
Lain Soykaf
820a4cd97c Fix OAuth registration redirect_uris array support 2026-01-26 20:51:36 +02:00
MediaFormat
ad5bb02bd6 fix tests 2026-01-26 20:51:36 +02:00
MediaFormat
cb389e788d fix field type, fix formatting 2026-01-26 20:51:36 +02:00
MediaFormat
ba280b2d0f add changelog.d entry 2026-01-26 20:51:36 +02:00
MediaFormat
861a9f9365 Change redirect_uris to accept array of strings 2026-01-26 20:51:36 +02:00
Haelwenn (lanodan) Monnier
519ef4be5e mix: upgrade vix from "~> 0.26.0" to "~> 0.36"
Dropping the last zero should allow to get 0.x updates rather
than only 0.36.x updates.

Fixes: https://git.pleroma.social/pleroma/pleroma/-/issues/3393
2026-01-26 20:51:36 +02:00
Phantasm
f6c410b06c Move LiveDashboard to /pleroma/live_dashboard 2026-01-26 20:51:35 +02:00
Phantasm
f16dad2879 Docs: Add admin documentation for LiveDashboard and Oban Web 2026-01-26 20:51:35 +02:00
Phantasm
421187dbfa Remove /pleroma/oban and /phoenix/live_dashboard from API routes
This is needed to prevent admin frontend overrides from misbehaving when
overriding AdminFE located at /pleroma/admin, since API routes are
interpreted as the first portion of their full path, ie:
/api/v1/pleroma/admin -> /api
2026-01-26 20:51:35 +02:00
Phantasm
ac6ec02725 changelog 2026-01-26 20:51:35 +02:00
Phantasm
af4bed50e6 Add Oban Web and upgrade LiveView, plug 2026-01-26 20:51:35 +02:00
Lain Soykaf
033083d1d1 Streamer: Fix Marker streaming bug, fix caching in tests. 2026-01-26 20:51:35 +02:00
Lain Soykaf
343e42126a Add changelog 2026-01-26 20:51:35 +02:00
Lain Soykaf
b2be7d48bc Mix: Silence migrations 2026-01-26 20:51:35 +02:00
Lain Soykaf
02185ec711 StripLocation, ReadDescription: Silence noisy errors. 2026-01-26 20:51:35 +02:00
Lain Soykaf
7283d4f9be Config: Make streaming in tests actually synchronous 2026-01-26 20:51:35 +02:00
Lain Soykaf
6a81e4fe00 Tests: Syncify tests that mutate global state. 2026-01-26 20:51:35 +02:00
nicole mikołajczyk
520bac27dc Add changelog entry
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-26 20:51:35 +02:00
nicole mikołajczyk
5068e31583 optimize follow_request_count for own account view
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-26 20:51:35 +02:00
floatingghost
c07506ab6e paginate follow requests (#460)
matches https://docs.joinmastodon.org/methods/follow_requests/#get mostly

Co-authored-by: FloatingGhost <hannah@coffee-and-dreams.uk>
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/460
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-26 20:51:35 +02:00
Lain Soykaf
3d7d119782 Linting 2026-01-26 20:51:35 +02:00
Lain Soykaf
b013ec9123 Emoji, AccountView, UtilController: Handle encoding of emoji 2026-01-26 20:51:35 +02:00
Lain Soykaf
7ce7d4d319 Linting 2026-01-26 20:51:35 +02:00
Lain Soykaf
33b8ccf21f Emoji: Handle more edge cases for local emoji with strange filenames. 2026-01-26 20:51:35 +02:00
Lain Soykaf
b552c25039 Add changelog 2026-01-26 20:51:35 +02:00
Lain Soykaf
72fea0c901 Emoji: Unify tag building, fix tests. 2026-01-26 20:51:35 +02:00
Lain Soykaf
1085f6d7cd TransmogrifierTest: Add failing test for EmojiReact url encoding 2026-01-26 20:51:35 +02:00
Lain Soykaf
89ac0b8f0a Add changelog 2026-01-26 20:51:35 +02:00
Lain Soykaf
65456aed12 Release-to-Docker: Add unzip / curl to make updates work 2026-01-26 20:51:35 +02:00
Lain Soykaf
5600634574 Installation: Add Release-Via-Docker option 2026-01-26 20:51:35 +02:00
nicole mikołajczyk
e40bedc601 Add v1/instance/domain_blocks endpoint
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-26 20:51:35 +02:00
Lain Soykaf
8936066427 test(http): cover reverse proxy redirects via CONNECT proxy
Exercises Pleroma.ReverseProxy.Client.Hackney with follow_redirect enabled behind an HTTPS CONNECT proxy, ensuring the client follows a relative redirect and can stream the final body.
2026-01-26 20:51:35 +02:00
Mark Felder
71a4b8d0f2 In-house redirect handler for mediaproxy with Hackney adapter
Also ensure we always pass an absolute URL to Hackney when parsing a redirect response

(cherry picked from commit 00ac6bce8d244eec7e2460358296619e5cacba6b)
2026-01-26 20:51:35 +02:00
Lain Soykaf
f2ad6100f2 test(http): reproduce hackney follow_redirect crash via CONNECT proxy
Hackney 1.25 crashes when follow_redirect is enabled behind an HTTPS CONNECT proxy and the Location header is relative (hackney_http_connect transport).
This test demonstrates the failure and verifies Tesla-level redirects work when hackney redirects are disabled.
2026-01-26 20:51:35 +02:00
Lain Soykaf
500340fc82 test(http): cover pooled redirect with hackney
Reproduces the Hackney 1.25 pooled redirect cleanup issue which can surface as :req_not_found when the adapter returns a Ref and the body is later fetched.
2026-01-26 20:51:35 +02:00
Lain Soykaf
b85507c764 http(hackney): disable adapter redirects by default
Hackney 1.25.x has redirect handling issues behind CONNECT proxies and with pools.
Disable hackney-level redirects and rely on Tesla.Middleware.FollowRedirects instead.
Also default to with_body: true so redirects can be followed reliably.
2026-01-26 20:51:35 +02:00
lain
01d94a0135 Revert "Merge branch 'revert-cdd6df06' into 'develop'"
This reverts merge request !4411
2026-01-26 20:51:35 +02:00
86 changed files with 1646 additions and 160 deletions

View file

@ -0,0 +1 @@
Encode custom emoji URLs in EmojiReact activity tags.

View file

@ -0,0 +1 @@
Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney

View file

@ -0,0 +1 @@
Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation

View file

@ -0,0 +1 @@
Add v1/instance/domain_blocks endpoint

View file

@ -0,0 +1 @@
Fix OAuth app registration to accept `redirect_uris` as an array of strings (RFC 7591), while keeping backwards compatibility with string input.

1
changelog.d/oban-web.add Normal file
View file

@ -0,0 +1 @@
Added Oban Web dashboard located at /pleroma/oban

View file

@ -0,0 +1 @@
Paginate follow requests

View file

@ -0,0 +1 @@
Moved Phoenix LiveDashboard to /pleroma/live_dashboard

View file

@ -0,0 +1 @@
Reduce the number of flaky tests by making them sync if they affect the global state, and silence noisy test output.

View file

@ -0,0 +1 @@
Add instructions on how to run a release in docker, to make it easier to run on older distros.

View file

@ -0,0 +1 @@
Fix compilation with vips-8.18.0 with bumping to vix 0.36.0

View file

@ -102,7 +102,6 @@ config :pleroma, :http, send_user_agent: false
rum_enabled = System.get_env("RUM_ENABLED") == "true"
config :pleroma, :database, rum_enabled: rum_enabled
IO.puts("RUM enabled: #{rum_enabled}")
config :joken, default_signer: "yU8uHKq+yyAkZ11Hx//jcdacWc8yQ1bxAAGrplzB0Zwwjkp35v0RK9SO8WTPr6QZ"
@ -192,7 +191,7 @@ config :pleroma, Pleroma.Application,
streamer_registry: false,
test_http_pools: true
config :pleroma, Pleroma.Web.Streaming, sync_streaming: true
config :pleroma, Pleroma.Web.Streamer, sync_streaming: true
config :pleroma, Pleroma.Uploaders.Uploader, timeout: 1_000
@ -207,8 +206,9 @@ config :pleroma, Pleroma.User.Backup, tempdir: "test/tmp"
if File.exists?("./config/test.secret.exs") do
import_config "test.secret.exs"
else
IO.puts(
"You may want to create test.secret.exs to declare custom database connection parameters."
)
end
# Avoid noisy shutdown logs from os_mon during tests.
config :os_mon,
start_cpu_sup: false,
start_memsup: false

View file

@ -0,0 +1,47 @@
# Dashboards
Pleroma comes with two types of backend dashboards viewable to instance administrators:
* [Phoenix LiveDashboard](https://hexdocs.pm/phoenix_live_dashboard/Phoenix.LiveDashboard.html) - A general system oriented dashboard for viewing statistics about Pleroma resource consumption, Pleroma's database and Pleroma's job processor (Oban).
* [Oban Web](https://hexdocs.pm/oban_web/overview.html) - A dashboard specific to Oban for viewing Oban statistics, managing jobs and job queues.
!!! note
Both dashboards require working Websockets.
If your browser or web server don't support Websockets, both dashboards either won't update or will not display all information.
## Phoenix LiveDashboard
Instance administrators can access this dashboard at `/pleroma/live_dashboard`, giving a simple overview of software versions including Erlang and Elixir versions, instance uptime and resource consumption.
This dashboard gives insights into the current state of the BEAM VM running Pleroma code and database statistics including basic diagnostics.
It can be useful for troubleshooting of some issues namely regarding database performance.
### Relevant dashboard tabs
* Home - A general overview of system information including software versions, uptime and memory BEAM memory consumption.
* OS Data - Information about the OS and system such as CPU load, memory usage and disk usage.
* Ecto Stats - Information about the Pleroma database.
- Diagnose - Basic database diagnostics, including a `bloat` warning when an index or a table have excessive bloat, which can lead to bad database performance.
- Bloat - A table showing size of "bloat" (unused wasted space) in database tables and indexes. Very high bloat size in the `activities` and `objects` tables can lead to bad performance especially on slower disks such as on most VPS providers.
- Db settings - A small list of PostgreSQL settings mostly relevant to database performance.
- Total table size - Shows sizes of all database tables including indexes sorted by size, useful for quickly checking overall database size.
- Long running queries - A list of of slow database queries and their duration. Multiple entries with duration in multiple seconds indicate a slowly performing database.
* Oban - Shows a list of all Oban jobs.
!!! note
The DB bloat warning for `index 'oban_jobs::oban_jobs_args_index'` in Ecto Stats can be safely ignored.
## Oban Web
An advanced dashboard and management console viewable to instance administrators specifically for Oban, Pleroma's job processor.
It allows managing jobs, including force retrying failed jobs and job deletion.
It can be accessed at `/pleroma/oban`.
!!! danger
This dashboard is very powerful! If you are unsure what a certain feature does, don't use it.
Changing individual queue state/settings in the "Queues" view is heavily discouraged.
* Shows a real time chart of either a number of executed jobs, or job execution/wait time per a given time frame and the state/queue/worker.
* Shows a list of jobs in each state, their argument, number of attempts and execution/scheduled time.
* Selecting one or multiple jobs in the list allows performing actions like canceling/deleting and retrying.
* Clicking on a job shows a detailed view including the full argument, when it was inserted, information about its attempts, and performing actions on it.

View file

@ -13,6 +13,9 @@ You will be running commands as root. If you aren't root already, please elevate
Similarly to other binaries, OTP releases tend to be only compatible with the distro they are built on, as such this guide focuses only on Debian/Ubuntu and Alpine.
!!! note
If you get `GLIBC_... not found` errors on Debian/Ubuntu, you can run the OTP release from `/opt/pleroma` inside a newer distro container without upgrading the host. See [`release_to_docker_en.md`](release_to_docker_en.md).
### Detecting flavour
Paste the following into the shell:

View file

@ -0,0 +1,61 @@
# Running OTP releases via Docker (glibc shim)
Pleroma OTP releases are built on specific distros. If your host OS is older than
the build environment, you may hit runtime linker errors such as:
```
/lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found
```
If you don't want to upgrade your host OS, you can run the existing OTP release
from `/opt/pleroma` inside an Ubuntu 24.04 container while keeping your existing
host config and data directories.
This approach uses a small "shim" container image to provide a newer `glibc`.
It is **not** the official Pleroma Docker image.
## Requirements
- Docker Engine + the Docker Compose plugin on the host
- Root access (or equivalent access to the Docker socket)
- Existing OTP release in `/opt/pleroma`
- Existing config in `/etc/pleroma` and data in `/var/lib/pleroma`
## Setup
1. Copy the provided templates:
```sh
mkdir -p /etc/pleroma/container
cp -a /opt/pleroma/installation/release-to-docker/* /etc/pleroma/container/
```
2. Build the shim image:
```sh
cd /etc/pleroma/container
docker compose build
```
3. Replace your systemd unit:
```sh
cp /etc/pleroma/container/pleroma.service /etc/systemd/system/pleroma.service
systemctl daemon-reload
systemctl enable --now pleroma
journalctl -u pleroma -f
```
## Running migrations / `pleroma_ctl`
Migrations are run automatically by default when the container starts. You can
disable this by setting `PLEROMA_RUN_MIGRATIONS=0` in
`/etc/pleroma/container/docker-compose.yml`.
To run admin commands inside the container:
```sh
cd /etc/pleroma/container
docker compose exec pleroma /opt/pleroma/bin/pleroma_ctl status
docker compose run --rm --no-deps pleroma /opt/pleroma/bin/pleroma_ctl migrate
```

View file

@ -0,0 +1,26 @@
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive \
LANG=C.UTF-8 \
LC_ALL=C.UTF-8
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
gosu \
libstdc++6 \
libncurses6 libncursesw6 \
openssl libssl3 \
libmagic1t64 file \
postgresql-client \
ffmpeg imagemagick libimage-exiftool-perl \
libvips42t64 \
unzip \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /opt/pleroma
COPY pleroma-host-release-entrypoint.sh /usr/local/bin/pleroma-host-release-entrypoint.sh
RUN chmod +x /usr/local/bin/pleroma-host-release-entrypoint.sh
ENTRYPOINT ["/usr/local/bin/pleroma-host-release-entrypoint.sh"]
CMD ["/opt/pleroma/bin/pleroma", "start"]

View file

@ -0,0 +1,66 @@
# Run OTP releases on older glibc using Docker
Pleroma OTP releases are built on specific distros and may require a newer `glibc`
than your host has. A typical failure looks like:
```
... /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.38' not found ...
```
If you don't want to upgrade your host OS, you can run the existing OTP release
from `/opt/pleroma` inside an Ubuntu 24.04 container while keeping your existing
host paths (`/etc/pleroma`, `/var/lib/pleroma`, etc.).
This folder provides a "shim" container + systemd unit. It is **not** the Pleroma
Docker image.
## What this does
- Builds a small Ubuntu 24.04 image with runtime libs (including newer `glibc`).
- Mounts your existing host release at `/opt/pleroma` into the container.
- Runs as the same UID/GID that owns `/opt/pleroma` on the host (via `gosu`).
- Optionally runs migrations automatically on container start.
- Uses `network_mode: host` so your existing config that talks to `localhost`
keeps working.
## Setup (Debian/Ubuntu host)
1. Install Docker Engine + the Docker Compose plugin.
2. Copy these files to a stable location (example: `/etc/pleroma/container`):
```
mkdir -p /etc/pleroma/container
cp -a /opt/pleroma/installation/release-to-docker/* /etc/pleroma/container/
```
3. Build the shim image:
```
cd /etc/pleroma/container
docker compose build
```
4. Replace your systemd unit:
```
cp /etc/pleroma/container/pleroma.service /etc/systemd/system/pleroma.service
systemctl daemon-reload
systemctl enable --now pleroma
journalctl -u pleroma -f
```
## Running `pleroma_ctl`
Since the host binary may not run on older `glibc`, run admin commands inside the
container:
```
cd /etc/pleroma/container
docker compose exec pleroma /opt/pleroma/bin/pleroma_ctl status
docker compose run --rm --no-deps pleroma /opt/pleroma/bin/pleroma_ctl migrate
```
## Configuration notes
- Migrations run automatically by default.
- Set `PLEROMA_RUN_MIGRATIONS=0` in `docker-compose.yml` to disable.

View file

@ -0,0 +1,22 @@
services:
pleroma:
build:
context: .
dockerfile: Dockerfile
image: pleroma-host-release-wrapper:ubuntu24
init: true
network_mode: host
restart: unless-stopped
environment:
HOME: /opt/pleroma
LANG: C.UTF-8
LC_ALL: C.UTF-8
ELIXIR_ERL_OPTIONS: "+fnu"
# Set to 0 to skip running migrations on container start.
PLEROMA_RUN_MIGRATIONS: "1"
volumes:
# Existing OTP release installation (host)
- /opt/pleroma:/opt/pleroma:rw
# Existing config + uploads + static (host)
- /etc/pleroma:/etc/pleroma:rw
- /var/lib/pleroma:/var/lib/pleroma:rw

View file

@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
uid="${PLEROMA_UID:-}"
gid="${PLEROMA_GID:-}"
if [[ -z "${uid}" || -z "${gid}" ]]; then
uid="$(stat -c '%u' /opt/pleroma)"
gid="$(stat -c '%g' /opt/pleroma)"
fi
export HOME="${HOME:-/opt/pleroma}"
if [[ "${PLEROMA_RUN_MIGRATIONS:-1}" != "0" && "${1:-}" == "/opt/pleroma/bin/pleroma" && "${2:-}" == "start" ]]; then
echo "Running migrations..."
gosu "${uid}:${gid}" /opt/pleroma/bin/pleroma_ctl migrate
fi
exec gosu "${uid}:${gid}" "$@"

View file

@ -0,0 +1,16 @@
[Unit]
Description=Pleroma social network (OTP release via Docker glibc shim)
After=network.target docker.service postgresql.service nginx.service
Requires=docker.service
[Service]
Type=simple
WorkingDirectory=/etc/pleroma/container
ExecStart=/usr/bin/docker compose up --build --remove-orphans
ExecStop=/usr/bin/docker compose down
Restart=on-failure
RestartSec=5s
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target

View file

@ -330,7 +330,13 @@ defmodule Mix.Tasks.Pleroma.Config do
|> Enum.each(&write_and_delete(&1, file, opts[:delete]))
:ok = File.close(file)
System.cmd("mix", ["format", path])
# Ensure `mix format` runs in the same env as the current task and doesn't
# emit config-time stderr noise (e.g. dev secret warnings) into `mix test`.
System.cmd("mix", ["format", path],
env: [{"MIX_ENV", to_string(Mix.env())}],
stderr_to_stdout: true
)
end
defp config_header, do: "import Config\r\n\r\n"

View file

@ -11,6 +11,8 @@ defmodule Pleroma.Emoji do
alias Pleroma.Emoji.Combinations
alias Pleroma.Emoji.Loader
alias Pleroma.Utils.URIEncoding
alias Pleroma.Web.Endpoint
require Logger
@ -189,6 +191,34 @@ defmodule Pleroma.Emoji do
def emoji_url(_), do: nil
@spec local_url(String.t() | nil) :: String.t() | nil
def local_url(nil), do: nil
def local_url("http" <> _ = url) do
URIEncoding.encode_url(url)
end
def local_url("/" <> _ = path) do
path = URIEncoding.encode_url(path, bypass_parse: true, bypass_decode: true)
Endpoint.url() <> path
end
def local_url(path) when is_binary(path) do
local_url("/" <> path)
end
def build_emoji_tag({name, url}) do
url = URIEncoding.encode_url(url)
%{
"icon" => %{"url" => "#{url}", "type" => "Image"},
"name" => ":" <> name <> ":",
"type" => "Emoji",
"updated" => "1970-01-01T00:00:00Z",
"id" => url
}
end
def emoji_name_with_instance(name, url) do
url = url |> URI.parse() |> Map.get(:host)
"#{name}@#{url}"

View file

@ -5,7 +5,6 @@
defmodule Pleroma.Emoji.Formatter do
alias Pleroma.Emoji
alias Pleroma.HTML
alias Pleroma.Web.Endpoint
alias Pleroma.Web.MediaProxy
def emojify(text) do
@ -44,7 +43,7 @@ defmodule Pleroma.Emoji.Formatter do
Emoji.get_all()
|> Enum.filter(fn {emoji, %Emoji{}} -> String.contains?(text, ":#{emoji}:") end)
|> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc ->
Map.put(acc, name, to_string(URI.merge(Endpoint.url(), file)))
Map.put(acc, name, Emoji.local_url(file))
end)
end

View file

@ -147,24 +147,22 @@ defmodule Pleroma.FollowingRelationship do
|> Repo.aggregate(:count, :id)
end
def get_follow_requests(%User{id: id}) do
def get_follow_requests_query(%User{id: id}) do
__MODULE__
|> join(:inner, [r], f in assoc(r, :follower))
|> join(:inner, [r], f in assoc(r, :follower), as: :follower)
|> where([r], r.state == ^:follow_pending)
|> where([r], r.following_id == ^id)
|> where([r, f], f.is_active == true)
|> select([r, f], f)
|> Repo.all()
|> where([r, follower: f], f.is_active == true)
|> select([r, follower: f], f)
end
def get_outgoing_follow_requests(%User{id: id}) do
def get_outgoing_follow_requests_query(%User{id: id}) do
__MODULE__
|> join(:inner, [r], f in assoc(r, :following))
|> join(:inner, [r], f in assoc(r, :following), as: :following)
|> where([r], r.state == ^:follow_pending)
|> where([r], r.follower_id == ^id)
|> where([r, f], f.is_active == true)
|> select([r, f], f)
|> Repo.all()
|> where([r, following: f], f.is_active == true)
|> select([r, following: f], f)
end
def following?(%User{id: follower_id}, %User{id: followed_id}) do

View file

@ -6,8 +6,9 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
@behaviour Pleroma.HTTP.AdapterHelper
@defaults [
follow_redirect: true,
force_redirect: true
follow_redirect: false,
force_redirect: false,
with_body: true
]
@spec options(keyword(), URI.t()) :: keyword()

View file

@ -5,6 +5,23 @@
defmodule Pleroma.ReverseProxy.Client.Hackney do
@behaviour Pleroma.ReverseProxy.Client
# In-app redirect handler to avoid Hackney redirect bugs:
# - https://github.com/benoitc/hackney/issues/527 (relative/protocol-less redirects can crash Hackney)
# - https://github.com/benoitc/hackney/issues/273 (redirects not followed when using HTTP proxy)
#
# Based on a redirect handler from Pleb, slightly modified to work with Hackney:
# https://declin.eu/objects/d4f38e62-5429-4614-86d1-e8fc16e6bf33
@redirect_statuses [301, 302, 303, 307, 308]
defp absolute_redirect_url(original_url, resp_headers) do
location =
Enum.find(resp_headers, fn {header, _location} ->
String.downcase(header) == "location"
end)
URI.merge(original_url, elem(location, 1))
|> URI.to_string()
end
@impl true
def request(method, url, headers, body, opts \\ []) do
opts =
@ -12,7 +29,35 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
path
end)
:hackney.request(method, url, headers, body, opts)
if opts[:follow_redirect] != false do
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
res = :hackney.request(method, url, headers, body, req_opts)
case res do
{:ok, code, resp_headers, _client} when code in @redirect_statuses ->
:hackney.request(
method,
absolute_redirect_url(url, resp_headers),
headers,
body,
req_opts
)
{:ok, code, resp_headers} when code in @redirect_statuses ->
:hackney.request(
method,
absolute_redirect_url(url, resp_headers),
headers,
body,
req_opts
)
_ ->
res
end
else
:hackney.request(method, url, headers, body, opts)
end
end
@impl true

View file

@ -29,22 +29,26 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do
do: current_description
defp read_when_empty(_, file, tag) do
try do
{tag_content, 0} =
System.cmd("exiftool", ["-b", "-s3", tag, file],
stderr_to_stdout: false,
parallelism: true
)
if File.exists?(file) do
try do
{tag_content, 0} =
System.cmd("exiftool", ["-m", "-b", "-s3", tag, file],
stderr_to_stdout: false,
parallelism: true
)
tag_content = String.trim(tag_content)
tag_content = String.trim(tag_content)
if tag_content != "" and
String.length(tag_content) <=
Pleroma.Config.get([:instance, :description_limit]),
do: tag_content,
else: nil
rescue
_ in ErlangError -> nil
if tag_content != "" and
String.length(tag_content) <=
Pleroma.Config.get([:instance, :description_limit]),
do: tag_content,
else: nil
rescue
_ in ErlangError -> nil
end
else
nil
end
end
end

View file

@ -16,11 +16,12 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripLocation do
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do
try do
case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", "-png:all=", file],
case System.cmd("exiftool", ["-m", "-overwrite_original", "-gps:all=", "-png:all=", file],
stderr_to_stdout: true,
parallelism: true
) do
{_response, 0} -> {:ok, :filtered}
{error, 1} -> {:error, error}
{error, _} -> {:error, error}
end
rescue
e in ErlangError ->

View file

@ -287,8 +287,14 @@ defmodule Pleroma.User do
defdelegate following(user), to: FollowingRelationship
defdelegate following?(follower, followed), to: FollowingRelationship
defdelegate following_ap_ids(user), to: FollowingRelationship
defdelegate get_follow_requests(user), to: FollowingRelationship
defdelegate get_outgoing_follow_requests(user), to: FollowingRelationship
defdelegate get_follow_requests_query(user), to: FollowingRelationship
defdelegate get_outgoing_follow_requests_query(user), to: FollowingRelationship
def get_follow_requests(user) do
get_follow_requests_query(user)
|> Repo.all()
end
defdelegate search(query, opts \\ []), to: User.Search
@doc """

View file

@ -17,7 +17,6 @@ defmodule Pleroma.Web.ActivityPub.Builder do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Web.CommonAPI.ActivityDraft
alias Pleroma.Web.Endpoint
require Pleroma.Constants
@ -64,15 +63,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
defp add_emoji_content(data, emoji, url) do
tag = [
%{
"id" => url,
"type" => "Emoji",
"name" => Emoji.maybe_quote(emoji),
"icon" => %{
"type" => "Image",
"url" => url
}
}
Emoji.build_emoji_tag({Emoji.maybe_strip_name(emoji), url})
]
data
@ -113,7 +104,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
defp local_custom_emoji_react(data, emoji) do
with %{file: path} = emojo <- Emoji.get(emoji) do
url = "#{Endpoint.url()}#{path}"
url = Emoji.local_url(path)
add_emoji_content(data, emojo.code, url)
else
_ -> {:error, "Emoji does not exist"}

View file

@ -27,7 +27,14 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
end
defp fetch(url) do
http_client_opts = Pleroma.Config.get([:media_proxy, :proxy_opts, :http], pool: :media)
# This module uses Tesla (Pleroma.HTTP) to fetch the MediaProxy URL.
# Redirect following is handled by Tesla middleware, so we must not enable
# adapter-level redirect logic (Hackney can crash on relative redirects when proxied).
http_client_opts =
[:media_proxy, :proxy_opts, :http]
|> Pleroma.Config.get(pool: :media)
|> Keyword.drop([:follow_redirect, :force_redirect])
HTTP.get(url, [], http_client_opts)
end

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
@behaviour Pleroma.Web.ActivityPub.Transmogrifier.API
alias Pleroma.Activity
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.Emoji
alias Pleroma.Maps
alias Pleroma.Object
alias Pleroma.Object.Containment
@ -1005,32 +1006,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def take_emoji_tags(%User{emoji: emoji}) do
emoji
|> Map.to_list()
|> Enum.map(&build_emoji_tag/1)
|> Enum.map(&Emoji.build_emoji_tag/1)
end
# TODO: we should probably send mtime instead of unix epoch time for updated
def add_emoji_tags(%{"emoji" => emoji} = object) do
tags = object["tag"] || []
out = Enum.map(emoji, &build_emoji_tag/1)
out = Enum.map(emoji, &Emoji.build_emoji_tag/1)
Map.put(object, "tag", tags ++ out)
end
def add_emoji_tags(object), do: object
def build_emoji_tag({name, url}) do
url = URI.encode(url)
%{
"icon" => %{"url" => "#{url}", "type" => "Image"},
"name" => ":" <> name <> ":",
"type" => "Emoji",
"updated" => "1970-01-01T00:00:00Z",
"id" => url
}
end
def set_conversation(object) do
Map.put(object, "conversation", object["context"])
end

View file

@ -123,7 +123,10 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
name: %Schema{type: :string, description: "Application Name"},
scopes: %Schema{type: :array, items: %Schema{type: :string}, description: "oAuth scopes"},
redirect_uris: %Schema{
type: :string,
oneOf: [
%Schema{type: :string},
%Schema{type: :array, items: %Schema{type: :string}}
],
description:
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
},
@ -141,7 +144,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
},
example: %{
"name" => "My App",
"redirect_uris" => "https://myapp.com/auth/callback",
"redirect_uris" => ["https://myapp.com/auth/callback"],
"website" => "https://myapp.com/",
"scopes" => ["read", "write"],
"trusted" => true
@ -157,7 +160,10 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
name: %Schema{type: :string, description: "Application Name"},
scopes: %Schema{type: :array, items: %Schema{type: :string}, description: "oAuth scopes"},
redirect_uris: %Schema{
type: :string,
oneOf: [
%Schema{type: :string},
%Schema{type: :array, items: %Schema{type: :string}}
],
description:
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
},
@ -175,7 +181,7 @@ defmodule Pleroma.Web.ApiSpec.Admin.OAuthAppOperation do
},
example: %{
"name" => "My App",
"redirect_uris" => "https://myapp.com/auth/callback",
"redirect_uris" => ["https://myapp.com/auth/callback"],
"website" => "https://myapp.com/",
"scopes" => ["read", "write"],
"trusted" => true

View file

@ -97,7 +97,10 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
properties: %{
client_name: %Schema{type: :string, description: "A name for your application."},
redirect_uris: %Schema{
type: :string,
oneOf: [
%Schema{type: :string},
%Schema{type: :array, items: %Schema{type: :string}}
],
description:
"Where the user should be redirected after authorization. To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter."
},

View file

@ -19,6 +19,7 @@ defmodule Pleroma.Web.ApiSpec.FollowRequestOperation do
summary: "Retrieve follow requests",
security: [%{"oAuth" => ["read:follows", "follow"]}],
operationId: "FollowRequestController.index",
parameters: pagination_params(),
responses: %{
200 =>
Operation.response("Array of Account", "application/json", %Schema{
@ -62,4 +63,22 @@ defmodule Pleroma.Web.ApiSpec.FollowRequestOperation do
required: true
)
end
defp pagination_params do
[
Operation.parameter(:max_id, :query, :string, "Return items older than this ID"),
Operation.parameter(
:since_id,
:query,
:string,
"Return the oldest items newer than this ID"
),
Operation.parameter(
:limit,
:query,
%Schema{type: :integer, default: 20},
"Maximum number of items to return. Will be ignored if it's more than 40"
)
]
end
end

View file

@ -57,6 +57,22 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
}
end
def domain_blocks_operation do
%Operation{
tags: ["Instance misc"],
summary: "Retrieve instance domain blocks",
operationId: "InstanceController.domain_blocks",
responses: %{
200 =>
Operation.response(
"Array of domain blocks",
"application/json",
array_of_domain_blocks()
)
}
}
end
def translation_languages_operation do
%Operation{
tags: ["Instance misc"],
@ -420,4 +436,19 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
}
}
end
defp array_of_domain_blocks do
%Schema{
type: :array,
items: %Schema{
type: :object,
properties: %{
domain: %Schema{type: :string},
digest: %Schema{type: :string},
severity: %Schema{type: :string},
comment: %Schema{type: :string}
}
}
}
end
end

View file

@ -18,6 +18,7 @@ defmodule Pleroma.Web.ApiSpec.PleromaFollowRequestOperation do
summary: "Retrieve outgoing follow requests",
security: [%{"oAuth" => ["read:follows", "follow"]}],
operationId: "PleromaFollowRequestController.outgoing",
parameters: pagination_params(),
responses: %{
200 =>
Operation.response("Array of Account", "application/json", %Schema{
@ -28,4 +29,22 @@ defmodule Pleroma.Web.ApiSpec.PleromaFollowRequestOperation do
}
}
end
defp pagination_params do
[
Operation.parameter(:max_id, :query, :string, "Return items older than this ID"),
Operation.parameter(
:since_id,
:query,
:string,
"Return the oldest items newer than this ID"
),
Operation.parameter(
:limit,
:query,
%Schema{type: :integer, default: 20},
"Maximum number of items to return. Will be ignored if it's more than 40"
)
]
end
end

View file

@ -29,6 +29,11 @@ defmodule Pleroma.Web.Fallback.RedirectController do
)
end
def live_dashboard(conn, _params) do
conn
|> redirect(to: "/pleroma/live_dashboard")
end
def redirector(conn, _params, code \\ 200) do
{:ok, index_content} = File.read(index_file_path(conn))

View file

@ -5,6 +5,10 @@
defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper,
only: [add_link_headers: 2]
alias Pleroma.Pagination
alias Pleroma.User
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.Plugs.OAuthScopesPlug
@ -24,10 +28,15 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.FollowRequestOperation
@doc "GET /api/v1/follow_requests"
def index(%{assigns: %{user: followed}} = conn, _params) do
follow_requests = User.get_follow_requests(followed)
def index(%{assigns: %{user: followed}} = conn, params) do
follow_requests =
followed
|> User.get_follow_requests_query()
|> Pagination.fetch_paginated(params, :keyset, :follower)
render(conn, "index.json", for: followed, users: follow_requests, as: :user)
conn
|> add_link_headers(follow_requests)
|> render("index.json", for: followed, users: follow_requests, as: :user)
end
@doc "POST /api/v1/follow_requests/:id/authorize"

View file

@ -7,7 +7,7 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do
plug(Pleroma.Web.ApiSpec.CastAndValidate)
plug(:skip_auth when action in [:show, :show2, :peers])
plug(:skip_auth)
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.InstanceOperation
@ -31,6 +31,11 @@ defmodule Pleroma.Web.MastodonAPI.InstanceController do
render(conn, "rules.json")
end
@doc "GET /api/v1/instance/domain_blocks"
def domain_blocks(conn, _params) do
render(conn, "domain_blocks.json")
end
@doc "GET /api/v1/instance/translation_languages"
def translation_languages(conn, _params) do
render(conn, "translation_languages.json")

View file

@ -9,6 +9,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
alias Pleroma.User
alias Pleroma.UserNote
alias Pleroma.UserRelationship
alias Pleroma.Utils.URIEncoding
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MastodonAPI.AccountView
alias Pleroma.Web.MediaProxy
@ -123,6 +124,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
target,
&User.blocks_user?(&1, &2)
),
block_expires_at: maybe_put_block_expires_at(user_relationships, target, reading_user),
blocked_by:
UserRelationship.exists?(
user_relationships,
@ -139,6 +141,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
target,
&User.mutes?(&1, &2)
),
mute_expires_at: maybe_put_mute_expires_at(user_relationships, target, reading_user),
muting_notifications:
UserRelationship.exists?(
user_relationships,
@ -238,7 +241,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
emojis =
Enum.map(user.emoji, fn {shortcode, raw_url} ->
url = MediaProxy.url(raw_url)
url =
raw_url
|> encode_emoji_url()
|> MediaProxy.url()
%{
shortcode: shortcode,
@ -339,8 +345,8 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|> maybe_put_unread_conversation_count(user, opts[:for])
|> maybe_put_unread_notification_count(user, opts[:for])
|> maybe_put_email_address(user, opts[:for])
|> maybe_put_mute_expires_at(user, opts[:for], opts)
|> maybe_put_block_expires_at(user, opts[:for], opts)
|> maybe_put_mute_expires_at(user, opts[:for], relationship)
|> maybe_put_block_expires_at(user, opts[:for], relationship)
|> maybe_show_birthday(user, opts[:for])
end
@ -356,8 +362,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
%User{id: user_id}
) do
count =
User.get_follow_requests(user)
|> length()
user
|> User.get_follow_requests_query()
|> Pleroma.Repo.aggregate(:count)
data
|> Kernel.put_in([:follow_requests_count], count)
@ -467,22 +474,58 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
defp maybe_put_email_address(data, _, _), do: data
defp maybe_put_mute_expires_at(data, %User{} = user, target, %{mutes: true}) do
Map.put(
data,
:mute_expires_at,
UserRelationship.get_mute_expire_date(target, user)
)
defp maybe_put_mute_expires_at(user_relationships, %User{} = target, %User{} = user) do
cond do
UserRelationship.exists?(user_relationships, :mute, user, target, &User.mutes_user?(&1, &2)) ->
UserRelationship.get_mute_expire_date(user, target)
true ->
nil
end
end
defp maybe_put_mute_expires_at(data, %User{} = target, %User{} = user, relationship) do
cond do
Map.has_key?(relationship, :mute_expires_at) ->
Map.put(data, :mute_expires_at, relationship.mute_expires_at)
User.mutes_user?(user, target) ->
Map.put(data, :mute_expires_at, UserRelationship.get_mute_expire_date(user, target))
true ->
Map.put(data, :mute_expires_at, nil)
end
end
defp maybe_put_mute_expires_at(data, _, _, _), do: data
defp maybe_put_block_expires_at(data, %User{} = user, target, %{blocks: true}) do
Map.put(
data,
:block_expires_at,
UserRelationship.get_block_expire_date(target, user)
)
defp maybe_put_block_expires_at(user_relationships, %User{} = target, %User{} = user) do
cond do
UserRelationship.exists?(
user_relationships,
:block,
user,
target,
&User.blocks_user?(&1, &2)
) ->
UserRelationship.get_block_expire_date(user, target)
true ->
nil
end
end
defp maybe_put_block_expires_at(data, %User{} = target, %User{} = user, relationship) do
cond do
Map.has_key?(relationship, :block_expires_at) ->
Map.put(data, :block_expires_at, relationship.block_expires_at)
User.blocks_user?(user, target) ->
Map.put(data, :block_expires_at, UserRelationship.get_block_expire_date(user, target))
true ->
Map.put(data, :block_expires_at, nil)
end
end
defp maybe_put_block_expires_at(data, _, _, _), do: data
@ -511,4 +554,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
# See https://git.pleroma.social/pleroma/pleroma-meta/-/issues/14
user.actor_type == "Service" || user.actor_type == "Group"
end
defp encode_emoji_url(nil), do: nil
defp encode_emoji_url("http" <> _ = url), do: URIEncoding.encode_url(url)
defp encode_emoji_url("/" <> _ = path),
do: URIEncoding.encode_url(path, bypass_parse: true, bypass_decode: true)
defp encode_emoji_url(path) when is_binary(path),
do: URIEncoding.encode_url(path, bypass_parse: true, bypass_decode: true)
end

View file

@ -6,14 +6,13 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiView do
use Pleroma.Web, :view
alias Pleroma.Emoji
alias Pleroma.Web.Endpoint
def render("index.json", %{custom_emojis: custom_emojis}) do
render_many(custom_emojis, __MODULE__, "show.json")
end
def render("show.json", %{custom_emoji: {shortcode, %Emoji{file: relative_url, tags: tags}}}) do
url = Endpoint.url() |> URI.merge(relative_url) |> to_string()
url = Emoji.local_url(relative_url)
%{
"shortcode" => shortcode,

View file

@ -5,11 +5,18 @@
defmodule Pleroma.Web.MastodonAPI.InstanceView do
use Pleroma.Web, :view
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
alias Pleroma.Config
alias Pleroma.Web.ActivityPub.MRF
@mastodon_api_level "2.7.2"
@block_severities %{
federated_timeline_removal: "silence",
reject: "suspend"
}
def render("show.json", _) do
instance = Config.get(:instance)
@ -90,6 +97,53 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do
}
end
def render("domain_blocks.json", _) do
if Config.get([:mrf, :transparency]) do
exclusions = Config.get([:mrf, :transparency_exclusions]) |> MRF.instance_list_from_tuples()
domain_blocks =
Config.get(:mrf_simple)
|> Enum.map(fn {rule, instances} ->
instances
|> Enum.map(fn
{host, reason} when not_empty_string(host) and not_empty_string(reason) ->
{host, reason}
{host, _reason} when not_empty_string(host) ->
{host, ""}
host when not_empty_string(host) ->
{host, ""}
_ ->
nil
end)
|> Enum.reject(&is_nil/1)
|> Enum.reject(fn {host, _} ->
host in exclusions or not Map.has_key?(@block_severities, rule)
end)
|> Enum.map(fn {host, reason} ->
domain_block = %{
domain: host,
digest: :crypto.hash(:sha256, host) |> Base.encode16(case: :lower),
severity: Map.get(@block_severities, rule)
}
if not_empty_string(reason) do
Map.put(domain_block, :comment, reason)
else
domain_block
end
end)
end)
|> List.flatten()
domain_blocks
else
[]
end
end
def render("translation_languages.json", _) do
with true <- Pleroma.Language.Translation.configured?(),
{:ok, languages} <- Pleroma.Language.Translation.languages_matrix() do

View file

@ -31,9 +31,32 @@ defmodule Pleroma.Web.OAuth.App do
@spec changeset(t(), map()) :: Ecto.Changeset.t()
def changeset(struct, params) do
params = normalize_redirect_uris_param(params)
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted, :user_id])
end
defp normalize_redirect_uris_param(%{} = params) do
case params do
%{redirect_uris: redirect_uris} when is_list(redirect_uris) ->
Map.put(params, :redirect_uris, normalize_redirect_uris(redirect_uris))
%{"redirect_uris" => redirect_uris} when is_list(redirect_uris) ->
Map.put(params, "redirect_uris", normalize_redirect_uris(redirect_uris))
_ ->
params
end
end
defp normalize_redirect_uris(redirect_uris) when is_list(redirect_uris) do
redirect_uris
|> Enum.filter(&is_binary/1)
|> Enum.map(&String.trim/1)
|> Enum.reject(&(&1 == ""))
|> Enum.join("\n")
end
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
def register_changeset(struct, params \\ %{}) do
changeset =

View file

@ -5,6 +5,10 @@
defmodule Pleroma.Web.PleromaAPI.FollowRequestController do
use Pleroma.Web, :controller
import Pleroma.Web.ControllerHelper,
only: [add_link_headers: 2]
alias Pleroma.Pagination
alias Pleroma.User
alias Pleroma.Web.Plugs.OAuthScopesPlug
@ -17,11 +21,15 @@ defmodule Pleroma.Web.PleromaAPI.FollowRequestController do
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaFollowRequestOperation
@doc "GET /api/v1/pleroma/outgoing_follow_requests"
def outgoing(%{assigns: %{user: follower}} = conn, _params) do
follow_requests = User.get_outgoing_follow_requests(follower)
def outgoing(%{assigns: %{user: follower}} = conn, params) do
follow_requests =
follower
|> User.get_outgoing_follow_requests_query()
|> Pagination.fetch_paginated(params, :keyset, :following)
conn
|> put_view(Pleroma.Web.MastodonAPI.FollowRequestView)
|> add_link_headers(follow_requests)
|> render("index.json", for: follower, users: follow_requests, as: :user)
end
end

View file

@ -7,7 +7,6 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderView do
alias Pleroma.BookmarkFolder
alias Pleroma.Emoji
alias Pleroma.Web.Endpoint
def render("show.json", %{folder: %BookmarkFolder{} = folder}) do
%{
@ -33,7 +32,7 @@ defmodule Pleroma.Web.PleromaAPI.BookmarkFolderView do
emoji = Emoji.get(emoji)
if emoji != nil do
Endpoint.url() |> URI.merge(emoji.file) |> to_string()
Emoji.local_url(emoji.file)
else
nil
end

View file

@ -5,6 +5,7 @@
defmodule Pleroma.Web.Router do
use Pleroma.Web, :router
import Phoenix.LiveDashboard.Router
import Oban.Web.Router
pipeline :accepts_html do
plug(:accepts, ["html"])
@ -813,6 +814,7 @@ defmodule Pleroma.Web.Router do
get("/instance", InstanceController, :show)
get("/instance/peers", InstanceController, :peers)
get("/instance/rules", InstanceController, :rules)
get("/instance/domain_blocks", InstanceController, :domain_blocks)
get("/instance/translation_languages", InstanceController, :translation_languages)
get("/statuses", StatusController, :index)
@ -1043,7 +1045,8 @@ defmodule Pleroma.Web.Router do
scope "/" do
pipe_through([:pleroma_html, :authenticate, :require_admin])
live_dashboard("/phoenix/live_dashboard", additional_pages: [oban: Oban.LiveDashboard])
live_dashboard("/pleroma/live_dashboard", additional_pages: [oban: Oban.LiveDashboard])
oban_dashboard("/pleroma/oban")
end
# Test-only routes needed to test action dispatching and plug chain execution
@ -1084,14 +1087,20 @@ defmodule Pleroma.Web.Router do
get("/:maybe_nickname_or_id", RedirectController, :redirector_with_meta)
match(:*, "/api/pleroma/*path", LegacyPleromaApiRerouterPlug, [])
get("/api/*path", RedirectController, :api_not_implemented)
get("/phoenix/live_dashboard", RedirectController, :live_dashboard)
get("/*path", RedirectController, :redirector_with_preload)
options("/*path", RedirectController, :empty)
end
# /pleroma/{phoenix,oban}/* need to get filtered out from api routes for frontend configuration
# to not drop admin overrides for /pleroma/admin.
@non_api_routes ["/pleroma/live_dashboard", "/pleroma/oban"]
def get_api_routes do
Phoenix.Router.routes(__MODULE__)
|> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
|> Enum.reject(fn r -> String.starts_with?(r.path, @non_api_routes) end)
|> Enum.map(fn r ->
r.path
|> String.split("/", trim: true)

View file

@ -300,7 +300,7 @@ defmodule Pleroma.Web.Streamer do
end)
end
defp do_stream("user", item) do
defp do_stream("user", %Activity{} = item) do
Logger.debug("Trying to push to users")
recipient_topics =

View file

@ -12,6 +12,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
alias Pleroma.Emoji
alias Pleroma.Healthcheck
alias Pleroma.User
alias Pleroma.Utils.URIEncoding
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
alias Pleroma.Web.CommonAPI
@ -180,12 +181,22 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
def emoji(conn, _params) do
emoji =
Enum.reduce(Emoji.get_all(), %{}, fn {code, %Emoji{file: file, tags: tags}}, acc ->
file = encode_emoji_url(file)
Map.put(acc, code, %{image_url: file, tags: tags})
end)
json(conn, emoji)
end
defp encode_emoji_url(nil), do: nil
defp encode_emoji_url("http" <> _ = url), do: URIEncoding.encode_url(url)
defp encode_emoji_url("/" <> _ = path),
do: URIEncoding.encode_url(path, bypass_parse: true, bypass_decode: true)
defp encode_emoji_url(path) when is_binary(path),
do: URIEncoding.encode_url(path, bypass_parse: true, bypass_decode: true)
def update_notification_settings(%{assigns: %{user: user}} = conn, params) do
with {:ok, _} <- User.update_notification_settings(user, params) do
json(conn, %{status: "success"})

View file

@ -130,7 +130,7 @@ defmodule Pleroma.Mixfile do
{:ecto_enum, "~> 1.4"},
{:postgrex, ">= 0.20.0"},
{:phoenix_html, "~> 3.3"},
{:phoenix_live_view, "~> 0.19.0"},
{:phoenix_live_view, "~> 1.1.0"},
{:phoenix_live_dashboard, "~> 0.8.0"},
{:telemetry_metrics, "~> 0.6"},
{:telemetry_poller, "~> 1.0"},
@ -140,6 +140,7 @@ defmodule Pleroma.Mixfile do
{:oban_plugins_lazarus,
git: "https://git.pleroma.social/pleroma/elixir-libraries/oban_plugins_lazarus.git",
ref: "e49fc355baaf0e435208bf5f534d31e26e897711"},
{:oban_web, "~> 2.11"},
{:gettext, "~> 0.20"},
{:bcrypt_elixir, "~> 2.2"},
{:trailing_format_plug, "~> 0.0.7"},
@ -192,7 +193,7 @@ defmodule Pleroma.Mixfile do
{:majic, "~> 1.0"},
{:open_api_spex, "~> 3.16"},
{:ecto_psql_extras, "~> 0.8"},
{:vix, "~> 0.26.0"},
{:vix, "~> 0.36"},
{:elixir_make, "~> 0.7.7", override: true},
{:blurhash, "~> 0.1.0", hex: :rinpatch_blurhash},
{:exile, "~> 0.10.0"},
@ -210,7 +211,7 @@ defmodule Pleroma.Mixfile do
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:mock, "~> 0.3.5", only: :test},
{:covertool, "~> 2.0", only: :test},
{:hackney, "~> 1.18.0", override: true},
{:hackney, "~> 1.25.0", override: true},
{:mox, "~> 1.0", only: :test},
{:websockex, "~> 0.4.3", only: :test},
{:benchee, "~> 1.0", only: :benchmark},
@ -230,7 +231,7 @@ defmodule Pleroma.Mixfile do
"ecto.rollback": ["pleroma.ecto.rollback"],
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"],
test: ["ecto.create --quiet", "ecto.migrate", "test --warnings-as-errors"],
test: ["ecto.create --quiet", "pleroma.ecto.migrate --quiet", "test --warnings-as-errors"],
docs: ["pleroma.docs", "docs"],
analyze: ["credo --strict --only=warnings,todo,fixme,consistency,readability"],
copyright: &add_copyright/1,

View file

@ -13,7 +13,7 @@
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "e7b7cc34cc16b383461b966484c297e4ec9aeef6", [ref: "e7b7cc34cc16b383461b966484c297e4ec9aeef6"]},
"castore": {:hex, :castore, "1.0.15", "8aa930c890fe18b6fe0a0cff27b27d0d4d231867897bd23ea772dee561f032a3", [:mix], [], "hexpm", "96ce4c69d7d5d7a0761420ef743e2f4096253931a3ba69e5ff8ef1844fe446d3"},
"cc_precompiler": {:hex, :cc_precompiler, "0.1.11", "8c844d0b9fb98a3edea067f94f616b3f6b29b959b6b3bf25fee94ffe34364768", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "3427232caf0835f94680e5bcf082408a70b48ad68a5f5c0b02a3bea9f3a075b9"},
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
"certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"},
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
"comeonin": {:hex, :comeonin, "5.5.1", "5113e5f3800799787de08a6e0db307133850e635d34e9fab23c70b6501669510", [:mix], [], "hexpm", "65aac8f19938145377cee73973f192c5645873dcf550a8a6b18187d17c13ccdb"},
"concurrent_limiter": {:hex, :concurrent_limiter, "0.1.1", "43ae1dc23edda1ab03dd66febc739c4ff710d047bb4d735754909f9a474ae01c", [:mix], [{:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "53968ff238c0fbb4d7ed76ddb1af0be6f3b2f77909f6796e249e737c505a16eb"},
@ -59,7 +59,7 @@
"gen_smtp": {:hex, :gen_smtp, "0.15.0", "9f51960c17769b26833b50df0b96123605a8024738b62db747fece14eb2fbfcc", [:rebar3], [], "hexpm", "29bd14a88030980849c7ed2447b8db6d6c9278a28b11a44cafe41b791205440f"},
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
"gun": {:hex, :gun, "2.2.0", "b8f6b7d417e277d4c2b0dc3c07dfdf892447b087f1cc1caff9c0f556b884e33d", [:make, :rebar3], [{:cowlib, ">= 2.15.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "76022700c64287feb4df93a1795cff6741b83fb37415c40c34c38d2a4645261a"},
"hackney": {:hex, :hackney, "1.18.2", "d7ff544ddae5e1cb49e9cf7fa4e356d7f41b283989a1c304bfc47a8cc1cf966f", [:rebar3], [{:certifi, "~> 2.12.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.4.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", "af94d5c9f97857db257090a4a10e5426ecb6f4918aa5cc666798566ae14b65fd"},
"hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.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.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.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.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"},
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
"http_signatures": {:hex, :http_signatures, "0.1.2", "ed1cc7043abcf5bb4f30d68fb7bad9d618ec1a45c4ff6c023664e78b67d9c406", [:mix], [], "hexpm", "f08aa9ac121829dae109d608d83c84b940ef2f183ae50f2dd1e9a8bc619d8be7"},
@ -95,7 +95,9 @@
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
"oban": {:hex, :oban, "2.19.4", "045adb10db1161dceb75c254782f97cdc6596e7044af456a59decb6d06da73c1", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:igniter, "~> 0.5", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5fcc6219e6464525b808d97add17896e724131f498444a292071bf8991c99f97"},
"oban_live_dashboard": {:hex, :oban_live_dashboard, "0.1.1", "8aa4ceaf381c818f7d5c8185cc59942b8ac82ef0cf559881aacf8d3f8ac7bdd3", [:mix], [{:oban, "~> 2.15", [hex: :oban, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "~> 0.7", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}], "hexpm", "16dc4ce9c9a95aa2e655e35ed4e675652994a8def61731a18af85e230e1caa63"},
"oban_met": {:hex, :oban_met, "1.0.5", "bb633ab06448dab2ef9194f6688d33b3d07fc3f2ad793a1a08f4dfbb2cc9fe50", [:mix], [{:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}], "hexpm", "64664d50805bbfd3903aeada1f3c39634652a87844797ee400b0bcc95a28f5ea"},
"oban_plugins_lazarus": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/oban_plugins_lazarus.git", "e49fc355baaf0e435208bf5f534d31e26e897711", [ref: "e49fc355baaf0e435208bf5f534d31e26e897711"]},
"oban_web": {:hex, :oban_web, "2.11.6", "53933cb4253c4d9f1098ee311c06f07935259f0e564dcf2d66bae4cc98e317fe", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:oban, "~> 2.19", [hex: :oban, repo: "hexpm", optional: false]}, {:oban_met, "~> 1.0", [hex: :oban_met, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}], "hexpm", "576d94b705688c313694c2c114ca21aa0f8f2ad1b9ca45c052c5ba316d3e8d10"},
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
"open_api_spex": {:hex, :open_api_spex, "3.22.0", "fbf90dc82681dc042a4ee79853c8e989efbba73d9e87439085daf849bbf8bc20", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0 or ~> 6.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "dd751ddbdd709bb4a5313e9a24530da6e66594773c7242a0c2592cbd9f589063"},
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
@ -105,12 +107,12 @@
"phoenix_html": {:hex, :phoenix_html, "3.3.4", "42a09fc443bbc1da37e372a5c8e6755d046f22b9b11343bf885067357da21cb3", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0249d3abec3714aff3415e7ee3d9786cb325be3151e6c4b3021502c585bf53fb"},
"phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.8.7", "405880012cb4b706f26dd1c6349125bfc903fb9e44d1ea668adaf4e04d4884b7", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:ecto_sqlite3_extras, "~> 1.1.7 or ~> 1.2.0", [hex: :ecto_sqlite3_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.19 or ~> 1.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "3a8625cab39ec261d48a13b7468dc619c0ede099601b084e343968309bd4d7d7"},
"phoenix_live_reload": {:hex, :phoenix_live_reload, "1.3.3", "3a53772a6118d5679bf50fc1670505a290e32a1d195df9e069d8c53ab040c054", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "766796676e5f558dbae5d1bdb066849673e956005e3730dfd5affd7a6da4abac"},
"phoenix_live_view": {:hex, :phoenix_live_view, "0.19.5", "6e730595e8e9b8c5da230a814e557768828fd8dfeeb90377d2d8dbb52d4ec00a", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b2eaa0dd3cfb9bd7fb949b88217df9f25aed915e986a28ad5c8a0d054e7ca9d3"},
"phoenix_live_view": {:hex, :phoenix_live_view, "1.1.19", "c95e9acbc374fb796ee3e24bfecc8213123c74d9f9e45667ca40bb0a4d242953", [:mix], [{:igniter, ">= 0.6.16 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:lazy_html, "~> 0.1.0", [hex: :lazy_html, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0 or ~> 1.8.0-rc", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "d5ad357d6b21562a5b431f0ad09dfe76db9ce5648c6949f1aac334c8c4455d32"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_swoosh": {:hex, :phoenix_swoosh, "1.2.1", "b74ccaa8046fbc388a62134360ee7d9742d5a8ae74063f34eb050279de7a99e1", [:mix], [{:finch, "~> 0.8", [hex: :finch, repo: "hexpm", optional: true]}, {:hackney, "~> 1.10", [hex: :hackney, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6", [hex: :phoenix, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_view, "~> 1.0 or ~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:swoosh, "~> 1.5", [hex: :swoosh, repo: "hexpm", optional: false]}], "hexpm", "4000eeba3f9d7d1a6bf56d2bd56733d5cadf41a7f0d8ffe5bb67e7d667e204a2"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"phoenix_view": {:hex, :phoenix_view, "2.0.4", "b45c9d9cf15b3a1af5fb555c674b525391b6a1fe975f040fb4d913397b31abf4", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}], "hexpm", "4e992022ce14f31fe57335db27a28154afcc94e9983266835bb3040243eb620b"},
"plug": {:hex, :plug, "1.18.1", "5067f26f7745b7e31bc3368bc1a2b818b9779faa959b49c934c17730efc911cf", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "57a57db70df2b422b564437d2d33cf8d33cd16339c1edb190cd11b1a3a546cc2"},
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
"plug_cowboy": {:hex, :plug_cowboy, "2.7.4", "729c752d17cf364e2b8da5bdb34fb5804f56251e88bb602aff48ae0bd8673d11", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9b85632bd7012615bae0a5d70084deb1b25d2bcbb32cab82d1e9a1e023168aa3"},
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "79fd4fcf34d110605c26560cbae8f23c603ec4158c08298bd4360fdea90bb5cf"},
@ -150,7 +152,7 @@
"ueberauth": {:hex, :ueberauth, "0.10.8", "ba78fbcbb27d811a6cd06ad851793aaf7d27c3b30c9e95349c2c362b344cd8f0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "f2d3172e52821375bccb8460e5fa5cb91cfd60b19b636b6e57e9759b6f8c10c1"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"},
"unsafe": {:hex, :unsafe, "1.0.2", "23c6be12f6c1605364801f4b47007c0c159497d0446ad378b5cf05f1855c0581", [:mix], [], "hexpm", "b485231683c3ab01a9cd44cb4a79f152c6f3bb87358439c6f68791b85c2df675"},
"vix": {:hex, :vix, "0.26.0", "027f10b6969b759318be84bd0bd8c88af877445e4e41cf96a0460392cea5399c", [:make, :mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:cc_precompiler, "~> 0.1.4 or ~> 0.2", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7.3 or ~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}], "hexpm", "71b0a79ae7f199cacfc8e679b0e4ba25ee47dc02e182c5b9097efb29fbe14efd"},
"vix": {:hex, :vix, "0.36.0", "3132dc065beda06dab1895a53d8c852d8e6a5bbca375c609435e968b1290e113", [:make, :mix], [{:cc_precompiler, "~> 0.1.4 or ~> 0.2", [hex: :cc_precompiler, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.7.3 or ~> 0.8", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}], "hexpm", "92f912b4e90c453f92942742105bcdb367ad53406759da251bd2e587e33f4134"},
"web_push_encryption": {:hex, :web_push_encryption, "0.3.1", "76d0e7375142dfee67391e7690e89f92578889cbcf2879377900b5620ee4708d", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.1", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "4f82b2e57622fb9337559058e8797cb0df7e7c9790793bdc4e40bc895f70e2a2"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
"websock_adapter": {:hex, :websock_adapter, "0.5.8", "3b97dc94e407e2d1fc666b2fb9acf6be81a1798a2602294aac000260a7c4a47d", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "315b9a1865552212b5f35140ad194e67ce31af45bcee443d4ecb96b5fd3f3782"},

18
test/fixtures/server.pem vendored Normal file
View file

@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIICpDCCAYwCCQC0vCQAnSoGdzANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
b2NhbGhvc3QwHhcNMjYwMTE2MTY1ODE5WhcNMzYwMTE0MTY1ODE5WjAUMRIwEAYD
VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCq
dZ4O2upZqwIo1eK5KrW1IIsjkfsFK8hE7Llh+4axcesiUKot0ib1CUhRSYiL1DLO
CIYQOw8IKQDVSC4JWAX9SsnX4W8dwexMQuSQG7/IKX2auC1bNNySFvoqM6Gq3GL9
MqBFonZGXDPZu8fmxsI/2p9+2GK13F+HXgoLlXSCoO3XELJaBmjv29tgxxWRxCiH
m4u0briSxgUEx+CctpKPvGDmLaoIOIhjtuoG6OjkeWUOp6jDcteazO23VxPyF5cS
NbRJgm8AckrTQ6wbWSnhyqF8rPEsIc0ZAlUdDEs5fL3sjugc566FvE+GOkZIEyDD
tgWbc4Ne+Kp/nnt6oVxpAgMBAAEwDQYJKoZIhvcNAQELBQADggEBADv+J1DTok8V
MKVKo0hsRnHTeJQ2+EIgOspuYlEzez3PysOZH6diAQxO2lzuo9LKxP3hnmw17XO/
P2oCzYyb9/P58VY/gr4UDIfuhgcE0cVfdsRhVId/I2FW6VP2f5q1TGbDUxSsVIlG
6hufn1aLBu90LtEbDkHqbnD05yYPwdqzWg4TrOXbX+jBhQrXJJdB3W7KTgozjRQw
F7+/2IyXoxXuxcwQBQlYhUbvGlsFqFpP/6cz2al5i5pNUkiNaSYwlRmuwa7zoTft
tHf57dhfXIpXET2BaJM6DSjDOOG/QleRXkvkTI5J21q+Bo+XnOzo19p4cZKJpTFC
SNgrftyNh3k=
-----END CERTIFICATE-----

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.PleromaTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false
import Mix.Pleroma
setup_all do

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.AppTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
setup_all do
Mix.shell(Mix.Shell.Process)

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.DatabaseTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity

View file

@ -16,6 +16,14 @@ defmodule Pleroma.HTTP.AdapterHelper.HackneyTest do
describe "options/2" do
setup do: clear_config([:http, :adapter], a: 1, b: 2)
test "uses redirect-safe defaults", %{uri: uri} do
opts = Hackney.options([], uri)
assert opts[:follow_redirect] == false
assert opts[:force_redirect] == false
assert opts[:with_body] == true
end
test "add proxy and opts from config", %{uri: uri} do
opts = Hackney.options([proxy: "localhost:8123"], uri)

View file

@ -0,0 +1,355 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.HackneyFollowRedirectRegressionTest do
use ExUnit.Case, async: false
setup do
{:ok, _} = Application.ensure_all_started(:hackney)
{:ok, tls_server} = start_tls_redirect_server()
{:ok, proxy} = start_connect_proxy()
on_exit(fn ->
stop_connect_proxy(proxy)
stop_tls_redirect_server(tls_server)
end)
{:ok, tls_server: tls_server, proxy: proxy}
end
test "hackney follow_redirect crashes behind CONNECT proxy on relative redirects", %{
tls_server: tls_server,
proxy: proxy
} do
url = "#{tls_server.base_url}/redirect"
opts = [
pool: :media,
proxy: proxy.proxy_url,
insecure: true,
connect_timeout: 1_000,
recv_timeout: 1_000,
follow_redirect: true,
force_redirect: true
]
{pid, ref} = spawn_monitor(fn -> :hackney.request(:get, url, [], <<>>, opts) end)
assert_receive {:DOWN, ^ref, :process, ^pid, reason}, 5_000
assert match?({%FunctionClauseError{}, _}, reason) or match?(%FunctionClauseError{}, reason) or
match?({:function_clause, _}, reason)
end
test "redirects work via proxy when hackney follow_redirect is disabled", %{
tls_server: tls_server,
proxy: proxy
} do
url = "#{tls_server.base_url}/redirect"
adapter_opts = [
pool: :media,
proxy: proxy.proxy_url,
insecure: true,
connect_timeout: 1_000,
recv_timeout: 1_000,
follow_redirect: false,
force_redirect: false,
with_body: true
]
client = Tesla.client([Tesla.Middleware.FollowRedirects], Tesla.Adapter.Hackney)
assert {:ok, %Tesla.Env{status: 200, body: "ok"}} =
Tesla.request(client, method: :get, url: url, opts: [adapter: adapter_opts])
end
test "reverse proxy hackney client follows redirects via proxy without crashing", %{
tls_server: tls_server,
proxy: proxy
} do
url = "#{tls_server.base_url}/redirect"
opts = [
pool: :media,
proxy: proxy.proxy_url,
insecure: true,
connect_timeout: 1_000,
recv_timeout: 1_000,
follow_redirect: true
]
assert {:ok, 200, _headers, ref} =
Pleroma.ReverseProxy.Client.Hackney.request(:get, url, [], "", opts)
assert collect_body(ref) == "ok"
Pleroma.ReverseProxy.Client.Hackney.close(ref)
end
defp collect_body(ref, acc \\ "") do
case Pleroma.ReverseProxy.Client.Hackney.stream_body(ref) do
:done -> acc
{:ok, data, _ref} -> collect_body(ref, acc <> data)
{:error, error} -> flunk("stream_body failed: #{inspect(error)}")
end
end
defp start_tls_redirect_server do
certfile = Path.expand("../../fixtures/server.pem", __DIR__)
keyfile = Path.expand("../../fixtures/private_key.pem", __DIR__)
{:ok, listener} =
:ssl.listen(0, [
:binary,
certfile: certfile,
keyfile: keyfile,
reuseaddr: true,
active: false,
packet: :raw,
ip: {127, 0, 0, 1}
])
{:ok, {{127, 0, 0, 1}, port}} = :ssl.sockname(listener)
{:ok, acceptor} =
Task.start_link(fn ->
accept_tls_loop(listener)
end)
{:ok, %{listener: listener, acceptor: acceptor, base_url: "https://127.0.0.1:#{port}"}}
end
defp stop_tls_redirect_server(%{listener: listener, acceptor: acceptor}) do
:ok = :ssl.close(listener)
if Process.alive?(acceptor) do
Process.exit(acceptor, :normal)
end
end
defp accept_tls_loop(listener) do
case :ssl.transport_accept(listener) do
{:ok, socket} ->
_ = Task.start(fn -> serve_tls(socket) end)
accept_tls_loop(listener)
{:error, :closed} ->
:ok
{:error, _reason} ->
:ok
end
end
defp serve_tls(tcp_socket) do
with {:ok, ssl_socket} <- :ssl.handshake(tcp_socket, 2_000),
{:ok, data} <- recv_ssl_headers(ssl_socket),
{:ok, path} <- parse_path(data) do
case path do
"/redirect" ->
send_ssl_response(ssl_socket, 302, "Found", [{"Location", "/final"}], "")
"/final" ->
send_ssl_response(ssl_socket, 200, "OK", [], "ok")
_ ->
send_ssl_response(ssl_socket, 404, "Not Found", [], "not found")
end
:ssl.close(ssl_socket)
else
_ ->
_ = :gen_tcp.close(tcp_socket)
:ok
end
end
defp recv_ssl_headers(socket, acc \\ <<>>) do
case :ssl.recv(socket, 0, 1_000) do
{:ok, data} ->
acc = acc <> data
if :binary.match(acc, "\r\n\r\n") != :nomatch do
{:ok, acc}
else
if byte_size(acc) > 8_192 do
{:error, :too_large}
else
recv_ssl_headers(socket, acc)
end
end
{:error, _} = error ->
error
end
end
defp send_ssl_response(socket, status, reason, headers, body) do
base_headers =
[
{"Content-Length", Integer.to_string(byte_size(body))},
{"Connection", "close"}
] ++ headers
iodata =
[
"HTTP/1.1 ",
Integer.to_string(status),
" ",
reason,
"\r\n",
Enum.map(base_headers, fn {k, v} -> [k, ": ", v, "\r\n"] end),
"\r\n",
body
]
:ssl.send(socket, iodata)
end
defp start_connect_proxy do
{:ok, listener} =
:gen_tcp.listen(0, [
:binary,
active: false,
packet: :raw,
reuseaddr: true,
ip: {127, 0, 0, 1}
])
{:ok, {{127, 0, 0, 1}, port}} = :inet.sockname(listener)
{:ok, acceptor} =
Task.start_link(fn ->
accept_proxy_loop(listener)
end)
{:ok, %{listener: listener, acceptor: acceptor, proxy_url: "127.0.0.1:#{port}"}}
end
defp stop_connect_proxy(%{listener: listener, acceptor: acceptor}) do
:ok = :gen_tcp.close(listener)
if Process.alive?(acceptor) do
Process.exit(acceptor, :normal)
end
end
defp accept_proxy_loop(listener) do
case :gen_tcp.accept(listener) do
{:ok, socket} ->
_ = Task.start(fn -> serve_proxy(socket) end)
accept_proxy_loop(listener)
{:error, :closed} ->
:ok
{:error, _reason} ->
:ok
end
end
defp serve_proxy(client_socket) do
with {:ok, {headers, rest}} <- recv_tcp_headers(client_socket),
{:ok, {host, port}} <- parse_connect(headers),
{:ok, upstream_socket} <- connect_upstream(host, port) do
:gen_tcp.send(client_socket, "HTTP/1.1 200 Connection established\r\n\r\n")
if rest != <<>> do
:gen_tcp.send(upstream_socket, rest)
end
tunnel(client_socket, upstream_socket)
else
_ ->
:gen_tcp.close(client_socket)
:ok
end
end
defp tunnel(client_socket, upstream_socket) do
parent = self()
_ = spawn_link(fn -> forward(client_socket, upstream_socket, parent) end)
_ = spawn_link(fn -> forward(upstream_socket, client_socket, parent) end)
receive do
:tunnel_closed -> :ok
after
10_000 -> :ok
end
:gen_tcp.close(client_socket)
:gen_tcp.close(upstream_socket)
end
defp forward(from_socket, to_socket, parent) do
case :gen_tcp.recv(from_socket, 0, 10_000) do
{:ok, data} ->
_ = :gen_tcp.send(to_socket, data)
forward(from_socket, to_socket, parent)
{:error, _reason} ->
send(parent, :tunnel_closed)
:ok
end
end
defp recv_tcp_headers(socket, acc \\ <<>>) do
case :gen_tcp.recv(socket, 0, 1_000) do
{:ok, data} ->
acc = acc <> data
case :binary.match(acc, "\r\n\r\n") do
:nomatch ->
if byte_size(acc) > 8_192 do
{:error, :too_large}
else
recv_tcp_headers(socket, acc)
end
{idx, _len} ->
split_at = idx + 4
<<headers::binary-size(split_at), rest::binary>> = acc
{:ok, {headers, rest}}
end
{:error, _} = error ->
error
end
end
defp parse_connect(data) do
with [request_line | _] <- String.split(data, "\r\n", trim: true),
["CONNECT", hostport | _] <- String.split(request_line, " ", parts: 3),
[host, port_str] <- String.split(hostport, ":", parts: 2),
{port, ""} <- Integer.parse(port_str) do
{:ok, {host, port}}
else
_ -> {:error, :invalid_connect}
end
end
defp connect_upstream(host, port) do
address =
case :inet.parse_address(String.to_charlist(host)) do
{:ok, ip} -> ip
{:error, _} -> String.to_charlist(host)
end
:gen_tcp.connect(address, port, [:binary, active: false, packet: :raw], 1_000)
end
defp parse_path(data) do
case String.split(data, "\r\n", parts: 2) do
[request_line | _] ->
case String.split(request_line, " ") do
[_method, path, _protocol] -> {:ok, path}
_ -> {:error, :invalid_request}
end
_ ->
{:error, :invalid_request}
end
end
end

View file

@ -0,0 +1,151 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.HackneyRedirectRegressionTest do
use ExUnit.Case, async: false
alias Pleroma.HTTP.AdapterHelper.Hackney, as: HackneyAdapterHelper
setup do
{:ok, _} = Application.ensure_all_started(:hackney)
{:ok, server} = start_server()
on_exit(fn -> stop_server(server) end)
{:ok, server: server}
end
test "pooled redirects work with follow_redirect disabled", %{server: server} do
url = "#{server.base_url}/redirect"
uri = URI.parse(url)
adapter_opts =
HackneyAdapterHelper.options(
[pool: :media, follow_redirect: false, no_proxy_env: true],
uri
)
client = Tesla.client([Tesla.Middleware.FollowRedirects], Tesla.Adapter.Hackney)
assert {:ok, %Tesla.Env{status: 200, body: "ok"}} =
Tesla.request(client, method: :get, url: url, opts: [adapter: adapter_opts])
end
defp start_server do
{:ok, listener} =
:gen_tcp.listen(0, [
:binary,
active: false,
packet: :raw,
reuseaddr: true,
ip: {127, 0, 0, 1}
])
{:ok, {{127, 0, 0, 1}, port}} = :inet.sockname(listener)
{:ok, acceptor} =
Task.start_link(fn ->
accept_loop(listener)
end)
{:ok, %{listener: listener, acceptor: acceptor, base_url: "http://127.0.0.1:#{port}"}}
end
defp stop_server(%{listener: listener, acceptor: acceptor}) do
:ok = :gen_tcp.close(listener)
if Process.alive?(acceptor) do
Process.exit(acceptor, :normal)
end
end
defp accept_loop(listener) do
case :gen_tcp.accept(listener) do
{:ok, socket} ->
serve(socket)
accept_loop(listener)
{:error, :closed} ->
:ok
{:error, _reason} ->
:ok
end
end
defp serve(socket) do
with {:ok, data} <- recv_headers(socket),
{:ok, path} <- parse_path(data) do
case path do
"/redirect" ->
send_response(socket, 302, "Found", [{"Location", "/final"}], "")
"/final" ->
send_response(socket, 200, "OK", [], "ok")
_ ->
send_response(socket, 404, "Not Found", [], "not found")
end
else
_ -> :ok
end
:gen_tcp.close(socket)
end
defp recv_headers(socket, acc \\ <<>>) do
case :gen_tcp.recv(socket, 0, 1_000) do
{:ok, data} ->
acc = acc <> data
if :binary.match(acc, "\r\n\r\n") != :nomatch do
{:ok, acc}
else
if byte_size(acc) > 8_192 do
{:error, :too_large}
else
recv_headers(socket, acc)
end
end
{:error, _} = error ->
error
end
end
defp parse_path(data) do
case String.split(data, "\r\n", parts: 2) do
[request_line | _] ->
case String.split(request_line, " ") do
[_method, path, _protocol] -> {:ok, path}
_ -> {:error, :invalid_request}
end
_ ->
{:error, :invalid_request}
end
end
defp send_response(socket, status, reason, headers, body) do
base_headers =
[
{"Content-Length", Integer.to_string(byte_size(body))},
{"Connection", "close"}
] ++ headers
iodata =
[
"HTTP/1.1 ",
Integer.to_string(status),
" ",
reason,
"\r\n",
Enum.map(base_headers, fn {k, v} -> [k, ": ", v, "\r\n"] end),
"\r\n",
body
]
:gen_tcp.send(socket, iodata)
end
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTPTest do
use ExUnit.Case, async: true
use ExUnit.Case, async: false
use Pleroma.Tests.Helpers
import Tesla.Mock

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Repo.Migrations.AutolinkerToLinkifyTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
import Pleroma.Factory
import Pleroma.Tests.Helpers
alias Pleroma.ConfigDB

View file

@ -25,8 +25,8 @@ defmodule Pleroma.Upload.Filter.Exiftool.StripLocationTest do
assert Filter.Exiftool.StripLocation.filter(upload) == {:ok, :filtered}
{exif_original, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010.#{type}"])
{exif_filtered, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010_tmp.#{type}"])
{exif_original, 0} = System.cmd("exiftool", ["-m", "test/fixtures/DSCN0010.#{type}"])
{exif_filtered, 0} = System.cmd("exiftool", ["-m", "test/fixtures/DSCN0010_tmp.#{type}"])
assert String.match?(exif_original, ~r/GPS/)
refute String.match?(exif_filtered, ~r/GPS/)

View file

@ -54,14 +54,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
setup do: clear_config([:media_proxy, :enabled], true)
test "it prefetches media proxy URIs" do
Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
{:ok, %Tesla.Env{status: 200, body: ""}}
end)
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
with_mock HTTP,
get: fn _, _, opts ->
send(self(), {:prefetch_opts, opts})
{:ok, []}
end do
MediaProxyWarmingPolicy.filter(@message)
assert called(HTTP.get(:_, :_, :_))
assert_receive {:prefetch_opts, opts}
refute Keyword.has_key?(opts, :follow_redirect)
refute Keyword.has_key?(opts, :force_redirect)
end
end
@ -81,10 +84,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
end
test "history-aware" do
Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
{:ok, %Tesla.Env{status: 200, body: ""}}
end)
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
MRF.filter_one(MediaProxyWarmingPolicy, @message_with_history)
@ -93,10 +92,6 @@ defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicyTest do
end
test "works with Updates" do
Tesla.Mock.mock(fn %{method: :get, url: "http://example.com/image.jpg"} ->
{:ok, %Tesla.Env{status: 200, body: ""}}
end)
with_mock HTTP, get: fn _, _, _ -> {:ok, []} end do
MRF.filter_one(MediaProxyWarmingPolicy, @message_with_history |> Map.put("type", "Update"))

View file

@ -1,5 +1,5 @@
defmodule Pleroma.Web.ActivityPub.MRF.RemoteReportPolicyTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
alias Pleroma.Web.ActivityPub.MRF.RemoteReportPolicy

View file

@ -1,14 +1,46 @@
defmodule Pleroma.Web.ActivityPub.Transmogrifier.EmojiTagBuildingTest do
use Pleroma.DataCase, async: true
alias Pleroma.Web.ActivityPub.Transmogrifier
test "it encodes the id to be a valid url" do
name = "hanapog"
url = "https://misskey.local.live/emojis/hana pog.png"
tag = Transmogrifier.build_emoji_tag({name, url})
tag = Pleroma.Emoji.build_emoji_tag({name, url})
assert tag["id"] == "https://misskey.local.live/emojis/hana%20pog.png"
end
test "it does not double-encode already encoded urls" do
name = "hanapog"
url = "https://misskey.local.live/emojis/hana%20pog.png"
tag = Pleroma.Emoji.build_emoji_tag({name, url})
assert tag["id"] == url
end
test "it encodes disallowed path characters" do
name = "hanapog"
url = "https://example.com/emojis/hana[pog].png"
tag = Pleroma.Emoji.build_emoji_tag({name, url})
assert tag["id"] == "https://example.com/emojis/hana%5Bpog%5D.png"
end
test "local_url does not decode percent in filenames" do
url = Pleroma.Emoji.local_url("/emoji/hana%20pog.png")
assert url == Pleroma.Web.Endpoint.url() <> "/emoji/hana%2520pog.png"
tag = Pleroma.Emoji.build_emoji_tag({"hanapog", url})
assert tag["id"] == url
end
test "local_url encodes question marks in filenames" do
url = Pleroma.Emoji.local_url("/emoji/file?name.png")
assert url == Pleroma.Web.Endpoint.url() <> "/emoji/file%3Fname.png"
end
end

View file

@ -759,6 +759,22 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
)
end
test "EmojiReact custom emoji urls are URI encoded" do
user = insert(:user, local: true)
note_activity = insert(:note_activity)
{:ok, react_activity} = CommonAPI.react_with_emoji(note_activity.id, user, ":dinosaur:")
{:ok, data} = Transmogrifier.prepare_outgoing(react_activity.data)
assert length(data["tag"]) == 1
tag = List.first(data["tag"])
url = tag["icon"]["url"]
assert url == "http://localhost:4001/emoji/dino%20walking.gif"
assert tag["id"] == "http://localhost:4001/emoji/dino%20walking.gif"
end
test "it prepares a quote post" do
user = insert(:user)

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.UtilsTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Repo

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.UserViewTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
import Pleroma.Factory
alias Pleroma.User

View file

@ -57,6 +57,28 @@ defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do
} = response
end
test "success with redirect_uris array", %{conn: conn} do
base_url = Endpoint.url()
app_name = "Trusted app"
response =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/pleroma/admin/oauth_app", %{
name: app_name,
redirect_uris: [base_url]
})
|> json_response_and_validate_schema(200)
assert %{
"client_id" => _,
"client_secret" => _,
"name" => ^app_name,
"redirect_uri" => ^base_url,
"trusted" => false
} = response
end
test "with trusted", %{conn: conn} do
base_url = Endpoint.url()
app_name = "Trusted app"

View file

@ -77,6 +77,10 @@ defmodule Pleroma.Web.FallbackTest do
assert redirected_to(get(conn, "/pleroma/admin")) =~ "/pleroma/admin/"
end
test "GET /phoenix/live_dashboard -> /pleroma/live_dashboard", %{conn: conn} do
assert redirected_to(get(conn, "/phoenix/live_dashboard")) =~ "/pleroma/live_dashboard"
end
test "OPTIONS /*path", %{conn: conn} do
assert conn
|> options("/foo")

View file

@ -1901,7 +1901,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
{:ok, _user_relationships} = User.mute(user, other_user1)
{:ok, _user_relationships} = User.mute(user, other_user2)
{:ok, _user_relationships} = User.mute(user, other_user3)
{:ok, _user_relationships} = User.mute(user, other_user3, %{duration: 24 * 60 * 60})
date =
DateTime.utc_now()
|> DateTime.add(24 * 60 * 60)
|> DateTime.truncate(:second)
|> DateTime.to_iso8601()
result =
conn
@ -1937,6 +1943,17 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|> json_response_and_validate_schema(200)
assert [%{"id" => ^id3}] = result
result =
conn
|> get("/api/v1/mutes")
|> json_response_and_validate_schema(200)
assert [
%{"id" => ^id3, "mute_expires_at" => ^date},
%{"id" => ^id2, "mute_expires_at" => nil},
%{"id" => ^id1, "mute_expires_at" => nil}
] = result
end
test "list of mutes with with_relationships parameter" do
@ -1951,20 +1968,44 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
{:ok, _} = User.mute(user, other_user1)
{:ok, _} = User.mute(user, other_user2)
{:ok, _} = User.mute(user, other_user3)
{:ok, _} = User.mute(user, other_user3, %{duration: 24 * 60 * 60})
date =
DateTime.utc_now()
|> DateTime.add(24 * 60 * 60)
|> DateTime.truncate(:second)
|> DateTime.to_iso8601()
assert [
%{
"id" => ^id3,
"pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
"pleroma" => %{
"relationship" => %{
"muting" => true,
"mute_expires_at" => ^date,
"followed_by" => true
}
}
},
%{
"id" => ^id2,
"pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
"pleroma" => %{
"relationship" => %{
"muting" => true,
"mute_expires_at" => nil,
"followed_by" => true
}
}
},
%{
"id" => ^id1,
"pleroma" => %{"relationship" => %{"muting" => true, "followed_by" => true}}
"pleroma" => %{
"relationship" => %{
"muting" => true,
"mute_expires_at" => nil,
"followed_by" => true
}
}
}
] =
conn
@ -1980,7 +2021,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
{:ok, _user_relationship} = User.block(user, other_user1)
{:ok, _user_relationship} = User.block(user, other_user3)
{:ok, _user_relationship} = User.block(user, other_user2)
{:ok, _user_relationship} = User.block(user, other_user2, %{duration: 24 * 60 * 60})
date =
DateTime.utc_now()
|> DateTime.add(24 * 60 * 60)
|> DateTime.truncate(:second)
|> DateTime.to_iso8601()
result =
conn
@ -2045,6 +2092,18 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|> json_response_and_validate_schema(200)
assert [%{"id" => ^id1}] = result
result =
conn
|> assign(:user, user)
|> get("api/v1/blocks")
|> json_response_and_validate_schema(200)
assert [
%{"id" => ^id3, "block_expires_at" => nil},
%{"id" => ^id2, "block_expires_at" => ^date},
%{"id" => ^id1, "block_expires_at" => nil}
] = result
end
test "list of blocks with with_relationships parameter" do
@ -2059,20 +2118,44 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
{:ok, _} = User.block(user, other_user1)
{:ok, _} = User.block(user, other_user2)
{:ok, _} = User.block(user, other_user3)
{:ok, _} = User.block(user, other_user3, %{duration: 24 * 60 * 60})
date =
DateTime.utc_now()
|> DateTime.add(24 * 60 * 60)
|> DateTime.truncate(:second)
|> DateTime.to_iso8601()
assert [
%{
"id" => ^id3,
"pleroma" => %{"relationship" => %{"blocking" => true, "followed_by" => false}}
"pleroma" => %{
"relationship" => %{
"blocking" => true,
"block_expires_at" => ^date,
"followed_by" => false
}
}
},
%{
"id" => ^id2,
"pleroma" => %{"relationship" => %{"blocking" => true, "followed_by" => false}}
"pleroma" => %{
"relationship" => %{
"blocking" => true,
"block_expires_at" => nil,
"followed_by" => false
}
}
},
%{
"id" => ^id1,
"pleroma" => %{"relationship" => %{"blocking" => true, "followed_by" => false}}
"pleroma" => %{
"relationship" => %{
"blocking" => true,
"block_expires_at" => nil,
"followed_by" => false
}
}
}
] =
conn

View file

@ -61,6 +61,33 @@ defmodule Pleroma.Web.MastodonAPI.AppControllerTest do
assert app.user_id == nil
end
test "creates an oauth app with redirect_uris array", %{conn: conn} do
app_attrs = build(:oauth_app)
conn =
conn
|> put_req_header("content-type", "application/json")
|> post("/api/v1/apps", %{
client_name: app_attrs.client_name,
redirect_uris: [app_attrs.redirect_uris]
})
[app] = Repo.all(App)
expected = %{
"name" => app.client_name,
"website" => app.website,
"client_id" => app.client_id,
"client_secret" => app.client_secret,
"id" => app.id |> to_string(),
"redirect_uri" => app.redirect_uris,
"vapid_key" => Push.vapid_config() |> Keyword.get(:public_key)
}
assert expected == json_response_and_validate_schema(conn, 200)
assert app.user_id == nil
end
test "creates an oauth app with a user", %{conn: conn} do
user = insert(:user)
app_attrs = build(:oauth_app)

View file

@ -10,6 +10,11 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do
import Pleroma.Factory
defp extract_next_link_header(header) do
[_, next_link] = Regex.run(~r{<(?<next_link>.*)>; rel="next"}, header)
next_link
end
describe "locked accounts" do
setup do
user = insert(:user, is_locked: true)
@ -31,6 +36,23 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestControllerTest do
assert to_string(other_user.id) == relationship["id"]
end
test "/api/v1/follow_requests paginates", %{user: user, conn: conn} do
for _ <- 1..21 do
other_user = insert(:user)
{:ok, _, _, _activity} = CommonAPI.follow(other_user, user)
{:ok, _, _} = User.follow(other_user, user, :follow_pending)
end
conn = get(conn, "/api/v1/follow_requests")
assert length(json_response_and_validate_schema(conn, 200)) == 20
assert [link_header] = get_resp_header(conn, "link")
assert link_header =~ "rel=\"next\""
next_link = extract_next_link_header(link_header)
assert next_link =~ "/api/v1/follow_requests"
conn = get(conn, next_link)
assert length(json_response_and_validate_schema(conn, 200)) == 1
end
test "/api/v1/follow_requests/:id/authorize works", %{user: user, conn: conn} do
other_user = insert(:user)

View file

@ -153,6 +153,49 @@ defmodule Pleroma.Web.MastodonAPI.InstanceControllerTest do
] = result["rules"]
end
describe "instance domain blocks" do
setup do
clear_config([:mrf_simple, :reject], [{"fediverse.pl", "uses pl-fe"}])
end
test "get instance domain blocks", %{conn: conn} do
conn = get(conn, "/api/v1/instance/domain_blocks")
assert [
%{
"comment" => "uses pl-fe",
"digest" => "55e3f44aefe7eb022d3b1daaf7396cabf7f181bf6093c8ea841e30c9fc7d8226",
"domain" => "fediverse.pl",
"severity" => "suspend"
}
] == json_response_and_validate_schema(conn, 200)
end
test "omits comment field if comment is empty", %{conn: conn} do
clear_config([:mrf_simple, :reject], ["fediverse.pl"])
conn = get(conn, "/api/v1/instance/domain_blocks")
assert [
%{
"digest" => "55e3f44aefe7eb022d3b1daaf7396cabf7f181bf6093c8ea841e30c9fc7d8226",
"domain" => "fediverse.pl",
"severity" => "suspend"
} = domain_block
] = json_response_and_validate_schema(conn, 200)
refute Map.has_key?(domain_block, "comment")
end
test "returns empty array if mrf transparency is disabled", %{conn: conn} do
clear_config([:mrf, :transparency], false)
conn = get(conn, "/api/v1/instance/domain_blocks")
assert [] == json_response_and_validate_schema(conn, 200)
end
end
test "translation languages matrix", %{conn: conn} do
clear_config([Pleroma.Language.Translation, :provider], TranslationMock)

View file

@ -105,6 +105,25 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
assert expected == AccountView.render("show.json", %{user: user, skip_visibility_check: true})
end
test "encodes emoji urls in the emojis field" do
user =
insert(:user,
name: ":brackets: :percent:",
emoji: %{
"brackets" => "/emoji/hana[pog].png",
"percent" => "/emoji/hana%20pog.png"
}
)
%{emojis: emojis} =
AccountView.render("show.json", %{user: user, skip_visibility_check: true})
emoji_urls = Map.new(emojis, &{&1.shortcode, &1.url})
assert emoji_urls["brackets"] == "/emoji/hana%5Bpog%5D.png"
assert emoji_urls["percent"] == "/emoji/hana%2520pog.png"
end
describe "roles and privileges" do
setup do
clear_config([:instance, :moderator_privileges], [:cofe, :only_moderator])
@ -420,8 +439,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
following: false,
followed_by: false,
blocking: false,
block_expires_at: nil,
blocked_by: false,
muting: false,
mute_expires_at: nil,
muting_notifications: false,
subscribing: false,
notifying: false,
@ -517,6 +538,53 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
test_relationship_rendering(user, other_user, expected)
end
test "represent a relationship for the blocking and blocked user with expiry" do
user = insert(:user)
other_user = insert(:user)
date = DateTime.utc_now() |> DateTime.add(24 * 60 * 60) |> DateTime.truncate(:second)
{:ok, user, other_user} = User.follow(user, other_user)
{:ok, _subscription} = User.subscribe(user, other_user)
{:ok, _user_relationship} = User.block(user, other_user, %{duration: 24 * 60 * 60})
{:ok, _user_relationship} = User.block(other_user, user)
expected =
Map.merge(
@blank_response,
%{
following: false,
blocking: true,
block_expires_at: date,
blocked_by: true,
id: to_string(other_user.id)
}
)
test_relationship_rendering(user, other_user, expected)
end
test "represent a relationship for the muting user with expiry" do
user = insert(:user)
other_user = insert(:user)
date = DateTime.utc_now() |> DateTime.add(24 * 60 * 60) |> DateTime.truncate(:second)
{:ok, _user_relationship} =
User.mute(user, other_user, %{notifications: true, duration: 24 * 60 * 60})
expected =
Map.merge(
@blank_response,
%{
muting: true,
mute_expires_at: date,
muting_notifications: true,
id: to_string(other_user.id)
}
)
test_relationship_rendering(user, other_user, expected)
end
test "represent a relationship for the user blocking a domain" do
user = insert(:user)
other_user = insert(:user, ap_id: "https://bad.site/users/other_user")
@ -837,12 +905,39 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
User.mute(user, other_user, %{notifications: true, duration: 24 * 60 * 60})
%{
pleroma: %{
relationship: %{
mute_expires_at: mute_expires_at
}
},
mute_expires_at: mute_expires_at
} = AccountView.render("show.json", %{user: other_user, for: user, mutes: true})
} = AccountView.render("show.json", %{user: other_user, for: user, embed_relationships: true})
assert DateTime.diff(
mute_expires_at,
DateTime.utc_now() |> DateTime.add(24 * 60 * 60)
) in -3..3
end
test "renders block expiration date" do
user = insert(:user)
other_user = insert(:user)
{:ok, _user_relationships} =
User.block(user, other_user, %{duration: 24 * 60 * 60})
%{
pleroma: %{
relationship: %{
block_expires_at: block_expires_at
}
},
block_expires_at: block_expires_at
} = AccountView.render("show.json", %{user: other_user, for: user, embed_relationships: true})
assert DateTime.diff(
block_expires_at,
DateTime.utc_now() |> DateTime.add(24 * 60 * 60)
) in -3..3
end
end

View file

@ -219,7 +219,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
data: %{
"reactions" => [
["👍", [user.ap_id], nil],
["dinosaur", [user.ap_id], "http://localhost:4001/emoji/dino walking.gif"]
["dinosaur", [user.ap_id], "http://localhost:4001/emoji/dino%20walking.gif"]
]
}
)
@ -243,7 +243,7 @@ defmodule Pleroma.Web.MastodonAPI.NotificationViewTest do
account: AccountView.render("show.json", %{user: other_user, for: user}),
status: StatusView.render("show.json", %{activity: activity, for: user}),
created_at: Utils.to_masto_date(notification.inserted_at),
emoji_url: "http://localhost:4001/emoji/dino walking.gif"
emoji_url: "http://localhost:4001/emoji/dino%20walking.gif"
}
test_notifications_rendering([notification], user, [expected])

View file

@ -54,7 +54,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
count: 2,
me: false,
name: "dinosaur",
url: "http://localhost:4001/emoji/dino walking.gif",
url: "http://localhost:4001/emoji/dino%20walking.gif",
account_ids: [other_user.id, user.id]
},
%{name: "🍵", count: 1, me: false, url: nil, account_ids: [third_user.id]}
@ -70,7 +70,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
count: 2,
me: true,
name: "dinosaur",
url: "http://localhost:4001/emoji/dino walking.gif",
url: "http://localhost:4001/emoji/dino%20walking.gif",
account_ids: [other_user.id, user.id]
},
%{name: "🍵", count: 1, me: false, url: nil, account_ids: [third_user.id]}

View file

@ -100,7 +100,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiReactionControllerTest do
"name" => "dinosaur",
"count" => 1,
"me" => true,
"url" => "http://localhost:4001/emoji/dino walking.gif",
"url" => "http://localhost:4001/emoji/dino%20walking.gif",
"account_ids" => [other_user.id]
}
]

View file

@ -4,7 +4,7 @@
defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
alias Pleroma.NullCache
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
alias Pleroma.Chat
alias Pleroma.Chat.MessageReference
@ -18,6 +18,11 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
import Mox
import Pleroma.Factory
setup do
Mox.stub_with(Pleroma.CachexMock, Pleroma.NullCache)
:ok
end
setup do: clear_config([:rich_media, :enabled], true)
test "it displays a chat message" do

View file

@ -106,7 +106,6 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
"manifest.json",
"auth",
"proxy",
"phoenix",
"test",
"user_exists",
"check_password"

View file

@ -4,7 +4,7 @@
defmodule Pleroma.Web.RichMedia.CardTest do
use Oban.Testing, repo: Pleroma.Repo
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
alias Pleroma.Tests.ObanHelpers
alias Pleroma.UnstubbedConfigMock, as: ConfigMock
@ -19,6 +19,8 @@ defmodule Pleroma.Web.RichMedia.CardTest do
setup do
mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
Mox.stub_with(Pleroma.CachexMock, Pleroma.NullCache)
ConfigMock
|> stub_with(Pleroma.Test.StaticConfig)

View file

@ -19,7 +19,12 @@ defmodule Pleroma.Web.StreamerTest do
@moduletag needs_streamer: true, capture_log: true
setup do: clear_config([:instance, :skip_thread_containment])
setup do
clear_config([:instance, :skip_thread_containment])
Mox.stub_with(Pleroma.CachexMock, Pleroma.NullCache)
:ok
end
describe "get_topic/_ (unauthenticated)" do
test "allows no stream" do

View file

@ -3,12 +3,13 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.WebFingerTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
alias Pleroma.Web.WebFinger
import Pleroma.Factory
import Tesla.Mock
setup do
Mox.stub_with(Pleroma.CachexMock, Pleroma.NullCache)
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
:ok
end

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.PublisherWorkerTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
use Oban.Testing, repo: Pleroma.Repo
import Pleroma.Factory

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.ReachabilityWorkerTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
use Oban.Testing, repo: Pleroma.Repo
import Mock

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.ReceiverWorkerTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
use Oban.Testing, repo: Pleroma.Repo
import Mock

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Workers.RemoteFetcherWorkerTest do
use Pleroma.DataCase, async: true
use Pleroma.DataCase, async: false
use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Workers.RemoteFetcherWorker