Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop

This commit is contained in:
sadposter 2020-03-03 11:16:59 +00:00
commit 3508f698b9
1599 changed files with 5421 additions and 1407 deletions

2
.gitignore vendored
View file

@ -48,3 +48,5 @@ docs/generated_config.md
.idea
pleroma.iml
# asdf
.tool-versions

View file

@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased]
### Security
- Mastodon API: Fix being able to request enourmous amount of statuses in timelines leading to DoS. Now limited to 40 per request.
### Removed
- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
- **Breaking**: OStatus protocol support
@ -35,6 +38,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Rate limiter is now disabled for localhost/socket (unless remoteip plug is enabled)
- Logger: default log level changed from `warn` to `info`.
- Config mix task `migrate_to_db` truncates `config` table before migrating the config file.
- Default to `prepare: :unnamed` in the database configuration.
<details>
<summary>API Changes</summary>
@ -56,6 +60,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Admin API: Render whole status in grouped reports
- Mastodon API: User timelines will now respect blocks, unless you are getting the user timeline of somebody you blocked (which would be empty otherwise).
- Mastodon API: Favoriting / Repeating a post multiple times will now return the identical response every time. Before, executing that action twice would return an error ("already favorited") on the second try.
- Mastodon API: Limit timeline requests to 3 per timeline per 500ms per user/ip by default.
</details>
### Added
@ -75,6 +80,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- A new users admin digest email
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
- Add an option `authorized_fetch_mode` to require HTTP signatures for AP fetches.
- ActivityPub: support for `replies` collection (output for outgoing federation & fetching on incoming federation).
- Mix task to refresh counter cache (`mix pleroma.refresh_counter_cache`)
<details>
<summary>API Changes</summary>
@ -102,6 +109,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: `feed` option for user atom feed.
- Pleroma API: Add Emoji reactions
- Admin API: Add `/api/pleroma/admin/instances/:instance/statuses` - lists all statuses from a given instance
- Admin API: Add `/api/pleroma/admin/users/:nickname/statuses` - lists all statuses from a given user
- Admin API: `PATCH /api/pleroma/users/confirm_email` to confirm email for multiple users, `PATCH /api/pleroma/users/resend_confirmation_email` to resend confirmation email for multiple users
- ActivityPub: Configurable `type` field of the actors.
- Mastodon API: `/api/v1/accounts/:id` has `source/pleroma/actor_type` field.
@ -116,6 +124,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Configuration: `feed.logo` option for tag feed.
- Tag feed: `/tags/:tag.rss` - list public statuses by hashtag.
- Mastodon API: Add `reacted` property to `emoji_reactions`
- Pleroma API: Add reactions for a single emoji.
- ActivityPub: `[:activitypub, :note_replies_output_limit]` setting sets the number of note self-replies to output on outgoing federation.
- Admin API: `GET /api/pleroma/admin/stats` to get status count by visibility scope
- Admin API: `GET /api/pleroma/admin/statuses` - list all statuses (accepts `godmode` and `local_only`)
</details>
### Fixed

View file

@ -219,6 +219,8 @@
max_expiration: 365 * 24 * 60 * 60
},
registrations_open: true,
invites_enabled: false,
account_activation_required: false,
federating: true,
federation_incoming_replies_max_depth: 100,
federation_reachability_timeout_days: 7,
@ -327,6 +329,7 @@
unfollow_blocked: true,
outgoing_blocks: true,
follow_handshake_timeout: 500,
note_replies_output_limit: 5,
sign_object_fetches: true,
authorized_fetch_mode: false
@ -400,6 +403,8 @@
config :phoenix, :json_library, Jason
config :phoenix, :filter_parameters, ["password", "confirm"]
config :pleroma, :gopher,
enabled: false,
ip: {0, 0, 0, 0},
@ -482,6 +487,7 @@
transmogrifier: 20,
scheduled_activities: 10,
background: 5,
remote_fetcher: 2,
attachments_cleanup: 5,
new_users_digest: 1
],
@ -594,6 +600,7 @@
config :pleroma, :rate_limit,
authentication: {60_000, 15},
timeline: {500, 3},
search: [{1000, 10}, {1000, 30}],
app_account_creation: {1_800_000, 25},
relations_actions: {10_000, 10},
@ -618,6 +625,10 @@
config :pleroma, configurable_from_database: false
config :pleroma, Pleroma.Repo,
parameters: [gin_fuzzy_search_limit: "500"],
prepare: :unnamed
# Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"

View file

