Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into alexgleason/pleroma-block-behavior
This commit is contained in:
commit
bae48c98e3
104 changed files with 3547 additions and 1805 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -12,6 +12,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details).
|
- Media preview proxy (requires `ffmpeg` and `ImageMagick` to be installed and media proxy to be enabled; see `:media_preview_proxy` config for more details).
|
||||||
- Pleroma API: Importing the mutes users from CSV files.
|
- Pleroma API: Importing the mutes users from CSV files.
|
||||||
- Experimental websocket-based federation between Pleroma instances.
|
- Experimental websocket-based federation between Pleroma instances.
|
||||||
|
- Support pagination of blocks and mutes
|
||||||
|
- App metrics: ability to restrict access to specified IP whitelist.
|
||||||
|
- Account backup
|
||||||
|
- Configuration: Add `:instance, autofollowing_nicknames` setting to provide a way to make accounts automatically follow new users that register on the local Pleroma instance.
|
||||||
|
- Ability to view remote timelines, with ex. `/api/v1/timelines/public?instance=lain.com` and streams `public:remote` and `public:remote:media`
|
||||||
- `[:activitypub, :blockers_visible]` config to control visibility of blockers.
|
- `[:activitypub, :blockers_visible]` config to control visibility of blockers.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -19,6 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- **Breaking** Requires `libmagic` (or `file`) to guess file types.
|
- **Breaking** Requires `libmagic` (or `file`) to guess file types.
|
||||||
- **Breaking:** Pleroma Admin API: emoji packs and files routes changed.
|
- **Breaking:** Pleroma Admin API: emoji packs and files routes changed.
|
||||||
- **Breaking:** Sensitive/NSFW statuses no longer disable link previews.
|
- **Breaking:** Sensitive/NSFW statuses no longer disable link previews.
|
||||||
|
- **Breaking:** App metrics endpoint (`/api/pleroma/app_metrics`) is disabled by default, check `docs/API/prometheus.md` on enabling and configuring.
|
||||||
- Search: Users are now findable by their urls.
|
- Search: Users are now findable by their urls.
|
||||||
- Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated.
|
- Renamed `:await_up_timeout` in `:connections_pool` namespace to `:connect_timeout`, old name is deprecated.
|
||||||
- Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated.
|
- Renamed `:timeout` in `pools` namespace to `:recv_timeout`, old name is deprecated.
|
||||||
|
@ -26,6 +32,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Users with the `discoverable` field set to false will not show up in searches.
|
- Users with the `discoverable` field set to false will not show up in searches.
|
||||||
- Minimum lifetime for ephmeral activities changed to 10 minutes and made configurable (`:min_lifetime` option).
|
- Minimum lifetime for ephmeral activities changed to 10 minutes and made configurable (`:min_lifetime` option).
|
||||||
- Introduced optional dependencies on `ffmpeg`, `ImageMagick`, `exiftool` software packages. Please refer to `docs/installation/optional/media_graphics_packages.md`.
|
- Introduced optional dependencies on `ffmpeg`, `ImageMagick`, `exiftool` software packages. Please refer to `docs/installation/optional/media_graphics_packages.md`.
|
||||||
|
- Polls now always return a `voters_count`, even if they are single-choice
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
|
@ -33,6 +40,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Pleroma API: Importing the mutes users from CSV files.
|
- Pleroma API: Importing the mutes users from CSV files.
|
||||||
- Admin API: Importing emoji from a zip file
|
- Admin API: Importing emoji from a zip file
|
||||||
- Pleroma API: Pagination for remote/local packs and emoji.
|
- Pleroma API: Pagination for remote/local packs and emoji.
|
||||||
|
- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `unconfirmed` status
|
||||||
|
- Admin API: (`GET /api/pleroma/admin/users`) added filters user by `actor_type`
|
||||||
|
- Pleroma API: Add `idempotency_key` to the chat message entity that can be used for optimistic message sending.
|
||||||
|
- Pleroma API: (`GET /api/v1/pleroma/federation_status`) Add a way to get a list of unreachable instances.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -48,8 +59,12 @@ switched to a new configuration mechanism, however it was not officially removed
|
||||||
|
|
||||||
- Add documented-but-missing chat pagination.
|
- Add documented-but-missing chat pagination.
|
||||||
- Allow sending out emails again.
|
- Allow sending out emails again.
|
||||||
|
- Allow sending chat messages to yourself.
|
||||||
|
- Fix remote users with a whitespace name.
|
||||||
|
- OStatus / static FE endpoints: fixed inaccessibility for anonymous users on non-federating instances, switched to handling per `:restrict_unauthenticated` setting.
|
||||||
|
- Mastodon API: Current user is now included in conversation if it's the only participant
|
||||||
|
- Mastodon API: Fixed last_status.account being not filled with account data
|
||||||
- See your own post when addressing a user from a blocked domain.
|
- See your own post when addressing a user from a blocked domain.
|
||||||
- Allow sending chat messages to yourself
|
|
||||||
|
|
||||||
## Unreleased (Patch)
|
## Unreleased (Patch)
|
||||||
|
|
||||||
|
|
|
@ -234,6 +234,7 @@ config :pleroma, :instance,
|
||||||
"text/bbcode"
|
"text/bbcode"
|
||||||
],
|
],
|
||||||
autofollowed_nicknames: [],
|
autofollowed_nicknames: [],
|
||||||
|
autofollowing_nicknames: [],
|
||||||
max_pinned_statuses: 1,
|
max_pinned_statuses: 1,
|
||||||
attachment_links: false,
|
attachment_links: false,
|
||||||
max_report_comment_size: 1000,
|
max_report_comment_size: 1000,
|
||||||
|
@ -551,6 +552,7 @@ config :pleroma, Oban,
|
||||||
queues: [
|
queues: [
|
||||||
activity_expiration: 10,
|
activity_expiration: 10,
|
||||||
token_expiration: 5,
|
token_expiration: 5,
|
||||||
|
backup: 1,
|
||||||
federator_incoming: 50,
|
federator_incoming: 50,
|
||||||
federator_outgoing: 50,
|
federator_outgoing: 50,
|
||||||
ingestion_queue: 50,
|
ingestion_queue: 50,
|
||||||
|
@ -636,7 +638,12 @@ config :pleroma, Pleroma.Emails.UserEmail,
|
||||||
|
|
||||||
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false
|
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false
|
||||||
|
|
||||||
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
|
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
|
||||||
|
enabled: false,
|
||||||
|
auth: false,
|
||||||
|
ip_whitelist: [],
|
||||||
|
path: "/api/pleroma/app_metrics",
|
||||||
|
format: :text
|
||||||
|
|
||||||
config :pleroma, Pleroma.ScheduledActivity,
|
config :pleroma, Pleroma.ScheduledActivity,
|
||||||
daily_user_limit: 25,
|
daily_user_limit: 25,
|
||||||
|
@ -830,6 +837,11 @@ config :floki, :html_parser, Floki.HTMLParser.FastHtml
|
||||||
|
|
||||||
config :pleroma, Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.PleromaAuthenticator
|
config :pleroma, Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.PleromaAuthenticator
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.User.Backup,
|
||||||
|
purge_after_days: 30,
|
||||||
|
limit_days: 7,
|
||||||
|
dir: nil
|
||||||
|
|
||||||
# Import environment specific config. This must remain at the bottom
|
# 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"
|
||||||
|
|
|
@ -831,6 +831,12 @@ config :pleroma, :config_description, [
|
||||||
description:
|
description:
|
||||||
"Set to nicknames of (local) users that every new user should automatically follow"
|
"Set to nicknames of (local) users that every new user should automatically follow"
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :autofollowing_nicknames,
|
||||||
|
type: {:list, :string},
|
||||||
|
description:
|
||||||
|
"Set to nicknames of (local) users that automatically follows every newly registered user"
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :attachment_links,
|
key: :attachment_links,
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
|
@ -1751,28 +1757,37 @@ config :pleroma, :config_description, [
|
||||||
related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
|
related_policy: "Pleroma.Web.ActivityPub.MRF.KeywordPolicy",
|
||||||
label: "MRF Keyword",
|
label: "MRF Keyword",
|
||||||
type: :group,
|
type: :group,
|
||||||
description: "Reject or Word-Replace messages with a keyword or regex",
|
description:
|
||||||
|
"Reject or Word-Replace messages matching a keyword or [Regex](https://hexdocs.pm/elixir/Regex.html).",
|
||||||
children: [
|
children: [
|
||||||
%{
|
%{
|
||||||
key: :reject,
|
key: :reject,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description:
|
description: """
|
||||||
"A list of patterns which result in message being rejected. Each pattern can be a string or a regular expression.",
|
A list of patterns which result in message being rejected.
|
||||||
|
|
||||||
|
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||||
|
""",
|
||||||
suggestions: ["foo", ~r/foo/iu]
|
suggestions: ["foo", ~r/foo/iu]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :federated_timeline_removal,
|
key: :federated_timeline_removal,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
description:
|
description: """
|
||||||
"A list of patterns which result in message being removed from federated timelines (a.k.a unlisted). Each pattern can be a string or a regular expression.",
|
A list of patterns which result in message being removed from federated timelines (a.k.a unlisted).
|
||||||
|
|
||||||
|
Each pattern can be a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||||
|
""",
|
||||||
suggestions: ["foo", ~r/foo/iu]
|
suggestions: ["foo", ~r/foo/iu]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :replace,
|
key: :replace,
|
||||||
type: {:list, :tuple},
|
type: {:list, :tuple},
|
||||||
description:
|
description: """
|
||||||
"A list of tuples containing {pattern, replacement}. Each pattern can be a string or a regular expression.",
|
**Pattern**: a string or [Regex](https://hexdocs.pm/elixir/Regex.html) in the format of `~r/PATTERN/`.
|
||||||
suggestions: [{"foo", "bar"}, {~r/foo/iu, "bar"}]
|
|
||||||
|
**Replacement**: a string. Leaving the field empty is permitted.
|
||||||
|
"""
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -2287,6 +2302,12 @@ config :pleroma, :config_description, [
|
||||||
description: "Activity expiration queue",
|
description: "Activity expiration queue",
|
||||||
suggestions: [10]
|
suggestions: [10]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :backup,
|
||||||
|
type: :integer,
|
||||||
|
description: "Backup queue",
|
||||||
|
suggestions: [1]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :attachments_cleanup,
|
key: :attachments_cleanup,
|
||||||
type: :integer,
|
type: :integer,
|
||||||
|
@ -3721,5 +3742,62 @@ config :pleroma, :config_description, [
|
||||||
suggestions: [2]
|
suggestions: [2]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: Pleroma.User.Backup,
|
||||||
|
type: :group,
|
||||||
|
description: "Account Backup",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :purge_after_days,
|
||||||
|
type: :integer,
|
||||||
|
description: "Remove backup achives after N days",
|
||||||
|
suggestions: [30]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :limit_days,
|
||||||
|
type: :integer,
|
||||||
|
description: "Limit user to export not more often than once per N days",
|
||||||
|
suggestions: [7]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: :prometheus,
|
||||||
|
key: Pleroma.Web.Endpoint.MetricsExporter,
|
||||||
|
type: :group,
|
||||||
|
description: "Prometheus app metrics endpoint configuration",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :enabled,
|
||||||
|
type: :boolean,
|
||||||
|
description: "[Pleroma extension] Enables app metrics endpoint."
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :ip_whitelist,
|
||||||
|
type: [{:list, :string}, {:list, :charlist}, {:list, :tuple}],
|
||||||
|
description:
|
||||||
|
"[Pleroma extension] If non-empty, restricts access to app metrics endpoint to specified IP addresses."
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :auth,
|
||||||
|
type: [:boolean, :tuple],
|
||||||
|
description: "Enables HTTP Basic Auth for app metrics endpoint.",
|
||||||
|
suggestion: [false, {:basic, "myusername", "mypassword"}]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :path,
|
||||||
|
type: :string,
|
||||||
|
description: "App metrics endpoint URI path.",
|
||||||
|
suggestions: ["/api/pleroma/app_metrics"]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :format,
|
||||||
|
type: :atom,
|
||||||
|
description: "App metrics endpoint output format.",
|
||||||
|
suggestions: [:text, :protobuf]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -20,12 +20,14 @@ Configuration options:
|
||||||
- `external`: only external users
|
- `external`: only external users
|
||||||
- `active`: only active users
|
- `active`: only active users
|
||||||
- `need_approval`: only unapproved users
|
- `need_approval`: only unapproved users
|
||||||
|
- `unconfirmed`: only unconfirmed users
|
||||||
- `deactivated`: only deactivated users
|
- `deactivated`: only deactivated users
|
||||||
- `is_admin`: users with admin role
|
- `is_admin`: users with admin role
|
||||||
- `is_moderator`: users with moderator role
|
- `is_moderator`: users with moderator role
|
||||||
- *optional* `page`: **integer** page number
|
- *optional* `page`: **integer** page number
|
||||||
- *optional* `page_size`: **integer** number of users per page (default is `50`)
|
- *optional* `page_size`: **integer** number of users per page (default is `50`)
|
||||||
- *optional* `tags`: **[string]** tags list
|
- *optional* `tags`: **[string]** tags list
|
||||||
|
- *optional* `actor_types`: **[string]** actor type list (`Person`, `Service`, `Application`)
|
||||||
- *optional* `name`: **string** user display name
|
- *optional* `name`: **string** user display name
|
||||||
- *optional* `email`: **string** user email
|
- *optional* `email`: **string** user email
|
||||||
- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com`
|
- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10&tags[]=some_tag&tags[]=another_tag&name=display_name&email=email@example.com`
|
||||||
|
|
|
@ -116,6 +116,10 @@ The modified chat message
|
||||||
This will return a list of chats that you have been involved in, sorted by their
|
This will return a list of chats that you have been involved in, sorted by their
|
||||||
last update (so new chats will be at the top).
|
last update (so new chats will be at the top).
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
|
||||||
|
- with_muted: Include chats from muted users (boolean).
|
||||||
|
|
||||||
Returned data:
|
Returned data:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
|
@ -173,11 +177,14 @@ Returned data:
|
||||||
"created_at": "2020-04-21T15:06:45.000Z",
|
"created_at": "2020-04-21T15:06:45.000Z",
|
||||||
"emojis": [],
|
"emojis": [],
|
||||||
"id": "12",
|
"id": "12",
|
||||||
"unread": false
|
"unread": false,
|
||||||
|
"idempotency_key": "75442486-0874-440c-9db1-a7006c25a31f"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- idempotency_key: The copy of the `idempotency-key` HTTP request header that can be used for optimistic message sending. Included only during the first few minutes after the message creation.
|
||||||
|
|
||||||
### Posting a chat message
|
### Posting a chat message
|
||||||
|
|
||||||
Posting a chat message for given Chat id works like this:
|
Posting a chat message for given Chat id works like this:
|
||||||
|
|
|
@ -9,9 +9,13 @@ Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mas
|
||||||
## Timelines
|
## Timelines
|
||||||
|
|
||||||
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
||||||
|
|
||||||
Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
Adding the parameter `exclude_visibilities` to the timeline queries will exclude the statuses with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`), e.g., `exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||||
|
|
||||||
Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
|
Adding the parameter `reply_visibility` to the public and home timelines queries will filter replies. Possible values: without parameter (default) shows all replies, `following` - replies directed to you or users you follow, `self` - replies directed to you.
|
||||||
|
|
||||||
|
Adding the parameter `instance=lain.com` to the public timeline will show only statuses originating from `lain.com` (or any remote instance).
|
||||||
|
|
||||||
## Statuses
|
## Statuses
|
||||||
|
|
||||||
- `visibility`: has an additional possible value `list`
|
- `visibility`: has an additional possible value `list`
|
||||||
|
@ -249,6 +253,8 @@ Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
|
There is an additional `user:pleroma_chat` stream. Incoming chat messages will make the current chat be sent to this `user` stream. The `event` of an incoming chat message is `pleroma:chat_update`. The payload is the updated chat with the incoming chat message in the `last_message` field.
|
||||||
|
|
||||||
|
For viewing remote server timelines, there are `public:remote` and `public:remote:media` streams. Each of these accept a parameter like `?instance=lain.com`.
|
||||||
|
|
||||||
## Not implemented
|
## Not implemented
|
||||||
|
|
||||||
Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.
|
Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer features and non-essential features are omitted. These features usually return an HTTP 200 status code, but with an empty response. While they may be added in the future, they are considered low priority.
|
||||||
|
|
|
@ -615,3 +615,41 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
|
||||||
{"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
|
{"name": "😀", "count": 2, "me": true, "accounts": [{"id" => "xyz.."...}, {"id" => "zyx..."}]}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `POST /api/v1/pleroma/backups`
|
||||||
|
### Create a user backup archive
|
||||||
|
|
||||||
|
* Method: `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Params: none
|
||||||
|
* Response: JSON
|
||||||
|
* Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[{
|
||||||
|
"content_type": "application/zip",
|
||||||
|
"file_size": 0,
|
||||||
|
"inserted_at": "2020-09-10T16:18:03.000Z",
|
||||||
|
"processed": false,
|
||||||
|
"url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip"
|
||||||
|
}]
|
||||||
|
```
|
||||||
|
|
||||||
|
## `GET /api/v1/pleroma/backups`
|
||||||
|
### Lists user backups
|
||||||
|
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params: none
|
||||||
|
* Response: JSON
|
||||||
|
* Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[{
|
||||||
|
"content_type": "application/zip",
|
||||||
|
"file_size": 55457,
|
||||||
|
"inserted_at": "2020-09-10T16:18:03.000Z",
|
||||||
|
"processed": true,
|
||||||
|
"url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip"
|
||||||
|
}]
|
||||||
|
```
|
||||||
|
|
|
@ -2,15 +2,37 @@
|
||||||
|
|
||||||
Pleroma includes support for exporting metrics via the [prometheus_ex](https://github.com/deadtrickster/prometheus.ex) library.
|
Pleroma includes support for exporting metrics via the [prometheus_ex](https://github.com/deadtrickster/prometheus.ex) library.
|
||||||
|
|
||||||
|
Config example:
|
||||||
|
|
||||||
|
```
|
||||||
|
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
|
||||||
|
enabled: true,
|
||||||
|
auth: {:basic, "myusername", "mypassword"},
|
||||||
|
ip_whitelist: ["127.0.0.1"],
|
||||||
|
path: "/api/pleroma/app_metrics",
|
||||||
|
format: :text
|
||||||
|
```
|
||||||
|
|
||||||
|
* `enabled` (Pleroma extension) enables the endpoint
|
||||||
|
* `ip_whitelist` (Pleroma extension) could be used to restrict access only to specified IPs
|
||||||
|
* `auth` sets the authentication (`false` for no auth; configurable to HTTP Basic Auth, see [prometheus-plugs](https://github.com/deadtrickster/prometheus-plugs#exporting) documentation)
|
||||||
|
* `format` sets the output format (`:text` or `:protobuf`)
|
||||||
|
* `path` sets the path to app metrics page
|
||||||
|
|
||||||
|
|
||||||
## `/api/pleroma/app_metrics`
|
## `/api/pleroma/app_metrics`
|
||||||
|
|
||||||
### Exports Prometheus application metrics
|
### Exports Prometheus application metrics
|
||||||
|
|
||||||
* Method: `GET`
|
* Method: `GET`
|
||||||
* Authentication: not required
|
* Authentication: not required by default (see configuration options above)
|
||||||
* Params: none
|
* Params: none
|
||||||
* Response: JSON
|
* Response: text
|
||||||
|
|
||||||
## Grafana
|
## Grafana
|
||||||
|
|
||||||
### Config example
|
### Config example
|
||||||
|
|
||||||
The following is a config example to use with [Grafana](https://grafana.com)
|
The following is a config example to use with [Grafana](https://grafana.com)
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
|
@ -1,12 +1,23 @@
|
||||||
# Managing frontends
|
# Managing frontends
|
||||||
|
|
||||||
`mix pleroma.frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]`
|
=== "OTP"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./bin/pleroma_ctl frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "From Source"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mix pleroma.frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>]
|
||||||
|
```
|
||||||
|
|
||||||
Frontend can be installed either from local zip file, or automatically downloaded from the web.
|
Frontend can be installed either from local zip file, or automatically downloaded from the web.
|
||||||
|
|
||||||
You can give all the options directly on the command like, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
|
You can give all the options directly on the command line, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
|
||||||
|
|
||||||
|
Currently, known `<frontend>` values are:
|
||||||
|
|
||||||
Currently known `<frontend>` values are:
|
|
||||||
- [admin-fe](https://git.pleroma.social/pleroma/admin-fe)
|
- [admin-fe](https://git.pleroma.social/pleroma/admin-fe)
|
||||||
- [kenoma](http://git.pleroma.social/lambadalambda/kenoma)
|
- [kenoma](http://git.pleroma.social/lambadalambda/kenoma)
|
||||||
- [pleroma-fe](http://git.pleroma.social/pleroma/pleroma-fe)
|
- [pleroma-fe](http://git.pleroma.social/pleroma/pleroma-fe)
|
||||||
|
@ -19,51 +30,67 @@ You can still install frontends that are not configured, see below.
|
||||||
|
|
||||||
For a frontend configured under the `available` key, it's enough to install it by name.
|
For a frontend configured under the `available` key, it's enough to install it by name.
|
||||||
|
|
||||||
```sh tab="OTP"
|
=== "OTP"
|
||||||
./bin/pleroma_ctl frontend install pleroma
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh tab="From Source"
|
```sh
|
||||||
mix pleroma.frontend install pleroma
|
./bin/pleroma_ctl frontend install pleroma
|
||||||
```
|
```
|
||||||
|
|
||||||
This will download the latest build for the the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
|
=== "From Source"
|
||||||
|
|
||||||
You can override any of the details. To install a pleroma build from a different url, you could do this:
|
```sh
|
||||||
|
mix pleroma.frontend install pleroma
|
||||||
|
```
|
||||||
|
|
||||||
```sh tab="OPT"
|
This will download the latest build for the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
|
||||||
./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh tab="From Source"
|
You can override any of the details. To install a pleroma build from a different URL, you could do this:
|
||||||
mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
|
|
||||||
```
|
=== "OTP"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "From Source"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
|
||||||
|
```
|
||||||
|
|
||||||
Similarly, you can also install from a local zip file.
|
Similarly, you can also install from a local zip file.
|
||||||
|
|
||||||
```sh tab="OTP"
|
=== "OTP"
|
||||||
./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh tab="From Source"
|
```sh
|
||||||
mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
|
./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
|
||||||
```
|
```
|
||||||
|
|
||||||
The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`
|
=== "From Source"
|
||||||
|
|
||||||
Careful: This folder will be completely replaced on installation
|
```sh
|
||||||
|
mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
|
||||||
|
|
||||||
|
Careful: This folder will be completely replaced on installation.
|
||||||
|
|
||||||
## Example installation for an unknown frontend
|
## Example installation for an unknown frontend
|
||||||
|
|
||||||
The installation process is the same, but you will have to give all the needed options on the commond line. For example:
|
The installation process is the same, but you will have to give all the needed options on the command line. For example:
|
||||||
|
|
||||||
```sh tab="OTP"
|
=== "OTP"
|
||||||
./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
|
|
||||||
```
|
|
||||||
|
|
||||||
```sh tab="From Source"
|
```sh
|
||||||
mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
|
./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
|
||||||
```
|
```
|
||||||
|
|
||||||
If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`
|
=== "From Source"
|
||||||
|
|
||||||
|
```sh
|
||||||
|
mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ To add configuration to your config file, you can copy it from the base config.
|
||||||
older software for theses nicknames.
|
older software for theses nicknames.
|
||||||
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
|
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
|
||||||
* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
|
* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
|
||||||
|
* `autofollowing_nicknames`: Set to nicknames of (local) users that automatically follows every newly registered user.
|
||||||
* `attachment_links`: Set to true to enable automatically adding attachment link text to statuses.
|
* `attachment_links`: Set to true to enable automatically adding attachment link text to statuses.
|
||||||
* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`).
|
* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`).
|
||||||
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`.
|
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). Default: `false`.
|
||||||
|
@ -1077,6 +1078,20 @@ Control favicons for instances.
|
||||||
|
|
||||||
* `enabled`: Allow/disallow displaying and getting instances favicons
|
* `enabled`: Allow/disallow displaying and getting instances favicons
|
||||||
|
|
||||||
|
## Pleroma.User.Backup
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Requires enabled email
|
||||||
|
|
||||||
|
* `:purge_after_days` an integer, remove backup achives after N days.
|
||||||
|
* `:limit_days` an integer, limit user to export not more often than once per N days.
|
||||||
|
* `:dir` a string with a path to backup temporary directory or `nil` to let Pleroma choose temporary directory in the following order:
|
||||||
|
1. the directory named by the TMPDIR environment variable
|
||||||
|
2. the directory named by the TEMP environment variable
|
||||||
|
3. the directory named by the TMP environment variable
|
||||||
|
4. C:\TMP on Windows or /tmp on Unix-like operating systems
|
||||||
|
5. as a last resort, the current working directory
|
||||||
|
|
||||||
## Frontend management
|
## Frontend management
|
||||||
|
|
||||||
Frontends in Pleroma are swappable - you can specify which one to use here.
|
Frontends in Pleroma are swappable - you can specify which one to use here.
|
||||||
|
|
|
@ -14,6 +14,7 @@ defmodule Pleroma.Activity do
|
||||||
alias Pleroma.ReportNote
|
alias Pleroma.ReportNote
|
||||||
alias Pleroma.ThreadMute
|
alias Pleroma.ThreadMute
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
@ -153,6 +154,18 @@ defmodule Pleroma.Activity do
|
||||||
|
|
||||||
def get_bookmark(_, _), do: nil
|
def get_bookmark(_, _), do: nil
|
||||||
|
|
||||||
|
def get_report(activity_id) do
|
||||||
|
opts = %{
|
||||||
|
type: "Flag",
|
||||||
|
skip_preload: true,
|
||||||
|
preload_report_notes: true
|
||||||
|
}
|
||||||
|
|
||||||
|
ActivityPub.fetch_activities_query([], opts)
|
||||||
|
|> where(id: ^activity_id)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
def change(struct, params \\ %{}) do
|
def change(struct, params \\ %{}) do
|
||||||
struct
|
struct
|
||||||
|> cast(params, [:data, :recipients])
|
|> cast(params, [:data, :recipients])
|
||||||
|
|
|
@ -40,7 +40,8 @@ defmodule Pleroma.Activity.Ir.Topics do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
|
defp item_creation_tags(tags, object, %{data: %{"type" => "Create"}} = activity) do
|
||||||
tags ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
|
tags ++
|
||||||
|
remote_topics(activity) ++ hashtags_to_topics(object) ++ attachment_topics(object, activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp item_creation_tags(tags, _, _) do
|
defp item_creation_tags(tags, _, _) do
|
||||||
|
@ -55,9 +56,19 @@ defmodule Pleroma.Activity.Ir.Topics do
|
||||||
|
|
||||||
defp hashtags_to_topics(_), do: []
|
defp hashtags_to_topics(_), do: []
|
||||||
|
|
||||||
|
defp remote_topics(%{local: true}), do: []
|
||||||
|
|
||||||
|
defp remote_topics(%{actor: actor}) when is_binary(actor),
|
||||||
|
do: ["public:remote:" <> URI.parse(actor).host]
|
||||||
|
|
||||||
|
defp remote_topics(_), do: []
|
||||||
|
|
||||||
defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: []
|
defp attachment_topics(%{data: %{"attachment" => []}}, _act), do: []
|
||||||
|
|
||||||
defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"]
|
defp attachment_topics(_object, %{local: true}), do: ["public:media", "public:local:media"]
|
||||||
|
|
||||||
|
defp attachment_topics(_object, %{actor: actor}) when is_binary(actor),
|
||||||
|
do: ["public:media", "public:remote:media:" <> URI.parse(actor).host]
|
||||||
|
|
||||||
defp attachment_topics(_object, _act), do: ["public:media"]
|
defp attachment_topics(_object, _act), do: ["public:media"]
|
||||||
end
|
end
|
||||||
|
|
|
@ -168,7 +168,11 @@ defmodule Pleroma.Application do
|
||||||
build_cachex("web_resp", limit: 2500),
|
build_cachex("web_resp", limit: 2500),
|
||||||
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
|
build_cachex("emoji_packs", expiration: emoji_packs_expiration(), limit: 10),
|
||||||
build_cachex("failed_proxy_url", limit: 2500),
|
build_cachex("failed_proxy_url", limit: 2500),
|
||||||
build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000)
|
build_cachex("banned_urls", default_ttl: :timer.hours(24 * 30), limit: 5_000),
|
||||||
|
build_cachex("chat_message_id_idempotency_key",
|
||||||
|
expiration: chat_message_id_idempotency_key_expiration(),
|
||||||
|
limit: 500_000
|
||||||
|
)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -178,6 +182,9 @@ defmodule Pleroma.Application do
|
||||||
defp idempotency_expiration,
|
defp idempotency_expiration,
|
||||||
do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))
|
do: expiration(default: :timer.seconds(6 * 60 * 60), interval: :timer.seconds(60))
|
||||||
|
|
||||||
|
defp chat_message_id_idempotency_key_expiration,
|
||||||
|
do: expiration(default: :timer.minutes(2), interval: :timer.seconds(60))
|
||||||
|
|
||||||
defp seconds_valid_interval,
|
defp seconds_valid_interval,
|
||||||
do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid]))
|
do: :timer.seconds(Config.get!([Pleroma.Captcha, :seconds_valid]))
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Captcha.Kocaptcha do
|
||||||
def new do
|
def new do
|
||||||
endpoint = Pleroma.Config.get!([__MODULE__, :endpoint])
|
endpoint = Pleroma.Config.get!([__MODULE__, :endpoint])
|
||||||
|
|
||||||
case Tesla.get(endpoint <> "/new") do
|
case Pleroma.HTTP.get(endpoint <> "/new") do
|
||||||
{:error, _} ->
|
{:error, _} ->
|
||||||
%{error: :kocaptcha_service_unavailable}
|
%{error: :kocaptcha_service_unavailable}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ defmodule Pleroma.Conversation do
|
||||||
def maybe_create_recipientships(participation, activity) do
|
def maybe_create_recipientships(participation, activity) do
|
||||||
participation = Repo.preload(participation, :recipients)
|
participation = Repo.preload(participation, :recipients)
|
||||||
|
|
||||||
if participation.recipients |> Enum.empty?() do
|
if Enum.empty?(participation.recipients) do
|
||||||
recipients = User.get_all_by_ap_id(activity.recipients)
|
recipients = User.get_all_by_ap_id(activity.recipients)
|
||||||
RecipientShip.create(recipients, participation)
|
RecipientShip.create(recipients, participation)
|
||||||
end
|
end
|
||||||
|
@ -69,10 +69,6 @@ defmodule Pleroma.Conversation do
|
||||||
Enum.map(users, fn user ->
|
Enum.map(users, fn user ->
|
||||||
invisible_conversation = Enum.any?(users, &User.blocks?(user, &1))
|
invisible_conversation = Enum.any?(users, &User.blocks?(user, &1))
|
||||||
|
|
||||||
unless invisible_conversation do
|
|
||||||
User.increment_unread_conversation_count(conversation, user)
|
|
||||||
end
|
|
||||||
|
|
||||||
opts = Keyword.put(opts, :invisible_conversation, invisible_conversation)
|
opts = Keyword.put(opts, :invisible_conversation, invisible_conversation)
|
||||||
|
|
||||||
{:ok, participation} =
|
{:ok, participation} =
|
||||||
|
|
|
@ -63,21 +63,10 @@ defmodule Pleroma.Conversation.Participation do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_as_read(participation) do
|
def mark_as_read(%__MODULE__{} = participation) do
|
||||||
__MODULE__
|
participation
|
||||||
|> where(id: ^participation.id)
|
|> change(read: true)
|
||||||
|> update(set: [read: true])
|
|> Repo.update()
|
||||||
|> select([p], p)
|
|
||||||
|> Repo.update_all([])
|
|
||||||
|> case do
|
|
||||||
{1, [participation]} ->
|
|
||||||
participation = Repo.preload(participation, :user)
|
|
||||||
User.set_unread_conversation_count(participation.user)
|
|
||||||
{:ok, participation}
|
|
||||||
|
|
||||||
error ->
|
|
||||||
error
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do
|
def mark_all_as_read(%User{local: true} = user, %User{} = target_user) do
|
||||||
|
@ -93,7 +82,6 @@ defmodule Pleroma.Conversation.Participation do
|
||||||
|> update([p], set: [read: true])
|
|> update([p], set: [read: true])
|
||||||
|> Repo.update_all([])
|
|> Repo.update_all([])
|
||||||
|
|
||||||
{:ok, user} = User.set_unread_conversation_count(user)
|
|
||||||
{:ok, user, []}
|
{:ok, user, []}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -108,7 +96,6 @@ defmodule Pleroma.Conversation.Participation do
|
||||||
|> select([p], p)
|
|> select([p], p)
|
||||||
|> Repo.update_all([])
|
|> Repo.update_all([])
|
||||||
|
|
||||||
{:ok, user} = User.set_unread_conversation_count(user)
|
|
||||||
{:ok, user, participations}
|
{:ok, user, participations}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -220,6 +207,12 @@ defmodule Pleroma.Conversation.Participation do
|
||||||
{:ok, Repo.preload(participation, :recipients, force: true)}
|
{:ok, Repo.preload(participation, :recipients, force: true)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec unread_count(User.t()) :: integer()
|
||||||
|
def unread_count(%User{id: user_id}) do
|
||||||
|
from(q in __MODULE__, where: q.user_id == ^user_id and q.read == false)
|
||||||
|
|> Repo.aggregate(:count, :id)
|
||||||
|
end
|
||||||
|
|
||||||
def unread_conversation_count_for_user(user) do
|
def unread_conversation_count_for_user(user) do
|
||||||
from(p in __MODULE__,
|
from(p in __MODULE__,
|
||||||
where: p.user_id == ^user.id,
|
where: p.user_id == ^user.id,
|
||||||
|
|
|
@ -189,4 +189,30 @@ defmodule Pleroma.Emails.UserEmail do
|
||||||
|
|
||||||
Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
|
Router.Helpers.subscription_url(Endpoint, :unsubscribe, token)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def backup_is_ready_email(backup, admin_user_id \\ nil) do
|
||||||
|
%{user: user} = Pleroma.Repo.preload(backup, :user)
|
||||||
|
download_url = Pleroma.Web.PleromaAPI.BackupView.download_url(backup)
|
||||||
|
|
||||||
|
html_body =
|
||||||
|
if is_nil(admin_user_id) do
|
||||||
|
"""
|
||||||
|
<p>You requested a full backup of your Pleroma account. It's ready for download:</p>
|
||||||
|
<p><a href="#{download_url}">#{download_url}</a></p>
|
||||||
|
"""
|
||||||
|
else
|
||||||
|
admin = Pleroma.Repo.get(User, admin_user_id)
|
||||||
|
|
||||||
|
"""
|
||||||
|
<p>Admin @#{admin.nickname} requested a full backup of your Pleroma account. It's ready for download:</p>
|
||||||
|
<p><a href="#{download_url}">#{download_url}</a></p>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
new()
|
||||||
|
|> to(recipient(user))
|
||||||
|
|> from(sender())
|
||||||
|
|> subject("Your account archive is ready")
|
||||||
|
|> html_body(html_body)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -594,7 +594,7 @@ defmodule Pleroma.Emoji.Pack do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp download_archive(url, sha) do
|
defp download_archive(url, sha) do
|
||||||
with {:ok, %{body: archive}} <- Tesla.get(url) do
|
with {:ok, %{body: archive}} <- Pleroma.HTTP.get(url) do
|
||||||
if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do
|
if Base.decode16!(sha) == :crypto.hash(:sha256, archive) do
|
||||||
{:ok, archive}
|
{:ok, archive}
|
||||||
else
|
else
|
||||||
|
@ -617,7 +617,7 @@ defmodule Pleroma.Emoji.Pack do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp update_sha_and_save_metadata(pack, data) do
|
defp update_sha_and_save_metadata(pack, data) do
|
||||||
with {:ok, %{body: zip}} <- Tesla.get(data[:"fallback-src"]),
|
with {:ok, %{body: zip}} <- Pleroma.HTTP.get(data[:"fallback-src"]),
|
||||||
:ok <- validate_has_all_files(pack, zip) do
|
:ok <- validate_has_all_files(pack, zip) do
|
||||||
fallback_sha = :sha256 |> :crypto.hash(zip) |> Base.encode16()
|
fallback_sha = :sha256 |> :crypto.hash(zip) |> Base.encode16()
|
||||||
|
|
||||||
|
|
19
lib/pleroma/helpers/inet_helper.ex
Normal file
19
lib/pleroma/helpers/inet_helper.ex
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Helpers.InetHelper do
|
||||||
|
def parse_address(ip) when is_tuple(ip) do
|
||||||
|
{:ok, ip}
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_address(ip) when is_binary(ip) do
|
||||||
|
ip
|
||||||
|
|> String.to_charlist()
|
||||||
|
|> parse_address()
|
||||||
|
end
|
||||||
|
|
||||||
|
def parse_address(ip) do
|
||||||
|
:inet.parse_address(ip)
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,6 +11,7 @@ defmodule Pleroma.Instances do
|
||||||
defdelegate reachable?(url_or_host), to: @adapter
|
defdelegate reachable?(url_or_host), to: @adapter
|
||||||
defdelegate set_reachable(url_or_host), to: @adapter
|
defdelegate set_reachable(url_or_host), to: @adapter
|
||||||
defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter
|
defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter
|
||||||
|
defdelegate get_consistently_unreachable(), to: @adapter
|
||||||
|
|
||||||
def set_consistently_unreachable(url_or_host),
|
def set_consistently_unreachable(url_or_host),
|
||||||
do: set_unreachable(url_or_host, reachability_datetime_threshold())
|
do: set_unreachable(url_or_host, reachability_datetime_threshold())
|
||||||
|
|
|
@ -119,6 +119,17 @@ defmodule Pleroma.Instances.Instance do
|
||||||
|
|
||||||
def set_unreachable(_, _), do: {:error, nil}
|
def set_unreachable(_, _), do: {:error, nil}
|
||||||
|
|
||||||
|
def get_consistently_unreachable do
|
||||||
|
reachability_datetime_threshold = Instances.reachability_datetime_threshold()
|
||||||
|
|
||||||
|
from(i in Instance,
|
||||||
|
where: ^reachability_datetime_threshold > i.unreachable_since,
|
||||||
|
order_by: i.unreachable_since,
|
||||||
|
select: {i.host, i.unreachable_since}
|
||||||
|
)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
defp parse_datetime(datetime) when is_binary(datetime) do
|
defp parse_datetime(datetime) when is_binary(datetime) do
|
||||||
NaiveDateTime.from_iso8601(datetime)
|
NaiveDateTime.from_iso8601(datetime)
|
||||||
end
|
end
|
||||||
|
|
|
@ -655,6 +655,16 @@ defmodule Pleroma.ModerationLog do
|
||||||
"@#{actor_nickname} deleted chat message ##{subject_id}"
|
"@#{actor_nickname} deleted chat message ##{subject_id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_log_entry_message(%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "create_backup",
|
||||||
|
"subject" => %{"nickname" => user_nickname}
|
||||||
|
}
|
||||||
|
}) do
|
||||||
|
"@#{actor_nickname} requested account backup for @#{user_nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
defp nicknames_to_string(nicknames) do
|
defp nicknames_to_string(nicknames) do
|
||||||
nicknames
|
nicknames
|
||||||
|> Enum.map(&"@#{&1}")
|
|> Enum.map(&"@#{&1}")
|
||||||
|
|
|
@ -128,7 +128,6 @@ defmodule Pleroma.User do
|
||||||
field(:hide_followers, :boolean, default: false)
|
field(:hide_followers, :boolean, default: false)
|
||||||
field(:hide_follows, :boolean, default: false)
|
field(:hide_follows, :boolean, default: false)
|
||||||
field(:hide_favorites, :boolean, default: true)
|
field(:hide_favorites, :boolean, default: true)
|
||||||
field(:unread_conversation_count, :integer, default: 0)
|
|
||||||
field(:pinned_activities, {:array, :string}, default: [])
|
field(:pinned_activities, {:array, :string}, default: [])
|
||||||
field(:email_notifications, :map, default: %{"digest" => false})
|
field(:email_notifications, :map, default: %{"digest" => false})
|
||||||
field(:mascot, :map, default: nil)
|
field(:mascot, :map, default: nil)
|
||||||
|
@ -426,7 +425,6 @@ defmodule Pleroma.User do
|
||||||
params,
|
params,
|
||||||
[
|
[
|
||||||
:bio,
|
:bio,
|
||||||
:name,
|
|
||||||
:emoji,
|
:emoji,
|
||||||
:ap_id,
|
:ap_id,
|
||||||
:inbox,
|
:inbox,
|
||||||
|
@ -455,7 +453,9 @@ defmodule Pleroma.User do
|
||||||
:accepts_chat_messages
|
:accepts_chat_messages
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|> validate_required([:name, :ap_id])
|
|> cast(params, [:name], empty_values: [])
|
||||||
|
|> validate_required([:ap_id])
|
||||||
|
|> validate_required([:name], trim: false)
|
||||||
|> unique_constraint(:nickname)
|
|> unique_constraint(:nickname)
|
||||||
|> validate_format(:nickname, @email_regex)
|
|> validate_format(:nickname, @email_regex)
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|> validate_length(:bio, max: bio_limit)
|
||||||
|
@ -765,6 +765,16 @@ defmodule Pleroma.User do
|
||||||
follow_all(user, autofollowed_users)
|
follow_all(user, autofollowed_users)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp autofollowing_users(user) do
|
||||||
|
candidates = Config.get([:instance, :autofollowing_nicknames])
|
||||||
|
|
||||||
|
User.Query.build(%{nickname: candidates, local: true, deactivated: false})
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.each(&follow(&1, user, :follow_accept))
|
||||||
|
|
||||||
|
{:ok, :success}
|
||||||
|
end
|
||||||
|
|
||||||
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
@doc "Inserts provided changeset, performs post-registration actions (confirmation email sending etc.)"
|
||||||
def register(%Ecto.Changeset{} = changeset) do
|
def register(%Ecto.Changeset{} = changeset) do
|
||||||
with {:ok, user} <- Repo.insert(changeset) do
|
with {:ok, user} <- Repo.insert(changeset) do
|
||||||
|
@ -774,6 +784,7 @@ defmodule Pleroma.User do
|
||||||
|
|
||||||
def post_register_action(%User{} = user) do
|
def post_register_action(%User{} = user) do
|
||||||
with {:ok, user} <- autofollow_users(user),
|
with {:ok, user} <- autofollow_users(user),
|
||||||
|
{:ok, _} <- autofollowing_users(user),
|
||||||
{:ok, user} <- set_cache(user),
|
{:ok, user} <- set_cache(user),
|
||||||
{:ok, _} <- send_welcome_email(user),
|
{:ok, _} <- send_welcome_email(user),
|
||||||
{:ok, _} <- send_welcome_message(user),
|
{:ok, _} <- send_welcome_message(user),
|
||||||
|
@ -1293,47 +1304,6 @@ defmodule Pleroma.User do
|
||||||
|> update_and_set_cache()
|
|> update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_unread_conversation_count(%User{local: true} = user) do
|
|
||||||
unread_query = Participation.unread_conversation_count_for_user(user)
|
|
||||||
|
|
||||||
User
|
|
||||||
|> join(:inner, [u], p in subquery(unread_query))
|
|
||||||
|> update([u, p],
|
|
||||||
set: [unread_conversation_count: p.count]
|
|
||||||
)
|
|
||||||
|> where([u], u.id == ^user.id)
|
|
||||||
|> select([u], u)
|
|
||||||
|> Repo.update_all([])
|
|
||||||
|> case do
|
|
||||||
{1, [user]} -> set_cache(user)
|
|
||||||
_ -> {:error, user}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_unread_conversation_count(user), do: {:ok, user}
|
|
||||||
|
|
||||||
def increment_unread_conversation_count(conversation, %User{local: true} = user) do
|
|
||||||
unread_query =
|
|
||||||
Participation.unread_conversation_count_for_user(user)
|
|
||||||
|> where([p], p.conversation_id == ^conversation.id)
|
|
||||||
|
|
||||||
User
|
|
||||||
|> join(:inner, [u], p in subquery(unread_query))
|
|
||||||
|> update([u, p],
|
|
||||||
inc: [unread_conversation_count: 1]
|
|
||||||
)
|
|
||||||
|> where([u], u.id == ^user.id)
|
|
||||||
|> where([u, p], p.count == 0)
|
|
||||||
|> select([u], u)
|
|
||||||
|> Repo.update_all([])
|
|
||||||
|> case do
|
|
||||||
{1, [user]} -> set_cache(user)
|
|
||||||
_ -> {:error, user}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def increment_unread_conversation_count(_, user), do: {:ok, user}
|
|
||||||
|
|
||||||
@spec get_users_from_set([String.t()], keyword()) :: [User.t()]
|
@spec get_users_from_set([String.t()], keyword()) :: [User.t()]
|
||||||
def get_users_from_set(ap_ids, opts \\ []) do
|
def get_users_from_set(ap_ids, opts \\ []) do
|
||||||
local_only = Keyword.get(opts, :local_only, true)
|
local_only = Keyword.get(opts, :local_only, true)
|
||||||
|
|
258
lib/pleroma/user/backup.ex
Normal file
258
lib/pleroma/user/backup.ex
Normal file
|
@ -0,0 +1,258 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.User.Backup do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
import Pleroma.Web.Gettext
|
||||||
|
|
||||||
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Bookmark
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.UserView
|
||||||
|
alias Pleroma.Workers.BackupWorker
|
||||||
|
|
||||||
|
schema "backups" do
|
||||||
|
field(:content_type, :string)
|
||||||
|
field(:file_name, :string)
|
||||||
|
field(:file_size, :integer, default: 0)
|
||||||
|
field(:processed, :boolean, default: false)
|
||||||
|
|
||||||
|
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(user, admin_id \\ nil) do
|
||||||
|
with :ok <- validate_email_enabled(),
|
||||||
|
:ok <- validate_user_email(user),
|
||||||
|
:ok <- validate_limit(user, admin_id),
|
||||||
|
{:ok, backup} <- user |> new() |> Repo.insert() do
|
||||||
|
BackupWorker.process(backup, admin_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def new(user) do
|
||||||
|
rand_str = :crypto.strong_rand_bytes(32) |> Base.url_encode64(padding: false)
|
||||||
|
datetime = Calendar.NaiveDateTime.Format.iso8601_basic(NaiveDateTime.utc_now())
|
||||||
|
name = "archive-#{user.nickname}-#{datetime}-#{rand_str}.zip"
|
||||||
|
|
||||||
|
%__MODULE__{
|
||||||
|
user_id: user.id,
|
||||||
|
content_type: "application/zip",
|
||||||
|
file_name: name
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(backup) do
|
||||||
|
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||||
|
|
||||||
|
with :ok <- uploader.delete_file(Path.join("backups", backup.file_name)) do
|
||||||
|
Repo.delete(backup)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_limit(_user, admin_id) when is_binary(admin_id), do: :ok
|
||||||
|
|
||||||
|
defp validate_limit(user, nil) do
|
||||||
|
case get_last(user.id) do
|
||||||
|
%__MODULE__{inserted_at: inserted_at} ->
|
||||||
|
days = Pleroma.Config.get([__MODULE__, :limit_days])
|
||||||
|
diff = Timex.diff(NaiveDateTime.utc_now(), inserted_at, :days)
|
||||||
|
|
||||||
|
if diff > days do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
{:error,
|
||||||
|
dngettext(
|
||||||
|
"errors",
|
||||||
|
"Last export was less than a day ago",
|
||||||
|
"Last export was less than %{days} days ago",
|
||||||
|
days,
|
||||||
|
days: days
|
||||||
|
)}
|
||||||
|
end
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_email_enabled do
|
||||||
|
if Pleroma.Config.get([Pleroma.Emails.Mailer, :enabled]) do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
{:error, dgettext("errors", "Backups require enabled email")}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_user_email(%User{email: nil}) do
|
||||||
|
{:error, dgettext("errors", "Email is required")}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_user_email(%User{email: email}) when is_binary(email), do: :ok
|
||||||
|
|
||||||
|
def get_last(user_id) do
|
||||||
|
__MODULE__
|
||||||
|
|> where(user_id: ^user_id)
|
||||||
|
|> order_by(desc: :id)
|
||||||
|
|> limit(1)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
|
def list(%User{id: user_id}) do
|
||||||
|
__MODULE__
|
||||||
|
|> where(user_id: ^user_id)
|
||||||
|
|> order_by(desc: :id)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_outdated(%__MODULE__{id: latest_id, user_id: user_id}) do
|
||||||
|
__MODULE__
|
||||||
|
|> where(user_id: ^user_id)
|
||||||
|
|> where([b], b.id != ^latest_id)
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.each(&BackupWorker.delete/1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(id), do: Repo.get(__MODULE__, id)
|
||||||
|
|
||||||
|
def process(%__MODULE__{} = backup) do
|
||||||
|
with {:ok, zip_file} <- export(backup),
|
||||||
|
{:ok, %{size: size}} <- File.stat(zip_file),
|
||||||
|
{:ok, _upload} <- upload(backup, zip_file) do
|
||||||
|
backup
|
||||||
|
|> cast(%{file_size: size, processed: true}, [:file_size, :processed])
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@files ['actor.json', 'outbox.json', 'likes.json', 'bookmarks.json']
|
||||||
|
def export(%__MODULE__{} = backup) do
|
||||||
|
backup = Repo.preload(backup, :user)
|
||||||
|
name = String.trim_trailing(backup.file_name, ".zip")
|
||||||
|
dir = dir(name)
|
||||||
|
|
||||||
|
with :ok <- File.mkdir(dir),
|
||||||
|
:ok <- actor(dir, backup.user),
|
||||||
|
:ok <- statuses(dir, backup.user),
|
||||||
|
:ok <- likes(dir, backup.user),
|
||||||
|
:ok <- bookmarks(dir, backup.user),
|
||||||
|
{:ok, zip_path} <- :zip.create(String.to_charlist(dir <> ".zip"), @files, cwd: dir),
|
||||||
|
{:ok, _} <- File.rm_rf(dir) do
|
||||||
|
{:ok, to_string(zip_path)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def dir(name) do
|
||||||
|
dir = Pleroma.Config.get([__MODULE__, :dir]) || System.tmp_dir!()
|
||||||
|
Path.join(dir, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def upload(%__MODULE__{} = backup, zip_path) do
|
||||||
|
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||||
|
|
||||||
|
upload = %Pleroma.Upload{
|
||||||
|
name: backup.file_name,
|
||||||
|
tempfile: zip_path,
|
||||||
|
content_type: backup.content_type,
|
||||||
|
path: Path.join("backups", backup.file_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
with {:ok, _} <- Pleroma.Uploaders.Uploader.put_file(uploader, upload),
|
||||||
|
:ok <- File.rm(zip_path) do
|
||||||
|
{:ok, upload}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp actor(dir, user) do
|
||||||
|
with {:ok, json} <-
|
||||||
|
UserView.render("user.json", %{user: user})
|
||||||
|
|> Map.merge(%{"likes" => "likes.json", "bookmarks" => "bookmarks.json"})
|
||||||
|
|> Jason.encode() do
|
||||||
|
File.write(Path.join(dir, "actor.json"), json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp write_header(file, name) do
|
||||||
|
IO.write(
|
||||||
|
file,
|
||||||
|
"""
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "#{name}.json",
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"orderedItems": [
|
||||||
|
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp write(query, dir, name, fun) do
|
||||||
|
path = Path.join(dir, "#{name}.json")
|
||||||
|
|
||||||
|
with {:ok, file} <- File.open(path, [:write, :utf8]),
|
||||||
|
:ok <- write_header(file, name) do
|
||||||
|
total =
|
||||||
|
query
|
||||||
|
|> Pleroma.Repo.chunk_stream(100)
|
||||||
|
|> Enum.reduce(0, fn i, acc ->
|
||||||
|
with {:ok, data} <- fun.(i),
|
||||||
|
{:ok, str} <- Jason.encode(data),
|
||||||
|
:ok <- IO.write(file, str <> ",\n") do
|
||||||
|
acc + 1
|
||||||
|
else
|
||||||
|
_ -> acc
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
with :ok <- :file.pwrite(file, {:eof, -2}, "\n],\n \"totalItems\": #{total}}") do
|
||||||
|
File.close(file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp bookmarks(dir, %{id: user_id} = _user) do
|
||||||
|
Bookmark
|
||||||
|
|> where(user_id: ^user_id)
|
||||||
|
|> join(:inner, [b], activity in assoc(b, :activity))
|
||||||
|
|> select([b, a], %{id: b.id, object: fragment("(?)->>'object'", a.data)})
|
||||||
|
|> write(dir, "bookmarks", fn a -> {:ok, a.object} end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp likes(dir, user) do
|
||||||
|
user.ap_id
|
||||||
|
|> Activity.Queries.by_actor()
|
||||||
|
|> Activity.Queries.by_type("Like")
|
||||||
|
|> select([like], %{id: like.id, object: fragment("(?)->>'object'", like.data)})
|
||||||
|
|> write(dir, "likes", fn a -> {:ok, a.object} end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp statuses(dir, user) do
|
||||||
|
opts =
|
||||||
|
%{}
|
||||||
|
|> Map.put(:type, ["Create", "Announce"])
|
||||||
|
|> Map.put(:actor_id, user.ap_id)
|
||||||
|
|
||||||
|
[
|
||||||
|
[Pleroma.Constants.as_public(), user.ap_id],
|
||||||
|
User.following(user),
|
||||||
|
Pleroma.List.memberships(user)
|
||||||
|
]
|
||||||
|
|> Enum.concat()
|
||||||
|
|> ActivityPub.fetch_activities_query(opts)
|
||||||
|
|> write(dir, "outbox", fn a ->
|
||||||
|
with {:ok, activity} <- Transmogrifier.prepare_outgoing(a.data) do
|
||||||
|
{:ok, Map.delete(activity, "@context")}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
|
@ -43,6 +43,7 @@ defmodule Pleroma.User.Query do
|
||||||
active: boolean(),
|
active: boolean(),
|
||||||
deactivated: boolean(),
|
deactivated: boolean(),
|
||||||
need_approval: boolean(),
|
need_approval: boolean(),
|
||||||
|
unconfirmed: boolean(),
|
||||||
is_admin: boolean(),
|
is_admin: boolean(),
|
||||||
is_moderator: boolean(),
|
is_moderator: boolean(),
|
||||||
super_users: boolean(),
|
super_users: boolean(),
|
||||||
|
@ -55,7 +56,8 @@ defmodule Pleroma.User.Query do
|
||||||
ap_id: [String.t()],
|
ap_id: [String.t()],
|
||||||
order_by: term(),
|
order_by: term(),
|
||||||
select: term(),
|
select: term(),
|
||||||
limit: pos_integer()
|
limit: pos_integer(),
|
||||||
|
actor_types: [String.t()]
|
||||||
}
|
}
|
||||||
| map()
|
| map()
|
||||||
|
|
||||||
|
@ -114,6 +116,10 @@ defmodule Pleroma.User.Query do
|
||||||
where(query, [u], u.is_admin == ^bool)
|
where(query, [u], u.is_admin == ^bool)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp compose_query({:actor_types, actor_types}, query) when is_list(actor_types) do
|
||||||
|
where(query, [u], u.actor_type in ^actor_types)
|
||||||
|
end
|
||||||
|
|
||||||
defp compose_query({:is_moderator, bool}, query) do
|
defp compose_query({:is_moderator, bool}, query) do
|
||||||
where(query, [u], u.is_moderator == ^bool)
|
where(query, [u], u.is_moderator == ^bool)
|
||||||
end
|
end
|
||||||
|
@ -156,6 +162,10 @@ defmodule Pleroma.User.Query do
|
||||||
where(query, [u], u.approval_pending)
|
where(query, [u], u.approval_pending)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp compose_query({:unconfirmed, _}, query) do
|
||||||
|
where(query, [u], u.confirmation_pending)
|
||||||
|
end
|
||||||
|
|
||||||
defp compose_query({:followers, %User{id: id}}, query) do
|
defp compose_query({:followers, %User{id: id}}, query) do
|
||||||
query
|
query
|
||||||
|> where([u], u.id != ^id)
|
|> where([u], u.id != ^id)
|
||||||
|
|
|
@ -976,16 +976,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
|
|
||||||
defp restrict_muted_reblogs(query, _), do: query
|
defp restrict_muted_reblogs(query, _), do: query
|
||||||
|
|
||||||
defp restrict_instance(query, %{instance: instance}) do
|
defp restrict_instance(query, %{instance: instance}) when is_binary(instance) do
|
||||||
users =
|
|
||||||
from(
|
from(
|
||||||
u in User,
|
activity in query,
|
||||||
select: u.ap_id,
|
where: fragment("split_part(actor::text, '/'::text, 3) = ?", ^instance)
|
||||||
where: fragment("? LIKE ?", u.nickname, ^"%@#{instance}")
|
|
||||||
)
|
)
|
||||||
|> Repo.all()
|
|
||||||
|
|
||||||
from(activity in query, where: activity.actor in ^users)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp restrict_instance(query, _), do: query
|
defp restrict_instance(query, _), do: query
|
||||||
|
@ -1418,6 +1413,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
{:ok, data} <- user_data_from_user_object(data) do
|
{:ok, data} <- user_data_from_user_object(data) do
|
||||||
{:ok, maybe_update_follow_information(data)}
|
{:ok, maybe_update_follow_information(data)}
|
||||||
else
|
else
|
||||||
|
# If this has been deleted, only log a debug and not an error
|
||||||
{:error, "Object has been deleted" = e} ->
|
{:error, "Object has been deleted" = e} ->
|
||||||
Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
Logger.debug("Could not decode user at fetch #{ap_id}, #{inspect(e)}")
|
||||||
{:error, e}
|
{:error, e}
|
||||||
|
|
|
@ -187,7 +187,7 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||||
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
|
{:ok, notifications} = Notification.create_notifications(activity, do_send: false)
|
||||||
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
|
{:ok, _user} = ActivityPub.increase_note_count_if_public(user, object)
|
||||||
|
|
||||||
if in_reply_to = object.data["inReplyTo"] do
|
if in_reply_to = object.data["inReplyTo"] && object.data["type"] != "Answer" do
|
||||||
Object.increase_replies_count(in_reply_to)
|
Object.increase_replies_count(in_reply_to)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -312,6 +312,12 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do
|
||||||
{:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
|
{:ok, chat} = Chat.bump_or_create(user.id, other_user.ap_id)
|
||||||
{:ok, cm_ref} = MessageReference.create(chat, object, user.ap_id != actor.ap_id)
|
{:ok, cm_ref} = MessageReference.create(chat, object, user.ap_id != actor.ap_id)
|
||||||
|
|
||||||
|
Cachex.put(
|
||||||
|
:chat_message_id_idempotency_key_cache,
|
||||||
|
cm_ref.id,
|
||||||
|
meta[:idempotency_key]
|
||||||
|
)
|
||||||
|
|
||||||
{
|
{
|
||||||
["user", "user:pleroma_chat"],
|
["user", "user:pleroma_chat"],
|
||||||
{user, %{cm_ref | chat: chat, object: object}}
|
{user, %{cm_ref | chat: chat, object: object}}
|
||||||
|
|
|
@ -44,29 +44,30 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||||
def is_list?(%{data: %{"listMessage" => _}}), do: true
|
def is_list?(%{data: %{"listMessage" => _}}), do: true
|
||||||
def is_list?(_), do: false
|
def is_list?(_), do: false
|
||||||
|
|
||||||
@spec visible_for_user?(Activity.t(), User.t() | nil) :: boolean()
|
@spec visible_for_user?(Activity.t() | nil, User.t() | nil) :: boolean()
|
||||||
def visible_for_user?(%{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
def visible_for_user?(%Activity{actor: ap_id}, %User{ap_id: ap_id}), do: true
|
||||||
|
|
||||||
def visible_for_user?(nil, _), do: false
|
def visible_for_user?(nil, _), do: false
|
||||||
|
|
||||||
def visible_for_user?(%{data: %{"listMessage" => _}}, nil), do: false
|
def visible_for_user?(%Activity{data: %{"listMessage" => _}}, nil), do: false
|
||||||
|
|
||||||
def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{} = user) do
|
def visible_for_user?(
|
||||||
|
%Activity{data: %{"listMessage" => list_ap_id}} = activity,
|
||||||
|
%User{} = user
|
||||||
|
) do
|
||||||
user.ap_id in activity.data["to"] ||
|
user.ap_id in activity.data["to"] ||
|
||||||
list_ap_id
|
list_ap_id
|
||||||
|> Pleroma.List.get_by_ap_id()
|
|> Pleroma.List.get_by_ap_id()
|
||||||
|> Pleroma.List.member?(user)
|
|> Pleroma.List.member?(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_for_user?(%{local: local} = activity, nil) do
|
def visible_for_user?(%Activity{} = activity, nil) do
|
||||||
cfg_key = if local, do: :local, else: :remote
|
if restrict_unauthenticated_access?(activity),
|
||||||
|
|
||||||
if Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key),
|
|
||||||
do: false,
|
do: false,
|
||||||
else: is_public?(activity)
|
else: is_public?(activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_for_user?(activity, user) do
|
def visible_for_user?(%Activity{} = activity, user) do
|
||||||
x = [user.ap_id | User.following(user)]
|
x = [user.ap_id | User.following(user)]
|
||||||
y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
|
y = [activity.actor] ++ activity.data["to"] ++ (activity.data["cc"] || [])
|
||||||
is_public?(activity) || Enum.any?(x, &(&1 in y))
|
is_public?(activity) || Enum.any?(x, &(&1 in y))
|
||||||
|
@ -82,6 +83,26 @@ defmodule Pleroma.Web.ActivityPub.Visibility do
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def restrict_unauthenticated_access?(%Activity{local: local}) do
|
||||||
|
restrict_unauthenticated_access_to_activity?(local)
|
||||||
|
end
|
||||||
|
|
||||||
|
def restrict_unauthenticated_access?(%Object{} = object) do
|
||||||
|
object
|
||||||
|
|> Object.local?()
|
||||||
|
|> restrict_unauthenticated_access_to_activity?()
|
||||||
|
end
|
||||||
|
|
||||||
|
def restrict_unauthenticated_access?(%User{} = user) do
|
||||||
|
User.visible_for(user, _reading_user = nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_unauthenticated_access_to_activity?(local?) when is_boolean(local?) do
|
||||||
|
cfg_key = if local?, do: :local, else: :remote
|
||||||
|
|
||||||
|
Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key)
|
||||||
|
end
|
||||||
|
|
||||||
def get_visibility(object) do
|
def get_visibility(object) do
|
||||||
to = object.data["to"] || []
|
to = object.data["to"] || []
|
||||||
cc = object.data["cc"] || []
|
cc = object.data["cc"] || []
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper, only: [json_response: 3]
|
import Pleroma.Web.ControllerHelper,
|
||||||
|
only: [json_response: 3, fetch_integer_param: 3]
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.MFA
|
alias Pleroma.MFA
|
||||||
|
@ -13,12 +14,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
alias Pleroma.Stats
|
alias Pleroma.Stats
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Builder
|
|
||||||
alias Pleroma.Web.ActivityPub.Pipeline
|
|
||||||
alias Pleroma.Web.AdminAPI
|
alias Pleroma.Web.AdminAPI
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
alias Pleroma.Web.AdminAPI.ModerationLogView
|
alias Pleroma.Web.AdminAPI.ModerationLogView
|
||||||
alias Pleroma.Web.AdminAPI.Search
|
|
||||||
alias Pleroma.Web.Endpoint
|
alias Pleroma.Web.Endpoint
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
@ -28,7 +26,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:accounts"], admin: true}
|
%{scopes: ["read:accounts"], admin: true}
|
||||||
when action in [:list_users, :user_show, :right_get, :show_user_credentials]
|
when action in [:right_get, :show_user_credentials, :create_backup]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
|
@ -37,12 +35,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
when action in [
|
when action in [
|
||||||
:get_password_reset,
|
:get_password_reset,
|
||||||
:force_password_reset,
|
:force_password_reset,
|
||||||
:user_delete,
|
|
||||||
:users_create,
|
|
||||||
:user_toggle_activation,
|
|
||||||
:user_activate,
|
|
||||||
:user_deactivate,
|
|
||||||
:user_approve,
|
|
||||||
:tag_users,
|
:tag_users,
|
||||||
:untag_users,
|
:untag_users,
|
||||||
:right_add,
|
:right_add,
|
||||||
|
@ -54,12 +46,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(
|
|
||||||
OAuthScopesPlug,
|
|
||||||
%{scopes: ["write:follows"], admin: true}
|
|
||||||
when action in [:user_follow, :user_unfollow]
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["read:statuses"], admin: true}
|
%{scopes: ["read:statuses"], admin: true}
|
||||||
|
@ -95,132 +81,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
|
|
||||||
action_fallback(AdminAPI.FallbackController)
|
action_fallback(AdminAPI.FallbackController)
|
||||||
|
|
||||||
def user_delete(conn, %{"nickname" => nickname}) do
|
|
||||||
user_delete(conn, %{"nicknames" => [nickname]})
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
|
||||||
users =
|
|
||||||
nicknames
|
|
||||||
|> Enum.map(&User.get_cached_by_nickname/1)
|
|
||||||
|
|
||||||
users
|
|
||||||
|> Enum.each(fn user ->
|
|
||||||
{:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
|
|
||||||
Pipeline.common_pipeline(delete_data, local: true)
|
|
||||||
end)
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
actor: admin,
|
|
||||||
subject: users,
|
|
||||||
action: "delete"
|
|
||||||
})
|
|
||||||
|
|
||||||
json(conn, nicknames)
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
|
||||||
"follower" => follower_nick,
|
|
||||||
"followed" => followed_nick
|
|
||||||
}) do
|
|
||||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
|
||||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
|
||||||
User.follow(follower, followed)
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
actor: admin,
|
|
||||||
followed: followed,
|
|
||||||
follower: follower,
|
|
||||||
action: "follow"
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
json(conn, "ok")
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_unfollow(%{assigns: %{user: admin}} = conn, %{
|
|
||||||
"follower" => follower_nick,
|
|
||||||
"followed" => followed_nick
|
|
||||||
}) do
|
|
||||||
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
|
||||||
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
|
||||||
User.unfollow(follower, followed)
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
actor: admin,
|
|
||||||
followed: followed,
|
|
||||||
follower: follower,
|
|
||||||
action: "unfollow"
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
json(conn, "ok")
|
|
||||||
end
|
|
||||||
|
|
||||||
def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
|
|
||||||
changesets =
|
|
||||||
Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
|
|
||||||
user_data = %{
|
|
||||||
nickname: nickname,
|
|
||||||
name: nickname,
|
|
||||||
email: email,
|
|
||||||
password: password,
|
|
||||||
password_confirmation: password,
|
|
||||||
bio: "."
|
|
||||||
}
|
|
||||||
|
|
||||||
User.register_changeset(%User{}, user_data, need_confirmation: false)
|
|
||||||
end)
|
|
||||||
|> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
|
|
||||||
Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
|
|
||||||
end)
|
|
||||||
|
|
||||||
case Pleroma.Repo.transaction(changesets) do
|
|
||||||
{:ok, users} ->
|
|
||||||
res =
|
|
||||||
users
|
|
||||||
|> Map.values()
|
|
||||||
|> Enum.map(fn user ->
|
|
||||||
{:ok, user} = User.post_register_action(user)
|
|
||||||
|
|
||||||
user
|
|
||||||
end)
|
|
||||||
|> Enum.map(&AccountView.render("created.json", %{user: &1}))
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
actor: admin,
|
|
||||||
subjects: Map.values(users),
|
|
||||||
action: "create"
|
|
||||||
})
|
|
||||||
|
|
||||||
json(conn, res)
|
|
||||||
|
|
||||||
{:error, id, changeset, _} ->
|
|
||||||
res =
|
|
||||||
Enum.map(changesets.operations, fn
|
|
||||||
{current_id, {:changeset, _current_changeset, _}} when current_id == id ->
|
|
||||||
AccountView.render("create-error.json", %{changeset: changeset})
|
|
||||||
|
|
||||||
{_, {:changeset, current_changeset, _}} ->
|
|
||||||
AccountView.render("create-error.json", %{changeset: current_changeset})
|
|
||||||
end)
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_status(:conflict)
|
|
||||||
|> json(res)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_show(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
|
||||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("show.json", %{user: user})
|
|
||||||
else
|
|
||||||
_ -> {:error, :not_found}
|
|
||||||
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
|
with_reblogs = params["with_reblogs"] == "true" || params["with_reblogs"] == true
|
||||||
{page, page_size} = page_params(params)
|
{page, page_size} = page_params(params)
|
||||||
|
@ -274,69 +134,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
|
||||||
user = User.get_cached_by_nickname(nickname)
|
|
||||||
|
|
||||||
{:ok, updated_user} = User.deactivate(user, !user.deactivated)
|
|
||||||
|
|
||||||
action = if user.deactivated, do: "activate", else: "deactivate"
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
actor: admin,
|
|
||||||
subject: [user],
|
|
||||||
action: action
|
|
||||||
})
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("show.json", %{user: updated_user})
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
|
||||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
|
||||||
{:ok, updated_users} = User.deactivate(users, false)
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
actor: admin,
|
|
||||||
subject: users,
|
|
||||||
action: "activate"
|
|
||||||
})
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("index.json", %{users: Keyword.values(updated_users)})
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
|
||||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
|
||||||
{:ok, updated_users} = User.deactivate(users, true)
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
actor: admin,
|
|
||||||
subject: users,
|
|
||||||
action: "deactivate"
|
|
||||||
})
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("index.json", %{users: Keyword.values(updated_users)})
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
|
||||||
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
|
||||||
{:ok, updated_users} = User.approve(users)
|
|
||||||
|
|
||||||
ModerationLog.insert_log(%{
|
|
||||||
actor: admin,
|
|
||||||
subject: users,
|
|
||||||
action: "approve"
|
|
||||||
})
|
|
||||||
|
|
||||||
conn
|
|
||||||
|> put_view(AccountView)
|
|
||||||
|> render("index.json", %{users: updated_users})
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
def tag_users(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames, "tags" => tags}) do
|
||||||
with {:ok, _} <- User.tag(nicknames, tags) do
|
with {:ok, _} <- User.tag(nicknames, tags) do
|
||||||
ModerationLog.insert_log(%{
|
ModerationLog.insert_log(%{
|
||||||
|
@ -363,43 +160,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_users(conn, params) do
|
|
||||||
{page, page_size} = page_params(params)
|
|
||||||
filters = maybe_parse_filters(params["filters"])
|
|
||||||
|
|
||||||
search_params = %{
|
|
||||||
query: params["query"],
|
|
||||||
page: page,
|
|
||||||
page_size: page_size,
|
|
||||||
tags: params["tags"],
|
|
||||||
name: params["name"],
|
|
||||||
email: params["email"]
|
|
||||||
}
|
|
||||||
|
|
||||||
with {:ok, users, count} <- Search.user(Map.merge(search_params, filters)) do
|
|
||||||
json(
|
|
||||||
conn,
|
|
||||||
AccountView.render("index.json",
|
|
||||||
users: users,
|
|
||||||
count: count,
|
|
||||||
page_size: page_size
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@filters ~w(local external active deactivated need_approval is_admin is_moderator)
|
|
||||||
|
|
||||||
@spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
|
|
||||||
defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
|
|
||||||
|
|
||||||
defp maybe_parse_filters(filters) do
|
|
||||||
filters
|
|
||||||
|> String.split(",")
|
|
||||||
|> Enum.filter(&Enum.member?(@filters, &1))
|
|
||||||
|> Map.new(&{String.to_existing_atom(&1), true})
|
|
||||||
end
|
|
||||||
|
|
||||||
def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
|
def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
|
||||||
"permission_group" => permission_group,
|
"permission_group" => permission_group,
|
||||||
"nicknames" => nicknames
|
"nicknames" => nicknames
|
||||||
|
@ -681,25 +441,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
json(conn, %{"status_visibility" => counters})
|
json(conn, %{"status_visibility" => counters})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create_backup(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
|
with %User{} = user <- User.get_by_nickname(nickname),
|
||||||
|
{:ok, _} <- Pleroma.User.Backup.create(user, admin.id) do
|
||||||
|
ModerationLog.insert_log(%{actor: admin, subject: user, action: "create_backup"})
|
||||||
|
|
||||||
|
json(conn, "")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp page_params(params) do
|
defp page_params(params) do
|
||||||
{get_page(params["page"]), get_page_size(params["page_size"])}
|
{
|
||||||
end
|
fetch_integer_param(params, "page", 1),
|
||||||
|
fetch_integer_param(params, "page_size", @users_page_size)
|
||||||
defp get_page(page_string) when is_nil(page_string), do: 1
|
}
|
||||||
|
|
||||||
defp get_page(page_string) do
|
|
||||||
case Integer.parse(page_string) do
|
|
||||||
{page, _} -> page
|
|
||||||
:error -> 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp get_page_size(page_size_string) when is_nil(page_size_string), do: @users_page_size
|
|
||||||
|
|
||||||
defp get_page_size(page_size_string) do
|
|
||||||
case Integer.parse(page_size_string) do
|
|
||||||
{page_size, _} -> page_size
|
|
||||||
:error -> @users_page_size
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -38,7 +38,7 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
|
||||||
end
|
end
|
||||||
|
|
||||||
def show(conn, %{id: id}) do
|
def show(conn, %{id: id}) do
|
||||||
with %Activity{} = report <- Activity.get_by_id(id) do
|
with %Activity{} = report <- Activity.get_report(id) do
|
||||||
render(conn, "show.json", Report.extract_report_info(report))
|
render(conn, "show.json", Report.extract_report_info(report))
|
||||||
else
|
else
|
||||||
_ -> {:error, :not_found}
|
_ -> {:error, :not_found}
|
||||||
|
|
281
lib/pleroma/web/admin_api/controllers/user_controller.ex
Normal file
281
lib/pleroma/web/admin_api/controllers/user_controller.ex
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.UserController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
import Pleroma.Web.ControllerHelper,
|
||||||
|
only: [fetch_integer_param: 3]
|
||||||
|
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.Builder
|
||||||
|
alias Pleroma.Web.ActivityPub.Pipeline
|
||||||
|
alias Pleroma.Web.AdminAPI
|
||||||
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
@users_page_size 50
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read:accounts"], admin: true}
|
||||||
|
when action in [:list, :show]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:accounts"], admin: true}
|
||||||
|
when action in [
|
||||||
|
:delete,
|
||||||
|
:create,
|
||||||
|
:toggle_activation,
|
||||||
|
:activate,
|
||||||
|
:deactivate,
|
||||||
|
:approve
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:follows"], admin: true}
|
||||||
|
when action in [:follow, :unfollow]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
def delete(conn, %{"nickname" => nickname}) do
|
||||||
|
delete(conn, %{"nicknames" => [nickname]})
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
|
||||||
|
Enum.each(users, fn user ->
|
||||||
|
{:ok, delete_data, _} = Builder.delete(admin, user.ap_id)
|
||||||
|
Pipeline.common_pipeline(delete_data, local: true)
|
||||||
|
end)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "delete"
|
||||||
|
})
|
||||||
|
|
||||||
|
json(conn, nicknames)
|
||||||
|
end
|
||||||
|
|
||||||
|
def follow(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
"follower" => follower_nick,
|
||||||
|
"followed" => followed_nick
|
||||||
|
}) do
|
||||||
|
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||||
|
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||||
|
User.follow(follower, followed)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
followed: followed,
|
||||||
|
follower: follower,
|
||||||
|
action: "follow"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
json(conn, "ok")
|
||||||
|
end
|
||||||
|
|
||||||
|
def unfollow(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
"follower" => follower_nick,
|
||||||
|
"followed" => followed_nick
|
||||||
|
}) do
|
||||||
|
with %User{} = follower <- User.get_cached_by_nickname(follower_nick),
|
||||||
|
%User{} = followed <- User.get_cached_by_nickname(followed_nick) do
|
||||||
|
User.unfollow(follower, followed)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
followed: followed,
|
||||||
|
follower: follower,
|
||||||
|
action: "unfollow"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
json(conn, "ok")
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
|
||||||
|
changesets =
|
||||||
|
Enum.map(users, fn %{"nickname" => nickname, "email" => email, "password" => password} ->
|
||||||
|
user_data = %{
|
||||||
|
nickname: nickname,
|
||||||
|
name: nickname,
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
password_confirmation: password,
|
||||||
|
bio: "."
|
||||||
|
}
|
||||||
|
|
||||||
|
User.register_changeset(%User{}, user_data, need_confirmation: false)
|
||||||
|
end)
|
||||||
|
|> Enum.reduce(Ecto.Multi.new(), fn changeset, multi ->
|
||||||
|
Ecto.Multi.insert(multi, Ecto.UUID.generate(), changeset)
|
||||||
|
end)
|
||||||
|
|
||||||
|
case Pleroma.Repo.transaction(changesets) do
|
||||||
|
{:ok, users} ->
|
||||||
|
res =
|
||||||
|
users
|
||||||
|
|> Map.values()
|
||||||
|
|> Enum.map(fn user ->
|
||||||
|
{:ok, user} = User.post_register_action(user)
|
||||||
|
|
||||||
|
user
|
||||||
|
end)
|
||||||
|
|> Enum.map(&AccountView.render("created.json", %{user: &1}))
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subjects: Map.values(users),
|
||||||
|
action: "create"
|
||||||
|
})
|
||||||
|
|
||||||
|
json(conn, res)
|
||||||
|
|
||||||
|
{:error, id, changeset, _} ->
|
||||||
|
res =
|
||||||
|
Enum.map(changesets.operations, fn
|
||||||
|
{current_id, {:changeset, _current_changeset, _}} when current_id == id ->
|
||||||
|
AccountView.render("create-error.json", %{changeset: changeset})
|
||||||
|
|
||||||
|
{_, {:changeset, current_changeset, _}} ->
|
||||||
|
AccountView.render("create-error.json", %{changeset: current_changeset})
|
||||||
|
end)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_status(:conflict)
|
||||||
|
|> json(res)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
|
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname, for: admin) do
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("show.json", %{user: user})
|
||||||
|
else
|
||||||
|
_ -> {:error, :not_found}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def toggle_activation(%{assigns: %{user: admin}} = conn, %{"nickname" => nickname}) do
|
||||||
|
user = User.get_cached_by_nickname(nickname)
|
||||||
|
|
||||||
|
{:ok, updated_user} = User.deactivate(user, !user.deactivated)
|
||||||
|
|
||||||
|
action = if user.deactivated, do: "activate", else: "deactivate"
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: [user],
|
||||||
|
action: action
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("show.json", %{user: updated_user})
|
||||||
|
end
|
||||||
|
|
||||||
|
def activate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
{:ok, updated_users} = User.deactivate(users, false)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "activate"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("index.json", %{users: Keyword.values(updated_users)})
|
||||||
|
end
|
||||||
|
|
||||||
|
def deactivate(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
{:ok, updated_users} = User.deactivate(users, true)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "deactivate"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("index.json", %{users: Keyword.values(updated_users)})
|
||||||
|
end
|
||||||
|
|
||||||
|
def approve(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
users = Enum.map(nicknames, &User.get_cached_by_nickname/1)
|
||||||
|
{:ok, updated_users} = User.approve(users)
|
||||||
|
|
||||||
|
ModerationLog.insert_log(%{
|
||||||
|
actor: admin,
|
||||||
|
subject: users,
|
||||||
|
action: "approve"
|
||||||
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(AccountView)
|
||||||
|
|> render("index.json", %{users: updated_users})
|
||||||
|
end
|
||||||
|
|
||||||
|
def list(conn, params) do
|
||||||
|
{page, page_size} = page_params(params)
|
||||||
|
filters = maybe_parse_filters(params["filters"])
|
||||||
|
|
||||||
|
search_params =
|
||||||
|
%{
|
||||||
|
query: params["query"],
|
||||||
|
page: page,
|
||||||
|
page_size: page_size,
|
||||||
|
tags: params["tags"],
|
||||||
|
name: params["name"],
|
||||||
|
email: params["email"],
|
||||||
|
actor_types: params["actor_types"]
|
||||||
|
}
|
||||||
|
|> Map.merge(filters)
|
||||||
|
|
||||||
|
with {:ok, users, count} <- Search.user(search_params) do
|
||||||
|
json(
|
||||||
|
conn,
|
||||||
|
AccountView.render("index.json",
|
||||||
|
users: users,
|
||||||
|
count: count,
|
||||||
|
page_size: page_size
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@filters ~w(local external active deactivated need_approval unconfirmed is_admin is_moderator)
|
||||||
|
|
||||||
|
@spec maybe_parse_filters(String.t()) :: %{required(String.t()) => true} | %{}
|
||||||
|
defp maybe_parse_filters(filters) when is_nil(filters) or filters == "", do: %{}
|
||||||
|
|
||||||
|
defp maybe_parse_filters(filters) do
|
||||||
|
filters
|
||||||
|
|> String.split(",")
|
||||||
|
|> Enum.filter(&Enum.member?(@filters, &1))
|
||||||
|
|> Map.new(&{String.to_existing_atom(&1), true})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp page_params(params) do
|
||||||
|
{
|
||||||
|
fetch_integer_param(params, "page", 1),
|
||||||
|
fetch_integer_param(params, "page_size", @users_page_size)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -52,7 +52,7 @@ defmodule Pleroma.Web.AdminAPI.AccountView do
|
||||||
:skip_thread_containment,
|
:skip_thread_containment,
|
||||||
:pleroma_settings_store,
|
:pleroma_settings_store,
|
||||||
:raw_fields,
|
:raw_fields,
|
||||||
:discoverable,
|
:is_discoverable,
|
||||||
:actor_type
|
:actor_type
|
||||||
])
|
])
|
||||||
|> Map.merge(%{
|
|> Map.merge(%{
|
||||||
|
|
|
@ -335,6 +335,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
||||||
operationId: "AccountController.mutes",
|
operationId: "AccountController.mutes",
|
||||||
description: "Accounts the user has muted.",
|
description: "Accounts the user has muted.",
|
||||||
security: [%{"oAuth" => ["follow", "read:mutes"]}],
|
security: [%{"oAuth" => ["follow", "read:mutes"]}],
|
||||||
|
parameters: pagination_params(),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||||
}
|
}
|
||||||
|
@ -348,6 +349,7 @@ defmodule Pleroma.Web.ApiSpec.AccountOperation do
|
||||||
operationId: "AccountController.blocks",
|
operationId: "AccountController.blocks",
|
||||||
description: "View your blocks. See also accounts/:id/{block,unblock}",
|
description: "View your blocks. See also accounts/:id/{block,unblock}",
|
||||||
security: [%{"oAuth" => ["read:blocks"]}],
|
security: [%{"oAuth" => ["read:blocks"]}],
|
||||||
|
parameters: pagination_params(),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
200 => Operation.response("Accounts", "application/json", array_of_accounts())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.BooleanLike
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.Chat
|
alias Pleroma.Web.ApiSpec.Schemas.Chat
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
|
alias Pleroma.Web.ApiSpec.Schemas.ChatMessage
|
||||||
|
|
||||||
|
@ -132,7 +133,10 @@ defmodule Pleroma.Web.ApiSpec.ChatOperation do
|
||||||
tags: ["chat"],
|
tags: ["chat"],
|
||||||
summary: "Get a list of chats that you participated in",
|
summary: "Get a list of chats that you participated in",
|
||||||
operationId: "ChatController.index",
|
operationId: "ChatController.index",
|
||||||
parameters: pagination_params(),
|
parameters: [
|
||||||
|
Operation.parameter(:with_muted, :query, BooleanLike, "Include chats from muted users")
|
||||||
|
| pagination_params()
|
||||||
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("The chats of the user", "application/json", chats_response())
|
200 => Operation.response("The chats of the user", "application/json", chats_response())
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.PleromaBackupOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Backups"],
|
||||||
|
summary: "List backups",
|
||||||
|
security: [%{"oAuth" => ["read:account"]}],
|
||||||
|
operationId: "PleromaAPI.BackupController.index",
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response(
|
||||||
|
"An array of backups",
|
||||||
|
"application/json",
|
||||||
|
%Schema{
|
||||||
|
type: :array,
|
||||||
|
items: backup()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Backups"],
|
||||||
|
summary: "Create a backup",
|
||||||
|
security: [%{"oAuth" => ["read:account"]}],
|
||||||
|
operationId: "PleromaAPI.BackupController.create",
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response(
|
||||||
|
"An array of backups",
|
||||||
|
"application/json",
|
||||||
|
%Schema{
|
||||||
|
type: :array,
|
||||||
|
items: backup()
|
||||||
|
}
|
||||||
|
),
|
||||||
|
400 => Operation.response("Bad Request", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp backup do
|
||||||
|
%Schema{
|
||||||
|
title: "Backup",
|
||||||
|
description: "Response schema for a backup",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
inserted_at: %Schema{type: :string, format: :"date-time"},
|
||||||
|
content_type: %Schema{type: :string},
|
||||||
|
file_name: %Schema{type: :string},
|
||||||
|
file_size: %Schema{type: :integer},
|
||||||
|
processed: %Schema{type: :boolean}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"content_type" => "application/zip",
|
||||||
|
"file_name" =>
|
||||||
|
"https://cofe.fe:4000/media/backups/archive-foobar-20200908T164207-Yr7vuT5Wycv-sN3kSN2iJ0k-9pMo60j9qmvRCdDqIew.zip",
|
||||||
|
"file_size" => 4105,
|
||||||
|
"inserted_at" => "2020-09-08T16:42:07.000Z",
|
||||||
|
"processed" => true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.PleromaInstancesOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["PleromaInstances"],
|
||||||
|
summary: "Instances federation status",
|
||||||
|
description: "Information about instances deemed unreachable by the server",
|
||||||
|
operationId: "PleromaInstances.show",
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("PleromaInstances", "application/json", pleroma_instances())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def pleroma_instances do
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
unreachable: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{hostname: %Schema{type: :string, format: :"date-time"}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"unreachable" => %{"consistently-unreachable.name" => "2020-10-14 22:07:58.216473"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -59,6 +59,7 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
||||||
security: [%{"oAuth" => ["read:statuses"]}],
|
security: [%{"oAuth" => ["read:statuses"]}],
|
||||||
parameters: [
|
parameters: [
|
||||||
local_param(),
|
local_param(),
|
||||||
|
instance_param(),
|
||||||
only_media_param(),
|
only_media_param(),
|
||||||
with_muted_param(),
|
with_muted_param(),
|
||||||
exclude_visibilities_param(),
|
exclude_visibilities_param(),
|
||||||
|
@ -158,8 +159,17 @@ defmodule Pleroma.Web.ApiSpec.TimelineOperation do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp instance_param do
|
||||||
|
Operation.parameter(
|
||||||
|
:instance,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :string},
|
||||||
|
"Show only statuses from the given domain"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp with_muted_param do
|
defp with_muted_param do
|
||||||
Operation.parameter(:with_muted, :query, BooleanLike, "Includeactivities by muted users")
|
Operation.parameter(:with_muted, :query, BooleanLike, "Include activities by muted users")
|
||||||
end
|
end
|
||||||
|
|
||||||
defp exclude_visibilities_param do
|
defp exclude_visibilities_param do
|
||||||
|
|
|
@ -28,8 +28,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do
|
||||||
},
|
},
|
||||||
votes_count: %Schema{
|
votes_count: %Schema{
|
||||||
type: :integer,
|
type: :integer,
|
||||||
nullable: true,
|
description: "How many votes have been received. Number."
|
||||||
description: "How many votes have been received. Number, or null if `multiple` is false."
|
},
|
||||||
|
voters_count: %Schema{
|
||||||
|
type: :integer,
|
||||||
|
description: "How many unique accounts have voted. Number."
|
||||||
},
|
},
|
||||||
voted: %Schema{
|
voted: %Schema{
|
||||||
type: :boolean,
|
type: :boolean,
|
||||||
|
@ -61,7 +64,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Poll do
|
||||||
expired: true,
|
expired: true,
|
||||||
multiple: false,
|
multiple: false,
|
||||||
votes_count: 10,
|
votes_count: 10,
|
||||||
voters_count: nil,
|
voters_count: 10,
|
||||||
voted: true,
|
voted: true,
|
||||||
own_votes: [
|
own_votes: [
|
||||||
1
|
1
|
||||||
|
|
|
@ -45,7 +45,8 @@ defmodule Pleroma.Web.CommonAPI do
|
||||||
{_, {:ok, %Activity{} = activity, _meta}} <-
|
{_, {:ok, %Activity{} = activity, _meta}} <-
|
||||||
{:common_pipeline,
|
{:common_pipeline,
|
||||||
Pipeline.common_pipeline(create_activity_data,
|
Pipeline.common_pipeline(create_activity_data,
|
||||||
local: true
|
local: true,
|
||||||
|
idempotency_key: opts[:idempotency_key]
|
||||||
)} do
|
)} do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
else
|
else
|
||||||
|
|
|
@ -7,6 +7,8 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
|
|
||||||
require Pleroma.Constants
|
require Pleroma.Constants
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
|
||||||
socket("/socket", Pleroma.Web.UserSocket)
|
socket("/socket", Pleroma.Web.UserSocket)
|
||||||
|
|
||||||
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
|
plug(Plug.Telemetry, event_prefix: [:phoenix, :endpoint])
|
||||||
|
@ -88,19 +90,19 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
plug(Plug.Parsers,
|
plug(Plug.Parsers,
|
||||||
parsers: [
|
parsers: [
|
||||||
:urlencoded,
|
:urlencoded,
|
||||||
{:multipart, length: {Pleroma.Config, :get, [[:instance, :upload_limit]]}},
|
{:multipart, length: {Config, :get, [[:instance, :upload_limit]]}},
|
||||||
:json
|
:json
|
||||||
],
|
],
|
||||||
pass: ["*/*"],
|
pass: ["*/*"],
|
||||||
json_decoder: Jason,
|
json_decoder: Jason,
|
||||||
length: Pleroma.Config.get([:instance, :upload_limit]),
|
length: Config.get([:instance, :upload_limit]),
|
||||||
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
|
body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []}
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(Plug.MethodOverride)
|
plug(Plug.MethodOverride)
|
||||||
plug(Plug.Head)
|
plug(Plug.Head)
|
||||||
|
|
||||||
secure_cookies = Pleroma.Config.get([__MODULE__, :secure_cookie_flag])
|
secure_cookies = Config.get([__MODULE__, :secure_cookie_flag])
|
||||||
|
|
||||||
cookie_name =
|
cookie_name =
|
||||||
if secure_cookies,
|
if secure_cookies,
|
||||||
|
@ -108,7 +110,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
else: "pleroma_key"
|
else: "pleroma_key"
|
||||||
|
|
||||||
extra =
|
extra =
|
||||||
Pleroma.Config.get([__MODULE__, :extra_cookie_attrs])
|
Config.get([__MODULE__, :extra_cookie_attrs])
|
||||||
|> Enum.join(";")
|
|> Enum.join(";")
|
||||||
|
|
||||||
# The session will be stored in the cookie and signed,
|
# The session will be stored in the cookie and signed,
|
||||||
|
@ -118,7 +120,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
Plug.Session,
|
Plug.Session,
|
||||||
store: :cookie,
|
store: :cookie,
|
||||||
key: cookie_name,
|
key: cookie_name,
|
||||||
signing_salt: Pleroma.Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
|
signing_salt: Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
|
||||||
http_only: true,
|
http_only: true,
|
||||||
secure: secure_cookies,
|
secure: secure_cookies,
|
||||||
extra: extra
|
extra: extra
|
||||||
|
@ -138,8 +140,34 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
use Prometheus.PlugExporter
|
use Prometheus.PlugExporter
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defmodule MetricsExporterCaller do
|
||||||
|
@behaviour Plug
|
||||||
|
|
||||||
|
def init(opts), do: opts
|
||||||
|
|
||||||
|
def call(conn, opts) do
|
||||||
|
prometheus_config = Application.get_env(:prometheus, MetricsExporter, [])
|
||||||
|
ip_whitelist = List.wrap(prometheus_config[:ip_whitelist])
|
||||||
|
|
||||||
|
cond do
|
||||||
|
!prometheus_config[:enabled] ->
|
||||||
|
conn
|
||||||
|
|
||||||
|
ip_whitelist != [] and
|
||||||
|
!Enum.find(ip_whitelist, fn ip ->
|
||||||
|
Pleroma.Helpers.InetHelper.parse_address(ip) == {:ok, conn.remote_ip}
|
||||||
|
end) ->
|
||||||
|
conn
|
||||||
|
|
||||||
|
true ->
|
||||||
|
MetricsExporter.call(conn, opts)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
plug(PipelineInstrumenter)
|
plug(PipelineInstrumenter)
|
||||||
plug(MetricsExporter)
|
|
||||||
|
plug(MetricsExporterCaller)
|
||||||
|
|
||||||
plug(Pleroma.Web.Router)
|
plug(Pleroma.Web.Router)
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,14 @@ defmodule Pleroma.Web.Feed.TagController do
|
||||||
alias Pleroma.Web.Feed.FeedView
|
alias Pleroma.Web.Feed.FeedView
|
||||||
|
|
||||||
def feed(conn, params) do
|
def feed(conn, params) do
|
||||||
unless Pleroma.Config.restrict_unauthenticated_access?(:activities, :local) do
|
if Config.get!([:instance, :public]) do
|
||||||
render_feed(conn, params)
|
render_feed(conn, params)
|
||||||
else
|
else
|
||||||
render_error(conn, :not_found, "Not found")
|
render_error(conn, :not_found, "Not found")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_feed(conn, %{"tag" => raw_tag} = params) do
|
defp render_feed(conn, %{"tag" => raw_tag} = params) do
|
||||||
{format, tag} = parse_tag(raw_tag)
|
{format, tag} = parse_tag(raw_tag)
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
|
@ -36,12 +36,13 @@ defmodule Pleroma.Web.Feed.TagController do
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()}
|
@spec parse_tag(binary() | any()) :: {format :: String.t(), tag :: String.t()}
|
||||||
defp parse_tag(raw_tag) when is_binary(raw_tag) do
|
defp parse_tag(raw_tag) do
|
||||||
case Enum.reverse(String.split(raw_tag, ".")) do
|
case is_binary(raw_tag) && Enum.reverse(String.split(raw_tag, ".")) do
|
||||||
[format | tag] when format in ["atom", "rss"] -> {format, Enum.join(tag, ".")}
|
[format | tag] when format in ["rss", "atom"] ->
|
||||||
_ -> {"rss", raw_tag}
|
{format, Enum.join(tag, ".")}
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp parse_tag(raw_tag), do: {"rss", raw_tag}
|
_ ->
|
||||||
|
{"atom", raw_tag}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.Feed.UserController do
|
defmodule Pleroma.Web.Feed.UserController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPubController
|
alias Pleroma.Web.ActivityPub.ActivityPubController
|
||||||
|
@ -22,13 +23,8 @@ defmodule Pleroma.Web.Feed.UserController do
|
||||||
|
|
||||||
def feed_redirect(%{assigns: %{format: format}} = conn, _params)
|
def feed_redirect(%{assigns: %{format: format}} = conn, _params)
|
||||||
when format in ["json", "activity+json"] do
|
when format in ["json", "activity+json"] do
|
||||||
with %{halted: false} = conn <-
|
|
||||||
Pleroma.Web.Plugs.EnsureAuthenticatedPlug.call(conn,
|
|
||||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
|
||||||
) do
|
|
||||||
ActivityPubController.call(conn, :user)
|
ActivityPubController.call(conn, :user)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def feed_redirect(conn, %{"nickname" => nickname}) do
|
def feed_redirect(conn, %{"nickname" => nickname}) do
|
||||||
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
||||||
|
@ -36,25 +32,18 @@ defmodule Pleroma.Web.Feed.UserController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def feed(conn, params) do
|
def feed(conn, %{"nickname" => nickname} = params) do
|
||||||
unless Pleroma.Config.restrict_unauthenticated_access?(:profiles, :local) do
|
|
||||||
render_feed(conn, params)
|
|
||||||
else
|
|
||||||
errors(conn, {:error, :not_found})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_feed(conn, %{"nickname" => nickname} = params) do
|
|
||||||
format = get_format(conn)
|
format = get_format(conn)
|
||||||
|
|
||||||
format =
|
format =
|
||||||
if format in ["rss", "atom"] do
|
if format in ["atom", "rss"] do
|
||||||
format
|
format
|
||||||
else
|
else
|
||||||
"atom"
|
"atom"
|
||||||
end
|
end
|
||||||
|
|
||||||
with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do
|
with {_, %User{local: true} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)},
|
||||||
|
{_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do
|
||||||
activities =
|
activities =
|
||||||
%{
|
%{
|
||||||
type: ["Create"],
|
type: ["Create"],
|
||||||
|
@ -69,7 +58,7 @@ defmodule Pleroma.Web.Feed.UserController do
|
||||||
|> render("user.#{format}",
|
|> render("user.#{format}",
|
||||||
user: user,
|
user: user,
|
||||||
activities: activities,
|
activities: activities,
|
||||||
feed_config: Pleroma.Config.get([:feed])
|
feed_config: Config.get([:feed])
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -81,6 +70,8 @@ defmodule Pleroma.Web.Feed.UserController do
|
||||||
def errors(conn, {:fetch_user, %User{local: false}}), do: errors(conn, {:error, :not_found})
|
def errors(conn, {:fetch_user, %User{local: false}}), do: errors(conn, {:error, :not_found})
|
||||||
def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
|
def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found})
|
||||||
|
|
||||||
|
def errors(conn, {:visibility, _}), do: errors(conn, {:error, :not_found})
|
||||||
|
|
||||||
def errors(conn, _) do
|
def errors(conn, _) do
|
||||||
render_error(conn, :internal_server_error, "Something went wrong")
|
render_error(conn, :internal_server_error, "Something went wrong")
|
||||||
end
|
end
|
||||||
|
|
|
@ -442,15 +442,27 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/mutes"
|
@doc "GET /api/v1/mutes"
|
||||||
def mutes(%{assigns: %{user: user}} = conn, _) do
|
def mutes(%{assigns: %{user: user}} = conn, params) do
|
||||||
users = User.muted_users(user, _restrict_deactivated = true)
|
users =
|
||||||
render(conn, "index.json", users: users, for: user, as: :user)
|
user
|
||||||
|
|> User.muted_users_relation(_restrict_deactivated = true)
|
||||||
|
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> add_link_headers(users)
|
||||||
|
|> render("index.json", users: users, for: user, as: :user)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/blocks"
|
@doc "GET /api/v1/blocks"
|
||||||
def blocks(%{assigns: %{user: user}} = conn, _) do
|
def blocks(%{assigns: %{user: user}} = conn, params) do
|
||||||
users = User.blocked_users(user, _restrict_deactivated = true)
|
users =
|
||||||
render(conn, "index.json", users: users, for: user, as: :user)
|
user
|
||||||
|
|> User.blocked_users_relation(_restrict_deactivated = true)
|
||||||
|
|> Pleroma.Pagination.fetch_paginated(Map.put(params, :skip_order, true))
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> add_link_headers(users)
|
||||||
|
|> render("index.json", users: users, for: user, as: :user)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/endorsements"
|
@doc "GET /api/v1/endorsements"
|
||||||
|
|
|
@ -111,6 +111,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do
|
||||||
|> Map.put(:blocking_user, user)
|
|> Map.put(:blocking_user, user)
|
||||||
|> Map.put(:muting_user, user)
|
|> Map.put(:muting_user, user)
|
||||||
|> Map.put(:reply_filtering_user, user)
|
|> Map.put(:reply_filtering_user, user)
|
||||||
|
|> Map.put(:instance, params[:instance])
|
||||||
|> ActivityPub.fetch_public_activities()
|
|> ActivityPub.fetch_public_activities()
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
|
|
@ -388,7 +388,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
|
||||||
data
|
data
|
||||||
|> Kernel.put_in(
|
|> Kernel.put_in(
|
||||||
[:pleroma, :unread_conversation_count],
|
[:pleroma, :unread_conversation_count],
|
||||||
user.unread_conversation_count
|
Pleroma.Conversation.Participation.unread_count(user)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -33,8 +33,15 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
|
||||||
end
|
end
|
||||||
|
|
||||||
activity = Activity.get_by_id_with_object(last_activity_id)
|
activity = Activity.get_by_id_with_object(last_activity_id)
|
||||||
# Conversations return all users except the current user.
|
|
||||||
users = Enum.reject(participation.recipients, &(&1.id == user.id))
|
# Conversations return all users except the current user,
|
||||||
|
# except when the current user is the only participant
|
||||||
|
users =
|
||||||
|
if length(participation.recipients) > 1 do
|
||||||
|
Enum.reject(participation.recipients, &(&1.id == user.id))
|
||||||
|
else
|
||||||
|
participation.recipients
|
||||||
|
end
|
||||||
|
|
||||||
%{
|
%{
|
||||||
id: participation.id |> to_string(),
|
id: participation.id |> to_string(),
|
||||||
|
@ -43,7 +50,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationView do
|
||||||
last_status:
|
last_status:
|
||||||
render(StatusView, "show.json",
|
render(StatusView, "show.json",
|
||||||
activity: activity,
|
activity: activity,
|
||||||
direct_conversation_id: participation.id
|
direct_conversation_id: participation.id,
|
||||||
|
for: user
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Web.MastodonAPI.PollView do
|
||||||
expired: expired,
|
expired: expired,
|
||||||
multiple: multiple,
|
multiple: multiple,
|
||||||
votes_count: votes_count,
|
votes_count: votes_count,
|
||||||
voters_count: (multiple || nil) && voters_count(object),
|
voters_count: voters_count(object),
|
||||||
options: options,
|
options: options,
|
||||||
voted: voted?(params),
|
voted: voted?(params),
|
||||||
emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"])
|
emojis: Pleroma.Web.MastodonAPI.StatusView.build_emojis(object.data["emoji"])
|
||||||
|
|
|
@ -16,10 +16,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
alias Pleroma.Web.Plugs.RateLimiter
|
alias Pleroma.Web.Plugs.RateLimiter
|
||||||
alias Pleroma.Web.Router
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug,
|
|
||||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
|
||||||
)
|
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
RateLimiter,
|
RateLimiter,
|
||||||
[name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
|
[name: :ap_routes, params: ["uuid"]] when action in [:object, :activity]
|
||||||
|
@ -37,14 +33,12 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
ActivityPubController.call(conn, :object)
|
ActivityPubController.call(conn, :object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def object(%{assigns: %{format: format}} = conn, _params) do
|
def object(conn, _params) do
|
||||||
with id <- Endpoint.url() <> conn.request_path,
|
with id <- Endpoint.url() <> conn.request_path,
|
||||||
{_, %Activity{} = activity} <-
|
{_, %Activity{} = activity} <-
|
||||||
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
{:activity, Activity.get_create_by_object_ap_id_with_object(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||||
case format do
|
redirect(conn, to: "/notice/#{activity.id}")
|
||||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||||
{:error, :not_found}
|
{:error, :not_found}
|
||||||
|
@ -59,13 +53,11 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
ActivityPubController.call(conn, :activity)
|
ActivityPubController.call(conn, :activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity(%{assigns: %{format: format}} = conn, _params) do
|
def activity(conn, _params) do
|
||||||
with id <- Endpoint.url() <> conn.request_path,
|
with id <- Endpoint.url() <> conn.request_path,
|
||||||
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
{_, %Activity{} = activity} <- {:activity, Activity.normalize(id)},
|
||||||
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
{_, true} <- {:public?, Visibility.is_public?(activity)} do
|
||||||
case format do
|
redirect(conn, to: "/notice/#{activity.id}")
|
||||||
_ -> redirect(conn, to: "/notice/#{activity.id}")
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
reason when reason in [{:public?, false}, {:activity, nil}] ->
|
||||||
{:error, :not_found}
|
{:error, :not_found}
|
||||||
|
@ -119,6 +111,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do
|
||||||
def notice_player(conn, %{"id" => id}) do
|
def notice_player(conn, %{"id" => id}) do
|
||||||
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
|
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id_with_object(id),
|
||||||
true <- Visibility.is_public?(activity),
|
true <- Visibility.is_public?(activity),
|
||||||
|
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||||
%Object{} = object <- Object.normalize(activity),
|
%Object{} = object <- Object.normalize(activity),
|
||||||
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
|
%{data: %{"attachment" => [%{"url" => [url | _]} | _]}} <- object,
|
||||||
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
|
true <- String.starts_with?(url["mediaType"], ["audio", "video"]) do
|
||||||
|
|
28
lib/pleroma/web/pleroma_api/controllers/backup_controller.ex
Normal file
28
lib/pleroma/web/pleroma_api/controllers/backup_controller.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.BackupController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.User.Backup
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["read:accounts"]} when action in [:index, :create])
|
||||||
|
plug(OpenApiSpex.Plug.CastAndValidate, render_error: Pleroma.Web.ApiSpec.RenderError)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaBackupOperation
|
||||||
|
|
||||||
|
def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
|
backups = Backup.list(user)
|
||||||
|
render(conn, "index.json", backups: backups)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(%{assigns: %{user: user}} = conn, _params) do
|
||||||
|
with {:ok, _} <- Backup.create(user) do
|
||||||
|
backups = Backup.list(user)
|
||||||
|
render(conn, "index.json", backups: backups)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -15,7 +15,6 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
|
alias Pleroma.Web.PleromaAPI.Chat.MessageReferenceView
|
||||||
alias Pleroma.Web.PleromaAPI.ChatView
|
|
||||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
@ -80,7 +79,8 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
||||||
%User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
|
%User{} = recipient <- User.get_cached_by_ap_id(chat.recipient),
|
||||||
{:ok, activity} <-
|
{:ok, activity} <-
|
||||||
CommonAPI.post_chat_message(user, recipient, params[:content],
|
CommonAPI.post_chat_message(user, recipient, params[:content],
|
||||||
media_id: params[:media_id]
|
media_id: params[:media_id],
|
||||||
|
idempotency_key: idempotency_key(conn)
|
||||||
),
|
),
|
||||||
message <- Object.normalize(activity, false),
|
message <- Object.normalize(activity, false),
|
||||||
cm_ref <- MessageReference.for_chat_and_object(chat, message) do
|
cm_ref <- MessageReference.for_chat_and_object(chat, message) do
|
||||||
|
@ -120,9 +120,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
||||||
) do
|
) do
|
||||||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
|
with {:ok, chat} <- Chat.get_by_user_and_id(user, id),
|
||||||
{_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
|
{_n, _} <- MessageReference.set_all_seen_for_chat(chat, last_read_id) do
|
||||||
conn
|
render(conn, "show.json", chat: chat)
|
||||||
|> put_view(ChatView)
|
|
||||||
|> render("show.json", chat: chat)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -140,33 +138,37 @@ defmodule Pleroma.Web.PleromaAPI.ChatController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def index(%{assigns: %{user: %{id: user_id} = user}} = conn, _params) do
|
def index(%{assigns: %{user: %{id: user_id} = user}} = conn, params) do
|
||||||
blocked_ap_ids = User.blocked_users_ap_ids(user)
|
exclude_users =
|
||||||
|
User.blocked_users_ap_ids(user) ++
|
||||||
|
if params[:with_muted], do: [], else: User.muted_users_ap_ids(user)
|
||||||
|
|
||||||
chats =
|
chats =
|
||||||
Chat.for_user_query(user_id)
|
user_id
|
||||||
|> where([c], c.recipient not in ^blocked_ap_ids)
|
|> Chat.for_user_query()
|
||||||
|
|> where([c], c.recipient not in ^exclude_users)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|
|
||||||
conn
|
render(conn, "index.json", chats: chats)
|
||||||
|> put_view(ChatView)
|
|
||||||
|> render("index.json", chats: chats)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(%{assigns: %{user: user}} = conn, %{id: id}) do
|
def create(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||||
with %User{ap_id: recipient} <- User.get_cached_by_id(id),
|
with %User{ap_id: recipient} <- User.get_cached_by_id(id),
|
||||||
{:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
|
{:ok, %Chat{} = chat} <- Chat.get_or_create(user.id, recipient) do
|
||||||
conn
|
render(conn, "show.json", chat: chat)
|
||||||
|> put_view(ChatView)
|
|
||||||
|> render("show.json", chat: chat)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def show(%{assigns: %{user: user}} = conn, %{id: id}) do
|
def show(%{assigns: %{user: user}} = conn, %{id: id}) do
|
||||||
with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
|
with {:ok, chat} <- Chat.get_by_user_and_id(user, id) do
|
||||||
conn
|
render(conn, "show.json", chat: chat)
|
||||||
|> put_view(ChatView)
|
end
|
||||||
|> render("show.json", chat: chat)
|
end
|
||||||
|
|
||||||
|
defp idempotency_key(conn) do
|
||||||
|
case get_req_header(conn, "idempotency-key") do
|
||||||
|
[key] -> key
|
||||||
|
_ -> nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.InstancesController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Instances
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaInstancesOperation
|
||||||
|
|
||||||
|
def show(conn, _params) do
|
||||||
|
unreachable =
|
||||||
|
Instances.get_consistently_unreachable()
|
||||||
|
|> Map.new(fn {host, date} -> {host, to_string(date)} end)
|
||||||
|
|
||||||
|
json(conn, %{"unreachable" => unreachable})
|
||||||
|
end
|
||||||
|
end
|
28
lib/pleroma/web/pleroma_api/views/backup_view.ex
Normal file
28
lib/pleroma/web/pleroma_api/views/backup_view.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.BackupView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
alias Pleroma.User.Backup
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
|
||||||
|
def render("show.json", %{backup: %Backup{} = backup}) do
|
||||||
|
%{
|
||||||
|
content_type: backup.content_type,
|
||||||
|
url: download_url(backup),
|
||||||
|
file_size: backup.file_size,
|
||||||
|
processed: backup.processed,
|
||||||
|
inserted_at: Utils.to_masto_date(backup.inserted_at)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("index.json", %{backups: backups}) do
|
||||||
|
render_many(backups, __MODULE__, "show.json")
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_url(%Backup{file_name: file_name}) do
|
||||||
|
Pleroma.Web.Endpoint.url() <> "/media/backups/" <> file_name
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
|
defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
|
||||||
use Pleroma.Web, :view
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
alias Pleroma.Web.MastodonAPI.StatusView
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
@ -37,6 +38,7 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
|
||||||
Pleroma.Web.RichMedia.Helpers.fetch_data_for_object(object)
|
Pleroma.Web.RichMedia.Helpers.fetch_data_for_object(object)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|> put_idempotency_key()
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("index.json", opts) do
|
def render("index.json", opts) do
|
||||||
|
@ -47,4 +49,13 @@ defmodule Pleroma.Web.PleromaAPI.Chat.MessageReferenceView do
|
||||||
Map.put(opts, :as, :chat_message_reference)
|
Map.put(opts, :as, :chat_message_reference)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp put_idempotency_key(data) do
|
||||||
|
with {:ok, idempotency_key} <- Cachex.get(:chat_message_id_idempotency_key_cache, data.id) do
|
||||||
|
data
|
||||||
|
|> Maps.put_if_present(:idempotency_key, idempotency_key)
|
||||||
|
else
|
||||||
|
_ -> data
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,22 +34,26 @@ defmodule Pleroma.Web.Plugs.FrontendStatic do
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(conn, opts) do
|
def call(conn, opts) do
|
||||||
frontend_type = Map.get(opts, :frontend_type, :primary)
|
with false <- invalid_path?(conn.path_info),
|
||||||
path = file_path("", frontend_type)
|
frontend_type <- Map.get(opts, :frontend_type, :primary),
|
||||||
|
path when not is_nil(path) <- file_path("", frontend_type) do
|
||||||
if path do
|
call_static(conn, opts, path)
|
||||||
conn
|
|
||||||
|> call_static(opts, path)
|
|
||||||
else
|
else
|
||||||
|
_ ->
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp invalid_path?(list) do
|
||||||
|
invalid_path?(list, :binary.compile_pattern(["/", "\\", ":", "\0"]))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp invalid_path?([h | _], _match) when h in [".", "..", ""], do: true
|
||||||
|
defp invalid_path?([h | t], match), do: String.contains?(h, match) or invalid_path?(t)
|
||||||
|
defp invalid_path?([], _match), do: false
|
||||||
|
|
||||||
defp call_static(conn, opts, from) do
|
defp call_static(conn, opts, from) do
|
||||||
opts =
|
opts = Map.put(opts, :from, from)
|
||||||
opts
|
|
||||||
|> Map.put(:from, from)
|
|
||||||
|
|
||||||
Plug.Static.call(conn, opts)
|
Plug.Static.call(conn, opts)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,26 @@
|
||||||
defmodule Pleroma.Web.Router do
|
defmodule Pleroma.Web.Router do
|
||||||
use Pleroma.Web, :router
|
use Pleroma.Web, :router
|
||||||
|
|
||||||
|
pipeline :accepts_html do
|
||||||
|
plug(:accepts, ["html"])
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :accepts_html_xml do
|
||||||
|
plug(:accepts, ["html", "xml", "rss", "atom"])
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :accepts_html_json do
|
||||||
|
plug(:accepts, ["html", "activity+json", "json"])
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :accepts_html_xml_json do
|
||||||
|
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
||||||
|
end
|
||||||
|
|
||||||
|
pipeline :accepts_xml_rss_atom do
|
||||||
|
plug(:accepts, ["xml", "rss", "atom"])
|
||||||
|
end
|
||||||
|
|
||||||
pipeline :browser do
|
pipeline :browser do
|
||||||
plug(:accepts, ["html"])
|
plug(:accepts, ["html"])
|
||||||
plug(:fetch_session)
|
plug(:fetch_session)
|
||||||
|
@ -129,16 +149,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
scope "/api/pleroma/admin", Pleroma.Web.AdminAPI do
|
||||||
pipe_through(:admin_api)
|
pipe_through(:admin_api)
|
||||||
|
|
||||||
post("/users/follow", AdminAPIController, :user_follow)
|
|
||||||
post("/users/unfollow", AdminAPIController, :user_unfollow)
|
|
||||||
|
|
||||||
put("/users/disable_mfa", AdminAPIController, :disable_mfa)
|
put("/users/disable_mfa", AdminAPIController, :disable_mfa)
|
||||||
delete("/users", AdminAPIController, :user_delete)
|
|
||||||
post("/users", AdminAPIController, :users_create)
|
|
||||||
patch("/users/:nickname/toggle_activation", AdminAPIController, :user_toggle_activation)
|
|
||||||
patch("/users/activate", AdminAPIController, :user_activate)
|
|
||||||
patch("/users/deactivate", AdminAPIController, :user_deactivate)
|
|
||||||
patch("/users/approve", AdminAPIController, :user_approve)
|
|
||||||
put("/users/tag", AdminAPIController, :tag_users)
|
put("/users/tag", AdminAPIController, :tag_users)
|
||||||
delete("/users/tag", AdminAPIController, :untag_users)
|
delete("/users/tag", AdminAPIController, :untag_users)
|
||||||
|
|
||||||
|
@ -161,6 +172,15 @@ defmodule Pleroma.Web.Router do
|
||||||
:right_delete_multiple
|
:right_delete_multiple
|
||||||
)
|
)
|
||||||
|
|
||||||
|
post("/users/follow", UserController, :follow)
|
||||||
|
post("/users/unfollow", UserController, :unfollow)
|
||||||
|
delete("/users", UserController, :delete)
|
||||||
|
post("/users", UserController, :create)
|
||||||
|
patch("/users/:nickname/toggle_activation", UserController, :toggle_activation)
|
||||||
|
patch("/users/activate", UserController, :activate)
|
||||||
|
patch("/users/deactivate", UserController, :deactivate)
|
||||||
|
patch("/users/approve", UserController, :approve)
|
||||||
|
|
||||||
get("/relay", RelayController, :index)
|
get("/relay", RelayController, :index)
|
||||||
post("/relay", RelayController, :follow)
|
post("/relay", RelayController, :follow)
|
||||||
delete("/relay", RelayController, :unfollow)
|
delete("/relay", RelayController, :unfollow)
|
||||||
|
@ -175,8 +195,8 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
|
get("/users/:nickname/credentials", AdminAPIController, :show_user_credentials)
|
||||||
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
patch("/users/:nickname/credentials", AdminAPIController, :update_user_credentials)
|
||||||
|
|
||||||
get("/users", AdminAPIController, :list_users)
|
get("/users", UserController, :list)
|
||||||
get("/users/:nickname", AdminAPIController, :user_show)
|
get("/users/:nickname", UserController, :show)
|
||||||
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
get("/users/:nickname/statuses", AdminAPIController, :list_user_statuses)
|
||||||
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
|
get("/users/:nickname/chats", AdminAPIController, :list_user_chats)
|
||||||
|
|
||||||
|
@ -223,6 +243,8 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/chats/:id", ChatController, :show)
|
get("/chats/:id", ChatController, :show)
|
||||||
get("/chats/:id/messages", ChatController, :messages)
|
get("/chats/:id/messages", ChatController, :messages)
|
||||||
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
delete("/chats/:id/messages/:message_id", ChatController, :delete_message)
|
||||||
|
|
||||||
|
post("/backups", AdminAPIController, :create_backup)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
|
@ -353,6 +375,9 @@ defmodule Pleroma.Web.Router do
|
||||||
put("/mascot", MascotController, :update)
|
put("/mascot", MascotController, :update)
|
||||||
|
|
||||||
post("/scrobble", ScrobbleController, :create)
|
post("/scrobble", ScrobbleController, :create)
|
||||||
|
|
||||||
|
get("/backups", BackupController, :index)
|
||||||
|
post("/backups", BackupController, :create)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope [] do
|
scope [] do
|
||||||
|
@ -373,6 +398,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
get("/accounts/:id/scrobbles", ScrobbleController, :index)
|
get("/accounts/:id/scrobbles", ScrobbleController, :index)
|
||||||
|
get("/federation_status", InstancesController, :show)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||||
|
@ -566,30 +592,43 @@ defmodule Pleroma.Web.Router do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
pipeline :ostatus do
|
|
||||||
plug(:accepts, ["html", "xml", "rss", "atom", "activity+json", "json"])
|
|
||||||
plug(Pleroma.Web.Plugs.StaticFEPlug)
|
|
||||||
end
|
|
||||||
|
|
||||||
pipeline :oembed do
|
|
||||||
plug(:accepts, ["json", "xml"])
|
|
||||||
end
|
|
||||||
|
|
||||||
scope "/", Pleroma.Web do
|
scope "/", Pleroma.Web do
|
||||||
pipe_through([:ostatus, :http_signature])
|
# Note: html format is supported only if static FE is enabled
|
||||||
|
# Note: http signature is only considered for json requests (no auth for non-json requests)
|
||||||
|
pipe_through([:accepts_html_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
|
||||||
|
|
||||||
get("/objects/:uuid", OStatus.OStatusController, :object)
|
get("/objects/:uuid", OStatus.OStatusController, :object)
|
||||||
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
get("/activities/:uuid", OStatus.OStatusController, :activity)
|
||||||
get("/notice/:id", OStatus.OStatusController, :notice)
|
get("/notice/:id", OStatus.OStatusController, :notice)
|
||||||
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
|
||||||
|
|
||||||
# Mastodon compatibility routes
|
# Mastodon compatibility routes
|
||||||
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
get("/users/:nickname/statuses/:id", OStatus.OStatusController, :object)
|
||||||
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
get("/users/:nickname/statuses/:id/activity", OStatus.OStatusController, :activity)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
# Note: html format is supported only if static FE is enabled
|
||||||
|
# Note: http signature is only considered for json requests (no auth for non-json requests)
|
||||||
|
pipe_through([:accepts_html_xml_json, :http_signature, Pleroma.Web.Plugs.StaticFEPlug])
|
||||||
|
|
||||||
|
# Note: returns user _profile_ for json requests, redirects to user _feed_ for non-json ones
|
||||||
|
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
# Note: html format is supported only if static FE is enabled
|
||||||
|
pipe_through([:accepts_html_xml, Pleroma.Web.Plugs.StaticFEPlug])
|
||||||
|
|
||||||
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
|
get("/users/:nickname/feed", Feed.UserController, :feed, as: :user_feed)
|
||||||
get("/users/:nickname", Feed.UserController, :feed_redirect, as: :user_feed)
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
pipe_through(:accepts_html)
|
||||||
|
get("/notice/:id/embed_player", OStatus.OStatusController, :notice_player)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope "/", Pleroma.Web do
|
||||||
|
pipe_through(:accepts_xml_rss_atom)
|
||||||
get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed)
|
get("/tags/:tag", Feed.TagController, :feed, as: :tag_feed)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -17,12 +17,96 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
||||||
plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
|
plug(:put_view, Pleroma.Web.StaticFE.StaticFEView)
|
||||||
plug(:assign_id)
|
plug(:assign_id)
|
||||||
|
|
||||||
plug(Pleroma.Web.Plugs.EnsureAuthenticatedPlug,
|
|
||||||
unless_func: &Pleroma.Web.Plugs.FederatingPlug.federating?/1
|
|
||||||
)
|
|
||||||
|
|
||||||
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
@page_keys ["max_id", "min_id", "limit", "since_id", "order"]
|
||||||
|
|
||||||
|
@doc "Renders requested local public activity or public activities of requested user"
|
||||||
|
def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
|
||||||
|
with %Activity{local: true} = activity <-
|
||||||
|
Activity.get_by_id_with_object(notice_id),
|
||||||
|
true <- Visibility.is_public?(activity.object),
|
||||||
|
{_, true} <- {:visible?, Visibility.visible_for_user?(activity, _reading_user = nil)},
|
||||||
|
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
|
||||||
|
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
|
||||||
|
|
||||||
|
timeline =
|
||||||
|
activity.object.data["context"]
|
||||||
|
|> ActivityPub.fetch_activities_for_context(%{})
|
||||||
|
|> Enum.reverse()
|
||||||
|
|> Enum.map(&represent(&1, &1.object.id == activity.object.id))
|
||||||
|
|
||||||
|
render(conn, "conversation.html", %{activities: timeline, meta: meta})
|
||||||
|
else
|
||||||
|
%Activity{object: %Object{data: data}} ->
|
||||||
|
conn
|
||||||
|
|> put_status(:found)
|
||||||
|
|> redirect(external: data["url"] || data["external_url"] || data["id"])
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
not_found(conn, "Post not found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
|
||||||
|
with {_, %User{local: true} = user} <-
|
||||||
|
{:fetch_user, User.get_cached_by_nickname_or_id(username_or_id)},
|
||||||
|
{_, :visible} <- {:visibility, User.visible_for(user, _reading_user = nil)} do
|
||||||
|
meta = Metadata.build_tags(%{user: user})
|
||||||
|
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.take(@page_keys)
|
||||||
|
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
||||||
|
|
||||||
|
timeline =
|
||||||
|
user
|
||||||
|
|> ActivityPub.fetch_user_activities(_reading_user = nil, params)
|
||||||
|
|> Enum.map(&represent/1)
|
||||||
|
|
||||||
|
prev_page_id =
|
||||||
|
(params["min_id"] || params["max_id"]) &&
|
||||||
|
List.first(timeline) && List.first(timeline).id
|
||||||
|
|
||||||
|
next_page_id = List.last(timeline) && List.last(timeline).id
|
||||||
|
|
||||||
|
render(conn, "profile.html", %{
|
||||||
|
user: User.sanitize_html(user),
|
||||||
|
timeline: timeline,
|
||||||
|
prev_page_id: prev_page_id,
|
||||||
|
next_page_id: next_page_id,
|
||||||
|
meta: meta
|
||||||
|
})
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
not_found(conn, "User not found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(%{assigns: %{object_id: _}} = conn, _params) do
|
||||||
|
url = Helpers.url(conn) <> conn.request_path
|
||||||
|
|
||||||
|
case Activity.get_create_by_object_ap_id_with_object(url) do
|
||||||
|
%Activity{} = activity ->
|
||||||
|
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||||
|
redirect(conn, to: to)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
not_found(conn, "Post not found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show(%{assigns: %{activity_id: _}} = conn, _params) do
|
||||||
|
url = Helpers.url(conn) <> conn.request_path
|
||||||
|
|
||||||
|
case Activity.get_by_ap_id(url) do
|
||||||
|
%Activity{} = activity ->
|
||||||
|
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
||||||
|
redirect(conn, to: to)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
not_found(conn, "Post not found.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
|
defp get_title(%Object{data: %{"name" => name}}) when is_binary(name),
|
||||||
do: name
|
do: name
|
||||||
|
|
||||||
|
@ -81,91 +165,6 @@ defmodule Pleroma.Web.StaticFE.StaticFEController do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
def show(%{assigns: %{notice_id: notice_id}} = conn, _params) do
|
|
||||||
with %Activity{local: true} = activity <-
|
|
||||||
Activity.get_by_id_with_object(notice_id),
|
|
||||||
true <- Visibility.is_public?(activity.object),
|
|
||||||
%User{} = user <- User.get_by_ap_id(activity.object.data["actor"]) do
|
|
||||||
meta = Metadata.build_tags(%{activity_id: notice_id, object: activity.object, user: user})
|
|
||||||
|
|
||||||
timeline =
|
|
||||||
activity.object.data["context"]
|
|
||||||
|> ActivityPub.fetch_activities_for_context(%{})
|
|
||||||
|> Enum.reverse()
|
|
||||||
|> Enum.map(&represent(&1, &1.object.id == activity.object.id))
|
|
||||||
|
|
||||||
render(conn, "conversation.html", %{activities: timeline, meta: meta})
|
|
||||||
else
|
|
||||||
%Activity{object: %Object{data: data}} ->
|
|
||||||
conn
|
|
||||||
|> put_status(:found)
|
|
||||||
|> redirect(external: data["url"] || data["external_url"] || data["id"])
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
not_found(conn, "Post not found.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def show(%{assigns: %{username_or_id: username_or_id}} = conn, params) do
|
|
||||||
case User.get_cached_by_nickname_or_id(username_or_id) do
|
|
||||||
%User{} = user ->
|
|
||||||
meta = Metadata.build_tags(%{user: user})
|
|
||||||
|
|
||||||
params =
|
|
||||||
params
|
|
||||||
|> Map.take(@page_keys)
|
|
||||||
|> Map.new(fn {k, v} -> {String.to_existing_atom(k), v} end)
|
|
||||||
|
|
||||||
timeline =
|
|
||||||
user
|
|
||||||
|> ActivityPub.fetch_user_activities(nil, params)
|
|
||||||
|> Enum.map(&represent/1)
|
|
||||||
|
|
||||||
prev_page_id =
|
|
||||||
(params["min_id"] || params["max_id"]) &&
|
|
||||||
List.first(timeline) && List.first(timeline).id
|
|
||||||
|
|
||||||
next_page_id = List.last(timeline) && List.last(timeline).id
|
|
||||||
|
|
||||||
render(conn, "profile.html", %{
|
|
||||||
user: User.sanitize_html(user),
|
|
||||||
timeline: timeline,
|
|
||||||
prev_page_id: prev_page_id,
|
|
||||||
next_page_id: next_page_id,
|
|
||||||
meta: meta
|
|
||||||
})
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
not_found(conn, "User not found.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def show(%{assigns: %{object_id: _}} = conn, _params) do
|
|
||||||
url = Helpers.url(conn) <> conn.request_path
|
|
||||||
|
|
||||||
case Activity.get_create_by_object_ap_id_with_object(url) do
|
|
||||||
%Activity{} = activity ->
|
|
||||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
|
||||||
redirect(conn, to: to)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
not_found(conn, "Post not found.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def show(%{assigns: %{activity_id: _}} = conn, _params) do
|
|
||||||
url = Helpers.url(conn) <> conn.request_path
|
|
||||||
|
|
||||||
case Activity.get_by_ap_id(url) do
|
|
||||||
%Activity{} = activity ->
|
|
||||||
to = Helpers.o_status_path(Pleroma.Web.Endpoint, :notice, activity)
|
|
||||||
redirect(conn, to: to)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
not_found(conn, "Post not found.")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
defp assign_id(%{path_info: ["notice", notice_id]} = conn, _opts),
|
||||||
do: assign(conn, :notice_id, notice_id)
|
do: assign(conn, :notice_id, notice_id)
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,15 @@ defmodule Pleroma.Web.Streamer do
|
||||||
{:ok, "hashtag:" <> tag}
|
{:ok, "hashtag:" <> tag}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Allow remote instance streams.
|
||||||
|
def get_topic("public:remote", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||||
|
{:ok, "public:remote:" <> instance}
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_topic("public:remote:media", _user, _oauth_token, %{"instance" => instance} = _params) do
|
||||||
|
{:ok, "public:remote:media:" <> instance}
|
||||||
|
end
|
||||||
|
|
||||||
# Expand user streams.
|
# Expand user streams.
|
||||||
def get_topic(
|
def get_topic(
|
||||||
stream,
|
stream,
|
||||||
|
|
54
lib/pleroma/workers/backup_worker.ex
Normal file
54
lib/pleroma/workers/backup_worker.ex
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Workers.BackupWorker do
|
||||||
|
use Oban.Worker, queue: :backup, max_attempts: 1
|
||||||
|
|
||||||
|
alias Oban.Job
|
||||||
|
alias Pleroma.User.Backup
|
||||||
|
|
||||||
|
def process(backup, admin_user_id \\ nil) do
|
||||||
|
%{"op" => "process", "backup_id" => backup.id, "admin_user_id" => admin_user_id}
|
||||||
|
|> new()
|
||||||
|
|> Oban.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
def schedule_deletion(backup) do
|
||||||
|
days = Pleroma.Config.get([Backup, :purge_after_days])
|
||||||
|
time = 60 * 60 * 24 * days
|
||||||
|
scheduled_at = Calendar.NaiveDateTime.add!(backup.inserted_at, time)
|
||||||
|
|
||||||
|
%{"op" => "delete", "backup_id" => backup.id}
|
||||||
|
|> new(scheduled_at: scheduled_at)
|
||||||
|
|> Oban.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(backup) do
|
||||||
|
%{"op" => "delete", "backup_id" => backup.id}
|
||||||
|
|> new()
|
||||||
|
|> Oban.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform(%Job{
|
||||||
|
args: %{"op" => "process", "backup_id" => backup_id, "admin_user_id" => admin_user_id}
|
||||||
|
}) do
|
||||||
|
with {:ok, %Backup{} = backup} <-
|
||||||
|
backup_id |> Backup.get() |> Backup.process(),
|
||||||
|
{:ok, _job} <- schedule_deletion(backup),
|
||||||
|
:ok <- Backup.remove_outdated(backup),
|
||||||
|
{:ok, _} <-
|
||||||
|
backup
|
||||||
|
|> Pleroma.Emails.UserEmail.backup_is_ready_email(admin_user_id)
|
||||||
|
|> Pleroma.Emails.Mailer.deliver() do
|
||||||
|
{:ok, backup}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform(%Job{args: %{"op" => "delete", "backup_id" => backup_id}}) do
|
||||||
|
case Backup.get(backup_id) do
|
||||||
|
%Backup{} = backup -> Backup.delete(backup)
|
||||||
|
nil -> :ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
4
mix.exs
4
mix.exs
|
@ -134,7 +134,7 @@ defmodule Pleroma.Mixfile do
|
||||||
{:cachex, "~> 3.2"},
|
{:cachex, "~> 3.2"},
|
||||||
{:poison, "~> 3.0", override: true},
|
{:poison, "~> 3.0", override: true},
|
||||||
{:tesla,
|
{:tesla,
|
||||||
git: "https://github.com/teamon/tesla/",
|
git: "https://github.com/teamon/tesla.git",
|
||||||
ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30",
|
ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30",
|
||||||
override: true},
|
override: true},
|
||||||
{:castore, "~> 0.1"},
|
{:castore, "~> 0.1"},
|
||||||
|
@ -196,7 +196,7 @@ defmodule Pleroma.Mixfile do
|
||||||
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
ref: "e0f16822d578866e186a0974d65ad58cddc1e2ab"},
|
||||||
{:restarter, path: "./restarter"},
|
{:restarter, path: "./restarter"},
|
||||||
{:majic,
|
{:majic,
|
||||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/majic", branch: "develop"},
|
git: "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", branch: "develop"},
|
||||||
{:open_api_spex,
|
{:open_api_spex,
|
||||||
git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git",
|
git: "https://git.pleroma.social/pleroma/elixir-libraries/open_api_spex.git",
|
||||||
ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"},
|
ref: "f296ac0924ba3cf79c7a588c4c252889df4c2edd"},
|
||||||
|
|
4
mix.lock
4
mix.lock
|
@ -66,7 +66,7 @@
|
||||||
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
||||||
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
|
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
|
||||||
"linkify": {:hex, :linkify, "0.2.0", "2518bbbea21d2caa9d372424e1ad845b640c6630e2d016f1bd1f518f9ebcca28", [:mix], [], "hexpm", "b8ca8a68b79e30b7938d6c996085f3db14939f29538a59ca5101988bb7f917f6"},
|
"linkify": {:hex, :linkify, "0.2.0", "2518bbbea21d2caa9d372424e1ad845b640c6630e2d016f1bd1f518f9ebcca28", [:mix], [], "hexpm", "b8ca8a68b79e30b7938d6c996085f3db14939f29538a59ca5101988bb7f917f6"},
|
||||||
"majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic", "4c692e544b28d1f5e543fb8a44be090f8cd96f80", [branch: "develop"]},
|
"majic": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/majic.git", "4c692e544b28d1f5e543fb8a44be090f8cd96f80", [branch: "develop"]},
|
||||||
"makeup": {:hex, :makeup, "1.0.3", "e339e2f766d12e7260e6672dd4047405963c5ec99661abdc432e6ec67d29ef95", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2e9b4996d11832947731f7608fed7ad2f9443011b3b479ae288011265cdd3dad"},
|
"makeup": {:hex, :makeup, "1.0.3", "e339e2f766d12e7260e6672dd4047405963c5ec99661abdc432e6ec67d29ef95", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "2e9b4996d11832947731f7608fed7ad2f9443011b3b479ae288011265cdd3dad"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||||
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
|
"meck": {:hex, :meck, "0.8.13", "ffedb39f99b0b99703b8601c6f17c7f76313ee12de6b646e671e3188401f7866", [:rebar3], [], "hexpm", "d34f013c156db51ad57cc556891b9720e6a1c1df5fe2e15af999c84d6cebeb1a"},
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
"swoosh": {:hex, :swoosh, "1.0.6", "6765e334c67dacabe721f0d701c7e5a6f06e4595c90df6f91e73ebd54d555833", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "7c50ef78e4acfd1cbd4907dc1fa87b5540675a6be9dc979d04890f49d7ec1830"},
|
"swoosh": {:hex, :swoosh, "1.0.6", "6765e334c67dacabe721f0d701c7e5a6f06e4595c90df6f91e73ebd54d555833", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm", "7c50ef78e4acfd1cbd4907dc1fa87b5540675a6be9dc979d04890f49d7ec1830"},
|
||||||
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
|
"syslog": {:hex, :syslog, "1.1.0", "6419a232bea84f07b56dc575225007ffe34d9fdc91abe6f1b2f254fd71d8efc2", [:rebar3], [], "hexpm", "4c6a41373c7e20587be33ef841d3de6f3beba08519809329ecc4d27b15b659e1"},
|
||||||
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
|
"telemetry": {:hex, :telemetry, "0.4.2", "2808c992455e08d6177322f14d3bdb6b625fbcfd233a73505870d8738a2f4599", [:rebar3], [], "hexpm", "2d1419bd9dda6a206d7b5852179511722e2b18812310d304620c7bd92a13fcef"},
|
||||||
"tesla": {:git, "https://github.com/teamon/tesla/", "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30", [ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30"]},
|
"tesla": {:git, "https://github.com/teamon/tesla.git", "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30", [ref: "9f7261ca49f9f901ceb73b60219ad6f8a9f6aa30"]},
|
||||||
"timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"},
|
"timex": {:hex, :timex, "3.6.2", "845cdeb6119e2fef10751c0b247b6c59d86d78554c83f78db612e3290f819bc2", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5 or ~> 1.0.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "26030b46199d02a590be61c2394b37ea25a3664c02fafbeca0b24c972025d47a"},
|
||||||
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
"trailing_format_plug": {:hex, :trailing_format_plug, "0.0.7", "64b877f912cf7273bed03379936df39894149e35137ac9509117e59866e10e45", [:mix], [{:plug, "> 0.12.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bd4fde4c15f3e993a999e019d64347489b91b7a9096af68b2bdadd192afa693f"},
|
||||||
"tzdata": {:hex, :tzdata, "1.0.4", "a3baa4709ea8dba552dca165af6ae97c624a2d6ac14bd265165eaa8e8af94af6", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "b02637db3df1fd66dd2d3c4f194a81633d0e4b44308d36c1b2fdfd1e4e6f169b"},
|
"tzdata": {:hex, :tzdata, "1.0.4", "a3baa4709ea8dba552dca165af6ae97c624a2d6ac14bd265165eaa8e8af94af6", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "b02637db3df1fd66dd2d3c4f194a81633d0e4b44308d36c1b2fdfd1e4e6f169b"},
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.RemoveUnreadConversationCountFromUser do
|
||||||
|
use Ecto.Migration
|
||||||
|
import Ecto.Query
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
def up do
|
||||||
|
alter table(:users) do
|
||||||
|
remove_if_exists(:unread_conversation_count, :integer)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
alter table(:users) do
|
||||||
|
add_if_not_exists(:unread_conversation_count, :integer, default: 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
flush()
|
||||||
|
recalc_unread_conversation_count()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp recalc_unread_conversation_count do
|
||||||
|
participations_subquery =
|
||||||
|
from(
|
||||||
|
p in "conversation_participations",
|
||||||
|
where: p.read == false,
|
||||||
|
group_by: p.user_id,
|
||||||
|
select: %{user_id: p.user_id, unread_conversation_count: count(p.id)}
|
||||||
|
)
|
||||||
|
|
||||||
|
from(
|
||||||
|
u in "users",
|
||||||
|
join: p in subquery(participations_subquery),
|
||||||
|
on: p.user_id == u.id,
|
||||||
|
update: [set: [unread_conversation_count: p.unread_conversation_count]]
|
||||||
|
)
|
||||||
|
|> Repo.update_all([])
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,12 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddUnreadIndexToConversationParticipation do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create(
|
||||||
|
index(:conversation_participations, [:user_id],
|
||||||
|
where: "read = false",
|
||||||
|
name: "unread_conversation_participation_count_index"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
17
priv/repo/migrations/20200831192323_create_backups.exs
Normal file
17
priv/repo/migrations/20200831192323_create_backups.exs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateBackups do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create_if_not_exists table(:backups) do
|
||||||
|
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||||
|
add(:file_name, :string, null: false)
|
||||||
|
add(:content_type, :string, null: false)
|
||||||
|
add(:processed, :boolean, null: false, default: false)
|
||||||
|
add(:file_size, :bigint)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(index(:backups, [:user_id]))
|
||||||
|
end
|
||||||
|
end
|
Binary file not shown.
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.5 KiB |
46
test/fixtures/mewmew_no_name.json
vendored
Normal file
46
test/fixtures/mewmew_no_name.json
vendored
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"@context" : [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://princess.cat/schemas/litepub-0.1.jsonld",
|
||||||
|
{
|
||||||
|
"@language" : "und"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attachment" : [],
|
||||||
|
"capabilities" : {
|
||||||
|
"acceptsChatMessages" : true
|
||||||
|
},
|
||||||
|
"discoverable" : false,
|
||||||
|
"endpoints" : {
|
||||||
|
"oauthAuthorizationEndpoint" : "https://princess.cat/oauth/authorize",
|
||||||
|
"oauthRegistrationEndpoint" : "https://princess.cat/api/v1/apps",
|
||||||
|
"oauthTokenEndpoint" : "https://princess.cat/oauth/token",
|
||||||
|
"sharedInbox" : "https://princess.cat/inbox",
|
||||||
|
"uploadMedia" : "https://princess.cat/api/ap/upload_media"
|
||||||
|
},
|
||||||
|
"followers" : "https://princess.cat/users/mewmew/followers",
|
||||||
|
"following" : "https://princess.cat/users/mewmew/following",
|
||||||
|
"icon" : {
|
||||||
|
"type" : "Image",
|
||||||
|
"url" : "https://princess.cat/media/12794fb50e86911e65be97f69196814049dcb398a2f8b58b99bb6591576e648c.png?name=blobcatpresentpink.png"
|
||||||
|
},
|
||||||
|
"id" : "https://princess.cat/users/mewmew",
|
||||||
|
"image" : {
|
||||||
|
"type" : "Image",
|
||||||
|
"url" : "https://princess.cat/media/05d8bf3953ab6028fc920494ffc643fbee9dcef40d7bdd06f107e19acbfbd7f9.png"
|
||||||
|
},
|
||||||
|
"inbox" : "https://princess.cat/users/mewmew/inbox",
|
||||||
|
"manuallyApprovesFollowers" : true,
|
||||||
|
"name" : " ",
|
||||||
|
"outbox" : "https://princess.cat/users/mewmew/outbox",
|
||||||
|
"preferredUsername" : "mewmew",
|
||||||
|
"publicKey" : {
|
||||||
|
"id" : "https://princess.cat/users/mewmew#main-key",
|
||||||
|
"owner" : "https://princess.cat/users/mewmew",
|
||||||
|
"publicKeyPem" : "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAru7VpygVef4zrFwnj0Mh\nrbO/2z2EdKN3rERtNrT8zWsLXNLQ50lfpRPnGDrd+xq7Rva4EIu0d5KJJ9n4vtY0\nuxK3On9vA2oyjLlR9O0lI3XTrHJborG3P7IPXrmNUMFpHiFHNqHp5tugUrs1gUFq\n7tmOmM92IP4Wjk8qNHFcsfnUbaPTX7sNIhteQKdi5HrTb/6lrEIe4G/FlMKRqxo3\nRNHuv6SNFQuiUKvFzjzazvjkjvBSm+aFROgdHa2tKl88StpLr7xmuY8qNFCRT6W0\nLacRp6c8ah5f03Kd+xCBVhCKvKaF1K0ERnQTBiitUh85md+Mtx/CoDoLnmpnngR3\nvQIDAQAB\n-----END PUBLIC KEY-----\n\n"
|
||||||
|
},
|
||||||
|
"summary" : "please reply to my posts as direct messages if you have many followers",
|
||||||
|
"tag" : [],
|
||||||
|
"type" : "Person",
|
||||||
|
"url" : "https://princess.cat/users/mewmew"
|
||||||
|
}
|
|
@ -97,6 +97,20 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
|
||||||
|
|
||||||
refute Enum.member?(topics, "hashtag:2")
|
refute Enum.member?(topics, "hashtag:2")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "non-local action produces public:remote topic", %{activity: activity} do
|
||||||
|
activity = %{activity | local: false, actor: "https://lain.com/users/lain"}
|
||||||
|
topics = Topics.get_activity_topics(activity)
|
||||||
|
|
||||||
|
assert Enum.member?(topics, "public:remote:lain.com")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "local action doesn't produce public:remote topic", %{activity: activity} do
|
||||||
|
activity = %{activity | local: true, actor: "https://lain.com/users/lain"}
|
||||||
|
topics = Topics.get_activity_topics(activity)
|
||||||
|
|
||||||
|
refute Enum.member?(topics, "public:remote:lain.com")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "public visibility create events with attachments" do
|
describe "public visibility create events with attachments" do
|
||||||
|
@ -128,6 +142,13 @@ defmodule Pleroma.Activity.Ir.TopicsTest do
|
||||||
|
|
||||||
refute Enum.member?(topics, "public:local:media")
|
refute Enum.member?(topics, "public:local:media")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "non-local action produces public:remote:media topic", %{activity: activity} do
|
||||||
|
activity = %{activity | local: false, actor: "https://lain.com/users/lain"}
|
||||||
|
topics = Topics.get_activity_topics(activity)
|
||||||
|
|
||||||
|
assert Enum.member?(topics, "public:remote:media:lain.com")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "non-public visibility" do
|
describe "non-public visibility" do
|
||||||
|
|
|
@ -37,9 +37,8 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
|
|
||||||
[%{read: true}] = Participation.for_user(user)
|
[%{read: true}] = Participation.for_user(user)
|
||||||
[%{read: false} = participation] = Participation.for_user(other_user)
|
[%{read: false} = participation] = Participation.for_user(other_user)
|
||||||
|
assert Participation.unread_count(user) == 0
|
||||||
assert User.get_cached_by_id(user.id).unread_conversation_count == 0
|
assert Participation.unread_count(other_user) == 1
|
||||||
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 1
|
|
||||||
|
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
CommonAPI.post(other_user, %{
|
CommonAPI.post(other_user, %{
|
||||||
|
@ -54,8 +53,8 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
[%{read: false}] = Participation.for_user(user)
|
[%{read: false}] = Participation.for_user(user)
|
||||||
[%{read: true}] = Participation.for_user(other_user)
|
[%{read: true}] = Participation.for_user(other_user)
|
||||||
|
|
||||||
assert User.get_cached_by_id(user.id).unread_conversation_count == 1
|
assert Participation.unread_count(user) == 1
|
||||||
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
|
assert Participation.unread_count(other_user) == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
test "for a new conversation, it sets the recipents of the participation" do
|
test "for a new conversation, it sets the recipents of the participation" do
|
||||||
|
@ -264,7 +263,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] =
|
assert [%{read: false}, %{read: false}, %{read: false}, %{read: false}] =
|
||||||
Participation.for_user(blocker)
|
Participation.for_user(blocker)
|
||||||
|
|
||||||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 4
|
assert Participation.unread_count(blocker) == 4
|
||||||
|
|
||||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||||
|
|
||||||
|
@ -272,15 +271,15 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] =
|
assert [%{read: true}, %{read: true}, %{read: true}, %{read: false}] =
|
||||||
Participation.for_user(blocker)
|
Participation.for_user(blocker)
|
||||||
|
|
||||||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 1
|
assert Participation.unread_count(blocker) == 1
|
||||||
|
|
||||||
# The conversation is not marked as read for the blocked user
|
# The conversation is not marked as read for the blocked user
|
||||||
assert [_, _, %{read: false}] = Participation.for_user(blocked)
|
assert [_, _, %{read: false}] = Participation.for_user(blocked)
|
||||||
assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
|
assert Participation.unread_count(blocker) == 1
|
||||||
|
|
||||||
# The conversation is not marked as read for the third user
|
# The conversation is not marked as read for the third user
|
||||||
assert [%{read: false}, _, _] = Participation.for_user(third_user)
|
assert [%{read: false}, _, _] = Participation.for_user(third_user)
|
||||||
assert User.get_cached_by_id(third_user.id).unread_conversation_count == 1
|
assert Participation.unread_count(third_user) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
test "the new conversation with the blocked user is not marked as unread " do
|
test "the new conversation with the blocked user is not marked as unread " do
|
||||||
|
@ -298,7 +297,7 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
})
|
})
|
||||||
|
|
||||||
assert [%{read: true}] = Participation.for_user(blocker)
|
assert [%{read: true}] = Participation.for_user(blocker)
|
||||||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
|
assert Participation.unread_count(blocker) == 0
|
||||||
|
|
||||||
# When the blocked user is a recipient
|
# When the blocked user is a recipient
|
||||||
{:ok, _direct2} =
|
{:ok, _direct2} =
|
||||||
|
@ -308,10 +307,10 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
})
|
})
|
||||||
|
|
||||||
assert [%{read: true}, %{read: true}] = Participation.for_user(blocker)
|
assert [%{read: true}, %{read: true}] = Participation.for_user(blocker)
|
||||||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
|
assert Participation.unread_count(blocker) == 0
|
||||||
|
|
||||||
assert [%{read: false}, _] = Participation.for_user(blocked)
|
assert [%{read: false}, _] = Participation.for_user(blocked)
|
||||||
assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
|
assert Participation.unread_count(blocked) == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
test "the conversation with the blocked user is not marked as unread on a reply" do
|
test "the conversation with the blocked user is not marked as unread on a reply" do
|
||||||
|
@ -327,8 +326,8 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
|
|
||||||
{:ok, _user_relationship} = User.block(blocker, blocked)
|
{:ok, _user_relationship} = User.block(blocker, blocked)
|
||||||
assert [%{read: true}] = Participation.for_user(blocker)
|
assert [%{read: true}] = Participation.for_user(blocker)
|
||||||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
|
|
||||||
|
|
||||||
|
assert Participation.unread_count(blocker) == 0
|
||||||
assert [blocked_participation] = Participation.for_user(blocked)
|
assert [blocked_participation] = Participation.for_user(blocked)
|
||||||
|
|
||||||
# When it's a reply from the blocked user
|
# When it's a reply from the blocked user
|
||||||
|
@ -340,8 +339,8 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
})
|
})
|
||||||
|
|
||||||
assert [%{read: true}] = Participation.for_user(blocker)
|
assert [%{read: true}] = Participation.for_user(blocker)
|
||||||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
|
|
||||||
|
|
||||||
|
assert Participation.unread_count(blocker) == 0
|
||||||
assert [third_user_participation] = Participation.for_user(third_user)
|
assert [third_user_participation] = Participation.for_user(third_user)
|
||||||
|
|
||||||
# When it's a reply from the third user
|
# When it's a reply from the third user
|
||||||
|
@ -353,11 +352,12 @@ defmodule Pleroma.Conversation.ParticipationTest do
|
||||||
})
|
})
|
||||||
|
|
||||||
assert [%{read: true}] = Participation.for_user(blocker)
|
assert [%{read: true}] = Participation.for_user(blocker)
|
||||||
assert User.get_cached_by_id(blocker.id).unread_conversation_count == 0
|
assert Participation.unread_count(blocker) == 0
|
||||||
|
|
||||||
# Marked as unread for the blocked user
|
# Marked as unread for the blocked user
|
||||||
assert [%{read: false}] = Participation.for_user(blocked)
|
assert [%{read: false}] = Participation.for_user(blocked)
|
||||||
assert User.get_cached_by_id(blocked.id).unread_conversation_count == 1
|
|
||||||
|
assert Participation.unread_count(blocked) == 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -49,6 +49,7 @@ defmodule Pleroma.Integration.MastodonWebsocketTest do
|
||||||
test "allows public streams without authentication" do
|
test "allows public streams without authentication" do
|
||||||
assert {:ok, _} = start_socket("?stream=public")
|
assert {:ok, _} = start_socket("?stream=public")
|
||||||
assert {:ok, _} = start_socket("?stream=public:local")
|
assert {:ok, _} = start_socket("?stream=public:local")
|
||||||
|
assert {:ok, _} = start_socket("?stream=public:remote&instance=lain.com")
|
||||||
assert {:ok, _} = start_socket("?stream=hashtag&tag=lain")
|
assert {:ok, _} = start_socket("?stream=hashtag&tag=lain")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
244
test/pleroma/user/backup_test.exs
Normal file
244
test/pleroma/user/backup_test.exs
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.User.BackupTest do
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
import Mock
|
||||||
|
import Pleroma.Factory
|
||||||
|
import Swoosh.TestAssertions
|
||||||
|
|
||||||
|
alias Pleroma.Bookmark
|
||||||
|
alias Pleroma.Tests.ObanHelpers
|
||||||
|
alias Pleroma.User.Backup
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Workers.BackupWorker
|
||||||
|
|
||||||
|
setup do
|
||||||
|
clear_config([Pleroma.Upload, :uploader])
|
||||||
|
clear_config([Backup, :limit_days])
|
||||||
|
clear_config([Pleroma.Emails.Mailer, :enabled], true)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it requries enabled email" do
|
||||||
|
Pleroma.Config.put([Pleroma.Emails.Mailer, :enabled], false)
|
||||||
|
user = insert(:user)
|
||||||
|
assert {:error, "Backups require enabled email"} == Backup.create(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it requries user's email" do
|
||||||
|
user = insert(:user, %{email: nil})
|
||||||
|
assert {:error, "Email is required"} == Backup.create(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it creates a backup record and an Oban job" do
|
||||||
|
%{id: user_id} = user = insert(:user)
|
||||||
|
assert {:ok, %Oban.Job{args: args}} = Backup.create(user)
|
||||||
|
assert_enqueued(worker: BackupWorker, args: args)
|
||||||
|
|
||||||
|
backup = Backup.get(args["backup_id"])
|
||||||
|
assert %Backup{user_id: ^user_id, processed: false, file_size: 0} = backup
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it return an error if the export limit is over" do
|
||||||
|
%{id: user_id} = user = insert(:user)
|
||||||
|
limit_days = Pleroma.Config.get([Backup, :limit_days])
|
||||||
|
assert {:ok, %Oban.Job{args: args}} = Backup.create(user)
|
||||||
|
backup = Backup.get(args["backup_id"])
|
||||||
|
assert %Backup{user_id: ^user_id, processed: false, file_size: 0} = backup
|
||||||
|
|
||||||
|
assert Backup.create(user) == {:error, "Last export was less than #{limit_days} days ago"}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it process a backup record" do
|
||||||
|
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
||||||
|
%{id: user_id} = user = insert(:user)
|
||||||
|
|
||||||
|
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id} = args}} = Backup.create(user)
|
||||||
|
assert {:ok, backup} = perform_job(BackupWorker, args)
|
||||||
|
assert backup.file_size > 0
|
||||||
|
assert %Backup{id: ^backup_id, processed: true, user_id: ^user_id} = backup
|
||||||
|
|
||||||
|
delete_job_args = %{"op" => "delete", "backup_id" => backup_id}
|
||||||
|
|
||||||
|
assert_enqueued(worker: BackupWorker, args: delete_job_args)
|
||||||
|
assert {:ok, backup} = perform_job(BackupWorker, delete_job_args)
|
||||||
|
refute Backup.get(backup_id)
|
||||||
|
|
||||||
|
email = Pleroma.Emails.UserEmail.backup_is_ready_email(backup)
|
||||||
|
|
||||||
|
assert_email_sent(
|
||||||
|
to: {user.name, user.email},
|
||||||
|
html_body: email.html_body
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it removes outdated backups after creating a fresh one" do
|
||||||
|
Pleroma.Config.put([Backup, :limit_days], -1)
|
||||||
|
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
assert {:ok, job1} = Backup.create(user)
|
||||||
|
|
||||||
|
assert {:ok, %Backup{}} = ObanHelpers.perform(job1)
|
||||||
|
assert {:ok, job2} = Backup.create(user)
|
||||||
|
assert Pleroma.Repo.aggregate(Backup, :count) == 2
|
||||||
|
assert {:ok, backup2} = ObanHelpers.perform(job2)
|
||||||
|
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
assert [^backup2] = Pleroma.Repo.all(Backup)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it creates a zip archive with user data" do
|
||||||
|
user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"})
|
||||||
|
|
||||||
|
{:ok, %{object: %{data: %{"id" => id1}}} = status1} =
|
||||||
|
CommonAPI.post(user, %{status: "status1"})
|
||||||
|
|
||||||
|
{:ok, %{object: %{data: %{"id" => id2}}} = status2} =
|
||||||
|
CommonAPI.post(user, %{status: "status2"})
|
||||||
|
|
||||||
|
{:ok, %{object: %{data: %{"id" => id3}}} = status3} =
|
||||||
|
CommonAPI.post(user, %{status: "status3"})
|
||||||
|
|
||||||
|
CommonAPI.favorite(user, status1.id)
|
||||||
|
CommonAPI.favorite(user, status2.id)
|
||||||
|
|
||||||
|
Bookmark.create(user.id, status2.id)
|
||||||
|
Bookmark.create(user.id, status3.id)
|
||||||
|
|
||||||
|
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
|
||||||
|
assert {:ok, path} = Backup.export(backup)
|
||||||
|
assert {:ok, zipfile} = :zip.zip_open(String.to_charlist(path), [:memory])
|
||||||
|
assert {:ok, {'actor.json', json}} = :zip.zip_get('actor.json', zipfile)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"@context" => [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"http://localhost:4001/schemas/litepub-0.1.jsonld",
|
||||||
|
%{"@language" => "und"}
|
||||||
|
],
|
||||||
|
"bookmarks" => "bookmarks.json",
|
||||||
|
"followers" => "http://cofe.io/users/cofe/followers",
|
||||||
|
"following" => "http://cofe.io/users/cofe/following",
|
||||||
|
"id" => "http://cofe.io/users/cofe",
|
||||||
|
"inbox" => "http://cofe.io/users/cofe/inbox",
|
||||||
|
"likes" => "likes.json",
|
||||||
|
"name" => "Cofe",
|
||||||
|
"outbox" => "http://cofe.io/users/cofe/outbox",
|
||||||
|
"preferredUsername" => "cofe",
|
||||||
|
"publicKey" => %{
|
||||||
|
"id" => "http://cofe.io/users/cofe#main-key",
|
||||||
|
"owner" => "http://cofe.io/users/cofe"
|
||||||
|
},
|
||||||
|
"type" => "Person",
|
||||||
|
"url" => "http://cofe.io/users/cofe"
|
||||||
|
} = Jason.decode!(json)
|
||||||
|
|
||||||
|
assert {:ok, {'outbox.json', json}} = :zip.zip_get('outbox.json', zipfile)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "outbox.json",
|
||||||
|
"orderedItems" => [
|
||||||
|
%{
|
||||||
|
"object" => %{
|
||||||
|
"actor" => "http://cofe.io/users/cofe",
|
||||||
|
"content" => "status1",
|
||||||
|
"type" => "Note"
|
||||||
|
},
|
||||||
|
"type" => "Create"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"object" => %{
|
||||||
|
"actor" => "http://cofe.io/users/cofe",
|
||||||
|
"content" => "status2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"actor" => "http://cofe.io/users/cofe",
|
||||||
|
"object" => %{
|
||||||
|
"content" => "status3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"totalItems" => 3,
|
||||||
|
"type" => "OrderedCollection"
|
||||||
|
} = Jason.decode!(json)
|
||||||
|
|
||||||
|
assert {:ok, {'likes.json', json}} = :zip.zip_get('likes.json', zipfile)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "likes.json",
|
||||||
|
"orderedItems" => [^id1, ^id2],
|
||||||
|
"totalItems" => 2,
|
||||||
|
"type" => "OrderedCollection"
|
||||||
|
} = Jason.decode!(json)
|
||||||
|
|
||||||
|
assert {:ok, {'bookmarks.json', json}} = :zip.zip_get('bookmarks.json', zipfile)
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"@context" => "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id" => "bookmarks.json",
|
||||||
|
"orderedItems" => [^id2, ^id3],
|
||||||
|
"totalItems" => 2,
|
||||||
|
"type" => "OrderedCollection"
|
||||||
|
} = Jason.decode!(json)
|
||||||
|
|
||||||
|
:zip.zip_close(zipfile)
|
||||||
|
File.rm!(path)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "it uploads and deletes a backup archive" do
|
||||||
|
setup do
|
||||||
|
clear_config(Pleroma.Uploaders.S3,
|
||||||
|
bucket: "test_bucket",
|
||||||
|
public_endpoint: "https://s3.amazonaws.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
clear_config([Pleroma.Upload, :uploader])
|
||||||
|
|
||||||
|
user = insert(:user, %{nickname: "cofe", name: "Cofe", ap_id: "http://cofe.io/users/cofe"})
|
||||||
|
|
||||||
|
{:ok, status1} = CommonAPI.post(user, %{status: "status1"})
|
||||||
|
{:ok, status2} = CommonAPI.post(user, %{status: "status2"})
|
||||||
|
{:ok, status3} = CommonAPI.post(user, %{status: "status3"})
|
||||||
|
CommonAPI.favorite(user, status1.id)
|
||||||
|
CommonAPI.favorite(user, status2.id)
|
||||||
|
Bookmark.create(user.id, status2.id)
|
||||||
|
Bookmark.create(user.id, status3.id)
|
||||||
|
|
||||||
|
assert {:ok, backup} = user |> Backup.new() |> Repo.insert()
|
||||||
|
assert {:ok, path} = Backup.export(backup)
|
||||||
|
|
||||||
|
[path: path, backup: backup]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "S3", %{path: path, backup: backup} do
|
||||||
|
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.S3)
|
||||||
|
|
||||||
|
with_mock ExAws,
|
||||||
|
request: fn
|
||||||
|
%{http_method: :put} -> {:ok, :ok}
|
||||||
|
%{http_method: :delete} -> {:ok, %{status_code: 204}}
|
||||||
|
end do
|
||||||
|
assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path)
|
||||||
|
assert {:ok, _backup} = Backup.delete(backup)
|
||||||
|
end
|
||||||
|
|
||||||
|
with_mock ExAws, request: fn %{http_method: :delete} -> {:ok, %{status_code: 204}} end do
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Local", %{path: path, backup: backup} do
|
||||||
|
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
||||||
|
|
||||||
|
assert {:ok, %Pleroma.Upload{}} = Backup.upload(backup, path)
|
||||||
|
assert {:ok, _backup} = Backup.delete(backup)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -388,6 +388,7 @@ defmodule Pleroma.UserTest do
|
||||||
}
|
}
|
||||||
|
|
||||||
setup do: clear_config([:instance, :autofollowed_nicknames])
|
setup do: clear_config([:instance, :autofollowed_nicknames])
|
||||||
|
setup do: clear_config([:instance, :autofollowing_nicknames])
|
||||||
setup do: clear_config([:welcome])
|
setup do: clear_config([:welcome])
|
||||||
setup do: clear_config([:instance, :account_activation_required])
|
setup do: clear_config([:instance, :account_activation_required])
|
||||||
|
|
||||||
|
@ -408,6 +409,23 @@ defmodule Pleroma.UserTest do
|
||||||
refute User.following?(registered_user, remote_user)
|
refute User.following?(registered_user, remote_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it adds automatic followers for new registered accounts" do
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :autofollowing_nicknames], [
|
||||||
|
user1.nickname,
|
||||||
|
user2.nickname
|
||||||
|
])
|
||||||
|
|
||||||
|
cng = User.register_changeset(%User{}, @full_user_data)
|
||||||
|
|
||||||
|
{:ok, registered_user} = User.register(cng)
|
||||||
|
|
||||||
|
assert User.following?(user1, registered_user)
|
||||||
|
assert User.following?(user2, registered_user)
|
||||||
|
end
|
||||||
|
|
||||||
test "it sends a welcome message if it is set" do
|
test "it sends a welcome message if it is set" do
|
||||||
welcome_user = insert(:user)
|
welcome_user = insert(:user)
|
||||||
Pleroma.Config.put([:welcome, :direct_message, :enabled], true)
|
Pleroma.Config.put([:welcome, :direct_message, :enabled], true)
|
||||||
|
|
|
@ -156,21 +156,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||||
|
|
||||||
assert response == "Not found"
|
assert response == "Not found"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
put_req_header(
|
|
||||||
conn,
|
|
||||||
"accept",
|
|
||||||
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""
|
|
||||||
)
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}.json", user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "mastodon compatibility routes" do
|
describe "mastodon compatibility routes" do
|
||||||
|
@ -338,18 +323,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||||
|
|
||||||
assert "Not found" == json_response(conn2, :not_found)
|
assert "Not found" == json_response(conn2, :not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user = insert(:user)
|
|
||||||
note = insert(:note)
|
|
||||||
uuid = String.split(note.data["id"], "/") |> List.last()
|
|
||||||
|
|
||||||
conn = put_req_header(conn, "accept", "application/activity+json")
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/objects/#{uuid}", user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/activities/:uuid" do
|
describe "/activities/:uuid" do
|
||||||
|
@ -421,18 +394,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||||
|
|
||||||
assert "Not found" == json_response(conn2, :not_found)
|
assert "Not found" == json_response(conn2, :not_found)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user = insert(:user)
|
|
||||||
activity = insert(:note_activity)
|
|
||||||
uuid = String.split(activity.data["id"], "/") |> List.last()
|
|
||||||
|
|
||||||
conn = put_req_header(conn, "accept", "application/activity+json")
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/activities/#{uuid}", user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "/inbox" do
|
describe "/inbox" do
|
||||||
|
@ -893,15 +854,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
|
||||||
|
|
||||||
assert response(conn, 200) =~ announce_activity.data["object"]
|
assert response(conn, 200) =~ announce_activity.data["object"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
|
||||||
conn: conn
|
|
||||||
} do
|
|
||||||
user = insert(:user)
|
|
||||||
conn = put_req_header(conn, "accept", "application/activity+json")
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}/outbox", user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "POST /users/:nickname/outbox (C2S)" do
|
describe "POST /users/:nickname/outbox (C2S)" do
|
||||||
|
|
|
@ -2300,4 +2300,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
|
||||||
assert length(activities) == 2
|
assert length(activities) == 2
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "allow fetching of accounts with an empty string name field" do
|
||||||
|
Tesla.Mock.mock(fn
|
||||||
|
%{method: :get, url: "https://princess.cat/users/mewmew"} ->
|
||||||
|
file = File.read!("test/fixtures/mewmew_no_name.json")
|
||||||
|
%Tesla.Env{status: 200, body: file}
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, user} = ActivityPub.make_user_from_ap_id("https://princess.cat/users/mewmew")
|
||||||
|
assert user.name == " "
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,6 +27,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnswerHandlingTest do
|
||||||
})
|
})
|
||||||
|
|
||||||
object = Object.normalize(activity)
|
object = Object.normalize(activity)
|
||||||
|
assert object.data["repliesCount"] == nil
|
||||||
|
|
||||||
data =
|
data =
|
||||||
File.read!("test/fixtures/mastodon-vote.json")
|
File.read!("test/fixtures/mastodon-vote.json")
|
||||||
|
@ -41,7 +42,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.AnswerHandlingTest do
|
||||||
assert answer_object.data["inReplyTo"] == object.data["id"]
|
assert answer_object.data["inReplyTo"] == object.data["id"]
|
||||||
|
|
||||||
new_object = Object.get_by_ap_id(object.data["id"])
|
new_object = Object.get_by_ap_id(object.data["id"])
|
||||||
assert new_object.data["replies_count"] == object.data["replies_count"]
|
assert new_object.data["repliesCount"] == nil
|
||||||
|
|
||||||
assert Enum.any?(
|
assert Enum.any?(
|
||||||
new_object.data["oneOf"],
|
new_object.data["oneOf"],
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,7 +9,6 @@ defmodule Pleroma.Web.AdminAPI.ChatControllerTest do
|
||||||
|
|
||||||
alias Pleroma.Chat
|
alias Pleroma.Chat
|
||||||
alias Pleroma.Chat.MessageReference
|
alias Pleroma.Chat.MessageReference
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.ModerationLog
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do
|
defmodule Pleroma.Web.AdminAPI.InstanceDocumentControllerTest do
|
||||||
use Pleroma.Web.ConnCase, async: true
|
use Pleroma.Web.ConnCase, async: true
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
alias Pleroma.Config
|
|
||||||
|
|
||||||
@dir "test/tmp/instance_static"
|
@dir "test/tmp/instance_static"
|
||||||
@default_instance_panel ~s(<p>Welcome to <a href="https://pleroma.social" target="_blank">Pleroma!</a></p>)
|
@default_instance_panel ~s(<p>Welcome to <a href="https://pleroma.social" target="_blank">Pleroma!</a></p>)
|
||||||
|
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.OAuthAppControllerTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
|
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.AdminAPI.RelayControllerTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.ModerationLog
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.ModerationLog
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.ReportNote
|
alias Pleroma.ReportNote
|
||||||
|
@ -38,12 +37,21 @@ defmodule Pleroma.Web.AdminAPI.ReportControllerTest do
|
||||||
status_ids: [activity.id]
|
status_ids: [activity.id]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/reports/#{report_id}/notes", %{
|
||||||
|
content: "this is an admin note"
|
||||||
|
})
|
||||||
|
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> get("/api/pleroma/admin/reports/#{report_id}")
|
|> get("/api/pleroma/admin/reports/#{report_id}")
|
||||||
|> json_response_and_validate_schema(:ok)
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
assert response["id"] == report_id
|
assert response["id"] == report_id
|
||||||
|
|
||||||
|
[notes] = response["notes"]
|
||||||
|
assert notes["content"] == "this is an admin note"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns 404 when report id is invalid", %{conn: conn} do
|
test "returns 404 when report id is invalid", %{conn: conn} do
|
||||||
|
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.AdminAPI.StatusControllerTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.ModerationLog
|
alias Pleroma.ModerationLog
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
970
test/pleroma/web/admin_api/controllers/user_controller_test.exs
Normal file
970
test/pleroma/web/admin_api/controllers/user_controller_test.exs
Normal file
|
@ -0,0 +1,970 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.UserControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
|
import Mock
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.ModerationLog
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Tests.ObanHelpers
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
Tesla.Mock.mock_global(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
setup do
|
||||||
|
admin = insert(:user, is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with valid `admin_token` query parameter, skips OAuth scopes check" do
|
||||||
|
clear_config([:admin_token], "password123")
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn = get(build_conn(), "/api/pleroma/admin/users/#{user.nickname}?admin_token=password123")
|
||||||
|
|
||||||
|
assert json_response(conn, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with [:auth, :enforce_oauth_admin_scope_usage]," do
|
||||||
|
setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], true)
|
||||||
|
|
||||||
|
test "GET /api/pleroma/admin/users/:nickname requires admin:read:accounts or broader scope",
|
||||||
|
%{admin: admin} do
|
||||||
|
user = insert(:user)
|
||||||
|
url = "/api/pleroma/admin/users/#{user.nickname}"
|
||||||
|
|
||||||
|
good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
|
||||||
|
good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
|
||||||
|
good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
|
||||||
|
|
||||||
|
bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
|
||||||
|
bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
|
||||||
|
bad_token3 = nil
|
||||||
|
|
||||||
|
for good_token <- [good_token1, good_token2, good_token3] do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, good_token)
|
||||||
|
|> get(url)
|
||||||
|
|
||||||
|
assert json_response(conn, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
for good_token <- [good_token1, good_token2, good_token3] do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, nil)
|
||||||
|
|> assign(:token, good_token)
|
||||||
|
|> get(url)
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden)
|
||||||
|
end
|
||||||
|
|
||||||
|
for bad_token <- [bad_token1, bad_token2, bad_token3] do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, bad_token)
|
||||||
|
|> get(url)
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "unless [:auth, :enforce_oauth_admin_scope_usage]," do
|
||||||
|
setup do: clear_config([:auth, :enforce_oauth_admin_scope_usage], false)
|
||||||
|
|
||||||
|
test "GET /api/pleroma/admin/users/:nickname requires " <>
|
||||||
|
"read:accounts or admin:read:accounts or broader scope",
|
||||||
|
%{admin: admin} do
|
||||||
|
user = insert(:user)
|
||||||
|
url = "/api/pleroma/admin/users/#{user.nickname}"
|
||||||
|
|
||||||
|
good_token1 = insert(:oauth_token, user: admin, scopes: ["admin"])
|
||||||
|
good_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read"])
|
||||||
|
good_token3 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts"])
|
||||||
|
good_token4 = insert(:oauth_token, user: admin, scopes: ["read:accounts"])
|
||||||
|
good_token5 = insert(:oauth_token, user: admin, scopes: ["read"])
|
||||||
|
|
||||||
|
good_tokens = [good_token1, good_token2, good_token3, good_token4, good_token5]
|
||||||
|
|
||||||
|
bad_token1 = insert(:oauth_token, user: admin, scopes: ["read:accounts:partial"])
|
||||||
|
bad_token2 = insert(:oauth_token, user: admin, scopes: ["admin:read:accounts:partial"])
|
||||||
|
bad_token3 = nil
|
||||||
|
|
||||||
|
for good_token <- good_tokens do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, good_token)
|
||||||
|
|> get(url)
|
||||||
|
|
||||||
|
assert json_response(conn, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
for good_token <- good_tokens do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, nil)
|
||||||
|
|> assign(:token, good_token)
|
||||||
|
|> get(url)
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden)
|
||||||
|
end
|
||||||
|
|
||||||
|
for bad_token <- [bad_token1, bad_token2, bad_token3] do
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, bad_token)
|
||||||
|
|> get(url)
|
||||||
|
|
||||||
|
assert json_response(conn, :forbidden)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "DELETE /api/pleroma/admin/users" do
|
||||||
|
test "single user", %{admin: admin, conn: conn} do
|
||||||
|
clear_config([:instance, :federating], true)
|
||||||
|
|
||||||
|
user =
|
||||||
|
insert(:user,
|
||||||
|
avatar: %{"url" => [%{"href" => "https://someurl"}]},
|
||||||
|
banner: %{"url" => [%{"href" => "https://somebanner"}]},
|
||||||
|
bio: "Hello world!",
|
||||||
|
name: "A guy"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create some activities to check they got deleted later
|
||||||
|
follower = insert(:user)
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: "test"})
|
||||||
|
{:ok, _, _, _} = CommonAPI.follow(user, follower)
|
||||||
|
{:ok, _, _, _} = CommonAPI.follow(follower, user)
|
||||||
|
user = Repo.get(User, user.id)
|
||||||
|
assert user.note_count == 1
|
||||||
|
assert user.follower_count == 1
|
||||||
|
assert user.following_count == 1
|
||||||
|
refute user.deactivated
|
||||||
|
|
||||||
|
with_mock Pleroma.Web.Federator,
|
||||||
|
publish: fn _ -> nil end,
|
||||||
|
perform: fn _, _ -> nil end do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> delete("/api/pleroma/admin/users?nickname=#{user.nickname}")
|
||||||
|
|
||||||
|
ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
assert User.get_by_nickname(user.nickname).deactivated
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} deleted users: @#{user.nickname}"
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == [user.nickname]
|
||||||
|
|
||||||
|
user = Repo.get(User, user.id)
|
||||||
|
assert user.deactivated
|
||||||
|
|
||||||
|
assert user.avatar == %{}
|
||||||
|
assert user.banner == %{}
|
||||||
|
assert user.note_count == 0
|
||||||
|
assert user.follower_count == 0
|
||||||
|
assert user.following_count == 0
|
||||||
|
assert user.bio == ""
|
||||||
|
assert user.name == nil
|
||||||
|
|
||||||
|
assert called(Pleroma.Web.Federator.publish(:_))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "multiple users", %{admin: admin, conn: conn} do
|
||||||
|
user_one = insert(:user)
|
||||||
|
user_two = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> delete("/api/pleroma/admin/users", %{
|
||||||
|
nicknames: [user_one.nickname, user_two.nickname]
|
||||||
|
})
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} deleted users: @#{user_one.nickname}, @#{user_two.nickname}"
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
assert response -- [user_one.nickname, user_two.nickname] == []
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "/api/pleroma/admin/users" do
|
||||||
|
test "Create", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users", %{
|
||||||
|
"users" => [
|
||||||
|
%{
|
||||||
|
"nickname" => "lain",
|
||||||
|
"email" => "lain@example.org",
|
||||||
|
"password" => "test"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"nickname" => "lain2",
|
||||||
|
"email" => "lain2@example.org",
|
||||||
|
"password" => "test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
response = json_response(conn, 200) |> Enum.map(&Map.get(&1, "type"))
|
||||||
|
assert response == ["success", "success"]
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ["lain", "lain2"] -- Enum.map(log_entry.data["subjects"], & &1["nickname"]) == []
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Cannot create user with existing email", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users", %{
|
||||||
|
"users" => [
|
||||||
|
%{
|
||||||
|
"nickname" => "lain",
|
||||||
|
"email" => user.email,
|
||||||
|
"password" => "test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 409) == [
|
||||||
|
%{
|
||||||
|
"code" => 409,
|
||||||
|
"data" => %{
|
||||||
|
"email" => user.email,
|
||||||
|
"nickname" => "lain"
|
||||||
|
},
|
||||||
|
"error" => "email has already been taken",
|
||||||
|
"type" => "error"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Cannot create user with existing nickname", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users", %{
|
||||||
|
"users" => [
|
||||||
|
%{
|
||||||
|
"nickname" => user.nickname,
|
||||||
|
"email" => "someuser@plerama.social",
|
||||||
|
"password" => "test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 409) == [
|
||||||
|
%{
|
||||||
|
"code" => 409,
|
||||||
|
"data" => %{
|
||||||
|
"email" => "someuser@plerama.social",
|
||||||
|
"nickname" => user.nickname
|
||||||
|
},
|
||||||
|
"error" => "nickname has already been taken",
|
||||||
|
"type" => "error"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "Multiple user creation works in transaction", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users", %{
|
||||||
|
"users" => [
|
||||||
|
%{
|
||||||
|
"nickname" => "newuser",
|
||||||
|
"email" => "newuser@pleroma.social",
|
||||||
|
"password" => "test"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"nickname" => "lain",
|
||||||
|
"email" => user.email,
|
||||||
|
"password" => "test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 409) == [
|
||||||
|
%{
|
||||||
|
"code" => 409,
|
||||||
|
"data" => %{
|
||||||
|
"email" => user.email,
|
||||||
|
"nickname" => "lain"
|
||||||
|
},
|
||||||
|
"error" => "email has already been taken",
|
||||||
|
"type" => "error"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"code" => 409,
|
||||||
|
"data" => %{
|
||||||
|
"email" => "newuser@pleroma.social",
|
||||||
|
"nickname" => "newuser"
|
||||||
|
},
|
||||||
|
"error" => "",
|
||||||
|
"type" => "error"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
assert User.get_by_nickname("newuser") === nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "/api/pleroma/admin/users/:nickname" do
|
||||||
|
test "Show", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
|
||||||
|
|
||||||
|
assert user_response(user) == json_response(conn, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when the user doesn't exist", %{conn: conn} do
|
||||||
|
user = build(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users/#{user.nickname}")
|
||||||
|
|
||||||
|
assert %{"error" => "Not found"} == json_response(conn, 404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "/api/pleroma/admin/users/follow" do
|
||||||
|
test "allows to force-follow another user", %{admin: admin, conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
follower = insert(:user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/follow", %{
|
||||||
|
"follower" => follower.nickname,
|
||||||
|
"followed" => user.nickname
|
||||||
|
})
|
||||||
|
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
|
assert User.following?(follower, user)
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} made @#{follower.nickname} follow @#{user.nickname}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "/api/pleroma/admin/users/unfollow" do
|
||||||
|
test "allows to force-unfollow another user", %{admin: admin, conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
follower = insert(:user)
|
||||||
|
|
||||||
|
User.follow(follower, user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/users/unfollow", %{
|
||||||
|
"follower" => follower.nickname,
|
||||||
|
"followed" => user.nickname
|
||||||
|
})
|
||||||
|
|
||||||
|
user = User.get_cached_by_id(user.id)
|
||||||
|
follower = User.get_cached_by_id(follower.id)
|
||||||
|
|
||||||
|
refute User.following?(follower, user)
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} made @#{follower.nickname} unfollow @#{user.nickname}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/users" do
|
||||||
|
test "renders users array for the first page", %{conn: conn, admin: admin} do
|
||||||
|
user = insert(:user, local: false, tags: ["foo", "bar"])
|
||||||
|
user2 = insert(:user, approval_pending: true, registration_reason: "I'm a chill dude")
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?page=1")
|
||||||
|
|
||||||
|
users =
|
||||||
|
[
|
||||||
|
user_response(
|
||||||
|
admin,
|
||||||
|
%{"roles" => %{"admin" => true, "moderator" => false}}
|
||||||
|
),
|
||||||
|
user_response(user, %{"local" => false, "tags" => ["foo", "bar"]}),
|
||||||
|
user_response(
|
||||||
|
user2,
|
||||||
|
%{
|
||||||
|
"local" => true,
|
||||||
|
"approval_pending" => true,
|
||||||
|
"registration_reason" => "I'm a chill dude",
|
||||||
|
"actor_type" => "Person"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 3,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => users
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "pagination works correctly with service users", %{conn: conn} do
|
||||||
|
service1 = User.get_or_create_service_actor_by_ap_id(Web.base_url() <> "/meido", "meido")
|
||||||
|
|
||||||
|
insert_list(25, :user)
|
||||||
|
|
||||||
|
assert %{"count" => 26, "page_size" => 10, "users" => users1} =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/users?page=1&filters=", %{page_size: "10"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert Enum.count(users1) == 10
|
||||||
|
assert service1 not in users1
|
||||||
|
|
||||||
|
assert %{"count" => 26, "page_size" => 10, "users" => users2} =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/users?page=2&filters=", %{page_size: "10"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert Enum.count(users2) == 10
|
||||||
|
assert service1 not in users2
|
||||||
|
|
||||||
|
assert %{"count" => 26, "page_size" => 10, "users" => users3} =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/users?page=3&filters=", %{page_size: "10"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert Enum.count(users3) == 6
|
||||||
|
assert service1 not in users3
|
||||||
|
end
|
||||||
|
|
||||||
|
test "renders empty array for the second page", %{conn: conn} do
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?page=2")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 2,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => []
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "regular search", %{conn: conn} do
|
||||||
|
user = insert(:user, nickname: "bob")
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?query=bo")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [user_response(user, %{"local" => true})]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by domain", %{conn: conn} do
|
||||||
|
user = insert(:user, nickname: "nickname@domain.com")
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?query=domain.com")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [user_response(user)]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by full nickname", %{conn: conn} do
|
||||||
|
user = insert(:user, nickname: "nickname@domain.com")
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?query=nickname@domain.com")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [user_response(user)]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by display name", %{conn: conn} do
|
||||||
|
user = insert(:user, name: "Display name")
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?name=display")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [user_response(user)]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search by email", %{conn: conn} do
|
||||||
|
user = insert(:user, email: "email@example.com")
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?email=email@example.com")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [user_response(user)]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "regular search with page size", %{conn: conn} do
|
||||||
|
user = insert(:user, nickname: "aalice")
|
||||||
|
user2 = insert(:user, nickname: "alice")
|
||||||
|
|
||||||
|
conn1 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=1")
|
||||||
|
|
||||||
|
assert json_response(conn1, 200) == %{
|
||||||
|
"count" => 2,
|
||||||
|
"page_size" => 1,
|
||||||
|
"users" => [user_response(user)]
|
||||||
|
}
|
||||||
|
|
||||||
|
conn2 = get(conn, "/api/pleroma/admin/users?query=a&page_size=1&page=2")
|
||||||
|
|
||||||
|
assert json_response(conn2, 200) == %{
|
||||||
|
"count" => 2,
|
||||||
|
"page_size" => 1,
|
||||||
|
"users" => [user_response(user2)]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "only local users" do
|
||||||
|
admin = insert(:user, is_admin: true, nickname: "john")
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
user = insert(:user, nickname: "bob")
|
||||||
|
|
||||||
|
insert(:user, nickname: "bobb", local: false)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> get("/api/pleroma/admin/users?query=bo&filters=local")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [user_response(user)]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "only local users with no query", %{conn: conn, admin: old_admin} do
|
||||||
|
admin = insert(:user, is_admin: true, nickname: "john")
|
||||||
|
user = insert(:user, nickname: "bob")
|
||||||
|
|
||||||
|
insert(:user, nickname: "bobb", local: false)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?filters=local")
|
||||||
|
|
||||||
|
users =
|
||||||
|
[
|
||||||
|
user_response(user),
|
||||||
|
user_response(admin, %{
|
||||||
|
"roles" => %{"admin" => true, "moderator" => false}
|
||||||
|
}),
|
||||||
|
user_response(old_admin, %{
|
||||||
|
"deactivated" => false,
|
||||||
|
"roles" => %{"admin" => true, "moderator" => false}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 3,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => users
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "only unconfirmed users", %{conn: conn} do
|
||||||
|
sad_user = insert(:user, nickname: "sadboy", confirmation_pending: true)
|
||||||
|
old_user = insert(:user, nickname: "oldboy", confirmation_pending: true)
|
||||||
|
|
||||||
|
insert(:user, nickname: "happyboy", approval_pending: false)
|
||||||
|
insert(:user, confirmation_pending: false)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/users?filters=unconfirmed")
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
users =
|
||||||
|
Enum.map([old_user, sad_user], fn user ->
|
||||||
|
user_response(user, %{
|
||||||
|
"confirmation_pending" => true,
|
||||||
|
"approval_pending" => false
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
|
||||||
|
assert result == %{"count" => 2, "page_size" => 50, "users" => users}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "only unapproved users", %{conn: conn} do
|
||||||
|
user =
|
||||||
|
insert(:user,
|
||||||
|
nickname: "sadboy",
|
||||||
|
approval_pending: true,
|
||||||
|
registration_reason: "Plz let me in!"
|
||||||
|
)
|
||||||
|
|
||||||
|
insert(:user, nickname: "happyboy", approval_pending: false)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?filters=need_approval")
|
||||||
|
|
||||||
|
users = [
|
||||||
|
user_response(
|
||||||
|
user,
|
||||||
|
%{"approval_pending" => true, "registration_reason" => "Plz let me in!"}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => users
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "load only admins", %{conn: conn, admin: admin} do
|
||||||
|
second_admin = insert(:user, is_admin: true)
|
||||||
|
insert(:user)
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?filters=is_admin")
|
||||||
|
|
||||||
|
users =
|
||||||
|
[
|
||||||
|
user_response(admin, %{
|
||||||
|
"deactivated" => false,
|
||||||
|
"roles" => %{"admin" => true, "moderator" => false}
|
||||||
|
}),
|
||||||
|
user_response(second_admin, %{
|
||||||
|
"deactivated" => false,
|
||||||
|
"roles" => %{"admin" => true, "moderator" => false}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 2,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => users
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "load only moderators", %{conn: conn} do
|
||||||
|
moderator = insert(:user, is_moderator: true)
|
||||||
|
insert(:user)
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?filters=is_moderator")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [
|
||||||
|
user_response(moderator, %{
|
||||||
|
"deactivated" => false,
|
||||||
|
"roles" => %{"admin" => false, "moderator" => true}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "load users with actor_type is Person", %{admin: admin, conn: conn} do
|
||||||
|
insert(:user, actor_type: "Service")
|
||||||
|
insert(:user, actor_type: "Application")
|
||||||
|
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get(user_path(conn, :list), %{actor_types: ["Person"]})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
users =
|
||||||
|
[
|
||||||
|
user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}),
|
||||||
|
user_response(user1),
|
||||||
|
user_response(user2)
|
||||||
|
]
|
||||||
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
|
||||||
|
assert response == %{"count" => 3, "page_size" => 50, "users" => users}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "load users with actor_type is Person and Service", %{admin: admin, conn: conn} do
|
||||||
|
user_service = insert(:user, actor_type: "Service")
|
||||||
|
insert(:user, actor_type: "Application")
|
||||||
|
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get(user_path(conn, :list), %{actor_types: ["Person", "Service"]})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
users =
|
||||||
|
[
|
||||||
|
user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}}),
|
||||||
|
user_response(user1),
|
||||||
|
user_response(user2),
|
||||||
|
user_response(user_service, %{"actor_type" => "Service"})
|
||||||
|
]
|
||||||
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
|
||||||
|
assert response == %{"count" => 4, "page_size" => 50, "users" => users}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "load users with actor_type is Service", %{conn: conn} do
|
||||||
|
user_service = insert(:user, actor_type: "Service")
|
||||||
|
insert(:user, actor_type: "Application")
|
||||||
|
insert(:user)
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get(user_path(conn, :list), %{actor_types: ["Service"]})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
users = [user_response(user_service, %{"actor_type" => "Service"})]
|
||||||
|
|
||||||
|
assert response == %{"count" => 1, "page_size" => 50, "users" => users}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "load users with tags list", %{conn: conn} do
|
||||||
|
user1 = insert(:user, tags: ["first"])
|
||||||
|
user2 = insert(:user, tags: ["second"])
|
||||||
|
insert(:user)
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users?tags[]=first&tags[]=second")
|
||||||
|
|
||||||
|
users =
|
||||||
|
[
|
||||||
|
user_response(user1, %{"tags" => ["first"]}),
|
||||||
|
user_response(user2, %{"tags" => ["second"]})
|
||||||
|
]
|
||||||
|
|> Enum.sort_by(& &1["nickname"])
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 2,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => users
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "`active` filters out users pending approval", %{token: token} do
|
||||||
|
insert(:user, approval_pending: true)
|
||||||
|
%{id: user_id} = insert(:user, approval_pending: false)
|
||||||
|
%{id: admin_id} = token.user
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, token.user)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> get("/api/pleroma/admin/users?filters=active")
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"count" => 2,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [
|
||||||
|
%{"id" => ^admin_id},
|
||||||
|
%{"id" => ^user_id}
|
||||||
|
]
|
||||||
|
} = json_response(conn, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it works with multiple filters" do
|
||||||
|
admin = insert(:user, nickname: "john", is_admin: true)
|
||||||
|
token = insert(:oauth_admin_token, user: admin)
|
||||||
|
user = insert(:user, nickname: "bob", local: false, deactivated: true)
|
||||||
|
|
||||||
|
insert(:user, nickname: "ken", local: true, deactivated: true)
|
||||||
|
insert(:user, nickname: "bobb", local: false, deactivated: false)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
build_conn()
|
||||||
|
|> assign(:user, admin)
|
||||||
|
|> assign(:token, token)
|
||||||
|
|> get("/api/pleroma/admin/users?filters=deactivated,external")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [user_response(user)]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it omits relay user", %{admin: admin, conn: conn} do
|
||||||
|
assert %User{} = Relay.get_actor()
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/users")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"count" => 1,
|
||||||
|
"page_size" => 50,
|
||||||
|
"users" => [
|
||||||
|
user_response(admin, %{"roles" => %{"admin" => true, "moderator" => false}})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/activate", %{admin: admin, conn: conn} do
|
||||||
|
user_one = insert(:user, deactivated: true)
|
||||||
|
user_two = insert(:user, deactivated: true)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
patch(
|
||||||
|
conn,
|
||||||
|
"/api/pleroma/admin/users/activate",
|
||||||
|
%{nicknames: [user_one.nickname, user_two.nickname]}
|
||||||
|
)
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
assert Enum.map(response["users"], & &1["deactivated"]) == [false, false]
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} activated users: @#{user_one.nickname}, @#{user_two.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/deactivate", %{admin: admin, conn: conn} do
|
||||||
|
user_one = insert(:user, deactivated: false)
|
||||||
|
user_two = insert(:user, deactivated: false)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
patch(
|
||||||
|
conn,
|
||||||
|
"/api/pleroma/admin/users/deactivate",
|
||||||
|
%{nicknames: [user_one.nickname, user_two.nickname]}
|
||||||
|
)
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
assert Enum.map(response["users"], & &1["deactivated"]) == [true, true]
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} deactivated users: @#{user_one.nickname}, @#{user_two.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/approve", %{admin: admin, conn: conn} do
|
||||||
|
user_one = insert(:user, approval_pending: true)
|
||||||
|
user_two = insert(:user, approval_pending: true)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
patch(
|
||||||
|
conn,
|
||||||
|
"/api/pleroma/admin/users/approve",
|
||||||
|
%{nicknames: [user_one.nickname, user_two.nickname]}
|
||||||
|
)
|
||||||
|
|
||||||
|
response = json_response(conn, 200)
|
||||||
|
assert Enum.map(response["users"], & &1["approval_pending"]) == [false, false]
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} approved users: @#{user_one.nickname}, @#{user_two.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "PATCH /api/pleroma/admin/users/:nickname/toggle_activation", %{admin: admin, conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn = patch(conn, "/api/pleroma/admin/users/#{user.nickname}/toggle_activation")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) ==
|
||||||
|
user_response(
|
||||||
|
user,
|
||||||
|
%{"deactivated" => !user.deactivated}
|
||||||
|
)
|
||||||
|
|
||||||
|
log_entry = Repo.one(ModerationLog)
|
||||||
|
|
||||||
|
assert ModerationLog.get_log_entry_message(log_entry) ==
|
||||||
|
"@#{admin.nickname} deactivated users: @#{user.nickname}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp user_response(user, attrs \\ %{}) do
|
||||||
|
%{
|
||||||
|
"deactivated" => user.deactivated,
|
||||||
|
"id" => user.id,
|
||||||
|
"nickname" => user.nickname,
|
||||||
|
"roles" => %{"admin" => false, "moderator" => false},
|
||||||
|
"local" => user.local,
|
||||||
|
"tags" => [],
|
||||||
|
"avatar" => User.avatar_url(user) |> MediaProxy.url(),
|
||||||
|
"display_name" => HTML.strip_tags(user.name || user.nickname),
|
||||||
|
"confirmation_pending" => false,
|
||||||
|
"approval_pending" => false,
|
||||||
|
"url" => user.ap_id,
|
||||||
|
"registration_reason" => nil,
|
||||||
|
"actor_type" => "Person"
|
||||||
|
}
|
||||||
|
|> Map.merge(attrs)
|
||||||
|
end
|
||||||
|
end
|
|
@ -143,6 +143,20 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do
|
||||||
assert user2 in users
|
assert user2 in users
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it returns users by actor_types" do
|
||||||
|
user_service = insert(:user, actor_type: "Service")
|
||||||
|
user_application = insert(:user, actor_type: "Application")
|
||||||
|
user1 = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
{:ok, [^user_service], 1} = Search.user(%{actor_types: ["Service"]})
|
||||||
|
{:ok, [^user_application], 1} = Search.user(%{actor_types: ["Application"]})
|
||||||
|
{:ok, [^user1, ^user2], 2} = Search.user(%{actor_types: ["Person"]})
|
||||||
|
|
||||||
|
{:ok, [^user_service, ^user1, ^user2], 3} =
|
||||||
|
Search.user(%{actor_types: ["Person", "Service"]})
|
||||||
|
end
|
||||||
|
|
||||||
test "it returns user by display name" do
|
test "it returns user by display name" do
|
||||||
user = insert(:user, name: "Display name")
|
user = insert(:user, name: "Display name")
|
||||||
insert(:user)
|
insert(:user)
|
||||||
|
@ -178,6 +192,17 @@ defmodule Pleroma.Web.AdminAPI.SearchTest do
|
||||||
assert count == 1
|
assert count == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it returns unconfirmed user" do
|
||||||
|
unconfirmed = insert(:user, confirmation_pending: true)
|
||||||
|
insert(:user)
|
||||||
|
insert(:user)
|
||||||
|
|
||||||
|
{:ok, _results, total} = Search.user()
|
||||||
|
{:ok, [^unconfirmed], count} = Search.user(%{unconfirmed: true})
|
||||||
|
assert total == 3
|
||||||
|
assert count == 1
|
||||||
|
end
|
||||||
|
|
||||||
test "it returns non-discoverable users" do
|
test "it returns non-discoverable users" do
|
||||||
insert(:user)
|
insert(:user)
|
||||||
insert(:user, is_discoverable: false)
|
insert(:user, is_discoverable: false)
|
||||||
|
|
68
test/pleroma/web/endpoint/metrics_exporter_test.exs
Normal file
68
test/pleroma/web/endpoint/metrics_exporter_test.exs
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.Endpoint.MetricsExporterTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Web.Endpoint.MetricsExporter
|
||||||
|
|
||||||
|
defp config do
|
||||||
|
Application.get_env(:prometheus, MetricsExporter)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "with default config" do
|
||||||
|
test "does NOT expose app metrics", %{conn: conn} do
|
||||||
|
conn
|
||||||
|
|> get(config()[:path])
|
||||||
|
|> json_response(404)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "when enabled" do
|
||||||
|
setup do
|
||||||
|
initial_config = config()
|
||||||
|
on_exit(fn -> Application.put_env(:prometheus, MetricsExporter, initial_config) end)
|
||||||
|
|
||||||
|
Application.put_env(
|
||||||
|
:prometheus,
|
||||||
|
MetricsExporter,
|
||||||
|
Keyword.put(initial_config, :enabled, true)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "serves app metrics", %{conn: conn} do
|
||||||
|
conn = get(conn, config()[:path])
|
||||||
|
assert response = response(conn, 200)
|
||||||
|
|
||||||
|
for metric <- [
|
||||||
|
"http_requests_total",
|
||||||
|
"http_request_duration_microseconds",
|
||||||
|
"phoenix_controller_call_duration",
|
||||||
|
"telemetry_scrape_duration",
|
||||||
|
"erlang_vm_memory_atom_bytes_total"
|
||||||
|
] do
|
||||||
|
assert response =~ ~r/#{metric}/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "when IP whitelist configured, " <>
|
||||||
|
"serves app metrics only if client IP is whitelisted",
|
||||||
|
%{conn: conn} do
|
||||||
|
Application.put_env(
|
||||||
|
:prometheus,
|
||||||
|
MetricsExporter,
|
||||||
|
Keyword.put(config(), :ip_whitelist, ["127.127.127.127", {1, 1, 1, 1}, '255.255.255.255'])
|
||||||
|
)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> get(config()[:path])
|
||||||
|
|> json_response(404)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> Map.put(:remote_ip, {127, 127, 127, 127})
|
||||||
|
|> get(config()[:path])
|
||||||
|
|> response(200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import SweetXml
|
import SweetXml
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.Feed.FeedView
|
alias Pleroma.Web.Feed.FeedView
|
||||||
|
@ -15,7 +16,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
||||||
setup do: clear_config([:feed])
|
setup do: clear_config([:feed])
|
||||||
|
|
||||||
test "gets a feed (ATOM)", %{conn: conn} do
|
test "gets a feed (ATOM)", %{conn: conn} do
|
||||||
Pleroma.Config.put(
|
Config.put(
|
||||||
[:feed, :post_title],
|
[:feed, :post_title],
|
||||||
%{max_length: 25, omission: "..."}
|
%{max_length: 25, omission: "..."}
|
||||||
)
|
)
|
||||||
|
@ -82,7 +83,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "gets a feed (RSS)", %{conn: conn} do
|
test "gets a feed (RSS)", %{conn: conn} do
|
||||||
Pleroma.Config.put(
|
Config.put(
|
||||||
[:feed, :post_title],
|
[:feed, :post_title],
|
||||||
%{max_length: 25, omission: "..."}
|
%{max_length: 25, omission: "..."}
|
||||||
)
|
)
|
||||||
|
@ -157,7 +158,7 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
||||||
response =
|
response =
|
||||||
conn
|
conn
|
||||||
|> put_req_header("accept", "application/rss+xml")
|
|> put_req_header("accept", "application/rss+xml")
|
||||||
|> get(tag_feed_path(conn, :feed, "pleromaart"))
|
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|
||||||
|> response(200)
|
|> response(200)
|
||||||
|
|
||||||
xml = parse(response)
|
xml = parse(response)
|
||||||
|
@ -183,14 +184,12 @@ defmodule Pleroma.Web.Feed.TagControllerTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "private instance" do
|
describe "private instance" do
|
||||||
setup do: clear_config([:instance, :public])
|
setup do: clear_config([:instance, :public], false)
|
||||||
|
|
||||||
test "returns 404 for tags feed", %{conn: conn} do
|
test "returns 404 for tags feed", %{conn: conn} do
|
||||||
Config.put([:instance, :public], false)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> put_req_header("accept", "application/rss+xml")
|
|> put_req_header("accept", "application/rss+xml")
|
||||||
|> get(tag_feed_path(conn, :feed, "pleromaart"))
|
|> get(tag_feed_path(conn, :feed, "pleromaart.rss"))
|
||||||
|> response(404)
|
|> response(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,7 +13,7 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
setup do: clear_config([:instance, :federating], true)
|
setup do: clear_config([:static_fe, :enabled], false)
|
||||||
|
|
||||||
describe "feed" do
|
describe "feed" do
|
||||||
setup do: clear_config([:feed])
|
setup do: clear_config([:feed])
|
||||||
|
@ -192,6 +192,16 @@ defmodule Pleroma.Web.Feed.UserControllerTest do
|
||||||
|> get(user_feed_path(conn, :feed, user.nickname))
|
|> get(user_feed_path(conn, :feed, user.nickname))
|
||||||
|> response(404)
|
|> response(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "does not require authentication on non-federating instances", %{conn: conn} do
|
||||||
|
clear_config([:instance, :federating], false)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_req_header("accept", "application/rss+xml")
|
||||||
|
|> get("/users/#{user.nickname}/feed.rss")
|
||||||
|
|> response(200)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Note: see ActivityPubControllerTest for JSON format tests
|
# Note: see ActivityPubControllerTest for JSON format tests
|
||||||
|
|
|
@ -1509,28 +1509,103 @@ defmodule Pleroma.Web.MastodonAPI.AccountControllerTest do
|
||||||
|
|
||||||
test "getting a list of mutes" do
|
test "getting a list of mutes" do
|
||||||
%{user: user, conn: conn} = oauth_access(["read:mutes"])
|
%{user: user, conn: conn} = oauth_access(["read:mutes"])
|
||||||
other_user = insert(:user)
|
%{id: id1} = other_user1 = insert(:user)
|
||||||
|
%{id: id2} = other_user2 = insert(:user)
|
||||||
|
%{id: id3} = other_user3 = insert(:user)
|
||||||
|
|
||||||
{:ok, _user_relationships} = User.mute(user, other_user)
|
{:ok, _user_relationships} = User.mute(user, other_user1)
|
||||||
|
{:ok, _user_relationships} = User.mute(user, other_user2)
|
||||||
|
{:ok, _user_relationships} = User.mute(user, other_user3)
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/mutes")
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/mutes")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
other_user_id = to_string(other_user.id)
|
assert [id1, id2, id3] == Enum.map(result, & &1["id"])
|
||||||
assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/mutes?limit=1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id1}] = result
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/mutes?since_id=#{id1}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/mutes?since_id=#{id1}&max_id=#{id3}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id2}] = result
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/mutes?since_id=#{id1}&limit=1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id2}] = result
|
||||||
end
|
end
|
||||||
|
|
||||||
test "getting a list of blocks" do
|
test "getting a list of blocks" do
|
||||||
%{user: user, conn: conn} = oauth_access(["read:blocks"])
|
%{user: user, conn: conn} = oauth_access(["read:blocks"])
|
||||||
other_user = insert(:user)
|
%{id: id1} = other_user1 = insert(:user)
|
||||||
|
%{id: id2} = other_user2 = insert(:user)
|
||||||
|
%{id: id3} = other_user3 = insert(:user)
|
||||||
|
|
||||||
{:ok, _user_relationship} = User.block(user, other_user)
|
{:ok, _user_relationship} = User.block(user, other_user1)
|
||||||
|
{:ok, _user_relationship} = User.block(user, other_user3)
|
||||||
|
{:ok, _user_relationship} = User.block(user, other_user2)
|
||||||
|
|
||||||
conn =
|
result =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> get("/api/v1/blocks")
|
|> get("/api/v1/blocks")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
other_user_id = to_string(other_user.id)
|
assert [id1, id2, id3] == Enum.map(result, & &1["id"])
|
||||||
assert [%{"id" => ^other_user_id}] = json_response_and_validate_schema(conn, 200)
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/blocks?limit=1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id1}] = result
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/blocks?since_id=#{id1}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id2}, %{"id" => ^id3}] = result
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/blocks?since_id=#{id1}&max_id=#{id3}")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id2}] = result
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/blocks?since_id=#{id1}&limit=1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert [%{"id" => ^id2}] = result
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Conversation.Participation
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
@ -28,10 +29,10 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
||||||
user_three: user_three,
|
user_three: user_three,
|
||||||
conn: conn
|
conn: conn
|
||||||
} do
|
} do
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
assert Participation.unread_count(user_two) == 0
|
||||||
{:ok, direct} = create_direct_message(user_one, [user_two, user_three])
|
{:ok, direct} = create_direct_message(user_one, [user_two, user_three])
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
|
assert Participation.unread_count(user_two) == 1
|
||||||
|
|
||||||
{:ok, _follower_only} =
|
{:ok, _follower_only} =
|
||||||
CommonAPI.post(user_one, %{
|
CommonAPI.post(user_one, %{
|
||||||
|
@ -54,12 +55,33 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
||||||
|
|
||||||
account_ids = Enum.map(res_accounts, & &1["id"])
|
account_ids = Enum.map(res_accounts, & &1["id"])
|
||||||
assert length(res_accounts) == 2
|
assert length(res_accounts) == 2
|
||||||
|
assert user_one.id not in account_ids
|
||||||
assert user_two.id in account_ids
|
assert user_two.id in account_ids
|
||||||
assert user_three.id in account_ids
|
assert user_three.id in account_ids
|
||||||
assert is_binary(res_id)
|
assert is_binary(res_id)
|
||||||
assert unread == false
|
assert unread == false
|
||||||
assert res_last_status["id"] == direct.id
|
assert res_last_status["id"] == direct.id
|
||||||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
|
assert res_last_status["account"]["id"] == user_one.id
|
||||||
|
assert Participation.unread_count(user_one) == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
test "includes the user if the user is the only participant", %{
|
||||||
|
user: user_one,
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
{:ok, _direct} = create_direct_message(user_one, [])
|
||||||
|
|
||||||
|
res_conn = get(conn, "/api/v1/conversations")
|
||||||
|
|
||||||
|
assert response = json_response_and_validate_schema(res_conn, 200)
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{
|
||||||
|
"accounts" => [account]
|
||||||
|
}
|
||||||
|
] = response
|
||||||
|
|
||||||
|
assert user_one.id == account["id"]
|
||||||
end
|
end
|
||||||
|
|
||||||
test "observes limit params", %{
|
test "observes limit params", %{
|
||||||
|
@ -134,8 +156,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
||||||
user_two = insert(:user)
|
user_two = insert(:user)
|
||||||
{:ok, direct} = create_direct_message(user_one, [user_two])
|
{:ok, direct} = create_direct_message(user_one, [user_two])
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
|
assert Participation.unread_count(user_one) == 0
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 1
|
assert Participation.unread_count(user_two) == 1
|
||||||
|
|
||||||
user_two_conn =
|
user_two_conn =
|
||||||
build_conn()
|
build_conn()
|
||||||
|
@ -155,8 +177,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
||||||
|> post("/api/v1/conversations/#{direct_conversation_id}/read")
|
|> post("/api/v1/conversations/#{direct_conversation_id}/read")
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 0
|
assert Participation.unread_count(user_one) == 0
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
assert Participation.unread_count(user_two) == 0
|
||||||
|
|
||||||
# The conversation is marked as unread on reply
|
# The conversation is marked as unread on reply
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
|
@ -171,8 +193,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
||||||
|> get("/api/v1/conversations")
|
|> get("/api/v1/conversations")
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1
|
assert Participation.unread_count(user_one) == 1
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
assert Participation.unread_count(user_two) == 0
|
||||||
|
|
||||||
# A reply doesn't increment the user's unread_conversation_count if the conversation is unread
|
# A reply doesn't increment the user's unread_conversation_count if the conversation is unread
|
||||||
{:ok, _} =
|
{:ok, _} =
|
||||||
|
@ -182,8 +204,8 @@ defmodule Pleroma.Web.MastodonAPI.ConversationControllerTest do
|
||||||
in_reply_to_status_id: direct.id
|
in_reply_to_status_id: direct.id
|
||||||
})
|
})
|
||||||
|
|
||||||
assert User.get_cached_by_id(user_one.id).unread_conversation_count == 1
|
assert Participation.unread_count(user_one) == 1
|
||||||
assert User.get_cached_by_id(user_two.id).unread_conversation_count == 0
|
assert Participation.unread_count(user_two) == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do
|
test "(vanilla) Mastodon frontend behaviour", %{user: user_one, conn: conn} do
|
||||||
|
|
|
@ -8,7 +8,6 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
import Tesla.Mock
|
import Tesla.Mock
|
||||||
|
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
|
@ -166,6 +165,18 @@ defmodule Pleroma.Web.MastodonAPI.TimelineControllerTest do
|
||||||
activities = json_response_and_validate_schema(res_conn, 200)
|
activities = json_response_and_validate_schema(res_conn, 200)
|
||||||
[%{"id" => ^activity_id}] = activities
|
[%{"id" => ^activity_id}] = activities
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "can be filtered by instance", %{conn: conn} do
|
||||||
|
user = insert(:user, ap_id: "https://lain.com/users/lain")
|
||||||
|
insert(:note_activity, local: false)
|
||||||
|
insert(:note_activity, local: false)
|
||||||
|
|
||||||
|
{:ok, _} = CommonAPI.post(user, %{status: "test"})
|
||||||
|
|
||||||
|
conn = get(conn, "/api/v1/timelines/public?instance=lain.com")
|
||||||
|
|
||||||
|
assert length(json_response_and_validate_schema(conn, :ok)) == 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_and_remote_activities do
|
defp local_and_remote_activities do
|
||||||
|
|
|
@ -36,9 +36,11 @@ defmodule Pleroma.Web.MastodonAPI.ConversationViewTest do
|
||||||
|
|
||||||
assert conversation.id == participation.id |> to_string()
|
assert conversation.id == participation.id |> to_string()
|
||||||
assert conversation.last_status.id == activity.id
|
assert conversation.last_status.id == activity.id
|
||||||
|
assert conversation.last_status.account.id == user.id
|
||||||
|
|
||||||
assert [account] = conversation.accounts
|
assert [account] = conversation.accounts
|
||||||
assert account.id == other_user.id
|
assert account.id == other_user.id
|
||||||
|
|
||||||
assert conversation.last_status.pleroma.direct_conversation_id == participation.id
|
assert conversation.last_status.pleroma.direct_conversation_id == participation.id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -44,7 +44,7 @@ defmodule Pleroma.Web.MastodonAPI.PollViewTest do
|
||||||
],
|
],
|
||||||
voted: false,
|
voted: false,
|
||||||
votes_count: 0,
|
votes_count: 0,
|
||||||
voters_count: nil
|
voters_count: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
result = PollView.render("show.json", %{object: object})
|
result = PollView.render("show.json", %{object: object})
|
||||||
|
|
|
@ -7,7 +7,6 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -21,7 +20,7 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
setup do: clear_config([:instance, :federating], true)
|
setup do: clear_config([:static_fe, :enabled], false)
|
||||||
|
|
||||||
describe "Mastodon compatibility routes" do
|
describe "Mastodon compatibility routes" do
|
||||||
setup %{conn: conn} do
|
setup %{conn: conn} do
|
||||||
|
@ -215,15 +214,16 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
assert response(conn, 404)
|
assert response(conn, 404)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
test "does not require authentication on non-federating instances", %{
|
||||||
conn: conn
|
conn: conn
|
||||||
} do
|
} do
|
||||||
user = insert(:user)
|
clear_config([:instance, :federating], false)
|
||||||
note_activity = insert(:note_activity)
|
note_activity = insert(:note_activity)
|
||||||
|
|
||||||
conn = put_req_header(conn, "accept", "text/html")
|
conn
|
||||||
|
|> put_req_header("accept", "text/html")
|
||||||
ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}", user)
|
|> get("/notice/#{note_activity.id}")
|
||||||
|
|> response(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -325,14 +325,16 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
|
||||||
|> response(404)
|
|> response(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{
|
test "does not require authentication on non-federating instances", %{
|
||||||
conn: conn,
|
conn: conn,
|
||||||
note_activity: note_activity
|
note_activity: note_activity
|
||||||
} do
|
} do
|
||||||
user = insert(:user)
|
clear_config([:instance, :federating], false)
|
||||||
conn = put_req_header(conn, "accept", "text/html")
|
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/notice/#{note_activity.id}/embed_player", user)
|
conn
|
||||||
|
|> put_req_header("accept", "text/html")
|
||||||
|
|> get("/notice/#{note_activity.id}/embed_player")
|
||||||
|
|> response(200)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.BackupControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.User.Backup
|
||||||
|
alias Pleroma.Web.PleromaAPI.BackupView
|
||||||
|
|
||||||
|
setup do
|
||||||
|
clear_config([Pleroma.Upload, :uploader])
|
||||||
|
clear_config([Backup, :limit_days])
|
||||||
|
oauth_access(["read:accounts"])
|
||||||
|
end
|
||||||
|
|
||||||
|
test "GET /api/v1/pleroma/backups", %{user: user, conn: conn} do
|
||||||
|
assert {:ok, %Oban.Job{args: %{"backup_id" => backup_id}}} = Backup.create(user)
|
||||||
|
|
||||||
|
backup = Backup.get(backup_id)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/backups")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{
|
||||||
|
"content_type" => "application/zip",
|
||||||
|
"url" => url,
|
||||||
|
"file_size" => 0,
|
||||||
|
"processed" => false,
|
||||||
|
"inserted_at" => _
|
||||||
|
}
|
||||||
|
] = response
|
||||||
|
|
||||||
|
assert url == BackupView.download_url(backup)
|
||||||
|
|
||||||
|
Pleroma.Tests.ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{
|
||||||
|
"url" => ^url,
|
||||||
|
"processed" => true
|
||||||
|
}
|
||||||
|
] =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/backups")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "POST /api/v1/pleroma/backups", %{user: _user, conn: conn} do
|
||||||
|
assert [
|
||||||
|
%{
|
||||||
|
"content_type" => "application/zip",
|
||||||
|
"url" => url,
|
||||||
|
"file_size" => 0,
|
||||||
|
"processed" => false,
|
||||||
|
"inserted_at" => _
|
||||||
|
}
|
||||||
|
] =
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/pleroma/backups")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
Pleroma.Tests.ObanHelpers.perform_all()
|
||||||
|
|
||||||
|
assert [
|
||||||
|
%{
|
||||||
|
"url" => ^url,
|
||||||
|
"processed" => true
|
||||||
|
}
|
||||||
|
] =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/backups")
|
||||||
|
|> json_response_and_validate_schema(:ok)
|
||||||
|
|
||||||
|
days = Pleroma.Config.get([Backup, :limit_days])
|
||||||
|
|
||||||
|
assert %{"error" => "Last export was less than #{days} days ago"} ==
|
||||||
|
conn
|
||||||
|
|> post("/api/v1/pleroma/backups")
|
||||||
|
|> json_response_and_validate_schema(400)
|
||||||
|
end
|
||||||
|
end
|
|
@ -82,11 +82,13 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
|
||||||
result =
|
result =
|
||||||
conn
|
conn
|
||||||
|> put_req_header("content-type", "application/json")
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> put_req_header("idempotency-key", "123")
|
||||||
|> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "Hallo!!"})
|
|> post("/api/v1/pleroma/chats/#{chat.id}/messages", %{"content" => "Hallo!!"})
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert result["content"] == "Hallo!!"
|
assert result["content"] == "Hallo!!"
|
||||||
assert result["chat_id"] == chat.id |> to_string()
|
assert result["chat_id"] == chat.id |> to_string()
|
||||||
|
assert result["idempotency_key"] == "123"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it fails if there is no content", %{conn: conn, user: user} do
|
test "it fails if there is no content", %{conn: conn, user: user} do
|
||||||
|
@ -341,6 +343,35 @@ defmodule Pleroma.Web.PleromaAPI.ChatControllerTest do
|
||||||
assert length(result) == 0
|
assert length(result) == 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it does not return chats with users you muted", %{conn: conn, user: user} do
|
||||||
|
recipient = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _} = Chat.get_or_create(user.id, recipient.ap_id)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/chats")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(result) == 1
|
||||||
|
|
||||||
|
User.mute(user, recipient)
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/chats")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(result) == 0
|
||||||
|
|
||||||
|
result =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/pleroma/chats?with_muted=true")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert length(result) == 1
|
||||||
|
end
|
||||||
|
|
||||||
test "it returns all chats", %{conn: conn, user: user} do
|
test "it returns all chats", %{conn: conn, user: user} do
|
||||||
Enum.each(1..30, fn _ ->
|
Enum.each(1..30, fn _ ->
|
||||||
recipient = insert(:user)
|
recipient = insert(:user)
|
||||||
|
|
|
@ -121,7 +121,7 @@ defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do
|
||||||
[participation2, participation1] = Participation.for_user(other_user)
|
[participation2, participation1] = Participation.for_user(other_user)
|
||||||
assert Participation.get(participation2.id).read == false
|
assert Participation.get(participation2.id).read == false
|
||||||
assert Participation.get(participation1.id).read == false
|
assert Participation.get(participation1.id).read == false
|
||||||
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 2
|
assert Participation.unread_count(other_user) == 2
|
||||||
|
|
||||||
[%{"unread" => false}, %{"unread" => false}] =
|
[%{"unread" => false}, %{"unread" => false}] =
|
||||||
conn
|
conn
|
||||||
|
@ -131,6 +131,6 @@ defmodule Pleroma.Web.PleromaAPI.ConversationControllerTest do
|
||||||
[participation2, participation1] = Participation.for_user(other_user)
|
[participation2, participation1] = Participation.for_user(other_user)
|
||||||
assert Participation.get(participation2.id).read == true
|
assert Participation.get(participation2.id).read == true
|
||||||
assert Participation.get(participation1.id).read == true
|
assert Participation.get(participation1.id).read == true
|
||||||
assert User.get_cached_by_id(other_user.id).unread_conversation_count == 0
|
assert Participation.unread_count(other_user) == 0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2020 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaApi.InstancesControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Instances
|
||||||
|
|
||||||
|
setup_all do: clear_config([:instance, :federation_reachability_timeout_days], 1)
|
||||||
|
|
||||||
|
setup do
|
||||||
|
constant = "http://consistently-unreachable.name/"
|
||||||
|
eventual = "http://eventually-unreachable.com/path"
|
||||||
|
|
||||||
|
{:ok, %Pleroma.Instances.Instance{unreachable_since: constant_unreachable}} =
|
||||||
|
Instances.set_consistently_unreachable(constant)
|
||||||
|
|
||||||
|
_eventual_unrechable = Instances.set_unreachable(eventual)
|
||||||
|
|
||||||
|
%{constant_unreachable: constant_unreachable, constant: constant}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "GET /api/v1/pleroma/federation_status", %{
|
||||||
|
conn: conn,
|
||||||
|
constant_unreachable: constant_unreachable,
|
||||||
|
constant: constant
|
||||||
|
} do
|
||||||
|
constant_host = URI.parse(constant).host
|
||||||
|
|
||||||
|
assert conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> get("/api/v1/pleroma/federation_status")
|
||||||
|
|> json_response_and_validate_schema(200) == %{
|
||||||
|
"unreachable" => %{constant_host => to_string(constant_unreachable)}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,7 +6,6 @@ defmodule Pleroma.Web.PleromaAPI.UserImportControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
use Oban.Testing, repo: Pleroma.Repo
|
use Oban.Testing, repo: Pleroma.Repo
|
||||||
|
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.Tests.ObanHelpers
|
alias Pleroma.Tests.ObanHelpers
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
|
@ -25,7 +25,9 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
|
||||||
}
|
}
|
||||||
|
|
||||||
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
|
{:ok, upload} = ActivityPub.upload(file, actor: user.ap_id)
|
||||||
{:ok, activity} = CommonAPI.post_chat_message(user, recipient, "kippis :firefox:")
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post_chat_message(user, recipient, "kippis :firefox:", idempotency_key: "123")
|
||||||
|
|
||||||
chat = Chat.get(user.id, recipient.ap_id)
|
chat = Chat.get(user.id, recipient.ap_id)
|
||||||
|
|
||||||
|
@ -42,6 +44,7 @@ defmodule Pleroma.Web.PleromaAPI.ChatMessageReferenceViewTest do
|
||||||
assert chat_message[:created_at]
|
assert chat_message[:created_at]
|
||||||
assert chat_message[:unread] == false
|
assert chat_message[:unread] == false
|
||||||
assert match?([%{shortcode: "firefox"}], chat_message[:emojis])
|
assert match?([%{shortcode: "firefox"}], chat_message[:emojis])
|
||||||
|
assert chat_message[:idempotency_key] == "123"
|
||||||
|
|
||||||
clear_config([:rich_media, :enabled], true)
|
clear_config([:rich_media, :enabled], true)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
|
defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
import Mock
|
||||||
|
|
||||||
@dir "test/tmp/instance_static"
|
@dir "test/tmp/instance_static"
|
||||||
|
|
||||||
|
@ -53,4 +54,24 @@ defmodule Pleroma.Web.Plugs.FrontendStaticPlugTest do
|
||||||
index = get(conn, "/pleroma/admin/")
|
index = get(conn, "/pleroma/admin/")
|
||||||
assert html_response(index, 200) == "from frontend plug"
|
assert html_response(index, 200) == "from frontend plug"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "exclude invalid path", %{conn: conn} do
|
||||||
|
name = "pleroma-fe"
|
||||||
|
ref = "dist"
|
||||||
|
clear_config([:media_proxy, :enabled], true)
|
||||||
|
clear_config([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
|
||||||
|
clear_config([:frontends, :primary], %{"name" => name, "ref" => ref})
|
||||||
|
path = "#{@dir}/frontends/#{name}/#{ref}"
|
||||||
|
|
||||||
|
File.mkdir_p!("#{path}/proxy/rr/ss")
|
||||||
|
File.write!("#{path}/proxy/rr/ss/Ek7w8WPVcAApOvN.jpg:large", "FB image")
|
||||||
|
|
||||||
|
url =
|
||||||
|
Pleroma.Web.MediaProxy.encode_url("https://pbs.twimg.com/media/Ek7w8WPVcAApOvN.jpg:large")
|
||||||
|
|
||||||
|
with_mock Pleroma.ReverseProxy,
|
||||||
|
call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do
|
||||||
|
assert %Plug.Conn{status: :success} = get(conn, url)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
|
defmodule Pleroma.Web.Plugs.HTTPSecurityPlugTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.Config
|
|
||||||
alias Plug.Conn
|
alias Plug.Conn
|
||||||
|
|
||||||
describe "http security enabled" do
|
describe "http security enabled" do
|
||||||
|
|
|
@ -6,14 +6,12 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
|
||||||
use Pleroma.Web.ConnCase
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config
|
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
setup_all do: clear_config([:static_fe, :enabled], true)
|
setup_all do: clear_config([:static_fe, :enabled], true)
|
||||||
setup do: clear_config([:instance, :federating], true)
|
|
||||||
|
|
||||||
setup %{conn: conn} do
|
setup %{conn: conn} do
|
||||||
conn = put_req_header(conn, "accept", "text/html")
|
conn = put_req_header(conn, "accept", "text/html")
|
||||||
|
@ -74,8 +72,27 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
|
||||||
refute html =~ ">test29<"
|
refute html =~ ">test29<"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
|
test "does not require authentication on non-federating instances", %{
|
||||||
ensure_federating_or_authenticated(conn, "/users/#{user.nickname}", user)
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
clear_config([:instance, :federating], false)
|
||||||
|
|
||||||
|
conn = get(conn, "/users/#{user.nickname}")
|
||||||
|
|
||||||
|
assert html_response(conn, 200) =~ user.nickname
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 for local user with `restrict_unauthenticated/profiles/local` setting", %{
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
clear_config([:restrict_unauthenticated, :profiles, :local], true)
|
||||||
|
|
||||||
|
local_user = insert(:user, local: true)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> get("/users/#{local_user.nickname}")
|
||||||
|
|> html_response(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -187,10 +204,28 @@ defmodule Pleroma.Web.StaticFE.StaticFEControllerTest do
|
||||||
assert html_response(conn, 302) =~ "redirected"
|
assert html_response(conn, 302) =~ "redirected"
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it requires authentication if instance is NOT federating", %{conn: conn, user: user} do
|
test "does not require authentication on non-federating instances", %{
|
||||||
|
conn: conn,
|
||||||
|
user: user
|
||||||
|
} do
|
||||||
|
clear_config([:instance, :federating], false)
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
|
{:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
|
||||||
|
|
||||||
ensure_federating_or_authenticated(conn, "/notice/#{activity.id}", user)
|
conn = get(conn, "/notice/#{activity.id}")
|
||||||
|
|
||||||
|
assert html_response(conn, 200) =~ "testing a thing!"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 for local public activity with `restrict_unauthenticated/activities/local` setting",
|
||||||
|
%{conn: conn, user: user} do
|
||||||
|
clear_config([:restrict_unauthenticated, :activities, :local], true)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{status: "testing a thing!"})
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> get("/notice/#{activity.id}")
|
||||||
|
|> html_response(404)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue