Merge remote-tracking branch 'pleroma/develop' into next
This commit is contained in:
commit
64f48c4239
158 changed files with 1195 additions and 121 deletions
|
@ -1,4 +1,4 @@
|
||||||
image: elixir:1.9.4
|
image: git.pleroma.social:5050/pleroma/pleroma/ci-base
|
||||||
|
|
||||||
variables: &global_variables
|
variables: &global_variables
|
||||||
POSTGRES_DB: pleroma_test
|
POSTGRES_DB: pleroma_test
|
||||||
|
@ -26,12 +26,7 @@ stages:
|
||||||
before_script:
|
before_script:
|
||||||
- echo $MIX_ENV
|
- echo $MIX_ENV
|
||||||
- rm -rf _build/*/lib/pleroma
|
- rm -rf _build/*/lib/pleroma
|
||||||
- apt-get update && apt-get install -y cmake
|
|
||||||
- mix local.hex --force
|
|
||||||
- mix local.rebar --force
|
|
||||||
- mix deps.get
|
- mix deps.get
|
||||||
- apt-get -qq update
|
|
||||||
- apt-get install -y libmagic-dev
|
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- rm -rf _build/*/lib/pleroma
|
- rm -rf _build/*/lib/pleroma
|
||||||
|
@ -88,7 +83,6 @@ unit-testing:
|
||||||
alias: postgres
|
alias: postgres
|
||||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
script:
|
script:
|
||||||
- apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg
|
|
||||||
- mix ecto.create
|
- mix ecto.create
|
||||||
- mix ecto.migrate
|
- mix ecto.migrate
|
||||||
- mix coveralls --preload-modules
|
- mix coveralls --preload-modules
|
||||||
|
@ -96,6 +90,7 @@ unit-testing:
|
||||||
unit-testing-erratic:
|
unit-testing-erratic:
|
||||||
stage: test
|
stage: test
|
||||||
retry: 2
|
retry: 2
|
||||||
|
allow_failure: true
|
||||||
only:
|
only:
|
||||||
changes:
|
changes:
|
||||||
- "**/*.ex"
|
- "**/*.ex"
|
||||||
|
@ -146,7 +141,6 @@ unit-testing-erratic:
|
||||||
# <<: *global_variables
|
# <<: *global_variables
|
||||||
# RUM_ENABLED: "true"
|
# RUM_ENABLED: "true"
|
||||||
# script:
|
# script:
|
||||||
# - apt-get update && apt-get install -y libimage-exiftool-perl ffmpeg
|
|
||||||
# - mix ecto.create
|
# - mix ecto.create
|
||||||
# - mix ecto.migrate
|
# - mix ecto.migrate
|
||||||
# - "mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/"
|
# - "mix ecto.migrate --migrations-path priv/repo/optional_migrations/rum_indexing/"
|
||||||
|
@ -161,6 +155,10 @@ lint:
|
||||||
- "**/*.exs"
|
- "**/*.exs"
|
||||||
- "mix.lock"
|
- "mix.lock"
|
||||||
cache: *testing_cache_policy
|
cache: *testing_cache_policy
|
||||||
|
before_script:
|
||||||
|
- mix local.hex --force
|
||||||
|
- mix local.rebar --force
|
||||||
|
- mix deps.get
|
||||||
script:
|
script:
|
||||||
- mix format --check-formatted
|
- mix format --check-formatted
|
||||||
|
|
||||||
|
@ -184,8 +182,13 @@ cycles:
|
||||||
- "**/*.exs"
|
- "**/*.exs"
|
||||||
- "mix.lock"
|
- "mix.lock"
|
||||||
cache: {}
|
cache: {}
|
||||||
script:
|
before_script:
|
||||||
|
- mix local.hex --force
|
||||||
|
- mix local.rebar --force
|
||||||
- mix deps.get
|
- mix deps.get
|
||||||
|
- apt-get update
|
||||||
|
- apt-get install cmake libmagic-dev -y
|
||||||
|
script:
|
||||||
- mix compile
|
- mix compile
|
||||||
- mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != "No cycles found")}'
|
- mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != "No cycles found")}'
|
||||||
|
|
||||||
|
|
28
CHANGELOG.md
28
CHANGELOG.md
|
@ -16,16 +16,44 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
### Added
|
### Added
|
||||||
- `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
|
- `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object
|
||||||
- Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney.
|
- Experimental support for Finch. Put `config :tesla, :adapter, {Tesla.Adapter.Finch, name: MyFinch}` in your secrets file to use it. Reverse Proxy will still use Hackney.
|
||||||
|
- `ForceMentionsInPostContent` MRF policy
|
||||||
- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis
|
- AdminAPI: allow moderators to manage reports, users, invites, and custom emojis
|
||||||
- AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc
|
- AdminAPI: restrict moderators to access sensitive data: change user credentials, get password reset token, read private statuses and chats, etc
|
||||||
|
- PleromaAPI: Add remote follow API endpoint at `POST /api/v1/pleroma/remote_interaction`
|
||||||
|
- MastoAPI: Add `GET /api/v1/accounts/lookup`
|
||||||
|
- MastoAPI: Profile Directory support
|
||||||
|
- MastoAPI: Support v2 Suggestions (handpicked accounts only)
|
||||||
|
- Ability to log slow Ecto queries by configuring `:pleroma, :telemetry, :slow_queries_logging`
|
||||||
|
- Added Phoenix LiveDashboard at `/phoenix/live_dashboard`
|
||||||
|
- Added `/manifest.json` for progressive web apps.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
- Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies
|
||||||
- Handle Reject for already-accepted Follows properly
|
- Handle Reject for already-accepted Follows properly
|
||||||
- Display OpenGraph data on alternative notice routes.
|
- Display OpenGraph data on alternative notice routes.
|
||||||
|
- Fix replies count for remote replies
|
||||||
|
- Fixed hashtags disappearing from the end of lines when Markdown is enabled
|
||||||
|
- ChatAPI: Add link headers
|
||||||
|
- Limited number of search results to 40 to prevent DoS attacks
|
||||||
|
- ActivityPub: fixed federation of attachment dimensions
|
||||||
|
- Fixed benchmarks
|
||||||
|
- Elixir 1.13 support
|
||||||
|
- Fixed crash when pinned_objects is nil
|
||||||
|
- Fixed slow timelines when there are a lot of deactivated users
|
||||||
|
- Fixed account deletion API
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
|
## 2.4.2 - 2022-01-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Federation issues caused by HTTP pool checkout timeouts
|
||||||
|
- Compatibility with Elixir 1.13
|
||||||
|
|
||||||
|
### Upgrade notes
|
||||||
|
|
||||||
|
1. Restart Pleroma
|
||||||
|
|
||||||
## 2.4.1 - 2021-08-29
|
## 2.4.1 - 2021-08-29
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
7
ci/Dockerfile
Normal file
7
ci/Dockerfile
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
FROM elixir:1.9.4
|
||||||
|
|
||||||
|
RUN apt-get update &&\
|
||||||
|
apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\
|
||||||
|
mix local.hex --force &&\
|
||||||
|
mix local.rebar --force
|
||||||
|
|
1
ci/build_and_push.sh
Executable file
1
ci/build_and_push.sh
Executable file
|
@ -0,0 +1 @@
|
||||||
|
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:latest --push .
|
|
@ -258,7 +258,8 @@
|
||||||
show_reactions: true,
|
show_reactions: true,
|
||||||
password_reset_token_validity: 60 * 60 * 24,
|
password_reset_token_validity: 60 * 60 * 24,
|
||||||
profile_directory: true,
|
profile_directory: true,
|
||||||
privileged_staff: false
|
privileged_staff: false,
|
||||||
|
max_endorsed_users: 20
|
||||||
|
|
||||||
config :pleroma, :welcome,
|
config :pleroma, :welcome,
|
||||||
direct_message: [
|
direct_message: [
|
||||||
|
|
|
@ -742,6 +742,16 @@
|
||||||
3
|
3
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :max_endorsed_users,
|
||||||
|
type: :integer,
|
||||||
|
description: "The maximum number of recommended accounts. 0 will disable the feature.",
|
||||||
|
suggestions: [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
3
|
||||||
|
]
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :autofollowed_nicknames,
|
key: :autofollowed_nicknames,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
|
|
|
@ -116,3 +116,9 @@ Feel free to contact us to be added to this list!
|
||||||
- Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r)
|
- Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r)
|
||||||
- Features: Does not requires JavaScript
|
- Features: Does not requires JavaScript
|
||||||
- Features: MastoAPI
|
- Features: MastoAPI
|
||||||
|
|
||||||
|
### Glitch-lily
|
||||||
|
- Source Code: <https://lily.kazv.moe/infra/glitch-lily>
|
||||||
|
- Contact: [@tusooa@kazv.moe](https://kazv.moe/users/tusooa)
|
||||||
|
- Features: MastoAPI
|
||||||
|
- Based on [glitch-soc](https://github.com/glitch-soc/mastodon) frontend
|
||||||
|
|
|
@ -125,6 +125,8 @@ To add configuration to your config file, you can copy it from the base config.
|
||||||
* `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections.
|
* `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.Workers.PurgeExpiredActivity` to be enabled for processing the scheduled delections.
|
||||||
* `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines.
|
* `Pleroma.Web.ActivityPub.MRF.ForceBotUnlistedPolicy`: Makes all bot posts to disappear from public timelines.
|
||||||
* `Pleroma.Web.ActivityPub.MRF.FollowBotPolicy`: Automatically follows newly discovered users from the specified bot account. Local accounts, locked accounts, and users with "#nobot" in their bio are respected and excluded from being followed.
|
* `Pleroma.Web.ActivityPub.MRF.FollowBotPolicy`: Automatically follows newly discovered users from the specified bot account. Local accounts, locked accounts, and users with "#nobot" in their bio are respected and excluded from being followed.
|
||||||
|
* `Pleroma.Web.ActivityPub.MRF.KeywordPolicy`: Rejects or removes from the federated timeline or replaces keywords. (See [`:mrf_keyword`](#mrf_keyword)).
|
||||||
|
* `Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent`: Forces every mentioned user to be reflected in the post content.
|
||||||
* `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
* `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
||||||
* `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
|
* `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value.
|
||||||
|
|
||||||
|
|
|
@ -377,12 +377,6 @@ Pleroma is generally compatible with the Mastodon 2.7.2 API, but some newer feat
|
||||||
|
|
||||||
- `GET /api/v1/identity_proofs`: Returns an empty array, `[]`
|
- `GET /api/v1/identity_proofs`: Returns an empty array, `[]`
|
||||||
|
|
||||||
### Endorsements
|
|
||||||
|
|
||||||
*Added in Mastodon 2.5.0*
|
|
||||||
|
|
||||||
- `GET /api/v1/endorsements`: Returns an empty array, `[]`
|
|
||||||
|
|
||||||
### Featured tags
|
### Featured tags
|
||||||
|
|
||||||
*Added in Mastodon 3.0.0*
|
*Added in Mastodon 3.0.0*
|
||||||
|
|
|
@ -37,7 +37,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
|
||||||
```
|
```
|
||||||
* Note: Same data as Mastodon API’s `/api/v1/custom_emojis` but in a different format
|
* Note: Same data as Mastodon API’s `/api/v1/custom_emojis` but in a different format
|
||||||
|
|
||||||
## `/api/v1/pleroma/follow_import`
|
## `/api/pleroma/follow_import`
|
||||||
### Imports your follows, for example from a Mastodon CSV file.
|
### Imports your follows, for example from a Mastodon CSV file.
|
||||||
* Method: `POST`
|
* Method: `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
@ -46,7 +46,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
|
||||||
* Response: HTTP 200 on success, 500 on error
|
* Response: HTTP 200 on success, 500 on error
|
||||||
* Note: Users that can't be followed are silently skipped.
|
* Note: Users that can't be followed are silently skipped.
|
||||||
|
|
||||||
## `/api/v1/pleroma/blocks_import`
|
## `/api/pleroma/blocks_import`
|
||||||
### Imports your blocks.
|
### Imports your blocks.
|
||||||
* Method: `POST`
|
* Method: `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
@ -54,7 +54,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
|
||||||
* `list`: STRING or FILE containing a whitespace-separated list of accounts to block
|
* `list`: STRING or FILE containing a whitespace-separated list of accounts to block
|
||||||
* Response: HTTP 200 on success, 500 on error
|
* Response: HTTP 200 on success, 500 on error
|
||||||
|
|
||||||
## `/api/v1/pleroma/mutes_import`
|
## `/api/pleroma/mutes_import`
|
||||||
### Imports your mutes.
|
### Imports your mutes.
|
||||||
* Method: `POST`
|
* Method: `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
@ -70,7 +70,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
|
||||||
* Response: Provider specific JSON, the only guaranteed parameter is `type`
|
* Response: Provider specific JSON, the only guaranteed parameter is `type`
|
||||||
* Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint", "seconds_valid": 300}`
|
* Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint", "seconds_valid": 300}`
|
||||||
|
|
||||||
## `/api/v1/pleroma/delete_account`
|
## `/api/pleroma/delete_account`
|
||||||
### Delete an account
|
### Delete an account
|
||||||
* Method `POST`
|
* Method `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
@ -79,7 +79,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
|
||||||
* Response: JSON. Returns `{"status": "success"}` if the deletion was successful, `{"error": "[error message]"}` otherwise
|
* Response: JSON. Returns `{"status": "success"}` if the deletion was successful, `{"error": "[error message]"}` otherwise
|
||||||
* Example response: `{"error": "Invalid password."}`
|
* Example response: `{"error": "Invalid password."}`
|
||||||
|
|
||||||
## `/api/v1/pleroma/disable_account`
|
## `/api/pleroma/disable_account`
|
||||||
### Disable an account
|
### Disable an account
|
||||||
* Method `POST`
|
* Method `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
@ -88,21 +88,22 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
|
||||||
* Response: JSON. Returns `{"status": "success"}` if the account was successfully disabled, `{"error": "[error message]"}` otherwise
|
* Response: JSON. Returns `{"status": "success"}` if the account was successfully disabled, `{"error": "[error message]"}` otherwise
|
||||||
* Example response: `{"error": "Invalid password."}`
|
* Example response: `{"error": "Invalid password."}`
|
||||||
|
|
||||||
## `/api/v1/pleroma/accounts/mfa`
|
## `/api/pleroma/accounts/mfa`
|
||||||
#### Gets current MFA settings
|
#### Gets current MFA settings
|
||||||
* method: `GET`
|
* method: `GET`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
* OAuth scope: `read:security`
|
* OAuth scope: `read:security`
|
||||||
* Response: JSON. Returns `{"enabled": "false", "totp": false }`
|
* Response: JSON. Returns `{"settings": {"enabled": "false", "totp": false }}`
|
||||||
|
* Note: `enabled` is whether multi-factor auth is enabled for the user in general, while `totp` is one type of MFA.
|
||||||
|
|
||||||
## `/api/v1/pleroma/accounts/mfa/setup/totp`
|
## `/api/pleroma/accounts/mfa/setup/totp`
|
||||||
#### Pre-setup the MFA/TOTP method
|
#### Pre-setup the MFA/TOTP method
|
||||||
* method: `GET`
|
* method: `GET`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
* OAuth scope: `write:security`
|
* OAuth scope: `write:security`
|
||||||
* Response: JSON. Returns `{"key": [secret_key], "provisioning_uri": "[qr code uri]" }` when successful, otherwise returns HTTP 422 `{"error": "error_msg"}`
|
* Response: JSON. Returns `{"key": [secret_key], "provisioning_uri": "[qr code uri]" }` when successful, otherwise returns HTTP 422 `{"error": "error_msg"}`
|
||||||
|
|
||||||
## `/api/v1/pleroma/accounts/mfa/confirm/totp`
|
## `/api/pleroma/accounts/mfa/confirm/totp`
|
||||||
#### Confirms & enables MFA/TOTP support for user account.
|
#### Confirms & enables MFA/TOTP support for user account.
|
||||||
* method: `POST`
|
* method: `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
@ -113,7 +114,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
|
||||||
* Response: JSON. Returns `{}` if the enable was successful, HTTP 422 `{"error": "[error message]"}` otherwise
|
* Response: JSON. Returns `{}` if the enable was successful, HTTP 422 `{"error": "[error message]"}` otherwise
|
||||||
|
|
||||||
|
|
||||||
## `/api/v1/pleroma/accounts/mfa/totp`
|
## `/api/pleroma/accounts/mfa/totp`
|
||||||
#### Disables MFA/TOTP method for user account.
|
#### Disables MFA/TOTP method for user account.
|
||||||
* method: `DELETE`
|
* method: `DELETE`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
@ -123,7 +124,7 @@ The `/api/v1/pleroma/*` path is backwards compatible with `/api/pleroma/*` (`/ap
|
||||||
* Response: JSON. Returns `{}` if the disable was successful, HTTP 422 `{"error": "[error message]"}` otherwise
|
* Response: JSON. Returns `{}` if the disable was successful, HTTP 422 `{"error": "[error message]"}` otherwise
|
||||||
* Example response: `{"error": "Invalid password."}`
|
* Example response: `{"error": "Invalid password."}`
|
||||||
|
|
||||||
## `/api/v1/pleroma/accounts/mfa/backup_codes`
|
## `/api/pleroma/accounts/mfa/backup_codes`
|
||||||
#### Generstes backup codes MFA for user account.
|
#### Generstes backup codes MFA for user account.
|
||||||
* method: `GET`
|
* method: `GET`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
@ -331,7 +332,7 @@ See [Admin-API](admin_api.md)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## `/api/v1/pleroma/change_email`
|
## `/api/pleroma/change_email`
|
||||||
### Change account email
|
### Change account email
|
||||||
* Method `POST`
|
* Method `POST`
|
||||||
* Authentication: required
|
* Authentication: required
|
||||||
|
@ -659,3 +660,38 @@ Emoji reactions work a lot like favourites do. They make it possible to react to
|
||||||
"url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip"
|
"url": "https://example.com/media/backups/archive-foobar-20200910T161803-QUhx6VYDRQ2wfV0SdA2Pfj_2CLM_ATUlw-D5l5TJf4Q.zip"
|
||||||
}]
|
}]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `GET /api/oauth_tokens`
|
||||||
|
### Retrieve a list of active sessions for the user
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: required
|
||||||
|
* Params: none
|
||||||
|
* Response: JSON
|
||||||
|
* Example response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"app_name": "Pleroma FE",
|
||||||
|
"id": 9275,
|
||||||
|
"valid_until": "2121-11-24T15:51:08.234234"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"app_name": "Patron",
|
||||||
|
"id": 8805,
|
||||||
|
"valid_until": "2121-10-26T18:09:59.857150"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"app_name": "Soapbox FE",
|
||||||
|
"id": 9727,
|
||||||
|
"valid_until": "2121-12-25T16:52:39.692877"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## `DELETE /api/oauth_tokens/:id`
|
||||||
|
### Revoke a user session by its ID
|
||||||
|
* Method: `DELETE`
|
||||||
|
* Authentication: required
|
||||||
|
* Params: none
|
||||||
|
* Response: HTTP 200 on success, 500 on error
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
reblog_mute: 3,
|
reblog_mute: 3,
|
||||||
notification_mute: 4,
|
notification_mute: 4,
|
||||||
inverse_subscription: 5,
|
inverse_subscription: 5,
|
||||||
suggestion_dismiss: 6
|
suggestion_dismiss: 6,
|
||||||
|
endorsement: 7
|
||||||
)
|
)
|
||||||
|
|
||||||
defenum(Pleroma.FollowingRelationship.State,
|
defenum(Pleroma.FollowingRelationship.State,
|
||||||
|
|
|
@ -34,32 +34,34 @@ def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do
|
||||||
|
|
||||||
def mention_handler("@" <> nickname, buffer, opts, acc) do
|
def mention_handler("@" <> nickname, buffer, opts, acc) do
|
||||||
case User.get_cached_by_nickname(nickname) do
|
case User.get_cached_by_nickname(nickname) do
|
||||||
%User{id: id} = user ->
|
%User{} = user ->
|
||||||
user_url = user.uri || user.ap_id
|
{mention_from_user(user, opts),
|
||||||
nickname_text = get_nickname_text(nickname, opts)
|
%{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
|
||||||
|
|
||||||
link =
|
|
||||||
Phoenix.HTML.Tag.content_tag(
|
|
||||||
:span,
|
|
||||||
Phoenix.HTML.Tag.content_tag(
|
|
||||||
:a,
|
|
||||||
["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
|
|
||||||
"data-user": id,
|
|
||||||
class: "u-url mention",
|
|
||||||
href: user_url,
|
|
||||||
rel: "ugc"
|
|
||||||
),
|
|
||||||
class: "h-card"
|
|
||||||
)
|
|
||||||
|> Phoenix.HTML.safe_to_string()
|
|
||||||
|
|
||||||
{link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
{buffer, acc}
|
{buffer, acc}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def mention_from_user(%User{id: id} = user, opts \\ %{mentions_format: :full}) do
|
||||||
|
user_url = user.uri || user.ap_id
|
||||||
|
nickname_text = get_nickname_text(user.nickname, opts)
|
||||||
|
|
||||||
|
Phoenix.HTML.Tag.content_tag(
|
||||||
|
:span,
|
||||||
|
Phoenix.HTML.Tag.content_tag(
|
||||||
|
:a,
|
||||||
|
["@", Phoenix.HTML.Tag.content_tag(:span, nickname_text)],
|
||||||
|
"data-user": id,
|
||||||
|
class: "u-url mention",
|
||||||
|
href: user_url,
|
||||||
|
rel: "ugc"
|
||||||
|
),
|
||||||
|
class: "h-card"
|
||||||
|
)
|
||||||
|
|> Phoenix.HTML.safe_to_string()
|
||||||
|
end
|
||||||
|
|
||||||
def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
|
def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
|
||||||
tag = String.downcase(tag)
|
tag = String.downcase(tag)
|
||||||
url = "#{Pleroma.Web.Endpoint.url()}/tag/#{tag}"
|
url = "#{Pleroma.Web.Endpoint.url()}/tag/#{tag}"
|
||||||
|
|
|
@ -78,6 +78,10 @@ defmodule Pleroma.User do
|
||||||
inverse_subscription: [
|
inverse_subscription: [
|
||||||
subscribee_subscriptions: :subscriber_users,
|
subscribee_subscriptions: :subscriber_users,
|
||||||
subscriber_subscriptions: :subscribee_users
|
subscriber_subscriptions: :subscribee_users
|
||||||
|
],
|
||||||
|
endorsement: [
|
||||||
|
endorser_endorsements: :endorsed_users,
|
||||||
|
endorsee_endorsements: :endorser_users
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -171,25 +175,25 @@ defmodule Pleroma.User do
|
||||||
{incoming_relation, incoming_relation_source}
|
{incoming_relation, incoming_relation_source}
|
||||||
]} <- @user_relationships_config do
|
]} <- @user_relationships_config do
|
||||||
# Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
|
# Definitions of `has_many` relations: :blocker_blocks, :muter_mutes, :reblog_muter_mutes,
|
||||||
# :notification_muter_mutes, :subscribee_subscriptions
|
# :notification_muter_mutes, :subscribee_subscriptions, :endorser_endorsements
|
||||||
has_many(outgoing_relation, UserRelationship,
|
has_many(outgoing_relation, UserRelationship,
|
||||||
foreign_key: :source_id,
|
foreign_key: :source_id,
|
||||||
where: [relationship_type: relationship_type]
|
where: [relationship_type: relationship_type]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
|
# Definitions of `has_many` relations: :blockee_blocks, :mutee_mutes, :reblog_mutee_mutes,
|
||||||
# :notification_mutee_mutes, :subscriber_subscriptions
|
# :notification_mutee_mutes, :subscriber_subscriptions, :endorsee_endorsements
|
||||||
has_many(incoming_relation, UserRelationship,
|
has_many(incoming_relation, UserRelationship,
|
||||||
foreign_key: :target_id,
|
foreign_key: :target_id,
|
||||||
where: [relationship_type: relationship_type]
|
where: [relationship_type: relationship_type]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
|
# Definitions of `has_many` relations: :blocked_users, :muted_users, :reblog_muted_users,
|
||||||
# :notification_muted_users, :subscriber_users
|
# :notification_muted_users, :subscriber_users, :endorsed_users
|
||||||
has_many(outgoing_relation_target, through: [outgoing_relation, :target])
|
has_many(outgoing_relation_target, through: [outgoing_relation, :target])
|
||||||
|
|
||||||
# Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
|
# Definitions of `has_many` relations: :blocker_users, :muter_users, :reblog_muter_users,
|
||||||
# :notification_muter_users, :subscribee_users
|
# :notification_muter_users, :subscribee_users, :endorser_users
|
||||||
has_many(incoming_relation_source, through: [incoming_relation, :source])
|
has_many(incoming_relation_source, through: [incoming_relation, :source])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -217,7 +221,7 @@ defmodule Pleroma.User do
|
||||||
@user_relationships_config do
|
@user_relationships_config do
|
||||||
# `def blocked_users_relation/2`, `def muted_users_relation/2`,
|
# `def blocked_users_relation/2`, `def muted_users_relation/2`,
|
||||||
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
|
# `def reblog_muted_users_relation/2`, `def notification_muted_users/2`,
|
||||||
# `def subscriber_users/2`
|
# `def subscriber_users/2`, `def endorsed_users_relation/2`
|
||||||
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
|
def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated? \\ false) do
|
||||||
target_users_query = assoc(user, unquote(outgoing_relation_target))
|
target_users_query = assoc(user, unquote(outgoing_relation_target))
|
||||||
|
|
||||||
|
@ -230,7 +234,7 @@ def unquote(:"#{outgoing_relation_target}_relation")(user, restrict_deactivated?
|
||||||
end
|
end
|
||||||
|
|
||||||
# `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
|
# `def blocked_users/2`, `def muted_users/2`, `def reblog_muted_users/2`,
|
||||||
# `def notification_muted_users/2`, `def subscriber_users/2`
|
# `def notification_muted_users/2`, `def subscriber_users/2`, `def endorsed_users/2`
|
||||||
def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
|
def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
|
||||||
__MODULE__
|
__MODULE__
|
||||||
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
||||||
|
@ -241,7 +245,8 @@ def unquote(outgoing_relation_target)(user, restrict_deactivated? \\ false) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
|
# `def blocked_users_ap_ids/2`, `def muted_users_ap_ids/2`, `def reblog_muted_users_ap_ids/2`,
|
||||||
# `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`
|
# `def notification_muted_users_ap_ids/2`, `def subscriber_users_ap_ids/2`,
|
||||||
|
# `def endorsed_users_ap_ids/2`
|
||||||
def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
|
def unquote(:"#{outgoing_relation_target}_ap_ids")(user, restrict_deactivated? \\ false) do
|
||||||
__MODULE__
|
__MODULE__
|
||||||
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
|> apply(unquote(:"#{outgoing_relation_target}_relation"), [
|
||||||
|
@ -1054,6 +1059,10 @@ def get_by_ap_id(ap_id) do
|
||||||
Repo.get_by(User, ap_id: ap_id)
|
Repo.get_by(User, ap_id: ap_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_by_uri(uri) do
|
||||||
|
Repo.get_by(User, uri: uri)
|
||||||
|
end
|
||||||
|
|
||||||
def get_all_by_ap_id(ap_ids) do
|
def get_all_by_ap_id(ap_ids) do
|
||||||
from(u in __MODULE__,
|
from(u in __MODULE__,
|
||||||
where: u.ap_id in ^ap_ids
|
where: u.ap_id in ^ap_ids
|
||||||
|
@ -1520,6 +1529,40 @@ def unblock(%User{} = blocker, %{ap_id: ap_id}) do
|
||||||
unblock(blocker, get_cached_by_ap_id(ap_id))
|
unblock(blocker, get_cached_by_ap_id(ap_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def endorse(%User{} = endorser, %User{} = target) do
|
||||||
|
with max_endorsed_users <- Pleroma.Config.get([:instance, :max_endorsed_users], 0),
|
||||||
|
endorsed_users <-
|
||||||
|
User.endorsed_users_relation(endorser)
|
||||||
|
|> Repo.aggregate(:count, :id) do
|
||||||
|
cond do
|
||||||
|
endorsed_users >= max_endorsed_users ->
|
||||||
|
{:error, "You have already pinned the maximum number of users"}
|
||||||
|
|
||||||
|
not following?(endorser, target) ->
|
||||||
|
{:error, "Could not endorse: You are not following #{target.nickname}"}
|
||||||
|
|
||||||
|
true ->
|
||||||
|
UserRelationship.create_endorsement(endorser, target)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def endorse(%User{} = endorser, %{ap_id: ap_id}) do
|
||||||
|
with %User{} = endorsed <- get_cached_by_ap_id(ap_id) do
|
||||||
|
endorse(endorser, endorsed)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def unendorse(%User{} = unendorser, %User{} = target) do
|
||||||
|
UserRelationship.delete_endorsement(unendorser, target)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unendorse(%User{} = unendorser, %{ap_id: ap_id}) do
|
||||||
|
with %User{} = user <- get_cached_by_ap_id(ap_id) do
|
||||||
|
unendorse(unendorser, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def mutes?(nil, _), do: false
|
def mutes?(nil, _), do: false
|
||||||
def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
|
def mutes?(%User{} = user, %User{} = target), do: mutes_user?(user, target)
|
||||||
|
|
||||||
|
@ -1565,6 +1608,10 @@ def subscribed_to?(%User{} = user, %{ap_id: ap_id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def endorses?(%User{} = user, %User{} = target) do
|
||||||
|
UserRelationship.endorsement_exists?(user, target)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type.
|
Returns map of outgoing (blocked, muted etc.) relationships' user AP IDs by relation type.
|
||||||
E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
|
E.g. `outgoing_relationships_ap_ids(user, [:block])` -> `%{block: ["https://some.site/users/userapid"]}`
|
||||||
|
|
|
@ -24,17 +24,20 @@ defmodule Pleroma.UserRelationship do
|
||||||
|
|
||||||
for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do
|
for relationship_type <- Keyword.keys(Pleroma.UserRelationship.Type.__enum_map__()) do
|
||||||
# `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,
|
# `def create_block/2`, `def create_mute/2`, `def create_reblog_mute/2`,
|
||||||
# `def create_notification_mute/2`, `def create_inverse_subscription/2`
|
# `def create_notification_mute/2`, `def create_inverse_subscription/2`,
|
||||||
|
# `def endorsement/2`
|
||||||
def unquote(:"create_#{relationship_type}")(source, target),
|
def unquote(:"create_#{relationship_type}")(source, target),
|
||||||
do: create(unquote(relationship_type), source, target)
|
do: create(unquote(relationship_type), source, target)
|
||||||
|
|
||||||
# `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`,
|
# `def delete_block/2`, `def delete_mute/2`, `def delete_reblog_mute/2`,
|
||||||
# `def delete_notification_mute/2`, `def delete_inverse_subscription/2`
|
# `def delete_notification_mute/2`, `def delete_inverse_subscription/2`,
|
||||||
|
# `def delete_endorsement/2`
|
||||||
def unquote(:"delete_#{relationship_type}")(source, target),
|
def unquote(:"delete_#{relationship_type}")(source, target),
|
||||||
do: delete(unquote(relationship_type), source, target)
|
do: delete(unquote(relationship_type), source, target)
|
||||||
|
|
||||||
# `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`,
|
# `def block_exists?/2`, `def mute_exists?/2`, `def reblog_mute_exists?/2`,
|
||||||
# `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`
|
# `def notification_mute_exists?/2`, `def inverse_subscription_exists?/2`,
|
||||||
|
# `def inverse_endorsement?/2`
|
||||||
def unquote(:"#{relationship_type}_exists?")(source, target),
|
def unquote(:"#{relationship_type}_exists?")(source, target),
|
||||||
do: exists?(unquote(relationship_type), source, target)
|
do: exists?(unquote(relationship_type), source, target)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1666,7 +1666,10 @@ def pin_data_from_featured_collection(%{
|
||||||
"orderedItems" => objects
|
"orderedItems" => objects
|
||||||
})
|
})
|
||||||
when type in ["OrderedCollection", "Collection"] do
|
when type in ["OrderedCollection", "Collection"] do
|
||||||
Map.new(objects, fn %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} end)
|
Map.new(objects, fn
|
||||||
|
%{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()}
|
||||||
|
object_ap_id when is_binary(object_ap_id) -> {object_ap_id, NaiveDateTime.utc_now()}
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_and_prepare_featured_from_ap_id(nil) do
|
def fetch_and_prepare_featured_from_ap_id(nil) do
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRF.ForceMentionsInContent do
|
||||||
|
alias Pleroma.Formatter
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||||
|
|
||||||
|
defp do_extract({:a, attrs, _}, acc) do
|
||||||
|
if Enum.find(attrs, fn {name, value} ->
|
||||||
|
name == "class" && value in ["mention", "u-url mention", "mention u-url"]
|
||||||
|
end) do
|
||||||
|
href = Enum.find(attrs, fn {name, _} -> name == "href" end) |> elem(1)
|
||||||
|
acc ++ [href]
|
||||||
|
else
|
||||||
|
acc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_extract({_, _, children}, acc) do
|
||||||
|
do_extract(children, acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_extract(nodes, acc) when is_list(nodes) do
|
||||||
|
Enum.reduce(nodes, acc, fn node, acc -> do_extract(node, acc) end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_extract(_, acc), do: acc
|
||||||
|
|
||||||
|
defp extract_mention_uris_from_content(content) do
|
||||||
|
{:ok, tree} = :fast_html.decode(content, format: [:html_atoms])
|
||||||
|
do_extract(tree, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def filter(%{"type" => "Create", "object" => %{"type" => "Note", "tag" => tag}} = object) do
|
||||||
|
# image-only posts from pleroma apparently reach this MRF without the content field
|
||||||
|
content = object["object"]["content"] || ""
|
||||||
|
|
||||||
|
mention_users =
|
||||||
|
tag
|
||||||
|
|> Enum.filter(fn tag -> tag["type"] == "Mention" end)
|
||||||
|
|> Enum.map(& &1["href"])
|
||||||
|
|> Enum.reject(&is_nil/1)
|
||||||
|
|> Enum.map(fn ap_id_or_uri ->
|
||||||
|
case User.get_or_fetch_by_ap_id(ap_id_or_uri) do
|
||||||
|
{:ok, user} -> {ap_id_or_uri, user}
|
||||||
|
_ -> {ap_id_or_uri, User.get_by_uri(ap_id_or_uri)}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> Enum.reject(fn {_, user} -> user == nil end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
explicitly_mentioned_uris = extract_mention_uris_from_content(content)
|
||||||
|
|
||||||
|
added_mentions =
|
||||||
|
Enum.reduce(mention_users, "", fn {uri, user}, acc ->
|
||||||
|
unless uri in explicitly_mentioned_uris do
|
||||||
|
acc <> Formatter.mention_from_user(user)
|
||||||
|
else
|
||||||
|
acc
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
content =
|
||||||
|
if added_mentions != "",
|
||||||
|
do: added_mentions <> " " <> content,
|
||||||
|
else: content
|
||||||
|
|
||||||
|
{:ok, put_in(object["object"]["content"], content)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def filter(object), do: {:ok, object}
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def describe, do: {:ok, %{}}
|
||||||
|
end
|
|
@ -334,6 +334,42 @@ def unblock_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def endorse_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account actions"],
|
||||||
|
summary: "Endorse",
|
||||||
|
operationId: "AccountController.endorse",
|
||||||
|
security: [%{"oAuth" => ["follow", "write:accounts"]}],
|
||||||
|
description: "Addds the given account to endorsed accounts list.",
|
||||||
|
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Relationship", "application/json", AccountRelationship),
|
||||||
|
400 =>
|
||||||
|
Operation.response("Bad Request", "application/json", %Schema{
|
||||||
|
allOf: [ApiError],
|
||||||
|
title: "Unprocessable Entity",
|
||||||
|
example: %{
|
||||||
|
"error" => "You have already pinned the maximum number of users"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def unendorse_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account actions"],
|
||||||
|
summary: "Unendorse",
|
||||||
|
operationId: "AccountController.unendorse",
|
||||||
|
security: [%{"oAuth" => ["follow", "write:accounts"]}],
|
||||||
|
description: "Removes the given account from endorsed accounts list.",
|
||||||
|
parameters: [%Reference{"$ref": "#/components/parameters/accountIdOrNickname"}],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Relationship", "application/json", AccountRelationship)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def note_operation do
|
def note_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Account actions"],
|
tags: ["Account actions"],
|
||||||
|
@ -400,15 +436,35 @@ def blocks_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def lookup_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Account lookup"],
|
||||||
|
summary: "Find a user by nickname",
|
||||||
|
operationId: "AccountController.lookup",
|
||||||
|
parameters: [
|
||||||
|
Operation.parameter(
|
||||||
|
:acct,
|
||||||
|
:query,
|
||||||
|
:string,
|
||||||
|
"User nickname"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Account", "application/json", Account),
|
||||||
|
404 => Operation.response("Error", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def endorsements_operation do
|
def endorsements_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Retrieve account information"],
|
tags: ["Retrieve account information"],
|
||||||
summary: "Endorsements",
|
summary: "Endorsements",
|
||||||
operationId: "AccountController.endorsements",
|
operationId: "AccountController.endorsements",
|
||||||
description: "Not implemented",
|
description: "Returns endorsed accounts",
|
||||||
security: [%{"oAuth" => ["read:accounts"]}],
|
security: [%{"oAuth" => ["read:accounts"]}],
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => empty_array_response()
|
200 => Operation.response("Array of Accounts", "application/json", array_of_accounts())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.ApiSpec.AppOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
alias OpenApiSpex.Schema
|
alias OpenApiSpex.Schema
|
||||||
alias Pleroma.Web.ApiSpec.Helpers
|
alias Pleroma.Web.ApiSpec.Helpers
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.App
|
||||||
|
|
||||||
@spec open_api_operation(atom) :: Operation.t()
|
@spec open_api_operation(atom) :: Operation.t()
|
||||||
def open_api_operation(action) do
|
def open_api_operation(action) do
|
||||||
|
@ -22,7 +23,7 @@ def create_operation do
|
||||||
operationId: "AppController.create",
|
operationId: "AppController.create",
|
||||||
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
requestBody: Helpers.request_body("Parameters", create_request(), required: true),
|
||||||
responses: %{
|
responses: %{
|
||||||
200 => Operation.response("App", "application/json", create_response()),
|
200 => Operation.response("App", "application/json", App),
|
||||||
422 =>
|
422 =>
|
||||||
Operation.response(
|
Operation.response(
|
||||||
"Unprocessable Entity",
|
"Unprocessable Entity",
|
||||||
|
@ -119,30 +120,4 @@ defp create_request do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp create_response do
|
|
||||||
%Schema{
|
|
||||||
title: "AppCreateResponse",
|
|
||||||
description: "Response schema for an app",
|
|
||||||
type: :object,
|
|
||||||
properties: %{
|
|
||||||
id: %Schema{type: :string},
|
|
||||||
name: %Schema{type: :string},
|
|
||||||
client_id: %Schema{type: :string},
|
|
||||||
client_secret: %Schema{type: :string},
|
|
||||||
redirect_uri: %Schema{type: :string},
|
|
||||||
vapid_key: %Schema{type: :string},
|
|
||||||
website: %Schema{type: :string, nullable: true}
|
|
||||||
},
|
|
||||||
example: %{
|
|
||||||
"id" => "123",
|
|
||||||
"name" => "My App",
|
|
||||||
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
|
||||||
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
|
||||||
"vapid_key" =>
|
|
||||||
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
|
|
||||||
"website" => "https://myapp.com/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
|
defmodule Pleroma.Web.ApiSpec.PleromaAccountOperation do
|
||||||
alias OpenApiSpex.Operation
|
alias OpenApiSpex.Operation
|
||||||
|
alias Pleroma.Web.ApiSpec.AccountOperation
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
alias Pleroma.Web.ApiSpec.Schemas.AccountRelationship
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
alias Pleroma.Web.ApiSpec.Schemas.ApiError
|
||||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||||
|
@ -62,6 +63,25 @@ def favourites_operation do
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def endorsements_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Retrieve account information"],
|
||||||
|
summary: "Endorsements",
|
||||||
|
description: "Returns endorsed accounts",
|
||||||
|
operationId: "PleromaAPI.AccountController.endorsements",
|
||||||
|
parameters: [with_relationships_param(), id_param()],
|
||||||
|
responses: %{
|
||||||
|
200 =>
|
||||||
|
Operation.response(
|
||||||
|
"Array of Accounts",
|
||||||
|
"application/json",
|
||||||
|
AccountOperation.array_of_accounts()
|
||||||
|
),
|
||||||
|
404 => Operation.response("Not Found", "application/json", ApiError)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def subscribe_operation do
|
def subscribe_operation do
|
||||||
%Operation{
|
%Operation{
|
||||||
tags: ["Account actions"],
|
tags: ["Account actions"],
|
||||||
|
|
31
lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex
Normal file
31
lib/pleroma/web/api_spec/operations/pleroma_app_operation.ex
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.PleromaAppOperation do
|
||||||
|
alias OpenApiSpex.Operation
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
alias Pleroma.Web.ApiSpec.Schemas.App
|
||||||
|
|
||||||
|
def open_api_operation(action) do
|
||||||
|
operation = String.to_existing_atom("#{action}_operation")
|
||||||
|
apply(__MODULE__, operation, [])
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec index_operation() :: Operation.t()
|
||||||
|
def index_operation do
|
||||||
|
%Operation{
|
||||||
|
tags: ["Applications"],
|
||||||
|
summary: "List applications",
|
||||||
|
description: "List the OAuth applications for the current user",
|
||||||
|
operationId: "AppController.index",
|
||||||
|
responses: %{
|
||||||
|
200 => Operation.response("Array of App", "application/json", array_of_apps())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp array_of_apps do
|
||||||
|
%Schema{type: :array, items: App, example: [App.schema().example]}
|
||||||
|
end
|
||||||
|
end
|
33
lib/pleroma/web/api_spec/schemas/app.ex
Normal file
33
lib/pleroma/web/api_spec/schemas/app.ex
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.ApiSpec.Schemas.App do
|
||||||
|
alias OpenApiSpex.Schema
|
||||||
|
|
||||||
|
require OpenApiSpex
|
||||||
|
|
||||||
|
OpenApiSpex.schema(%{
|
||||||
|
title: "App",
|
||||||
|
description: "Response schema for an app",
|
||||||
|
type: :object,
|
||||||
|
properties: %{
|
||||||
|
id: %Schema{type: :string},
|
||||||
|
name: %Schema{type: :string},
|
||||||
|
client_id: %Schema{type: :string},
|
||||||
|
client_secret: %Schema{type: :string},
|
||||||
|
redirect_uri: %Schema{type: :string},
|
||||||
|
vapid_key: %Schema{type: :string},
|
||||||
|
website: %Schema{type: :string, nullable: true}
|
||||||
|
},
|
||||||
|
example: %{
|
||||||
|
"id" => "123",
|
||||||
|
"name" => "My App",
|
||||||
|
"client_id" => "TWhM-tNSuncnqN7DBJmoyeLnk6K3iJJ71KKXxgL1hPM",
|
||||||
|
"client_secret" => "ZEaFUFmF0umgBX1qKJDjaU99Q31lDkOU8NutzTOoliw",
|
||||||
|
"vapid_key" =>
|
||||||
|
"BCk-QqERU0q-CfYZjcuB6lnyyOYfJ2AifKqfeGIm7Z-HiTU5T9eTG5GxVA0_OH5mMlI4UkkDTpaZwozy0TzdZ2M=",
|
||||||
|
"website" => "https://myapp.com/"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
end
|
|
@ -117,7 +117,8 @@ def follow(follower, followed) do
|
||||||
def unfollow(follower, unfollowed) do
|
def unfollow(follower, unfollowed) do
|
||||||
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
|
with {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),
|
||||||
{:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed),
|
{:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed),
|
||||||
{:ok, _subscription} <- User.unsubscribe(follower, unfollowed) do
|
{:ok, _subscription} <- User.unsubscribe(follower, unfollowed),
|
||||||
|
{:ok, _endorsement} <- User.unendorse(follower, unfollowed) do
|
||||||
{:ok, follower}
|
{:ok, follower}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,7 +32,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
|
|
||||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
plug(:skip_auth when action == :create)
|
plug(:skip_auth when action in [:create, :lookup])
|
||||||
|
|
||||||
plug(:skip_public_check when action in [:show, :statuses])
|
plug(:skip_public_check when action in [:show, :statuses])
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
plug(
|
plug(
|
||||||
OAuthScopesPlug,
|
OAuthScopesPlug,
|
||||||
%{scopes: ["write:accounts"]}
|
%{scopes: ["write:accounts"]}
|
||||||
when action in [:update_credentials, :note]
|
when action in [:update_credentials, :note, :endorse, :unendorse]
|
||||||
)
|
)
|
||||||
|
|
||||||
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
|
plug(OAuthScopesPlug, %{scopes: ["read:lists"]} when action == :lists)
|
||||||
|
@ -84,7 +84,9 @@ defmodule Pleroma.Web.MastodonAPI.AccountController do
|
||||||
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
plug(OAuthScopesPlug, %{scopes: ["follow", "write:mutes"]} when action in [:mute, :unmute])
|
||||||
|
|
||||||
@relationship_actions [:follow, :unfollow]
|
@relationship_actions [:follow, :unfollow]
|
||||||
@needs_account ~W(followers following lists follow unfollow mute unmute block unblock note)a
|
@needs_account ~W(
|
||||||
|
followers following lists follow unfollow mute unmute block unblock note endorse unendorse
|
||||||
|
)a
|
||||||
|
|
||||||
plug(
|
plug(
|
||||||
RateLimiter,
|
RateLimiter,
|
||||||
|
@ -451,6 +453,24 @@ def note(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "POST /api/v1/accounts/:id/pin"
|
||||||
|
def endorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
|
||||||
|
with {:ok, _user_relationships} <- User.endorse(endorser, endorsed) do
|
||||||
|
render(conn, "relationship.json", user: endorser, target: endorsed)
|
||||||
|
else
|
||||||
|
{:error, message} -> json_response(conn, :bad_request, %{error: message})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "POST /api/v1/accounts/:id/unpin"
|
||||||
|
def unendorse(%{assigns: %{user: endorser, account: endorsed}} = conn, _params) do
|
||||||
|
with {:ok, _user_relationships} <- User.unendorse(endorser, endorsed) do
|
||||||
|
render(conn, "relationship.json", user: endorser, target: endorsed)
|
||||||
|
else
|
||||||
|
{:error, message} -> json_response(conn, :forbidden, %{error: message})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/follows"
|
@doc "POST /api/v1/follows"
|
||||||
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
|
def follow_by_uri(%{body_params: %{uri: uri}} = conn, _) do
|
||||||
case User.get_cached_by_nickname(uri) do
|
case User.get_cached_by_nickname(uri) do
|
||||||
|
@ -493,8 +513,33 @@ def blocks(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> render("index.json", users: users, for: user, as: :user)
|
|> render("index.json", users: users, for: user, as: :user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "GET /api/v1/accounts/lookup"
|
||||||
|
def lookup(conn, %{acct: nickname} = _params) do
|
||||||
|
with %User{} = user <- User.get_by_nickname(nickname) do
|
||||||
|
render(conn, "show.json",
|
||||||
|
user: user,
|
||||||
|
skip_visibility_check: true
|
||||||
|
)
|
||||||
|
else
|
||||||
|
error -> user_visibility_error(conn, error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/endorsements"
|
@doc "GET /api/v1/endorsements"
|
||||||
def endorsements(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
def endorsements(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
users =
|
||||||
|
user
|
||||||
|
|> User.endorsed_users_relation(_restrict_deactivated = true)
|
||||||
|
|> Pleroma.Repo.all()
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> render("index.json",
|
||||||
|
users: users,
|
||||||
|
for: user,
|
||||||
|
as: :user,
|
||||||
|
embed_relationships: embed_relationships?(params)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
@doc "GET /api/v1/identity_proofs"
|
@doc "GET /api/v1/identity_proofs"
|
||||||
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
def identity_proofs(conn, params), do: MastodonAPIController.empty_array(conn, params)
|
||||||
|
|
|
@ -10,7 +10,9 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||||
|
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Maps
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.App
|
alias Pleroma.Web.OAuth.App
|
||||||
alias Pleroma.Web.OAuth.Scopes
|
alias Pleroma.Web.OAuth.Scopes
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
|
@ -26,11 +28,13 @@ defmodule Pleroma.Web.MastodonAPI.AppController do
|
||||||
@doc "POST /api/v1/apps"
|
@doc "POST /api/v1/apps"
|
||||||
def create(%{body_params: params} = conn, _params) do
|
def create(%{body_params: params} = conn, _params) do
|
||||||
scopes = Scopes.fetch_scopes(params, ["read"])
|
scopes = Scopes.fetch_scopes(params, ["read"])
|
||||||
|
user_id = get_user_id(conn)
|
||||||
|
|
||||||
app_attrs =
|
app_attrs =
|
||||||
params
|
params
|
||||||
|> Map.take([:client_name, :redirect_uris, :website])
|
|> Map.take([:client_name, :redirect_uris, :website])
|
||||||
|> Map.put(:scopes, scopes)
|
|> Map.put(:scopes, scopes)
|
||||||
|
|> Maps.put_if_present(:user_id, user_id)
|
||||||
|
|
||||||
with cs <- App.register_changeset(%App{}, app_attrs),
|
with cs <- App.register_changeset(%App{}, app_attrs),
|
||||||
{:ok, app} <- Repo.insert(cs) do
|
{:ok, app} <- Repo.insert(cs) do
|
||||||
|
@ -38,6 +42,9 @@ def create(%{body_params: params} = conn, _params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_user_id(%{assigns: %{user: %User{id: user_id}}}), do: user_id
|
||||||
|
defp get_user_id(_conn), do: nil
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
GET /api/v1/apps/verify_credentials
|
GET /api/v1/apps/verify_credentials
|
||||||
Gets compact non-secret representation of the app. Supports app tokens and user tokens.
|
Gets compact non-secret representation of the app. Supports app tokens and user tokens.
|
||||||
|
|
|
@ -160,11 +160,18 @@ def render(
|
||||||
target,
|
target,
|
||||||
&User.muting_reblogs?(&1, &2)
|
&User.muting_reblogs?(&1, &2)
|
||||||
),
|
),
|
||||||
endorsed: false,
|
|
||||||
note:
|
note:
|
||||||
UserNote.show(
|
UserNote.show(
|
||||||
reading_user,
|
reading_user,
|
||||||
target
|
target
|
||||||
|
),
|
||||||
|
endorsed:
|
||||||
|
UserRelationship.exists?(
|
||||||
|
user_relationships,
|
||||||
|
:endorsement,
|
||||||
|
target,
|
||||||
|
reading_user,
|
||||||
|
&User.endorses?(&2, &1)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
|
@ -68,6 +68,9 @@ def features do
|
||||||
"shareable_emoji_packs",
|
"shareable_emoji_packs",
|
||||||
"multifetch",
|
"multifetch",
|
||||||
"pleroma:api/v1/notifications:include_types_filter",
|
"pleroma:api/v1/notifications:include_types_filter",
|
||||||
|
if Config.get([:activitypub, :blockers_visible]) do
|
||||||
|
"blockers_visible"
|
||||||
|
end,
|
||||||
if Config.get([:media_proxy, :enabled]) do
|
if Config.get([:media_proxy, :enabled]) do
|
||||||
"media_proxy"
|
"media_proxy"
|
||||||
end,
|
end,
|
||||||
|
|
|
@ -7,6 +7,7 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
|
@ -19,6 +20,8 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
field(:client_secret, :string)
|
field(:client_secret, :string)
|
||||||
field(:trusted, :boolean, default: false)
|
field(:trusted, :boolean, default: false)
|
||||||
|
|
||||||
|
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||||
|
|
||||||
has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
|
has_many(:oauth_authorizations, Pleroma.Web.OAuth.Authorization, on_delete: :delete_all)
|
||||||
has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
|
has_many(:oauth_tokens, Pleroma.Web.OAuth.Token, on_delete: :delete_all)
|
||||||
|
|
||||||
|
@ -27,7 +30,7 @@ defmodule Pleroma.Web.OAuth.App do
|
||||||
|
|
||||||
@spec changeset(t(), map()) :: Ecto.Changeset.t()
|
@spec changeset(t(), map()) :: Ecto.Changeset.t()
|
||||||
def changeset(struct, params) do
|
def changeset(struct, params) do
|
||||||
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted])
|
cast(struct, params, [:client_name, :redirect_uris, :scopes, :website, :trusted, :user_id])
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
|
@spec register_changeset(t(), map()) :: Ecto.Changeset.t()
|
||||||
|
@ -129,6 +132,12 @@ def search(params) do
|
||||||
{:ok, Repo.all(query), count}
|
{:ok, Repo.all(query), count}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec get_user_apps(User.t()) :: {:ok, [t()], non_neg_integer()}
|
||||||
|
def get_user_apps(%User{id: user_id}) do
|
||||||
|
from(a in __MODULE__, where: a.user_id == ^user_id)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
@spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
@spec destroy(pos_integer()) :: {:ok, t()} | {:error, Ecto.Changeset.t()}
|
||||||
def destroy(id) do
|
def destroy(id) do
|
||||||
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
with %__MODULE__{} = app <- Repo.get(__MODULE__, id) do
|
||||||
|
|
|
@ -6,7 +6,12 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
import Pleroma.Web.ControllerHelper,
|
import Pleroma.Web.ControllerHelper,
|
||||||
only: [json_response: 3, add_link_headers: 2, assign_account_by_id: 2]
|
only: [
|
||||||
|
json_response: 3,
|
||||||
|
add_link_headers: 2,
|
||||||
|
embed_relationships?: 1,
|
||||||
|
assign_account_by_id: 2
|
||||||
|
]
|
||||||
|
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -40,9 +45,18 @@ defmodule Pleroma.Web.PleromaAPI.AccountController do
|
||||||
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
|
%{scopes: ["read:favourites"], fallback: :proceed_unauthenticated} when action == :favourites
|
||||||
)
|
)
|
||||||
|
|
||||||
|
plug(
|
||||||
|
OAuthScopesPlug,
|
||||||
|
%{fallback: :proceed_unauthenticated, scopes: ["read:accounts"]}
|
||||||
|
when action == :endorsements
|
||||||
|
)
|
||||||
|
|
||||||
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
|
plug(RateLimiter, [name: :account_confirmation_resend] when action == :confirmation_resend)
|
||||||
|
|
||||||
plug(:assign_account_by_id when action in [:favourites, :subscribe, :unsubscribe])
|
plug(
|
||||||
|
:assign_account_by_id
|
||||||
|
when action in [:favourites, :endorsements, :subscribe, :unsubscribe]
|
||||||
|
)
|
||||||
|
|
||||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAccountOperation
|
||||||
|
|
||||||
|
@ -90,6 +104,22 @@ def favourites(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "GET /api/v1/pleroma/accounts/:id/endorsements"
|
||||||
|
def endorsements(%{assigns: %{user: for_user, account: user}} = conn, params) do
|
||||||
|
users =
|
||||||
|
user
|
||||||
|
|> User.endorsed_users_relation(_restrict_deactivated = true)
|
||||||
|
|> Pleroma.Repo.all()
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> render("index.json",
|
||||||
|
for: for_user,
|
||||||
|
users: users,
|
||||||
|
as: :user,
|
||||||
|
embed_relationships: embed_relationships?(params)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
|
@doc "POST /api/v1/pleroma/accounts/:id/subscribe"
|
||||||
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
|
def subscribe(%{assigns: %{user: user, account: subscription_target}} = conn, _params) do
|
||||||
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do
|
with {:ok, _subscription} <- User.subscribe(user, subscription_target) do
|
||||||
|
|
23
lib/pleroma/web/pleroma_api/controllers/app_controller.ex
Normal file
23
lib/pleroma/web/pleroma_api/controllers/app_controller.ex
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.AppController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Web.OAuth.App
|
||||||
|
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||||
|
|
||||||
|
plug(OAuthScopesPlug, %{scopes: ["follow", "read"]} when action in [:index])
|
||||||
|
|
||||||
|
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||||
|
|
||||||
|
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PleromaAppOperation
|
||||||
|
|
||||||
|
@doc "GET /api/v1/pleroma/apps"
|
||||||
|
def index(%{assigns: %{user: user}} = conn, _params) do
|
||||||
|
with apps <- App.get_user_apps(user) do
|
||||||
|
render(conn, "index.json", %{apps: apps})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
11
lib/pleroma/web/pleroma_api/views/app_view.ex
Normal file
11
lib/pleroma/web/pleroma_api/views/app_view.ex
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.PleromaAPI.AppView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
def render("index.json", %{apps: apps}) do
|
||||||
|
render_many(apps, Pleroma.Web.MastodonAPI.AppView, "show.json")
|
||||||
|
end
|
||||||
|
end
|
|
@ -399,6 +399,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
scope "/api/v1/pleroma", Pleroma.Web.PleromaAPI do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
|
|
||||||
|
get("/apps", AppController, :index)
|
||||||
get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index)
|
get("/statuses/:id/reactions/:emoji", EmojiReactionController, :index)
|
||||||
get("/statuses/:id/reactions", EmojiReactionController, :index)
|
get("/statuses/:id/reactions", EmojiReactionController, :index)
|
||||||
end
|
end
|
||||||
|
@ -443,6 +444,7 @@ defmodule Pleroma.Web.Router do
|
||||||
scope [] do
|
scope [] do
|
||||||
pipe_through(:api)
|
pipe_through(:api)
|
||||||
get("/accounts/:id/favourites", AccountController, :favourites)
|
get("/accounts/:id/favourites", AccountController, :favourites)
|
||||||
|
get("/accounts/:id/endorsements", AccountController, :endorsements)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope [] do
|
scope [] do
|
||||||
|
@ -489,6 +491,8 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/accounts/:id/mute", AccountController, :mute)
|
post("/accounts/:id/mute", AccountController, :mute)
|
||||||
post("/accounts/:id/unmute", AccountController, :unmute)
|
post("/accounts/:id/unmute", AccountController, :unmute)
|
||||||
post("/accounts/:id/note", AccountController, :note)
|
post("/accounts/:id/note", AccountController, :note)
|
||||||
|
post("/accounts/:id/pin", AccountController, :endorse)
|
||||||
|
post("/accounts/:id/unpin", AccountController, :unendorse)
|
||||||
|
|
||||||
get("/conversations", ConversationController, :index)
|
get("/conversations", ConversationController, :index)
|
||||||
post("/conversations/:id/read", ConversationController, :mark_as_read)
|
post("/conversations/:id/read", ConversationController, :mark_as_read)
|
||||||
|
@ -588,6 +592,8 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/accounts/search", SearchController, :account_search)
|
get("/accounts/search", SearchController, :account_search)
|
||||||
get("/search", SearchController, :search)
|
get("/search", SearchController, :search)
|
||||||
|
|
||||||
|
get("/accounts/lookup", AccountController, :lookup)
|
||||||
|
|
||||||
get("/accounts/:id/statuses", AccountController, :statuses)
|
get("/accounts/:id/statuses", AccountController, :statuses)
|
||||||
get("/accounts/:id/followers", AccountController, :followers)
|
get("/accounts/:id/followers", AccountController, :followers)
|
||||||
get("/accounts/:id/following", AccountController, :following)
|
get("/accounts/:id/following", AccountController, :following)
|
||||||
|
|
9
mix.exs
9
mix.exs
|
@ -150,8 +150,7 @@ defp deps do
|
||||||
git: "https://github.com/msantos/crypt.git",
|
git: "https://github.com/msantos/crypt.git",
|
||||||
ref: "f75cd55325e33cbea198fb41fe41871392f8fb76"},
|
ref: "f75cd55325e33cbea198fb41fe41871392f8fb76"},
|
||||||
{:cors_plug, "~> 2.0"},
|
{:cors_plug, "~> 2.0"},
|
||||||
{:web_push_encryption,
|
{:web_push_encryption, "~> 0.3.1"},
|
||||||
git: "https://github.com/lanodan/elixir-web-push-encryption.git", branch: "bugfix/otp-24"},
|
|
||||||
{:swoosh, "~> 1.0"},
|
{:swoosh, "~> 1.0"},
|
||||||
{:phoenix_swoosh, "~> 0.3"},
|
{:phoenix_swoosh, "~> 0.3"},
|
||||||
{:gen_smtp, "~> 0.13"},
|
{:gen_smtp, "~> 0.13"},
|
||||||
|
@ -159,7 +158,7 @@ defp deps do
|
||||||
{:floki, "~> 0.27"},
|
{:floki, "~> 0.27"},
|
||||||
{:timex, "~> 3.6"},
|
{:timex, "~> 3.6"},
|
||||||
{:ueberauth, "~> 0.4"},
|
{:ueberauth, "~> 0.4"},
|
||||||
{:linkify, "~> 0.5.1"},
|
{:linkify, "~> 0.5.2"},
|
||||||
{:http_signatures, "~> 0.1.1"},
|
{:http_signatures, "~> 0.1.1"},
|
||||||
{:telemetry, "~> 0.3"},
|
{:telemetry, "~> 0.3"},
|
||||||
{:poolboy, "~> 1.5"},
|
{:poolboy, "~> 1.5"},
|
||||||
|
@ -184,9 +183,7 @@ defp deps do
|
||||||
{:ex_const, "~> 0.2"},
|
{:ex_const, "~> 0.2"},
|
||||||
{:plug_static_index_html, "~> 1.0.0"},
|
{:plug_static_index_html, "~> 1.0.0"},
|
||||||
{:flake_id, "~> 0.1.0"},
|
{:flake_id, "~> 0.1.0"},
|
||||||
{:concurrent_limiter,
|
{:concurrent_limiter, "~> 0.1.1"},
|
||||||
git: "https://gitlab.com/soapbox-pub/elixir-libraries/concurrent_limiter.git",
|
|
||||||
ref: "d81be41024569330f296fc472e24198d7499ba78"},
|
|
||||||
{:remote_ip,
|
{:remote_ip,
|
||||||
git: "https://git.pleroma.social/pleroma/remote_ip.git",
|
git: "https://git.pleroma.social/pleroma/remote_ip.git",
|
||||||
ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"},
|
ref: "b647d0deecaa3acb140854fe4bda5b7e1dc6d1c8"},
|
||||||
|
|
6
mix.lock
6
mix.lock
|
@ -14,7 +14,7 @@
|
||||||
"certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"},
|
"certifi": {:hex, :certifi, "2.8.0", "d4fb0a6bb20b7c9c3643e22507e42f356ac090a1dcea9ab99e27e0376d695eba", [:rebar3], [], "hexpm", "6ac7efc1c6f8600b08d625292d4bbf584e14847ce1b6b5c44d983d273e1097ea"},
|
||||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||||
"comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"},
|
"comeonin": {:hex, :comeonin, "5.3.2", "5c2f893d05c56ae3f5e24c1b983c2d5dfb88c6d979c9287a76a7feb1e1d8d646", [:mix], [], "hexpm", "d0993402844c49539aeadb3fe46a3c9bd190f1ecf86b6f9ebd71957534c95f04"},
|
||||||
"concurrent_limiter": {:git, "https://gitlab.com/soapbox-pub/elixir-libraries/concurrent_limiter.git", "d81be41024569330f296fc472e24198d7499ba78", [ref: "d81be41024569330f296fc472e24198d7499ba78"]},
|
"concurrent_limiter": {:hex, :concurrent_limiter, "0.1.1", "43ae1dc23edda1ab03dd66febc739c4ff710d047bb4d735754909f9a474ae01c", [:mix], [{:telemetry, "~> 0.3", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "53968ff238c0fbb4d7ed76ddb1af0be6f3b2f77909f6796e249e737c505a16eb"},
|
||||||
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
|
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
|
||||||
"cors_plug": {:hex, :cors_plug, "2.0.3", "316f806d10316e6d10f09473f19052d20ba0a0ce2a1d910ddf57d663dac402ae", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ee4ae1418e6ce117fc42c2ba3e6cbdca4e95ecd2fe59a05ec6884ca16d469aea"},
|
"cors_plug": {:hex, :cors_plug, "2.0.3", "316f806d10316e6d10f09473f19052d20ba0a0ce2a1d910ddf57d663dac402ae", [:mix], [{:plug, "~> 1.8", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "ee4ae1418e6ce117fc42c2ba3e6cbdca4e95ecd2fe59a05ec6884ca16d469aea"},
|
||||||
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
|
"cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"},
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
"jose": {:hex, :jose, "1.11.1", "59da64010c69aad6cde2f5b9248b896b84472e99bd18f246085b7b9fe435dcdb", [:mix, :rebar3], [], "hexpm", "078f6c9fb3cd2f4cfafc972c814261a7d1e8d2b3685c0a76eb87e158efff1ac5"},
|
"jose": {:hex, :jose, "1.11.1", "59da64010c69aad6cde2f5b9248b896b84472e99bd18f246085b7b9fe435dcdb", [:mix, :rebar3], [], "hexpm", "078f6c9fb3cd2f4cfafc972c814261a7d1e8d2b3685c0a76eb87e158efff1ac5"},
|
||||||
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
"jumper": {:hex, :jumper, "1.0.1", "3c00542ef1a83532b72269fab9f0f0c82bf23a35e27d278bfd9ed0865cecabff", [:mix], [], "hexpm", "318c59078ac220e966d27af3646026db9b5a5e6703cb2aa3e26bcfaba65b7433"},
|
||||||
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
|
"libring": {:hex, :libring, "1.4.0", "41246ba2f3fbc76b3971f6bce83119dfec1eee17e977a48d8a9cfaaf58c2a8d6", [:mix], [], "hexpm"},
|
||||||
"linkify": {:hex, :linkify, "0.5.1", "6dc415cbc948b2f6ecec7cb226aab7ba9d3a1815bb501ae33e042334d707ecee", [:mix], [], "hexpm", "a3128c7e22fada4aa7214009501d8131e1fa3faf2f0a68b33dba379dc84ff944"},
|
"linkify": {:hex, :linkify, "0.5.2", "fb66be139fdf1656ecb31f78a93592724d1b78d960a1b3598bd661013ea0e3c7", [:mix], [], "hexpm", "8d71ac690218d8952c90cbeb63cb8cc33738bb230d8a56d487d9447f2a5eab86"},
|
||||||
"majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"},
|
"majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"},
|
||||||
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||||
|
@ -137,6 +137,6 @@
|
||||||
"ueberauth": {:hex, :ueberauth, "0.6.3", "d42ace28b870e8072cf30e32e385579c57b9cc96ec74fa1f30f30da9c14f3cc0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "afc293d8a1140d6591b53e3eaf415ca92842cb1d32fad3c450c6f045f7f91b60"},
|
"ueberauth": {:hex, :ueberauth, "0.6.3", "d42ace28b870e8072cf30e32e385579c57b9cc96ec74fa1f30f30da9c14f3cc0", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "afc293d8a1140d6591b53e3eaf415ca92842cb1d32fad3c450c6f045f7f91b60"},
|
||||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
|
||||||
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
|
"unsafe": {:hex, :unsafe, "1.0.1", "a27e1874f72ee49312e0a9ec2e0b27924214a05e3ddac90e91727bc76f8613d8", [:mix], [], "hexpm", "6c7729a2d214806450d29766abc2afaa7a2cbecf415be64f36a6691afebb50e5"},
|
||||||
"web_push_encryption": {:git, "https://github.com/lanodan/elixir-web-push-encryption.git", "026a043037a89db4da8f07560bc8f9c68bcf0cc0", [branch: "bugfix/otp-24"]},
|
"web_push_encryption": {:hex, :web_push_encryption, "0.3.1", "76d0e7375142dfee67391e7690e89f92578889cbcf2879377900b5620ee4708d", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.11.1", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "4f82b2e57622fb9337559058e8797cb0df7e7c9790793bdc4e40bc895f70e2a2"},
|
||||||
"websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []},
|
"websocket_client": {:git, "https://github.com/jeremyong/websocket_client.git", "9a6f65d05ebf2725d62fb19262b21f1805a59fbf", []},
|
||||||
}
|
}
|
||||||
|
|
11
priv/repo/migrations/20210818023112_add_user_id_to_apps.exs
Normal file
11
priv/repo/migrations/20210818023112_add_user_id_to_apps.exs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddUserIdToApps do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
alter table(:apps) do
|
||||||
|
add(:user_id, references(:users, type: :uuid, on_delete: :delete_all))
|
||||||
|
end
|
||||||
|
|
||||||
|
create_if_not_exists(index(:apps, [:user_id]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.UserRelationshipsTargetIdRelationshipTypeIndex do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create_if_not_exists(index(:user_relationships, [:target_id, :relationship_type]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/app.9a4c5ede37b2f0230836.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.fb9ee54b02db0c974e51.js></script><script type=text/javascript src=/static/js/app.ce97bd1883ee9dd7b809.js></script></body></html>
|
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,user-scalable=no"><!--server-generated-meta--><link rel=icon type=image/png href=/favicon.png><link href=/static/css/app.7d2d223f75c3a14b0991.css rel=stylesheet></head><body class=hidden><noscript>To use Pleroma, please enable JavaScript.</noscript><div id=app></div><script type=text/javascript src=/static/js/vendors~app.cea10ab53f3aa19fc30e.js></script><script type=text/javascript src=/static/js/app.6c972d84b60f601b01f8.js></script></body></html>
|
Binary file not shown.
1
priv/static/static/css/app.7d2d223f75c3a14b0991.css.map
Normal file
1
priv/static/static/css/app.7d2d223f75c3a14b0991.css.map
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
priv/static/static/js/10.02ffbc25214f297f720f.js
Normal file
BIN
priv/static/static/js/10.02ffbc25214f297f720f.js
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/12.5ca41e245bb40263bc7f.js
Normal file
BIN
priv/static/static/js/12.5ca41e245bb40263bc7f.js
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/14.4e05e7c284119777ecc5.js
Normal file
BIN
priv/static/static/js/14.4e05e7c284119777ecc5.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/14.4e05e7c284119777ecc5.js.map
Normal file
BIN
priv/static/static/js/14.4e05e7c284119777ecc5.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/15.23f179cc3adc903bb537.js.map
Normal file
BIN
priv/static/static/js/15.23f179cc3adc903bb537.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/16.43dd2c64dcb160dd96a6.js.map
Normal file
BIN
priv/static/static/js/16.43dd2c64dcb160dd96a6.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/17.d1deeeb81b7cab98b068.js.map
Normal file
BIN
priv/static/static/js/17.d1deeeb81b7cab98b068.js.map
Normal file
Binary file not shown.
BIN
priv/static/static/js/18.a4d5b399e228a6a45a7b.js
Normal file
BIN
priv/static/static/js/18.a4d5b399e228a6a45a7b.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/18.a4d5b399e228a6a45a7b.js.map
Normal file
BIN
priv/static/static/js/18.a4d5b399e228a6a45a7b.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/19.e513835c3274271258fa.js.map
Normal file
BIN
priv/static/static/js/19.e513835c3274271258fa.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/2.fec2056b00b4fa3921ba.js
Normal file
BIN
priv/static/static/js/2.fec2056b00b4fa3921ba.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/2.fec2056b00b4fa3921ba.js.map
Normal file
BIN
priv/static/static/js/2.fec2056b00b4fa3921ba.js.map
Normal file
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/20.683b112f4dcea887f707.js.map
Normal file
BIN
priv/static/static/js/20.683b112f4dcea887f707.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/21.b2844ccdcfc3c8191e8e.js.map
Normal file
BIN
priv/static/static/js/21.b2844ccdcfc3c8191e8e.js.map
Normal file
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/22.68c0a771d79e3383f5e8.js.map
Normal file
BIN
priv/static/static/js/22.68c0a771d79e3383f5e8.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/23.0b6cdf4c9dc52c4291c0.js.map
Normal file
BIN
priv/static/static/js/23.0b6cdf4c9dc52c4291c0.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/24.5cfb87799bd882b933dd.js.map
Normal file
BIN
priv/static/static/js/24.5cfb87799bd882b933dd.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/25.8185e4d775cea9fe47e1.js
Normal file
BIN
priv/static/static/js/25.8185e4d775cea9fe47e1.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/25.8185e4d775cea9fe47e1.js.map
Normal file
BIN
priv/static/static/js/25.8185e4d775cea9fe47e1.js.map
Normal file
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/26.34ec129dd8f860ce4a8e.js.map
Normal file
BIN
priv/static/static/js/26.34ec129dd8f860ce4a8e.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/27.0f4a5145681cfb5a896e.js.map
Normal file
BIN
priv/static/static/js/27.0f4a5145681cfb5a896e.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/28.75c01cd71372c39d5af8.js.map
Normal file
BIN
priv/static/static/js/28.75c01cd71372c39d5af8.js.map
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue