From: rinpatch Date: Fri, 16 Apr 2021 09:53:47 +0000 (+0000) Subject: Merge branch 'feature/521-pinned-post-federation' into 'develop' X-Git-Url: http://git.squeep.com/?a=commitdiff_plain;h=79376b4afb8bba0766cb3d04179aeaf4c0b7000b;hp=1885268c9c242aca2a51bd15ed839bd65d6a52dc;p=akkoma Merge branch 'feature/521-pinned-post-federation' into 'develop' Pinned posts federation Closes #521 See merge request pleroma/pleroma!3312 --- diff --git a/CHANGELOG.md b/CHANGELOG.md index fb26c7a73..9b0678023 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,13 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## Unreleased +### Changed + - The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change. +### Added + +- MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded. + ## Unreleased (Patch) ### Fixed - Try to save exported ConfigDB settings (migrate_from_db) in the system temp directory if default location is not writable. +- Uploading custom instance thumbnail via AdminAPI/AdminFE generated invalid URL to the image +- Applying ConcurrentLimiter settings via AdminAPI +- User login failures if their `notification_settings` were in a NULL state. ## [2.3.0] - 2020-03-01 diff --git a/config/config.exs b/config/config.exs index 8d1e17b42..4381068ac 100644 --- a/config/config.exs +++ b/config/config.exs @@ -409,6 +409,8 @@ config :pleroma, :mrf_object_age, threshold: 604_800, actions: [:delist, :strip_followers] +config :pleroma, :mrf_follow_bot, follower_nickname: nil + config :pleroma, :rich_media, enabled: true, ignore_hosts: [], diff --git a/config/description.exs b/config/description.exs index 41e5e4056..bb1f43305 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2942,6 +2942,23 @@ config :pleroma, :config_description, [ } ] }, + %{ + group: :pleroma, + key: :mrf_follow_bot, + tab: :mrf, + related_policy: "Pleroma.Web.ActivityPub.MRF.FollowBotPolicy", + label: "MRF FollowBot Policy", + type: :group, + description: "Automatically follows newly discovered accounts.", + children: [ + %{ + key: :follower_nickname, + type: :string, + description: "The name of the bot account to use for following newly discovered users.", + suggestions: ["followbot"] + } + ] + }, %{ group: :pleroma, key: :modules, diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 8f2c4347e..069421722 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -124,6 +124,7 @@ To add configuration to your config file, you can copy it from the base config. * `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)). * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections. * `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines. + * `Pleroma.Web.ActivityPub.MRF.FollowBotPolicy`: Automatically follows newly discovered users from the specified bot account. Local accounts, locked accounts, and users with "#nobot" in their bio are respected and excluded from being followed. * `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo). * `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value. @@ -220,6 +221,11 @@ Notes: - The hashtags in the configuration do not have a leading `#`. - This MRF Policy is always enabled, if you want to disable it you have to set empty lists +#### :mrf_follow_bot + +* `follower_nickname`: The name of the bot account to use for following newly discovered users. Using `followbot` or similar is strongly suggested. + + ### :activitypub * `unfollow_blocked`: Whether blocks result in people getting unfollowed * `outgoing_blocks`: Whether to federate blocks to other instances diff --git a/docs/index.md b/docs/index.md index 1a90d0a8d..80c5d2631 100644 --- a/docs/index.md +++ b/docs/index.md @@ -20,7 +20,7 @@ The default front-end used by Pleroma is Pleroma-FE. You can find more informati ### Mastodon interface If the Pleroma interface isn't your thing, or you're just trying something new but you want to keep using the familiar Mastodon interface, we got that too! -Just add a "/web" after your instance url (e.g. ) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC! +Just add a "/web" after your instance url (e.g. ) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC! The Mastodon interface is from the Glitch-soc fork. For more information on the Mastodon interface you can check the [Mastodon](https://docs.joinmastodon.org/) and [Glitch-soc](https://glitch-soc.github.io/docs/) documentation. Remember, what you see is only the frontend part of Mastodon, the backend is still Pleroma. diff --git a/lib/pleroma/config/release_runtime_provider.ex b/lib/pleroma/config/release_runtime_provider.ex index 8227195dc..e5e9d3dcd 100644 --- a/lib/pleroma/config/release_runtime_provider.ex +++ b/lib/pleroma/config/release_runtime_provider.ex @@ -1,6 +1,6 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do @moduledoc """ - Imports `runtime.exs` and `{env}.exported_from_db.secret.exs` for elixir releases. + Imports runtime config and `{env}.exported_from_db.secret.exs` for releases. """ @behaviour Config.Provider @@ -8,10 +8,11 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do def init(opts), do: opts @impl true - def load(config, _opts) do + def load(config, opts) do with_defaults = Config.Reader.merge(config, Pleroma.Config.Holder.release_defaults()) - config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs" + config_path = + opts[:config_path] || System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs" with_runtime_config = if File.exists?(config_path) do @@ -24,7 +25,7 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do warning = [ IO.ANSI.red(), IO.ANSI.bright(), - "!!! #{config_path} not found! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file", + "!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file", IO.ANSI.reset() ] @@ -33,13 +34,14 @@ defmodule Pleroma.Config.ReleaseRuntimeProvider do end exported_config_path = - config_path - |> Path.dirname() - |> Path.join("prod.exported_from_db.secret.exs") + opts[:exported_config_path] || + config_path + |> Path.dirname() + |> Path.join("#{Pleroma.Config.get(:env)}.exported_from_db.secret.exs") with_exported = if File.exists?(exported_config_path) do - exported_config = Config.Reader.read!(with_runtime_config) + exported_config = Config.Reader.read!(exported_config_path) Config.Reader.merge(with_runtime_config, exported_config) else with_runtime_config diff --git a/lib/pleroma/config_db.ex b/lib/pleroma/config_db.ex index b874e0e37..cb57673e3 100644 --- a/lib/pleroma/config_db.ex +++ b/lib/pleroma/config_db.ex @@ -387,6 +387,6 @@ defmodule Pleroma.ConfigDB do @spec module_name?(String.t()) :: boolean() def module_name?(string) do Regex.match?(~r/^(Pleroma|Phoenix|Tesla|Quack|Ueberauth|Swoosh)\./, string) or - string in ["Oban", "Ueberauth", "ExSyslogger"] + string in ["Oban", "Ueberauth", "ExSyslogger", "ConcurrentLimiter"] end end diff --git a/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex new file mode 100644 index 000000000..7307c9c14 --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/follow_bot_policy.ex @@ -0,0 +1,59 @@ +defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicy do + @behaviour Pleroma.Web.ActivityPub.MRF + alias Pleroma.Config + alias Pleroma.User + alias Pleroma.Web.CommonAPI + + require Logger + + @impl true + def filter(message) do + with follower_nickname <- Config.get([:mrf_follow_bot, :follower_nickname]), + %User{actor_type: "Service"} = follower <- + User.get_cached_by_nickname(follower_nickname), + %{"type" => "Create", "object" => %{"type" => "Note"}} <- message do + try_follow(follower, message) + else + nil -> + Logger.warn( + "#{__MODULE__} skipped because of missing `:mrf_follow_bot, :follower_nickname` configuration, the :follower_nickname + account does not exist, or the account is not correctly configured as a bot." + ) + + {:ok, message} + + _ -> + {:ok, message} + end + end + + defp try_follow(follower, message) do + to = Map.get(message, "to", []) + cc = Map.get(message, "cc", []) + actor = [message["actor"]] + + Enum.concat([to, cc, actor]) + |> List.flatten() + |> Enum.uniq() + |> User.get_all_by_ap_id() + |> Enum.each(fn user -> + with false <- user.local, + false <- User.following?(follower, user), + false <- User.locked?(user), + false <- (user.bio || "") |> String.downcase() |> String.contains?("nobot") do + Logger.debug( + "#{__MODULE__}: Follow request from #{follower.nickname} to #{user.nickname}" + ) + + CommonAPI.follow(follower, user) + end + end) + + {:ok, message} + end + + @impl true + def describe do + {:ok, %{}} + end +end diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index 14c3e8531..25df36cae 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -38,37 +38,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do @impl true def validate(object, meta) - def validate(%{"type" => type} = object, meta) - when type in ~w[Accept Reject] do - with {:ok, object} <- - object - |> AcceptRejectValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Event"} = object, meta) do - with {:ok, object} <- - object - |> EventValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Follow"} = object, meta) do - with {:ok, object} <- - object - |> FollowValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - def validate(%{"type" => "Block"} = block_activity, meta) do with {:ok, block_activity} <- block_activity @@ -88,16 +57,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end end - def validate(%{"type" => "Update"} = update_activity, meta) do - with {:ok, update_activity} <- - update_activity - |> UpdateValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - update_activity = stringify_keys(update_activity) - {:ok, update_activity, meta} - end - end - def validate(%{"type" => "Undo"} = object, meta) do with {:ok, object} <- object @@ -124,76 +83,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end end - def validate(%{"type" => "Like"} = object, meta) do - with {:ok, object} <- - object - |> LikeValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "ChatMessage"} = object, meta) do - with {:ok, object} <- - object - |> ChatMessageValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Question"} = object, meta) do - with {:ok, object} <- - object - |> QuestionValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => type} = object, meta) when type in ~w[Audio Video] do - with {:ok, object} <- - object - |> AudioVideoValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Article"} = object, meta) do - with {:ok, object} <- - object - |> ArticleNoteValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "Answer"} = object, meta) do - with {:ok, object} <- - object - |> AnswerValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - - def validate(%{"type" => "EmojiReact"} = object, meta) do - with {:ok, object} <- - object - |> EmojiReactValidator.cast_and_validate() - |> Ecto.Changeset.apply_action(:insert) do - object = stringify_keys(object) - {:ok, object, meta} - end - end - def validate( %{"type" => "Create", "object" => %{"type" => "ChatMessage"} = object} = create_activity, meta @@ -225,10 +114,30 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do end end - def validate(%{"type" => "Announce"} = object, meta) do + def validate(%{"type" => type} = object, meta) + when type in ~w[Accept Reject Follow Update Like EmojiReact Announce + Event ChatMessage Question Audio Video Article Answer] do + validator = + case type do + "Accept" -> AcceptRejectValidator + "Reject" -> AcceptRejectValidator + "Follow" -> FollowValidator + "Update" -> UpdateValidator + "Like" -> LikeValidator + "EmojiReact" -> EmojiReactValidator + "Announce" -> AnnounceValidator + "Event" -> EventValidator + "ChatMessage" -> ChatMessageValidator + "Question" -> QuestionValidator + "Audio" -> AudioVideoValidator + "Video" -> AudioVideoValidator + "Article" -> ArticleNoteValidator + "Answer" -> AnswerValidator + end + with {:ok, object} <- object - |> AnnounceValidator.cast_and_validate() + |> validator.cast_and_validate() |> Ecto.Changeset.apply_action(:insert) do object = stringify_keys(object) {:ok, object, meta} diff --git a/lib/pleroma/web/mastodon_api/views/instance_view.ex b/lib/pleroma/web/mastodon_api/views/instance_view.ex index 73205fb6d..dac68d8e6 100644 --- a/lib/pleroma/web/mastodon_api/views/instance_view.ex +++ b/lib/pleroma/web/mastodon_api/views/instance_view.ex @@ -23,7 +23,8 @@ defmodule Pleroma.Web.MastodonAPI.InstanceView do streaming_api: Pleroma.Web.Endpoint.websocket_url() }, stats: Pleroma.Stats.get_stats(), - thumbnail: Pleroma.Web.base_url() <> Keyword.get(instance, :instance_thumbnail), + thumbnail: + URI.merge(Pleroma.Web.base_url(), Keyword.get(instance, :instance_thumbnail)) |> to_string, languages: ["en"], registrations: Keyword.get(instance, :registrations_open), approval_required: Keyword.get(instance, :account_approval_required), diff --git a/mix.exs b/mix.exs index ae74f50a3..fe5d9d963 100644 --- a/mix.exs +++ b/mix.exs @@ -38,7 +38,7 @@ defmodule Pleroma.Mixfile do include_executables_for: [:unix], applications: [ex_syslogger: :load, syslog: :load, eldap: :transient], steps: [:assemble, &put_otp_version/1, ©_files/1, ©_nginx_config/1], - config_providers: [{Pleroma.Config.ReleaseRuntimeProvider, nil}] + config_providers: [{Pleroma.Config.ReleaseRuntimeProvider, []}] ] ] ] diff --git a/priv/repo/migrations/20210401143153_user_notification_settings_fix.exs b/priv/repo/migrations/20210401143153_user_notification_settings_fix.exs new file mode 100644 index 000000000..cf68f1be6 --- /dev/null +++ b/priv/repo/migrations/20210401143153_user_notification_settings_fix.exs @@ -0,0 +1,17 @@ +defmodule Pleroma.Repo.Migrations.UserNotificationSettingsFix do + use Ecto.Migration + + def up do + execute(~s(UPDATE users + SET + notification_settings = '{"followers": true, "follows": true, "non_follows": true, "non_followers": true}'::jsonb WHERE notification_settings IS NULL +)) + + execute("ALTER TABLE users + ALTER COLUMN notification_settings SET NOT NULL") + end + + def down do + :ok + end +end diff --git a/test/fixtures/config/temp.exported_from_db.secret.exs b/test/fixtures/config/temp.exported_from_db.secret.exs new file mode 100644 index 000000000..64bee7f32 --- /dev/null +++ b/test/fixtures/config/temp.exported_from_db.secret.exs @@ -0,0 +1,5 @@ +use Mix.Config + +config :pleroma, exported_config_merged: true + +config :pleroma, :first_setting, key: "new value" diff --git a/test/pleroma/config/release_runtime_provider_test.exs b/test/pleroma/config/release_runtime_provider_test.exs new file mode 100644 index 000000000..6578d3268 --- /dev/null +++ b/test/pleroma/config/release_runtime_provider_test.exs @@ -0,0 +1,45 @@ +defmodule Pleroma.Config.ReleaseRuntimeProviderTest do + use ExUnit.Case, async: true + + alias Pleroma.Config.ReleaseRuntimeProvider + + describe "load/2" do + test "loads release defaults config and warns about non-existent runtime config" do + ExUnit.CaptureIO.capture_io(fn -> + merged = ReleaseRuntimeProvider.load([], []) + assert merged == Pleroma.Config.Holder.release_defaults() + end) =~ + "!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file" + end + + test "merged runtime config" do + merged = + ReleaseRuntimeProvider.load([], config_path: "test/fixtures/config/temp.secret.exs") + + assert merged[:pleroma][:first_setting] == [key: "value", key2: [Pleroma.Repo]] + assert merged[:pleroma][:second_setting] == [key: "value2", key2: ["Activity"]] + end + + test "merged exported config" do + ExUnit.CaptureIO.capture_io(fn -> + merged = + ReleaseRuntimeProvider.load([], + exported_config_path: "test/fixtures/config/temp.exported_from_db.secret.exs" + ) + + assert merged[:pleroma][:exported_config_merged] + end) =~ + "!!! Config path is not declared! Please ensure it exists and that PLEROMA_CONFIG_PATH is unset or points to an existing file" + end + + test "runtime config is merged with exported config" do + merged = + ReleaseRuntimeProvider.load([], + config_path: "test/fixtures/config/temp.secret.exs", + exported_config_path: "test/fixtures/config/temp.exported_from_db.secret.exs" + ) + + assert merged[:pleroma][:first_setting] == [key2: [Pleroma.Repo], key: "new value"] + end + end +end diff --git a/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs b/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs new file mode 100644 index 000000000..a61562558 --- /dev/null +++ b/test/pleroma/web/activity_pub/mrf/follow_bot_policy_test.exs @@ -0,0 +1,126 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.FollowBotPolicyTest do + use Pleroma.DataCase, async: true + + alias Pleroma.User + alias Pleroma.Web.ActivityPub.MRF.FollowBotPolicy + + import Pleroma.Factory + + describe "FollowBotPolicy" do + test "follows remote users" do + bot = insert(:user, actor_type: "Service") + remote_user = insert(:user, local: false) + clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [remote_user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "content" => "Test post", + "type" => "Note", + "attributedTo" => remote_user.ap_id, + "inReplyTo" => nil + }, + "actor" => remote_user.ap_id + } + + refute User.following?(bot, remote_user) + + assert User.get_follow_requests(remote_user) |> length == 0 + + FollowBotPolicy.filter(message) + + assert User.get_follow_requests(remote_user) |> length == 1 + end + + test "does not follow users with #nobot in bio" do + bot = insert(:user, actor_type: "Service") + remote_user = insert(:user, %{local: false, bio: "go away bots! #nobot"}) + clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [remote_user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "content" => "I don't like follow bots", + "type" => "Note", + "attributedTo" => remote_user.ap_id, + "inReplyTo" => nil + }, + "actor" => remote_user.ap_id + } + + refute User.following?(bot, remote_user) + + assert User.get_follow_requests(remote_user) |> length == 0 + + FollowBotPolicy.filter(message) + + assert User.get_follow_requests(remote_user) |> length == 0 + end + + test "does not follow local users" do + bot = insert(:user, actor_type: "Service") + local_user = insert(:user, local: true) + clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [local_user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "content" => "Hi I'm a local user", + "type" => "Note", + "attributedTo" => local_user.ap_id, + "inReplyTo" => nil + }, + "actor" => local_user.ap_id + } + + refute User.following?(bot, local_user) + + assert User.get_follow_requests(local_user) |> length == 0 + + FollowBotPolicy.filter(message) + + assert User.get_follow_requests(local_user) |> length == 0 + end + + test "does not follow users requiring follower approval" do + bot = insert(:user, actor_type: "Service") + remote_user = insert(:user, %{local: false, is_locked: true}) + clear_config([:mrf_follow_bot, :follower_nickname], bot.nickname) + + message = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => [remote_user.follower_address], + "cc" => ["https://www.w3.org/ns/activitystreams#Public"], + "type" => "Create", + "object" => %{ + "content" => "I don't like randos following me", + "type" => "Note", + "attributedTo" => remote_user.ap_id, + "inReplyTo" => nil + }, + "actor" => remote_user.ap_id + } + + refute User.following?(bot, remote_user) + + assert User.get_follow_requests(remote_user) |> length == 0 + + FollowBotPolicy.filter(message) + + assert User.get_follow_requests(remote_user) |> length == 0 + end + end +end diff --git a/test/pleroma/web/admin_api/controllers/config_controller_test.exs b/test/pleroma/web/admin_api/controllers/config_controller_test.exs index 578a4c914..c39c1b1e1 100644 --- a/test/pleroma/web/admin_api/controllers/config_controller_test.exs +++ b/test/pleroma/web/admin_api/controllers/config_controller_test.exs @@ -1410,6 +1410,82 @@ defmodule Pleroma.Web.AdminAPI.ConfigControllerTest do "need_reboot" => false } end + + test "custom instance thumbnail", %{conn: conn} do + clear_config([:instance]) + + params = %{ + "group" => ":pleroma", + "key" => ":instance", + "value" => [ + %{ + "tuple" => [ + ":instance_thumbnail", + "https://example.com/media/new_thumbnail.jpg" + ] + } + ] + } + + res = + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{"configs" => [params]}) + |> json_response_and_validate_schema(200) + + assert res == %{ + "configs" => [ + %{ + "db" => [":instance_thumbnail"], + "group" => ":pleroma", + "key" => ":instance", + "value" => params["value"] + } + ], + "need_reboot" => false + } + + _res = + assert conn + |> get("/api/v1/instance") + |> json_response_and_validate_schema(200) + + assert res = %{"thumbnail" => "https://example.com/media/new_thumbnail.jpg"} + end + + test "Concurrent Limiter", %{conn: conn} do + clear_config([ConcurrentLimiter]) + + params = %{ + "group" => ":pleroma", + "key" => "ConcurrentLimiter", + "value" => [ + %{ + "tuple" => [ + "Pleroma.Web.RichMedia.Helpers", + [ + %{"tuple" => [":max_running", 6]}, + %{"tuple" => [":max_waiting", 6]} + ] + ] + }, + %{ + "tuple" => [ + "Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy", + [ + %{"tuple" => [":max_running", 7]}, + %{"tuple" => [":max_waiting", 7]} + ] + ] + } + ] + } + + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{"configs" => [params]}) + |> json_response_and_validate_schema(200) + end end describe "GET /api/pleroma/admin/config/descriptions" do