Merge remote-tracking branch 'origin/develop' into global-status-expiration
This commit is contained in:
commit
0f386110c6
1688 changed files with 7671 additions and 5249 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.ex diff=elixir
|
||||
*.exs diff=elixir
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -47,3 +47,5 @@ docs/generated_config.md
|
|||
.idea
|
||||
pleroma.iml
|
||||
|
||||
# asdf
|
||||
.tool-versions
|
||||
|
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [Unreleased]
|
||||
### Security
|
||||
- Mastodon API: Fix being able to request enourmous amount of statuses in timelines leading to DoS. Now limited to 40 per request.
|
||||
|
||||
### Removed
|
||||
- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
|
||||
- **Breaking**: OStatus protocol support
|
||||
|
@ -21,6 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- **Breaking:** attachment links (`config :pleroma, :instance, no_attachment_links` and `config :pleroma, Pleroma.Upload, link_name`) disabled by default
|
||||
- **Breaking:** OAuth: defaulted `[:auth, :enforce_oauth_admin_scope_usage]` setting to `true` which demands `admin` OAuth scope to perform admin actions (in addition to `is_admin` flag on User); make sure to use bundled or newer versions of AdminFE & PleromaFE to access admin / moderator features.
|
||||
- **Breaking:** Dynamic configuration has been rearchitected. The `:pleroma, :instance, dynamic_configuration` setting has been replaced with `config :pleroma, configurable_from_database`. Please backup your configuration to a file and run the migration task to ensure consistency with the new schema.
|
||||
- **Breaking:** `:instance, no_attachment_links` has been replaced with `:instance, attachment_links` which still takes a boolean value but doesn't use double negative language.
|
||||
- Replaced [pleroma_job_queue](https://git.pleroma.social/pleroma/pleroma_job_queue) and `Pleroma.Web.Federator.RetryQueue` with [Oban](https://github.com/sorentwo/oban) (see [`docs/config.md`](docs/config.md) on migrating customized worker / retry settings)
|
||||
- Introduced [quantum](https://github.com/quantum-elixir/quantum-core) job scheduler
|
||||
- Enabled `:instance, extended_nickname_format` in the default config
|
||||
|
@ -35,7 +39,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Logger: default log level changed from `warn` to `info`.
|
||||
- Config mix task `migrate_to_db` truncates `config` table before migrating the config file.
|
||||
- MFR policy to set global expiration for all local Create activities
|
||||
|
||||
- Default to `prepare: :unnamed` in the database configuration.
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
|
||||
|
@ -57,6 +61,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Admin API: Render whole status in grouped reports
|
||||
- Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise).
|
||||
- Mastodon API: Favoriting / Repeating a post multiple times will now return the identical response every time. Before, executing that action twice would return an error ("already favorited") on the second try.
|
||||
- Mastodon API: Limit timeline requests to 3 per timeline per 500ms per user/ip by default.
|
||||
</details>
|
||||
|
||||
### Added
|
||||
|
@ -73,7 +78,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- User notification settings: Add `privacy_option` option.
|
||||
- Support for custom Elixir modules (such as MRF policies)
|
||||
- User settings: Add _This account is a_ option.
|
||||
- A new users admin digest email
|
||||
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
|
||||
- Add an option `authorized_fetch_mode` to require HTTP signatures for AP fetches.
|
||||
- ActivityPub: support for `replies` collection (output for outgoing federation & fetching on incoming federation).
|
||||
- Mix task to refresh counter cache (`mix pleroma.refresh_counter_cache`)
|
||||
<details>
|
||||
<summary>API Changes</summary>
|
||||
|
||||
|
@ -101,6 +110,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Configuration: `feed` option for user atom feed.
|
||||
- Pleroma API: Add Emoji reactions
|
||||
- Admin API: Add `/api/pleroma/admin/instances/:instance/statuses` - lists all statuses from a given instance
|
||||
- Admin API: Add `/api/pleroma/admin/users/:nickname/statuses` - lists all statuses from a given user
|
||||
- Admin API: `PATCH /api/pleroma/users/confirm_email` to confirm email for multiple users, `PATCH /api/pleroma/users/resend_confirmation_email` to resend confirmation email for multiple users
|
||||
- ActivityPub: Configurable `type` field of the actors.
|
||||
- Mastodon API: `/api/v1/accounts/:id` has `source/pleroma/actor_type` field.
|
||||
|
@ -115,6 +125,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Configuration: `feed.logo` option for tag feed.
|
||||
- Tag feed: `/tags/:tag.rss` - list public statuses by hashtag.
|
||||
- Mastodon API: Add `reacted` property to `emoji_reactions`
|
||||
- Pleroma API: Add reactions for a single emoji.
|
||||
- ActivityPub: `[:activitypub, :note_replies_output_limit]` setting sets the number of note self-replies to output on outgoing federation.
|
||||
- Admin API: `GET /api/pleroma/admin/stats` to get status count by visibility scope
|
||||
- Admin API: `GET /api/pleroma/admin/statuses` - list all statuses (accepts `godmode` and `local_only`)
|
||||
</details>
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -219,6 +219,8 @@
|
|||
max_expiration: 365 * 24 * 60 * 60
|
||||
},
|
||||
registrations_open: true,
|
||||
invites_enabled: false,
|
||||
account_activation_required: false,
|
||||
federating: true,
|
||||
federation_incoming_replies_max_depth: 100,
|
||||
federation_reachability_timeout_days: 7,
|
||||
|
@ -241,7 +243,7 @@
|
|||
mrf_transparency_exclusions: [],
|
||||
autofollowed_nicknames: [],
|
||||
max_pinned_statuses: 1,
|
||||
no_attachment_links: true,
|
||||
attachment_links: false,
|
||||
welcome_user_nickname: nil,
|
||||
welcome_message: nil,
|
||||
max_report_comment_size: 1000,
|
||||
|
@ -326,7 +328,9 @@
|
|||
unfollow_blocked: true,
|
||||
outgoing_blocks: true,
|
||||
follow_handshake_timeout: 500,
|
||||
sign_object_fetches: true
|
||||
note_replies_output_limit: 5,
|
||||
sign_object_fetches: true,
|
||||
authorized_fetch_mode: false
|
||||
|
||||
config :pleroma, :streamer,
|
||||
workers: 3,
|
||||
|
@ -400,6 +404,8 @@
|
|||
|
||||
config :phoenix, :json_library, Jason
|
||||
|
||||
config :phoenix, :filter_parameters, ["password", "confirm"]
|
||||
|
||||
config :pleroma, :gopher,
|
||||
enabled: false,
|
||||
ip: {0, 0, 0, 0},
|
||||
|
@ -482,13 +488,16 @@
|
|||
transmogrifier: 20,
|
||||
scheduled_activities: 10,
|
||||
background: 5,
|
||||
attachments_cleanup: 5
|
||||
remote_fetcher: 2,
|
||||
attachments_cleanup: 5,
|
||||
new_users_digest: 1
|
||||
],
|
||||
crontab: [
|
||||
{"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
|
||||
{"0 * * * *", Pleroma.Workers.Cron.StatsWorker},
|
||||
{"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
|
||||
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker}
|
||||
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
|
||||
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
|
||||
]
|
||||
|
||||
config :pleroma, :workers,
|
||||
|
@ -562,6 +571,8 @@
|
|||
text_muted_color: "#b9b9ba"
|
||||
}
|
||||
|
||||
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false
|
||||
|
||||
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
|
||||
|
||||
config :pleroma, Pleroma.ScheduledActivity,
|
||||
|
@ -590,6 +601,7 @@
|
|||
|
||||
config :pleroma, :rate_limit,
|
||||
authentication: {60_000, 15},
|
||||
timeline: {500, 3},
|
||||
search: [{1000, 10}, {1000, 30}],
|
||||
app_account_creation: {1_800_000, 25},
|
||||
relations_actions: {10_000, 10},
|
||||
|
@ -614,6 +626,10 @@
|
|||
|
||||
config :pleroma, configurable_from_database: false
|
||||
|
||||
config :pleroma, Pleroma.Repo,
|
||||
parameters: [gin_fuzzy_search_limit: "500"],
|
||||
prepare: :unnamed
|
||||
|
||||
# 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"
|
||||
|
|
|
@ -101,7 +101,7 @@
|
|||
%{
|
||||
key: :versions,
|
||||
type: {:list, :atom},
|
||||
description: "List of TLS version to use",
|
||||
description: "List of TLS versions to use",
|
||||
suggestions: [:tlsv1, ":tlsv1.1", ":tlsv1.2"]
|
||||
}
|
||||
]
|
||||
|
@ -534,7 +534,8 @@
|
|||
%{
|
||||
key: :description,
|
||||
type: :string,
|
||||
description: "The instance's description, can be seen in nodeinfo and /api/v1/instance",
|
||||
description:
|
||||
"The instance's description. It can be seen in nodeinfo and `/api/v1/instance`",
|
||||
suggestions: [
|
||||
"Very cool instance"
|
||||
]
|
||||
|
@ -637,29 +638,31 @@
|
|||
%{
|
||||
key: :registrations_open,
|
||||
type: :boolean,
|
||||
description: "Enable registrations for anyone, invitations can be enabled when `false`"
|
||||
description:
|
||||
"Enable registrations for anyone. Invitations require this setting to be disabled."
|
||||
},
|
||||
%{
|
||||
key: :invites_enabled,
|
||||
type: :boolean,
|
||||
description: "Enable user invitations for admins (depends on `registrations_open: false`)"
|
||||
description:
|
||||
"Enable user invitations for admins (depends on `registrations_open` being disabled)."
|
||||
},
|
||||
%{
|
||||
key: :account_activation_required,
|
||||
type: :boolean,
|
||||
description: "Require users to confirm their emails before signing in"
|
||||
description: "Require users to confirm their emails before signing in."
|
||||
},
|
||||
%{
|
||||
key: :federating,
|
||||
type: :boolean,
|
||||
description: "Enable federation with other instances"
|
||||
description: "Enable federation with other instances."
|
||||
},
|
||||
%{
|
||||
key: :federation_incoming_replies_max_depth,
|
||||
label: "Fed. incoming replies max depth",
|
||||
type: :integer,
|
||||
description:
|
||||
"Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while" <>
|
||||
"Max. depth of reply-to and reply activities fetching on incoming federation, to prevent out-of-memory situations while" <>
|
||||
" fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.",
|
||||
suggestions: [
|
||||
100
|
||||
|
@ -761,14 +764,14 @@
|
|||
key: :extended_nickname_format,
|
||||
type: :boolean,
|
||||
description:
|
||||
"Set to `true` to use extended local nicknames format (allows underscores/dashes)." <>
|
||||
"Enable to use extended local nicknames format (allows underscores/dashes)." <>
|
||||
" This will break federation with older software for theses nicknames."
|
||||
},
|
||||
%{
|
||||
key: :cleanup_attachments,
|
||||
type: :boolean,
|
||||
description: """
|
||||
"Set to `true` to remove associated attachments when status is removed.
|
||||
Enable to remove associated attachments when status is removed.
|
||||
This will not affect duplicates and attachments without status.
|
||||
Enabling this will increase load to database when deleting statuses on larger instances.
|
||||
"""
|
||||
|
@ -796,10 +799,9 @@
|
|||
]
|
||||
},
|
||||
%{
|
||||
key: :no_attachment_links,
|
||||
key: :attachment_links,
|
||||
type: :boolean,
|
||||
description:
|
||||
"Set to `true` to disable automatically adding attachment link text to statuses"
|
||||
description: "Enable to automatically add attachment link text to statuses"
|
||||
},
|
||||
%{
|
||||
key: :welcome_message,
|
||||
|
@ -830,14 +832,14 @@
|
|||
key: :safe_dm_mentions,
|
||||
type: :boolean,
|
||||
description:
|
||||
"If set to `true`, only mentions at the beginning of a post will be used to address people in direct messages." <>
|
||||
"If enabled, 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. \"@admin please keep an eye on @bad_actor\")." <>
|
||||
" Default: `false`"
|
||||
" Default: disabled"
|
||||
},
|
||||
%{
|
||||
key: :healthcheck,
|
||||
type: :boolean,
|
||||
description: "If set to `true`, system data will be shown on /api/pleroma/healthcheck"
|
||||
description: "If enabled, system data will be shown on `/api/pleroma/healthcheck`"
|
||||
},
|
||||
%{
|
||||
key: :remote_post_retention_days,
|
||||
|
@ -867,7 +869,7 @@
|
|||
%{
|
||||
key: :skip_thread_containment,
|
||||
type: :boolean,
|
||||
description: "Skip filter out broken threads. Default: `true`"
|
||||
description: "Skip filtering out broken threads. Default: enabled"
|
||||
},
|
||||
%{
|
||||
key: :limit_to_local_content,
|
||||
|
@ -1159,17 +1161,15 @@
|
|||
key: :alwaysShowSubjectInput,
|
||||
label: "Always show subject input",
|
||||
type: :boolean,
|
||||
description: "When set to `false`, auto-hide the subject field when it's empty"
|
||||
description: "When disabled, auto-hide the subject field if it's empty"
|
||||
},
|
||||
%{
|
||||
key: :logoMask,
|
||||
label: "Logo mask",
|
||||
type: :boolean,
|
||||
description:
|
||||
"By default it assumes logo used will be monochrome-with-alpha one, this is done to be compatible with both light and dark themes, " <>
|
||||
"so that white logo designed with dark theme in mind won't be invisible over light theme, this is done via CSS3 Masking. " <>
|
||||
"Basically - it will take alpha channel of the image and fill non-transparent areas of it with solid color. " <>
|
||||
"If you really want colorful logo - it can be done by setting logoMask to false."
|
||||
"By default it assumes logo used will be monochrome with alpha channel to be compatible with both light and dark themes. " <>
|
||||
"If you want a colorful logo you must disable logoMask."
|
||||
},
|
||||
%{
|
||||
key: :logoMargin,
|
||||
|
@ -1183,13 +1183,13 @@
|
|||
%{
|
||||
key: :stickers,
|
||||
type: :boolean,
|
||||
description: "Enables/disables stickers."
|
||||
description: "Enables stickers."
|
||||
},
|
||||
%{
|
||||
key: :enableEmojiPicker,
|
||||
label: "Emoji picker",
|
||||
type: :boolean,
|
||||
description: "Enables/disables emoji picker."
|
||||
description: "Enables emoji picker."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -1297,14 +1297,14 @@
|
|||
%{
|
||||
key: :media_removal,
|
||||
type: {:list, :string},
|
||||
description: "List of instances to remove medias from",
|
||||
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 put medias as NSFW (sensitive) from",
|
||||
description: "List of instances to tag all media as NSFW (sensitive) from",
|
||||
suggestions: ["example.com", "*.example.com"]
|
||||
},
|
||||
%{
|
||||
|
@ -1438,21 +1438,21 @@
|
|||
key: :reject,
|
||||
type: [:string, :regex],
|
||||
description:
|
||||
"A list of patterns which result in message being rejected, each pattern can be a string or a regular expression.",
|
||||
"A list of patterns which result in message being rejected. Each pattern can be a string or a regular expression.",
|
||||
suggestions: ["foo", ~r/foo/iu]
|
||||
},
|
||||
%{
|
||||
key: :federated_timeline_removal,
|
||||
type: [:string, :regex],
|
||||
description:
|
||||
"A list of patterns which result in message being removed from federated timelines (a.k.a unlisted), each pattern can be a string or a regular expression.",
|
||||
"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: [{:tuple, :string, :string}, {:tuple, :regex, :string}],
|
||||
description:
|
||||
"A list of tuples containing {pattern, replacement}, pattern can be a string or a regular expression.",
|
||||
"A list of tuples containing {pattern, replacement}. Each pattern can be a string or a regular expression.",
|
||||
suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
|
||||
}
|
||||
]
|
||||
|
@ -1467,7 +1467,7 @@
|
|||
%{
|
||||
key: :actors,
|
||||
type: {:list, :string},
|
||||
description: "A list of actors, for which to drop any posts mentioning",
|
||||
description: "A list of actors for which any post mentioning them will be dropped.",
|
||||
suggestions: ["actor1", "actor2"]
|
||||
}
|
||||
]
|
||||
|
@ -1630,160 +1630,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: Pleroma.Web.Endpoint,
|
||||
type: :group,
|
||||
description: "Phoenix endpoint configuration",
|
||||
children: [
|
||||
%{
|
||||
key: :http,
|
||||
label: "HTTP",
|
||||
type: {:keyword, :integer, :tuple},
|
||||
description: "http protocol configuration",
|
||||
suggestions: [
|
||||
port: 8080,
|
||||
ip: {127, 0, 0, 1}
|
||||
],
|
||||
children: [
|
||||
%{
|
||||
key: :dispatch,
|
||||
type: {:list, :tuple},
|
||||
description: "dispatch settings",
|
||||
suggestions: [
|
||||
{:_,
|
||||
[
|
||||
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
|
||||
{"/websocket", Phoenix.Endpoint.CowboyWebSocket,
|
||||
{Phoenix.Transports.WebSocket,
|
||||
{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}},
|
||||
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
|
||||
]}
|
||||
# end copied from config.exs
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :ip,
|
||||
label: "IP",
|
||||
type: :tuple,
|
||||
description: "ip",
|
||||
suggestions: [
|
||||
{0, 0, 0, 0}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :port,
|
||||
type: :integer,
|
||||
description: "port",
|
||||
suggestions: [
|
||||
2020
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :url,
|
||||
label: "URL",
|
||||
type: {:keyword, :string, :integer},
|
||||
description: "configuration for generating urls",
|
||||
suggestions: [
|
||||
host: "example.com",
|
||||
port: 2020,
|
||||
scheme: "https"
|
||||
],
|
||||
children: [
|
||||
%{
|
||||
key: :host,
|
||||
type: :string,
|
||||
description: "Host",
|
||||
suggestions: [
|
||||
"example.com"
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :port,
|
||||
type: :integer,
|
||||
description: "port",
|
||||
suggestions: [
|
||||
2020
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :scheme,
|
||||
type: :string,
|
||||
description: "Scheme",
|
||||
suggestions: [
|
||||
"https",
|
||||
"https"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :instrumenters,
|
||||
type: {:list, :module},
|
||||
suggestions: [Pleroma.Web.Endpoint.Instrumenter]
|
||||
},
|
||||
%{
|
||||
key: :protocol,
|
||||
type: :string,
|
||||
suggestions: ["https"]
|
||||
},
|
||||
%{
|
||||
key: :secret_key_base,
|
||||
type: :string,
|
||||
suggestions: ["aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl"]
|
||||
},
|
||||
%{
|
||||
key: :signing_salt,
|
||||
type: :string,
|
||||
suggestions: ["CqaoopA2"]
|
||||
},
|
||||
%{
|
||||
key: :render_errors,
|
||||
type: :keyword,
|
||||
suggestions: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
|
||||
children: [
|
||||
%{
|
||||
key: :view,
|
||||
type: :module,
|
||||
suggestions: [Pleroma.Web.ErrorView]
|
||||
},
|
||||
%{
|
||||
key: :accepts,
|
||||
type: {:list, :string},
|
||||
suggestions: ["json"]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :pubsub,
|
||||
type: :keyword,
|
||||
suggestions: [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2],
|
||||
children: [
|
||||
%{
|
||||
key: :name,
|
||||
type: :module,
|
||||
suggestions: [Pleroma.PubSub]
|
||||
},
|
||||
%{
|
||||
key: :adapter,
|
||||
type: :module,
|
||||
suggestions: [Phoenix.PubSub.PG2]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :secure_cookie_flag,
|
||||
type: :boolean
|
||||
},
|
||||
%{
|
||||
key: :extra_cookie_attrs,
|
||||
type: {:list, :string},
|
||||
suggestions: ["SameSite=Lax"]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :activitypub,
|
||||
|
@ -1805,6 +1651,12 @@
|
|||
type: :boolean,
|
||||
description: "Sign object fetches with HTTP signatures"
|
||||
},
|
||||
%{
|
||||
key: :note_replies_output_limit,
|
||||
type: :integer,
|
||||
description:
|
||||
"The number of Note replies' URIs to be included with outgoing federation (`5` to match Mastodon hardcoded value, `0` to disable the output)."
|
||||
},
|
||||
%{
|
||||
key: :follow_handshake_timeout,
|
||||
type: :integer,
|
||||
|
@ -1871,9 +1723,8 @@
|
|||
type: :string,
|
||||
description:
|
||||
"A mailto link for the administrative contact." <>
|
||||
" It's best if this email is not a personal email address, but rather a group email so that if a person leaves an organization," <>
|
||||
" is unavailable for an extended period, or otherwise can't respond, someone else on the list can.",
|
||||
suggestions: ["Subject"]
|
||||
" It's best if this email is not a personal email address, but rather a group email to the instance moderation team.",
|
||||
suggestions: ["mailto:moderators@pleroma.com"]
|
||||
},
|
||||
%{
|
||||
key: :public_key,
|
||||
|
@ -1940,7 +1791,7 @@
|
|||
key: :admin_token,
|
||||
type: :string,
|
||||
description: "Token",
|
||||
suggestions: ["some_random_token"]
|
||||
suggestions: ["We recommend a secure random string or UUID"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2002,6 +1853,7 @@
|
|||
"Background jobs queues (keys: queues, values: max numbers of concurrent jobs)",
|
||||
suggestions: [
|
||||
activity_expiration: 10,
|
||||
attachments_cleanup: 5,
|
||||
background: 5,
|
||||
federator_incoming: 50,
|
||||
federator_outgoing: 50,
|
||||
|
@ -2017,6 +1869,12 @@
|
|||
description: "Activity expiration queue",
|
||||
suggestions: [10]
|
||||
},
|
||||
%{
|
||||
key: :attachments_cleanup,
|
||||
type: :integer,
|
||||
description: "Attachment deletion queue",
|
||||
suggestions: [5]
|
||||
},
|
||||
%{
|
||||
key: :background,
|
||||
type: :integer,
|
||||
|
@ -2060,6 +1918,18 @@
|
|||
suggestions: [50]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
key: :crontab,
|
||||
type: {:list, :tuple},
|
||||
description: "Settings for cron background jobs",
|
||||
suggestions: [
|
||||
{"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
|
||||
{"0 * * * *", Pleroma.Workers.Cron.StatsWorker},
|
||||
{"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
|
||||
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
|
||||
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2101,7 +1971,7 @@
|
|||
key: :unfurl_nsfw,
|
||||
label: "Unfurl NSFW",
|
||||
type: :boolean,
|
||||
description: "If set to `true` NSFW attachments will be shown in previews"
|
||||
description: "When enabled NSFW attachments will be shown in previews"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2115,7 +1985,7 @@
|
|||
%{
|
||||
key: :enabled,
|
||||
type: :boolean,
|
||||
description: "Enables/disables RichMedia."
|
||||
description: "Enables RichMedia parsing of URLs."
|
||||
},
|
||||
%{
|
||||
key: :ignore_hosts,
|
||||
|
@ -2161,8 +2031,7 @@
|
|||
%{
|
||||
key: :enabled,
|
||||
type: :boolean,
|
||||
description:
|
||||
"If enabled, when a new user is federated with, fetch some of their latest posts"
|
||||
description: "Fetch posts when a new user is federated with"
|
||||
},
|
||||
%{
|
||||
key: :pages,
|
||||
|
@ -2181,13 +2050,13 @@
|
|||
%{
|
||||
key: :class,
|
||||
type: [:string, false],
|
||||
description: "Specify the class to be added to the generated link. `False` to clear",
|
||||
description: "Specify the class to be added to the generated link. Disable to clear",
|
||||
suggestions: ["auto-linker", false]
|
||||
},
|
||||
%{
|
||||
key: :rel,
|
||||
type: [:string, false],
|
||||
description: "Override the rel attribute. `False` to clear",
|
||||
description: "Override the rel attribute. Disable to clear",
|
||||
suggestions: ["ugc", "noopener noreferrer", false]
|
||||
},
|
||||
%{
|
||||
|
@ -2297,7 +2166,7 @@
|
|||
key: :ssl,
|
||||
label: "SSL",
|
||||
type: :boolean,
|
||||
description: "`True` to use SSL, usually implies the port 636"
|
||||
description: "Enable to use SSL, usually implies the port 636"
|
||||
},
|
||||
%{
|
||||
key: :sslopts,
|
||||
|
@ -2324,7 +2193,7 @@
|
|||
key: :tls,
|
||||
label: "TLS",
|
||||
type: :boolean,
|
||||
description: "`True` to start TLS, usually implies the port 389"
|
||||
description: "Enable to use STARTTLS, usually implies the port 389"
|
||||
},
|
||||
%{
|
||||
key: :tlsopts,
|
||||
|
@ -2373,8 +2242,8 @@
|
|||
type: :boolean,
|
||||
description:
|
||||
"OAuth admin scope requirement toggle. " <>
|
||||
"If `true`, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <>
|
||||
"(client app must support admin scopes). If `false` and token doesn't have admin scope(s)," <>
|
||||
"If enabled, admin actions explicitly demand admin OAuth scope(s) presence in OAuth token " <>
|
||||
"(client app must support admin scopes). If disabled and token doesn't have admin scope(s)," <>
|
||||
"`is_admin` user flag grants access to admin-specific actions."
|
||||
},
|
||||
%{
|
||||
|
@ -2396,7 +2265,7 @@
|
|||
key: :oauth_consumer_strategies,
|
||||
type: {:list, :string},
|
||||
description:
|
||||
"The list of enabled OAuth consumer strategies; by default it's set by OAUTH_CONSUMER_STRATEGIES environment variable." <>
|
||||
"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\"" <>
|
||||
" (e.g. twitter or keycloak:ueberauth_keycloak_strategy in case dependency is named differently than ueberauth_<strategy>).",
|
||||
suggestions: ["twitter", "keycloak:ueberauth_keycloak_strategy"]
|
||||
|
@ -2512,6 +2381,20 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: Pleroma.Emails.NewUsersDigestEmail,
|
||||
type: :group,
|
||||
description: "New users admin email digest",
|
||||
children: [
|
||||
%{
|
||||
key: :enabled,
|
||||
type: :boolean,
|
||||
description: "enables new users admin digest email when `true`",
|
||||
suggestions: [false]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :oauth2,
|
||||
|
@ -2533,7 +2416,7 @@
|
|||
%{
|
||||
key: :clean_expired_tokens,
|
||||
type: :boolean,
|
||||
description: "Enable a background job to clean expired oauth tokens. Default: `false`."
|
||||
description: "Enable a background job to clean expired oauth tokens. Default: disabled."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -2584,19 +2467,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :database,
|
||||
type: :group,
|
||||
description: "Database related settings",
|
||||
children: [
|
||||
%{
|
||||
key: :rum_enabled,
|
||||
type: :boolean,
|
||||
description: "If RUM indexes should be used. Default: `false`"
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :rate_limit,
|
||||
|
@ -2610,6 +2480,12 @@
|
|||
description: "For the search requests (account & status search etc.)",
|
||||
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
|
||||
},
|
||||
%{
|
||||
key: :timeline,
|
||||
type: [:tuple, {:list, :tuple}],
|
||||
description: "For requests to timelines (each timeline has it's own limiter)",
|
||||
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
|
||||
},
|
||||
%{
|
||||
key: :app_account_creation,
|
||||
type: [:tuple, {:list, :tuple}],
|
||||
|
@ -2760,20 +2636,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :prometheus,
|
||||
key: Pleroma.Web.Endpoint.MetricsExporter,
|
||||
type: :group,
|
||||
description: "Prometheus settings",
|
||||
children: [
|
||||
%{
|
||||
key: :path,
|
||||
type: :string,
|
||||
description: "API endpoint with metrics",
|
||||
suggestions: ["/api/pleroma/app_metrics"]
|
||||
}
|
||||
]
|
||||
},
|
||||
%{
|
||||
group: :http_signatures,
|
||||
type: :group,
|
||||
|
@ -2979,7 +2841,7 @@
|
|||
%{
|
||||
key: :enabled,
|
||||
type: :boolean,
|
||||
description: "Enable/disable the plug. Default: `false`."
|
||||
description: "Enable/disable the plug. Default: disabled."
|
||||
},
|
||||
%{
|
||||
key: :headers,
|
||||
|
@ -3033,7 +2895,7 @@
|
|||
%{
|
||||
key: :enabled,
|
||||
type: :boolean,
|
||||
description: "Enables the rendering of static HTML. Defaults to `false`."
|
||||
description: "Enables the rendering of static HTML. Default: disabled."
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -3041,7 +2903,7 @@
|
|||
group: :pleroma,
|
||||
key: :feed,
|
||||
type: :group,
|
||||
description: "Configure feed rendering.",
|
||||
description: "Configure feed rendering",
|
||||
children: [
|
||||
%{
|
||||
key: :post_title,
|
||||
|
@ -3091,7 +2953,7 @@
|
|||
group: :pleroma,
|
||||
key: :modules,
|
||||
type: :group,
|
||||
description: "Custom Runtime Modules.",
|
||||
description: "Custom Runtime Modules",
|
||||
children: [
|
||||
%{
|
||||
key: :runtime_dir,
|
||||
|
@ -3102,14 +2964,21 @@
|
|||
},
|
||||
%{
|
||||
group: :pleroma,
|
||||
key: :streamer,
|
||||
type: :group,
|
||||
description: "Allow instance configuration from database.",
|
||||
description: "Settings for notifications streamer",
|
||||
children: [
|
||||
%{
|
||||
key: :configurable_from_database,
|
||||
type: :boolean,
|
||||
description:
|
||||
"Allow transferring configuration to DB with the subsequent customization from Admin api. Defaults to `false`"
|
||||
key: :workers,
|
||||
type: :integer,
|
||||
description: "Number of workers to send notifications.",
|
||||
suggestions: [3]
|
||||
},
|
||||
%{
|
||||
key: :overflow_workers,
|
||||
type: :integer,
|
||||
description: "Maximum number of workers created if pool is empty.",
|
||||
suggestions: [2]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -74,11 +74,7 @@
|
|||
total_user_limit: 3,
|
||||
enabled: false
|
||||
|
||||
config :pleroma, :rate_limit,
|
||||
search: [{1000, 30}, {1000, 30}],
|
||||
app_account_creation: {10_000, 5},
|
||||
password_reset: {1000, 30},
|
||||
ap_routes: nil
|
||||
config :pleroma, :rate_limit, %{}
|
||||
|
||||
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
||||
|
||||
|
@ -94,6 +90,8 @@
|
|||
|
||||
config :pleroma, :modules, runtime_dir: "test/fixtures/modules"
|
||||
|
||||
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: true
|
||||
|
||||
if File.exists?("./config/test.secret.exs") do
|
||||
import_config "test.secret.exs"
|
||||
else
|
||||
|
|
|
@ -260,10 +260,24 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
|||
- `nickname` or `id`
|
||||
- *optional* `page_size`: number of statuses to return (default is `20`)
|
||||
- *optional* `godmode`: `true`/`false` – allows to see private statuses
|
||||
- *optional* `with_reblogs`: `true`/`false` – allows to see reblogs (default is false)
|
||||
- Response:
|
||||
- On failure: `Not found`
|
||||
- On success: JSON array of user's latest statuses
|
||||
|
||||
## `GET /api/pleroma/admin/instances/:instance/statuses`
|
||||
|
||||
### Retrive instance's latest statuses
|
||||
|
||||
- Params:
|
||||
- `instance`: instance name
|
||||
- *optional* `page_size`: number of statuses to return (default is `20`)
|
||||
- *optional* `godmode`: `true`/`false` – allows to see private statuses
|
||||
- *optional* `with_reblogs`: `true`/`false` – allows to see reblogs (default is false)
|
||||
- Response:
|
||||
- On failure: `Not found`
|
||||
- On success: JSON array of instance's latest statuses
|
||||
|
||||
## `POST /api/pleroma/admin/relay`
|
||||
|
||||
### Follow a Relay
|
||||
|
@ -682,6 +696,8 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
|||
|
||||
### Get list of merged default settings with saved in database.
|
||||
|
||||
*If `need_reboot` flag exists in response, instance must be restarted, so reboot time settings can take effect.*
|
||||
|
||||
**Only works when configuration from database is enabled.**
|
||||
|
||||
- Params:
|
||||
|
@ -692,20 +708,24 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
|||
|
||||
```json
|
||||
{
|
||||
configs: [
|
||||
"configs": [
|
||||
{
|
||||
"group": ":pleroma",
|
||||
"key": "Pleroma.Upload",
|
||||
"value": []
|
||||
}
|
||||
]
|
||||
],
|
||||
"need_reboot": true
|
||||
}
|
||||
```
|
||||
need_reboot - *optional*, if were changed reboot time settings.
|
||||
|
||||
## `POST /api/pleroma/admin/config`
|
||||
|
||||
### Update config settings
|
||||
|
||||
*If `need_reboot` flag exists in response, instance must be restarted, so reboot time settings can take effect.*
|
||||
|
||||
**Only works when configuration from database is enabled.**
|
||||
|
||||
Some modifications are necessary to save the config settings correctly:
|
||||
|
@ -793,7 +813,7 @@ config :quack,
|
|||
```
|
||||
```json
|
||||
{
|
||||
configs: [
|
||||
"configs": [
|
||||
{"group": ":quack", "key": ":level", "value": ":debug"},
|
||||
{"group": ":quack", "key": ":meta", "value": [":all"]},
|
||||
...
|
||||
|
@ -804,7 +824,7 @@ config :quack,
|
|||
|
||||
```json
|
||||
{
|
||||
configs: [
|
||||
"configs": [
|
||||
{
|
||||
"group": ":pleroma",
|
||||
"key": "Pleroma.Upload",
|
||||
|
@ -836,15 +856,17 @@ config :quack,
|
|||
- 400 Bad Request `"To use this endpoint you need to enable configuration from database."`
|
||||
```json
|
||||
{
|
||||
configs: [
|
||||
"configs": [
|
||||
{
|
||||
"group": ":pleroma",
|
||||
"key": "Pleroma.Upload",
|
||||
"value": [...]
|
||||
}
|
||||
]
|
||||
],
|
||||
"need_reboot": true
|
||||
}
|
||||
```
|
||||
need_reboot - *optional*, if were changed reboot time settings.
|
||||
|
||||
## ` GET /api/pleroma/admin/config/descriptions`
|
||||
|
||||
|
@ -931,3 +953,20 @@ Loads json generated from `config/descriptions.exs`.
|
|||
- Params:
|
||||
- `nicknames`
|
||||
- Response: Array of user nicknames
|
||||
|
||||
## `GET /api/pleroma/admin/stats`
|
||||
|
||||
### Stats
|
||||
|
||||
- Response:
|
||||
|
||||
```json
|
||||
{
|
||||
"status_visibility": {
|
||||
"direct": 739,
|
||||
"private": 9,
|
||||
"public": 17,
|
||||
"unlisted": 14
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
|
@ -459,3 +459,16 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
|
|||
{"name": "☕", "count": 1, "me": false, "accounts": [{"id" => "abc..."}]}
|
||||
]
|
||||
```
|
||||
|
||||
## `GET /api/v1/pleroma/statuses/:id/reactions/:emoji`
|
||||
### Get an object of emoji to account mappings with accounts that reacted to the post for a specific emoji`
|
||||
* Method: `GET`
|
||||
* Authentication: optional
|
||||
* Params: None
|
||||
* Response: JSON, a list of emoji/account list tuples
|
||||
* Example Response:
|
||||
```json
|
||||
[
|
||||
{"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
|
||||
]
|
||||
```
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
6. Run `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>`
|
||||
7. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any.
|
||||
8. Restart the Pleroma service.
|
||||
|
||||
9. After you've restarted Pleroma, you will notice that postgres will take up more cpu resources than usual. A lot in fact. To fix this you must do a VACUUM ANLAYZE. This can also be done while the instance is still running like so:
|
||||
$ sudo -u postgres psql pleroma_database_name
|
||||
pleroma=# VACUUM ANALYZE;
|
||||
[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
|
||||
|
||||
## Remove
|
||||
|
|
|
@ -1,4 +1,21 @@
|
|||
# Updating your instance
|
||||
|
||||
You should **always check the release notes/changelog** in case there are config deprecations, special update special update steps, etc.
|
||||
|
||||
Besides that, doing the following is generally enough:
|
||||
|
||||
## For OTP installations
|
||||
|
||||
```sh
|
||||
# Download the new release
|
||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl update"
|
||||
|
||||
# Migrate the database, you are advised to stop the instance before doing that
|
||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
|
||||
```
|
||||
|
||||
## For from source installations (using git)
|
||||
|
||||
1. Go to the working directory of Pleroma (default is `/opt/pleroma`)
|
||||
2. Run `git pull`. This pulls the latest changes from upstream.
|
||||
3. Run `mix deps.get`. This pulls in any new dependencies.
|
||||
|
|
|
@ -2,9 +2,11 @@
|
|||
|
||||
This is a cheat sheet for Pleroma configuration file, any setting possible to configure should be listed here.
|
||||
|
||||
Pleroma configuration works by first importing the base config (`config/config.exs` on source installs, compiled-in on OTP releases), then overriding it by the environment config (`config/$MIX_ENV.exs` on source installs, N/A to OTP releases) and then overriding it by user config (`config/$MIX_ENV.secret.exs` on source installs, typically `/etc/pleroma/config.exs` on OTP releases).
|
||||
For OTP installations the configuration is typically stored in `/etc/pleroma/config.exs`.
|
||||
|
||||
You shouldn't edit the base config directly to avoid breakages and merge conflicts, but it can be used as a reference if you don't understand how an option is supposed to be formatted, the latest version of it can be viewed [here](https://git.pleroma.social/pleroma/pleroma/blob/develop/config/config.exs).
|
||||
For from source installations Pleroma configuration works by first importing the base config `config/config.exs`, then overriding it by the environment config `config/$MIX_ENV.exs` and then overriding it by user config `config/$MIX_ENV.secret.exs`. In from source installations you should always make the changes to the user config and NEVER to the base config to avoid breakages and merge conflicts. So for production you change/add configuration to `config/prod.secret.exs`.
|
||||
|
||||
To add configuration to your config file, you can copy it from the base config. The latest version of it can be viewed [here](https://git.pleroma.social/pleroma/pleroma/blob/develop/config/config.exs). You can also use this file if you don't know how an option is supposed to be formatted.
|
||||
|
||||
## :instance
|
||||
* `name`: The instance’s name.
|
||||
|
@ -148,14 +150,19 @@ config :pleroma, :mrf_user_allowlist,
|
|||
* `days`: Default global expiration time for all local Create activities (in days)
|
||||
|
||||
### :activitypub
|
||||
* ``unfollow_blocked``: Whether blocks result in people getting unfollowed
|
||||
* ``outgoing_blocks``: Whether to federate blocks to other instances
|
||||
* ``deny_follow_blocked``: Whether to disallow following an account that has blocked the user in question
|
||||
* ``sign_object_fetches``: Sign object fetches with HTTP signatures
|
||||
* `unfollow_blocked`: Whether blocks result in people getting unfollowed
|
||||
* `outgoing_blocks`: Whether to federate blocks to other instances
|
||||
* `deny_follow_blocked`: Whether to disallow following an account that has blocked the user in question
|
||||
* `sign_object_fetches`: Sign object fetches with HTTP signatures
|
||||
* `authorized_fetch_mode`: Require HTTP signatures for AP fetches
|
||||
|
||||
### :fetch_initial_posts
|
||||
* `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts
|
||||
* `pages`: the amount of pages to fetch
|
||||
|
||||
!!! warning
|
||||
Be careful with this setting, fetching posts may lead to new users being discovered whose posts will then also be fetched. This can lead to serious load on your instance and database.
|
||||
|
||||
* `enabled`: If enabled, when a new user is discovered by your instance, fetch some of their latest posts.
|
||||
* `pages`: The amount of pages to fetch
|
||||
|
||||
## Pleroma.ScheduledActivity
|
||||
|
||||
|
@ -347,6 +354,7 @@ Means that:
|
|||
Supported rate limiters:
|
||||
|
||||
* `:search` - Account/Status search.
|
||||
* `:timeline` - Timeline requests (each timeline has it's own limiter).
|
||||
* `:app_account_creation` - Account registration from the API.
|
||||
* `:relations_actions` - Following/Unfollowing in general.
|
||||
* `:relation_id_action` - Following/Unfollowing for a specific user.
|
||||
|
@ -506,6 +514,10 @@ Email notifications settings.
|
|||
- `:logo` - a path to a custom logo. Set it to `nil` to use the default Pleroma logo.
|
||||
- `:styling` - a map with color settings for email templates.
|
||||
|
||||
### Pleroma.Emails.NewUsersDigestEmail
|
||||
|
||||
- `:enabled` - a boolean, enables new users admin digest email when `true`. Defaults to `false`.
|
||||
|
||||
## Background jobs
|
||||
|
||||
### Oban
|
||||
|
|
74
docs/configuration/howto_theming_your_instance.md
Normal file
74
docs/configuration/howto_theming_your_instance.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
# Theming your instance
|
||||
|
||||
To add a custom theme to your instance, you'll first need to get a custom theme, upload it to the server, make it available to the instance and eventually you can set it as default.
|
||||
|
||||
## Getting a custom theme
|
||||
|
||||
### Create your own theme
|
||||
|
||||
* You can create your own theme using the Pleroma FE by going to settings (gear on the top right) and choose the Theme tab. Here you have the options to create a personal theme.
|
||||
* To download your theme, you can do Save preset
|
||||
* If you want to upload a theme to customise it further, you can upload it using Load preset
|
||||
|
||||
This will only save the theme for you personally. To make it available to the whole instance, you'll need to upload it to the server.
|
||||
|
||||
### Get an existing theme
|
||||
|
||||
* You can download a theme from another instance by going to that instance, go to settings and make sure you have the theme selected that you want. Then you can do Save preset to download it.
|
||||
* You can also find and download custom themes at <https://plthemes.vulpes.one/>
|
||||
|
||||
## Adding the custom theme to the instance
|
||||
|
||||
### Upload the theme to the server
|
||||
|
||||
Themes can be found in the [static directory](static_dir.md). Create `STATIC-DIR/static/themes/` if needed and copy your theme there. Next you need to add an entry for your theme to `STATIC-DIR/static/styles.json`. If you use a from source installation, you'll first need to copy the file from `priv/static/static/styles.json`.
|
||||
|
||||
Example of `styles.json` where we add our own `my-awesome-theme.json`
|
||||
```json
|
||||
{
|
||||
"pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
|
||||
"ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ],
|
||||
"monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ],
|
||||
|
||||
"redmond-xx": "/static/themes/redmond-xx.json",
|
||||
"redmond-xx-se": "/static/themes/redmond-xx-se.json",
|
||||
"redmond-xxi": "/static/themes/redmond-xxi.json",
|
||||
"breezy-dark": "/static/themes/breezy-dark.json",
|
||||
"breezy-light": "/static/themes/breezy-light.json",
|
||||
"mammal": "/static/themes/mammal.json",
|
||||
"my-awesome-theme": "/static/themes/my-awesome-theme.json"
|
||||
}
|
||||
```
|
||||
|
||||
Now you'll already be able to select the theme in Pleroma FE from the drop-down. You don't need to restart Pleroma because we only changed static served files. You may need to refresh the page in your browser. You'll notice however that the theme doesn't have a name, it's just an empty entry in the drop-down.
|
||||
|
||||
### Give the theme a name
|
||||
|
||||
When you open one of the themes that ship with Pleroma, you'll notice that the json has a `"name"` key. Add a key-value pair to your theme where the key name is `"name"` and the value the name you want to give your theme. After this you can refresh te page in your browser and the name should be visible in the drop-down.
|
||||
|
||||
Example of `my-awesome-theme.json` where we add the name "My Awesome Theme"
|
||||
```json
|
||||
{
|
||||
"_pleroma_theme_version": 2,
|
||||
"name": "My Awesome Theme",
|
||||
"theme": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Set as default theme
|
||||
|
||||
Now we can set the new theme as default in the [Pleroma FE configuration](General-tips-for-customizing-Pleroma-FE.md).
|
||||
|
||||
Example of adding the new theme in the back-end config files
|
||||
```elixir
|
||||
config :pleroma, :frontend_configurations,
|
||||
pleroma_fe: %{
|
||||
theme: "my-awesome-theme"
|
||||
}
|
||||
```
|
||||
|
||||
If you added it in the back-end configuration file, you'll need to restart your instance for the changes to take effect. If you don't see the changes, it's probably because the browser has cached the previous theme. In that case you'll want to clear browser caches. Alternatively you can use a private/incognito window just to see the changes.
|
||||
|
|
@ -123,7 +123,7 @@ In addition to that, replace the existing nginx config's contents with the examp
|
|||
|
||||
If not an I2P-only instance, add the nginx config below to your existing config at `/etc/nginx/sites-enabled/pleroma.nginx`.
|
||||
|
||||
And for both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself seperately from the clearnet (if your instance is also on the clearnet).
|
||||
And for both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself separately from the clearnet (if your instance is also on the clearnet).
|
||||
Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
|
||||
```
|
||||
config :pleroma, :http_security,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
# Message Rewrite Facility
|
||||
|
||||
The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages.
|
||||
|
||||
Possible uses include:
|
||||
|
@ -10,7 +11,8 @@ Possible uses include:
|
|||
* removing media from messages
|
||||
* sending only public messages to a specific instance
|
||||
|
||||
The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
|
||||
The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
|
||||
|
||||
It is possible to use multiple, active MRF policies at the same time.
|
||||
|
||||
## Quarantine Instances
|
||||
|
@ -18,7 +20,8 @@ It is possible to use multiple, active MRF policies at the same time.
|
|||
You have the ability to prevent from private / followers-only messages from federating with specific instances. Which means they will only get the public or unlisted messages from your instance.
|
||||
|
||||
If, for example, you're using `MIX_ENV=prod` aka using production mode, you would open your configuration file located in `config/prod.secret.exs` and edit or add the option under your `:instance` config object. Then you would specify the instance within quotes.
|
||||
```
|
||||
|
||||
```elixir
|
||||
config :pleroma, :instance,
|
||||
[...]
|
||||
quarantined_instances: ["instance.example", "other.example"]
|
||||
|
@ -28,15 +31,15 @@ config :pleroma, :instance,
|
|||
|
||||
`SimplePolicy` is capable of handling most common admin tasks.
|
||||
|
||||
To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
|
||||
To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
|
||||
|
||||
```
|
||||
```elixir
|
||||
config :pleroma, :instance,
|
||||
[...]
|
||||
rewrite_policy: Pleroma.Web.ActivityPub.MRF.SimplePolicy
|
||||
```
|
||||
|
||||
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
||||
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
||||
|
||||
* `media_removal`: Servers in this group will have media stripped from incoming messages.
|
||||
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
||||
|
@ -50,7 +53,7 @@ Servers should be configured as lists.
|
|||
|
||||
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`:
|
||||
|
||||
```
|
||||
```elixir
|
||||
config :pleroma, :instance,
|
||||
rewrite_policy: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
|
||||
|
||||
|
@ -60,30 +63,31 @@ config :pleroma, :mrf_simple,
|
|||
reject: ["spam.com"],
|
||||
federated_timeline_removal: ["spam.university"],
|
||||
report_removal: ["whiny.whiner"]
|
||||
|
||||
```
|
||||
|
||||
### Use with Care
|
||||
|
||||
The effects of MRF policies can be very drastic. It is important to use this functionality carefully. Always try to talk to an admin before writing an MRF policy concerning their instance.
|
||||
The effects of MRF policies can be very drastic. It is important to use this functionality carefully. Always try to talk to an admin before writing an MRF policy concerning their instance.
|
||||
|
||||
## Writing your own MRF Policy
|
||||
|
||||
As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `rewrite_policy` config setting.
|
||||
As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `rewrite_policy` config setting.
|
||||
|
||||
For example, here is a sample policy module which rewrites all messages to "new message content":
|
||||
|
||||
```elixir
|
||||
# This is a sample MRF policy which rewrites all Notes to have "new message
|
||||
# content."
|
||||
defmodule Site.RewritePolicy do
|
||||
@behavior Pleroma.Web.ActivityPub.MRF
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.RewritePolicy do
|
||||
@moduledoc "MRF policy which rewrites all Notes to have 'new message content'."
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
# Catch messages which contain Note objects with actual data to filter.
|
||||
# Capture the object as `object`, the message content as `content` and the
|
||||
# message itself as `message`.
|
||||
@impl true
|
||||
def filter(%{"type" => Create", "object" => {"type" => "Note", "content" => content} = object} = message)
|
||||
def filter(
|
||||
%{"type" => "Create", "object" => %{"type" => "Note", "content" => content} = object} =
|
||||
message
|
||||
)
|
||||
when is_binary(content) do
|
||||
# Subject / CW is stored as summary instead of `name` like other AS2 objects
|
||||
# because of Mastodon doing it that way.
|
||||
|
@ -106,17 +110,22 @@ defmodule Site.RewritePolicy do
|
|||
# Let all other messages through without modifying them.
|
||||
@impl true
|
||||
def filter(message), do: {:ok, message}
|
||||
|
||||
@impl true
|
||||
def describe do
|
||||
{:ok, %{mrf_sample: %{content: "new message content"}}}`
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
If you save this file as `lib/site/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so:
|
||||
If you save this file as `lib/pleroma/web/activity_pub/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so:
|
||||
|
||||
```
|
||||
```elixir
|
||||
config :pleroma, :instance,
|
||||
rewrite_policy: [
|
||||
Pleroma.Web.ActivityPub.MRF.SimplePolicy,
|
||||
Site.RewritePolicy
|
||||
Pleroma.Web.ActivityPub.MRF.RewritePolicy
|
||||
]
|
||||
```
|
||||
|
||||
Please note that the Pleroma developers consider custom MRF policy modules to fall under the purview of the AGPL. As such, you are obligated to release the sources to your custom MRF policy modules upon request.
|
||||
Please note that the Pleroma developers consider custom MRF policy modules to fall under the purview of the AGPL. As such, you are obligated to release the sources to your custom MRF policy modules upon request.
|
||||
|
|
|
@ -75,7 +75,7 @@ If not a Tor-only instance,
|
|||
add the nginx config below to your existing config at `/etc/nginx/sites-enabled/pleroma.nginx`.
|
||||
|
||||
---
|
||||
For both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself seperately from the clearnet (if your instance is also on the clearnet).
|
||||
For both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself separately from the clearnet (if your instance is also on the clearnet).
|
||||
Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
|
||||
```
|
||||
config :pleroma, :http_security,
|
||||
|
|
|
@ -73,6 +73,15 @@ rc-service postgresql restart
|
|||
systemctl restart postgresql
|
||||
```
|
||||
|
||||
If you are using PostgreSQL 12 or higher, add this to your Ecto database configuration
|
||||
|
||||
```elixir
|
||||
prepare: :named,
|
||||
parameters: [
|
||||
plan_cache_mode: "force_custom_plan"
|
||||
]
|
||||
```
|
||||
|
||||
### Installing Pleroma
|
||||
```sh
|
||||
# Create a Pleroma user
|
||||
|
@ -259,19 +268,14 @@ su pleroma -s $SHELL -lc "./bin/pleroma_ctl user new joeuser joeuser@sld.tld --a
|
|||
```
|
||||
This will create an account withe the username of 'joeuser' with the email address of joeuser@sld.tld, and set that user's account as an admin. This will result in a link that you can paste into the browser, which logs you in and enables you to set the password.
|
||||
|
||||
### Updating
|
||||
Generally, doing the following is enough:
|
||||
```sh
|
||||
# Download the new release
|
||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl update"
|
||||
|
||||
# Migrate the database, you are advised to stop the instance before doing that
|
||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
|
||||
```
|
||||
But you should **always check the release notes/changelog** in case there are config deprecations, special update steps, etc.
|
||||
|
||||
## Further reading
|
||||
|
||||
* [Backup your instance](../administration/backup.md)
|
||||
* [Hardening your instance](../configuration/hardening.md)
|
||||
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
||||
* [Updating your instance](../administration/updating.md)
|
||||
|
||||
## 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**.
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ On the top right you will also see a wrench icon. This opens your personal setti
|
|||
This is where the interesting stuff happens!
|
||||
Depending on the timeline you will see different statuses, but each status has a standard structure:
|
||||
|
||||
- Profile pic, name and link to profile. An optional left-arrow if it's a reply to another status (hovering will reveal the replied-to status). Clicking on the profile pic will uncollapse the user's profile.
|
||||
- Profile pic, name and link to profile. An optional left-arrow if it's a reply to another status (hovering will reveal the reply-to status). Clicking on the profile pic will uncollapse the user's profile.
|
||||
- A `+` button on the right allows you to Expand/Collapse an entire discussion thread. It also updates in realtime!
|
||||
- An arrow icon allows you to open the status on the instance where it's originating from.
|
||||
- The text of the status, including mentions and attachements. If you click on a mention, it will automatically open the profile page of that person.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.Config do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.Emoji do
|
||||
|
@ -186,11 +186,7 @@ def run(["gen-pack", src]) do
|
|||
|
||||
tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
|
||||
|
||||
{:ok, _} =
|
||||
:zip.unzip(
|
||||
binary_archive,
|
||||
cwd: tmp_pack_dir
|
||||
)
|
||||
{:ok, _} = :zip.unzip(binary_archive, cwd: String.to_charlist(tmp_pack_dir))
|
||||
|
||||
emoji_map = Pleroma.Emoji.Loader.make_shortcode_to_file_map(tmp_pack_dir, exts)
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.Instance do
|
||||
use Mix.Task
|
||||
import Mix.Pleroma
|
||||
|
||||
alias Pleroma.Config
|
||||
|
||||
@shortdoc "Manages Pleroma instance"
|
||||
@moduledoc File.read!("docs/administration/CLI_tasks/instance.md")
|
||||
|
||||
|
@ -63,7 +65,8 @@ def run(["gen" | rest]) do
|
|||
get_option(
|
||||
options,
|
||||
:instance_name,
|
||||
"What is the name of your instance? (e.g. Pleroma/Soykaf)"
|
||||
"What is the name of your instance? (e.g. The Corndog Emporium)",
|
||||
domain
|
||||
)
|
||||
|
||||
email = get_option(options, :admin_email, "What is your admin email address?")
|
||||
|
@ -153,6 +156,8 @@ def run(["gen" | rest]) do
|
|||
Pleroma.Config.get([:instance, :static_dir])
|
||||
)
|
||||
|
||||
Config.put([:instance, :static_dir], static_dir)
|
||||
|
||||
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||
jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
|
||||
|
@ -202,8 +207,14 @@ def run(["gen" | rest]) do
|
|||
write_robots_txt(indexable, template_dir)
|
||||
|
||||
shell_info(
|
||||
"\n All files successfully written! Refer to the installation instructions for your platform for next steps"
|
||||
"\n All files successfully written! Refer to the installation instructions for your platform for next steps."
|
||||
)
|
||||
|
||||
if db_configurable? do
|
||||
shell_info(
|
||||
" Please transfer your config to the database after running database migrations. Refer to \"Transfering the config to/from the database\" section of the docs for more information."
|
||||
)
|
||||
end
|
||||
else
|
||||
shell_error(
|
||||
"The task would have overwritten the following files:\n" <>
|
||||
|
|
46
lib/mix/tasks/pleroma/refresh_counter_cache.ex
Normal file
46
lib/mix/tasks/pleroma/refresh_counter_cache.ex
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.RefreshCounterCache do
|
||||
@shortdoc "Refreshes counter cache"
|
||||
|
||||
use Mix.Task
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.CounterCache
|
||||
alias Pleroma.Repo
|
||||
|
||||
require Logger
|
||||
import Ecto.Query
|
||||
|
||||
def run([]) do
|
||||
Mix.Pleroma.start_pleroma()
|
||||
|
||||
["public", "unlisted", "private", "direct"]
|
||||
|> Enum.each(fn visibility ->
|
||||
count = status_visibility_count_query(visibility)
|
||||
name = "status_visibility_#{visibility}"
|
||||
CounterCache.set(name, count)
|
||||
Mix.Pleroma.shell_info("Set #{name} to #{count}")
|
||||
end)
|
||||
|
||||
Mix.Pleroma.shell_info("Done")
|
||||
end
|
||||
|
||||
defp status_visibility_count_query(visibility) do
|
||||
Activity
|
||||
|> where(
|
||||
[a],
|
||||
fragment(
|
||||
"activity_visibility(?, ?, ?) = ?",
|
||||
a.actor,
|
||||
a.recipients,
|
||||
a.data,
|
||||
^visibility
|
||||
)
|
||||
)
|
||||
|> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
|
||||
|> Repo.aggregate(:count, :id, timeout: :timer.minutes(30))
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.RobotsTxt do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.User do
|
||||
|
@ -100,8 +100,7 @@ def run(["rm", nickname]) do
|
|||
User.perform(:delete, user)
|
||||
shell_info("User #{nickname} deleted.")
|
||||
else
|
||||
_ ->
|
||||
shell_error("No local user #{nickname}")
|
||||
_ -> shell_error("No local user #{nickname}")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Activity do
|
||||
|
@ -310,7 +310,7 @@ def follow_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do
|
|||
|
||||
def restrict_deactivated_users(query) do
|
||||
deactivated_users =
|
||||
from(u in User.Query.build(deactivated: true), select: u.ap_id)
|
||||
from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|
||||
|> Repo.all()
|
||||
|
||||
Activity.Queries.exclude_authors(query, deactivated_users)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Activity.Queries do
|
||||
|
@ -7,7 +7,7 @@ defmodule Pleroma.Activity.Queries do
|
|||
Contains queries for Activity.
|
||||
"""
|
||||
|
||||
import Ecto.Query, only: [from: 2]
|
||||
import Ecto.Query, only: [from: 2, where: 3]
|
||||
|
||||
@type query :: Ecto.Queryable.t() | Activity.t()
|
||||
|
||||
|
@ -30,7 +30,7 @@ def by_actor(query \\ Activity, actor) do
|
|||
)
|
||||
end
|
||||
|
||||
@spec by_author(query, String.t()) :: query
|
||||
@spec by_author(query, User.t()) :: query
|
||||
def by_author(query \\ Activity, %User{ap_id: ap_id}) do
|
||||
from(a in query, where: a.actor == ^ap_id)
|
||||
end
|
||||
|
@ -63,6 +63,22 @@ def by_object_id(query, object_id) when is_binary(object_id) do
|
|||
)
|
||||
end
|
||||
|
||||
@spec by_object_in_reply_to_id(query, String.t(), keyword()) :: query
|
||||
def by_object_in_reply_to_id(query, in_reply_to_id, opts \\ []) do
|
||||
query =
|
||||
if opts[:skip_preloading] do
|
||||
Activity.with_joined_object(query)
|
||||
else
|
||||
Activity.with_preloaded_object(query)
|
||||
end
|
||||
|
||||
where(
|
||||
query,
|
||||
[activity, object: o],
|
||||
fragment("(?)->>'inReplyTo' = ?", o.data, ^to_string(in_reply_to_id))
|
||||
)
|
||||
end
|
||||
|
||||
@spec by_type(query, String.t()) :: query
|
||||
def by_type(query \\ Activity, activity_type) do
|
||||
from(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Activity.Search do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ActivityExpiration do
|
||||
|
@ -62,6 +62,6 @@ def validate_scheduled_at(changeset) do
|
|||
def expires_late_enough?(scheduled_at) do
|
||||
now = NaiveDateTime.utc_now()
|
||||
diff = NaiveDateTime.diff(scheduled_at, now, :millisecond)
|
||||
diff >= @min_activity_lifetime
|
||||
diff > @min_activity_lifetime
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Application do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Captcha do
|
||||
|
@ -50,7 +50,7 @@ def handle_call(:new, _from, state) do
|
|||
token = new_captcha[:token]
|
||||
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
|
||||
sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign")
|
||||
# Basicallty copy what Phoenix.Token does here, add the time to
|
||||
# Basically copy what Phoenix.Token does here, add the time to
|
||||
# the actual data and make it a binary to then encrypt it
|
||||
encrypted_captcha_answer =
|
||||
%{
|
||||
|
@ -62,7 +62,7 @@ def handle_call(:new, _from, state) do
|
|||
|
||||
{
|
||||
:reply,
|
||||
# Repalce the answer with the encrypted answer
|
||||
# Replace the answer with the encrypted answer
|
||||
%{new_captcha | answer_data: encrypted_captcha_answer},
|
||||
state
|
||||
}
|
||||
|
@ -82,7 +82,8 @@ def handle_call({:validate, token, captcha, answer_data}, _from, state) do
|
|||
valid_if_after = DateTime.subtract!(DateTime.now_utc(), seconds_valid)
|
||||
|
||||
result =
|
||||
with {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
|
||||
with false <- is_nil(answer_data),
|
||||
{:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
|
||||
%{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do
|
||||
try do
|
||||
if DateTime.before?(at, valid_if_after),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Captcha.Native do
|
||||
|
@ -10,8 +10,8 @@ defmodule Pleroma.Captcha.Native do
|
|||
@impl Service
|
||||
def new do
|
||||
case Captcha.get() do
|
||||
{:timeout} ->
|
||||
%{error: dgettext("errors", "Captcha timeout")}
|
||||
:error ->
|
||||
%{error: dgettext("errors", "Captcha error")}
|
||||
|
||||
{:ok, answer_data, img_binary} ->
|
||||
%{
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ConfigDB do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Config.Holder do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Config.Loader do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Config.TransferTask do
|
||||
|
@ -146,9 +146,7 @@ defp group_and_subkey_need_reboot?(group, key, value) do
|
|||
defp update_env(group, key, nil), do: Application.delete_env(group, key)
|
||||
defp update_env(group, key, value), do: Application.put_env(group, key, value)
|
||||
|
||||
defp restart(_, :pleroma, :test), do: Logger.warn("pleroma restarted")
|
||||
|
||||
defp restart(_, :pleroma, _), do: send(Restarter.Pleroma, :after_boot)
|
||||
defp restart(_, :pleroma, env), do: Restarter.Pleroma.restart_after_boot(env)
|
||||
|
||||
defp restart(started_applications, app, _) do
|
||||
with {^app, _, _} <- List.keyfind(started_applications, app, 0),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Conversation.Participation do
|
||||
|
@ -133,10 +133,8 @@ def restrict_recipients(query, user, %{"recipients" => user_ids}) do
|
|||
[user.id | user_ids]
|
||||
|> Enum.uniq()
|
||||
|> Enum.reduce([], fn user_id, acc ->
|
||||
case FlakeId.Ecto.CompatType.dump(user_id) do
|
||||
{:ok, user_id} -> [user_id | acc]
|
||||
_ -> acc
|
||||
end
|
||||
{:ok, user_id} = FlakeId.Ecto.CompatType.dump(user_id)
|
||||
[user_id | acc]
|
||||
end)
|
||||
|
||||
conversation_subquery =
|
||||
|
|
41
lib/pleroma/counter_cache.ex
Normal file
41
lib/pleroma/counter_cache.ex
Normal file
|
@ -0,0 +1,41 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.CounterCache do
|
||||
alias Pleroma.CounterCache
|
||||
alias Pleroma.Repo
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
import Ecto.Query
|
||||
|
||||
schema "counter_cache" do
|
||||
field(:name, :string)
|
||||
field(:count, :integer)
|
||||
end
|
||||
|
||||
def changeset(struct, params) do
|
||||
struct
|
||||
|> cast(params, [:name, :count])
|
||||
|> validate_required([:name])
|
||||
|> unique_constraint(:name)
|
||||
end
|
||||
|
||||
def get_as_map(names) when is_list(names) do
|
||||
CounterCache
|
||||
|> where([cc], cc.name in ^names)
|
||||
|> Repo.all()
|
||||
|> Enum.group_by(& &1.name, & &1.count)
|
||||
|> Map.new(fn {k, v} -> {k, hd(v)} end)
|
||||
end
|
||||
|
||||
def set(name, count) do
|
||||
%CounterCache{}
|
||||
|> changeset(%{"name" => name, "count" => count})
|
||||
|> Repo.insert(
|
||||
on_conflict: [set: [count: count]],
|
||||
returning: true,
|
||||
conflict_target: :name
|
||||
)
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Emails.AdminEmail do
|
||||
|
|
32
lib/pleroma/emails/new_users_digest_email.ex
Normal file
32
lib/pleroma/emails/new_users_digest_email.ex
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Emails.NewUsersDigestEmail do
|
||||
use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email_styled}
|
||||
|
||||
defp instance_notify_email do
|
||||
Pleroma.Config.get([:instance, :notify_email]) || Pleroma.Config.get([:instance, :email])
|
||||
end
|
||||
|
||||
def new_users(to, users_and_statuses) do
|
||||
instance_name = Pleroma.Config.get([:instance, :name])
|
||||
styling = Pleroma.Config.get([Pleroma.Emails.UserEmail, :styling])
|
||||
|
||||
logo_url =
|
||||
Pleroma.Web.Endpoint.url() <>
|
||||
Pleroma.Config.get([:frontend_configurations, :pleroma_fe, :logo])
|
||||
|
||||
new()
|
||||
|> to({to.name, to.email})
|
||||
|> from({instance_name, instance_notify_email()})
|
||||
|> subject("#{instance_name} New Users")
|
||||
|> render_body("new_users_digest.html", %{
|
||||
title: "New Users",
|
||||
users_and_statuses: users_and_statuses,
|
||||
instance: instance_name,
|
||||
styling: styling,
|
||||
logo_url: logo_url
|
||||
})
|
||||
end
|
||||
end
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.FollowingRelationship do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Formatter do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.HTML do
|
||||
|
@ -108,6 +108,7 @@ def extract_first_external_url(object, content) do
|
|||
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||
result =
|
||||
content
|
||||
|> Floki.parse_fragment!()
|
||||
|> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"]")
|
||||
|> Floki.attribute("a", "href")
|
||||
|> Enum.at(0)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.MIME do
|
||||
|
@ -9,7 +9,7 @@ defmodule Pleroma.MIME do
|
|||
@default "application/octet-stream"
|
||||
@read_bytes 35
|
||||
|
||||
@spec file_mime_type(String.t()) ::
|
||||
@spec file_mime_type(String.t(), String.t()) ::
|
||||
{:ok, content_type :: String.t(), filename :: String.t()} | {:error, any()} | :error
|
||||
def file_mime_type(path, filename) do
|
||||
with {:ok, content_type} <- file_mime_type(path),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Notification do
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Object do
|
||||
use Ecto.Schema
|
||||
|
||||
import Ecto.Query
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Fetcher
|
||||
|
@ -12,9 +15,6 @@ defmodule Pleroma.Object do
|
|||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
import Ecto.Query
|
||||
import Ecto.Changeset
|
||||
|
||||
require Logger
|
||||
|
||||
@type t() :: %__MODULE__{}
|
||||
|
@ -145,18 +145,18 @@ def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}),
|
|||
# Legacy objects can be mutated by anybody
|
||||
def authorize_mutation(%Object{}, %User{}), do: true
|
||||
|
||||
@spec get_cached_by_ap_id(String.t()) :: Object.t() | nil
|
||||
def get_cached_by_ap_id(ap_id) do
|
||||
key = "object:#{ap_id}"
|
||||
|
||||
Cachex.fetch!(:object_cache, key, fn _ ->
|
||||
object = get_by_ap_id(ap_id)
|
||||
|
||||
if object do
|
||||
{:commit, object}
|
||||
else
|
||||
{:ignore, object}
|
||||
end
|
||||
end)
|
||||
with {:ok, nil} <- Cachex.get(:object_cache, key),
|
||||
object when not is_nil(object) <- get_by_ap_id(ap_id),
|
||||
{:ok, true} <- Cachex.put(:object_cache, key, object) do
|
||||
object
|
||||
else
|
||||
{:ok, object} -> object
|
||||
nil -> nil
|
||||
end
|
||||
end
|
||||
|
||||
def context_mapping(context) do
|
||||
|
@ -301,4 +301,26 @@ def update_data(%Object{data: data} = object, attrs \\ %{}) do
|
|||
def local?(%Object{data: %{"id" => id}}) do
|
||||
String.starts_with?(id, Pleroma.Web.base_url() <> "/")
|
||||
end
|
||||
|
||||
def replies(object, opts \\ []) do
|
||||
object = Object.normalize(object)
|
||||
|
||||
query =
|
||||
Object
|
||||
|> where(
|
||||
[o],
|
||||
fragment("(?)->>'inReplyTo' = ?", o.data, ^object.data["id"])
|
||||
)
|
||||
|> order_by([o], asc: o.id)
|
||||
|
||||
if opts[:self_only] do
|
||||
actor = object.data["actor"]
|
||||
where(query, [o], fragment("(?)->>'actor' = ?", o.data, ^actor))
|
||||
else
|
||||
query
|
||||
end
|
||||
end
|
||||
|
||||
def self_replies(object, opts \\ []),
|
||||
do: replies(object, Keyword.put(opts, :self_only, true))
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Object.Containment do
|
||||
|
@ -39,15 +39,8 @@ def get_actor(%{"actor" => nil, "attributedTo" => actor}) when not is_nil(actor)
|
|||
defp compare_uris(_, %URI{scheme: "tag"}), do: :ok
|
||||
end
|
||||
|
||||
defp compare_uris(%URI{} = id_uri, %URI{} = other_uri) do
|
||||
if id_uri.host == other_uri.host do
|
||||
:ok
|
||||
else
|
||||
:error
|
||||
end
|
||||
end
|
||||
|
||||
defp compare_uris(_, _), do: :error
|
||||
defp compare_uris(%URI{host: host} = _id_uri, %URI{host: host} = _other_uri), do: :ok
|
||||
defp compare_uris(_id_uri, _other_uri), do: :error
|
||||
|
||||
@doc """
|
||||
Checks that an imported AP object's actor matches the domain it came from.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Object.Fetcher do
|
||||
|
@ -10,6 +10,7 @@ defmodule Pleroma.Object.Fetcher do
|
|||
alias Pleroma.Signature
|
||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||
alias Pleroma.Web.Federator
|
||||
|
||||
require Logger
|
||||
require Pleroma.Constants
|
||||
|
@ -59,20 +60,23 @@ def refetch_object(%Object{data: %{"id" => id}} = object) do
|
|||
end
|
||||
end
|
||||
|
||||
# TODO:
|
||||
# This will create a Create activity, which we need internally at the moment.
|
||||
# Note: will create a Create activity, which we need internally at the moment.
|
||||
def fetch_object_from_id(id, options \\ []) do
|
||||
with {:fetch_object, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
|
||||
{:fetch, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
|
||||
{:normalize, nil} <- {:normalize, Object.normalize(data, false)},
|
||||
with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
|
||||
{_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])},
|
||||
{_, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
|
||||
{_, nil} <- {:normalize, Object.normalize(data, false)},
|
||||
params <- prepare_activity_params(data),
|
||||
{:containment, :ok} <- {:containment, Containment.contain_origin(id, params)},
|
||||
{:transmogrifier, {:ok, activity}} <-
|
||||
{_, :ok} <- {:containment, Containment.contain_origin(id, params)},
|
||||
{_, {:ok, activity}} <-
|
||||
{:transmogrifier, Transmogrifier.handle_incoming(params, options)},
|
||||
{:object, _data, %Object{} = object} <-
|
||||
{_, _data, %Object{} = object} <-
|
||||
{:object, data, Object.normalize(activity, false)} do
|
||||
{:ok, object}
|
||||
else
|
||||
{:allowed_depth, false} ->
|
||||
{:error, "Max thread distance exceeded."}
|
||||
|
||||
{:containment, _} ->
|
||||
{:error, "Object containment failed."}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Pagination do
|
||||
|
@ -12,11 +12,15 @@ defmodule Pleroma.Pagination do
|
|||
|
||||
alias Pleroma.Repo
|
||||
|
||||
@type type :: :keyset | :offset
|
||||
|
||||
@default_limit 20
|
||||
@max_limit 40
|
||||
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
||||
|
||||
def page_keys, do: @page_keys
|
||||
|
||||
@spec fetch_paginated(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
||||
def fetch_paginated(query, params, type \\ :keyset, table_binding \\ nil)
|
||||
|
||||
def fetch_paginated(query, %{"total" => true} = params, :keyset, table_binding) do
|
||||
|
@ -57,6 +61,7 @@ def fetch_paginated(query, params, :offset, table_binding) do
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
||||
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
|
||||
|
||||
def paginate(query, options, :keyset, table_binding) do
|
||||
|
@ -130,7 +135,11 @@ defp restrict(query, :offset, %{offset: offset}, _table_binding) do
|
|||
end
|
||||
|
||||
defp restrict(query, :limit, options, _table_binding) do
|
||||
limit = Map.get(options, :limit, @default_limit)
|
||||
limit =
|
||||
case Map.get(options, :limit, @default_limit) do
|
||||
limit when limit < @max_limit -> limit
|
||||
_ -> @max_limit
|
||||
end
|
||||
|
||||
query
|
||||
|> limit(^limit)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Plugs.HTTPSecurityPlug do
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
||||
import Plug.Conn
|
||||
import Phoenix.Controller, only: [get_format: 1, text: 2]
|
||||
require Logger
|
||||
|
||||
def init(options) do
|
||||
|
@ -15,25 +16,27 @@ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
|
|||
end
|
||||
|
||||
def call(conn, _opts) do
|
||||
headers = get_req_header(conn, "signature")
|
||||
signature = Enum.at(headers, 0)
|
||||
if get_format(conn) == "activity+json" do
|
||||
conn
|
||||
|> maybe_assign_valid_signature()
|
||||
|> maybe_require_signature()
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
|
||||
if signature do
|
||||
defp maybe_assign_valid_signature(conn) do
|
||||
if has_signature_header?(conn) do
|
||||
# set (request-target) header to the appropriate value
|
||||
# we also replace the digest header with the one we computed
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header(
|
||||
"(request-target)",
|
||||
String.downcase("#{conn.method}") <> " #{conn.request_path}"
|
||||
)
|
||||
request_target = String.downcase("#{conn.method}") <> " #{conn.request_path}"
|
||||
|
||||
conn =
|
||||
if conn.assigns[:digest] do
|
||||
conn
|
||||
|> put_req_header("digest", conn.assigns[:digest])
|
||||
else
|
||||
conn
|
||||
conn
|
||||
|> put_req_header("(request-target)", request_target)
|
||||
|> case do
|
||||
%{assigns: %{digest: digest}} = conn -> put_req_header(conn, "digest", digest)
|
||||
conn -> conn
|
||||
end
|
||||
|
||||
assign(conn, :valid_signature, HTTPSignatures.validate_conn(conn))
|
||||
|
@ -42,4 +45,21 @@ def call(conn, _opts) do
|
|||
conn
|
||||
end
|
||||
end
|
||||
|
||||
defp has_signature_header?(conn) do
|
||||
conn |> get_req_header("signature") |> Enum.at(0, false)
|
||||
end
|
||||
|
||||
defp maybe_require_signature(%{assigns: %{valid_signature: true}} = conn), do: conn
|
||||
|
||||
defp maybe_require_signature(conn) do
|
||||
if Pleroma.Config.get([:activitypub, :authorized_fetch_mode], false) do
|
||||
conn
|
||||
|> put_status(:unauthorized)
|
||||
|> text("Request not signed")
|
||||
|> halt()
|
||||
else
|
||||
conn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Plugs.OAuthScopesPlug do
|
||||
|
|
|
@ -7,8 +7,8 @@ def start_link(init_arg) do
|
|||
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
|
||||
end
|
||||
|
||||
def add_limiter(limiter_name, expiration) do
|
||||
{:ok, _pid} =
|
||||
def add_or_return_limiter(limiter_name, expiration) do
|
||||
result =
|
||||
DynamicSupervisor.start_child(
|
||||
__MODULE__,
|
||||
%{
|
||||
|
@ -28,6 +28,12 @@ def add_limiter(limiter_name, expiration) do
|
|||
]}
|
||||
}
|
||||
)
|
||||
|
||||
case result do
|
||||
{:ok, _pid} = result -> result
|
||||
{:error, {:already_started, pid}} -> {:ok, pid}
|
||||
_ -> result
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Plugs.RateLimiter do
|
||||
|
@ -7,12 +7,14 @@ defmodule Pleroma.Plugs.RateLimiter do
|
|||
|
||||
## Configuration
|
||||
|
||||
A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where:
|
||||
A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration.
|
||||
The basic configuration is a tuple where:
|
||||
|
||||
* The first element: `scale` (Integer). The time scale in milliseconds.
|
||||
* The second element: `limit` (Integer). How many requests to limit in the time scale provided.
|
||||
|
||||
It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
|
||||
It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a
|
||||
list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
|
||||
|
||||
To disable a limiter set its value to `nil`.
|
||||
|
||||
|
@ -64,91 +66,102 @@ defmodule Pleroma.Plugs.RateLimiter do
|
|||
import Pleroma.Web.TranslationHelpers
|
||||
import Plug.Conn
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Plugs.RateLimiter.LimiterSupervisor
|
||||
alias Pleroma.User
|
||||
|
||||
require Logger
|
||||
|
||||
def init(opts) do
|
||||
limiter_name = Keyword.get(opts, :name)
|
||||
@doc false
|
||||
def init(plug_opts) do
|
||||
plug_opts
|
||||
end
|
||||
|
||||
case Pleroma.Config.get([:rate_limit, limiter_name]) do
|
||||
nil ->
|
||||
nil
|
||||
|
||||
config ->
|
||||
name_root = Keyword.get(opts, :bucket_name, limiter_name)
|
||||
|
||||
%{
|
||||
name: name_root,
|
||||
limits: config,
|
||||
opts: opts
|
||||
}
|
||||
def call(conn, plug_opts) do
|
||||
if disabled?() do
|
||||
handle_disabled(conn)
|
||||
else
|
||||
action_settings = action_settings(plug_opts)
|
||||
handle(conn, action_settings)
|
||||
end
|
||||
end
|
||||
|
||||
# Do not limit if there is no limiter configuration
|
||||
def call(conn, nil), do: conn
|
||||
defp handle_disabled(conn) do
|
||||
if Config.get(:env) == :prod do
|
||||
Logger.warn("Rate limiter is disabled for localhost/socket")
|
||||
end
|
||||
|
||||
def call(conn, settings) do
|
||||
case disabled?() do
|
||||
true ->
|
||||
if Pleroma.Config.get(:env) == :prod,
|
||||
do: Logger.warn("Rate limiter is disabled for localhost/socket")
|
||||
conn
|
||||
end
|
||||
|
||||
defp handle(conn, nil), do: conn
|
||||
|
||||
defp handle(conn, action_settings) do
|
||||
action_settings
|
||||
|> incorporate_conn_info(conn)
|
||||
|> check_rate()
|
||||
|> case do
|
||||
{:ok, _count} ->
|
||||
conn
|
||||
|
||||
false ->
|
||||
settings
|
||||
|> incorporate_conn_info(conn)
|
||||
|> check_rate()
|
||||
|> case do
|
||||
{:ok, _count} ->
|
||||
conn
|
||||
|
||||
{:error, _count} ->
|
||||
render_throttled_error(conn)
|
||||
end
|
||||
{:error, _count} ->
|
||||
render_throttled_error(conn)
|
||||
end
|
||||
end
|
||||
|
||||
def disabled? do
|
||||
localhost_or_socket =
|
||||
Pleroma.Config.get([Pleroma.Web.Endpoint, :http, :ip])
|
||||
Config.get([Pleroma.Web.Endpoint, :http, :ip])
|
||||
|> Tuple.to_list()
|
||||
|> Enum.join(".")
|
||||
|> String.match?(~r/^local|^127.0.0.1/)
|
||||
|
||||
remote_ip_disabled = not Pleroma.Config.get([Pleroma.Plugs.RemoteIp, :enabled])
|
||||
remote_ip_disabled = not Config.get([Pleroma.Plugs.RemoteIp, :enabled])
|
||||
|
||||
localhost_or_socket and remote_ip_disabled
|
||||
end
|
||||
|
||||
def inspect_bucket(conn, name_root, settings) do
|
||||
settings =
|
||||
settings
|
||||
|> incorporate_conn_info(conn)
|
||||
@inspect_bucket_not_found {:error, :not_found}
|
||||
|
||||
bucket_name = make_bucket_name(%{settings | name: name_root})
|
||||
key_name = make_key_name(settings)
|
||||
limit = get_limits(settings)
|
||||
def inspect_bucket(conn, bucket_name_root, plug_opts) do
|
||||
with %{name: _} = action_settings <- action_settings(plug_opts) do
|
||||
action_settings = incorporate_conn_info(action_settings, conn)
|
||||
bucket_name = make_bucket_name(%{action_settings | name: bucket_name_root})
|
||||
key_name = make_key_name(action_settings)
|
||||
limit = get_limits(action_settings)
|
||||
|
||||
case Cachex.get(bucket_name, key_name) do
|
||||
{:error, :no_cache} ->
|
||||
{:err, :not_found}
|
||||
case Cachex.get(bucket_name, key_name) do
|
||||
{:error, :no_cache} ->
|
||||
@inspect_bucket_not_found
|
||||
|
||||
{:ok, nil} ->
|
||||
{0, limit}
|
||||
{:ok, nil} ->
|
||||
{0, limit}
|
||||
|
||||
{:ok, value} ->
|
||||
{value, limit - value}
|
||||
{:ok, value} ->
|
||||
{value, limit - value}
|
||||
end
|
||||
else
|
||||
_ -> @inspect_bucket_not_found
|
||||
end
|
||||
end
|
||||
|
||||
defp check_rate(settings) do
|
||||
bucket_name = make_bucket_name(settings)
|
||||
key_name = make_key_name(settings)
|
||||
limit = get_limits(settings)
|
||||
def action_settings(plug_opts) do
|
||||
with limiter_name when is_atom(limiter_name) <- plug_opts[:name],
|
||||
limits when not is_nil(limits) <- Config.get([:rate_limit, limiter_name]) do
|
||||
bucket_name_root = Keyword.get(plug_opts, :bucket_name, limiter_name)
|
||||
|
||||
%{
|
||||
name: bucket_name_root,
|
||||
limits: limits,
|
||||
opts: plug_opts
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
defp check_rate(action_settings) do
|
||||
bucket_name = make_bucket_name(action_settings)
|
||||
key_name = make_key_name(action_settings)
|
||||
limit = get_limits(action_settings)
|
||||
|
||||
case Cachex.get_and_update(bucket_name, key_name, &increment_value(&1, limit)) do
|
||||
{:commit, value} ->
|
||||
|
@ -158,8 +171,8 @@ defp check_rate(settings) do
|
|||
{:error, value}
|
||||
|
||||
{:error, :no_cache} ->
|
||||
initialize_buckets(settings)
|
||||
check_rate(settings)
|
||||
initialize_buckets!(action_settings)
|
||||
check_rate(action_settings)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -169,16 +182,19 @@ defp increment_value(val, limit) when val >= limit, do: {:ignore, val}
|
|||
|
||||
defp increment_value(val, _limit), do: {:commit, val + 1}
|
||||
|
||||
defp incorporate_conn_info(settings, %{assigns: %{user: %User{id: user_id}}, params: params}) do
|
||||
Map.merge(settings, %{
|
||||
defp incorporate_conn_info(action_settings, %{
|
||||
assigns: %{user: %User{id: user_id}},
|
||||
params: params
|
||||
}) do
|
||||
Map.merge(action_settings, %{
|
||||
mode: :user,
|
||||
conn_params: params,
|
||||
conn_info: "#{user_id}"
|
||||
})
|
||||
end
|
||||
|
||||
defp incorporate_conn_info(settings, %{params: params} = conn) do
|
||||
Map.merge(settings, %{
|
||||
defp incorporate_conn_info(action_settings, %{params: params} = conn) do
|
||||
Map.merge(action_settings, %{
|
||||
mode: :anon,
|
||||
conn_params: params,
|
||||
conn_info: "#{ip(conn)}"
|
||||
|
@ -197,10 +213,10 @@ defp render_throttled_error(conn) do
|
|||
|> halt()
|
||||
end
|
||||
|
||||
defp make_key_name(settings) do
|
||||
defp make_key_name(action_settings) do
|
||||
""
|
||||
|> attach_params(settings)
|
||||
|> attach_identity(settings)
|
||||
|> attach_selected_params(action_settings)
|
||||
|> attach_identity(action_settings)
|
||||
end
|
||||
|
||||
defp get_scale(_, {scale, _}), do: scale
|
||||
|
@ -215,28 +231,35 @@ defp get_limits(%{mode: :user, limits: [_, {_, limit}]}), do: limit
|
|||
|
||||
defp get_limits(%{limits: [{_, limit}, _]}), do: limit
|
||||
|
||||
defp make_bucket_name(%{mode: :user, name: name_root}),
|
||||
do: user_bucket_name(name_root)
|
||||
defp make_bucket_name(%{mode: :user, name: bucket_name_root}),
|
||||
do: user_bucket_name(bucket_name_root)
|
||||
|
||||
defp make_bucket_name(%{mode: :anon, name: name_root}),
|
||||
do: anon_bucket_name(name_root)
|
||||
defp make_bucket_name(%{mode: :anon, name: bucket_name_root}),
|
||||
do: anon_bucket_name(bucket_name_root)
|
||||
|
||||
defp attach_params(input, %{conn_params: conn_params, opts: opts}) do
|
||||
param_string =
|
||||
opts
|
||||
defp attach_selected_params(input, %{conn_params: conn_params, opts: plug_opts}) do
|
||||
params_string =
|
||||
plug_opts
|
||||
|> Keyword.get(:params, [])
|
||||
|> Enum.sort()
|
||||
|> Enum.map(&Map.get(conn_params, &1, ""))
|
||||
|> Enum.join(":")
|
||||
|
||||
"#{input}#{param_string}"
|
||||
[input, params_string]
|
||||
|> Enum.join(":")
|
||||
|> String.replace_leading(":", "")
|
||||
end
|
||||
|
||||
defp initialize_buckets(%{name: _name, limits: nil}), do: :ok
|
||||
defp initialize_buckets!(%{name: _name, limits: nil}), do: :ok
|
||||
|
||||
defp initialize_buckets(%{name: name, limits: limits}) do
|
||||
LimiterSupervisor.add_limiter(anon_bucket_name(name), get_scale(:anon, limits))
|
||||
LimiterSupervisor.add_limiter(user_bucket_name(name), get_scale(:user, limits))
|
||||
defp initialize_buckets!(%{name: name, limits: limits}) do
|
||||
{:ok, _pid} =
|
||||
LimiterSupervisor.add_or_return_limiter(anon_bucket_name(name), get_scale(:anon, limits))
|
||||
|
||||
{:ok, _pid} =
|
||||
LimiterSupervisor.add_or_return_limiter(user_bucket_name(name), get_scale(:user, limits))
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp attach_identity(base, %{mode: :user, conn_info: conn_info}),
|
||||
|
@ -245,6 +268,6 @@ defp attach_identity(base, %{mode: :user, conn_info: conn_info}),
|
|||
defp attach_identity(base, %{mode: :anon, conn_info: conn_info}),
|
||||
do: "ip:#{base}:#{conn_info}"
|
||||
|
||||
defp user_bucket_name(name_root), do: "user:#{name_root}" |> String.to_atom()
|
||||
defp anon_bucket_name(name_root), do: "anon:#{name_root}" |> String.to_atom()
|
||||
defp user_bucket_name(bucket_name_root), do: "user:#{bucket_name_root}" |> String.to_atom()
|
||||
defp anon_bucket_name(bucket_name_root), do: "anon:#{bucket_name_root}" |> String.to_atom()
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Plugs.RemoteIp do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Plugs.UserEnabledPlug do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Plugs.UserIsAdminPlug do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Repo do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.ScheduledActivity do
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Stats do
|
||||
import Ecto.Query
|
||||
alias Pleroma.CounterCache
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
|
@ -96,4 +97,21 @@ defp get_stat_data do
|
|||
}
|
||||
}
|
||||
end
|
||||
|
||||
def get_status_visibility_count do
|
||||
counter_cache =
|
||||
CounterCache.get_as_map([
|
||||
"status_visibility_public",
|
||||
"status_visibility_private",
|
||||
"status_visibility_unlisted",
|
||||
"status_visibility_direct"
|
||||
])
|
||||
|
||||
%{
|
||||
public: counter_cache["status_visibility_public"] || 0,
|
||||
unlisted: counter_cache["status_visibility_unlisted"] || 0,
|
||||
private: counter_cache["status_visibility_private"] || 0,
|
||||
direct: counter_cache["status_visibility_direct"] || 0
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,6 +37,7 @@ defmodule Pleroma.Upload do
|
|||
Plug.Upload.t()
|
||||
| (data_uri_string :: String.t())
|
||||
| {:from_local, name :: String.t(), id :: String.t(), path :: String.t()}
|
||||
| map()
|
||||
|
||||
@type option ::
|
||||
{:type, :avatar | :banner | :background}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Uploaders.Local do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Uploaders.S3 do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Uploaders.Uploader do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User do
|
||||
|
@ -749,9 +749,18 @@ def invalidate_cache(user) do
|
|||
Cachex.del(:user_cache, "nickname:#{user.nickname}")
|
||||
end
|
||||
|
||||
@spec get_cached_by_ap_id(String.t()) :: User.t() | nil
|
||||
def get_cached_by_ap_id(ap_id) do
|
||||
key = "ap_id:#{ap_id}"
|
||||
Cachex.fetch!(:user_cache, key, fn _ -> get_by_ap_id(ap_id) end)
|
||||
|
||||
with {:ok, nil} <- Cachex.get(:user_cache, key),
|
||||
user when not is_nil(user) <- get_by_ap_id(ap_id),
|
||||
{:ok, true} <- Cachex.put(:user_cache, key, user) do
|
||||
user
|
||||
else
|
||||
{:ok, user} -> user
|
||||
nil -> nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_cached_by_id(id) do
|
||||
|
@ -853,14 +862,14 @@ def get_followers_query(user, page) do
|
|||
@spec get_followers_query(User.t()) :: Ecto.Query.t()
|
||||
def get_followers_query(user), do: get_followers_query(user, nil)
|
||||
|
||||
@spec get_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
|
||||
@spec get_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
|
||||
def get_followers(user, page \\ nil) do
|
||||
user
|
||||
|> get_followers_query(page)
|
||||
|> Repo.all()
|
||||
end
|
||||
|
||||
@spec get_external_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
|
||||
@spec get_external_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
|
||||
def get_external_followers(user, page \\ nil) do
|
||||
user
|
||||
|> get_followers_query(page)
|
||||
|
@ -1304,7 +1313,6 @@ def perform(:delete, %User{} = user) do
|
|||
Repo.delete(user)
|
||||
end
|
||||
|
||||
@spec perform(atom(), User.t()) :: {:ok, User.t()}
|
||||
def perform(:fetch_initial_posts, %User{} = user) do
|
||||
pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
|
||||
|
||||
|
@ -1336,7 +1344,6 @@ def perform(:blocks_import, %User{} = blocker, blocked_identifiers)
|
|||
)
|
||||
end
|
||||
|
||||
@spec perform(atom(), User.t(), list()) :: list() | {:error, any()}
|
||||
def perform(:follow_import, %User{} = follower, followed_identifiers)
|
||||
when is_list(followed_identifiers) do
|
||||
Enum.map(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.Query do
|
||||
|
@ -48,7 +48,7 @@ defmodule Pleroma.User.Query do
|
|||
followers: User.t(),
|
||||
friends: User.t(),
|
||||
recipients_from_activity: [String.t()],
|
||||
nickname: [String.t()],
|
||||
nickname: [String.t()] | String.t(),
|
||||
ap_id: [String.t()],
|
||||
order_by: term(),
|
||||
select: term(),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.User.Search do
|
||||
|
@ -33,9 +33,15 @@ defp format_query(query_string) do
|
|||
# Strip the beginning @ off if there is a query
|
||||
query_string = String.trim_leading(query_string, "@")
|
||||
|
||||
with [name, domain] <- String.split(query_string, "@"),
|
||||
formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") do
|
||||
name <> "@" <> to_string(:idna.encode(formatted_domain))
|
||||
with [name, domain] <- String.split(query_string, "@") do
|
||||
encoded_domain =
|
||||
domain
|
||||
|> String.replace(~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "")
|
||||
|> String.to_charlist()
|
||||
|> :idna.encode()
|
||||
|> to_string()
|
||||
|
||||
name <> "@" <> encoded_domain
|
||||
else
|
||||
_ -> query_string
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.UserRelationship do
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
|||
alias Pleroma.Activity.Ir.Topics
|
||||
alias Pleroma.ActivityExpiration
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.Constants
|
||||
alias Pleroma.Conversation
|
||||
alias Pleroma.Conversation.Participation
|
||||
alias Pleroma.Notification
|
||||
|
@ -125,6 +126,7 @@ def increase_poll_votes_if_vote(%{
|
|||
|
||||
def increase_poll_votes_if_vote(_create_data), do: :noop
|
||||
|
||||
@spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
|
||||
with nil <- Activity.normalize(map),
|
||||
map <- lazy_put_activity_defaults(map, fake),
|
||||
|
@ -242,12 +244,19 @@ def stream_out(_activity) do
|
|||
:noop
|
||||
end
|
||||
|
||||
def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
|
||||
@spec create(map(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def create(params, fake \\ false) do
|
||||
with {:ok, result} <- Repo.transaction(fn -> do_create(params, fake) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_create(%{to: to, actor: actor, context: context, object: object} = params, fake) do
|
||||
additional = params[:additional] || %{}
|
||||
# only accept false as false value
|
||||
local = !(params[:local] == false)
|
||||
published = params[:published]
|
||||
quick_insert? = Pleroma.Config.get([:env]) == :benchmark
|
||||
quick_insert? = Config.get([:env]) == :benchmark
|
||||
|
||||
with create_data <-
|
||||
make_create_data(
|
||||
|
@ -270,10 +279,11 @@ def create(%{to: to, actor: actor, context: context, object: object} = params, f
|
|||
{:ok, activity}
|
||||
|
||||
{:error, message} ->
|
||||
{:error, message}
|
||||
Repo.rollback(message)
|
||||
end
|
||||
end
|
||||
|
||||
@spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def listen(%{to: to, actor: actor, context: context, object: object} = params) do
|
||||
additional = params[:additional] || %{}
|
||||
# only accept false as false value
|
||||
|
@ -288,20 +298,20 @@ def listen(%{to: to, actor: actor, context: context, object: object} = params) d
|
|||
{:ok, activity} <- insert(listen_data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:error, message} ->
|
||||
{:error, message}
|
||||
end
|
||||
end
|
||||
|
||||
@spec accept(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def accept(params) do
|
||||
accept_or_reject("Accept", params)
|
||||
end
|
||||
|
||||
@spec reject(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def reject(params) do
|
||||
accept_or_reject("Reject", params)
|
||||
end
|
||||
|
||||
@spec accept_or_reject(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
|
||||
local = Map.get(params, :local, true)
|
||||
activity_id = Map.get(params, :activity_id, nil)
|
||||
|
@ -315,6 +325,7 @@ def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
|
|||
end
|
||||
end
|
||||
|
||||
@spec update(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||
local = !(params[:local] == false)
|
||||
activity_id = params[:activity_id]
|
||||
|
@ -333,7 +344,16 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
|||
end
|
||||
end
|
||||
|
||||
@spec react_with_emoji(User.t(), Object.t(), String.t(), keyword()) ::
|
||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||
def react_with_emoji(user, object, emoji, options \\ []) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_react_with_emoji(user, object, emoji, options) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_react_with_emoji(user, object, emoji, options) do
|
||||
with local <- Keyword.get(options, :local, true),
|
||||
activity_id <- Keyword.get(options, :activity_id, nil),
|
||||
true <- Pleroma.Emoji.is_unicode_emoji?(emoji),
|
||||
|
@ -343,11 +363,21 @@ def react_with_emoji(user, object, emoji, options \\ []) do
|
|||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
e -> {:error, e}
|
||||
false -> {:error, false}
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec unreact_with_emoji(User.t(), String.t(), keyword()) ::
|
||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||
def unreact_with_emoji(user, reaction_id, options \\ []) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_unreact_with_emoji(user, reaction_id, options) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_unreact_with_emoji(user, reaction_id, options) do
|
||||
with local <- Keyword.get(options, :local, true),
|
||||
activity_id <- Keyword.get(options, :activity_id, nil),
|
||||
user_ap_id <- user.ap_id,
|
||||
|
@ -359,17 +389,25 @@ def unreact_with_emoji(user, reaction_id, options \\ []) do
|
|||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
e -> {:error, e}
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: This is weird, maybe we shouldn't check here if we can make the activity.
|
||||
def like(
|
||||
%User{ap_id: ap_id} = user,
|
||||
%Object{data: %{"id" => _}} = object,
|
||||
activity_id \\ nil,
|
||||
local \\ true
|
||||
) do
|
||||
@spec like(User.t(), Object.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||
def like(user, object, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, result} <- Repo.transaction(fn -> do_like(user, object, activity_id, local) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_like(
|
||||
%User{ap_id: ap_id} = user,
|
||||
%Object{data: %{"id" => _}} = object,
|
||||
activity_id,
|
||||
local
|
||||
) do
|
||||
with nil <- get_existing_like(ap_id, object),
|
||||
like_data <- make_like_data(user, object, activity_id),
|
||||
{:ok, activity} <- insert(like_data, local),
|
||||
|
@ -377,12 +415,24 @@ def like(
|
|||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
%Activity{} = activity -> {:ok, activity, object}
|
||||
error -> {:error, error}
|
||||
%Activity{} = activity ->
|
||||
{:ok, activity, object}
|
||||
|
||||
{:error, error} ->
|
||||
Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec unlike(User.t(), Object.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t(), Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
|
||||
def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_unlike(actor, object, activity_id, local) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_unlike(actor, object, activity_id, local) do
|
||||
with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
|
||||
unlike_data <- make_unlike_data(actor, like_activity, activity_id),
|
||||
{:ok, unlike_activity} <- insert(unlike_data, local),
|
||||
|
@ -391,10 +441,13 @@ def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ tru
|
|||
:ok <- maybe_federate(unlike_activity) do
|
||||
{:ok, unlike_activity, like_activity, object}
|
||||
else
|
||||
_e -> {:ok, object}
|
||||
nil -> {:ok, object}
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
|
||||
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||
def announce(
|
||||
%User{ap_id: _} = user,
|
||||
%Object{data: %{"id" => _}} = object,
|
||||
|
@ -402,6 +455,13 @@ def announce(
|
|||
local \\ true,
|
||||
public \\ true
|
||||
) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_announce(user, object, activity_id, local, public) do
|
||||
with true <- is_announceable?(object, user, public),
|
||||
announce_data <- make_announce_data(user, object, activity_id, public),
|
||||
{:ok, activity} <- insert(announce_data, local),
|
||||
|
@ -409,16 +469,26 @@ def announce(
|
|||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity, object}
|
||||
else
|
||||
error -> {:error, error}
|
||||
false -> {:error, false}
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec unannounce(User.t(), Object.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
|
||||
def unannounce(
|
||||
%User{} = actor,
|
||||
%Object{} = object,
|
||||
activity_id \\ nil,
|
||||
local \\ true
|
||||
) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_unannounce(actor, object, activity_id, local) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_unannounce(actor, object, activity_id, local) do
|
||||
with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
|
||||
unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
|
||||
{:ok, unannounce_activity} <- insert(unannounce_data, local),
|
||||
|
@ -427,30 +497,61 @@ def unannounce(
|
|||
{:ok, object} <- remove_announce_from_object(announce_activity, object) do
|
||||
{:ok, unannounce_activity, object}
|
||||
else
|
||||
_e -> {:ok, object}
|
||||
nil -> {:ok, object}
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t()} | {:error, any()}
|
||||
def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_follow(follower, followed, activity_id, local) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_follow(follower, followed, activity_id, local) do
|
||||
with data <- make_follow_data(follower, followed, activity_id),
|
||||
{:ok, activity} <- insert(data, local),
|
||||
:ok <- maybe_federate(activity),
|
||||
_ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t()} | nil | {:error, any()}
|
||||
def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_unfollow(follower, followed, activity_id, local) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_unfollow(follower, followed, activity_id, local) do
|
||||
with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
|
||||
{:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
|
||||
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
|
||||
{:ok, activity} <- insert(unfollow_data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
nil -> nil
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
|
||||
@spec delete(User.t() | Object.t(), keyword()) :: {:ok, User.t() | Object.t()} | {:error, any()}
|
||||
def delete(entity, options \\ []) do
|
||||
with {:ok, result} <- Repo.transaction(fn -> do_delete(entity, options) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_delete(%User{ap_id: ap_id, follower_address: follower_address} = user, _) do
|
||||
with data <- %{
|
||||
"to" => [follower_address],
|
||||
"type" => "Delete",
|
||||
|
@ -463,7 +564,7 @@ def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
|
|||
end
|
||||
end
|
||||
|
||||
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
|
||||
defp do_delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options) do
|
||||
local = Keyword.get(options, :local, true)
|
||||
activity_id = Keyword.get(options, :activity_id, nil)
|
||||
actor = Keyword.get(options, :actor, actor)
|
||||
|
@ -488,11 +589,22 @@ def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ [
|
|||
{:ok, _actor} <- decrease_note_count_if_public(user, object),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
{:error, error} ->
|
||||
Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
|
||||
@spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t()} | {:error, any()}
|
||||
def block(blocker, blocked, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_block(blocker, blocked, activity_id, local) do
|
||||
outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
|
||||
unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
|
||||
|
||||
|
@ -507,20 +619,32 @@ def block(blocker, blocked, activity_id \\ nil, local \\ true) do
|
|||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
_e -> {:ok, nil}
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec unblock(User.t(), User.t(), String.t() | nil, boolean()) ::
|
||||
{:ok, Activity.t()} | {:error, any()} | nil
|
||||
def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
|
||||
with {:ok, result} <-
|
||||
Repo.transaction(fn -> do_unblock(blocker, blocked, activity_id, local) end) do
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
defp do_unblock(blocker, blocked, activity_id, local) do
|
||||
with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
|
||||
unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
|
||||
{:ok, activity} <- insert(unblock_data, local),
|
||||
:ok <- maybe_federate(activity) do
|
||||
{:ok, activity}
|
||||
else
|
||||
nil -> nil
|
||||
{:error, error} -> Repo.rollback(error)
|
||||
end
|
||||
end
|
||||
|
||||
@spec flag(map()) :: {:ok, Activity.t()} | any
|
||||
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def flag(
|
||||
%{
|
||||
actor: actor,
|
||||
|
@ -557,6 +681,7 @@ def flag(
|
|||
end
|
||||
end
|
||||
|
||||
@spec move(User.t(), User.t(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
|
||||
def move(%User{} = origin, %User{} = target, local \\ true) do
|
||||
params = %{
|
||||
"type" => "Move",
|
||||
|
@ -582,7 +707,7 @@ def move(%User{} = origin, %User{} = target, local \\ true) do
|
|||
end
|
||||
|
||||
defp fetch_activities_for_context_query(context, opts) do
|
||||
public = [Pleroma.Constants.as_public()]
|
||||
public = [Constants.as_public()]
|
||||
|
||||
recipients =
|
||||
if opts["user"],
|
||||
|
@ -627,10 +752,11 @@ def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
|
|||
|> Repo.one()
|
||||
end
|
||||
|
||||
@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
|
||||
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
|
||||
opts = Map.drop(opts, ["user"])
|
||||
|
||||
[Pleroma.Constants.as_public()]
|
||||
[Constants.as_public()]
|
||||
|> fetch_activities_query(opts)
|
||||
|> restrict_unlisted()
|
||||
|> Pagination.fetch_paginated(opts, pagination)
|
||||
|
@ -781,13 +907,18 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
|
|||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
def fetch_instance_activities(params) do
|
||||
def fetch_statuses(reading_user, params) do
|
||||
params =
|
||||
params
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("instance", params["instance"])
|
||||
|
||||
fetch_activities([Pleroma.Constants.as_public()], params, :offset)
|
||||
recipients =
|
||||
user_activities_recipients(%{
|
||||
"godmode" => params["godmode"],
|
||||
"reading_user" => reading_user
|
||||
})
|
||||
|
||||
fetch_activities(recipients, params, :offset)
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
|
@ -797,9 +928,9 @@ defp user_activities_recipients(%{"godmode" => true}) do
|
|||
|
||||
defp user_activities_recipients(%{"reading_user" => reading_user}) do
|
||||
if reading_user do
|
||||
[Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
|
||||
[Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
|
||||
else
|
||||
[Pleroma.Constants.as_public()]
|
||||
[Constants.as_public()]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1006,7 +1137,7 @@ defp restrict_unlisted(query) do
|
|||
fragment(
|
||||
"not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
|
||||
activity.data,
|
||||
^[Pleroma.Constants.as_public()]
|
||||
^[Constants.as_public()]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
@ -1180,7 +1311,7 @@ def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
|
|||
@doc """
|
||||
Fetch favorites activities of user with order by sort adds to favorites
|
||||
"""
|
||||
@spec fetch_favourites(User.t(), map(), atom()) :: list(Activity.t())
|
||||
@spec fetch_favourites(User.t(), map(), Pagination.type()) :: list(Activity.t())
|
||||
def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
|
||||
user.ap_id
|
||||
|> Activity.Queries.by_actor()
|
||||
|
@ -1218,7 +1349,7 @@ def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
|
|||
where:
|
||||
fragment("? && ?", activity.recipients, ^recipients) or
|
||||
(fragment("? && ?", activity.recipients, ^recipients_with_public) and
|
||||
^Pleroma.Constants.as_public() in activity.recipients)
|
||||
^Constants.as_public() in activity.recipients)
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -1234,6 +1365,7 @@ def fetch_activities_bounded(
|
|||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
|
||||
def upload(file, opts \\ []) do
|
||||
with {:ok, data} <- Upload.store(file, opts) do
|
||||
obj_data =
|
||||
|
@ -1331,8 +1463,7 @@ defp normalize_counter(counter) when is_integer(counter), do: counter
|
|||
defp normalize_counter(_), do: 0
|
||||
|
||||
defp maybe_update_follow_information(data) do
|
||||
with {:enabled, true} <-
|
||||
{:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
|
||||
with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
|
||||
{:ok, info} <- fetch_follow_information_for_user(data) do
|
||||
info = Map.merge(data[:info] || %{}, info)
|
||||
Map.put(data, :info, info)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
|
||||
|
@ -17,6 +17,7 @@ defp old_user?(%User{} = u) do
|
|||
# does the post contain links?
|
||||
defp contains_links?(%{"content" => content} = _object) do
|
||||
content
|
||||
|> Floki.parse_fragment!()
|
||||
|> Floki.filter_out("a.mention,a.hashtag,a[rel~=\"tag\"],a.zrl")
|
||||
|> Floki.attribute("a", "href")
|
||||
|> length() > 0
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
@moduledoc "Filter activities depending on their age"
|
||||
@behaviour MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
defp check_date(%{"published" => published} = message) do
|
||||
with %DateTime{} = now <- DateTime.utc_now(),
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.MRF
|
||||
@moduledoc "Filter activities depending on their origin instance"
|
||||
@behaviour MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
require Pleroma.Constants
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
|
||||
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
|
|||
|
||||
require Logger
|
||||
|
||||
@behaviour MRF
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
defp lookup_subchain(actor) do
|
||||
with matches <- Config.get([:mrf_subchain, :match_actor]),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Publisher do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Relay do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||
|
@ -156,10 +156,11 @@ def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options)
|
|||
when not is_nil(in_reply_to) do
|
||||
in_reply_to_id = prepare_in_reply_to(in_reply_to)
|
||||
object = Map.put(object, "inReplyToAtomUri", in_reply_to_id)
|
||||
depth = (options[:depth] || 0) + 1
|
||||
|
||||
if Federator.allowed_incoming_reply_depth?(options[:depth]) do
|
||||
if Federator.allowed_thread_distance?(depth) do
|
||||
with {:ok, replied_object} <- get_obj_helper(in_reply_to_id, options),
|
||||
%Activity{} = _ <- Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
|
||||
%Activity{} <- Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
|
||||
object
|
||||
|> Map.put("inReplyTo", replied_object.data["id"])
|
||||
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
|
||||
|
@ -312,7 +313,7 @@ def fix_type(object, options \\ [])
|
|||
|
||||
def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options)
|
||||
when is_binary(reply_id) do
|
||||
with true <- Federator.allowed_incoming_reply_depth?(options[:depth]),
|
||||
with true <- Federator.allowed_thread_distance?(options[:depth]),
|
||||
{:ok, %{data: %{"type" => "Question"} = _} = _} <- get_obj_helper(reply_id, options) do
|
||||
Map.put(object, "type", "Answer")
|
||||
else
|
||||
|
@ -406,8 +407,7 @@ def handle_incoming(
|
|||
|
||||
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
||||
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
|
||||
options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
|
||||
object = fix_object(data["object"], options)
|
||||
object = fix_object(object, options)
|
||||
|
||||
params = %{
|
||||
to: data["to"],
|
||||
|
@ -424,7 +424,20 @@ def handle_incoming(
|
|||
])
|
||||
}
|
||||
|
||||
ActivityPub.create(params)
|
||||
with {:ok, created_activity} <- ActivityPub.create(params) do
|
||||
reply_depth = (options[:depth] || 0) + 1
|
||||
|
||||
if Federator.allowed_thread_distance?(reply_depth) do
|
||||
for reply_id <- replies(object) do
|
||||
Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{
|
||||
"id" => reply_id,
|
||||
"depth" => reply_depth
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
{:ok, created_activity}
|
||||
end
|
||||
else
|
||||
%Activity{} = activity -> {:ok, activity}
|
||||
_e -> :error
|
||||
|
@ -442,7 +455,8 @@ def handle_incoming(
|
|||
|> fix_addressing
|
||||
|
||||
with {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
|
||||
options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
|
||||
reply_depth = (options[:depth] || 0) + 1
|
||||
options = Keyword.put(options, :depth, reply_depth)
|
||||
object = fix_object(object, options)
|
||||
|
||||
params = %{
|
||||
|
@ -903,6 +917,50 @@ def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_r
|
|||
|
||||
def set_reply_to_uri(obj), do: obj
|
||||
|
||||
@doc """
|
||||
Serialized Mastodon-compatible `replies` collection containing _self-replies_.
|
||||
Based on Mastodon's ActivityPub::NoteSerializer#replies.
|
||||
"""
|
||||
def set_replies(obj_data) do
|
||||
replies_uris =
|
||||
with limit when limit > 0 <-
|
||||
Pleroma.Config.get([:activitypub, :note_replies_output_limit], 0),
|
||||
%Object{} = object <- Object.get_cached_by_ap_id(obj_data["id"]) do
|
||||
object
|
||||
|> Object.self_replies()
|
||||
|> select([o], fragment("?->>'id'", o.data))
|
||||
|> limit(^limit)
|
||||
|> Repo.all()
|
||||
else
|
||||
_ -> []
|
||||
end
|
||||
|
||||
set_replies(obj_data, replies_uris)
|
||||
end
|
||||
|
||||
defp set_replies(obj, []) do
|
||||
obj
|
||||
end
|
||||
|
||||
defp set_replies(obj, replies_uris) do
|
||||
replies_collection = %{
|
||||
"type" => "Collection",
|
||||
"items" => replies_uris
|
||||
}
|
||||
|
||||
Map.merge(obj, %{"replies" => replies_collection})
|
||||
end
|
||||
|
||||
def replies(%{"replies" => %{"first" => %{"items" => items}}}) when not is_nil(items) do
|
||||
items
|
||||
end
|
||||
|
||||
def replies(%{"replies" => %{"items" => items}}) when not is_nil(items) do
|
||||
items
|
||||
end
|
||||
|
||||
def replies(_), do: []
|
||||
|
||||
# Prepares the object of an outgoing create activity.
|
||||
def prepare_object(object) do
|
||||
object
|
||||
|
@ -914,6 +972,7 @@ def prepare_object(object) do
|
|||
|> prepare_attachments
|
||||
|> set_conversation
|
||||
|> set_reply_to_uri
|
||||
|> set_replies
|
||||
|> strip_internal_fields
|
||||
|> strip_internal_tags
|
||||
|> set_type
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ActivityPub.Utils do
|
||||
|
@ -45,8 +45,8 @@ def normalize_params(params) do
|
|||
Map.put(params, "actor", get_ap_id(params["actor"]))
|
||||
end
|
||||
|
||||
@spec determine_explicit_mentions(map()) :: map()
|
||||
def determine_explicit_mentions(%{"tag" => tag} = _) when is_list(tag) do
|
||||
@spec determine_explicit_mentions(map()) :: [any]
|
||||
def determine_explicit_mentions(%{"tag" => tag}) when is_list(tag) do
|
||||
Enum.flat_map(tag, fn
|
||||
%{"type" => "Mention", "href" => href} -> [href]
|
||||
_ -> []
|
||||
|
@ -427,7 +427,7 @@ defp fetch_likes(object) do
|
|||
@doc """
|
||||
Updates a follow activity's state (for locked accounts).
|
||||
"""
|
||||
@spec update_follow_state_for_all(Activity.t(), String.t()) :: {:ok, Activity} | {:error, any()}
|
||||
@spec update_follow_state_for_all(Activity.t(), String.t()) :: {:ok, Activity | nil}
|
||||
def update_follow_state_for_all(
|
||||
%Activity{data: %{"actor" => actor, "object" => object}} = activity,
|
||||
state
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||
|
@ -8,10 +8,12 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.ConfigDB
|
||||
alias Pleroma.ModerationLog
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.ReportNote
|
||||
alias Pleroma.Stats
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserInviteToken
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
@ -97,7 +99,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
|||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{scopes: ["read"], admin: true}
|
||||
when action in [:config_show, :list_log]
|
||||
when action in [:config_show, :list_log, :stats]
|
||||
)
|
||||
|
||||
plug(
|
||||
|
@ -242,13 +244,15 @@ def user_show(conn, %{"nickname" => nickname}) do
|
|||
end
|
||||
|
||||
def list_instance_statuses(conn, %{"instance" => instance} = params) do
|
||||
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_instance_activities(%{
|
||||
ActivityPub.fetch_statuses(nil, %{
|
||||
"instance" => instance,
|
||||
"limit" => page_size,
|
||||
"offset" => (page - 1) * page_size
|
||||
"offset" => (page - 1) * page_size,
|
||||
"exclude_reblogs" => !with_reblogs && "true"
|
||||
})
|
||||
|
||||
conn
|
||||
|
@ -257,6 +261,7 @@ def list_instance_statuses(conn, %{"instance" => instance} = params) do
|
|||
end
|
||||
|
||||
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
||||
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
|
||||
godmode = params["godmode"] == "true" || params["godmode"] == true
|
||||
|
||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
|
||||
|
@ -265,7 +270,8 @@ def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
|||
activities =
|
||||
ActivityPub.fetch_user_activities(user, nil, %{
|
||||
"limit" => page_size,
|
||||
"godmode" => godmode
|
||||
"godmode" => godmode,
|
||||
"exclude_reblogs" => !with_reblogs && "true"
|
||||
})
|
||||
|
||||
conn
|
||||
|
@ -570,8 +576,8 @@ def relay_unfollow(%{assigns: %{user: admin}} = conn, %{"relay_url" => target})
|
|||
@doc "Sends registration invite via email"
|
||||
def email_invite(%{assigns: %{user: user}} = conn, %{"email" => email} = params) do
|
||||
with true <-
|
||||
Pleroma.Config.get([:instance, :invites_enabled]) &&
|
||||
!Pleroma.Config.get([:instance, :registrations_open]),
|
||||
Config.get([:instance, :invites_enabled]) &&
|
||||
!Config.get([:instance, :registrations_open]),
|
||||
{:ok, invite_token} <- UserInviteToken.create_invite(),
|
||||
email <-
|
||||
Pleroma.Emails.UserEmail.user_invitation_email(
|
||||
|
@ -739,6 +745,24 @@ def report_notes_delete(%{assigns: %{user: user}} = conn, %{
|
|||
end
|
||||
end
|
||||
|
||||
def list_statuses(%{assigns: %{user: admin}} = conn, params) do
|
||||
godmode = params["godmode"] == "true" || params["godmode"] == true
|
||||
local_only = params["local_only"] == "true" || params["local_only"] == true
|
||||
{page, page_size} = page_params(params)
|
||||
|
||||
activities =
|
||||
ActivityPub.fetch_statuses(admin, %{
|
||||
"godmode" => godmode,
|
||||
"local_only" => local_only,
|
||||
"limit" => page_size,
|
||||
"offset" => (page - 1) * page_size
|
||||
})
|
||||
|
||||
conn
|
||||
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|
||||
|> render("index.json", %{activities: activities, as: :activity})
|
||||
end
|
||||
|
||||
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
|
||||
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
||||
{:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
|
||||
|
@ -808,7 +832,7 @@ def config_show(conn, _params) do
|
|||
configs = ConfigDB.get_all_as_keyword()
|
||||
|
||||
merged =
|
||||
Pleroma.Config.Holder.config()
|
||||
Config.Holder.config()
|
||||
|> ConfigDB.merge(configs)
|
||||
|> Enum.map(fn {group, values} ->
|
||||
Enum.map(values, fn {key, value} ->
|
||||
|
@ -838,7 +862,16 @@ def config_show(conn, _params) do
|
|||
end)
|
||||
|> List.flatten()
|
||||
|
||||
json(conn, %{configs: merged})
|
||||
response = %{configs: merged}
|
||||
|
||||
response =
|
||||
if Restarter.Pleroma.need_reboot?() do
|
||||
Map.put(response, :need_reboot, true)
|
||||
else
|
||||
response
|
||||
end
|
||||
|
||||
json(conn, response)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -863,20 +896,26 @@ def config_update(conn, %{"configs" => configs}) do
|
|||
Ecto.get_meta(config, :state) == :deleted
|
||||
end)
|
||||
|
||||
Pleroma.Config.TransferTask.load_and_update_env(deleted, false)
|
||||
Config.TransferTask.load_and_update_env(deleted, false)
|
||||
|
||||
need_reboot? =
|
||||
Enum.any?(updated, fn config ->
|
||||
group = ConfigDB.from_string(config.group)
|
||||
key = ConfigDB.from_string(config.key)
|
||||
value = ConfigDB.from_binary(config.value)
|
||||
Pleroma.Config.TransferTask.pleroma_need_restart?(group, key, value)
|
||||
end)
|
||||
Restarter.Pleroma.need_reboot?() ||
|
||||
Enum.any?(updated, fn config ->
|
||||
group = ConfigDB.from_string(config.group)
|
||||
key = ConfigDB.from_string(config.key)
|
||||
value = ConfigDB.from_binary(config.value)
|
||||
Config.TransferTask.pleroma_need_restart?(group, key, value)
|
||||
end)
|
||||
|
||||
response = %{configs: updated}
|
||||
|
||||
response =
|
||||
if need_reboot?, do: Map.put(response, :need_reboot, need_reboot?), else: response
|
||||
if need_reboot? do
|
||||
Restarter.Pleroma.need_reboot()
|
||||
Map.put(response, :need_reboot, need_reboot?)
|
||||
else
|
||||
response
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_view(ConfigView)
|
||||
|
@ -886,18 +925,14 @@ def config_update(conn, %{"configs" => configs}) do
|
|||
|
||||
def restart(conn, _params) do
|
||||
with :ok <- configurable_from_database(conn) do
|
||||
if Pleroma.Config.get(:env) == :test do
|
||||
Logger.warn("pleroma restarted")
|
||||
else
|
||||
send(Restarter.Pleroma, {:restart, 50})
|
||||
end
|
||||
Restarter.Pleroma.restart(Config.get(:env), 50)
|
||||
|
||||
json(conn, %{})
|
||||
end
|
||||
end
|
||||
|
||||
defp configurable_from_database(conn) do
|
||||
if Pleroma.Config.get(:configurable_from_database) do
|
||||
if Config.get(:configurable_from_database) do
|
||||
:ok
|
||||
else
|
||||
errors(
|
||||
|
@ -941,6 +976,13 @@ def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" =
|
|||
conn |> json("")
|
||||
end
|
||||
|
||||
def stats(conn, _) do
|
||||
count = Stats.get_status_visibility_count()
|
||||
|
||||
conn
|
||||
|> json(%{"status_visibility" => count})
|
||||
end
|
||||
|
||||
def errors(conn, {:error, :not_found}) do
|
||||
conn
|
||||
|> put_status(:not_found)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.Search do
|
||||
|
@ -18,7 +18,11 @@ defmacro not_empty_string(string) do
|
|||
|
||||
@spec user(map()) :: {:ok, [User.t()], pos_integer()}
|
||||
def user(params \\ %{}) do
|
||||
query = User.Query.build(params) |> order_by([u], u.nickname)
|
||||
query =
|
||||
params
|
||||
|> Map.drop([:page, :page_size])
|
||||
|> User.Query.build()
|
||||
|> order_by([u], u.nickname)
|
||||
|
||||
paginated_query =
|
||||
User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.ConfigView do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AdminAPI.StatusView do
|
||||
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
|
|||
alias Pleroma.User
|
||||
|
||||
def render("index.json", opts) do
|
||||
render_many(opts.activities, __MODULE__, "show.json", opts)
|
||||
safe_render_many(opts.activities, __MODULE__, "show.json", opts)
|
||||
end
|
||||
|
||||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.CommonAPI do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.CommonAPI.Utils do
|
||||
|
@ -228,9 +228,9 @@ def make_content_html(
|
|||
data,
|
||||
visibility
|
||||
) do
|
||||
no_attachment_links =
|
||||
attachment_links =
|
||||
data
|
||||
|> Map.get("no_attachment_links", Config.get([:instance, :no_attachment_links]))
|
||||
|> Map.get("attachment_links", Config.get([:instance, :attachment_links]))
|
||||
|> truthy_param?()
|
||||
|
||||
content_type = get_content_type(data["content_type"])
|
||||
|
@ -244,7 +244,7 @@ def make_content_html(
|
|||
|
||||
status
|
||||
|> format_input(content_type, options)
|
||||
|> maybe_add_attachments(attachments, no_attachment_links)
|
||||
|> maybe_add_attachments(attachments, attachment_links)
|
||||
|> maybe_add_nsfw_tag(data)
|
||||
end
|
||||
|
||||
|
@ -270,7 +270,7 @@ def make_context(_, %Participation{} = participation) do
|
|||
def make_context(%Activity{data: %{"context" => context}}, _), do: context
|
||||
def make_context(_, _), do: Utils.generate_context_id()
|
||||
|
||||
def maybe_add_attachments(parsed, _attachments, true = _no_links), do: parsed
|
||||
def maybe_add_attachments(parsed, _attachments, false = _no_links), do: parsed
|
||||
|
||||
def maybe_add_attachments({text, mentions, tags}, attachments, _no_links) do
|
||||
text = add_attachments(text, attachments)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ControllerHelper do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Endpoint do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Federator do
|
||||
|
@ -15,13 +15,19 @@ defmodule Pleroma.Web.Federator do
|
|||
|
||||
require Logger
|
||||
|
||||
@doc "Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161)"
|
||||
@doc """
|
||||
Returns `true` if the distance to target object does not exceed max configured value.
|
||||
Serves to prevent fetching of very long threads, especially useful on smaller instances.
|
||||
Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161).
|
||||
Applies to fetching of both ancestor (reply-to) and child (reply) objects.
|
||||
"""
|
||||
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||
def allowed_incoming_reply_depth?(depth) do
|
||||
max_replies_depth = Pleroma.Config.get([:instance, :federation_incoming_replies_max_depth])
|
||||
def allowed_thread_distance?(distance) do
|
||||
max_distance = Pleroma.Config.get([:instance, :federation_incoming_replies_max_depth])
|
||||
|
||||
if max_replies_depth do
|
||||
(depth || 1) <= max_replies_depth
|
||||
if max_distance && max_distance >= 0 do
|
||||
# Default depth is 0 (an object has zero distance from itself in its thread)
|
||||
(distance || 0) <= max_distance
|
||||
else
|
||||
true
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Feed.FeedView do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Feed.TagController do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Feed.UserController do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastoFEController do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||
|
@ -10,9 +10,20 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
|||
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Plugs.RateLimiter
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
||||
# TODO: Replace with a macro when there is a Phoenix release with
|
||||
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
|
||||
# in it
|
||||
|
||||
plug(RateLimiter, [name: :timeline, bucket_name: :direct_timeline] when action == :direct)
|
||||
plug(RateLimiter, [name: :timeline, bucket_name: :public_timeline] when action == :public)
|
||||
plug(RateLimiter, [name: :timeline, bucket_name: :home_timeline] when action == :home)
|
||||
plug(RateLimiter, [name: :timeline, bucket_name: :hashtag_timeline] when action == :hashtag)
|
||||
plug(RateLimiter, [name: :timeline, bucket_name: :list_timeline] when action == :list)
|
||||
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct])
|
||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue