forked from AkkomaGang/akkoma
Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into update-validator
This commit is contained in:
commit
2c603f2009
71 changed files with 1965 additions and 370 deletions
|
@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Changed
|
### Changed
|
||||||
- **Breaking:** Elixir >=1.9 is now required (was >= 1.8)
|
- **Breaking:** Elixir >=1.9 is now required (was >= 1.8)
|
||||||
- In Conversations, return only direct messages as `last_status`
|
- In Conversations, return only direct messages as `last_status`
|
||||||
|
- Using the `only_media` filter on timelines will now exclude reblog media
|
||||||
- MFR policy to set global expiration for all local Create activities
|
- MFR policy to set global expiration for all local Create activities
|
||||||
- OGP rich media parser merged with TwitterCard
|
- OGP rich media parser merged with TwitterCard
|
||||||
<details>
|
<details>
|
||||||
|
@ -19,6 +20,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- **Breaking:** removed `with_move` parameter from notifications timeline.
|
- **Breaking:** removed `with_move` parameter from notifications timeline.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Chats: Added support for federated chats. For details, see the docs.
|
- Chats: Added support for federated chats. For details, see the docs.
|
||||||
- ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.
|
- ActivityPub: Added support for existing AP ids for instances migrated from Mastodon.
|
||||||
- Instance: Add `background_image` to configuration and `/api/v1/instance`
|
- Instance: Add `background_image` to configuration and `/api/v1/instance`
|
||||||
|
@ -34,12 +36,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Notifications: Added `follow_request` notification type.
|
- Notifications: Added `follow_request` notification type.
|
||||||
- Added `:reject_deletes` group to SimplePolicy
|
- Added `:reject_deletes` group to SimplePolicy
|
||||||
- MRF (`EmojiStealPolicy`): New MRF Policy which allows to automatically download emojis from remote instances
|
- MRF (`EmojiStealPolicy`): New MRF Policy which allows to automatically download emojis from remote instances
|
||||||
|
- Support pagination in emoji packs API (for packs and for files in pack)
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>API Changes</summary>
|
<summary>API Changes</summary>
|
||||||
- Mastodon API: Extended `/api/v1/instance`.
|
- Mastodon API: Extended `/api/v1/instance`.
|
||||||
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
|
- Mastodon API: Support for `include_types` in `/api/v1/notifications`.
|
||||||
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
|
- Mastodon API: Added `/api/v1/notifications/:id/dismiss` endpoint.
|
||||||
- Mastodon API: Add support for filtering replies in public and home timelines
|
- Mastodon API: Add support for filtering replies in public and home timelines
|
||||||
|
- Mastodon API: Support for `bot` field in `/api/v1/accounts/update_credentials`
|
||||||
- Admin API: endpoints for create/update/delete OAuth Apps.
|
- Admin API: endpoints for create/update/delete OAuth Apps.
|
||||||
- Admin API: endpoint for status view.
|
- Admin API: endpoint for status view.
|
||||||
- OTP: Add command to reload emoji packs
|
- OTP: Add command to reload emoji packs
|
||||||
|
|
10
README.md
10
README.md
|
@ -34,6 +34,16 @@ Currently Pleroma is not packaged by any OS/Distros, but if you want to package
|
||||||
### Docker
|
### Docker
|
||||||
While we don’t provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://glitch.sh/sn0w/pleroma-docker>.
|
While we don’t provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://glitch.sh/sn0w/pleroma-docker>.
|
||||||
|
|
||||||
|
### Compilation Troubleshooting
|
||||||
|
If you ever encounter compilation issues during the updating of Pleroma, you can try these commands and see if they fix things:
|
||||||
|
|
||||||
|
- `mix deps.clean --all`
|
||||||
|
- `mix local.rebar`
|
||||||
|
- `mix local.hex`
|
||||||
|
- `rm -r _build`
|
||||||
|
|
||||||
|
If you are not developing Pleroma, it is better to use the OTP release, which comes with everything precompiled.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
- Latest Released revision: <https://docs.pleroma.social>
|
- Latest Released revision: <https://docs.pleroma.social>
|
||||||
- Latest Git revision: <https://docs-develop.pleroma.social>
|
- Latest Git revision: <https://docs-develop.pleroma.social>
|
||||||
|
|
|
@ -186,6 +186,7 @@
|
||||||
notify_email: "noreply@example.com",
|
notify_email: "noreply@example.com",
|
||||||
description: "Pleroma: An efficient and flexible fediverse server",
|
description: "Pleroma: An efficient and flexible fediverse server",
|
||||||
background_image: "/images/city.jpg",
|
background_image: "/images/city.jpg",
|
||||||
|
instance_thumbnail: "/instance/thumbnail.jpeg",
|
||||||
limit: 5_000,
|
limit: 5_000,
|
||||||
chat_limit: 5_000,
|
chat_limit: 5_000,
|
||||||
remote_limit: 100_000,
|
remote_limit: 100_000,
|
||||||
|
@ -407,6 +408,13 @@
|
||||||
],
|
],
|
||||||
whitelist: []
|
whitelist: []
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Http,
|
||||||
|
method: :purge,
|
||||||
|
headers: [],
|
||||||
|
options: []
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script, script_path: nil
|
||||||
|
|
||||||
config :pleroma, :chat, enabled: true
|
config :pleroma, :chat, enabled: true
|
||||||
|
|
||||||
config :phoenix, :format_encoders, json: Jason
|
config :phoenix, :format_encoders, json: Jason
|
||||||
|
|
|
@ -1650,6 +1650,31 @@
|
||||||
"The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.",
|
"The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.",
|
||||||
suggestions: ["https://example.com"]
|
suggestions: ["https://example.com"]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :invalidation,
|
||||||
|
type: :keyword,
|
||||||
|
descpiption: "",
|
||||||
|
suggestions: [
|
||||||
|
enabled: true,
|
||||||
|
provider: Pleroma.Web.MediaProxy.Invalidation.Script
|
||||||
|
],
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :enabled,
|
||||||
|
type: :boolean,
|
||||||
|
description: "Enables invalidate media cache"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :provider,
|
||||||
|
type: :module,
|
||||||
|
description: "Module which will be used to cache purge.",
|
||||||
|
suggestions: [
|
||||||
|
Pleroma.Web.MediaProxy.Invalidation.Script,
|
||||||
|
Pleroma.Web.MediaProxy.Invalidation.Http
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :proxy_opts,
|
key: :proxy_opts,
|
||||||
type: :keyword,
|
type: :keyword,
|
||||||
|
@ -1722,6 +1747,45 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: Pleroma.Web.MediaProxy.Invalidation.Http,
|
||||||
|
type: :group,
|
||||||
|
description: "HTTP invalidate settings",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :method,
|
||||||
|
type: :atom,
|
||||||
|
description: "HTTP method of request. Default: :purge"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :headers,
|
||||||
|
type: {:list, :tuple},
|
||||||
|
description: "HTTP headers of request.",
|
||||||
|
suggestions: [{"x-refresh", 1}]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: :options,
|
||||||
|
type: :keyword,
|
||||||
|
description: "Request options.",
|
||||||
|
suggestions: [params: %{ts: "xxx"}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
group: :pleroma,
|
||||||
|
key: Pleroma.Web.MediaProxy.Invalidation.Script,
|
||||||
|
type: :group,
|
||||||
|
description: "Script invalidate settings",
|
||||||
|
children: [
|
||||||
|
%{
|
||||||
|
key: :script_path,
|
||||||
|
type: :string,
|
||||||
|
description: "Path to shell script. Which will run purge cache.",
|
||||||
|
suggestions: ["./installation/nginx-cache-purge.sh.example"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
group: :pleroma,
|
group: :pleroma,
|
||||||
key: :gopher,
|
key: :gopher,
|
||||||
|
|
|
@ -488,35 +488,39 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
|
|
||||||
### Change the user's email, password, display and settings-related fields
|
### Change the user's email, password, display and settings-related fields
|
||||||
|
|
||||||
- Params:
|
* Params:
|
||||||
- `email`
|
* `email`
|
||||||
- `password`
|
* `password`
|
||||||
- `name`
|
* `name`
|
||||||
- `bio`
|
* `bio`
|
||||||
- `avatar`
|
* `avatar`
|
||||||
- `locked`
|
* `locked`
|
||||||
- `no_rich_text`
|
* `no_rich_text`
|
||||||
- `default_scope`
|
* `default_scope`
|
||||||
- `banner`
|
* `banner`
|
||||||
- `hide_follows`
|
* `hide_follows`
|
||||||
- `hide_followers`
|
* `hide_followers`
|
||||||
- `hide_followers_count`
|
* `hide_followers_count`
|
||||||
- `hide_follows_count`
|
* `hide_follows_count`
|
||||||
- `hide_favorites`
|
* `hide_favorites`
|
||||||
- `allow_following_move`
|
* `allow_following_move`
|
||||||
- `background`
|
* `background`
|
||||||
- `show_role`
|
* `show_role`
|
||||||
- `skip_thread_containment`
|
* `skip_thread_containment`
|
||||||
- `fields`
|
* `fields`
|
||||||
- `discoverable`
|
* `discoverable`
|
||||||
- `actor_type`
|
* `actor_type`
|
||||||
|
|
||||||
- Response:
|
* Responses:
|
||||||
|
|
||||||
|
Status: 200
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"status": "success"}
|
{"status": "success"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Status: 400
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"errors":
|
{"errors":
|
||||||
{"actor_type": "is invalid"},
|
{"actor_type": "is invalid"},
|
||||||
|
@ -525,8 +529,10 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Status: 404
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{"error": "Unable to update user."}
|
{"error": "Not found"}
|
||||||
```
|
```
|
||||||
|
|
||||||
## `GET /api/pleroma/admin/reports`
|
## `GET /api/pleroma/admin/reports`
|
||||||
|
@ -1224,4 +1230,66 @@ Loads json generated from `config/descriptions.exs`.
|
||||||
- Response:
|
- Response:
|
||||||
- On success: `204`, empty response
|
- On success: `204`, empty response
|
||||||
- On failure:
|
- On failure:
|
||||||
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
||||||
|
|
||||||
|
## `GET /api/pleroma/admin/media_proxy_caches`
|
||||||
|
|
||||||
|
### Get a list of all banned MediaProxy URLs in Cachex
|
||||||
|
|
||||||
|
- Authentication: required
|
||||||
|
- Params:
|
||||||
|
- *optional* `page`: **integer** page number
|
||||||
|
- *optional* `page_size`: **integer** number of log entries per page (default is `50`)
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
``` json
|
||||||
|
{
|
||||||
|
"urls": [
|
||||||
|
"http://example.com/media/a688346.jpg",
|
||||||
|
"http://example.com/media/fb1f4d.jpg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## `POST /api/pleroma/admin/media_proxy_caches/delete`
|
||||||
|
|
||||||
|
### Remove a banned MediaProxy URL from Cachex
|
||||||
|
|
||||||
|
- Authentication: required
|
||||||
|
- Params:
|
||||||
|
- `urls` (array)
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
``` json
|
||||||
|
{
|
||||||
|
"urls": [
|
||||||
|
"http://example.com/media/a688346.jpg",
|
||||||
|
"http://example.com/media/fb1f4d.jpg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## `POST /api/pleroma/admin/media_proxy_caches/purge`
|
||||||
|
|
||||||
|
### Purge a MediaProxy URL
|
||||||
|
|
||||||
|
- Authentication: required
|
||||||
|
- Params:
|
||||||
|
- `urls` (array)
|
||||||
|
- `ban` (boolean)
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
``` json
|
||||||
|
{
|
||||||
|
"urls": [
|
||||||
|
"http://example.com/media/a688346.jpg",
|
||||||
|
"http://example.com/media/fb1f4d.jpg"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
|
@ -450,18 +450,44 @@ The status posting endpoint takes an additional parameter, `in_reply_to_conversa
|
||||||
* Response: JSON, list with updated files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message.
|
* Response: JSON, list with updated files for updated pack (hashmap -> shortcode => filename) with status 200, either error status with error message.
|
||||||
|
|
||||||
## `GET /api/pleroma/emoji/packs`
|
## `GET /api/pleroma/emoji/packs`
|
||||||
|
|
||||||
### Lists local custom emoji packs
|
### Lists local custom emoji packs
|
||||||
|
|
||||||
* Method `GET`
|
* Method `GET`
|
||||||
* Authentication: not required
|
* Authentication: not required
|
||||||
* Params: None
|
* Params:
|
||||||
* Response: JSON, "ok" and 200 status and the JSON hashmap of pack name to pack contents
|
* `page`: page number for packs (default 1)
|
||||||
|
* `page_size`: page size for packs (default 50)
|
||||||
|
* Response: `packs` key with JSON hashmap of pack name to pack contents and `count` key for count of packs.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"packs": {
|
||||||
|
"pack_name": {...}, // pack contents
|
||||||
|
...
|
||||||
|
},
|
||||||
|
"count": 0 // packs count
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## `GET /api/pleroma/emoji/packs/:name`
|
## `GET /api/pleroma/emoji/packs/:name`
|
||||||
|
|
||||||
### Get pack.json for the pack
|
### Get pack.json for the pack
|
||||||
|
|
||||||
* Method `GET`
|
* Method `GET`
|
||||||
* Authentication: not required
|
* Authentication: not required
|
||||||
* Params: None
|
* Params:
|
||||||
* Response: JSON, pack json with `files` and `pack` keys with 200 status or 404 if the pack does not exist
|
* `page`: page number for files (default 1)
|
||||||
|
* `page_size`: page size for files (default 30)
|
||||||
|
* Response: JSON, pack json with `files`, `files_count` and `pack` keys with 200 status or 404 if the pack does not exist.
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"files": {...},
|
||||||
|
"files_count": 0, // emoji count in pack
|
||||||
|
"pack": {...}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## `GET /api/pleroma/emoji/packs/:name/archive`
|
## `GET /api/pleroma/emoji/packs/:name/archive`
|
||||||
### Requests a local pack archive from the instance
|
### Requests a local pack archive from the instance
|
||||||
|
|
|
@ -60,7 +60,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.
|
||||||
* `no_attachment_links`: Set to true to disable automatically adding attachment link text to statuses.
|
* `attachment_links`: Set to true to enable automatically adding attachment link text to statuses.
|
||||||
* `welcome_message`: A message that will be send to a newly registered users as a direct message.
|
* `welcome_message`: A message that will be send to a newly registered users as a direct message.
|
||||||
* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
|
* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
|
||||||
* `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`).
|
||||||
|
@ -268,7 +268,7 @@ This section describe PWA manifest instance-specific values. Currently this opti
|
||||||
|
|
||||||
#### Pleroma.Web.MediaProxy.Invalidation.Script
|
#### Pleroma.Web.MediaProxy.Invalidation.Script
|
||||||
|
|
||||||
This strategy allow perform external bash script to purge cache.
|
This strategy allow perform external shell script to purge cache.
|
||||||
Urls of attachments pass to script as arguments.
|
Urls of attachments pass to script as arguments.
|
||||||
|
|
||||||
* `script_path`: path to external script.
|
* `script_path`: path to external script.
|
||||||
|
@ -284,8 +284,8 @@ config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script,
|
||||||
This strategy allow perform custom http request to purge cache.
|
This strategy allow perform custom http request to purge cache.
|
||||||
|
|
||||||
* `method`: http method. default is `purge`
|
* `method`: http method. default is `purge`
|
||||||
* `headers`: http headers. default is empty
|
* `headers`: http headers.
|
||||||
* `options`: request options. default is empty
|
* `options`: request options.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```elixir
|
```elixir
|
||||||
|
|
|
@ -225,10 +225,7 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
||||||
|
|
||||||
#### Further reading
|
#### Further reading
|
||||||
|
|
||||||
* [Backup your instance](../administration/backup.md)
|
{! backend/installation/further_reading.include !}
|
||||||
* [Hardening your instance](../configuration/hardening.md)
|
|
||||||
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
|
||||||
* [Updating your instance](../administration/updating.md)
|
|
||||||
|
|
||||||
## Questions
|
## Questions
|
||||||
|
|
||||||
|
|
|
@ -200,10 +200,7 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
||||||
|
|
||||||
#### Further reading
|
#### Further reading
|
||||||
|
|
||||||
* [Backup your instance](../administration/backup.md)
|
{! backend/installation/further_reading.include !}
|
||||||
* [Hardening your instance](../configuration/hardening.md)
|
|
||||||
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
|
||||||
* [Updating your instance](../administration/updating.md)
|
|
||||||
|
|
||||||
## Questions
|
## Questions
|
||||||
|
|
||||||
|
|
|
@ -186,10 +186,7 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
||||||
|
|
||||||
#### Further reading
|
#### Further reading
|
||||||
|
|
||||||
* [Backup your instance](../administration/backup.md)
|
{! backend/installation/further_reading.include !}
|
||||||
* [Hardening your instance](../configuration/hardening.md)
|
|
||||||
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
|
||||||
* [Updating your instance](../administration/updating.md)
|
|
||||||
|
|
||||||
## Questions
|
## Questions
|
||||||
|
|
||||||
|
|
|
@ -175,10 +175,7 @@ sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress
|
||||||
|
|
||||||
#### その他の設定とカスタマイズ
|
#### その他の設定とカスタマイズ
|
||||||
|
|
||||||
* [Backup your instance](../administration/backup.md)
|
{! backend/installation/further_reading.include !}
|
||||||
* [Hardening your instance](../configuration/hardening.md)
|
|
||||||
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
|
||||||
* [Updating your instance](../administration/updating.md)
|
|
||||||
|
|
||||||
## 質問ある?
|
## 質問ある?
|
||||||
|
|
||||||
|
|
5
docs/installation/further_reading.include
Normal file
5
docs/installation/further_reading.include
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
* [How Federation Works/Why is my Federated Timeline empty?](https://blog.soykaf.com/post/how-federation-works/)
|
||||||
|
* [Backup your instance](../administration/backup.md)
|
||||||
|
* [Updating your instance](../administration/updating.md)
|
||||||
|
* [Hardening your instance](../configuration/hardening.md)
|
||||||
|
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
|
@ -283,10 +283,7 @@ If you opted to allow sudo for the `pleroma` user but would like to remove the a
|
||||||
|
|
||||||
#### Further reading
|
#### Further reading
|
||||||
|
|
||||||
* [Backup your instance](../administration/backup.md)
|
{! backend/installation/further_reading.include !}
|
||||||
* [Hardening your instance](../configuration/hardening.md)
|
|
||||||
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
|
||||||
* [Updating your instance](../administration/updating.md)
|
|
||||||
|
|
||||||
## Questions
|
## Questions
|
||||||
|
|
||||||
|
|
|
@ -196,3 +196,11 @@ incorrect timestamps. You should have ntpd running.
|
||||||
## Instances running NetBSD
|
## Instances running NetBSD
|
||||||
|
|
||||||
* <https://catgirl.science>
|
* <https://catgirl.science>
|
||||||
|
|
||||||
|
#### Further reading
|
||||||
|
|
||||||
|
{! backend/installation/further_reading.include !}
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||||
|
|
|
@ -242,3 +242,11 @@ If your instance is up and running, you can create your first user with administ
|
||||||
```
|
```
|
||||||
LC_ALL=en_US.UTF-8 MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
|
LC_ALL=en_US.UTF-8 MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Further reading
|
||||||
|
|
||||||
|
{! backend/installation/further_reading.include !}
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
||||||
|
|
|
@ -270,10 +270,7 @@ This will create an account withe the username of 'joeuser' with the email addre
|
||||||
|
|
||||||
## Further reading
|
## Further reading
|
||||||
|
|
||||||
* [Backup your instance](../administration/backup.md)
|
{! backend/installation/further_reading.include !}
|
||||||
* [Hardening your instance](../configuration/hardening.md)
|
|
||||||
* [How to activate mediaproxy](../configuration/howto_mediaproxy.md)
|
|
||||||
* [Updating your instance](../administration/updating.md)
|
|
||||||
|
|
||||||
## Questions
|
## Questions
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ CACHE_DIRECTORY="/tmp/pleroma-media-cache"
|
||||||
## $3 - (optional) the number of parallel processes to run for grep.
|
## $3 - (optional) the number of parallel processes to run for grep.
|
||||||
get_cache_files() {
|
get_cache_files() {
|
||||||
local max_parallel=${3-16}
|
local max_parallel=${3-16}
|
||||||
find $2 -maxdepth 2 -type d | xargs -P $max_parallel -n 1 grep -E Rl "^KEY:.*$1" | sort -u
|
find $2 -maxdepth 2 -type d | xargs -P $max_parallel -n 1 grep -E -Rl "^KEY:.*$1" | sort -u
|
||||||
}
|
}
|
||||||
|
|
||||||
## Removes an item from the given cache zone.
|
## Removes an item from the given cache zone.
|
||||||
|
@ -37,4 +37,4 @@ purge() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
purge $1
|
purge $@
|
||||||
|
|
|
@ -148,7 +148,8 @@ defp cachex_children do
|
||||||
build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500),
|
build_cachex("idempotency", expiration: idempotency_expiration(), limit: 2500),
|
||||||
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)
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule Pleroma.Emoji.Pack do
|
defmodule Pleroma.Emoji.Pack do
|
||||||
@derive {Jason.Encoder, only: [:files, :pack]}
|
@derive {Jason.Encoder, only: [:files, :pack, :files_count]}
|
||||||
defstruct files: %{},
|
defstruct files: %{},
|
||||||
|
files_count: 0,
|
||||||
pack_file: nil,
|
pack_file: nil,
|
||||||
path: nil,
|
path: nil,
|
||||||
pack: %{},
|
pack: %{},
|
||||||
|
@ -8,6 +9,7 @@ defmodule Pleroma.Emoji.Pack do
|
||||||
|
|
||||||
@type t() :: %__MODULE__{
|
@type t() :: %__MODULE__{
|
||||||
files: %{String.t() => Path.t()},
|
files: %{String.t() => Path.t()},
|
||||||
|
files_count: non_neg_integer(),
|
||||||
pack_file: Path.t(),
|
pack_file: Path.t(),
|
||||||
path: Path.t(),
|
path: Path.t(),
|
||||||
pack: map(),
|
pack: map(),
|
||||||
|
@ -16,7 +18,7 @@ defmodule Pleroma.Emoji.Pack do
|
||||||
|
|
||||||
alias Pleroma.Emoji
|
alias Pleroma.Emoji
|
||||||
|
|
||||||
@spec create(String.t()) :: :ok | {:error, File.posix()} | {:error, :empty_values}
|
@spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values}
|
||||||
def create(name) do
|
def create(name) do
|
||||||
with :ok <- validate_not_empty([name]),
|
with :ok <- validate_not_empty([name]),
|
||||||
dir <- Path.join(emoji_path(), name),
|
dir <- Path.join(emoji_path(), name),
|
||||||
|
@ -26,10 +28,27 @@ def create(name) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec show(String.t()) :: {:ok, t()} | {:error, atom()}
|
defp paginate(entities, 1, page_size), do: Enum.take(entities, page_size)
|
||||||
def show(name) do
|
|
||||||
|
defp paginate(entities, page, page_size) do
|
||||||
|
entities
|
||||||
|
|> Enum.chunk_every(page_size)
|
||||||
|
|> Enum.at(page - 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec show(keyword()) :: {:ok, t()} | {:error, atom()}
|
||||||
|
def show(opts) do
|
||||||
|
name = opts[:name]
|
||||||
|
|
||||||
with :ok <- validate_not_empty([name]),
|
with :ok <- validate_not_empty([name]),
|
||||||
{:ok, pack} <- load_pack(name) do
|
{:ok, pack} <- load_pack(name) do
|
||||||
|
shortcodes =
|
||||||
|
pack.files
|
||||||
|
|> Map.keys()
|
||||||
|
|> paginate(opts[:page], opts[:page_size])
|
||||||
|
|
||||||
|
pack = Map.put(pack, :files, Map.take(pack.files, shortcodes))
|
||||||
|
|
||||||
{:ok, validate_pack(pack)}
|
{:ok, validate_pack(pack)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -120,10 +139,10 @@ def list_remote(url) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec list_local() :: {:ok, map()}
|
@spec list_local(keyword()) :: {:ok, map(), non_neg_integer()}
|
||||||
def list_local do
|
def list_local(opts) do
|
||||||
with {:ok, results} <- list_packs_dir() do
|
with {:ok, results} <- list_packs_dir() do
|
||||||
packs =
|
all_packs =
|
||||||
results
|
results
|
||||||
|> Enum.map(fn name ->
|
|> Enum.map(fn name ->
|
||||||
case load_pack(name) do
|
case load_pack(name) do
|
||||||
|
@ -132,9 +151,13 @@ def list_local do
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> Enum.reject(&is_nil/1)
|
|> Enum.reject(&is_nil/1)
|
||||||
|
|
||||||
|
packs =
|
||||||
|
all_packs
|
||||||
|
|> paginate(opts[:page], opts[:page_size])
|
||||||
|> Map.new(fn pack -> {pack.name, validate_pack(pack)} end)
|
|> Map.new(fn pack -> {pack.name, validate_pack(pack)} end)
|
||||||
|
|
||||||
{:ok, packs}
|
{:ok, packs, length(all_packs)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -146,7 +169,7 @@ def get_archive(name) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec download(String.t(), String.t(), String.t()) :: :ok | {:error, atom()}
|
@spec download(String.t(), String.t(), String.t()) :: {:ok, t()} | {:error, atom()}
|
||||||
def download(name, url, as) do
|
def download(name, url, as) do
|
||||||
uri = url |> String.trim() |> URI.parse()
|
uri = url |> String.trim() |> URI.parse()
|
||||||
|
|
||||||
|
@ -197,7 +220,12 @@ def load_pack(name) do
|
||||||
|> Map.put(:path, Path.dirname(pack_file))
|
|> Map.put(:path, Path.dirname(pack_file))
|
||||||
|> Map.put(:name, name)
|
|> Map.put(:name, name)
|
||||||
|
|
||||||
{:ok, pack}
|
files_count =
|
||||||
|
pack.files
|
||||||
|
|> Map.keys()
|
||||||
|
|> length()
|
||||||
|
|
||||||
|
{:ok, Map.put(pack, :files_count, files_count)}
|
||||||
else
|
else
|
||||||
{:error, :not_found}
|
{:error, :not_found}
|
||||||
end
|
end
|
||||||
|
@ -296,7 +324,9 @@ defp downloadable?(pack) do
|
||||||
# Otherwise, they'd have to download it from external-src
|
# Otherwise, they'd have to download it from external-src
|
||||||
pack.pack["share-files"] &&
|
pack.pack["share-files"] &&
|
||||||
Enum.all?(pack.files, fn {_, file} ->
|
Enum.all?(pack.files, fn {_, file} ->
|
||||||
File.exists?(Path.join(pack.path, file))
|
pack.path
|
||||||
|
|> Path.join(file)
|
||||||
|
|> File.exists?()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -440,7 +470,7 @@ defp list_packs_dir do
|
||||||
# with the API so it should be sufficient
|
# with the API so it should be sufficient
|
||||||
with {:create_dir, :ok} <- {:create_dir, File.mkdir_p(emoji_path)},
|
with {:create_dir, :ok} <- {:create_dir, File.mkdir_p(emoji_path)},
|
||||||
{:ls, {:ok, results}} <- {:ls, File.ls(emoji_path)} do
|
{:ls, {:ok, results}} <- {:ls, File.ls(emoji_path)} do
|
||||||
{:ok, results}
|
{:ok, Enum.sort(results)}
|
||||||
else
|
else
|
||||||
{:create_dir, {:error, e}} -> {:error, :create_dir, e}
|
{:create_dir, {:error, e}} -> {:error, :create_dir, e}
|
||||||
{:ls, {:error, e}} -> {:error, :ls, e}
|
{:ls, {:error, e}} -> {:error, :ls, e}
|
||||||
|
|
|
@ -64,6 +64,12 @@ def fetch_paginated(query, params, :offset, table_binding) do
|
||||||
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
@spec paginate(Ecto.Query.t(), map(), type(), atom() | nil) :: [Ecto.Schema.t()]
|
||||||
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
|
def paginate(query, options, method \\ :keyset, table_binding \\ nil)
|
||||||
|
|
||||||
|
def paginate(list, options, _method, _table_binding) when is_list(list) do
|
||||||
|
offset = options[:offset] || 0
|
||||||
|
limit = options[:limit] || 0
|
||||||
|
Enum.slice(list, offset, limit)
|
||||||
|
end
|
||||||
|
|
||||||
def paginate(query, options, :keyset, table_binding) do
|
def paginate(query, options, :keyset, table_binding) do
|
||||||
query
|
query
|
||||||
|> restrict(:min_id, options, table_binding)
|
|> restrict(:min_id, options, table_binding)
|
||||||
|
|
|
@ -10,6 +10,8 @@ defmodule Pleroma.Plugs.UploadedMedia do
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
@behaviour Plug
|
@behaviour Plug
|
||||||
# no slashes
|
# no slashes
|
||||||
@path "media"
|
@path "media"
|
||||||
|
@ -35,8 +37,7 @@ def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
|
||||||
%{query_params: %{"name" => name}} = conn ->
|
%{query_params: %{"name" => name}} = conn ->
|
||||||
name = String.replace(name, "\"", "\\\"")
|
name = String.replace(name, "\"", "\\\"")
|
||||||
|
|
||||||
conn
|
put_resp_header(conn, "content-disposition", "filename=\"#{name}\"")
|
||||||
|> put_resp_header("content-disposition", "filename=\"#{name}\"")
|
|
||||||
|
|
||||||
conn ->
|
conn ->
|
||||||
conn
|
conn
|
||||||
|
@ -47,7 +48,8 @@ def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
|
||||||
|
|
||||||
with uploader <- Keyword.fetch!(config, :uploader),
|
with uploader <- Keyword.fetch!(config, :uploader),
|
||||||
proxy_remote = Keyword.get(config, :proxy_remote, false),
|
proxy_remote = Keyword.get(config, :proxy_remote, false),
|
||||||
{:ok, get_method} <- uploader.get_file(file) do
|
{:ok, get_method} <- uploader.get_file(file),
|
||||||
|
false <- media_is_banned(conn, get_method) do
|
||||||
get_media(conn, get_method, proxy_remote, opts)
|
get_media(conn, get_method, proxy_remote, opts)
|
||||||
else
|
else
|
||||||
_ ->
|
_ ->
|
||||||
|
@ -59,6 +61,14 @@ def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
|
||||||
|
|
||||||
def call(conn, _opts), do: conn
|
def call(conn, _opts), do: conn
|
||||||
|
|
||||||
|
defp media_is_banned(%{request_path: path} = _conn, {:static_dir, _}) do
|
||||||
|
MediaProxy.in_banned_urls(Pleroma.Web.base_url() <> path)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp media_is_banned(_, {:url, url}), do: MediaProxy.in_banned_urls(url)
|
||||||
|
|
||||||
|
defp media_is_banned(_, _), do: false
|
||||||
|
|
||||||
defp get_media(conn, {:static_dir, directory}, _, opts) do
|
defp get_media(conn, {:static_dir, directory}, _, opts) do
|
||||||
static_opts =
|
static_opts =
|
||||||
Map.get(opts, :static_plug_opts)
|
Map.get(opts, :static_plug_opts)
|
||||||
|
|
|
@ -263,37 +263,60 @@ def account_status(%User{deactivated: true}), do: :deactivated
|
||||||
def account_status(%User{password_reset_pending: true}), do: :password_reset_pending
|
def account_status(%User{password_reset_pending: true}), do: :password_reset_pending
|
||||||
|
|
||||||
def account_status(%User{confirmation_pending: true}) do
|
def account_status(%User{confirmation_pending: true}) do
|
||||||
case Config.get([:instance, :account_activation_required]) do
|
if Config.get([:instance, :account_activation_required]) do
|
||||||
true -> :confirmation_pending
|
:confirmation_pending
|
||||||
_ -> :active
|
else
|
||||||
|
:active
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def account_status(%User{}), do: :active
|
def account_status(%User{}), do: :active
|
||||||
|
|
||||||
@spec visible_for?(User.t(), User.t() | nil) :: boolean()
|
@spec visible_for(User.t(), User.t() | nil) ::
|
||||||
def visible_for?(user, for_user \\ nil)
|
:visible
|
||||||
|
| :invisible
|
||||||
|
| :restricted_unauthenticated
|
||||||
|
| :deactivated
|
||||||
|
| :confirmation_pending
|
||||||
|
def visible_for(user, for_user \\ nil)
|
||||||
|
|
||||||
def visible_for?(%User{invisible: true}, _), do: false
|
def visible_for(%User{invisible: true}, _), do: :invisible
|
||||||
|
|
||||||
def visible_for?(%User{id: user_id}, %User{id: user_id}), do: true
|
def visible_for(%User{id: user_id}, %User{id: user_id}), do: :visible
|
||||||
|
|
||||||
def visible_for?(%User{local: local} = user, nil) do
|
def visible_for(%User{} = user, nil) do
|
||||||
cfg_key =
|
if restrict_unauthenticated?(user) do
|
||||||
if local,
|
:restrict_unauthenticated
|
||||||
do: :local,
|
else
|
||||||
else: :remote
|
visible_account_status(user)
|
||||||
|
end
|
||||||
if Config.get([:restrict_unauthenticated, :profiles, cfg_key]),
|
|
||||||
do: false,
|
|
||||||
else: account_status(user) == :active
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_for?(%User{} = user, for_user) do
|
def visible_for(%User{} = user, for_user) do
|
||||||
account_status(user) == :active || superuser?(for_user)
|
if superuser?(for_user) do
|
||||||
|
:visible
|
||||||
|
else
|
||||||
|
visible_account_status(user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def visible_for?(_, _), do: false
|
def visible_for(_, _), do: :invisible
|
||||||
|
|
||||||
|
defp restrict_unauthenticated?(%User{local: local}) do
|
||||||
|
config_key = if local, do: :local, else: :remote
|
||||||
|
|
||||||
|
Config.get([:restrict_unauthenticated, :profiles, config_key], false)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp visible_account_status(user) do
|
||||||
|
status = account_status(user)
|
||||||
|
|
||||||
|
if status in [:active, :password_reset_pending] do
|
||||||
|
:visible
|
||||||
|
else
|
||||||
|
status
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@spec superuser?(User.t()) :: boolean()
|
@spec superuser?(User.t()) :: boolean()
|
||||||
def superuser?(%User{local: true, is_admin: true}), do: true
|
def superuser?(%User{local: true, is_admin: true}), do: true
|
||||||
|
@ -465,6 +488,7 @@ def update_changeset(struct, params \\ %{}) do
|
||||||
|> validate_format(:nickname, local_nickname_regex())
|
|> validate_format(:nickname, local_nickname_regex())
|
||||||
|> validate_length(:bio, max: bio_limit)
|
|> validate_length(:bio, max: bio_limit)
|
||||||
|> validate_length(:name, min: 1, max: name_limit)
|
|> validate_length(:name, min: 1, max: name_limit)
|
||||||
|
|> validate_inclusion(:actor_type, ["Person", "Service"])
|
||||||
|> put_fields()
|
|> put_fields()
|
||||||
|> put_emoji()
|
|> put_emoji()
|
||||||
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|
|> put_change_if_present(:bio, &{:ok, parse_bio(&1, struct)})
|
||||||
|
@ -758,7 +782,6 @@ def follow(%User{} = follower, %User{} = followed, state \\ :follow_accept) do
|
||||||
|
|
||||||
follower
|
follower
|
||||||
|> update_following_count()
|
|> update_following_count()
|
||||||
|> set_cache()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -787,7 +810,6 @@ defp do_unfollow(%User{} = follower, %User{} = followed) do
|
||||||
{:ok, follower} =
|
{:ok, follower} =
|
||||||
follower
|
follower
|
||||||
|> update_following_count()
|
|> update_following_count()
|
||||||
|> set_cache()
|
|
||||||
|
|
||||||
{:ok, follower, followed}
|
{:ok, follower, followed}
|
||||||
|
|
||||||
|
@ -1139,35 +1161,25 @@ defp follow_information_changeset(user, params) do
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec update_follower_count(User.t()) :: {:ok, User.t()}
|
||||||
def update_follower_count(%User{} = user) do
|
def update_follower_count(%User{} = user) do
|
||||||
if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
||||||
follower_count_query =
|
follower_count = FollowingRelationship.follower_count(user)
|
||||||
User.Query.build(%{followers: user, deactivated: false})
|
|
||||||
|> select([u], %{count: count(u.id)})
|
|
||||||
|
|
||||||
User
|
user
|
||||||
|> where(id: ^user.id)
|
|> follow_information_changeset(%{follower_count: follower_count})
|
||||||
|> join(:inner, [u], s in subquery(follower_count_query))
|
|> update_and_set_cache
|
||||||
|> update([u, s],
|
|
||||||
set: [follower_count: s.count]
|
|
||||||
)
|
|
||||||
|> select([u], u)
|
|
||||||
|> Repo.update_all([])
|
|
||||||
|> case do
|
|
||||||
{1, [user]} -> set_cache(user)
|
|
||||||
_ -> {:error, user}
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
{:ok, maybe_fetch_follow_information(user)}
|
{:ok, maybe_fetch_follow_information(user)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec update_following_count(User.t()) :: User.t()
|
@spec update_following_count(User.t()) :: {:ok, User.t()}
|
||||||
def update_following_count(%User{local: false} = user) do
|
def update_following_count(%User{local: false} = user) do
|
||||||
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
if Pleroma.Config.get([:instance, :external_user_synchronization]) do
|
||||||
maybe_fetch_follow_information(user)
|
{:ok, maybe_fetch_follow_information(user)}
|
||||||
else
|
else
|
||||||
user
|
{:ok, user}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1176,7 +1188,7 @@ def update_following_count(%User{local: true} = user) do
|
||||||
|
|
||||||
user
|
user
|
||||||
|> follow_information_changeset(%{following_count: following_count})
|
|> follow_information_changeset(%{following_count: following_count})
|
||||||
|> Repo.update!()
|
|> update_and_set_cache()
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_unread_conversation_count(%User{local: true} = user) do
|
def set_unread_conversation_count(%User{local: true} = user) do
|
||||||
|
|
|
@ -812,7 +812,8 @@ defp restrict_media(_query, %{only_media: _val, skip_preload: true}) do
|
||||||
|
|
||||||
defp restrict_media(query, %{only_media: true}) do
|
defp restrict_media(query, %{only_media: true}) do
|
||||||
from(
|
from(
|
||||||
[_activity, object] in query,
|
[activity, object] in query,
|
||||||
|
where: fragment("(?)->>'type' = ?", activity.data, "Create"),
|
||||||
where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
|
where: fragment("not (?)->'attachment' = (?)", object.data, ^[])
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -514,7 +514,6 @@ defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
|
||||||
{new_user, for_user}
|
{new_user, for_user}
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Add support for "object" field
|
|
||||||
@doc """
|
@doc """
|
||||||
Endpoint based on <https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload>
|
Endpoint based on <https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload>
|
||||||
|
|
||||||
|
@ -525,6 +524,8 @@ defp ensure_user_keys_present_and_maybe_refresh_for_user(user, for_user) do
|
||||||
Response:
|
Response:
|
||||||
- HTTP Code: 201 Created
|
- HTTP Code: 201 Created
|
||||||
- HTTP Body: ActivityPub object to be inserted into another's `attachment` field
|
- HTTP Body: ActivityPub object to be inserted into another's `attachment` field
|
||||||
|
|
||||||
|
Note: Will not point to a URL with a `Location` header because no standalone Activity has been created.
|
||||||
"""
|
"""
|
||||||
def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
|
def upload_media(%{assigns: %{user: %User{} = user}} = conn, %{"file" => file} = data) do
|
||||||
with {:ok, object} <-
|
with {:ok, object} <-
|
||||||
|
|
|
@ -13,8 +13,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do
|
||||||
|
|
||||||
defp delist_message(message, threshold) when threshold > 0 do
|
defp delist_message(message, threshold) when threshold > 0 do
|
||||||
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
|
follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address
|
||||||
|
to = message["to"] || []
|
||||||
|
cc = message["cc"] || []
|
||||||
|
|
||||||
follower_collection? = Enum.member?(message["to"] ++ message["cc"], follower_collection)
|
follower_collection? = Enum.member?(to ++ cc, follower_collection)
|
||||||
|
|
||||||
message =
|
message =
|
||||||
case get_recipient_count(message) do
|
case get_recipient_count(message) do
|
||||||
|
@ -71,7 +73,8 @@ defp get_recipient_count(message) do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def filter(%{"type" => "Create"} = message) do
|
def filter(%{"type" => "Create", "object" => %{"type" => object_type}} = message)
|
||||||
|
when object_type in ~w{Note Article} do
|
||||||
reject_threshold =
|
reject_threshold =
|
||||||
Pleroma.Config.get(
|
Pleroma.Config.get(
|
||||||
[:mrf_hellthread, :reject_threshold],
|
[:mrf_hellthread, :reject_threshold],
|
||||||
|
|
|
@ -41,7 +41,6 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do
|
||||||
field(:announcements, {:array, :string}, default: [])
|
field(:announcements, {:array, :string}, default: [])
|
||||||
|
|
||||||
# see if needed
|
# see if needed
|
||||||
field(:conversation, :string)
|
|
||||||
field(:context_id, :string)
|
field(:context_id, :string)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -172,8 +172,8 @@ def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options)
|
||||||
object
|
object
|
||||||
|> Map.put("inReplyTo", replied_object.data["id"])
|
|> Map.put("inReplyTo", replied_object.data["id"])
|
||||||
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
|
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
|
||||||
|> Map.put("conversation", replied_object.data["context"] || object["conversation"])
|
|
||||||
|> Map.put("context", replied_object.data["context"] || object["conversation"])
|
|> Map.put("context", replied_object.data["context"] || object["conversation"])
|
||||||
|
|> Map.drop(["conversation"])
|
||||||
else
|
else
|
||||||
e ->
|
e ->
|
||||||
Logger.error("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
|
Logger.error("Couldn't fetch #{inspect(in_reply_to_id)}, error: #{inspect(e)}")
|
||||||
|
@ -207,7 +207,7 @@ def fix_context(object) do
|
||||||
|
|
||||||
object
|
object
|
||||||
|> Map.put("context", context)
|
|> Map.put("context", context)
|
||||||
|> Map.put("conversation", context)
|
|> Map.drop(["conversation"])
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do
|
def fix_attachments(%{"attachment" => attachment} = object) when is_list(attachment) do
|
||||||
|
@ -458,7 +458,7 @@ def handle_incoming(
|
||||||
to: data["to"],
|
to: data["to"],
|
||||||
object: object,
|
object: object,
|
||||||
actor: user,
|
actor: user,
|
||||||
context: object["conversation"],
|
context: object["context"],
|
||||||
local: false,
|
local: false,
|
||||||
published: data["published"],
|
published: data["published"],
|
||||||
additional:
|
additional:
|
||||||
|
|
|
@ -111,8 +111,7 @@ def user_delete(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames})
|
||||||
action: "delete"
|
action: "delete"
|
||||||
})
|
})
|
||||||
|
|
||||||
conn
|
json(conn, nicknames)
|
||||||
|> json(nicknames)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
def user_follow(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
@ -131,8 +130,7 @@ def user_follow(%{assigns: %{user: admin}} = conn, %{
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
conn
|
json(conn, "ok")
|
||||||
|> json("ok")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_unfollow(%{assigns: %{user: admin}} = conn, %{
|
def user_unfollow(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
@ -151,8 +149,7 @@ def user_unfollow(%{assigns: %{user: admin}} = conn, %{
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
conn
|
json(conn, "ok")
|
||||||
|> json("ok")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
|
def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
|
||||||
|
@ -191,8 +188,7 @@ def users_create(%{assigns: %{user: admin}} = conn, %{"users" => users}) do
|
||||||
action: "create"
|
action: "create"
|
||||||
})
|
})
|
||||||
|
|
||||||
conn
|
json(conn, res)
|
||||||
|> json(res)
|
|
||||||
|
|
||||||
{:error, id, changeset, _} ->
|
{:error, id, changeset, _} ->
|
||||||
res =
|
res =
|
||||||
|
@ -363,8 +359,8 @@ defp maybe_parse_filters(filters) do
|
||||||
filters
|
filters
|
||||||
|> String.split(",")
|
|> String.split(",")
|
||||||
|> Enum.filter(&Enum.member?(@filters, &1))
|
|> Enum.filter(&Enum.member?(@filters, &1))
|
||||||
|> Enum.map(&String.to_atom(&1))
|
|> Enum.map(&String.to_atom/1)
|
||||||
|> Enum.into(%{}, &{&1, true})
|
|> Map.new(&{&1, true})
|
||||||
end
|
end
|
||||||
|
|
||||||
def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
|
def right_add_multiple(%{assigns: %{user: admin}} = conn, %{
|
||||||
|
@ -568,10 +564,10 @@ def update_user_credentials(
|
||||||
{:error, changeset} ->
|
{:error, changeset} ->
|
||||||
errors = Map.new(changeset.errors, fn {key, {error, _}} -> {key, error} end)
|
errors = Map.new(changeset.errors, fn {key, {error, _}} -> {key, error} end)
|
||||||
|
|
||||||
json(conn, %{errors: errors})
|
{:errors, errors}
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
json(conn, %{error: "Unable to update user."})
|
{:error, :not_found}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -616,7 +612,7 @@ defp configurable_from_database do
|
||||||
def reload_emoji(conn, _params) do
|
def reload_emoji(conn, _params) do
|
||||||
Pleroma.Emoji.reload()
|
Pleroma.Emoji.reload()
|
||||||
|
|
||||||
conn |> json("ok")
|
json(conn, "ok")
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
@ -630,7 +626,7 @@ def confirm_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}
|
||||||
action: "confirm_email"
|
action: "confirm_email"
|
||||||
})
|
})
|
||||||
|
|
||||||
conn |> json("")
|
json(conn, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" => nicknames}) do
|
||||||
|
@ -644,14 +640,13 @@ def resend_confirmation_email(%{assigns: %{user: admin}} = conn, %{"nicknames" =
|
||||||
action: "resend_confirmation_email"
|
action: "resend_confirmation_email"
|
||||||
})
|
})
|
||||||
|
|
||||||
conn |> json("")
|
json(conn, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
def stats(conn, _) do
|
def stats(conn, _) do
|
||||||
count = Stats.get_status_visibility_count()
|
count = Stats.get_status_visibility_count()
|
||||||
|
|
||||||
conn
|
json(conn, %{"status_visibility" => count})
|
||||||
|> json(%{"status_visibility" => count})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
defp page_params(params) do
|
defp page_params(params) do
|
||||||
|
|
|
@ -17,6 +17,12 @@ def call(conn, {:error, reason}) do
|
||||||
|> json(%{error: reason})
|
|> json(%{error: reason})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def call(conn, {:errors, errors}) do
|
||||||
|
conn
|
||||||
|
|> put_status(:bad_request)
|
||||||
|
|> json(%{errors: errors})
|
||||||
|
end
|
||||||
|
|
||||||
def call(conn, {:param_cast, _}) do
|
def call(conn, {:param_cast, _}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(:bad_request)
|
|> put_status(:bad_request)
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
# 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.MediaProxyCacheController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Plugs.OAuthScopesPlug
|
||||||
|
alias Pleroma.Web.ApiSpec.Admin, as: Spec
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["read:media_proxy_caches"], admin: true} when action in [:index]
|
||||||
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{scopes: ["write:media_proxy_caches"], admin: true} when action in [:purge, :delete]
|
||||||
|
)
|
||||||
|
|
||||||
|
action_fallback(Pleroma.Web.AdminAPI.FallbackController)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Spec.MediaProxyCacheOperation
|
||||||
|
|
||||||
|
def index(%{assigns: %{user: _}} = conn, params) do
|
||||||
|
cursor =
|
||||||
|
:banned_urls_cache
|
||||||
|
|> :ets.table([{:traverse, {:select, Cachex.Query.create(true, :key)}}])
|
||||||
|
|> :qlc.cursor()
|
||||||
|
|
||||||
|
urls =
|
||||||
|
case params.page do
|
||||||
|
1 ->
|
||||||
|
:qlc.next_answers(cursor, params.page_size)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
:qlc.next_answers(cursor, (params.page - 1) * params.page_size)
|
||||||
|
:qlc.next_answers(cursor, params.page_size)
|
||||||
|
end
|
||||||
|
|
||||||
|
:qlc.delete_cursor(cursor)
|
||||||
|
|
||||||
|
render(conn, "index.json", urls: urls)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete(%{assigns: %{user: _}, body_params: %{urls: urls}} = conn, _) do
|
||||||
|
MediaProxy.remove_from_banned_urls(urls)
|
||||||
|
render(conn, "index.json", urls: urls)
|
||||||
|
end
|
||||||
|
|
||||||
|
def purge(%{assigns: %{user: _}, body_params: %{urls: urls, ban: ban}} = conn, _) do
|
||||||
|
MediaProxy.Invalidation.purge(urls)
|
||||||
|
|
||||||
|
if ban do
|
||||||
|
MediaProxy.put_in_banned_urls(urls)
|
||||||
|
end
|
||||||
|
|
||||||
|
render(conn, "index.json", urls: urls)
|
||||||
|
end
|
||||||
|
end
|
11
lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex
Normal file
11
lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# 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.MediaProxyCacheView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
def render("index.json", %{urls: urls}) do
|
||||||
|
%{urls: urls}
|
||||||
|
end
|
||||||
|
end
|
|
@ -39,6 +39,12 @@ def pagination_params do
|
||||||
:string,
|
:string,
|
||||||
"Return the newest items newer than this ID"
|
"Return the newest items newer than this ID"
|
||||||
),
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:offset,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 0},
|
||||||
|
"Return items past this number of items"
|
||||||
|
),
|
||||||
Operation.parameter(
|
Operation.parameter(
|
||||||
:limit,
|
:limit,
|
||||||
:query,
|
:query,
|
||||||
|
|
|
@ -102,6 +102,7 @@ def show_operation do
|
||||||
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Account", "application/json", Account),
|
200 => Operation.response("Account", "application/json", Account),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
404 => Operation.response("Error", "application/json", ApiError)
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,6 +143,7 @@ def statuses_operation do
|
||||||
] ++ pagination_params(),
|
] ++ pagination_params(),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Statuses", "application/json", array_of_statuses()),
|
200 => Operation.response("Statuses", "application/json", array_of_statuses()),
|
||||||
|
401 => Operation.response("Error", "application/json", ApiError),
|
||||||
404 => Operation.response("Error", "application/json", ApiError)
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
# 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.Admin.MediaProxyCacheOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
|
|
||||||
|
import Pleroma.Web.ApiSpec.Helpers
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "MediaProxyCache"],
|
||||||
|
summary: "Fetch a paginated list of all banned MediaProxy URLs in Cachex",
|
||||||
|
operationId: "AdminAPI.MediaProxyCacheController.index",
|
||||||
|
security: [%{"oAuth" => ["read:media_proxy_caches"]}],
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:page,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 1},
|
||||||
|
"Page"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page_size,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 50},
|
||||||
|
"Number of statuses to return"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => success_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "MediaProxyCache"],
|
||||||
|
summary: "Remove a banned MediaProxy URL from Cachex",
|
||||||
|
operationId: "AdminAPI.MediaProxyCacheController.delete",
|
||||||
|
security: [%{"oAuth" => ["write:media_proxy_caches"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:urls],
|
||||||
|
properties: %{
|
||||||
|
urls: %Schema{type: :array, items: %Schema{type: :string, format: :uri}}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
200 => success_response(),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def purge_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Admin", "MediaProxyCache"],
|
||||||
|
summary: "Purge and optionally ban a MediaProxy URL",
|
||||||
|
operationId: "AdminAPI.MediaProxyCacheController.purge",
|
||||||
|
security: [%{"oAuth" => ["write:media_proxy_caches"]}],
|
||||||
|
requestBody:
|
||||||
|
request_body(
|
||||||
|
"Parameters",
|
||||||
|
%Schema{
|
||||||
|
type: :object,
|
||||||
|
required: [:urls],
|
||||||
|
properties: %{
|
||||||
|
urls: %Schema{type: :array, items: %Schema{type: :string, format: :uri}},
|
||||||
|
ban: %Schema{type: :boolean, default: true}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required: true
|
||||||
|
),
|
||||||
|
responses: %{
|
||||||
|
200 => success_response(),
|
||||||
|
400 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp success_response do
|
||||||
|
Operation.response("Array of banned MediaProxy URLs in Cachex", "application/json", %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
urls: %Schema{
|
||||||
|
type: :array,
|
||||||
|
items: %Schema{
|
||||||
|
type: :string,
|
||||||
|
format: :uri,
|
||||||
|
description: "MediaProxy URLs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
|
@ -163,6 +163,13 @@ def notification do
|
||||||
description:
|
description:
|
||||||
"Status that was the object of the notification, e.g. in mentions, reblogs, favourites, or polls.",
|
"Status that was the object of the notification, e.g. in mentions, reblogs, favourites, or polls.",
|
||||||
nullable: true
|
nullable: true
|
||||||
|
},
|
||||||
|
pleroma: %Schema{
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
is_seen: %Schema{type: :boolean},
|
||||||
|
is_muted: %Schema{type: :boolean}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
example: %{
|
example: %{
|
||||||
|
@ -170,7 +177,8 @@ def notification do
|
||||||
"type" => "mention",
|
"type" => "mention",
|
||||||
"created_at" => "2019-11-23T07:49:02.064Z",
|
"created_at" => "2019-11-23T07:49:02.064Z",
|
||||||
"account" => Account.schema().example,
|
"account" => Account.schema().example,
|
||||||
"status" => Status.schema().example
|
"status" => Status.schema().example,
|
||||||
|
"pleroma" => %{"is_seen" => false, "is_muted" => false}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,6 +33,20 @@ def index_operation do
|
||||||
tags: ["Emoji Packs"],
|
tags: ["Emoji Packs"],
|
||||||
summary: "Lists local custom emoji packs",
|
summary: "Lists local custom emoji packs",
|
||||||
operationId: "PleromaAPI.EmojiPackController.index",
|
operationId: "PleromaAPI.EmojiPackController.index",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:page,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 1},
|
||||||
|
"Page"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page_size,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 50},
|
||||||
|
"Number of emoji packs to return"
|
||||||
|
)
|
||||||
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => emoji_packs_response()
|
200 => emoji_packs_response()
|
||||||
}
|
}
|
||||||
|
@ -44,7 +58,21 @@ def show_operation do
|
||||||
tags: ["Emoji Packs"],
|
tags: ["Emoji Packs"],
|
||||||
summary: "Show emoji pack",
|
summary: "Show emoji pack",
|
||||||
operationId: "PleromaAPI.EmojiPackController.show",
|
operationId: "PleromaAPI.EmojiPackController.show",
|
||||||
parameters: [name_param()],
|
parameters: [
|
||||||
|
name_param(),
|
||||||
|
Operation.parameter(
|
||||||
|
:page,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 1},
|
||||||
|
"Page"
|
||||||
|
),
|
||||||
|
Operation.parameter(
|
||||||
|
:page_size,
|
||||||
|
:query,
|
||||||
|
%Schema{type: :integer, default: 30},
|
||||||
|
"Number of emoji to return"
|
||||||
|
)
|
||||||
|
],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("Emoji Pack", "application/json", emoji_pack()),
|
200 => Operation.response("Emoji Pack", "application/json", emoji_pack()),
|
||||||
400 => Operation.response("Bad Request", "application/json", ApiError),
|
400 => Operation.response("Bad Request", "application/json", ApiError),
|
||||||
|
|
|
@ -49,7 +49,7 @@ def manifest(conn, _params) do
|
||||||
|> render("manifest.json")
|
|> render("manifest.json")
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "PUT /api/web/settings"
|
@doc "PUT /api/web/settings: Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere"
|
||||||
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
|
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
|
||||||
with {:ok, _} <- User.mastodon_settings_update(user, settings) do
|
with {:ok, _} <- User.mastodon_settings_update(user, settings) do
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
|
|
|
@ -179,6 +179,9 @@ def update_credentials(%{assigns: %{user: user}, body_params: params} = conn, _p
|
||||||
|> Maps.put_if_present(:pleroma_settings_store, params[:pleroma_settings_store])
|
|> Maps.put_if_present(:pleroma_settings_store, params[:pleroma_settings_store])
|
||||||
|> Maps.put_if_present(:default_scope, params[:default_scope])
|
|> Maps.put_if_present(:default_scope, params[:default_scope])
|
||||||
|> Maps.put_if_present(:default_scope, params["source"]["privacy"])
|
|> Maps.put_if_present(:default_scope, params["source"]["privacy"])
|
||||||
|
|> Maps.put_if_present(:actor_type, params[:bot], fn bot ->
|
||||||
|
if bot, do: {:ok, "Service"}, else: {:ok, "Person"}
|
||||||
|
end)
|
||||||
|> Maps.put_if_present(:actor_type, params[:actor_type])
|
|> Maps.put_if_present(:actor_type, params[:actor_type])
|
||||||
|
|
||||||
# What happens here:
|
# What happens here:
|
||||||
|
@ -238,17 +241,17 @@ def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])
|
||||||
@doc "GET /api/v1/accounts/:id"
|
@doc "GET /api/v1/accounts/:id"
|
||||||
def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id}) do
|
def show(%{assigns: %{user: for_user}} = conn, %{id: nickname_or_id}) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user),
|
with %User{} = user <- User.get_cached_by_nickname_or_id(nickname_or_id, for: for_user),
|
||||||
true <- User.visible_for?(user, for_user) do
|
:visible <- User.visible_for(user, for_user) do
|
||||||
render(conn, "show.json", user: user, for: for_user)
|
render(conn, "show.json", user: user, for: for_user)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :not_found, "Can't find user")
|
error -> user_visibility_error(conn, error)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/accounts/:id/statuses"
|
@doc "GET /api/v1/accounts/:id/statuses"
|
||||||
def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user),
|
with %User{} = user <- User.get_cached_by_nickname_or_id(params.id, for: reading_user),
|
||||||
true <- User.visible_for?(user, reading_user) do
|
:visible <- User.visible_for(user, reading_user) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|> Map.delete(:tagged)
|
|> Map.delete(:tagged)
|
||||||
|
@ -265,7 +268,17 @@ def statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
as: :activity
|
as: :activity
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
_e -> render_error(conn, :not_found, "Can't find user")
|
error -> user_visibility_error(conn, error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp user_visibility_error(conn, error) do
|
||||||
|
case error do
|
||||||
|
:restrict_unauthenticated ->
|
||||||
|
render_error(conn, :unauthorized, "This API requires an authenticated user")
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
render_error(conn, :not_found, "Can't find user")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -107,21 +107,21 @@ defp resource_search(_, "statuses", query, options) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp resource_search(:v2, "hashtags", query, _options) do
|
defp resource_search(:v2, "hashtags", query, options) do
|
||||||
tags_path = Web.base_url() <> "/tag/"
|
tags_path = Web.base_url() <> "/tag/"
|
||||||
|
|
||||||
query
|
query
|
||||||
|> prepare_tags()
|
|> prepare_tags(options)
|
||||||
|> Enum.map(fn tag ->
|
|> Enum.map(fn tag ->
|
||||||
%{name: tag, url: tags_path <> tag}
|
%{name: tag, url: tags_path <> tag}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp resource_search(:v1, "hashtags", query, _options) do
|
defp resource_search(:v1, "hashtags", query, options) do
|
||||||
prepare_tags(query)
|
prepare_tags(query, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp prepare_tags(query, add_joined_tag \\ true) do
|
defp prepare_tags(query, options) do
|
||||||
tags =
|
tags =
|
||||||
query
|
query
|
||||||
|> preprocess_uri_query()
|
|> preprocess_uri_query()
|
||||||
|
@ -139,13 +139,20 @@ defp prepare_tags(query, add_joined_tag \\ true) do
|
||||||
|
|
||||||
tags = Enum.map(tags, fn tag -> String.trim_leading(tag, "#") end)
|
tags = Enum.map(tags, fn tag -> String.trim_leading(tag, "#") end)
|
||||||
|
|
||||||
if Enum.empty?(explicit_tags) && add_joined_tag do
|
tags =
|
||||||
tags
|
if Enum.empty?(explicit_tags) && !options[:skip_joined_tag] do
|
||||||
|> Kernel.++([joined_tag(tags)])
|
add_joined_tag(tags)
|
||||||
|> Enum.uniq_by(&String.downcase/1)
|
else
|
||||||
else
|
tags
|
||||||
tags
|
end
|
||||||
end
|
|
||||||
|
Pleroma.Pagination.paginate(tags, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp add_joined_tag(tags) do
|
||||||
|
tags
|
||||||
|
|> Kernel.++([joined_tag(tags)])
|
||||||
|
|> Enum.uniq_by(&String.downcase/1)
|
||||||
end
|
end
|
||||||
|
|
||||||
# If `query` is a URI, returns last component of its path, otherwise returns `query`
|
# If `query` is a URI, returns last component of its path, otherwise returns `query`
|
||||||
|
|
|
@ -35,7 +35,7 @@ def render("index.json", %{users: users} = opts) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("show.json", %{user: user} = opts) do
|
def render("show.json", %{user: user} = opts) do
|
||||||
if User.visible_for?(user, opts[:for]) do
|
if User.visible_for(user, opts[:for]) == :visible do
|
||||||
do_render("show.json", opts)
|
do_render("show.json", opts)
|
||||||
else
|
else
|
||||||
%{}
|
%{}
|
||||||
|
@ -179,7 +179,7 @@ defp do_render("show.json", %{user: user} = opts) do
|
||||||
0
|
0
|
||||||
end
|
end
|
||||||
|
|
||||||
bot = user.actor_type in ["Application", "Service"]
|
bot = user.actor_type == "Service"
|
||||||
|
|
||||||
emojis =
|
emojis =
|
||||||
Enum.map(user.emoji, fn {shortcode, raw_url} ->
|
Enum.map(user.emoji, fn {shortcode, raw_url} ->
|
||||||
|
|
|
@ -23,7 +23,7 @@ def render("show.json", _) do
|
||||||
streaming_api: Pleroma.Web.Endpoint.websocket_url()
|
streaming_api: Pleroma.Web.Endpoint.websocket_url()
|
||||||
},
|
},
|
||||||
stats: Pleroma.Stats.get_stats(),
|
stats: Pleroma.Stats.get_stats(),
|
||||||
thumbnail: instance_thumbnail(),
|
thumbnail: Keyword.get(instance, :instance_thumbnail),
|
||||||
languages: ["en"],
|
languages: ["en"],
|
||||||
registrations: Keyword.get(instance, :registrations_open),
|
registrations: Keyword.get(instance, :registrations_open),
|
||||||
# Extra (not present in Mastodon):
|
# Extra (not present in Mastodon):
|
||||||
|
@ -88,9 +88,4 @@ def federation do
|
||||||
end
|
end
|
||||||
|> Map.put(:enabled, Config.get([:instance, :federating]))
|
|> Map.put(:enabled, Config.get([:instance, :federating]))
|
||||||
end
|
end
|
||||||
|
|
||||||
defp instance_thumbnail do
|
|
||||||
Pleroma.Config.get([:instance, :instance_thumbnail]) ||
|
|
||||||
"#{Pleroma.Web.base_url()}/instance/thumbnail.jpeg"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -84,12 +84,7 @@ def render(
|
||||||
|
|
||||||
# Note: :relationships contain user mutes (needed for :muted flag in :status)
|
# Note: :relationships contain user mutes (needed for :muted flag in :status)
|
||||||
status_render_opts = %{relationships: opts[:relationships]}
|
status_render_opts = %{relationships: opts[:relationships]}
|
||||||
|
account = AccountView.render("show.json", %{user: actor, for: reading_user})
|
||||||
account =
|
|
||||||
AccountView.render(
|
|
||||||
"show.json",
|
|
||||||
%{user: actor, for: reading_user}
|
|
||||||
)
|
|
||||||
|
|
||||||
response = %{
|
response = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
|
@ -97,6 +92,7 @@ def render(
|
||||||
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
|
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
|
||||||
account: account,
|
account: account,
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
|
is_muted: User.mutes?(reading_user, actor),
|
||||||
is_seen: notification.seen
|
is_seen: notification.seen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,22 +5,34 @@
|
||||||
defmodule Pleroma.Web.MediaProxy.Invalidation do
|
defmodule Pleroma.Web.MediaProxy.Invalidation do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
|
|
||||||
@callback purge(list(String.t()), map()) :: {:ok, String.t()} | {:error, String.t()}
|
@callback purge(list(String.t()), Keyword.t()) :: {:ok, list(String.t())} | {:error, String.t()}
|
||||||
|
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
@spec purge(list(String.t())) :: {:ok, String.t()} | {:error, String.t()}
|
@spec enabled?() :: boolean()
|
||||||
|
def enabled?, do: Config.get([:media_proxy, :invalidation, :enabled])
|
||||||
|
|
||||||
|
@spec purge(list(String.t()) | String.t()) :: {:ok, list(String.t())} | {:error, String.t()}
|
||||||
def purge(urls) do
|
def purge(urls) do
|
||||||
[:media_proxy, :invalidation, :enabled]
|
prepared_urls = prepare_urls(urls)
|
||||||
|> Config.get()
|
|
||||||
|> do_purge(urls)
|
if enabled?() do
|
||||||
|
do_purge(prepared_urls)
|
||||||
|
else
|
||||||
|
{:ok, prepared_urls}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_purge(true, urls) do
|
defp do_purge(urls) do
|
||||||
provider = Config.get([:media_proxy, :invalidation, :provider])
|
provider = Config.get([:media_proxy, :invalidation, :provider])
|
||||||
options = Config.get(provider)
|
options = Config.get(provider)
|
||||||
provider.purge(urls, options)
|
provider.purge(urls, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_purge(_, _), do: :ok
|
def prepare_urls(urls) do
|
||||||
|
urls
|
||||||
|
|> List.wrap()
|
||||||
|
|> Enum.map(&MediaProxy.url/1)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,10 +9,10 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.Http do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Pleroma.Web.MediaProxy.Invalidation
|
@impl Pleroma.Web.MediaProxy.Invalidation
|
||||||
def purge(urls, opts) do
|
def purge(urls, opts \\ []) do
|
||||||
method = Map.get(opts, :method, :purge)
|
method = Keyword.get(opts, :method, :purge)
|
||||||
headers = Map.get(opts, :headers, [])
|
headers = Keyword.get(opts, :headers, [])
|
||||||
options = Map.get(opts, :options, [])
|
options = Keyword.get(opts, :options, [])
|
||||||
|
|
||||||
Logger.debug("Running cache purge: #{inspect(urls)}")
|
Logger.debug("Running cache purge: #{inspect(urls)}")
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ def purge(urls, opts) do
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:ok, "success"}
|
{:ok, urls}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_purge(method, url, headers, options) do
|
defp do_purge(method, url, headers, options) do
|
||||||
|
|
|
@ -10,32 +10,34 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.Script do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@impl Pleroma.Web.MediaProxy.Invalidation
|
@impl Pleroma.Web.MediaProxy.Invalidation
|
||||||
def purge(urls, %{script_path: script_path} = _options) do
|
def purge(urls, opts \\ []) do
|
||||||
args =
|
args =
|
||||||
urls
|
urls
|
||||||
|> List.wrap()
|
|> List.wrap()
|
||||||
|> Enum.uniq()
|
|> Enum.uniq()
|
||||||
|> Enum.join(" ")
|
|> Enum.join(" ")
|
||||||
|
|
||||||
path = Path.expand(script_path)
|
opts
|
||||||
|
|> Keyword.get(:script_path)
|
||||||
Logger.debug("Running cache purge: #{inspect(urls)}, #{path}")
|
|> do_purge([args])
|
||||||
|
|> handle_result(urls)
|
||||||
case do_purge(path, [args]) do
|
|
||||||
{result, exit_status} when exit_status > 0 ->
|
|
||||||
Logger.error("Error while cache purge: #{inspect(result)}")
|
|
||||||
{:error, inspect(result)}
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
{:ok, "success"}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def purge(_, _), do: {:error, "not found script path"}
|
defp do_purge(script_path, args) when is_binary(script_path) do
|
||||||
|
path = Path.expand(script_path)
|
||||||
defp do_purge(path, args) do
|
Logger.debug("Running cache purge: #{inspect(args)}, #{inspect(path)}")
|
||||||
System.cmd(path, args)
|
System.cmd(path, args)
|
||||||
rescue
|
rescue
|
||||||
error -> {inspect(error), 1}
|
error -> error
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_purge(_, _), do: {:error, "not found script path"}
|
||||||
|
|
||||||
|
defp handle_result({_result, 0}, urls), do: {:ok, urls}
|
||||||
|
defp handle_result({:error, error}, urls), do: handle_result(error, urls)
|
||||||
|
|
||||||
|
defp handle_result(error, _) do
|
||||||
|
Logger.error("Error while cache purge: #{inspect(error)}")
|
||||||
|
{:error, inspect(error)}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,20 +6,53 @@ defmodule Pleroma.Web.MediaProxy do
|
||||||
alias Pleroma.Config
|
alias Pleroma.Config
|
||||||
alias Pleroma.Upload
|
alias Pleroma.Upload
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Web.MediaProxy.Invalidation
|
||||||
|
|
||||||
@base64_opts [padding: false]
|
@base64_opts [padding: false]
|
||||||
|
|
||||||
|
@spec in_banned_urls(String.t()) :: boolean()
|
||||||
|
def in_banned_urls(url), do: elem(Cachex.exists?(:banned_urls_cache, url(url)), 1)
|
||||||
|
|
||||||
|
def remove_from_banned_urls(urls) when is_list(urls) do
|
||||||
|
Cachex.execute!(:banned_urls_cache, fn cache ->
|
||||||
|
Enum.each(Invalidation.prepare_urls(urls), &Cachex.del(cache, &1))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def remove_from_banned_urls(url) when is_binary(url) do
|
||||||
|
Cachex.del(:banned_urls_cache, url(url))
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_in_banned_urls(urls) when is_list(urls) do
|
||||||
|
Cachex.execute!(:banned_urls_cache, fn cache ->
|
||||||
|
Enum.each(Invalidation.prepare_urls(urls), &Cachex.put(cache, &1, true))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def put_in_banned_urls(url) when is_binary(url) do
|
||||||
|
Cachex.put(:banned_urls_cache, url(url), true)
|
||||||
|
end
|
||||||
|
|
||||||
def url(url) when is_nil(url) or url == "", do: nil
|
def url(url) when is_nil(url) or url == "", do: nil
|
||||||
def url("/" <> _ = url), do: url
|
def url("/" <> _ = url), do: url
|
||||||
|
|
||||||
def url(url) do
|
def url(url) do
|
||||||
if disabled?() or local?(url) or whitelisted?(url) do
|
if disabled?() or not url_proxiable?(url) do
|
||||||
url
|
url
|
||||||
else
|
else
|
||||||
encode_url(url)
|
encode_url(url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec url_proxiable?(String.t()) :: boolean()
|
||||||
|
def url_proxiable?(url) do
|
||||||
|
if local?(url) or whitelisted?(url) do
|
||||||
|
false
|
||||||
|
else
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
defp disabled?, do: !Config.get([:media_proxy, :enabled], false)
|
defp disabled?, do: !Config.get([:media_proxy, :enabled], false)
|
||||||
|
|
||||||
defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
|
defp local?(url), do: String.starts_with?(url, Pleroma.Web.base_url())
|
||||||
|
|
|
@ -14,10 +14,11 @@ def remote(conn, %{"sig" => sig64, "url" => url64} = params) do
|
||||||
with config <- Pleroma.Config.get([:media_proxy], []),
|
with config <- Pleroma.Config.get([:media_proxy], []),
|
||||||
true <- Keyword.get(config, :enabled, false),
|
true <- Keyword.get(config, :enabled, false),
|
||||||
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
|
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
|
||||||
|
{_, false} <- {:in_banned_urls, MediaProxy.in_banned_urls(url)},
|
||||||
:ok <- filename_matches(params, conn.request_path, url) do
|
:ok <- filename_matches(params, conn.request_path, url) do
|
||||||
ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts))
|
ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts))
|
||||||
else
|
else
|
||||||
false ->
|
error when error in [false, {:in_banned_urls, true}] ->
|
||||||
send_resp(conn, 404, Plug.Conn.Status.reason_phrase(404))
|
send_resp(conn, 404, Plug.Conn.Status.reason_phrase(404))
|
||||||
|
|
||||||
{:error, :invalid_signature} ->
|
{:error, :invalid_signature} ->
|
||||||
|
|
|
@ -37,14 +37,14 @@ def remote(conn, %{url: url}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def index(conn, _params) do
|
def index(conn, params) do
|
||||||
emoji_path =
|
emoji_path =
|
||||||
[:instance, :static_dir]
|
[:instance, :static_dir]
|
||||||
|> Pleroma.Config.get!()
|
|> Pleroma.Config.get!()
|
||||||
|> Path.join("emoji")
|
|> Path.join("emoji")
|
||||||
|
|
||||||
with {:ok, packs} <- Pack.list_local() do
|
with {:ok, packs, count} <- Pack.list_local(page: params.page, page_size: params.page_size) do
|
||||||
json(conn, packs)
|
json(conn, %{packs: packs, count: count})
|
||||||
else
|
else
|
||||||
{:error, :create_dir, e} ->
|
{:error, :create_dir, e} ->
|
||||||
conn
|
conn
|
||||||
|
@ -60,10 +60,10 @@ def index(conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def show(conn, %{name: name}) do
|
def show(conn, %{name: name, page: page, page_size: page_size}) do
|
||||||
name = String.trim(name)
|
name = String.trim(name)
|
||||||
|
|
||||||
with {:ok, pack} <- Pack.show(name) do
|
with {:ok, pack} <- Pack.show(name: name, page: page, page_size: page_size) do
|
||||||
json(conn, pack)
|
json(conn, pack)
|
||||||
else
|
else
|
||||||
{:error, :not_found} ->
|
{:error, :not_found} ->
|
||||||
|
|
|
@ -209,6 +209,10 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/oauth_app", OAuthAppController, :create)
|
post("/oauth_app", OAuthAppController, :create)
|
||||||
patch("/oauth_app/:id", OAuthAppController, :update)
|
patch("/oauth_app/:id", OAuthAppController, :update)
|
||||||
delete("/oauth_app/:id", OAuthAppController, :delete)
|
delete("/oauth_app/:id", OAuthAppController, :delete)
|
||||||
|
|
||||||
|
get("/media_proxy_caches", MediaProxyCacheController, :index)
|
||||||
|
post("/media_proxy_caches/delete", MediaProxyCacheController, :delete)
|
||||||
|
post("/media_proxy_caches/purge", MediaProxyCacheController, :purge)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
scope "/api/pleroma/emoji", Pleroma.Web.PleromaAPI do
|
||||||
|
@ -463,6 +467,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/web", Pleroma.Web do
|
scope "/api/web", Pleroma.Web do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
|
|
||||||
|
# Backend-obscure settings blob for MastoFE, don't parse/reuse elsewhere
|
||||||
put("/settings", MastoFEController, :put_settings)
|
put("/settings", MastoFEController, :put_settings)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,19 @@ def perform(
|
||||||
},
|
},
|
||||||
_job
|
_job
|
||||||
) do
|
) do
|
||||||
hrefs =
|
attachments
|
||||||
Enum.flat_map(attachments, fn attachment ->
|
|> Enum.flat_map(fn item -> Enum.map(item["url"], & &1["href"]) end)
|
||||||
Enum.map(attachment["url"], & &1["href"])
|
|> fetch_objects
|
||||||
end)
|
|> prepare_objects(actor, Enum.map(attachments, & &1["name"]))
|
||||||
|
|> filter_objects
|
||||||
|
|> do_clean
|
||||||
|
|
||||||
names = Enum.map(attachments, & &1["name"])
|
{:ok, :success}
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform(%{"op" => "cleanup_attachments", "object" => _object}, _job), do: {:ok, :skip}
|
||||||
|
|
||||||
|
defp do_clean({object_ids, attachment_urls}) do
|
||||||
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||||
|
|
||||||
prefix =
|
prefix =
|
||||||
|
@ -39,68 +45,70 @@ def perform(
|
||||||
"/"
|
"/"
|
||||||
)
|
)
|
||||||
|
|
||||||
# find all objects for copies of the attachments, name and actor doesn't matter here
|
Enum.each(attachment_urls, fn href ->
|
||||||
object_ids_and_hrefs =
|
href
|
||||||
from(o in Object,
|
|> String.trim_leading("#{base_url}/#{prefix}")
|
||||||
where:
|
|> uploader.delete_file()
|
||||||
fragment(
|
end)
|
||||||
"to_jsonb(array(select jsonb_array_elements((?)#>'{url}') ->> 'href' where jsonb_typeof((?)#>'{url}') = 'array'))::jsonb \\?| (?)",
|
|
||||||
o.data,
|
|
||||||
o.data,
|
|
||||||
^hrefs
|
|
||||||
)
|
|
||||||
)
|
|
||||||
# The query above can be time consumptive on large instances until we
|
|
||||||
# refactor how uploads are stored
|
|
||||||
|> Repo.all(timeout: :infinity)
|
|
||||||
# we should delete 1 object for any given attachment, but don't delete
|
|
||||||
# files if there are more than 1 object for it
|
|
||||||
|> Enum.reduce(%{}, fn %{
|
|
||||||
id: id,
|
|
||||||
data: %{
|
|
||||||
"url" => [%{"href" => href}],
|
|
||||||
"actor" => obj_actor,
|
|
||||||
"name" => name
|
|
||||||
}
|
|
||||||
},
|
|
||||||
acc ->
|
|
||||||
Map.update(acc, href, %{id: id, count: 1}, fn val ->
|
|
||||||
case obj_actor == actor and name in names do
|
|
||||||
true ->
|
|
||||||
# set id of the actor's object that will be deleted
|
|
||||||
%{val | id: id, count: val.count + 1}
|
|
||||||
|
|
||||||
false ->
|
delete_objects(object_ids)
|
||||||
# another actor's object, just increase count to not delete file
|
|
||||||
%{val | count: val.count + 1}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
end)
|
|
||||||
|> Enum.map(fn {href, %{id: id, count: count}} ->
|
|
||||||
# only delete files that have single instance
|
|
||||||
with 1 <- count do
|
|
||||||
href
|
|
||||||
|> String.trim_leading("#{base_url}/#{prefix}")
|
|
||||||
|> uploader.delete_file()
|
|
||||||
|
|
||||||
{id, href}
|
|
||||||
else
|
|
||||||
_ -> {id, nil}
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
object_ids = Enum.map(object_ids_and_hrefs, fn {id, _} -> id end)
|
|
||||||
|
|
||||||
from(o in Object, where: o.id in ^object_ids)
|
|
||||||
|> Repo.delete_all()
|
|
||||||
|
|
||||||
object_ids_and_hrefs
|
|
||||||
|> Enum.filter(fn {_, href} -> not is_nil(href) end)
|
|
||||||
|> Enum.map(&elem(&1, 1))
|
|
||||||
|> Pleroma.Web.MediaProxy.Invalidation.purge()
|
|
||||||
|
|
||||||
{:ok, :success}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def perform(%{"op" => "cleanup_attachments", "object" => _object}, _job), do: {:ok, :skip}
|
defp delete_objects([_ | _] = object_ids) do
|
||||||
|
Repo.delete_all(from(o in Object, where: o.id in ^object_ids))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp delete_objects(_), do: :ok
|
||||||
|
|
||||||
|
# we should delete 1 object for any given attachment, but don't delete
|
||||||
|
# files if there are more than 1 object for it
|
||||||
|
defp filter_objects(objects) do
|
||||||
|
Enum.reduce(objects, {[], []}, fn {href, %{id: id, count: count}}, {ids, hrefs} ->
|
||||||
|
with 1 <- count do
|
||||||
|
{ids ++ [id], hrefs ++ [href]}
|
||||||
|
else
|
||||||
|
_ -> {ids ++ [id], hrefs}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prepare_objects(objects, actor, names) do
|
||||||
|
objects
|
||||||
|
|> Enum.reduce(%{}, fn %{
|
||||||
|
id: id,
|
||||||
|
data: %{
|
||||||
|
"url" => [%{"href" => href}],
|
||||||
|
"actor" => obj_actor,
|
||||||
|
"name" => name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
acc ->
|
||||||
|
Map.update(acc, href, %{id: id, count: 1}, fn val ->
|
||||||
|
case obj_actor == actor and name in names do
|
||||||
|
true ->
|
||||||
|
# set id of the actor's object that will be deleted
|
||||||
|
%{val | id: id, count: val.count + 1}
|
||||||
|
|
||||||
|
false ->
|
||||||
|
# another actor's object, just increase count to not delete file
|
||||||
|
%{val | count: val.count + 1}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fetch_objects(hrefs) do
|
||||||
|
from(o in Object,
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"to_jsonb(array(select jsonb_array_elements((?)#>'{url}') ->> 'href' where jsonb_typeof((?)#>'{url}') = 'array'))::jsonb \\?| (?)",
|
||||||
|
o.data,
|
||||||
|
o.data,
|
||||||
|
^hrefs
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# The query above can be time consumptive on large instances until we
|
||||||
|
# refactor how uploads are stored
|
||||||
|
|> Repo.all(timeout: :infinity)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
43
mix.exs
43
mix.exs
|
@ -230,32 +230,37 @@ defp aliases do
|
||||||
defp version(version) do
|
defp version(version) do
|
||||||
identifier_filter = ~r/[^0-9a-z\-]+/i
|
identifier_filter = ~r/[^0-9a-z\-]+/i
|
||||||
|
|
||||||
# Pre-release version, denoted from patch version with a hyphen
|
{_cmdgit, cmdgit_err} = System.cmd("sh", ["-c", "command -v git"])
|
||||||
{tag, tag_err} =
|
|
||||||
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true)
|
|
||||||
|
|
||||||
{describe, describe_err} = System.cmd("git", ["describe", "--tags", "--abbrev=8"])
|
|
||||||
{commit_hash, commit_hash_err} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
|
||||||
|
|
||||||
git_pre_release =
|
git_pre_release =
|
||||||
cond do
|
if cmdgit_err == 0 do
|
||||||
tag_err == 0 and describe_err == 0 ->
|
{tag, tag_err} =
|
||||||
describe
|
System.cmd("git", ["describe", "--tags", "--abbrev=0"], stderr_to_stdout: true)
|
||||||
|> String.trim()
|
|
||||||
|> String.replace(String.trim(tag), "")
|
|
||||||
|> String.trim_leading("-")
|
|
||||||
|> String.trim()
|
|
||||||
|
|
||||||
commit_hash_err == 0 ->
|
{describe, describe_err} = System.cmd("git", ["describe", "--tags", "--abbrev=8"])
|
||||||
"0-g" <> String.trim(commit_hash)
|
{commit_hash, commit_hash_err} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
||||||
|
|
||||||
true ->
|
# Pre-release version, denoted from patch version with a hyphen
|
||||||
""
|
cond do
|
||||||
|
tag_err == 0 and describe_err == 0 ->
|
||||||
|
describe
|
||||||
|
|> String.trim()
|
||||||
|
|> String.replace(String.trim(tag), "")
|
||||||
|
|> String.trim_leading("-")
|
||||||
|
|> String.trim()
|
||||||
|
|
||||||
|
commit_hash_err == 0 ->
|
||||||
|
"0-g" <> String.trim(commit_hash)
|
||||||
|
|
||||||
|
true ->
|
||||||
|
nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Branch name as pre-release version component, denoted with a dot
|
# Branch name as pre-release version component, denoted with a dot
|
||||||
branch_name =
|
branch_name =
|
||||||
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
with 0 <- cmdgit_err,
|
||||||
|
{branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
||||||
branch_name <- String.trim(branch_name),
|
branch_name <- String.trim(branch_name),
|
||||||
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
|
branch_name <- System.get_env("PLEROMA_BUILD_BRANCH") || branch_name,
|
||||||
true <-
|
true <-
|
||||||
|
@ -269,7 +274,7 @@ defp version(version) do
|
||||||
|
|
||||||
branch_name
|
branch_name
|
||||||
else
|
else
|
||||||
_ -> "stable"
|
_ -> ""
|
||||||
end
|
end
|
||||||
|
|
||||||
build_name =
|
build_name =
|
||||||
|
|
580
priv/gettext/it/LC_MESSAGES/errors.po
Normal file
580
priv/gettext/it/LC_MESSAGES/errors.po
Normal file
|
@ -0,0 +1,580 @@
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-06-19 14:33+0000\n"
|
||||||
|
"PO-Revision-Date: 2020-06-19 20:38+0000\n"
|
||||||
|
"Last-Translator: Ben Is <srsbzns@cock.li>\n"
|
||||||
|
"Language-Team: Italian <https://translate.pleroma.social/projects/pleroma/"
|
||||||
|
"pleroma/it/>\n"
|
||||||
|
"Language: it\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||||
|
"X-Generator: Weblate 4.0.4\n"
|
||||||
|
|
||||||
|
## This file is a PO Template file.
|
||||||
|
##
|
||||||
|
## `msgid`s here are often extracted from source code.
|
||||||
|
## Add new translations manually only if they're dynamic
|
||||||
|
## translations that can't be statically extracted.
|
||||||
|
##
|
||||||
|
## Run `mix gettext.extract` to bring this file up to
|
||||||
|
## date. Leave `msgstr`s empty as changing them here as no
|
||||||
|
## effect: edit them in PO (`.po`) files instead.
|
||||||
|
## From Ecto.Changeset.cast/4
|
||||||
|
msgid "can't be blank"
|
||||||
|
msgstr "non può essere nullo"
|
||||||
|
|
||||||
|
## From Ecto.Changeset.unique_constraint/3
|
||||||
|
msgid "has already been taken"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.put_change/3
|
||||||
|
msgid "is invalid"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_format/3
|
||||||
|
msgid "has invalid format"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_subset/3
|
||||||
|
msgid "has an invalid entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_exclusion/3
|
||||||
|
msgid "is reserved"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_confirmation/3
|
||||||
|
msgid "does not match confirmation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.no_assoc_constraint/3
|
||||||
|
msgid "is still associated with this entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "are still associated with this entry"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_length/3
|
||||||
|
msgid "should be %{count} character(s)"
|
||||||
|
msgid_plural "should be %{count} character(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should have %{count} item(s)"
|
||||||
|
msgid_plural "should have %{count} item(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should be at least %{count} character(s)"
|
||||||
|
msgid_plural "should be at least %{count} character(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should have at least %{count} item(s)"
|
||||||
|
msgid_plural "should have at least %{count} item(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should be at most %{count} character(s)"
|
||||||
|
msgid_plural "should be at most %{count} character(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
msgid "should have at most %{count} item(s)"
|
||||||
|
msgid_plural "should have at most %{count} item(s)"
|
||||||
|
msgstr[0] ""
|
||||||
|
msgstr[1] ""
|
||||||
|
|
||||||
|
## From Ecto.Changeset.validate_number/3
|
||||||
|
msgid "must be less than %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "must be greater than %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "must be less than or equal to %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "must be greater than or equal to %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "must be equal to %{number}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:421
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Account not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:249
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Already voted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:360
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Bad request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:425
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't delete object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/status_controller.ex:196
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't delete this post"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/controller_helper.ex:95
|
||||||
|
#: lib/pleroma/web/controller_helper.ex:101
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't display this activity"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:227
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:254
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't find user"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:114
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't get favorites"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:437
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Can't like object"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:556
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Cannot post an empty status without attachments"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:504
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Comment must be up to %{max_size} characters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/config/config_db.ex:222
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Config with params %{params} not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:95
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not delete"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:141
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not favorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:370
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not pin"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:112
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not repeat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:188
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not unfavorite"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:380
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not unpin"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:126
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not unrepeat"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:428
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:437
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not update state"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:202
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Error."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:106
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid CAPTCHA"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:117
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:569
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid credentials"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:38
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid credentials."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:265
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid indices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:1147
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid parameters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:411
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid password."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:187
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:109
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Kocaptcha service unavailable"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:113
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Missing parameters"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:540
|
||||||
|
#, elixir-format
|
||||||
|
msgid "No such conversation"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:439
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:465 lib/pleroma/web/admin_api/admin_api_controller.ex:507
|
||||||
|
#, elixir-format
|
||||||
|
msgid "No such permission_group"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/uploaded_media.ex:74
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:485 lib/pleroma/web/admin_api/admin_api_controller.ex:1135
|
||||||
|
#: lib/pleroma/web/feed/user_controller.ex:73 lib/pleroma/web/ostatus/ostatus_controller.ex:143
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:241
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Poll's author can't vote"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:20
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:37 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:49
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:50 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:290
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:71
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Record not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:1153
|
||||||
|
#: lib/pleroma/web/feed/user_controller.ex:79 lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:32
|
||||||
|
#: lib/pleroma/web/ostatus/ostatus_controller.ex:149
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Something went wrong"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/activity_draft.ex:107
|
||||||
|
#, elixir-format
|
||||||
|
msgid "The message visibility must be direct"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/utils.ex:566
|
||||||
|
#, elixir-format
|
||||||
|
msgid "The status is over the character limit"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/ensure_public_or_authenticated_plug.ex:31
|
||||||
|
#, elixir-format
|
||||||
|
msgid "This resource requires authentication."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/rate_limiter/rate_limiter.ex:206
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Throttled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:266
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Too many choices"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:442
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unhandled activity type"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:536
|
||||||
|
#, elixir-format
|
||||||
|
msgid "You can't revoke your own admin status."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:218
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:309
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Your account is currently disabled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:180
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:332
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Your login is missing a confirmed e-mail address"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:389
|
||||||
|
#, elixir-format
|
||||||
|
msgid "can't read inbox of %{nickname} as %{as_nickname}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:472
|
||||||
|
#, elixir-format
|
||||||
|
msgid "can't update outbox of %{nickname} as %{as_nickname}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:388
|
||||||
|
#, elixir-format
|
||||||
|
msgid "conversation is already muted"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:316
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:491
|
||||||
|
#, elixir-format
|
||||||
|
msgid "error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:29
|
||||||
|
#, elixir-format
|
||||||
|
msgid "mascots can only be images"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:60
|
||||||
|
#, elixir-format
|
||||||
|
msgid "not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:395
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Bad OAuth request."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:115
|
||||||
|
#, elixir-format
|
||||||
|
msgid "CAPTCHA already used"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:112
|
||||||
|
#, elixir-format
|
||||||
|
msgid "CAPTCHA expired"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/uploaded_media.ex:55
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Failed"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:411
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Failed to authenticate: %{message}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:442
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Failed to set up user account."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/oauth_scopes_plug.ex:38
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Insufficient permissions: %{permissions}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/uploaded_media.ex:94
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Internal Error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/fallback_controller.ex:22
|
||||||
|
#: lib/pleroma/web/oauth/fallback_controller.ex:29
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid Username/Password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:118
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid answer data"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:128
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Nodeinfo schema version not handled"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:169
|
||||||
|
#, elixir-format
|
||||||
|
msgid "This action is outside the authorized scopes"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/fallback_controller.ex:14
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unknown error, please check the details and try again."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:116
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:155
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unlisted redirect_uri."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:391
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unsupported OAuth provider: %{provider}."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/uploaders/uploader.ex:72
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Uploader callback timeout"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/uploader_controller.ex:23
|
||||||
|
#, elixir-format
|
||||||
|
msgid "bad request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:103
|
||||||
|
#, elixir-format
|
||||||
|
msgid "CAPTCHA Error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:200
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not add reaction emoji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/common_api/common_api.ex:211
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Could not remove reaction emoji"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/twitter_api/twitter_api.ex:129
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Invalid CAPTCHA (Missing parameter: %{name})"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:92
|
||||||
|
#, elixir-format
|
||||||
|
msgid "List not found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:124
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Missing parameter: %{name}"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:207
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:322
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Password reset is required"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/tests/auth_test_controller.ex:9
|
||||||
|
#: lib/pleroma/web/activity_pub/activity_pub_controller.ex:6 lib/pleroma/web/admin_api/admin_api_controller.ex:6
|
||||||
|
#: lib/pleroma/web/controller_helper.ex:6 lib/pleroma/web/fallback_redirect_controller.ex:6
|
||||||
|
#: lib/pleroma/web/feed/tag_controller.ex:6 lib/pleroma/web/feed/user_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mailer/subscription_controller.ex:2 lib/pleroma/web/masto_fe_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/account_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/app_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/auth_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/conversation_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/custom_emoji_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/domain_block_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/fallback_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/filter_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/follow_request_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/instance_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/list_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/marker_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/mastodon_api_controller.ex:14 lib/pleroma/web/mastodon_api/controllers/media_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/notification_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/poll_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/report_controller.ex:8 lib/pleroma/web/mastodon_api/controllers/scheduled_activity_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/search_controller.ex:6 lib/pleroma/web/mastodon_api/controllers/status_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:7 lib/pleroma/web/mastodon_api/controllers/suggestion_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:6 lib/pleroma/web/media_proxy/media_proxy_controller.ex:6
|
||||||
|
#: lib/pleroma/web/mongooseim/mongoose_im_controller.ex:6 lib/pleroma/web/nodeinfo/nodeinfo_controller.ex:6
|
||||||
|
#: lib/pleroma/web/oauth/fallback_controller.ex:6 lib/pleroma/web/oauth/mfa_controller.ex:10
|
||||||
|
#: lib/pleroma/web/oauth/oauth_controller.ex:6 lib/pleroma/web/ostatus/ostatus_controller.ex:6
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/account_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:2
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/mascot_controller.ex:6 lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex:6
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/scrobble_controller.ex:6
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/two_factor_authentication_controller.ex:7 lib/pleroma/web/static_fe/static_fe_controller.ex:6
|
||||||
|
#: lib/pleroma/web/twitter_api/controllers/password_controller.ex:10 lib/pleroma/web/twitter_api/controllers/remote_follow_controller.ex:6
|
||||||
|
#: lib/pleroma/web/twitter_api/controllers/util_controller.ex:6 lib/pleroma/web/twitter_api/twitter_api_controller.ex:6
|
||||||
|
#: lib/pleroma/web/uploader_controller.ex:6 lib/pleroma/web/web_finger/web_finger_controller.ex:6
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Security violation: OAuth scopes check was neither handled nor explicitly skipped."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/ensure_authenticated_plug.ex:28
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Two-factor authentication enabled, you must use a access token."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:210
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while adding file to pack."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:138
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while creating pack."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:278
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while removing file from pack."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:250
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while updating file in pack."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex:179
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Unexpected error occurred while updating pack metadata."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/plugs/user_is_admin_plug.ex:40
|
||||||
|
#, elixir-format
|
||||||
|
msgid "User is not an admin or OAuth admin scope is not granted."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/subscription_controller.ex:61
|
||||||
|
#, elixir-format
|
||||||
|
msgid "Web push subscription is disabled on this Pleroma instance"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/admin_api/admin_api_controller.ex:502
|
||||||
|
#, elixir-format
|
||||||
|
msgid "You can't revoke your own admin/moderator status."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex:105
|
||||||
|
#, elixir-format
|
||||||
|
msgid "authorization required for timeline view"
|
||||||
|
msgstr ""
|
BIN
test/instance_static/emoji/test_pack/blank2.png
Normal file
BIN
test/instance_static/emoji/test_pack/blank2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 95 B |
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"blank": "blank.png"
|
"blank": "blank.png",
|
||||||
|
"blank2": "blank2.png"
|
||||||
},
|
},
|
||||||
"pack": {
|
"pack": {
|
||||||
"description": "Test description",
|
"description": "Test description",
|
||||||
|
|
Binary file not shown.
|
@ -4,7 +4,7 @@
|
||||||
"homepage": "https://pleroma.social",
|
"homepage": "https://pleroma.social",
|
||||||
"description": "Test description",
|
"description": "Test description",
|
||||||
"fallback-src": "https://nonshared-pack",
|
"fallback-src": "https://nonshared-pack",
|
||||||
"fallback-src-sha256": "74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF",
|
"fallback-src-sha256": "1967BB4E42BCC34BCC12D57BE7811D3B7BE52F965BCE45C87BD377B9499CE11D",
|
||||||
"share-files": false
|
"share-files": false
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
|
|
|
@ -1342,11 +1342,11 @@ test "returns false for a non-invisible user" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "visible_for?/2" do
|
describe "visible_for/2" do
|
||||||
test "returns true when the account is itself" do
|
test "returns true when the account is itself" do
|
||||||
user = insert(:user, local: true)
|
user = insert(:user, local: true)
|
||||||
|
|
||||||
assert User.visible_for?(user, user)
|
assert User.visible_for(user, user) == :visible
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns false when the account is unauthenticated and auth is required" do
|
test "returns false when the account is unauthenticated and auth is required" do
|
||||||
|
@ -1355,14 +1355,14 @@ test "returns false when the account is unauthenticated and auth is required" do
|
||||||
user = insert(:user, local: true, confirmation_pending: true)
|
user = insert(:user, local: true, confirmation_pending: true)
|
||||||
other_user = insert(:user, local: true)
|
other_user = insert(:user, local: true)
|
||||||
|
|
||||||
refute User.visible_for?(user, other_user)
|
refute User.visible_for(user, other_user) == :visible
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns true when the account is unauthenticated and auth is not required" do
|
test "returns true when the account is unauthenticated and auth is not required" do
|
||||||
user = insert(:user, local: true, confirmation_pending: true)
|
user = insert(:user, local: true, confirmation_pending: true)
|
||||||
other_user = insert(:user, local: true)
|
other_user = insert(:user, local: true)
|
||||||
|
|
||||||
assert User.visible_for?(user, other_user)
|
assert User.visible_for(user, other_user) == :visible
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns true when the account is unauthenticated and being viewed by a privileged account (auth required)" do
|
test "returns true when the account is unauthenticated and being viewed by a privileged account (auth required)" do
|
||||||
|
@ -1371,7 +1371,7 @@ test "returns true when the account is unauthenticated and being viewed by a pri
|
||||||
user = insert(:user, local: true, confirmation_pending: true)
|
user = insert(:user, local: true, confirmation_pending: true)
|
||||||
other_user = insert(:user, local: true, is_admin: true)
|
other_user = insert(:user, local: true, is_admin: true)
|
||||||
|
|
||||||
assert User.visible_for?(user, other_user)
|
assert User.visible_for(user, other_user) == :visible
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do
|
||||||
|
|
||||||
import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy
|
import Pleroma.Web.ActivityPub.MRF.HellthreadPolicy
|
||||||
|
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
|
|
||||||
|
@ -20,7 +22,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do
|
||||||
"https://instance.tld/users/user1",
|
"https://instance.tld/users/user1",
|
||||||
"https://instance.tld/users/user2",
|
"https://instance.tld/users/user2",
|
||||||
"https://instance.tld/users/user3"
|
"https://instance.tld/users/user3"
|
||||||
]
|
],
|
||||||
|
"object" => %{
|
||||||
|
"type" => "Note"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[user: user, message: message]
|
[user: user, message: message]
|
||||||
|
@ -28,6 +33,17 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicyTest do
|
||||||
|
|
||||||
setup do: clear_config(:mrf_hellthread)
|
setup do: clear_config(:mrf_hellthread)
|
||||||
|
|
||||||
|
test "doesn't die on chat messages" do
|
||||||
|
Pleroma.Config.put([:mrf_hellthread], %{delist_threshold: 2, reject_threshold: 0})
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post_chat_message(user, other_user, "moin")
|
||||||
|
|
||||||
|
assert {:ok, _} = filter(activity.data)
|
||||||
|
end
|
||||||
|
|
||||||
describe "reject" do
|
describe "reject" do
|
||||||
test "rejects the message if the recipient count is above reject_threshold", %{
|
test "rejects the message if the recipient count is above reject_threshold", %{
|
||||||
message: message
|
message: message
|
||||||
|
|
|
@ -1415,9 +1415,6 @@ test "returns modified object when allowed incoming reply", %{data: data} do
|
||||||
|
|
||||||
assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
|
assert modified_object["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873"
|
||||||
|
|
||||||
assert modified_object["conversation"] ==
|
|
||||||
"tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
|
|
||||||
|
|
||||||
assert modified_object["context"] ==
|
assert modified_object["context"] ==
|
||||||
"tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
|
"tag:shitposter.club,2017-05-05:objectType=thread:nonce=3c16e9c2681f6d26"
|
||||||
end
|
end
|
||||||
|
|
|
@ -1599,14 +1599,14 @@ test "changes actor type from permitted list", %{conn: conn, user: user} do
|
||||||
assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
|
assert patch(conn, "/api/pleroma/admin/users/#{user.nickname}/credentials", %{
|
||||||
"actor_type" => "Application"
|
"actor_type" => "Application"
|
||||||
})
|
})
|
||||||
|> json_response(200) == %{"errors" => %{"actor_type" => "is invalid"}}
|
|> json_response(400) == %{"errors" => %{"actor_type" => "is invalid"}}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "update non existing user", %{conn: conn} do
|
test "update non existing user", %{conn: conn} do
|
||||||
assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
|
assert patch(conn, "/api/pleroma/admin/users/non-existing/credentials", %{
|
||||||
"password" => "new_password"
|
"password" => "new_password"
|
||||||
})
|
})
|
||||||
|> json_response(200) == %{"error" => "Unable to update user."}
|
|> json_response(404) == %{"error" => "Not found"}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
# 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.MediaProxyCacheControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
import Mock
|
||||||
|
|
||||||
|
alias Pleroma.Web.MediaProxy
|
||||||
|
|
||||||
|
setup do: clear_config([:media_proxy])
|
||||||
|
|
||||||
|
setup do
|
||||||
|
on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
|
||||||
|
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)
|
||||||
|
|
||||||
|
Config.put([:media_proxy, :enabled], true)
|
||||||
|
Config.put([:media_proxy, :invalidation, :enabled], true)
|
||||||
|
Config.put([:media_proxy, :invalidation, :provider], MediaProxy.Invalidation.Script)
|
||||||
|
|
||||||
|
{:ok, %{admin: admin, token: token, conn: conn}}
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/media_proxy_caches" do
|
||||||
|
test "shows banned MediaProxy URLs", %{conn: conn} do
|
||||||
|
MediaProxy.put_in_banned_urls([
|
||||||
|
"http://localhost:4001/media/a688346.jpg",
|
||||||
|
"http://localhost:4001/media/fb1f4d.jpg"
|
||||||
|
])
|
||||||
|
|
||||||
|
MediaProxy.put_in_banned_urls("http://localhost:4001/media/gb1f44.jpg")
|
||||||
|
MediaProxy.put_in_banned_urls("http://localhost:4001/media/tb13f47.jpg")
|
||||||
|
MediaProxy.put_in_banned_urls("http://localhost:4001/media/wb1f46.jpg")
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/media_proxy_caches?page_size=2")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response["urls"] == [
|
||||||
|
"http://localhost:4001/media/fb1f4d.jpg",
|
||||||
|
"http://localhost:4001/media/a688346.jpg"
|
||||||
|
]
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=2")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response["urls"] == [
|
||||||
|
"http://localhost:4001/media/gb1f44.jpg",
|
||||||
|
"http://localhost:4001/media/tb13f47.jpg"
|
||||||
|
]
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=3")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response["urls"] == ["http://localhost:4001/media/wb1f46.jpg"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/media_proxy_caches/delete" do
|
||||||
|
test "deleted MediaProxy URLs from banned", %{conn: conn} do
|
||||||
|
MediaProxy.put_in_banned_urls([
|
||||||
|
"http://localhost:4001/media/a688346.jpg",
|
||||||
|
"http://localhost:4001/media/fb1f4d.jpg"
|
||||||
|
])
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/media_proxy_caches/delete", %{
|
||||||
|
urls: ["http://localhost:4001/media/a688346.jpg"]
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response["urls"] == ["http://localhost:4001/media/a688346.jpg"]
|
||||||
|
refute MediaProxy.in_banned_urls("http://localhost:4001/media/a688346.jpg")
|
||||||
|
assert MediaProxy.in_banned_urls("http://localhost:4001/media/fb1f4d.jpg")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/media_proxy_caches/purge" do
|
||||||
|
test "perform invalidates cache of MediaProxy", %{conn: conn} do
|
||||||
|
urls = [
|
||||||
|
"http://example.com/media/a688346.jpg",
|
||||||
|
"http://example.com/media/fb1f4d.jpg"
|
||||||
|
]
|
||||||
|
|
||||||
|
with_mocks [
|
||||||
|
{MediaProxy.Invalidation.Script, [],
|
||||||
|
[
|
||||||
|
purge: fn _, _ -> {"ok", 0} end
|
||||||
|
]}
|
||||||
|
] do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/media_proxy_caches/purge", %{urls: urls, ban: false})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response["urls"] == urls
|
||||||
|
|
||||||
|
refute MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg")
|
||||||
|
refute MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "perform invalidates cache of MediaProxy and adds url to banned", %{conn: conn} do
|
||||||
|
urls = [
|
||||||
|
"http://example.com/media/a688346.jpg",
|
||||||
|
"http://example.com/media/fb1f4d.jpg"
|
||||||
|
]
|
||||||
|
|
||||||
|
with_mocks [{MediaProxy.Invalidation.Script, [], [purge: fn _, _ -> {"ok", 0} end]}] do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> put_req_header("content-type", "application/json")
|
||||||
|
|> post("/api/pleroma/admin/media_proxy_caches/purge", %{
|
||||||
|
urls: urls,
|
||||||
|
ban: true
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert response["urls"] == urls
|
||||||
|
|
||||||
|
assert MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg")
|
||||||
|
assert MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -400,4 +400,71 @@ test "update fields when invalid request", %{conn: conn} do
|
||||||
|> json_response_and_validate_schema(403)
|
|> json_response_and_validate_schema(403)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "Mark account as bot" do
|
||||||
|
setup do: oauth_access(["write:accounts"])
|
||||||
|
setup :request_content_type
|
||||||
|
|
||||||
|
test "changing actor_type to Service makes account a bot", %{conn: conn} do
|
||||||
|
account =
|
||||||
|
conn
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{actor_type: "Service"})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert account["bot"]
|
||||||
|
assert account["source"]["pleroma"]["actor_type"] == "Service"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "changing actor_type to Person makes account a human", %{conn: conn} do
|
||||||
|
account =
|
||||||
|
conn
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{actor_type: "Person"})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
refute account["bot"]
|
||||||
|
assert account["source"]["pleroma"]["actor_type"] == "Person"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "changing actor_type to Application causes error", %{conn: conn} do
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{actor_type: "Application"})
|
||||||
|
|> json_response_and_validate_schema(403)
|
||||||
|
|
||||||
|
assert %{"error" => "Invalid request"} == response
|
||||||
|
end
|
||||||
|
|
||||||
|
test "changing bot field to true changes actor_type to Service", %{conn: conn} do
|
||||||
|
account =
|
||||||
|
conn
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{bot: "true"})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert account["bot"]
|
||||||
|
assert account["source"]["pleroma"]["actor_type"] == "Service"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "changing bot field to false changes actor_type to Person", %{conn: conn} do
|
||||||
|
account =
|
||||||
|
conn
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{bot: "false"})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
refute account["bot"]
|
||||||
|
assert account["source"]["pleroma"]["actor_type"] == "Person"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "actor_type field has a higher priority than bot", %{conn: conn} do
|
||||||
|
account =
|
||||||
|
conn
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
actor_type: "Person",
|
||||||
|
bot: "true"
|
||||||
|
})
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
refute account["bot"]
|
||||||
|
assert account["source"]["pleroma"]["actor_type"] == "Person"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -127,6 +127,15 @@ test "returns 404 for internal.fetch actor", %{conn: conn} do
|
||||||
|> get("/api/v1/accounts/internal.fetch")
|
|> get("/api/v1/accounts/internal.fetch")
|
||||||
|> json_response_and_validate_schema(404)
|
|> json_response_and_validate_schema(404)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns 404 for deactivated user", %{conn: conn} do
|
||||||
|
user = insert(:user, deactivated: true)
|
||||||
|
|
||||||
|
assert %{"error" => "Can't find user"} =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.id}")
|
||||||
|
|> json_response_and_validate_schema(:not_found)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp local_and_remote_users do
|
defp local_and_remote_users do
|
||||||
|
@ -143,15 +152,15 @@ defp local_and_remote_users do
|
||||||
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
|
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
|
||||||
|
|
||||||
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
|
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
|
||||||
assert %{"error" => "Can't find user"} ==
|
assert %{"error" => "This API requires an authenticated user"} ==
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/accounts/#{local.id}")
|
|> get("/api/v1/accounts/#{local.id}")
|
||||||
|> json_response_and_validate_schema(:not_found)
|
|> json_response_and_validate_schema(:unauthorized)
|
||||||
|
|
||||||
assert %{"error" => "Can't find user"} ==
|
assert %{"error" => "This API requires an authenticated user"} ==
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/accounts/#{remote.id}")
|
|> get("/api/v1/accounts/#{remote.id}")
|
||||||
|> json_response_and_validate_schema(:not_found)
|
|> json_response_and_validate_schema(:unauthorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "if user is authenticated", %{local: local, remote: remote} do
|
test "if user is authenticated", %{local: local, remote: remote} do
|
||||||
|
@ -173,8 +182,8 @@ test "if user is authenticated", %{local: local, remote: remote} do
|
||||||
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
|
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
|
||||||
res_conn = get(conn, "/api/v1/accounts/#{local.id}")
|
res_conn = get(conn, "/api/v1/accounts/#{local.id}")
|
||||||
|
|
||||||
assert json_response_and_validate_schema(res_conn, :not_found) == %{
|
assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
|
||||||
"error" => "Can't find user"
|
"error" => "This API requires an authenticated user"
|
||||||
}
|
}
|
||||||
|
|
||||||
res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
|
res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
|
||||||
|
@ -203,8 +212,8 @@ test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} d
|
||||||
|
|
||||||
res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
|
res_conn = get(conn, "/api/v1/accounts/#{remote.id}")
|
||||||
|
|
||||||
assert json_response_and_validate_schema(res_conn, :not_found) == %{
|
assert json_response_and_validate_schema(res_conn, :unauthorized) == %{
|
||||||
"error" => "Can't find user"
|
"error" => "This API requires an authenticated user"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -249,6 +258,24 @@ test "works with announces that are just addressed to public", %{conn: conn} do
|
||||||
assert id == announce.id
|
assert id == announce.id
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "deactivated user", %{conn: conn} do
|
||||||
|
user = insert(:user, deactivated: true)
|
||||||
|
|
||||||
|
assert %{"error" => "Can't find user"} ==
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.id}/statuses")
|
||||||
|
|> json_response_and_validate_schema(:not_found)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "returns 404 when user is invisible", %{conn: conn} do
|
||||||
|
user = insert(:user, %{invisible: true})
|
||||||
|
|
||||||
|
assert %{"error" => "Can't find user"} =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.id}")
|
||||||
|
|> json_response_and_validate_schema(404)
|
||||||
|
end
|
||||||
|
|
||||||
test "respects blocks", %{user: user_one, conn: conn} do
|
test "respects blocks", %{user: user_one, conn: conn} do
|
||||||
user_two = insert(:user)
|
user_two = insert(:user)
|
||||||
user_three = insert(:user)
|
user_three = insert(:user)
|
||||||
|
@ -350,9 +377,10 @@ test "unimplemented pinned statuses feature", %{conn: conn} do
|
||||||
assert json_response_and_validate_schema(conn, 200) == []
|
assert json_response_and_validate_schema(conn, 200) == []
|
||||||
end
|
end
|
||||||
|
|
||||||
test "gets an users media", %{conn: conn} do
|
test "gets an users media, excludes reblogs", %{conn: conn} do
|
||||||
note = insert(:note_activity)
|
note = insert(:note_activity)
|
||||||
user = User.get_cached_by_ap_id(note.data["actor"])
|
user = User.get_cached_by_ap_id(note.data["actor"])
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
file = %Plug.Upload{
|
file = %Plug.Upload{
|
||||||
content_type: "image/jpg",
|
content_type: "image/jpg",
|
||||||
|
@ -364,6 +392,13 @@ test "gets an users media", %{conn: conn} do
|
||||||
|
|
||||||
{:ok, %{id: image_post_id}} = CommonAPI.post(user, %{status: "cofe", media_ids: [media_id]})
|
{:ok, %{id: image_post_id}} = CommonAPI.post(user, %{status: "cofe", media_ids: [media_id]})
|
||||||
|
|
||||||
|
{:ok, %{id: media_id}} = ActivityPub.upload(file, actor: other_user.ap_id)
|
||||||
|
|
||||||
|
{:ok, %{id: other_image_post_id}} =
|
||||||
|
CommonAPI.post(other_user, %{status: "cofe2", media_ids: [media_id]})
|
||||||
|
|
||||||
|
{:ok, _announce} = CommonAPI.repeat(other_image_post_id, user)
|
||||||
|
|
||||||
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")
|
conn = get(conn, "/api/v1/accounts/#{user.id}/statuses?only_media=true")
|
||||||
|
|
||||||
assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
|
assert [%{"id" => ^image_post_id}] = json_response_and_validate_schema(conn, 200)
|
||||||
|
@ -422,15 +457,15 @@ defp local_and_remote_activities(%{local: local, remote: remote}) do
|
||||||
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
|
setup do: clear_config([:restrict_unauthenticated, :profiles, :remote], true)
|
||||||
|
|
||||||
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
|
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
|
||||||
assert %{"error" => "Can't find user"} ==
|
assert %{"error" => "This API requires an authenticated user"} ==
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/accounts/#{local.id}/statuses")
|
|> get("/api/v1/accounts/#{local.id}/statuses")
|
||||||
|> json_response_and_validate_schema(:not_found)
|
|> json_response_and_validate_schema(:unauthorized)
|
||||||
|
|
||||||
assert %{"error" => "Can't find user"} ==
|
assert %{"error" => "This API requires an authenticated user"} ==
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/accounts/#{remote.id}/statuses")
|
|> get("/api/v1/accounts/#{remote.id}/statuses")
|
||||||
|> json_response_and_validate_schema(:not_found)
|
|> json_response_and_validate_schema(:unauthorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "if user is authenticated", %{local: local, remote: remote} do
|
test "if user is authenticated", %{local: local, remote: remote} do
|
||||||
|
@ -451,10 +486,10 @@ test "if user is authenticated", %{local: local, remote: remote} do
|
||||||
setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
|
setup do: clear_config([:restrict_unauthenticated, :profiles, :local], true)
|
||||||
|
|
||||||
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
|
test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} do
|
||||||
assert %{"error" => "Can't find user"} ==
|
assert %{"error" => "This API requires an authenticated user"} ==
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/accounts/#{local.id}/statuses")
|
|> get("/api/v1/accounts/#{local.id}/statuses")
|
||||||
|> json_response_and_validate_schema(:not_found)
|
|> json_response_and_validate_schema(:unauthorized)
|
||||||
|
|
||||||
res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
|
res_conn = get(conn, "/api/v1/accounts/#{remote.id}/statuses")
|
||||||
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
|
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
|
||||||
|
@ -481,10 +516,10 @@ test "if user is unauthenticated", %{conn: conn, local: local, remote: remote} d
|
||||||
res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
|
res_conn = get(conn, "/api/v1/accounts/#{local.id}/statuses")
|
||||||
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
|
assert length(json_response_and_validate_schema(res_conn, 200)) == 1
|
||||||
|
|
||||||
assert %{"error" => "Can't find user"} ==
|
assert %{"error" => "This API requires an authenticated user"} ==
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/accounts/#{remote.id}/statuses")
|
|> get("/api/v1/accounts/#{remote.id}/statuses")
|
||||||
|> json_response_and_validate_schema(:not_found)
|
|> json_response_and_validate_schema(:unauthorized)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "if user is authenticated", %{local: local, remote: remote} do
|
test "if user is authenticated", %{local: local, remote: remote} do
|
||||||
|
|
|
@ -151,6 +151,22 @@ test "constructs hashtags from search query", %{conn: conn} do
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "supports pagination of hashtags search results", %{conn: conn} do
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> get(
|
||||||
|
"/api/v2/search?#{
|
||||||
|
URI.encode_query(%{q: "#some #text #with #hashtags", limit: 2, offset: 1})
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert results["hashtags"] == [
|
||||||
|
%{"name" => "text", "url" => "#{Web.base_url()}/tag/text"},
|
||||||
|
%{"name" => "with", "url" => "#{Web.base_url()}/tag/with"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
test "excludes a blocked users from search results", %{conn: conn} do
|
test "excludes a blocked users from search results", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"})
|
user_smith = insert(:user, %{nickname: "Agent", name: "I love 2hu"})
|
||||||
|
|
|
@ -1561,7 +1561,7 @@ test "favorites paginate correctly" do
|
||||||
|
|
||||||
# Using the header for pagination works correctly
|
# Using the header for pagination works correctly
|
||||||
[next, _] = get_resp_header(result, "link") |> hd() |> String.split(", ")
|
[next, _] = get_resp_header(result, "link") |> hd() |> String.split(", ")
|
||||||
[_, max_id] = Regex.run(~r/max_id=(.*)>;/, next)
|
[_, max_id] = Regex.run(~r/max_id=([^&]+)/, next)
|
||||||
|
|
||||||
assert max_id == third_favorite.id
|
assert max_id == third_favorite.id
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ test "ChatMessage notification" do
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
pleroma: %{is_seen: false},
|
pleroma: %{is_seen: false, is_muted: false},
|
||||||
type: "pleroma:chat_mention",
|
type: "pleroma:chat_mention",
|
||||||
account: AccountView.render("show.json", %{user: user, for: recipient}),
|
account: AccountView.render("show.json", %{user: user, for: recipient}),
|
||||||
chat_message: MessageReferenceView.render("show.json", %{chat_message_reference: cm_ref}),
|
chat_message: MessageReferenceView.render("show.json", %{chat_message_reference: cm_ref}),
|
||||||
|
@ -68,7 +68,7 @@ test "Mention notification" do
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
pleroma: %{is_seen: false},
|
pleroma: %{is_seen: false, is_muted: false},
|
||||||
type: "mention",
|
type: "mention",
|
||||||
account:
|
account:
|
||||||
AccountView.render("show.json", %{
|
AccountView.render("show.json", %{
|
||||||
|
@ -92,7 +92,7 @@ test "Favourite notification" do
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
pleroma: %{is_seen: false},
|
pleroma: %{is_seen: false, is_muted: false},
|
||||||
type: "favourite",
|
type: "favourite",
|
||||||
account: AccountView.render("show.json", %{user: another_user, for: user}),
|
account: AccountView.render("show.json", %{user: another_user, for: user}),
|
||||||
status: StatusView.render("show.json", %{activity: create_activity, for: user}),
|
status: StatusView.render("show.json", %{activity: create_activity, for: user}),
|
||||||
|
@ -112,7 +112,7 @@ test "Reblog notification" do
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
pleroma: %{is_seen: false},
|
pleroma: %{is_seen: false, is_muted: false},
|
||||||
type: "reblog",
|
type: "reblog",
|
||||||
account: AccountView.render("show.json", %{user: another_user, for: user}),
|
account: AccountView.render("show.json", %{user: another_user, for: user}),
|
||||||
status: StatusView.render("show.json", %{activity: reblog_activity, for: user}),
|
status: StatusView.render("show.json", %{activity: reblog_activity, for: user}),
|
||||||
|
@ -130,7 +130,7 @@ test "Follow notification" do
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
pleroma: %{is_seen: false},
|
pleroma: %{is_seen: false, is_muted: false},
|
||||||
type: "follow",
|
type: "follow",
|
||||||
account: AccountView.render("show.json", %{user: follower, for: followed}),
|
account: AccountView.render("show.json", %{user: follower, for: followed}),
|
||||||
created_at: Utils.to_masto_date(notification.inserted_at)
|
created_at: Utils.to_masto_date(notification.inserted_at)
|
||||||
|
@ -171,7 +171,7 @@ test "Move notification" do
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
pleroma: %{is_seen: false},
|
pleroma: %{is_seen: false, is_muted: false},
|
||||||
type: "move",
|
type: "move",
|
||||||
account: AccountView.render("show.json", %{user: old_user, for: follower}),
|
account: AccountView.render("show.json", %{user: old_user, for: follower}),
|
||||||
target: AccountView.render("show.json", %{user: new_user, for: follower}),
|
target: AccountView.render("show.json", %{user: new_user, for: follower}),
|
||||||
|
@ -196,7 +196,7 @@ test "EmojiReact notification" do
|
||||||
|
|
||||||
expected = %{
|
expected = %{
|
||||||
id: to_string(notification.id),
|
id: to_string(notification.id),
|
||||||
pleroma: %{is_seen: false},
|
pleroma: %{is_seen: false, is_muted: false},
|
||||||
type: "pleroma:emoji_reaction",
|
type: "pleroma:emoji_reaction",
|
||||||
emoji: "☕",
|
emoji: "☕",
|
||||||
account: AccountView.render("show.json", %{user: other_user, for: user}),
|
account: AccountView.render("show.json", %{user: other_user, for: user}),
|
||||||
|
@ -206,4 +206,26 @@ test "EmojiReact notification" do
|
||||||
|
|
||||||
test_notifications_rendering([notification], user, [expected])
|
test_notifications_rendering([notification], user, [expected])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "muted notification" do
|
||||||
|
user = insert(:user)
|
||||||
|
another_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _} = Pleroma.UserRelationship.create_mute(user, another_user)
|
||||||
|
{:ok, create_activity} = CommonAPI.post(user, %{status: "hey"})
|
||||||
|
{:ok, favorite_activity} = CommonAPI.favorite(another_user, create_activity.id)
|
||||||
|
{:ok, [notification]} = Notification.create_notifications(favorite_activity)
|
||||||
|
create_activity = Activity.get_by_id(create_activity.id)
|
||||||
|
|
||||||
|
expected = %{
|
||||||
|
id: to_string(notification.id),
|
||||||
|
pleroma: %{is_seen: false, is_muted: true},
|
||||||
|
type: "favourite",
|
||||||
|
account: AccountView.render("show.json", %{user: another_user, for: user}),
|
||||||
|
status: StatusView.render("show.json", %{activity: create_activity, for: user}),
|
||||||
|
created_at: Utils.to_masto_date(notification.inserted_at)
|
||||||
|
}
|
||||||
|
|
||||||
|
test_notifications_rendering([notification], user, [expected])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
64
test/web/media_proxy/invalidation_test.exs
Normal file
64
test/web/media_proxy/invalidation_test.exs
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
defmodule Pleroma.Web.MediaProxy.InvalidationTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
use Pleroma.Tests.Helpers
|
||||||
|
|
||||||
|
alias Pleroma.Config
|
||||||
|
alias Pleroma.Web.MediaProxy.Invalidation
|
||||||
|
|
||||||
|
import ExUnit.CaptureLog
|
||||||
|
import Mock
|
||||||
|
import Tesla.Mock
|
||||||
|
|
||||||
|
setup do: clear_config([:media_proxy])
|
||||||
|
|
||||||
|
setup do
|
||||||
|
on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Invalidation.Http" do
|
||||||
|
test "perform request to clear cache" do
|
||||||
|
Config.put([:media_proxy, :enabled], false)
|
||||||
|
Config.put([:media_proxy, :invalidation, :enabled], true)
|
||||||
|
Config.put([:media_proxy, :invalidation, :provider], Invalidation.Http)
|
||||||
|
|
||||||
|
Config.put([Invalidation.Http], method: :purge, headers: [{"x-refresh", 1}])
|
||||||
|
image_url = "http://example.com/media/example.jpg"
|
||||||
|
Pleroma.Web.MediaProxy.put_in_banned_urls(image_url)
|
||||||
|
|
||||||
|
mock(fn
|
||||||
|
%{
|
||||||
|
method: :purge,
|
||||||
|
url: "http://example.com/media/example.jpg",
|
||||||
|
headers: [{"x-refresh", 1}]
|
||||||
|
} ->
|
||||||
|
%Tesla.Env{status: 200}
|
||||||
|
end)
|
||||||
|
|
||||||
|
assert capture_log(fn ->
|
||||||
|
assert Pleroma.Web.MediaProxy.in_banned_urls(image_url)
|
||||||
|
assert Invalidation.purge([image_url]) == {:ok, [image_url]}
|
||||||
|
assert Pleroma.Web.MediaProxy.in_banned_urls(image_url)
|
||||||
|
end) =~ "Running cache purge: [\"#{image_url}\"]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "Invalidation.Script" do
|
||||||
|
test "run script to clear cache" do
|
||||||
|
Config.put([:media_proxy, :enabled], false)
|
||||||
|
Config.put([:media_proxy, :invalidation, :enabled], true)
|
||||||
|
Config.put([:media_proxy, :invalidation, :provider], Invalidation.Script)
|
||||||
|
Config.put([Invalidation.Script], script_path: "purge-nginx")
|
||||||
|
|
||||||
|
image_url = "http://example.com/media/example.jpg"
|
||||||
|
Pleroma.Web.MediaProxy.put_in_banned_urls(image_url)
|
||||||
|
|
||||||
|
with_mocks [{System, [], [cmd: fn _, _ -> {"ok", 0} end]}] do
|
||||||
|
assert capture_log(fn ->
|
||||||
|
assert Pleroma.Web.MediaProxy.in_banned_urls(image_url)
|
||||||
|
assert Invalidation.purge([image_url]) == {:ok, [image_url]}
|
||||||
|
assert Pleroma.Web.MediaProxy.in_banned_urls(image_url)
|
||||||
|
end) =~ "Running cache purge: [\"#{image_url}\"]"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,6 +5,10 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.HttpTest do
|
||||||
import ExUnit.CaptureLog
|
import ExUnit.CaptureLog
|
||||||
import Tesla.Mock
|
import Tesla.Mock
|
||||||
|
|
||||||
|
setup do
|
||||||
|
on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
|
||||||
|
end
|
||||||
|
|
||||||
test "logs hasn't error message when request is valid" do
|
test "logs hasn't error message when request is valid" do
|
||||||
mock(fn
|
mock(fn
|
||||||
%{method: :purge, url: "http://example.com/media/example.jpg"} ->
|
%{method: :purge, url: "http://example.com/media/example.jpg"} ->
|
||||||
|
@ -14,8 +18,8 @@ test "logs hasn't error message when request is valid" do
|
||||||
refute capture_log(fn ->
|
refute capture_log(fn ->
|
||||||
assert Invalidation.Http.purge(
|
assert Invalidation.Http.purge(
|
||||||
["http://example.com/media/example.jpg"],
|
["http://example.com/media/example.jpg"],
|
||||||
%{}
|
[]
|
||||||
) == {:ok, "success"}
|
) == {:ok, ["http://example.com/media/example.jpg"]}
|
||||||
end) =~ "Error while cache purge"
|
end) =~ "Error while cache purge"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,8 +32,8 @@ test "it write error message in logs when request invalid" do
|
||||||
assert capture_log(fn ->
|
assert capture_log(fn ->
|
||||||
assert Invalidation.Http.purge(
|
assert Invalidation.Http.purge(
|
||||||
["http://example.com/media/example1.jpg"],
|
["http://example.com/media/example1.jpg"],
|
||||||
%{}
|
[]
|
||||||
) == {:ok, "success"}
|
) == {:ok, ["http://example.com/media/example1.jpg"]}
|
||||||
end) =~ "Error while cache purge: url - http://example.com/media/example1.jpg"
|
end) =~ "Error while cache purge: url - http://example.com/media/example1.jpg"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,17 +4,23 @@ defmodule Pleroma.Web.MediaProxy.Invalidation.ScriptTest do
|
||||||
|
|
||||||
import ExUnit.CaptureLog
|
import ExUnit.CaptureLog
|
||||||
|
|
||||||
|
setup do
|
||||||
|
on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
|
||||||
|
end
|
||||||
|
|
||||||
test "it logger error when script not found" do
|
test "it logger error when script not found" do
|
||||||
assert capture_log(fn ->
|
assert capture_log(fn ->
|
||||||
assert Invalidation.Script.purge(
|
assert Invalidation.Script.purge(
|
||||||
["http://example.com/media/example.jpg"],
|
["http://example.com/media/example.jpg"],
|
||||||
%{script_path: "./example"}
|
script_path: "./example"
|
||||||
) == {:error, "\"%ErlangError{original: :enoent}\""}
|
) == {:error, "%ErlangError{original: :enoent}"}
|
||||||
end) =~ "Error while cache purge: \"%ErlangError{original: :enoent}\""
|
end) =~ "Error while cache purge: %ErlangError{original: :enoent}"
|
||||||
|
|
||||||
assert Invalidation.Script.purge(
|
capture_log(fn ->
|
||||||
["http://example.com/media/example.jpg"],
|
assert Invalidation.Script.purge(
|
||||||
%{}
|
["http://example.com/media/example.jpg"],
|
||||||
) == {:error, "not found script path"}
|
[]
|
||||||
|
) == {:error, "\"not found script path\""}
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,10 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyControllerTest do
|
||||||
setup do: clear_config(:media_proxy)
|
setup do: clear_config(:media_proxy)
|
||||||
setup do: clear_config([Pleroma.Web.Endpoint, :secret_key_base])
|
setup do: clear_config([Pleroma.Web.Endpoint, :secret_key_base])
|
||||||
|
|
||||||
|
setup do
|
||||||
|
on_exit(fn -> Cachex.clear(:banned_urls_cache) end)
|
||||||
|
end
|
||||||
|
|
||||||
test "it returns 404 when MediaProxy disabled", %{conn: conn} do
|
test "it returns 404 when MediaProxy disabled", %{conn: conn} do
|
||||||
Config.put([:media_proxy, :enabled], false)
|
Config.put([:media_proxy, :enabled], false)
|
||||||
|
|
||||||
|
@ -66,4 +70,16 @@ test "it performs ReverseProxy.call when signature valid", %{conn: conn} do
|
||||||
assert %Plug.Conn{status: :success} = get(conn, url)
|
assert %Plug.Conn{status: :success} = get(conn, url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it returns 404 when url contains in banned_urls cache", %{conn: conn} do
|
||||||
|
Config.put([:media_proxy, :enabled], true)
|
||||||
|
Config.put([Pleroma.Web.Endpoint, :secret_key_base], "00000000000")
|
||||||
|
url = Pleroma.Web.MediaProxy.encode_url("https://google.fn/test.png")
|
||||||
|
Pleroma.Web.MediaProxy.put_in_banned_urls("https://google.fn/test.png")
|
||||||
|
|
||||||
|
with_mock Pleroma.ReverseProxy,
|
||||||
|
call: fn _conn, _url, _opts -> %Plug.Conn{status: :success} end do
|
||||||
|
assert %Plug.Conn{status: 404, resp_body: "Not Found"} = get(conn, url)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,15 +30,55 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackControllerTest do
|
||||||
test "GET /api/pleroma/emoji/packs", %{conn: conn} do
|
test "GET /api/pleroma/emoji/packs", %{conn: conn} do
|
||||||
resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
|
resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
shared = resp["test_pack"]
|
assert resp["count"] == 3
|
||||||
assert shared["files"] == %{"blank" => "blank.png"}
|
|
||||||
|
assert resp["packs"]
|
||||||
|
|> Map.keys()
|
||||||
|
|> length() == 3
|
||||||
|
|
||||||
|
shared = resp["packs"]["test_pack"]
|
||||||
|
assert shared["files"] == %{"blank" => "blank.png", "blank2" => "blank2.png"}
|
||||||
assert Map.has_key?(shared["pack"], "download-sha256")
|
assert Map.has_key?(shared["pack"], "download-sha256")
|
||||||
assert shared["pack"]["can-download"]
|
assert shared["pack"]["can-download"]
|
||||||
assert shared["pack"]["share-files"]
|
assert shared["pack"]["share-files"]
|
||||||
|
|
||||||
non_shared = resp["test_pack_nonshared"]
|
non_shared = resp["packs"]["test_pack_nonshared"]
|
||||||
assert non_shared["pack"]["share-files"] == false
|
assert non_shared["pack"]["share-files"] == false
|
||||||
assert non_shared["pack"]["can-download"] == false
|
assert non_shared["pack"]["can-download"] == false
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/emoji/packs?page_size=1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert resp["count"] == 3
|
||||||
|
|
||||||
|
packs = Map.keys(resp["packs"])
|
||||||
|
|
||||||
|
assert length(packs) == 1
|
||||||
|
|
||||||
|
[pack1] = packs
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/emoji/packs?page_size=1&page=2")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert resp["count"] == 3
|
||||||
|
packs = Map.keys(resp["packs"])
|
||||||
|
assert length(packs) == 1
|
||||||
|
[pack2] = packs
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/emoji/packs?page_size=1&page=3")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert resp["count"] == 3
|
||||||
|
packs = Map.keys(resp["packs"])
|
||||||
|
assert length(packs) == 1
|
||||||
|
[pack3] = packs
|
||||||
|
assert [pack1, pack2, pack3] |> Enum.uniq() |> length() == 3
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "GET /api/pleroma/emoji/packs/remote" do
|
describe "GET /api/pleroma/emoji/packs/remote" do
|
||||||
|
@ -332,7 +372,7 @@ test "for a pack with a fallback source", ctx do
|
||||||
Map.put(
|
Map.put(
|
||||||
new_data,
|
new_data,
|
||||||
"fallback-src-sha256",
|
"fallback-src-sha256",
|
||||||
"74409E2674DAA06C072729C6C8426C4CB3B7E0B85ED77792DB7A436E11D76DAF"
|
"1967BB4E42BCC34BCC12D57BE7811D3B7BE52F965BCE45C87BD377B9499CE11D"
|
||||||
)
|
)
|
||||||
|
|
||||||
assert ctx[:admin_conn]
|
assert ctx[:admin_conn]
|
||||||
|
@ -398,7 +438,7 @@ test "don't rewrite old emoji", %{admin_conn: admin_conn} do
|
||||||
assert admin_conn
|
assert admin_conn
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{
|
|> post("/api/pleroma/emoji/packs/test_pack/files", %{
|
||||||
shortcode: "blank2",
|
shortcode: "blank3",
|
||||||
filename: "dir/blank.png",
|
filename: "dir/blank.png",
|
||||||
file: %Plug.Upload{
|
file: %Plug.Upload{
|
||||||
filename: "blank.png",
|
filename: "blank.png",
|
||||||
|
@ -407,7 +447,8 @@ test "don't rewrite old emoji", %{admin_conn: admin_conn} do
|
||||||
})
|
})
|
||||||
|> json_response_and_validate_schema(200) == %{
|
|> json_response_and_validate_schema(200) == %{
|
||||||
"blank" => "blank.png",
|
"blank" => "blank.png",
|
||||||
"blank2" => "dir/blank.png"
|
"blank2" => "blank2.png",
|
||||||
|
"blank3" => "dir/blank.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
|
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
|
||||||
|
@ -431,7 +472,7 @@ test "rewrite old emoji with force option", %{admin_conn: admin_conn} do
|
||||||
assert admin_conn
|
assert admin_conn
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|> post("/api/pleroma/emoji/packs/test_pack/files", %{
|
|> post("/api/pleroma/emoji/packs/test_pack/files", %{
|
||||||
shortcode: "blank2",
|
shortcode: "blank3",
|
||||||
filename: "dir/blank.png",
|
filename: "dir/blank.png",
|
||||||
file: %Plug.Upload{
|
file: %Plug.Upload{
|
||||||
filename: "blank.png",
|
filename: "blank.png",
|
||||||
|
@ -440,7 +481,8 @@ test "rewrite old emoji with force option", %{admin_conn: admin_conn} do
|
||||||
})
|
})
|
||||||
|> json_response_and_validate_schema(200) == %{
|
|> json_response_and_validate_schema(200) == %{
|
||||||
"blank" => "blank.png",
|
"blank" => "blank.png",
|
||||||
"blank2" => "dir/blank.png"
|
"blank2" => "blank2.png",
|
||||||
|
"blank3" => "dir/blank.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
|
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
|
||||||
|
@ -448,14 +490,15 @@ test "rewrite old emoji with force option", %{admin_conn: admin_conn} do
|
||||||
assert admin_conn
|
assert admin_conn
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{
|
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{
|
||||||
shortcode: "blank2",
|
shortcode: "blank3",
|
||||||
new_shortcode: "blank3",
|
new_shortcode: "blank4",
|
||||||
new_filename: "dir_2/blank_3.png",
|
new_filename: "dir_2/blank_3.png",
|
||||||
force: true
|
force: true
|
||||||
})
|
})
|
||||||
|> json_response_and_validate_schema(200) == %{
|
|> json_response_and_validate_schema(200) == %{
|
||||||
"blank" => "blank.png",
|
"blank" => "blank.png",
|
||||||
"blank3" => "dir_2/blank_3.png"
|
"blank2" => "blank2.png",
|
||||||
|
"blank4" => "dir_2/blank_3.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png")
|
assert File.exists?("#{@emoji_path}/test_pack/dir_2/blank_3.png")
|
||||||
|
@ -481,7 +524,7 @@ test "add file with not loaded pack", %{admin_conn: admin_conn} do
|
||||||
assert admin_conn
|
assert admin_conn
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|> post("/api/pleroma/emoji/packs/not_loaded/files", %{
|
|> post("/api/pleroma/emoji/packs/not_loaded/files", %{
|
||||||
shortcode: "blank2",
|
shortcode: "blank3",
|
||||||
filename: "dir/blank.png",
|
filename: "dir/blank.png",
|
||||||
file: %Plug.Upload{
|
file: %Plug.Upload{
|
||||||
filename: "blank.png",
|
filename: "blank.png",
|
||||||
|
@ -535,7 +578,8 @@ test "new with shortcode as file with update", %{admin_conn: admin_conn} do
|
||||||
})
|
})
|
||||||
|> json_response_and_validate_schema(200) == %{
|
|> json_response_and_validate_schema(200) == %{
|
||||||
"blank" => "blank.png",
|
"blank" => "blank.png",
|
||||||
"blank4" => "dir/blank.png"
|
"blank4" => "dir/blank.png",
|
||||||
|
"blank2" => "blank2.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
|
assert File.exists?("#{@emoji_path}/test_pack/dir/blank.png")
|
||||||
|
@ -549,7 +593,8 @@ test "new with shortcode as file with update", %{admin_conn: admin_conn} do
|
||||||
})
|
})
|
||||||
|> json_response_and_validate_schema(200) == %{
|
|> json_response_and_validate_schema(200) == %{
|
||||||
"blank3" => "dir_2/blank_3.png",
|
"blank3" => "dir_2/blank_3.png",
|
||||||
"blank" => "blank.png"
|
"blank" => "blank.png",
|
||||||
|
"blank2" => "blank2.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
refute File.exists?("#{@emoji_path}/test_pack/dir/")
|
refute File.exists?("#{@emoji_path}/test_pack/dir/")
|
||||||
|
@ -557,7 +602,10 @@ test "new with shortcode as file with update", %{admin_conn: admin_conn} do
|
||||||
|
|
||||||
assert admin_conn
|
assert admin_conn
|
||||||
|> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3")
|
|> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3")
|
||||||
|> json_response_and_validate_schema(200) == %{"blank" => "blank.png"}
|
|> json_response_and_validate_schema(200) == %{
|
||||||
|
"blank" => "blank.png",
|
||||||
|
"blank2" => "blank2.png"
|
||||||
|
}
|
||||||
|
|
||||||
refute File.exists?("#{@emoji_path}/test_pack/dir_2/")
|
refute File.exists?("#{@emoji_path}/test_pack/dir_2/")
|
||||||
|
|
||||||
|
@ -581,7 +629,8 @@ test "new with shortcode from url", %{admin_conn: admin_conn} do
|
||||||
})
|
})
|
||||||
|> json_response_and_validate_schema(200) == %{
|
|> json_response_and_validate_schema(200) == %{
|
||||||
"blank_url" => "blank_url.png",
|
"blank_url" => "blank_url.png",
|
||||||
"blank" => "blank.png"
|
"blank" => "blank.png",
|
||||||
|
"blank2" => "blank2.png"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert File.exists?("#{@emoji_path}/test_pack/blank_url.png")
|
assert File.exists?("#{@emoji_path}/test_pack/blank_url.png")
|
||||||
|
@ -602,15 +651,16 @@ test "new without shortcode", %{admin_conn: admin_conn} do
|
||||||
})
|
})
|
||||||
|> json_response_and_validate_schema(200) == %{
|
|> json_response_and_validate_schema(200) == %{
|
||||||
"shortcode" => "shortcode.png",
|
"shortcode" => "shortcode.png",
|
||||||
"blank" => "blank.png"
|
"blank" => "blank.png",
|
||||||
|
"blank2" => "blank2.png"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do
|
test "remove non existing shortcode in pack.json", %{admin_conn: admin_conn} do
|
||||||
assert admin_conn
|
assert admin_conn
|
||||||
|> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank2")
|
|> delete("/api/pleroma/emoji/packs/test_pack/files?shortcode=blank3")
|
||||||
|> json_response_and_validate_schema(:bad_request) == %{
|
|> json_response_and_validate_schema(:bad_request) == %{
|
||||||
"error" => "Emoji \"blank2\" does not exist"
|
"error" => "Emoji \"blank3\" does not exist"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -618,12 +668,12 @@ test "update non existing emoji", %{admin_conn: admin_conn} do
|
||||||
assert admin_conn
|
assert admin_conn
|
||||||
|> put_req_header("content-type", "multipart/form-data")
|
|> put_req_header("content-type", "multipart/form-data")
|
||||||
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{
|
|> patch("/api/pleroma/emoji/packs/test_pack/files", %{
|
||||||
shortcode: "blank2",
|
shortcode: "blank3",
|
||||||
new_shortcode: "blank3",
|
new_shortcode: "blank4",
|
||||||
new_filename: "dir_2/blank_3.png"
|
new_filename: "dir_2/blank_3.png"
|
||||||
})
|
})
|
||||||
|> json_response_and_validate_schema(:bad_request) == %{
|
|> json_response_and_validate_schema(:bad_request) == %{
|
||||||
"error" => "Emoji \"blank2\" does not exist"
|
"error" => "Emoji \"blank3\" does not exist"
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -651,7 +701,8 @@ test "creating and deleting a pack", %{admin_conn: admin_conn} do
|
||||||
|
|
||||||
assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{
|
assert Jason.decode!(File.read!("#{@emoji_path}/test_created/pack.json")) == %{
|
||||||
"pack" => %{},
|
"pack" => %{},
|
||||||
"files" => %{}
|
"files" => %{},
|
||||||
|
"files_count" => 0
|
||||||
}
|
}
|
||||||
|
|
||||||
assert admin_conn
|
assert admin_conn
|
||||||
|
@ -709,14 +760,14 @@ test "filesystem import", %{admin_conn: admin_conn, conn: conn} do
|
||||||
|
|
||||||
resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
|
resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
refute Map.has_key?(resp, "test_pack_for_import")
|
refute Map.has_key?(resp["packs"], "test_pack_for_import")
|
||||||
|
|
||||||
assert admin_conn
|
assert admin_conn
|
||||||
|> get("/api/pleroma/emoji/packs/import")
|
|> get("/api/pleroma/emoji/packs/import")
|
||||||
|> json_response_and_validate_schema(200) == ["test_pack_for_import"]
|
|> json_response_and_validate_schema(200) == ["test_pack_for_import"]
|
||||||
|
|
||||||
resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
|
resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
|
||||||
assert resp["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}
|
assert resp["packs"]["test_pack_for_import"]["files"] == %{"blank" => "blank.png"}
|
||||||
|
|
||||||
File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
|
File.rm!("#{@emoji_path}/test_pack_for_import/pack.json")
|
||||||
refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json")
|
refute File.exists?("#{@emoji_path}/test_pack_for_import/pack.json")
|
||||||
|
@ -736,7 +787,7 @@ test "filesystem import", %{admin_conn: admin_conn, conn: conn} do
|
||||||
|
|
||||||
resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
|
resp = conn |> get("/api/pleroma/emoji/packs") |> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
assert resp["test_pack_for_import"]["files"] == %{
|
assert resp["packs"]["test_pack_for_import"]["files"] == %{
|
||||||
"blank" => "blank.png",
|
"blank" => "blank.png",
|
||||||
"blank2" => "blank.png",
|
"blank2" => "blank.png",
|
||||||
"foo" => "blank.png"
|
"foo" => "blank.png"
|
||||||
|
@ -746,7 +797,8 @@ test "filesystem import", %{admin_conn: admin_conn, conn: conn} do
|
||||||
describe "GET /api/pleroma/emoji/packs/:name" do
|
describe "GET /api/pleroma/emoji/packs/:name" do
|
||||||
test "shows pack.json", %{conn: conn} do
|
test "shows pack.json", %{conn: conn} do
|
||||||
assert %{
|
assert %{
|
||||||
"files" => %{"blank" => "blank.png"},
|
"files" => files,
|
||||||
|
"files_count" => 2,
|
||||||
"pack" => %{
|
"pack" => %{
|
||||||
"can-download" => true,
|
"can-download" => true,
|
||||||
"description" => "Test description",
|
"description" => "Test description",
|
||||||
|
@ -759,6 +811,28 @@ test "shows pack.json", %{conn: conn} do
|
||||||
conn
|
conn
|
||||||
|> get("/api/pleroma/emoji/packs/test_pack")
|
|> get("/api/pleroma/emoji/packs/test_pack")
|
||||||
|> json_response_and_validate_schema(200)
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert files == %{"blank" => "blank.png", "blank2" => "blank2.png"}
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"files" => files,
|
||||||
|
"files_count" => 2
|
||||||
|
} =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/emoji/packs/test_pack?page_size=1")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert files |> Map.keys() |> length() == 1
|
||||||
|
|
||||||
|
assert %{
|
||||||
|
"files" => files,
|
||||||
|
"files_count" => 2
|
||||||
|
} =
|
||||||
|
conn
|
||||||
|
|> get("/api/pleroma/emoji/packs/test_pack?page_size=1&page=2")
|
||||||
|
|> json_response_and_validate_schema(200)
|
||||||
|
|
||||||
|
assert files |> Map.keys() |> length() == 1
|
||||||
end
|
end
|
||||||
|
|
||||||
test "non existing pack", %{conn: conn} do
|
test "non existing pack", %{conn: conn} do
|
||||||
|
|
Loading…
Reference in a new issue