@ -662,7 +662,7 @@
label: "Fed. incoming replies max depth",
type: :integer,
description:
"Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while" <>
"Max. depth of reply-to and reply activities fetching on incoming federation, to prevent out-of-memory situations while" <>
" fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes.",
suggestions: [
100
@ -1615,160 +1615,6 @@
}
]
},
%{
group: :pleroma,
key: Pleroma.Web.Endpoint,
type: :group,
description: "Phoenix endpoint configuration",
children: [
%{
key: :http,
label: "HTTP",
type: {:keyword, :integer, :tuple},
description: "http protocol configuration",
suggestions: [
port: 8080,
ip: {127, 0, 0, 1}
],
children: [
%{
key: :dispatch,
type: {:list, :tuple},
description: "dispatch settings",
suggestions: [
{:_,
[
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
{"/websocket", Phoenix.Endpoint.CowboyWebSocket,
{Phoenix.Transports.WebSocket,
{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}},
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
]}
# end copied from config.exs
]
},
%{
key: :ip,
label: "IP",
type: :tuple,
description: "ip",
suggestions: [
{0, 0, 0, 0}
]
},
%{
key: :port,
type: :integer,
description: "port",
suggestions: [
2020
]
}
]
},
%{
key: :url,
label: "URL",
type: {:keyword, :string, :integer},
description: "configuration for generating urls",
suggestions: [
host: "example.com",
port: 2020,
scheme: "https"
],
children: [
%{
key: :host,
type: :string,
description: "Host",
suggestions: [
"example.com"
]
},
%{
key: :port,
type: :integer,
description: "port",
suggestions: [
2020
]
},
%{
key: :scheme,
type: :string,
description: "Scheme",
suggestions: [
"https",
"https"
]
}
]
},
%{
key: :instrumenters,
type: {:list, :module},
suggestions: [Pleroma.Web.Endpoint.Instrumenter]
},
%{
key: :protocol,
type: :string,
suggestions: ["https"]
},
%{
key: :secret_key_base,
type: :string,
suggestions: ["aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl"]
},
%{
key: :signing_salt,
type: :string,
suggestions: ["CqaoopA2"]
},
%{
key: :render_errors,
type: :keyword,
suggestions: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
children: [
%{
key: :view,
type: :module,
suggestions: [Pleroma.Web.ErrorView]
},
%{
key: :accepts,
type: {:list, :string},
suggestions: ["json"]
}
]
},
%{
key: :pubsub,
type: :keyword,
suggestions: [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2],
children: [
%{
key: :name,
type: :module,
suggestions: [Pleroma.PubSub]
},
%{
key: :adapter,
type: :module,
suggestions: [Phoenix.PubSub.PG2]
}
]
},
%{
key: :secure_cookie_flag,
type: :boolean
},
%{
key: :extra_cookie_attrs,
type: {:list, :string},
suggestions: ["SameSite=Lax"]
}
]
},
%{
group: :pleroma,
key: :activitypub,
@ -1790,6 +1636,12 @@
type: :boolean,
description: "Sign object fetches with HTTP signatures"
},
%{
key: :note_replies_output_limit,
type: :integer,
description:
"The number of Note replies' URIs to be included with outgoing federation (`5` to match Mastodon hardcoded value, `0` to disable the output)."
},
%{
key: :follow_handshake_timeout,
type: :integer,
@ -2051,6 +1903,18 @@
suggestions: [50]
}
]
},
%{
key: :crontab,
type: {:list, :tuple},
description: "Settings for cron background jobs",
suggestions: [
{"0 0 * * *", Pleroma.Workers.Cron.ClearOauthTokenWorker},
{"0 * * * *", Pleroma.Workers.Cron.StatsWorker},
{"* * * * *", Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker},
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
]
}
]
},
@ -2588,19 +2452,6 @@
}
]
},
%{
group: :pleroma,
key: :database,
type: :group,
description: "Database related settings",
children: [
%{
key: :rum_enabled,
type: :boolean,
description: "If RUM indexes should be used. Default: disabled"
}
]
},
%{
group: :pleroma,
key: :rate_limit,
@ -2614,6 +2465,12 @@
description: "For the search requests (account & status search etc.)",
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
},
%{
key: :timeline,
type: [:tuple, {:list, :tuple}],
description: "For requests to timelines (each timeline has it's own limiter)",
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
},
%{
key: :app_account_creation,
type: [:tuple, {:list, :tuple}],
@ -2764,20 +2621,6 @@
}
]
},
%{
group: :prometheus,
key: Pleroma.Web.Endpoint.MetricsExporter,
type: :group,
description: "Prometheus settings",
children: [
%{
key: :path,
type: :string,
description: "API endpoint with metrics",
suggestions: ["/api/pleroma/app_metrics"]
}
]
},
%{
group: :http_signatures,
type: :group,
@ -3045,7 +2888,7 @@
group: :pleroma,
key: :feed,
type: :group,
description: "Configure feed rendering.",
description: "Configure feed rendering",
children: [
%{
key: :post_title,
@ -3095,7 +2938,7 @@
group: :pleroma,
key: :modules,
type: :group,
description: "Custom Runtime Modules.",
description: "Custom Runtime Modules",
children: [
%{
key: :runtime_dir,
@ -3106,14 +2949,21 @@
},
%{
group: :pleroma,
key: :streamer,
type: :group,
description: "Allow instance configuration from database.",
description: "Settings for notifications streamer",
children: [
%{
key: :configurable_from_database,
type: :boolean,
description:
"Allow transferring configuration to DB with the subsequent customization from Admin api. Default: disabled"
key: :workers,
type: :integer,
description: "Number of workers to send notifications.",
suggestions: [3]
},
%{
key: :overflow_workers,
type: :integer,
description: "Maximum number of workers created if pool is empty.",
suggestions: [2]
}
]
}

View file

@ -74,11 +74,7 @@
total_user_limit: 3,
enabled: false
config :pleroma, :rate_limit,
search: [{1000, 30}, {1000, 30}],
app_account_creation: {10_000, 5},
password_reset: {1000, 30},
ap_routes: nil
config :pleroma, :rate_limit, %{}
config :pleroma, :http_security, report_uri: "https://endpoint.com"

View file

@ -260,10 +260,24 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
- `nickname` or `id`
- *optional* `page_size`: number of statuses to return (default is `20`)
- *optional* `godmode`: `true`/`false` allows to see private statuses
- *optional* `with_reblogs`: `true`/`false` allows to see reblogs (default is false)
- Response:
- On failure: `Not found`
- On success: JSON array of user's latest statuses
## `GET /api/pleroma/admin/instances/:instance/statuses`
### Retrive instance's latest statuses
- Params:
- `instance`: instance name
- *optional* `page_size`: number of statuses to return (default is `20`)
- *optional* `godmode`: `true`/`false` allows to see private statuses
- *optional* `with_reblogs`: `true`/`false` allows to see reblogs (default is false)
- Response:
- On failure: `Not found`
- On success: JSON array of instance's latest statuses
## `POST /api/pleroma/admin/relay`
### Follow a Relay
@ -939,3 +953,20 @@ Loads json generated from `config/descriptions.exs`.
- Params:
- `nicknames`
- Response: Array of user nicknames
## `GET /api/pleroma/admin/stats`
### Stats
- Response:
```json
{
"status_visibility": {
"direct": 739,
"private": 9,
"public": 17,
"unlisted": 14
}
}
```

View file

@ -459,3 +459,16 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
{"name": "☕", "count": 1, "me": false, "accounts": [{"id" => "abc..."}]}
]
```
## `GET /api/v1/pleroma/statuses/:id/reactions/:emoji`
### Get an object of emoji to account mappings with accounts that reacted to the post for a specific emoji`
* Method: `GET`
* Authentication: optional
* Params: None
* Response: JSON, a list of emoji/account list tuples
* Example Response:
```json
[
{"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
]
```

View file

@ -18,7 +18,9 @@
6. Run `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>`
7. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any.
8. Restart the Pleroma service.
9. After you've restarted Pleroma, you will notice that postgres will take up more cpu resources than usual. A lot in fact. To fix this you must do a VACUUM ANLAYZE. This can also be done while the instance is still running like so:
$ sudo -u postgres psql pleroma_database_name
pleroma=# VACUUM ANALYZE;
[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
## Remove

View file

@ -2,9 +2,11 @@
This is a cheat sheet for Pleroma configuration file, any setting possible to configure should be listed here.
Pleroma configuration works by first importing the base config (`config/config.exs` on source installs, compiled-in on OTP releases), then overriding it by the environment config (`config/$MIX_ENV.exs` on source installs, N/A to OTP releases) and then overriding it by user config (`config/$MIX_ENV.secret.exs` on source installs, typically `/etc/pleroma/config.exs` on OTP releases).
For OTP installations the configuration is typically stored in `/etc/pleroma/config.exs`.
You shouldn't edit the base config directly to avoid breakages and merge conflicts, but it can be used as a reference if you don't understand how an option is supposed to be formatted, the latest version of it can be viewed [here](https://git.pleroma.social/pleroma/pleroma/blob/develop/config/config.exs).
For from source installations Pleroma configuration works by first importing the base config `config/config.exs`, then overriding it by the environment config `config/$MIX_ENV.exs` and then overriding it by user config `config/$MIX_ENV.secret.exs`. In from source installations you should always make the changes to the user config and NEVER to the base config to avoid breakages and merge conflicts. So for production you change/add configuration to `config/prod.secret.exs`.
To add configuration to your config file, you can copy it from the base config. The latest version of it can be viewed [here](https://git.pleroma.social/pleroma/pleroma/blob/develop/config/config.exs). You can also use this file if you don't know how an option is supposed to be formatted.
## :instance
* `name`: The instances name.
@ -150,8 +152,12 @@ config :pleroma, :mrf_user_allowlist,
* `authorized_fetch_mode`: Require HTTP signatures for AP fetches
### :fetch_initial_posts
* `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts
* `pages`: the amount of pages to fetch
!!! warning
Be careful with this setting, fetching posts may lead to new users being discovered whose posts will then also be fetched. This can lead to serious load on your instance and database.
* `enabled`: If enabled, when a new user is discovered by your instance, fetch some of their latest posts.
* `pages`: The amount of pages to fetch
## Pleroma.ScheduledActivity
@ -343,6 +349,7 @@ Means that:
Supported rate limiters:
* `:search` - Account/Status search.
* `:timeline` - Timeline requests (each timeline has it's own limiter).
* `:app_account_creation` - Account registration from the API.
* `:relations_actions` - Following/Unfollowing in general.
* `:relation_id_action` - Following/Unfollowing for a specific user.

View file

@ -0,0 +1,74 @@
# Theming your instance
To add a custom theme to your instance, you'll first need to get a custom theme, upload it to the server, make it available to the instance and eventually you can set it as default.
## Getting a custom theme
### Create your own theme
* You can create your own theme using the Pleroma FE by going to settings (gear on the top right) and choose the Theme tab. Here you have the options to create a personal theme.
* To download your theme, you can do Save preset
* If you want to upload a theme to customise it further, you can upload it using Load preset
This will only save the theme for you personally. To make it available to the whole instance, you'll need to upload it to the server.
### Get an existing theme
* You can download a theme from another instance by going to that instance, go to settings and make sure you have the theme selected that you want. Then you can do Save preset to download it.
* You can also find and download custom themes at <https://plthemes.vulpes.one/>
## Adding the custom theme to the instance
### Upload the theme to the server
Themes can be found in the [static directory](static_dir.md). Create `STATIC-DIR/static/themes/` if needed and copy your theme there. Next you need to add an entry for your theme to `STATIC-DIR/static/styles.json`. If you use a from source installation, you'll first need to copy the file from `priv/static/static/styles.json`.
Example of `styles.json` where we add our own `my-awesome-theme.json`
```json
{
"pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
"pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
"classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
"ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ],
"monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ],
"redmond-xx": "/static/themes/redmond-xx.json",
"redmond-xx-se": "/static/themes/redmond-xx-se.json",
"redmond-xxi": "/static/themes/redmond-xxi.json",
"breezy-dark": "/static/themes/breezy-dark.json",
"breezy-light": "/static/themes/breezy-light.json",
"mammal": "/static/themes/mammal.json",
"my-awesome-theme": "/static/themes/my-awesome-theme.json"
}
```
Now you'll already be able to select the theme in Pleroma FE from the drop-down. You don't need to restart Pleroma because we only changed static served files. You may need to refresh the page in your browser. You'll notice however that the theme doesn't have a name, it's just an empty entry in the drop-down.
### Give the theme a name
When you open one of the themes that ship with Pleroma, you'll notice that the json has a `"name"` key. Add a key-value pair to your theme where the key name is `"name"` and the value the name you want to give your theme. After this you can refresh te page in your browser and the name should be visible in the drop-down.
Example of `my-awesome-theme.json` where we add the name "My Awesome Theme"
```json
{
"_pleroma_theme_version": 2,
"name": "My Awesome Theme",
"theme": {}
}
```
### Set as default theme
Now we can set the new theme as default in the [Pleroma FE configuration](General-tips-for-customizing-Pleroma-FE.md).
Example of adding the new theme in the back-end config files
```elixir
config :pleroma, :frontend_configurations,
pleroma_fe: %{
theme: "my-awesome-theme"
}
```
If you added it in the back-end configuration file, you'll need to restart your instance for the changes to take effect. If you don't see the changes, it's probably because the browser has cached the previous theme. In that case you'll want to clear browser caches. Alternatively you can use a private/incognito window just to see the changes.

View file

@ -123,7 +123,7 @@ In addition to that, replace the existing nginx config's contents with the examp
If not an I2P-only instance, add the nginx config below to your existing config at `/etc/nginx/sites-enabled/pleroma.nginx`.
And for both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself seperately from the clearnet (if your instance is also on the clearnet).
And for both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself separately from the clearnet (if your instance is also on the clearnet).
Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
```
config :pleroma, :http_security,

View file

@ -75,7 +75,7 @@ If not a Tor-only instance,
add the nginx config below to your existing config at `/etc/nginx/sites-enabled/pleroma.nginx`.
---
For both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself seperately from the clearnet (if your instance is also on the clearnet).
For both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself separately from the clearnet (if your instance is also on the clearnet).
Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
```
config :pleroma, :http_security,

View file

@ -73,6 +73,15 @@ rc-service postgresql restart
systemctl restart postgresql
```
If you are using PostgreSQL 12 or higher, add this to your Ecto database configuration
```elixir
prepare: :named,
parameters: [
plan_cache_mode: "force_custom_plan"
]
```
### Installing Pleroma
```sh
# Create a Pleroma user

View file

@ -41,7 +41,7 @@ On the top right you will also see a wrench icon. This opens your personal setti
This is where the interesting stuff happens!
Depending on the timeline you will see different statuses, but each status has a standard structure:
- Profile pic, name and link to profile. An optional left-arrow if it's a reply to another status (hovering will reveal the replied-to status). Clicking on the profile pic will uncollapse the user's profile.
- Profile pic, name and link to profile. An optional left-arrow if it's a reply to another status (hovering will reveal the reply-to status). Clicking on the profile pic will uncollapse the user's profile.
- A `+` button on the right allows you to Expand/Collapse an entire discussion thread. It also updates in realtime!
- An arrow icon allows you to open the status on the instance where it's originating from.
- The text of the status, including mentions and attachements. If you click on a mention, it will automatically open the profile page of that person.

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Config do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Emoji do
@ -186,11 +186,7 @@ def run(["gen-pack", src]) do
tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
{:ok, _} =
:zip.unzip(
binary_archive,
cwd: tmp_pack_dir
)
{:ok, _} = :zip.unzip(binary_archive, cwd: String.to_charlist(tmp_pack_dir))
emoji_map = Pleroma.Emoji.Loader.make_shortcode_to_file_map(tmp_pack_dir, exts)

View file

@ -1,11 +1,13 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Instance do
use Mix.Task
import Mix.Pleroma
alias Pleroma.Config
@shortdoc "Manages Pleroma instance"
@moduledoc File.read!("docs/administration/CLI_tasks/instance.md")
@ -63,7 +65,8 @@ def run(["gen" | rest]) do
get_option(
options,
:instance_name,
"What is the name of your instance? (e.g. Pleroma/Soykaf)"
"What is the name of your instance? (e.g. The Corndog Emporium)",
domain
)
email = get_option(options, :admin_email, "What is your admin email address?")
@ -153,6 +156,8 @@ def run(["gen" | rest]) do
Pleroma.Config.get([:instance, :static_dir])
)
Config.put([:instance, :static_dir], static_dir)
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
jwt_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
@ -202,8 +207,14 @@ def run(["gen" | rest]) do
write_robots_txt(indexable, template_dir)
shell_info(
"\n All files successfully written! Refer to the installation instructions for your platform for next steps"
"\n All files successfully written! Refer to the installation instructions for your platform for next steps."
)
if db_configurable? do
shell_info(
" Please transfer your config to the database after running database migrations. Refer to \"Transfering the config to/from the database\" section of the docs for more information."
)
end
else
shell_error(
"The task would have overwritten the following files:\n" <>

View file

@ -0,0 +1,46 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.RefreshCounterCache do
@shortdoc "Refreshes counter cache"
use Mix.Task
alias Pleroma.Activity
alias Pleroma.CounterCache
alias Pleroma.Repo
require Logger
import Ecto.Query
def run([]) do
Mix.Pleroma.start_pleroma()
["public", "unlisted", "private", "direct"]
|> Enum.each(fn visibility ->
count = status_visibility_count_query(visibility)
name = "status_visibility_#{visibility}"
CounterCache.set(name, count)
Mix.Pleroma.shell_info("Set #{name} to #{count}")
end)
Mix.Pleroma.shell_info("Done")
end
defp status_visibility_count_query(visibility) do
Activity
|> where(
[a],
fragment(
"activity_visibility(?, ?, ?) = ?",
a.actor,
a.recipients,
a.data,
^visibility
)
)
|> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
|> Repo.aggregate(:count, :id, timeout: :timer.minutes(30))
end
end

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.RobotsTxt do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.User do
@ -100,8 +100,7 @@ def run(["rm", nickname]) do
User.perform(:delete, user)
shell_info("User #{nickname} deleted.")
else
_ ->
shell_error("No local user #{nickname}")
_ -> shell_error("No local user #{nickname}")
end
end

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity do
@ -310,7 +310,7 @@ def follow_requests_for_actor(%Pleroma.User{ap_id: ap_id}) do
def restrict_deactivated_users(query) do
deactivated_users =
from(u in User.Query.build(deactivated: true), select: u.ap_id)
from(u in User.Query.build(%{deactivated: true}), select: u.ap_id)
|> Repo.all()
Activity.Queries.exclude_authors(query, deactivated_users)

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.Queries do
@ -7,7 +7,7 @@ defmodule Pleroma.Activity.Queries do
Contains queries for Activity.
"""
import Ecto.Query, only: [from: 2]
import Ecto.Query, only: [from: 2, where: 3]
@type query :: Ecto.Queryable.t() | Activity.t()
@ -30,7 +30,7 @@ def by_actor(query \\ Activity, actor) do
)
end
@spec by_author(query, String.t()) :: query
@spec by_author(query, User.t()) :: query
def by_author(query \\ Activity, %User{ap_id: ap_id}) do
from(a in query, where: a.actor == ^ap_id)
end
@ -63,6 +63,22 @@ def by_object_id(query, object_id) when is_binary(object_id) do
)
end
@spec by_object_in_reply_to_id(query, String.t(), keyword()) :: query
def by_object_in_reply_to_id(query, in_reply_to_id, opts \\ []) do
query =
if opts[:skip_preloading] do
Activity.with_joined_object(query)
else
Activity.with_preloaded_object(query)
end
where(
query,
[activity, object: o],
fragment("(?)->>'inReplyTo' = ?", o.data, ^to_string(in_reply_to_id))
)
end
@spec by_type(query, String.t()) :: query
def by_type(query \\ Activity, activity_type) do
from(

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Activity.Search do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ActivityExpiration do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Application do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha do
@ -50,7 +50,7 @@ def handle_call(:new, _from, state) do
token = new_captcha[:token]
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign")
# Basicallty copy what Phoenix.Token does here, add the time to
# Basically copy what Phoenix.Token does here, add the time to
# the actual data and make it a binary to then encrypt it
encrypted_captcha_answer =
%{
@ -62,7 +62,7 @@ def handle_call(:new, _from, state) do
{
:reply,
# Repalce the answer with the encrypted answer
# Replace the answer with the encrypted answer
%{new_captcha | answer_data: encrypted_captcha_answer},
state
}
@ -82,7 +82,8 @@ def handle_call({:validate, token, captcha, answer_data}, _from, state) do
valid_if_after = DateTime.subtract!(DateTime.now_utc(), seconds_valid)
result =
with {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
with false <- is_nil(answer_data),
{:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
%{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do
try do
if DateTime.before?(at, valid_if_after),

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Captcha.Native do
@ -10,8 +10,8 @@ defmodule Pleroma.Captcha.Native do
@impl Service
def new do
case Captcha.get() do
{:timeout} ->
%{error: dgettext("errors", "Captcha timeout")}
:error ->
%{error: dgettext("errors", "Captcha error")}
{:ok, answer_data, img_binary} ->
%{

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ConfigDB do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Holder do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.Loader do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Config.TransferTask do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Conversation.Participation do
@ -133,10 +133,8 @@ def restrict_recipients(query, user, %{"recipients" => user_ids}) do
[user.id | user_ids]
|> Enum.uniq()
|> Enum.reduce([], fn user_id, acc ->
case FlakeId.Ecto.CompatType.dump(user_id) do
{:ok, user_id} -> [user_id | acc]
_ -> acc
end
{:ok, user_id} = FlakeId.Ecto.CompatType.dump(user_id)
[user_id | acc]
end)
conversation_subquery =

View file

@ -0,0 +1,41 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.CounterCache do
alias Pleroma.CounterCache
alias Pleroma.Repo
use Ecto.Schema
import Ecto.Changeset
import Ecto.Query
schema "counter_cache" do
field(:name, :string)
field(:count, :integer)
end
def changeset(struct, params) do
struct
|> cast(params, [:name, :count])
|> validate_required([:name])
|> unique_constraint(:name)
end
def get_as_map(names) when is_list(names) do
CounterCache
|> where([cc], cc.name in ^names)
|> Repo.all()
|> Enum.group_by(& &1.name, & &1.count)
|> Map.new(fn {k, v} -> {k, hd(v)} end)
end
def set(name, count) do
%CounterCache{}
|> changeset(%{"name" => name, "count" => count})
|> Repo.insert(
on_conflict: [set: [count: count]],
returning: true,
conflict_target: :name
)
end
end

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Emails.AdminEmail do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.FollowingRelationship do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Formatter do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTML do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.MIME do
@ -9,7 +9,7 @@ defmodule Pleroma.MIME do
@default "application/octet-stream"
@read_bytes 35
@spec file_mime_type(String.t()) ::
@spec file_mime_type(String.t(), String.t()) ::
{:ok, content_type :: String.t(), filename :: String.t()} | {:error, any()} | :error
def file_mime_type(path, filename) do
with {:ok, content_type} <- file_mime_type(path),

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Notification do

View file

@ -1,10 +1,13 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Object do
use Ecto.Schema
import Ecto.Query
import Ecto.Changeset
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Object.Fetcher
@ -12,9 +15,6 @@ defmodule Pleroma.Object do
alias Pleroma.Repo
alias Pleroma.User
import Ecto.Query
import Ecto.Changeset
require Logger
@type t() :: %__MODULE__{}
@ -145,18 +145,18 @@ def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}),
# Legacy objects can be mutated by anybody
def authorize_mutation(%Object{}, %User{}), do: true
@spec get_cached_by_ap_id(String.t()) :: Object.t() | nil
def get_cached_by_ap_id(ap_id) do
key = "object:#{ap_id}"
Cachex.fetch!(:object_cache, key, fn _ ->
object = get_by_ap_id(ap_id)
if object do
{:commit, object}
else
{:ignore, object}
end
end)
with {:ok, nil} <- Cachex.get(:object_cache, key),
object when not is_nil(object) <- get_by_ap_id(ap_id),
{:ok, true} <- Cachex.put(:object_cache, key, object) do
object
else
{:ok, object} -> object
nil -> nil
end
end
def context_mapping(context) do
@ -301,4 +301,26 @@ def update_data(%Object{data: data} = object, attrs \\ %{}) do
def local?(%Object{data: %{"id" => id}}) do
String.starts_with?(id, Pleroma.Web.base_url() <> "/")
end
def replies(object, opts \\ []) do
object = Object.normalize(object)
query =
Object
|> where(
[o],
fragment("(?)->>'inReplyTo' = ?", o.data, ^object.data["id"])
)
|> order_by([o], asc: o.id)
if opts[:self_only] do
actor = object.data["actor"]
where(query, [o], fragment("(?)->>'actor' = ?", o.data, ^actor))
else
query
end
end
def self_replies(object, opts \\ []),
do: replies(object, Keyword.put(opts, :self_only, true))
end

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Object.Containment do
@ -39,15 +39,8 @@ def get_actor(%{"actor" => nil, "attributedTo" => actor}) when not is_nil(actor)
defp compare_uris(_, %URI{scheme: "tag"}), do: :ok
end
defp compare_uris(%URI{} = id_uri, %URI{} = other_uri) do
if id_uri.host == other_uri.host do
:ok
else
:error
end
end
defp compare_uris(_, _), do: :error
defp compare_uris(%URI{host: host} = _id_uri, %URI{host: host} = _other_uri), do: :ok
defp compare_uris(_id_uri, _other_uri), do: :error
@doc """
Checks that an imported AP object's actor matches the domain it came from.

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Object.Fetcher do
@ -10,6 +10,7 @@ defmodule Pleroma.Object.Fetcher do
alias Pleroma.Signature
alias Pleroma.Web.ActivityPub.InternalFetchActor
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.Federator
require Logger
require Pleroma.Constants
@ -59,20 +60,23 @@ def refetch_object(%Object{data: %{"id" => id}} = object) do
end
end
# TODO:
# This will create a Create activity, which we need internally at the moment.
# Note: will create a Create activity, which we need internally at the moment.
def fetch_object_from_id(id, options \\ []) do
with {:fetch_object, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
{:fetch, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
{:normalize, nil} <- {:normalize, Object.normalize(data, false)},
with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
{_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])},
{_, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
{_, nil} <- {:normalize, Object.normalize(data, false)},
params <- prepare_activity_params(data),
{:containment, :ok} <- {:containment, Containment.contain_origin(id, params)},
{:transmogrifier, {:ok, activity}} <-
{_, :ok} <- {:containment, Containment.contain_origin(id, params)},
{_, {:ok, activity}} <-
{:transmogrifier, Transmogrifier.handle_incoming(params, options)},
{:object, _data, %Object{} = object} <-
{_, _data, %Object{} = object} <-
{:object, data, Object.normalize(activity, false)} do
{:ok, object}
else
{:allowed_depth, false} ->
{:error, "Max thread distance exceeded."}
{:containment, _} ->
{:error, "Object containment failed."}

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Pagination do
@ -12,11 +12,15 @@ defmodule Pleroma.Pagination do
alias Pleroma.Repo
@type type :: :keyset | :offset
@default_limit 20
@max_limit 40
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
def page_keys, do: @page_keys
@spec fetch_paginated(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
def fetch_paginated(query, params, type \\ :keyset, table_binding \\ nil)
def fetch_paginated(query, %{"total" => true} = params, :keyset, table_binding) do
@ -57,6 +61,7 @@ def fetch_paginated(query, params, :offset, table_binding) do
|> Repo.all()
end
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
def paginate(query, options, :keyset, table_binding) do
@ -130,7 +135,11 @@ defp restrict(query, :offset, %{offset: offset}, _table_binding) do
end
defp restrict(query, :limit, options, _table_binding) do
limit = Map.get(options, :limit, @default_limit)
limit =
case Map.get(options, :limit, @default_limit) do
limit when limit < @max_limit -> limit
_ -> @max_limit
end
query
|> limit(^limit)

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.HTTPSecurityPlug do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.OAuthScopesPlug do

View file

@ -7,8 +7,8 @@ def start_link(init_arg) do
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
def add_limiter(limiter_name, expiration) do
{:ok, _pid} =
def add_or_return_limiter(limiter_name, expiration) do
result =
DynamicSupervisor.start_child(
__MODULE__,
%{
@ -28,6 +28,12 @@ def add_limiter(limiter_name, expiration) do
]}
}
)
case result do
{:ok, _pid} = result -> result
{:error, {:already_started, pid}} -> {:ok, pid}
_ -> result
end
end
@impl true

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.RateLimiter do
@ -7,12 +7,14 @@ defmodule Pleroma.Plugs.RateLimiter do
## Configuration
A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration. The basic configuration is a tuple where:
A keyword list of rate limiters where a key is a limiter name and value is the limiter configuration.
The basic configuration is a tuple where:
* The first element: `scale` (Integer). The time scale in milliseconds.
* The second element: `limit` (Integer). How many requests to limit in the time scale provided.
It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a
list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
To disable a limiter set its value to `nil`.
@ -64,91 +66,102 @@ defmodule Pleroma.Plugs.RateLimiter do
import Pleroma.Web.TranslationHelpers
import Plug.Conn
alias Pleroma.Config
alias Pleroma.Plugs.RateLimiter.LimiterSupervisor
alias Pleroma.User
require Logger
def init(opts) do
limiter_name = Keyword.get(opts, :name)
@doc false
def init(plug_opts) do
plug_opts
end
case Pleroma.Config.get([:rate_limit, limiter_name]) do
nil ->
nil
config ->
name_root = Keyword.get(opts, :bucket_name, limiter_name)
%{
name: name_root,
limits: config,
opts: opts
}
def call(conn, plug_opts) do
if disabled?() do
handle_disabled(conn)
else
action_settings = action_settings(plug_opts)
handle(conn, action_settings)
end
end
# Do not limit if there is no limiter configuration
def call(conn, nil), do: conn
defp handle_disabled(conn) do
if Config.get(:env) == :prod do
Logger.warn("Rate limiter is disabled for localhost/socket")
end
def call(conn, settings) do
case disabled?() do
true ->
if Pleroma.Config.get(:env) == :prod,
do: Logger.warn("Rate limiter is disabled for localhost/socket")
conn
end
defp handle(conn, nil), do: conn
defp handle(conn, action_settings) do
action_settings
|> incorporate_conn_info(conn)
|> check_rate()
|> case do
{:ok, _count} ->
conn
false ->
settings
|> incorporate_conn_info(conn)
|> check_rate()
|> case do
{:ok, _count} ->
conn
{:error, _count} ->
render_throttled_error(conn)
end
{:error, _count} ->
render_throttled_error(conn)
end
end
def disabled? do
localhost_or_socket =
Pleroma.Config.get([Pleroma.Web.Endpoint, :http, :ip])
Config.get([Pleroma.Web.Endpoint, :http, :ip])
|> Tuple.to_list()
|> Enum.join(".")
|> String.match?(~r/^local|^127.0.0.1/)
remote_ip_disabled = not Pleroma.Config.get([Pleroma.Plugs.RemoteIp, :enabled])
remote_ip_disabled = not Config.get([Pleroma.Plugs.RemoteIp, :enabled])
localhost_or_socket and remote_ip_disabled
end
def inspect_bucket(conn, name_root, settings) do
settings =
settings
|> incorporate_conn_info(conn)
@inspect_bucket_not_found {:error, :not_found}
bucket_name = make_bucket_name(%{settings | name: name_root})
key_name = make_key_name(settings)
limit = get_limits(settings)
def inspect_bucket(conn, bucket_name_root, plug_opts) do
with %{name: _} = action_settings <- action_settings(plug_opts) do
action_settings = incorporate_conn_info(action_settings, conn)
bucket_name = make_bucket_name(%{action_settings | name: bucket_name_root})
key_name = make_key_name(action_settings)
limit = get_limits(action_settings)
case Cachex.get(bucket_name, key_name) do
{:error, :no_cache} ->
{:err, :not_found}
case Cachex.get(bucket_name, key_name) do
{:error, :no_cache} ->
@inspect_bucket_not_found
{:ok, nil} ->
{0, limit}
{:ok, nil} ->
{0, limit}
{:ok, value} ->
{value, limit - value}
{:ok, value} ->
{value, limit - value}
end
else
_ -> @inspect_bucket_not_found
end
end
defp check_rate(settings) do
bucket_name = make_bucket_name(settings)
key_name = make_key_name(settings)
limit = get_limits(settings)
def action_settings(plug_opts) do
with limiter_name when is_atom(limiter_name) <- plug_opts[:name],
limits when not is_nil(limits) <- Config.get([:rate_limit, limiter_name]) do
bucket_name_root = Keyword.get(plug_opts, :bucket_name, limiter_name)
%{
name: bucket_name_root,
limits: limits,
opts: plug_opts
}
end
end
defp check_rate(action_settings) do
bucket_name = make_bucket_name(action_settings)
key_name = make_key_name(action_settings)
limit = get_limits(action_settings)
case Cachex.get_and_update(bucket_name, key_name, &increment_value(&1, limit)) do
{:commit, value} ->
@ -158,8 +171,8 @@ defp check_rate(settings) do
{:error, value}
{:error, :no_cache} ->
initialize_buckets(settings)
check_rate(settings)
initialize_buckets!(action_settings)
check_rate(action_settings)
end
end
@ -169,16 +182,19 @@ defp increment_value(val, limit) when val >= limit, do: {:ignore, val}
defp increment_value(val, _limit), do: {:commit, val + 1}
defp incorporate_conn_info(settings, %{assigns: %{user: %User{id: user_id}}, params: params}) do
Map.merge(settings, %{
defp incorporate_conn_info(action_settings, %{
assigns: %{user: %User{id: user_id}},
params: params
}) do
Map.merge(action_settings, %{
mode: :user,
conn_params: params,
conn_info: "#{user_id}"
})
end
defp incorporate_conn_info(settings, %{params: params} = conn) do
Map.merge(settings, %{
defp incorporate_conn_info(action_settings, %{params: params} = conn) do
Map.merge(action_settings, %{
mode: :anon,
conn_params: params,
conn_info: "#{ip(conn)}"
@ -197,10 +213,10 @@ defp render_throttled_error(conn) do
|> halt()
end
defp make_key_name(settings) do
defp make_key_name(action_settings) do
""
|> attach_params(settings)
|> attach_identity(settings)
|> attach_selected_params(action_settings)
|> attach_identity(action_settings)
end
defp get_scale(_, {scale, _}), do: scale
@ -215,28 +231,35 @@ defp get_limits(%{mode: :user, limits: [_, {_, limit}]}), do: limit
defp get_limits(%{limits: [{_, limit}, _]}), do: limit
defp make_bucket_name(%{mode: :user, name: name_root}),
do: user_bucket_name(name_root)
defp make_bucket_name(%{mode: :user, name: bucket_name_root}),
do: user_bucket_name(bucket_name_root)
defp make_bucket_name(%{mode: :anon, name: name_root}),
do: anon_bucket_name(name_root)
defp make_bucket_name(%{mode: :anon, name: bucket_name_root}),
do: anon_bucket_name(bucket_name_root)
defp attach_params(input, %{conn_params: conn_params, opts: opts}) do
param_string =
opts
defp attach_selected_params(input, %{conn_params: conn_params, opts: plug_opts}) do
params_string =
plug_opts
|> Keyword.get(:params, [])
|> Enum.sort()
|> Enum.map(&Map.get(conn_params, &1, ""))
|> Enum.join(":")
"#{input}#{param_string}"
[input, params_string]
|> Enum.join(":")
|> String.replace_leading(":", "")
end
defp initialize_buckets(%{name: _name, limits: nil}), do: :ok
defp initialize_buckets!(%{name: _name, limits: nil}), do: :ok
defp initialize_buckets(%{name: name, limits: limits}) do
LimiterSupervisor.add_limiter(anon_bucket_name(name), get_scale(:anon, limits))
LimiterSupervisor.add_limiter(user_bucket_name(name), get_scale(:user, limits))
defp initialize_buckets!(%{name: name, limits: limits}) do
{:ok, _pid} =
LimiterSupervisor.add_or_return_limiter(anon_bucket_name(name), get_scale(:anon, limits))
{:ok, _pid} =
LimiterSupervisor.add_or_return_limiter(user_bucket_name(name), get_scale(:user, limits))
:ok
end
defp attach_identity(base, %{mode: :user, conn_info: conn_info}),
@ -245,6 +268,6 @@ defp attach_identity(base, %{mode: :user, conn_info: conn_info}),
defp attach_identity(base, %{mode: :anon, conn_info: conn_info}),
do: "ip:#{base}:#{conn_info}"
defp user_bucket_name(name_root), do: "user:#{name_root}" |> String.to_atom()
defp anon_bucket_name(name_root), do: "anon:#{name_root}" |> String.to_atom()
defp user_bucket_name(bucket_name_root), do: "user:#{bucket_name_root}" |> String.to_atom()
defp anon_bucket_name(bucket_name_root), do: "anon:#{bucket_name_root}" |> String.to_atom()
end

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.RemoteIp do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.UserEnabledPlug do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Plugs.UserIsAdminPlug do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Repo do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.ScheduledActivity do

View file

@ -1,9 +1,10 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Stats do
import Ecto.Query
alias Pleroma.CounterCache
alias Pleroma.Repo
alias Pleroma.User
@ -96,4 +97,21 @@ defp get_stat_data do
}
}
end
def get_status_visibility_count do
counter_cache =
CounterCache.get_as_map([
"status_visibility_public",
"status_visibility_private",
"status_visibility_unlisted",
"status_visibility_direct"
])
%{
public: counter_cache["status_visibility_public"] || 0,
unlisted: counter_cache["status_visibility_unlisted"] || 0,
private: counter_cache["status_visibility_private"] || 0,
direct: counter_cache["status_visibility_direct"] || 0
}
end
end

View file

@ -37,6 +37,7 @@ defmodule Pleroma.Upload do
Plug.Upload.t()
| (data_uri_string :: String.t())
| {:from_local, name :: String.t(), id :: String.t(), path :: String.t()}
| map()
@type option ::
{:type, :avatar | :banner | :background}

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.Local do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.S3 do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Uploaders.Uploader do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User do
@ -749,9 +749,18 @@ def invalidate_cache(user) do
Cachex.del(:user_cache, "nickname:#{user.nickname}")
end
@spec get_cached_by_ap_id(String.t()) :: User.t() | nil
def get_cached_by_ap_id(ap_id) do
key = "ap_id:#{ap_id}"
Cachex.fetch!(:user_cache, key, fn _ -> get_by_ap_id(ap_id) end)
with {:ok, nil} <- Cachex.get(:user_cache, key),
user when not is_nil(user) <- get_by_ap_id(ap_id),
{:ok, true} <- Cachex.put(:user_cache, key, user) do
user
else
{:ok, user} -> user
nil -> nil
end
end
def get_cached_by_id(id) do
@ -853,14 +862,14 @@ def get_followers_query(user, page) do
@spec get_followers_query(User.t()) :: Ecto.Query.t()
def get_followers_query(user), do: get_followers_query(user, nil)
@spec get_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
@spec get_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
def get_followers(user, page \\ nil) do
user
|> get_followers_query(page)
|> Repo.all()
end
@spec get_external_followers(User.t(), pos_integer()) :: {:ok, list(User.t())}
@spec get_external_followers(User.t(), pos_integer() | nil) :: {:ok, list(User.t())}
def get_external_followers(user, page \\ nil) do
user
|> get_followers_query(page)
@ -1304,7 +1313,6 @@ def perform(:delete, %User{} = user) do
Repo.delete(user)
end
@spec perform(atom(), User.t()) :: {:ok, User.t()}
def perform(:fetch_initial_posts, %User{} = user) do
pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
@ -1336,7 +1344,6 @@ def perform(:blocks_import, %User{} = blocker, blocked_identifiers)
)
end
@spec perform(atom(), User.t(), list()) :: list() | {:error, any()}
def perform(:follow_import, %User{} = follower, followed_identifiers)
when is_list(followed_identifiers) do
Enum.map(

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Query do
@ -48,7 +48,7 @@ defmodule Pleroma.User.Query do
followers: User.t(),
friends: User.t(),
recipients_from_activity: [String.t()],
nickname: [String.t()],
nickname: [String.t()] | String.t(),
ap_id: [String.t()],
order_by: term(),
select: term(),

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.User.Search do
@ -33,9 +33,15 @@ defp format_query(query_string) do
# Strip the beginning @ off if there is a query
query_string = String.trim_leading(query_string, "@")
with [name, domain] <- String.split(query_string, "@"),
formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") do
name <> "@" <> to_string(:idna.encode(formatted_domain))
with [name, domain] <- String.split(query_string, "@") do
encoded_domain =
domain
|> String.replace(~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "")
|> String.to_charlist()
|> :idna.encode()
|> to_string()
name <> "@" <> encoded_domain
else
_ -> query_string
end

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.UserRelationship do

View file

@ -1,11 +1,12 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.Activity
alias Pleroma.Activity.Ir.Topics
alias Pleroma.Config
alias Pleroma.Constants
alias Pleroma.Conversation
alias Pleroma.Conversation.Participation
alias Pleroma.Notification
@ -124,6 +125,7 @@ def increase_poll_votes_if_vote(%{
def increase_poll_votes_if_vote(_create_data), do: :noop
@spec insert(map(), boolean(), boolean(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
with nil <- Activity.normalize(map),
map <- lazy_put_activity_defaults(map, fake),
@ -231,12 +233,19 @@ def stream_out(_activity) do
:noop
end
def create(%{to: to, actor: actor, context: context, object: object} = params, fake \\ false) do
@spec create(map(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
def create(params, fake \\ false) do
with {:ok, result} <- Repo.transaction(fn -> do_create(params, fake) end) do
result
end
end
defp do_create(%{to: to, actor: actor, context: context, object: object} = params, fake) do
additional = params[:additional] || %{}
# only accept false as false value
local = !(params[:local] == false)
published = params[:published]
quick_insert? = Pleroma.Config.get([:env]) == :benchmark
quick_insert? = Config.get([:env]) == :benchmark
with create_data <-
make_create_data(
@ -259,10 +268,11 @@ def create(%{to: to, actor: actor, context: context, object: object} = params, f
{:ok, activity}
{:error, message} ->
{:error, message}
Repo.rollback(message)
end
end
@spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
def listen(%{to: to, actor: actor, context: context, object: object} = params) do
additional = params[:additional] || %{}
# only accept false as false value
@ -277,20 +287,20 @@ def listen(%{to: to, actor: actor, context: context, object: object} = params) d
{:ok, activity} <- insert(listen_data, local),
:ok <- maybe_federate(activity) do
{:ok, activity}
else
{:error, message} ->
{:error, message}
end
end
@spec accept(map()) :: {:ok, Activity.t()} | {:error, any()}
def accept(params) do
accept_or_reject("Accept", params)
end
@spec reject(map()) :: {:ok, Activity.t()} | {:error, any()}
def reject(params) do
accept_or_reject("Reject", params)
end
@spec accept_or_reject(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()}
def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
local = Map.get(params, :local, true)
activity_id = Map.get(params, :activity_id, nil)
@ -304,6 +314,7 @@ def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
end
end
@spec update(map()) :: {:ok, Activity.t()} | {:error, any()}
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
local = !(params[:local] == false)
activity_id = params[:activity_id]
@ -322,7 +333,16 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
end
end
@spec react_with_emoji(User.t(), Object.t(), String.t(), keyword()) ::
{:ok, Activity.t(), Object.t()} | {:error, any()}
def react_with_emoji(user, object, emoji, options \\ []) do
with {:ok, result} <-
Repo.transaction(fn -> do_react_with_emoji(user, object, emoji, options) end) do
result
end
end
defp do_react_with_emoji(user, object, emoji, options) do
with local <- Keyword.get(options, :local, true),
activity_id <- Keyword.get(options, :activity_id, nil),
true <- Pleroma.Emoji.is_unicode_emoji?(emoji),
@ -332,11 +352,21 @@ def react_with_emoji(user, object, emoji, options \\ []) do
:ok <- maybe_federate(activity) do
{:ok, activity, object}
else
e -> {:error, e}
false -> {:error, false}
{:error, error} -> Repo.rollback(error)
end
end
@spec unreact_with_emoji(User.t(), String.t(), keyword()) ::
{:ok, Activity.t(), Object.t()} | {:error, any()}
def unreact_with_emoji(user, reaction_id, options \\ []) do
with {:ok, result} <-
Repo.transaction(fn -> do_unreact_with_emoji(user, reaction_id, options) end) do
result
end
end
defp do_unreact_with_emoji(user, reaction_id, options) do
with local <- Keyword.get(options, :local, true),
activity_id <- Keyword.get(options, :activity_id, nil),
user_ap_id <- user.ap_id,
@ -348,17 +378,25 @@ def unreact_with_emoji(user, reaction_id, options \\ []) do
:ok <- maybe_federate(activity) do
{:ok, activity, object}
else
e -> {:error, e}
{:error, error} -> Repo.rollback(error)
end
end
# TODO: This is weird, maybe we shouldn't check here if we can make the activity.
def like(
%User{ap_id: ap_id} = user,
%Object{data: %{"id" => _}} = object,
activity_id \\ nil,
local \\ true
) do
@spec like(User.t(), Object.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t(), Object.t()} | {:error, any()}
def like(user, object, activity_id \\ nil, local \\ true) do
with {:ok, result} <- Repo.transaction(fn -> do_like(user, object, activity_id, local) end) do
result
end
end
defp do_like(
%User{ap_id: ap_id} = user,
%Object{data: %{"id" => _}} = object,
activity_id,
local
) do
with nil <- get_existing_like(ap_id, object),
like_data <- make_like_data(user, object, activity_id),
{:ok, activity} <- insert(like_data, local),
@ -366,12 +404,24 @@ def like(
:ok <- maybe_federate(activity) do
{:ok, activity, object}
else
%Activity{} = activity -> {:ok, activity, object}
error -> {:error, error}
%Activity{} = activity ->
{:ok, activity, object}
{:error, error} ->
Repo.rollback(error)
end
end
@spec unlike(User.t(), Object.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t(), Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ true) do
with {:ok, result} <-
Repo.transaction(fn -> do_unlike(actor, object, activity_id, local) end) do
result
end
end
defp do_unlike(actor, object, activity_id, local) do
with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
unlike_data <- make_unlike_data(actor, like_activity, activity_id),
{:ok, unlike_activity} <- insert(unlike_data, local),
@ -380,10 +430,13 @@ def unlike(%User{} = actor, %Object{} = object, activity_id \\ nil, local \\ tru
:ok <- maybe_federate(unlike_activity) do
{:ok, unlike_activity, like_activity, object}
else
_e -> {:ok, object}
nil -> {:ok, object}
{:error, error} -> Repo.rollback(error)
end
end
@spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
{:ok, Activity.t(), Object.t()} | {:error, any()}
def announce(
%User{ap_id: _} = user,
%Object{data: %{"id" => _}} = object,
@ -391,6 +444,13 @@ def announce(
local \\ true,
public \\ true
) do
with {:ok, result} <-
Repo.transaction(fn -> do_announce(user, object, activity_id, local, public) end) do
result
end
end
defp do_announce(user, object, activity_id, local, public) do
with true <- is_announceable?(object, user, public),
announce_data <- make_announce_data(user, object, activity_id, public),
{:ok, activity} <- insert(announce_data, local),
@ -398,16 +458,26 @@ def announce(
:ok <- maybe_federate(activity) do
{:ok, activity, object}
else
error -> {:error, error}
false -> {:error, false}
{:error, error} -> Repo.rollback(error)
end
end
@spec unannounce(User.t(), Object.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
def unannounce(
%User{} = actor,
%Object{} = object,
activity_id \\ nil,
local \\ true
) do
with {:ok, result} <-
Repo.transaction(fn -> do_unannounce(actor, object, activity_id, local) end) do
result
end
end
defp do_unannounce(actor, object, activity_id, local) do
with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
{:ok, unannounce_activity} <- insert(unannounce_data, local),
@ -416,30 +486,61 @@ def unannounce(
{:ok, object} <- remove_announce_from_object(announce_activity, object) do
{:ok, unannounce_activity, object}
else
_e -> {:ok, object}
nil -> {:ok, object}
{:error, error} -> Repo.rollback(error)
end
end
@spec follow(User.t(), User.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t()} | {:error, any()}
def follow(follower, followed, activity_id \\ nil, local \\ true) do
with {:ok, result} <-
Repo.transaction(fn -> do_follow(follower, followed, activity_id, local) end) do
result
end
end
defp do_follow(follower, followed, activity_id, local) do
with data <- make_follow_data(follower, followed, activity_id),
{:ok, activity} <- insert(data, local),
:ok <- maybe_federate(activity),
_ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
{:ok, activity}
else
{:error, error} -> Repo.rollback(error)
end
end
@spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t()} | nil | {:error, any()}
def unfollow(follower, followed, activity_id \\ nil, local \\ true) do
with {:ok, result} <-
Repo.transaction(fn -> do_unfollow(follower, followed, activity_id, local) end) do
result
end
end
defp do_unfollow(follower, followed, activity_id, local) do
with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
{:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
{:ok, activity} <- insert(unfollow_data, local),
:ok <- maybe_federate(activity) do
{:ok, activity}
else
nil -> nil
{:error, error} -> Repo.rollback(error)
end
end
def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
@spec delete(User.t() | Object.t(), keyword()) :: {:ok, User.t() | Object.t()} | {:error, any()}
def delete(entity, options \\ []) do
with {:ok, result} <- Repo.transaction(fn -> do_delete(entity, options) end) do
result
end
end
defp do_delete(%User{ap_id: ap_id, follower_address: follower_address} = user, _) do
with data <- %{
"to" => [follower_address],
"type" => "Delete",
@ -452,7 +553,7 @@ def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
end
end
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ []) do
defp do_delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options) do
local = Keyword.get(options, :local, true)
activity_id = Keyword.get(options, :activity_id, nil)
actor = Keyword.get(options, :actor, actor)
@ -477,11 +578,22 @@ def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, options \\ [
{:ok, _actor} <- decrease_note_count_if_public(user, object),
:ok <- maybe_federate(activity) do
{:ok, activity}
else
{:error, error} ->
Repo.rollback(error)
end
end
@spec block(User.t(), User.t(), String.t() | nil, boolean) :: {:ok, Activity.t() | nil}
@spec block(User.t(), User.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t()} | {:error, any()}
def block(blocker, blocked, activity_id \\ nil, local \\ true) do
with {:ok, result} <-
Repo.transaction(fn -> do_block(blocker, blocked, activity_id, local) end) do
result
end
end
defp do_block(blocker, blocked, activity_id, local) do
outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
@ -496,20 +608,32 @@ def block(blocker, blocked, activity_id \\ nil, local \\ true) do
:ok <- maybe_federate(activity) do
{:ok, activity}
else
_e -> {:ok, nil}
{:error, error} -> Repo.rollback(error)
end
end
@spec unblock(User.t(), User.t(), String.t() | nil, boolean()) ::
{:ok, Activity.t()} | {:error, any()} | nil
def unblock(blocker, blocked, activity_id \\ nil, local \\ true) do
with {:ok, result} <-
Repo.transaction(fn -> do_unblock(blocker, blocked, activity_id, local) end) do
result
end
end
defp do_unblock(blocker, blocked, activity_id, local) do
with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
{:ok, activity} <- insert(unblock_data, local),
:ok <- maybe_federate(activity) do
{:ok, activity}
else
nil -> nil
{:error, error} -> Repo.rollback(error)
end
end
@spec flag(map()) :: {:ok, Activity.t()} | any
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
def flag(
%{
actor: actor,
@ -546,6 +670,7 @@ def flag(
end
end
@spec move(User.t(), User.t(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
def move(%User{} = origin, %User{} = target, local \\ true) do
params = %{
"type" => "Move",
@ -571,7 +696,7 @@ def move(%User{} = origin, %User{} = target, local \\ true) do
end
defp fetch_activities_for_context_query(context, opts) do
public = [Pleroma.Constants.as_public()]
public = [Constants.as_public()]
recipients =
if opts["user"],
@ -616,10 +741,11 @@ def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
|> Repo.one()
end
@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
opts = Map.drop(opts, ["user"])
[Pleroma.Constants.as_public()]
[Constants.as_public()]
|> fetch_activities_query(opts)
|> restrict_unlisted()
|> Pagination.fetch_paginated(opts, pagination)
@ -770,13 +896,18 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
|> Enum.reverse()
end
def fetch_instance_activities(params) do
def fetch_statuses(reading_user, params) do
params =
params
|> Map.put("type", ["Create", "Announce"])
|> Map.put("instance", params["instance"])
fetch_activities([Pleroma.Constants.as_public()], params, :offset)
recipients =
user_activities_recipients(%{
"godmode" => params["godmode"],
"reading_user" => reading_user
})
fetch_activities(recipients, params, :offset)
|> Enum.reverse()
end
@ -786,9 +917,9 @@ defp user_activities_recipients(%{"godmode" => true}) do
defp user_activities_recipients(%{"reading_user" => reading_user}) do
if reading_user do
[Pleroma.Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
[Constants.as_public()] ++ [reading_user.ap_id | User.following(reading_user)]
else
[Pleroma.Constants.as_public()]
[Constants.as_public()]
end
end
@ -995,7 +1126,7 @@ defp restrict_unlisted(query) do
fragment(
"not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
activity.data,
^[Pleroma.Constants.as_public()]
^[Constants.as_public()]
)
)
end
@ -1169,7 +1300,7 @@ def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
@doc """
Fetch favorites activities of user with order by sort adds to favorites
"""
@spec fetch_favourites(User.t(), map(), atom()) :: list(Activity.t())
@spec fetch_favourites(User.t(), map(), Pagination.type()) :: list(Activity.t())
def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
user.ap_id
|> Activity.Queries.by_actor()
@ -1207,7 +1338,7 @@ def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
where:
fragment("? && ?", activity.recipients, ^recipients) or
(fragment("? && ?", activity.recipients, ^recipients_with_public) and
^Pleroma.Constants.as_public() in activity.recipients)
^Constants.as_public() in activity.recipients)
)
end
@ -1223,6 +1354,7 @@ def fetch_activities_bounded(
|> Enum.reverse()
end
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
def upload(file, opts \\ []) do
with {:ok, data} <- Upload.store(file, opts) do
obj_data =
@ -1320,8 +1452,7 @@ defp normalize_counter(counter) when is_integer(counter), do: counter
defp normalize_counter(_), do: 0
defp maybe_update_follow_information(data) do
with {:enabled, true} <-
{:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
{:ok, info} <- fetch_follow_information_for_user(data) do
info = Map.merge(data[:info] || %{}, info)
Map.put(data, :info, info)

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Publisher do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Relay do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Transmogrifier do
@ -156,10 +156,11 @@ def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options)
when not is_nil(in_reply_to) do
in_reply_to_id = prepare_in_reply_to(in_reply_to)
object = Map.put(object, "inReplyToAtomUri", in_reply_to_id)
depth = (options[:depth] || 0) + 1
if Federator.allowed_incoming_reply_depth?(options[:depth]) do
if Federator.allowed_thread_distance?(depth) do
with {:ok, replied_object} <- get_obj_helper(in_reply_to_id, options),
%Activity{} = _ <- Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
%Activity{} <- Activity.get_create_by_object_ap_id(replied_object.data["id"]) do
object
|> Map.put("inReplyTo", replied_object.data["id"])
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
@ -312,7 +313,7 @@ def fix_type(object, options \\ [])
def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options)
when is_binary(reply_id) do
with true <- Federator.allowed_incoming_reply_depth?(options[:depth]),
with true <- Federator.allowed_thread_distance?(options[:depth]),
{:ok, %{data: %{"type" => "Question"} = _} = _} <- get_obj_helper(reply_id, options) do
Map.put(object, "type", "Answer")
else
@ -406,8 +407,7 @@ def handle_incoming(
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
object = fix_object(data["object"], options)
object = fix_object(object, options)
params = %{
to: data["to"],
@ -424,7 +424,20 @@ def handle_incoming(
])
}
ActivityPub.create(params)
with {:ok, created_activity} <- ActivityPub.create(params) do
reply_depth = (options[:depth] || 0) + 1
if Federator.allowed_thread_distance?(reply_depth) do
for reply_id <- replies(object) do
Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{
"id" => reply_id,
"depth" => reply_depth
})
end
end
{:ok, created_activity}
end
else
%Activity{} = activity -> {:ok, activity}
_e -> :error
@ -442,7 +455,8 @@ def handle_incoming(
|> fix_addressing
with {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
options = Keyword.put(options, :depth, (options[:depth] || 0) + 1)
reply_depth = (options[:depth] || 0) + 1
options = Keyword.put(options, :depth, reply_depth)
object = fix_object(object, options)
params = %{
@ -903,6 +917,50 @@ def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) when is_binary(in_r
def set_reply_to_uri(obj), do: obj
@doc """
Serialized Mastodon-compatible `replies` collection containing _self-replies_.
Based on Mastodon's ActivityPub::NoteSerializer#replies.
"""
def set_replies(obj_data) do
replies_uris =
with limit when limit > 0 <-
Pleroma.Config.get([:activitypub, :note_replies_output_limit], 0),
%Object{} = object <- Object.get_cached_by_ap_id(obj_data["id"]) do
object
|> Object.self_replies()
|> select([o], fragment("?->>'id'", o.data))
|> limit(^limit)
|> Repo.all()
else
_ -> []
end
set_replies(obj_data, replies_uris)
end
defp set_replies(obj, []) do
obj
end
defp set_replies(obj, replies_uris) do
replies_collection = %{
"type" => "Collection",
"items" => replies_uris
}
Map.merge(obj, %{"replies" => replies_collection})
end
def replies(%{"replies" => %{"first" => %{"items" => items}}}) when not is_nil(items) do
items
end
def replies(%{"replies" => %{"items" => items}}) when not is_nil(items) do
items
end
def replies(_), do: []
# Prepares the object of an outgoing create activity.
def prepare_object(object) do
object
@ -914,6 +972,7 @@ def prepare_object(object) do
|> prepare_attachments
|> set_conversation
|> set_reply_to_uri
|> set_replies
|> strip_internal_fields
|> strip_internal_tags
|> set_type

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.Utils do
@ -45,8 +45,8 @@ def normalize_params(params) do
Map.put(params, "actor", get_ap_id(params["actor"]))
end
@spec determine_explicit_mentions(map()) :: map()
def determine_explicit_mentions(%{"tag" => tag} = _) when is_list(tag) do
@spec determine_explicit_mentions(map()) :: [any]
def determine_explicit_mentions(%{"tag" => tag}) when is_list(tag) do
Enum.flat_map(tag, fn
%{"type" => "Mention", "href" => href} -> [href]
_ -> []
@ -427,7 +427,7 @@ defp fetch_likes(object) do
@doc """
Updates a follow activity's state (for locked accounts).
"""
@spec update_follow_state_for_all(Activity.t(), String.t()) :: {:ok, Activity} | {:error, any()}
@spec update_follow_state_for_all(Activity.t(), String.t()) :: {:ok, Activity | nil}
def update_follow_state_for_all(
%Activity{data: %{"actor" => actor, "object" => object}} = activity,
state

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
@ -13,6 +13,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
alias Pleroma.ModerationLog
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.ReportNote
alias Pleroma.Stats
alias Pleroma.User
alias Pleroma.UserInviteToken
alias Pleroma.Web.ActivityPub.ActivityPub
@ -98,7 +99,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
plug(
OAuthScopesPlug,
%{scopes: ["read"], admin: true}
when action in [:config_show, :list_log]
when action in [:config_show, :list_log, :stats]
)
plug(
@ -243,13 +244,15 @@ def user_show(conn, %{"nickname" => nickname}) do
end
def list_instance_statuses(conn, %{"instance" => instance} = params) do
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
{page, page_size} = page_params(params)
activities =
ActivityPub.fetch_instance_activities(%{
ActivityPub.fetch_statuses(nil, %{
"instance" => instance,
"limit" => page_size,
"offset" => (page - 1) * page_size
"offset" => (page - 1) * page_size,
"exclude_reblogs" => !with_reblogs && "true"
})
conn
@ -258,6 +261,7 @@ def list_instance_statuses(conn, %{"instance" => instance} = params) do
end
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
godmode = params["godmode"] == "true" || params["godmode"] == true
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
@ -266,7 +270,8 @@ def list_user_statuses(conn, %{"nickname" => nickname} = params) do
activities =
ActivityPub.fetch_user_activities(user, nil, %{
"limit" => page_size,
"godmode" => godmode
"godmode" => godmode,
"exclude_reblogs" => !with_reblogs && "true"
})
conn
@ -740,6 +745,24 @@ def report_notes_delete(%{assigns: %{user: user}} = conn, %{
end
end
def list_statuses(%{assigns: %{user: admin}} = conn, params) do
godmode = params["godmode"] == "true" || params["godmode"] == true
local_only = params["local_only"] == "true" || params["local_only"] == true
{page, page_size} = page_params(params)
activities =
ActivityPub.fetch_statuses(admin, %{
"godmode" => godmode,
"local_only" => local_only,
"limit" => page_size,
"offset" => (page - 1) * page_size
})
conn
|> put_view(Pleroma.Web.AdminAPI.StatusView)
|> render("index.json", %{activities: activities, as: :activity})
end
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
{:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
@ -953,6 +976,13 @@ def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" =
conn |> json("")
end
def stats(conn, _) do
count = Stats.get_status_visibility_count()
conn
|> json(%{"status_visibility" => count})
end
def errors(conn, {:error, :not_found}) do
conn
|> put_status(:not_found)

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.Search do
@ -18,7 +18,11 @@ defmacro not_empty_string(string) do
@spec user(map()) :: {:ok, [User.t()], pos_integer()}
def user(params \\ %{}) do
query = User.Query.build(params) |> order_by([u], u.nickname)
query =
params
|> Map.drop([:page, :page_size])
|> User.Query.build()
|> order_by([u], u.nickname)
paginated_query =
User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size)

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.ConfigView do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.AdminAPI.StatusView do
@ -10,7 +10,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
alias Pleroma.User
def render("index.json", opts) do
render_many(opts.activities, __MODULE__, "show.json", opts)
safe_render_many(opts.activities, __MODULE__, "show.json", opts)
end
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPI do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.CommonAPI.Utils do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ControllerHelper do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Endpoint do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Federator do
@ -15,13 +15,19 @@ defmodule Pleroma.Web.Federator do
require Logger
@doc "Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161)"
@doc """
Returns `true` if the distance to target object does not exceed max configured value.
Serves to prevent fetching of very long threads, especially useful on smaller instances.
Addresses [memory leaks on recursive replies fetching](https://git.pleroma.social/pleroma/pleroma/issues/161).
Applies to fetching of both ancestor (reply-to) and child (reply) objects.
"""
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
def allowed_incoming_reply_depth?(depth) do
max_replies_depth = Pleroma.Config.get([:instance, :federation_incoming_replies_max_depth])
def allowed_thread_distance?(distance) do
max_distance = Pleroma.Config.get([:instance, :federation_incoming_replies_max_depth])
if max_replies_depth do
(depth || 1) <= max_replies_depth
if max_distance && max_distance >= 0 do
# Default depth is 0 (an object has zero distance from itself in its thread)
(distance || 0) <= max_distance
else
true
end

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Feed.FeedView do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Feed.TagController do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.Feed.UserController do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastoFEController do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.NotificationController do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.SearchController do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.StatusController do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.SubscriptionController do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.SuggestionController do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.TimelineController do
@ -10,9 +10,20 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
alias Pleroma.Pagination
alias Pleroma.Plugs.OAuthScopesPlug
alias Pleroma.Plugs.RateLimiter
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ActivityPub
# TODO: Replace with a macro when there is a Phoenix release with
# https://github.com/phoenixframework/phoenix/commit/2e8c63c01fec4dde5467dbbbf9705ff9e780735e
# in it
plug(RateLimiter, [name: :timeline, bucket_name: :direct_timeline] when action == :direct)
plug(RateLimiter, [name: :timeline, bucket_name: :public_timeline] when action == :public)
plug(RateLimiter, [name: :timeline, bucket_name: :home_timeline] when action == :home)
plug(RateLimiter, [name: :timeline, bucket_name: :hashtag_timeline] when action == :hashtag)
plug(RateLimiter, [name: :timeline, bucket_name: :list_timeline] when action == :list)
plug(OAuthScopesPlug, %{scopes: ["read:statuses"]} when action in [:home, :direct])
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.MastodonAPI do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AccountView do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.AppView do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.NotificationView do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.PollView do

View file

@ -1,5 +1,5 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MastodonAPI.StatusView do

Some files were not shown because too many files have changed in this diff Show more