Scrape instance nodeinfo #251

Merged
floatingghost merged 15 commits from instance-info into develop 2022-11-06 22:49:39 +00:00
17 changed files with 175 additions and 74 deletions
Showing only changes of commit f66ddf697b - Show all commits

View file

@ -14,6 +14,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Follows no longer override domain blocks, a domain block is final - Follows no longer override domain blocks, a domain block is final
- Deletes are now the lowest priority to publish and will be handled after creates - Deletes are now the lowest priority to publish and will be handled after creates
## Fixed
- Registrations via ldap are now compatible with the latest OTP24
## 2022.10 ## 2022.10
### Added ### Added

View file

@ -185,7 +185,7 @@ config :pleroma, :http,
adapter: [] adapter: []
config :pleroma, :instance, config :pleroma, :instance,
name: "Pleroma", name: "Akkoma",
email: "example@example.com", email: "example@example.com",
notify_email: "noreply@example.com", notify_email: "noreply@example.com",
description: "Akkoma: The cooler fediverse server", description: "Akkoma: The cooler fediverse server",

View file

@ -1389,6 +1389,12 @@ config :pleroma, :config_description, [
label: "Render misskey markdown", label: "Render misskey markdown",
type: :boolean, type: :boolean,
description: "Whether to render Misskey-flavoured markdown" description: "Whether to render Misskey-flavoured markdown"
},
%{
key: :stopGifs,
label: "Stop Gifs",
type: :boolean,
description: "Whether to pause animated images until they're hovered on"
} }
] ]
}, },

View file

