Browse Source

Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into remake-remodel-dms

1570-levenshtein-distance-user-search
lain 4 years ago
parent
commit
af6d01ec93
65 changed files with 447 additions and 155 deletions
  1. +1
    -1
      config/config.exs
  2. +17
    -1
      docs/API/admin_api.md
  3. +31
    -0
      docs/configuration/postgresql.md
  4. +2
    -2
      docs/installation/debian_based_en.md
  5. +2
    -2
      docs/installation/debian_based_jp.md
  6. +1
    -1
      docs/installation/otp_en.md
  7. +21
    -17
      lib/mix/tasks/pleroma/emoji.ex
  8. +1
    -4
      lib/pleroma/activity/queries.ex
  9. +1
    -1
      lib/pleroma/conversation.ex
  10. +1
    -1
      lib/pleroma/emoji/pack.ex
  11. +5
    -13
      lib/pleroma/notification.ex
  12. +62
    -20
      lib/pleroma/plugs/http_security_plug.ex
  13. +18
    -9
      lib/pleroma/user.ex
  14. +20
    -7
      lib/pleroma/web/activity_pub/activity_pub.ex
  15. +7
    -3
      lib/pleroma/web/activity_pub/transmogrifier.ex
  16. +5
    -4
      lib/pleroma/web/admin_api/controllers/admin_api_controller.ex
  17. +1
    -1
      lib/pleroma/web/feed/user_controller.ex
  18. +1
    -1
      lib/pleroma/web/mastodon_api/controllers/account_controller.ex
  19. +2
    -2
      lib/pleroma/web/mastodon_api/controllers/status_controller.ex
  20. +7
    -5
      lib/pleroma/web/mastodon_api/views/account_view.ex
  21. +1
    -1
      lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex
  22. +2
    -0
      lib/pleroma/workers/cron/clear_oauth_token_worker.ex
  23. +2
    -0
      lib/pleroma/workers/cron/digest_emails_worker.ex
  24. +4
    -0
      lib/pleroma/workers/cron/new_users_digest_worker.ex
  25. +2
    -0
      lib/pleroma/workers/cron/purge_expired_activities_worker.ex
  26. +7
    -0
      priv/repo/migrations/20200526144426_add_apps_indexes.exs
  27. +8
    -0
      priv/repo/migrations/20200527104138_change_notification_user_index.exs
  28. +0
    -1
      priv/static/adminfe/chunk-3384.2278f87c.css
  29. +1
    -0
      priv/static/adminfe/chunk-3384.d50ed383.css
  30. +1
    -1
      priv/static/adminfe/chunk-7e30.f2b9674a.css
  31. +1
    -0
      priv/static/adminfe/chunk-e458.6c0703cb.css
  32. +0
    -1
      priv/static/adminfe/chunk-e458.f88bafea.css
  33. +1
    -1
      priv/static/adminfe/index.html
  34. +2
    -0
      priv/static/adminfe/static/js/app.0146039c.js
  35. +1
    -0
      priv/static/adminfe/static/js/app.0146039c.js.map
  36. +0
    -2
      priv/static/adminfe/static/js/app.203f69f8.js
  37. +0
    -1
      priv/static/adminfe/static/js/app.203f69f8.js.map
  38. +0
    -2
      priv/static/adminfe/static/js/chunk-3384.458ffaf1.js
  39. +0
    -1
      priv/static/adminfe/static/js/chunk-3384.458ffaf1.js.map
  40. +2
    -0
      priv/static/adminfe/static/js/chunk-3384.b2ebeeca.js
  41. +1
    -0
      priv/static/adminfe/static/js/chunk-3384.b2ebeeca.js.map
  42. +0
    -2
      priv/static/adminfe/static/js/chunk-4011.67fb1692.js
  43. +0
    -1
      priv/static/adminfe/static/js/chunk-4011.67fb1692.js.map
  44. +2
    -0
      priv/static/adminfe/static/js/chunk-7e30.ec42e302.js
  45. +1
    -0
      priv/static/adminfe/static/js/chunk-7e30.ec42e302.js.map
  46. +0
    -2
      priv/static/adminfe/static/js/chunk-e458.4e5aad44.js
  47. +0
    -1
      priv/static/adminfe/static/js/chunk-e458.4e5aad44.js.map
  48. +2
    -0
      priv/static/adminfe/static/js/chunk-e458.bb460d81.js
  49. +1
    -0
      priv/static/adminfe/static/js/chunk-e458.bb460d81.js.map
  50. +2
    -2
      priv/static/adminfe/static/js/runtime.b08eb412.js
  51. +1
    -1
      priv/static/adminfe/static/js/runtime.b08eb412.js.map
  52. +3
    -0
      test/instance_static/local_pack/files.json
  53. +10
    -0
      test/instance_static/local_pack/manifest.json
  54. +7
    -4
      test/notification_test.exs
  55. +1
    -0
      test/plugs/authentication_plug_test.exs
  56. +1
    -1
      test/plugs/http_security_plug_test.exs
  57. +2
    -1
      test/support/factory.ex
  58. +13
    -0
      test/tasks/emoji_test.exs
  59. +20
    -0
      test/user_test.exs
  60. +30
    -0
      test/web/activity_pub/activity_pub_controller_test.exs
  61. +19
    -14
      test/web/activity_pub/transmogrifier_test.exs
  62. +31
    -4
      test/web/admin_api/controllers/admin_api_controller_test.exs
  63. +25
    -1
      test/web/feed/user_controller_test.exs
  64. +31
    -4
      test/web/mastodon_api/views/account_view_test.exs
  65. +3
    -11
      test/web/media_proxy/media_proxy_test.exs

+ 1
- 1
config/config.exs View File

@@ -274,7 +274,7 @@ config :pleroma, :markup,
config :pleroma, :frontend_configurations, config :pleroma, :frontend_configurations,
pleroma_fe: %{ pleroma_fe: %{
alwaysShowSubjectInput: true, alwaysShowSubjectInput: true,
background: "/static/aurora_borealis.jpg",
background: "/images/city.jpg",
collapseMessageWithSubject: false, collapseMessageWithSubject: false,
disableChat: false, disableChat: false,
greentext: false, greentext: false,


+ 17
- 1
docs/API/admin_api.md View File

@@ -511,7 +511,23 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- `discoverable` - `discoverable`
- `actor_type` - `actor_type`


- Response: none (code `200`)
- Response:

```json
{"status": "success"}
```

```json
{"errors":
{"actor_type": "is invalid"},
{"email": "has invalid format"},
...
}
```

```json
{"error": "Unable to update user."}
```


## `GET /api/pleroma/admin/reports` ## `GET /api/pleroma/admin/reports`




+ 31
- 0
docs/configuration/postgresql.md View File

@@ -0,0 +1,31 @@
# Optimizing your PostgreSQL performance

Pleroma performance depends to a large extent on good database performance. The default PostgreSQL settings are mostly fine, but often you can get better performance by changing a few settings.

You can use [PGTune](https://pgtune.leopard.in.ua) to get recommendations for your setup. If you do, set the "Number of Connections" field to 20, as Pleroma will only use 10 concurrent connections anyway. If you don't, it will give you advice that might even hurt your performance.

We also recommend not using the "Network Storage" option.

## Example configurations

Here are some configuration suggestions for PostgreSQL 10+.

### 1GB RAM, 1 CPU
```
shared_buffers = 256MB
effective_cache_size = 768MB
maintenance_work_mem = 64MB
work_mem = 13107kB
```

### 2GB RAM, 2 CPU
```
shared_buffers = 512MB
effective_cache_size = 1536MB
maintenance_work_mem = 128MB
work_mem = 26214kB
max_worker_processes = 2
max_parallel_workers_per_gather = 1
max_parallel_workers = 2
```


+ 2
- 2
docs/installation/debian_based_en.md View File

@@ -38,8 +38,8 @@ sudo apt install git build-essential postgresql postgresql-contrib
* Download and add the Erlang repository: * Download and add the Erlang repository:


```shell ```shell
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_1.0_all.deb
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_2.0_all.deb
``` ```


* Install Elixir and Erlang: * Install Elixir and Erlang:


+ 2
- 2
docs/installation/debian_based_jp.md View File

@@ -40,8 +40,8 @@ sudo apt install git build-essential postgresql postgresql-contrib


* Erlangのリポジトリをダウンロードおよびインストールします。 * Erlangのリポジトリをダウンロードおよびインストールします。
``` ```
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_1.0_all.deb
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
sudo dpkg -i /tmp/erlang-solutions_2.0_all.deb
``` ```


* ElixirとErlangをインストールします、 * ElixirとErlangをインストールします、


+ 1
- 1
docs/installation/otp_en.md View File

@@ -63,7 +63,7 @@ apt install postgresql-11-rum
``` ```


#### (Optional) Performance configuration #### (Optional) Performance configuration
For optimal performance, you may use [PGTune](https://pgtune.leopard.in.ua), don't forget to restart postgresql after editing the configuration
It is encouraged to check [Optimizing your PostgreSQL performance](../configuration/postgresql.md) document, for tips on PostgreSQL tuning.


```sh tab="Alpine" ```sh tab="Alpine"
rc-service postgresql restart rc-service postgresql restart


+ 21
- 17
lib/mix/tasks/pleroma/emoji.ex View File

@@ -15,7 +15,7 @@ defmodule Mix.Tasks.Pleroma.Emoji do
{options, [], []} = parse_global_opts(args) {options, [], []} = parse_global_opts(args)


url_or_path = options[:manifest] || default_manifest() url_or_path = options[:manifest] || default_manifest()
manifest = fetch_manifest(url_or_path)
manifest = fetch_and_decode(url_or_path)


Enum.each(manifest, fn {name, info} -> Enum.each(manifest, fn {name, info} ->
to_print = [ to_print = [
@@ -42,12 +42,12 @@ defmodule Mix.Tasks.Pleroma.Emoji do


url_or_path = options[:manifest] || default_manifest() url_or_path = options[:manifest] || default_manifest()


manifest = fetch_manifest(url_or_path)
manifest = fetch_and_decode(url_or_path)


for pack_name <- pack_names do for pack_name <- pack_names do
if Map.has_key?(manifest, pack_name) do if Map.has_key?(manifest, pack_name) do
pack = manifest[pack_name] pack = manifest[pack_name]
src_url = pack["src"]
src = pack["src"]


IO.puts( IO.puts(
IO.ANSI.format([ IO.ANSI.format([
@@ -57,11 +57,11 @@ defmodule Mix.Tasks.Pleroma.Emoji do
:normal, :normal,
" from ", " from ",
:underline, :underline,
src_url
src
]) ])
) )


binary_archive = Tesla.get!(client(), src_url).body
{:ok, binary_archive} = fetch(src)
archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16() archive_sha = :crypto.hash(:sha256, binary_archive) |> Base.encode16()


sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright] sha_status_text = ["SHA256 of ", :bright, pack_name, :normal, " source file is ", :bright]
@@ -74,8 +74,8 @@ defmodule Mix.Tasks.Pleroma.Emoji do
raise "Bad SHA256 for #{pack_name}" raise "Bad SHA256 for #{pack_name}"
end end


# The url specified in files should be in the same directory
files_url =
# The location specified in files should be in the same directory
files_loc =
url_or_path url_or_path
|> Path.dirname() |> Path.dirname()
|> Path.join(pack["files"]) |> Path.join(pack["files"])
@@ -88,11 +88,11 @@ defmodule Mix.Tasks.Pleroma.Emoji do
:normal, :normal,
" from ", " from ",
:underline, :underline,
files_url
files_loc
]) ])
) )


files = Tesla.get!(client(), files_url).body |> Jason.decode!()
files = fetch_and_decode(files_loc)


IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name])) IO.puts(IO.ANSI.format(["Unpacking ", :bright, pack_name]))


@@ -237,16 +237,20 @@ defmodule Mix.Tasks.Pleroma.Emoji do
end end
end end


defp fetch_manifest(from) do
Jason.decode!(
if String.starts_with?(from, "http") do
Tesla.get!(client(), from).body
else
File.read!(from)
end
)
defp fetch_and_decode(from) do
with {:ok, json} <- fetch(from) do
Jason.decode!(json)
end
end end


defp fetch("http" <> _ = from) do
with {:ok, %{body: body}} <- Tesla.get(client(), from) do
{:ok, body}
end
end

defp fetch(path), do: File.read(path)

defp parse_global_opts(args) do defp parse_global_opts(args) do
OptionParser.parse( OptionParser.parse(
args, args,


+ 1
- 4
lib/pleroma/activity/queries.ex View File

@@ -24,10 +24,7 @@ defmodule Pleroma.Activity.Queries do


@spec by_actor(query, String.t()) :: query @spec by_actor(query, String.t()) :: query
def by_actor(query \\ Activity, actor) do def by_actor(query \\ Activity, actor) do
from(
activity in query,
where: fragment("(?)->>'actor' = ?", activity.data, ^actor)
)
from(a in query, where: a.actor == ^actor)
end end


@spec by_author(query, User.t()) :: query @spec by_author(query, User.t()) :: query


+ 1
- 1
lib/pleroma/conversation.ex View File

@@ -63,7 +63,7 @@ defmodule Pleroma.Conversation do
ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do ap_id when is_binary(ap_id) and byte_size(ap_id) > 0 <- object.data["context"] do
{:ok, conversation} = create_for_ap_id(ap_id) {:ok, conversation} = create_for_ap_id(ap_id)


users = User.get_users_from_set(activity.recipients, false)
users = User.get_users_from_set(activity.recipients, local_only: false)


participations = participations =
Enum.map(users, fn user -> Enum.map(users, fn user ->


+ 1
- 1
lib/pleroma/emoji/pack.ex View File

@@ -499,7 +499,7 @@ defmodule Pleroma.Emoji.Pack do
if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do
{:ok, archive} {:ok, archive}
else else
{:error, :imvalid_checksum}
{:error, :invalid_checksum}
end end
end end
end end


+ 5
- 13
lib/pleroma/notification.ex View File

@@ -92,8 +92,9 @@ defmodule Pleroma.Notification do
|> join(:left, [n, a], object in Object, |> join(:left, [n, a], object in Object,
on: on:
fragment( fragment(
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
object.data, object.data,
a.data,
a.data a.data
) )
) )
@@ -224,18 +225,8 @@ defmodule Pleroma.Notification do
|> Marker.multi_set_last_read_id(user, "notifications") |> Marker.multi_set_last_read_id(user, "notifications")
|> Repo.transaction() |> Repo.transaction()


Notification
for_user_query(user)
|> where([n], n.id in ^notification_ids) |> where([n], n.id in ^notification_ids)
|> join(:inner, [n], activity in assoc(n, :activity))
|> join(:left, [n, a], object in Object,
on:
fragment(
"(?->>'id') = COALESCE((? -> 'object'::text) ->> 'id'::text)",
object.data,
a.data
)
)
|> preload([n, a, o], activity: {a, object: o})
|> Repo.all() |> Repo.all()
end end


@@ -370,7 +361,8 @@ defmodule Pleroma.Notification do
when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact"] do when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact"] do
potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity) potential_receiver_ap_ids = get_potential_receiver_ap_ids(activity)


potential_receivers = User.get_users_from_set(potential_receiver_ap_ids, local_only)
potential_receivers =
User.get_users_from_set(potential_receiver_ap_ids, local_only: local_only)


notification_enabled_ap_ids = notification_enabled_ap_ids =
potential_receiver_ap_ids potential_receiver_ap_ids


+ 62
- 20
lib/pleroma/plugs/http_security_plug.ex View File

@@ -31,7 +31,7 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
{"x-content-type-options", "nosniff"}, {"x-content-type-options", "nosniff"},
{"referrer-policy", referrer_policy}, {"referrer-policy", referrer_policy},
{"x-download-options", "noopen"}, {"x-download-options", "noopen"},
{"content-security-policy", csp_string() <> ";"}
{"content-security-policy", csp_string()}
] ]


