diff --git a/.gitignore b/.gitignore
index 599b52b9e..6ae21e914 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,6 +27,8 @@ erl_crash.dump
# variables.
/config/*.secret.exs
/config/generated_config.exs
+/config/*.env
+
# Database setup file, some may forget to delete it
/config/setup_db.psql
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 121e4abfe..fd0c5c8d4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,6 +25,8 @@ before_script:
- apt-get update && apt-get install -y cmake
- mix local.hex --force
- mix local.rebar --force
+ - apt-get -qq update
+ - apt-get install -y libmagic-dev
build:
stage: build
@@ -196,7 +198,7 @@ amd64:
variables: &release-variables
MIX_ENV: prod
before_script: &before-release
- - apt-get update && apt-get install -y cmake
+ - apt-get update && apt-get install -y cmake libmagic-dev
- echo "import Mix.Config" > config/prod.secret.exs
- mix local.hex --force
- mix local.rebar --force
@@ -215,7 +217,7 @@ amd64-musl:
cache: *release-cache
variables: *release-variables
before_script: &before-release-musl
- - apk add git gcc g++ musl-dev make cmake
+ - apk add git gcc g++ musl-dev make cmake file-dev
- echo "import Mix.Config" > config/prod.secret.exs
- mix local.hex --force
- mix local.rebar --force
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c546dd0bf..f61ccd2c3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,11 +9,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Mix tasks for controlling user account confirmation status in bulk (`mix pleroma.user confirm_all` and `mix pleroma.user unconfirm_all`)
- Mix task for sending confirmation emails to all unconfirmed users (`mix pleroma.email send_confirmation_mails`)
- Mix task option for force-unfollowing relays
+- Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details).
+- Reports now generate notifications for admins and mods.
+- Pleroma API: Importing the mutes users from CSV files.
+- Experimental websocket-based federation between Pleroma instances.
+- Support pagination of blocks and mutes
+- App metrics: ability to restrict access to specified IP whitelist.
+- Account backup
+- Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
+- Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`
+- The site title is now injected as a `title` tag like preloads or metadata.
### Changed
+- **Breaking** Requires `libmagic` (or `file`) to guess file types.
- **Breaking:** Pleroma Admin API: emoji packs and files routes changed.
- **Breaking:** Sensitive/NSFW statuses no longer disable link previews.
+- **Breaking:** App metrics endpoint (`/api/pleroma/app_metrics`) is disabled by default, check `docs/API/prometheus.md` on enabling and configuring.
- Search: Users are now findable by their urls.
- Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated.
- Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated.
@@ -22,11 +34,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Minimum lifetime for ephmeral activities changed to 10 minutes and made configurable (`:min_lifetime` option).
- Introduced optional dependencies on `ffmpeg`, `ImageMagick`, `exiftool` software packages. Please refer to `docs/installation/optional/media_graphics_packages.md`.
- Changed `mix pleroma.user toggle_confirmed` to `mix pleroma.user confirm`
-
-### Added
-- Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details).
-- Pleroma API: Importing the mutes users from CSV files.
-- Experimental websocket-based federation between Pleroma instances.
+- Polls now always return a `voters_count`, even if they are single-choice
+- Admin Emails: The ap id is used as the user link in emails now.
API Changes
@@ -34,6 +43,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Pleroma API: Importing the mutes users from CSV files.
- Admin API: Importing emoji from a zip file
- Pleroma API: Pagination for remote/local packs and emoji.
+- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `unconfirmed` status
+- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `actor_type`
+- Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending.
+- Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances.
+- Mastodon API: User and conversation mutes can now auto-expire if `expires_in` parameter was given while adding the mute.
@@ -49,12 +63,20 @@ switched to a new configuration mechanism, however it was not officially removed
- Add documented-but-missing chat pagination.
- Allow sending out emails again.
+- Allow sending chat messages to yourself.
+- Fix remote users with a whitespace name.
+- OStatus / static FE endpoints: fixed inaccessibility for anonymous users on non-federating instances, switched to handling per `:restrict_unauthenticated` setting.
+- Mastodon API: Current user is now included in conversation if it's the only participant
+- Mastodon API: Fixed last_status.account being not filled with account data
## Unreleased (Patch)
### Changed
- API: Empty parameter values for integer parameters are now ignored in non-strict validaton mode.
+### Fixes
+- Config generation: rename `Pleroma.Upload.Filter.ExifTool` to `Pleroma.Upload.Filter.Exiftool`
+
## [2.1.2] - 2020-09-17
### Security
diff --git a/Dockerfile b/Dockerfile
index c210cf79c..4e7c01c5d 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,7 +4,7 @@ COPY . .
ENV MIX_ENV=prod
-RUN apk add git gcc g++ musl-dev make cmake &&\
+RUN apk add git gcc g++ musl-dev make cmake file-dev &&\
echo "import Mix.Config" > config/prod.secret.exs &&\
mix local.hex --force &&\
mix local.rebar --force &&\
diff --git a/config/config.exs b/config/config.exs
index 273da5bb6..0b8a75aad 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -123,7 +123,6 @@
# Configures the endpoint
config :pleroma, Pleroma.Web.Endpoint,
- instrumenters: [Pleroma.Web.Endpoint.Instrumenter],
url: [host: "localhost"],
http: [
ip: {127, 0, 0, 1},
@@ -143,7 +142,7 @@
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
signing_salt: "CqaoopA2",
render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
- pubsub: [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2],
+ pubsub_server: Pleroma.PubSub,
secure_cookie_flag: true,
extra_cookie_attrs: [
"SameSite=Lax"
@@ -235,6 +234,7 @@
"text/bbcode"
],
autofollowed_nicknames: [],
+ autofollowing_nicknames: [],
max_pinned_statuses: 1,
attachment_links: false,
max_report_comment_size: 1000,
@@ -551,6 +551,7 @@
queues: [
activity_expiration: 10,
token_expiration: 5,
+ backup: 1,
federator_incoming: 50,
federator_outgoing: 50,
ingestion_queue: 50,
@@ -561,7 +562,8 @@
background: 5,
remote_fetcher: 2,
attachments_cleanup: 5,
- new_users_digest: 1
+ new_users_digest: 1,
+ mute_expire: 5
],
plugins: [Oban.Plugins.Pruner],
crontab: [
@@ -636,7 +638,12 @@
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false
-config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
+config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
+ enabled: false,
+ auth: false,
+ ip_whitelist: [],
+ path: "/api/pleroma/app_metrics",
+ format: :text
config :pleroma, Pleroma.ScheduledActivity,
daily_user_limit: 25,
@@ -802,6 +809,8 @@
timeout: 300_000
]
+config :pleroma, :majic_pool, size: 2
+
private_instance? = :if_instance_is_private
config :pleroma, :restrict_unauthenticated,
@@ -812,7 +821,7 @@
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
config :pleroma, :mrf,
- policies: Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy,
+ policies: [Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy, Pleroma.Web.ActivityPub.MRF.TagPolicy],
transparency: true,
transparency_exclusions: []
@@ -828,6 +837,11 @@
config :pleroma, Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.PleromaAuthenticator
+config :pleroma, Pleroma.User.Backup,
+ purge_after_days: 30,
+ limit_days: 7,
+ dir: nil
+
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"
diff --git a/config/description.exs b/config/description.exs
index 6e83a8e09..0552b37e0 100644
--- a/config/description.exs
+++ b/config/description.exs
@@ -1,5 +1,4 @@
use Mix.Config
-alias Pleroma.Docs.Generator
websocket_config = [
path: "/websocket",
@@ -829,13 +828,13 @@
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"
- ]
+ "Set to nicknames of (local) users that every new user should automatically follow"
+ },
+ %{
+ key: :autofollowing_nicknames,
+ type: {:list, :string},
+ description:
+ "Set to nicknames of (local) users that automatically follows every newly registered user"
},
%{
key: :attachment_links,
@@ -1555,289 +1554,6 @@
}
]
},
- %{
- group: :pleroma,
- key: :mrf,
- tab: :mrf,
- label: "MRF",
- type: :group,
- description: "General MRF settings",
- children: [
- %{
- key: :policies,
- type: [:module, {:list, :module}],
- description:
- "A list of MRF policies enabled. Module names are shortened (removed leading `Pleroma.Web.ActivityPub.MRF.` part), but on adding custom module you need to use full name.",
- suggestions: {:list_behaviour_implementations, Pleroma.Web.ActivityPub.MRF}
- },
- %{
- key: :transparency,
- label: "MRF transparency",
- type: :boolean,
- description:
- "Make the content of your Message Rewrite Facility settings public (via nodeinfo)"
- },
- %{
- key: :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"
- ]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_simple,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.SimplePolicy",
- label: "MRF Simple",
- type: :group,
- description: "Simple ingress policies",
- children: [
- %{
- key: :media_removal,
- type: {:list, :string},
- description: "List of instances to strip media attachments from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :media_nsfw,
- label: "Media NSFW",
- type: {:list, :string},
- description: "List of instances to tag all media as NSFW (sensitive) from",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :federated_timeline_removal,
- type: {:list, :string},
- description:
- "List of instances to remove from the Federated (aka The Whole Known Network) Timeline",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :reject,
- type: {:list, :string},
- description: "List of instances to reject activities from (except deletes)",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :accept,
- type: {:list, :string},
- description: "List of instances to only accept activities from (except deletes)",
- suggestions: ["example.com", "*.example.com"]
- },
- %{
- key: :followers_only,
- type: {:list, :string},
- description: "Force posts from the given instances to be visible by followers only",
- 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"]
- },
- %{
- key: :reject_deletes,
- type: {:list, :string},
- description: "List of instances to reject deletions from",
- suggestions: ["example.com", "*.example.com"]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_activity_expiration,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
- label: "MRF Activity Expiration Policy",
- type: :group,
- description: "Adds automatic expiration to all local activities",
- children: [
- %{
- key: :days,
- type: :integer,
- description: "Default global expiration time for all local activities (in days)",
- suggestions: [90, 365]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_subchain,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.SubchainPolicy",
- label: "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, {:list, :string}},
- 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,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.RejectNonPublic",
- description: "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"
- },
- %{
- key: :allow_direct,
- type: :boolean,
- description: "Whether to allow direct messages"
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_hellthread,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.HellthreadPolicy",
- label: "MRF Hellthread",
- type: :group,
- description: "Block messages with excessive user mentions",
- children: [
- %{
- key: :delist_threshold,
- type: :integer,
- description:
- "Number of mentioned users after which the message gets removed from timelines and" <>
- "disables notifications. 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,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
- label: "MRF Keyword",
- type: :group,
- description: "Reject or Word-Replace messages with a keyword or regex",
- children: [
- %{
- key: :reject,
- type: {:list, :string},
- 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: {:list, :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", ~r/foo/iu]
- },
- %{
- key: :replace,
- type: {:list, :tuple},
- description:
- "A list of tuples containing {pattern, replacement}. Each pattern can be a string or a regular expression.",
- suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_mention,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.MentionPolicy",
- label: "MRF Mention",
- type: :group,
- description: "Block messages which mention a specific user",
- children: [
- %{
- key: :actors,
- type: {:list, :string},
- description: "A list of actors for which any post mentioning them will be dropped",
- suggestions: ["actor1", "actor2"]
- }
- ]
- },
- %{
- group: :pleroma,
- key: :mrf_vocabulary,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.VocabularyPolicy",
- label: "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,
- # tab: :mrf,
- # related_policy: "Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy",
- # type: :map,
- # 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",
- # suggestions: [
- # %{"example.org" => ["https://example.org/users/admin"]}
- # ]
- # ]
- # },
%{
group: :pleroma,
key: :media_proxy,
@@ -2288,6 +2004,12 @@
description: "Activity expiration queue",
suggestions: [10]
},
+ %{
+ key: :backup,
+ type: :integer,
+ description: "Backup queue",
+ suggestions: [1]
+ },
%{
key: :attachments_cleanup,
type: :integer,
@@ -3144,22 +2866,6 @@
}
]
},
- %{
- group: :pleroma,
- key: :mrf_normalize_markup,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.NormalizeMarkup",
- label: "MRF Normalize Markup",
- description: "MRF NormalizeMarkup settings. Scrub configured hypertext markup.",
- type: :group,
- children: [
- %{
- key: :scrub_policy,
- type: :module,
- suggestions: [Pleroma.HTML.Scrubber.Default]
- }
- ]
- },
%{
group: :pleroma,
key: Pleroma.User,
@@ -3349,33 +3055,6 @@
}
]
},
- %{
- group: :pleroma,
- key: :mrf_object_age,
- tab: :mrf,
- related_policy: "Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy",
- label: "MRF Object Age",
- type: :group,
- description:
- "Rejects or delists posts based on their timestamp deviance from your server's clock.",
- children: [
- %{
- key: :threshold,
- type: :integer,
- description: "Required age (in seconds) of a post before actions are taken.",
- suggestions: [172_800]
- },
- %{
- key: :actions,
- type: {:list, :atom},
- description:
- "A list of actions to apply to the post. `:delist` removes the post from public timelines; " <>
- "`:strip_followers` removes followers from the ActivityPub recipient list ensuring they won't be delivered to home timelines; " <>
- "`:reject` rejects the message entirely",
- suggestions: [:delist, :strip_followers, :reject]
- }
- ]
- },
%{
group: :pleroma,
key: :modules,
@@ -3708,5 +3387,76 @@
]
}
]
+ },
+ %{
+ group: :pleroma,
+ key: :majic_pool,
+ type: :group,
+ description: "Majic/libmagic configuration",
+ children: [
+ %{
+ key: :size,
+ type: :integer,
+ description: "Number of majic workers to start.",
+ suggestions: [2]
+ }
+ ]
+ },
+ %{
+ group: :pleroma,
+ key: Pleroma.User.Backup,
+ type: :group,
+ description: "Account Backup",
+ children: [
+ %{
+ key: :purge_after_days,
+ type: :integer,
+ description: "Remove backup achives after N days",
+ suggestions: [30]
+ },
+ %{
+ key: :limit_days,
+ type: :integer,
+ description: "Limit user to export not more often than once per N days",
+ suggestions: [7]
+ }
+ ]
+ },
+ %{
+ group: :prometheus,
+ key: Pleroma.Web.Endpoint.MetricsExporter,
+ type: :group,
+ description: "Prometheus app metrics endpoint configuration",
+ children: [
+ %{
+ key: :enabled,
+ type: :boolean,
+ description: "[Pleroma extension] Enables app metrics endpoint."
+ },
+ %{
+ key: :ip_whitelist,
+ type: [{:list, :string}, {:list, :charlist}, {:list, :tuple}],
+ description:
+ "[Pleroma extension] If non-empty, restricts access to app metrics endpoint to specified IP addresses."
+ },
+ %{
+ key: :auth,
+ type: [:boolean, :tuple],
+ description: "Enables HTTP Basic Auth for app metrics endpoint.",
+ suggestion: [false, {:basic, "myusername", "mypassword"}]
+ },
+ %{
+ key: :path,
+ type: :string,
+ description: "App metrics endpoint URI path.",
+ suggestions: ["/api/pleroma/app_metrics"]
+ },
+ %{
+ key: :format,
+ type: :atom,
+ description: "App metrics endpoint output format.",
+ suggestions: [:text, :protobuf]
+ }
+ ]
}
]
diff --git a/docs/API/admin_api.md b/docs/API/admin_api.md
index 7bf13daef..f7b5bcae7 100644
--- a/docs/API/admin_api.md
+++ b/docs/API/admin_api.md
@@ -20,12 +20,14 @@ Configuration options:
- `external`: only external users
- `active`: only active users
- `need_approval`: only unapproved users
+ - `unconfirmed`: only unconfirmed users
- `deactivated`: only deactivated users
- `is_admin`: users with admin role
- `is_moderator`: users with moderator role
- *optional* `page`: **integer** page number
- *optional* `page_size`: **integer** number of users per page (default is `50`)
- *optional* `tags`: **[string]** tags list
+ - *optional* `actor_types`: **[string]** actor type list (`Person`, `Service`, `Application`)
- *optional* `name`: **string** user display name
- *optional* `email`: **string** user email
- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com`
diff --git a/docs/API/chats.md b/docs/API/chats.md
index aa6119670..f50144c86 100644
--- a/docs/API/chats.md
+++ b/docs/API/chats.md
@@ -116,6 +116,10 @@ The modified chat message
This will return a list of chats that you have been involved in, sorted by their
last update (so new chats will be at the top).
+Parameters:
+
+- with_muted: Include chats from muted users (boolean).
+
Returned data:
```json
@@ -173,11 +177,14 @@ Returned data:
"created_at": "2020-04-21T15:06:45.000Z",
"emojis": [],
"id": "12",
- "unread": false
+ "unread": false,
+ "idempotency_key": "75442486-0874-440c-9db1-a7006c25a31f"
}
]
```
+- idempotency_key: The copy of the `idempotency-key` HTTP request header that can be used for optimistic message sending. Included only during the first few minutes after the message creation.
+
### Posting a chat message
Posting a chat message for given Chat id works like this:
diff --git a/docs/API/differences_in_mastoapi_responses.md b/docs/API/differences_in_mastoapi_responses.md
index 38865dc68..ba48a2ca1 100644
--- a/docs/API/differences_in_mastoapi_responses.md
+++ b/docs/API/differences_in_mastoapi_responses.md
@@ -9,9 +9,13 @@ Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mas
## Timelines
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
+
Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
+
Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
+Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance).
+
## Statuses
- `visibility`: has an additional possible value `list`
@@ -125,12 +129,30 @@ The `type` value is `pleroma:emoji_reaction`. Has these fields:
- `account`: The account of the user who reacted
- `status`: The status that was reacted on
+### ChatMention Notification (not default)
+
+This notification has to be requested explicitly.
+
+The `type` value is `pleroma:chat_mention`
+
+- `account`: The account who sent the message
+- `chat_message`: The chat message
+
+### Report Notification (not default)
+
+This notification has to be requested explicitly.
+
+The `type` value is `pleroma:report`
+
+- `account`: The account who reported
+- `report`: The report
+
## GET `/api/v1/notifications`
Accepts additional parameters:
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
-- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
+- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`, `pleroma:chat_mention`, `pleroma:report`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
## DELETE `/api/v1/notifications/destroy_multiple`
@@ -249,6 +271,12 @@ Has these additional fields under the `pleroma` object:
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
+For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
+
+## User muting and thread muting
+
+Both user muting and thread muting can be done for only a certain time by adding an `expires_in` parameter to the API calls and giving the expiration time in seconds.
+
## Not implemented
Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.
diff --git a/docs/API/pleroma_api.md b/docs/API/pleroma_api.md
index 3fd141bd2..7a0a80dad 100644
--- a/docs/API/pleroma_api.md
+++ b/docs/API/pleroma_api.md
@@ -615,3 +615,41 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
{"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
]
```
+
+## `POST /api/v1/pleroma/backups`
+### Create a user backup archive
+
+* Method: `POST`
+* Authentication: required
+* Params: none
+* Response: JSON
+* Example response:
+
+```json
+[{
+ "content_type": "application/zip",
+ "file_size": 0,
+ "inserted_at": "2020-09-10T16:18:03.000Z",
+ "processed": false,
+ "url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip"
+}]
+```
+
+## `GET /api/v1/pleroma/backups`
+### Lists user backups
+
+* Method: `GET`
+* Authentication: not required
+* Params: none
+* Response: JSON
+* Example response:
+
+```json
+[{
+ "content_type": "application/zip",
+ "file_size": 55457,
+ "inserted_at": "2020-09-10T16:18:03.000Z",
+ "processed": true,
+ "url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip"
+}]
+```
diff --git a/docs/API/prometheus.md b/docs/API/prometheus.md
index 19c564e3c..a5158d905 100644
--- a/docs/API/prometheus.md
+++ b/docs/API/prometheus.md
@@ -2,15 +2,37 @@
Pleroma includes support for exporting metrics via the [prometheus_ex](https://github.com/deadtrickster/prometheus.ex) library.
+Config example:
+
+```
+config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
+ enabled: true,
+ auth: {:basic, "myusername", "mypassword"},
+ ip_whitelist: ["127.0.0.1"],
+ path: "/api/pleroma/app_metrics",
+ format: :text
+```
+
+* `enabled` (Pleroma extension) enables the endpoint
+* `ip_whitelist` (Pleroma extension) could be used to restrict access only to specified IPs
+* `auth` sets the authentication (`false` for no auth; configurable to HTTP Basic Auth, see [prometheus-plugs](https://github.com/deadtrickster/prometheus-plugs#exporting) documentation)
+* `format` sets the output format (`:text` or `:protobuf`)
+* `path` sets the path to app metrics page
+
+
## `/api/pleroma/app_metrics`
+
### Exports Prometheus application metrics
+
* Method: `GET`
-* Authentication: not required
+* Authentication: not required by default (see configuration options above)
* Params: none
-* Response: JSON
+* Response: text
## Grafana
+
### Config example
+
The following is a config example to use with [Grafana](https://grafana.com)
```
diff --git a/docs/administration/CLI_tasks/frontend.md b/docs/administration/CLI_tasks/frontend.md
index 7d1c1e937..d4a48cb56 100644
--- a/docs/administration/CLI_tasks/frontend.md
+++ b/docs/administration/CLI_tasks/frontend.md
@@ -1,12 +1,23 @@
# Managing frontends
-`mix pleroma.frontend install [--ref ] [--file ] [--build-url ] [--path ] [--build-dir ]`
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl frontend install [--ref ] [--file ] [--build-url ] [--path ] [--build-dir ]
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.frontend install [--ref ] [--file ] [--build-url ] [--path ] [--build-dir ]
+ ```
Frontend can be installed either from local zip file, or automatically downloaded from the web.
-You can give all the options directly on the command like, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
+You can give all the options directly on the command line, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
+
+Currently, known `` values are:
-Currently known `` values are:
- [admin-fe](https://git.pleroma.social/pleroma/admin-fe)
- [kenoma](http://git.pleroma.social/lambadalambda/kenoma)
- [pleroma-fe](http://git.pleroma.social/pleroma/pleroma-fe)
@@ -19,51 +30,67 @@ You can still install frontends that are not configured, see below.
For a frontend configured under the `available` key, it's enough to install it by name.
-```sh tab="OTP"
-./bin/pleroma_ctl frontend install pleroma
-```
+=== "OTP"
-```sh tab="From Source"
-mix pleroma.frontend install pleroma
-```
+ ```sh
+ ./bin/pleroma_ctl frontend install pleroma
+ ```
-This will download the latest build for the the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
+=== "From Source"
-You can override any of the details. To install a pleroma build from a different url, you could do this:
+ ```sh
+ mix pleroma.frontend install pleroma
+ ```
-```sh tab="OPT"
-./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
-```
+This will download the latest build for the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
-```sh tab="From Source"
-mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
-```
+You can override any of the details. To install a pleroma build from a different URL, you could do this:
+
+=== "OTP"
+
+ ```sh
+ ./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
+ ```
+
+=== "From Source"
+
+ ```sh
+ mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
+ ```
Similarly, you can also install from a local zip file.
-```sh tab="OTP"
-./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
-```
+=== "OTP"
-```sh tab="From Source"
-mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
-```
+ ```sh
+ ./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
+ ```
-The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`
+=== "From Source"
-Careful: This folder will be completely replaced on installation
+ ```sh
+ mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
+ ```
+
+The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
+
+Careful: This folder will be completely replaced on installation.
## Example installation for an unknown frontend
-The installation process is the same, but you will have to give all the needed options on the commond line. For example:
+The installation process is the same, but you will have to give all the needed options on the command line. For example:
-```sh tab="OTP"
-./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
-```
+=== "OTP"
-```sh tab="From Source"
-mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
-```
+ ```sh
+ ./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
+ ```
-If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`
+=== "From Source"
+
+ ```sh
+ mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
+ ```
+
+If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
diff --git a/docs/administration/CLI_tasks/instance.md b/docs/administration/CLI_tasks/instance.md
index d6913280a..982b22bf3 100644
--- a/docs/administration/CLI_tasks/instance.md
+++ b/docs/administration/CLI_tasks/instance.md
@@ -40,3 +40,5 @@ If any of the options are left unspecified, you will be prompted interactively.
- `--strip-uploads ` - use ExifTool to strip uploads of sensitive location data
- `--anonymize-uploads ` - randomize uploaded filenames
- `--dedupe-uploads ` - store files based on their hash to reduce data storage requirements if duplicates are uploaded with different filenames
+- `--skip-release-env` - skip generation the release environment file
+- `--release-env-file` - release environment file path
diff --git a/docs/ap_extensions.md b/docs/ap_extensions.md
index c4550a1ac..3d1caeb3e 100644
--- a/docs/ap_extensions.md
+++ b/docs/ap_extensions.md
@@ -1,11 +1,41 @@
-# ChatMessages
+# AP Extensions
+## Actor endpoints
-ChatMessages are the messages sent in 1-on-1 chats. They are similar to
+The following endpoints are additionally present into our actors.
+
+- `oauthRegistrationEndpoint` (`http://litepub.social/ns#oauthRegistrationEndpoint`)
+- `uploadMedia` (`https://www.w3.org/ns/activitystreams#uploadMedia`)
+
+### oauthRegistrationEndpoint
+
+Points to MastodonAPI `/api/v1/apps` for now.
+
+See
+
+### uploadMedia
+
+Inspired by , it is part of the ActivityStreams namespace because it used to be part of the ActivityPub specification and got removed from it.
+
+Content-Type: multipart/form-data
+
+Parameters:
+- (required) `file`: The file being uploaded
+- (optionnal) `description`: A plain-text description of the media, for accessibility purposes.
+
+Response: HTTP 201 Created with the object into the body, no `Location` header provided as it doesn't have an `id`
+
+The object given in the reponse should then be inserted into an Object's `attachment` field.
+
+## ChatMessages
+
+`ChatMessage`s are the messages sent in 1-on-1 chats. They are similar to
`Note`s, but the addresing is done by having a single AP actor in the `to`
field. Addressing multiple actors is not allowed. These messages are always
private, there is no public version of them. They are created with a `Create`
activity.
+They are part of the `litepub` namespace as `http://litepub.social/ns#ChatMessage`.
+
Example:
```json
diff --git a/docs/clients.md b/docs/clients.md
index f84295b1f..3d81763e1 100644
--- a/docs/clients.md
+++ b/docs/clients.md
@@ -7,97 +7,105 @@ Feel free to contact us to be added to this list!
- Homepage:
- Source Code:
- Platforms: Windows, Mac, Linux
-- Features: Streaming Ready
+- Features: MastoAPI, Streaming Ready
### Social
- Source Code:
- Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted)
- Platforms: Linux (GNOME)
- Note(2019-01-28): Not at a pre-alpha stage yet
+- Features: MastoAPI
### Whalebird
- Homepage:
- Source Code:
- Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto)
- Platforms: Windows, Mac, Linux
-- Features: Streaming Ready
+- Features: MastoAPI, Streaming Ready
## Handheld
+### AndStatus
+- Homepage:
+- Source Code:
+- Platforms: Android
+- Features: MastoAPI, ActivityPub (Client-to-Server)
+
### Amaroq
- Homepage:
- Source Code:
- Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy)
- Platforms: iOS
-- Features: No Streaming
+- Features: MastoAPI, No Streaming
### Fedilab
- Homepage:
- Source Code:
- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab)
- Platforms: Android
-- Features: Streaming Ready, Moderation, Text Formatting
+- Features: MastoAPI, Streaming Ready, Moderation, Text Formatting
### Kyclos
- Source Code:
- Platforms: SailfishOS
-- Features: No Streaming
+- Features: MastoAPI, No Streaming
### Husky
- Source code:
- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky)
- Platforms: Android
-- Features: No Streaming, Emoji Reactions, Text Formatting, FE Stickers
+- Features: MastoAPI, No Streaming, Emoji Reactions, Text Formatting, FE Stickers
### Fedi
- Homepage:
- Source Code: Proprietary, but gratis
- Platforms: iOS, Android
-- Features: Pleroma-specific features like Reactions
+- Features: MastoAPI, Pleroma-specific features like Reactions
### Tusky
- Homepage:
- Source Code:
- Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck)
- Platforms: Android
-- Features: No Streaming
+- Features: MastoAPI, No Streaming
### Twidere
- Homepage:
- Source Code:
- Contact:
- Platform: Android
-- Features: No Streaming
+- Features: MastoAPI, No Streaming
### Indigenous
- Homepage:
- Source Code:
-- Contact: [@realize.be@realize.be](@realize.be@realize.be)
+- Contact: [@swentel@realize.be](https://realize.be)
- Platforms: Android
-- Features: No Streaming
+- Features: MastoAPI, No Streaming
## Alternative Web Interfaces
### Brutaldon
- Homepage:
- Source Code:
- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
-- Features: No Streaming
+- Features: MastoAPI, No Streaming
### Halcyon
- Source Code:
- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
-- Features: Streaming Ready
+- Features: MastoAPI, Streaming Ready
### Pinafore
- Homepage:
- Source Code:
- Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore)
- Note: Pleroma support is a secondary goal
-- Features: No Streaming
+- Features: MastoAPI, No Streaming
### Sengi
- Homepage:
- Source Code:
- Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app)
+- Features: MastoAPI
### DashFE
- Source Code:
@@ -107,3 +115,4 @@ Feel free to contact us to be added to this list!
- Source Code:
- Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r)
- Features: Does not requires JavaScript
+- Features: MastoAPI
diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md
index 0b13d7e88..ebf95ebc9 100644
--- a/docs/configuration/cheatsheet.md
+++ b/docs/configuration/cheatsheet.md
@@ -45,6 +45,7 @@ To add configuration to your config file, you can copy it from the base config.
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.
+* `autofollowing_nicknames`: Set to nicknames of (local) users that automatically follows every newly registered user.
* `attachment_links`: Set to true to enable automatically adding attachment link text to statuses.
* `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`.
@@ -1077,6 +1078,20 @@ Control favicons for instances.
* `enabled`: Allow/disallow displaying and getting instances favicons
+## Pleroma.User.Backup
+
+!!! note
+ Requires enabled email
+
+* `:purge_after_days` an integer, remove backup achives after N days.
+* `:limit_days` an integer, limit user to export not more often than once per N days.
+* `:dir` a string with a path to backup temporary directory or `nil` to let Pleroma choose temporary directory in the following order:
+ 1. the directory named by the TMPDIR environment variable
+ 2. the directory named by the TEMP environment variable
+ 3. the directory named by the TMP environment variable
+ 4. C:\TMP on Windows or /tmp on Unix-like operating systems
+ 5. as a last resort, the current working directory
+
## Frontend management
Frontends in Pleroma are swappable - you can specify which one to use here.
diff --git a/docs/configuration/howto_ejabberd.md b/docs/configuration/howto_ejabberd.md
new file mode 100644
index 000000000..520a0acbc
--- /dev/null
+++ b/docs/configuration/howto_ejabberd.md
@@ -0,0 +1,136 @@
+# Configuring Ejabberd (XMPP Server) to use Pleroma for authentication
+
+If you want to give your Pleroma users an XMPP (chat) account, you can configure [Ejabberd](https://github.com/processone/ejabberd) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account.
+
+In general, you just have to follow the configuration described at [https://docs.ejabberd.im/admin/configuration/authentication/#external-script](https://docs.ejabberd.im/admin/configuration/authentication/#external-script). Please read this section carefully.
+
+Copy the script below to suitable path on your system and set owner and permissions. Also do not forget adjusting `PLEROMA_HOST` and `PLEROMA_PORT`, if necessary.
+
+```bash
+cp pleroma_ejabberd_auth.py /etc/ejabberd/pleroma_ejabberd_auth.py
+chown ejabberd /etc/ejabberd/pleroma_ejabberd_auth.py
+chmod 700 /etc/ejabberd/pleroma_ejabberd_auth.py
+```
+
+Set external auth params in ejabberd.yaml file:
+
+```bash
+auth_method: [external]
+extauth_program: "python3 /etc/ejabberd/pleroma_ejabberd_auth.py"
+extauth_instances: 3
+auth_use_cache: false
+```
+
+Restart / reload your ejabberd service.
+
+After restarting your Ejabberd server, your users should now be able to connect with their Pleroma credentials.
+
+
+```python
+import sys
+import struct
+import http.client
+from base64 import b64encode
+import logging
+
+
+PLEROMA_HOST = "127.0.0.1"
+PLEROMA_PORT = "4000"
+AUTH_ENDPOINT = "/api/v1/accounts/verify_credentials"
+USER_ENDPOINT = "/api/v1/accounts"
+LOGFILE = "/var/log/ejabberd/pleroma_auth.log"
+
+logging.basicConfig(filename=LOGFILE, level=logging.INFO)
+
+
+# Pleroma functions
+def create_connection():
+ return http.client.HTTPConnection(PLEROMA_HOST, PLEROMA_PORT)
+
+
+def verify_credentials(user: str, password: str) -> bool:
+ user_pass_b64 = b64encode("{}:{}".format(
+ user, password).encode('utf-8')).decode("ascii")
+ params = {}
+ headers = {
+ "Authorization": "Basic {}".format(user_pass_b64)
+ }
+
+ try:
+ conn = create_connection()
+ conn.request("GET", AUTH_ENDPOINT, params, headers)
+
+ response = conn.getresponse()
+ if response.status == 200:
+ return True
+
+ return False
+ except Exception as e:
+ logging.info("Can not connect: %s", str(e))
+ return False
+
+
+def does_user_exist(user: str) -> bool:
+ conn = create_connection()
+ conn.request("GET", "{}/{}".format(USER_ENDPOINT, user))
+
+ response = conn.getresponse()
+ if response.status == 200:
+ return True
+
+ return False
+
+
+def auth(username: str, server: str, password: str) -> bool:
+ return verify_credentials(username, password)
+
+
+def isuser(username, server):
+ return does_user_exist(username)
+
+
+def read():
+ (pkt_size,) = struct.unpack('>H', bytes(sys.stdin.read(2), encoding='utf8'))
+ pkt = sys.stdin.read(pkt_size)
+ cmd = pkt.split(':')[0]
+ if cmd == 'auth':
+ username, server, password = pkt.split(':', 3)[1:]
+ write(auth(username, server, password))
+ elif cmd == 'isuser':
+ username, server = pkt.split(':', 2)[1:]
+ write(isuser(username, server))
+ elif cmd == 'setpass':
+ # u, s, p = pkt.split(':', 3)[1:]
+ write(False)
+ elif cmd == 'tryregister':
+ # u, s, p = pkt.split(':', 3)[1:]
+ write(False)
+ elif cmd == 'removeuser':
+ # u, s = pkt.split(':', 2)[1:]
+ write(False)
+ elif cmd == 'removeuser3':
+ # u, s, p = pkt.split(':', 3)[1:]
+ write(False)
+ else:
+ write(False)
+
+
+def write(result):
+ if result:
+ sys.stdout.write('\x00\x02\x00\x01')
+ else:
+ sys.stdout.write('\x00\x02\x00\x00')
+ sys.stdout.flush()
+
+
+if __name__ == "__main__":
+ logging.info("Starting pleroma ejabberd auth daemon...")
+ while True:
+ try:
+ read()
+ except Exception as e:
+ logging.info(
+ "Error while processing data from ejabberd %s", str(e))
+ pass
+
+```
\ No newline at end of file
diff --git a/docs/configuration/optimizing_beam.md b/docs/configuration/optimizing_beam.md
new file mode 100644
index 000000000..e336bd36c
--- /dev/null
+++ b/docs/configuration/optimizing_beam.md
@@ -0,0 +1,66 @@
+# Optimizing the BEAM
+
+Pleroma is built upon the Erlang/OTP VM known as BEAM. The BEAM VM is highly optimized for latency, but this has drawbacks in environments without dedicated hardware. One of the tricks used by the BEAM VM is [busy waiting](https://en.wikipedia.org/wiki/Busy_waiting). This allows the application to pretend to be busy working so the OS kernel does not pause the application process and switch to another process waiting for the CPU to execute its workload. It does this by spinning for a period of time which inflates the apparent CPU usage of the application so it is immediately ready to execute another task. This can be observed with utilities like **top(1)** which will show consistently high CPU usage for the process. Switching between procesess is a rather expensive operation and also clears CPU caches further affecting latency and performance. The goal of busy waiting is to avoid this penalty.
+
+This strategy is very successful in making a performant and responsive application, but is not desirable on Virtual Machines or hardware with few CPU cores. Pleroma instances are often deployed on the same server as the required PostgreSQL database which can lead to situations where the Pleroma application is holding the CPU in a busy-wait loop and as a result the database cannot process requests in a timely manner. The fewer CPUs available, the more this problem is exacerbated. The latency is further amplified by the OS being installed on a Virtual Machine as the Hypervisor uses CPU time-slicing to pause the entire OS and switch between other tasks.
+
+More adventurous admins can be creative with CPU affinity (e.g., *taskset* for Linux and *cpuset* on FreeBSD) to pin processes to specific CPUs and eliminate much of this contention. The most important advice is to run as few processes as possible on your server to achieve the best performance. Even idle background processes can occasionally create [software interrupts](https://en.wikipedia.org/wiki/Interrupt) and take attention away from the executing process creating latency spikes and invalidation of the CPU caches as they must be cleared when switching between processes for security.
+
+Please only change these settings if you are experiencing issues or really know what you are doing. In general, there's no need to change these settings.
+
+## VPS Provider Recommendations
+
+### Good
+
+* Hetzner Cloud
+
+### Bad
+
+* AWS (known to use burst scheduling)
+
+
+## Example configurations
+
+Tuning the BEAM requires you provide a config file normally called [vm.args](http://erlang.org/doc/man/erl.html#emulator-flags). If you are using systemd to manage the service you can modify the unit file as such:
+
+`ExecStart=/usr/bin/elixir --erl '-args_file /opt/pleroma/config/vm.args' -S /usr/bin/mix phx.server`
+
+Check your OS documentation to adopt a similar strategy on other platforms.
+
+### Virtual Machine and/or few CPU cores
+
+Disable the busy-waiting. This should generally only be done if you're on a platform that does burst scheduling, like AWS.
+
+**vm.args:**
+
+```
++sbwt none
++sbwtdcpu none
++sbwtdio none
+```
+
+### Dedicated Hardware
+
+Enable more busy waiting, increase the internal maximum limit of BEAM processes and ports. You can use this if you run on dedicated hardware, but it is not necessary.
+
+**vm.args:**
+
+```
++P 16777216
++Q 16777216
++K true
++A 128
++sbt db
++sbwt very_long
++swt very_low
++sub true
++Mulmbcs 32767
++Mumbcgs 1
++Musmbcs 2047
+```
+
+## Additional Reading
+
+* [WhatsApp: Scaling to Millions of Simultaneous Connections](https://www.erlang-factory.com/upload/presentations/558/efsf2012-whatsapp-scaling.pdf)
+* [Preemptive Scheduling and Spinlocks](https://www.uio.no/studier/emner/matnat/ifi/nedlagte-emner/INF3150/h03/annet/slides/preemptive.pdf)
+* [The Curious Case of BEAM CPU Usage](https://stressgrid.com/blog/beam_cpu_usage/)
diff --git a/docs/dev.md b/docs/dev.md
index 22e0691f1..aa89a941f 100644
--- a/docs/dev.md
+++ b/docs/dev.md
@@ -21,3 +21,26 @@ This document contains notes and guidelines for Pleroma developers.
## Auth-related configuration, OAuth consumer mode etc.
See `Authentication` section of [the configuration cheatsheet](configuration/cheatsheet.md#authentication).
+
+## MRF policies descriptions
+
+If MRF policy depends on config, it can be added into MRF tab to adminFE by adding `config_description/0` method, which returns map with special structure.
+
+Example:
+
+```elixir
+%{
+ key: :mrf_activity_expiration,
+ related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
+ label: "MRF Activity Expiration Policy",
+ description: "Adds automatic expiration to all local activities",
+ children: [
+ %{
+ key: :days,
+ type: :integer,
+ description: "Default global expiration time for all local activities (in days)",
+ suggestions: [90, 365]
+ }
+ ]
+ }
+```
diff --git a/docs/installation/alpine_linux_en.md b/docs/installation/alpine_linux_en.md
index d89c7f46f..62f2fb778 100644
--- a/docs/installation/alpine_linux_en.md
+++ b/docs/installation/alpine_linux_en.md
@@ -13,6 +13,7 @@ It assumes that you have administrative rights, either as root or a user with [s
* `erlang-parsetools`
* `erlang-xmerl`
* `git`
+* `file-dev`
* Development Tools
* `cmake`
@@ -42,7 +43,7 @@ sudo apk upgrade
* Install some tools, which are needed later:
```shell
-sudo apk add git build-base cmake
+sudo apk add git build-base cmake file-dev
```
### Install Elixir and Erlang
diff --git a/docs/installation/arch_linux_en.md b/docs/installation/arch_linux_en.md
index 724b4660a..0eb6d2d5f 100644
--- a/docs/installation/arch_linux_en.md
+++ b/docs/installation/arch_linux_en.md
@@ -10,6 +10,7 @@ This guide will assume that you have administrative rights, either as root or a
* `git`
* `base-devel`
* `cmake`
+* `file`
#### Optional packages used in this guide
@@ -30,7 +31,7 @@ sudo pacman -Syu
* Install some of the above mentioned programs:
```shell
-sudo pacman -S git base-devel elixir cmake
+sudo pacman -S git base-devel elixir cmake file
```
### Install PostgreSQL
diff --git a/docs/installation/debian_based_en.md b/docs/installation/debian_based_en.md
index eac499a29..75ceb6595 100644
--- a/docs/installation/debian_based_en.md
+++ b/docs/installation/debian_based_en.md
@@ -10,6 +10,7 @@ This guide will assume you are on Debian Stretch. This guide should also work wi
* `elixir` (1.8+, Follow the guide to install from the Erlang Solutions repo or use [asdf](https://github.com/asdf-vm/asdf) as the pleroma user)
* `erlang-dev`
* `erlang-nox`
+* `libmagic-dev`
* `git`
* `build-essential`
* `cmake`
@@ -34,7 +35,7 @@ sudo apt full-upgrade
* Install some of the above mentioned programs:
```shell
-sudo apt install git build-essential postgresql postgresql-contrib cmake
+sudo apt install git build-essential postgresql postgresql-contrib cmake libmagic-devel
```
### Install Elixir and Erlang
@@ -100,6 +101,7 @@ sudo -Hu pleroma mix deps.get
mv config/{generated_config.exs,prod.secret.exs}
```
+
* The previous command creates also the file `config/setup_db.psql`, with which you can create the database:
```shell
diff --git a/docs/installation/debian_based_jp.md b/docs/installation/debian_based_jp.md
index 764afbe1a..94e22325c 100644
--- a/docs/installation/debian_based_jp.md
+++ b/docs/installation/debian_based_jp.md
@@ -17,6 +17,7 @@
- `git`
- `build-essential`
- `cmake`
+- `libmagic-dev`
#### このガイドで利用している追加パッケージ
@@ -36,7 +37,7 @@ sudo apt full-upgrade
* 上記に挙げたパッケージをインストールしておきます。
```
-sudo apt install git build-essential postgresql postgresql-contrib cmake ffmpeg imagemagick
+sudo apt install git build-essential postgresql postgresql-contrib cmake ffmpeg imagemagick libmagic-dev
```
### ElixirとErlangをインストールします
diff --git a/docs/installation/gentoo_en.md b/docs/installation/gentoo_en.md
index 638fc4e47..f2380ab72 100644
--- a/docs/installation/gentoo_en.md
+++ b/docs/installation/gentoo_en.md
@@ -29,6 +29,7 @@ Gentoo quite pointedly does not come with a cron daemon installed, and as such i
* `dev-lang/elixir`
* `dev-vcs/git`
* `dev-util/cmake`
+* `sys-apps/file`
#### Optional ebuilds used in this guide
@@ -50,7 +51,7 @@ Gentoo quite pointedly does not come with a cron daemon installed, and as such i
* Emerge all required the required and suggested software in one go:
```shell
- # emerge --ask dev-db/postgresql dev-lang/elixir dev-vcs/git www-servers/nginx app-crypt/certbot app-crypt/certbot-nginx dev-util/cmake
+ # emerge --ask dev-db/postgresql dev-lang/elixir dev-vcs/git www-servers/nginx app-crypt/certbot app-crypt/certbot-nginx dev-util/cmake sys-apps/file
```
If you would not like to install the optional packages, remove them from this line.
diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md
index 92584d80d..63eda63ca 100644
--- a/docs/installation/otp_en.md
+++ b/docs/installation/otp_en.md
@@ -27,22 +27,23 @@ Other than things bundled in the OTP release Pleroma depends on:
* PostgreSQL (also utilizes extensions in postgresql-contrib)
* nginx (could be swapped with another reverse proxy but this guide covers only it)
* certbot (for Let's Encrypt certificates, could be swapped with another ACME client, but this guide covers only it)
+* libmagic/file
=== "Alpine"
```
echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories
apk update
- apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot
+ apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot file-dev
```
=== "Debian/Ubuntu"
```
- apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot
+ apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot libmagic-dev
```
### Installing optional packages
-Per [`docs/installation/optional/media_graphics_packages.md`](docs/installation/optional/media_graphics_packages.md):
+Per [`docs/installation/optional/media_graphics_packages.md`](optional/media_graphics_packages.md):
* ImageMagick
* ffmpeg
* exiftool
@@ -158,7 +159,7 @@ su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
# su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate --migrations-path priv/repo/optional_migrations/rum_indexing/"
# Start the instance to verify that everything is working as expected
-su pleroma -s $SHELL -lc "./bin/pleroma daemon"
+su pleroma -s $SHELL -lc "export $(cat /opt/pleroma/config/pleroma.env); ./bin/pleroma daemon"
# Wait for about 20 seconds and query the instance endpoint, if it shows your uri, name and email correctly, you are configured correctly
sleep 20 && curl http://localhost:4000/api/v1/instance
@@ -310,4 +311,3 @@ This will create an account withe the username of 'joeuser' with the email addre
## Questions
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
-
diff --git a/installation/pleroma.service b/installation/pleroma.service
index 5dcbc1387..8338228d8 100644
--- a/installation/pleroma.service
+++ b/installation/pleroma.service
@@ -29,8 +29,6 @@ ProtectHome=true
ProtectSystem=full
; Sets up a new /dev mount for the process and only adds API pseudo devices like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled by default because it may not work on devices like the Raspberry Pi.
PrivateDevices=false
-; Ensures that the service process and all its children can never gain new privileges through execve().
-NoNewPrivileges=true
; Drops the sysadmin capability from the daemon.
CapabilityBoundingSet=~CAP_SYS_ADMIN
diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex
index fc21ae062..ac8688424 100644
--- a/lib/mix/tasks/pleroma/instance.ex
+++ b/lib/mix/tasks/pleroma/instance.ex
@@ -284,7 +284,7 @@ defp write_robots_txt(static_dir, indexable, template_dir) do
defp upload_filters(filters) when is_map(filters) do
enabled_filters =
if filters.strip do
- [Pleroma.Upload.Filter.ExifTool]
+ [Pleroma.Upload.Filter.Exiftool]
else
[]
end
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
index c454f1d28..544ba3550 100644
--- a/lib/mix/tasks/pleroma/user.ex
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -419,7 +419,7 @@ def run(["list"]) do
|> Enum.each(fn user ->
shell_info(
"#{user.nickname} moderator: #{user.is_moderator}, admin: #{user.is_admin}, locked: #{
- user.locked
+ user.is_locked
}, deactivated: #{user.deactivated}"
)
end)
@@ -447,10 +447,10 @@ defp set_admin(user, value) do
defp set_locked(user, value) do
{:ok, user} =
user
- |> Changeset.change(%{locked: value})
+ |> Changeset.change(%{is_locked: value})
|> User.update_and_set_cache()
- shell_info("Locked status of #{user.nickname}: #{user.locked}")
+ shell_info("Locked status of #{user.nickname}: #{user.is_locked}")
user
end
diff --git a/lib/phoenix/transports/web_socket/raw.ex b/lib/phoenix/transports/web_socket/raw.ex
index aab7fad99..c3665bebe 100644
--- a/lib/phoenix/transports/web_socket/raw.ex
+++ b/lib/phoenix/transports/web_socket/raw.ex
@@ -31,7 +31,12 @@ def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do
case conn do
%{halted: false} = conn ->
- case Transport.connect(endpoint, handler, transport, __MODULE__, nil, conn.params) do
+ case handler.connect(%{
+ endpoint: endpoint,
+ transport: transport,
+ options: [serializer: nil],
+ params: conn.params
+ }) do
{:ok, socket} ->
{:ok, conn, {__MODULE__, {socket, opts}}}
diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex
index 17af04257..553834da0 100644
--- a/lib/pleroma/activity.ex
+++ b/lib/pleroma/activity.ex
@@ -14,6 +14,7 @@ defmodule Pleroma.Activity do
alias Pleroma.ReportNote
alias Pleroma.ThreadMute
alias Pleroma.User
+ alias Pleroma.Web.ActivityPub.ActivityPub
import Ecto.Changeset
import Ecto.Query
@@ -153,6 +154,18 @@ def get_bookmark(%Activity{} = activity, %User{} = user) do
def get_bookmark(_, _), do: nil
+ def get_report(activity_id) do
+ opts = %{
+ type: "Flag",
+ skip_preload: true,
+ preload_report_notes: true
+ }
+
+ ActivityPub.fetch_activities_query([], opts)
+ |> where(id: ^activity_id)
+ |> Repo.one()
+ end
+
def change(struct, params \\ %{}) do
struct
|> cast(params, [:data, :recipients])
diff --git a/lib/pleroma/activity/ir/topics.ex b/lib/pleroma/activity/ir/topics.ex
index 9e65bedad..fe2e8cb5c 100644
--- a/lib/pleroma/activity/ir/topics.ex
+++ b/lib/pleroma/activity/ir/topics.ex
@@ -40,7 +40,8 @@ defp visibility_tags(object, activity) do
end
defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
- tags ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
+ tags ++
+ remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
end
defp item_creation_tags(tags, _, _) do
@@ -55,9 +56,19 @@ defp hashtags_to_topics(%{data: %{"tag" => tags}}) do
defp hashtags_to_topics(_), do: []
+ defp remote_topics(%{local: true}), do: []
+
+ defp remote_topics(%{actor: actor}) when is_binary(actor),
+ do: ["public:remote:" <> URI.parse(actor).host]
+
+ defp remote_topics(_), do: []
+
defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: []
defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"]
+ defp attachment_topics(_object, %{actor: actor}) when is_binary(actor),
+ do: ["public:media", "public:remote:media:" <> URI.parse(actor).host]
+
defp attachment_topics(_object, _act), do: ["public:media"]
end
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index 958e32db2..7c4cd9626 100644
--- a/lib/pleroma/application.ex
+++ b/lib/pleroma/application.ex
@@ -95,11 +95,12 @@ def start(_type, _args) do
[
Pleroma.Stats,
Pleroma.JobQueueMonitor,
+ {Majic.Pool, [name: Pleroma.MajicPool, pool_size: Config.get([:majic_pool, :size], 2)]},
{Oban, Config.get(Oban)}
] ++
task_children(@env) ++
dont_run_in_test(@env) ++
- chat_child(@env, chat_enabled?()) ++
+ chat_child(chat_enabled?()) ++
[
Pleroma.Web.Endpoint,
Pleroma.Gopher.Server
@@ -150,7 +151,10 @@ defp setup_instrumenters do
Pleroma.Web.Endpoint.MetricsExporter.setup()
Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
- Pleroma.Web.Endpoint.Instrumenter.setup()
+
+ # Note: disabled until prometheus-phx is integrated into prometheus-phoenix:
+ # Pleroma.Web.Endpoint.Instrumenter.setup()
+ PrometheusPhx.setup()
end
defp cachex_children do
@@ -164,7 +168,11 @@ defp cachex_children do
build_cachex("web_resp", limit: 2500),
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
build_cachex("failed_proxy_url", limit: 2500),
- build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000)
+ build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000),
+ build_cachex("chat_message_id_idempotency_key",
+ expiration: chat_message_id_idempotency_key_expiration(),
+ limit: 500_000
+ )
]
end
@@ -174,6 +182,9 @@ defp emoji_packs_expiration,
defp idempotency_expiration,
do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))
+ defp chat_message_id_idempotency_key_expiration,
+ do: expiration(default: :timer.minutes(2), interval: :timer.seconds(60))
+
defp seconds_valid_interval,
do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid]))
@@ -201,11 +212,14 @@ defp dont_run_in_test(_) do
]
end
- defp chat_child(_env, true) do
- [Pleroma.Web.ChatChannel.ChatChannelState]
+ defp chat_child(true) do
+ [
+ Pleroma.Web.ChatChannel.ChatChannelState,
+ {Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
+ ]
end
- defp chat_child(_, _), do: []
+ defp chat_child(_), do: []
defp task_children(:test) do
[
diff --git a/lib/pleroma/captcha/kocaptcha.ex b/lib/pleroma/captcha/kocaptcha.ex
index 337506647..201b55ab4 100644
--- a/lib/pleroma/captcha/kocaptcha.ex
+++ b/lib/pleroma/captcha/kocaptcha.ex
@@ -10,7 +10,7 @@ defmodule Pleroma.Captcha.Kocaptcha do
def new do
endpoint = Pleroma.Config.get!([__MODULE__, :endpoint])
- case Tesla.get(endpoint <> "/new") do
+ case Pleroma.HTTP.get(endpoint <> "/new") do
{:error, _} ->
%{error: :kocaptcha_service_unavailable}
diff --git a/lib/pleroma/conversation.ex b/lib/pleroma/conversation.ex
index e76eb0087..77933f0be 100644
--- a/lib/pleroma/conversation.ex
+++ b/lib/pleroma/conversation.ex
@@ -43,7 +43,7 @@ def get_for_ap_id(ap_id) do
def maybe_create_recipientships(participation, activity) do
participation = Repo.preload(participation, :recipients)
- if participation.recipients |> Enum.empty?() do
+ if Enum.empty?(participation.recipients) do
recipients = User.get_all_by_ap_id(activity.recipients)
RecipientShip.create(recipients, participation)
end
@@ -69,10 +69,6 @@ def create_or_bump_for(activity, opts \\ []) do
Enum.map(users, fn user ->
invisible_conversation = Enum.any?(users, &User.blocks?(user, &1))
- unless invisible_conversation do
- User.increment_unread_conversation_count(conversation, user)
- end
-
opts = Keyword.put(opts, :invisible_conversation, invisible_conversation)
{:ok, participation} =
diff --git a/lib/pleroma/conversation/participation.ex b/lib/pleroma/conversation/participation.ex
index 8bc3e85d6..4c32b273a 100644
--- a/lib/pleroma/conversation/participation.ex
+++ b/lib/pleroma/conversation/participation.ex
@@ -63,21 +63,10 @@ def mark_as_read(%User{} = user, %Conversation{} = conversation) do
end
end
- def mark_as_read(participation) do
- __MODULE__
- |> where(id: ^participation.id)
- |> update(set: [read: true])
- |> select([p], p)
- |> Repo.update_all([])
- |> case do
- {1, [participation]} ->
- participation = Repo.preload(participation, :user)
- User.set_unread_conversation_count(participation.user)
- {:ok, participation}
-
- error ->
- error
- end
+ def mark_as_read(%__MODULE__{} = participation) do
+ participation
+ |> change(read: true)
+ |> Repo.update()
end
def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do
@@ -93,7 +82,6 @@ def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do
|> update([p], set: [read: true])
|> Repo.update_all([])
- {:ok, user} = User.set_unread_conversation_count(user)
{:ok, user, []}
end
@@ -108,7 +96,6 @@ def mark_all_as_read(%User{} = user) do
|> select([p], p)
|> Repo.update_all([])
- {:ok, user} = User.set_unread_conversation_count(user)
{:ok, user, participations}
end
@@ -220,6 +207,12 @@ def set_recipients(participation, user_ids) do
{:ok, Repo.preload(participation, :recipients, force: true)}
end
+ @spec unread_count(User.t()) :: integer()
+ def unread_count(%User{id: user_id}) do
+ from(q in __MODULE__, where: q.user_id == ^user_id and q.read == false)
+ |> Repo.aggregate(:count, :id)
+ end
+
def unread_conversation_count_for_user(user) do
from(p in __MODULE__,
where: p.user_id == ^user.id,
diff --git a/lib/pleroma/docs/json.ex b/lib/pleroma/docs/json.ex
index 13618b509..a583e2a5b 100644
--- a/lib/pleroma/docs/json.ex
+++ b/lib/pleroma/docs/json.ex
@@ -11,7 +11,11 @@ defmodule Pleroma.Docs.JSON do
@spec compile :: :ok
def compile do
- :persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(@raw_descriptions))
+ descriptions =
+ Pleroma.Web.ActivityPub.MRF.config_descriptions()
+ |> Enum.reduce(@raw_descriptions, fn description, acc -> [description | acc] end)
+
+ :persistent_term.put(@term, Pleroma.Docs.Generator.convert_to_strings(descriptions))
end
@spec compiled_descriptions :: Map.t()
diff --git a/lib/pleroma/emails/admin_email.ex b/lib/pleroma/emails/admin_email.ex
index 8979db2f8..02274554f 100644
--- a/lib/pleroma/emails/admin_email.ex
+++ b/lib/pleroma/emails/admin_email.ex
@@ -18,10 +18,6 @@ defp instance_notify_email do
Keyword.get(instance_config(), :notify_email, instance_config()[:email])
end
- defp user_url(user) do
- Helpers.user_feed_url(Pleroma.Web.Endpoint, :feed_redirect, user.id)
- end
-
def test_email(mail_to \\ nil) do
html_body = """
Instance Test Email
@@ -69,8 +65,8 @@ def report(to, reporter, account, statuses, comment) do
end
html_body = """
-