@ -59,6 +59,7 @@ To add configuration to your config file, you can copy it from the base config.
* `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances. * `cleanup_attachments`: Remove attachments along with statuses. Does not affect duplicate files and attachments without status. Enabling this will increase load to database when deleting statuses on larger instances.
* `show_reactions`: Let favourites and emoji reactions be viewed through the API (default: `true`). * `show_reactions`: Let favourites and emoji reactions be viewed through the API (default: `true`).
* `password_reset_token_validity`: The time after which reset tokens aren't accepted anymore, in seconds (default: one day). * `password_reset_token_validity`: The time after which reset tokens aren't accepted anymore, in seconds (default: one day).
* `local_bubble`: Array of domains representing instances closely related to yours. Used to populate the `bubble` timeline. e.g `['example.com']`, (default: `[]`)
## :database ## :database
* `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes). * `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes).

View file

@ -67,8 +67,9 @@ defmodule Pleroma.UserRelationship do
target_id: target.id target_id: target.id
}) })
|> Repo.insert( |> Repo.insert(
on_conflict: {:replace_all_except, [:id]}, on_conflict: {:replace_all_except, [:id, :inserted_at]},
conflict_target: [:source_id, :relationship_type, :target_id] conflict_target: [:source_id, :relationship_type, :target_id],
returning: true
) )
end end

View file

@ -102,7 +102,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
{:scope, :eldap.wholeSubtree()}, {:scope, :eldap.wholeSubtree()},
{:timeout, @search_timeout} {:timeout, @search_timeout}
]) do ]) do
{:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _}} -> {:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _, _}} ->
params = %{ params = %{
name: name, name: name,
nickname: name, nickname: name,

View file

@ -16,7 +16,7 @@ defmodule Pleroma.Web.Push.Impl do
require Logger require Logger
import Ecto.Query import Ecto.Query
@types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact"] @types ["Create", "Follow", "Announce", "Like", "Move", "EmojiReact", "Update"]
@doc "Performs sending notifications for user subscriptions" @doc "Performs sending notifications for user subscriptions"
@spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type} @spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type}
@ -167,6 +167,15 @@ defmodule Pleroma.Web.Push.Impl do
end end
end end
def format_body(
%{activity: %{data: %{"type" => "Update"}}},
actor,
_object,
_mastodon_type
) do
"@#{actor.nickname} edited a status"
end
def format_title(activity, mastodon_type \\ nil) def format_title(activity, mastodon_type \\ nil)
def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do def format_title(%{activity: %{data: %{"directMessage" => true}}}, _mastodon_type) do
@ -180,6 +189,7 @@ defmodule Pleroma.Web.Push.Impl do
"follow_request" -> "New Follow Request" "follow_request" -> "New Follow Request"
"reblog" -> "New Repeat" "reblog" -> "New Repeat"
"favourite" -> "New Favorite" "favourite" -> "New Favorite"
"update" -> "New Update"
"pleroma:emoji_reaction" -> "New Reaction" "pleroma:emoji_reaction" -> "New Reaction"
type -> "New #{String.capitalize(type || "event")}" type -> "New #{String.capitalize(type || "event")}"
end end

BIN
priv/static/favicon.png Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7 KiB

After

Width:  |  Height:  |  Size: 7 KiB

View file

@ -61,6 +61,12 @@ defmodule Restarter.Pleroma do
{:noreply, @init_state} {:noreply, @init_state}
end end
# Don't actually restart during tests.
# We just check if the correct call has been done.
# If we actually restart, we get errors during the tests like
# (RuntimeError) could not lookup Ecto repo Pleroma.Repo because it was not started or
# it does not exist
# See tests in Pleroma.Config.TransferTaskTest
def handle_cast({:restart, :test, _}, state) do def handle_cast({:restart, :test, _}, state) do
Logger.debug("pleroma manually restarted") Logger.debug("pleroma manually restarted")
{:noreply, Map.put(state, :need_reboot, false)} {:noreply, Map.put(state, :need_reboot, false)}
@ -74,6 +80,12 @@ defmodule Restarter.Pleroma do
def handle_cast({:after_boot, _}, %{after_boot: true} = state), do: {:noreply, state} def handle_cast({:after_boot, _}, %{after_boot: true} = state), do: {:noreply, state}
# Don't actually restart during tests.
# We just check if the correct call has been done.
# If we actually restart, we get errors during the tests like
# (RuntimeError) could not lookup Ecto repo Pleroma.Repo because it was not started or
# it does not exist
# See tests in Pleroma.Config.TransferTaskTest
def handle_cast({:after_boot, :test}, state) do def handle_cast({:after_boot, :test}, state) do
Logger.debug("pleroma restarted after boot") Logger.debug("pleroma restarted after boot")
state = %{state | after_boot: true, rebooted: true} state = %{state | after_boot: true, rebooted: true}

View file

@ -13,7 +13,8 @@ defmodule Restarter.MixProject do
def application do def application do
[ [
mod: {Restarter, []} mod: {Restarter, []},
extra_applications: [:logger]
] ]
end end

View file

@ -119,44 +119,87 @@ defmodule Pleroma.Config.TransferTaskTest do
describe "pleroma restart" do describe "pleroma restart" do
setup do setup do
on_exit(fn -> Restarter.Pleroma.refresh() end) on_exit(fn ->
Restarter.Pleroma.refresh()
# Restarter.Pleroma.refresh/0 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
# See https://stackoverflow.com/questions/51361856/how-to-use-task-await-with-genserver
Restarter.Pleroma.rebooted?()
end)
end end
@tag :erratic
test "don't restart if no reboot time settings were changed" do test "don't restart if no reboot time settings were changed" do
clear_config(:emoji) clear_config(:emoji)
insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]]) insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]])
refute String.contains?( refute String.contains?(
capture_log(fn -> TransferTask.start_link([]) end), capture_log(fn ->
TransferTask.start_link([])
# TransferTask.start_link/1 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
Restarter.Pleroma.rebooted?()
end),
"pleroma restarted" "pleroma restarted"
) )
end end
@tag :erratic
test "on reboot time key" do test "on reboot time key" do
clear_config([:pleroma, :rate_limit]) clear_config(:rate_limit)
insert(:config, key: {:pleroma, :rate_limit}, value: [enabled: false]) insert(:config, key: :rate_limit, value: [enabled: false])
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
# Note that we don't actually restart Pleroma.
# See module Restarter.Pleroma
assert capture_log(fn ->
TransferTask.start_link([])
# TransferTask.start_link/1 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
Restarter.Pleroma.rebooted?()
end) =~ "pleroma restarted"
end end
@tag :erratic
test "on reboot time subkey" do test "on reboot time subkey" do
clear_config(Pleroma.Captcha) clear_config(Pleroma.Captcha)
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60]) insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
# Note that we don't actually restart Pleroma.
# See module Restarter.Pleroma
assert capture_log(fn ->
TransferTask.start_link([])
# TransferTask.start_link/1 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
Restarter.Pleroma.rebooted?()
end) =~ "pleroma restarted"
end end
@tag :erratic
test "don't restart pleroma on reboot time key and subkey if there is false flag" do test "don't restart pleroma on reboot time key and subkey if there is false flag" do
clear_config([:pleroma, :rate_limit]) clear_config(:rate_limit)
clear_config(Pleroma.Captcha) clear_config(Pleroma.Captcha)
insert(:config, key: {:pleroma, :rate_limit}, value: [enabled: false]) insert(:config, key: :rate_limit, value: [enabled: false])
insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60]) insert(:config, key: Pleroma.Captcha, value: [seconds_valid: 60])
refute String.contains?( refute String.contains?(
capture_log(fn -> TransferTask.load_and_update_env([], false) end), capture_log(fn ->
TransferTask.load_and_update_env([], false)
# TransferTask.start_link/1 is an asynchronous call.
# A GenServer will first finish the previous call before starting a new one.
# Here we do a synchronous call.
# That way we are sure that the previous call has finished before we continue.
Restarter.Pleroma.rebooted?()
end),
"pleroma restarted" "pleroma restarted"
) )
end end

View file

@ -122,11 +122,11 @@ defmodule Pleroma.Conversation.ParticipationTest do
end end
test "it marks a participation as read" do test "it marks a participation as read" do
participation = insert(:participation, %{read: false}) participation = insert(:participation, %{updated_at: ~N[2017-07-17 17:09:58], read: false})
{:ok, updated_participation} = Participation.mark_as_read(participation) {:ok, updated_participation} = Participation.mark_as_read(participation)
assert updated_participation.read assert updated_participation.read
assert updated_participation.updated_at == participation.updated_at assert :gt = NaiveDateTime.compare(updated_participation.updated_at, participation.updated_at)
end end
test "it marks a participation as unread" do test "it marks a participation as unread" do

View file

@ -5,8 +5,9 @@
defmodule Pleroma.UserRelationshipTest do defmodule Pleroma.UserRelationshipTest do
alias Pleroma.UserRelationship alias Pleroma.UserRelationship
use Pleroma.DataCase, async: true use Pleroma.DataCase, async: false
import Mock
import Pleroma.Factory import Pleroma.Factory
describe "*_exists?/2" do describe "*_exists?/2" do
@ -79,7 +80,12 @@ defmodule Pleroma.UserRelationshipTest do
end end
test "if record already exists, returns it", %{users: [user1, user2]} do test "if record already exists, returns it", %{users: [user1, user2]} do
user_block = UserRelationship.create_block(user1, user2) user_block =
with_mock NaiveDateTime, [:passthrough], utc_now: fn -> ~N[2017-03-17 17:09:58] end do
{:ok, %{inserted_at: ~N[2017-03-17 17:09:58]}} =
UserRelationship.create_block(user1, user2)
end
assert user_block == UserRelationship.create_block(user1, user2) assert user_block == UserRelationship.create_block(user1, user2)
end end
end end

View file

@ -3,9 +3,10 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
use Pleroma.Web.ConnCase, async: true use Pleroma.Web.ConnCase, async: false
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
import Mock
import Pleroma.Factory import Pleroma.Factory
alias Pleroma.Filter alias Pleroma.Filter
@ -53,25 +54,20 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
in_seconds = 600 in_seconds = 600
response = response =
conn with_mock NaiveDateTime, [:passthrough], utc_now: fn -> ~N[2017-03-17 17:09:58] end do
|> put_req_header("content-type", "application/json") conn
|> post("/api/v1/filters", %{ |> put_req_header("content-type", "application/json")
"phrase" => "knights", |> post("/api/v1/filters", %{
context: ["home"], "phrase" => "knights",
expires_in: in_seconds context: ["home"],
}) expires_in: in_seconds
|> json_response_and_validate_schema(200) })
|> json_response_and_validate_schema(200)
end
assert response["irreversible"] == false assert response["irreversible"] == false
expires_at = assert response["expires_at"] == "2017-03-17T17:19:58.000Z"
NaiveDateTime.utc_now()
|> NaiveDateTime.add(in_seconds)
assert NaiveDateTime.diff(
NaiveDateTime.from_iso8601!(response["expires_at"]),
expires_at
) < 5
filter = Filter.get(response["id"], user) filter = Filter.get(response["id"], user)
@ -183,26 +179,21 @@ defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
in_seconds = 600 in_seconds = 600
response = response =
conn with_mock NaiveDateTime, [:passthrough], utc_now: fn -> ~N[2017-03-17 17:09:58] end do
|> put_req_header("content-type", "application/json") conn
|> put("/api/v1/filters/#{filter.filter_id}", %{ |> put_req_header("content-type", "application/json")
phrase: "nii", |> put("/api/v1/filters/#{filter.filter_id}", %{
context: ["public"], phrase: "nii",
expires_in: in_seconds, context: ["public"],
irreversible: true expires_in: in_seconds,
}) irreversible: true
|> json_response_and_validate_schema(200) })
|> json_response_and_validate_schema(200)
end
assert response["irreversible"] == true assert response["irreversible"] == true
expected_time = assert response["expires_at"] == "2017-03-17T17:19:58.000Z"
NaiveDateTime.utc_now()
|> NaiveDateTime.add(in_seconds)
assert NaiveDateTime.diff(
NaiveDateTime.from_iso8601!(response["expires_at"]),
expected_time
) < 5
filter = Filter.get(response["id"], user) filter = Filter.get(response["id"], user)

View file

@ -71,7 +71,7 @@ defmodule Pleroma.Web.OAuth.LDAPAuthorizationTest do
equalityMatch: fn _type, _value -> :ok end, equalityMatch: fn _type, _value -> :ok end,
wholeSubtree: fn -> :ok end, wholeSubtree: fn -> :ok end,
search: fn _connection, _options -> search: fn _connection, _options ->
{:ok, {:eldap_search_result, [{:eldap_entry, '', []}], []}} {:ok, {:eldap_search_result, [{:eldap_entry, '', []}], [], []}}
end, end,
close: fn _connection -> close: fn _connection ->
send(self(), :close_connection) send(self(), :close_connection)

View file

@ -48,38 +48,42 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do
refute RateLimiter.disabled?(build_conn()) refute RateLimiter.disabled?(build_conn())
end end
@tag :erratic
test "it restricts based on config values" do test "it restricts based on config values" do
limiter_name = :test_plug_opts limiter_name = :test_plug_opts
scale = 80 scale = 80
limit = 5 limit = 5
clear_config([Pleroma.Web.Endpoint, :http, :ip], {8, 8, 8, 8}) clear_config([Pleroma.Web.Endpoint, :http, :ip], {127, 0, 0, 1})
clear_config([:rate_limit, limiter_name], {scale, limit}) clear_config([:rate_limit, limiter_name], {scale, limit})
plug_opts = RateLimiter.init(name: limiter_name) plug_opts = RateLimiter.init(name: limiter_name)
conn = build_conn(:get, "/") conn = build_conn(:get, "/")
for i <- 1..5 do for _ <- 1..5 do
conn = RateLimiter.call(conn, plug_opts) conn_limited = RateLimiter.call(conn, plug_opts)
assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
Process.sleep(10) refute conn_limited.status == Conn.Status.code(:too_many_requests)
refute conn_limited.resp_body
refute conn_limited.halted
end end
conn = RateLimiter.call(conn, plug_opts) conn_limited = RateLimiter.call(conn, plug_opts)
assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests) assert %{"error" => "Throttled"} = ConnTest.json_response(conn_limited, :too_many_requests)
assert conn.halted assert conn_limited.halted
Process.sleep(50) expire_ttl(conn, limiter_name)
conn = build_conn(:get, "/") for _ <- 1..5 do
conn_limited = RateLimiter.call(conn, plug_opts)
conn = RateLimiter.call(conn, plug_opts) refute conn_limited.status == Conn.Status.code(:too_many_requests)
assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts) refute conn_limited.resp_body
refute conn_limited.halted
end
refute conn.status == Conn.Status.code(:too_many_requests) conn_limited = RateLimiter.call(conn, plug_opts)
refute conn.resp_body assert %{"error" => "Throttled"} = ConnTest.json_response(conn_limited, :too_many_requests)
refute conn.halted assert conn_limited.halted
end end
describe "options" do describe "options" do
@ -263,4 +267,12 @@ defmodule Pleroma.Web.Plugs.RateLimiterTest do
refute {:err, :not_found} == RateLimiter.inspect_bucket(conn, limiter_name, opts) refute {:err, :not_found} == RateLimiter.inspect_bucket(conn, limiter_name, opts)
end end
def expire_ttl(%{remote_ip: remote_ip} = _conn, bucket_name_root) do
bucket_name = "anon:#{bucket_name_root}" |> String.to_atom()
key_name = "ip::#{remote_ip |> Tuple.to_list() |> Enum.join(".")}"
{:ok, bucket_value} = Cachex.get(bucket_name, key_name)
Cachex.put(bucket_name, key_name, bucket_value, ttl: -1)
end
end end

View file

@ -200,6 +200,21 @@ defmodule Pleroma.Web.Push.ImplTest do
"New Reaction" "New Reaction"
end end
test "renders title and body for update activity" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{status: "lorem ipsum"})
{:ok, activity} = CommonAPI.update(user, activity, %{status: "edited status"})
object = Object.normalize(activity, fetch: false)
assert Impl.format_body(%{activity: activity, type: "update"}, user, object) ==
"@#{user.nickname} edited a status"
assert Impl.format_title(%{activity: activity, type: "update"}) ==
"New Update"
end
test "renders title for create activity with direct visibility" do test "renders title for create activity with direct visibility" do
user = insert(:user, nickname: "Bob") user = insert(:user, nickname: "Bob")