config editing through database
authorAlex S <alex.strizhakov@gmail.com>
Sun, 29 Sep 2019 08:17:38 +0000 (11:17 +0300)
committerAlexander Strizhakov <alex.strizhakov@gmail.com>
Fri, 10 Jan 2020 12:51:59 +0000 (15:51 +0300)
20 files changed:
config/description.exs
docs/API/admin_api.md
docs/admin/config.md [new file with mode: 0644]
lib/mix/tasks/pleroma/config.ex
lib/pleroma/config/transfer_task.ex
lib/pleroma/docs/generator.ex
lib/pleroma/docs/json.ex
lib/pleroma/web/activity_pub/mrf/media_proxy_warming_policy.ex [moved from lib/pleroma/web/activity_pub/mrf/mediaproxy_warming_policy.ex with 100% similarity]
lib/pleroma/web/activity_pub/mrf/no_op_policy.ex [moved from lib/pleroma/web/activity_pub/mrf/noop_policy.ex with 100% similarity]
lib/pleroma/web/activity_pub/mrf/user_allow_list_policy.ex [moved from lib/pleroma/web/activity_pub/mrf/user_allowlist_policy.ex with 100% similarity]
lib/pleroma/web/activity_pub/mrf/vocabulary_policy.ex
lib/pleroma/web/admin_api/admin_api_controller.ex
lib/pleroma/web/admin_api/config.ex
lib/pleroma/web/router.ex
test/config/transfer_task_test.exs
test/docs/generator_test.exs [new file with mode: 0644]
test/support/factory.ex
test/tasks/config_test.exs
test/web/admin_api/admin_api_controller_test.exs
test/web/admin_api/config_test.exs

index 45e4b43f16c3b34022aa1a517d5e9a0309d7cb9c..eeb4a6fe98794c7981e291ea7553b9987206e8ba 100644 (file)
@@ -23,17 +23,17 @@ config :pleroma, :config_description, [
         key: :uploader,
         type: :module,
         description: "Module which will be used for uploads",
-        suggestions: [
-          Generator.uploaders_list()
-        ]
+        suggestions: [Pleroma.Uploaders.Local, Pleroma.Uploaders.MDII, Pleroma.Uploaders.S3]
       },
       %{
         key: :filters,
         type: {:list, :module},
         description: "List of filter modules for uploads",
-        suggestions: [
-          Generator.filters_list()
-        ]
+        suggestions:
+          Generator.list_modules_in_dir(
+            "lib/pleroma/upload/filter",
+            "Elixir.Pleroma.Upload.Filter."
+          )
       },
       %{
         key: :link_name,
@@ -58,7 +58,50 @@ config :pleroma, :config_description, [
       %{
         key: :proxy_opts,
         type: :keyword,
-        description: "Proxy options, see `Pleroma.ReverseProxy` documentation"
+        description: "Options for Pleroma.ReverseProxy",
+        suggestions: [
+          redirect_on_failure: false,
+          max_body_length: 25 * 1_048_576,
+          http: [
+            follow_redirect: true,
+            pool: :media
+          ]
+        ],
+        children: [
+          %{
+            key: :redirect_on_failure,
+            type: :boolean,
+            description:
+              "Redirects the client to the real remote URL if there's any HTTP errors. " <>
+                "Any error during body processing will not be redirected as the response is chunked"
+          },
+          %{
+            key: :max_body_length,
+            type: :integer,
+            description:
+              "limits the content length to be approximately the " <>
+                "specified length. It is validated with the `content-length` header and also verified when proxying"
+          },
+          %{
+            key: :http,
+            type: :keyword,
+            description: "HTTP options",
+            children: [
+              %{
+                key: :adapter,
+                type: :keyword,
+                description: "Adapter specific options"
+              },
+              %{
+                key: :proxy_url,
+                label: "Proxy URL",
+                type: [:string, :tuple],
+                description: "Proxy URL",
+                suggestions: ["127.0.0.1:8123", {:socks5, :localhost, 9050}]
+              }
+            ]
+          }
+        ]
       }
     ]
   },
@@ -131,9 +174,8 @@ config :pleroma, :config_description, [
         description: "List of actions for the mogrify command",
         suggestions: [
           "strip",
-          ["strip", "auto-orient"],
-          [{"implode", "1"}],
-          ["strip", "auto-orient", {"implode", "1"}]
+          "auto-orient",
+          {"implode", "1"}
         ]
       }
     ]
@@ -151,8 +193,7 @@ config :pleroma, :config_description, [
           "Text to replace filenames in links. If no setting, {random}.extension will be used. You can get the original" <>
             " filename extension by using {extension}, for example custom-file-name.{extension}",
         suggestions: [
-          "custom-file-name.{extension}",
-          nil
+          "custom-file-name.{extension}"
         ]
       }
     ]
@@ -213,12 +254,14 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.SMTP},
         key: :ssl,
+        label: "SSL",
         type: :boolean,
         description: "`Swoosh.Adapters.SMTP` adapter specific setting"
       },
       %{
         group: {:subgroup, Swoosh.Adapters.SMTP},
         key: :tls,
+        label: "TLS",
         type: :atom,
         description: "`Swoosh.Adapters.SMTP` adapter specific setting",
         suggestions: [:always, :never, :if_available]
@@ -247,12 +290,14 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.SMTP},
         key: :no_mx_lookups,
+        label: "No MX lookups",
         type: :boolean,
         description: "`Swoosh.Adapters.SMTP` adapter specific setting"
       },
       %{
         group: {:subgroup, Swoosh.Adapters.Sendgrid},
         key: :api_key,
+        label: "API key",
         type: :string,
         description: "`Swoosh.Adapters.Sendgrid` adapter specific setting",
         suggestions: ["my-api-key"]
@@ -280,6 +325,7 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.Mandrill},
         key: :api_key,
+        label: "API key",
         type: :string,
         description: "`Swoosh.Adapters.Mandrill` adapter specific setting",
         suggestions: ["my-api-key"]
@@ -287,6 +333,7 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.Mailgun},
         key: :api_key,
+        label: "API key",
         type: :string,
         description: "`Swoosh.Adapters.Mailgun` adapter specific setting",
         suggestions: ["my-api-key"]
@@ -301,6 +348,7 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.Mailjet},
         key: :api_key,
+        label: "API key",
         type: :string,
         description: "`Swoosh.Adapters.Mailjet` adapter specific setting",
         suggestions: ["my-api-key"]
@@ -315,6 +363,7 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.Postmark},
         key: :api_key,
+        label: "API key",
         type: :string,
         description: "`Swoosh.Adapters.Postmark` adapter specific setting",
         suggestions: ["my-api-key"]
@@ -322,6 +371,7 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.SparkPost},
         key: :api_key,
+        label: "API key",
         type: :string,
         description: "`Swoosh.Adapters.SparkPost` adapter specific setting",
         suggestions: ["my-api-key"]
@@ -336,7 +386,7 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.AmazonSES},
         key: :region,
-        type: {:string},
+        type: :string,
         description: "`Swoosh.Adapters.AmazonSES` adapter specific setting",
         suggestions: ["us-east-1", "us-east-2"]
       },
@@ -357,6 +407,7 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.Dyn},
         key: :api_key,
+        label: "API key",
         type: :string,
         description: "`Swoosh.Adapters.Dyn` adapter specific setting",
         suggestions: ["my-api-key"]
@@ -370,6 +421,7 @@ config :pleroma, :config_description, [
       %{
         group: {:subgroup, Swoosh.Adapters.SocketLabs},
         key: :api_key,
+        label: "API key",
         type: :string,
         description: "`Swoosh.Adapters.SocketLabs` adapter specific setting"
       },
@@ -392,22 +444,20 @@ config :pleroma, :config_description, [
         type: {:list, :string},
         description: "List of the scheme part that is considered valid to be an URL",
         suggestions: [
-          [
-            "https",
-            "http",
-            "dat",
-            "dweb",
-            "gopher",
-            "ipfs",
-            "ipns",
-            "irc",
-            "ircs",
-            "magnet",
-            "mailto",
-            "mumble",
-            "ssb",
-            "xmpp"
-          ]
+          "https",
+          "http",
+          "dat",
+          "dweb",
+          "gopher",
+          "ipfs",
+          "ipns",
+          "irc",
+          "ircs",
+          "magnet",
+          "mailto",
+          "mumble",
+          "ssb",
+          "xmpp"
         ]
       }
     ]
@@ -578,7 +628,7 @@ config :pleroma, :config_description, [
       },
       %{
         key: :federation_publisher_modules,
-        type: [:list, :module],
+        type: {:list, :module},
         description: "List of modules for federation publishing",
         suggestions: [
           Pleroma.Web.ActivityPub.Publisher
@@ -591,12 +641,13 @@ config :pleroma, :config_description, [
       },
       %{
         key: :rewrite_policy,
-        type: {:list, :module},
+        type: [:module, {:list, :module}],
         description: "A list of MRF policies enabled",
-        suggestions: [
-          Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
-          Generator.mrf_list()
-        ]
+        suggestions:
+          Generator.list_modules_in_dir(
+            "lib/pleroma/web/activity_pub/mrf",
+            "Elixir.Pleroma.Web.ActivityPub.MRF."
+          )
       },
       %{
         key: :public,
@@ -644,17 +695,19 @@ config :pleroma, :config_description, [
       },
       %{
         key: :mrf_transparency,
+        label: "MRF transparency",
         type: :boolean,
         description:
           "Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
       },
       %{
         key: :mrf_transparency_exclusions,
+        label: "MRF transparency exclusions",
         type: {:list, :string},
         description:
           "Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value",
         suggestions: [
-          ["exclusion.com"]
+          "exclusion.com"
         ]
       },
       %{
@@ -698,8 +751,7 @@ config :pleroma, :config_description, [
         description:
           "A message that will be send to a newly registered users as a direct message",
         suggestions: [
-          "Hi, @username! Welcome to the board!",
-          nil
+          "Hi, @username! Welcome on board!"
         ]
       },
       %{
@@ -707,8 +759,7 @@ config :pleroma, :config_description, [
         type: :string,
         description: "The nickname of the local user that sends the welcome message",
         suggestions: [
-          "lain",
-          nil
+          "lain"
         ]
       },
       %{
@@ -829,7 +880,7 @@ config :pleroma, :config_description, [
         type: [:atom, :tuple, :module],
         description:
           "Where logs will be send, :console - send logs to stdout, {ExSyslogger, :ex_syslogger} - to syslog, Quack.Logger - to Slack.",
-        suggestions: [[:console, {ExSyslogger, :ex_syslogger}, Quack.Logger]]
+        suggestions: [:console, {ExSyslogger, :ex_syslogger}, Quack.Logger]
       }
     ]
   },
@@ -861,7 +912,7 @@ config :pleroma, :config_description, [
       %{
         key: :metadata,
         type: {:list, :atom},
-        suggestions: [[:request_id]]
+        suggestions: [:request_id]
       }
     ]
   },
@@ -886,7 +937,7 @@ config :pleroma, :config_description, [
       %{
         key: :metadata,
         type: {:list, :atom},
-        suggestions: [[:request_id]]
+        suggestions: [:request_id]
       }
     ]
   },
@@ -931,10 +982,14 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :frontend_configurations,
     type: :group,
-    description: "A keyword list that keeps the configuration data for any kind of frontend",
+    description:
+      "This form can be used to configure a keyword list that keeps the configuration data for any " <>
+        "kind of frontend. By default, settings for pleroma_fe and masto_fe are configured. If you want to " <>
+        "add your own configuration your settings need to be complete as they will override the defaults.",
     children: [
       %{
         key: :pleroma_fe,
+        label: "Pleroma FE",
         type: :map,
         description: "Settings for Pleroma FE",
         suggestions: [
@@ -977,6 +1032,7 @@ config :pleroma, :config_description, [
           },
           %{
             key: :redirectRootNoLogin,
+            label: "Redirect root no login",
             type: :string,
             description:
               "relative URL which indicates where to redirect when a user isn't logged in",
@@ -984,6 +1040,7 @@ config :pleroma, :config_description, [
           },
           %{
             key: :redirectRootLogin,
+            label: "Redirect root login",
             type: :string,
             description:
               "relative URL which indicates where to redirect when a user is logged in",
@@ -991,44 +1048,52 @@ config :pleroma, :config_description, [
           },
           %{
             key: :showInstanceSpecificPanel,
+            label: "Show instance specific panel",
             type: :boolean,
             description: "Whenether to show the instance's specific panel"
           },
           %{
             key: :scopeOptionsEnabled,
+            label: "Scope options enabled",
             type: :boolean,
             description: "Enable setting an notice visibility and subject/CW when posting"
           },
           %{
             key: :formattingOptionsEnabled,
+            label: "Formatting options enabled",
             type: :boolean,
             description:
               "Enable setting a formatting different than plain-text (ie. HTML, Markdown) when posting, relates to :instance, allowed_post_formats"
           },
           %{
             key: :collapseMessageWithSubject,
+            label: "Collapse message with subject",
             type: :boolean,
             description:
               "When a message has a subject(aka Content Warning), collapse it by default"
           },
           %{
             key: :hidePostStats,
+            label: "Hide post stats",
             type: :boolean,
             description: "Hide notices statistics(repeats, favorites, ...)"
           },
           %{
             key: :hideUserStats,
+            label: "Hide user stats",
             type: :boolean,
             description:
               "Hide profile statistics(posts, posts per day, followers, followings, ...)"
           },
           %{
             key: :scopeCopy,
+            label: "Scope copy",
             type: :boolean,
             description: "Copy the scope (private/unlisted/public) in replies to posts by default"
           },
           %{
             key: :subjectLineBehavior,
+            label: "Subject line behavior",
             type: :string,
             description: "Allows changing the default behaviour of subject lines in replies.
           `email`: Copy and preprend re:, as in email,
@@ -1038,6 +1103,7 @@ config :pleroma, :config_description, [
           },
           %{
             key: :alwaysShowSubjectInput,
+            label: "Always show subject input",
             type: :boolean,
             description: "When set to false, auto-hide the subject field when it's empty"
           }
@@ -1045,6 +1111,7 @@ config :pleroma, :config_description, [
       },
       %{
         key: :masto_fe,
+        label: "Masto FE",
         type: :map,
         description: "Settings for Masto FE",
         suggestions: [
@@ -1055,6 +1122,7 @@ config :pleroma, :config_description, [
         children: [
           %{
             key: :showInstanceSpecificPanel,
+            label: "Show instance specific panel",
             type: :boolean,
             description: "Whenether to show the instance's specific panel"
           }
@@ -1071,20 +1139,18 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :mascots,
-        type: :keyword,
+        type: {:keyword, :map},
         description:
           "Keyword of mascots, each element MUST contain both a url and a mime_type key",
         suggestions: [
-          [
-            pleroma_fox_tan: %{
-              url: "/images/pleroma-fox-tan-smol.png",
-              mime_type: "image/png"
-            },
-            pleroma_fox_tan_shy: %{
-              url: "/images/pleroma-fox-tan-shy.png",
-              mime_type: "image/png"
-            }
-          ]
+          pleroma_fox_tan: %{
+            url: "/images/pleroma-fox-tan-smol.png",
+            mime_type: "image/png"
+          },
+          pleroma_fox_tan_shy: %{
+            url: "/images/pleroma-fox-tan-shy.png",
+            mime_type: "image/png"
+          }
         ]
       },
       %{
@@ -1140,6 +1206,7 @@ config :pleroma, :config_description, [
   %{
     group: :pleroma,
     key: :mrf_simple,
+    label: "MRF simple",
     type: :group,
     description: "Message Rewrite Facility",
     children: [
@@ -1151,6 +1218,7 @@ config :pleroma, :config_description, [
       },
       %{
         key: :media_nsfw,
+        label: "Media NSFW",
         type: {:list, :string},
         description: "List of instances to put medias as NSFW(sensitive) from",
         suggestions: ["example.com", "*.example.com"]
@@ -1197,6 +1265,7 @@ config :pleroma, :config_description, [
   %{
     group: :pleroma,
     key: :mrf_subchain,
+    label: "MRF subchain",
     type: :group,
     description:
       "This policy processes messages through an alternate pipeline when a given message matches certain criteria." <>
@@ -1217,10 +1286,14 @@ config :pleroma, :config_description, [
   %{
     group: :pleroma,
     key: :mrf_rejectnonpublic,
+    description:
+      "MRF RejectNonPublic settings. RejectNonPublic drops posts with non-public visibility settings.",
+    label: "MRF reject non public",
     type: :group,
     children: [
       %{
         key: :allow_followersonly,
+        label: "Allow followers-only",
         type: :boolean,
         description: "whether to allow followers-only posts"
       },
@@ -1234,6 +1307,7 @@ config :pleroma, :config_description, [
   %{
     group: :pleroma,
     key: :mrf_hellthread,
+    label: "MRF hellthread",
     type: :group,
     description: "Block messages with too much mentions",
     children: [
@@ -1257,6 +1331,7 @@ config :pleroma, :config_description, [
   %{
     group: :pleroma,
     key: :mrf_keyword,
+    label: "MRF keyword",
     type: :group,
     description: "Reject or Word-Replace messages with a keyword or regex",
     children: [
@@ -1276,9 +1351,9 @@ config :pleroma, :config_description, [
       },
       %{
         key: :replace,
-        type: [{:string, :string}, {:regex, :string}],
+        type: [{:tuple, :string, :string}, {:tuple, :regex, :string}],
         description:
-          "A list of patterns which result in message being removed from federated timelines (a.k.a unlisted), each pattern can be a string or a regular expression",
+          "A list of tuples containing {pattern, replacement}, pattern can be a string or a regular expression.",
         suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
       }
     ]
@@ -1286,6 +1361,7 @@ config :pleroma, :config_description, [
   %{
     group: :pleroma,
     key: :mrf_mention,
+    label: "MRF mention",
     type: :group,
     description: "Block messages which mention a user",
     children: [
@@ -1293,13 +1369,14 @@ config :pleroma, :config_description, [
         key: :actors,
         type: {:list, :string},
         description: "A list of actors, for which to drop any posts mentioning",
-        suggestions: [["actor1", "actor2"]]
+        suggestions: ["actor1", "actor2"]
       }
     ]
   },
   %{
     group: :pleroma,
     key: :mrf_vocabulary,
+    label: "MRF vocabulary",
     type: :group,
     description: "Filter messages which belong to certain activity vocabularies",
     children: [
@@ -1308,14 +1385,14 @@ config :pleroma, :config_description, [
         type: {:list, :string},
         description:
           "A list of ActivityStreams terms to accept. If empty, all supported messages are accepted",
-        suggestions: [["Create", "Follow", "Mention", "Announce", "Like"]]
+        suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
       },
       %{
         key: :reject,
         type: {:list, :string},
         description:
           "A list of ActivityStreams terms to reject. If empty, no messages are rejected",
-        suggestions: [["Create", "Follow", "Mention", "Announce", "Like"]]
+        suggestions: ["Create", "Follow", "Mention", "Announce", "Like"]
       }
     ]
   },
@@ -1355,7 +1432,49 @@ config :pleroma, :config_description, [
         key: :proxy_opts,
         type: :keyword,
         description: "Options for Pleroma.ReverseProxy",
-        suggestions: [[max_body_length: 25 * 1_048_576, redirect_on_failure: false]]
+        suggestions: [
+          redirect_on_failure: false,
+          max_body_length: 25 * 1_048_576,
+          http: [
+            follow_redirect: true,
+            pool: :media
+          ]
+        ],
+        children: [
+          %{
+            key: :redirect_on_failure,
+            type: :boolean,
+            description:
+              "Redirects the client to the real remote URL if there's any HTTP errors. " <>
+                "Any error during body processing will not be redirected as the response is chunked"
+          },
+          %{
+            key: :max_body_length,
+            type: :integer,
+            description:
+              "limits the content length to be approximately the " <>
+                "specified length. It is validated with the `content-length` header and also verified when proxying"
+          },
+          %{
+            key: :http,
+            type: :keyword,
+            description: "HTTP options",
+            children: [
+              %{
+                key: :adapter,
+                type: :keyword,
+                description: "Adapter specific options"
+              },
+              %{
+                key: :proxy_url,
+                label: "Proxy URL",
+                type: [:string, :tuple],
+                description: "Proxy URL",
+                suggestions: ["127.0.0.1:8123", {:socks5, :localhost, 9050}]
+              }
+            ]
+          }
+        ]
       },
       %{
         key: :whitelist,
@@ -1404,10 +1523,12 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :http,
-        type: :keyword,
+        label: "HTTP",
+        type: {:keyword, :integer, :tuple},
         description: "http protocol configuration",
         suggestions: [
-          [port: 8080, ip: {127, 0, 0, 1}]
+          port: 8080,
+          ip: {127, 0, 0, 1}
         ],
         children: [
           %{
@@ -1415,21 +1536,20 @@ config :pleroma, :config_description, [
             type: {:list, :tuple},
             description: "dispatch settings",
             suggestions: [
-              [
-                {:_,
-                 [
-                   {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
-                   {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
-                    {Phoenix.Transports.WebSocket,
-                     {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}},
-                   {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
-                 ]}
-                # end copied from config.exs
-              ]
+              {:_,
+               [
+                 {"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
+                 {"/websocket", Phoenix.Endpoint.CowboyWebSocket,
+                  {Phoenix.Transports.WebSocket,
+                   {Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}},
+                 {:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
+               ]}
+              # end copied from config.exs
             ]
           },
           %{
             key: :ip,
+            label: "IP",
             type: :tuple,
             description: "ip",
             suggestions: [
@@ -1448,10 +1568,13 @@ config :pleroma, :config_description, [
       },
       %{
         key: :url,
-        type: :keyword,
+        label: "URL",
+        type: {:keyword, :string, :integer},
         description: "configuration for generating urls",
         suggestions: [
-          [host: "example.com", port: 2020, scheme: "https"]
+          host: "example.com",
+          port: 2020,
+          scheme: "https"
         ],
         children: [
           %{
@@ -1504,7 +1627,7 @@ config :pleroma, :config_description, [
       %{
         key: :render_errors,
         type: :keyword,
-        suggestions: [[view: Pleroma.Web.ErrorView, accepts: ~w(json)]],
+        suggestions: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
         children: [
           %{
             key: :view,
@@ -1521,7 +1644,7 @@ config :pleroma, :config_description, [
       %{
         key: :pubsub,
         type: :keyword,
-        suggestions: [[name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]],
+        suggestions: [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2],
         children: [
           %{
             key: :name,
@@ -1588,17 +1711,20 @@ config :pleroma, :config_description, [
       },
       %{
         key: :sts,
+        label: "STS",
         type: :boolean,
         description: "Whether to additionally send a Strict-Transport-Security header"
       },
       %{
         key: :sts_max_age,
+        label: "STS max age",
         type: :integer,
         description: "The maximum age for the Strict-Transport-Security header if sent",
         suggestions: [31_536_000]
       },
       %{
         key: :ct_max_age,
+        label: "CT max age",
         type: :integer,
         description: "The maximum age for the Expect-CT header if sent",
         suggestions: [2_592_000]
@@ -1611,6 +1737,7 @@ config :pleroma, :config_description, [
       },
       %{
         key: :report_uri,
+        label: "Report URI",
         type: :string,
         description: "Adds the specified url to report-uri and report-to group in CSP header",
         suggestions: ["https://example.com/report-uri"]
@@ -1754,20 +1881,18 @@ config :pleroma, :config_description, [
       },
       %{
         key: :queues,
-        type: :keyword,
+        type: {:keyword, :integer},
         description:
           "Background jobs queues (keys: queues, values: max numbers of concurrent jobs)",
         suggestions: [
-          [
-            activity_expiration: 10,
-            background: 5,
-            federator_incoming: 50,
-            federator_outgoing: 50,
-            mailer: 10,
-            scheduled_activities: 10,
-            transmogrifier: 20,
-            web_push: 50
-          ]
+          activity_expiration: 10,
+          background: 5,
+          federator_incoming: 50,
+          federator_outgoing: 50,
+          mailer: 10,
+          scheduled_activities: 10,
+          transmogrifier: 20,
+          web_push: 50
         ],
         children: [
           %{
@@ -1830,7 +1955,7 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :retries,
-        type: :keyword,
+        type: {:keyword, :integer},
         description: "Max retry attempts for failed jobs, per `Oban` queue",
         suggestions: [
           [
@@ -1845,22 +1970,21 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: Pleroma.Web.Metadata,
     type: :group,
-    decsription: "Metadata-related settings",
+    description: "Metadata-related settings",
     children: [
       %{
         key: :providers,
         type: {:list, :module},
         description: "List of metadata providers to enable",
         suggestions: [
-          [
-            Pleroma.Web.Metadata.Providers.OpenGraph,
-            Pleroma.Web.Metadata.Providers.TwitterCard,
-            Pleroma.Web.Metadata.Providers.RelMe
-          ]
+          Pleroma.Web.Metadata.Providers.OpenGraph,
+          Pleroma.Web.Metadata.Providers.TwitterCard,
+          Pleroma.Web.Metadata.Providers.RelMe
         ]
       },
       %{
         key: :unfurl_nsfw,
+        label: "Unfurl NSFW",
         type: :boolean,
         description: "If set to true nsfw attachments will be shown in previews"
       }
@@ -1870,39 +1994,45 @@ config :pleroma, :config_description, [
     group: :pleroma,
     key: :rich_media,
     type: :group,
+    description:
+      "If enabled the instance will parse metadata from attached links to generate link previews.",
     children: [
       %{
         key: :enabled,
         type: :boolean,
-        description:
-          "if enabled the instance will parse metadata from attached links to generate link previews"
+        description: "Enables/disables RichMedia."
       },
       %{
         key: :ignore_hosts,
         type: {:list, :string},
-        description: "list of hosts which will be ignored by the metadata parser",
-        suggestions: [["accounts.google.com", "xss.website"]]
+        description: "List of hosts which will be ignored by the metadata parser.",
+        suggestions: ["accounts.google.com", "xss.website"]
       },
       %{
         key: :ignore_tld,
+        label: "Ignore TLD",
         type: {:list, :string},
-        description: "list TLDs (top-level domains) which will ignore for parse metadata",
-        suggestions: [["local", "localdomain", "lan"]]
+        description: "List TLDs (top-level domains) which will ignore for parse metadata.",
+        suggestions: ["local", "localdomain", "lan"]
       },
       %{
         key: :parsers,
         type: {:list, :module},
-        description: "list of Rich Media parsers",
+        description: "List of Rich Media parsers.",
         suggestions: [
-          Generator.richmedia_parsers()
+          Pleroma.Web.RichMedia.Parsers.MetaTagsParser,
+          Pleroma.Web.RichMedia.Parsers.OEmbed,
+          Pleroma.Web.RichMedia.Parsers.OGP,
+          Pleroma.Web.RichMedia.Parsers.TwitterCard
         ]
       },
       %{
         key: :ttl_setters,
+        label: "TTL setters",
         type: {:list, :module},
-        description: "list of rich media ttl setters",
+        description: "List of rich media ttl setters.",
         suggestions: [
-          [Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl]
+          Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl
         ]
       }
     ]
@@ -2055,23 +2185,57 @@ config :pleroma, :config_description, [
       },
       %{
         key: :ssl,
+        label: "SSL",
         type: :boolean,
         description: "true to use SSL, usually implies the port 636"
       },
       %{
         key: :sslopts,
+        label: "SSL options",
         type: :keyword,
-        description: "additional SSL options"
+        description: "additional SSL options",
+        suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer],
+        children: [
+          %{
+            key: :cacertfile,
+            type: :string,
+            description: "Path to file with PEM encoded cacerts",
+            suggestions: ["path/to/file/with/PEM/cacerts"]
+          },
+          %{
+            key: :verify,
+            type: :atom,
+            description: "Type of cert verification",
+            suggestions: [:verify_peer]
+          }
+        ]
       },
       %{
         key: :tls,
+        label: "TLS",
         type: :boolean,
         description: "true to start TLS, usually implies the port 389"
       },
       %{
         key: :tlsopts,
+        label: "TLS options",
         type: :keyword,
-        description: "additional TLS options"
+        description: "additional TLS options",
+        suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer],
+        children: [
+          %{
+            key: :cacertfile,
+            type: :string,
+            description: "Path to file with PEM encoded cacerts",
+            suggestions: ["path/to/file/with/PEM/cacerts"]
+          },
+          %{
+            key: :verify,
+            type: :atom,
+            description: "Type of cert verification",
+            suggestions: [:verify_peer]
+          }
+        ]
       },
       %{
         key: :base,
@@ -2120,7 +2284,7 @@ config :pleroma, :config_description, [
       },
       %{
         key: :oauth_consumer_strategies,
-        type: :string,
+        type: {:list, :string},
         description:
           "the list of enabled OAuth consumer strategies; by default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <>
             " Each entry in this space-delimited string should be of format <strategy> or <strategy>:<dependency>" <>
@@ -2163,7 +2327,7 @@ config :pleroma, :config_description, [
           },
           %{
             key: :interval,
-            type: :ininteger,
+            type: :integer,
             description: "Minimum interval between digest emails to one user",
             suggestions: [7]
           },
@@ -2185,9 +2349,9 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :logo,
-        type: [:string, nil],
+        type: :string,
         description: "a path to a custom logo. Set it to nil to use the default Pleroma logo",
-        suggestions: ["some/path/logo.png", nil]
+        suggestions: ["some/path/logo.png"]
       },
       %{
         key: :styling,
@@ -2279,26 +2443,24 @@ config :pleroma, :config_description, [
         key: :shortcode_globs,
         type: {:list, :string},
         description: "Location of custom emoji files. * can be used as a wildcard",
-        suggestions: [["/emoji/custom/**/*.png"]]
+        suggestions: ["/emoji/custom/**/*.png"]
       },
       %{
         key: :pack_extensions,
         type: {:list, :string},
         description:
           "A list of file extensions for emojis, when no emoji.txt for a pack is present",
-        suggestions: [[".png", ".gif"]]
+        suggestions: [".png", ".gif"]
       },
       %{
         key: :groups,
-        type: :keyword,
+        type: {:keyword, :string, {:list, :string}},
         description:
           "Emojis are ordered in groups (tags). This is an array of key-value pairs where the key is the groupname" <>
             " and the value the location or array of locations. * can be used as a wildcard",
         suggestions: [
-          [
-            # Put groups that have higher priority than defaults here. Example in `docs/config/custom_emoji.md`
-            Custom: ["/emoji/*.png", "/emoji/**/*.png"]
-          ]
+          # Put groups that have higher priority than defaults here. Example in `docs/config/custom_emoji.md`
+          Custom: ["/emoji/*.png", "/emoji/**/*.png"]
         ]
       },
       %{
@@ -2389,7 +2551,8 @@ config :pleroma, :config_description, [
     group: :esshd,
     type: :group,
     description:
-      "To enable simple command line interface accessible over ssh, add a setting like this to your configuration file",
+      "Before enabling this you must add :esshd to mix.exs as one of the extra_applications " <>
+        "and generate host keys in your priv dir with ssh-keygen -m PEM -N \"\" -b 2048 -t rsa -f ssh_host_rsa_key",
     children: [
       %{
         key: :enabled,
@@ -2443,27 +2606,27 @@ config :pleroma, :config_description, [
           %{
             key: "application/xml",
             type: {:list, :string},
-            suggestions: [["xml"]]
+            suggestions: ["xml"]
           },
           %{
             key: "application/xrd+xml",
             type: {:list, :string},
-            suggestions: [["xrd+xml"]]
+            suggestions: ["xrd+xml"]
           },
           %{
             key: "application/jrd+json",
             type: {:list, :string},
-            suggestions: [["jrd+json"]]
+            suggestions: ["jrd+json"]
           },
           %{
             key: "application/activity+json",
             type: {:list, :string},
-            suggestions: [["activity+json"]]
+            suggestions: ["activity+json"]
           },
           %{
             key: "application/ld+json",
             type: {:list, :string},
-            suggestions: [["activity+json"]]
+            suggestions: ["activity+json"]
           }
         ]
       }
@@ -2560,6 +2723,8 @@ config :pleroma, :config_description, [
   %{
     group: :pleroma,
     key: Pleroma.Uploaders.MDII,
+    description:
+      "Uploader for https://github.com/hakaba-hitoyo/minimal-digital-image-infrastructure",
     type: :group,
     children: [
       %{
@@ -2582,8 +2747,10 @@ config :pleroma, :config_description, [
     children: [
       %{
         key: :proxy_url,
-        type: [:string, :atom, nil],
-        suggestions: ["localhost:9020", {:socks5, :localhost, 3090}, nil]
+        label: "Proxy URL",
+        type: [:string, :tuple],
+        description: "Proxy URL",
+        suggestions: ["localhost:9020", {:socks5, :localhost, 3090}]
       },
       %{
         key: :send_user_agent,
@@ -2592,16 +2759,8 @@ config :pleroma, :config_description, [
       %{
         key: :adapter,
         type: :keyword,
-        suggestions: [
-          [
-            ssl_options: [
-              # Workaround for remote server certificate chain issues
-              partial_chain: &:hackney_connect.partial_chain/1,
-              # We don't support TLS v1.3 yet
-              versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]
-            ]
-          ]
-        ]
+        description: "Adapter specific options",
+        suggestions: []
       }
     ]
   },
@@ -2629,7 +2788,7 @@ config :pleroma, :config_description, [
       %{
         key: :scrub_policy,
         type: {:list, :module},
-        suggestions: [[Pleroma.HTML.Transform.MediaProxy, Pleroma.HTML.Scrubber.Default]]
+        suggestions: [Pleroma.HTML.Transform.MediaProxy, Pleroma.HTML.Scrubber.Default]
       }
     ]
   },
@@ -2647,6 +2806,8 @@ config :pleroma, :config_description, [
   %{
     group: :pleroma,
     key: :mrf_normalize_markup,
+    label: "MRF normalize markup",
+    description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
     type: :group,
     children: [
       %{
@@ -2665,38 +2826,36 @@ config :pleroma, :config_description, [
         key: :restricted_nicknames,
         type: {:list, :string},
         suggestions: [
-          [
-            ".well-known",
-            "~",
-            "about",
-            "activities",
-            "api",
-            "auth",
-            "check_password",
-            "dev",
-            "friend-requests",
-            "inbox",
-            "internal",
-            "main",
-            "media",
-            "nodeinfo",
-            "notice",
-            "oauth",
-            "objects",
-            "ostatus_subscribe",
-            "pleroma",
-            "proxy",
-            "push",
-            "registration",
-            "relay",
-            "settings",
-            "status",
-            "tag",
-            "user-search",
-            "user_exists",
-            "users",
-            "web"
-          ]
+          ".well-known",
+          "~",
+          "about",
+          "activities",
+          "api",
+          "auth",
+          "check_password",
+          "dev",
+          "friend-requests",
+          "inbox",
+          "internal",
+          "main",
+          "media",
+          "nodeinfo",
+          "notice",
+          "oauth",
+          "objects",
+          "ostatus_subscribe",
+          "pleroma",
+          "proxy",
+          "push",
+          "registration",
+          "relay",
+          "settings",
+          "status",
+          "tag",
+          "user-search",
+          "user_exists",
+          "users",
+          "web"
         ]
       }
     ]
@@ -2713,20 +2872,18 @@ config :pleroma, :config_description, [
       %{
         key: :methods,
         type: {:list, :string},
-        suggestions: [["POST", "PUT", "DELETE", "GET", "PATCH", "OPTIONS"]]
+        suggestions: ["POST", "PUT", "DELETE", "GET", "PATCH", "OPTIONS"]
       },
       %{
         key: :expose,
-        type: :string,
+        type: {:list, :string},
         suggestions: [
-          [
-            "Link",
-            "X-RateLimit-Reset",
-            "X-RateLimit-Limit",
-            "X-RateLimit-Remaining",
-            "X-Request-Id",
-            "Idempotency-Key"
-          ]
+          "Link",
+          "X-RateLimit-Reset",
+          "X-RateLimit-Limit",
+          "X-RateLimit-Remaining",
+          "X-Request-Id",
+          "Idempotency-Key"
         ]
       },
       %{
@@ -2736,7 +2893,7 @@ config :pleroma, :config_description, [
       %{
         key: :headers,
         type: {:list, :string},
-        suggestions: [["Authorization", "Content-Type", "Idempotency-Key"]]
+        suggestions: ["Authorization", "Content-Type", "Idempotency-Key"]
       }
     ]
   },
@@ -2745,16 +2902,14 @@ config :pleroma, :config_description, [
     key: Pleroma.Plugs.RemoteIp,
     type: :group,
     description: """
-    **If your instance is not behind at least one reverse proxy, you should not enable this plug.**
-
     `Pleroma.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
+    **If your instance is not behind at least one reverse proxy, you should not enable this plug.**
     """,
     children: [
       %{
         key: :enabled,
         type: :boolean,
-        description: "Enable/disable the plug. Defaults to `false`.",
-        suggestions: [true, false]
+        description: "Enable/disable the plug. Defaults to `false`."
       },
       %{
         key: :headers,
@@ -2788,7 +2943,7 @@ config :pleroma, :config_description, [
         type: :integer,
         description:
           "activity pub routes (except question activities). Defaults to `nil` (no expiration).",
-        suggestions: [30_000, nil]
+        suggestions: [30_000]
       },
       %{
         key: :activity_pub_question,
index d98a78af025c10c31216eea87fb04a0501d0005b..851c526d6c6429147d337fbbc14cf681b93956f8 100644 (file)
@@ -669,7 +669,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
 
 ### Run mix task pleroma.config migrate_to_db
 
-Copy settings on key `:pleroma` to DB.
+Copies `pleroma` environment settings to the database.
 
 - Params: none
 - Response:
@@ -682,7 +682,7 @@ Copy settings on key `:pleroma` to DB.
 
 ### Run mix task pleroma.config migrate_from_db
 
-Copy all settings from DB to `config/prod.exported_from_db.secret.exs` with deletion from DB.
+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
 - Response:
@@ -693,9 +693,9 @@ Copy all settings from DB to `config/prod.exported_from_db.secret.exs` with dele
 
 ## `GET /api/pleroma/admin/config`
 
-### List config settings
+### Get saved config settings
 
-List config settings only works with `:pleroma => :instance => :dynamic_configuration` setting to `true`.
+**Only works when `:dynamic_configuration` is `true`.**
 
 - Params: none
 - Response:
@@ -704,9 +704,9 @@ List config settings only works with `:pleroma => :instance => :dynamic_configur
 {
   configs: [
     {
-      "group": string,
-      "key": string or string with leading `:` for atoms,
-      "value": string or {} or [] or {"tuple": []}
+      "group": ":pleroma",
+      "key": "Pleroma.Upload",
+      "value": []
      }
   ]
 }
@@ -716,44 +716,61 @@ List config settings only works with `:pleroma => :instance => :dynamic_configur
 
 ### Update config settings
 
-Updating config settings only works with `:pleroma => :instance => :dynamic_configuration` setting to `true`.
-Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`.
-Atom keys and values can be passed with `:` in the beginning, e.g. `":upload"`.
-Tuples can be passed as `{"tuple": ["first_val", Pleroma.Module, []]}`.
-`{"tuple": ["some_string", "Pleroma.Some.Module", []]}` will be converted to `{"some_string", Pleroma.Some.Module, []}`.
-Keywords can be passed as lists with 2 child tuples, e.g.
-`[{"tuple": ["first_val", Pleroma.Module]}, {"tuple": ["second_val", true]}]`.
+**Only works when `:dynamic_configuration` is `true`.**
+
+Some modifications are necessary to save the config settings correctly:
 
-If value contains list of settings `[subkey: val1, subkey2: val2, subkey3: val3]`, it's possible to remove only subkeys instead of all settings passing `subkeys` parameter. E.g.:
-{"group": "pleroma", "key": "some_key", "delete": "true", "subkeys": [":subkey", ":subkey3"]}.
+- strings which start with `Pleroma.`, `Phoenix.`, `Tesla.` or strings like `Oban`, `Ueberauth` will be converted to modules;
+```
+"Pleroma.Upload" -> Pleroma.Upload
+"Oban" -> Oban
+```
+- strings starting with `:` will be converted to atoms;
+```
+":pleroma" -> :pleroma
+```
+- objects with `tuple` key and array value will be converted to atoms;
+```
+{"tuple": ["string", "Pleroma.Upload", []]} -> {"string", Pleroma.Upload, []}
+```
+- arrays with *tuple objects* and 2 childs in array will be converted to keywords;
+```
+[{"tuple": [":key1", "value"]}, {"tuple": [":key2", "value"]}] -> [key1: "value", key2: "value"]
+```
 
-Compile time settings (need instance reboot):
-- all settings by this keys:
+Most of the settings will be applied in `runtime`, this means that you don't need to restart the instance. But some settings are applied in `compile time` and require a reboot of the instance, such as:
+- all settings inside these keys:
   - `:hackney_pools`
   - `:chat`
   - `Pleroma.Web.Endpoint`
-  - `Pleroma.Repo`
-- part settings:
-  - `Pleroma.Captcha` -> `:seconds_valid`
-  - `Pleroma.Upload` -> `:proxy_remote`
-  - `:instance` -> `:upload_limit`
+- partially settings inside these keys:
+  - `:seconds_valid` in `Pleroma.Captcha`
+  - `:proxy_remote` in `Pleroma.Upload`
+  - `:upload_limit` in `:instance`
 
 - Params:
-  - `configs` => [
-    - `group` (string)
-    - `key` (string or string with leading `:` for atoms)
-    - `value` (string, [], {} or {"tuple": []})
-    - `delete` = true (optional, if parameter must be deleted)
-    - `subkeys` [(string with leading `:` for atoms)] (optional, works only if `delete=true` parameter is passed, otherwise will be ignored)
-  ]
+  - `configs` - array of config objects
+  - config object params:
+    - `group` - string (**required**)
+    - `key` - string (**required**)
+    - `value` - string, [], {} or {"tuple": []} (**required**)
+    - `delete` - true (*optional*, if setting must be deleted)
+    - `subkeys` - array of strings (*optional*, only works when `delete=true` parameter is passed, otherwise will be ignored)
+
+*When a value have several nested settings, you can delete only some nested settings by passing a parameter `subkeys`, without deleting all settings by key.*
+```
+[subkey: val1, subkey2: val2, subkey3: val3] \\ initial value
+{"group": ":pleroma", "key": "some_key", "delete": true, "subkeys": [":subkey", ":subkey3"]} \\ passing json for deletion
+[subkey2: val2] \\ value after deletion
+```
 
-- Request (example):
+- Request:
 
 ```json
 {
   configs: [
     {
-      "group": "pleroma",
+      "group": ":pleroma",
       "key": "Pleroma.Upload",
       "value": [
         {"tuple": [":uploader", "Pleroma.Uploaders.Local"]},
@@ -784,14 +801,47 @@ Compile time settings (need instance reboot):
 {
   configs: [
     {
-      "group": string,
-      "key": string or string with leading `:` for atoms,
-      "value": string or {} or [] or {"tuple": []}
+      "group": ":pleroma",
+      "key": "Pleroma.Upload",
+      "value": [...]
      }
   ]
 }
 ```
 
+## ` GET /api/pleroma/admin/config/descriptions`
+
+### Get JSON with config descriptions.
+Loads json generated from `config/descriptions.exs`.
+
+- Params: none
+- Response:
+
+```json
+[{
+    "group": ":pleroma", // string
+    "key": "ModuleName", // string
+    "type": "group", // string or list with possible values,
+    "description": "Upload general settings", // string
+    "children": [
+      {
+        "key": ":uploader", // string or module name `Pleroma.Upload`
+        "type": "module",
+        "description": "Module which will be used for uploads",
+        "suggestions": ["module1", "module2"]
+      },
+      {
+        "key": ":filters",
+        "type": ["list", "module"],
+        "description": "List of filter modules for uploads",
+        "suggestions": [
+          "module1", "module2", "module3"
+        ]
+      }
+    ]
+}]
+```
+
 ## `GET /api/pleroma/admin/moderation_log`
 
 ### Get moderation log
diff --git a/docs/admin/config.md b/docs/admin/config.md
new file mode 100644 (file)
index 0000000..f42ec89
--- /dev/null
@@ -0,0 +1,59 @@
+# Configuring instance
+You can configure your instance from admin interface. You need account with admin rights and little change in config file, which will allow settings dynamic configuration from database.
+
+```elixir
+config :pleroma, :instance,
+  dynamic_configuration: true
+```
+
+## 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.
+
+File with duplicated settings is located in `config/{env}.exported_from_db.exs`. For prod env it will be `config/prod.exported_from_db.exs`.
+
+## How to set it up
+You need to migrate your existing settings to the database. You can do this with mix task (all config files will remain untouched):
+```bash
+mix pleroma.config migrate_to_db
+```
+Now you can change settings in admin interface. After each save, settings are duplicated to the `config/{env}.exported_from_db.exs` file.
+
+<span style="color:red">**ATTENTION**</span>
+
+**<span style="color:red">Be careful while changing the settings. Every inaccurate configuration change can break the federation or the instance load.</span>**
+
+*Compile time settings, which require instance reboot and can break instance loading:*
+- all settings inside these keys:
+  - `:hackney_pools`
+  - `:chat`
+  - `Pleroma.Web.Endpoint`
+- partially settings inside these keys:
+  - `:seconds_valid` in `Pleroma.Captcha`
+  - `:proxy_remote` in `Pleroma.Upload`
+  - `:upload_limit` in `:instance`
+
+## How to remove it
+
+1. Truncate or delete all values from `config` table
+```sql
+TRUNCATE TABLE config;
+```
+2. Delete `config/{env}.exported_from_db.exs`.
+
+For `prod` env:
+```bash
+cd /opt/pleroma
+cp config/prod.exported_from_db.exs config/exported_from_db.back
+rm -rf config/prod.exported_from_db.exs
+```
+*If you don't want to backup settings, you can skip step with `cp` command.*
+
+3. Set dynamic configuration to `false`.
+```elixir
+config :pleroma, :instance,
+  dynamic_configuration: false
+```
+4. Restart pleroma instance
+```bash
+sudo service pleroma restart
+```
index 590c7a91431ee271daa10007997b17e97b7a7201..bb126463c790b0a88967c222daa07bcd70a2c0b6 100644 (file)
@@ -9,27 +9,29 @@ defmodule Mix.Tasks.Pleroma.Config do
   alias Pleroma.Web.AdminAPI.Config
   @shortdoc "Manages the location of the config"
   @moduledoc File.read!("docs/administration/CLI_tasks/config.md")
+
+  @groups [
+    :pleroma,
+    :logger,
+    :quack,
+    :mime,
+    :tesla,
+    :phoenix,
+    :cors_plug,
+    :auto_linker,
+    :esshd,
+    :ueberauth,
+    :prometheus,
+    :http_signatures,
+    :web_push_encryption,
+    :joken
+  ]
+
   def run(["migrate_to_db"]) do
     start_pleroma()
 
     if Pleroma.Config.get([:instance, :dynamic_configuration]) do
-      Application.get_all_env(:pleroma)
-      |> Enum.reject(fn {k, _v} -> k in [Pleroma.Repo, :env] end)
-      |> Enum.each(fn {k, v} ->
-        key = to_string(k) |> String.replace("Elixir.", "")
-
-        key =
-          if String.starts_with?(key, "Pleroma.") do
-            key
-          else
-            ":" <> key
-          end
-
-        {:ok, _} = Config.update_or_create(%{group: "pleroma", key: key, value: v})
-        Mix.shell().info("#{key} is migrated.")
-      end)
-
-      Mix.shell().info("Settings migrated.")
+      Enum.each(@groups, &load_and_create(&1))
     else
       Mix.shell().info(
         "Migration is not allowed by config. You can change this behavior in instance settings."
@@ -37,38 +39,63 @@ defmodule Mix.Tasks.Pleroma.Config do
     end
   end
 
-  def run(["migrate_from_db", env, delete?]) do
+  def run(["migrate_from_db" | options]) do
     start_pleroma()
 
-    delete? = if delete? == "true", do: true, else: false
-
-    if Pleroma.Config.get([:instance, :dynamic_configuration]) do
-      config_path = "config/#{env}.exported_from_db.secret.exs"
+    {opts, _} =
+      OptionParser.parse!(options,
+        strict: [env: :string, delete_from_db: :boolean],
+        aliases: [d: :delete_from_db]
+      )
 
-      {:ok, file} = File.open(config_path, [:write, :utf8])
+    with {:active?, true} <- {:active?, Pleroma.Config.get([:instance, :dynamic_configuration])},
+         env_path when is_binary(env_path) <- opts[:env],
+         config_path <- "config/#{env_path}.exported_from_db.secret.exs",
+         {:ok, file} <- File.open(config_path, [:write, :utf8]) do
       IO.write(file, "use Mix.Config\r\n")
 
-      Repo.all(Config)
-      |> Enum.each(fn config ->
-        IO.write(
-          file,
-          "config :#{config.group}, #{config.key}, #{
-            inspect(Config.from_binary(config.value), limit: :infinity)
-          }\r\n\r\n"
-        )
-
-        if delete? do
-          {:ok, _} = Repo.delete(config)
-          Mix.shell().info("#{config.key} deleted from DB.")
-        end
-      end)
+      Config
+      |> Repo.all()
+      |> Enum.each(&write_to_file_with_deletion(&1, file, opts[:delete_from_db]))
 
       File.close(file)
       System.cmd("mix", ["format", config_path])
     else
-      Mix.shell().info(
-        "Migration is not allowed by config. You can change this behavior in instance settings."
-      )
+      {:active?, false} ->
+        Mix.shell().info(
+          "migration is not allowed by config. You can change this behavior in instance settings."
+        )
+
+      error ->
+        Mix.shell().info("error occuried while opening file. #{inspect(error)}")
+    end
+  end
+
+  defp load_and_create(group) do
+    group
+    |> Application.get_all_env()
+    |> Enum.reject(fn {k, _v} -> k in [Pleroma.Repo, :env] end)
+    |> Enum.each(fn {key, value} ->
+      key_str = inspect(key)
+
+      {:ok, _} = Config.update_or_create(%{group: ":#{group}", key: key_str, value: value})
+      Mix.shell().info("settings for key #{key_str} migrated.")
+    end)
+
+    Mix.shell().info("settings for group :#{group} migrated.")
+  end
+
+  defp write_to_file_with_deletion(config, file, with_deletion) do
+    IO.write(
+      file,
+      "config #{config.group}, #{config.key}, #{
+        inspect(Config.from_binary(config.value), limit: :infinity)
+      }\r\n\r\n"
+    )
+
+    if with_deletion do
+      {:ok, _} = Repo.delete(config)
+      Mix.shell().info("#{config.key} deleted from DB.")
     end
   end
 end
index 3214c9951281dd9ceea9847cc879900075e0f4cf..0bc4c4029a30a155a61712eee3f94e43cbca1f18 100644 (file)
@@ -4,56 +4,59 @@
 
 defmodule Pleroma.Config.TransferTask do
   use Task
+
+  require Logger
+
+  alias Pleroma.Repo
   alias Pleroma.Web.AdminAPI.Config
 
   def start_link(_) do
     load_and_update_env()
-    if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Pleroma.Repo)
+    if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Repo)
     :ignore
   end
 
   def load_and_update_env do
-    if Pleroma.Config.get([:instance, :dynamic_configuration]) and
-         Ecto.Adapters.SQL.table_exists?(Pleroma.Repo, "config") do
-      for_restart =
-        Pleroma.Repo.all(Config)
-        |> Enum.map(&update_env(&1))
-
+    with true <- Pleroma.Config.get([:instance, :dynamic_configuration]),
+         true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"),
+         started_applications <- Application.started_applications() do
       # 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)
+      Config
+      |> Repo.all()
+      |> Enum.map(&update_env(&1))
+      |> Enum.uniq()
+      # TODO: some problem with prometheus after restart!
+      |> Enum.reject(&(&1 in [:pleroma, nil, :prometheus]))
+      |> Enum.each(&restart(started_applications, &1))
     end
   end
 
   defp update_env(setting) do
     try do
-      key =
-        if String.starts_with?(setting.key, "Pleroma.") do
-          "Elixir." <> setting.key
-        else
-          String.trim_leading(setting.key, ":")
-        end
+      key = Config.from_string(setting.key)
+      group = Config.from_string(setting.group)
+      value = Config.from_binary(setting.value)
 
-      group = String.to_existing_atom(setting.group)
-
-      Application.put_env(
-        group,
-        String.to_existing_atom(key),
-        Config.from_binary(setting.value)
-      )
+      :ok = Application.put_env(group, key, value)
 
       group
     rescue
       e ->
-        require Logger
-
         Logger.warn(
           "updating env causes error, key: #{inspect(setting.key)}, error: #{inspect(e)}"
         )
+
+        nil
+    end
+  end
+
+  defp restart(started_applications, app) do
+    with {^app, _, _} <- List.keyfind(started_applications, app, 0),
+         :ok <- Application.stop(app) do
+      :ok = Application.start(app)
+    else
+      nil -> Logger.warn("#{app} is not started.")
+      error -> Logger.warn(inspect(error))
     end
   end
 end
index aa578eee280657f4a7bf9eb35016f58bfbc795e9..b57e47e8be71148fcfdb84975c83f637194716be 100644 (file)
@@ -6,68 +6,108 @@ defmodule Pleroma.Docs.Generator do
     implementation.process(descriptions)
   end
 
-  @spec uploaders_list() :: [module()]
-  def uploaders_list do
-    {:ok, modules} = :application.get_key(:pleroma, :modules)
+  @spec list_modules_in_dir(String.t(), String.t()) :: [module()]
+  def list_modules_in_dir(dir, start) do
+    with {:ok, files} <- File.ls(dir) do
+      files
+      |> Enum.filter(&String.ends_with?(&1, ".ex"))
+      |> Enum.map(fn filename ->
+        module = filename |> String.trim_trailing(".ex") |> Macro.camelize()
+        String.to_existing_atom(start <> module)
+      end)
+    end
+  end
+
+  @doc """
+  Converts:
+  - atoms to strings with leading `:`
+  - module names to strings, without leading `Elixir.`
+  - add humanized labels to `keys` if label is not defined, e.g. `:instance` -> `Instance`
+  """
+  @spec convert_to_strings([map()]) :: [map()]
+  def convert_to_strings(descriptions) do
+    Enum.map(descriptions, &format_entity(&1))
+  end
+
+  defp format_entity(entity) do
+    entity
+    |> format_key()
+    |> Map.put(:group, atom_to_string(entity[:group]))
+    |> format_children()
+  end
 
-    Enum.filter(modules, fn module ->
-      name_as_list = Module.split(module)
+  defp format_key(%{key: key} = entity) do
+    entity
+    |> Map.put(:key, atom_to_string(key))
+    |> Map.put(:label, entity[:label] || humanize(key))
+  end
 
-      List.starts_with?(name_as_list, ["Pleroma", "Uploaders"]) and
-        List.last(name_as_list) != "Uploader"
-    end)
+  defp format_key(%{group: group} = entity) do
+    Map.put(entity, :label, entity[:label] || humanize(group))
   end
 
-  @spec filters_list() :: [module()]
-  def filters_list do
-    {:ok, modules} = :application.get_key(:pleroma, :modules)
+  defp format_key(entity), do: entity
+
+  defp format_children(%{children: children} = entity) do
+    Map.put(entity, :children, Enum.map(children, &format_child(&1)))
+  end
 
-    Enum.filter(modules, fn module ->
-      name_as_list = Module.split(module)
+  defp format_children(entity), do: entity
+
+  defp format_child(%{suggestions: suggestions} = entity) do
+    entity
+    |> Map.put(:suggestions, format_suggestions(suggestions))
+    |> format_key()
+    |> format_children()
+  end
 
-      List.starts_with?(name_as_list, ["Pleroma", "Upload", "Filter"])
-    end)
+  defp format_child(entity) do
+    entity
+    |> format_key()
+    |> format_children()
   end
 
-  @spec mrf_list() :: [module()]
-  def mrf_list do
-    {:ok, modules} = :application.get_key(:pleroma, :modules)
+  defp atom_to_string(entity) when is_binary(entity), do: entity
 
-    Enum.filter(modules, fn module ->
-      name_as_list = Module.split(module)
+  defp atom_to_string(entity) when is_atom(entity), do: inspect(entity)
 
-      List.starts_with?(name_as_list, ["Pleroma", "Web", "ActivityPub", "MRF"]) and
-        length(name_as_list) > 4
-    end)
+  defp humanize(entity) do
+    string = inspect(entity)
+
+    if String.starts_with?(string, ":"),
+      do: Phoenix.Naming.humanize(entity),
+      else: string
   end
 
-  @spec richmedia_parsers() :: [module()]
-  def richmedia_parsers do
-    {:ok, modules} = :application.get_key(:pleroma, :modules)
+  defp format_suggestions([]), do: []
+
+  defp format_suggestions([suggestion | tail]) do
+    [format_suggestion(suggestion) | format_suggestions(tail)]
+  end
+
+  defp format_suggestion(entity) when is_atom(entity) do
+    atom_to_string(entity)
+  end
 
-    Enum.filter(modules, fn module ->
-      name_as_list = Module.split(module)
+  defp format_suggestion([head | tail] = entity) when is_list(entity) do
+    [format_suggestion(head) | format_suggestions(tail)]
+  end
 
-      List.starts_with?(name_as_list, ["Pleroma", "Web", "RichMedia", "Parsers"]) and
-        length(name_as_list) == 5
-    end)
+  defp format_suggestion(entity) when is_tuple(entity) do
+    format_suggestions(Tuple.to_list(entity)) |> List.to_tuple()
   end
+
+  defp format_suggestion(entity), do: entity
 end
 
 defimpl Jason.Encoder, for: Tuple do
-  def encode(tuple, opts) do
-    Jason.Encode.list(Tuple.to_list(tuple), opts)
-  end
+  def encode(tuple, opts), do: Jason.Encode.list(Tuple.to_list(tuple), opts)
 end
 
 defimpl Jason.Encoder, for: [Regex, Function] do
-  def encode(term, opts) do
-    Jason.Encode.string(inspect(term), opts)
-  end
+  def encode(term, opts), do: Jason.Encode.string(inspect(term), opts)
 end
 
 defimpl String.Chars, for: Regex do
-  def to_string(term) do
-    inspect(term)
-  end
+  def to_string(term), do: inspect(term)
 end
index f2a56d845e594752db66cd24d67483be0f9f6221..f191b60132f4fd53866681390cae039b5a07d4f9 100644 (file)
@@ -3,18 +3,22 @@ defmodule Pleroma.Docs.JSON do
 
   @spec process(keyword()) :: {:ok, String.t()}
   def process(descriptions) do
-    config_path = "docs/generate_config.json"
-
-    with {:ok, file} <- File.open(config_path, [:write, :utf8]),
-         json <- generate_json(descriptions),
+    with path <- "docs/generated_config.json",
+         {:ok, file} <- File.open(path, [:write, :utf8]),
+         formatted_descriptions <-
+           Pleroma.Docs.Generator.convert_to_strings(descriptions),
+         json <- Jason.encode!(formatted_descriptions),
          :ok <- IO.write(file, json),
          :ok <- File.close(file) do
-      {:ok, config_path}
+      {:ok, path}
     end
   end
 
-  @spec generate_json([keyword()]) :: String.t()
-  def generate_json(descriptions) do
-    Jason.encode!(descriptions)
+  def compile do
+    with {config, _paths} <- Mix.Config.eval!("config/description.exs") do
+      config[:pleroma][:config_description]
+      |> Pleroma.Docs.Generator.convert_to_strings()
+      |> Jason.encode!()
+    end
   end
 end
index 4eaea00d8de7659eeb4d3e309bcdd1f34f84b186..9a03d67c0b9e481995ccbe0173701eb30770018d 100644 (file)
@@ -19,8 +19,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
   def filter(%{"type" => message_type} = message) do
     with accepted_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :accept]),
          rejected_vocabulary <- Pleroma.Config.get([:mrf_vocabulary, :reject]),
-         true <-
-           length(accepted_vocabulary) == 0 || Enum.member?(accepted_vocabulary, message_type),
+         true <- accepted_vocabulary == [] || Enum.member?(accepted_vocabulary, message_type),
          false <-
            length(rejected_vocabulary) > 0 && Enum.member?(rejected_vocabulary, message_type),
          {:ok, _} <- filter(message["object"]) do
index c8abeff06f922dee857709221b725c2c5f59d460..376f880611aac8afcbcbc328a8543ae947415233 100644 (file)
@@ -4,6 +4,9 @@
 
 defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   use Pleroma.Web, :controller
+
+  import Pleroma.Web.ControllerHelper, only: [json_response: 3]
+
   alias Pleroma.Activity
   alias Pleroma.ModerationLog
   alias Pleroma.Plugs.OAuthScopesPlug
@@ -25,10 +28,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   alias Pleroma.Web.MastodonAPI.StatusView
   alias Pleroma.Web.Router
 
-  import Pleroma.Web.ControllerHelper, only: [json_response: 3]
-
   require Logger
 
+  @descriptions_json Pleroma.Docs.JSON.compile()
+  @users_page_size 50
+
   plug(
     OAuthScopesPlug,
     %{scopes: ["read:accounts"], admin: true}
@@ -93,8 +97,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
     when action in [:relay_follow, :relay_unfollow, :config_update]
   )
 
-  @users_page_size 50
-
   action_fallback(:errors)
 
   def user_delete(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
@@ -782,10 +784,22 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
   end
 
   def migrate_from_db(conn, _params) do
-    Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "true"])
+    Mix.Tasks.Pleroma.Config.run([
+      "migrate_from_db",
+      "--env",
+      to_string(Pleroma.Config.get(:env)),
+      "-d"
+    ])
+
     json(conn, %{})
   end
 
+  def config_descriptions(conn, _params) do
+    conn
+    |> Plug.Conn.put_resp_content_type("application/json")
+    |> Plug.Conn.send_resp(200, @descriptions_json)
+  end
+
   def config_show(conn, _params) do
     configs = Pleroma.Repo.all(Config)
 
@@ -800,17 +814,27 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
         updated =
           Enum.map(configs, fn
             %{"group" => group, "key" => key, "delete" => "true"} = params ->
-              {:ok, config} = Config.delete(%{group: group, key: key, subkeys: params["subkeys"]})
-              config
+              with {:ok, config} <-
+                     Config.delete(%{group: group, key: key, subkeys: params["subkeys"]}) do
+                config
+              end
 
             %{"group" => group, "key" => key, "value" => value} ->
-              {:ok, config} = Config.update_or_create(%{group: group, key: key, value: value})
-              config
+              with {:ok, config} <-
+                     Config.update_or_create(%{group: group, key: key, value: value}) do
+                config
+              end
           end)
           |> Enum.reject(&is_nil(&1))
 
         Pleroma.Config.TransferTask.load_and_update_env()
-        Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "false"])
+
+        Mix.Tasks.Pleroma.Config.run([
+          "migrate_from_db",
+          "--env",
+          to_string(Pleroma.Config.get(:env))
+        ])
+
         updated
       else
         []
index 1917a55805b9a728f0048ccf5150f54b498746be..a74acfbc626027d35f497923e2692e1d7febbc49 100644 (file)
@@ -24,6 +24,8 @@ defmodule Pleroma.Web.AdminAPI.Config do
 
   @spec changeset(Config.t(), map()) :: Changeset.t()
   def changeset(config, params \\ %{}) do
+    params = Map.put(params, :value, transform(params[:value]))
+
     config
     |> cast(params, [:key, :group, :value])
     |> validate_required([:key, :group, :value])
@@ -33,42 +35,43 @@ defmodule Pleroma.Web.AdminAPI.Config do
   @spec create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
   def create(params) do
     %Config{}
-    |> changeset(Map.put(params, :value, transform(params[:value])))
+    |> changeset(params)
     |> Repo.insert()
   end
 
   @spec update(Config.t(), map()) :: {:ok, Config} | {:error, Changeset.t()}
   def update(%Config{} = config, %{value: value}) do
     config
-    |> change(value: transform(value))
+    |> changeset(%{value: value})
     |> Repo.update()
   end
 
   @spec update_or_create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
   def update_or_create(params) do
-    with %Config{} = config <- Config.get_by_params(Map.take(params, [:group, :key])) do
+    search_opts = Map.take(params, [:group, :key])
+
+    with %Config{} = config <- Config.get_by_params(search_opts) do
       Config.update(config, params)
     else
       nil -> Config.create(params)
     end
   end
 
-  @spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
+  @spec delete(map()) :: {:ok, Config.t()} | {:error, Changeset.t()} | {:ok, nil}
   def delete(params) do
-    with %Config{} = config <- Config.get_by_params(Map.delete(params, :subkeys)) do
-      if params[:subkeys] do
-        updated_value =
-          Keyword.drop(
-            :erlang.binary_to_term(config.value),
-            Enum.map(params[:subkeys], &do_transform_string(&1))
-          )
-
-        Config.update(config, %{value: updated_value})
-      else
+    search_opts = Map.delete(params, :subkeys)
+
+    with %Config{} = config <- Config.get_by_params(search_opts),
+         {config, sub_keys} when is_list(sub_keys) <- {config, params[:subkeys]},
+         old_value <- :erlang.binary_to_term(config.value),
+         keys <- Enum.map(sub_keys, &do_transform_string(&1)),
+         new_value <- Keyword.drop(old_value, keys) do
+      Config.update(config, %{value: new_value})
+    else
+      {config, nil} ->
         Repo.delete(config)
         {:ok, nil}
-      end
-    else
+
       nil ->
         err =
           dgettext("errors", "Config with params %{params} not found", params: inspect(params))
@@ -82,10 +85,22 @@ defmodule Pleroma.Web.AdminAPI.Config do
 
   @spec from_binary_with_convert(binary()) :: any()
   def from_binary_with_convert(binary) do
-    from_binary(binary)
+    binary
+    |> from_binary()
     |> do_convert()
   end
 
+  @spec from_string(String.t()) :: atom() | no_return()
+  def from_string(":" <> entity), do: String.to_existing_atom(entity)
+
+  def from_string(entity) when is_binary(entity) do
+    if is_module_name?(entity) do
+      String.to_existing_atom("Elixir.#{entity}")
+    else
+      entity
+    end
+  end
+
   defp do_convert(entity) when is_list(entity) do
     for v <- entity, into: [], do: do_convert(v)
   end
@@ -97,6 +112,7 @@ defmodule Pleroma.Web.AdminAPI.Config do
   end
 
   defp do_convert({:dispatch, [entity]}), do: %{"tuple" => [":dispatch", [inspect(entity)]]}
+  # TODO: will become useless after removing hackney
   defp do_convert({:partial_chain, entity}), do: %{"tuple" => [":partial_chain", inspect(entity)]}
 
   defp do_convert(entity) when is_tuple(entity),
@@ -105,21 +121,15 @@ defmodule Pleroma.Web.AdminAPI.Config do
   defp do_convert(entity) when is_boolean(entity) or is_number(entity) or is_nil(entity),
     do: entity
 
-  defp do_convert(entity) when is_atom(entity) do
-    string = to_string(entity)
-
-    if String.starts_with?(string, "Elixir."),
-      do: do_convert(string),
-      else: ":" <> string
-  end
-
-  defp do_convert("Elixir." <> module_name), do: module_name
+  defp do_convert(entity) when is_atom(entity), do: inspect(entity)
 
   defp do_convert(entity) when is_binary(entity), do: entity
 
-  @spec transform(any()) :: binary()
+  @spec transform(any()) :: binary() | no_return()
   def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity) do
-    :erlang.term_to_binary(do_transform(entity))
+    entity
+    |> do_transform()
+    |> :erlang.term_to_binary()
   end
 
   def transform(entity), do: :erlang.term_to_binary(entity)
@@ -131,6 +141,7 @@ defmodule Pleroma.Web.AdminAPI.Config do
     {:dispatch, [dispatch_settings]}
   end
 
+  # TODO: will become useless after removing hackney
   defp do_transform(%{"tuple" => [":partial_chain", entity]}) do
     {partial_chain, []} = do_eval(entity)
     {:partial_chain, partial_chain}
@@ -149,34 +160,63 @@ defmodule Pleroma.Web.AdminAPI.Config do
   end
 
   defp do_transform(entity) when is_binary(entity) do
-    String.trim(entity)
+    entity
+    |> String.trim()
     |> do_transform_string()
   end
 
   defp do_transform(entity), do: entity
 
-  defp do_transform_string("~r/" <> pattern) do
-    modificator = String.split(pattern, "/") |> List.last()
-    pattern = String.trim_trailing(pattern, "/" <> modificator)
+  @delimiters ["/", "|", "\"", "'", {"(", ")"}, {"[", "]"}, {"{", "}"}, {"<", ">"}]
+
+  defp find_valid_delimiter([], _string, _),
+    do: raise(ArgumentError, message: "valid delimiter for Regex expression not found")
 
-    case modificator do
-      "" -> ~r/#{pattern}/
-      "i" -> ~r/#{pattern}/i
-      "u" -> ~r/#{pattern}/u
-      "s" -> ~r/#{pattern}/s
+  defp find_valid_delimiter([{leading, closing} = delimiter | others], pattern, regex_delimiter)
+       when is_tuple(delimiter) do
+    if String.contains?(pattern, closing) do
+      find_valid_delimiter(others, pattern, regex_delimiter)
+    else
+      {:ok, {leading, closing}}
+    end
+  end
+
+  defp find_valid_delimiter([delimiter | others], pattern, regex_delimiter) do
+    if String.contains?(pattern, delimiter) do
+      find_valid_delimiter(others, pattern, regex_delimiter)
+    else
+      {:ok, {delimiter, delimiter}}
+    end
+  end
+
+  @regex_parts ~r/^~r(?'delimiter'[\/|"'([{<]{1})(?'pattern'.+)[\/|"')\]}>]{1}(?'modifier'[uismxfU]*)/u
+
+  defp do_transform_string("~r" <> _pattern = regex) do
+    with %{"modifier" => modifier, "pattern" => pattern, "delimiter" => regex_delimiter} <-
+           Regex.named_captures(@regex_parts, regex),
+         {:ok, {leading, closing}} <- find_valid_delimiter(@delimiters, pattern, regex_delimiter),
+         {result, _} <- Code.eval_string("~r#{leading}#{pattern}#{closing}#{modifier}") do
+      result
     end
   end
 
   defp do_transform_string(":" <> atom), do: String.to_atom(atom)
 
   defp do_transform_string(value) do
-    if String.starts_with?(value, "Pleroma") or String.starts_with?(value, "Phoenix"),
-      do: String.to_existing_atom("Elixir." <> value),
-      else: value
+    if is_module_name?(value) do
+      String.to_existing_atom("Elixir." <> value)
+    else
+      value
+    end
+  end
+
+  @spec is_module_name?(String.t()) :: boolean()
+  def is_module_name?(string) do
+    Regex.match?(~r/^(Pleroma|Phoenix|Tesla)\./, string) or string in ["Oban", "Ueberauth"]
   end
 
   defp do_eval(entity) do
     cleaned_string = String.replace(entity, ~r/[^\w|^{:,[|^,|^[|^\]^}|^\/|^\.|^"]^\s/, "")
-    Code.eval_string(cleaned_string, [], requires: [], macros: [])
+    Code.eval_string(cleaned_string)
   end
 end
index f6c1282832b23de5d5d565a931d950ffbdebd342..a182e90e7005df74db4384dc1d770a89f20110ea 100644 (file)
@@ -195,6 +195,7 @@ defmodule Pleroma.Web.Router do
 
     get("/config", AdminAPIController, :config_show)
     post("/config", AdminAPIController, :config_update)
+    get("/config/descriptions", AdminAPIController, :config_descriptions)
     get("/config/migrate_to_db", AdminAPIController, :migrate_to_db)
     get("/config/migrate_from_db", AdminAPIController, :migrate_from_db)
 
index 9074f3b97fdf72c130300fb00ca84523e268b093..4b3dd8bbda119c065c4a8fb8c0d346b2589e2a1a 100644 (file)
@@ -14,14 +14,14 @@ defmodule Pleroma.Config.TransferTaskTest do
     refute Application.get_env(:idna, :test_key)
 
     Pleroma.Web.AdminAPI.Config.create(%{
-      group: "pleroma",
-      key: "test_key",
+      group: ":pleroma",
+      key: ":test_key",
       value: [live: 2, com: 3]
     })
 
     Pleroma.Web.AdminAPI.Config.create(%{
-      group: "idna",
-      key: "test_key",
+      group: ":idna",
+      key: ":test_key",
       value: [live: 15, com: 35]
     })
 
@@ -38,14 +38,14 @@ defmodule Pleroma.Config.TransferTaskTest do
 
   test "non existing atom" do
     Pleroma.Web.AdminAPI.Config.create(%{
-      group: "pleroma",
-      key: "undefined_atom_key",
+      group: ":pleroma",
+      key: ":undefined_atom_key",
       value: [live: 2, com: 3]
     })
 
     assert ExUnit.CaptureLog.capture_log(fn ->
              Pleroma.Config.TransferTask.start_link([])
            end) =~
-             "updating env causes error, key: \"undefined_atom_key\", error: %ArgumentError{message: \"argument error\"}"
+             "updating env causes error, key: \":undefined_atom_key\", error: %ArgumentError{message: \"argument error\"}"
   end
 end
diff --git a/test/docs/generator_test.exs b/test/docs/generator_test.exs
new file mode 100644 (file)
index 0000000..42e7c32
--- /dev/null
@@ -0,0 +1,211 @@
+defmodule Pleroma.Docs.GeneratorTest do
+  use ExUnit.Case, async: true
+  alias Pleroma.Docs.Generator
+
+  @descriptions [
+    %{
+      group: :pleroma,
+      key: Pleroma.Upload,
+      type: :group,
+      description: "",
+      children: [
+        %{
+          key: :uploader,
+          type: :module,
+          description: "",
+          suggestions:
+            Generator.list_modules_in_dir(
+              "lib/pleroma/upload/filter",
+              "Elixir.Pleroma.Upload.Filter."
+            )
+        },
+        %{
+          key: :filters,
+          type: {:list, :module},
+          description: "",
+          suggestions:
+            Generator.list_modules_in_dir(
+              "lib/pleroma/web/activity_pub/mrf",
+              "Elixir.Pleroma.Web.ActivityPub.MRF."
+            )
+        },
+        %{
+          key: Pleroma.Upload,
+          type: :string,
+          description: "",
+          suggestions: [""]
+        },
+        %{
+          key: :some_key,
+          type: :keyword,
+          description: "",
+          suggestions: [],
+          children: [
+            %{
+              key: :another_key,
+              type: :integer,
+              description: "",
+              suggestions: [5]
+            },
+            %{
+              key: :another_key_with_label,
+              label: "Another label",
+              type: :integer,
+              description: "",
+              suggestions: [7]
+            }
+          ]
+        },
+        %{
+          key: :key1,
+          type: :atom,
+          description: "",
+          suggestions: [
+            :atom,
+            Pleroma.Upload,
+            {:tuple, "string", 8080},
+            [:atom, Pleroma.Upload, {:atom, Pleroma.Upload}]
+          ]
+        },
+        %{
+          key: Pleroma.Upload,
+          label: "Special Label",
+          type: :string,
+          description: "",
+          suggestions: [""]
+        },
+        %{
+          group: {:subgroup, Swoosh.Adapters.SMTP},
+          key: :auth,
+          type: :atom,
+          description: "`Swoosh.Adapters.SMTP` adapter specific setting",
+          suggestions: [:always, :never, :if_available]
+        },
+        %{
+          key: "application/xml",
+          type: {:list, :string},
+          suggestions: ["xml"]
+        }
+      ]
+    },
+    %{
+      group: :tesla,
+      key: :adapter,
+      type: :group,
+      description: ""
+    },
+    %{
+      group: :cors_plug,
+      type: :group,
+      children: [%{key: :key1, type: :string, suggestions: [""]}]
+    },
+    %{group: "Some string group", key: "Some string key", type: :group}
+  ]
+
+  describe "convert_to_strings/1" do
+    test "group, key, label" do
+      [desc1, desc2 | _] = Generator.convert_to_strings(@descriptions)
+
+      assert desc1[:group] == ":pleroma"
+      assert desc1[:key] == "Pleroma.Upload"
+      assert desc1[:label] == "Pleroma.Upload"
+
+      assert desc2[:group] == ":tesla"
+      assert desc2[:key] == ":adapter"
+      assert desc2[:label] == "Adapter"
+    end
+
+    test "group without key" do
+      descriptions = Generator.convert_to_strings(@descriptions)
+      desc = Enum.at(descriptions, 2)
+
+      assert desc[:group] == ":cors_plug"
+      refute desc[:key]
+      assert desc[:label] == "Cors plug"
+    end
+
+    test "children key, label, type" do
+      [%{children: [child1, child2, child3, child4 | _]} | _] =
+        Generator.convert_to_strings(@descriptions)
+
+      assert child1[:key] == ":uploader"
+      assert child1[:label] == "Uploader"
+      assert child1[:type] == :module
+
+      assert child2[:key] == ":filters"
+      assert child2[:label] == "Filters"
+      assert child2[:type] == {:list, :module}
+
+      assert child3[:key] == "Pleroma.Upload"
+      assert child3[:label] == "Pleroma.Upload"
+      assert child3[:type] == :string
+
+      assert child4[:key] == ":some_key"
+      assert child4[:label] == "Some key"
+      assert child4[:type] == :keyword
+    end
+
+    test "child with predefined label" do
+      [%{children: children} | _] = Generator.convert_to_strings(@descriptions)
+      child = Enum.at(children, 5)
+      assert child[:key] == "Pleroma.Upload"
+      assert child[:label] == "Special Label"
+    end
+
+    test "subchild" do
+      [%{children: children} | _] = Generator.convert_to_strings(@descriptions)
+      child = Enum.at(children, 3)
+      %{children: [subchild | _]} = child
+
+      assert subchild[:key] == ":another_key"
+      assert subchild[:label] == "Another key"
+      assert subchild[:type] == :integer
+    end
+
+    test "subchild with predefined label" do
+      [%{children: children} | _] = Generator.convert_to_strings(@descriptions)
+      child = Enum.at(children, 3)
+      %{children: subchildren} = child
+      subchild = Enum.at(subchildren, 1)
+
+      assert subchild[:key] == ":another_key_with_label"
+      assert subchild[:label] == "Another label"
+    end
+
+    test "module suggestions" do
+      [%{children: [%{suggestions: suggestions} | _]} | _] =
+        Generator.convert_to_strings(@descriptions)
+
+      Enum.each(suggestions, fn suggestion ->
+        assert String.starts_with?(suggestion, "Pleroma.")
+      end)
+    end
+
+    test "atoms in suggestions with leading `:`" do
+      [%{children: children} | _] = Generator.convert_to_strings(@descriptions)
+      %{suggestions: suggestions} = Enum.at(children, 4)
+      assert Enum.at(suggestions, 0) == ":atom"
+      assert Enum.at(suggestions, 1) == "Pleroma.Upload"
+      assert Enum.at(suggestions, 2) == {":tuple", "string", 8080}
+      assert Enum.at(suggestions, 3) == [":atom", "Pleroma.Upload", {":atom", "Pleroma.Upload"}]
+
+      %{suggestions: suggestions} = Enum.at(children, 6)
+      assert Enum.at(suggestions, 0) == ":always"
+      assert Enum.at(suggestions, 1) == ":never"
+      assert Enum.at(suggestions, 2) == ":if_available"
+    end
+
+    test "group, key as string in main desc" do
+      descriptions = Generator.convert_to_strings(@descriptions)
+      desc = Enum.at(descriptions, 3)
+      assert desc[:group] == "Some string group"
+      assert desc[:key] == "Some string key"
+    end
+
+    test "key as string subchild" do
+      [%{children: children} | _] = Generator.convert_to_strings(@descriptions)
+      child = Enum.at(children, 7)
+      assert child[:key] == "application/xml"
+    end
+  end
+end
index 314f26ec99d1de43b7d7f07311b50ab39753a867..a7aa54f73069c3c3f8410df516f510a99cd234cb 100644 (file)
@@ -377,8 +377,8 @@ defmodule Pleroma.Factory do
 
   def config_factory do
     %Pleroma.Web.AdminAPI.Config{
-      key: sequence(:key, &"some_key_#{&1}"),
-      group: "pleroma",
+      key: sequence(:key, &":some_key_#{&1}"),
+      group: ":pleroma",
       value:
         sequence(
           :value,
index fab9d6e9acfb2df6254196870b43afb9260fb26e..055f678b99403887a7c767c5a17099ee45bf0070 100644 (file)
@@ -9,16 +9,14 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
 
   setup_all do
     Mix.shell(Mix.Shell.Process)
-    temp_file = "config/temp.exported_from_db.secret.exs"
 
     on_exit(fn ->
       Mix.shell(Mix.Shell.IO)
       Application.delete_env(:pleroma, :first_setting)
       Application.delete_env(:pleroma, :second_setting)
-      :ok = File.rm(temp_file)
     end)
 
-    {:ok, temp_file: temp_file}
+    :ok
   end
 
   clear_config_all([:instance, :dynamic_configuration]) do
@@ -28,38 +26,44 @@ defmodule Mix.Tasks.Pleroma.ConfigTest do
   test "settings are migrated to db" do
     assert Repo.all(Config) == []
 
-    Application.put_env(:pleroma, :first_setting, key: "value", key2: [Pleroma.Repo])
-    Application.put_env(:pleroma, :second_setting, key: "value2", key2: [Pleroma.Activity])
+    Application.put_env(:pleroma, :first_setting, key: "value", key2: [Repo])
+    Application.put_env(:pleroma, :second_setting, key: "value2", key2: ["Activity"])
 
     Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
 
-    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"})
+    config1 = Config.get_by_params(%{group: ":pleroma", key: ":first_setting"})
+    config2 = 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]]
+    assert Config.from_binary(config1.value) == [key: "value", key2: [Repo]]
+    assert Config.from_binary(config2.value) == [key: "value2", key2: ["Activity"]]
   end
 
-  test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do
+  test "settings are migrated to file and deleted from db" do
+    env = "temp"
+    config_file = "config/#{env}.exported_from_db.secret.exs"
+
+    on_exit(fn ->
+      :ok = File.rm(config_file)
+    end)
+
     Config.create(%{
-      group: "pleroma",
+      group: ":pleroma",
       key: ":setting_first",
-      value: [key: "value", key2: [Pleroma.Activity]]
+      value: [key: "value", key2: ["Activity"]]
     })
 
     Config.create(%{
-      group: "pleroma",
+      group: ":pleroma",
       key: ":setting_second",
-      value: [key: "valu2", key2: [Pleroma.Repo]]
+      value: [key: "value2", key2: [Repo]]
     })
 
-    Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "temp", "true"])
+    Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "--env", env, "-d"])
 
     assert Repo.all(Config) == []
-    assert File.exists?(temp_file)
-    {:ok, file} = File.read(temp_file)
 
+    file = File.read!(config_file)
     assert file =~ "config :pleroma, :setting_first,"
     assert file =~ "config :pleroma, :setting_second,"
   end
index 49ff005b66fe2002f012e4f955c81129a47e93bc..fd54504ac41537d18118090e7347f0b196d06ee9 100644 (file)
@@ -1950,6 +1950,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       %{
         "configs" => [
           %{
+            "group" => ":pleroma",
             "key" => key1,
             "value" => _
           },
@@ -1995,15 +1996,15 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       conn =
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
-            %{group: "pleroma", key: "key1", value: "value1"},
+            %{group: ":pleroma", key: ":key1", value: "value1"},
             %{
-              group: "ueberauth",
+              group: ":ueberauth",
               key: "Ueberauth.Strategy.Twitter.OAuth",
               value: [%{"tuple" => [":consumer_secret", "aaaa"]}]
             },
             %{
-              group: "pleroma",
-              key: "key2",
+              group: ":pleroma",
+              key: ":key2",
               value: %{
                 ":nested_1" => "nested_value1",
                 ":nested_2" => [
@@ -2013,21 +2014,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
               }
             },
             %{
-              group: "pleroma",
-              key: "key3",
+              group: ":pleroma",
+              key: ":key3",
               value: [
                 %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
                 %{"nested_4" => true}
               ]
             },
             %{
-              group: "pleroma",
-              key: "key4",
+              group: ":pleroma",
+              key: ":key4",
               value: %{":nested_5" => ":upload", "endpoint" => "https://example.com"}
             },
             %{
-              group: "idna",
-              key: "key5",
+              group: ":idna",
+              key: ":key5",
               value: %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
             }
           ]
@@ -2036,18 +2037,18 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
-                   "group" => "pleroma",
-                   "key" => "key1",
+                   "group" => ":pleroma",
+                   "key" => ":key1",
                    "value" => "value1"
                  },
                  %{
-                   "group" => "ueberauth",
+                   "group" => ":ueberauth",
                    "key" => "Ueberauth.Strategy.Twitter.OAuth",
                    "value" => [%{"tuple" => [":consumer_secret", "aaaa"]}]
                  },
                  %{
-                   "group" => "pleroma",
-                   "key" => "key2",
+                   "group" => ":pleroma",
+                   "key" => ":key2",
                    "value" => %{
                      ":nested_1" => "nested_value1",
                      ":nested_2" => [
@@ -2057,21 +2058,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                    }
                  },
                  %{
-                   "group" => "pleroma",
-                   "key" => "key3",
+                   "group" => ":pleroma",
+                   "key" => ":key3",
                    "value" => [
                      %{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
                      %{"nested_4" => true}
                    ]
                  },
                  %{
-                   "group" => "pleroma",
-                   "key" => "key4",
+                   "group" => ":pleroma",
+                   "key" => ":key4",
                    "value" => %{"endpoint" => "https://example.com", ":nested_5" => ":upload"}
                  },
                  %{
-                   "group" => "idna",
-                   "key" => "key5",
+                   "group" => ":idna",
+                   "key" => ":key5",
                    "value" => %{"tuple" => ["string", "Pleroma.Captcha.NotReal", []]}
                  }
                ]
@@ -2101,8 +2102,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "update config setting & delete", %{conn: conn} do
-      config1 = insert(:config, key: "keyaa1")
-      config2 = insert(:config, key: "keyaa2")
+      config1 = insert(:config, key: ":keyaa1")
+      config2 = insert(:config, key: ":keyaa2")
 
       insert(:config,
         group: "ueberauth",
@@ -2126,7 +2127,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
-                   "group" => "pleroma",
+                   "group" => ":pleroma",
                    "key" => config1.key,
                    "value" => "another_value"
                  }
@@ -2138,11 +2139,14 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     end
 
     test "common config example", %{conn: conn} do
+      adapter = Application.get_env(:tesla, :adapter)
+      on_exit(fn -> Application.put_env(:tesla, :adapter, adapter) end)
+
       conn =
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{
-              "group" => "pleroma",
+              "group" => ":pleroma",
               "key" => "Pleroma.Captcha.NotReal",
               "value" => [
                 %{"tuple" => [":enabled", false]},
@@ -2154,16 +2158,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                 %{"tuple" => [":regex1", "~r/https:\/\/example.com/"]},
                 %{"tuple" => [":regex2", "~r/https:\/\/example.com/u"]},
                 %{"tuple" => [":regex3", "~r/https:\/\/example.com/i"]},
-                %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]}
+                %{"tuple" => [":regex4", "~r/https:\/\/example.com/s"]},
+                %{"tuple" => [":name", "Pleroma"]}
               ]
-            }
+            },
+            %{"group" => ":tesla", "key" => ":adapter", "value" => "Tesla.Adapter.Httpc"}
           ]
         })
 
+      assert Application.get_env(:tesla, :adapter) == Tesla.Adapter.Httpc
+      assert Pleroma.Config.get([Pleroma.Captcha.NotReal, :name]) == "Pleroma"
+
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
-                   "group" => "pleroma",
+                   "group" => ":pleroma",
                    "key" => "Pleroma.Captcha.NotReal",
                    "value" => [
                      %{"tuple" => [":enabled", false]},
@@ -2175,9 +2184,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                      %{"tuple" => [":regex1", "~r/https:\\/\\/example.com/"]},
                      %{"tuple" => [":regex2", "~r/https:\\/\\/example.com/u"]},
                      %{"tuple" => [":regex3", "~r/https:\\/\\/example.com/i"]},
-                     %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]}
+                     %{"tuple" => [":regex4", "~r/https:\\/\\/example.com/s"]},
+                     %{"tuple" => [":name", "Pleroma"]}
                    ]
-                 }
+                 },
+                 %{"group" => ":tesla", "key" => ":adapter", "value" => "Tesla.Adapter.Httpc"}
                ]
              }
     end
@@ -2187,7 +2198,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{
-              "group" => "pleroma",
+              "group" => ":pleroma",
               "key" => "Pleroma.Web.Endpoint.NotReal",
               "value" => [
                 %{
@@ -2251,7 +2262,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
-                   "group" => "pleroma",
+                   "group" => ":pleroma",
                    "key" => "Pleroma.Web.Endpoint.NotReal",
                    "value" => [
                      %{
@@ -2318,7 +2329,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{
-              "group" => "pleroma",
+              "group" => ":pleroma",
               "key" => ":key1",
               "value" => [
                 %{"tuple" => [":key2", "some_val"]},
@@ -2348,7 +2359,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                %{
                  "configs" => [
                    %{
-                     "group" => "pleroma",
+                     "group" => ":pleroma",
                      "key" => ":key1",
                      "value" => [
                        %{"tuple" => [":key2", "some_val"]},
@@ -2380,7 +2391,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{
-              "group" => "pleroma",
+              "group" => ":pleroma",
               "key" => ":key1",
               "value" => %{"key" => "some_val"}
             }
@@ -2391,7 +2402,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
                %{
                  "configs" => [
                    %{
-                     "group" => "pleroma",
+                     "group" => ":pleroma",
                      "key" => ":key1",
                      "value" => %{"key" => "some_val"}
                    }
@@ -2404,7 +2415,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{
-              "group" => "pleroma",
+              "group" => ":pleroma",
               "key" => "Pleroma.Web.Endpoint.NotReal",
               "value" => [
                 %{
@@ -2437,7 +2448,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
-                   "group" => "pleroma",
+                   "group" => ":pleroma",
                    "key" => "Pleroma.Web.Endpoint.NotReal",
                    "value" => [
                      %{
@@ -2467,7 +2478,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         post(conn, "/api/pleroma/admin/config", %{
           configs: [
             %{
-              "group" => "oban",
+              "group" => ":oban",
               "key" => ":queues",
               "value" => [
                 %{"tuple" => [":federator_incoming", 50]},
@@ -2485,7 +2496,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert json_response(conn, 200) == %{
                "configs" => [
                  %{
-                   "group" => "oban",
+                   "group" => ":oban",
                    "key" => ":queues",
                    "value" => [
                      %{"tuple" => [":federator_incoming", 50]},
@@ -2504,7 +2515,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
     test "delete part of settings by atom subkeys", %{conn: conn} do
       config =
         insert(:config,
-          key: "keyaa1",
+          key: ":keyaa1",
           value: :erlang.term_to_binary(subkey1: "val1", subkey2: "val2", subkey3: "val3")
         )
 
@@ -2524,8 +2535,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
         json_response(conn, 200) == %{
           "configs" => [
             %{
-              "group" => "pleroma",
-              "key" => "keyaa1",
+              "group" => ":pleroma",
+              "key" => ":keyaa1",
               "value" => [%{"tuple" => [":subkey2", "val2"]}]
             }
           ]
@@ -3099,6 +3110,21 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIControllerTest do
       assert ReportNote |> Repo.all() |> length() == 1
     end
   end
+
+  test "GET /api/pleroma/admin/config/descriptions", %{conn: conn} do
+    admin = insert(:user, is_admin: true)
+
+    conn =
+      assign(conn, :user, admin)
+      |> get("/api/pleroma/admin/config/descriptions")
+
+    assert [child | _others] = json_response(conn, 200)
+
+    assert child["children"]
+    assert child["key"]
+    assert String.starts_with?(child["group"], ":")
+    assert child["description"]
+  end
 end
 
 # Needed for testing
index 204446b79aa69f3c38bd5ee8044127e18fbd52d0..bff31bb853b75b74a277f36265c2aeba3b70a02a 100644 (file)
@@ -91,14 +91,26 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
       assert Config.from_binary(binary) == Pleroma.Bookmark
     end
 
+    test "pleroma string" do
+      binary = Config.transform("Pleroma")
+      assert binary == :erlang.term_to_binary("Pleroma")
+      assert Config.from_binary(binary) == "Pleroma"
+    end
+
     test "phoenix module" do
       binary = Config.transform("Phoenix.Socket.V1.JSONSerializer")
       assert binary == :erlang.term_to_binary(Phoenix.Socket.V1.JSONSerializer)
       assert Config.from_binary(binary) == Phoenix.Socket.V1.JSONSerializer
     end
 
+    test "tesla module" do
+      binary = Config.transform("Tesla.Adapter.Hackney")
+      assert binary == :erlang.term_to_binary(Tesla.Adapter.Hackney)
+      assert Config.from_binary(binary) == Tesla.Adapter.Hackney
+    end
+
     test "sigil" do
-      binary = Config.transform("~r/comp[lL][aA][iI][nN]er/")
+      binary = Config.transform("~r[comp[lL][aA][iI][nN]er]")
       assert binary == :erlang.term_to_binary(~r/comp[lL][aA][iI][nN]er/)
       assert Config.from_binary(binary) == ~r/comp[lL][aA][iI][nN]er/
     end
@@ -109,10 +121,10 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
       assert Config.from_binary(binary) == ~r/https:\/\/example.com/
     end
 
-    test "link sigil with u modifier" do
-      binary = Config.transform("~r/https:\/\/example.com/u")
-      assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/u)
-      assert Config.from_binary(binary) == ~r/https:\/\/example.com/u
+    test "link sigil with um modifiers" do
+      binary = Config.transform("~r/https:\/\/example.com/um")
+      assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/um)
+      assert Config.from_binary(binary) == ~r/https:\/\/example.com/um
     end
 
     test "link sigil with i modifier" do
@@ -127,6 +139,12 @@ defmodule Pleroma.Web.AdminAPI.ConfigTest do
       assert Config.from_binary(binary) == ~r/https:\/\/example.com/s
     end
 
+    test "raise if valid delimiter not found" do
+      assert_raise ArgumentError, "valid delimiter for Regex expression not found", fn ->
+        Config.transform("~r/https://[]{}<>\"'()|example.com/s")
+      end
+    end
+
     test "2 child tuple" do
       binary = Config.transform(%{"tuple" => ["v1", ":v2"]})
       assert binary == :erlang.term_to_binary({"v1", :v2})