if report_uri do if report_uri do
@@ -43,23 +43,46 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
] ]
} }


headers ++ [{"reply-to", Jason.encode!(report_group)}]
[{"reply-to", Jason.encode!(report_group)} | headers]
else else
headers headers
end end
end end


static_csp_rules = [
"default-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
"style-src 'self' 'unsafe-inline'",
"font-src 'self'",
"manifest-src 'self'"
]

@csp_start [Enum.join(static_csp_rules, ";") <> ";"]

defp csp_string do defp csp_string do
scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme] scheme = Config.get([Pleroma.Web.Endpoint, :url])[:scheme]
static_url = Pleroma.Web.Endpoint.static_url() static_url = Pleroma.Web.Endpoint.static_url()
websocket_url = Pleroma.Web.Endpoint.websocket_url() websocket_url = Pleroma.Web.Endpoint.websocket_url()
report_uri = Config.get([:http_security, :report_uri]) report_uri = Config.get([:http_security, :report_uri])


connect_src = "connect-src 'self' #{static_url} #{websocket_url}"
img_src = "img-src 'self' data: blob:"
media_src = "media-src 'self'"

{img_src, media_src} =
if Config.get([:media_proxy, :enabled]) &&
!Config.get([:media_proxy, :proxy_opts, :redirect_on_failure]) do
sources = get_proxy_and_attachment_sources()
{[img_src, sources], [media_src, sources]}
else
{img_src, media_src}
end

connect_src = ["connect-src 'self' ", static_url, ?\s, websocket_url]


connect_src = connect_src =
if Pleroma.Config.get(:env) == :dev do if Pleroma.Config.get(:env) == :dev do
connect_src <> " http://localhost:3035/"
[connect_src, " http://localhost:3035/"]
else else
connect_src connect_src
end end
@@ -71,27 +94,46 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do
"script-src 'self'" "script-src 'self'"
end end


main_part = [
"default-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
"img-src 'self' data: blob: https:",
"media-src 'self' https:",
"style-src 'self' 'unsafe-inline'",
"font-src 'self'",
"manifest-src 'self'",
connect_src,
script_src
]
report = if report_uri, do: ["report-uri ", report_uri, ";report-to csp-endpoint"]
insecure = if scheme == "https", do: "upgrade-insecure-requests"

@csp_start
|> add_csp_param(img_src)
|> add_csp_param(media_src)
|> add_csp_param(connect_src)
|> add_csp_param(script_src)
|> add_csp_param(insecure)
|> add_csp_param(report)
|> :erlang.iolist_to_binary()
end

defp get_proxy_and_attachment_sources do
media_proxy_whitelist =
Enum.reduce(Config.get([:media_proxy, :whitelist]), [], fn host, acc ->
add_source(acc, host)
end)


report = if report_uri, do: ["report-uri #{report_uri}; report-to csp-endpoint"], else: []
upload_base_url =
if Config.get([Pleroma.Upload, :base_url]),
do: URI.parse(Config.get([Pleroma.Upload, :base_url])).host


insecure = if scheme == "https", do: ["upgrade-insecure-requests"], else: []
s3_endpoint =
if Config.get([Pleroma.Upload, :uploader]) == Pleroma.Uploaders.S3,
do: URI.parse(Config.get([Pleroma.Uploaders.S3, :public_endpoint])).host


(main_part ++ report ++ insecure)
|> Enum.join("; ")
[]
|> add_source(upload_base_url)
|> add_source(s3_endpoint)
|> add_source(media_proxy_whitelist)
end end


defp add_source(iodata, nil), do: iodata
defp add_source(iodata, source), do: [[?\s, source] | iodata]

defp add_csp_param(csp_iodata, nil), do: csp_iodata

defp add_csp_param(csp_iodata, param), do: [[param, ?;] | csp_iodata]

def warn_if_disabled do def warn_if_disabled do
unless Config.get([:http_security, :enabled]) do unless Config.get([:http_security, :enabled]) do
Logger.warn(" Logger.warn("


+ 18
- 9
lib/pleroma/user.ex View File

@@ -538,9 +538,10 @@ defmodule Pleroma.User do
|> delete_change(:also_known_as) |> delete_change(:also_known_as)
|> unique_constraint(:email) |> unique_constraint(:email)
|> validate_format(:email, @email_regex) |> validate_format(:email, @email_regex)
|> validate_inclusion(:actor_type, ["Person", "Service"])
end end


@spec update_as_admin(%User{}, map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
@spec update_as_admin(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t()}
def update_as_admin(user, params) do def update_as_admin(user, params) do
params = Map.put(params, "password_confirmation", params["password"]) params = Map.put(params, "password_confirmation", params["password"])
changeset = update_as_admin_changeset(user, params) changeset = update_as_admin_changeset(user, params)
@@ -561,7 +562,7 @@ defmodule Pleroma.User do
|> put_change(:password_reset_pending, false) |> put_change(:password_reset_pending, false)
end end


@spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}
@spec reset_password(User.t(), map()) :: {:ok, User.t()} | {:error, Changeset.t()}
def reset_password(%User{} = user, params) do def reset_password(%User{} = user, params) do
reset_password(user, user, params) reset_password(user, user, params)
end end
@@ -1208,8 +1209,9 @@ defmodule Pleroma.User do


def increment_unread_conversation_count(_, user), do: {:ok, user} def increment_unread_conversation_count(_, user), do: {:ok, user}


@spec get_users_from_set([String.t()], boolean()) :: [User.t()]
def get_users_from_set(ap_ids, local_only \\ true) do
@spec get_users_from_set([String.t()], keyword()) :: [User.t()]
def get_users_from_set(ap_ids, opts \\ []) do
local_only = Keyword.get(opts, :local_only, true)
criteria = %{ap_id: ap_ids, deactivated: false} criteria = %{ap_id: ap_ids, deactivated: false}
criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria criteria = if local_only, do: Map.put(criteria, :local, true), else: criteria


@@ -1618,12 +1620,19 @@ defmodule Pleroma.User do
def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id) def fetch_by_ap_id(ap_id), do: ActivityPub.make_user_from_ap_id(ap_id)


def get_or_fetch_by_ap_id(ap_id) do def get_or_fetch_by_ap_id(ap_id) do
user = get_cached_by_ap_id(ap_id)
cached_user = get_cached_by_ap_id(ap_id)


if !is_nil(user) and !needs_update?(user) do
{:ok, user}
else
fetch_by_ap_id(ap_id)
maybe_fetched_user = needs_update?(cached_user) && fetch_by_ap_id(ap_id)

case {cached_user, maybe_fetched_user} do
{_, {:ok, %User{} = user}} ->
{:ok, user}

{%User{} = user, _} ->
{:ok, user}

_ ->
{:error, :not_found}
end end
end end




+ 20
- 7
lib/pleroma/web/activity_pub/activity_pub.ex View File

@@ -545,14 +545,27 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Repo.one() |> Repo.one()
end end


@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
@spec fetch_public_or_unlisted_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_or_unlisted_activities(opts \\ %{}, pagination \\ :keyset) do
opts = Map.drop(opts, ["user"]) opts = Map.drop(opts, ["user"])


[Constants.as_public()]
|> fetch_activities_query(opts)
|> restrict_unlisted()
|> Pagination.fetch_paginated(opts, pagination)
query = fetch_activities_query([Constants.as_public()], opts)

query =
if opts["restrict_unlisted"] do
restrict_unlisted(query)
else
query
end

Pagination.fetch_paginated(query, opts, pagination)
end

@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
opts
|> Map.put("restrict_unlisted", true)
|> fetch_public_or_unlisted_activities(pagination)
end end


@valid_visibilities ~w[direct unlisted public private] @valid_visibilities ~w[direct unlisted public private]
@@ -1165,7 +1178,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> Activity.with_joined_object() |> Activity.with_joined_object()
|> Object.with_joined_activity() |> Object.with_joined_activity()
|> select([_like, object, activity], %{activity | object: object}) |> select([_like, object, activity], %{activity | object: object})
|> order_by([like, _, _], desc: like.id)
|> order_by([like, _, _], desc_nulls_last: like.id)
|> Pagination.fetch_paginated( |> Pagination.fetch_paginated(
Map.merge(params, %{"skip_order" => true}), Map.merge(params, %{"skip_order" => true}),
pagination, pagination,


+ 7
- 3
lib/pleroma/web/activity_pub/transmogrifier.ex View File

@@ -1055,10 +1055,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
Map.put(object, "tag", tags) Map.put(object, "tag", tags)
end end


# TODO These should be added on our side on insertion, it doesn't make much
# sense to regenerate these all the time
def add_mention_tags(object) do def add_mention_tags(object) do
{enabled_receivers, disabled_receivers} = Utils.get_notified_from_object(object)
potential_receivers = enabled_receivers ++ disabled_receivers
mentions = Enum.map(potential_receivers, &build_mention_tag/1)
to = object["to"] || []
cc = object["cc"] || []
mentioned = User.get_users_from_set(to ++ cc, local_only: false)

mentions = Enum.map(mentioned, &build_mention_tag/1)


tags = object["tag"] || [] tags = object["tag"] || []
Map.put(object, "tag", tags ++ mentions) Map.put(object, "tag", tags ++ mentions)


+ 5
- 4
lib/pleroma/web/admin_api/controllers/admin_api_controller.ex View File

@@ -693,7 +693,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
%{assigns: %{user: admin}} = conn, %{assigns: %{user: admin}} = conn,
%{"nickname" => nickname} = params %{"nickname" => nickname} = params
) do ) do
with {_, user} <- {:user, User.get_cached_by_nickname(nickname)},
with {_, %User{} = user} <- {:user, User.get_cached_by_nickname(nickname)},
{:ok, _user} <- {:ok, _user} <-
User.update_as_admin(user, params) do User.update_as_admin(user, params) do
ModerationLog.insert_log(%{ ModerationLog.insert_log(%{
@@ -715,11 +715,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
json(conn, %{status: "success"}) json(conn, %{status: "success"})
else else
{:error, changeset} -> {:error, changeset} ->
{_, {error, _}} = Enum.at(changeset.errors, 0)
json(conn, %{error: "New password #{error}."})
errors = Map.new(changeset.errors, fn {key, {error, _}} -> {key, error} end)

json(conn, %{errors: errors})


_ -> _ ->
json(conn, %{error: "Unable to change password."})
json(conn, %{error: "Unable to update user."})
end end
end end




+ 1
- 1
lib/pleroma/web/feed/user_controller.ex View File

@@ -56,7 +56,7 @@ defmodule Pleroma.Web.Feed.UserController do
"actor_id" => user.ap_id "actor_id" => user.ap_id
} }
|> put_if_exist("max_id", params["max_id"]) |> put_if_exist("max_id", params["max_id"])
|> ActivityPub.fetch_public_activities()
|> ActivityPub.fetch_public_or_unlisted_activities()


conn conn
|> put_resp_content_type("application/#{format}+xml") |> put_resp_content_type("application/#{format}+xml")


+ 1
- 1
lib/pleroma/web/mastodon_api/controllers/account_controller.ex View File

@@ -81,7 +81,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do


plug( plug(
RateLimiter, RateLimiter,
[name: :relation_id_action, params: ["id", "uri"]] when action in @relationship_actions
[name: :relation_id_action, params: [:id, :uri]] when action in @relationship_actions
) )


plug(RateLimiter, [name: :relations_actions] when action in @relationship_actions) plug(RateLimiter, [name: :relations_actions] when action in @relationship_actions)


+ 2
- 2
lib/pleroma/web/mastodon_api/controllers/status_controller.ex View File

@@ -84,13 +84,13 @@ defmodule Pleroma.Web.MastodonAPI.StatusController do


plug( plug(
RateLimiter, RateLimiter,
[name: :status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: ["id"]]
[name: :status_id_action, bucket_name: "status_id_action:reblog_unreblog", params: [:id]]
when action in ~w(reblog unreblog)a when action in ~w(reblog unreblog)a
) )


plug( plug(
RateLimiter, RateLimiter,
[name: :status_id_action, bucket_name: "status_id_action:fav_unfav", params: ["id"]]
[name: :status_id_action, bucket_name: "status_id_action:fav_unfav", params: [:id]]
when action in ~w(favourite unfavourite)a when action in ~w(favourite unfavourite)a
) )




+ 7
- 5
lib/pleroma/web/mastodon_api/views/account_view.ex View File

@@ -182,12 +182,14 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
bot = user.actor_type in ["Application", "Service"] bot = user.actor_type in ["Application", "Service"]


emojis = emojis =
Enum.map(user.emoji, fn {shortcode, url} ->
Enum.map(user.emoji, fn {shortcode, raw_url} ->
url = MediaProxy.url(raw_url)

%{ %{
"shortcode" => shortcode,
"url" => url,
"static_url" => url,
"visible_in_picker" => false
shortcode: shortcode,
url: url,
static_url: url,
visible_in_picker: false
} }
end) end)




+ 1
- 1
lib/pleroma/web/pleroma_api/controllers/emoji_pack_controller.ex View File

@@ -106,7 +106,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do
|> put_status(:internal_server_error) |> put_status(:internal_server_error)
|> json(%{error: "The requested instance does not support sharing emoji packs"}) |> json(%{error: "The requested instance does not support sharing emoji packs"})


{:error, :imvalid_checksum} ->
{:error, :invalid_checksum} ->
conn conn
|> put_status(:internal_server_error) |> put_status(:internal_server_error)
|> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"}) |> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"})


+ 2
- 0
lib/pleroma/workers/cron/clear_oauth_token_worker.ex View File

@@ -16,6 +16,8 @@ defmodule Pleroma.Workers.Cron.ClearOauthTokenWorker do
def perform(_opts, _job) do def perform(_opts, _job) do
if Config.get([:oauth2, :clean_expired_tokens], false) do if Config.get([:oauth2, :clean_expired_tokens], false) do
Token.delete_expired_tokens() Token.delete_expired_tokens()
else
:ok
end end
end end
end end

+ 2
- 0
lib/pleroma/workers/cron/digest_emails_worker.ex View File

@@ -37,6 +37,8 @@ defmodule Pleroma.Workers.Cron.DigestEmailsWorker do
) )
|> Repo.all() |> Repo.all()
|> send_emails |> send_emails
else
:ok
end end
end end




+ 4
- 0
lib/pleroma/workers/cron/new_users_digest_worker.ex View File

