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