config.ex (5414B)
1 # Pleroma: A lightweight social networking server 2 # Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> 3 # SPDX-License-Identifier: AGPL-3.0-only 4 5 defmodule Pleroma.Web.AdminAPI.Config do 6 use Ecto.Schema 7 import Ecto.Changeset 8 import Pleroma.Web.Gettext 9 alias __MODULE__ 10 alias Pleroma.Repo 11 12 @type t :: %__MODULE__{} 13 14 schema "config" do 15 field(:key, :string) 16 field(:group, :string) 17 field(:value, :binary) 18 19 timestamps() 20 end 21 22 @spec get_by_params(map()) :: Config.t() | nil 23 def get_by_params(params), do: Repo.get_by(Config, params) 24 25 @spec changeset(Config.t(), map()) :: Changeset.t() 26 def changeset(config, params \\ %{}) do 27 config 28 |> cast(params, [:key, :group, :value]) 29 |> validate_required([:key, :group, :value]) 30 |> unique_constraint(:key, name: :config_group_key_index) 31 end 32 33 @spec create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} 34 def create(params) do 35 %Config{} 36 |> changeset(Map.put(params, :value, transform(params[:value]))) 37 |> Repo.insert() 38 end 39 40 @spec update(Config.t(), map()) :: {:ok, Config} | {:error, Changeset.t()} 41 def update(%Config{} = config, %{value: value}) do 42 config 43 |> change(value: transform(value)) 44 |> Repo.update() 45 end 46 47 @spec update_or_create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} 48 def update_or_create(params) do 49 with %Config{} = config <- Config.get_by_params(Map.take(params, [:group, :key])) do 50 Config.update(config, params) 51 else 52 nil -> Config.create(params) 53 end 54 end 55 56 @spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} 57 def delete(params) do 58 with %Config{} = config <- Config.get_by_params(Map.delete(params, :subkeys)) do 59 if params[:subkeys] do 60 updated_value = 61 Keyword.drop( 62 :erlang.binary_to_term(config.value), 63 Enum.map(params[:subkeys], &do_transform_string(&1)) 64 ) 65 66 Config.update(config, %{value: updated_value}) 67 else 68 Repo.delete(config) 69 {:ok, nil} 70 end 71 else 72 nil -> 73 err = 74 dgettext("errors", "Config with params %{params} not found", params: inspect(params)) 75 76 {:error, err} 77 end 78 end 79 80 @spec from_binary(binary()) :: term() 81 def from_binary(binary), do: :erlang.binary_to_term(binary) 82 83 @spec from_binary_with_convert(binary()) :: any() 84 def from_binary_with_convert(binary) do 85 from_binary(binary) 86 |> do_convert() 87 end 88 89 defp do_convert(entity) when is_list(entity) do 90 for v <- entity, into: [], do: do_convert(v) 91 end 92 93 defp do_convert(%Regex{} = entity), do: inspect(entity) 94 95 defp do_convert(entity) when is_map(entity) do 96 for {k, v} <- entity, into: %{}, do: {do_convert(k), do_convert(v)} 97 end 98 99 defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]} 100 defp do_convert({:partial_chain, entity}), do: %{"tuple" => [":partial_chain", inspect(entity)]} 101 102 defp do_convert(entity) when is_tuple(entity), 103 do: %{"tuple" => do_convert(Tuple.to_list(entity))} 104 105 defp do_convert(entity) when is_boolean(entity) or is_number(entity) or is_nil(entity), 106 do: entity 107 108 defp do_convert(entity) when is_atom(entity) do 109 string = to_string(entity) 110 111 if String.starts_with?(string, "Elixir."), 112 do: do_convert(string), 113 else: ":" <> string 114 end 115 116 defp do_convert("Elixir." <> module_name), do: module_name 117 118 defp do_convert(entity) when is_binary(entity), do: entity 119 120 @spec transform(any()) :: binary() 121 def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do 122 :erlang.term_to_binary(do_transform(entity)) 123 end 124 125 def transform(entity), do: :erlang.term_to_binary(entity) 126 127 defp do_transform(%Regex{} = entity), do: entity 128 129 defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do 130 {dispatch_settings, []} = do_eval(entity) 131 {:dispatch, [dispatch_settings]} 132 end 133 134 defp do_transform(%{"tuple" => [":partial_chain", entity]}) do 135 {partial_chain, []} = do_eval(entity) 136 {:partial_chain, partial_chain} 137 end 138 139 defp do_transform(%{"tuple" => entity}) do 140 Enum.reduce(entity, {}, fn val, acc -> Tuple.append(acc, do_transform(val)) end) 141 end 142 143 defp do_transform(entity) when is_map(entity) do 144 for {k, v} <- entity, into: %{}, do: {do_transform(k), do_transform(v)} 145 end 146 147 defp do_transform(entity) when is_list(entity) do 148 for v <- entity, into: [], do: do_transform(v) 149 end 150 151 defp do_transform(entity) when is_binary(entity) do 152 String.trim(entity) 153 |> do_transform_string() 154 end 155 156 defp do_transform(entity), do: entity 157 158 defp do_transform_string("~r/" <> pattern) do 159 modificator = String.split(pattern, "/") |> List.last() 160 pattern = String.trim_trailing(pattern, "/" <> modificator) 161 162 case modificator do 163 "" -> ~r/#{pattern}/ 164 "i" -> ~r/#{pattern}/i 165 "u" -> ~r/#{pattern}/u 166 "s" -> ~r/#{pattern}/s 167 end 168 end 169 170 defp do_transform_string(":" <> atom), do: String.to_atom(atom) 171 172 defp do_transform_string(value) do 173 if String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix"), 174 do: String.to_existing_atom("Elixir." <> value), 175 else: value 176 end 177 178 defp do_eval(entity) do 179 cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "") 180 Code.eval_string(cleaned_string, [], requires: [], macros: []) 181 end 182 end