From 40b3289c26137ee4d07c7fb79faf232714cc7592 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 6 Sep 2019 17:08:47 +0700 Subject: [PATCH 01/34] Refactor `add_link_headers/7` -> `add_link_headers/3` --- lib/pleroma/web/controller_helper.ex | 95 ++++++------------- .../controllers/mastodon_api_controller.ex | 28 +++--- .../web/pleroma_api/pleroma_api_controller.ex | 27 ++---- 3 files changed, 50 insertions(+), 100 deletions(-) diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index eeac9f503..b53a01955 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -34,79 +34,38 @@ defp param_to_integer(val, default) when is_binary(val) do defp param_to_integer(_, default), do: default - def add_link_headers( - conn, - method, - activities, - param \\ nil, - params \\ %{}, - func3 \\ nil, - func4 \\ nil - ) do - params = - conn.params - |> Map.drop(["since_id", "max_id", "min_id"]) - |> Map.merge(params) + def add_link_headers(conn, activities, extra_params \\ %{}) do + case List.last(activities) do + %{id: max_id} -> + params = + conn.params + |> Map.drop(Map.keys(conn.path_params)) + |> Map.drop(["since_id", "max_id", "min_id"]) + |> Map.merge(extra_params) - last = List.last(activities) + limit = + params + |> Map.get("limit", "20") + |> String.to_integer() - func3 = func3 || (&mastodon_api_url/3) - func4 = func4 || (&mastodon_api_url/4) + min_id = + if length(activities) <= limit do + activities + |> List.first() + |> Map.get(:id) + else + activities + |> Enum.at(limit * -1) + |> Map.get(:id) + end - if last do - max_id = last.id + next_url = current_url(conn, Map.merge(params, %{max_id: max_id})) + prev_url = current_url(conn, Map.merge(params, %{min_id: min_id})) - limit = - params - |> Map.get("limit", "20") - |> String.to_integer() + put_resp_header(conn, "link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"") - min_id = - if length(activities) <= limit do - activities - |> List.first() - |> Map.get(:id) - else - activities - |> Enum.at(limit * -1) - |> Map.get(:id) - end - - {next_url, prev_url} = - if param do - { - func4.( - Pleroma.Web.Endpoint, - method, - param, - Map.merge(params, %{max_id: max_id}) - ), - func4.( - Pleroma.Web.Endpoint, - method, - param, - Map.merge(params, %{min_id: min_id}) - ) - } - else - { - func3.( - Pleroma.Web.Endpoint, - method, - Map.merge(params, %{max_id: max_id}) - ), - func3.( - Pleroma.Web.Endpoint, - method, - Map.merge(params, %{min_id: min_id}) - ) - } - end - - conn - |> put_resp_header("link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"") - else - conn + _ -> + conn end end end diff --git a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex index 8dfad7a54..f30a21bcc 100644 --- a/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do use Pleroma.Web, :controller import Pleroma.Web.ControllerHelper, - only: [json_response: 3, add_link_headers: 5, add_link_headers: 4, add_link_headers: 3] + only: [json_response: 3, add_link_headers: 2, add_link_headers: 3] alias Ecto.Changeset alias Pleroma.Activity @@ -365,7 +365,7 @@ def home_timeline(%{assigns: %{user: user}} = conn, params) do |> Enum.reverse() conn - |> add_link_headers(:home_timeline, activities) + |> add_link_headers(activities) |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) end @@ -384,7 +384,7 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do |> Enum.reverse() conn - |> add_link_headers(:public_timeline, activities, false, %{"local" => local_only}) + |> add_link_headers(activities, %{"local" => local_only}) |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) end @@ -398,7 +398,7 @@ def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do activities = ActivityPub.fetch_user_activities(user, reading_user, params) conn - |> add_link_headers(:user_statuses, activities, params["id"]) + |> add_link_headers(activities) |> put_view(StatusView) |> render("index.json", %{ activities: activities, @@ -422,7 +422,7 @@ def dm_timeline(%{assigns: %{user: user}} = conn, params) do |> Pagination.fetch_paginated(params) conn - |> add_link_headers(:dm_timeline, activities) + |> add_link_headers(activities) |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) end @@ -523,7 +523,7 @@ def poll_vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choic def scheduled_statuses(%{assigns: %{user: user}} = conn, params) do with scheduled_activities <- MastodonAPI.get_scheduled_activities(user, params) do conn - |> add_link_headers(:scheduled_statuses, scheduled_activities) + |> add_link_headers(scheduled_activities) |> put_view(ScheduledActivityView) |> render("index.json", %{scheduled_activities: scheduled_activities}) end @@ -706,7 +706,7 @@ def notifications(%{assigns: %{user: user}} = conn, params) do notifications = MastodonAPI.get_notifications(user, params) conn - |> add_link_headers(:notifications, notifications) + |> add_link_headers(notifications) |> put_view(NotificationView) |> render("index.json", %{notifications: notifications, for: user}) end @@ -894,7 +894,7 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do |> Enum.reverse() conn - |> add_link_headers(:hashtag_timeline, activities, params["tag"], %{"local" => local_only}) + |> add_link_headers(activities, %{"local" => local_only}) |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) end @@ -910,7 +910,7 @@ def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do end conn - |> add_link_headers(:followers, followers, user) + |> add_link_headers(followers) |> put_view(AccountView) |> render("accounts.json", %{for: for_user, users: followers, as: :user}) end @@ -927,7 +927,7 @@ def following(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params) do end conn - |> add_link_headers(:following, followers, user) + |> add_link_headers(followers) |> put_view(AccountView) |> render("accounts.json", %{for: for_user, users: followers, as: :user}) end @@ -1152,7 +1152,7 @@ def favourites(%{assigns: %{user: user}} = conn, params) do |> Enum.reverse() conn - |> add_link_headers(:favourites, activities) + |> add_link_headers(activities) |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) end @@ -1179,7 +1179,7 @@ def user_favourites(%{assigns: %{user: for_user}} = conn, %{"id" => id} = params |> Enum.reverse() conn - |> add_link_headers(:favourites, activities) + |> add_link_headers(activities) |> put_view(StatusView) |> render("index.json", %{activities: activities, for: for_user, as: :activity}) else @@ -1200,7 +1200,7 @@ def bookmarks(%{assigns: %{user: user}} = conn, params) do |> Enum.map(fn b -> Map.put(b.activity, :bookmark, Map.delete(b, :activity)) end) conn - |> add_link_headers(:bookmarks, bookmarks) + |> add_link_headers(bookmarks) |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) end @@ -1640,7 +1640,7 @@ def conversations(%{assigns: %{user: user}} = conn, params) do end) conn - |> add_link_headers(:conversations, participations) + |> add_link_headers(participations) |> json(conversations) end diff --git a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex index f4df3b024..d17ccf84d 100644 --- a/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/pleroma_api_controller.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do use Pleroma.Web, :controller - import Pleroma.Web.ControllerHelper, only: [add_link_headers: 7] + import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2] alias Pleroma.Conversation.Participation alias Pleroma.Notification @@ -27,31 +27,22 @@ def conversation_statuses( %{assigns: %{user: user}} = conn, %{"id" => participation_id} = params ) do - params = - params - |> Map.put("blocking_user", user) - |> Map.put("muting_user", user) - |> Map.put("user", user) - - participation = - participation_id - |> Participation.get(preload: [:conversation]) + participation = Participation.get(participation_id, preload: [:conversation]) if user.id == participation.user_id do + params = + params + |> Map.put("blocking_user", user) + |> Map.put("muting_user", user) + |> Map.put("user", user) + activities = participation.conversation.ap_id |> ActivityPub.fetch_activities_for_context(params) |> Enum.reverse() conn - |> add_link_headers( - :conversation_statuses, - activities, - participation_id, - params, - nil, - &pleroma_api_url/4 - ) + |> add_link_headers(activities) |> put_view(StatusView) |> render("index.json", %{activities: activities, for: user, as: :activity}) end From e5c6bf3673a8361d1417eba1ccc44edec7658ac4 Mon Sep 17 00:00:00 2001 From: shadowfacts Date: Sat, 7 Sep 2019 19:50:45 +0000 Subject: [PATCH 02/34] Mastodon API: URI encode hashtag name in generated URLs Otherwise hashtags with word characters other than those allowed in URLs (e.g. Japanese characters) produce hashtag URLs that are invalid. --- lib/pleroma/web/mastodon_api/views/status_view.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index e71083b91..708b8c2fd 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -499,7 +499,7 @@ def build_tags(object_tags) when is_list(object_tags) do object_tags = for tag when is_binary(tag) <- object_tags, do: tag Enum.reduce(object_tags, [], fn tag, tags -> - tags ++ [%{name: tag, url: "/tag/#{tag}"}] + tags ++ [%{name: tag, url: "/tag/#{URI.encode(tag)}"}] end) end From 43f02dfe38547e07fb189aab78539af9e02302b3 Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 10 Sep 2019 22:01:45 +0300 Subject: [PATCH 03/34] Revert "Parallelize template rendering" This reverts commit 1ad71592adb47762287aec8c36d0fca565c38362. Since it had no limit on the number on concurrent processes it OOM killed instances while rendering hellthreads. When I tried introducing a concurrency limit with Task.async_stream/manual folds it lead to about 3 times worse performance on threads larger than 1000 activities (we are talking 30s vs 1.2 minutes), I think this is not worth the about 1.5 times performance increase on smaller threads when using it. --- lib/mix/tasks/pleroma/benchmark.ex | 38 +++++-------------- .../web/mastodon_api/views/status_view.ex | 4 +- lib/pleroma/web/web.ex | 18 +-------- 3 files changed, 12 insertions(+), 48 deletions(-) diff --git a/lib/mix/tasks/pleroma/benchmark.ex b/lib/mix/tasks/pleroma/benchmark.ex index 4cc634727..a45940bf3 100644 --- a/lib/mix/tasks/pleroma/benchmark.ex +++ b/lib/mix/tasks/pleroma/benchmark.ex @@ -37,37 +37,17 @@ def run(["render_timeline", nickname]) do |> Map.put("blocking_user", user) |> Map.put("muting_user", user) |> Map.put("user", user) - |> Map.put("limit", 80) |> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities() |> Enum.reverse() - inputs = %{ - "One activity" => Enum.take_random(activities, 1), - "Ten activities" => Enum.take_random(activities, 10), - "Twenty activities" => Enum.take_random(activities, 20), - "Forty activities" => Enum.take_random(activities, 40), - "Eighty activities" => Enum.take_random(activities, 80) - } - - Benchee.run( - %{ - "Parallel rendering" => fn activities -> - Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ - activities: activities, - for: user, - as: :activity - }) - end, - "Standart rendering" => fn activities -> - Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ - activities: activities, - for: user, - as: :activity, - parallel: false - }) - end - }, - inputs: inputs - ) + Benchee.run(%{ + "render_timeline" => fn -> + Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ + activities: activities, + for: user, + as: :activity + }) + end + }) end end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index e71083b91..b6a3431f9 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -73,14 +73,12 @@ defp reblogged?(activity, user) do def render("index.json", opts) do replied_to_activities = get_replied_to_activities(opts.activities) - parallel = unless is_nil(opts[:parallel]), do: opts[:parallel], else: true opts.activities |> safe_render_many( StatusView, "status.json", - Map.put(opts, :replied_to_activities, replied_to_activities), - parallel + Map.put(opts, :replied_to_activities, replied_to_activities) ) end diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index bfb6c7287..687346554 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -66,23 +66,9 @@ def safe_render(view, template, assigns \\ %{}) do end @doc """ - Same as `render_many/4` but wrapped in rescue block and parallelized (unless disabled by passing false as a fifth argument). + Same as `render_many/4` but wrapped in rescue block. """ - def safe_render_many(collection, view, template, assigns \\ %{}, parallel \\ true) - - def safe_render_many(collection, view, template, assigns, true) do - Enum.map(collection, fn resource -> - Task.async(fn -> - as = Map.get(assigns, :as) || view.__resource__ - assigns = Map.put(assigns, as, resource) - safe_render(view, template, assigns) - end) - end) - |> Enum.map(&Task.await(&1, :infinity)) - |> Enum.filter(& &1) - end - - def safe_render_many(collection, view, template, assigns, false) do + def safe_render_many(collection, view, template, assigns \\ %{}) do Enum.map(collection, fn resource -> as = Map.get(assigns, :as) || view.__resource__ assigns = Map.put(assigns, as, resource) From ab4960cc7f6703d93e3bf2d316978150a6dba59f Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 12:00:36 +0300 Subject: [PATCH 04/34] deleting old config.md file --- docs/config.md | 701 ------------------------------------------------- 1 file changed, 701 deletions(-) delete mode 100644 docs/config.md diff --git a/docs/config.md b/docs/config.md deleted file mode 100644 index 9136532e0..000000000 --- a/docs/config.md +++ /dev/null @@ -1,701 +0,0 @@ -# Configuration - -This file describe the configuration, it is recommended to edit the relevant *.secret.exs file instead of the others founds in the ``config`` directory. -If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``. - -## Pleroma.Upload -* `uploader`: Select which `Pleroma.Uploaders` to use -* `filters`: List of `Pleroma.Upload.Filter` to use. -* `link_name`: When enabled Pleroma will add a `name` parameter to the url of the upload, for example `https://instance.tld/media/corndog.png?name=corndog.png`. This is needed to provide the correct filename in Content-Disposition headers when using filters like `Pleroma.Upload.Filter.Dedupe` -* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host. -* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it. -* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation. - -Note: `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`. - -## Pleroma.Uploaders.Local -* `uploads`: Which directory to store the user-uploads in, relative to pleroma’s working directory - -## Pleroma.Uploaders.S3 -* `bucket`: S3 bucket name -* `bucket_namespace`: S3 bucket namespace -* `public_endpoint`: S3 endpoint that the user finally accesses(ex. "https://s3.dualstack.ap-northeast-1.amazonaws.com") -* `truncated_namespace`: If you use S3 compatible service such as Digital Ocean Spaces or CDN, set folder name or "" etc. -For example, when using CDN to S3 virtual host format, set "". -At this time, write CNAME to CDN in public_endpoint. - -## Pleroma.Upload.Filter.Mogrify - -* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`. - -## Pleroma.Upload.Filter.Dedupe - -No specific configuration. - -## Pleroma.Upload.Filter.AnonymizeFilename - -This filter replaces the filename (not the path) of an upload. For complete obfuscation, add -`Pleroma.Upload.Filter.Dedupe` before AnonymizeFilename. - -* `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used. You can get the original filename extension by using `{extension}`, for example `custom-file-name.{extension}`. - -## Pleroma.Emails.Mailer -* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox. -* `api_key` / `password` and / or other adapter-specific settings, per the above documentation. -* `enabled`: Allows enable/disable send emails. Default: `false`. - -An example for Sendgrid adapter: - -```elixir -config :pleroma, Pleroma.Emails.Mailer, - adapter: Swoosh.Adapters.Sendgrid, - api_key: "YOUR_API_KEY" -``` - -An example for SMTP adapter: - -```elixir -config :pleroma, Pleroma.Emails.Mailer, - adapter: Swoosh.Adapters.SMTP, - relay: "smtp.gmail.com", - username: "YOUR_USERNAME@gmail.com", - password: "YOUR_SMTP_PASSWORD", - port: 465, - ssl: true, - tls: :always, - auth: :always -``` - -## :uri_schemes -* `valid_schemes`: List of the scheme part that is considered valid to be an URL - -## :instance -* `name`: The instance’s name -* `email`: Email used to reach an Administrator/Moderator of the instance -* `notify_email`: Email used for notifications. -* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance`` -* `limit`: Posts character limit (CW/Subject included in the counter) -* `remote_limit`: Hard character limit beyond which remote posts will be dropped. -* `upload_limit`: File size limit of uploads (except for avatar, background, banner) -* `avatar_upload_limit`: File size limit of user’s profile avatars -* `background_upload_limit`: File size limit of user’s profile backgrounds -* `banner_upload_limit`: File size limit of user’s profile banners -* `poll_limits`: A map with poll limits for **local** polls - * `max_options`: Maximum number of options - * `max_option_chars`: Maximum number of characters per option - * `min_expiration`: Minimum expiration time (in seconds) - * `max_expiration`: Maximum expiration time (in seconds) -* `registrations_open`: Enable registrations for anyone, invitations can be enabled when false. -* `invites_enabled`: Enable user invitations for admins (depends on `registrations_open: false`). -* `account_activation_required`: Require users to confirm their emails before signing in. -* `federating`: Enable federation with other instances -* `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes. -* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it. -* `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance -* `rewrite_policy`: Message Rewrite Policy, either one or a list. Here are the ones available by default: - * `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default) - * `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production - * `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See ``:mrf_simple`` section) - * `Pleroma.Web.ActivityPub.MRF.TagPolicy`: Applies policies to individual users based on tags, which can be set using pleroma-fe/admin-fe/any other app that supports Pleroma Admin API. For example it allows marking posts from individual users nsfw (sensitive) - * `Pleroma.Web.ActivityPub.MRF.SubchainPolicy`: Selectively runs other MRF policies when messages match (see ``:mrf_subchain`` section) - * `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See ``:mrf_rejectnonpublic`` section) - * `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:. - * `Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy`: Rejects posts from likely spambots by rejecting posts from new users that contain links. - * `Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy`: Crawls attachments using their MediaProxy URLs so that the MediaProxy cache is primed. - * `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (see `:mrf_mention` section) - * `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (see `:mrf_vocabulary` section) -* `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. -* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send. -* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json`` -* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML) -* `mrf_transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo). -* `mrf_transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value. -* `scope_copy`: Copy the scope (private/unlisted/public) in replies to posts by default. -* `subject_line_behavior`: Allows changing the default behaviour of subject lines in replies. Valid values: - * "email": Copy and preprend re:, as in email. - * "masto": Copy verbatim, as in Mastodon. - * "noop": Don't copy the subject. -* `always_show_subject_input`: When set to false, auto-hide the subject field when it's empty. -* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with - older software for theses nicknames. -* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature. -* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow. -* `no_attachment_links`: Set to true to disable automatically adding attachment link text to statuses -* `welcome_message`: A message that will be send to a newly registered users as a direct message. -* `welcome_user_nickname`: The nickname of the local user that sends the welcome message. -* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`) -* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`. -* `healthcheck`: If set to true, system data will be shown on ``/api/pleroma/healthcheck``. -* `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database. -* `user_bio_length`: A user bio maximum length (default: `5000`) -* `user_name_length`: A user name maximum length (default: `100`) -* `skip_thread_containment`: Skip filter out broken threads. The default is `false`. -* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`. -* `dynamic_configuration`: Allow transferring configuration to DB with the subsequent customization from Admin api. -* `max_account_fields`: The maximum number of custom fields in the user profile (default: `10`) -* `max_remote_account_fields`: The maximum number of custom fields in the remote user profile (default: `20`) -* `account_field_name_length`: An account field name maximum length (default: `512`) -* `account_field_value_length`: An account field value maximum length (default: `512`) -* `external_user_synchronization`: Enabling following/followers counters synchronization for external users. - - - -## :logger -* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog, and `Quack.Logger` to log to Slack - -An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed: -```elixir -config :logger, - backends: [{ExSyslogger, :ex_syslogger}] - -config :logger, :ex_syslogger, - level: :warn -``` - -Another example, keeping console output and adding the pid to syslog output: -```elixir -config :logger, - backends: [:console, {ExSyslogger, :ex_syslogger}] - -config :logger, :ex_syslogger, - level: :warn, - option: [:pid, :ndelay] -``` - -See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_syslogger’s documentation](https://hexdocs.pm/ex_syslogger/) - -An example of logging info to local syslog, but warn to a Slack channel: -```elixir -config :logger, - backends: [ {ExSyslogger, :ex_syslogger}, Quack.Logger ], - level: :info - -config :logger, :ex_syslogger, - level: :info, - ident: "pleroma", - format: "$metadata[$level] $message" - -config :quack, - level: :warn, - meta: [:all], - webhook_url: "https://hooks.slack.com/services/YOUR-API-KEY-HERE" -``` - -See the [Quack Github](https://github.com/azohra/quack) for more details - -## :frontend_configurations - -This 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. - -Frontends can access these settings at `/api/pleroma/frontend_configurations` - -To add your own configuration for PleromaFE, use it like this: - -```elixir -config :pleroma, :frontend_configurations, - pleroma_fe: %{ - theme: "pleroma-dark", - # ... see /priv/static/static/config.json for the available keys. -}, - masto_fe: %{ - showInstanceSpecificPanel: true - } -``` - -These settings **need to be complete**, they will override the defaults. - -NOTE: for versions < 1.0, you need to set [`:fe`](#fe) to false, as shown a few lines below. - -## :fe -__THIS IS DEPRECATED__ - -If you are using this method, please change it to the [`frontend_configurations`](#frontend_configurations) method. -Please **set this option to false** in your config like this: - -```elixir -config :pleroma, :fe, false -``` - -This section is used to configure Pleroma-FE, unless ``:managed_config`` in ``:instance`` is set to false. - -* `theme`: Which theme to use, they are defined in ``styles.json`` -* `logo`: URL of the logo, defaults to Pleroma’s logo -* `logo_mask`: Whether to use only the logo's shape as a mask (true) or as a regular image (false) -* `logo_margin`: What margin to use around the logo -* `background`: URL of the background, unless viewing a user profile with a background that is set -* `redirect_root_no_login`: relative URL which indicates where to redirect when a user isn’t logged in. -* `redirect_root_login`: relative URL which indicates where to redirect when a user is logged in. -* `show_instance_panel`: Whenether to show the instance’s specific panel. -* `scope_options_enabled`: Enable setting an notice visibility and subject/CW when posting -* `formatting_options_enabled`: Enable setting a formatting different than plain-text (ie. HTML, Markdown) when posting, relates to ``:instance, allowed_post_formats`` -* `collapse_message_with_subjects`: When a message has a subject(aka Content Warning), collapse it by default -* `hide_post_stats`: Hide notices statistics(repeats, favorites, …) -* `hide_user_stats`: Hide profile statistics(posts, posts per day, followers, followings, …) - -## :assets - -This section configures assets to be used with various frontends. Currently the only option -relates to mascots on the mastodon frontend - -* `mascots`: KeywordList of mascots, each element __MUST__ contain both a `url` and a - `mime_type` key. -* `default_mascot`: An element from `mascots` - This will be used as the default mascot - on MastoFE (default: `:pleroma_fox_tan`) - -## :mrf_simple -* `media_removal`: List of instances to remove medias from -* `media_nsfw`: List of instances to put medias as NSFW(sensitive) from -* `federated_timeline_removal`: List of instances to remove from Federated (aka The Whole Known Network) Timeline -* `reject`: List of instances to reject any activities from -* `accept`: List of instances to accept any activities from -* `report_removal`: List of instances to reject reports from -* `avatar_removal`: List of instances to strip avatars from -* `banner_removal`: List of instances to strip banners from - -## :mrf_subchain -This policy processes messages through an alternate pipeline when a given message matches certain criteria. -All criteria are configured as a map of regular expressions to lists of policy modules. - -* `match_actor`: Matches a series of regular expressions against the actor field. - -Example: - -``` -config :pleroma, :mrf_subchain, - match_actor: %{ - ~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy] - } -``` - -## :mrf_rejectnonpublic -* `allow_followersonly`: whether to allow followers-only posts -* `allow_direct`: whether to allow direct messages - -## :mrf_hellthread -* `delist_threshold`: Number of mentioned users after which the message gets delisted (the message can still be seen, but it will not show up in public timelines and mentioned users won't get notifications about it). Set to 0 to disable. -* `reject_threshold`: Number of mentioned users after which the messaged gets rejected. Set to 0 to disable. - -## :mrf_keyword -* `reject`: A list of patterns which result in message being rejected, each pattern can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html) -* `federated_timeline_removal`: 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](https://hexdocs.pm/elixir/Regex.html) -* `replace`: A list of tuples containing `{pattern, replacement}`, `pattern` can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html) - -## :mrf_mention -* `actors`: A list of actors, for which to drop any posts mentioning. - -## :mrf_vocabulary -* `accept`: A list of ActivityStreams terms to accept. If empty, all supported messages are accepted. -* `reject`: A list of ActivityStreams terms to reject. If empty, no messages are rejected. - -## :media_proxy -* `enabled`: Enables proxying of remote media to the instance’s proxy -* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts. -* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`. -* `whitelist`: List of domains to bypass the mediaproxy - -## :gopher -* `enabled`: Enables the gopher interface -* `ip`: IP address to bind to -* `port`: Port to bind to -* `dstport`: Port advertised in urls (optional, defaults to `port`) - -## Pleroma.Web.Endpoint -`Phoenix` endpoint configuration, all configuration options can be viewed [here](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-dynamic-configuration), only common options are listed here -* `http` - a list containing http protocol configuration, all configuration options can be viewed [here](https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html#module-options), only common options are listed here. For deployment using docker, you need to set this to `[ip: {0,0,0,0}, port: 4000]` to make pleroma accessible from other containers (such as your nginx server). - - `ip` - a tuple consisting of 4 integers - - `port` -* `url` - a list containing the configuration for generating urls, accepts - - `host` - the host without the scheme and a post (e.g `example.com`, not `https://example.com:2020`) - - `scheme` - e.g `http`, `https` - - `port` - - `path` -* `extra_cookie_attrs` - a list of `Key=Value` strings to be added as non-standard cookie attributes. Defaults to `["SameSite=Lax"]`. See the [SameSite article](https://www.owasp.org/index.php/SameSite) on OWASP for more info. - - - -**Important note**: if you modify anything inside these lists, default `config.exs` values will be overwritten, which may result in breakage, to make sure this does not happen please copy the default value for the list from `config.exs` and modify/add only what you need - -Example: -```elixir -config :pleroma, Pleroma.Web.Endpoint, - url: [host: "example.com", port: 2020, scheme: "https"], - http: [ - # start copied from config.exs - dispatch: [ - {:_, - [ - {"/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 - ], - port: 8080, - ip: {127, 0, 0, 1} - ] -``` - -This will make Pleroma listen on `127.0.0.1` port `8080` and generate urls starting with `https://example.com:2020` - -## :activitypub -* ``unfollow_blocked``: Whether blocks result in people getting unfollowed -* ``outgoing_blocks``: Whether to federate blocks to other instances -* ``deny_follow_blocked``: Whether to disallow following an account that has blocked the user in question -* ``sign_object_fetches``: Sign object fetches with HTTP signatures - -## :http_security -* ``enabled``: Whether the managed content security policy is enabled -* ``sts``: Whether to additionally send a `Strict-Transport-Security` header -* ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent -* ``ct_max_age``: The maximum age for the `Expect-CT` header if sent -* ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"` -* ``report_uri``: Adds the specified url to `report-uri` and `report-to` group in CSP header. - -## :mrf_user_allowlist - -The keys in this section are the domain names that the policy should apply to. -Each key should be assigned a list of users that should be allowed through by -their ActivityPub ID. - -An example: - -```elixir -config :pleroma, :mrf_user_allowlist, - "example.org": ["https://example.org/users/admin"] -``` - -## :web_push_encryption, :vapid_details - -Web Push Notifications configuration. You can use the mix task `mix web_push.gen.keypair` to generate it. - -* ``subject``: a mailto link for the administrative contact. It’s best if this email is not a personal email address, but rather a group email so that if a person leaves an organization, is unavailable for an extended period, or otherwise can’t respond, someone else on the list can. -* ``public_key``: VAPID public key -* ``private_key``: VAPID private key - -## Pleroma.Captcha -* `enabled`: Whether the captcha should be shown on registration -* `method`: The method/service to use for captcha -* `seconds_valid`: The time in seconds for which the captcha is valid - -### Pleroma.Captcha.Kocaptcha -Kocaptcha is a very simple captcha service with a single API endpoint, -the source code is here: https://github.com/koto-bank/kocaptcha. The default endpoint -`https://captcha.kotobank.ch` is hosted by the developer. - -* `endpoint`: the kocaptcha endpoint to use - -## :admin_token - -Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the 'admin_token' parameter. Example: - -```elixir -config :pleroma, :admin_token, "somerandomtoken" -``` - -You can then do - -```sh -curl "http://localhost:4000/api/pleroma/admin/invite_token?admin_token=somerandomtoken" -``` - -## :pleroma_job_queue - -[Pleroma Job Queue](https://git.pleroma.social/pleroma/pleroma_job_queue) configuration: a list of queues with maximum concurrent jobs. - -Pleroma has the following queues: - -* `federator_outgoing` - Outgoing federation -* `federator_incoming` - Incoming federation -* `mailer` - Email sender, see [`Pleroma.Emails.Mailer`](#pleroma-emails-mailer) -* `transmogrifier` - Transmogrifier -* `web_push` - Web push notifications -* `scheduled_activities` - Scheduled activities, see [`Pleroma.ScheduledActivities`](#pleromascheduledactivity) - -Example: - -```elixir -config :pleroma_job_queue, :queues, - federator_incoming: 50, - federator_outgoing: 50 -``` - -This config contains two queues: `federator_incoming` and `federator_outgoing`. Both have the `max_jobs` set to `50`. - -## Pleroma.Web.Federator.RetryQueue - -* `enabled`: If set to `true`, failed federation jobs will be retried -* `max_jobs`: The maximum amount of parallel federation jobs running at the same time. -* `initial_timeout`: The initial timeout in seconds -* `max_retries`: The maximum number of times a federation job is retried - -## Pleroma.Web.Metadata -* `providers`: a list of metadata providers to enable. Providers available: - * Pleroma.Web.Metadata.Providers.OpenGraph - * Pleroma.Web.Metadata.Providers.TwitterCard - * Pleroma.Web.Metadata.Providers.RelMe - add links from user bio with rel=me into the `
` as `` -* `unfurl_nsfw`: If set to `true` nsfw attachments will be shown in previews - -## :rich_media -* `enabled`: if enabled the instance will parse metadata from attached links to generate link previews -* `ignore_hosts`: list of hosts which will be ignored by the metadata parser. For example `["accounts.google.com", "xss.website"]`, defaults to `[]`. -* `ignore_tld`: list TLDs (top-level domains) which will ignore for parse metadata. default is ["local", "localdomain", "lan"] -* `parsers`: list of Rich Media parsers - -## :fetch_initial_posts -* `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts -* `pages`: the amount of pages to fetch - -## :hackney_pools - -Advanced. Tweaks Hackney (http client) connections pools. - -There's three pools used: - -* `:federation` for the federation jobs. - You may want this pool max_connections to be at least equal to the number of federator jobs + retry queue jobs. -* `:media` for rich media, media proxy -* `:upload` for uploaded media (if using a remote uploader and `proxy_remote: true`) - -For each pool, the options are: - -* `max_connections` - how much connections a pool can hold -* `timeout` - retention duration for connections - -## :auto_linker - -Configuration for the `auto_linker` library: - -* `class: "auto-linker"` - specify the class to be added to the generated link. false to clear -* `rel: "noopener noreferrer"` - override the rel attribute. false to clear -* `new_window: true` - set to false to remove `target='_blank'` attribute -* `scheme: false` - Set to true to link urls with schema `http://google.com` -* `truncate: false` - Set to a number to truncate urls longer then the number. Truncated urls will end in `..` -* `strip_prefix: true` - Strip the scheme prefix -* `extra: false` - link urls with rarely used schemes (magnet, ipfs, irc, etc.) - -Example: - -```elixir -config :auto_linker, - opts: [ - scheme: true, - extra: true, - class: false, - strip_prefix: false, - new_window: false, - rel: false - ] -``` - -## Pleroma.ScheduledActivity - -* `daily_user_limit`: the number of scheduled activities a user is allowed to create in a single day (Default: `25`) -* `total_user_limit`: the number of scheduled activities a user is allowed to create in total (Default: `300`) -* `enabled`: whether scheduled activities are sent to the job queue to be executed - -## Pleroma.ActivityExpiration - -# `enabled`: whether expired activities will be sent to the job queue to be deleted - -## Pleroma.Web.Auth.Authenticator - -* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator -* `Pleroma.Web.Auth.LDAPAuthenticator`: LDAP authentication - -## :ldap - -Use LDAP for user authentication. When a user logs in to the Pleroma -instance, the name and password will be verified by trying to authenticate -(bind) to an LDAP server. If a user exists in the LDAP directory but there -is no account with the same name yet on the Pleroma instance then a new -Pleroma account will be created with the same name as the LDAP user name. - -* `enabled`: enables LDAP authentication -* `host`: LDAP server hostname -* `port`: LDAP port, e.g. 389 or 636 -* `ssl`: true to use SSL, usually implies the port 636 -* `sslopts`: additional SSL options -* `tls`: true to start TLS, usually implies the port 389 -* `tlsopts`: additional TLS options -* `base`: LDAP base, e.g. "dc=example,dc=com" -* `uid`: LDAP attribute name to authenticate the user, e.g. when "cn", the filter will be "cn=username,base" - -## BBS / SSH access - -To enable simple command line interface accessible over ssh, add a setting like this to your configuration file: - -```exs -app_dir = File.cwd! -priv_dir = Path.join([app_dir, "priv/ssh_keys"]) - -config :esshd, - enabled: true, - priv_dir: priv_dir, - handler: "Pleroma.BBS.Handler", - port: 10_022, - password_authenticator: "Pleroma.BBS.Authenticator" -``` - -Feel free to adjust the priv_dir and port number. Then you will have to create the key for the keys (in the example `priv/ssh_keys`) and create the host keys with `ssh-keygen -m PEM -N "" -b 2048 -t rsa -f ssh_host_rsa_key`. After restarting, you should be able to connect to your Pleroma instance with `ssh username@server -p $PORT` - -## :auth - -* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator -* `Pleroma.Web.Auth.LDAPAuthenticator`: LDAP authentication - -Authentication / authorization settings. - -* `auth_template`: authentication form template. By default it's `show.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/show.html.eex`. -* `oauth_consumer_template`: OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex`. -* `oauth_consumer_strategies`: 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 `` or `:` (e.g. `twitter` or `keycloak:ueberauth_keycloak_strategy` in case dependency is named differently than `ueberauth_`). - -## :email_notifications - -Email notifications settings. - - - digest - emails of "what you've missed" for users who have been - inactive for a while. - - active: globally enable or disable digest emails - - schedule: When to send digest email, in [crontab format](https://en.wikipedia.org/wiki/Cron). - "0 0 * * 0" is the default, meaning "once a week at midnight on Sunday morning" - - interval: Minimum interval between digest emails to one user - - inactivity_threshold: Minimum user inactivity threshold - -## Pleroma.Emails.UserEmail - -- `:logo` - a path to a custom logo. Set it to `nil` to use the default Pleroma logo. -- `:styling` - a map with color settings for email templates. - -## OAuth consumer mode - -OAuth consumer mode allows sign in / sign up via external OAuth providers (e.g. Twitter, Facebook, Google, Microsoft, etc.). -Implementation is based on Ueberauth; see the list of [available strategies](https://github.com/ueberauth/ueberauth/wiki/List-of-Strategies). - -Note: each strategy is shipped as a separate dependency; in order to get the strategies, run `OAUTH_CONSUMER_STRATEGIES="..." mix deps.get`, -e.g. `OAUTH_CONSUMER_STRATEGIES="twitter facebook google microsoft" mix deps.get`. -The server should also be started with `OAUTH_CONSUMER_STRATEGIES="..." mix phx.server` in case you enable any strategies. - -Note: each strategy requires separate setup (on external provider side and Pleroma side). Below are the guidelines on setting up most popular strategies. - -Note: make sure that `"SameSite=Lax"` is set in `extra_cookie_attrs` when you have this feature enabled. OAuth consumer mode will not work with `"SameSite=Strict"` - -* For Twitter, [register an app](https://developer.twitter.com/en/apps), configure callback URL to https:///oauth/twitter/callback - -* For Facebook, [register an app](https://developers.facebook.com/apps), configure callback URL to https:///oauth/facebook/callback, enable Facebook Login service at https://developers.facebook.com/apps//fb-login/settings/ - -* For Google, [register an app](https://console.developers.google.com), configure callback URL to https:///oauth/google/callback - -* For Microsoft, [register an app](https://portal.azure.com), configure callback URL to https:///oauth/microsoft/callback - -Once the app is configured on external OAuth provider side, add app's credentials and strategy-specific settings (if any — e.g. see Microsoft below) to `config/prod.secret.exs`, -per strategy's documentation (e.g. [ueberauth_twitter](https://github.com/ueberauth/ueberauth_twitter)). Example config basing on environment variables: - -```elixir -# Twitter -config :ueberauth, Ueberauth.Strategy.Twitter.OAuth, - consumer_key: System.get_env("TWITTER_CONSUMER_KEY"), - consumer_secret: System.get_env("TWITTER_CONSUMER_SECRET") - -# Facebook -config :ueberauth, Ueberauth.Strategy.Facebook.OAuth, - client_id: System.get_env("FACEBOOK_APP_ID"), - client_secret: System.get_env("FACEBOOK_APP_SECRET"), - redirect_uri: System.get_env("FACEBOOK_REDIRECT_URI") - -# Google -config :ueberauth, Ueberauth.Strategy.Google.OAuth, - client_id: System.get_env("GOOGLE_CLIENT_ID"), - client_secret: System.get_env("GOOGLE_CLIENT_SECRET"), - redirect_uri: System.get_env("GOOGLE_REDIRECT_URI") - -# Microsoft -config :ueberauth, Ueberauth.Strategy.Microsoft.OAuth, - client_id: System.get_env("MICROSOFT_CLIENT_ID"), - client_secret: System.get_env("MICROSOFT_CLIENT_SECRET") - -config :ueberauth, Ueberauth, - providers: [ - microsoft: {Ueberauth.Strategy.Microsoft, [callback_params: []]} - ] - -# Keycloak -# Note: make sure to add `keycloak:ueberauth_keycloak_strategy` entry to `OAUTH_CONSUMER_STRATEGIES` environment variable -keycloak_url = "https://publicly-reachable-keycloak-instance.org:8080" - -config :ueberauth, Ueberauth.Strategy.Keycloak.OAuth, - client_id: System.get_env("KEYCLOAK_CLIENT_ID"), - client_secret: System.get_env("KEYCLOAK_CLIENT_SECRET"), - site: keycloak_url, - authorize_url: "#{keycloak_url}/auth/realms/master/protocol/openid-connect/auth", - token_url: "#{keycloak_url}/auth/realms/master/protocol/openid-connect/token", - userinfo_url: "#{keycloak_url}/auth/realms/master/protocol/openid-connect/userinfo", - token_method: :post - -config :ueberauth, Ueberauth, - providers: [ - keycloak: {Ueberauth.Strategy.Keycloak, [uid_field: :email]} - ] -``` - -## OAuth 2.0 provider - :oauth2 - -Configure OAuth 2 provider capabilities: - -* `token_expires_in` - The lifetime in seconds of the access token. -* `issue_new_refresh_token` - Keeps old refresh token or generate new refresh token when to obtain an access token. -* `clean_expired_tokens` - Enable a background job to clean expired oauth tokens. Defaults to `false`. -* `clean_expired_tokens_interval` - Interval to run the job to clean expired tokens. Defaults to `86_400_000` (24 hours). - -## :emoji -* `shortcode_globs`: Location of custom emoji files. `*` can be used as a wildcard. Example `["/emoji/custom/**/*.png"]` -* `pack_extensions`: A list of file extensions for emojis, when no emoji.txt for a pack is present. Example `[".png", ".gif"]` -* `groups`: 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. Example `[Custom: ["/emoji/*.png", "/emoji/custom/*.png"]]` -* `default_manifest`: Location of the JSON-manifest. This manifest contains information about the emoji-packs you can download. Currently only one manifest can be added (no arrays). - -## Database options - -### RUM indexing for full text search -* `rum_enabled`: If RUM indexes should be used. Defaults to `false`. - -RUM indexes are an alternative indexing scheme that is not included in PostgreSQL by default. While they may eventually be mainlined, for now they have to be installed as a PostgreSQL extension from https://github.com/postgrespro/rum. - -Their advantage over the standard GIN indexes is that they allow efficient ordering of search results by timestamp, which makes search queries a lot faster on larger servers, by one or two orders of magnitude. They take up around 3 times as much space as GIN indexes. - -To enable them, both the `rum_enabled` flag has to be set and the following special migration has to be run: - -`mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/` - -This will probably take a long time. - -## :rate_limit - -This is an advanced feature and disabled by default. - -A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where: - -* The first element: `scale` (Integer). The time scale in milliseconds. -* The second element: `limit` (Integer). How many requests to limit in the time scale provided. - -It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated. - -See [`Pleroma.Plugs.RateLimiter`](Pleroma.Plugs.RateLimiter.html) documentation for examples. - -Supported rate limiters: - -* `:search` for the search requests (account & status search etc.) -* `:app_account_creation` for registering user accounts from the same IP address -* `:relations_actions` for actions on relations with all users (follow, unfollow) -* `:relation_id_action` for actions on relation with a specific user (follow, unfollow) -* `:statuses_actions` for create / delete / fav / unfav / reblog / unreblog actions on any statuses -* `:status_id_action` for fav / unfav or reblog / unreblog actions on the same status by the same user - -## :web_cache_ttl - -The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration. - -Available caches: - -* `:activity_pub` - activity pub routes (except question activities). Defaults to `nil` (no expiration). -* `:activity_pub_question` - activity pub routes (question activities). Defaults to `30_000` (30 seconds). From 171cefd88972c6ec1f37a2e014a9a484ae91ab9a Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 13:20:09 +0300 Subject: [PATCH 05/34] description.exs --- config/description.exs | 2813 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2813 insertions(+) create mode 100644 config/description.exs diff --git a/config/description.exs b/config/description.exs new file mode 100644 index 000000000..537b9d996 --- /dev/null +++ b/config/description.exs @@ -0,0 +1,2813 @@ +use Mix.Config +alias Pleroma.Docs.Formatter + +websocket_config = [ + path: "/websocket", + serializer: [ + {Phoenix.Socket.V1.JSONSerializer, "~> 1.0.0"}, + {Phoenix.Socket.V2.JSONSerializer, "~> 2.0.0"} + ], + timeout: 60_000, + transport_log: false, + compress: false +] + +config :pleroma, :config_description, [ + %{ + group: :pleroma, + key: Pleroma.Upload, + type: :group, + description: "Upload general settings", + children: [ + %{ + key: :uploader, + type: :module, + description: "Module which will be used for uploads", + suggestions: [ + Formatter.uploaders_list() + ] + }, + %{ + key: :filters, + type: {:list, :module}, + description: "List of filter modules for uploads", + suggestions: [ + Formatter.filters_list() + ] + }, + %{ + key: :link_name, + type: :boolean, + description: "If enabled Pleroma will add name parameter to the url off the upload", + suggestions: [ + true, + false + ] + }, + %{ + key: :base_url, + type: :string, + description: "Base url for the uploads, needed if you use CDN", + suggestions: [ + "https://cdn-host.com" + ] + }, + %{ + key: :proxy_remote, + type: :boolean, + description: "If enabled, Pleroma will proxy media requests instead of redirecting to it", + suggestions: [ + true, + false + ] + }, + %{ + key: :proxy_opts, + type: :keyword, + description: "Proxy options, see `Pleroma.ReverseProxy` documentation", + suggestions: ["somehow created link to Pleroma.ReverseProxy options"] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Uploaders.Local, + type: :group, + description: "Local uploader-related settings", + children: [ + %{ + key: :uploads, + type: :string, + description: "Path where user uploads will be saved", + suggestions: [ + "uploads" + ] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Uploaders.S3, + type: :group, + description: "S3 uploader-related settings", + children: [ + %{ + key: :bucket, + type: :strings, + description: "S3 bucket", + suggestions: [ + "bucket" + ] + }, + %{ + key: :bucket_namespace, + type: :string, + description: "S3 bucket namespace", + suggestions: ["pleroma"] + }, + %{ + key: :public_endpoint, + type: :string, + description: "S3 endpoint", + suggestions: ["https://s3.amazonaws.com"] + }, + %{ + key: :truncated_namespace, + type: :string, + description: + "If you use S3 compatible service such as Digital Ocean Spaces or CDN, set folder name or \"\" etc." <> + " For example, when using CDN to S3 virtual host format, set \"\". At this time, write CNAME to CDN in public_endpoint.", + suggestions: [""] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Upload.Filter.Mogrify, + type: :group, + description: "Uploads mogrify filter settings", + children: [ + %{ + key: :args, + type: [:string, {:list, :string}, {:list, :tuple}], + description: "List of actions for the mogrify command", + suggestions: [ + "strip", + ["strip", "auto-orient"], + [{"implode", "1"}], + ["strip", "auto-orient", {"implode", "1"}] + ] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Upload.Filter.AnonymizeFilename, + type: :group, + description: "Filter replaces the filename of the upload", + children: [ + %{ + key: :text, + type: :string, + 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 + ] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Emails.Mailer, + type: :group, + description: "Mailer-related settings", + children: [ + %{ + key: :adapter, + type: :module, + description: + "One of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters)," <> + " or Swoosh.Adapters.Local for in-memory mailbox", + suggestions: [ + Swoosh.Adapters.SMTP, + Swoosh.Adapters.Sendgrid, + Swoosh.Adapters.Sendmail, + Swoosh.Adapters.Mandrill, + Swoosh.Adapters.Mailgun, + Swoosh.Adapters.Mailjet, + Swoosh.Adapters.Postmark, + Swoosh.Adapters.SparkPost, + Swoosh.Adapters.AmazonSES, + Swoosh.Adapters.Dyn, + Swoosh.Adapters.SocketLabs, + Swoosh.Adapters.Gmail + ] + }, + %{ + key: :enabled, + type: :boolean, + description: "Allow/disallow send emails", + suggestions: [ + true, + false + ] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :relay, + type: :string, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: ["smtp.gmail.com"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :username, + type: :string, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: ["pleroma"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :password, + type: :string, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: ["password"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :ssl, + type: :boolean, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: [true, false] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :tls, + type: :atom, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: [:always, :never, :if_available] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :auth, + type: :atom, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: [:always, :never, :if_available] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :port, + type: :integer, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: [1025] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :retries, + type: :integer, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: [5] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SMTP}, + key: :no_mx_lookups, + type: :boolean, + description: "`Swoosh.Adapters.SMTP` adapter specific setting", + suggestions: [true, false] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Sendgrid}, + key: :api_key, + type: :string, + description: "`Swoosh.Adapters.Sendgrid` adapter specific setting", + suggestions: ["my-api-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Sendmail}, + key: :cmd_path, + type: :string, + description: "`Swoosh.Adapters.Sendmail` adapter specific setting", + suggestions: ["/usr/bin/sendmail"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Sendmail}, + key: :cmd_args, + type: :string, + description: "`Swoosh.Adapters.Sendmail` adapter specific setting", + suggestions: ["-N delay,failure,success"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Sendmail}, + key: :qmail, + type: :boolean, + description: "`Swoosh.Adapters.Sendmail` adapter specific setting", + suggestions: [true, false] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Mandrill}, + key: :api_key, + type: :string, + description: "`Swoosh.Adapters.Mandrill` adapter specific setting", + suggestions: ["my-api-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Mailgun}, + key: :api_key, + type: :string, + description: "`Swoosh.Adapters.Mailgun` adapter specific setting", + suggestions: ["my-api-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Mailgun}, + key: :domain, + type: :string, + description: "`Swoosh.Adapters.Mailgun` adapter specific setting", + suggestions: ["pleroma.com"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Mailjet}, + key: :api_key, + type: :string, + description: "`Swoosh.Adapters.Mailjet` adapter specific setting", + suggestions: ["my-api-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Mailjet}, + key: :secret, + type: :string, + description: "`Swoosh.Adapters.Mailjet` adapter specific setting", + suggestions: ["my-secret-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Postmark}, + key: :api_key, + type: :string, + description: "`Swoosh.Adapters.Postmark` adapter specific setting", + suggestions: ["my-api-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SparkPost}, + key: :api_key, + type: :string, + description: "`Swoosh.Adapters.SparkPost` adapter specific setting", + suggestions: ["my-api-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SparkPost}, + key: :endpoint, + type: :string, + description: "`Swoosh.Adapters.SparkPost` adapter specific setting", + suggestions: ["https://api.sparkpost.com/api/v1"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.AmazonSES}, + key: :region, + type: {:string}, + description: "`Swoosh.Adapters.AmazonSES` adapter specific setting", + suggestions: ["us-east-1", "us-east-2"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.AmazonSES}, + key: :access_key, + type: :string, + description: "`Swoosh.Adapters.AmazonSES` adapter specific setting", + suggestions: ["aws-access-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.AmazonSES}, + key: :secret, + type: :string, + description: "`Swoosh.Adapters.AmazonSES` adapter specific setting", + suggestions: ["aws-secret-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Dyn}, + key: :api_key, + type: :string, + description: "`Swoosh.Adapters.Dyn` adapter specific setting", + suggestions: ["my-api-key"] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SocketLabs}, + key: :server_id, + type: :string, + description: "`Swoosh.Adapters.SocketLabs` adapter specific setting", + suggestions: [""] + }, + %{ + group: {:subgroup, Swoosh.Adapters.SocketLabs}, + key: :api_key, + type: :string, + description: "`Swoosh.Adapters.SocketLabs` adapter specific setting", + suggestions: [""] + }, + %{ + group: {:subgroup, Swoosh.Adapters.Gmail}, + key: :access_token, + type: :string, + description: "`Swoosh.Adapters.Gmail` adapter specific setting", + suggestions: [""] + } + ] + }, + %{ + group: :pleroma, + key: :uri_schemes, + type: :group, + description: "URI schemes related settings", + children: [ + %{ + key: :valid_schemes, + 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" + ] + ] + } + ] + }, + %{ + group: :pleroma, + key: :instance, + type: :group, + description: "Instance-related settings", + children: [ + %{ + key: :name, + type: :string, + description: "Name of the instance", + suggestions: [ + "Pleroma" + ] + }, + %{ + key: :email, + type: :string, + description: "Email used to reach an Administrator/Moderator of the instance", + suggestions: [ + "email@example.com" + ] + }, + %{ + key: :notify_email, + type: :string, + description: "Email used for notifications", + suggestions: [ + "notify@example.com" + ] + }, + %{ + key: :description, + type: :string, + description: "The instance's description, can be seen in nodeinfo and /api/v1/instance", + suggestions: [ + "Very cool instance" + ] + }, + %{ + key: :limit, + type: :integer, + description: "Posts character limit (CW/Subject included in the counter)", + suggestions: [ + 5_000 + ] + }, + %{ + key: :remote_limit, + type: :integer, + description: "Hard character limit beyond which remote posts will be dropped", + suggestions: [ + 100_000 + ] + }, + %{ + key: :upload_limit, + type: :integer, + description: "File size limit of uploads (except for avatar, background, banner)", + suggestions: [ + 16_000_000 + ] + }, + %{ + key: :avatar_upload_limit, + type: :integer, + description: "File size limit of user's profile avatars", + suggestions: [ + 2_000_000 + ] + }, + %{ + key: :background_upload_limit, + type: :integer, + description: "File size limit of user's profile backgrounds", + suggestions: [ + 4_000_000 + ] + }, + %{ + key: :banner_upload_limit, + type: :integer, + description: "File size limit of user's profile banners", + suggestions: [ + 4_000_000 + ] + }, + %{ + key: :poll_limits, + type: :map, + description: "A map with poll limits for local polls", + suggestions: [ + %{ + max_options: 20, + max_option_chars: 200, + min_expiration: 0, + max_expiration: 31_536_000 + } + ], + children: [ + %{ + key: :max_options, + type: :integer, + description: "Maximum number of options", + suggestions: [20] + }, + %{ + key: :max_option_chars, + type: :integer, + description: "Maximum number of characters per option", + suggestions: [200] + }, + %{ + key: :min_expiration, + type: :integer, + description: "Minimum expiration time (in seconds)", + suggestions: [0] + }, + %{ + key: :max_expiration, + type: :integer, + description: "Maximum expiration time (in seconds)", + suggestions: [3600] + } + ] + }, + %{ + key: :registrations_open, + type: :boolean, + description: "Enable registrations for anyone, invitations can be enabled when false", + suggestions: [ + true, + false + ] + }, + %{ + key: :invites_enabled, + type: :boolean, + description: "Enable user invitations for admins (depends on registrations_open: false)", + suggestions: [ + true, + false + ] + }, + %{ + key: :account_activation_required, + type: :boolean, + description: "Require users to confirm their emails before signing in", + suggestions: [ + true, + false + ] + }, + %{ + key: :federating, + type: :boolean, + description: "Enable federation with other instances", + suggestions: [ + true, + false + ] + }, + %{ + key: :federation_incoming_replies_max_depth, + type: :integer, + description: + "Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while" <> + " fetching very long threads. If set to nil, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes", + suggestions: [ + 100 + ] + }, + %{ + key: :federation_reachability_timeout_days, + type: :integer, + description: + "Timeout (in days) of each external federation target being unreachable prior to pausing federating to it", + suggestions: [ + 7 + ] + }, + %{ + key: :federation_publisher_modules, + type: [:list, :module], + description: "List of modules for federation publishing", + suggestions: [ + Pleroma.Web.ActivityPub.Publisher, + Pleroma.Web.Websub, + Pleroma.Web.Salmo + ] + }, + %{ + key: :allow_relay, + type: :boolean, + description: "Enable Pleroma's Relay, which makes it possible to follow a whole instance", + suggestions: [ + true, + false + ] + }, + %{ + key: :rewrite_policy, + type: {:list, :module}, + description: "A list of MRF policies enabled", + suggestions: [ + Pleroma.Web.ActivityPub.MRF.NoOpPolicy, + Formatter.mrf_list() + ] + }, + %{ + key: :public, + type: :boolean, + description: + "Makes the client API in authentificated mode-only except for user-profiles." <> + " Useful for disabling the Local Timeline and The Whole Known Network", + suggestions: [ + true, + false + ] + }, + %{ + key: :quarantined_instances, + type: {:list, :string}, + description: + "List of ActivityPub instances where private(DMs, followers-only) activities will not be send", + suggestions: [ + "quarantined.com", + "*.quarantined.com" + ] + }, + %{ + key: :managed_config, + type: :boolean, + description: + "Whenether the config for pleroma-fe is configured in this config or in static/config.json", + suggestions: [ + true, + false + ] + }, + %{ + key: :static_dir, + type: :string, + description: "Instance static directory", + suggestions: [ + "instance/static/" + ] + }, + %{ + key: :allowed_post_formats, + type: {:list, :string}, + description: "MIME-type list of formats allowed to be posted (transformed into HTML)", + suggestions: [ + [ + "text/plain", + "text/html", + "text/markdown", + "text/bbcode" + ] + ] + }, + %{ + key: :mrf_transparency, + type: :boolean, + description: + "Make the content of your Message Rewrite Facility settings public (via nodeinfo)", + suggestions: [ + true, + false + ] + }, + %{ + key: :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"] + ] + }, + %{ + key: :extended_nickname_format, + type: :boolean, + description: + "Set to true to use extended local nicknames format (allows underscores/dashes)." <> + " This will break federation with older software for theses nicknames", + suggestions: [ + true, + false + ] + }, + %{ + key: :max_pinned_statuses, + type: :integer, + description: "The maximum number of pinned statuses. 0 will disable the feature", + suggestions: [ + 0, + 1, + 3 + ] + }, + %{ + key: :autofollowed_nicknames, + type: {:list, :string}, + description: + "Set to nicknames of (local) users that every new user should automatically follow", + suggestions: [ + "lain", + "kaniini", + "lanodan", + "rinpatch" + ] + }, + %{ + key: :no_attachment_links, + type: :boolean, + description: + "Set to true to disable automatically adding attachment link text to statuses", + suggestions: [ + true, + false + ] + }, + %{ + key: :welcome_message, + type: :string, + description: + "A message that will be send to a newly registered users as a direct message", + suggestions: [ + "Hi, @username! Welcome to the board!", + nil + ] + }, + %{ + key: :welcome_user_nickname, + type: :string, + description: "The nickname of the local user that sends the welcome message", + suggestions: [ + "lain", + nil + ] + }, + %{ + key: :max_report_comment_size, + type: :integer, + description: "The maximum size of the report comment (Default: 1000)", + suggestions: [ + 1_000 + ] + }, + %{ + key: :safe_dm_mentions, + type: :boolean, + description: + "If set to true, only mentions at the beginning of a post will be used to address people in direct messages." <> + " This is to prevent accidental mentioning of people when talking about them (e.g. \"@friend hey i really don't like @enemy\")." <> + " Default: false", + suggestions: [ + true, + false + ] + }, + %{ + key: :healthcheck, + type: :boolean, + description: "If set to true, system data will be shown on /api/pleroma/healthcheck", + suggestions: [ + true, + false + ] + }, + %{ + key: :remote_post_retention_days, + type: :integer, + description: + "The default amount of days to retain remote posts when pruning the database", + suggestions: [ + 90 + ] + }, + %{ + key: :user_bio_length, + type: :integer, + description: "A user bio maximum length (default: 5000)", + suggestions: [ + 5_000 + ] + }, + %{ + key: :user_name_length, + type: :integer, + description: "A user name maximum length (default: 100)", + suggestions: [ + 100 + ] + }, + %{ + key: :skip_thread_containment, + type: :boolean, + description: "Skip filter out broken threads. The default is true", + suggestions: [ + true, + false + ] + }, + %{ + key: :limit_to_local_content, + type: [:atom, false], + description: + "Limit unauthenticated users to search for local statutes and users only. The default is :unauthenticated ", + suggestions: [ + :unauthenticated, + :all, + false + ] + }, + %{ + key: :dynamic_configuration, + type: :boolean, + description: + "Allow transferring configuration to DB with the subsequent customization from Admin api. Defaults to `false`", + suggestions: [ + true, + false + ] + }, + %{ + key: :max_account_fields, + type: :integer, + description: "The maximum number of custom fields in the user profile (default: 10)", + suggestions: [ + 10 + ] + }, + %{ + key: :max_remote_account_fields, + type: :integer, + description: + "The maximum number of custom fields in the remote user profile (default: 20)", + suggestions: [ + 20 + ] + }, + %{ + key: :account_field_name_length, + type: :integer, + description: "An account field name maximum length (default: 512)", + suggestions: [ + 512 + ] + }, + %{ + key: :account_field_value_length, + type: :integer, + description: "An account field value maximum length (default: 512)", + suggestions: [ + 512 + ] + }, + %{ + key: :external_user_synchronization, + type: :boolean, + description: "Enabling following/followers counters synchronization for external users", + suggestions: [ + true, + false + ] + } + ] + }, + %{ + group: :logger, + type: :group, + description: "Logger-related settings", + children: [ + %{ + key: :backends, + 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]] + } + ] + }, + %{ + group: :logger, + type: :group, + key: :ex_syslogger, + description: "ExSyslogger-related settings", + children: [ + %{ + key: :level, + type: :atom, + description: "Log level", + suggestions: [:debug, :info, :warn, :error] + }, + %{ + key: :ident, + type: :string, + description: + "A string that's prepended to every message, and is typically set to the app name", + suggestions: ["pleroma"] + }, + %{ + key: :format, + type: :string, + description: "It defaults to \"$date $time [$level] $levelpad$node $metadata $message\"", + suggestions: ["$metadata[$level] $message"] + }, + %{ + key: :metadata, + type: {:list, :atom}, + description: "", + suggestions: [[:request_id]] + } + ] + }, + %{ + group: :logger, + type: :group, + key: :console, + description: "Console logger settings", + children: [ + %{ + key: :level, + type: :atom, + description: "Log level", + suggestions: [:debug, :info, :warn, :error] + }, + %{ + key: :format, + type: :string, + description: "It defaults to \"$date $time [$level] $levelpad$node $metadata $message\"", + suggestions: ["$metadata[$level] $message"] + }, + %{ + key: :metadata, + type: {:list, :atom}, + description: "", + suggestions: [[:request_id]] + } + ] + }, + %{ + group: :quack, + type: :group, + description: "Quack-related settings", + children: [ + %{ + key: :level, + type: :atom, + description: "Log level", + suggestions: [:debug, :info, :warn, :error] + }, + %{ + key: :meta, + type: {:list, :atom}, + description: "Configure which metadata you want to report on", + suggestions: [ + :application, + :module, + :file, + :function, + :line, + :pid, + :crash_reason, + :initial_call, + :registered_name, + :all, + :none + ] + }, + %{ + key: :webhook_url, + type: :string, + description: "Configure the Slack incoming webhook", + suggestions: ["https://hooks.slack.com/services/YOUR-KEY-HERE"] + } + ] + }, + %{ + group: :pleroma, + key: :frontend_configurations, + type: :group, + description: "A keyword list that keeps the configuration data for any kind of frontend", + children: [ + %{ + key: :pleroma_fe, + type: :map, + description: "Settings for Pleroma FE", + suggestions: [ + %{ + theme: "pleroma-dark", + logo: "/static/logo.png", + background: "/images/city.jpg", + redirectRootNoLogin: "/main/all", + redirectRootLogin: "/main/friends", + showInstanceSpecificPanel: true, + scopeOptionsEnabled: false, + formattingOptionsEnabled: false, + collapseMessageWithSubject: false, + hidePostStats: false, + hideUserStats: false, + scopeCopy: true, + subjectLineBehavior: "email", + alwaysShowSubjectInput: true + } + ], + children: [ + %{ + key: :theme, + type: :string, + description: "Which theme to use, they are defined in styles.json", + suggestions: ["pleroma-dark"] + }, + %{ + key: :logo, + type: :string, + description: "URL of the logo, defaults to Pleroma's logo", + suggestions: ["/static/logo.png"] + }, + %{ + key: :background, + type: :string, + description: + "URL of the background, unless viewing a user profile with a background that is set", + suggestions: ["/images/city.jpg"] + }, + %{ + key: :redirectRootNoLogin, + type: :string, + description: + "relative URL which indicates where to redirect when a user isn't logged in", + suggestions: ["/main/all"] + }, + %{ + key: :redirectRootLogin, + type: :string, + description: + "relative URL which indicates where to redirect when a user is logged in", + suggestions: ["/main/friends"] + }, + %{ + key: :showInstanceSpecificPanel, + type: :boolean, + description: "Whenether to show the instance's specific panel", + suggestions: [true, false] + }, + %{ + key: :scopeOptionsEnabled, + type: :boolean, + description: "Enable setting an notice visibility and subject/CW when posting", + suggestions: [true, false] + }, + %{ + key: :formattingOptionsEnabled, + type: :boolean, + description: + "Enable setting a formatting different than plain-text (ie. HTML, Markdown) when posting, relates to :instance, allowed_post_formats", + suggestions: [true, false] + }, + %{ + key: :collapseMessageWithSubject, + type: :boolean, + description: + "When a message has a subject(aka Content Warning), collapse it by default", + suggestions: [true, false] + }, + %{ + key: :hidePostStats, + type: :boolean, + description: "Hide notices statistics(repeats, favorites, ...)", + suggestions: [true, false] + }, + %{ + key: :hideUserStats, + type: :boolean, + description: + "Hide profile statistics(posts, posts per day, followers, followings, ...)", + suggestions: [true, false] + }, + %{ + key: :scopeCopy, + type: :boolean, + description: + "Copy the scope (private/unlisted/public) in replies to posts by default", + suggestions: [true, false] + }, + %{ + key: :subjectLineBehavior, + type: :string, + description: "Allows changing the default behaviour of subject lines in replies. + `email`: Copy and preprend re:, as in email, + `masto`: Copy verbatim, as in Mastodon, + `noop`: Don't copy the subjec", + suggestions: ["email", "masto", "noop"] + }, + %{ + key: :alwaysShowSubjectInput, + type: :boolean, + description: "When set to false, auto-hide the subject field when it's empty", + suggestions: [true, false] + } + ] + }, + %{ + key: :masto_fe, + type: :map, + description: "Settings for Masto FE", + suggestions: [ + %{ + showInstanceSpecificPanel: true + } + ], + children: [ + %{ + key: :showInstanceSpecificPanel, + type: :boolean, + description: "Whenether to show the instance's specific panel", + suggestions: [true, false] + } + ] + } + ] + }, + %{ + group: :pleroma, + key: :assets, + type: :group, + description: + "This section configures assets to be used with various frontends. Currently the only option relates to mascots on the mastodon frontend", + children: [ + %{ + key: :mascots, + type: :keyword, + 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" + } + ] + ] + }, + %{ + key: :default_mascot, + type: :atom, + description: + "This will be used as the default mascot on MastoFE (default: :pleroma_fox_tan)", + suggestions: [ + :pleroma_fox_tan + ] + } + ] + }, + %{ + group: :pleroma, + key: :mrf_simple, + type: :group, + description: "Message Rewrite Facility", + children: [ + %{ + key: :media_removal, + type: {:list, :string}, + description: "List of instances to remove medias from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :media_nsfw, + type: {:list, :string}, + description: "List of instances to put medias as NSFW(sensitive) from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :federated_timeline_removal, + type: {:list, :string}, + description: + "List of instances to remove from Federated (aka The Whole Known Network) Timeline", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :reject, + type: {:list, :string}, + description: "List of instances to reject any activities from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :accept, + type: {:list, :string}, + description: "List of instances to accept any activities from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :report_removal, + type: {:list, :string}, + description: "List of instances to reject reports from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :avatar_removal, + type: {:list, :string}, + description: "List of instances to strip avatars from", + suggestions: ["example.com", "*.example.com"] + }, + %{ + key: :banner_removal, + type: {:list, :string}, + description: "List of instances to strip banners from", + suggestions: ["example.com", "*.example.com"] + } + ] + }, + %{ + group: :pleroma, + key: :mrf_subchain, + type: :group, + description: + "This policy processes messages through an alternate pipeline when a given message matches certain criteria." <> + " All criteria are configured as a map of regular expressions to lists of policy modules.", + children: [ + %{ + key: :match_actor, + type: :map, + description: "Matches a series of regular expressions against the actor field", + suggestions: [ + %{ + ~r/https:\/\/example.com/s => [Pleroma.Web.ActivityPub.MRF.DropPolicy] + } + ] + } + ] + }, + %{ + group: :pleroma, + key: :mrf_rejectnonpublic, + type: :group, + description: "", + children: [ + %{ + key: :allow_followersonly, + type: :boolean, + description: "whether to allow followers-only posts", + suggestions: [true, false] + }, + %{ + key: :allow_direct, + type: :boolean, + description: "whether to allow direct messages", + suggestions: [true, false] + } + ] + }, + %{ + group: :pleroma, + key: :mrf_hellthread, + type: :group, + description: "Block messages with too much mentions", + children: [ + %{ + key: :delist_threshold, + type: :integer, + description: + "Number of mentioned users after which the message gets delisted (the message can still be seen, " <> + " but it will not show up in public timelines and mentioned users won't get notifications about it). Set to 0 to disable", + suggestions: [10] + }, + %{ + key: :reject_threshold, + type: :integer, + description: + "Number of mentioned users after which the messaged gets rejected. Set to 0 to disable", + suggestions: [20] + } + ] + }, + %{ + group: :pleroma, + key: :mrf_keyword, + type: :group, + description: "Reject or Word-Replace messages with a keyword or regex", + children: [ + %{ + key: :reject, + type: [:string, :regex], + description: + "A list of patterns which result in message being rejected, each pattern can be a string or a regular expression", + suggestions: ["foo", ~r/foo/iu] + }, + %{ + key: :federated_timeline_removal, + type: [:string, :regex], + 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", + suggestions: ["foo", ~r/foo/iu] + }, + %{ + key: :replace, + type: [{:string, :string}, {: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", + suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}] + } + ] + }, + %{ + group: :pleroma, + key: :mrf_mention, + type: :group, + description: "Block messages which mention a user", + children: [ + %{ + key: :actors, + type: {:list, :string}, + description: "A list of actors, for which to drop any posts mentioning", + suggestions: [["actor1", "actor2"]] + } + ] + }, + %{ + group: :pleroma, + key: :mrf_vocabulary, + type: :group, + description: "Filter messages which belong to certain activity vocabularies", + children: [ + %{ + key: :accept, + type: {:list, :string}, + description: + "A list of ActivityStreams terms to accept. If empty, all supported messages are accepted", + 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"]] + } + ] + }, + # %{ + # group: :pleroma, + # key: :mrf_user_allowlist, + # type: :group, + # description: + # "The keys in this section are the domain names that the policy should apply to." <> + # " Each key should be assigned a list of users that should be allowed through by their ActivityPub ID", + # children: [ + # ["example.org": ["https://example.org/users/admin"]], + # suggestions: [ + # ["example.org": ["https://example.org/users/admin"]] + # ] + # ] + # }, + %{ + group: :pleroma, + key: :media_proxy, + type: :group, + description: "Media proxy", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "Enables proxying of remote media to the instance's proxy", + suggestions: [true, false] + }, + %{ + key: :base_url, + type: :string, + description: + "The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts", + suggestions: ["https://example.com"] + }, + %{ + key: :proxy_opts, + type: :keyword, + description: "Options for Pleroma.ReverseProxy", + suggestions: [[max_body_length: 25 * 1_048_576, redirect_on_failure: false]] + }, + %{ + key: :whitelist, + type: {:list, :string}, + description: "List of domains to bypass the mediaproxy", + suggestions: ["example.com"] + } + ] + }, + %{ + group: :pleroma, + key: :gopher, + type: :group, + description: "Gopher settings", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "Enables the gopher interface", + suggestions: [true, false] + }, + %{ + key: :ip, + type: :tuple, + description: "IP address to bind to", + suggestions: [{0, 0, 0, 0}] + }, + %{ + key: :port, + type: :integer, + description: "Port to bind to", + suggestions: [9999] + }, + %{ + key: :dstport, + type: :integer, + description: "Port advertised in urls (optional, defaults to port)", + suggestions: [9999] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Web.Endpoint, + type: :group, + description: "Phoenix endpoint configuration", + children: [ + %{ + key: :http, + type: :keyword, + description: "http protocol configuration", + suggestions: [ + [port: 8080, ip: {127, 0, 0, 1}] + ], + children: [ + %{ + key: :dispatch, + 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 + ] + ] + }, + %{ + key: :ip, + type: :tuple, + description: "ip", + suggestions: [ + {0, 0, 0, 0} + ] + }, + %{ + key: :port, + type: :integer, + description: "port", + suggestions: [ + 2020 + ] + } + ] + }, + %{ + key: :url, + type: :keyword, + description: "configuration for generating urls", + suggestions: [ + [host: "example.com", port: 2020, scheme: "https"] + ], + children: [ + %{ + key: :host, + type: :string, + description: "Host", + suggestions: [ + "example.com" + ] + }, + %{ + key: :port, + type: :integer, + description: "port", + suggestions: [ + 2020 + ] + }, + %{ + key: :scheme, + type: :string, + description: "Scheme", + suggestions: [ + "https", + "https" + ] + } + ] + }, + %{ + key: :instrumenters, + type: {:list, :module}, + description: "", + suggestions: [Pleroma.Web.Endpoint.Instrumenter] + }, + %{ + key: :protocol, + type: :string, + description: "", + suggestions: ["https"] + }, + %{ + key: :secret_key_base, + type: :string, + description: "", + suggestions: ["aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl"] + }, + %{ + key: :signing_salt, + type: :string, + description: "", + suggestions: ["CqaoopA2"] + }, + %{ + key: :render_errors, + type: :keyword, + description: "", + suggestions: [[view: Pleroma.Web.ErrorView, accepts: ~w(json)]], + children: [ + %{ + key: :view, + type: :module, + description: "", + suggestions: [Pleroma.Web.ErrorView] + }, + %{ + key: :accepts, + type: {:list, :string}, + description: "", + suggestions: ["json"] + } + ] + }, + %{ + key: :pubsub, + type: :keyword, + description: "", + suggestions: [[name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]], + children: [ + %{ + key: :name, + type: :module, + description: "", + suggestions: [Pleroma.PubSub] + }, + %{ + key: :adapter, + type: :module, + description: "", + suggestions: [Phoenix.PubSub.PG2] + } + ] + }, + %{ + key: :secure_cookie_flag, + type: :boolean, + description: "", + suggestions: [true, false] + }, + %{ + key: :extra_cookie_attrs, + type: {:list, :string}, + description: "", + suggestions: ["SameSite=Lax"] + } + ] + }, + %{ + group: :pleroma, + key: :activitypub, + type: :group, + description: "ActivityPub-related settings", + children: [ + %{ + key: :unfollow_blocked, + type: :boolean, + description: "Whether blocks result in people getting unfollowed", + suggestions: [true, false] + }, + %{ + key: :outgoing_blocks, + type: :boolean, + description: "Whether to federate blocks to other instances", + suggestions: [true, false] + }, + %{ + key: :sign_object_fetches, + type: :boolean, + description: "Sign object fetches with HTTP signatures", + suggestions: [true, false] + }, + %{ + key: :follow_handshake_timeout, + type: :integer, + description: "Following handshake timeout", + suggestions: [500] + } + ] + }, + %{ + group: :pleroma, + key: :http_security, + type: :group, + description: "HTTP security settings", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "Whether the managed content security policy is enabled", + suggestions: [true, false] + }, + %{ + key: :sts, + type: :boolean, + description: "Whether to additionally send a Strict-Transport-Security header", + suggestions: [true, false] + }, + %{ + key: :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, + type: :integer, + description: "The maximum age for the Expect-CT header if sent", + suggestions: [2_592_000] + }, + %{ + key: :referrer_policy, + type: :string, + description: "The referrer policy to use, either \"same-origin\" or \"no-referrer\"", + suggestions: ["same-origin", "no-referrer"] + }, + %{ + key: :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"] + } + ] + }, + %{ + group: :web_push_encryption, + key: :vapid_details, + type: :group, + description: + "Web Push Notifications configuration. You can use the mix task mix web_push.gen.keypair to generate it", + children: [ + %{ + key: :subject, + type: :string, + description: + "a mailto link for the administrative contact." <> + " It's best if this email is not a personal email address, but rather a group email so that if a person leaves an organization," <> + " is unavailable for an extended period, or otherwise can't respond, someone else on the list can", + suggestions: ["Subject"] + }, + %{ + key: :public_key, + type: :string, + description: "VAPID public key", + suggestions: ["Public key"] + }, + %{ + key: :private_key, + type: :string, + description: "VAPID private keyn", + suggestions: ["Private key"] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Captcha, + type: :group, + description: "Captcha-related settings", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "Whether the captcha should be shown on registration", + suggestions: [true, false] + }, + %{ + key: :method, + type: :module, + description: "The method/service to use for captcha", + suggestions: [Pleroma.Captcha.Kocaptcha] + }, + %{ + key: :seconds_valid, + type: :integer, + description: "The time in seconds for which the captcha is valid", + suggestions: [60] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Captcha.Kocaptcha, + type: :group, + description: + "Kocaptcha is a very simple captcha service with a single API endpoint, the source code is" <> + " here: https://github.com/koto-bank/kocaptcha. The default endpoint https://captcha.kotobank.ch is hosted by the developer", + children: [ + %{ + key: :endpoint, + type: :string, + description: "the kocaptcha endpoint to use", + suggestions: ["https://captcha.kotobank.ch"] + } + ] + }, + %{ + group: :pleroma, + type: :group, + description: + "Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the 'admin_token' parameter", + children: [ + %{ + key: :admin_token, + type: :string, + description: "Token", + suggestions: ["some_random_token"] + } + ] + }, + %{ + group: :pleroma_job_queue, + key: :queues, + type: :group, + description: "Pleroma Job Queue configuration: a list of queues with maximum concurrent jobs", + children: [ + %{ + key: :federator_outgoing, + type: :integer, + description: "Outgoing federation queue", + suggestions: [50] + }, + %{ + key: :federator_incoming, + type: :integer, + description: "Incoming federation queue", + suggestions: [50] + }, + %{ + key: :mailer, + type: :integer, + description: "Email sender queue, see Pleroma.Emails.Mailer", + suggestions: [10] + }, + %{ + key: :web_push, + type: :integer, + description: "Web push notifications queue", + suggestions: [50] + }, + %{ + key: :transmogrifier, + type: :integer, + description: "Transmogrifier queue", + suggestions: [20] + }, + %{ + key: :scheduled_activities, + type: :integer, + description: "Scheduled activities queue, see Pleroma.ScheduledActivities", + suggestions: [10] + }, + %{ + key: :activity_expiration, + type: :integer, + description: "Activity expiration queue", + suggestions: [10] + }, + %{ + key: :background, + type: :integer, + description: "Background queue", + suggestions: [5] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Web.Federator.RetryQueue, + type: :group, + description: "", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "If set to true, failed federation jobs will be retried", + suggestions: [true, false] + }, + %{ + key: :max_jobs, + type: :integer, + description: "The maximum amount of parallel federation jobs running at the same time", + suggestions: [20] + }, + %{ + key: :initial_timeout, + type: :integer, + description: "The initial timeout in seconds", + suggestions: [30] + }, + %{ + key: :max_retries, + type: :integer, + description: "The maximum number of times a federation job is retried", + suggestions: [5] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Web.Metadata, + type: :group, + decsription: "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 + ] + ] + }, + %{ + key: :unfurl_nsfw, + type: :boolean, + description: "If set to true nsfw attachments will be shown in previews", + suggestions: [ + true, + false + ] + } + ] + }, + %{ + group: :pleroma, + key: :rich_media, + type: :group, + description: "", + children: [ + %{ + key: :enabled, + type: :boolean, + description: + "if enabled the instance will parse metadata from attached links to generate link previews", + suggestions: [true, false] + }, + %{ + key: :ignore_hosts, + type: {:list, :string}, + description: "list of hosts which will be ignored by the metadata parser", + suggestions: [["accounts.google.com", "xss.website"]] + }, + %{ + key: :ignore_tld, + type: {:list, :string}, + 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", + suggestions: [ + Formatter.richmedia_parsers() + ] + }, + %{ + key: :ttl_setters, + type: {:list, :module}, + description: "list of rich media ttl setters", + suggestions: [ + [Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl] + ] + } + ] + }, + %{ + group: :pleroma, + key: :fetch_initial_posts, + type: :group, + description: "Fetching initial posts settings", + children: [ + %{ + key: :enabled, + type: :boolean, + description: + "if enabled, when a new user is federated with, fetch some of their latest posts", + suggestions: [true, false] + }, + %{ + key: :pages, + type: :integer, + description: "the amount of pages to fetch", + suggestions: [5] + } + ] + }, + %{ + group: :auto_linker, + key: :opts, + type: :group, + description: "Configuration for the auto_linker library", + children: [ + %{ + key: :class, + type: [:string, false], + description: "specify the class to be added to the generated link. false to clear", + suggestions: ["auto-linker", false] + }, + %{ + key: :rel, + type: [:string, false], + description: "override the rel attribute. false to clear", + suggestions: ["noopener noreferrer", false] + }, + %{ + key: :new_window, + type: :boolean, + description: "set to false to remove target='_blank' attribute", + suggestions: [true, false] + }, + %{ + key: :scheme, + type: :boolean, + description: "Set to true to link urls with schema http://google.com", + suggestions: [true, false] + }, + %{ + key: :truncate, + type: [:integer, false], + description: + "Set to a number to truncate urls longer then the number. Truncated urls will end in `..`", + suggestions: [15, false] + }, + %{ + key: :strip_prefix, + type: :boolean, + description: "Strip the scheme prefix", + suggestions: [true, false] + }, + %{ + key: :extra, + type: :boolean, + description: "link urls with rarely used schemes (magnet, ipfs, irc, etc.)", + suggestions: [true, false] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.ScheduledActivity, + type: :group, + description: "Scheduled activities settings", + children: [ + %{ + key: :daily_user_limit, + type: :integer, + description: + "the number of scheduled activities a user is allowed to create in a single day (Default: 25)", + suggestions: [25] + }, + %{ + key: :total_user_limit, + type: :integer, + description: + "the number of scheduled activities a user is allowed to create in total (Default: 300)", + suggestions: [300] + }, + %{ + key: :enabled, + type: :boolean, + description: "whether scheduled activities are sent to the job queue to be executed", + suggestions: [true, false] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.ActivityExpiration, + type: :group, + description: "Expired activity settings", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "whether expired activities will be sent to the job queue to be deleted", + suggestions: [true, false] + } + ] + }, + %{ + group: :pleroma, + type: :group, + description: "Authenticator", + children: [ + %{ + key: Pleroma.Web.Auth.Authenticator, + type: :module, + description: "", + suggestions: [Pleroma.Web.Auth.PleromaAuthenticator, Pleroma.Web.Auth.LDAPAuthenticator] + } + ] + }, + %{ + group: :pleroma, + key: :ldap, + type: :group, + description: + "Use LDAP for user authentication. When a user logs in to the Pleroma instance, the name and password" <> + " will be verified by trying to authenticate (bind) to an LDAP server." <> + " If a user exists in the LDAP directory but there is no account with the same name yet on the" <> + " Pleroma instance then a new Pleroma account will be created with the same name as the LDAP user name.", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "enables LDAP authentication", + suggestions: [true, false] + }, + %{ + key: :host, + type: :string, + description: "LDAP server hostname", + suggestions: ["localhosts"] + }, + %{ + key: :port, + type: :integer, + description: "LDAP port, e.g. 389 or 636", + suggestions: [389, 636] + }, + %{ + key: :ssl, + type: :boolean, + description: "true to use SSL, usually implies the port 636", + suggestions: [true, false] + }, + %{ + key: :sslopts, + type: :keyword, + description: "additional SSL options", + suggestions: [] + }, + %{ + key: :tls, + type: :boolean, + description: "true to start TLS, usually implies the port 389", + suggestions: [true, false] + }, + %{ + key: :tlsopts, + type: :keyword, + description: "additional TLS options", + suggestions: [] + }, + %{ + key: :base, + type: :string, + description: "LDAP base, e.g. \"dc=example,dc=com\"", + suggestions: ["dc=example,dc=com"] + }, + %{ + key: :uid, + type: :string, + description: + "LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"", + suggestions: ["cn"] + } + ] + }, + %{ + group: :pleroma, + key: :auth, + type: :group, + description: "Authentication / authorization settings", + children: [ + %{ + key: :auth_template, + type: :string, + description: + "authentication form template. By default it's show.html which corresponds to lib/pleroma/web/templates/o_auth/o_auth/show.html.ee", + suggestions: ["show.html"] + }, + %{ + key: :oauth_consumer_template, + type: :string, + description: + "OAuth consumer mode authentication form template. By default it's consumer.html which corresponds to" <> + " lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex", + suggestions: ["consumer.html"] + }, + %{ + key: :oauth_consumer_strategies, + type: :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 or :" <> + " (e.g. twitter or keycloak:ueberauth_keycloak_strategy in case dependency is named differently than ueberauth_).", + suggestions: ["twitter", "keycloak:ueberauth_keycloak_strategy"] + } + ] + }, + %{ + group: :pleroma, + key: :email_notifications, + type: :group, + description: "Email notifications settings", + children: [ + %{ + key: :digest, + type: :map, + description: + "emails of \"what you've missed\" for users who have been inactive for a while", + suggestions: [ + %{ + active: false, + schedule: "0 0 * * 0", + interval: 7, + inactivity_threshold: 7 + } + ], + children: [ + %{ + key: :active, + type: :boolean, + description: "globally enable or disable digest emails", + suggestions: [true, false] + }, + %{ + key: :schedule, + type: :string, + description: + "When to send digest email, in crontab format. \"0 0 0\" is the default, meaning \"once a week at midnight on Sunday morning\"", + suggestions: ["0 0 * * 0"] + }, + %{ + key: :interval, + type: :ininteger, + description: "Minimum interval between digest emails to one user", + suggestions: [7] + }, + %{ + key: :inactivity_threshold, + type: :integer, + description: "Minimum user inactivity threshold", + suggestions: [7] + } + ] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Emails.UserEmail, + type: :group, + description: "Email template settings", + children: [ + %{ + key: :logo, + # type: [:string, nil], + description: "a path to a custom logo. Set it to nil to use the default Pleroma logo", + suggestions: ["some/path/logo.png", nil] + }, + %{ + key: :styling, + type: :map, + description: "a map with color settings for email templates.", + suggestions: [ + %{ + link_color: "#d8a070", + background_color: "#2C3645", + content_background_color: "#1B2635", + header_color: "#d8a070", + text_color: "#b9b9ba", + text_muted_color: "#b9b9ba" + } + ], + children: [ + %{ + key: :link_color, + type: :string, + description: "", + suggestions: ["#d8a070"] + }, + %{ + key: :background_color, + type: :string, + description: "", + suggestions: ["#2C3645"] + }, + %{ + key: :content_background_color, + type: :string, + description: "", + suggestions: ["#1B2635"] + }, + %{ + key: :header_color, + type: :string, + description: "", + suggestions: ["#d8a070"] + }, + %{ + key: :text_color, + type: :string, + description: "", + suggestions: ["#b9b9ba"] + }, + %{ + key: :text_muted_color, + type: :string, + description: "", + suggestions: ["#b9b9ba"] + } + ] + } + ] + }, + %{ + group: :pleroma, + key: :oauth2, + type: :group, + description: "Configure OAuth 2 provider capabilities", + children: [ + %{ + key: :token_expires_in, + type: :integer, + description: "The lifetime in seconds of the access token", + suggestions: [600] + }, + %{ + key: :issue_new_refresh_token, + type: :boolean, + description: + "Keeps old refresh token or generate new refresh token when to obtain an access token", + suggestions: [true, false] + }, + %{ + key: :clean_expired_tokens, + type: :boolean, + description: "Enable a background job to clean expired oauth tokens. Defaults to false", + suggestions: [true, false] + }, + %{ + key: :clean_expired_tokens_interval, + type: :integer, + description: + "Interval to run the job to clean expired tokens. Defaults to 86_400_000 (24 hours).", + suggestions: [86_400_000] + } + ] + }, + %{ + group: :pleroma, + key: :emoji, + type: :group, + description: "", + children: [ + %{ + key: :shortcode_globs, + type: {:list, :string}, + description: "Location of custom emoji files. * can be used as a wildcard", + 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"]] + }, + %{ + key: :groups, + type: :keyword, + 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"] + ] + ] + }, + %{ + key: :default_manifest, + type: :string, + description: + "Location of the JSON-manifest. This manifest contains information about the emoji-packs you can download." <> + " Currently only one manifest can be added (no arrays)", + suggestions: ["https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json"] + } + ] + }, + %{ + group: :pleroma, + key: :database, + type: :group, + description: "Database related settings", + children: [ + %{ + key: :rum_enabled, + type: :boolean, + description: "If RUM indexes should be used. Defaults to false", + suggestions: [true, false] + } + ] + }, + %{ + group: :pleroma, + key: :rate_limit, + type: :group, + description: "Rate limit settings. This is an advanced feature and disabled by default.", + children: [ + %{ + key: :search, + type: [:tuple, {:list, :tuple}], + description: "for the search requests (account & status search etc.)", + suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] + }, + %{ + key: :app_account_creation, + type: [:tuple, {:list, :tuple}], + description: "for registering user accounts from the same IP address", + suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] + }, + %{ + key: :relations_actions, + type: [:tuple, {:list, :tuple}], + description: "for actions on relations with all users (follow, unfollow)", + suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] + }, + %{ + key: :relation_id_action, + type: [:tuple, {:list, :tuple}], + description: "for actions on relation with a specific user (follow, unfollow)", + suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] + }, + %{ + key: :statuses_actions, + type: [:tuple, {:list, :tuple}], + description: + "for create / delete / fav / unfav / reblog / unreblog actions on any statuses", + suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] + }, + %{ + key: :status_id_action, + type: [:tuple, {:list, :tuple}], + description: + "for fav / unfav or reblog / unreblog actions on the same status by the same user", + suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]] + } + ] + }, + %{ + group: :esshd, + type: :group, + description: + "To enable simple command line interface accessible over ssh, add a setting like this to your configuration file", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "Enables ssh", + suggestions: [true, false] + }, + %{ + key: :priv_dir, + type: :string, + description: "Dir with ssh keys", + suggestions: ["/some/path/ssh_keys"] + }, + %{ + key: :handler, + type: :string, + description: "Handler module", + suggestions: ["Pleroma.BBS.Handler"] + }, + %{ + key: :port, + type: :integer, + description: "Port to connect", + suggestions: [10_022] + }, + %{ + key: :password_authenticator, + type: :string, + description: "Authenticator module", + suggestions: ["Pleroma.BBS.Authenticator"] + } + ] + }, + %{ + group: :mime, + type: :group, + description: "Mime types", + children: [ + %{ + key: :types, + type: :map, + description: "", + suggestions: [ + %{ + "application/xml" => ["xml"], + "application/xrd+xml" => ["xrd+xml"], + "application/jrd+json" => ["jrd+json"], + "application/activity+json" => ["activity+json"], + "application/ld+json" => ["activity+json"] + } + ], + children: [ + %{ + key: "application/xml", + type: {:list, :string}, + description: "", + suggestions: [["xml"]] + }, + %{ + key: "application/xrd+xml", + type: {:list, :string}, + description: "", + suggestions: [["xrd+xml"]] + }, + %{ + key: "application/jrd+json", + type: {:list, :string}, + description: "", + suggestions: [["jrd+json"]] + }, + %{ + key: "application/activity+json", + type: {:list, :string}, + description: "", + suggestions: [["activity+json"]] + }, + %{ + key: "application/ld+json", + type: {:list, :string}, + description: "", + suggestions: [["activity+json"]] + } + ] + } + ] + }, + %{ + group: :tesla, + type: :group, + description: "Tesla settings", + children: [ + %{ + key: :adapter, + type: :module, + description: "Tesla adapter", + suggestions: [Tesla.Adapter.Hackney] + } + ] + }, + %{ + group: :pleroma, + key: :chat, + type: :group, + description: "Pleroma chat settings", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "", + suggestions: [true, false] + } + ] + }, + %{ + group: :pleroma, + key: :suggestions, + type: :group, + description: "", + children: [ + %{ + key: :enabled, + type: :boolean, + description: "Enables suggestions", + suggestions: [] + }, + %{ + key: :third_party_engine, + type: :string, + description: "URL for third party engine", + suggestions: [ + "http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-suggestions-api.cgi?{{host}}+{{user}}" + ] + }, + %{ + key: :timeout, + type: :integer, + description: "Request timeout to third party engine", + suggestions: [300_000] + }, + %{ + key: :limit, + type: :integer, + description: "Limit for suggestions", + suggestions: [40] + }, + %{ + key: :web, + type: :string, + description: "", + suggestions: ["https://vinayaka.distsn.org"] + } + ] + }, + %{ + group: :prometheus, + key: Pleroma.Web.Endpoint.MetricsExporter, + type: :group, + description: "Prometheus settings", + children: [ + %{ + key: :path, + type: :string, + description: "API endpoint with metrics", + suggestions: ["/api/pleroma/app_metrics"] + } + ] + }, + %{ + group: :http_signatures, + type: :group, + description: "HTTP Signatures settings", + children: [ + %{ + key: :adapter, + type: :module, + description: "", + suggestions: [Pleroma.Signature] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.Uploaders.MDII, + type: :group, + description: "", + children: [ + %{ + key: :cgi, + type: :string, + description: "", + suggestions: ["https://mdii.sakura.ne.jp/mdii-post.cgi"] + }, + %{ + key: :files, + type: :string, + description: "", + suggestions: ["https://mdii.sakura.ne.jp"] + } + ] + }, + %{ + group: :pleroma, + key: :http, + type: :group, + description: "HTTP settings", + children: [ + %{ + key: :proxy_url, + type: [:string, :atom, nil], + description: "", + suggestions: ["localhost:9020", {:socks5, :localhost, 3090}, nil] + }, + %{ + key: :send_user_agent, + type: :boolean, + description: "", + suggestions: [true, false] + }, + %{ + key: :adapter, + type: :keyword, + description: "", + 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"] + ] + ] + ] + } + ] + }, + %{ + group: :pleroma, + key: :markup, + type: :group, + description: "", + children: [ + %{ + key: :allow_inline_images, + type: :boolean, + description: "", + suggestions: [true, false] + }, + %{ + key: :allow_headings, + type: :boolean, + description: "", + suggestions: [true, false] + }, + %{ + key: :allow_tables, + type: :boolean, + description: "", + suggestions: [true, false] + }, + %{ + key: :allow_fonts, + type: :boolean, + description: "", + suggestions: [true, false] + }, + %{ + key: :scrub_policy, + type: {:list, :module}, + description: "", + suggestions: [[Pleroma.HTML.Transform.MediaProxy, Pleroma.HTML.Scrubber.Default]] + } + ] + }, + %{ + group: :pleroma, + key: :user, + type: :group, + description: "", + children: [ + %{ + key: :deny_follow_blocked, + type: :boolean, + description: "", + suggestions: [true, false] + } + ] + }, + %{ + group: :pleroma, + key: :mrf_normalize_markup, + type: :group, + description: "", + children: [ + %{ + key: :scrub_policy, + type: :module, + description: "", + suggestions: [Pleroma.HTML.Scrubber.Default] + } + ] + }, + %{ + group: :pleroma, + key: Pleroma.User, + type: :group, + description: "", + children: [ + %{ + key: :restricted_nicknames, + type: {:list, :string}, + description: "", + 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" + ] + ] + } + ] + }, + %{ + group: :cors_plug, + type: :group, + description: "", + children: [ + %{ + key: :max_age, + type: :integer, + description: "", + suggestions: [86_400] + }, + %{ + key: :methods, + type: {:list, :string}, + description: "", + suggestions: [["POST", "PUT", "DELETE", "GET", "PATCH", "OPTIONS"]] + }, + %{ + key: :expose, + type: :string, + description: "", + suggestions: [ + [ + "Link", + "X-RateLimit-Reset", + "X-RateLimit-Limit", + "X-RateLimit-Remaining", + "X-Request-Id", + "Idempotency-Key" + ] + ] + }, + %{ + key: :credentials, + type: :boolean, + description: "", + suggestions: [true, false] + }, + %{ + key: :headers, + type: {:list, :string}, + description: "", + suggestions: [["Authorization", "Content-Type", "Idempotency-Key"]] + } + ] + } +] From 67e430093187c50f307810e88ed0e73afe825b75 Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 13:21:48 +0300 Subject: [PATCH 06/34] description formatters --- lib/pleroma/docs/formatter.ex | 69 +++++++++++++++++++++++++++++++++++ lib/pleroma/docs/json.ex | 15 ++++++++ lib/pleroma/docs/markdown.ex | 67 ++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 lib/pleroma/docs/formatter.ex create mode 100644 lib/pleroma/docs/json.ex create mode 100644 lib/pleroma/docs/markdown.ex diff --git a/lib/pleroma/docs/formatter.ex b/lib/pleroma/docs/formatter.ex new file mode 100644 index 000000000..a1c757936 --- /dev/null +++ b/lib/pleroma/docs/formatter.ex @@ -0,0 +1,69 @@ +defmodule Pleroma.Docs.Formatter do + @callback process(keyword()) :: {:ok, String.t()} + + @spec process(module(), keyword()) :: {:ok, String.t()} + def process(implementation, descriptions) do + implementation.process(descriptions) + end + + def uploaders_list do + {:ok, modules} = :application.get_key(:pleroma, :modules) + + Enum.filter(modules, fn module -> + name_as_list = Module.split(module) + + List.starts_with?(name_as_list, ["Pleroma", "Uploaders"]) and + List.last(name_as_list) in ["S3", "Local", "MDII"] + end) + end + + def filters_list do + {:ok, modules} = :application.get_key(:pleroma, :modules) + + Enum.filter(modules, fn module -> + name_as_list = Module.split(module) + + List.starts_with?(name_as_list, ["Pleroma", "Upload", "Filter"]) + end) + end + + def mrf_list do + {:ok, modules} = :application.get_key(:pleroma, :modules) + + Enum.filter(modules, fn module -> + name_as_list = Module.split(module) + + List.starts_with?(name_as_list, ["Pleroma", "Web", "ActivityPub", "MRF"]) and + length(name_as_list) > 4 + end) + end + + def richmedia_parsers do + {:ok, modules} = :application.get_key(:pleroma, :modules) + + Enum.filter(modules, fn module -> + name_as_list = Module.split(module) + + List.starts_with?(name_as_list, ["Pleroma", "Web", "RichMedia", "Parsers"]) and + length(name_as_list) == 5 + end) + end +end + +defimpl Jason.Encoder, for: Tuple do + def encode(tuple, opts) do + Jason.Encode.list(Tuple.to_list(tuple), opts) + end +end + +defimpl Jason.Encoder, for: [Regex, Function] do + def encode(term, opts) do + Jason.Encode.string(inspect(term), opts) + end +end + +defimpl String.Chars, for: Regex do + def to_string(term) do + inspect(term) + end +end diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex new file mode 100644 index 000000000..38f015017 --- /dev/null +++ b/lib/pleroma/docs/json.ex @@ -0,0 +1,15 @@ +defmodule Pleroma.Docs.JSON do + @behaviour Pleroma.Docs.Formatter + def process(descriptions) do + config_path = "docs/generate_config.json" + {:ok, file} = File.open(config_path, [:write]) + json = generate_json(descriptions) + IO.write(file, json) + :ok = File.close(file) + {:ok, config_path} + end + + def generate_json(descriptions) do + Jason.encode!(descriptions) + end +end diff --git a/lib/pleroma/docs/markdown.ex b/lib/pleroma/docs/markdown.ex new file mode 100644 index 000000000..27a096631 --- /dev/null +++ b/lib/pleroma/docs/markdown.ex @@ -0,0 +1,67 @@ +defmodule Pleroma.Docs.Markdown do + @behaviour Pleroma.Docs.Formatter + + def process(descriptions) do + config_path = "docs/config.md" + {:ok, file} = File.open(config_path, [:write]) + IO.write(file, "# Generated configuration\r\n\r\n") + IO.write(file, "Date of generation: #{Date.utc_today()}\r\n\r\n") + + IO.write( + file, + "This file describe the configuration, it is recommended to edit the relevant *.secret.exs file instead of the others founds in the ``config`` directory. +If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``.\r\n\r\n" + ) + + for group <- descriptions do + if is_nil(group[:key]) do + IO.write(file, "## #{inspect(group[:group])}\r\n\r\n") + else + IO.write(file, "## #{inspect(group[:key])}\r\n\r\n") + end + + IO.write(file, "Type: `#{group[:type]}` \r\n") + IO.write(file, "#{group[:description]} \r\n\r\n") + + for child <- group[:children] do + print_child_header(file, child) + + print_suggestions(file, child[:suggestions]) + + if child[:children] do + for subchild <- child[:children] do + print_child_header(file, subchild) + + print_suggestions(file, subchild[:suggestions]) + end + end + end + + IO.write(file, "\r\n") + end + + :ok = File.close(file) + {:ok, config_path} + end + + defp print_suggestion(file, suggestion) when is_function(suggestion) do + IO.write(file, " `#{inspect(suggestion.())}`\r\n") + end + + defp print_suggestion(file, suggestion) do + IO.write(file, " `#{inspect(suggestion)}`\r\n") + end + + defp print_suggestions(file, suggestions) do + IO.write(file, "Suggestions: \r\n") + + for suggestion <- suggestions do + print_suggestion(file, suggestion) + end + end + + defp print_child_header(file, child) do + IO.write(file, "* `#{inspect(child[:key])}`: #{child[:description]} \r\n") + IO.write(file, "Type: `#{inspect(child[:type])}` \r\n") + end +end From 511d93fa5486a58b63576ce1b8af551bbafe703f Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 13:22:21 +0300 Subject: [PATCH 07/34] mix docs generates config.md --- lib/mix/tasks/pleroma/docs.ex | 40 +++++++++++++++++++++++++++++++++++ mix.exs | 3 ++- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 lib/mix/tasks/pleroma/docs.ex diff --git a/lib/mix/tasks/pleroma/docs.ex b/lib/mix/tasks/pleroma/docs.ex new file mode 100644 index 000000000..5ae3b8716 --- /dev/null +++ b/lib/mix/tasks/pleroma/docs.ex @@ -0,0 +1,40 @@ +defmodule Mix.Tasks.Pleroma.Docs do + use Mix.Task + import Mix.Pleroma + + @shortdoc "Generates docs from descriptions.exs" + @moduledoc """ + Generates docs from `descriptions.exs`. + + Supports two formats: `markdown` and `json`. + + ## Generate markdown docs + + `mix pleroma.docs` + + ## Generate json docs + + `mix pleroma.docs json`s + """ + + def run(["json"]) do + do_run(Pleroma.Docs.JSON) + end + + def run(_) do + do_run(Pleroma.Docs.Markdown) + end + + defp do_run(implementation) do + start_pleroma() + descriptions = Config.Reader.read!("config/description.exs") + + {:ok, file_path} = + Pleroma.Docs.Formatter.process( + implementation, + descriptions[:pleroma][:config_description] + ) + + Mix.shell().info([:green, "Markdown docs successfully generated to #{file_path}."]) + end +end diff --git a/mix.exs b/mix.exs index 3170d6f2d..c52ab08b0 100644 --- a/mix.exs +++ b/mix.exs @@ -172,7 +172,8 @@ defp aliases do "ecto.rollback": ["pleroma.ecto.rollback"], "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], "ecto.reset": ["ecto.drop", "ecto.setup"], - test: ["ecto.create --quiet", "ecto.migrate", "test"] + test: ["ecto.create --quiet", "ecto.migrate", "test"], + docs: ["pleroma.docs", "docs"] ] end From a1f2dfb10a777592ea85d4bf8c5f91c859ec225b Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 14:04:21 +0300 Subject: [PATCH 08/34] expanding regex sigils to use modifiers --- lib/pleroma/web/admin_api/config.ex | 15 +++++++++--- .../admin_api/admin_api_controller_test.exs | 12 ++++++++-- test/web/admin_api/config_test.exs | 24 +++++++++++++++++++ 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/web/admin_api/config.ex b/lib/pleroma/web/admin_api/config.ex index a10cc779b..1917a5580 100644 --- a/lib/pleroma/web/admin_api/config.ex +++ b/lib/pleroma/web/admin_api/config.ex @@ -90,6 +90,8 @@ defp do_convert(entity) when is_list(entity) do for v <- entity, into: [], do: do_convert(v) end + defp do_convert(%Regex{} = entity), do: inspect(entity) + defp do_convert(entity) when is_map(entity) do for {k, v} <- entity, into: %{}, do: {do_convert(k), do_convert(v)} end @@ -122,7 +124,7 @@ def transform(entity) when is_binary(entity) or is_map(entity) or is_list(entity def transform(entity), do: :erlang.term_to_binary(entity) - defp do_transform(%Regex{} = entity) when is_map(entity), do: entity + defp do_transform(%Regex{} = entity), do: entity defp do_transform(%{"tuple" => [":dispatch", [entity]]}) do {dispatch_settings, []} = do_eval(entity) @@ -154,8 +156,15 @@ defp do_transform(entity) when is_binary(entity) do defp do_transform(entity), do: entity defp do_transform_string("~r/" <> pattern) do - pattern = String.trim_trailing(pattern, "/") - ~r/#{pattern}/ + modificator = String.split(pattern, "/") |> List.last() + pattern = String.trim_trailing(pattern, "/" <> modificator) + + case modificator do + "" -> ~r/#{pattern}/ + "i" -> ~r/#{pattern}/i + "u" -> ~r/#{pattern}/u + "s" -> ~r/#{pattern}/s + end end defp do_transform_string(":" <> atom), do: String.to_atom(atom) diff --git a/test/web/admin_api/admin_api_controller_test.exs b/test/web/admin_api/admin_api_controller_test.exs index 4e2c27431..3b6d75a4c 100644 --- a/test/web/admin_api/admin_api_controller_test.exs +++ b/test/web/admin_api/admin_api_controller_test.exs @@ -1779,7 +1779,11 @@ test "common config example", %{conn: conn} do %{"tuple" => [":seconds_valid", 60]}, %{"tuple" => [":path", ""]}, %{"tuple" => [":key1", nil]}, - %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]} + %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, + %{"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"]} ] } ] @@ -1796,7 +1800,11 @@ test "common config example", %{conn: conn} do %{"tuple" => [":seconds_valid", 60]}, %{"tuple" => [":path", ""]}, %{"tuple" => [":key1", nil]}, - %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]} + %{"tuple" => [":partial_chain", "&:hackney_connect.partial_chain/1"]}, + %{"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"]} ] } ] diff --git a/test/web/admin_api/config_test.exs b/test/web/admin_api/config_test.exs index 3190dc1c8..204446b79 100644 --- a/test/web/admin_api/config_test.exs +++ b/test/web/admin_api/config_test.exs @@ -103,6 +103,30 @@ test "sigil" do assert Config.from_binary(binary) == ~r/comp[lL][aA][iI][nN]er/ end + test "link sigil" do + binary = Config.transform("~r/https:\/\/example.com/") + assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/) + 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 + end + + test "link sigil with i modifier" do + binary = Config.transform("~r/https:\/\/example.com/i") + assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/i) + assert Config.from_binary(binary) == ~r/https:\/\/example.com/i + end + + test "link sigil with s modifier" do + binary = Config.transform("~r/https:\/\/example.com/s") + assert binary == :erlang.term_to_binary(~r/https:\/\/example.com/s) + assert Config.from_binary(binary) == ~r/https:\/\/example.com/s + end + test "2 child tuple" do binary = Config.transform(%{"tuple" => ["v1", ":v2"]}) assert binary == :erlang.term_to_binary({"v1", :v2}) From 68e45a327f7dbe0fdc98878202d6a72d8856b96b Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 14:09:07 +0300 Subject: [PATCH 09/34] changelog --- CHANGELOG.md | 3 ++- mix.lock | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7f1aee0e..3d9c6a4b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,11 +20,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - **Breaking:** `/api/pleroma/notifications/read` is moved to `/api/v1/pleroma/notifications/read` and now supports `max_id` and responds with Mastodon API entities. - Configuration: OpenGraph and TwitterCard providers enabled by default - Configuration: Filter.AnonymizeFilename added ability to retain file extension with custom text -- Mastodon API: `pleroma.thread_muted` key in the Status entity +- Configuration: added `config/description.exs`, from which `docs/config.md` is generated - Federation: Return 403 errors when trying to request pages from a user's follower/following collections if they have `hide_followers`/`hide_follows` set - NodeInfo: Return `skipThreadContainment` in `metadata` for the `skip_thread_containment` option - NodeInfo: Return `mailerEnabled` in `metadata` - Mastodon API: Unsubscribe followers when they unfollow a user +- Mastodon API: `pleroma.thread_muted` key in the Status entity - AdminAPI: Add "godmode" while fetching user statuses (i.e. admin can see private statuses) - Improve digest email template – Pagination: (optional) return `total` alongside with `items` when paginating diff --git a/mix.lock b/mix.lock index 2639e96e9..07bc8d488 100644 --- a/mix.lock +++ b/mix.lock @@ -20,7 +20,7 @@ "db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"}, "decimal": {:hex, :decimal, "1.8.0", "ca462e0d885f09a1c5a342dbd7c1dcf27ea63548c65a65e67334f4b61803822e", [:mix], [], "hexpm"}, "deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm"}, - "earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"}, + "earmark": {:hex, :earmark, "1.3.6", "ce1d0675e10a5bb46b007549362bd3f5f08908843957687d8484fe7f37466b19", [:mix], [], "hexpm"}, "ecto": {:hex, :ecto, "3.1.4", "69d852da7a9f04ede725855a35ede48d158ca11a404fe94f8b2fb3b2162cd3c9", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"}, "ecto_sql": {:hex, :ecto_sql, "3.1.3", "2c536139190492d9de33c5fefac7323c5eaaa82e1b9bf93482a14649042f7cd9", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, "esshd": {:hex, :esshd, "0.1.0", "6f93a2062adb43637edad0ea7357db2702a4b80dd9683482fe00f5134e97f4c1", [:mix], [], "hexpm"}, @@ -46,8 +46,8 @@ "jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"}, "joken": {:hex, :joken, "2.0.1", "ec9ab31bf660f343380da033b3316855197c8d4c6ef597fa3fcb451b326beb14", [:mix], [{:jose, "~> 1.9", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm"}, "jose": {:hex, :jose, "1.9.0", "4167c5f6d06ffaebffd15cdb8da61a108445ef5e85ab8f5a7ad926fdf3ada154", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup": {:hex, :makeup, "1.0.0", "671df94cf5a594b739ce03b0d0316aa64312cee2574b6a44becb83cd90fb05dc", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.14.0", "cf8b7c66ad1cff4c14679698d532f0b5d45a3968ffbcbfd590339cb57742f1ae", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"}, "meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"}, "mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"}, @@ -56,7 +56,7 @@ "mock": {:hex, :mock, "0.3.3", "42a433794b1291a9cf1525c6d26b38e039e0d3a360732b5e467bfc77ef26c914", [:mix], [{:meck, "~> 0.8.13", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm"}, "mogrify": {:hex, :mogrify, "0.6.1", "de1b527514f2d95a7bbe9642eb556061afb337e220cf97adbf3a4e6438ed70af", [:mix], [], "hexpm"}, "mox": {:hex, :mox, "0.5.1", "f86bb36026aac1e6f924a4b6d024b05e9adbed5c63e8daa069bd66fb3292165b", [:mix], [], "hexpm"}, - "nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"}, + "nimble_parsec": {:hex, :nimble_parsec, "0.5.1", "c90796ecee0289dbb5ad16d3ad06f957b0cd1199769641c961cfe0b97db190e0", [:mix], [], "hexpm"}, "parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"}, "pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.3", "6706a148809a29c306062862c803406e88f048277f6e85b68faf73291e820b84", [:mix], [], "hexpm"}, "phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"}, From 0559c82bdb39cba019099d404723f38fed6e2aba Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 14:27:55 +0300 Subject: [PATCH 10/34] fix --- lib/mix/tasks/pleroma/docs.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mix/tasks/pleroma/docs.ex b/lib/mix/tasks/pleroma/docs.ex index 5ae3b8716..d68e02383 100644 --- a/lib/mix/tasks/pleroma/docs.ex +++ b/lib/mix/tasks/pleroma/docs.ex @@ -27,7 +27,7 @@ def run(_) do defp do_run(implementation) do start_pleroma() - descriptions = Config.Reader.read!("config/description.exs") + {descriptions, _paths} = Mix.Config.eval!("config/description.exs") {:ok, file_path} = Pleroma.Docs.Formatter.process( From 6721301086674afe721f9eea478a2037756be93c Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 19:14:01 +0300 Subject: [PATCH 11/34] some changes --- config/description.exs | 15 ++++---- lib/mix/tasks/pleroma/docs.ex | 6 +-- .../docs/{formatter.ex => generator.ex} | 6 ++- lib/pleroma/docs/json.ex | 5 ++- lib/pleroma/docs/markdown.ex | 38 ++++++++++++------- 5 files changed, 44 insertions(+), 26 deletions(-) rename lib/pleroma/docs/{formatter.ex => generator.ex} (90%) diff --git a/config/description.exs b/config/description.exs index 537b9d996..684d3fc0e 100644 --- a/config/description.exs +++ b/config/description.exs @@ -1,5 +1,5 @@ use Mix.Config -alias Pleroma.Docs.Formatter +alias Pleroma.Docs.Generator websocket_config = [ path: "/websocket", @@ -24,7 +24,7 @@ type: :module, description: "Module which will be used for uploads", suggestions: [ - Formatter.uploaders_list() + Generator.uploaders_list() ] }, %{ @@ -32,7 +32,7 @@ type: {:list, :module}, description: "List of filter modules for uploads", suggestions: [ - Formatter.filters_list() + Generator.filters_list() ] }, %{ @@ -64,8 +64,7 @@ %{ key: :proxy_opts, type: :keyword, - description: "Proxy options, see `Pleroma.ReverseProxy` documentation", - suggestions: ["somehow created link to Pleroma.ReverseProxy options"] + description: "Proxy options, see `Pleroma.ReverseProxy` documentation" } ] }, @@ -93,7 +92,7 @@ children: [ %{ key: :bucket, - type: :strings, + type: :string, description: "S3 bucket", suggestions: [ "bucket" @@ -629,7 +628,7 @@ description: "A list of MRF policies enabled", suggestions: [ Pleroma.Web.ActivityPub.MRF.NoOpPolicy, - Formatter.mrf_list() + Generator.mrf_list() ] }, %{ @@ -1920,7 +1919,7 @@ type: {:list, :module}, description: "list of Rich Media parsers", suggestions: [ - Formatter.richmedia_parsers() + Generator.richmedia_parsers() ] }, %{ diff --git a/lib/mix/tasks/pleroma/docs.ex b/lib/mix/tasks/pleroma/docs.ex index d68e02383..821ee74f9 100644 --- a/lib/mix/tasks/pleroma/docs.ex +++ b/lib/mix/tasks/pleroma/docs.ex @@ -8,11 +8,11 @@ defmodule Mix.Tasks.Pleroma.Docs do Supports two formats: `markdown` and `json`. - ## Generate markdown docs + ## Generate Markdown docs `mix pleroma.docs` - ## Generate json docs + ## Generate JSON docs `mix pleroma.docs json`s """ @@ -30,7 +30,7 @@ defp do_run(implementation) do {descriptions, _paths} = Mix.Config.eval!("config/description.exs") {:ok, file_path} = - Pleroma.Docs.Formatter.process( + Pleroma.Docs.Generator.process( implementation, descriptions[:pleroma][:config_description] ) diff --git a/lib/pleroma/docs/formatter.ex b/lib/pleroma/docs/generator.ex similarity index 90% rename from lib/pleroma/docs/formatter.ex rename to lib/pleroma/docs/generator.ex index a1c757936..e788712cc 100644 --- a/lib/pleroma/docs/formatter.ex +++ b/lib/pleroma/docs/generator.ex @@ -1,4 +1,4 @@ -defmodule Pleroma.Docs.Formatter do +defmodule Pleroma.Docs.Generator do @callback process(keyword()) :: {:ok, String.t()} @spec process(module(), keyword()) :: {:ok, String.t()} @@ -6,6 +6,7 @@ def process(implementation, descriptions) do implementation.process(descriptions) end + @spec uploaders_list() :: [module()] def uploaders_list do {:ok, modules} = :application.get_key(:pleroma, :modules) @@ -17,6 +18,7 @@ def uploaders_list do end) end + @spec filters_list() :: [module()] def filters_list do {:ok, modules} = :application.get_key(:pleroma, :modules) @@ -27,6 +29,7 @@ def filters_list do end) end + @spec mrf_list() :: [module()] def mrf_list do {:ok, modules} = :application.get_key(:pleroma, :modules) @@ -38,6 +41,7 @@ def mrf_list do end) end + @spec richmedia_parsers() :: [module()] def richmedia_parsers do {:ok, modules} = :application.get_key(:pleroma, :modules) diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex index 38f015017..aed730e78 100644 --- a/lib/pleroma/docs/json.ex +++ b/lib/pleroma/docs/json.ex @@ -1,5 +1,7 @@ defmodule Pleroma.Docs.JSON do - @behaviour Pleroma.Docs.Formatter + @behaviour Pleroma.Docs.Generator + + @spec process(keyword()) :: {:ok, String.t()} def process(descriptions) do config_path = "docs/generate_config.json" {:ok, file} = File.open(config_path, [:write]) @@ -9,6 +11,7 @@ def process(descriptions) do {:ok, config_path} end + @spec generate_json([keyword()]) :: String.t() def generate_json(descriptions) do Jason.encode!(descriptions) end diff --git a/lib/pleroma/docs/markdown.ex b/lib/pleroma/docs/markdown.ex index 27a096631..c66640bf1 100644 --- a/lib/pleroma/docs/markdown.ex +++ b/lib/pleroma/docs/markdown.ex @@ -1,16 +1,17 @@ defmodule Pleroma.Docs.Markdown do - @behaviour Pleroma.Docs.Formatter + @behaviour Pleroma.Docs.Generator + @spec process(keyword()) :: {:ok, String.t()} def process(descriptions) do config_path = "docs/config.md" {:ok, file} = File.open(config_path, [:write]) - IO.write(file, "# Generated configuration\r\n\r\n") + IO.write(file, "# Configuration\r\n\r\n") IO.write(file, "Date of generation: #{Date.utc_today()}\r\n\r\n") IO.write( file, - "This file describe the configuration, it is recommended to edit the relevant *.secret.exs file instead of the others founds in the ``config`` directory. -If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``.\r\n\r\n" + "This file describe the configuration, it is recommended to edit the relevant `*.secret.exs` file instead of the others founds in the ``config`` directory. \r\n + If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``.\r\n\r\n" ) for group <- descriptions do @@ -20,7 +21,6 @@ def process(descriptions) do IO.write(file, "## #{inspect(group[:key])}\r\n\r\n") end - IO.write(file, "Type: `#{group[:type]}` \r\n") IO.write(file, "#{group[:description]} \r\n\r\n") for child <- group[:children] do @@ -44,24 +44,36 @@ def process(descriptions) do {:ok, config_path} end + defp print_suggestion(file, suggestion) when is_list(suggestion) do + IO.write(file, " `#{inspect(suggestion)}`\r\n") + end + defp print_suggestion(file, suggestion) when is_function(suggestion) do IO.write(file, " `#{inspect(suggestion.())}`\r\n") end - defp print_suggestion(file, suggestion) do - IO.write(file, " `#{inspect(suggestion)}`\r\n") + defp print_suggestion(file, suggestion, as_list \\ false) do + list_mark = if as_list, do: "*", else: "" + IO.write(file, " #{list_mark} `#{inspect(suggestion)}`\r\n") end - defp print_suggestions(file, suggestions) do - IO.write(file, "Suggestions: \r\n") + defp print_suggestions(_file, nil), do: nil - for suggestion <- suggestions do - print_suggestion(file, suggestion) + defp print_suggestions(file, suggestions) do + IO.write(file, " Suggestions: \r\n") + + if length(suggestions) > 1 do + for suggestion <- suggestions do + print_suggestion(file, suggestion, true) + end + else + print_suggestion(file, List.first(suggestions)) end end defp print_child_header(file, child) do - IO.write(file, "* `#{inspect(child[:key])}`: #{child[:description]} \r\n") - IO.write(file, "Type: `#{inspect(child[:type])}` \r\n") + IO.write(file, "* `#{inspect(child[:key])}` \r\n") + IO.write(file, " #{child[:description]} \r\n") + IO.write(file, " Type: `#{inspect(child[:type])}` \r\n") end end From 8f5ee7db06e379e44505744fd21e59cc46432ac3 Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 19:59:13 +0300 Subject: [PATCH 12/34] typo fix --- lib/mix/tasks/pleroma/docs.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mix/tasks/pleroma/docs.ex b/lib/mix/tasks/pleroma/docs.ex index 821ee74f9..fb8a2a014 100644 --- a/lib/mix/tasks/pleroma/docs.ex +++ b/lib/mix/tasks/pleroma/docs.ex @@ -14,7 +14,7 @@ defmodule Mix.Tasks.Pleroma.Docs do ## Generate JSON docs - `mix pleroma.docs json`s + `mix pleroma.docs json` """ def run(["json"]) do From 0624e06a9c981264f238041a2ad22392d4a8f792 Mon Sep 17 00:00:00 2001 From: Alex S Date: Fri, 30 Aug 2019 20:14:18 +0300 Subject: [PATCH 13/34] little fix --- lib/pleroma/docs/markdown.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/docs/markdown.ex b/lib/pleroma/docs/markdown.ex index c66640bf1..797ce73bf 100644 --- a/lib/pleroma/docs/markdown.ex +++ b/lib/pleroma/docs/markdown.ex @@ -10,8 +10,8 @@ def process(descriptions) do IO.write( file, - "This file describe the configuration, it is recommended to edit the relevant `*.secret.exs` file instead of the others founds in the ``config`` directory. \r\n - If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``.\r\n\r\n" + "This file describe the configuration, it is recommended to edit the relevant `*.secret.exs` file instead of the others founds in the ``config`` directory. \r\n\r\n" <> + " If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``.\r\n\r\n" ) for group <- descriptions do From 65bc9e66ad3848594df67c4b0d5539b1c8540161 Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Tue, 3 Sep 2019 06:45:02 +0000 Subject: [PATCH 14/34] Apply suggestion to config/description.exs --- config/description.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/description.exs b/config/description.exs index 684d3fc0e..48cc70e6e 100644 --- a/config/description.exs +++ b/config/description.exs @@ -38,7 +38,7 @@ %{ key: :link_name, type: :boolean, - description: "If enabled Pleroma will add name parameter to the url off the upload", + description: "If enabled, a name parameter will be added to the url of the upload. For example `https://instance.tld/media/imagehash.png?name=realname.png`", suggestions: [ true, false From 5db2920644d8a82e53eaef228e39edef1e6af5aa Mon Sep 17 00:00:00 2001 From: Alexander Strizhakov Date: Tue, 3 Sep 2019 06:45:16 +0000 Subject: [PATCH 15/34] Apply suggestion to config/description.exs --- config/description.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/description.exs b/config/description.exs index 48cc70e6e..37a8a5664 100644 --- a/config/description.exs +++ b/config/description.exs @@ -55,7 +55,7 @@ %{ key: :proxy_remote, type: :boolean, - description: "If enabled, Pleroma will proxy media requests instead of redirecting to it", + description: "If enabled, requests to media stored using a remote uploader will be proxied instead of being redirected.", suggestions: [ true, false From 35757b6d0eb7b59d511bfea6a166683e18d6aa97 Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 3 Sep 2019 09:45:54 +0300 Subject: [PATCH 16/34] don't add behaviour to suggestions --- lib/pleroma/docs/generator.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pleroma/docs/generator.ex b/lib/pleroma/docs/generator.ex index e788712cc..aa578eee2 100644 --- a/lib/pleroma/docs/generator.ex +++ b/lib/pleroma/docs/generator.ex @@ -14,7 +14,7 @@ def uploaders_list do name_as_list = Module.split(module) List.starts_with?(name_as_list, ["Pleroma", "Uploaders"]) and - List.last(name_as_list) in ["S3", "Local", "MDII"] + List.last(name_as_list) != "Uploader" end) end From 10827eecad5ef521dff3673c0891cbec2c8bb44b Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 3 Sep 2019 11:56:21 +0300 Subject: [PATCH 17/34] formatting --- config/description.exs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/description.exs b/config/description.exs index 37a8a5664..a56a95089 100644 --- a/config/description.exs +++ b/config/description.exs @@ -38,7 +38,8 @@ %{ key: :link_name, type: :boolean, - description: "If enabled, a name parameter will be added to the url of the upload. For example `https://instance.tld/media/imagehash.png?name=realname.png`", + description: + "If enabled, a name parameter will be added to the url of the upload. For example `https://instance.tld/media/imagehash.png?name=realname.png`", suggestions: [ true, false @@ -55,7 +56,8 @@ %{ key: :proxy_remote, type: :boolean, - description: "If enabled, requests to media stored using a remote uploader will be proxied instead of being redirected.", + description: + "If enabled, requests to media stored using a remote uploader will be proxied instead of being redirected.", suggestions: [ true, false From 6b0e8b73df32ce926551713972d9d6af484ccc2e Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 3 Sep 2019 12:30:44 +0300 Subject: [PATCH 18/34] bump ex_doc version --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index c52ab08b0..96ef723b2 100644 --- a/mix.exs +++ b/mix.exs @@ -125,7 +125,7 @@ defp deps do {:crypt, git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"}, {:cors_plug, "~> 1.5"}, - {:ex_doc, "~> 0.20.2", only: :dev, runtime: false}, + {:ex_doc, "~> 0.21", only: :dev, runtime: false}, {:web_push_encryption, "~> 0.2.1"}, {:swoosh, "~> 0.23.2"}, {:phoenix_swoosh, "~> 0.2"}, diff --git a/mix.lock b/mix.lock index 07bc8d488..5762dae4f 100644 --- a/mix.lock +++ b/mix.lock @@ -29,7 +29,7 @@ "ex_aws": {:hex, :ex_aws, "2.1.0", "b92651527d6c09c479f9013caa9c7331f19cba38a650590d82ebf2c6c16a1d8a", [:mix], [{:configparser_ex, "~> 2.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "1.6.3 or 1.6.5 or 1.7.1 or 1.8.6 or ~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8", [hex: :jsx, repo: "hexpm", optional: true]}, {:poison, ">= 1.2.0", [hex: :poison, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.6", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:xml_builder, "~> 0.1.0", [hex: :xml_builder, repo: "hexpm", optional: true]}], "hexpm"}, "ex_aws_s3": {:hex, :ex_aws_s3, "2.0.1", "9e09366e77f25d3d88c5393824e613344631be8db0d1839faca49686e99b6704", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm"}, "ex_const": {:hex, :ex_const, "0.2.4", "d06e540c9d834865b012a17407761455efa71d0ce91e5831e86881b9c9d82448", [:mix], [], "hexpm"}, - "ex_doc": {:hex, :ex_doc, "0.20.2", "1bd0dfb0304bade58beb77f20f21ee3558cc3c753743ae0ddbb0fd7ba2912331", [:mix], [{:earmark, "~> 1.3", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, + "ex_doc": {:hex, :ex_doc, "0.21.2", "caca5bc28ed7b3bdc0b662f8afe2bee1eedb5c3cf7b322feeeb7c6ebbde089d6", [:mix], [{:earmark, "~> 1.3.3 or ~> 1.4", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"}, "ex_machina": {:hex, :ex_machina, "2.3.0", "92a5ad0a8b10ea6314b876a99c8c9e3f25f4dde71a2a835845b136b9adaf199a", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm"}, "ex_rated": {:hex, :ex_rated, "1.3.3", "30ecbdabe91f7eaa9d37fa4e81c85ba420f371babeb9d1910adbcd79ec798d27", [:mix], [{:ex2ms, "~> 1.5", [hex: :ex2ms, repo: "hexpm", optional: false]}], "hexpm"}, "ex_syslogger": {:git, "https://github.com/slashmili/ex_syslogger.git", "f3963399047af17e038897c69e20d552e6899e1d", [tag: "1.4.0"]}, From 57dc59d98d0ae5b4315bbc5c8a9c7ca59f6341f9 Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 3 Sep 2019 12:31:43 +0300 Subject: [PATCH 19/34] little fix --- lib/mix/tasks/pleroma/docs.ex | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/mix/tasks/pleroma/docs.ex b/lib/mix/tasks/pleroma/docs.ex index fb8a2a014..4be53ce75 100644 --- a/lib/mix/tasks/pleroma/docs.ex +++ b/lib/mix/tasks/pleroma/docs.ex @@ -35,6 +35,8 @@ defp do_run(implementation) do descriptions[:pleroma][:config_description] ) - Mix.shell().info([:green, "Markdown docs successfully generated to #{file_path}."]) + type = if implementation == Pleroma.Docs.Markdown, do: "Markdown", else: "JSON" + + Mix.shell().info([:green, "#{type} docs successfully generated to #{file_path}."]) end end From aa7fb22008abab1a48a5dac56a32fc9e37d04f88 Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 3 Sep 2019 12:32:00 +0300 Subject: [PATCH 20/34] placeholder for config.md --- docs/config.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/config.md diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 000000000..7f54a34b1 --- /dev/null +++ b/docs/config.md @@ -0,0 +1 @@ +This file is a placeholder, please run mix pleroma.docs to generate it. From 5ff12e7df10dd70f70e5929ede5dc7570e066c57 Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 3 Sep 2019 19:22:25 +0300 Subject: [PATCH 21/34] some changes --- lib/pleroma/docs/markdown.ex | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/lib/pleroma/docs/markdown.ex b/lib/pleroma/docs/markdown.ex index 797ce73bf..24930cc9f 100644 --- a/lib/pleroma/docs/markdown.ex +++ b/lib/pleroma/docs/markdown.ex @@ -4,24 +4,24 @@ defmodule Pleroma.Docs.Markdown do @spec process(keyword()) :: {:ok, String.t()} def process(descriptions) do config_path = "docs/config.md" - {:ok, file} = File.open(config_path, [:write]) - IO.write(file, "# Configuration\r\n\r\n") - IO.write(file, "Date of generation: #{Date.utc_today()}\r\n\r\n") + {:ok, file} = File.open(config_path, [:utf8, :write]) + IO.write(file, "# Configuration\n") + IO.write(file, "Date of generation: #{Date.utc_today()}\n\n") IO.write( file, - "This file describe the configuration, it is recommended to edit the relevant `*.secret.exs` file instead of the others founds in the ``config`` directory. \r\n\r\n" <> - " If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``.\r\n\r\n" + "This file describe the configuration, it is recommended to edit the relevant `*.secret.exs` file instead of the others founds in the ``config`` directory.\n\n" <> + "If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``.\n\n" ) for group <- descriptions do if is_nil(group[:key]) do - IO.write(file, "## #{inspect(group[:group])}\r\n\r\n") + IO.write(file, "## #{inspect(group[:group])}\n") else - IO.write(file, "## #{inspect(group[:key])}\r\n\r\n") + IO.write(file, "## #{inspect(group[:key])}\n") end - IO.write(file, "#{group[:description]} \r\n\r\n") + IO.write(file, "#{group[:description]}\n") for child <- group[:children] do print_child_header(file, child) @@ -37,7 +37,7 @@ def process(descriptions) do end end - IO.write(file, "\r\n") + IO.write(file, "\n") end :ok = File.close(file) @@ -45,22 +45,22 @@ def process(descriptions) do end defp print_suggestion(file, suggestion) when is_list(suggestion) do - IO.write(file, " `#{inspect(suggestion)}`\r\n") + IO.write(file, " `#{inspect(suggestion)}`\n") end defp print_suggestion(file, suggestion) when is_function(suggestion) do - IO.write(file, " `#{inspect(suggestion.())}`\r\n") + IO.write(file, " `#{inspect(suggestion.())}`\n") end defp print_suggestion(file, suggestion, as_list \\ false) do - list_mark = if as_list, do: "*", else: "" - IO.write(file, " #{list_mark} `#{inspect(suggestion)}`\r\n") + list_mark = if as_list, do: "- ", else: "" + IO.write(file, " #{list_mark}`#{inspect(suggestion)}`\n") end defp print_suggestions(_file, nil), do: nil defp print_suggestions(file, suggestions) do - IO.write(file, " Suggestions: \r\n") + IO.write(file, "Suggestions:\n") if length(suggestions) > 1 do for suggestion <- suggestions do @@ -72,8 +72,7 @@ defp print_suggestions(file, suggestions) do end defp print_child_header(file, child) do - IO.write(file, "* `#{inspect(child[:key])}` \r\n") - IO.write(file, " #{child[:description]} \r\n") - IO.write(file, " Type: `#{inspect(child[:type])}` \r\n") + IO.write(file, "- `#{inspect(child[:key])}` -`#{inspect(child[:type])}` \n") + IO.write(file, "#{child[:description]} \n") end end From be32d90a0cdee77f4cd6ecbbbade1748c88637b8 Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 3 Sep 2019 20:06:22 +0300 Subject: [PATCH 22/34] little refactor --- lib/pleroma/docs/json.ex | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex index aed730e78..18ba01d58 100644 --- a/lib/pleroma/docs/json.ex +++ b/lib/pleroma/docs/json.ex @@ -4,11 +4,13 @@ defmodule Pleroma.Docs.JSON do @spec process(keyword()) :: {:ok, String.t()} def process(descriptions) do config_path = "docs/generate_config.json" - {:ok, file} = File.open(config_path, [:write]) - json = generate_json(descriptions) - IO.write(file, json) - :ok = File.close(file) - {:ok, config_path} + + with {:ok, file} <- File.open(config_path, [:write]), + json <- generate_json(descriptions), + :ok <- IO.write(file, json), + :ok <- File.close(file) do + {:ok, config_path} + end end @spec generate_json([keyword()]) :: String.t() From 38b29779c3372741ce1d7b652922058ad2fda79c Mon Sep 17 00:00:00 2001 From: Alex S Date: Tue, 3 Sep 2019 20:11:32 +0300 Subject: [PATCH 23/34] refactoring --- lib/mix/tasks/pleroma/docs.ex | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/mix/tasks/pleroma/docs.ex b/lib/mix/tasks/pleroma/docs.ex index 4be53ce75..0d2663648 100644 --- a/lib/mix/tasks/pleroma/docs.ex +++ b/lib/mix/tasks/pleroma/docs.ex @@ -27,16 +27,16 @@ def run(_) do defp do_run(implementation) do start_pleroma() - {descriptions, _paths} = Mix.Config.eval!("config/description.exs") - {:ok, file_path} = - Pleroma.Docs.Generator.process( - implementation, - descriptions[:pleroma][:config_description] - ) + with {descriptions, _paths} <- Mix.Config.eval!("config/description.exs"), + {:ok, file_path} <- + Pleroma.Docs.Generator.process( + implementation, + descriptions[:pleroma][:config_description] + ) do + type = if implementation == Pleroma.Docs.Markdown, do: "Markdown", else: "JSON" - type = if implementation == Pleroma.Docs.Markdown, do: "Markdown", else: "JSON" - - Mix.shell().info([:green, "#{type} docs successfully generated to #{file_path}."]) + Mix.shell().info([:green, "#{type} docs successfully generated to #{file_path}."]) + end end end From e47089cf5506952befd6995f04e2683a168ff9e2 Mon Sep 17 00:00:00 2001 From: Alex S Date: Wed, 11 Sep 2019 09:32:58 +0300 Subject: [PATCH 24/34] web_cache_ttl description --- config/description.exs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/config/description.exs b/config/description.exs index a56a95089..c5ae63915 100644 --- a/config/description.exs +++ b/config/description.exs @@ -2810,5 +2810,28 @@ suggestions: [["Authorization", "Content-Type", "Idempotency-Key"]] } ] + }, + %{ + group: :pleroma, + key: :web_cache_ttl, + type: :group, + description: + "The expiration time for the web responses cache. Values should be in milliseconds or `nil` to disable expiration.", + children: [ + %{ + key: :activity_pub, + type: :integer, + description: + "activity pub routes (except question activities). Defaults to `nil` (no expiration).", + suggestions: [30_000, nil] + }, + %{ + key: :activity_pub_question, + type: :integer, + description: + "activity pub routes (question activities). Defaults to `30_000` (30 seconds).", + suggestions: [30_000] + } + ] } ] From 5a76d5d2391f0fa6b4c58bf190b0cdbfff96014f Mon Sep 17 00:00:00 2001 From: rinpatch Date: Tue, 10 Sep 2019 23:08:15 +0300 Subject: [PATCH 25/34] Add extended benchmark --- lib/mix/tasks/pleroma/benchmark.ex | 40 ++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/mix/tasks/pleroma/benchmark.ex b/lib/mix/tasks/pleroma/benchmark.ex index a45940bf3..84dccf7f3 100644 --- a/lib/mix/tasks/pleroma/benchmark.ex +++ b/lib/mix/tasks/pleroma/benchmark.ex @@ -27,7 +27,7 @@ def run(["tag"]) do }) end - def run(["render_timeline", nickname]) do + def run(["render_timeline", nickname | _] = args) do start_pleroma() user = Pleroma.User.get_by_nickname(nickname) @@ -37,17 +37,41 @@ def run(["render_timeline", nickname]) do |> Map.put("blocking_user", user) |> Map.put("muting_user", user) |> Map.put("user", user) + |> Map.put("limit", 4096) |> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities() |> Enum.reverse() - Benchee.run(%{ - "render_timeline" => fn -> - Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ - activities: activities, - for: user, - as: :activity + inputs = %{ + "1 activity" => Enum.take_random(activities, 1), + "10 activities" => Enum.take_random(activities, 10), + "20 activities" => Enum.take_random(activities, 20), + "40 activities" => Enum.take_random(activities, 40), + "80 activities" => Enum.take_random(activities, 80) + } + + inputs = + if Enum.at(args, 2) == "extended" do + Map.merge(inputs, %{ + "200 activities" => Enum.take_random(activities, 200), + "500 activities" => Enum.take_random(activities, 500), + "2000 activities" => Enum.take_random(activities, 2000), + "4096 activities" => Enum.take_random(activities, 4096) }) + else + inputs end - }) + + Benchee.run( + %{ + "Standart rendering" => fn activities -> + Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{ + activities: activities, + for: user, + as: :activity + }) + end + }, + inputs: inputs + ) end end From 56828abf6de81a52079b26cf899b91fdd822f2ce Mon Sep 17 00:00:00 2001 From: rinpatch Date: Wed, 11 Sep 2019 23:04:01 +0300 Subject: [PATCH 26/34] Use Jason for rendering responses Although Jason readme says Phoenix 1.4+ already does it by default, [it actually does it only for new projects](https://github.com/phoenixframework/phoenix/blob/3bfb9f6e900c9a2e31cb95736e2cb5bdad329b61/lib/phoenix.ex#L58-L59) --- config/config.exs | 2 ++ lib/{ => pleroma}/healthcheck.ex | 1 + 2 files changed, 3 insertions(+) rename lib/{ => pleroma}/healthcheck.ex (98%) diff --git a/config/config.exs b/config/config.exs index 5206fe375..0e91df886 100644 --- a/config/config.exs +++ b/config/config.exs @@ -373,6 +373,8 @@ config :phoenix, :format_encoders, json: Jason +config :phoenix, :json_library, Jason + config :pleroma, :gopher, enabled: false, ip: {0, 0, 0, 0}, diff --git a/lib/healthcheck.ex b/lib/pleroma/healthcheck.ex similarity index 98% rename from lib/healthcheck.ex rename to lib/pleroma/healthcheck.ex index f97d14432..977b78c26 100644 --- a/lib/healthcheck.ex +++ b/lib/pleroma/healthcheck.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Healthcheck do alias Pleroma.Healthcheck alias Pleroma.Repo + @derive Jason.Encoder defstruct pool_size: 0, active: 0, idle: 0, From 74e4c72c4a0a079e8e2a245ec9b1d22684baf2e1 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Wed, 11 Sep 2019 16:16:09 -0500 Subject: [PATCH 27/34] Fix double quotes in error logs Example: pleroma: [error] Couldn't fetch ""https://pleroma.soykaf.com/objects/6288a14b-0623-40fc-a26a-0d358f8a11ca"", error: nil --- lib/pleroma/web/activity_pub/transmogrifier.ex | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 468961bd0..350b83abb 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -185,12 +185,12 @@ def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options) |> Map.put("context", replied_object.data["context"] || object["conversation"]) else e -> - Logger.error("Couldn't fetch \"#{inspect(in_reply_to_id)}\", error: #{inspect(e)}") + Logger.error("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}") object end e -> - Logger.error("Couldn't fetch \"#{inspect(in_reply_to_id)}\", error: #{inspect(e)}") + Logger.error("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}") object end else From 102eb4455c6285378bf7c25822d82378c7024aa4 Mon Sep 17 00:00:00 2001 From: Mark Felder Date: Thu, 12 Sep 2019 12:29:08 -0500 Subject: [PATCH 28/34] Fix associated test as well --- test/web/activity_pub/transmogrifier_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 0661d5d7c..bebecce8d 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -102,7 +102,7 @@ test "it does not crash if the object in inReplyTo can't be fetched" do assert capture_log(fn -> {:ok, _returned_activity} = Transmogrifier.handle_incoming(data) - end) =~ "[error] Couldn't fetch \"\"https://404.site/whatever\"\", error: nil" + end) =~ "[error] Couldn't fetch \"https://404.site/whatever\", error: nil" end test "it works for incoming notices" do From 528a88a68665a5bc9ed2d19ccfe99119839fe6e5 Mon Sep 17 00:00:00 2001 From: Angelina Filippova Date: Fri, 13 Sep 2019 03:31:16 +0000 Subject: [PATCH 29/34] Fix admin api docs for creating users --- docs/api/admin_api.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/api/admin_api.md b/docs/api/admin_api.md index d79c342be..fd608c459 100644 --- a/docs/api/admin_api.md +++ b/docs/api/admin_api.md @@ -60,9 +60,13 @@ Authentication is required and the user must be an admin. - Method: `POST` - Params: - - `nickname` - - `email` - - `password` + `users`: [ + { + `nickname`, + `email`, + `password` + } + ] - Response: User’s nickname ## `/api/pleroma/admin/users/follow` From 39dc9b470c7ad8348a13f181039f11d14a42fa2b Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 3 Sep 2019 21:58:30 +0700 Subject: [PATCH 30/34] Cleanup Pleroma.Activity and Pleroma.Web.ActivityPub.Utils --- lib/pleroma/activity.ex | 193 ++++++-------------------- lib/pleroma/activity/queries.ex | 32 ++++- lib/pleroma/user.ex | 2 +- lib/pleroma/web/activity_pub/utils.ex | 167 ++++++++-------------- test/user_test.exs | 2 +- 5 files changed, 128 insertions(+), 268 deletions(-) diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 44f1e3011..ec558168a 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Activity do use Ecto.Schema alias Pleroma.Activity + alias Pleroma.Activity.Queries alias Pleroma.ActivityExpiration alias Pleroma.Bookmark alias Pleroma.Notification @@ -65,8 +66,8 @@ defmodule Pleroma.Activity do timestamps() end - def with_joined_object(query) do - join(query, :inner, [activity], o in Object, + def with_joined_object(query, join_type \\ :inner) do + join(query, join_type, [activity], o in Object, on: fragment( "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", @@ -78,10 +79,10 @@ def with_joined_object(query) do ) end - def with_preloaded_object(query) do + def with_preloaded_object(query, join_type \\ :inner) do query |> has_named_binding?(:object) - |> if(do: query, else: with_joined_object(query)) + |> if(do: query, else: with_joined_object(query, join_type)) |> preload([activity, object: object], object: object) end @@ -107,12 +108,9 @@ def with_set_thread_muted_field(query, %User{} = user) do def with_set_thread_muted_field(query, _), do: query def get_by_ap_id(ap_id) do - Repo.one( - from( - activity in Activity, - where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id)) - ) - ) + ap_id + |> Queries.by_ap_id() + |> Repo.one() end def get_bookmark(%Activity{} = activity, %User{} = user) do @@ -133,21 +131,10 @@ def change(struct, params \\ %{}) do end def get_by_ap_id_with_object(ap_id) do - Repo.one( - from( - activity in Activity, - where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id)), - left_join: o in Object, - on: - fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", - o.data, - activity.data, - activity.data - ), - preload: [object: o] - ) - ) + ap_id + |> Queries.by_ap_id() + |> with_preloaded_object(:left) + |> Repo.one() end def get_by_id(id) do @@ -158,18 +145,9 @@ def get_by_id(id) do end def get_by_id_with_object(id) do - from(activity in Activity, - where: activity.id == ^id, - inner_join: o in Object, - on: - fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", - o.data, - activity.data, - activity.data - ), - preload: [object: o] - ) + Activity + |> where(id: ^id) + |> with_preloaded_object() |> Repo.one() end @@ -180,51 +158,21 @@ def all_by_ids_with_object(ids) do |> Repo.all() end - def by_object_ap_id(ap_id) do - from( - activity in Activity, - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - activity.data, - activity.data, - ^to_string(ap_id) - ) - ) + @doc """ + Accepts `ap_id` or list of `ap_id`. + Returns a query. + """ + @spec create_by_object_ap_id(String.t() | [String.t()]) :: Ecto.Queryable.t() + def create_by_object_ap_id(ap_id) do + ap_id + |> Queries.by_object_id() + |> Queries.by_type("Create") end - def create_by_object_ap_id(ap_ids) when is_list(ap_ids) do - from( - activity in Activity, - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", - activity.data, - activity.data, - ^ap_ids - ), - where: fragment("(?)->>'type' = 'Create'", activity.data) - ) - end - - def create_by_object_ap_id(ap_id) when is_binary(ap_id) do - from( - activity in Activity, - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - activity.data, - activity.data, - ^to_string(ap_id) - ), - where: fragment("(?)->>'type' = 'Create'", activity.data) - ) - end - - def create_by_object_ap_id(_), do: nil - def get_all_create_by_object_ap_id(ap_id) do - Repo.all(create_by_object_ap_id(ap_id)) + ap_id + |> create_by_object_ap_id() + |> Repo.all() end def get_create_by_object_ap_id(ap_id) when is_binary(ap_id) do @@ -235,54 +183,17 @@ def get_create_by_object_ap_id(ap_id) when is_binary(ap_id) do def get_create_by_object_ap_id(_), do: nil - def create_by_object_ap_id_with_object(ap_ids) when is_list(ap_ids) do - from( - activity in Activity, - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", - activity.data, - activity.data, - ^ap_ids - ), - where: fragment("(?)->>'type' = 'Create'", activity.data), - inner_join: o in Object, - on: - fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", - o.data, - activity.data, - activity.data - ), - preload: [object: o] - ) + @doc """ + Accepts `ap_id` or list of `ap_id`. + Returns a query. + """ + @spec create_by_object_ap_id_with_object(String.t() | [String.t()]) :: Ecto.Queryable.t() + def create_by_object_ap_id_with_object(ap_id) do + ap_id + |> create_by_object_ap_id() + |> with_preloaded_object() end - def create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do - from( - activity in Activity, - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - activity.data, - activity.data, - ^to_string(ap_id) - ), - where: fragment("(?)->>'type' = 'Create'", activity.data), - inner_join: o in Object, - on: - fragment( - "(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')", - o.data, - activity.data, - activity.data - ), - preload: [object: o] - ) - end - - def create_by_object_ap_id_with_object(_), do: nil - def get_create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do ap_id |> create_by_object_ap_id_with_object() @@ -306,7 +217,8 @@ def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id) def normalize(_), do: nil def delete_by_ap_id(id) when is_binary(id) do - by_object_ap_id(id) + id + |> Queries.by_object_id() |> select([u], u) |> Repo.delete_all() |> elem(1) @@ -350,31 +262,10 @@ def all_by_actor_and_id(actor, status_ids) do end def follow_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do - from( - a in Activity, - where: - fragment( - "? ->> 'type' = 'Follow'", - a.data - ), - where: - fragment( - "? ->> 'state' = 'pending'", - a.data - ), - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - a.data, - a.data, - ^ap_id - ) - ) - end - - @spec query_by_actor(actor()) :: Ecto.Query.t() - def query_by_actor(actor) do - from(a in Activity, where: a.actor == ^actor) + ap_id + |> Queries.by_object_id() + |> Queries.by_type("Follow") + |> where([a], fragment("? ->> 'state' = 'pending'", a.data)) end def restrict_deactivated_users(query) do diff --git a/lib/pleroma/activity/queries.ex b/lib/pleroma/activity/queries.ex index aa5b29566..13fa33831 100644 --- a/lib/pleroma/activity/queries.ex +++ b/lib/pleroma/activity/queries.ex @@ -13,6 +13,14 @@ defmodule Pleroma.Activity.Queries do alias Pleroma.Activity + @spec by_ap_id(query, String.t()) :: query + def by_ap_id(query \\ Activity, ap_id) do + from( + activity in query, + where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id)) + ) + end + @spec by_actor(query, String.t()) :: query def by_actor(query \\ Activity, actor) do from( @@ -21,8 +29,23 @@ def by_actor(query \\ Activity, actor) do ) end - @spec by_object_id(query, String.t()) :: query - def by_object_id(query \\ Activity, object_id) do + @spec by_object_id(query, String.t() | [String.t()]) :: query + def by_object_id(query \\ Activity, object_id) + + def by_object_id(query, object_ids) when is_list(object_ids) do + from( + activity in query, + where: + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", + activity.data, + activity.data, + ^object_ids + ) + ) + end + + def by_object_id(query, object_id) when is_binary(object_id) do from(activity in query, where: fragment( @@ -41,9 +64,4 @@ def by_type(query \\ Activity, activity_type) do where: fragment("(?)->>'type' = ?", activity.data, ^activity_type) ) end - - @spec limit(query, pos_integer()) :: query - def limit(query \\ Activity, limit) do - from(activity in query, limit: ^limit) - end end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 3aa245f2a..ceca11def 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1219,7 +1219,7 @@ def follow_import(%User{} = follower, followed_identifiers) when is_list(followe def delete_user_activities(%User{ap_id: ap_id} = user) do ap_id - |> Activity.query_by_actor() + |> Activity.Queries.by_actor() |> RepoStreamer.chunk_stream(50) |> Stream.each(fn activities -> Enum.each(activities, &delete_activity(&1)) diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index c9c0c3763..47917f5d3 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -85,15 +85,13 @@ defp extract_list(lst) when is_list(lst), do: lst defp extract_list(_), do: [] def maybe_splice_recipient(ap_id, params) do - need_splice = + need_splice? = !recipient_in_collection(ap_id, params["to"]) && !recipient_in_collection(ap_id, params["cc"]) - cc_list = extract_list(params["cc"]) - - if need_splice do - params - |> Map.put("cc", [ap_id | cc_list]) + if need_splice? do + cc_list = extract_list(params["cc"]) + Map.put(params, "cc", [ap_id | cc_list]) else params end @@ -139,7 +137,7 @@ def get_notified_from_object(%{"type" => type} = object) when type in @supported "object" => object } - Notification.get_notified_from_activity(%Activity{data: fake_create_activity}, false) + get_notified_from_object(fake_create_activity) end def get_notified_from_object(object) do @@ -188,9 +186,9 @@ def maybe_federate(_), do: :ok Adds an id and a published data if they aren't there, also adds it to an included object """ - def lazy_put_activity_defaults(map, fake \\ false) do + def lazy_put_activity_defaults(map, fake? \\ false) do map = - unless fake do + if not fake? do %{data: %{"id" => context}, id: context_id} = create_context(map["context"]) map @@ -207,7 +205,7 @@ def lazy_put_activity_defaults(map, fake \\ false) do end if is_map(map["object"]) do - object = lazy_put_object_defaults(map["object"], map, fake) + object = lazy_put_object_defaults(map["object"], map, fake?) %{map | "object" => object} else map @@ -217,9 +215,9 @@ def lazy_put_activity_defaults(map, fake \\ false) do @doc """ Adds an id and published date if they aren't there. """ - def lazy_put_object_defaults(map, activity \\ %{}, fake) + def lazy_put_object_defaults(map, activity \\ %{}, fake?) - def lazy_put_object_defaults(map, activity, true = _fake) do + def lazy_put_object_defaults(map, activity, true = _fake?) do map |> Map.put_new_lazy("published", &make_date/0) |> Map.put_new("id", "pleroma:fake_object_id") @@ -228,7 +226,7 @@ def lazy_put_object_defaults(map, activity, true = _fake) do |> Map.put_new("context_id", activity["context_id"]) end - def lazy_put_object_defaults(map, activity, _fake) do + def lazy_put_object_defaults(map, activity, _fake?) do map |> Map.put_new_lazy("id", &generate_object_id/0) |> Map.put_new_lazy("published", &make_date/0) @@ -242,9 +240,7 @@ def lazy_put_object_defaults(map, activity, _fake) do def insert_full_object(%{"object" => %{"type" => type} = object_data} = map) when is_map(object_data) and type in @supported_object_types do with {:ok, object} <- Object.create(object_data) do - map = - map - |> Map.put("object", object.data["id"]) + map = Map.put(map, "object", object.data["id"]) {:ok, map, object} end @@ -263,7 +259,7 @@ def get_existing_like(actor, %{data: %{"id" => id}}) do |> Activity.Queries.by_actor() |> Activity.Queries.by_object_id(id) |> Activity.Queries.by_type("Like") - |> Activity.Queries.limit(1) + |> limit(1) |> Repo.one() end @@ -380,12 +376,11 @@ def update_follow_state( %Activity{data: %{"actor" => actor, "object" => object}} = activity, state ) do - with new_data <- - activity.data - |> Map.put("state", state), - changeset <- Changeset.change(activity, data: new_data), - {:ok, activity} <- Repo.update(changeset), - _ <- User.set_follow_state_cache(actor, object, state) do + new_data = Map.put(activity.data, "state", state) + changeset = Changeset.change(activity, data: new_data) + + with {:ok, activity} <- Repo.update(changeset) do + User.set_follow_state_cache(actor, object, state) {:ok, activity} end end @@ -410,28 +405,14 @@ def make_follow_data( end def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do - query = - from( - activity in Activity, - where: - fragment( - "? ->> 'type' = 'Follow'", - activity.data - ), - where: activity.actor == ^follower_id, - # this is to use the index - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - activity.data, - activity.data, - ^followed_id - ), - order_by: [fragment("? desc nulls last", activity.id)], - limit: 1 - ) - - Repo.one(query) + "Follow" + |> Activity.Queries.by_type() + |> where(actor: ^follower_id) + # this is to use the index + |> Activity.Queries.by_object_id(followed_id) + |> order_by([activity], fragment("? desc nulls last", activity.id)) + |> limit(1) + |> Repo.one() end #### Announce-related helpers @@ -439,23 +420,13 @@ def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do @doc """ Retruns an existing announce activity if the notice has already been announced """ - def get_existing_announce(actor, %{data: %{"id" => id}}) do - query = - from( - activity in Activity, - where: activity.actor == ^actor, - # this is to use the index - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - activity.data, - activity.data, - ^id - ), - where: fragment("(?)->>'type' = 'Announce'", activity.data) - ) - - Repo.one(query) + def get_existing_announce(actor, %{data: %{"id" => ap_id}}) do + "Announce" + |> Activity.Queries.by_type() + |> where(actor: ^actor) + # this is to use the index + |> Activity.Queries.by_object_id(ap_id) + |> Repo.one() end @doc """ @@ -538,11 +509,13 @@ def add_announce_to_object( object ) do announcements = - if is_list(object.data["announcements"]), do: object.data["announcements"], else: [] + if is_list(object.data["announcements"]) do + Enum.uniq([actor | object.data["announcements"]]) + else + [actor] + end - with announcements <- [actor | announcements] |> Enum.uniq() do - update_element_in_object("announcement", announcements, object) - end + update_element_in_object("announcement", announcements, object) end def add_announce_to_object(_, object), do: {:ok, object} @@ -570,28 +543,14 @@ def make_unfollow_data(follower, followed, follow_activity, activity_id) do #### Block-related helpers def fetch_latest_block(%User{ap_id: blocker_id}, %User{ap_id: blocked_id}) do - query = - from( - activity in Activity, - where: - fragment( - "? ->> 'type' = 'Block'", - activity.data - ), - where: activity.actor == ^blocker_id, - # this is to use the index - where: - fragment( - "coalesce((?)->'object'->>'id', (?)->>'object') = ?", - activity.data, - activity.data, - ^blocked_id - ), - order_by: [fragment("? desc nulls last", activity.id)], - limit: 1 - ) - - Repo.one(query) + "Block" + |> Activity.Queries.by_type() + |> where(actor: ^blocker_id) + # this is to use the index + |> Activity.Queries.by_object_id(blocked_id) + |> order_by([activity], fragment("? desc nulls last", activity.id)) + |> limit(1) + |> Repo.one() end def make_block_data(blocker, blocked, activity_id) do @@ -695,11 +654,11 @@ def fetch_ordered_collection(from, pages_left, acc \\ []) do #### Report-related helpers def update_report_state(%Activity{} = activity, state) when state in @supported_report_states do - with new_data <- Map.put(activity.data, "state", state), - changeset <- Changeset.change(activity, data: new_data), - {:ok, activity} <- Repo.update(changeset) do - {:ok, activity} - end + new_data = Map.put(activity.data, "state", state) + + activity + |> Changeset.change(data: new_data) + |> Repo.update() end def update_report_state(_, _), do: {:error, "Unsupported state"} @@ -766,21 +725,13 @@ defp get_updated_targets( end def get_existing_votes(actor, %{data: %{"id" => id}}) do - query = - from( - [activity, object: object] in Activity.with_preloaded_object(Activity), - where: fragment("(?)->>'type' = 'Create'", activity.data), - where: fragment("(?)->>'actor' = ?", activity.data, ^actor), - where: - fragment( - "(?)->>'inReplyTo' = ?", - object.data, - ^to_string(id) - ), - where: fragment("(?)->>'type' = 'Answer'", object.data) - ) - - Repo.all(query) + actor + |> Activity.Queries.by_actor() + |> Activity.Queries.by_type("Create") + |> Activity.with_preloaded_object() + |> where([a, object: o], fragment("(?)->>'inReplyTo' = ?", o.data, ^to_string(id))) + |> where([a, object: o], fragment("(?)->>'type' = 'Answer'", o.data)) + |> Repo.all() end defp maybe_put(map, _key, nil), do: map diff --git a/test/user_test.exs b/test/user_test.exs index a25b72f4e..206258fee 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1081,7 +1081,7 @@ test "it deletes a user, all follow relationships and all activities", %{user: u user_activities = user.ap_id - |> Activity.query_by_actor() + |> Activity.Queries.by_actor() |> Repo.all() |> Enum.map(fn act -> act.data["type"] end) From 5bfbad13ad4dd009b172748d81f56ead21c700de Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Tue, 3 Sep 2019 21:33:02 +0700 Subject: [PATCH 31/34] Add more tests for Pleroma.Activity --- test/activity_test.exs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/activity_test.exs b/test/activity_test.exs index 4152aaa7e..f9f789a76 100644 --- a/test/activity_test.exs +++ b/test/activity_test.exs @@ -185,4 +185,35 @@ test "all_by_ids_with_object/1" do assert [%{id: ^id1, object: %Object{}}, %{id: ^id2, object: %Object{}}] = activities end + + test "get_by_id_with_object/1" do + %{id: id} = insert(:note_activity) + + assert %Activity{id: ^id, object: %Object{}} = Activity.get_by_id_with_object(id) + end + + test "get_by_ap_id_with_object/1" do + %{data: %{"id" => ap_id}} = insert(:note_activity) + + assert %Activity{data: %{"id" => ^ap_id}, object: %Object{}} = + Activity.get_by_ap_id_with_object(ap_id) + end + + test "get_by_id/1" do + %{id: id} = insert(:note_activity) + + assert %Activity{id: ^id} = Activity.get_by_id(id) + end + + test "all_by_actor_and_id/2" do + user = insert(:user) + + {:ok, %{id: id1}} = Pleroma.Web.CommonAPI.post(user, %{"status" => "cofe"}) + {:ok, %{id: id2}} = Pleroma.Web.CommonAPI.post(user, %{"status" => "cofefe"}) + + assert [] == Activity.all_by_actor_and_id(user, []) + + assert [%Activity{id: ^id2}, %Activity{id: ^id1}] = + Activity.all_by_actor_and_id(user.ap_id, [id1, id2]) + end end From 25d8216804c7742cd8549799a7785723f2a70afa Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Fri, 13 Sep 2019 13:09:35 +0700 Subject: [PATCH 32/34] Add email change endpoint --- CHANGELOG.md | 1 + docs/api/pleroma_api.md | 11 +- lib/pleroma/user.ex | 9 ++ lib/pleroma/web/router.ex | 1 + .../controllers/util_controller.ex | 19 ++++ test/user_test.exs | 27 +++++ test/web/twitter_api/util_controller_test.exs | 107 ++++++++++++++++++ 7 files changed, 174 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7f1aee0e..d5bb2e07c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -105,6 +105,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - ActivityPub: Optional signing of ActivityPub object fetches. - Admin API: Endpoint for fetching latest user's statuses - Pleroma API: Add `/api/v1/pleroma/accounts/confirmation_resend?email=` for resending account confirmation. +- Pleroma API: Email change endpoint. - Relays: Added a task to list relay subscriptions. - Mix Tasks: `mix pleroma.database fix_likes_collections` - Federation: Remove `likes` from objects. diff --git a/docs/api/pleroma_api.md b/docs/api/pleroma_api.md index 7d343e97a..8a726a7cb 100644 --- a/docs/api/pleroma_api.md +++ b/docs/api/pleroma_api.md @@ -252,7 +252,7 @@ See [Admin-API](Admin-API.md) * Params: * `email`: email of that needs to be verified * Authentication: not required -* Response: 204 No Content +* Response: 204 No Content ## `/api/v1/pleroma/mascot` ### Gets user mascot image @@ -321,6 +321,15 @@ See [Admin-API](Admin-API.md) } ``` +## `/api/pleroma/change_email` +### Change account email +* Method `POST` +* Authentication: required +* Params: + * `password`: user's password + * `email`: new email +* Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise + # Pleroma Conversations Pleroma Conversations have the same general structure that Mastodon Conversations have. The behavior differs in the following ways when using these endpoints: diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 3aa245f2a..1f6a75d03 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -1624,4 +1624,13 @@ defp put_password_hash(changeset), do: changeset def is_internal_user?(%User{nickname: nil}), do: true def is_internal_user?(%User{local: true, nickname: "internal." <> _}), do: true def is_internal_user?(_), do: false + + def change_email(user, email) do + user + |> cast(%{email: email}, [:email]) + |> validate_required([:email]) + |> unique_constraint(:email) + |> validate_format(:email, @email_regex) + |> update_and_set_cache() + end end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 7cd59acb2..b0464037e 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -224,6 +224,7 @@ defmodule Pleroma.Web.Router do scope [] do pipe_through(:oauth_write) + post("/change_email", UtilController, :change_email) post("/change_password", UtilController, :change_password) post("/delete_account", UtilController, :delete_account) put("/notification_settings", UtilController, :update_notificaton_settings) diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 3405bd3b7..867787c57 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -314,6 +314,25 @@ def change_password(%{assigns: %{user: user}} = conn, params) do end end + def change_email(%{assigns: %{user: user}} = conn, params) do + case CommonAPI.Utils.confirm_current_password(user, params["password"]) do + {:ok, user} -> + with {:ok, _user} <- User.change_email(user, params["email"]) do + json(conn, %{status: "success"}) + else + {:error, changeset} -> + {_, {error, _}} = Enum.at(changeset.errors, 0) + json(conn, %{error: "Email #{error}."}) + + _ -> + json(conn, %{error: "Unable to change email."}) + end + + {:error, msg} -> + json(conn, %{error: msg}) + end + end + def delete_account(%{assigns: %{user: user}} = conn, params) do case CommonAPI.Utils.confirm_current_password(user, params["password"]) do {:ok, user} -> diff --git a/test/user_test.exs b/test/user_test.exs index a25b72f4e..ed8cdbe31 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1614,4 +1614,31 @@ test "syncronizes the counters with the remote instance for the follower when en assert User.user_info(other_user).following_count == 152 end end + + describe "change_email/2" do + setup do + [user: insert(:user)] + end + + test "blank email returns error", %{user: user} do + assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, "") + assert {:error, %{errors: [email: {"can't be blank", _}]}} = User.change_email(user, nil) + end + + test "non unique email returns error", %{user: user} do + %{email: email} = insert(:user) + + assert {:error, %{errors: [email: {"has already been taken", _}]}} = + User.change_email(user, email) + end + + test "invalid email returns error", %{user: user} do + assert {:error, %{errors: [email: {"has invalid format", _}]}} = + User.change_email(user, "cofe") + end + + test "changes email", %{user: user} do + assert {:ok, %User{email: "cofe@cofe.party"}} = User.change_email(user, "cofe@cofe.party") + end + end end diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index cf8e69d2b..a3c6145c0 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -662,4 +662,111 @@ test "it returns new captcha", %{conn: conn} do assert called(Pleroma.Captcha.new()) end end + + defp with_credentials(conn, username, password) do + header_content = "Basic " <> Base.encode64("#{username}:#{password}") + put_req_header(conn, "authorization", header_content) + end + + defp valid_user(_context) do + user = insert(:user) + [user: user] + end + + describe "POST /api/pleroma/change_email" do + setup [:valid_user] + + test "without credentials", %{conn: conn} do + conn = post(conn, "/api/pleroma/change_email") + assert json_response(conn, 403) == %{"error" => "Invalid credentials."} + end + + test "with credentials and invalid password", %{conn: conn, user: current_user} do + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/pleroma/change_email", %{ + "password" => "hi", + "email" => "test@test.com" + }) + + assert json_response(conn, 200) == %{"error" => "Invalid password."} + end + + test "with credentials, valid password and invalid email", %{ + conn: conn, + user: current_user + } do + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/pleroma/change_email", %{ + "password" => "test", + "email" => "foobar" + }) + + assert json_response(conn, 200) == %{"error" => "Email has invalid format."} + end + + test "with credentials, valid password and no email", %{ + conn: conn, + user: current_user + } do + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/pleroma/change_email", %{ + "password" => "test" + }) + + assert json_response(conn, 200) == %{"error" => "Email can't be blank."} + end + + test "with credentials, valid password and blank email", %{ + conn: conn, + user: current_user + } do + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/pleroma/change_email", %{ + "password" => "test", + "email" => "" + }) + + assert json_response(conn, 200) == %{"error" => "Email can't be blank."} + end + + test "with credentials, valid password and non unique email", %{ + conn: conn, + user: current_user + } do + user = insert(:user) + + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/pleroma/change_email", %{ + "password" => "test", + "email" => user.email + }) + + assert json_response(conn, 200) == %{"error" => "Email has already been taken."} + end + + test "with credentials, valid password and valid email", %{ + conn: conn, + user: current_user + } do + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/pleroma/change_email", %{ + "password" => "test", + "email" => "cofe@foobar.com" + }) + + assert json_response(conn, 200) == %{"status" => "success"} + end + end end From 7b5c81b3918b7ff99f00e72194075f43c3f81165 Mon Sep 17 00:00:00 2001 From: Egor Kislitsyn Date: Wed, 28 Aug 2019 14:50:58 +0700 Subject: [PATCH 33/34] Add a note about compatibility with Mastodon --- docs/api/pleroma_api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/api/pleroma_api.md b/docs/api/pleroma_api.md index 8a726a7cb..30fac77da 100644 --- a/docs/api/pleroma_api.md +++ b/docs/api/pleroma_api.md @@ -329,12 +329,13 @@ See [Admin-API](Admin-API.md) * `password`: user's password * `email`: new email * Response: JSON. Returns `{"status": "success"}` if the change was successful, `{"error": "[error message]"}` otherwise +* Note: Currently, Mastodon has no API for changing email. If they add it in future it might be incompatible with Pleroma. # Pleroma Conversations Pleroma Conversations have the same general structure that Mastodon Conversations have. The behavior differs in the following ways when using these endpoints: -1. Pleroma Conversations never add or remove recipients, unless explicitly changed by the user. +1. Pleroma Conversations never add or remove recipients, unless explicitly changed by the user. 2. Pleroma Conversations statuses can be requested by Conversation id. 3. Pleroma Conversations can be replied to. From 25a64a4aa0a10bf06c2ccdf9a6c493f184170a89 Mon Sep 17 00:00:00 2001 From: stwf Date: Fri, 13 Sep 2019 11:46:41 -0400 Subject: [PATCH 34/34] Capture test error messages where appropriate --- test/integration/mastodon_websocket_test.exs | 29 ++++++++++++++----- test/web/activity_pub/publisher_test.exs | 24 +++++++++------ test/web/activity_pub/relay_test.exs | 13 +++++++-- .../mastodon_api_controller_test.exs | 14 +++++---- test/web/twitter_api/util_controller_test.exs | 13 +++++---- .../web_finger/web_finger_controller_test.exs | 13 +++++---- 6 files changed, 70 insertions(+), 36 deletions(-) diff --git a/test/integration/mastodon_websocket_test.exs b/test/integration/mastodon_websocket_test.exs index 3975cdcd6..63bf73412 100644 --- a/test/integration/mastodon_websocket_test.exs +++ b/test/integration/mastodon_websocket_test.exs @@ -5,6 +5,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do use Pleroma.DataCase + import ExUnit.CaptureLog import Pleroma.Factory alias Pleroma.Integration.WebsocketClient @@ -39,13 +40,17 @@ def start_socket(qs \\ nil, headers \\ []) do end test "refuses invalid requests" do - assert {:error, {400, _}} = start_socket() - assert {:error, {404, _}} = start_socket("?stream=ncjdk") + capture_log(fn -> + assert {:error, {400, _}} = start_socket() + assert {:error, {404, _}} = start_socket("?stream=ncjdk") + end) end test "requires authentication and a valid token for protected streams" do - assert {:error, {403, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") - assert {:error, {403, _}} = start_socket("?stream=user") + capture_log(fn -> + assert {:error, {403, _}} = start_socket("?stream=user&access_token=aaaaaaaaaaaa") + assert {:error, {403, _}} = start_socket("?stream=user") + end) end test "allows public streams without authentication" do @@ -100,19 +105,27 @@ test "accepts valid tokens", state do test "accepts the 'user' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}") - assert {:error, {403, "Forbidden"}} = start_socket("?stream=user") + + assert capture_log(fn -> + assert {:error, {403, "Forbidden"}} = start_socket("?stream=user") + end) =~ ":badarg" end test "accepts the 'user:notification' stream", %{token: token} = _state do assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}") - assert {:error, {403, "Forbidden"}} = start_socket("?stream=user:notification") + + assert capture_log(fn -> + assert {:error, {403, "Forbidden"}} = start_socket("?stream=user:notification") + end) =~ ":badarg" end test "accepts valid token on Sec-WebSocket-Protocol header", %{token: token} do assert {:ok, _} = start_socket("?stream=user", [{"Sec-WebSocket-Protocol", token.token}]) - assert {:error, {403, "Forbidden"}} = - start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) + assert capture_log(fn -> + assert {:error, {403, "Forbidden"}} = + start_socket("?stream=user", [{"Sec-WebSocket-Protocol", "I am a friend"}]) + end) =~ ":badarg" end end end diff --git a/test/web/activity_pub/publisher_test.exs b/test/web/activity_pub/publisher_test.exs index 36a39c84c..381757e1b 100644 --- a/test/web/activity_pub/publisher_test.exs +++ b/test/web/activity_pub/publisher_test.exs @@ -5,6 +5,7 @@ defmodule Pleroma.Web.ActivityPub.PublisherTest do use Pleroma.DataCase + import ExUnit.CaptureLog import Pleroma.Factory import Tesla.Mock import Mock @@ -188,7 +189,10 @@ test "it returns inbox for messages involving single recipients in total" do actor = insert(:user) inbox = "http://connrefused.site/users/nick1/inbox" - assert {:error, _} = Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + assert capture_log(fn -> + assert {:error, _} = + Publisher.publish_one(%{inbox: inbox, json: "{}", actor: actor, id: 1}) + end) =~ "connrefused" assert called(Instances.set_unreachable(inbox)) end @@ -212,14 +216,16 @@ test "it returns inbox for messages involving single recipients in total" do actor = insert(:user) inbox = "http://connrefused.site/users/nick1/inbox" - assert {:error, _} = - Publisher.publish_one(%{ - inbox: inbox, - json: "{}", - actor: actor, - id: 1, - unreachable_since: NaiveDateTime.utc_now() - }) + assert capture_log(fn -> + assert {:error, _} = + Publisher.publish_one(%{ + inbox: inbox, + json: "{}", + actor: actor, + id: 1, + unreachable_since: NaiveDateTime.utc_now() + }) + end) =~ "connrefused" refute called(Instances.set_unreachable(inbox)) end diff --git a/test/web/activity_pub/relay_test.exs b/test/web/activity_pub/relay_test.exs index 4f7d592a6..9db4255d8 100644 --- a/test/web/activity_pub/relay_test.exs +++ b/test/web/activity_pub/relay_test.exs @@ -10,6 +10,7 @@ defmodule Pleroma.Web.ActivityPub.RelayTest do alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Relay + import ExUnit.CaptureLog import Pleroma.Factory import Mock @@ -20,7 +21,9 @@ test "gets an actor for the relay" do describe "follow/1" do test "returns errors when user not found" do - assert Relay.follow("test-ap-id") == {:error, "Could not fetch by AP id"} + assert capture_log(fn -> + assert Relay.follow("test-ap-id") == {:error, "Could not fetch by AP id"} + end) =~ "Could not fetch by AP id" end test "returns activity" do @@ -37,7 +40,9 @@ test "returns activity" do describe "unfollow/1" do test "returns errors when user not found" do - assert Relay.unfollow("test-ap-id") == {:error, "Could not fetch by AP id"} + assert capture_log(fn -> + assert Relay.unfollow("test-ap-id") == {:error, "Could not fetch by AP id"} + end) =~ "Could not fetch by AP id" end test "returns activity" do @@ -78,7 +83,9 @@ test "returns error when object is unknown" do } ) - assert Relay.publish(activity) == {:error, nil} + assert capture_log(fn -> + assert Relay.publish(activity) == {:error, nil} + end) =~ "[error] error: nil" end test_with_mock "returns announce activity and publish to federate", diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index f4902d043..806ae7e69 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -3963,13 +3963,15 @@ test "returns error", %{conn: conn, user: user} do Config.put([:suggestions, :enabled], true) Config.put([:suggestions, :third_party_engine], "http://test500?{{host}}&{{user}}") - res = - conn - |> assign(:user, user) - |> get("/api/v1/suggestions") - |> json_response(500) + assert capture_log(fn -> + res = + conn + |> assign(:user, user) + |> get("/api/v1/suggestions") + |> json_response(500) - assert res == "Something went wrong" + assert res == "Something went wrong" + end) =~ "Could not retrieve suggestions" end test "returns suggestions", %{conn: conn, user: user, other_user: other_user} do diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index cf8e69d2b..e36d3130f 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -8,6 +8,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilControllerTest do alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.CommonAPI + import ExUnit.CaptureLog import Pleroma.Factory import Mock @@ -338,12 +339,14 @@ test "show follow page if the `acct` is a account link", %{conn: conn} do test "show follow page with error when user cannot fecth by `acct` link", %{conn: conn} do user = insert(:user) - response = - conn - |> assign(:user, user) - |> get("/ostatus_subscribe?acct=https://mastodon.social/users/not_found") + assert capture_log(fn -> + response = + conn + |> assign(:user, user) + |> get("/ostatus_subscribe?acct=https://mastodon.social/users/not_found") - assert html_response(response, 200) =~ "Error fetching user" + assert html_response(response, 200) =~ "Error fetching user" + end) =~ "Object has been deleted" end end diff --git a/test/web/web_finger/web_finger_controller_test.exs b/test/web/web_finger/web_finger_controller_test.exs index e23086b2a..bd3ccaaf7 100644 --- a/test/web/web_finger/web_finger_controller_test.exs +++ b/test/web/web_finger/web_finger_controller_test.exs @@ -5,6 +5,7 @@ defmodule Pleroma.Web.WebFinger.WebFingerControllerTest do use Pleroma.Web.ConnCase + import ExUnit.CaptureLog import Pleroma.Factory import Tesla.Mock @@ -75,11 +76,13 @@ test "it returns 404 when user isn't found (XML)" do test "Sends a 404 when invalid format" do user = insert(:user) - assert_raise Phoenix.NotAcceptableError, fn -> - build_conn() - |> put_req_header("accept", "text/html") - |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") - end + assert capture_log(fn -> + assert_raise Phoenix.NotAcceptableError, fn -> + build_conn() + |> put_req_header("accept", "text/html") + |> get("/.well-known/webfinger?resource=acct:#{user.nickname}@localhost") + end + end) =~ "no supported media type in accept header" end test "Sends a 400 when resource param is missing" do