@@ -55,7 +55,11 @@ defmodule Pleroma.Workers.Cron.NewUsersDigestWorker do
|> Repo.all() |> Repo.all()
|> Enum.map(&Pleroma.Emails.NewUsersDigestEmail.new_users(&1, users_and_statuses)) |> Enum.map(&Pleroma.Emails.NewUsersDigestEmail.new_users(&1, users_and_statuses))
|> Enum.each(&Pleroma.Emails.Mailer.deliver/1) |> Enum.each(&Pleroma.Emails.Mailer.deliver/1)
else
:ok
end end
else
:ok
end end
end end
end end

+ 2
- 0
lib/pleroma/workers/cron/purge_expired_activities_worker.ex View File

@@ -23,6 +23,8 @@ defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker do
def perform(_opts, _job) do def perform(_opts, _job) do
if Config.get([ActivityExpiration, :enabled]) do if Config.get([ActivityExpiration, :enabled]) do
Enum.each(ActivityExpiration.due_expirations(@interval), &delete_activity/1) Enum.each(ActivityExpiration.due_expirations(@interval), &delete_activity/1)
else
:ok
end end
end end




+ 7
- 0
priv/repo/migrations/20200526144426_add_apps_indexes.exs View File

@@ -0,0 +1,7 @@
defmodule Pleroma.Repo.Migrations.AddAppsIndexes do
use Ecto.Migration

def change do
create(index(:apps, [:client_id, :client_secret]))
end
end

+ 8
- 0
priv/repo/migrations/20200527104138_change_notification_user_index.exs View File

@@ -0,0 +1,8 @@
defmodule Pleroma.Repo.Migrations.ChangeNotificationUserIndex do
use Ecto.Migration

def change do
drop_if_exists(index(:notifications, [:user_id]))
create_if_not_exists(index(:notifications, [:user_id, "id desc nulls last"]))
end
end

+ 0
- 1
priv/static/adminfe/chunk-3384.2278f87c.css
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/chunk-3384.d50ed383.css
File diff suppressed because it is too large
View File


priv/static/adminfe/chunk-7e30.f2b9674a.css
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/chunk-e458.6c0703cb.css View File

@@ -0,0 +1 @@
.status-card{margin-bottom:10px}.status-card .account{text-decoration:underline;line-height:26px;font-size:13px}.status-card .image{width:20%}.status-card .image img{width:100%}.status-card .show-more-button{margin-left:5px}.status-card .status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-card .status-avatar-img{display:inline-block;width:15px;height:15px;margin-right:5px}.status-card .status-account-name{display:inline-block;margin:0;height:22px}.status-card .status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-card .status-checkbox{margin-right:7px}.status-card .status-content{font-size:15px;line-height:26px}.status-card .status-deleted{font-style:italic;margin-top:3px}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.status-card .status-without-content{font-style:italic}@media only screen and (max-width:480px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 4px 3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.statuses-container{padding:0 15px}.statuses-container h1{margin:10px 0 15px}.statuses-container .status-container{margin:0 0 10px}.statuses-header-container .el-button.is-plain:focus,.statuses-header-container .el-button.is-plain:hover{border-color:#dcdfe6;color:#606266;cursor:default}.checkbox-container{margin-bottom:15px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 0 15px}.reboot-button{padding:10px;margin:0;width:145px}.select-instance{width:396px}.statuses-header,.statuses-header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.statuses-pagination{padding:15px 0;text-align:center}@media only screen and (max-width:480px){.checkbox-container{margin-bottom:10px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:10px 0}.select-field{width:100%;margin-bottom:5px}.select-instance{width:100%}.statuses-header-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.statuses-header-container .el-button-group{width:100%}.statuses-header-container .el-button{padding:10px 6.5px;width:50%}.statuses-header-container .el-button-group>.el-button:first-child{border-bottom-left-radius:0}.statuses-header-container .el-button-group>.el-button:not(:first-child):not(:last-child).private-button{border-top-right-radius:4px}.statuses-header-container .el-button-group>.el-button:not(:first-child):not(:last-child).public-button{border-bottom-left-radius:4px;border-top:#fff}.statuses-header-container .el-button-group>.el-button:last-child{border-top-right-radius:0;border-top:#fff}.statuses-header-container .reboot-button{margin:10px 0 0}}

+ 0
- 1
priv/static/adminfe/chunk-e458.f88bafea.css View File

@@ -1 +0,0 @@
.status-card{margin-bottom:10px}.status-card .account{text-decoration:underline;line-height:26px;font-size:13px}.status-card .image{width:20%}.status-card .image img{width:100%}.status-card .show-more-button{margin-left:5px}.status-card .status-account{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.status-card .status-avatar-img{display:inline-block;width:15px;height:15px;margin-right:5px}.status-card .status-account-name{display:inline-block;margin:0;height:22px}.status-card .status-body{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.status-card .status-checkbox{margin-right:7px}.status-card .status-content{font-size:15px;line-height:26px}.status-card .status-deleted{font-style:italic;margin-top:3px}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.status-card .status-without-content{font-style:italic}@media only screen and (max-width:480px){.el-message{min-width:80%}.el-message-box{width:80%}.status-card .el-card__header{padding:10px 17px}.status-card .el-tag{margin:3px 4px 3px 0}.status-card .status-account-container{margin-bottom:5px}.status-card .status-actions-button{margin:3px 0}.status-card .status-actions{display:-webkit-box;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap}.status-card .status-header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.statuses-container{padding:0 15px}.statuses-container h1{margin:10px 0 15px}.statuses-container .status-container{margin:0 0 10px}.checkbox-container{margin-bottom:15px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;margin:22px 0 15px}.reboot-button{padding:10px;margin:0;width:145px}.select-instance{width:396px}.statuses-header,.statuses-header-container{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.statuses-pagination{padding:15px 0;text-align:center}@media only screen and (max-width:480px){.checkbox-container{margin-bottom:10px}.filter-container{display:-webkit-box;display:-ms-flexbox;display:flex;height:36px;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;margin:10px 0}.select-field{width:100%;margin-bottom:5px}.select-instance{width:100%}.statuses-header-container{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:start;-ms-flex-align:start;align-items:flex-start}.statuses-header-container .el-button{padding:10px 6.5px}.statuses-header-container .reboot-button{margin:10px 0 0}}

+ 1
- 1
priv/static/adminfe/index.html View File

@@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.796ca6d4.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.1b4f6ce0.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.203f69f8.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge,chrome=1"><meta name=renderer content=webkit><meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"><title>Admin FE</title><link rel="shortcut icon" href=favicon.ico><link href=chunk-elementUI.1abbc9b8.css rel=stylesheet><link href=chunk-libs.686b5876.css rel=stylesheet><link href=app.796ca6d4.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=static/js/runtime.b08eb412.js></script><script type=text/javascript src=static/js/chunk-elementUI.fba0efec.js></script><script type=text/javascript src=static/js/chunk-libs.b8c453ab.js></script><script type=text/javascript src=static/js/app.0146039c.js></script></body></html>

+ 2
- 0
priv/static/adminfe/static/js/app.0146039c.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/app.0146039c.js.map
File diff suppressed because it is too large
View File


+ 0
- 2
priv/static/adminfe/static/js/app.203f69f8.js
File diff suppressed because it is too large
View File


+ 0
- 1
priv/static/adminfe/static/js/app.203f69f8.js.map
File diff suppressed because it is too large
View File


+ 0
- 2
priv/static/adminfe/static/js/chunk-3384.458ffaf1.js
File diff suppressed because it is too large
View File


+ 0
- 1
priv/static/adminfe/static/js/chunk-3384.458ffaf1.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
priv/static/adminfe/static/js/chunk-3384.b2ebeeca.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/chunk-3384.b2ebeeca.js.map
File diff suppressed because it is too large
View File


+ 0
- 2
priv/static/adminfe/static/js/chunk-4011.67fb1692.js
File diff suppressed because it is too large
View File


+ 0
- 1
priv/static/adminfe/static/js/chunk-4011.67fb1692.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
priv/static/adminfe/static/js/chunk-7e30.ec42e302.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/chunk-7e30.ec42e302.js.map
File diff suppressed because it is too large
View File


+ 0
- 2
priv/static/adminfe/static/js/chunk-e458.4e5aad44.js
File diff suppressed because it is too large
View File


+ 0
- 1
priv/static/adminfe/static/js/chunk-e458.4e5aad44.js.map
File diff suppressed because it is too large
View File


+ 2
- 0
priv/static/adminfe/static/js/chunk-e458.bb460d81.js
File diff suppressed because it is too large
View File


+ 1
- 0
priv/static/adminfe/static/js/chunk-e458.bb460d81.js.map
File diff suppressed because it is too large
View File


priv/static/adminfe/static/js/runtime.1b4f6ce0.js → priv/static/adminfe/static/js/runtime.b08eb412.js View File

@@ -1,2 +1,2 @@
!function(e){function n(n){for(var r,c,a=n[0],f=n[1],h=n[2],i=0,l=[];i<a.length;i++)c=a[i],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);for(d&&d(n);l.length;)l.shift()();return o.push.apply(o,h||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[];c[e]?n.push(c[e]):0!==c[e]&&{"chunk-6b68":1,"chunk-d38a":1,"chunk-0558":1,"chunk-0778":1,"chunk-3384":1,"chunk-6e81":1,"chunk-4011":1,"chunk-970d":1,"chunk-22d2":1,"chunk-e458":1,"chunk-7637":1,"chunk-0961":1}[e]&&n.push(c[e]=new Promise(function(n,t){for(var r=({}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0",ZhIB:"31d6cfe0","chunk-6b68":"0cc00484","chunk-d38a":"cabdc22e","chunk-0558":"af0d89cd","chunk-0778":"d9e7180a","chunk-3384":"2278f87c","chunk-6e81":"0e80d020","chunk-7f9e":"31d6cfe0","chunk-4011":"c4799067","chunk-df62":"31d6cfe0","chunk-970d":"f59cca8c","chunk-22d2":"813009b9","chunk-e458":"f88bafea","chunk-7637":"941c4edb",oAJy:"31d6cfe0","chunk-0961":"d3692214","chunk-16d0":"31d6cfe0"}[e]+".css",c=a.p+r,u=document.getElementsByTagName("link"),o=0;o<u.length;o++){var f=(i=u[o]).getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(f===r||f===c))return n()}var h=document.getElementsByTagName("style");for(o=0;o<h.length;o++){var i;if((f=(i=h[o]).getAttribute("data-href"))===r||f===c)return n()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=n,d.onerror=function(n){var r=n&&n.target&&n.target.src||c,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.request=r,t(u)},d.href=c,document.getElementsByTagName("head")[0].appendChild(d)}).then(function(){c[e]=0}));var t=u[e];if(0!==t)if(t)n.push(t[2]);else{var r=new Promise(function(n,r){t=u[e]=[n,r]});n.push(t[2]=r);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594",ZhIB:"861df339","chunk-6b68":"fbc0f684","chunk-d38a":"a851004a","chunk-0558":"75954137","chunk-0778":"b17650df","chunk-3384":"458ffaf1","chunk-6e81":"3733ace2","chunk-7f9e":"c49aa694","chunk-4011":"67fb1692","chunk-df62":"6c5105a6","chunk-970d":"2457e066","chunk-22d2":"a0cf7976","chunk-e458":"4e5aad44","chunk-7637":"8f5fb36e",oAJy:"840fb1c2","chunk-0961":"ef33e81b","chunk-16d0":"6ce78978"}[e]+".js"}(e),o=function(n){f.onerror=f.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var h=setTimeout(function(){o({type:"timeout",target:f})},12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],h=f.push.bind(f);f.push=n,f=f.slice();for(var i=0;i<f.length;i++)n(f[i]);var d=h;t()}([]);
//# sourceMappingURL=runtime.1b4f6ce0.js.map
!function(e){function n(n){for(var r,c,a=n[0],f=n[1],h=n[2],i=0,l=[];i<a.length;i++)c=a[i],u[c]&&l.push(u[c][0]),u[c]=0;for(r in f)Object.prototype.hasOwnProperty.call(f,r)&&(e[r]=f[r]);for(d&&d(n);l.length;)l.shift()();return o.push.apply(o,h||[]),t()}function t(){for(var e,n=0;n<o.length;n++){for(var t=o[n],r=!0,c=1;c<t.length;c++){var f=t[c];0!==u[f]&&(r=!1)}r&&(o.splice(n--,1),e=a(a.s=t[0]))}return e}var r={},c={runtime:0},u={runtime:0},o=[];function a(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,a),t.l=!0,t.exports}a.e=function(e){var n=[];c[e]?n.push(c[e]):0!==c[e]&&{"chunk-6b68":1,"chunk-d38a":1,"chunk-0558":1,"chunk-0778":1,"chunk-3384":1,"chunk-6e81":1,"chunk-7e30":1,"chunk-e458":1,"chunk-970d":1,"chunk-22d2":1,"chunk-7637":1,"chunk-0961":1}[e]&&n.push(c[e]=new Promise(function(n,t){for(var r=({}[e]||e)+"."+{"7zzA":"31d6cfe0",JEtC:"31d6cfe0",ZhIB:"31d6cfe0","chunk-6b68":"0cc00484","chunk-d38a":"cabdc22e","chunk-0558":"af0d89cd","chunk-0778":"d9e7180a","chunk-3384":"d50ed383","chunk-6e81":"0e80d020","chunk-7f9e":"31d6cfe0","chunk-7e30":"f2b9674a","chunk-df62":"31d6cfe0","chunk-e458":"6c0703cb","chunk-970d":"f59cca8c","chunk-22d2":"813009b9","chunk-7637":"941c4edb",oAJy:"31d6cfe0","chunk-0961":"d3692214","chunk-16d0":"31d6cfe0"}[e]+".css",c=a.p+r,u=document.getElementsByTagName("link"),o=0;o<u.length;o++){var f=(i=u[o]).getAttribute("data-href")||i.getAttribute("href");if("stylesheet"===i.rel&&(f===r||f===c))return n()}var h=document.getElementsByTagName("style");for(o=0;o<h.length;o++){var i;if((f=(i=h[o]).getAttribute("data-href"))===r||f===c)return n()}var d=document.createElement("link");d.rel="stylesheet",d.type="text/css",d.onload=n,d.onerror=function(n){var r=n&&n.target&&n.target.src||c,u=new Error("Loading CSS chunk "+e+" failed.\n("+r+")");u.request=r,t(u)},d.href=c,document.getElementsByTagName("head")[0].appendChild(d)}).then(function(){c[e]=0}));var t=u[e];if(0!==t)if(t)n.push(t[2]);else{var r=new Promise(function(n,r){t=u[e]=[n,r]});n.push(t[2]=r);var o,f=document.createElement("script");f.charset="utf-8",f.timeout=120,a.nc&&f.setAttribute("nonce",a.nc),f.src=function(e){return a.p+"static/js/"+({}[e]||e)+"."+{"7zzA":"e1ae1c94",JEtC:"f9ba4594",ZhIB:"861df339","chunk-6b68":"fbc0f684","chunk-d38a":"a851004a","chunk-0558":"75954137","chunk-0778":"b17650df","chunk-3384":"b2ebeeca","chunk-6e81":"3733ace2","chunk-7f9e":"c49aa694","chunk-7e30":"ec42e302","chunk-df62":"6c5105a6","chunk-e458":"bb460d81","chunk-970d":"2457e066","chunk-22d2":"a0cf7976","chunk-7637":"8f5fb36e",oAJy:"840fb1c2","chunk-0961":"ef33e81b","chunk-16d0":"6ce78978"}[e]+".js"}(e),o=function(n){f.onerror=f.onload=null,clearTimeout(h);var t=u[e];if(0!==t){if(t){var r=n&&("load"===n.type?"missing":n.type),c=n&&n.target&&n.target.src,o=new Error("Loading chunk "+e+" failed.\n("+r+": "+c+")");o.type=r,o.request=c,t[1](o)}u[e]=void 0}};var h=setTimeout(function(){o({type:"timeout",target:f})},12e4);f.onerror=f.onload=o,document.head.appendChild(f)}return Promise.all(n)},a.m=e,a.c=r,a.d=function(e,n,t){a.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:t})},a.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},a.t=function(e,n){if(1&n&&(e=a(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(a.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)a.d(t,r,function(n){return e[n]}.bind(null,r));return t},a.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return a.d(n,"a",n),n},a.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},a.p="",a.oe=function(e){throw console.error(e),e};var f=window.webpackJsonp=window.webpackJsonp||[],h=f.push.bind(f);f.push=n,f=f.slice();for(var i=0;i<f.length;i++)n(f[i]);var d=h;t()}([]);
//# sourceMappingURL=runtime.b08eb412.js.map

priv/static/adminfe/static/js/runtime.b08eb412.js.map
File diff suppressed because it is too large
View File


+ 3
- 0
test/instance_static/local_pack/files.json View File

@@ -0,0 +1,3 @@
{
"blank": "blank.png"
}

+ 10
- 0
test/instance_static/local_pack/manifest.json View File

@@ -0,0 +1,10 @@
{
"local": {
"src_sha256": "384025A1AC6314473863A11AC7AB38A12C01B851A3F82359B89B4D4211D3291D",
"src": "test/fixtures/emoji/packs/blank.png.zip",
"license": "Apache 2.0",
"homepage": "https://example.com",
"files": "files.json",
"description": "Some local pack"
}
}

+ 7
- 4
test/notification_test.exs View File

@@ -454,8 +454,7 @@ defmodule Pleroma.NotificationTest do
status: "hey again @#{other_user.nickname}!" status: "hey again @#{other_user.nickname}!"
}) })


[n2, n1] = notifs = Notification.for_user(other_user)
assert length(notifs) == 2
[n2, n1] = Notification.for_user(other_user)


assert n2.id > n1.id assert n2.id > n1.id


@@ -464,7 +463,9 @@ defmodule Pleroma.NotificationTest do
status: "hey yet again @#{other_user.nickname}!" status: "hey yet again @#{other_user.nickname}!"
}) })


Notification.set_read_up_to(other_user, n2.id)
[_, read_notification] = Notification.set_read_up_to(other_user, n2.id)

assert read_notification.activity.object


[n3, n2, n1] = Notification.for_user(other_user) [n3, n2, n1] = Notification.for_user(other_user)


@@ -972,7 +973,9 @@ defmodule Pleroma.NotificationTest do


{:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"}) {:ok, _activity} = CommonAPI.post(muted, %{status: "hey @#{user.nickname}"})


assert length(Notification.for_user(user)) == 1
[notification] = Notification.for_user(user)

assert notification.activity.object
end end


test "it doesn't return notifications for muted user with notifications" do test "it doesn't return notifications for muted user with notifications" do


+ 1
- 0
test/plugs/authentication_plug_test.exs View File

@@ -68,6 +68,7 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
assert "$pbkdf2" <> _ = user.password_hash assert "$pbkdf2" <> _ = user.password_hash
end end


@tag :skip_on_mac
test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do test "with a crypt hash, it updates to a pkbdf2 hash", %{conn: conn} do
user = user =
insert(:user, insert(:user,


+ 1
- 1
test/plugs/http_security_plug_test.exs View File

@@ -67,7 +67,7 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do


[csp] = Conn.get_resp_header(conn, "content-security-policy") [csp] = Conn.get_resp_header(conn, "content-security-policy")


assert csp =~ ~r|report-uri https://endpoint.com; report-to csp-endpoint;|
assert csp =~ ~r|report-uri https://endpoint.com;report-to csp-endpoint;|


[reply_to] = Conn.get_resp_header(conn, "reply-to") [reply_to] = Conn.get_resp_header(conn, "reply-to")




+ 2
- 1
test/support/factory.ex View File

@@ -34,7 +34,8 @@ defmodule Pleroma.Factory do
last_digest_emailed_at: NaiveDateTime.utc_now(), last_digest_emailed_at: NaiveDateTime.utc_now(),
last_refreshed_at: NaiveDateTime.utc_now(), last_refreshed_at: NaiveDateTime.utc_now(),
notification_settings: %Pleroma.User.NotificationSetting{}, notification_settings: %Pleroma.User.NotificationSetting{},
multi_factor_authentication_settings: %Pleroma.MFA.Settings{}
multi_factor_authentication_settings: %Pleroma.MFA.Settings{},
ap_enabled: true
} }


%{ %{


+ 13
- 0
test/tasks/emoji_test.exs View File

@@ -73,6 +73,19 @@ defmodule Mix.Tasks.Pleroma.EmojiTest do
on_exit(fn -> File.rm_rf!("test/instance_static/emoji/finmoji") end) on_exit(fn -> File.rm_rf!("test/instance_static/emoji/finmoji") end)
end end


test "install local emoji pack" do
assert capture_io(fn ->
Emoji.run([
"get-packs",
"local",
"--manifest",
"test/instance_static/local_pack/manifest.json"
])
end) =~ "Writing pack.json for"

on_exit(fn -> File.rm_rf!("test/instance_static/emoji/local") end)
end

test "pack not found" do test "pack not found" do
mock(fn mock(fn
%{ %{


+ 20
- 0
test/user_test.exs View File

@@ -586,6 +586,26 @@ defmodule Pleroma.UserTest do


refute user.last_refreshed_at == orig_user.last_refreshed_at refute user.last_refreshed_at == orig_user.last_refreshed_at
end end

@tag capture_log: true
test "it returns the old user if stale, but unfetchable" do
a_week_ago = NaiveDateTime.add(NaiveDateTime.utc_now(), -604_800)

orig_user =
insert(
:user,
local: false,
nickname: "admin@mastodon.example.org",
ap_id: "http://mastodon.example.org/users/raymoo",
last_refreshed_at: a_week_ago
)

assert orig_user.last_refreshed_at == a_week_ago

{:ok, user} = User.get_or_fetch_by_ap_id("http://mastodon.example.org/users/raymoo")

assert user.last_refreshed_at == orig_user.last_refreshed_at
end
end end


test "returns an ap_id for a user" do test "returns an ap_id for a user" do


+ 30
- 0
test/web/activity_pub/activity_pub_controller_test.exs View File

@@ -451,6 +451,36 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
assert Activity.get_by_ap_id(data["id"]) assert Activity.get_by_ap_id(data["id"])
end end


@tag capture_log: true
test "it inserts an incoming activity into the database" <>
"even if we can't fetch the user but have it in our db",
%{conn: conn} do
user =
insert(:user,
ap_id: "https://mastodon.example.org/users/raymoo",
ap_enabled: true,
local: false,
last_refreshed_at: nil
)

data =
File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
|> Map.put("actor", user.ap_id)
|> put_in(["object", "attridbutedTo"], user.ap_id)

conn =
conn
|> assign(:valid_signature, true)
|> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data)

assert "ok" == json_response(conn, 200)

ObanHelpers.perform(all_enqueued(worker: ReceiverWorker))
assert Activity.get_by_ap_id(data["id"])
end

test "it clears `unreachable` federation status of the sender", %{conn: conn} do test "it clears `unreachable` federation status of the sender", %{conn: conn} do
data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!()




+ 19
- 14
test/web/activity_pub/transmogrifier_test.exs View File

@@ -1094,23 +1094,28 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
{:ok, activity} = {:ok, activity} =
CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"}) CommonAPI.post(user, %{status: "hey, @#{other_user.nickname}, how are ya? #2hu"})


{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
object = modified["object"]
with_mock Pleroma.Notification,
get_notified_from_activity: fn _, _ -> [] end do
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)


expected_mention = %{
"href" => other_user.ap_id,
"name" => "@#{other_user.nickname}",
"type" => "Mention"
}
object = modified["object"]


expected_tag = %{
"href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
"type" => "Hashtag",
"name" => "#2hu"
}
expected_mention = %{
"href" => other_user.ap_id,
"name" => "@#{other_user.nickname}",
"type" => "Mention"
}


assert Enum.member?(object["tag"], expected_tag)
assert Enum.member?(object["tag"], expected_mention)
expected_tag = %{
"href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
"type" => "Hashtag",
"name" => "#2hu"
}

refute called(Pleroma.Notification.get_notified_from_activity(:_, :_))
assert Enum.member?(object["tag"], expected_tag)
assert Enum.member?(object["tag"], expected_mention)
end
end end


test "it adds the sensitive property" do test "it adds the sensitive property" do


+ 31
- 4
test/web/admin_api/controllers/admin_api_controller_test.exs View File

@@ -3191,8 +3191,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
end end


describe "PATCH /users/:nickname/credentials" do describe "PATCH /users/:nickname/credentials" do
test "changes password and email", %{conn: conn, admin: admin} do
setup do
user = insert(:user) user = insert(:user)
[user: user]
end

test "changes password and email", %{conn: conn, admin: admin, user: user} do
assert user.password_reset_pending == false assert user.password_reset_pending == false


conn = conn =
@@ -3222,9 +3226,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
"@#{admin.nickname} forced password reset for users: @#{user.nickname}" "@#{admin.nickname} forced password reset for users: @#{user.nickname}"
end end


test "returns 403 if requested by a non-admin" do
user = insert(:user)

test "returns 403 if requested by a non-admin", %{user: user} do
conn = conn =
build_conn() build_conn()
|> assign(:user, user) |> assign(:user, user)
@@ -3236,6 +3238,31 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do


assert json_response(conn, :forbidden) assert json_response(conn, :forbidden)
end end

test "changes actor type from permitted list", %{conn: conn, user: user} do
assert user.actor_type == "Person"

assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
"actor_type" => "Service"
})
|> json_response(200) == %{"status" => "success"}

updated_user = User.get_by_id(user.id)

assert updated_user.actor_type == "Service"

assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
"actor_type" => "Application"
})
|> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}}
end

test "update non existing user", %{conn: conn} do
assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
"password" => "new_password"
})
|> json_response(200) == %{"error" => "Unable to update user."}
end
end end


describe "PATCH /users/:nickname/force_password_reset" do describe "PATCH /users/:nickname/force_password_reset" do


+ 25
- 1
test/web/feed/user_controller_test.exs View File

@@ -11,13 +11,14 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
alias Pleroma.Config alias Pleroma.Config
alias Pleroma.Object alias Pleroma.Object
alias Pleroma.User alias Pleroma.User
alias Pleroma.Web.CommonAPI


setup do: clear_config([:instance, :federating], true) setup do: clear_config([:instance, :federating], true)


describe "feed" do describe "feed" do
setup do: clear_config([:feed]) setup do: clear_config([:feed])


test "gets a feed", %{conn: conn} do
test "gets an atom feed", %{conn: conn} do
Config.put( Config.put(
[:feed, :post_title], [:feed, :post_title],
%{max_length: 10, omission: "..."} %{max_length: 10, omission: "..."}
@@ -157,6 +158,29 @@ defmodule Pleroma.Web.Feed.UserControllerTest do


assert response(conn, 404) assert response(conn, 404)
end end

test "returns feed with public and unlisted activities", %{conn: conn} do
user = insert(:user)

{:ok, _} = CommonAPI.post(user, %{status: "public", visibility: "public"})
{:ok, _} = CommonAPI.post(user, %{status: "direct", visibility: "direct"})
{:ok, _} = CommonAPI.post(user, %{status: "unlisted", visibility: "unlisted"})
{:ok, _} = CommonAPI.post(user, %{status: "private", visibility: "private"})

resp =
conn
|> put_req_header("accept", "application/atom+xml")
|> get(user_feed_path(conn, :feed, user.nickname))
|> response(200)

activity_titles =
resp
|> SweetXml.parse()
|> SweetXml.xpath(~x"//entry/title/text()"l)
|> Enum.sort()

assert activity_titles == ['public', 'unlisted']
end
end end


# Note: see ActivityPubControllerTest for JSON format tests # Note: see ActivityPubControllerTest for JSON format tests


+ 31
- 4
test/web/mastodon_api/views/account_view_test.exs View File

@@ -54,10 +54,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
header_static: "http://localhost:4001/images/banner.png", header_static: "http://localhost:4001/images/banner.png",
emojis: [ emojis: [
%{ %{
"static_url" => "/file.png",
"url" => "/file.png",
"shortcode" => "karjalanpiirakka",
"visible_in_picker" => false
static_url: "/file.png",
url: "/file.png",
shortcode: "karjalanpiirakka",
visible_in_picker: false
} }
], ],
fields: [], fields: [],
@@ -493,4 +493,31 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
AccountView.render("show.json", %{user: user, for: user}) AccountView.render("show.json", %{user: user, for: user})
end end
end end

test "uses mediaproxy urls when it's enabled" do
clear_config([:media_proxy, :enabled], true)

user =
insert(:user,
avatar: %{"url" => [%{"href" => "https://evil.website/avatar.png"}]},
banner: %{"url" => [%{"href" => "https://evil.website/banner.png"}]},
emoji: %{"joker_smile" => "https://evil.website/society.png"}
)

AccountView.render("show.json", %{user: user})
|> Enum.all?(fn
{key, url} when key in [:avatar, :avatar_static, :header, :header_static] ->
String.starts_with?(url, Pleroma.Web.base_url())

{:emojis, emojis} ->
Enum.all?(emojis, fn %{url: url, static_url: static_url} ->
String.starts_with?(url, Pleroma.Web.base_url()) &&
String.starts_with?(static_url, Pleroma.Web.base_url())
end)

_ ->
true
end)
|> assert()
end
end end

+ 3
- 11
test/web/media_proxy/media_proxy_test.exs View File

@@ -124,15 +124,7 @@ defmodule Pleroma.Web.MediaProxyTest do
end end


test "uses the configured base_url" do test "uses the configured base_url" do
base_url = Pleroma.Config.get([:media_proxy, :base_url])

if base_url do
on_exit(fn ->
Pleroma.Config.put([:media_proxy, :base_url], base_url)
end)
end

Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
clear_config([:media_proxy, :base_url], "https://cache.pleroma.social")


url = "https://pleroma.soykaf.com/static/logo.png" url = "https://pleroma.soykaf.com/static/logo.png"
encoded = url(url) encoded = url(url)
@@ -213,8 +205,8 @@ defmodule Pleroma.Web.MediaProxyTest do
end end


test "does not change whitelisted urls" do test "does not change whitelisted urls" do
Pleroma.Config.put([:media_proxy, :whitelist], ["mycdn.akamai.com"])
Pleroma.Config.put([:media_proxy, :base_url], "https://cache.pleroma.social")
clear_config([:media_proxy, :whitelist], ["mycdn.akamai.com"])
clear_config([:media_proxy, :base_url], "https://cache.pleroma.social")


media_url = "https://mycdn.akamai.com" media_url = "https://mycdn.akamai.com"




Loading…
Cancel
Save