forked from AkkomaGang/akkoma
Merge branch 'develop' into instance-info
This commit is contained in:
commit
f66ddf697b
17 changed files with 175 additions and 74 deletions
|
@ -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
|
||||
- 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
|
||||
|
||||
### Added
|
||||
|
|
|
@ -185,7 +185,7 @@
|
|||
adapter: []
|
||||
|
||||
config :pleroma, :instance,
|
||||
name: "Pleroma",
|
||||
name: "Akkoma",
|
||||
email: "example@example.com",
|
||||
notify_email: "noreply@example.com",
|
||||
description: "Akkoma: The cooler fediverse server",
|
||||
|
|
|
@ -1389,6 +1389,12 @@
|
|||
label: "Render misskey markdown",
|
||||
type: :boolean,
|
||||
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"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -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.
|
||||
* `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).
|
||||
* `local_bubble`: Array of domains representing instances closely related to yours. Used to populate the `bubble` timeline. e.g `['example.com']`, (default: `[]`)
|
||||
|
||||
## :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).
|
||||
|
|
|
@ -67,8 +67,9 @@ def create(relationship_type, %User{} = source, %User{} = target) do
|
|||
target_id: target.id
|
||||
})
|
||||
|> Repo.insert(
|
||||
on_conflict: {:replace_all_except, [:id]},
|
||||
conflict_target: [:source_id, :relationship_type, :target_id]
|
||||
on_conflict: {:replace_all_except, [:id, :inserted_at]},
|
||||
conflict_target: [:source_id, :relationship_type, :target_id],
|
||||
returning: true
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ defp register_user(connection, base, uid, name) do
|
|||
{:scope, :eldap.wholeSubtree()},
|
||||
{:timeout, @search_timeout}
|
||||
]) do
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _}} ->
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _, attributes}], _, _}} ->
|
||||
params = %{
|
||||
name: name,
|
||||
nickname: name,
|
||||
|
|
|
@ -16,7 +16,7 @@ defmodule Pleroma.Web.Push.Impl do
|
|||
require Logger
|
||||
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"
|
||||
@spec perform(Notification.t()) :: list(any) | :error | {:error, :unknown_type}
|
||||
|
@ -167,6 +167,15 @@ def format_body(
|
|||
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: %{data: %{"directMessage" => true}}}, _mastodon_type) do
|
||||
|
@ -180,6 +189,7 @@ def format_title(%{type: type}, mastodon_type) do
|
|||
"follow_request" -> "New Follow Request"
|
||||
"reblog" -> "New Repeat"
|
||||
"favourite" -> "New Favorite"
|
||||
"update" -> "New Update"
|
||||
"pleroma:emoji_reaction" -> "New Reaction"
|
||||
type -> "New #{String.capitalize(type || "event")}"
|
||||
end
|
||||
|
|
BIN
priv/static/favicon.png
Normal file
BIN
priv/static/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7 KiB |
|
@ -61,6 +61,12 @@ def handle_cast(:refresh, _state) do
|
|||
{:noreply, @init_state}
|
||||
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
|
||||
Logger.debug("pleroma manually restarted")
|
||||
{:noreply, Map.put(state, :need_reboot, false)}
|
||||
|
@ -74,6 +80,12 @@ def handle_cast({:restart, _, delay}, state) do
|
|||
|
||||
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
|
||||
Logger.debug("pleroma restarted after boot")
|
||||
state = %{state | after_boot: true, rebooted: true}
|
||||
|
|
|
@ -13,7 +13,8 @@ def project do
|
|||
|
||||
def application do
|
||||
[
|
||||
mod: {Restarter, []}
|
||||
mod: {Restarter, []},
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -119,44 +119,87 @@ test "transfer config values with full subkey update" do
|
|||
|
||||
describe "pleroma restart" 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
|
||||
|
||||
@tag :erratic
|
||||
test "don't restart if no reboot time settings were changed" do
|
||||
clear_config(:emoji)
|
||||
insert(:config, key: :emoji, value: [groups: [a: 1, b: 2]])
|
||||
|
||||
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"
|
||||
)
|
||||
end
|
||||
|
||||
@tag :erratic
|
||||
test "on reboot time key" do
|
||||
clear_config([:pleroma, :rate_limit])
|
||||
insert(:config, key: {:pleroma, :rate_limit}, value: [enabled: false])
|
||||
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
|
||||
clear_config(:rate_limit)
|
||||
insert(:config, key: :rate_limit, value: [enabled: false])
|
||||
|
||||
# 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
|
||||
|
||||
@tag :erratic
|
||||
test "on reboot time subkey" do
|
||||
clear_config(Pleroma.Captcha)
|
||||
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
|
||||
|
||||
@tag :erratic
|
||||
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)
|
||||
|
||||
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])
|
||||
|
||||
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"
|
||||
)
|
||||
end
|
||||
|
|
|
@ -122,11 +122,11 @@ test "recreating an existing participations sets it to unread" do
|
|||
end
|
||||
|
||||
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)
|
||||
|
||||
assert updated_participation.read
|
||||
assert updated_participation.updated_at == participation.updated_at
|
||||
assert :gt = NaiveDateTime.compare(updated_participation.updated_at, participation.updated_at)
|
||||
end
|
||||
|
||||
test "it marks a participation as unread" do
|
||||
|
|
|
@ -5,8 +5,9 @@
|
|||
defmodule Pleroma.UserRelationshipTest do
|
||||
alias Pleroma.UserRelationship
|
||||
|
||||
use Pleroma.DataCase, async: true
|
||||
use Pleroma.DataCase, async: false
|
||||
|
||||
import Mock
|
||||
import Pleroma.Factory
|
||||
|
||||
describe "*_exists?/2" do
|
||||
|
@ -79,7 +80,12 @@ test "creates user relationship record if it doesn't exist", %{users: [user1, us
|
|||
end
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.FilterControllerTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
use Pleroma.Web.ConnCase, async: false
|
||||
use Oban.Testing, repo: Pleroma.Repo
|
||||
|
||||
import Mock
|
||||
import Pleroma.Factory
|
||||
|
||||
alias Pleroma.Filter
|
||||
|
@ -53,25 +54,20 @@ test "a filter with expires_in", %{conn: conn, user: user} do
|
|||
in_seconds = 600
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/filters", %{
|
||||
"phrase" => "knights",
|
||||
context: ["home"],
|
||||
expires_in: in_seconds
|
||||
})
|
||||
|> json_response_and_validate_schema(200)
|
||||
with_mock NaiveDateTime, [:passthrough], utc_now: fn -> ~N[2017-03-17 17:09:58] end do
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> post("/api/v1/filters", %{
|
||||
"phrase" => "knights",
|
||||
context: ["home"],
|
||||
expires_in: in_seconds
|
||||
})
|
||||
|> json_response_and_validate_schema(200)
|
||||
end
|
||||
|
||||
assert response["irreversible"] == false
|
||||
|
||||
expires_at =
|
||||
NaiveDateTime.utc_now()
|
||||
|> NaiveDateTime.add(in_seconds)
|
||||
|
||||
assert NaiveDateTime.diff(
|
||||
NaiveDateTime.from_iso8601!(response["expires_at"]),
|
||||
expires_at
|
||||
) < 5
|
||||
assert response["expires_at"] == "2017-03-17T17:19:58.000Z"
|
||||
|
||||
filter = Filter.get(response["id"], user)
|
||||
|
||||
|
@ -183,26 +179,21 @@ test "with adding expires_at", %{conn: conn, user: user} do
|
|||
in_seconds = 600
|
||||
|
||||
response =
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/api/v1/filters/#{filter.filter_id}", %{
|
||||
phrase: "nii",
|
||||
context: ["public"],
|
||||
expires_in: in_seconds,
|
||||
irreversible: true
|
||||
})
|
||||
|> json_response_and_validate_schema(200)
|
||||
with_mock NaiveDateTime, [:passthrough], utc_now: fn -> ~N[2017-03-17 17:09:58] end do
|
||||
conn
|
||||
|> put_req_header("content-type", "application/json")
|
||||
|> put("/api/v1/filters/#{filter.filter_id}", %{
|
||||
phrase: "nii",
|
||||
context: ["public"],
|
||||
expires_in: in_seconds,
|
||||
irreversible: true
|
||||
})
|
||||
|> json_response_and_validate_schema(200)
|
||||
end
|
||||
|
||||
assert response["irreversible"] == true
|
||||
|
||||
expected_time =
|
||||
NaiveDateTime.utc_now()
|
||||
|> NaiveDateTime.add(in_seconds)
|
||||
|
||||
assert NaiveDateTime.diff(
|
||||
NaiveDateTime.from_iso8601!(response["expires_at"]),
|
||||
expected_time
|
||||
) < 5
|
||||
assert response["expires_at"] == "2017-03-17T17:19:58.000Z"
|
||||
|
||||
filter = Filter.get(response["id"], user)
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ test "creates a new user after successful LDAP authorization" do
|
|||
equalityMatch: fn _type, _value -> :ok end,
|
||||
wholeSubtree: fn -> :ok end,
|
||||
search: fn _connection, _options ->
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, '', []}], []}}
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, '', []}], [], []}}
|
||||
end,
|
||||
close: fn _connection ->
|
||||
send(self(), :close_connection)
|
||||
|
|
|
@ -48,38 +48,42 @@ test "it is enabled if remote_ip_found flag doesn't exist" do
|
|||
refute RateLimiter.disabled?(build_conn())
|
||||
end
|
||||
|
||||
@tag :erratic
|
||||
test "it restricts based on config values" do
|
||||
limiter_name = :test_plug_opts
|
||||
scale = 80
|
||||
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})
|
||||
|
||||
plug_opts = RateLimiter.init(name: limiter_name)
|
||||
conn = build_conn(:get, "/")
|
||||
|
||||
for i <- 1..5 do
|
||||
conn = RateLimiter.call(conn, plug_opts)
|
||||
assert {^i, _} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
|
||||
Process.sleep(10)
|
||||
for _ <- 1..5 do
|
||||
conn_limited = RateLimiter.call(conn, plug_opts)
|
||||
|
||||
refute conn_limited.status == Conn.Status.code(:too_many_requests)
|
||||
refute conn_limited.resp_body
|
||||
refute conn_limited.halted
|
||||
end
|
||||
|
||||
conn = RateLimiter.call(conn, plug_opts)
|
||||
assert %{"error" => "Throttled"} = ConnTest.json_response(conn, :too_many_requests)
|
||||
assert conn.halted
|
||||
conn_limited = RateLimiter.call(conn, plug_opts)
|
||||
assert %{"error" => "Throttled"} = ConnTest.json_response(conn_limited, :too_many_requests)
|
||||
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)
|
||||
assert {1, 4} = RateLimiter.inspect_bucket(conn, limiter_name, plug_opts)
|
||||
refute conn_limited.status == Conn.Status.code(:too_many_requests)
|
||||
refute conn_limited.resp_body
|
||||
refute conn_limited.halted
|
||||
end
|
||||
|
||||
refute conn.status == Conn.Status.code(:too_many_requests)
|
||||
refute conn.resp_body
|
||||
refute conn.halted
|
||||
conn_limited = RateLimiter.call(conn, plug_opts)
|
||||
assert %{"error" => "Throttled"} = ConnTest.json_response(conn_limited, :too_many_requests)
|
||||
assert conn_limited.halted
|
||||
end
|
||||
|
||||
describe "options" do
|
||||
|
@ -263,4 +267,12 @@ test "doesn't crash due to a race condition when multiple requests are made at t
|
|||
|
||||
refute {:err, :not_found} == RateLimiter.inspect_bucket(conn, limiter_name, opts)
|
||||
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
|
||||
|
|
|
@ -200,6 +200,21 @@ test "renders title and body for pleroma:emoji_reaction activity" do
|
|||
"New Reaction"
|
||||
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
|
||||
user = insert(:user, nickname: "Bob")
|
||||
|
||||
|
|
Loading…
Reference in a new issue