Merge branch 'develop' into issue/1469

This commit is contained in:
Maksim Pechnikov 2020-02-06 10:56:37 +03:00
commit f8bd85fb7e
22 changed files with 388 additions and 114 deletions

View File

@ -9,10 +9,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- **Breaking**: OStatus protocol support - **Breaking**: OStatus protocol support
- **Breaking**: MDII uploader - **Breaking**: MDII uploader
- **Breaking**: Using third party engines for user recommendation - **Breaking**: Using third party engines for user recommendation
<details>
<summary>API Changes</summary>
- **Breaking**: AdminAPI: migrate_from_db endpoint
</details>
### Changed ### Changed
- **Breaking:** Pleroma won't start if it detects unapplied migrations - **Breaking:** Pleroma won't start if it detects unapplied migrations
- **Breaking:** attachments are removed along with statuses. Does not affect duplicate files and attachments without status.
- **Breaking:** Elixir >=1.8 is now required (was >= 1.7) - **Breaking:** Elixir >=1.8 is now required (was >= 1.7)
- **Breaking:** `Pleroma.Plugs.RemoteIp` and `:rate_limiter` enabled by default. Please ensure your reverse proxy forwards the real IP! - **Breaking:** `Pleroma.Plugs.RemoteIp` and `:rate_limiter` enabled by default. Please ensure your reverse proxy forwards the real IP!
- **Breaking:** attachment links (`config :pleroma, :instance, no_attachment_links` and `config :pleroma, Pleroma.Upload, link_name`) disabled by default - **Breaking:** attachment links (`config :pleroma, :instance, no_attachment_links` and `config :pleroma, Pleroma.Upload, link_name`) disabled by default
@ -54,6 +57,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added ### Added
- `:chat_limit` option to limit chat characters. - `:chat_limit` option to limit chat characters.
- `cleanup_attachments` option to 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.
- Refreshing poll results for remote polls - Refreshing poll results for remote polls
- Authentication: Added rate limit for password-authorized actions / login existence checks - Authentication: Added rate limit for password-authorized actions / login existence checks
- Static Frontend: Add the ability to render user profiles and notices server-side without requiring JS app. - Static Frontend: Add the ability to render user profiles and notices server-side without requiring JS app.

View File

@ -271,7 +271,8 @@
account_field_name_length: 512, account_field_name_length: 512,
account_field_value_length: 2048, account_field_value_length: 2048,
external_user_synchronization: true, external_user_synchronization: true,
extended_nickname_format: true extended_nickname_format: true,
cleanup_attachments: false
config :pleroma, :feed, config :pleroma, :feed,
post_title: %{ post_title: %{
@ -508,7 +509,6 @@
config :auto_linker, config :auto_linker,
opts: [ opts: [
scheme: true,
extra: true, extra: true,
# TODO: Set to :no_scheme when it works properly # TODO: Set to :no_scheme when it works properly
validate_tld: true, validate_tld: true,

View File

@ -764,6 +764,15 @@
"Set to `true` to use extended local nicknames format (allows underscores/dashes)." <> "Set to `true` to use extended local nicknames format (allows underscores/dashes)." <>
" This will break federation with older software for theses nicknames." " This will break federation with older software for theses nicknames."
}, },
%{
key: :cleanup_attachments,
type: :boolean,
description: """
"Set to `true` to remove associated attachments when status is removed.
This will not affect duplicates and attachments without status.
Enabling this will increase load to database when deleting statuses on larger instances.
"""
},
%{ %{
key: :max_pinned_statuses, key: :max_pinned_statuses,
type: :integer, type: :integer,
@ -2171,11 +2180,6 @@
type: :boolean, type: :boolean,
description: "Set to `false` to remove target='_blank' attribute" description: "Set to `false` to remove target='_blank' attribute"
}, },
%{
key: :scheme,
type: :boolean,
description: "Set to `true` to link urls with schema http://google.com"
},
%{ %{
key: :truncate, key: :truncate,
type: [:integer, false], type: [:integer, false],

View File

@ -665,11 +665,9 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- 404 Not Found `"Not found"` - 404 Not Found `"Not found"`
- On success: 200 OK `{}` - On success: 200 OK `{}`
## `GET /api/pleroma/admin/config/migrate_from_db` ## `GET /api/pleroma/admin/restart`
### Run mix task pleroma.config migrate_from_db ### Restarts pleroma application
Copies all settings from database to `config/{env}.exported_from_db.secret.exs` with deletion from the table. Where `{env}` is the environment in which `pleroma` is running.
- Params: none - Params: none
- Response: - Response:

View File

@ -6,11 +6,7 @@ config :pleroma, configurable_from_database: true
``` ```
## How it works ## How it works
Settings are stored in database and are applied in `runtime` after each change. Most of the settings take effect immediately, except some, which need instance reboot. These settings are needed in `compile time`, that's why settings are duplicated to the file. Settings are stored in database and are applied in `runtime` after each change. Most of the settings take effect immediately, except some, which need instance reboot.
File with duplicated settings is located in `config/{env}.exported_from_db.exs` if pleroma is runned from source. For prod env it will be `config/prod.exported_from_db.exs`.
For releases: `/etc/pleroma/prod.exported_from_db.secret.exs` or `PLEROMA_CONFIG_PATH/prod.exported_from_db.exs`.
## How to set it up ## How to set it up
You need to migrate your existing settings to the database. This task will migrate only added by user settings. You need to migrate your existing settings to the database. This task will migrate only added by user settings.
@ -25,7 +21,7 @@ You can do this with mix task (all config files will remain untouched):
mix pleroma.config migrate_to_db mix pleroma.config migrate_to_db
``` ```
Now you can change settings in admin interface. After each save, settings from database are duplicated to the `config/{env}.exported_from_db.exs` file. Now you can change settings in admin interface. If `reboot time` settings were changed, pleroma must be rebooted.
<span style="color:red">**ATTENTION**</span> <span style="color:red">**ATTENTION**</span>
@ -35,10 +31,19 @@ Now you can change settings in admin interface. After each save, settings from d
- all settings inside these keys: - all settings inside these keys:
- `:hackney_pools` - `:hackney_pools`
- `:chat` - `:chat`
- `Oban`
- `:rate_limit`
- `:markup`
- `:streamer`
- partially settings inside these keys: - partially settings inside these keys:
- `:seconds_valid` in `Pleroma.Captcha` - `:seconds_valid` in `Pleroma.Captcha`
- `:proxy_remote` in `Pleroma.Upload` - `:proxy_remote` in `Pleroma.Upload`
- `:upload_limit` in `:instance` - `:upload_limit` in `:instance`
- `:digest` in `:email_notifications`
- `:clean_expired_tokens` in `:oauth2`
- `:enabled` in `Pleroma.ActivityExpiration`
- `:enabled` in `Pleroma.ScheduledActivity`
- `:enabled` in `:gopher`
## How to dump settings from database to file ## How to dump settings from database to file
@ -59,7 +64,7 @@ mix pleroma.config migrate_from_db [-d]
```sql ```sql
TRUNCATE TABLE config; TRUNCATE TABLE config;
``` ```
2. Delete `config/{env}.exported_from_db.exs`. 2. If migrate_from_db task was runned, backup and delete `config/{env}.exported_from_db.exs`.
For `prod` env: For `prod` env:
```bash ```bash

View File

@ -69,6 +69,7 @@ You shouldn't edit the base config directly to avoid breakages and merge conflic
* `account_field_name_length`: An account field name maximum length (default: `512`). * `account_field_name_length`: An account field name maximum length (default: `512`).
* `account_field_value_length`: An account field value maximum length (default: `2048`). * `account_field_value_length`: An account field value maximum length (default: `2048`).
* `external_user_synchronization`: Enabling following/followers counters synchronization for external users. * `external_user_synchronization`: Enabling following/followers counters synchronization for external users.
* `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.
## Federation ## Federation
### MRF policies ### MRF policies

View File

@ -3,8 +3,6 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Loader do defmodule Pleroma.Config.Loader do
@paths ["config/config.exs", "config/#{Mix.env()}.exs"]
@reject_keys [ @reject_keys [
Pleroma.Repo, Pleroma.Repo,
Pleroma.Web.Endpoint, Pleroma.Web.Endpoint,
@ -35,8 +33,8 @@ defp do_merge(conf1, conf2), do: Mix.Config.merge(conf1, conf2)
def load_and_merge do def load_and_merge do
all_paths = all_paths =
if Pleroma.Config.get(:release), if Pleroma.Config.get(:release),
do: @paths ++ ["config/releases.exs"], do: ["config/config.exs", "config/releases.exs"],
else: @paths else: ["config/config.exs"]
all_paths all_paths
|> Enum.map(&load(&1)) |> Enum.map(&load(&1))

View File

@ -10,6 +10,30 @@ defmodule Pleroma.Config.TransferTask do
require Logger require Logger
@type env() :: :test | :benchmark | :dev | :prod
@reboot_time_keys [
{:pleroma, :hackney_pools},
{:pleroma, :chat},
{:pleroma, Oban},
{:pleroma, :rate_limit},
{:pleroma, :markup},
{:plerome, :streamer}
]
@reboot_time_subkeys [
{:pleroma, Pleroma.Captcha, [:seconds_valid]},
{:pleroma, Pleroma.Upload, [:proxy_remote]},
{:pleroma, :instance, [:upload_limit]},
{:pleroma, :email_notifications, [:digest]},
{:pleroma, :oauth2, [:clean_expired_tokens]},
{:pleroma, Pleroma.ActivityExpiration, [:enabled]},
{:pleroma, Pleroma.ScheduledActivity, [:enabled]},
{:pleroma, :gopher, [:enabled]}
]
@reject [nil, :prometheus]
def start_link(_) do def start_link(_) do
load_and_update_env() load_and_update_env()
if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Repo) if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Repo)
@ -17,21 +41,34 @@ def start_link(_) do
end end
@spec load_and_update_env([ConfigDB.t()]) :: :ok | false @spec load_and_update_env([ConfigDB.t()]) :: :ok | false
def load_and_update_env(deleted \\ []) do def load_and_update_env(deleted \\ [], restart_pleroma? \\ true) do
with true <- Pleroma.Config.get(:configurable_from_database), with true <- Pleroma.Config.get(:configurable_from_database),
true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"), true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"),
started_applications <- Application.started_applications() do started_applications <- Application.started_applications() do
# We need to restart applications for loaded settings take effect # We need to restart applications for loaded settings take effect
in_db = Repo.all(ConfigDB) in_db = Repo.all(ConfigDB)
with_deleted = in_db ++ deleted with_deleted = in_db ++ deleted
with_deleted reject_for_restart = if restart_pleroma?, do: @reject, else: [:pleroma | @reject]
|> Enum.map(&merge_and_update(&1))
|> Enum.uniq() applications =
# TODO: some problem with prometheus after restart! with_deleted
|> Enum.reject(&(&1 in [:pleroma, nil, :prometheus])) |> Enum.map(&merge_and_update(&1))
|> Enum.each(&restart(started_applications, &1)) |> Enum.uniq()
# TODO: some problem with prometheus after restart!
|> Enum.reject(&(&1 in reject_for_restart))
# to be ensured that pleroma will be restarted last
applications =
if :pleroma in applications do
List.delete(applications, :pleroma) ++ [:pleroma]
else
applications
end
Enum.each(applications, &restart(started_applications, &1, Pleroma.Config.get(:env)))
:ok :ok
end end
@ -43,12 +80,25 @@ defp merge_and_update(setting) do
group = ConfigDB.from_string(setting.group) group = ConfigDB.from_string(setting.group)
default = Pleroma.Config.Holder.config(group, key) default = Pleroma.Config.Holder.config(group, key)
merged_value = merge_value(setting, default, group, key) value = ConfigDB.from_binary(setting.value)
merged_value =
if Ecto.get_meta(setting, :state) == :deleted do
default
else
if can_be_merged?(default, value) do
ConfigDB.merge_group(group, key, default, value)
else
value
end
end
:ok = update_env(group, key, merged_value) :ok = update_env(group, key, merged_value)
if group != :logger do if group != :logger do
group if group != :pleroma or pleroma_need_restart?(group, key, value) do
group
end
else else
# change logger configuration in runtime, without restart # change logger configuration in runtime, without restart
if Keyword.keyword?(merged_value) and if Keyword.keyword?(merged_value) and
@ -76,22 +126,31 @@ defp merge_and_update(setting) do
end end
end end
defp merge_value(%{__meta__: %{state: :deleted}}, default, _group, _key), do: default @spec pleroma_need_restart?(atom(), atom(), any()) :: boolean()
def pleroma_need_restart?(group, key, value) do
group_and_key_need_reboot?(group, key) or group_and_subkey_need_reboot?(group, key, value)
end
defp merge_value(setting, default, group, key) do defp group_and_key_need_reboot?(group, key) do
value = ConfigDB.from_binary(setting.value) Enum.any?(@reboot_time_keys, fn {g, k} -> g == group and k == key end)
end
if can_be_merged?(default, value) do defp group_and_subkey_need_reboot?(group, key, value) do
ConfigDB.merge_group(group, key, default, value) Keyword.keyword?(value) and
else Enum.any?(@reboot_time_subkeys, fn {g, k, subkeys} ->
value g == group and k == key and
end Enum.any?(Keyword.keys(value), &(&1 in subkeys))
end)
end end
defp update_env(group, key, nil), do: Application.delete_env(group, key) defp update_env(group, key, nil), do: Application.delete_env(group, key)
defp update_env(group, key, value), do: Application.put_env(group, key, value) defp update_env(group, key, value), do: Application.put_env(group, key, value)
defp restart(started_applications, app) do defp restart(_, :pleroma, :test), do: Logger.warn("pleroma restarted")
defp restart(_, :pleroma, _), do: send(Restarter.Pleroma, :after_boot)
defp restart(started_applications, app, _) do
with {^app, _, _} <- List.keyfind(started_applications, app, 0), with {^app, _, _} <- List.keyfind(started_applications, app, 0),
:ok <- Application.stop(app) do :ok <- Application.stop(app) do
:ok = Application.start(app) :ok = Application.start(app)

View File

@ -13,7 +13,8 @@ defmodule Pleroma.Formatter do
@auto_linker_config hashtag: true, @auto_linker_config hashtag: true,
hashtag_handler: &Pleroma.Formatter.hashtag_handler/4, hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,
mention: true, mention: true,
mention_handler: &Pleroma.Formatter.mention_handler/4 mention_handler: &Pleroma.Formatter.mention_handler/4,
scheme: true
def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do
case User.get_cached_by_nickname(nickname) do case User.get_cached_by_nickname(nickname) do

View File

@ -184,11 +184,14 @@ def delete(%Object{data: %{"id" => id}} = object) do
with {:ok, _obj} = swap_object_with_tombstone(object), with {:ok, _obj} = swap_object_with_tombstone(object),
deleted_activity = Activity.delete_all_by_object_ap_id(id), deleted_activity = Activity.delete_all_by_object_ap_id(id),
{:ok, true} <- Cachex.del(:object_cache, "object:#{id}"), {:ok, true} <- Cachex.del(:object_cache, "object:#{id}"),
{:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path), {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do
{:ok, _} <- with true <- Pleroma.Config.get([:instance, :cleanup_attachments]) do
Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{ {:ok, _} =
"object" => object Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{
}) do "object" => object
})
end
{:ok, object, deleted_activity} {:ok, object, deleted_activity}
end end
end end

View File

@ -97,7 +97,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
plug( plug(
OAuthScopesPlug, OAuthScopesPlug,
%{scopes: ["read"], admin: true} %{scopes: ["read"], admin: true}
when action in [:config_show, :migrate_from_db, :list_log] when action in [:config_show, :list_log]
) )
plug( plug(
@ -793,19 +793,6 @@ def config_descriptions(conn, _params) do
|> Plug.Conn.send_resp(200, @descriptions_json) |> Plug.Conn.send_resp(200, @descriptions_json)
end end
def migrate_from_db(conn, _params) do
with :ok <- configurable_from_database(conn) do
Mix.Tasks.Pleroma.Config.run([
"migrate_from_db",
"--env",
to_string(Pleroma.Config.get(:env)),
"-d"
])
json(conn, %{})
end
end
def config_show(conn, %{"only_db" => true}) do def config_show(conn, %{"only_db" => true}) do
with :ok <- configurable_from_database(conn) do with :ok <- configurable_from_database(conn) do
configs = Pleroma.Repo.all(ConfigDB) configs = Pleroma.Repo.all(ConfigDB)
@ -890,17 +877,36 @@ def config_update(conn, %{"configs" => configs}) do
Ecto.get_meta(config, :state) == :deleted Ecto.get_meta(config, :state) == :deleted
end) end)
Pleroma.Config.TransferTask.load_and_update_env(deleted) Pleroma.Config.TransferTask.load_and_update_env(deleted, false)
Mix.Tasks.Pleroma.Config.run([ need_reboot? =
"migrate_from_db", Enum.any?(updated, fn config ->
"--env", group = ConfigDB.from_string(config.group)
to_string(Pleroma.Config.get(:env)) key = ConfigDB.from_string(config.key)
]) value = ConfigDB.from_binary(config.value)
Pleroma.Config.TransferTask.pleroma_need_restart?(group, key, value)
end)
response = %{configs: updated}
response =
if need_reboot?, do: Map.put(response, :need_reboot, need_reboot?), else: response
conn conn
|> put_view(ConfigView) |> put_view(ConfigView)
|> render("index.json", %{configs: updated}) |> render("index.json", response)
end
end
def restart(conn, _params) do
with :ok <- configurable_from_database(conn) do
if Pleroma.Config.get(:env) == :test do
Logger.warn("pleroma restarted")
else
send(Restarter.Pleroma, {:restart, 50})
end
json(conn, %{})
end end
end end

View File

@ -5,10 +5,16 @@
defmodule Pleroma.Web.AdminAPI.ConfigView do defmodule Pleroma.Web.AdminAPI.ConfigView do
use Pleroma.Web, :view use Pleroma.Web, :view
def render("index.json", %{configs: configs}) do def render("index.json", %{configs: configs} = params) do
%{ map = %{
configs: render_many(configs, __MODULE__, "show.json", as: :config) configs: render_many(configs, __MODULE__, "show.json", as: :config)
} }
if params[:need_reboot] do
Map.put(map, :need_reboot, true)
else
map
end
end end
def render("show.json", %{config: config}) do def render("show.json", %{config: config}) do

View File

@ -315,8 +315,9 @@ def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do
with %Activity{ with %Activity{
actor: ^user_ap_id, actor: ^user_ap_id,
data: %{"type" => "Create"}, data: %{"type" => "Create"},
object: %Object{data: %{"type" => "Note"}} object: %Object{data: %{"type" => object_type}}
} = activity <- get_by_id_or_ap_id(id_or_ap_id), } = activity <- get_by_id_or_ap_id(id_or_ap_id),
true <- object_type in ["Note", "Article", "Question"],
true <- Visibility.is_public?(activity), true <- Visibility.is_public?(activity),
{:ok, _user} <- User.add_pinnned_activity(user, activity) do {:ok, _user} <- User.add_pinnned_activity(user, activity) do
{:ok, activity} {:ok, activity}

View File

@ -196,7 +196,7 @@ defmodule Pleroma.Web.Router do
get("/config", AdminAPIController, :config_show) get("/config", AdminAPIController, :config_show)
post("/config", AdminAPIController, :config_update) post("/config", AdminAPIController, :config_update)
get("/config/descriptions", AdminAPIController, :config_descriptions) get("/config/descriptions", AdminAPIController, :config_descriptions)
get("/config/migrate_from_db", AdminAPIController, :migrate_from_db) get("/restart", AdminAPIController, :restart)
get("/moderation_log", AdminAPIController, :list_log) get("/moderation_log", AdminAPIController, :list_log)

10
mix.exs
View File

@ -8,7 +8,7 @@ def project do
elixir: "~> 1.8", elixir: "~> 1.8",
elixirc_paths: elixirc_paths(Mix.env()), elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers(), compilers: [:phoenix, :gettext] ++ Mix.compilers(),
elixirc_options: [warnings_as_errors: true], elixirc_options: [warnings_as_errors: warnings_as_errors(Mix.env())],
xref: [exclude: [:eldap]], xref: [exclude: [:eldap]],
start_permanent: Mix.env() == :prod, start_permanent: Mix.env() == :prod,
aliases: aliases(), aliases: aliases(),
@ -73,6 +73,11 @@ defp elixirc_paths(:benchmark), do: ["lib", "benchmarks"]
defp elixirc_paths(:test), do: ["lib", "test/support"] defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"] defp elixirc_paths(_), do: ["lib"]
defp warnings_as_errors(:prod), do: false
# Uncomment this if you need testing configurable_from_database logic
# defp warnings_as_errors(:dev), do: false
defp warnings_as_errors(_), do: true
# Specifies OAuth dependencies. # Specifies OAuth dependencies.
defp oauth_deps do defp oauth_deps do
oauth_strategy_packages = oauth_strategy_packages =
@ -166,7 +171,8 @@ defp deps do
{:captcha, {:captcha,
git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", git: "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git",
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"}, ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
{:mox, "~> 0.5", only: :test} {:mox, "~> 0.5", only: :test},
{:restarter, path: "./restarter"}
] ++ oauth_deps() ] ++ oauth_deps()
end end

28
restarter/lib/pleroma.ex Normal file
View File

@ -0,0 +1,28 @@
defmodule Restarter.Pleroma do
use GenServer
def start_link(_) do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_), do: {:ok, %{}}
def handle_info(:after_boot, %{after_boot: true} = state), do: {:noreply, state}
def handle_info(:after_boot, state) do
restart(:pleroma)
{:noreply, Map.put(state, :after_boot, true)}
end
def handle_info({:restart, delay}, state) do
Process.sleep(delay)
restart(:pleroma)
{:noreply, state}
end
defp restart(app) do
:ok = Application.ensure_started(app)
:ok = Application.stop(app)
:ok = Application.start(app)
end
end

View File

@ -0,0 +1,8 @@
defmodule Restarter do
use Application
def start(_, _) do
opts = [strategy: :one_for_one, name: Restarter.Supervisor]
Supervisor.start_link([Restarter.Pleroma], opts)
end
end

21
restarter/mix.exs Normal file
View File

@ -0,0 +1,21 @@
defmodule Restarter.MixProject do
use Mix.Project
def project do
[
app: :restarter,
version: "0.1.0",
elixir: "~> 1.8",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end
def application do
[
mod: {Restarter, []}
]
end
defp deps, do: []
end

View File

@ -5,6 +5,8 @@
defmodule Pleroma.Config.TransferTaskTest do defmodule Pleroma.Config.TransferTaskTest do
use Pleroma.DataCase use Pleroma.DataCase
import ExUnit.CaptureLog
alias Pleroma.Config.TransferTask alias Pleroma.Config.TransferTask
alias Pleroma.ConfigDB alias Pleroma.ConfigDB
@ -105,4 +107,75 @@ test "transfer config values with full subkey update" do
Application.put_env(:pleroma, :assets, assets) Application.put_env(:pleroma, :assets, assets)
end) end)
end end
describe "pleroma restart" do
test "don't restart if no reboot time settings were changed" do
emoji = Application.get_env(:pleroma, :emoji)
on_exit(fn -> Application.put_env(:pleroma, :emoji, emoji) end)
ConfigDB.create(%{
group: ":pleroma",
key: ":emoji",
value: [groups: [a: 1, b: 2]]
})
refute String.contains?(
capture_log(fn -> TransferTask.start_link([]) end),
"pleroma restarted"
)
end
test "restart pleroma on reboot time key" do
chat = Application.get_env(:pleroma, :chat)
on_exit(fn -> Application.put_env(:pleroma, :chat, chat) end)
ConfigDB.create(%{
group: ":pleroma",
key: ":chat",
value: [enabled: false]
})
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
end
test "restart pleroma on reboot time subkey" do
captcha = Application.get_env(:pleroma, Pleroma.Captcha)
on_exit(fn -> Application.put_env(:pleroma, Pleroma.Captcha, captcha) end)
ConfigDB.create(%{
group: ":pleroma",
key: "Pleroma.Captcha",
value: [seconds_valid: 60]
})
assert capture_log(fn -> TransferTask.start_link([]) end) =~ "pleroma restarted"
end
test "don't restart pleroma on reboot time key and subkey if there is false flag" do
chat = Application.get_env(:pleroma, :chat)
captcha = Application.get_env(:pleroma, Pleroma.Captcha)
on_exit(fn ->
Application.put_env(:pleroma, :chat, chat)
Application.put_env(:pleroma, Pleroma.Captcha, captcha)
end)
ConfigDB.create(%{
group: ":pleroma",
key: ":chat",
value: [enabled: false]
})
ConfigDB.create(%{
group: ":pleroma",
key: "Pleroma.Captcha",
value: [seconds_valid: 60]
})
refute String.contains?(
capture_log(fn -> TransferTask.load_and_update_env([], false) end),
"pleroma restarted"
)
end
end
end end

View File

@ -76,8 +76,9 @@ test "ensures cache is cleared for the object" do
describe "delete attachments" do describe "delete attachments" do
clear_config([Pleroma.Upload]) clear_config([Pleroma.Upload])
test "in subdirectories" do test "Disabled via config" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([:instance, :cleanup_attachments], false)
file = %Plug.Upload{ file = %Plug.Upload{
content_type: "image/jpg", content_type: "image/jpg",
@ -103,6 +104,41 @@ test "in subdirectories" do
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker)) ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
refute Object.get_by_id(attachment.id) == nil
assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
end
test "in subdirectories" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([:instance, :cleanup_attachments], true)
file = %Plug.Upload{
content_type: "image/jpg",
path: Path.absname("test/fixtures/image.jpg"),
filename: "an_image.jpg"
}
user = insert(:user)
{:ok, %Object{} = attachment} =
Pleroma.Web.ActivityPub.ActivityPub.upload(file, actor: user.ap_id)
%{data: %{"attachment" => [%{"url" => [%{"href" => href}]}]}} =
note = insert(:note, %{user: user, data: %{"attachment" => [attachment.data]}})
uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
path = href |> Path.dirname() |> Path.basename()
assert {:ok, ["an_image.jpg"]} == File.ls("#{uploads_dir}/#{path}")
Object.delete(note)
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
assert Object.get_by_id(attachment.id) == nil assert Object.get_by_id(attachment.id) == nil
assert {:ok, []} == File.ls("#{uploads_dir}/#{path}") assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
@ -111,6 +147,7 @@ test "in subdirectories" do
test "with dedupe enabled" do test "with dedupe enabled" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe]) Pleroma.Config.put([Pleroma.Upload, :filters], [Pleroma.Upload.Filter.Dedupe])
Pleroma.Config.put([:instance, :cleanup_attachments], true)
uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads]) uploads_dir = Pleroma.Config.get!([Pleroma.Uploaders.Local, :uploads])
@ -139,6 +176,7 @@ test "with dedupe enabled" do
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker)) ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
assert Object.get_by_id(attachment.id) == nil assert Object.get_by_id(attachment.id) == nil
assert {:ok, files} = File.ls(uploads_dir) assert {:ok, files} = File.ls(uploads_dir)
refute filename in files refute filename in files
@ -146,6 +184,7 @@ test "with dedupe enabled" do
test "with objects that have legacy data.url attribute" do test "with objects that have legacy data.url attribute" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([:instance, :cleanup_attachments], true)
file = %Plug.Upload{ file = %Plug.Upload{
content_type: "image/jpg", content_type: "image/jpg",
@ -173,6 +212,7 @@ test "with objects that have legacy data.url attribute" do
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker)) ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
assert Object.get_by_id(attachment.id) == nil assert Object.get_by_id(attachment.id) == nil
assert {:ok, []} == File.ls("#{uploads_dir}/#{path}") assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")
@ -181,6 +221,7 @@ test "with objects that have legacy data.url attribute" do
test "With custom base_url" do test "With custom base_url" do
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local) Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
Pleroma.Config.put([Pleroma.Upload, :base_url], "https://sub.domain.tld/dir/") Pleroma.Config.put([Pleroma.Upload, :base_url], "https://sub.domain.tld/dir/")
Pleroma.Config.put([:instance, :cleanup_attachments], true)
file = %Plug.Upload{ file = %Plug.Upload{
content_type: "image/jpg", content_type: "image/jpg",
@ -206,6 +247,7 @@ test "With custom base_url" do
ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker)) ObanHelpers.perform(all_enqueued(worker: Pleroma.Workers.AttachmentsCleanupWorker))
assert Object.get_by_id(note.id).data["deleted"]
assert Object.get_by_id(attachment.id) == nil assert Object.get_by_id(attachment.id) == nil
assert {:ok, []} == File.ls("#{uploads_dir}/#{path}") assert {:ok, []} == File.ls("#{uploads_dir}/#{path}")

View File

@ -2043,7 +2043,6 @@ test "POST /api/pleroma/admin/config error", %{conn: conn} do
Application.delete_env(:pleroma, Pleroma.Captcha.NotReal) Application.delete_env(:pleroma, Pleroma.Captcha.NotReal)
Application.put_env(:pleroma, :http, http) Application.put_env(:pleroma, :http, http)
Application.put_env(:tesla, :adapter, Tesla.Mock) Application.put_env(:tesla, :adapter, Tesla.Mock)
:ok = File.rm("config/test.exported_from_db.secret.exs")
end) end)
end end
@ -2170,7 +2169,7 @@ test "create new config setting in db", %{conn: conn} do
assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []} assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []}
end end
test "save config setting without key", %{conn: conn} do test "save configs setting without explicit key", %{conn: conn} do
level = Application.get_env(:quack, :level) level = Application.get_env(:quack, :level)
meta = Application.get_env(:quack, :meta) meta = Application.get_env(:quack, :meta)
webhook_url = Application.get_env(:quack, :webhook_url) webhook_url = Application.get_env(:quack, :webhook_url)
@ -2256,6 +2255,34 @@ test "saving config with partial update", %{conn: conn} do
} }
end end
test "saving config which need pleroma reboot", %{conn: conn} do
chat = Pleroma.Config.get(:chat)
on_exit(fn -> Pleroma.Config.put(:chat, chat) end)
conn =
post(
conn,
"/api/pleroma/admin/config",
%{
configs: [
%{group: ":pleroma", key: ":chat", value: [%{"tuple" => [":enabled", true]}]}
]
}
)
assert json_response(conn, 200) == %{
"configs" => [
%{
"db" => [":enabled"],
"group" => ":pleroma",
"key" => ":chat",
"value" => [%{"tuple" => [":enabled", true]}]
}
],
"need_reboot" => true
}
end
test "saving config with nested merge", %{conn: conn} do test "saving config with nested merge", %{conn: conn} do
config = config =
insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2])) insert(:config, key: ":key1", value: :erlang.term_to_binary(key1: 1, key2: [k1: 1, k2: 2]))
@ -2957,47 +2984,15 @@ test "proxy tuple ip", %{conn: conn} do
end end
end end
describe "config mix tasks run" do describe "GET /api/pleroma/admin/restart" do
setup do
Mix.shell(Mix.Shell.Quiet)
on_exit(fn ->
Mix.shell(Mix.Shell.IO)
end)
:ok
end
clear_config(:configurable_from_database) do clear_config(:configurable_from_database) do
Pleroma.Config.put(:configurable_from_database, true) Pleroma.Config.put(:configurable_from_database, true)
end end
clear_config([:feed, :post_title]) do test "pleroma restarts", %{conn: conn} do
Pleroma.Config.put([:feed, :post_title], %{max_length: 100, omission: ""}) ExUnit.CaptureLog.capture_log(fn ->
end assert conn |> get("/api/pleroma/admin/restart") |> json_response(200) == %{}
end) =~ "pleroma restarted"
test "transfer settings to DB and to file", %{conn: conn} do
assert Repo.all(Pleroma.ConfigDB) == []
Mix.Tasks.Pleroma.Config.migrate_to_db("test/fixtures/config/temp.secret.exs")
assert Repo.aggregate(Pleroma.ConfigDB, :count, :id) > 0
conn = get(conn, "/api/pleroma/admin/config/migrate_from_db")
assert json_response(conn, 200) == %{}
assert Repo.all(Pleroma.ConfigDB) == []
end
test "returns error if configuration from database is off", %{conn: conn} do
initial = Pleroma.Config.get(:configurable_from_database)
on_exit(fn -> Pleroma.Config.put(:configurable_from_database, initial) end)
Pleroma.Config.put(:configurable_from_database, false)
conn = get(conn, "/api/pleroma/admin/config/migrate_from_db")
assert json_response(conn, 400) ==
"To use this endpoint you need to enable configuration from database."
assert Repo.all(Pleroma.ConfigDB) == []
end end
end end

View File

@ -324,6 +324,21 @@ test "pin status", %{user: user, activity: activity} do
assert %User{pinned_activities: [^id]} = user assert %User{pinned_activities: [^id]} = user
end end
test "pin poll", %{user: user} do
{:ok, activity} =
CommonAPI.post(user, %{
"status" => "How is fediverse today?",
"poll" => %{"options" => ["Absolutely outstanding", "Not good"], "expires_in" => 20}
})
assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)
id = activity.id
user = refresh_record(user)
assert %User{pinned_activities: [^id]} = user
end
test "unlisted statuses can be pinned", %{user: user} do test "unlisted statuses can be pinned", %{user: user} do
{:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!", "visibility" => "unlisted"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!!!", "visibility" => "unlisted"})
assert {:ok, ^activity} = CommonAPI.pin(activity.id, user) assert {:ok, ^activity} = CommonAPI.pin(activity.id, user)