Merge branch 'develop' into 'fix/prune-hashtags'
# Conflicts: # CHANGELOG.md
This commit is contained in:
commit
84f42b92f0
|
@ -28,6 +28,7 @@ erl_crash.dump
|
||||||
# variables.
|
# variables.
|
||||||
/config/*.secret.exs
|
/config/*.secret.exs
|
||||||
/config/generated_config.exs
|
/config/generated_config.exs
|
||||||
|
/config/runtime.exs
|
||||||
/config/*.env
|
/config/*.env
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,11 +37,20 @@ after_script:
|
||||||
|
|
||||||
build:
|
build:
|
||||||
stage: build
|
stage: build
|
||||||
|
only:
|
||||||
|
changes:
|
||||||
|
- "**/*.ex"
|
||||||
|
- "**/*.exs"
|
||||||
|
- "mix.lock"
|
||||||
script:
|
script:
|
||||||
- mix compile --force
|
- mix compile --force
|
||||||
|
|
||||||
spec-build:
|
spec-build:
|
||||||
stage: test
|
stage: test
|
||||||
|
only:
|
||||||
|
changes:
|
||||||
|
- "lib/pleroma/web/api_spec/**/*.ex"
|
||||||
|
- "lib/pleroma/web/api_spec.ex"
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- spec.json
|
- spec.json
|
||||||
|
@ -64,6 +73,11 @@ benchmark:
|
||||||
|
|
||||||
unit-testing:
|
unit-testing:
|
||||||
stage: test
|
stage: test
|
||||||
|
only:
|
||||||
|
changes:
|
||||||
|
- "**/*.ex"
|
||||||
|
- "**/*.exs"
|
||||||
|
- "mix.lock"
|
||||||
retry: 2
|
retry: 2
|
||||||
cache: &testing_cache_policy
|
cache: &testing_cache_policy
|
||||||
<<: *global_cache_policy
|
<<: *global_cache_policy
|
||||||
|
@ -97,6 +111,11 @@ unit-testing:
|
||||||
|
|
||||||
unit-testing-rum:
|
unit-testing-rum:
|
||||||
stage: test
|
stage: test
|
||||||
|
only:
|
||||||
|
changes:
|
||||||
|
- "**/*.ex"
|
||||||
|
- "**/*.exs"
|
||||||
|
- "mix.lock"
|
||||||
retry: 2
|
retry: 2
|
||||||
cache: *testing_cache_policy
|
cache: *testing_cache_policy
|
||||||
services:
|
services:
|
||||||
|
@ -115,12 +134,22 @@ unit-testing-rum:
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
stage: test
|
stage: test
|
||||||
|
only:
|
||||||
|
changes:
|
||||||
|
- "**/*.ex"
|
||||||
|
- "**/*.exs"
|
||||||
|
- "mix.lock"
|
||||||
cache: *testing_cache_policy
|
cache: *testing_cache_policy
|
||||||
script:
|
script:
|
||||||
- mix format --check-formatted
|
- mix format --check-formatted
|
||||||
|
|
||||||
analysis:
|
analysis:
|
||||||
stage: test
|
stage: test
|
||||||
|
only:
|
||||||
|
changes:
|
||||||
|
- "**/*.ex"
|
||||||
|
- "**/*.exs"
|
||||||
|
- "mix.lock"
|
||||||
cache: *testing_cache_policy
|
cache: *testing_cache_policy
|
||||||
script:
|
script:
|
||||||
- mix credo --strict --only=warnings,todo,fixme,consistency,readability
|
- mix credo --strict --only=warnings,todo,fixme,consistency,readability
|
||||||
|
|
|
@ -8,16 +8,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- **Breaking:** Configuration: `:chat, enabled` moved to `:shout, enabled` and `:instance, chat_limit` moved to `:shout, limit`
|
||||||
|
- Support for Erlang/OTP 24
|
||||||
- The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change.
|
- The `application` metadata returned with statuses is no longer hardcoded. Apps that want to display these details will now have valid data for new posts after this change.
|
||||||
- HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising.
|
- HTTPSecurityPlug now sends a response header to opt out of Google's FLoC (Federated Learning of Cohorts) targeted advertising.
|
||||||
|
- Email address is now returned if requesting user is the owner of the user account so it can be exposed in client and FE user settings UIs.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded.
|
- MRF (`FollowBotPolicy`): New MRF Policy which makes a designated local Bot account attempt to follow all users in public Notes received by your instance. Users who require approving follower requests or have #nobot in their profile are excluded.
|
||||||
- Return OAuth token `id` (primary key) in POST `/oauth/token`.
|
- Return OAuth token `id` (primary key) in POST `/oauth/token`.
|
||||||
|
- `AnalyzeMetadata` upload filter for extracting attachment dimensions and generating blurhashes.
|
||||||
|
- Attachment dimensions and blurhashes are federated when available.
|
||||||
|
- Pinned posts federation
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Don't crash so hard when email settings are invalid.
|
- Don't crash so hard when email settings are invalid.
|
||||||
|
- Checking activated Upload Filters for required commands.
|
||||||
- Mix task `pleroma.database prune_objects`
|
- Mix task `pleroma.database prune_objects`
|
||||||
|
|
||||||
## Unreleased (Patch)
|
## Unreleased (Patch)
|
||||||
|
@ -29,6 +36,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Applying ConcurrentLimiter settings via AdminAPI
|
- Applying ConcurrentLimiter settings via AdminAPI
|
||||||
- User login failures if their `notification_settings` were in a NULL state.
|
- User login failures if their `notification_settings` were in a NULL state.
|
||||||
- Mix task `pleroma.user delete_activities` query transaction timeout is now :infinity
|
- Mix task `pleroma.user delete_activities` query transaction timeout is now :infinity
|
||||||
|
- MRF (`SimplePolicy`): Embedded objects are now checked. If any embedded object would be rejected, its parent is rejected. This fixes Announces leaking posts from blocked domains.
|
||||||
- Fixed some Markdown issues, including trailing slash in links.
|
- Fixed some Markdown issues, including trailing slash in links.
|
||||||
|
|
||||||
## [2.3.0] - 2020-03-01
|
## [2.3.0] - 2020-03-01
|
||||||
|
|
|
@ -33,7 +33,7 @@ ARG DATA=/var/lib/pleroma
|
||||||
|
|
||||||
RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
|
RUN echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories &&\
|
||||||
apk update &&\
|
apk update &&\
|
||||||
apk add exiftool imagemagick libmagic ncurses postgresql-client &&\
|
apk add exiftool ffmpeg imagemagick libmagic ncurses postgresql-client &&\
|
||||||
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
||||||
mkdir -p ${DATA}/uploads &&\
|
mkdir -p ${DATA}/uploads &&\
|
||||||
mkdir -p ${DATA}/static &&\
|
mkdir -p ${DATA}/static &&\
|
||||||
|
|
|
@ -50,5 +50,5 @@ If you are not developing Pleroma, it is better to use the OTP release, which co
|
||||||
- Latest Git revision: <https://docs-develop.pleroma.social>
|
- Latest Git revision: <https://docs-develop.pleroma.social>
|
||||||
|
|
||||||
## Community Channels
|
## Community Channels
|
||||||
* IRC: **#pleroma** and **#pleroma-dev** on freenode, webchat is available at <https://irc.pleroma.social>
|
* IRC: **#pleroma** and **#pleroma-dev** on libera.chat, webchat is available at <https://irc.pleroma.social>
|
||||||
* Matrix: <https://matrix.to/#/#freenode_#pleroma:matrix.org> and <https://matrix.to/#/#freenode_#pleroma-dev:matrix.org>
|
* Matrix: [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) and [#pleroma-dev:libera.chat](https://matrix.to/#/#pleroma-dev:libera.chat)
|
||||||
|
|
|
@ -299,7 +299,7 @@ defp insert_activity(:attachment, visibility, group, users, _opts) do
|
||||||
"url" => [
|
"url" => [
|
||||||
%{
|
%{
|
||||||
"href" =>
|
"href" =>
|
||||||
"#{Pleroma.Web.base_url()}/media/b1b873552422a07bf53af01f3c231c841db4dfc42c35efde681abaf0f2a4eab7.jpg",
|
"#{Pleroma.Web.Endpoint.url()}/media/b1b873552422a07bf53af01f3c231c841db4dfc42c35efde681abaf0f2a4eab7.jpg",
|
||||||
"mediaType" => "image/jpeg",
|
"mediaType" => "image/jpeg",
|
||||||
"type" => "Link"
|
"type" => "Link"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use Mix.Config
|
import Config
|
||||||
|
|
||||||
# We don't run a server during test. If one is required,
|
# We don't run a server during test. If one is required,
|
||||||
# you can enable the server option below.
|
# you can enable the server option below.
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
#
|
#
|
||||||
# This configuration file is loaded before any dependency and
|
# This configuration file is loaded before any dependency and
|
||||||
# is restricted to this project.
|
# is restricted to this project.
|
||||||
use Mix.Config
|
import Config
|
||||||
|
|
||||||
# General application configuration
|
# General application configuration
|
||||||
config :pleroma, ecto_repos: [Pleroma.Repo]
|
config :pleroma, ecto_repos: [Pleroma.Repo]
|
||||||
|
@ -190,7 +190,6 @@
|
||||||
instance_thumbnail: "/instance/thumbnail.jpeg",
|
instance_thumbnail: "/instance/thumbnail.jpeg",
|
||||||
limit: 5_000,
|
limit: 5_000,
|
||||||
description_limit: 5_000,
|
description_limit: 5_000,
|
||||||
chat_limit: 5_000,
|
|
||||||
remote_limit: 100_000,
|
remote_limit: 100_000,
|
||||||
upload_limit: 16_000_000,
|
upload_limit: 16_000_000,
|
||||||
avatar_upload_limit: 2_000_000,
|
avatar_upload_limit: 2_000_000,
|
||||||
|
@ -457,7 +456,9 @@
|
||||||
image_quality: 85,
|
image_quality: 85,
|
||||||
min_content_length: 100 * 1024
|
min_content_length: 100 * 1024
|
||||||
|
|
||||||
config :pleroma, :chat, enabled: true
|
config :pleroma, :shout,
|
||||||
|
enabled: true,
|
||||||
|
limit: 5_000
|
||||||
|
|
||||||
config :phoenix, :format_encoders, json: Jason
|
config :phoenix, :format_encoders, json: Jason
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use Mix.Config
|
import Config
|
||||||
|
|
||||||
websocket_config = [
|
websocket_config = [
|
||||||
path: "/websocket",
|
path: "/websocket",
|
||||||
|
@ -544,14 +544,6 @@
|
||||||
5_000
|
5_000
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
%{
|
|
||||||
key: :chat_limit,
|
|
||||||
type: :integer,
|
|
||||||
description: "Character limit of the instance chat messages",
|
|
||||||
suggestions: [
|
|
||||||
5_000
|
|
||||||
]
|
|
||||||
},
|
|
||||||
%{
|
%{
|
||||||
key: :remote_limit,
|
key: :remote_limit,
|
||||||
type: :integer,
|
type: :integer,
|
||||||
|
@ -682,7 +674,8 @@
|
||||||
%{
|
%{
|
||||||
key: :allow_relay,
|
key: :allow_relay,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Enable Pleroma's Relay, which makes it possible to follow a whole instance"
|
description:
|
||||||
|
"Permits remote instances to subscribe to all public posts of your instance. (Important!) This may increase the visibility of your instance."
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :public,
|
key: :public,
|
||||||
|
@ -1182,7 +1175,6 @@
|
||||||
alwaysShowSubjectInput: true,
|
alwaysShowSubjectInput: true,
|
||||||
background: "/static/aurora_borealis.jpg",
|
background: "/static/aurora_borealis.jpg",
|
||||||
collapseMessageWithSubject: false,
|
collapseMessageWithSubject: false,
|
||||||
disableChat: false,
|
|
||||||
greentext: false,
|
greentext: false,
|
||||||
hideFilteredStatuses: false,
|
hideFilteredStatuses: false,
|
||||||
hideMutedPosts: false,
|
hideMutedPosts: false,
|
||||||
|
@ -1229,12 +1221,6 @@
|
||||||
description:
|
description:
|
||||||
"When a message has a subject (aka Content Warning), collapse it by default"
|
"When a message has a subject (aka Content Warning), collapse it by default"
|
||||||
},
|
},
|
||||||
%{
|
|
||||||
key: :disableChat,
|
|
||||||
label: "PleromaFE Chat",
|
|
||||||
type: :boolean,
|
|
||||||
description: "Disables PleromaFE Chat component"
|
|
||||||
},
|
|
||||||
%{
|
%{
|
||||||
key: :greentext,
|
key: :greentext,
|
||||||
label: "Greentext",
|
label: "Greentext",
|
||||||
|
@ -2652,13 +2638,22 @@
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: :chat,
|
key: :shout,
|
||||||
type: :group,
|
type: :group,
|
||||||
description: "Pleroma chat settings",
|
description: "Pleroma shout settings",
|
||||||
children: [
|
children: [
|
||||||
%{
|
%{
|
||||||
key: :enabled,
|
key: :enabled,
|
||||||
type: :boolean
|
type: :boolean,
|
||||||
|
description: "Enables the backend Shoutbox chat feature."
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :limit,
|
||||||
|
type: :integer,
|
||||||
|
description: "Shout message character limit.",
|
||||||
|
suggestions: [
|
||||||
|
5_000
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use Mix.Config
|
import Config
|
||||||
|
|
||||||
# For development, we disable any cache and enable
|
# For development, we disable any cache and enable
|
||||||
# debugging and code reloading.
|
# debugging and code reloading.
|
||||||
|
@ -54,6 +54,10 @@
|
||||||
|
|
||||||
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
|
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
|
||||||
|
|
||||||
|
# Reduce recompilation time
|
||||||
|
# https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
|
||||||
|
config :phoenix, :plug_init_mode, :runtime
|
||||||
|
|
||||||
if File.exists?("./config/dev.secret.exs") do
|
if File.exists?("./config/dev.secret.exs") do
|
||||||
import_config "dev.secret.exs"
|
import_config "dev.secret.exs"
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use Mix.Config
|
import Config
|
||||||
|
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
http: [
|
http: [
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use Mix.Config
|
import Config
|
||||||
|
|
||||||
# For production, we often load configuration from external
|
# For production, we often load configuration from external
|
||||||
# sources, such as your system environment. For this reason,
|
# sources, such as your system environment. For this reason,
|
||||||
|
@ -63,7 +63,12 @@
|
||||||
|
|
||||||
# Finally import the config/prod.secret.exs
|
# Finally import the config/prod.secret.exs
|
||||||
# which should be versioned separately.
|
# which should be versioned separately.
|
||||||
import_config "prod.secret.exs"
|
if File.exists?("./config/prod.secret.exs") do
|
||||||
|
import_config "prod.secret.exs"
|
||||||
|
else
|
||||||
|
"`config/prod.secret.exs` not found. You may want to create one by running `mix pleroma.instance gen`"
|
||||||
|
|> IO.warn([])
|
||||||
|
end
|
||||||
|
|
||||||
if File.exists?("./config/prod.exported_from_db.secret.exs"),
|
if File.exists?("./config/prod.exported_from_db.secret.exs"),
|
||||||
do: import_config("prod.exported_from_db.secret.exs")
|
do: import_config("prod.exported_from_db.secret.exs")
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use Mix.Config
|
import Config
|
||||||
|
|
||||||
# We don't run a server during test. If one is required,
|
# We don't run a server during test. If one is required,
|
||||||
# you can enable the server option below.
|
# you can enable the server option below.
|
||||||
|
@ -133,6 +133,10 @@
|
||||||
ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
|
ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
|
||||||
logger: Pleroma.LoggerMock
|
logger: Pleroma.LoggerMock
|
||||||
|
|
||||||
|
# Reduce recompilation time
|
||||||
|
# https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
|
||||||
|
config :phoenix, :plug_init_mode, :runtime
|
||||||
|
|
||||||
if File.exists?("./config/test.secret.exs") do
|
if File.exists?("./config/test.secret.exs") do
|
||||||
import_config "test.secret.exs"
|
import_config "test.secret.exs"
|
||||||
else
|
else
|
||||||
|
|
|
@ -8,9 +8,10 @@ For from source installations Pleroma configuration works by first importing the
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
## :chat
|
## :shout
|
||||||
|
|
||||||
* `enabled` - Enables the backend chat. Defaults to `true`.
|
* `enabled` - Enables the backend Shoutbox chat feature. Defaults to `true`.
|
||||||
|
* `limit` - Shout character limit. Defaults to `5_000`
|
||||||
|
|
||||||
## :instance
|
## :instance
|
||||||
* `name`: The instance’s name.
|
* `name`: The instance’s name.
|
||||||
|
@ -19,7 +20,6 @@ To add configuration to your config file, you can copy it from the base config.
|
||||||
* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``.
|
* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``.
|
||||||
* `limit`: Posts character limit (CW/Subject included in the counter).
|
* `limit`: Posts character limit (CW/Subject included in the counter).
|
||||||
* `description_limit`: The character limit for image descriptions.
|
* `description_limit`: The character limit for image descriptions.
|
||||||
* `chat_limit`: Character limit of the instance chat messages.
|
|
||||||
* `remote_limit`: Hard character limit beyond which remote posts will be dropped.
|
* `remote_limit`: Hard character limit beyond which remote posts will be dropped.
|
||||||
* `upload_limit`: File size limit of uploads (except for avatar, background, banner).
|
* `upload_limit`: File size limit of uploads (except for avatar, background, banner).
|
||||||
* `avatar_upload_limit`: File size limit of user’s profile avatars.
|
* `avatar_upload_limit`: File size limit of user’s profile avatars.
|
||||||
|
@ -37,7 +37,7 @@ To add configuration to your config file, you can copy it from the base config.
|
||||||
* `federating`: Enable federation with other instances.
|
* `federating`: Enable federation with other instances.
|
||||||
* `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
|
* `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.
|
||||||
* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
|
* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
|
||||||
* `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance.
|
* `allow_relay`: Permits remote instances to subscribe to all public posts of your instance. This may increase the visibility of your instance.
|
||||||
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
|
* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details.
|
||||||
* `quarantined_instances`: List of ActivityPub instances where private (DMs, followers-only) activities will not be send.
|
* `quarantined_instances`: List of ActivityPub instances where private (DMs, followers-only) activities will not be send.
|
||||||
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
|
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML).
|
||||||
|
|
|
@ -300,7 +300,7 @@ See [Admin-API](admin_api.md)
|
||||||
* Note: Behaves exactly the same as `POST /api/v1/upload`.
|
* Note: Behaves exactly the same as `POST /api/v1/upload`.
|
||||||
Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`.
|
Can only accept images - any attempt to upload non-image files will be met with `HTTP 415 Unsupported Media Type`.
|
||||||
|
|
||||||
## `/api/v1/pleroma/notification_settings`
|
## `/api/pleroma/notification_settings`
|
||||||
### Updates user notification settings
|
### Updates user notification settings
|
||||||
* Method `PUT`
|
* Method `PUT`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
|
|
@ -117,7 +117,7 @@ cd /opt/pleroma
|
||||||
sudo -Hu pleroma mix deps.get
|
sudo -Hu pleroma mix deps.get
|
||||||
```
|
```
|
||||||
|
|
||||||
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
|
||||||
* Answer with `yes` if it asks you to install `rebar3`.
|
* Answer with `yes` if it asks you to install `rebar3`.
|
||||||
* This may take some time, because parts of pleroma get compiled first.
|
* This may take some time, because parts of pleroma get compiled first.
|
||||||
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||||
|
@ -240,4 +240,4 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
||||||
|
|
||||||
## Questions
|
## 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**.
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||||
|
|
|
@ -92,7 +92,7 @@ cd /opt/pleroma
|
||||||
sudo -Hu pleroma mix deps.get
|
sudo -Hu pleroma mix deps.get
|
||||||
```
|
```
|
||||||
|
|
||||||
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
|
||||||
* Answer with `yes` if it asks you to install `rebar3`.
|
* Answer with `yes` if it asks you to install `rebar3`.
|
||||||
* This may take some time, because parts of pleroma get compiled first.
|
* This may take some time, because parts of pleroma get compiled first.
|
||||||
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||||
|
@ -215,4 +215,4 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
||||||
|
|
||||||
## Questions
|
## 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**.
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||||
|
|
|
@ -90,7 +90,7 @@ cd /opt/pleroma
|
||||||
sudo -Hu pleroma mix deps.get
|
sudo -Hu pleroma mix deps.get
|
||||||
```
|
```
|
||||||
|
|
||||||
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
* Generate the configuration: `sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen`
|
||||||
* Answer with `yes` if it asks you to install `rebar3`.
|
* Answer with `yes` if it asks you to install `rebar3`.
|
||||||
* This may take some time, because parts of pleroma get compiled first.
|
* This may take some time, because parts of pleroma get compiled first.
|
||||||
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||||
|
@ -202,4 +202,4 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
||||||
|
|
||||||
## Questions
|
## 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**.
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||||
|
|
|
@ -89,7 +89,7 @@ sudo -Hu pleroma mix deps.get
|
||||||
|
|
||||||
* コンフィギュレーションを生成します。
|
* コンフィギュレーションを生成します。
|
||||||
```
|
```
|
||||||
sudo -Hu pleroma mix pleroma.instance gen
|
sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen
|
||||||
```
|
```
|
||||||
* rebar3をインストールしてもよいか聞かれたら、yesを入力してください。
|
* rebar3をインストールしてもよいか聞かれたら、yesを入力してください。
|
||||||
* このときにpleromaの一部がコンパイルされるため、この処理には時間がかかります。
|
* このときにpleromaの一部がコンパイルされるため、この処理には時間がかかります。
|
||||||
|
@ -103,7 +103,7 @@ sudo -Hu pleroma mv config/{generated_config.exs,prod.secret.exs}
|
||||||
|
|
||||||
* 先程のコマンドで、すでに `config/setup_db.psql` というファイルが作られています。このファイルをもとに、データベースを作成します。
|
* 先程のコマンドで、すでに `config/setup_db.psql` というファイルが作られています。このファイルをもとに、データベースを作成します。
|
||||||
```
|
```
|
||||||
sudo -Hu pleroma mix pleroma.instance gen
|
sudo -Hu pleroma MIX_ENV=prod mix pleroma.instance gen
|
||||||
```
|
```
|
||||||
|
|
||||||
* そして、データベースのマイグレーションを実行します。
|
* そして、データベースのマイグレーションを実行します。
|
||||||
|
@ -191,5 +191,5 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
||||||
|
|
||||||
インストールについて質問がある、もしくは、うまくいかないときは、以下のところで質問できます。
|
インストールについて質問がある、もしくは、うまくいかないときは、以下のところで質問できます。
|
||||||
|
|
||||||
* [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org)
|
* [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat)
|
||||||
* **Freenode** の **#pleroma** IRCチャンネル
|
* **libera.chat** の **#pleroma** IRCチャンネル
|
||||||
|
|
|
@ -54,7 +54,7 @@ Configure Pleroma. Note that you need a domain name at this point:
|
||||||
```
|
```
|
||||||
$ cd /home/pleroma/pleroma
|
$ cd /home/pleroma/pleroma
|
||||||
$ mix deps.get # Enter "y" when asked to install Hex
|
$ mix deps.get # Enter "y" when asked to install Hex
|
||||||
$ mix pleroma.instance gen # You will be asked a few questions here.
|
$ MIX_ENV=prod mix pleroma.instance gen # You will be asked a few questions here.
|
||||||
$ cp config/generated_config.exs config/prod.secret.exs
|
$ cp config/generated_config.exs config/prod.secret.exs
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -213,4 +213,4 @@ incorrect timestamps. You should have ntpd running.
|
||||||
|
|
||||||
## Questions
|
## 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**.
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||||
|
|
|
@ -135,7 +135,7 @@ pleroma$ mix deps.get
|
||||||
* Generate the configuration:
|
* Generate the configuration:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pleroma$ mix pleroma.instance gen
|
pleroma$ MIX_ENV=prod mix pleroma.instance gen
|
||||||
```
|
```
|
||||||
|
|
||||||
* Answer with `yes` if it asks you to install `rebar3`.
|
* Answer with `yes` if it asks you to install `rebar3`.
|
||||||
|
@ -298,4 +298,4 @@ If you opted to allow sudo for the `pleroma` user but would like to remove the a
|
||||||
|
|
||||||
## Questions
|
## 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**.
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||||
|
|
|
@ -71,7 +71,7 @@ Configure Pleroma. Note that you need a domain name at this point:
|
||||||
```
|
```
|
||||||
$ cd /home/pleroma/pleroma
|
$ cd /home/pleroma/pleroma
|
||||||
$ mix deps.get
|
$ mix deps.get
|
||||||
$ mix pleroma.instance gen # You will be asked a few questions here.
|
$ MIX_ENV=prod mix pleroma.instance gen # You will be asked a few questions here.
|
||||||
```
|
```
|
||||||
|
|
||||||
Since Postgres is configured, we can now initialize the database. There should
|
Since Postgres is configured, we can now initialize the database. There should
|
||||||
|
@ -193,8 +193,6 @@ Run `# /etc/rc.d/pleroma start` to start Pleroma.
|
||||||
|
|
||||||
Restart nginx with `# /etc/rc.d/nginx restart` and you should be up and running.
|
Restart nginx with `# /etc/rc.d/nginx restart` and you should be up and running.
|
||||||
|
|
||||||
If you need further help, contact niaa on freenode.
|
|
||||||
|
|
||||||
Make sure your time is in sync, or other instances will receive your posts with
|
Make sure your time is in sync, or other instances will receive your posts with
|
||||||
incorrect timestamps. You should have ntpd running.
|
incorrect timestamps. You should have ntpd running.
|
||||||
|
|
||||||
|
@ -208,4 +206,4 @@ incorrect timestamps. You should have ntpd running.
|
||||||
|
|
||||||
## Questions
|
## 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**.
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||||
|
|
|
@ -239,7 +239,7 @@ Enter a shell as \_pleroma (as root `su _pleroma -`) and enter pleroma's install
|
||||||
Then follow the main installation guide:
|
Then follow the main installation guide:
|
||||||
|
|
||||||
* run `mix deps.get`
|
* run `mix deps.get`
|
||||||
* run `mix pleroma.instance gen` and enter your instance's information when asked
|
* run `MIX_ENV=prod mix pleroma.instance gen` and enter your instance's information when asked
|
||||||
* copy config/generated\_config.exs to config/prod.secret.exs. The default values should be sufficient but you should edit it and check that everything seems OK.
|
* copy config/generated\_config.exs to config/prod.secret.exs. The default values should be sufficient but you should edit it and check that everything seems OK.
|
||||||
* exit your current shell back to a root one and run `psql -U postgres -f /home/_pleroma/pleroma/config/setup_db.psql` to setup the database.
|
* exit your current shell back to a root one and run `psql -U postgres -f /home/_pleroma/pleroma/config/setup_db.psql` to setup the database.
|
||||||
* return to a \_pleroma shell into pleroma's installation directory (`su _pleroma -;cd ~/pleroma`) and run `MIX_ENV=prod mix ecto.migrate`
|
* return to a \_pleroma shell into pleroma's installation directory (`su _pleroma -;cd ~/pleroma`) and run `MIX_ENV=prod mix ecto.migrate`
|
||||||
|
@ -264,4 +264,4 @@ LC_ALL=en_US.UTF-8 MIX_ENV=prod mix pleroma.user new <username> <your@emailaddre
|
||||||
|
|
||||||
## Questions
|
## 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**.
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
|
||||||
|
|
|
@ -10,8 +10,8 @@ suositeltavaa tehdä komennon `doas` avulla, katso `doas (1)` ja `doas.conf (5)`
|
||||||
Tästä eteenpäin oletuksena on, että domain "esimerkki.com" osoittaa
|
Tästä eteenpäin oletuksena on, että domain "esimerkki.com" osoittaa
|
||||||
serverin IP-osoitteeseen.
|
serverin IP-osoitteeseen.
|
||||||
|
|
||||||
Jos asennuksen kanssa on ongelmia, IRC-kanava #pleroma Freenodessa tai
|
Jos asennuksen kanssa on ongelmia, IRC-kanava #pleroma Libera.chat tai
|
||||||
Matrix-kanava #freenode_#pleroma:matrix.org ovat hyviä paikkoja löytää apua
|
Matrix-kanava #pleroma:libera.chat ovat hyviä paikkoja löytää apua
|
||||||
(englanniksi), `/msg eal kukkuu` jos haluat välttämättä puhua härmää.
|
(englanniksi), `/msg eal kukkuu` jos haluat välttämättä puhua härmää.
|
||||||
|
|
||||||
Asenna tarvittava ohjelmisto:
|
Asenna tarvittava ohjelmisto:
|
||||||
|
|
|
@ -232,7 +232,7 @@ At this point if you open your (sub)domain in a browser you should see a 502 err
|
||||||
|
|
||||||
If everything worked, you should see Pleroma-FE when visiting your domain. If that didn't happen, try reviewing the installation steps, starting Pleroma in the foreground and seeing if there are any errrors.
|
If everything worked, you should see Pleroma-FE when visiting your domain. If that didn't happen, try reviewing the installation steps, starting Pleroma in the foreground and seeing if there are any errrors.
|
||||||
|
|
||||||
Still doesn't work? Feel free to contact us on [#pleroma on freenode](https://irc.pleroma.social) or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org>, you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma-support/issues/new)
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC, you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma-support/issues/new).
|
||||||
|
|
||||||
## Post installation
|
## Post installation
|
||||||
|
|
||||||
|
@ -301,4 +301,4 @@ This will create an account withe the username of 'joeuser' with the email addre
|
||||||
|
|
||||||
## Questions
|
## 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**.
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC, you can also [file an issue on our Gitlab](https://git.pleroma.social/pleroma/pleroma-support/issues/new).
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Activity.HTML do
|
||||||
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.Object
|
||||||
|
|
||||||
|
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||||
|
|
||||||
|
def get_cached_scrubbed_html_for_activity(
|
||||||
|
content,
|
||||||
|
scrubbers,
|
||||||
|
activity,
|
||||||
|
key \\ "",
|
||||||
|
callback \\ fn x -> x end
|
||||||
|
) do
|
||||||
|
key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
|
||||||
|
|
||||||
|
@cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||||
|
object = Object.normalize(activity, fetch: false)
|
||||||
|
HTML.ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_cached_stripped_html_for_activity(content, activity, key) do
|
||||||
|
get_cached_scrubbed_html_for_activity(
|
||||||
|
content,
|
||||||
|
FastSanitize.Sanitizer.StripTags,
|
||||||
|
activity,
|
||||||
|
key,
|
||||||
|
&HtmlEntities.decode/1
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
|
||||||
|
generate_scrubber_signature([scrubber])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp generate_scrubber_signature(scrubbers) do
|
||||||
|
Enum.reduce(scrubbers, "", fn scrubber, signature ->
|
||||||
|
"#{signature}#{to_string(scrubber)}"
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
|
@ -25,7 +25,7 @@ def user_agent do
|
||||||
if Process.whereis(Pleroma.Web.Endpoint) do
|
if Process.whereis(Pleroma.Web.Endpoint) do
|
||||||
case Config.get([:http, :user_agent], :default) do
|
case Config.get([:http, :user_agent], :default) do
|
||||||
:default ->
|
:default ->
|
||||||
info = "#{Pleroma.Web.base_url()} <#{Config.get([:instance, :email], "")}>"
|
info = "#{Pleroma.Web.Endpoint.url()} <#{Config.get([:instance, :email], "")}>"
|
||||||
named_version() <> "; " <> info
|
named_version() <> "; " <> info
|
||||||
|
|
||||||
custom ->
|
custom ->
|
||||||
|
@ -102,7 +102,7 @@ def start(_type, _args) do
|
||||||
] ++
|
] ++
|
||||||
task_children(@mix_env) ++
|
task_children(@mix_env) ++
|
||||||
dont_run_in_test(@mix_env) ++
|
dont_run_in_test(@mix_env) ++
|
||||||
chat_child(chat_enabled?()) ++
|
shout_child(shout_enabled?()) ++
|
||||||
[Pleroma.Gopher.Server]
|
[Pleroma.Gopher.Server]
|
||||||
|
|
||||||
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
# See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
|
||||||
|
@ -216,7 +216,7 @@ def build_cachex(type, opts),
|
||||||
type: :worker
|
type: :worker
|
||||||
}
|
}
|
||||||
|
|
||||||
defp chat_enabled?, do: Config.get([:chat, :enabled])
|
defp shout_enabled?, do: Config.get([:shout, :enabled])
|
||||||
|
|
||||||
defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
|
defp dont_run_in_test(env) when env in [:test, :benchmark], do: []
|
||||||
|
|
||||||
|
@ -237,14 +237,14 @@ defp background_migrators do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp chat_child(true) do
|
defp shout_child(true) do
|
||||||
[
|
[
|
||||||
Pleroma.Web.ChatChannel.ChatChannelState,
|
Pleroma.Web.ShoutChannel.ShoutChannelState,
|
||||||
{Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
|
{Phoenix.PubSub, [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp chat_child(_), do: []
|
defp shout_child(_), do: []
|
||||||
|
|
||||||
defp task_children(:test) do
|
defp task_children(:test) do
|
||||||
[
|
[
|
||||||
|
|
|
@ -164,9 +164,11 @@ defp do_check_rum!(setting, migrate) do
|
||||||
|
|
||||||
defp check_system_commands!(:ok) do
|
defp check_system_commands!(:ok) do
|
||||||
filter_commands_statuses = [
|
filter_commands_statuses = [
|
||||||
check_filter(Pleroma.Upload.Filters.Exiftool, "exiftool"),
|
check_filter(Pleroma.Upload.Filter.Exiftool, "exiftool"),
|
||||||
check_filter(Pleroma.Upload.Filters.Mogrify, "mogrify"),
|
check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"),
|
||||||
check_filter(Pleroma.Upload.Filters.Mogrifun, "mogrify")
|
check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"),
|
||||||
|
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"),
|
||||||
|
check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "convert")
|
||||||
]
|
]
|
||||||
|
|
||||||
preview_proxy_commands_status =
|
preview_proxy_commands_status =
|
||||||
|
|
|
@ -41,7 +41,8 @@ def warn do
|
||||||
:ok <- check_gun_pool_options(),
|
:ok <- check_gun_pool_options(),
|
||||||
:ok <- check_activity_expiration_config(),
|
:ok <- check_activity_expiration_config(),
|
||||||
:ok <- check_remote_ip_plug_name(),
|
:ok <- check_remote_ip_plug_name(),
|
||||||
:ok <- check_uploders_s3_public_endpoint() do
|
:ok <- check_uploders_s3_public_endpoint(),
|
||||||
|
:ok <- check_old_chat_shoutbox() do
|
||||||
:ok
|
:ok
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -215,4 +216,27 @@ def check_uploders_s3_public_endpoint do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec check_old_chat_shoutbox() :: :ok | nil
|
||||||
|
def check_old_chat_shoutbox do
|
||||||
|
instance_config = Pleroma.Config.get([:instance])
|
||||||
|
chat_config = Pleroma.Config.get([:chat]) || []
|
||||||
|
|
||||||
|
use_old_config =
|
||||||
|
Keyword.has_key?(instance_config, :chat_limit) or
|
||||||
|
Keyword.has_key?(chat_config, :enabled)
|
||||||
|
|
||||||
|
if use_old_config do
|
||||||
|
Logger.error("""
|
||||||
|
!!!DEPRECATION WARNING!!!
|
||||||
|
Your config is using the old namespace for the Shoutbox configuration. You need to convert to the new namespace. e.g.,
|
||||||
|
\n* `config :pleroma, :chat, enabled` and `config :pleroma, :instance, chat_limit` are now equal to:
|
||||||
|
\n* `config :pleroma, :shout, enabled` and `config :pleroma, :shout, limit`
|
||||||
|
""")
|
||||||
|
|
||||||
|
:error
|
||||||
|
else
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Config.Loader do
|
defmodule Pleroma.Config.Loader do
|
||||||
@reject_keys [
|
defp reject_keys,
|
||||||
|
do: [
|
||||||
Pleroma.Repo,
|
Pleroma.Repo,
|
||||||
Pleroma.Web.Endpoint,
|
Pleroma.Web.Endpoint,
|
||||||
:env,
|
:env,
|
||||||
|
@ -12,7 +13,8 @@ defmodule Pleroma.Config.Loader do
|
||||||
:swarm
|
:swarm
|
||||||
]
|
]
|
||||||
|
|
||||||
@reject_groups [
|
defp reject_groups,
|
||||||
|
do: [
|
||||||
:postgrex,
|
:postgrex,
|
||||||
:tesla
|
:tesla
|
||||||
]
|
]
|
||||||
|
@ -52,7 +54,7 @@ defp filter(configs) do
|
||||||
@spec filter_group(atom(), keyword()) :: keyword()
|
@spec filter_group(atom(), keyword()) :: keyword()
|
||||||
def filter_group(group, configs) do
|
def filter_group(group, configs) do
|
||||||
Enum.reject(configs[group], fn {key, _v} ->
|
Enum.reject(configs[group], fn {key, _v} ->
|
||||||
key in @reject_keys or group in @reject_groups or
|
key in reject_keys() or group in reject_groups() or
|
||||||
(group == :phoenix and key == :serve_endpoints)
|
(group == :phoenix and key == :serve_endpoints)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,9 +13,10 @@ defmodule Pleroma.Config.TransferTask do
|
||||||
|
|
||||||
@type env() :: :test | :benchmark | :dev | :prod
|
@type env() :: :test | :benchmark | :dev | :prod
|
||||||
|
|
||||||
@reboot_time_keys [
|
defp reboot_time_keys,
|
||||||
|
do: [
|
||||||
{:pleroma, :hackney_pools},
|
{:pleroma, :hackney_pools},
|
||||||
{:pleroma, :chat},
|
{:pleroma, :shout},
|
||||||
{:pleroma, Oban},
|
{:pleroma, Oban},
|
||||||
{:pleroma, :rate_limit},
|
{:pleroma, :rate_limit},
|
||||||
{:pleroma, :markup},
|
{:pleroma, :markup},
|
||||||
|
@ -24,7 +25,8 @@ defmodule Pleroma.Config.TransferTask do
|
||||||
{:pleroma, :connections_pool}
|
{:pleroma, :connections_pool}
|
||||||
]
|
]
|
||||||
|
|
||||||
@reboot_time_subkeys [
|
defp reboot_time_subkeys,
|
||||||
|
do: [
|
||||||
{:pleroma, Pleroma.Captcha, [:seconds_valid]},
|
{:pleroma, Pleroma.Captcha, [:seconds_valid]},
|
||||||
{:pleroma, Pleroma.Upload, [:proxy_remote]},
|
{:pleroma, Pleroma.Upload, [:proxy_remote]},
|
||||||
{:pleroma, :instance, [:upload_limit]},
|
{:pleroma, :instance, [:upload_limit]},
|
||||||
|
@ -165,12 +167,12 @@ def pleroma_need_restart?(group, key, value) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp group_and_key_need_reboot?(group, key) do
|
defp group_and_key_need_reboot?(group, key) do
|
||||||
Enum.any?(@reboot_time_keys, fn {g, k} -> g == group and k == key end)
|
Enum.any?(reboot_time_keys(), fn {g, k} -> g == group and k == key end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp group_and_subkey_need_reboot?(group, key, value) do
|
defp group_and_subkey_need_reboot?(group, key, value) do
|
||||||
Keyword.keyword?(value) and
|
Keyword.keyword?(value) and
|
||||||
Enum.any?(@reboot_time_subkeys, fn {g, k, subkeys} ->
|
Enum.any?(reboot_time_subkeys(), fn {g, k, subkeys} ->
|
||||||
g == group and k == key and
|
g == group and k == key and
|
||||||
Enum.any?(Keyword.keys(value), &(&1 in subkeys))
|
Enum.any?(Keyword.keys(value), &(&1 in subkeys))
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -27,6 +27,4 @@ defmodule Pleroma.Constants do
|
||||||
do:
|
do:
|
||||||
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css)
|
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css)
|
||||||
)
|
)
|
||||||
|
|
||||||
def as_local_public, do: Pleroma.Web.base_url() <> "/#Public"
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,21 +13,33 @@ def cast(object) when is_binary(object) do
|
||||||
cast([object])
|
cast([object])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cast(object) when is_map(object) do
|
||||||
|
case ObjectID.cast(object) do
|
||||||
|
{:ok, data} -> {:ok, [data]}
|
||||||
|
_ -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def cast(data) when is_list(data) do
|
def cast(data) when is_list(data) do
|
||||||
|
data =
|
||||||
data
|
data
|
||||||
|> Enum.reduce_while({:ok, []}, fn element, {:ok, list} ->
|
|> Enum.reduce_while([], fn element, list ->
|
||||||
case ObjectID.cast(element) do
|
case ObjectID.cast(element) do
|
||||||
{:ok, id} ->
|
{:ok, id} ->
|
||||||
{:cont, {:ok, [id | list]}}
|
{:cont, [id | list]}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{:halt, :error}
|
{:cont, list}
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|> Enum.sort()
|
||||||
|
|> Enum.uniq()
|
||||||
|
|
||||||
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast(_) do
|
def cast(data) do
|
||||||
:error
|
{:error, data}
|
||||||
end
|
end
|
||||||
|
|
||||||
def dump(data) do
|
def dump(data) do
|
||||||
|
|
|
@ -73,7 +73,7 @@ def report(to, reporter, account, statuses, comment) do
|
||||||
#{comment_html}
|
#{comment_html}
|
||||||
#{statuses_html}
|
#{statuses_html}
|
||||||
<p>
|
<p>
|
||||||
<a href="#{Pleroma.Web.base_url()}/pleroma/admin/#/reports/index">View Reports in AdminFE</a>
|
<a href="#{Pleroma.Web.Endpoint.url()}/pleroma/admin/#/reports/index">View Reports in AdminFE</a>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
new()
|
new()
|
||||||
|
@ -87,7 +87,7 @@ def new_unapproved_registration(to, account) do
|
||||||
html_body = """
|
html_body = """
|
||||||
<p>New account for review: <a href="#{account.ap_id}">@#{account.nickname}</a></p>
|
<p>New account for review: <a href="#{account.ap_id}">@#{account.nickname}</a></p>
|
||||||
<blockquote>#{HTML.strip_tags(account.registration_reason)}</blockquote>
|
<blockquote>#{HTML.strip_tags(account.registration_reason)}</blockquote>
|
||||||
<a href="#{Pleroma.Web.base_url()}/pleroma/admin/#/users/#{account.id}/">Visit AdminFE</a>
|
<a href="#{Pleroma.Web.Endpoint.url()}/pleroma/admin/#/users/#{account.id}/">Visit AdminFE</a>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
new()
|
new()
|
||||||
|
|
|
@ -5,15 +5,22 @@
|
||||||
defmodule Pleroma.Emails.UserEmail do
|
defmodule Pleroma.Emails.UserEmail do
|
||||||
@moduledoc "User emails"
|
@moduledoc "User emails"
|
||||||
|
|
||||||
use Phoenix.Swoosh, view: Pleroma.Web.EmailView, layout: {Pleroma.Web.LayoutView, :email}
|
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
|
import Swoosh.Email
|
||||||
|
import Phoenix.Swoosh, except: [render_body: 3]
|
||||||
import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
|
import Pleroma.Config.Helpers, only: [instance_name: 0, sender: 0]
|
||||||
|
|
||||||
|
def render_body(email, template, assigns \\ %{}) do
|
||||||
|
email
|
||||||
|
|> put_new_layout({Pleroma.Web.LayoutView, :email})
|
||||||
|
|> put_new_view(Pleroma.Web.EmailView)
|
||||||
|
|> Phoenix.Swoosh.render_body(template, assigns)
|
||||||
|
end
|
||||||
|
|
||||||
defp recipient(email, nil), do: email
|
defp recipient(email, nil), do: email
|
||||||
defp recipient(email, name), do: {name, email}
|
defp recipient(email, name), do: {name, email}
|
||||||
defp recipient(%User{} = user), do: recipient(user.email, user.name)
|
defp recipient(%User{} = user), do: recipient(user.email, user.name)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
defmodule Pleroma.Emoji.Formatter do
|
defmodule Pleroma.Emoji.Formatter do
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
def emojify(text) do
|
def emojify(text) do
|
||||||
|
@ -44,7 +44,7 @@ def get_emoji_map(text) when is_binary(text) do
|
||||||
Emoji.get_all()
|
Emoji.get_all()
|
||||||
|> Enum.filter(fn {emoji, %Emoji{}} -> String.contains?(text, ":#{emoji}:") end)
|
|> Enum.filter(fn {emoji, %Emoji{}} -> String.contains?(text, ":#{emoji}:") end)
|
||||||
|> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc ->
|
|> Enum.reduce(%{}, fn {name, %Emoji{file: file}}, acc ->
|
||||||
Map.put(acc, name, to_string(URI.merge(Web.base_url(), file)))
|
Map.put(acc, name, to_string(URI.merge(Endpoint.url(), file)))
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ def mention_handler("@" <> nickname, buffer, opts, acc) do
|
||||||
|
|
||||||
def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
|
def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
|
||||||
tag = String.downcase(tag)
|
tag = String.downcase(tag)
|
||||||
url = "#{Pleroma.Web.base_url()}/tag/#{tag}"
|
url = "#{Pleroma.Web.Endpoint.url()}/tag/#{tag}"
|
||||||
|
|
||||||
link =
|
link =
|
||||||
Phoenix.HTML.Tag.content_tag(:a, tag_text,
|
Phoenix.HTML.Tag.content_tag(:a, tag_text,
|
||||||
|
|
|
@ -11,9 +11,7 @@ defmodule Pleroma.Gun do
|
||||||
@callback await(pid(), reference()) :: {:response, :fin, 200, []}
|
@callback await(pid(), reference()) :: {:response, :fin, 200, []}
|
||||||
@callback set_owner(pid(), pid()) :: :ok
|
@callback set_owner(pid(), pid()) :: :ok
|
||||||
|
|
||||||
@api Pleroma.Config.get([Pleroma.Gun], Pleroma.Gun.API)
|
defp api, do: Pleroma.Config.get([Pleroma.Gun], Pleroma.Gun.API)
|
||||||
|
|
||||||
defp api, do: @api
|
|
||||||
|
|
||||||
def open(host, port, opts), do: api().open(host, port, opts)
|
def open(host, port, opts), do: api().open(host, port, opts)
|
||||||
|
|
||||||
|
|
|
@ -5,11 +5,11 @@
|
||||||
defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
|
defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
|
||||||
use GenServer, restart: :temporary
|
use GenServer, restart: :temporary
|
||||||
|
|
||||||
@registry Pleroma.Gun.ConnectionPool
|
defp registry, do: Pleroma.Gun.ConnectionPool
|
||||||
|
|
||||||
def start_monitor do
|
def start_monitor do
|
||||||
pid =
|
pid =
|
||||||
case :gen_server.start(__MODULE__, [], name: {:via, Registry, {@registry, "reclaimer"}}) do
|
case :gen_server.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do
|
||||||
{:ok, pid} ->
|
{:ok, pid} ->
|
||||||
pid
|
pid
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ def handle_continue(:reclaim, _) do
|
||||||
# {worker_pid, crf, last_reference} end)
|
# {worker_pid, crf, last_reference} end)
|
||||||
unused_conns =
|
unused_conns =
|
||||||
Registry.select(
|
Registry.select(
|
||||||
@registry,
|
registry(),
|
||||||
[
|
[
|
||||||
{{:_, :"$1", {:_, :"$2", :"$3", :"$4"}}, [{:==, :"$2", []}], [{{:"$1", :"$3", :"$4"}}]}
|
{{:_, :"$1", {:_, :"$2", :"$3", :"$4"}}, [{:==, :"$2", []}], [{{:"$1", :"$3", :"$4"}}]}
|
||||||
]
|
]
|
||||||
|
|
|
@ -6,10 +6,10 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
|
||||||
alias Pleroma.Gun
|
alias Pleroma.Gun
|
||||||
use GenServer, restart: :temporary
|
use GenServer, restart: :temporary
|
||||||
|
|
||||||
@registry Pleroma.Gun.ConnectionPool
|
defp registry, do: Pleroma.Gun.ConnectionPool
|
||||||
|
|
||||||
def start_link([key | _] = opts) do
|
def start_link([key | _] = opts) do
|
||||||
GenServer.start_link(__MODULE__, opts, name: {:via, Registry, {@registry, key}})
|
GenServer.start_link(__MODULE__, opts, name: {:via, Registry, {registry(), key}})
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
@ -24,7 +24,7 @@ def handle_continue({:connect, [key, uri, opts, client_pid]}, _) do
|
||||||
time = :erlang.monotonic_time(:millisecond)
|
time = :erlang.monotonic_time(:millisecond)
|
||||||
|
|
||||||
{_, _} =
|
{_, _} =
|
||||||
Registry.update_value(@registry, key, fn _ ->
|
Registry.update_value(registry(), key, fn _ ->
|
||||||
{conn_pid, [client_pid], 1, time}
|
{conn_pid, [client_pid], 1, time}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ def handle_call(:add_client, {client_pid, _}, %{key: key, protocol: protocol} =
|
||||||
time = :erlang.monotonic_time(:millisecond)
|
time = :erlang.monotonic_time(:millisecond)
|
||||||
|
|
||||||
{{conn_pid, used_by, _, _}, _} =
|
{{conn_pid, used_by, _, _}, _} =
|
||||||
Registry.update_value(@registry, key, fn {conn_pid, used_by, crf, last_reference} ->
|
Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} ->
|
||||||
{conn_pid, [client_pid | used_by], crf(time - last_reference, crf), time}
|
{conn_pid, [client_pid | used_by], crf(time - last_reference, crf), time}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ def handle_call(:add_client, {client_pid, _}, %{key: key, protocol: protocol} =
|
||||||
@impl true
|
@impl true
|
||||||
def handle_call(:remove_client, {client_pid, _}, %{key: key} = state) do
|
def handle_call(:remove_client, {client_pid, _}, %{key: key} = state) do
|
||||||
{{_conn_pid, used_by, _crf, _last_reference}, _} =
|
{{_conn_pid, used_by, _crf, _last_reference}, _} =
|
||||||
Registry.update_value(@registry, key, fn {conn_pid, used_by, crf, last_reference} ->
|
Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} ->
|
||||||
{conn_pid, List.delete(used_by, client_pid), crf, last_reference}
|
{conn_pid, List.delete(used_by, client_pid), crf, last_reference}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -49,31 +49,6 @@ def filter_tags(html, scrubber) do
|
||||||
def filter_tags(html), do: filter_tags(html, nil)
|
def filter_tags(html), do: filter_tags(html, nil)
|
||||||
def strip_tags(html), do: filter_tags(html, FastSanitize.Sanitizer.StripTags)
|
def strip_tags(html), do: filter_tags(html, FastSanitize.Sanitizer.StripTags)
|
||||||
|
|
||||||
def get_cached_scrubbed_html_for_activity(
|
|
||||||
content,
|
|
||||||
scrubbers,
|
|
||||||
activity,
|
|
||||||
key \\ "",
|
|
||||||
callback \\ fn x -> x end
|
|
||||||
) do
|
|
||||||
key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
|
|
||||||
|
|
||||||
@cachex.fetch!(:scrubber_cache, key, fn _key ->
|
|
||||||
object = Pleroma.Object.normalize(activity, fetch: false)
|
|
||||||
ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false, callback)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_cached_stripped_html_for_activity(content, activity, key) do
|
|
||||||
get_cached_scrubbed_html_for_activity(
|
|
||||||
content,
|
|
||||||
FastSanitize.Sanitizer.StripTags,
|
|
||||||
activity,
|
|
||||||
key,
|
|
||||||
&HtmlEntities.decode/1
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def ensure_scrubbed_html(
|
def ensure_scrubbed_html(
|
||||||
content,
|
content,
|
||||||
scrubbers,
|
scrubbers,
|
||||||
|
@ -92,16 +67,6 @@ def ensure_scrubbed_html(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
|
|
||||||
generate_scrubber_signature([scrubber])
|
|
||||||
end
|
|
||||||
|
|
||||||
defp generate_scrubber_signature(scrubbers) do
|
|
||||||
Enum.reduce(scrubbers, "", fn scrubber, signature ->
|
|
||||||
"#{signature}#{to_string(scrubber)}"
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
def extract_first_external_url_from_object(%{data: %{"content" => content}} = object)
|
def extract_first_external_url_from_object(%{data: %{"content" => content}} = object)
|
||||||
when is_binary(content) do
|
when is_binary(content) do
|
||||||
unless object.data["fake"] do
|
unless object.data["fake"] do
|
||||||
|
|
|
@ -54,8 +54,8 @@ def pool_timeout(pool) do
|
||||||
Config.get([:pools, pool, :recv_timeout], default)
|
Config.get([:pools, pool, :recv_timeout], default)
|
||||||
end
|
end
|
||||||
|
|
||||||
@prefix Pleroma.Gun.ConnectionPool
|
|
||||||
def limiter_setup do
|
def limiter_setup do
|
||||||
|
prefix = Pleroma.Gun.ConnectionPool
|
||||||
wait = Config.get([:connections_pool, :connection_acquisition_wait])
|
wait = Config.get([:connections_pool, :connection_acquisition_wait])
|
||||||
retries = Config.get([:connections_pool, :connection_acquisition_retries])
|
retries = Config.get([:connections_pool, :connection_acquisition_retries])
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ def limiter_setup do
|
||||||
max_waiting = Keyword.get(opts, :max_waiting, 10)
|
max_waiting = Keyword.get(opts, :max_waiting, 10)
|
||||||
|
|
||||||
result =
|
result =
|
||||||
ConcurrentLimiter.new(:"#{@prefix}.#{name}", max_running, max_waiting,
|
ConcurrentLimiter.new(:"#{prefix}.#{name}", max_running, max_waiting,
|
||||||
wait: wait,
|
wait: wait,
|
||||||
max_retries: retries
|
max_retries: retries
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
defmodule Pleroma.HTTP.WebPush do
|
defmodule Pleroma.HTTP.WebPush do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
def post(url, payload, headers) do
|
def post(url, payload, headers, options \\ []) do
|
||||||
list_headers = Map.to_list(headers)
|
list_headers = Map.to_list(headers)
|
||||||
Pleroma.HTTP.post(url, payload, list_headers)
|
Pleroma.HTTP.post(url, payload, list_headers, options)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,4 +12,10 @@ def put_if_present(map, key, value, value_function \\ &{:ok, &1}) when is_map(ma
|
||||||
_ -> map
|
_ -> map
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def safe_put_in(data, keys, value) when is_map(data) and is_list(keys) do
|
||||||
|
Kernel.put_in(data, keys, value)
|
||||||
|
rescue
|
||||||
|
_ -> data
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -366,7 +366,7 @@ def update_data(%Object{data: data} = object, attrs \\ %{}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def local?(%Object{data: %{"id" => id}}) do
|
def local?(%Object{data: %{"id" => id}}) do
|
||||||
String.starts_with?(id, Pleroma.Web.base_url() <> "/")
|
String.starts_with?(id, Pleroma.Web.Endpoint.url() <> "/")
|
||||||
end
|
end
|
||||||
|
|
||||||
def replies(object, opts \\ []) do
|
def replies(object, opts \\ []) do
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Object.Fetcher do
|
defmodule Pleroma.Object.Fetcher do
|
||||||
alias Pleroma.HTTP
|
alias Pleroma.HTTP
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
@ -101,6 +102,9 @@ def fetch_object_from_id(id, options \\ []) do
|
||||||
{:transmogrifier, {:error, {:reject, e}}} ->
|
{:transmogrifier, {:error, {:reject, e}}} ->
|
||||||
{:reject, e}
|
{:reject, e}
|
||||||
|
|
||||||
|
{:transmogrifier, {:reject, e}} ->
|
||||||
|
{:reject, e}
|
||||||
|
|
||||||
{:transmogrifier, _} = e ->
|
{:transmogrifier, _} = e ->
|
||||||
{:error, e}
|
{:error, e}
|
||||||
|
|
||||||
|
@ -124,12 +128,14 @@ def fetch_object_from_id(id, options \\ []) do
|
||||||
defp prepare_activity_params(data) do
|
defp prepare_activity_params(data) do
|
||||||
%{
|
%{
|
||||||
"type" => "Create",
|
"type" => "Create",
|
||||||
"to" => data["to"] || [],
|
|
||||||
"cc" => data["cc"] || [],
|
|
||||||
# Should we seriously keep this attributedTo thing?
|
# Should we seriously keep this attributedTo thing?
|
||||||
"actor" => data["actor"] || data["attributedTo"],
|
"actor" => data["actor"] || data["attributedTo"],
|
||||||
"object" => data
|
"object" => data
|
||||||
}
|
}
|
||||||
|
|> Maps.put_if_present("to", data["to"])
|
||||||
|
|> Maps.put_if_present("cc", data["cc"])
|
||||||
|
|> Maps.put_if_present("bto", data["bto"])
|
||||||
|
|> Maps.put_if_present("bcc", data["bcc"])
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_object_from_id!(id, options \\ []) do
|
def fetch_object_from_id!(id, options \\ []) do
|
||||||
|
|
|
@ -411,7 +411,7 @@ defp increase_read_duration(_) do
|
||||||
{:ok, :no_duration_limit, :no_duration_limit}
|
{:ok, :no_duration_limit, :no_duration_limit}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp client, do: Pleroma.ReverseProxy.Client
|
defp client, do: Pleroma.ReverseProxy.Client.Wrapper
|
||||||
|
|
||||||
defp track_failed_url(url, error, opts) do
|
defp track_failed_url(url, error, opts) do
|
||||||
ttl =
|
ttl =
|
||||||
|
|
|
@ -17,22 +17,4 @@ defmodule Pleroma.ReverseProxy.Client do
|
||||||
@callback stream_body(map()) :: {:ok, binary(), map()} | :done | {:error, atom() | String.t()}
|
@callback stream_body(map()) :: {:ok, binary(), map()} | :done | {:error, atom() | String.t()}
|
||||||
|
|
||||||
@callback close(reference() | pid() | map()) :: :ok
|
@callback close(reference() | pid() | map()) :: :ok
|
||||||
|
|
||||||
def request(method, url, headers, body \\ "", opts \\ []) do
|
|
||||||
client().request(method, url, headers, body, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
def stream_body(ref), do: client().stream_body(ref)
|
|
||||||
|
|
||||||
def close(ref), do: client().close(ref)
|
|
||||||
|
|
||||||
defp client do
|
|
||||||
:tesla
|
|
||||||
|> Application.get_env(:adapter)
|
|
||||||
|> client()
|
|
||||||
end
|
|
||||||
|
|
||||||
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
|
|
||||||
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
|
|
||||||
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.ReverseProxy.Client.Wrapper do
|
||||||
|
@moduledoc "Meta-client that calls the appropriate client from the config."
|
||||||
|
@behaviour Pleroma.ReverseProxy.Client
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def request(method, url, headers, body \\ "", opts \\ []) do
|
||||||
|
client().request(method, url, headers, body, opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def stream_body(ref), do: client().stream_body(ref)
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def close(ref), do: client().close(ref)
|
||||||
|
|
||||||
|
defp client do
|
||||||
|
:tesla
|
||||||
|
|> Application.get_env(:adapter)
|
||||||
|
|> client()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp client(Tesla.Adapter.Hackney), do: Pleroma.ReverseProxy.Client.Hackney
|
||||||
|
defp client(Tesla.Adapter.Gun), do: Pleroma.ReverseProxy.Client.Tesla
|
||||||
|
defp client(_), do: Pleroma.Config.get!(Pleroma.ReverseProxy.Client)
|
||||||
|
end
|
|
@ -23,6 +23,9 @@ defmodule Pleroma.Upload do
|
||||||
is once created permanent and changing it (especially in uploaders) is probably a bad idea!
|
is once created permanent and changing it (especially in uploaders) is probably a bad idea!
|
||||||
* `:tempfile` - path to the temporary file. Prefer in-place changes on the file rather than changing the
|
* `:tempfile` - path to the temporary file. Prefer in-place changes on the file rather than changing the
|
||||||
path as the temporary file is also tracked by `Plug.Upload{}` and automatically deleted once the request is over.
|
path as the temporary file is also tracked by `Plug.Upload{}` and automatically deleted once the request is over.
|
||||||
|
* `:width` - width of the media in pixels
|
||||||
|
* `:height` - height of the media in pixels
|
||||||
|
* `:blurhash` - string hash of the image encoded with the blurhash algorithm (https://blurha.sh/)
|
||||||
|
|
||||||
Related behaviors:
|
Related behaviors:
|
||||||
|
|
||||||
|
@ -32,6 +35,7 @@ defmodule Pleroma.Upload do
|
||||||
"""
|
"""
|
||||||
alias Ecto.UUID
|
alias Ecto.UUID
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Maps
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@type source ::
|
@type source ::
|
||||||
|
@ -53,9 +57,12 @@ defmodule Pleroma.Upload do
|
||||||
name: String.t(),
|
name: String.t(),
|
||||||
tempfile: String.t(),
|
tempfile: String.t(),
|
||||||
content_type: String.t(),
|
content_type: String.t(),
|
||||||
|
width: integer(),
|
||||||
|
height: integer(),
|
||||||
|
blurhash: String.t(),
|
||||||
path: String.t()
|
path: String.t()
|
||||||
}
|
}
|
||||||
defstruct [:id, :name, :tempfile, :content_type, :path]
|
defstruct [:id, :name, :tempfile, :content_type, :width, :height, :blurhash, :path]
|
||||||
|
|
||||||
defp get_description(opts, upload) do
|
defp get_description(opts, upload) do
|
||||||
case {opts[:description], Pleroma.Config.get([Pleroma.Upload, :default_description])} do
|
case {opts[:description], Pleroma.Config.get([Pleroma.Upload, :default_description])} do
|
||||||
|
@ -89,9 +96,12 @@ def store(upload, opts \\ []) do
|
||||||
"mediaType" => upload.content_type,
|
"mediaType" => upload.content_type,
|
||||||
"href" => url_from_spec(upload, opts.base_url, url_spec)
|
"href" => url_from_spec(upload, opts.base_url, url_spec)
|
||||||
}
|
}
|
||||||
|
|> Maps.put_if_present("width", upload.width)
|
||||||
|
|> Maps.put_if_present("height", upload.height)
|
||||||
],
|
],
|
||||||
"name" => description
|
"name" => description
|
||||||
}}
|
}
|
||||||
|
|> Maps.put_if_present("blurhash", upload.blurhash)}
|
||||||
else
|
else
|
||||||
{:description_limit, _} ->
|
{:description_limit, _} ->
|
||||||
{:error, :description_too_long}
|
{:error, :description_too_long}
|
||||||
|
@ -225,7 +235,7 @@ def base_url do
|
||||||
|
|
||||||
case uploader do
|
case uploader do
|
||||||
Pleroma.Uploaders.Local ->
|
Pleroma.Uploaders.Local ->
|
||||||
upload_base_url || Pleroma.Web.base_url() <> "/media/"
|
upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/"
|
||||||
|
|
||||||
Pleroma.Uploaders.S3 ->
|
Pleroma.Uploaders.S3 ->
|
||||||
bucket = Config.get([Pleroma.Uploaders.S3, :bucket])
|
bucket = Config.get([Pleroma.Uploaders.S3, :bucket])
|
||||||
|
@ -251,7 +261,7 @@ def base_url do
|
||||||
end
|
end
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
public_endpoint || upload_base_url || Pleroma.Web.base_url() <> "/media/"
|
public_endpoint || upload_base_url || Pleroma.Web.Endpoint.url() <> "/media/"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Upload.Filter.AnalyzeMetadata do
|
||||||
|
@moduledoc """
|
||||||
|
Extracts metadata about the upload, such as width/height
|
||||||
|
"""
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@behaviour Pleroma.Upload.Filter
|
||||||
|
|
||||||
|
@spec filter(Pleroma.Upload.t()) ::
|
||||||
|
{:ok, :filtered, Pleroma.Upload.t()} | {:ok, :noop} | {:error, String.t()}
|
||||||
|
def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _} = upload) do
|
||||||
|
try do
|
||||||
|
image =
|
||||||
|
file
|
||||||
|
|> Mogrify.open()
|
||||||
|
|> Mogrify.verbose()
|
||||||
|
|
||||||
|
upload =
|
||||||
|
upload
|
||||||
|
|> Map.put(:width, image.width)
|
||||||
|
|> Map.put(:height, image.height)
|
||||||
|
|> Map.put(:blurhash, get_blurhash(file))
|
||||||
|
|
||||||
|
{:ok, :filtered, upload}
|
||||||
|
rescue
|
||||||
|
e in ErlangError ->
|
||||||
|
Logger.warn("#{__MODULE__}: #{inspect(e)}")
|
||||||
|
{:ok, :noop}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter(_), do: {:ok, :noop}
|
||||||
|
|
||||||
|
defp get_blurhash(file) do
|
||||||
|
with {:ok, blurhash} <- :eblurhash.magick(file) do
|
||||||
|
blurhash
|
||||||
|
else
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -35,7 +35,7 @@ defmodule Pleroma.Uploaders.Uploader do
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@type file_spec :: {:file | :url, String.t()}
|
@type file_spec :: {:file | :url, String.t()}
|
||||||
@callback put_file(Pleroma.Upload.t()) ::
|
@callback put_file(upload :: struct()) ::
|
||||||
:ok | {:ok, file_spec()} | {:error, String.t()} | :wait_callback
|
:ok | {:ok, file_spec()} | {:error, String.t()} | :wait_callback
|
||||||
|
|
||||||
@callback delete_file(file :: String.t()) :: :ok | {:error, String.t()}
|
@callback delete_file(file :: String.t()) :: :ok | {:error, String.t()}
|
||||||
|
@ -46,7 +46,7 @@ defmodule Pleroma.Uploaders.Uploader do
|
||||||
| {:error, Plug.Conn.t(), String.t()}
|
| {:error, Plug.Conn.t(), String.t()}
|
||||||
@optional_callbacks http_callback: 2
|
@optional_callbacks http_callback: 2
|
||||||
|
|
||||||
@spec put_file(module(), Pleroma.Upload.t()) :: {:ok, file_spec()} | {:error, String.t()}
|
@spec put_file(module(), upload :: struct()) :: {:ok, file_spec()} | {:error, String.t()}
|
||||||
def put_file(uploader, upload) do
|
def put_file(uploader, upload) do
|
||||||
case uploader.put_file(upload) do
|
case uploader.put_file(upload) do
|
||||||
:ok -> {:ok, {:file, upload.path}}
|
:ok -> {:ok, {:file, upload.path}}
|
||||||
|
|
|
@ -27,13 +27,13 @@ defmodule Pleroma.User do
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserRelationship
|
alias Pleroma.UserRelationship
|
||||||
alias Pleroma.Web
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Builder
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
alias Pleroma.Web.ActivityPub.Pipeline
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.OAuth
|
alias Pleroma.Web.OAuth
|
||||||
alias Pleroma.Web.RelMe
|
alias Pleroma.Web.RelMe
|
||||||
alias Pleroma.Workers.BackgroundWorker
|
alias Pleroma.Workers.BackgroundWorker
|
||||||
|
@ -360,7 +360,7 @@ def avatar_url(user, options \\ []) do
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
unless options[:no_default] do
|
unless options[:no_default] do
|
||||||
Config.get([:assets, :default_user_avatar], "#{Web.base_url()}/images/avi.png")
|
Config.get([:assets, :default_user_avatar], "#{Endpoint.url()}/images/avi.png")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -368,13 +368,13 @@ def avatar_url(user, options \\ []) do
|
||||||
def banner_url(user, options \\ []) do
|
def banner_url(user, options \\ []) do
|
||||||
case user.banner do
|
case user.banner do
|
||||||
%{"url" => [%{"href" => href} | _]} -> href
|
%{"url" => [%{"href" => href} | _]} -> href
|
||||||
_ -> !options[:no_default] && "#{Web.base_url()}/images/banner.png"
|
_ -> !options[:no_default] && "#{Endpoint.url()}/images/banner.png"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Should probably be renamed or removed
|
# Should probably be renamed or removed
|
||||||
@spec ap_id(User.t()) :: String.t()
|
@spec ap_id(User.t()) :: String.t()
|
||||||
def ap_id(%User{nickname: nickname}), do: "#{Web.base_url()}/users/#{nickname}"
|
def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"
|
||||||
|
|
||||||
@spec ap_followers(User.t()) :: String.t()
|
@spec ap_followers(User.t()) :: String.t()
|
||||||
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
|
def ap_followers(%User{follower_address: fa}) when is_binary(fa), do: fa
|
||||||
|
|
|
@ -35,9 +35,10 @@ def controller do
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
import Pleroma.Web.Router.Helpers
|
|
||||||
import Pleroma.Web.TranslationHelpers
|
import Pleroma.Web.TranslationHelpers
|
||||||
|
|
||||||
|
alias Pleroma.Web.Router.Helpers, as: Routes
|
||||||
|
|
||||||
plug(:set_put_layout)
|
plug(:set_put_layout)
|
||||||
|
|
||||||
defp set_put_layout(conn, _) do
|
defp set_put_layout(conn, _) do
|
||||||
|
@ -131,7 +132,8 @@ def view do
|
||||||
|
|
||||||
import Pleroma.Web.ErrorHelpers
|
import Pleroma.Web.ErrorHelpers
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
import Pleroma.Web.Router.Helpers
|
|
||||||
|
alias Pleroma.Web.Router.Helpers, as: Routes
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -229,20 +231,4 @@ def call(%Plug.Conn{} = conn, options) do
|
||||||
defmacro __using__(which) when is_atom(which) do
|
defmacro __using__(which) when is_atom(which) do
|
||||||
apply(__MODULE__, which, [])
|
apply(__MODULE__, which, [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def base_url do
|
|
||||||
Pleroma.Web.Endpoint.url()
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: Change to Phoenix.Router.routes/1 for Phoenix 1.6.0+
|
|
||||||
def get_api_routes do
|
|
||||||
Pleroma.Web.Router.__routes__()
|
|
||||||
|> Enum.reject(fn r -> r.plug == Pleroma.Web.Fallback.RedirectController end)
|
|
||||||
|> Enum.map(fn r ->
|
|
||||||
r.path
|
|
||||||
|> String.split("/", trim: true)
|
|
||||||
|> List.first()
|
|
||||||
end)
|
|
||||||
|> Enum.uniq()
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -88,7 +88,7 @@ defp increase_replies_count_if_reply(%{
|
||||||
|
|
||||||
defp increase_replies_count_if_reply(_create_data), do: :noop
|
defp increase_replies_count_if_reply(_create_data), do: :noop
|
||||||
|
|
||||||
@object_types ~w[ChatMessage Question Answer Audio Video Event Article]
|
@object_types ~w[ChatMessage Question Answer Audio Video Event Article Note]
|
||||||
@impl true
|
@impl true
|
||||||
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
def persist(%{"type" => type} = object, meta) when type in @object_types do
|
||||||
with {:ok, object} <- Object.create(object) do
|
with {:ok, object} <- Object.create(object) do
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPub.Persisting do
|
defmodule Pleroma.Web.ActivityPub.ActivityPub.Persisting do
|
||||||
@callback persist(map(), keyword()) :: {:ok, Activity.t() | Object.t()}
|
@callback persist(map(), keyword()) :: {:ok, struct()}
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPub.Streaming do
|
defmodule Pleroma.Web.ActivityPub.ActivityPub.Streaming do
|
||||||
alias Pleroma.Activity
|
@callback stream_out(struct()) :: any()
|
||||||
alias Pleroma.Object
|
@callback stream_out_participations(struct(), struct()) :: any()
|
||||||
alias Pleroma.User
|
|
||||||
|
|
||||||
@callback stream_out(Activity.t()) :: any()
|
|
||||||
@callback stream_out_participations(Object.t(), User.t()) :: any()
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -223,7 +223,7 @@ def announce(actor, object, options \\ []) do
|
||||||
[actor.follower_address]
|
[actor.follower_address]
|
||||||
|
|
||||||
public? and Visibility.is_local_public?(object) ->
|
public? and Visibility.is_local_public?(object) ->
|
||||||
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_local_public()]
|
[actor.follower_address, object.data["actor"], Utils.as_local_public()]
|
||||||
|
|
||||||
public? ->
|
public? ->
|
||||||
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
|
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
|
||||||
|
|
|
@ -6,7 +6,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.NoEmptyPolicy do
|
||||||
@moduledoc "Filter local activities which have no content"
|
@moduledoc "Filter local activities which have no content"
|
||||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web.Endpoint
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def filter(%{"actor" => actor} = object) do
|
def filter(%{"actor" => actor} = object) do
|
||||||
|
@ -24,7 +24,7 @@ def filter(%{"actor" => actor} = object) do
|
||||||
def filter(object), do: {:ok, object}
|
def filter(object), do: {:ok, object}
|
||||||
|
|
||||||
defp is_local?(actor) do
|
defp is_local?(actor) do
|
||||||
if actor |> String.starts_with?("#{Web.base_url()}") do
|
if actor |> String.starts_with?("#{Endpoint.url()}") do
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
false
|
false
|
||||||
|
|
|
@ -177,6 +177,14 @@ defp check_banner_removal(%{host: actor_host} = _actor_info, %{"image" => _image
|
||||||
|
|
||||||
defp check_banner_removal(_actor_info, object), do: {:ok, object}
|
defp check_banner_removal(_actor_info, object), do: {:ok, object}
|
||||||
|
|
||||||
|
defp check_object(%{"object" => object} = activity) do
|
||||||
|
with {:ok, _object} <- filter(object) do
|
||||||
|
{:ok, activity}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp check_object(object), do: {:ok, object}
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def filter(%{"type" => "Delete", "actor" => actor} = object) do
|
def filter(%{"type" => "Delete", "actor" => actor} = object) do
|
||||||
%{host: actor_host} = URI.parse(actor)
|
%{host: actor_host} = URI.parse(actor)
|
||||||
|
@ -202,7 +210,8 @@ def filter(%{"actor" => actor} = object) do
|
||||||
{:ok, object} <- check_media_nsfw(actor_info, object),
|
{:ok, object} <- check_media_nsfw(actor_info, object),
|
||||||
{:ok, object} <- check_ftl_removal(actor_info, object),
|
{:ok, object} <- check_ftl_removal(actor_info, object),
|
||||||
{:ok, object} <- check_followers_only(actor_info, object),
|
{:ok, object} <- check_followers_only(actor_info, object),
|
||||||
{:ok, object} <- check_report_removal(actor_info, object) do
|
{:ok, object} <- check_report_removal(actor_info, object),
|
||||||
|
{:ok, object} <- check_object(object) do
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
else
|
else
|
||||||
{:reject, nil} -> {:reject, "[SimplePolicy]"}
|
{:reject, nil} -> {:reject, "[SimplePolicy]"}
|
||||||
|
@ -227,6 +236,19 @@ def filter(%{"id" => actor, "type" => obj_type} = object)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filter(object) when is_binary(object) do
|
||||||
|
uri = URI.parse(object)
|
||||||
|
|
||||||
|
with {:ok, object} <- check_accept(uri, object),
|
||||||
|
{:ok, object} <- check_reject(uri, object) do
|
||||||
|
{:ok, object}
|
||||||
|
else
|
||||||
|
{:reject, nil} -> {:reject, "[SimplePolicy]"}
|
||||||
|
{:reject, _} = e -> e
|
||||||
|
_ -> {:reject, "[SimplePolicy]"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def filter(object), do: {:ok, object}
|
def filter(object), do: {:ok, object}
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -102,7 +102,7 @@ def validate(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
|
%{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity,
|
||||||
meta
|
meta
|
||||||
)
|
)
|
||||||
when objtype in ~w[Question Answer Audio Video Event Article] do
|
when objtype in ~w[Question Answer Audio Video Event Article Note] do
|
||||||
with {:ok, object_data} <- cast_and_apply(object),
|
with {:ok, object_data} <- cast_and_apply(object),
|
||||||
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
|
meta = Keyword.put(meta, :object_data, object_data |> stringify_keys),
|
||||||
{:ok, create_activity} <-
|
{:ok, create_activity} <-
|
||||||
|
@ -115,7 +115,7 @@ def validate(
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate(%{"type" => type} = object, meta)
|
def validate(%{"type" => type} = object, meta)
|
||||||
when type in ~w[Event Question Audio Video Article] do
|
when type in ~w[Event Question Audio Video Article Note] do
|
||||||
validator =
|
validator =
|
||||||
case type do
|
case type do
|
||||||
"Event" -> EventValidator
|
"Event" -> EventValidator
|
||||||
|
@ -123,6 +123,7 @@ def validate(%{"type" => type} = object, meta)
|
||||||
"Audio" -> AudioVideoValidator
|
"Audio" -> AudioVideoValidator
|
||||||
"Video" -> AudioVideoValidator
|
"Video" -> AudioVideoValidator
|
||||||
"Article" -> ArticleNoteValidator
|
"Article" -> ArticleNoteValidator
|
||||||
|
"Note" -> ArticleNoteValidator
|
||||||
end
|
end
|
||||||
|
|
||||||
with {:ok, object} <-
|
with {:ok, object} <-
|
||||||
|
@ -194,7 +195,7 @@ def cast_and_apply(%{"type" => "Event"} = object) do
|
||||||
EventValidator.cast_and_apply(object)
|
EventValidator.cast_and_apply(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_and_apply(%{"type" => "Article"} = object) do
|
def cast_and_apply(%{"type" => type} = object) when type in ~w[Article Note] do
|
||||||
ArticleNoteValidator.cast_and_apply(object)
|
ArticleNoteValidator.cast_and_apply(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,7 @@ def validate_announcable(cng) do
|
||||||
false <- Visibility.is_public?(object) do
|
false <- Visibility.is_public?(object) do
|
||||||
same_actor = object.data["actor"] == actor.ap_id
|
same_actor = object.data["actor"] == actor.ap_id
|
||||||
recipients = get_field(cng, :to) ++ get_field(cng, :cc)
|
recipients = get_field(cng, :to) ++ get_field(cng, :cc)
|
||||||
local_public = Pleroma.Constants.as_local_public()
|
local_public = Utils.as_local_public()
|
||||||
|
|
||||||
is_public =
|
is_public =
|
||||||
Enum.member?(recipients, Pleroma.Constants.as_public()) or
|
Enum.member?(recipients, Pleroma.Constants.as_public()) or
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
@ -23,6 +24,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do
|
||||||
field(:name, :string)
|
field(:name, :string)
|
||||||
field(:inReplyTo, ObjectValidators.ObjectID)
|
field(:inReplyTo, ObjectValidators.ObjectID)
|
||||||
field(:attributedTo, ObjectValidators.ObjectID)
|
field(:attributedTo, ObjectValidators.ObjectID)
|
||||||
|
field(:context, :string)
|
||||||
|
|
||||||
# TODO: Remove actor on objects
|
# TODO: Remove actor on objects
|
||||||
field(:actor, ObjectValidators.ObjectID)
|
field(:actor, ObjectValidators.ObjectID)
|
||||||
|
@ -46,6 +48,11 @@ def cast_data(data) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(struct, data) do
|
def changeset(struct, data) do
|
||||||
|
data =
|
||||||
|
data
|
||||||
|
|> CommonFixes.fix_actor()
|
||||||
|
|> CommonFixes.fix_object_defaults()
|
||||||
|
|
||||||
struct
|
struct
|
||||||
|> cast(data, __schema__(:fields))
|
|> cast(data, __schema__(:fields))
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,6 +50,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNoteValidator do
|
||||||
|
|
||||||
field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
|
field(:likes, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
|
field(:announcements, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
|
|
||||||
|
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_and_apply(data) do
|
def cast_and_apply(data) do
|
||||||
|
@ -65,25 +67,39 @@ def cast_and_validate(data) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_data(data) do
|
def cast_data(data) do
|
||||||
data = fix(data)
|
|
||||||
|
|
||||||
%__MODULE__{}
|
%__MODULE__{}
|
||||||
|> changeset(data)
|
|> changeset(data)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fix_url(%{"url" => url} = data) when is_map(url) do
|
defp fix_url(%{"url" => url} = data) when is_bitstring(url), do: data
|
||||||
Map.put(data, "url", url["href"])
|
defp fix_url(%{"url" => url} = data) when is_map(url), do: Map.put(data, "url", url["href"])
|
||||||
end
|
|
||||||
|
|
||||||
defp fix_url(data), do: data
|
defp fix_url(data), do: data
|
||||||
|
|
||||||
|
defp fix_tag(%{"tag" => tag} = data) when is_list(tag), do: data
|
||||||
|
defp fix_tag(%{"tag" => tag} = data) when is_map(tag), do: Map.put(data, "tag", [tag])
|
||||||
|
defp fix_tag(data), do: Map.drop(data, ["tag"])
|
||||||
|
|
||||||
|
defp fix_replies(%{"replies" => %{"first" => %{"items" => replies}}} = data)
|
||||||
|
when is_list(replies),
|
||||||
|
do: Map.put(data, "replies", replies)
|
||||||
|
|
||||||
|
defp fix_replies(%{"replies" => %{"items" => replies}} = data) when is_list(replies),
|
||||||
|
do: Map.put(data, "replies", replies)
|
||||||
|
|
||||||
|
defp fix_replies(%{"replies" => replies} = data) when is_bitstring(replies),
|
||||||
|
do: Map.drop(data, ["replies"])
|
||||||
|
|
||||||
|
defp fix_replies(data), do: data
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_defaults()
|
|
||||||
|> CommonFixes.fix_attribution()
|
|
||||||
|> CommonFixes.fix_actor()
|
|> CommonFixes.fix_actor()
|
||||||
|
|> CommonFixes.fix_object_defaults()
|
||||||
|> fix_url()
|
|> fix_url()
|
||||||
|
|> fix_tag()
|
||||||
|
|> fix_replies()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
|
|> Transmogrifier.fix_content_map()
|
||||||
end
|
end
|
||||||
|
|
||||||
def changeset(struct, data) do
|
def changeset(struct, data) do
|
||||||
|
|
|
@ -20,6 +20,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator do
|
||||||
field(:type, :string)
|
field(:type, :string)
|
||||||
field(:href, ObjectValidators.Uri)
|
field(:href, ObjectValidators.Uri)
|
||||||
field(:mediaType, :string, default: "application/octet-stream")
|
field(:mediaType, :string, default: "application/octet-stream")
|
||||||
|
field(:width, :integer)
|
||||||
|
field(:height, :integer)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@ def url_changeset(struct, data) do
|
||||||
data = fix_media_type(data)
|
data = fix_media_type(data)
|
||||||
|
|
||||||
struct
|
struct
|
||||||
|> cast(data, [:type, :href, :mediaType])
|
|> cast(data, [:type, :href, :mediaType, :width, :height])
|
||||||
|> validate_inclusion(:type, ["Link"])
|
|> validate_inclusion(:type, ["Link"])
|
||||||
|> validate_required([:type, :href, :mediaType])
|
|> validate_required([:type, :href, :mediaType])
|
||||||
end
|
end
|
||||||
|
@ -59,7 +61,7 @@ def url_changeset(struct, data) do
|
||||||
def fix_media_type(data) do
|
def fix_media_type(data) do
|
||||||
data = Map.put_new(data, "mediaType", data["mimeType"])
|
data = Map.put_new(data, "mediaType", data["mimeType"])
|
||||||
|
|
||||||
if MIME.valid?(data["mediaType"]) do
|
if is_bitstring(data["mediaType"]) && MIME.extensions(data["mediaType"]) != [] do
|
||||||
data
|
data
|
||||||
else
|
else
|
||||||
Map.put(data, "mediaType", "application/octet-stream")
|
Map.put(data, "mediaType", "application/octet-stream")
|
||||||
|
|
|
@ -119,9 +119,8 @@ defp fix_content(data), do: data
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_defaults()
|
|
||||||
|> CommonFixes.fix_attribution()
|
|
||||||
|> CommonFixes.fix_actor()
|
|> CommonFixes.fix_actor()
|
||||||
|
|> CommonFixes.fix_object_defaults()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
|> fix_url()
|
|> fix_url()
|
||||||
|> fix_content()
|
|> fix_content()
|
||||||
|
|
|
@ -3,26 +3,55 @@
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do
|
||||||
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
alias Pleroma.Object.Containment
|
alias Pleroma.Object.Containment
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
|
||||||
# based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults
|
def cast_and_filter_recipients(message, field, follower_collection, field_fallback \\ []) do
|
||||||
def fix_defaults(data) do
|
{:ok, data} = ObjectValidators.Recipients.cast(message[field] || field_fallback)
|
||||||
|
|
||||||
|
data =
|
||||||
|
Enum.reject(data, fn x ->
|
||||||
|
String.ends_with?(x, "/followers") and x != follower_collection
|
||||||
|
end)
|
||||||
|
|
||||||
|
Map.put(message, field, data)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fix_object_defaults(data) do
|
||||||
%{data: %{"id" => context}, id: context_id} =
|
%{data: %{"id" => context}, id: context_id} =
|
||||||
Utils.create_context(data["context"] || data["conversation"])
|
Utils.create_context(data["context"] || data["conversation"])
|
||||||
|
|
||||||
|
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["attributedTo"])
|
||||||
|
|
||||||
data
|
data
|
||||||
|> Map.put("context", context)
|
|> Map.put("context", context)
|
||||||
|> Map.put("context_id", context_id)
|
|> Map.put("context_id", context_id)
|
||||||
|
|> cast_and_filter_recipients("to", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("cc", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("bto", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("bcc", follower_collection)
|
||||||
|
|> Transmogrifier.fix_implicit_addressing(follower_collection)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_attribution(data) do
|
def fix_activity_addressing(activity, _meta) do
|
||||||
data
|
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(activity["actor"])
|
||||||
|> Map.put_new("actor", data["attributedTo"])
|
|
||||||
|
activity
|
||||||
|
|> cast_and_filter_recipients("to", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("cc", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("bto", follower_collection)
|
||||||
|
|> cast_and_filter_recipients("bcc", follower_collection)
|
||||||
|
|> Transmogrifier.fix_implicit_addressing(follower_collection)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_actor(data) do
|
def fix_actor(data) do
|
||||||
actor = Containment.get_actor(data)
|
actor =
|
||||||
|
data
|
||||||
|
|> Map.put_new("actor", data["attributedTo"])
|
||||||
|
|> Containment.get_actor()
|
||||||
|
|
||||||
data
|
data
|
||||||
|> Map.put("actor", actor)
|
|> Map.put("actor", actor)
|
||||||
|
|
|
@ -15,6 +15,7 @@ def validate_any_presence(cng, fields) do
|
||||||
fields
|
fields
|
||||||
|> Enum.map(fn field -> get_field(cng, field) end)
|
|> Enum.map(fn field -> get_field(cng, field) end)
|
||||||
|> Enum.any?(fn
|
|> Enum.any?(fn
|
||||||
|
nil -> false
|
||||||
[] -> false
|
[] -> false
|
||||||
_ -> true
|
_ -> true
|
||||||
end)
|
end)
|
||||||
|
|
|
@ -10,8 +10,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
|
||||||
|
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
@ -23,6 +25,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateGenericValidator do
|
||||||
field(:type, :string)
|
field(:type, :string)
|
||||||
field(:to, ObjectValidators.Recipients, default: [])
|
field(:to, ObjectValidators.Recipients, default: [])
|
||||||
field(:cc, ObjectValidators.Recipients, default: [])
|
field(:cc, ObjectValidators.Recipients, default: [])
|
||||||
|
field(:bto, ObjectValidators.Recipients, default: [])
|
||||||
|
field(:bcc, ObjectValidators.Recipients, default: [])
|
||||||
field(:object, ObjectValidators.ObjectID)
|
field(:object, ObjectValidators.ObjectID)
|
||||||
field(:expires_at, ObjectValidators.DateTime)
|
field(:expires_at, ObjectValidators.DateTime)
|
||||||
|
|
||||||
|
@ -54,39 +58,37 @@ def changeset(struct, data) do
|
||||||
|> cast(data, __schema__(:fields))
|
|> cast(data, __schema__(:fields))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fix_context(data, meta) do
|
# CommonFixes.fix_activity_addressing adapted for Create specific behavior
|
||||||
if object = meta[:object_data] do
|
defp fix_addressing(data, object) do
|
||||||
Map.put_new(data, "context", object["context"])
|
%User{follower_address: follower_collection} = User.get_cached_by_ap_id(data["actor"])
|
||||||
else
|
|
||||||
data
|
data
|
||||||
end
|
|> CommonFixes.cast_and_filter_recipients("to", follower_collection, object["to"])
|
||||||
|
|> CommonFixes.cast_and_filter_recipients("cc", follower_collection, object["cc"])
|
||||||
|
|> CommonFixes.cast_and_filter_recipients("bto", follower_collection, object["bto"])
|
||||||
|
|> CommonFixes.cast_and_filter_recipients("bcc", follower_collection, object["bcc"])
|
||||||
|
|> Transmogrifier.fix_implicit_addressing(follower_collection)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fix_addressing(data, meta) do
|
def fix(data, meta) do
|
||||||
if object = meta[:object_data] do
|
object = meta[:object_data]
|
||||||
data
|
|
||||||
|> Map.put_new("to", object["to"] || [])
|
|
||||||
|> Map.put_new("cc", object["cc"] || [])
|
|
||||||
else
|
|
||||||
data
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp fix(data, meta) do
|
|
||||||
data
|
data
|
||||||
|> fix_context(meta)
|
|
||||||
|> fix_addressing(meta)
|
|
||||||
|> CommonFixes.fix_actor()
|
|> CommonFixes.fix_actor()
|
||||||
|
|> Map.put_new("context", object["context"])
|
||||||
|
|> fix_addressing(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp validate_data(cng, meta) do
|
defp validate_data(cng, meta) do
|
||||||
|
object = meta[:object_data]
|
||||||
|
|
||||||
cng
|
cng
|
||||||
|> validate_required([:actor, :type, :object])
|
|> validate_required([:actor, :type, :object, :to, :cc])
|
||||||
|> validate_inclusion(:type, ["Create"])
|
|> validate_inclusion(:type, ["Create"])
|
||||||
|> CommonValidations.validate_actor_presence()
|
|> CommonValidations.validate_actor_presence()
|
||||||
|> CommonValidations.validate_any_presence([:to, :cc])
|
|> validate_actors_match(object)
|
||||||
|> validate_actors_match(meta)
|
|> validate_context_match(object)
|
||||||
|> validate_context_match(meta)
|
|> validate_addressing_match(object)
|
||||||
|> validate_object_nonexistence()
|
|> validate_object_nonexistence()
|
||||||
|> validate_object_containment()
|
|> validate_object_containment()
|
||||||
end
|
end
|
||||||
|
@ -118,8 +120,8 @@ def validate_object_nonexistence(cng) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_actors_match(cng, meta) do
|
def validate_actors_match(cng, object) do
|
||||||
attributed_to = meta[:object_data]["attributedTo"] || meta[:object_data]["actor"]
|
attributed_to = object["attributedTo"] || object["actor"]
|
||||||
|
|
||||||
cng
|
cng
|
||||||
|> validate_change(:actor, fn :actor, actor ->
|
|> validate_change(:actor, fn :actor, actor ->
|
||||||
|
@ -131,7 +133,7 @@ def validate_actors_match(cng, meta) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_context_match(cng, %{object_data: %{"context" => object_context}}) do
|
def validate_context_match(cng, %{"context" => object_context}) do
|
||||||
cng
|
cng
|
||||||
|> validate_change(:context, fn :context, context ->
|
|> validate_change(:context, fn :context, context ->
|
||||||
if context == object_context do
|
if context == object_context do
|
||||||
|
@ -142,5 +144,18 @@ def validate_context_match(cng, %{object_data: %{"context" => object_context}})
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_context_match(cng, _), do: cng
|
def validate_addressing_match(cng, object) do
|
||||||
|
[:to, :cc, :bcc, :bto]
|
||||||
|
|> Enum.reduce(cng, fn field, cng ->
|
||||||
|
object_data = object[to_string(field)]
|
||||||
|
|
||||||
|
validate_change(cng, field, fn field, data ->
|
||||||
|
if data == object_data do
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
[{field, "field doesn't match with object (#{inspect(object_data)})"}]
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
# Pleroma: A lightweight social networking server
|
|
||||||
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
|
||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do
|
|
||||||
use Ecto.Schema
|
|
||||||
|
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator
|
|
||||||
|
|
||||||
import Ecto.Changeset
|
|
||||||
|
|
||||||
@primary_key false
|
|
||||||
|
|
||||||
embedded_schema do
|
|
||||||
field(:id, ObjectValidators.ObjectID, primary_key: true)
|
|
||||||
field(:actor, ObjectValidators.ObjectID)
|
|
||||||
field(:type, :string)
|
|
||||||
field(:to, ObjectValidators.Recipients, default: [])
|
|
||||||
field(:cc, ObjectValidators.Recipients, default: [])
|
|
||||||
field(:bto, ObjectValidators.Recipients, default: [])
|
|
||||||
field(:bcc, ObjectValidators.Recipients, default: [])
|
|
||||||
embeds_one(:object, NoteValidator)
|
|
||||||
end
|
|
||||||
|
|
||||||
def cast_data(data) do
|
|
||||||
cast(%__MODULE__{}, data, __schema__(:fields))
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -72,8 +72,8 @@ def cast_data(data) do
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_defaults()
|
|> CommonFixes.fix_actor()
|
||||||
|> CommonFixes.fix_attribution()
|
|> CommonFixes.fix_object_defaults()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,8 @@ defp fix_closed(data) do
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_defaults()
|
|> CommonFixes.fix_actor()
|
||||||
|> CommonFixes.fix_attribution()
|
|> CommonFixes.fix_object_defaults()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
|> fix_closed()
|
|> fix_closed()
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,19 +15,19 @@ defmodule Pleroma.Web.ActivityPub.Pipeline do
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
|
|
||||||
@side_effects Config.get([:pipeline, :side_effects], SideEffects)
|
defp side_effects, do: Config.get([:pipeline, :side_effects], SideEffects)
|
||||||
@federator Config.get([:pipeline, :federator], Federator)
|
defp federator, do: Config.get([:pipeline, :federator], Federator)
|
||||||
@object_validator Config.get([:pipeline, :object_validator], ObjectValidator)
|
defp object_validator, do: Config.get([:pipeline, :object_validator], ObjectValidator)
|
||||||
@mrf Config.get([:pipeline, :mrf], MRF)
|
defp mrf, do: Config.get([:pipeline, :mrf], MRF)
|
||||||
@activity_pub Config.get([:pipeline, :activity_pub], ActivityPub)
|
defp activity_pub, do: Config.get([:pipeline, :activity_pub], ActivityPub)
|
||||||
@config Config.get([:pipeline, :config], Config)
|
defp config, do: Config.get([:pipeline, :config], Config)
|
||||||
|
|
||||||
@spec common_pipeline(map(), keyword()) ::
|
@spec common_pipeline(map(), keyword()) ::
|
||||||
{:ok, Activity.t() | Object.t(), keyword()} | {:error, any()}
|
{:ok, Activity.t() | Object.t(), keyword()} | {:error, any()}
|
||||||
def common_pipeline(object, meta) do
|
def common_pipeline(object, meta) do
|
||||||
case Repo.transaction(fn -> do_common_pipeline(object, meta) end, Utils.query_timeout()) do
|
case Repo.transaction(fn -> do_common_pipeline(object, meta) end, Utils.query_timeout()) do
|
||||||
{:ok, {:ok, activity, meta}} ->
|
{:ok, {:ok, activity, meta}} ->
|
||||||
@side_effects.handle_after_transaction(meta)
|
side_effects().handle_after_transaction(meta)
|
||||||
{:ok, activity, meta}
|
{:ok, activity, meta}
|
||||||
|
|
||||||
{:ok, value} ->
|
{:ok, value} ->
|
||||||
|
@ -44,10 +44,10 @@ def common_pipeline(object, meta) do
|
||||||
def do_common_pipeline(%{__struct__: _}, _meta), do: {:error, :is_struct}
|
def do_common_pipeline(%{__struct__: _}, _meta), do: {:error, :is_struct}
|
||||||
|
|
||||||
def do_common_pipeline(message, meta) do
|
def do_common_pipeline(message, meta) do
|
||||||
with {_, {:ok, message, meta}} <- {:validate, @object_validator.validate(message, meta)},
|
with {_, {:ok, message, meta}} <- {:validate, object_validator().validate(message, meta)},
|
||||||
{_, {:ok, message, meta}} <- {:mrf, @mrf.pipeline_filter(message, meta)},
|
{_, {:ok, message, meta}} <- {:mrf, mrf().pipeline_filter(message, meta)},
|
||||||
{_, {:ok, message, meta}} <- {:persist, @activity_pub.persist(message, meta)},
|
{_, {:ok, message, meta}} <- {:persist, activity_pub().persist(message, meta)},
|
||||||
{_, {:ok, message, meta}} <- {:side_effects, @side_effects.handle(message, meta)},
|
{_, {:ok, message, meta}} <- {:side_effects, side_effects().handle(message, meta)},
|
||||||
{_, {:ok, _}} <- {:federation, maybe_federate(message, meta)} do
|
{_, {:ok, _}} <- {:federation, maybe_federate(message, meta)} do
|
||||||
{:ok, message, meta}
|
{:ok, message, meta}
|
||||||
else
|
else
|
||||||
|
@ -60,7 +60,7 @@ defp maybe_federate(%Object{}, _), do: {:ok, :not_federated}
|
||||||
|
|
||||||
defp maybe_federate(%Activity{} = activity, meta) do
|
defp maybe_federate(%Activity{} = activity, meta) do
|
||||||
with {:ok, local} <- Keyword.fetch(meta, :local) do
|
with {:ok, local} <- Keyword.fetch(meta, :local) do
|
||||||
do_not_federate = meta[:do_not_federate] || !@config.get([:instance, :federating])
|
do_not_federate = meta[:do_not_federate] || !config().get([:instance, :federating])
|
||||||
|
|
||||||
if !do_not_federate and local and not Visibility.is_local_public?(activity) do
|
if !do_not_federate and local and not Visibility.is_local_public?(activity) do
|
||||||
activity =
|
activity =
|
||||||
|
@ -70,7 +70,7 @@ defp maybe_federate(%Activity{} = activity, meta) do
|
||||||
activity
|
activity
|
||||||
end
|
end
|
||||||
|
|
||||||
@federator.publish(activity)
|
federator().publish(activity)
|
||||||
{:ok, :federated}
|
{:ok, :federated}
|
||||||
else
|
else
|
||||||
{:ok, :not_federated}
|
{:ok, :not_federated}
|
||||||
|
|
|
@ -272,7 +272,7 @@ def gather_webfinger_links(%User{} = user) do
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
"rel" => "http://ostatus.org/schema/1.0/subscribe",
|
"rel" => "http://ostatus.org/schema/1.0/subscribe",
|
||||||
"template" => "#{Pleroma.Web.base_url()}/ostatus_subscribe?acct={uri}"
|
"template" => "#{Pleroma.Web.Endpoint.url()}/ostatus_subscribe?acct={uri}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -203,6 +203,19 @@ def handle(%{data: %{"type" => "Create"}} = activity, meta) do
|
||||||
Object.increase_replies_count(in_reply_to)
|
Object.increase_replies_count(in_reply_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
reply_depth = (meta[:depth] || 0) + 1
|
||||||
|
|
||||||
|
# FIXME: Force inReplyTo to replies
|
||||||
|
if Pleroma.Web.Federator.allowed_thread_distance?(reply_depth) and
|
||||||
|
object.data["replies"] != nil do
|
||||||
|
for reply_id <- object.data["replies"] do
|
||||||
|
Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{
|
||||||
|
"id" => reply_id,
|
||||||
|
"depth" => reply_depth
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn ->
|
ConcurrentLimiter.limit(Pleroma.Web.RichMedia.Helpers, fn ->
|
||||||
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
|
Task.start(fn -> Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity) end)
|
||||||
end)
|
end)
|
||||||
|
@ -423,7 +436,7 @@ def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_object_creation(%{"type" => objtype} = object, meta)
|
def handle_object_creation(%{"type" => objtype} = object, meta)
|
||||||
when objtype in ~w[Audio Video Question Event Article] do
|
when objtype in ~w[Audio Video Question Event Article Note] do
|
||||||
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do
|
||||||
{:ok, object, meta}
|
{:ok, object, meta}
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,7 +43,6 @@ def fix_object(object, options \\ []) do
|
||||||
|> fix_content_map()
|
|> fix_content_map()
|
||||||
|> fix_addressing()
|
|> fix_addressing()
|
||||||
|> fix_summary()
|
|> fix_summary()
|
||||||
|> fix_type(options)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_summary(%{"summary" => nil} = object) do
|
def fix_summary(%{"summary" => nil} = object) do
|
||||||
|
@ -72,17 +71,21 @@ def fix_addressing_list(map, field) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_explicit_addressing(
|
# if directMessage flag is set to true, leave the addressing alone
|
||||||
%{"to" => to, "cc" => cc} = object,
|
def fix_explicit_addressing(%{"directMessage" => true} = object, _follower_collection),
|
||||||
explicit_mentions,
|
do: object
|
||||||
follower_collection
|
|
||||||
) do
|
|
||||||
explicit_to = Enum.filter(to, fn x -> x in explicit_mentions end)
|
|
||||||
|
|
||||||
|
def fix_explicit_addressing(%{"to" => to, "cc" => cc} = object, follower_collection) do
|
||||||
|
explicit_mentions =
|
||||||
|
Utils.determine_explicit_mentions(object) ++
|
||||||
|
[Pleroma.Constants.as_public(), follower_collection]
|
||||||
|
|
||||||
|
explicit_to = Enum.filter(to, fn x -> x in explicit_mentions end)
|
||||||
explicit_cc = Enum.filter(to, fn x -> x not in explicit_mentions end)
|
explicit_cc = Enum.filter(to, fn x -> x not in explicit_mentions end)
|
||||||
|
|
||||||
final_cc =
|
final_cc =
|
||||||
(cc ++ explicit_cc)
|
(cc ++ explicit_cc)
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|> Enum.reject(fn x -> String.ends_with?(x, "/followers") and x != follower_collection end)
|
|> Enum.reject(fn x -> String.ends_with?(x, "/followers") and x != follower_collection end)
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|
|
||||||
|
@ -91,29 +94,6 @@ def fix_explicit_addressing(
|
||||||
|> Map.put("cc", final_cc)
|
|> Map.put("cc", final_cc)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_explicit_addressing(object, _explicit_mentions, _followers_collection), do: object
|
|
||||||
|
|
||||||
# if directMessage flag is set to true, leave the addressing alone
|
|
||||||
def fix_explicit_addressing(%{"directMessage" => true} = object), do: object
|
|
||||||
|
|
||||||
def fix_explicit_addressing(object) do
|
|
||||||
explicit_mentions = Utils.determine_explicit_mentions(object)
|
|
||||||
|
|
||||||
%User{follower_address: follower_collection} =
|
|
||||||
object
|
|
||||||
|> Containment.get_actor()
|
|
||||||
|> User.get_cached_by_ap_id()
|
|
||||||
|
|
||||||
explicit_mentions =
|
|
||||||
explicit_mentions ++
|
|
||||||
[
|
|
||||||
Pleroma.Constants.as_public(),
|
|
||||||
follower_collection
|
|
||||||
]
|
|
||||||
|
|
||||||
fix_explicit_addressing(object, explicit_mentions, follower_collection)
|
|
||||||
end
|
|
||||||
|
|
||||||
# if as:Public is addressed, then make sure the followers collection is also addressed
|
# if as:Public is addressed, then make sure the followers collection is also addressed
|
||||||
# so that the activities will be delivered to local users.
|
# so that the activities will be delivered to local users.
|
||||||
def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collection) do
|
def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collection) do
|
||||||
|
@ -137,19 +117,19 @@ def fix_implicit_addressing(%{"to" => to, "cc" => cc} = object, followers_collec
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_implicit_addressing(object, _), do: object
|
|
||||||
|
|
||||||
def fix_addressing(object) do
|
def fix_addressing(object) do
|
||||||
{:ok, %User{} = user} = User.get_or_fetch_by_ap_id(object["actor"])
|
{:ok, %User{follower_address: follower_collection}} =
|
||||||
followers_collection = User.ap_followers(user)
|
object
|
||||||
|
|> Containment.get_actor()
|
||||||
|
|> User.get_or_fetch_by_ap_id()
|
||||||
|
|
||||||
object
|
object
|
||||||
|> fix_addressing_list("to")
|
|> fix_addressing_list("to")
|
||||||
|> fix_addressing_list("cc")
|
|> fix_addressing_list("cc")
|
||||||
|> fix_addressing_list("bto")
|
|> fix_addressing_list("bto")
|
||||||
|> fix_addressing_list("bcc")
|
|> fix_addressing_list("bcc")
|
||||||
|> fix_explicit_addressing()
|
|> fix_explicit_addressing(follower_collection)
|
||||||
|> fix_implicit_addressing(followers_collection)
|
|> fix_implicit_addressing(follower_collection)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_actor(%{"attributedTo" => actor} = object) do
|
def fix_actor(%{"attributedTo" => actor} = object) do
|
||||||
|
@ -223,10 +203,17 @@ def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachm
|
||||||
|
|
||||||
media_type =
|
media_type =
|
||||||
cond do
|
cond do
|
||||||
is_map(url) && MIME.valid?(url["mediaType"]) -> url["mediaType"]
|
is_map(url) && MIME.extensions(url["mediaType"]) != [] ->
|
||||||
MIME.valid?(data["mediaType"]) -> data["mediaType"]
|
url["mediaType"]
|
||||||
MIME.valid?(data["mimeType"]) -> data["mimeType"]
|
|
||||||
true -> nil
|
is_bitstring(data["mediaType"]) && MIME.extensions(data["mediaType"]) != [] ->
|
||||||
|
data["mediaType"]
|
||||||
|
|
||||||
|
is_bitstring(data["mimeType"]) && MIME.extensions(data["mimeType"]) != [] ->
|
||||||
|
data["mimeType"]
|
||||||
|
|
||||||
|
true ->
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
href =
|
href =
|
||||||
|
@ -244,6 +231,8 @@ def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachm
|
||||||
"type" => Map.get(url || %{}, "type", "Link")
|
"type" => Map.get(url || %{}, "type", "Link")
|
||||||
}
|
}
|
||||||
|> Maps.put_if_present("mediaType", media_type)
|
|> Maps.put_if_present("mediaType", media_type)
|
||||||
|
|> Maps.put_if_present("width", (url || %{})["width"] || data["width"])
|
||||||
|
|> Maps.put_if_present("height", (url || %{})["height"] || data["height"])
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"url" => [attachment_url],
|
"url" => [attachment_url],
|
||||||
|
@ -340,19 +329,18 @@ def fix_content_map(%{"contentMap" => content_map} = object) do
|
||||||
|
|
||||||
def fix_content_map(object), do: object
|
def fix_content_map(object), do: object
|
||||||
|
|
||||||
def fix_type(object, options \\ [])
|
defp fix_type(%{"type" => "Note", "inReplyTo" => reply_id, "name" => _} = object, options)
|
||||||
|
|
||||||
def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options)
|
|
||||||
when is_binary(reply_id) do
|
when is_binary(reply_id) do
|
||||||
with true <- Federator.allowed_thread_distance?(options[:depth]),
|
options = Keyword.put(options, :fetch, true)
|
||||||
{:ok, %{data: %{"type" => "Question"} = _} = _} <- get_obj_helper(reply_id, options) do
|
|
||||||
|
with %Object{data: %{"type" => "Question"}} <- Object.normalize(reply_id, options) do
|
||||||
Map.put(object, "type", "Answer")
|
Map.put(object, "type", "Answer")
|
||||||
else
|
else
|
||||||
_ -> object
|
_ -> object
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_type(object, _), do: object
|
defp fix_type(object, _options), do: object
|
||||||
|
|
||||||
# Reduce the object list to find the reported user.
|
# Reduce the object list to find the reported user.
|
||||||
defp get_reported(objects) do
|
defp get_reported(objects) do
|
||||||
|
@ -423,10 +411,9 @@ def handle_incoming(%{"id" => id}, _options) when is_binary(id) and byte_size(id
|
||||||
# - tags
|
# - tags
|
||||||
# - emoji
|
# - emoji
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype} = object} = data,
|
%{"type" => "Create", "object" => %{"type" => "Page"} = object} = data,
|
||||||
options
|
options
|
||||||
)
|
) do
|
||||||
when objtype in ~w{Note Page} do
|
|
||||||
actor = Containment.get_actor(data)
|
actor = Containment.get_actor(data)
|
||||||
|
|
||||||
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
||||||
|
@ -518,14 +505,23 @@ def handle_incoming(
|
||||||
|
|
||||||
def handle_incoming(
|
def handle_incoming(
|
||||||
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
|
%{"type" => "Create", "object" => %{"type" => objtype, "id" => obj_id}} = data,
|
||||||
_options
|
options
|
||||||
)
|
)
|
||||||
when objtype in ~w{Question Answer ChatMessage Audio Video Event Article} do
|
when objtype in ~w{Question Answer ChatMessage Audio Video Event Article Note} do
|
||||||
data = Map.put(data, "object", strip_internal_fields(data["object"]))
|
fetch_options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
|
||||||
|
|
||||||
|
object =
|
||||||
|
data["object"]
|
||||||
|
|> strip_internal_fields()
|
||||||
|
|> fix_type(fetch_options)
|
||||||
|
|> fix_in_reply_to(fetch_options)
|
||||||
|
|
||||||
|
data = Map.put(data, "object", object)
|
||||||
|
options = Keyword.put(options, :local, false)
|
||||||
|
|
||||||
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
|
with {:ok, %User{}} <- ObjectValidator.fetch_actor(data),
|
||||||
nil <- Activity.get_create_by_object_ap_id(obj_id),
|
nil <- Activity.get_create_by_object_ap_id(obj_id),
|
||||||
{:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do
|
{:ok, activity, _} <- Pipeline.common_pipeline(data, options) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
%Activity{} = activity -> {:ok, activity}
|
%Activity{} = activity -> {:ok, activity}
|
||||||
|
@ -949,7 +945,7 @@ def prepare_attachments(object) do
|
||||||
object
|
object
|
||||||
|> Map.get("attachment", [])
|
|> Map.get("attachment", [])
|
||||||
|> Enum.map(fn data ->
|
|> Enum.map(fn data ->
|
||||||
[%{"mediaType" => media_type, "href" => href} | _] = data["url"]
|
[%{"mediaType" => media_type, "href" => href} = url | _] = data["url"]
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"url" => href,
|
"url" => href,
|
||||||
|
@ -957,6 +953,9 @@ def prepare_attachments(object) do
|
||||||
"name" => data["name"],
|
"name" => data["name"],
|
||||||
"type" => "Document"
|
"type" => "Document"
|
||||||
}
|
}
|
||||||
|
|> Maps.put_if_present("width", url["width"])
|
||||||
|
|> Maps.put_if_present("height", url["height"])
|
||||||
|
|> Maps.put_if_present("blurhash", data["blurhash"])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Map.put(object, "attachment", attachments)
|
Map.put(object, "attachment", attachments)
|
||||||
|
|
|
@ -12,7 +12,6 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Visibility
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
|
@ -38,6 +37,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
@supported_report_states ~w(open closed resolved)
|
@supported_report_states ~w(open closed resolved)
|
||||||
@valid_visibilities ~w(public unlisted private direct)
|
@valid_visibilities ~w(public unlisted private direct)
|
||||||
|
|
||||||
|
def as_local_public, do: Endpoint.url() <> "/#Public"
|
||||||
|
|
||||||
# Some implementations send the actor URI as the actor field, others send the entire actor object,
|
# Some implementations send the actor URI as the actor field, others send the entire actor object,
|
||||||
# so figure out what the actor's URI is based on what we have.
|
# so figure out what the actor's URI is based on what we have.
|
||||||
def get_ap_id(%{"id" => id} = _), do: id
|
def get_ap_id(%{"id" => id} = _), do: id
|
||||||
|
@ -96,8 +97,11 @@ def maybe_splice_recipient(ap_id, params) do
|
||||||
!label_in_collection?(ap_id, params["cc"])
|
!label_in_collection?(ap_id, params["cc"])
|
||||||
|
|
||||||
if need_splice? do
|
if need_splice? do
|
||||||
cc_list = extract_list(params["cc"])
|
cc = [ap_id | extract_list(params["cc"])]
|
||||||
Map.put(params, "cc", [ap_id | cc_list])
|
|
||||||
|
params
|
||||||
|
|> Map.put("cc", cc)
|
||||||
|
|> Maps.safe_put_in(["object", "cc"], cc)
|
||||||
else
|
else
|
||||||
params
|
params
|
||||||
end
|
end
|
||||||
|
@ -107,7 +111,7 @@ def make_json_ld_header do
|
||||||
%{
|
%{
|
||||||
"@context" => [
|
"@context" => [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"#{Web.base_url()}/schemas/litepub-0.1.jsonld",
|
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
|
||||||
%{
|
%{
|
||||||
"@language" => "und"
|
"@language" => "und"
|
||||||
}
|
}
|
||||||
|
@ -132,7 +136,7 @@ def generate_object_id do
|
||||||
end
|
end
|
||||||
|
|
||||||
def generate_id(type) do
|
def generate_id(type) do
|
||||||
"#{Web.base_url()}/#{type}/#{UUID.generate()}"
|
"#{Endpoint.url()}/#{type}/#{UUID.generate()}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_notified_from_object(%{"type" => type} = object) when type in @supported_object_types do
|
def get_notified_from_object(%{"type" => type} = object) when type in @supported_object_types do
|
||||||
|
|
|
@ -261,7 +261,8 @@ def render("featured.json", %{
|
||||||
%{
|
%{
|
||||||
"id" => featured_address,
|
"id" => featured_address,
|
||||||
"type" => "OrderedCollection",
|
"type" => "OrderedCollection",
|
||||||
"orderedItems" => objects
|
"orderedItems" => objects,
|
||||||
|
"totalItems" => length(objects)
|
||||||
}
|
}
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,14 +20,14 @@ def is_public?(%{"directMessage" => true}), do: false
|
||||||
|
|
||||||
def is_public?(data) do
|
def is_public?(data) do
|
||||||
Utils.label_in_message?(Pleroma.Constants.as_public(), data) or
|
Utils.label_in_message?(Pleroma.Constants.as_public(), data) or
|
||||||
Utils.label_in_message?(Pleroma.Constants.as_local_public(), data)
|
Utils.label_in_message?(Utils.as_local_public(), data)
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_local_public?(%Object{data: data}), do: is_local_public?(data)
|
def is_local_public?(%Object{data: data}), do: is_local_public?(data)
|
||||||
def is_local_public?(%Activity{data: data}), do: is_local_public?(data)
|
def is_local_public?(%Activity{data: data}), do: is_local_public?(data)
|
||||||
|
|
||||||
def is_local_public?(data) do
|
def is_local_public?(data) do
|
||||||
Utils.label_in_message?(Pleroma.Constants.as_local_public(), data) and
|
Utils.label_in_message?(Utils.as_local_public(), data) and
|
||||||
not Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
not Utils.label_in_message?(Pleroma.Constants.as_public(), data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -127,7 +127,7 @@ def get_visibility(object) do
|
||||||
Pleroma.Constants.as_public() in cc ->
|
Pleroma.Constants.as_public() in cc ->
|
||||||
"unlisted"
|
"unlisted"
|
||||||
|
|
||||||
Pleroma.Constants.as_local_public() in to ->
|
Utils.as_local_public() in to ->
|
||||||
"local"
|
"local"
|
||||||
|
|
||||||
# this should use the sql for the object's activity
|
# this should use the sql for the object's activity
|
||||||
|
|
|
@ -13,7 +13,6 @@ defmodule Pleroma.Web.AdminAPI.OAuthAppController do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(:put_view, Pleroma.Web.MastodonAPI.AppView)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.OAuthAppView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
alias Pleroma.Web.MastodonAPI
|
||||||
|
|
||||||
|
def render(view, opts), do: MastodonAPI.AppView.render(view, opts)
|
||||||
|
end
|
|
@ -105,6 +105,7 @@ def show_operation do
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Media", "application/json", Attachment),
|
200 => Operation.response("Media", "application/json", Attachment),
|
||||||
401 => Operation.response("Media", "application/json", ApiError),
|
401 => Operation.response("Media", "application/json", ApiError),
|
||||||
|
403 => Operation.response("Media", "application/json", ApiError),
|
||||||
422 => Operation.response("Media", "application/json", ApiError)
|
422 => Operation.response("Media", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,7 +115,8 @@ def hashtag_operation do
|
||||||
],
|
],
|
||||||
operationId: "TimelineController.hashtag",
|
operationId: "TimelineController.hashtag",
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Array of Status", "application/json", array_of_statuses())
|
200 => Operation.response("Array of Status", "application/json", array_of_statuses()),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def emoji_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Emojis"],
|
||||||
|
summary: "List all custom emojis",
|
||||||
|
operationId: "UtilController.emoji",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("List", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
additionalProperties: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
image_url: %Schema{type: :string},
|
||||||
|
tags: %Schema{type: :array, items: %Schema{type: :string}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"firefox" => %{
|
||||||
|
"image_url" => "/emoji/firefox.png",
|
||||||
|
"tag" => ["Fun"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def frontend_configurations_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Configuration"],
|
||||||
|
summary: "Dump frontend configurations",
|
||||||
|
operationId: "UtilController.frontend_configurations",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("List", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
additionalProperties: %Schema{type: :object}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def change_password_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account credentials"],
|
||||||
|
summary: "Change account password",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
operationId: "UtilController.change_password",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:password, :query, :string, "Current password", required: true),
|
||||||
|
Operation.parameter(:new_password, :query, :string, "New password", required: true),
|
||||||
|
Operation.parameter(
|
||||||
|
:new_password_confirmation,
|
||||||
|
:query,
|
||||||
|
:string,
|
||||||
|
"New password, confirmation",
|
||||||
|
required: true
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Success", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{status: %Schema{type: :string, example: "success"}}
|
||||||
|
}),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def change_email_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account credentials"],
|
||||||
|
summary: "Change account email",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
operationId: "UtilController.change_email",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:password, :query, :string, "Current password", required: true),
|
||||||
|
Operation.parameter(:email, :query, :string, "New email", required: true)
|
||||||
|
],
|
||||||
|
requestBody: nil,
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Success", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{status: %Schema{type: :string, example: "success"}}
|
||||||
|
}),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_notificaton_settings_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Accounts"],
|
||||||
|
summary: "Update Notification Settings",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
operationId: "UtilController.update_notificaton_settings",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:block_from_strangers,
|
||||||
|
:query,
|
||||||
|
BooleanLike,
|
||||||
|
"blocks notifications from accounts you do not follow"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:hide_notification_contents,
|
||||||
|
:query,
|
||||||
|
BooleanLike,
|
||||||
|
"removes the contents of a message from the push notification"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
requestBody: nil,
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Success", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{status: %Schema{type: :string, example: "success"}}
|
||||||
|
}),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def disable_account_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account credentials"],
|
||||||
|
summary: "Disable Account",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
operationId: "UtilController.disable_account",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:password, :query, :string, "Password")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Success", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{status: %Schema{type: :string, example: "success"}}
|
||||||
|
}),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_account_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account credentials"],
|
||||||
|
summary: "Delete Account",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
operationId: "UtilController.delete_account",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(:password, :query, :string, "Password")
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response("Success", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{status: %Schema{type: :string, example: "success"}}
|
||||||
|
}),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def captcha_operation do
|
||||||
|
%Operation{
|
||||||
|
summary: "Get a captcha",
|
||||||
|
operationId: "UtilController.captcha",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Success", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def healthcheck_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Accounts"],
|
||||||
|
summary: "Quick status check on the instance",
|
||||||
|
security: [%{"oAuth" => ["write:accounts"]}],
|
||||||
|
operationId: "UtilController.healthcheck",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Healthy", "application/json", %Schema{type: :object}),
|
||||||
|
503 =>
|
||||||
|
Operation.response("Disabled or Unhealthy", "application/json", %Schema{type: :object})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def remote_subscribe_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Accounts"],
|
||||||
|
summary: "Remote Subscribe",
|
||||||
|
operationId: "UtilController.remote_subscribe",
|
||||||
|
parameters: [],
|
||||||
|
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -23,6 +23,7 @@ def follow_operation do
|
||||||
requestBody: request_body("Parameters", import_request(), required: true),
|
requestBody: request_body("Parameters", import_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => ok_response(),
|
200 => ok_response(),
|
||||||
|
403 => Operation.response("Error", "application/json", ApiError),
|
||||||
500 => Operation.response("Error", "application/json", ApiError)
|
500 => Operation.response("Error", "application/json", ApiError)
|
||||||
},
|
},
|
||||||
security: [%{"oAuth" => ["write:follow"]}]
|
security: [%{"oAuth" => ["write:follow"]}]
|
||||||
|
|
|
@ -8,7 +8,7 @@ defmodule Pleroma.Web.UserSocket do
|
||||||
|
|
||||||
## Channels
|
## Channels
|
||||||
# channel "room:*", Pleroma.Web.RoomChannel
|
# channel "room:*", Pleroma.Web.RoomChannel
|
||||||
channel("chat:*", Pleroma.Web.ChatChannel)
|
channel("chat:*", Pleroma.Web.ShoutChannel)
|
||||||
|
|
||||||
# Socket params are passed from the client and can
|
# Socket params are passed from the client and can
|
||||||
# be used to verify and authenticate a user. After
|
# be used to verify and authenticate a user. After
|
||||||
|
@ -22,7 +22,7 @@ defmodule Pleroma.Web.UserSocket do
|
||||||
# See `Phoenix.Token` documentation for examples in
|
# See `Phoenix.Token` documentation for examples in
|
||||||
# performing token verification on connect.
|
# performing token verification on connect.
|
||||||
def connect(%{"token" => token}, socket) do
|
def connect(%{"token" => token}, socket) do
|
||||||
with true <- Pleroma.Config.get([:chat, :enabled]),
|
with true <- Pleroma.Config.get([:shout, :enabled]),
|
||||||
{:ok, user_id} <- Phoenix.Token.verify(socket, "user socket", token, max_age: 84_600),
|
{:ok, user_id} <- Phoenix.Token.verify(socket, "user socket", token, max_age: 84_600),
|
||||||
%User{} = user <- Pleroma.User.get_cached_by_id(user_id) do
|
%User{} = user <- Pleroma.User.get_cached_by_id(user_id) do
|
||||||
{:ok, assign(socket, :user_name, user.nickname)}
|
{:ok, assign(socket, :user_name, user.nickname)}
|
||||||
|
|
|
@ -69,7 +69,7 @@ def get_to_and_cc(%{visibility: visibility} = draft) when visibility in ["public
|
||||||
to =
|
to =
|
||||||
case visibility do
|
case visibility do
|
||||||
"public" -> [Pleroma.Constants.as_public() | draft.mentions]
|
"public" -> [Pleroma.Constants.as_public() | draft.mentions]
|
||||||
"local" -> [Pleroma.Constants.as_local_public() | draft.mentions]
|
"local" -> [Utils.as_local_public() | draft.mentions]
|
||||||
end
|
end
|
||||||
|
|
||||||
cc = [draft.user.follower_address]
|
cc = [draft.user.follower_address]
|
||||||
|
|
|
@ -102,7 +102,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
plug(Plug.Parsers,
|
plug(Plug.Parsers,
|
||||||
parsers: [
|
parsers: [
|
||||||
:urlencoded,
|
:urlencoded,
|
||||||
{:multipart, length: {Config, :get, [[:instance, :upload_limit]]}},
|
{:multipart, length: Config.get([:instance, :upload_limit])},
|
||||||
:json
|
:json
|
||||||
],
|
],
|
||||||
pass: ["*/*"],
|
pass: ["*/*"],
|
||||||
|
|
|
@ -96,6 +96,11 @@ def perform(:incoming_ap_doc, params) do
|
||||||
Logger.debug("Unhandled actor #{actor}, #{inspect(e)}")
|
Logger.debug("Unhandled actor #{actor}, #{inspect(e)}")
|
||||||
{:error, e}
|
{:error, e}
|
||||||
|
|
||||||
|
{:error, {:validate_object, _}} = e ->
|
||||||
|
Logger.error("Incoming AP doc validation error: #{inspect(e)}")
|
||||||
|
Logger.debug(Jason.encode!(params, pretty: true))
|
||||||
|
e
|
||||||
|
|
||||||
e ->
|
e ->
|
||||||
# Just drop those for now
|
# Just drop those for now
|
||||||
Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end)
|
Logger.debug(fn -> "Unhandled activity\n" <> Jason.encode!(params, pretty: true) end)
|
||||||
|
|
|
@ -52,10 +52,10 @@ def most_recent_update(activities, user) do
|
||||||
def feed_logo do
|
def feed_logo do
|
||||||
case Pleroma.Config.get([:feed, :logo]) do
|
case Pleroma.Config.get([:feed, :logo]) do
|
||||||
nil ->
|
nil ->
|
||||||
"#{Pleroma.Web.base_url()}/static/logo.svg"
|
"#{Pleroma.Web.Endpoint.url()}/static/logo.svg"
|
||||||
|
|
||||||
logo ->
|
logo ->
|
||||||
"#{Pleroma.Web.base_url()}#{logo}"
|
"#{Pleroma.Web.Endpoint.url()}#{logo}"
|
||||||
end
|
end
|
||||||
|> MediaProxy.url()
|
|> MediaProxy.url()
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,7 +28,7 @@ def feed_redirect(%{assigns: %{format: format}} = conn, _params)
|
||||||
|
|
||||||
def feed_redirect(conn, %{"nickname" => nickname}) do
|
def feed_redirect(conn, %{"nickname" => nickname}) do
|
||||||
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
||||||
redirect(conn, external: "#{user_feed_url(conn, :feed, user.nickname)}.atom")
|
redirect(conn, external: "#{Routes.user_feed_url(conn, :feed, user.nickname)}.atom")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ def login(conn, params) do
|
||||||
defp redirect_to_oauth_form(conn, _params) do
|
defp redirect_to_oauth_form(conn, _params) do
|
||||||
with {:ok, app} <- local_mastofe_app() do
|
with {:ok, app} <- local_mastofe_app() do
|
||||||
path =
|
path =
|
||||||
o_auth_path(conn, :authorize,
|
Routes.o_auth_path(conn, :authorize,
|
||||||
response_type: "code",
|
response_type: "code",
|
||||||
client_id: app.client_id,
|
client_id: app.client_id,
|
||||||
redirect_uri: ".",
|
redirect_uri: ".",
|
||||||
|
@ -90,7 +90,7 @@ def password_reset(conn, params) do
|
||||||
defp local_mastodon_post_login_path(conn) do
|
defp local_mastodon_post_login_path(conn) do
|
||||||
case get_session(conn, :return_to) do
|
case get_session(conn, :return_to) do
|
||||||
nil ->
|
nil ->
|
||||||
masto_fe_path(conn, :index, ["getting-started"])
|
Routes.masto_fe_path(conn, :index, ["getting-started"])
|
||||||
|
|
||||||
return_to ->
|
return_to ->
|
||||||
delete_session(conn, :return_to)
|
delete_session(conn, :return_to)
|
||||||
|
|
|
@ -9,7 +9,6 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
plug(:put_view, Pleroma.Web.MastodonAPI.AccountView)
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(:assign_follower when action != :index)
|
plug(:assign_follower when action != :index)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ defmodule Pleroma.Web.MastodonAPI.MediaController do
|
||||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:create, :create2])
|
plug(Majic.Plug, [pool: Pleroma.MajicPool] when action in [:create, :create2])
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:media"]} when action == :show)
|
plug(OAuthScopesPlug, %{scopes: ["read:media"]} when action == :show)
|
||||||
plug(OAuthScopesPlug, %{scopes: ["write:media"]} when action != :show)
|
plug(OAuthScopesPlug, %{scopes: ["write:media"]} when action != :show)
|
||||||
|
|
|
@ -8,8 +8,8 @@ defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web
|
|
||||||
alias Pleroma.Web.ControllerHelper
|
alias Pleroma.Web.ControllerHelper
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.MastodonAPI.AccountView
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
@ -108,7 +108,7 @@ defp resource_search(_, "statuses", query, options) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp resource_search(:v2, "hashtags", query, options) do
|
defp resource_search(:v2, "hashtags", query, options) do
|
||||||
tags_path = Web.base_url() <> "/tag/"
|
tags_path = Endpoint.url() <> "/tag/"
|
||||||
|
|
||||||
query
|
query
|
||||||
|> prepare_tags(options)
|
|> prepare_tags(options)
|
||||||
|
|
|
@ -37,8 +37,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
when action in [:public, :hashtag]
|
when action in [:public, :hashtag]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(:put_view, Pleroma.Web.MastodonAPI.StatusView)
|
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TimelineOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.TimelineOperation
|
||||||
|
|
||||||
# GET /api/v1/timelines/home
|
# GET /api/v1/timelines/home
|
||||||
|
|
|
@ -292,6 +292,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
|> maybe_put_allow_following_move(user, opts[:for])
|
|> maybe_put_allow_following_move(user, opts[:for])
|
||||||
|> maybe_put_unread_conversation_count(user, opts[:for])
|
|> maybe_put_unread_conversation_count(user, opts[:for])
|
||||||
|> maybe_put_unread_notification_count(user, opts[:for])
|
|> maybe_put_unread_notification_count(user, opts[:for])
|
||||||
|
|> maybe_put_email_address(user, opts[:for])
|
||||||
end
|
end
|
||||||
|
|
||||||
defp username_from_nickname(string) when is_binary(string) do
|
defp username_from_nickname(string) when is_binary(string) do
|
||||||
|
@ -403,6 +404,16 @@ defp maybe_put_unread_notification_count(data, %User{id: user_id}, %User{id: use
|
||||||
|
|
||||||
defp maybe_put_unread_notification_count(data, _, _), do: data
|
defp maybe_put_unread_notification_count(data, _, _), do: data
|
||||||
|
|
||||||
|
defp maybe_put_email_address(data, %User{id: user_id}, %User{id: user_id} = user) do
|
||||||
|
Kernel.put_in(
|
||||||
|
data,
|
||||||
|
[:pleroma, :email],
|
||||||
|
user.email
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_email_address(data, _, _), do: data
|
||||||
|
|
||||||
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
||||||
defp image_url(_), do: nil
|
defp image_url(_), do: nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,14 +6,14 @@ defmodule Pleroma.Web.MastodonAPI.CustomEmojiView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web.Endpoint
|
||||||
|
|
||||||
def render("index.json", %{custom_emojis: custom_emojis}) do
|
def render("index.json", %{custom_emojis: custom_emojis}) do
|
||||||
render_many(custom_emojis, __MODULE__, "show.json")
|
render_many(custom_emojis, __MODULE__, "show.json")
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("show.json", %{custom_emoji: {shortcode, %Emoji{file: relative_url, tags: tags}}}) do
|
def render("show.json", %{custom_emoji: {shortcode, %Emoji{file: relative_url, tags: tags}}}) do
|
||||||
url = Web.base_url() |> URI.merge(relative_url) |> to_string()
|
url = Endpoint.url() |> URI.merge(relative_url) |> to_string()
|
||||||
|
|
||||||
%{
|
%{
|
||||||
"shortcode" => shortcode,
|
"shortcode" => shortcode,
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.FollowRequestView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
alias Pleroma.Web.MastodonAPI
|
||||||
|
|
||||||
|
def render(view, opts), do: MastodonAPI.AccountView.render(view, opts)
|
||||||
|
end
|
|
@ -14,7 +14,7 @@ def render("show.json", _) do
|
||||||
instance = Config.get(:instance)
|
instance = Config.get(:instance)
|
||||||
|
|
||||||
%{
|
%{
|
||||||
uri: Pleroma.Web.base_url(),
|
uri: Pleroma.Web.Endpoint.url(),
|
||||||
title: Keyword.get(instance, :name),
|
title: Keyword.get(instance, :name),
|
||||||
description: Keyword.get(instance, :description),
|
description: Keyword.get(instance, :description),
|
||||||
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
|
version: "#{@mastodon_api_level} (compatible; #{Pleroma.Application.named_version()})",
|
||||||
|
@ -24,7 +24,8 @@ def render("show.json", _) do
|
||||||
},
|
},
|
||||||
stats: Pleroma.Stats.get_stats(),
|
stats: Pleroma.Stats.get_stats(),
|
||||||
thumbnail:
|
thumbnail:
|
||||||
URI.merge(Pleroma.Web.base_url(), Keyword.get(instance, :instance_thumbnail)) |> to_string,
|
URI.merge(Pleroma.Web.Endpoint.url(), Keyword.get(instance, :instance_thumbnail))
|
||||||
|
|> to_string,
|
||||||
languages: ["en"],
|
languages: ["en"],
|
||||||
registrations: Keyword.get(instance, :registrations_open),
|
registrations: Keyword.get(instance, :registrations_open),
|
||||||
approval_required: Keyword.get(instance, :account_approval_required),
|
approval_required: Keyword.get(instance, :account_approval_required),
|
||||||
|
@ -35,8 +36,8 @@ def render("show.json", _) do
|
||||||
avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
|
avatar_upload_limit: Keyword.get(instance, :avatar_upload_limit),
|
||||||
background_upload_limit: Keyword.get(instance, :background_upload_limit),
|
background_upload_limit: Keyword.get(instance, :background_upload_limit),
|
||||||
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
banner_upload_limit: Keyword.get(instance, :banner_upload_limit),
|
||||||
background_image: Pleroma.Web.base_url() <> Keyword.get(instance, :background_image),
|
background_image: Pleroma.Web.Endpoint.url() <> Keyword.get(instance, :background_image),
|
||||||
chat_limit: Keyword.get(instance, :chat_limit),
|
shout_limit: Config.get([:shout, :limit]),
|
||||||
description_limit: Keyword.get(instance, :description_limit),
|
description_limit: Keyword.get(instance, :description_limit),
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
metadata: %{
|
metadata: %{
|
||||||
|
@ -68,9 +69,13 @@ def features do
|
||||||
if Config.get([:gopher, :enabled]) do
|
if Config.get([:gopher, :enabled]) do
|
||||||
"gopher"
|
"gopher"
|
||||||
end,
|
end,
|
||||||
if Config.get([:chat, :enabled]) do
|
# backwards compat
|
||||||
|
if Config.get([:shout, :enabled]) do
|
||||||
"chat"
|
"chat"
|
||||||
end,
|
end,
|
||||||
|
if Config.get([:shout, :enabled]) do
|
||||||
|
"shout"
|
||||||
|
end,
|
||||||
if Config.get([:instance, :allow_relay]) do
|
if Config.get([:instance, :allow_relay]) do
|
||||||
"relay"
|
"relay"
|
||||||
end,
|
end,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue