forked from AkkomaGang/akkoma
Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
3508f698b9
1599 changed files with 5443 additions and 1437 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -48,3 +48,5 @@ docs/generated_config.md
|
||||||
.idea
|
.idea
|
||||||
pleroma.iml
|
pleroma.iml
|
||||||
|
|
||||||
|
# asdf
|
||||||
|
.tool-versions
|
||||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [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
|
### Removed
|
||||||
- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
|
- **Breaking**: Removed 1.0+ deprecated configurations `Pleroma.Upload, :strip_exif` and `:instance, :dedupe_media`
|
||||||
- **Breaking**: OStatus protocol support
|
- **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)
|
- Rate limiter is now disabled for localhost/socket (unless remoteip plug is enabled)
|
||||||
- Logger: default log level changed from `warn` to `info`.
|
- Logger: default log level changed from `warn` to `info`.
|
||||||
- Config mix task `migrate_to_db` truncates `config` table before migrating the config file.
|
- Config mix task `migrate_to_db` truncates `config` table before migrating the config file.
|
||||||
|
- Default to `prepare: :unnamed` in the database configuration.
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<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
|
- 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: 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: 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>
|
</details>
|
||||||
|
|
||||||
### Added
|
### 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
|
- A new users admin digest email
|
||||||
- OAuth: admin scopes support (relevant setting: `[:auth, :enforce_oauth_admin_scope_usage]`).
|
- 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.
|
- 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>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<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.
|
- Configuration: `feed` option for user atom feed.
|
||||||
- Pleroma API: Add Emoji reactions
|
- 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/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
|
- 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.
|
- ActivityPub: Configurable `type` field of the actors.
|
||||||
- Mastodon API: `/api/v1/accounts/:id` has `source/pleroma/actor_type` field.
|
- 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.
|
- Configuration: `feed.logo` option for tag feed.
|
||||||
- Tag feed: `/tags/:tag.rss` - list public statuses by hashtag.
|
- Tag feed: `/tags/:tag.rss` - list public statuses by hashtag.
|
||||||
- Mastodon API: Add `reacted` property to `emoji_reactions`
|
- 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>
|
</details>
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
|
@ -219,6 +219,8 @@
|
||||||
max_expiration: 365 * 24 * 60 * 60
|
max_expiration: 365 * 24 * 60 * 60
|
||||||
},
|
},
|
||||||
registrations_open: true,
|
registrations_open: true,
|
||||||
|
invites_enabled: false,
|
||||||
|
account_activation_required: false,
|
||||||
federating: true,
|
federating: true,
|
||||||
federation_incoming_replies_max_depth: 100,
|
federation_incoming_replies_max_depth: 100,
|
||||||
federation_reachability_timeout_days: 7,
|
federation_reachability_timeout_days: 7,
|
||||||
|
@ -327,6 +329,7 @@
|
||||||
unfollow_blocked: true,
|
unfollow_blocked: true,
|
||||||
outgoing_blocks: true,
|
outgoing_blocks: true,
|
||||||
follow_handshake_timeout: 500,
|
follow_handshake_timeout: 500,
|
||||||
|
note_replies_output_limit: 5,
|
||||||
sign_object_fetches: true,
|
sign_object_fetches: true,
|
||||||
authorized_fetch_mode: false
|
authorized_fetch_mode: false
|
||||||
|
|
||||||
|
@ -400,6 +403,8 @@
|
||||||
|
|
||||||
config :phoenix, :json_library, Jason
|
config :phoenix, :json_library, Jason
|
||||||
|
|
||||||
|
config :phoenix, :filter_parameters, ["password", "confirm"]
|
||||||
|
|
||||||
config :pleroma, :gopher,
|
config :pleroma, :gopher,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
ip: {0, 0, 0, 0},
|
ip: {0, 0, 0, 0},
|
||||||
|
@ -482,6 +487,7 @@
|
||||||
transmogrifier: 20,
|
transmogrifier: 20,
|
||||||
scheduled_activities: 10,
|
scheduled_activities: 10,
|
||||||
background: 5,
|
background: 5,
|
||||||
|
remote_fetcher: 2,
|
||||||
attachments_cleanup: 5,
|
attachments_cleanup: 5,
|
||||||
new_users_digest: 1
|
new_users_digest: 1
|
||||||
],
|
],
|
||||||
|
@ -594,6 +600,7 @@
|
||||||
|
|
||||||
config :pleroma, :rate_limit,
|
config :pleroma, :rate_limit,
|
||||||
authentication: {60_000, 15},
|
authentication: {60_000, 15},
|
||||||
|
timeline: {500, 3},
|
||||||
search: [{1000, 10}, {1000, 30}],
|
search: [{1000, 10}, {1000, 30}],
|
||||||
app_account_creation: {1_800_000, 25},
|
app_account_creation: {1_800_000, 25},
|
||||||
relations_actions: {10_000, 10},
|
relations_actions: {10_000, 10},
|
||||||
|
@ -618,6 +625,10 @@
|
||||||
|
|
||||||
config :pleroma, configurable_from_database: false
|
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
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
import_config "#{Mix.env()}.exs"
|
import_config "#{Mix.env()}.exs"
|
||||||
|
|
|
@ -662,7 +662,7 @@
|
||||||
label: "Fed. incoming replies max depth",
|
label: "Fed. incoming replies max depth",
|
||||||
type: :integer,
|
type: :integer,
|
||||||
description:
|
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.",
|
" 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: [
|
suggestions: [
|
||||||
100
|
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,
|
group: :pleroma,
|
||||||
key: :activitypub,
|
key: :activitypub,
|
||||||
|
@ -1790,6 +1636,12 @@
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
description: "Sign object fetches with HTTP signatures"
|
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,
|
key: :follow_handshake_timeout,
|
||||||
type: :integer,
|
type: :integer,
|
||||||
|
@ -2051,6 +1903,18 @@
|
||||||
suggestions: [50]
|
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,
|
group: :pleroma,
|
||||||
key: :rate_limit,
|
key: :rate_limit,
|
||||||
|
@ -2614,6 +2465,12 @@
|
||||||
description: "For the search requests (account & status search etc.)",
|
description: "For the search requests (account & status search etc.)",
|
||||||
suggestions: [{1000, 10}, [{10_000, 10}, {10_000, 50}]]
|
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,
|
key: :app_account_creation,
|
||||||
type: [:tuple, {:list, :tuple}],
|
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,
|
group: :http_signatures,
|
||||||
type: :group,
|
type: :group,
|
||||||
|
@ -3045,7 +2888,7 @@
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: :feed,
|
key: :feed,
|
||||||
type: :group,
|
type: :group,
|
||||||
description: "Configure feed rendering.",
|
description: "Configure feed rendering",
|
||||||
children: [
|
children: [
|
||||||
%{
|
%{
|
||||||
key: :post_title,
|
key: :post_title,
|
||||||
|
@ -3095,7 +2938,7 @@
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: :modules,
|
key: :modules,
|
||||||
type: :group,
|
type: :group,
|
||||||
description: "Custom Runtime Modules.",
|
description: "Custom Runtime Modules",
|
||||||
children: [
|
children: [
|
||||||
%{
|
%{
|
||||||
key: :runtime_dir,
|
key: :runtime_dir,
|
||||||
|
@ -3106,14 +2949,21 @@
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
|
key: :streamer,
|
||||||
type: :group,
|
type: :group,
|
||||||
description: "Allow instance configuration from database.",
|
description: "Settings for notifications streamer",
|
||||||
children: [
|
children: [
|
||||||
%{
|
%{
|
||||||
key: :configurable_from_database,
|
key: :workers,
|
||||||
type: :boolean,
|
type: :integer,
|
||||||
description:
|
description: "Number of workers to send notifications.",
|
||||||
"Allow transferring configuration to DB with the subsequent customization from Admin api. Default: disabled"
|
suggestions: [3]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :overflow_workers,
|
||||||
|
type: :integer,
|
||||||
|
description: "Maximum number of workers created if pool is empty.",
|
||||||
|
suggestions: [2]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,11 +74,7 @@
|
||||||
total_user_limit: 3,
|
total_user_limit: 3,
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
config :pleroma, :rate_limit,
|
config :pleroma, :rate_limit, %{}
|
||||||
search: [{1000, 30}, {1000, 30}],
|
|
||||||
app_account_creation: {10_000, 5},
|
|
||||||
password_reset: {1000, 30},
|
|
||||||
ap_routes: nil
|
|
||||||
|
|
||||||
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
||||||
|
|
||||||
|
|
|
@ -260,10 +260,24 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- `nickname` or `id`
|
- `nickname` or `id`
|
||||||
- *optional* `page_size`: number of statuses to return (default is `20`)
|
- *optional* `page_size`: number of statuses to return (default is `20`)
|
||||||
- *optional* `godmode`: `true`/`false` – allows to see private statuses
|
- *optional* `godmode`: `true`/`false` – allows to see private statuses
|
||||||
|
- *optional* `with_reblogs`: `true`/`false` – allows to see reblogs (default is false)
|
||||||
- Response:
|
- Response:
|
||||||
- On failure: `Not found`
|
- On failure: `Not found`
|
||||||
- On success: JSON array of user's latest statuses
|
- 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`
|
## `POST /api/pleroma/admin/relay`
|
||||||
|
|
||||||
### Follow a Relay
|
### Follow a Relay
|
||||||
|
@ -939,3 +953,20 @@ Loads json generated from `config/descriptions.exs`.
|
||||||
- Params:
|
- Params:
|
||||||
- `nicknames`
|
- `nicknames`
|
||||||
- Response: Array of user nicknames
|
- Response: Array of user nicknames
|
||||||
|
|
||||||
|
## `GET /api/pleroma/admin/stats`
|
||||||
|
|
||||||
|
### Stats
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status_visibility": {
|
||||||
|
"direct": 739,
|
||||||
|
"private": 9,
|
||||||
|
"public": 17,
|
||||||
|
"unlisted": 14
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -459,3 +459,16 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
|
||||||
{"name": "☕", "count": 1, "me": false, "accounts": [{"id" => "abc..."}]}
|
{"name": "☕", "count": 1, "me": false, "accounts": [{"id" => "abc..."}]}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `GET /api/v1/pleroma/statuses/:id/reactions/:emoji`
|
||||||
|
### Get an object of emoji to account mappings with accounts that reacted to the post for a specific emoji`
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: optional
|
||||||
|
* Params: None
|
||||||
|
* Response: JSON, a list of emoji/account list tuples
|
||||||
|
* Example Response:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
6. Run `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>`
|
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.
|
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.
|
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.
|
[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
|
||||||
|
|
||||||
## Remove
|
## Remove
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
This is a cheat sheet for Pleroma configuration file, any setting possible to configure should be listed here.
|
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
|
## :instance
|
||||||
* `name`: The instance’s name.
|
* `name`: The instance’s name.
|
||||||
|
@ -150,8 +152,12 @@ config :pleroma, :mrf_user_allowlist,
|
||||||
* `authorized_fetch_mode`: Require HTTP signatures for AP fetches
|
* `authorized_fetch_mode`: Require HTTP signatures for AP fetches
|
||||||
|
|
||||||
### :fetch_initial_posts
|
### :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
|
## Pleroma.ScheduledActivity
|
||||||
|
|
||||||
|
@ -343,6 +349,7 @@ Means that:
|
||||||
Supported rate limiters:
|
Supported rate limiters:
|
||||||
|
|
||||||
* `:search` - Account/Status search.
|
* `:search` - Account/Status search.
|
||||||
|
* `:timeline` - Timeline requests (each timeline has it's own limiter).
|
||||||
* `:app_account_creation` - Account registration from the API.
|
* `:app_account_creation` - Account registration from the API.
|
||||||
* `:relations_actions` - Following/Unfollowing in general.
|
* `:relations_actions` - Following/Unfollowing in general.
|
||||||
* `:relation_id_action` - Following/Unfollowing for a specific user.
|
* `:relation_id_action` - Following/Unfollowing for a specific user.
|
||||||
|
|
74
docs/configuration/howto_theming_your_instance.md
Normal file
74
docs/configuration/howto_theming_your_instance.md
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# Theming your instance
|
||||||
|
|
||||||
|
To add a custom theme to your instance, you'll first need to get a custom theme, upload it to the server, make it available to the instance and eventually you can set it as default.
|
||||||
|
|
||||||
|
## Getting a custom theme
|
||||||
|
|
||||||
|
### Create your own theme
|
||||||
|
|
||||||
|
* You can create your own theme using the Pleroma FE by going to settings (gear on the top right) and choose the Theme tab. Here you have the options to create a personal theme.
|
||||||
|
* To download your theme, you can do Save preset
|
||||||
|
* If you want to upload a theme to customise it further, you can upload it using Load preset
|
||||||
|
|
||||||
|
This will only save the theme for you personally. To make it available to the whole instance, you'll need to upload it to the server.
|
||||||
|
|
||||||
|
### Get an existing theme
|
||||||
|
|
||||||
|
* You can download a theme from another instance by going to that instance, go to settings and make sure you have the theme selected that you want. Then you can do Save preset to download it.
|
||||||
|
* You can also find and download custom themes at <https://plthemes.vulpes.one/>
|
||||||
|
|
||||||
|
## Adding the custom theme to the instance
|
||||||
|
|
||||||
|
### Upload the theme to the server
|
||||||
|
|
||||||
|
Themes can be found in the [static directory](static_dir.md). Create `STATIC-DIR/static/themes/` if needed and copy your theme there. Next you need to add an entry for your theme to `STATIC-DIR/static/styles.json`. If you use a from source installation, you'll first need to copy the file from `priv/static/static/styles.json`.
|
||||||
|
|
||||||
|
Example of `styles.json` where we add our own `my-awesome-theme.json`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||||
|
"pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||||
|
"classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||||
|
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
|
||||||
|
"ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ],
|
||||||
|
"monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ],
|
||||||
|
|
||||||
|
"redmond-xx": "/static/themes/redmond-xx.json",
|
||||||
|
"redmond-xx-se": "/static/themes/redmond-xx-se.json",
|
||||||
|
"redmond-xxi": "/static/themes/redmond-xxi.json",
|
||||||
|
"breezy-dark": "/static/themes/breezy-dark.json",
|
||||||
|
"breezy-light": "/static/themes/breezy-light.json",
|
||||||
|
"mammal": "/static/themes/mammal.json",
|
||||||
|
"my-awesome-theme": "/static/themes/my-awesome-theme.json"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you'll already be able to select the theme in Pleroma FE from the drop-down. You don't need to restart Pleroma because we only changed static served files. You may need to refresh the page in your browser. You'll notice however that the theme doesn't have a name, it's just an empty entry in the drop-down.
|
||||||
|
|
||||||
|
### Give the theme a name
|
||||||
|
|
||||||
|
When you open one of the themes that ship with Pleroma, you'll notice that the json has a `"name"` key. Add a key-value pair to your theme where the key name is `"name"` and the value the name you want to give your theme. After this you can refresh te page in your browser and the name should be visible in the drop-down.
|
||||||
|
|
||||||
|
Example of `my-awesome-theme.json` where we add the name "My Awesome Theme"
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"_pleroma_theme_version": 2,
|
||||||
|
"name": "My Awesome Theme",
|
||||||
|
"theme": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Set as default theme
|
||||||
|
|
||||||
|
Now we can set the new theme as default in the [Pleroma FE configuration](General-tips-for-customizing-Pleroma-FE.md).
|
||||||
|
|
||||||
|
Example of adding the new theme in the back-end config files
|
||||||
|
```elixir
|
||||||
|
config :pleroma, :frontend_configurations,
|
||||||
|
pleroma_fe: %{
|
||||||
|
theme: "my-awesome-theme"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If you added it in the back-end configuration file, you'll need to restart your instance for the changes to take effect. If you don't see the changes, it's probably because the browser has cached the previous theme. In that case you'll want to clear browser caches. Alternatively you can use a private/incognito window just to see the changes.
|
||||||
|
|
|
@ -123,7 +123,7 @@ In addition to that, replace the existing nginx config's contents with the examp
|
||||||
|
|
||||||
If not an I2P-only instance, add the nginx config below to your existing config at `/etc/nginx/sites-enabled/pleroma.nginx`.
|
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/):
|
Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
|
||||||
```
|
```
|
||||||
config :pleroma, :http_security,
|
config :pleroma, :http_security,
|
||||||
|
|
|
@ -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`.
|
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/):
|
Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
|
||||||
```
|
```
|
||||||
config :pleroma, :http_security,
|
config :pleroma, :http_security,
|
||||||
|
|
|
@ -73,6 +73,15 @@ rc-service postgresql restart
|
||||||
systemctl restart postgresql
|
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
|
### Installing Pleroma
|
||||||
```sh
|
```sh
|
||||||
# Create a Pleroma user
|
# Create a Pleroma user
|
||||||
|
|
|
@ -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!
|
This is where the interesting stuff happens!
|
||||||
Depending on the timeline you will see different statuses, but each status has a standard structure:
|
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!
|
- 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.
|
- 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.
|
- The text of the status, including mentions and attachements. If you click on a mention, it will automatically open the profile page of that person.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.Config do
|
defmodule Mix.Tasks.Pleroma.Config do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.Emoji do
|
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}")
|
tmp_pack_dir = Path.join(System.tmp_dir!(), "emoji-pack-#{name}")
|
||||||
|
|
||||||
{:ok, _} =
|
{:ok, _} = :zip.unzip(binary_archive, cwd: String.to_charlist(tmp_pack_dir))
|
||||||
:zip.unzip(
|
|
||||||
binary_archive,
|
|
||||||
cwd: tmp_pack_dir
|
|
||||||
)
|
|
||||||
|
|
||||||
emoji_map = Pleroma.Emoji.Loader.make_shortcode_to_file_map(tmp_pack_dir, exts)
|
emoji_map = Pleroma.Emoji.Loader.make_shortcode_to_file_map(tmp_pack_dir, exts)
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.Instance do
|
defmodule Mix.Tasks.Pleroma.Instance do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
import Mix.Pleroma
|
import Mix.Pleroma
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
|
||||||
@shortdoc "Manages Pleroma instance"
|
@shortdoc "Manages Pleroma instance"
|
||||||
@moduledoc File.read!("docs/administration/CLI_tasks/instance.md")
|
@moduledoc File.read!("docs/administration/CLI_tasks/instance.md")
|
||||||
|
|
||||||
|
@ -63,7 +65,8 @@ def run(["gen" | rest]) do
|
||||||
get_option(
|
get_option(
|
||||||
options,
|
options,
|
||||||
:instance_name,
|
: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?")
|
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])
|
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)
|
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)
|
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)
|
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)
|
write_robots_txt(indexable, template_dir)
|
||||||
|
|
||||||
shell_info(
|
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
|
else
|
||||||
shell_error(
|
shell_error(
|
||||||
"The task would have overwritten the following files:\n" <>
|
"The task would have overwritten the following files:\n" <>
|
||||||
|
|
46
lib/mix/tasks/pleroma/refresh_counter_cache.ex
Normal file
46
lib/mix/tasks/pleroma/refresh_counter_cache.ex
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.RefreshCounterCache do
|
||||||
|
@shortdoc "Refreshes counter cache"
|
||||||
|
|
||||||
|
use Mix.Task
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.CounterCache
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
def run([]) do
|
||||||
|
Mix.Pleroma.start_pleroma()
|
||||||
|
|
||||||
|
["public", "unlisted", "private", "direct"]
|
||||||
|
|> Enum.each(fn visibility ->
|
||||||
|
count = status_visibility_count_query(visibility)
|
||||||
|
name = "status_visibility_#{visibility}"
|
||||||
|
CounterCache.set(name, count)
|
||||||
|
Mix.Pleroma.shell_info("Set #{name} to #{count}")
|
||||||
|
end)
|
||||||
|
|
||||||
|
Mix.Pleroma.shell_info("Done")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp status_visibility_count_query(visibility) do
|
||||||
|
Activity
|
||||||
|
|> where(
|
||||||
|
[a],
|
||||||
|
fragment(
|
||||||
|
"activity_visibility(?, ?, ?) = ?",
|
||||||
|
a.actor,
|
||||||
|
a.recipients,
|
||||||
|
a.data,
|
||||||
|
^visibility
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data))
|
||||||
|
|> Repo.aggregate(:count, :id, timeout: :timer.minutes(30))
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.RobotsTxt do
|
defmodule Mix.Tasks.Pleroma.RobotsTxt do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Mix.Tasks.Pleroma.User do
|
defmodule Mix.Tasks.Pleroma.User do
|
||||||
|
@ -100,8 +100,7 @@ def run(["rm", nickname]) do
|
||||||
User.perform(:delete, user)
|
User.perform(:delete, user)
|
||||||
shell_info("User #{nickname} deleted.")
|
shell_info("User #{nickname} deleted.")
|
||||||
else
|
else
|
||||||
_ ->
|
_ -> shell_error("No local user #{nickname}")
|
||||||
shell_error("No local user #{nickname}")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Activity do
|
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
|
def restrict_deactivated_users(query) do
|
||||||
deactivated_users =
|
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()
|
|> Repo.all()
|
||||||
|
|
||||||
Activity.Queries.exclude_authors(query, deactivated_users)
|
Activity.Queries.exclude_authors(query, deactivated_users)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Activity.Queries do
|
defmodule Pleroma.Activity.Queries do
|
||||||
|
@ -7,7 +7,7 @@ defmodule Pleroma.Activity.Queries do
|
||||||
Contains queries for Activity.
|
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()
|
@type query :: Ecto.Queryable.t() | Activity.t()
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ def by_actor(query \\ Activity, actor) do
|
||||||
)
|
)
|
||||||
end
|
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
|
def by_author(query \\ Activity, %User{ap_id: ap_id}) do
|
||||||
from(a in query, where: a.actor == ^ap_id)
|
from(a in query, where: a.actor == ^ap_id)
|
||||||
end
|
end
|
||||||
|
@ -63,6 +63,22 @@ def by_object_id(query, object_id) when is_binary(object_id) do
|
||||||
)
|
)
|
||||||
end
|
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
|
@spec by_type(query, String.t()) :: query
|
||||||
def by_type(query \\ Activity, activity_type) do
|
def by_type(query \\ Activity, activity_type) do
|
||||||
from(
|
from(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Activity.Search do
|
defmodule Pleroma.Activity.Search do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.ActivityExpiration do
|
defmodule Pleroma.ActivityExpiration do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Application do
|
defmodule Pleroma.Application do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Captcha do
|
defmodule Pleroma.Captcha do
|
||||||
|
@ -50,7 +50,7 @@ def handle_call(:new, _from, state) do
|
||||||
token = new_captcha[:token]
|
token = new_captcha[:token]
|
||||||
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
|
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
|
||||||
sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign")
|
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
|
# the actual data and make it a binary to then encrypt it
|
||||||
encrypted_captcha_answer =
|
encrypted_captcha_answer =
|
||||||
%{
|
%{
|
||||||
|
@ -62,7 +62,7 @@ def handle_call(:new, _from, state) do
|
||||||
|
|
||||||
{
|
{
|
||||||
:reply,
|
:reply,
|
||||||
# Repalce the answer with the encrypted answer
|
# Replace the answer with the encrypted answer
|
||||||
%{new_captcha | answer_data: encrypted_captcha_answer},
|
%{new_captcha | answer_data: encrypted_captcha_answer},
|
||||||
state
|
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)
|
valid_if_after = DateTime.subtract!(DateTime.now_utc(), seconds_valid)
|
||||||
|
|
||||||
result =
|
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
|
%{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do
|
||||||
try do
|
try do
|
||||||
if DateTime.before?(at, valid_if_after),
|
if DateTime.before?(at, valid_if_after),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Captcha.Native do
|
defmodule Pleroma.Captcha.Native do
|
||||||
|
@ -10,8 +10,8 @@ defmodule Pleroma.Captcha.Native do
|
||||||
@impl Service
|
@impl Service
|
||||||
def new do
|
def new do
|
||||||
case Captcha.get() do
|
case Captcha.get() do
|
||||||
{:timeout} ->
|
:error ->
|
||||||
%{error: dgettext("errors", "Captcha timeout")}
|
%{error: dgettext("errors", "Captcha error")}
|
||||||
|
|
||||||
{:ok, answer_data, img_binary} ->
|
{:ok, answer_data, img_binary} ->
|
||||||
%{
|
%{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.ConfigDB do
|
defmodule Pleroma.ConfigDB do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Config.Holder do
|
defmodule Pleroma.Config.Holder do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Config.Loader do
|
defmodule Pleroma.Config.Loader do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Config.TransferTask do
|
defmodule Pleroma.Config.TransferTask do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Conversation.Participation do
|
defmodule Pleroma.Conversation.Participation do
|
||||||
|
@ -133,10 +133,8 @@ def restrict_recipients(query, user, %{"recipients" => user_ids}) do
|
||||||
[user.id | user_ids]
|
[user.id | user_ids]
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|> Enum.reduce([], fn user_id, acc ->
|
|> Enum.reduce([], fn user_id, acc ->
|
||||||
case FlakeId.Ecto.CompatType.dump(user_id) do
|
{:ok, user_id} = FlakeId.Ecto.CompatType.dump(user_id)
|
||||||
{:ok, user_id} -> [user_id | acc]
|
[user_id | acc]
|
||||||
_ -> acc
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
conversation_subquery =
|
conversation_subquery =
|
||||||
|
|
41
lib/pleroma/counter_cache.ex
Normal file
41
lib/pleroma/counter_cache.ex
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.CounterCache do
|
||||||
|
alias Pleroma.CounterCache
|
||||||
|
alias Pleroma.Repo
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
schema "counter_cache" do
|
||||||
|
field(:name, :string)
|
||||||
|
field(:count, :integer)
|
||||||
|
end
|
||||||
|
|
||||||
|
def changeset(struct, params) do
|
||||||
|
struct
|
||||||
|
|> cast(params, [:name, :count])
|
||||||
|
|> validate_required([:name])
|
||||||
|
|> unique_constraint(:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_as_map(names) when is_list(names) do
|
||||||
|
CounterCache
|
||||||
|
|> where([cc], cc.name in ^names)
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.group_by(& &1.name, & &1.count)
|
||||||
|
|> Map.new(fn {k, v} -> {k, hd(v)} end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set(name, count) do
|
||||||
|
%CounterCache{}
|
||||||
|
|> changeset(%{"name" => name, "count" => count})
|
||||||
|
|> Repo.insert(
|
||||||
|
on_conflict: [set: [count: count]],
|
||||||
|
returning: true,
|
||||||
|
conflict_target: :name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Emails.AdminEmail do
|
defmodule Pleroma.Emails.AdminEmail do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.FollowingRelationship do
|
defmodule Pleroma.FollowingRelationship do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Formatter do
|
defmodule Pleroma.Formatter do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.HTML do
|
defmodule Pleroma.HTML do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.MIME do
|
defmodule Pleroma.MIME do
|
||||||
|
@ -9,7 +9,7 @@ defmodule Pleroma.MIME do
|
||||||
@default "application/octet-stream"
|
@default "application/octet-stream"
|
||||||
@read_bytes 35
|
@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
|
{:ok, content_type :: String.t(), filename :: String.t()} | {:error, any()} | :error
|
||||||
def file_mime_type(path, filename) do
|
def file_mime_type(path, filename) do
|
||||||
with {:ok, content_type} <- file_mime_type(path),
|
with {:ok, content_type} <- file_mime_type(path),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Notification do
|
defmodule Pleroma.Notification do
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Object do
|
defmodule Pleroma.Object do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Object.Fetcher
|
alias Pleroma.Object.Fetcher
|
||||||
|
@ -12,9 +15,6 @@ defmodule Pleroma.Object do
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
import Ecto.Query
|
|
||||||
import Ecto.Changeset
|
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@type t() :: %__MODULE__{}
|
@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
|
# Legacy objects can be mutated by anybody
|
||||||
def authorize_mutation(%Object{}, %User{}), do: true
|
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
|
def get_cached_by_ap_id(ap_id) do
|
||||||
key = "object:#{ap_id}"
|
key = "object:#{ap_id}"
|
||||||
|
|
||||||
Cachex.fetch!(:object_cache, key, fn _ ->
|
with {:ok, nil} <- Cachex.get(:object_cache, key),
|
||||||
object = get_by_ap_id(ap_id)
|
object when not is_nil(object) <- get_by_ap_id(ap_id),
|
||||||
|
{:ok, true} <- Cachex.put(:object_cache, key, object) do
|
||||||
if object do
|
object
|
||||||
{:commit, object}
|
else
|
||||||
else
|
{:ok, object} -> object
|
||||||
{:ignore, object}
|
nil -> nil
|
||||||
end
|
end
|
||||||
end)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def context_mapping(context) do
|
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
|
def local?(%Object{data: %{"id" => id}}) do
|
||||||
String.starts_with?(id, Pleroma.Web.base_url() <> "/")
|
String.starts_with?(id, Pleroma.Web.base_url() <> "/")
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Object.Containment do
|
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
|
defp compare_uris(_, %URI{scheme: "tag"}), do: :ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp compare_uris(%URI{} = id_uri, %URI{} = other_uri) do
|
defp compare_uris(%URI{host: host} = _id_uri, %URI{host: host} = _other_uri), do: :ok
|
||||||
if id_uri.host == other_uri.host do
|
defp compare_uris(_id_uri, _other_uri), do: :error
|
||||||
:ok
|
|
||||||
else
|
|
||||||
:error
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp compare_uris(_, _), do: :error
|
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Checks that an imported AP object's actor matches the domain it came from.
|
Checks that an imported AP object's actor matches the domain it came from.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Object.Fetcher do
|
defmodule Pleroma.Object.Fetcher do
|
||||||
|
@ -10,6 +10,7 @@ defmodule Pleroma.Object.Fetcher do
|
||||||
alias Pleroma.Signature
|
alias Pleroma.Signature
|
||||||
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
alias Pleroma.Web.ActivityPub.InternalFetchActor
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.Federator
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
@ -59,20 +60,23 @@ def refetch_object(%Object{data: %{"id" => id}} = object) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO:
|
# Note: will create a Create activity, which we need internally at the moment.
|
||||||
# This will create a Create activity, which we need internally at the moment.
|
|
||||||
def fetch_object_from_id(id, options \\ []) do
|
def fetch_object_from_id(id, options \\ []) do
|
||||||
with {:fetch_object, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
|
with {_, nil} <- {:fetch_object, Object.get_cached_by_ap_id(id)},
|
||||||
{:fetch, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
|
{_, true} <- {:allowed_depth, Federator.allowed_thread_distance?(options[:depth])},
|
||||||
{:normalize, nil} <- {:normalize, Object.normalize(data, false)},
|
{_, {:ok, data}} <- {:fetch, fetch_and_contain_remote_object_from_id(id)},
|
||||||
|
{_, nil} <- {:normalize, Object.normalize(data, false)},
|
||||||
params <- prepare_activity_params(data),
|
params <- prepare_activity_params(data),
|
||||||
{:containment, :ok} <- {:containment, Containment.contain_origin(id, params)},
|
{_, :ok} <- {:containment, Containment.contain_origin(id, params)},
|
||||||
{:transmogrifier, {:ok, activity}} <-
|
{_, {:ok, activity}} <-
|
||||||
{:transmogrifier, Transmogrifier.handle_incoming(params, options)},
|
{:transmogrifier, Transmogrifier.handle_incoming(params, options)},
|
||||||
{:object, _data, %Object{} = object} <-
|
{_, _data, %Object{} = object} <-
|
||||||
{:object, data, Object.normalize(activity, false)} do
|
{:object, data, Object.normalize(activity, false)} do
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
else
|
else
|
||||||
|
{:allowed_depth, false} ->
|
||||||
|
{:error, "Max thread distance exceeded."}
|
||||||
|
|
||||||
{:containment, _} ->
|
{:containment, _} ->
|
||||||
{:error, "Object containment failed."}
|
{:error, "Object containment failed."}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Pagination do
|
defmodule Pleroma.Pagination do
|
||||||
|
@ -12,11 +12,15 @@ defmodule Pleroma.Pagination do
|
||||||
|
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
@type type :: :keyset | :offset
|
||||||
|
|
||||||
@default_limit 20
|
@default_limit 20
|
||||||
|
@max_limit 40
|
||||||
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
||||||
|
|
||||||
def page_keys, do: @page_keys
|
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, params, type \\ :keyset, table_binding \\ nil)
|
||||||
|
|
||||||
def fetch_paginated(query, %{"total" => true} = params, :keyset, table_binding) do
|
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()
|
|> Repo.all()
|
||||||
end
|
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, method \\ :keyset, table_binding \\ nil)
|
||||||
|
|
||||||
def paginate(query, options, :keyset, table_binding) do
|
def paginate(query, options, :keyset, table_binding) do
|
||||||
|
@ -130,7 +135,11 @@ defp restrict(query, :offset, %{offset: offset}, _table_binding) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict(query, :limit, options, _table_binding) do
|
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
|
query
|
||||||
|> limit(^limit)
|
|> limit(^limit)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.HTTPSecurityPlug do
|
defmodule Pleroma.Plugs.HTTPSecurityPlug do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.OAuthScopesPlug do
|
defmodule Pleroma.Plugs.OAuthScopesPlug do
|
||||||
|
|
|
@ -7,8 +7,8 @@ def start_link(init_arg) do
|
||||||
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
|
DynamicSupervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_limiter(limiter_name, expiration) do
|
def add_or_return_limiter(limiter_name, expiration) do
|
||||||
{:ok, _pid} =
|
result =
|
||||||
DynamicSupervisor.start_child(
|
DynamicSupervisor.start_child(
|
||||||
__MODULE__,
|
__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
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.RateLimiter do
|
defmodule Pleroma.Plugs.RateLimiter do
|
||||||
|
@ -7,12 +7,14 @@ defmodule Pleroma.Plugs.RateLimiter do
|
||||||
|
|
||||||
## Configuration
|
## 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 first element: `scale` (Integer). The time scale in milliseconds.
|
||||||
* The second element: `limit` (Integer). How many requests to limit in the time scale provided.
|
* 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`.
|
To disable a limiter set its value to `nil`.
|
||||||
|
|
||||||
|
@ -64,91 +66,102 @@ defmodule Pleroma.Plugs.RateLimiter do
|
||||||
import Pleroma.Web.TranslationHelpers
|
import Pleroma.Web.TranslationHelpers
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.Plugs.RateLimiter.LimiterSupervisor
|
alias Pleroma.Plugs.RateLimiter.LimiterSupervisor
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def init(opts) do
|
@doc false
|
||||||
limiter_name = Keyword.get(opts, :name)
|
def init(plug_opts) do
|
||||||
|
plug_opts
|
||||||
|
end
|
||||||
|
|
||||||
case Pleroma.Config.get([:rate_limit, limiter_name]) do
|
def call(conn, plug_opts) do
|
||||||
nil ->
|
if disabled?() do
|
||||||
nil
|
handle_disabled(conn)
|
||||||
|
else
|
||||||
config ->
|
action_settings = action_settings(plug_opts)
|
||||||
name_root = Keyword.get(opts, :bucket_name, limiter_name)
|
handle(conn, action_settings)
|
||||||
|
|
||||||
%{
|
|
||||||
name: name_root,
|
|
||||||
limits: config,
|
|
||||||
opts: opts
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Do not limit if there is no limiter configuration
|
defp handle_disabled(conn) do
|
||||||
def call(conn, nil), do: conn
|
if Config.get(:env) == :prod do
|
||||||
|
Logger.warn("Rate limiter is disabled for localhost/socket")
|
||||||
|
end
|
||||||
|
|
||||||
def call(conn, settings) do
|
conn
|
||||||
case disabled?() do
|
end
|
||||||
true ->
|
|
||||||
if Pleroma.Config.get(:env) == :prod,
|
|
||||||
do: Logger.warn("Rate limiter is disabled for localhost/socket")
|
|
||||||
|
|
||||||
|
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
|
conn
|
||||||
|
|
||||||
false ->
|
{:error, _count} ->
|
||||||
settings
|
render_throttled_error(conn)
|
||||||
|> incorporate_conn_info(conn)
|
|
||||||
|> check_rate()
|
|
||||||
|> case do
|
|
||||||
{:ok, _count} ->
|
|
||||||
conn
|
|
||||||
|
|
||||||
{:error, _count} ->
|
|
||||||
render_throttled_error(conn)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def disabled? do
|
def disabled? do
|
||||||
localhost_or_socket =
|
localhost_or_socket =
|
||||||
Pleroma.Config.get([Pleroma.Web.Endpoint, :http, :ip])
|
Config.get([Pleroma.Web.Endpoint, :http, :ip])
|
||||||
|> Tuple.to_list()
|
|> Tuple.to_list()
|
||||||
|> Enum.join(".")
|
|> Enum.join(".")
|
||||||
|> String.match?(~r/^local|^127.0.0.1/)
|
|> 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
|
localhost_or_socket and remote_ip_disabled
|
||||||
end
|
end
|
||||||
|
|
||||||
def inspect_bucket(conn, name_root, settings) do
|
@inspect_bucket_not_found {:error, :not_found}
|
||||||
settings =
|
|
||||||
settings
|
|
||||||
|> incorporate_conn_info(conn)
|
|
||||||
|
|
||||||
bucket_name = make_bucket_name(%{settings | name: name_root})
|
def inspect_bucket(conn, bucket_name_root, plug_opts) do
|
||||||
key_name = make_key_name(settings)
|
with %{name: _} = action_settings <- action_settings(plug_opts) do
|
||||||
limit = get_limits(settings)
|
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
|
case Cachex.get(bucket_name, key_name) do
|
||||||
{:error, :no_cache} ->
|
{:error, :no_cache} ->
|
||||||
{:err, :not_found}
|
@inspect_bucket_not_found
|
||||||
|
|
||||||
{:ok, nil} ->
|
{:ok, nil} ->
|
||||||
{0, limit}
|
{0, limit}
|
||||||
|
|
||||||
{:ok, value} ->
|
{:ok, value} ->
|
||||||
{value, limit - value}
|
{value, limit - value}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
_ -> @inspect_bucket_not_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp check_rate(settings) do
|
def action_settings(plug_opts) do
|
||||||
bucket_name = make_bucket_name(settings)
|
with limiter_name when is_atom(limiter_name) <- plug_opts[:name],
|
||||||
key_name = make_key_name(settings)
|
limits when not is_nil(limits) <- Config.get([:rate_limit, limiter_name]) do
|
||||||
limit = get_limits(settings)
|
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
|
case Cachex.get_and_update(bucket_name, key_name, &increment_value(&1, limit)) do
|
||||||
{:commit, value} ->
|
{:commit, value} ->
|
||||||
|
@ -158,8 +171,8 @@ defp check_rate(settings) do
|
||||||
{:error, value}
|
{:error, value}
|
||||||
|
|
||||||
{:error, :no_cache} ->
|
{:error, :no_cache} ->
|
||||||
initialize_buckets(settings)
|
initialize_buckets!(action_settings)
|
||||||
check_rate(settings)
|
check_rate(action_settings)
|
||||||
end
|
end
|
||||||
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 increment_value(val, _limit), do: {:commit, val + 1}
|
||||||
|
|
||||||
defp incorporate_conn_info(settings, %{assigns: %{user: %User{id: user_id}}, params: params}) do
|
defp incorporate_conn_info(action_settings, %{
|
||||||
Map.merge(settings, %{
|
assigns: %{user: %User{id: user_id}},
|
||||||
|
params: params
|
||||||
|
}) do
|
||||||
|
Map.merge(action_settings, %{
|
||||||
mode: :user,
|
mode: :user,
|
||||||
conn_params: params,
|
conn_params: params,
|
||||||
conn_info: "#{user_id}"
|
conn_info: "#{user_id}"
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
defp incorporate_conn_info(settings, %{params: params} = conn) do
|
defp incorporate_conn_info(action_settings, %{params: params} = conn) do
|
||||||
Map.merge(settings, %{
|
Map.merge(action_settings, %{
|
||||||
mode: :anon,
|
mode: :anon,
|
||||||
conn_params: params,
|
conn_params: params,
|
||||||
conn_info: "#{ip(conn)}"
|
conn_info: "#{ip(conn)}"
|
||||||
|
@ -197,10 +213,10 @@ defp render_throttled_error(conn) do
|
||||||
|> halt()
|
|> halt()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp make_key_name(settings) do
|
defp make_key_name(action_settings) do
|
||||||
""
|
""
|
||||||
|> attach_params(settings)
|
|> attach_selected_params(action_settings)
|
||||||
|> attach_identity(settings)
|
|> attach_identity(action_settings)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_scale(_, {scale, _}), do: scale
|
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 get_limits(%{limits: [{_, limit}, _]}), do: limit
|
||||||
|
|
||||||
defp make_bucket_name(%{mode: :user, name: name_root}),
|
defp make_bucket_name(%{mode: :user, name: bucket_name_root}),
|
||||||
do: user_bucket_name(name_root)
|
do: user_bucket_name(bucket_name_root)
|
||||||
|
|
||||||
defp make_bucket_name(%{mode: :anon, name: name_root}),
|
defp make_bucket_name(%{mode: :anon, name: bucket_name_root}),
|
||||||
do: anon_bucket_name(name_root)
|
do: anon_bucket_name(bucket_name_root)
|
||||||
|
|
||||||
defp attach_params(input, %{conn_params: conn_params, opts: opts}) do
|
defp attach_selected_params(input, %{conn_params: conn_params, opts: plug_opts}) do
|
||||||
param_string =
|
params_string =
|
||||||
opts
|
plug_opts
|
||||||
|> Keyword.get(:params, [])
|
|> Keyword.get(:params, [])
|
||||||
|> Enum.sort()
|
|> Enum.sort()
|
||||||
|> Enum.map(&Map.get(conn_params, &1, ""))
|
|> Enum.map(&Map.get(conn_params, &1, ""))
|
||||||
|> Enum.join(":")
|
|> Enum.join(":")
|
||||||
|
|
||||||
"#{input}#{param_string}"
|
[input, params_string]
|
||||||
|
|> Enum.join(":")
|
||||||
|
|> String.replace_leading(":", "")
|
||||||
end
|
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
|
defp initialize_buckets!(%{name: name, limits: limits}) do
|
||||||
LimiterSupervisor.add_limiter(anon_bucket_name(name), get_scale(:anon, limits))
|
{:ok, _pid} =
|
||||||
LimiterSupervisor.add_limiter(user_bucket_name(name), get_scale(:user, limits))
|
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
|
end
|
||||||
|
|
||||||
defp attach_identity(base, %{mode: :user, conn_info: conn_info}),
|
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}),
|
defp attach_identity(base, %{mode: :anon, conn_info: conn_info}),
|
||||||
do: "ip:#{base}:#{conn_info}"
|
do: "ip:#{base}:#{conn_info}"
|
||||||
|
|
||||||
defp user_bucket_name(name_root), do: "user:#{name_root}" |> String.to_atom()
|
defp user_bucket_name(bucket_name_root), do: "user:#{bucket_name_root}" |> String.to_atom()
|
||||||
defp anon_bucket_name(name_root), do: "anon:#{name_root}" |> String.to_atom()
|
defp anon_bucket_name(bucket_name_root), do: "anon:#{bucket_name_root}" |> String.to_atom()
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.RemoteIp do
|
defmodule Pleroma.Plugs.RemoteIp do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.UserEnabledPlug do
|
defmodule Pleroma.Plugs.UserEnabledPlug do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Plugs.UserIsAdminPlug do
|
defmodule Pleroma.Plugs.UserIsAdminPlug do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Repo do
|
defmodule Pleroma.Repo do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.ScheduledActivity do
|
defmodule Pleroma.ScheduledActivity do
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Stats do
|
defmodule Pleroma.Stats do
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
alias Pleroma.CounterCache
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@ -96,4 +97,21 @@ defp get_stat_data do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
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
|
end
|
||||||
|
|
|
@ -37,6 +37,7 @@ defmodule Pleroma.Upload do
|
||||||
Plug.Upload.t()
|
Plug.Upload.t()
|
||||||
| (data_uri_string :: String.t())
|
| (data_uri_string :: String.t())
|
||||||
| {:from_local, name :: String.t(), id :: String.t(), path :: String.t()}
|
| {:from_local, name :: String.t(), id :: String.t(), path :: String.t()}
|
||||||
|
| map()
|
||||||
|
|
||||||
@type option ::
|
@type option ::
|
||||||
{:type, :avatar | :banner | :background}
|
{:type, :avatar | :banner | :background}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Uploaders.Local do
|
defmodule Pleroma.Uploaders.Local do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Uploaders.S3 do
|
defmodule Pleroma.Uploaders.S3 do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Uploaders.Uploader do
|
defmodule Pleroma.Uploaders.Uploader do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.User do
|
defmodule Pleroma.User do
|
||||||
|
@ -749,9 +749,18 @@ def invalidate_cache(user) do
|
||||||
Cachex.del(:user_cache, "nickname:#{user.nickname}")
|
Cachex.del(:user_cache, "nickname:#{user.nickname}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_cached_by_ap_id(String.t()) :: User.t() | nil
|
||||||
def get_cached_by_ap_id(ap_id) do
|
def get_cached_by_ap_id(ap_id) do
|
||||||
key = "ap_id:#{ap_id}"
|
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
|
end
|
||||||
|
|
||||||
def get_cached_by_id(id) do
|
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()
|
@spec get_followers_query(User.t()) :: Ecto.Query.t()
|
||||||
def get_followers_query(user), do: get_followers_query(user, nil)
|
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
|
def get_followers(user, page \\ nil) do
|
||||||
user
|
user
|
||||||
|> get_followers_query(page)
|
|> get_followers_query(page)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
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
|
def get_external_followers(user, page \\ nil) do
|
||||||
user
|
user
|
||||||
|> get_followers_query(page)
|
|> get_followers_query(page)
|
||||||
|
@ -1304,7 +1313,6 @@ def perform(:delete, %User{} = user) do
|
||||||
Repo.delete(user)
|
Repo.delete(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec perform(atom(), User.t()) :: {:ok, User.t()}
|
|
||||||
def perform(:fetch_initial_posts, %User{} = user) do
|
def perform(:fetch_initial_posts, %User{} = user) do
|
||||||
pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
|
pages = Pleroma.Config.get!([:fetch_initial_posts, :pages])
|
||||||
|
|
||||||
|
@ -1336,7 +1344,6 @@ def perform(:blocks_import, %User{} = blocker, blocked_identifiers)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec perform(atom(), User.t(), list()) :: list() | {:error, any()}
|
|
||||||
def perform(:follow_import, %User{} = follower, followed_identifiers)
|
def perform(:follow_import, %User{} = follower, followed_identifiers)
|
||||||
when is_list(followed_identifiers) do
|
when is_list(followed_identifiers) do
|
||||||
Enum.map(
|
Enum.map(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.User.Query do
|
defmodule Pleroma.User.Query do
|
||||||
|
@ -48,7 +48,7 @@ defmodule Pleroma.User.Query do
|
||||||
followers: User.t(),
|
followers: User.t(),
|
||||||
friends: User.t(),
|
friends: User.t(),
|
||||||
recipients_from_activity: [String.t()],
|
recipients_from_activity: [String.t()],
|
||||||
nickname: [String.t()],
|
nickname: [String.t()] | String.t(),
|
||||||
ap_id: [String.t()],
|
ap_id: [String.t()],
|
||||||
order_by: term(),
|
order_by: term(),
|
||||||
select: term(),
|
select: term(),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.User.Search do
|
defmodule Pleroma.User.Search do
|
||||||
|
@ -33,9 +33,15 @@ defp format_query(query_string) do
|
||||||
# Strip the beginning @ off if there is a query
|
# Strip the beginning @ off if there is a query
|
||||||
query_string = String.trim_leading(query_string, "@")
|
query_string = String.trim_leading(query_string, "@")
|
||||||
|
|
||||||
with [name, domain] <- String.split(query_string, "@"),
|
with [name, domain] <- String.split(query_string, "@") do
|
||||||
formatted_domain <- String.replace(domain, ~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "") do
|
encoded_domain =
|
||||||
name <> "@" <> to_string(:idna.encode(formatted_domain))
|
domain
|
||||||
|
|> String.replace(~r/[!-\-|@|[-`|{-~|\/|:|\s]+/, "")
|
||||||
|
|> String.to_charlist()
|
||||||
|
|> :idna.encode()
|
||||||
|
|> to_string()
|
||||||
|
|
||||||
|
name <> "@" <> encoded_domain
|
||||||
else
|
else
|
||||||
_ -> query_string
|
_ -> query_string
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.UserRelationship do
|
defmodule Pleroma.UserRelationship do
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Activity.Ir.Topics
|
alias Pleroma.Activity.Ir.Topics
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Constants
|
||||||
alias Pleroma.Conversation
|
alias Pleroma.Conversation
|
||||||
alias Pleroma.Conversation.Participation
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.Notification
|
alias Pleroma.Notification
|
||||||
|
@ -124,6 +125,7 @@ def increase_poll_votes_if_vote(%{
|
||||||
|
|
||||||
def increase_poll_votes_if_vote(_create_data), do: :noop
|
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
|
def insert(map, local \\ true, fake \\ false, bypass_actor_check \\ false) when is_map(map) do
|
||||||
with nil <- Activity.normalize(map),
|
with nil <- Activity.normalize(map),
|
||||||
map <- lazy_put_activity_defaults(map, fake),
|
map <- lazy_put_activity_defaults(map, fake),
|
||||||
|
@ -231,12 +233,19 @@ def stream_out(_activity) do
|
||||||
:noop
|
:noop
|
||||||
end
|
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] || %{}
|
additional = params[:additional] || %{}
|
||||||
# only accept false as false value
|
# only accept false as false value
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
published = params[:published]
|
published = params[:published]
|
||||||
quick_insert? = Pleroma.Config.get([:env]) == :benchmark
|
quick_insert? = Config.get([:env]) == :benchmark
|
||||||
|
|
||||||
with create_data <-
|
with create_data <-
|
||||||
make_create_data(
|
make_create_data(
|
||||||
|
@ -259,10 +268,11 @@ def create(%{to: to, actor: actor, context: context, object: object} = params, f
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
|
||||||
{:error, message} ->
|
{:error, message} ->
|
||||||
{:error, message}
|
Repo.rollback(message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec listen(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def listen(%{to: to, actor: actor, context: context, object: object} = params) do
|
def listen(%{to: to, actor: actor, context: context, object: object} = params) do
|
||||||
additional = params[:additional] || %{}
|
additional = params[:additional] || %{}
|
||||||
# only accept false as false value
|
# 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, activity} <- insert(listen_data, local),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
|
||||||
{:error, message} ->
|
|
||||||
{:error, message}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec accept(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def accept(params) do
|
def accept(params) do
|
||||||
accept_or_reject("Accept", params)
|
accept_or_reject("Accept", params)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec reject(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def reject(params) do
|
def reject(params) do
|
||||||
accept_or_reject("Reject", params)
|
accept_or_reject("Reject", params)
|
||||||
end
|
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
|
def accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do
|
||||||
local = Map.get(params, :local, true)
|
local = Map.get(params, :local, true)
|
||||||
activity_id = Map.get(params, :activity_id, nil)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec update(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
local = !(params[:local] == false)
|
local = !(params[:local] == false)
|
||||||
activity_id = params[:activity_id]
|
activity_id = params[:activity_id]
|
||||||
|
@ -322,7 +333,16 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do
|
||||||
end
|
end
|
||||||
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
|
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),
|
with local <- Keyword.get(options, :local, true),
|
||||||
activity_id <- Keyword.get(options, :activity_id, nil),
|
activity_id <- Keyword.get(options, :activity_id, nil),
|
||||||
true <- Pleroma.Emoji.is_unicode_emoji?(emoji),
|
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 <- maybe_federate(activity) do
|
||||||
{:ok, activity, object}
|
{:ok, activity, object}
|
||||||
else
|
else
|
||||||
e -> {:error, e}
|
false -> {:error, false}
|
||||||
|
{:error, error} -> Repo.rollback(error)
|
||||||
end
|
end
|
||||||
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
|
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),
|
with local <- Keyword.get(options, :local, true),
|
||||||
activity_id <- Keyword.get(options, :activity_id, nil),
|
activity_id <- Keyword.get(options, :activity_id, nil),
|
||||||
user_ap_id <- user.ap_id,
|
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 <- maybe_federate(activity) do
|
||||||
{:ok, activity, object}
|
{:ok, activity, object}
|
||||||
else
|
else
|
||||||
e -> {:error, e}
|
{:error, error} -> Repo.rollback(error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: This is weird, maybe we shouldn't check here if we can make the activity.
|
# TODO: This is weird, maybe we shouldn't check here if we can make the activity.
|
||||||
def like(
|
@spec like(User.t(), Object.t(), String.t() | nil, boolean()) ::
|
||||||
%User{ap_id: ap_id} = user,
|
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||||
%Object{data: %{"id" => _}} = object,
|
def like(user, object, activity_id \\ nil, local \\ true) do
|
||||||
activity_id \\ nil,
|
with {:ok, result} <- Repo.transaction(fn -> do_like(user, object, activity_id, local) end) do
|
||||||
local \\ true
|
result
|
||||||
) do
|
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),
|
with nil <- get_existing_like(ap_id, object),
|
||||||
like_data <- make_like_data(user, object, activity_id),
|
like_data <- make_like_data(user, object, activity_id),
|
||||||
{:ok, activity} <- insert(like_data, local),
|
{:ok, activity} <- insert(like_data, local),
|
||||||
|
@ -366,12 +404,24 @@ def like(
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity, object}
|
{:ok, activity, object}
|
||||||
else
|
else
|
||||||
%Activity{} = activity -> {:ok, activity, object}
|
%Activity{} = activity ->
|
||||||
error -> {:error, error}
|
{:ok, activity, object}
|
||||||
|
|
||||||
|
{:error, error} ->
|
||||||
|
Repo.rollback(error)
|
||||||
end
|
end
|
||||||
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
|
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),
|
with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
|
||||||
unlike_data <- make_unlike_data(actor, like_activity, activity_id),
|
unlike_data <- make_unlike_data(actor, like_activity, activity_id),
|
||||||
{:ok, unlike_activity} <- insert(unlike_data, local),
|
{: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 <- maybe_federate(unlike_activity) do
|
||||||
{:ok, unlike_activity, like_activity, object}
|
{:ok, unlike_activity, like_activity, object}
|
||||||
else
|
else
|
||||||
_e -> {:ok, object}
|
nil -> {:ok, object}
|
||||||
|
{:error, error} -> Repo.rollback(error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec announce(User.t(), Object.t(), String.t() | nil, boolean(), boolean()) ::
|
||||||
|
{:ok, Activity.t(), Object.t()} | {:error, any()}
|
||||||
def announce(
|
def announce(
|
||||||
%User{ap_id: _} = user,
|
%User{ap_id: _} = user,
|
||||||
%Object{data: %{"id" => _}} = object,
|
%Object{data: %{"id" => _}} = object,
|
||||||
|
@ -391,6 +444,13 @@ def announce(
|
||||||
local \\ true,
|
local \\ true,
|
||||||
public \\ true
|
public \\ true
|
||||||
) do
|
) 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),
|
with true <- is_announceable?(object, user, public),
|
||||||
announce_data <- make_announce_data(user, object, activity_id, public),
|
announce_data <- make_announce_data(user, object, activity_id, public),
|
||||||
{:ok, activity} <- insert(announce_data, local),
|
{:ok, activity} <- insert(announce_data, local),
|
||||||
|
@ -398,16 +458,26 @@ def announce(
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity, object}
|
{:ok, activity, object}
|
||||||
else
|
else
|
||||||
error -> {:error, error}
|
false -> {:error, false}
|
||||||
|
{:error, error} -> Repo.rollback(error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec unannounce(User.t(), Object.t(), String.t() | nil, boolean()) ::
|
||||||
|
{:ok, Activity.t(), Object.t()} | {:ok, Object.t()} | {:error, any()}
|
||||||
def unannounce(
|
def unannounce(
|
||||||
%User{} = actor,
|
%User{} = actor,
|
||||||
%Object{} = object,
|
%Object{} = object,
|
||||||
activity_id \\ nil,
|
activity_id \\ nil,
|
||||||
local \\ true
|
local \\ true
|
||||||
) do
|
) 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),
|
with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
|
||||||
unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
|
unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
|
||||||
{:ok, unannounce_activity} <- insert(unannounce_data, local),
|
{:ok, unannounce_activity} <- insert(unannounce_data, local),
|
||||||
|
@ -416,30 +486,61 @@ def unannounce(
|
||||||
{:ok, object} <- remove_announce_from_object(announce_activity, object) do
|
{:ok, object} <- remove_announce_from_object(announce_activity, object) do
|
||||||
{:ok, unannounce_activity, object}
|
{:ok, unannounce_activity, object}
|
||||||
else
|
else
|
||||||
_e -> {:ok, object}
|
nil -> {:ok, object}
|
||||||
|
{:error, error} -> Repo.rollback(error)
|
||||||
end
|
end
|
||||||
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
|
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),
|
with data <- make_follow_data(follower, followed, activity_id),
|
||||||
{:ok, activity} <- insert(data, local),
|
{:ok, activity} <- insert(data, local),
|
||||||
:ok <- maybe_federate(activity),
|
:ok <- maybe_federate(activity),
|
||||||
_ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
|
_ <- User.set_follow_state_cache(follower.ap_id, followed.ap_id, activity.data["state"]) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
{:error, error} -> Repo.rollback(error)
|
||||||
end
|
end
|
||||||
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
|
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),
|
with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
|
||||||
{:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
|
{:ok, follow_activity} <- update_follow_state(follow_activity, "cancelled"),
|
||||||
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
|
unfollow_data <- make_unfollow_data(follower, followed, follow_activity, activity_id),
|
||||||
{:ok, activity} <- insert(unfollow_data, local),
|
{:ok, activity} <- insert(unfollow_data, local),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
nil -> nil
|
||||||
|
{:error, error} -> Repo.rollback(error)
|
||||||
end
|
end
|
||||||
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 <- %{
|
with data <- %{
|
||||||
"to" => [follower_address],
|
"to" => [follower_address],
|
||||||
"type" => "Delete",
|
"type" => "Delete",
|
||||||
|
@ -452,7 +553,7 @@ def delete(%User{ap_id: ap_id, follower_address: follower_address} = user) do
|
||||||
end
|
end
|
||||||
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)
|
local = Keyword.get(options, :local, true)
|
||||||
activity_id = Keyword.get(options, :activity_id, nil)
|
activity_id = Keyword.get(options, :activity_id, nil)
|
||||||
actor = Keyword.get(options, :actor, actor)
|
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, _actor} <- decrease_note_count_if_public(user, object),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
{:error, error} ->
|
||||||
|
Repo.rollback(error)
|
||||||
end
|
end
|
||||||
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
|
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])
|
outgoing_blocks = Config.get([:activitypub, :outgoing_blocks])
|
||||||
unfollow_blocked = Config.get([:activitypub, :unfollow_blocked])
|
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 <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
_e -> {:ok, nil}
|
{:error, error} -> Repo.rollback(error)
|
||||||
end
|
end
|
||||||
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
|
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),
|
with %Activity{} = block_activity <- fetch_latest_block(blocker, blocked),
|
||||||
unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
|
unblock_data <- make_unblock_data(blocker, blocked, block_activity, activity_id),
|
||||||
{:ok, activity} <- insert(unblock_data, local),
|
{:ok, activity} <- insert(unblock_data, local),
|
||||||
:ok <- maybe_federate(activity) do
|
:ok <- maybe_federate(activity) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
nil -> nil
|
||||||
|
{:error, error} -> Repo.rollback(error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec flag(map()) :: {:ok, Activity.t()} | any
|
@spec flag(map()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def flag(
|
def flag(
|
||||||
%{
|
%{
|
||||||
actor: actor,
|
actor: actor,
|
||||||
|
@ -546,6 +670,7 @@ def flag(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec move(User.t(), User.t(), boolean()) :: {:ok, Activity.t()} | {:error, any()}
|
||||||
def move(%User{} = origin, %User{} = target, local \\ true) do
|
def move(%User{} = origin, %User{} = target, local \\ true) do
|
||||||
params = %{
|
params = %{
|
||||||
"type" => "Move",
|
"type" => "Move",
|
||||||
|
@ -571,7 +696,7 @@ def move(%User{} = origin, %User{} = target, local \\ true) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fetch_activities_for_context_query(context, opts) do
|
defp fetch_activities_for_context_query(context, opts) do
|
||||||
public = [Pleroma.Constants.as_public()]
|
public = [Constants.as_public()]
|
||||||
|
|
||||||
recipients =
|
recipients =
|
||||||
if opts["user"],
|
if opts["user"],
|
||||||
|
@ -616,10 +741,11 @@ def fetch_latest_activity_id_for_context(context, opts \\ %{}) do
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec fetch_public_activities(map(), Pagination.type()) :: [Activity.t()]
|
||||||
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
|
def fetch_public_activities(opts \\ %{}, pagination \\ :keyset) do
|
||||||
opts = Map.drop(opts, ["user"])
|
opts = Map.drop(opts, ["user"])
|
||||||
|
|
||||||
[Pleroma.Constants.as_public()]
|
[Constants.as_public()]
|
||||||
|> fetch_activities_query(opts)
|
|> fetch_activities_query(opts)
|
||||||
|> restrict_unlisted()
|
|> restrict_unlisted()
|
||||||
|> Pagination.fetch_paginated(opts, pagination)
|
|> Pagination.fetch_paginated(opts, pagination)
|
||||||
|
@ -770,13 +896,18 @@ def fetch_user_activities(user, reading_user, params \\ %{}) do
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_instance_activities(params) do
|
def fetch_statuses(reading_user, params) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|> 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()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -786,9 +917,9 @@ defp user_activities_recipients(%{"godmode" => true}) do
|
||||||
|
|
||||||
defp user_activities_recipients(%{"reading_user" => reading_user}) do
|
defp user_activities_recipients(%{"reading_user" => reading_user}) do
|
||||||
if 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
|
else
|
||||||
[Pleroma.Constants.as_public()]
|
[Constants.as_public()]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -995,7 +1126,7 @@ defp restrict_unlisted(query) do
|
||||||
fragment(
|
fragment(
|
||||||
"not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
|
"not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
|
||||||
activity.data,
|
activity.data,
|
||||||
^[Pleroma.Constants.as_public()]
|
^[Constants.as_public()]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
@ -1169,7 +1300,7 @@ def fetch_activities(recipients, opts \\ %{}, pagination \\ :keyset) do
|
||||||
@doc """
|
@doc """
|
||||||
Fetch favorites activities of user with order by sort adds to favorites
|
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
|
def fetch_favourites(user, params \\ %{}, pagination \\ :keyset) do
|
||||||
user.ap_id
|
user.ap_id
|
||||||
|> Activity.Queries.by_actor()
|
|> Activity.Queries.by_actor()
|
||||||
|
@ -1207,7 +1338,7 @@ def fetch_activities_bounded_query(query, recipients, recipients_with_public) do
|
||||||
where:
|
where:
|
||||||
fragment("? && ?", activity.recipients, ^recipients) or
|
fragment("? && ?", activity.recipients, ^recipients) or
|
||||||
(fragment("? && ?", activity.recipients, ^recipients_with_public) and
|
(fragment("? && ?", activity.recipients, ^recipients_with_public) and
|
||||||
^Pleroma.Constants.as_public() in activity.recipients)
|
^Constants.as_public() in activity.recipients)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1223,6 +1354,7 @@ def fetch_activities_bounded(
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec upload(Upload.source(), keyword()) :: {:ok, Object.t()} | {:error, any()}
|
||||||
def upload(file, opts \\ []) do
|
def upload(file, opts \\ []) do
|
||||||
with {:ok, data} <- Upload.store(file, opts) do
|
with {:ok, data} <- Upload.store(file, opts) do
|
||||||
obj_data =
|
obj_data =
|
||||||
|
@ -1320,8 +1452,7 @@ defp normalize_counter(counter) when is_integer(counter), do: counter
|
||||||
defp normalize_counter(_), do: 0
|
defp normalize_counter(_), do: 0
|
||||||
|
|
||||||
defp maybe_update_follow_information(data) do
|
defp maybe_update_follow_information(data) do
|
||||||
with {:enabled, true} <-
|
with {:enabled, true} <- {:enabled, Config.get([:instance, :external_user_synchronization])},
|
||||||
{:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])},
|
|
||||||
{:ok, info} <- fetch_follow_information_for_user(data) do
|
{:ok, info} <- fetch_follow_information_for_user(data) do
|
||||||
info = Map.merge(data[:info] || %{}, info)
|
info = Map.merge(data[:info] || %{}, info)
|
||||||
Map.put(data, :info, info)
|
Map.put(data, :info, info)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.SubchainPolicy do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.VocabularyPolicy do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Publisher do
|
defmodule Pleroma.Web.ActivityPub.Publisher do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Relay do
|
defmodule Pleroma.Web.ActivityPub.Relay do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
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
|
when not is_nil(in_reply_to) do
|
||||||
in_reply_to_id = prepare_in_reply_to(in_reply_to)
|
in_reply_to_id = prepare_in_reply_to(in_reply_to)
|
||||||
object = Map.put(object, "inReplyToAtomUri", in_reply_to_id)
|
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),
|
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
|
object
|
||||||
|> Map.put("inReplyTo", replied_object.data["id"])
|
|> Map.put("inReplyTo", replied_object.data["id"])
|
||||||
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_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)
|
def fix_type(%{"inReplyTo" => reply_id, "name" => _} = object, options)
|
||||||
when is_binary(reply_id) do
|
when is_binary(reply_id) do
|
||||||
with true <- Federator.allowed_incoming_reply_depth?(options[:depth]),
|
with true <- Federator.allowed_thread_distance?(options[:depth]),
|
||||||
{:ok, %{data: %{"type" => "Question"} = _} = _} <- get_obj_helper(reply_id, options) do
|
{:ok, %{data: %{"type" => "Question"} = _} = _} <- get_obj_helper(reply_id, options) do
|
||||||
Map.put(object, "type", "Answer")
|
Map.put(object, "type", "Answer")
|
||||||
else
|
else
|
||||||
|
@ -406,8 +407,7 @@ def handle_incoming(
|
||||||
|
|
||||||
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
with nil <- Activity.get_create_by_object_ap_id(object["id"]),
|
||||||
{:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
|
{: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(object, options)
|
||||||
object = fix_object(data["object"], options)
|
|
||||||
|
|
||||||
params = %{
|
params = %{
|
||||||
to: data["to"],
|
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
|
else
|
||||||
%Activity{} = activity -> {:ok, activity}
|
%Activity{} = activity -> {:ok, activity}
|
||||||
_e -> :error
|
_e -> :error
|
||||||
|
@ -442,7 +455,8 @@ def handle_incoming(
|
||||||
|> fix_addressing
|
|> fix_addressing
|
||||||
|
|
||||||
with {:ok, %User{} = user} <- User.get_or_fetch_by_ap_id(data["actor"]) do
|
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)
|
object = fix_object(object, options)
|
||||||
|
|
||||||
params = %{
|
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
|
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.
|
# Prepares the object of an outgoing create activity.
|
||||||
def prepare_object(object) do
|
def prepare_object(object) do
|
||||||
object
|
object
|
||||||
|
@ -914,6 +972,7 @@ def prepare_object(object) do
|
||||||
|> prepare_attachments
|
|> prepare_attachments
|
||||||
|> set_conversation
|
|> set_conversation
|
||||||
|> set_reply_to_uri
|
|> set_reply_to_uri
|
||||||
|
|> set_replies
|
||||||
|> strip_internal_fields
|
|> strip_internal_fields
|
||||||
|> strip_internal_tags
|
|> strip_internal_tags
|
||||||
|> set_type
|
|> set_type
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ActivityPub.Utils do
|
defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
|
@ -45,8 +45,8 @@ def normalize_params(params) do
|
||||||
Map.put(params, "actor", get_ap_id(params["actor"]))
|
Map.put(params, "actor", get_ap_id(params["actor"]))
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec determine_explicit_mentions(map()) :: map()
|
@spec determine_explicit_mentions(map()) :: [any]
|
||||||
def determine_explicit_mentions(%{"tag" => tag} = _) when is_list(tag) do
|
def determine_explicit_mentions(%{"tag" => tag}) when is_list(tag) do
|
||||||
Enum.flat_map(tag, fn
|
Enum.flat_map(tag, fn
|
||||||
%{"type" => "Mention", "href" => href} -> [href]
|
%{"type" => "Mention", "href" => href} -> [href]
|
||||||
_ -> []
|
_ -> []
|
||||||
|
@ -427,7 +427,7 @@ defp fetch_likes(object) do
|
||||||
@doc """
|
@doc """
|
||||||
Updates a follow activity's state (for locked accounts).
|
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(
|
def update_follow_state_for_all(
|
||||||
%Activity{data: %{"actor" => actor, "object" => object}} = activity,
|
%Activity{data: %{"actor" => actor, "object" => object}} = activity,
|
||||||
state
|
state
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
alias Pleroma.ModerationLog
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.ReportNote
|
alias Pleroma.ReportNote
|
||||||
|
alias Pleroma.Stats
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.UserInviteToken
|
alias Pleroma.UserInviteToken
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -98,7 +99,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read"], admin: true}
|
%{scopes: ["read"], admin: true}
|
||||||
when action in [:config_show, :list_log]
|
when action in [:config_show, :list_log, :stats]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -243,13 +244,15 @@ def user_show(conn, %{"nickname" => nickname}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_instance_statuses(conn, %{"instance" => instance} = params) do
|
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)
|
{page, page_size} = page_params(params)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_instance_activities(%{
|
ActivityPub.fetch_statuses(nil, %{
|
||||||
"instance" => instance,
|
"instance" => instance,
|
||||||
"limit" => page_size,
|
"limit" => page_size,
|
||||||
"offset" => (page - 1) * page_size
|
"offset" => (page - 1) * page_size,
|
||||||
|
"exclude_reblogs" => !with_reblogs && "true"
|
||||||
})
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -258,6 +261,7 @@ def list_instance_statuses(conn, %{"instance" => instance} = params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_user_statuses(conn, %{"nickname" => nickname} = params) do
|
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
|
godmode = params["godmode"] == "true" || params["godmode"] == true
|
||||||
|
|
||||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do
|
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 =
|
activities =
|
||||||
ActivityPub.fetch_user_activities(user, nil, %{
|
ActivityPub.fetch_user_activities(user, nil, %{
|
||||||
"limit" => page_size,
|
"limit" => page_size,
|
||||||
"godmode" => godmode
|
"godmode" => godmode,
|
||||||
|
"exclude_reblogs" => !with_reblogs && "true"
|
||||||
})
|
})
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -740,6 +745,24 @@ def report_notes_delete(%{assigns: %{user: user}} = conn, %{
|
||||||
end
|
end
|
||||||
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
|
def status_update(%{assigns: %{user: admin}} = conn, %{"id" => id} = params) do
|
||||||
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
with {:ok, activity} <- CommonAPI.update_activity_scope(id, params) do
|
||||||
{:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
|
{:ok, sensitive} = Ecto.Type.cast(:boolean, params["sensitive"])
|
||||||
|
@ -953,6 +976,13 @@ def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" =
|
||||||
conn |> json("")
|
conn |> json("")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def stats(conn, _) do
|
||||||
|
count = Stats.get_status_visibility_count()
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> json(%{"status_visibility" => count})
|
||||||
|
end
|
||||||
|
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:not_found)
|
|> put_status(:not_found)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.AdminAPI.Search do
|
defmodule Pleroma.Web.AdminAPI.Search do
|
||||||
|
@ -18,7 +18,11 @@ defmacro not_empty_string(string) do
|
||||||
|
|
||||||
@spec user(map()) :: {:ok, [User.t()], pos_integer()}
|
@spec user(map()) :: {:ok, [User.t()], pos_integer()}
|
||||||
def user(params \\ %{}) do
|
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 =
|
paginated_query =
|
||||||
User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size)
|
User.Query.paginate(query, params[:page] || 1, params[:page_size] || @page_size)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.AdminAPI.ConfigView do
|
defmodule Pleroma.Web.AdminAPI.ConfigView do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.AdminAPI.StatusView do
|
defmodule Pleroma.Web.AdminAPI.StatusView do
|
||||||
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.AdminAPI.StatusView do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
def render("index.json", opts) do
|
def render("index.json", opts) do
|
||||||
render_many(opts.activities, __MODULE__, "show.json", opts)
|
safe_render_many(opts.activities, __MODULE__, "show.json", opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
def render("show.json", %{activity: %{data: %{"object" => _object}} = activity} = opts) do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.CommonAPI do
|
defmodule Pleroma.Web.CommonAPI do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.CommonAPI.Utils do
|
defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.ControllerHelper do
|
defmodule Pleroma.Web.ControllerHelper do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Endpoint do
|
defmodule Pleroma.Web.Endpoint do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Federator do
|
defmodule Pleroma.Web.Federator do
|
||||||
|
@ -15,13 +15,19 @@ defmodule Pleroma.Web.Federator do
|
||||||
|
|
||||||
require Logger
|
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
|
# credo:disable-for-previous-line Credo.Check.Readability.MaxLineLength
|
||||||
def allowed_incoming_reply_depth?(depth) do
|
def allowed_thread_distance?(distance) do
|
||||||
max_replies_depth = Pleroma.Config.get([:instance, :federation_incoming_replies_max_depth])
|
max_distance = Pleroma.Config.get([:instance, :federation_incoming_replies_max_depth])
|
||||||
|
|
||||||
if max_replies_depth do
|
if max_distance && max_distance >= 0 do
|
||||||
(depth || 1) <= max_replies_depth
|
# Default depth is 0 (an object has zero distance from itself in its thread)
|
||||||
|
(distance || 0) <= max_distance
|
||||||
else
|
else
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Feed.FeedView do
|
defmodule Pleroma.Web.Feed.FeedView do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Feed.TagController do
|
defmodule Pleroma.Web.Feed.TagController do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.Feed.UserController do
|
defmodule Pleroma.Web.Feed.UserController do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastoFEController do
|
defmodule Pleroma.Web.MastoFEController do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
defmodule Pleroma.Web.MastodonAPI.NotificationController do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.SearchController do
|
defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
defmodule Pleroma.Web.MastodonAPI.StatusController do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
defmodule Pleroma.Web.MastodonAPI.SubscriptionController do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
defmodule Pleroma.Web.MastodonAPI.SuggestionController do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
|
@ -10,9 +10,20 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
|
|
||||||
alias Pleroma.Pagination
|
alias Pleroma.Pagination
|
||||||
alias Pleroma.Plugs.OAuthScopesPlug
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Plugs.RateLimiter
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
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:statuses"]} when action in [:home, :direct])
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)
|
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :list)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
defmodule Pleroma.Web.MastodonAPI.MastodonAPI do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.AccountView do
|
defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.AppView do
|
defmodule Pleroma.Web.MastodonAPI.AppView do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
defmodule Pleroma.Web.MastodonAPI.NotificationView do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.PollView do
|
defmodule Pleroma.Web.MastodonAPI.PollView do
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# Pleroma: A lightweight social networking server
|
# 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
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Web.MastodonAPI.StatusView do
|
defmodule Pleroma.Web.MastodonAPI.StatusView do
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue