@@ -568,8 +568,9 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret | |||
{ | |||
configs: [ | |||
{ | |||
"group": string, | |||
"key": string, | |||
"value": string or {} or [] | |||
"value": string or {} or [] or {"tuple": []} | |||
} | |||
] | |||
} | |||
@@ -597,8 +598,9 @@ Compile time settings (need instance reboot): | |||
- Method `POST` | |||
- Params: | |||
- `configs` => [ | |||
- `group` (string) | |||
- `key` (string) | |||
- `value` (string, [], {}) | |||
- `value` (string, [], {} or {"tuple": []}) | |||
- `delete` = true (optional, if parameter must be deleted) | |||
] | |||
@@ -608,6 +610,7 @@ Compile time settings (need instance reboot): | |||
{ | |||
configs: [ | |||
{ | |||
"group": "pleroma", | |||
"key": "Pleroma.Upload", | |||
"value": { | |||
"uploader": "Pleroma.Uploaders.Local", | |||
@@ -636,6 +639,7 @@ Compile time settings (need instance reboot): | |||
{ | |||
configs: [ | |||
{ | |||
"group": string, | |||
"key": string, | |||
"value": string or {} or [] or {"tuple": []} | |||
} | |||
@@ -24,7 +24,7 @@ defmodule Mix.Tasks.Pleroma.Config do | |||
|> Enum.reject(fn {k, _v} -> k in [Pleroma.Repo, :env] end) | |||
|> Enum.each(fn {k, v} -> | |||
key = to_string(k) |> String.replace("Elixir.", "") | |||
{:ok, _} = Config.update_or_create(%{key: key, value: v}) | |||
{:ok, _} = Config.update_or_create(%{group: "pleroma", key: key, value: v}) | |||
Mix.shell().info("#{key} is migrated.") | |||
end) | |||
@@ -51,7 +51,9 @@ defmodule Mix.Tasks.Pleroma.Config do | |||
IO.write( | |||
file, | |||
"config :pleroma, #{config.key}#{mark} #{inspect(Config.from_binary(config.value))}\r\n" | |||
"config :#{config.group}, #{config.key}#{mark} #{ | |||
inspect(Config.from_binary(config.value)) | |||
}\r\n" | |||
) | |||
{:ok, _} = Repo.delete(config) | |||
@@ -11,8 +11,17 @@ defmodule Pleroma.Config.TransferTask do | |||
def load_and_update_env do | |||
if Pleroma.Config.get([:instance, :dynamic_configuration]) and | |||
Ecto.Adapters.SQL.table_exists?(Pleroma.Repo, "config") do | |||
Pleroma.Repo.all(Config) | |||
|> Enum.each(&update_env(&1)) | |||
for_restart = | |||
Pleroma.Repo.all(Config) | |||
|> Enum.map(&update_env(&1)) | |||
# We need to restart applications for loaded settings take effect | |||
for_restart | |||
|> Enum.reject(&(&1 in [:pleroma, :ok])) | |||
|> Enum.each(fn app -> | |||
Application.stop(app) | |||
:ok = Application.start(app) | |||
end) | |||
end | |||
end | |||
@@ -25,11 +34,15 @@ defmodule Pleroma.Config.TransferTask do | |||
setting.key | |||
end | |||
group = String.to_existing_atom(setting.group) | |||
Application.put_env( | |||
:pleroma, | |||
group, | |||
String.to_existing_atom(key), | |||
Config.from_binary(setting.value) | |||
) | |||
group | |||
rescue | |||
e -> | |||
require Logger | |||
@@ -377,12 +377,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do | |||
if Pleroma.Config.get([:instance, :dynamic_configuration]) do | |||
updated = | |||
Enum.map(configs, fn | |||
%{"key" => key, "value" => value} -> | |||
{:ok, config} = Config.update_or_create(%{key: key, value: value}) | |||
%{"group" => group, "key" => key, "value" => value} -> | |||
{:ok, config} = Config.update_or_create(%{group: group, key: key, value: value}) | |||
config | |||
%{"key" => key, "delete" => "true"} -> | |||
{:ok, _} = Config.delete(key) | |||
%{"group" => group, "key" => key, "delete" => "true"} -> | |||
{:ok, _} = Config.delete(%{group: group, key: key}) | |||
nil | |||
end) | |||
|> Enum.reject(&is_nil(&1)) | |||
@@ -12,26 +12,27 @@ defmodule Pleroma.Web.AdminAPI.Config do | |||
schema "config" do | |||
field(:key, :string) | |||
field(:group, :string) | |||
field(:value, :binary) | |||
timestamps() | |||
end | |||
@spec get_by_key(String.t()) :: Config.t() | nil | |||
def get_by_key(key), do: Repo.get_by(Config, key: key) | |||
@spec get_by_params(map()) :: Config.t() | nil | |||
def get_by_params(params), do: Repo.get_by(Config, params) | |||
@spec changeset(Config.t(), map()) :: Changeset.t() | |||
def changeset(config, params \\ %{}) do | |||
config | |||
|> cast(params, [:key, :value]) | |||
|> validate_required([:key, :value]) | |||
|> unique_constraint(:key) | |||
|> cast(params, [:key, :group, :value]) | |||
|> validate_required([:key, :group, :value]) | |||
|> unique_constraint(:key, name: :config_group_key_index) | |||
end | |||
@spec create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} | |||
def create(%{key: key, value: value}) do | |||
def create(params) do | |||
%Config{} | |||
|> changeset(%{key: key, value: transform(value)}) | |||
|> changeset(Map.put(params, :value, transform(params[:value]))) | |||
|> Repo.insert() | |||
end | |||
@@ -43,20 +44,20 @@ defmodule Pleroma.Web.AdminAPI.Config do | |||
end | |||
@spec update_or_create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} | |||
def update_or_create(%{key: key} = params) do | |||
with %Config{} = config <- Config.get_by_key(key) do | |||
def update_or_create(params) do | |||
with %Config{} = config <- Config.get_by_params(Map.take(params, [:group, :key])) do | |||
Config.update(config, params) | |||
else | |||
nil -> Config.create(params) | |||
end | |||
end | |||
@spec delete(String.t()) :: {:ok, Config.t()} | {:error, Changeset.t()} | |||
def delete(key) do | |||
with %Config{} = config <- Config.get_by_key(key) do | |||
@spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} | |||
def delete(params) do | |||
with %Config{} = config <- Config.get_by_params(params) do | |||
Repo.delete(config) | |||
else | |||
nil -> {:error, "Config with key #{key} not found"} | |||
nil -> {:error, "Config with params #{inspect(params)} not found"} | |||
end | |||
end | |||
@@ -90,6 +91,8 @@ defmodule Pleroma.Web.AdminAPI.Config do | |||
end | |||
@spec transform(any()) :: binary() | |||
def transform(%{"tuple" => _} = entity), do: :erlang.term_to_binary(do_transform(entity)) | |||
def transform(entity) when is_map(entity) do | |||
tuples = | |||
for {k, v} <- entity, | |||
@@ -10,6 +10,7 @@ defmodule Pleroma.Web.AdminAPI.ConfigView do | |||
def render("show.json", %{config: config}) do | |||
%{ | |||
key: config.key, | |||
group: config.group, | |||
value: Pleroma.Web.AdminAPI.Config.from_binary_to_map(config.value) | |||
} | |||
end | |||
@@ -0,0 +1,12 @@ | |||
defmodule Pleroma.Repo.Migrations.AddGroupKeyToConfig do | |||
use Ecto.Migration | |||
def change do | |||
alter table("config") do | |||
add(:group, :string) | |||
end | |||
drop(unique_index("config", :key)) | |||
create(unique_index("config", [:group, :key])) | |||
end | |||
end |
@@ -13,19 +13,37 @@ defmodule Pleroma.Config.TransferTaskTest do | |||
test "transfer config values from db to env" do | |||
refute Application.get_env(:pleroma, :test_key) | |||
Pleroma.Web.AdminAPI.Config.create(%{key: "test_key", value: [live: 2, com: 3]}) | |||
refute Application.get_env(:idna, :test_key) | |||
Pleroma.Web.AdminAPI.Config.create(%{ | |||
group: "pleroma", | |||
key: "test_key", | |||
value: [live: 2, com: 3] | |||
}) | |||
Pleroma.Web.AdminAPI.Config.create(%{ | |||
group: "idna", | |||
key: "test_key", | |||
value: [live: 15, com: 35] | |||
}) | |||
Pleroma.Config.TransferTask.start_link() | |||
assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3] | |||
assert Application.get_env(:idna, :test_key) == [live: 15, com: 35] | |||
on_exit(fn -> | |||
Application.delete_env(:pleroma, :test_key) | |||
Application.delete_env(:idna, :test_key) | |||
end) | |||
end | |||
test "non existing atom" do | |||
Pleroma.Web.AdminAPI.Config.create(%{key: "undefined_atom_key", value: [live: 2, com: 3]}) | |||
Pleroma.Web.AdminAPI.Config.create(%{ | |||
group: "pleroma", | |||
key: "undefined_atom_key", | |||
value: [live: 2, com: 3] | |||
}) | |||
assert ExUnit.CaptureLog.capture_log(fn -> | |||
Pleroma.Config.TransferTask.start_link() | |||
@@ -314,6 +314,7 @@ defmodule Pleroma.Factory do | |||
def config_factory do | |||
%Pleroma.Web.AdminAPI.Config{ | |||
key: sequence(:key, &"some_key_#{&1}"), | |||
group: "pleroma", | |||
value: | |||
sequence( | |||
:value, | |||
@@ -30,17 +30,26 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do | |||
Mix.Tasks.Pleroma.Config.run(["migrate_to_db"]) | |||
first_db = Config.get_by_key("first_setting") | |||
second_db = Config.get_by_key("second_setting") | |||
refute Config.get_by_key("Pleroma.Repo") | |||
first_db = Config.get_by_params(%{group: "pleroma", key: "first_setting"}) | |||
second_db = Config.get_by_params(%{group: "pleroma", key: "second_setting"}) | |||
refute Config.get_by_params(%{group: "pleroma", key: "Pleroma.Repo"}) | |||
assert Config.from_binary(first_db.value) == [key: "value", key2: [Pleroma.Repo]] | |||
assert Config.from_binary(second_db.value) == [key: "value2", key2: [Pleroma.Activity]] | |||
end | |||
test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do | |||
Config.create(%{key: "setting_first", value: [key: "value", key2: [Pleroma.Activity]]}) | |||
Config.create(%{key: "setting_second", value: [key: "valu2", key2: [Pleroma.Repo]]}) | |||
Config.create(%{ | |||
group: "pleroma", | |||
key: "setting_first", | |||
value: [key: "value", key2: [Pleroma.Activity]] | |||
}) | |||
Config.create(%{ | |||
group: "pleroma", | |||
key: "setting_second", | |||
value: [key: "valu2", key2: [Pleroma.Repo]] | |||
}) | |||
Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "temp"]) | |||
@@ -1363,8 +1363,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
conn = | |||
post(conn, "/api/pleroma/admin/config", %{ | |||
configs: [ | |||
%{key: "key1", value: "value1"}, | |||
%{group: "pleroma", key: "key1", value: "value1"}, | |||
%{ | |||
group: "pleroma", | |||
key: "key2", | |||
value: %{ | |||
"nested_1" => "nested_value1", | |||
@@ -1375,6 +1376,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
} | |||
}, | |||
%{ | |||
group: "pleroma", | |||
key: "key3", | |||
value: [ | |||
%{"nested_3" => ":nested_3", "nested_33" => "nested_33"}, | |||
@@ -1382,8 +1384,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
] | |||
}, | |||
%{ | |||
group: "pleroma", | |||
key: "key4", | |||
value: %{"nested_5" => ":upload", "endpoint" => "https://example.com"} | |||
}, | |||
%{ | |||
group: "idna", | |||
key: "key5", | |||
value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]} | |||
} | |||
] | |||
}) | |||
@@ -1391,10 +1399,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
assert json_response(conn, 200) == %{ | |||
"configs" => [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key1", | |||
"value" => "value1" | |||
}, | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key2", | |||
"value" => [ | |||
%{"nested_1" => "nested_value1"}, | |||
@@ -1407,6 +1417,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
] | |||
}, | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key3", | |||
"value" => [ | |||
[%{"nested_3" => "nested_3"}, %{"nested_33" => "nested_33"}], | |||
@@ -1414,8 +1425,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
] | |||
}, | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "key4", | |||
"value" => [%{"endpoint" => "https://example.com"}, %{"nested_5" => "upload"}] | |||
}, | |||
%{ | |||
"group" => "idna", | |||
"key" => "key5", | |||
"value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]} | |||
} | |||
] | |||
} | |||
@@ -1439,6 +1456,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
endpoint: "https://example.com", | |||
nested_5: :upload | |||
] | |||
assert Application.get_env(:idna, :key5) == {"string", Pleroma.Captcha.NotReal, []} | |||
end | |||
test "update config setting & delete", %{conn: conn} do | |||
@@ -1448,14 +1467,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
conn = | |||
post(conn, "/api/pleroma/admin/config", %{ | |||
configs: [ | |||
%{key: config1.key, value: "another_value"}, | |||
%{key: config2.key, delete: "true"} | |||
%{group: config1.group, key: config1.key, value: "another_value"}, | |||
%{group: config2.group, key: config2.key, delete: "true"} | |||
] | |||
}) | |||
assert json_response(conn, 200) == %{ | |||
"configs" => [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => config1.key, | |||
"value" => "another_value" | |||
} | |||
@@ -1471,6 +1491,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
post(conn, "/api/pleroma/admin/config", %{ | |||
configs: [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "Pleroma.Captcha.NotReal", | |||
"value" => %{ | |||
"enabled" => ":false", | |||
@@ -1484,6 +1505,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
assert json_response(conn, 200) == %{ | |||
"configs" => [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "Pleroma.Captcha.NotReal", | |||
"value" => [ | |||
%{"enabled" => false}, | |||
@@ -1500,6 +1522,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
post(conn, "/api/pleroma/admin/config", %{ | |||
configs: [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "Pleroma.Web.Endpoint.NotReal", | |||
"value" => [ | |||
%{ | |||
@@ -1557,6 +1580,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do | |||
assert json_response(conn, 200) == %{ | |||
"configs" => [ | |||
%{ | |||
"group" => "pleroma", | |||
"key" => "Pleroma.Web.Endpoint.NotReal", | |||
"value" => [ | |||
%{ | |||
@@ -7,18 +7,18 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do | |||
config = insert(:config) | |||
insert(:config) | |||
assert config == Config.get_by_key(config.key) | |||
assert config == Config.get_by_params(%{group: config.group, key: config.key}) | |||
end | |||
test "create/1" do | |||
{:ok, config} = Config.create(%{key: "some_key", value: "some_value"}) | |||
assert config == Config.get_by_key("some_key") | |||
{:ok, config} = Config.create(%{group: "pleroma", key: "some_key", value: "some_value"}) | |||
assert config == Config.get_by_params(%{group: "pleroma", key: "some_key"}) | |||
end | |||
test "update/1" do | |||
config = insert(:config) | |||
{:ok, updated} = Config.update(config, %{value: "some_value"}) | |||
loaded = Config.get_by_key(config.key) | |||
loaded = Config.get_by_params(%{group: config.group, key: config.key}) | |||
assert loaded == updated | |||
end | |||
@@ -27,8 +27,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do | |||
key2 = "another_key" | |||
params = [ | |||
%{key: key2, value: "another_value"}, | |||
%{key: config.key, value: "new_value"} | |||
%{group: "pleroma", key: key2, value: "another_value"}, | |||
%{group: config.group, key: config.key, value: "new_value"} | |||
] | |||
assert Repo.all(Config) |> length() == 1 | |||
@@ -37,8 +37,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do | |||
assert Repo.all(Config) |> length() == 2 | |||
config1 = Config.get_by_key(config.key) | |||
config2 = Config.get_by_key(key2) | |||
config1 = Config.get_by_params(%{group: config.group, key: config.key}) | |||
config2 = Config.get_by_params(%{group: "pleroma", key: key2}) | |||
assert config1.value == Config.transform("new_value") | |||
assert config2.value == Config.transform("another_value") | |||
@@ -46,8 +46,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do | |||
test "delete/1" do | |||
config = insert(:config) | |||
{:ok, _} = Config.delete(config.key) | |||
refute Config.get_by_key(config.key) | |||
{:ok, _} = Config.delete(%{key: config.key, group: config.group}) | |||
refute Config.get_by_params(%{key: config.key, group: config.group}) | |||
end | |||
describe "transform/1" do | |||