forked from AkkomaGang/akkoma
Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into develop
This commit is contained in:
commit
f0fc2da964
69 changed files with 2161 additions and 637 deletions
102
.gitlab-ci.yml
102
.gitlab-ci.yml
|
@ -16,6 +16,7 @@ stages:
|
||||||
- build
|
- build
|
||||||
- test
|
- test
|
||||||
- deploy
|
- deploy
|
||||||
|
- release
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- mix local.hex --force
|
- mix local.hex --force
|
||||||
|
@ -42,6 +43,7 @@ docs-build:
|
||||||
paths:
|
paths:
|
||||||
- priv/static/doc
|
- priv/static/doc
|
||||||
|
|
||||||
|
|
||||||
unit-testing:
|
unit-testing:
|
||||||
stage: test
|
stage: test
|
||||||
services:
|
services:
|
||||||
|
@ -140,3 +142,103 @@ stop_review_app:
|
||||||
- ssh-keyscan -H "pleroma.online" >> ~/.ssh/known_hosts
|
- ssh-keyscan -H "pleroma.online" >> ~/.ssh/known_hosts
|
||||||
- ssh -t dokku@pleroma.online -- --force apps:destroy "$CI_ENVIRONMENT_SLUG"
|
- ssh -t dokku@pleroma.online -- --force apps:destroy "$CI_ENVIRONMENT_SLUG"
|
||||||
- ssh -t dokku@pleroma.online -- --force postgres:destroy $(echo $CI_ENVIRONMENT_SLUG | sed -e 's/-/_/g')_db
|
- ssh -t dokku@pleroma.online -- --force postgres:destroy $(echo $CI_ENVIRONMENT_SLUG | sed -e 's/-/_/g')_db
|
||||||
|
|
||||||
|
amd64:
|
||||||
|
stage: release
|
||||||
|
# TODO: Replace with upstream image when 1.9.0 comes out
|
||||||
|
image: rinpatch/elixir:1.9.0-rc.0
|
||||||
|
only: &release-only
|
||||||
|
- master@pleroma/pleroma
|
||||||
|
- develop@pleroma/pleroma
|
||||||
|
artifacts: &release-artifacts
|
||||||
|
name: "pleroma-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA-$CI_JOB_NAME"
|
||||||
|
paths:
|
||||||
|
- release/*
|
||||||
|
# Ideally it would be never for master branch and with the next commit for develop,
|
||||||
|
# but Gitlab does not support neither `only` for artifacts
|
||||||
|
# nor setting it to never from .gitlab-ci.yml
|
||||||
|
# nor expiring with the next commit
|
||||||
|
expire_in: 42 yrs
|
||||||
|
|
||||||
|
cache: &release-cache
|
||||||
|
key: $CI_COMMIT_REF_NAME-$CI_JOB_NAME
|
||||||
|
paths:
|
||||||
|
- deps
|
||||||
|
variables: &release-variables
|
||||||
|
MIX_ENV: prod
|
||||||
|
before_script: &before-release
|
||||||
|
- echo "import Mix.Config" > config/prod.secret.exs
|
||||||
|
- mix local.hex --force
|
||||||
|
- mix local.rebar --force
|
||||||
|
script: &release
|
||||||
|
- mix deps.get --only prod
|
||||||
|
- mkdir release
|
||||||
|
- mix release --path release
|
||||||
|
|
||||||
|
|
||||||
|
amd64-musl:
|
||||||
|
stage: release
|
||||||
|
artifacts: *release-artifacts
|
||||||
|
only: *release-only
|
||||||
|
# TODO: Replace with upstream image when 1.9.0 comes out
|
||||||
|
image: rinpatch/elixir:1.9.0-rc.0-alpine
|
||||||
|
cache: *release-cache
|
||||||
|
variables: *release-variables
|
||||||
|
before_script: &before-release-musl
|
||||||
|
- apk add git gcc g++ musl-dev make
|
||||||
|
- echo "import Mix.Config" > config/prod.secret.exs
|
||||||
|
- mix local.hex --force
|
||||||
|
- mix local.rebar --force
|
||||||
|
script: *release
|
||||||
|
|
||||||
|
arm:
|
||||||
|
stage: release
|
||||||
|
artifacts: *release-artifacts
|
||||||
|
only: *release-only
|
||||||
|
tags:
|
||||||
|
- arm32
|
||||||
|
# TODO: Replace with upstream image when 1.9.0 comes out
|
||||||
|
image: rinpatch/elixir:1.9.0-rc.0-arm
|
||||||
|
cache: *release-cache
|
||||||
|
variables: *release-variables
|
||||||
|
before_script: *before-release
|
||||||
|
script: *release
|
||||||
|
|
||||||
|
arm-musl:
|
||||||
|
stage: release
|
||||||
|
artifacts: *release-artifacts
|
||||||
|
only: *release-only
|
||||||
|
tags:
|
||||||
|
- arm32
|
||||||
|
# TODO: Replace with upstream image when 1.9.0 comes out
|
||||||
|
image: rinpatch/elixir:1.9.0-rc.0-arm-alpine
|
||||||
|
cache: *release-cache
|
||||||
|
variables: *release-variables
|
||||||
|
before_script: *before-release-musl
|
||||||
|
script: *release
|
||||||
|
|
||||||
|
arm64:
|
||||||
|
stage: release
|
||||||
|
artifacts: *release-artifacts
|
||||||
|
only: *release-only
|
||||||
|
tags:
|
||||||
|
- arm
|
||||||
|
# TODO: Replace with upstream image when 1.9.0 comes out
|
||||||
|
image: rinpatch/elixir:1.9.0-rc.0-arm64
|
||||||
|
cache: *release-cache
|
||||||
|
variables: *release-variables
|
||||||
|
before_script: *before-release
|
||||||
|
script: *release
|
||||||
|
|
||||||
|
arm64-musl:
|
||||||
|
stage: release
|
||||||
|
artifacts: *release-artifacts
|
||||||
|
only: *release-only
|
||||||
|
tags:
|
||||||
|
- arm
|
||||||
|
# TODO: Replace with upstream image when 1.9.0 comes out
|
||||||
|
image: rinpatch/elixir:1.9.0-rc.0-arm64-alpine
|
||||||
|
cache: *release-cache
|
||||||
|
variables: *release-variables
|
||||||
|
before_script: *before-release-musl
|
||||||
|
script: *release
|
||||||
|
|
|
@ -19,6 +19,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Mix Tasks: `mix pleroma.database remove_embedded_objects`
|
- Mix Tasks: `mix pleroma.database remove_embedded_objects`
|
||||||
- Mix Tasks: `mix pleroma.database update_users_following_followers_counts`
|
- Mix Tasks: `mix pleroma.database update_users_following_followers_counts`
|
||||||
- Mix Tasks: `mix pleroma.user toggle_confirmed`
|
- Mix Tasks: `mix pleroma.user toggle_confirmed`
|
||||||
|
- Mix Tasks: `mix pleroma.config migrate_to_db`
|
||||||
|
- Mix Tasks: `mix pleroma.config migrate_from_db`
|
||||||
- Federation: Support for `Question` and `Answer` objects
|
- Federation: Support for `Question` and `Answer` objects
|
||||||
- Federation: Support for reports
|
- Federation: Support for reports
|
||||||
- Configuration: `poll_limits` option
|
- Configuration: `poll_limits` option
|
||||||
|
@ -37,7 +39,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
- Admin API: added filters (role, tags, email, name) for users endpoint
|
- Admin API: added filters (role, tags, email, name) for users endpoint
|
||||||
- Admin API: Endpoints for managing reports
|
- Admin API: Endpoints for managing reports
|
||||||
- Admin API: Endpoints for deleting and changing the scope of individual reported statuses
|
- Admin API: Endpoints for deleting and changing the scope of individual reported statuses
|
||||||
|
- Admin API: Endpoints to view and change config settings.
|
||||||
- AdminFE: initial release with basic user management accessible at /pleroma/admin/
|
- AdminFE: initial release with basic user management accessible at /pleroma/admin/
|
||||||
|
- Mastodon API: Add chat token to `verify_credentials` response
|
||||||
|
- Mastodon API: Add background image setting to `update_credentials`
|
||||||
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
||||||
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension)
|
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension)
|
||||||
- Mastodon API: `/api/v1/pleroma/accounts/:id/favourites` (API extension)
|
- Mastodon API: `/api/v1/pleroma/accounts/:id/favourites` (API extension)
|
||||||
|
|
|
@ -245,7 +245,8 @@
|
||||||
healthcheck: false,
|
healthcheck: false,
|
||||||
remote_post_retention_days: 90,
|
remote_post_retention_days: 90,
|
||||||
skip_thread_containment: true,
|
skip_thread_containment: true,
|
||||||
limit_to_local_content: :unauthenticated
|
limit_to_local_content: :unauthenticated,
|
||||||
|
dynamic_configuration: false
|
||||||
|
|
||||||
config :pleroma, :markup,
|
config :pleroma, :markup,
|
||||||
# XXX - unfortunately, inline images must be enabled by default right now, because
|
# XXX - unfortunately, inline images must be enabled by default right now, because
|
||||||
|
|
|
@ -59,3 +59,6 @@
|
||||||
"!!! RUNNING IN LOCALHOST DEV MODE! !!!\nFEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs"
|
"!!! RUNNING IN LOCALHOST DEV MODE! !!!\nFEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs"
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if File.exists?("./config/dev.migrated.secret.exs"),
|
||||||
|
do: import_config("./config/dev.migrated.secret.exs")
|
||||||
|
|
|
@ -63,3 +63,6 @@
|
||||||
# Finally import the config/prod.secret.exs
|
# Finally import the config/prod.secret.exs
|
||||||
# which should be versioned separately.
|
# which should be versioned separately.
|
||||||
import_config "prod.secret.exs"
|
import_config "prod.secret.exs"
|
||||||
|
|
||||||
|
if File.exists?("./config/prod.migrated.secret.exs"),
|
||||||
|
do: import_config("./config/prod.migrated.secret.exs")
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
import Config
|
import Config
|
||||||
|
|
||||||
|
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
|
||||||
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
|
||||||
|
|
||||||
config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs"
|
config_path = System.get_env("PLEROMA_CONFIG_PATH") || "/etc/pleroma/config.exs"
|
||||||
|
|
||||||
if File.exists?(config_path) do
|
if File.exists?(config_path) do
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
total_user_limit: 3,
|
total_user_limit: 3,
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|
||||||
config :pleroma, :rate_limit, app_account_creation: {1000, 5}
|
config :pleroma, :rate_limit, app_account_creation: {10_000, 5}
|
||||||
|
|
||||||
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
||||||
|
|
||||||
|
|
|
@ -289,7 +289,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- `limit`: optional, the number of records to retrieve
|
- `limit`: optional, the number of records to retrieve
|
||||||
- `since_id`: optional, returns results that are more recent than the specified id
|
- `since_id`: optional, returns results that are more recent than the specified id
|
||||||
- `max_id`: optional, returns results that are older than the specified id
|
- `max_id`: optional, returns results that are older than the specified id
|
||||||
- Response:
|
- Response:
|
||||||
- On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin
|
- On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin
|
||||||
- On success: JSON, returns a list of reports, where:
|
- On success: JSON, returns a list of reports, where:
|
||||||
- `account`: the user who has been reported
|
- `account`: the user who has been reported
|
||||||
|
@ -443,7 +443,7 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- Params:
|
- Params:
|
||||||
- `id`
|
- `id`
|
||||||
- Response:
|
- Response:
|
||||||
- On failure:
|
- On failure:
|
||||||
- 403 Forbidden `{"error": "error_msg"}`
|
- 403 Forbidden `{"error": "error_msg"}`
|
||||||
- 404 Not Found `"Not found"`
|
- 404 Not Found `"Not found"`
|
||||||
- On success: JSON, Report object (see above)
|
- On success: JSON, Report object (see above)
|
||||||
|
@ -454,8 +454,8 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- Params:
|
- Params:
|
||||||
- `id`
|
- `id`
|
||||||
- `state`: required, the new state. Valid values are `open`, `closed` and `resolved`
|
- `state`: required, the new state. Valid values are `open`, `closed` and `resolved`
|
||||||
- Response:
|
- Response:
|
||||||
- On failure:
|
- On failure:
|
||||||
- 400 Bad Request `"Unsupported state"`
|
- 400 Bad Request `"Unsupported state"`
|
||||||
- 403 Forbidden `{"error": "error_msg"}`
|
- 403 Forbidden `{"error": "error_msg"}`
|
||||||
- 404 Not Found `"Not found"`
|
- 404 Not Found `"Not found"`
|
||||||
|
@ -467,10 +467,10 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- Params:
|
- Params:
|
||||||
- `id`
|
- `id`
|
||||||
- `status`: required, the message
|
- `status`: required, the message
|
||||||
- Response:
|
- Response:
|
||||||
- On failure:
|
- On failure:
|
||||||
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
- 400 Bad Request `"Invalid parameters"` when `status` is missing
|
||||||
- 403 Forbidden `{"error": "error_msg"}`
|
- 403 Forbidden `{"error": "error_msg"}`
|
||||||
- 404 Not Found `"Not found"`
|
- 404 Not Found `"Not found"`
|
||||||
- On success: JSON, created Mastodon Status entity
|
- On success: JSON, created Mastodon Status entity
|
||||||
|
|
||||||
|
@ -540,10 +540,10 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- `id`
|
- `id`
|
||||||
- `sensitive`: optional, valid values are `true` or `false`
|
- `sensitive`: optional, valid values are `true` or `false`
|
||||||
- `visibility`: optional, valid values are `public`, `private` and `unlisted`
|
- `visibility`: optional, valid values are `public`, `private` and `unlisted`
|
||||||
- Response:
|
- Response:
|
||||||
- On failure:
|
- On failure:
|
||||||
- 400 Bad Request `"Unsupported visibility"`
|
- 400 Bad Request `"Unsupported visibility"`
|
||||||
- 403 Forbidden `{"error": "error_msg"}`
|
- 403 Forbidden `{"error": "error_msg"}`
|
||||||
- 404 Not Found `"Not found"`
|
- 404 Not Found `"Not found"`
|
||||||
- On success: JSON, Mastodon Status entity
|
- On success: JSON, Mastodon Status entity
|
||||||
|
|
||||||
|
@ -552,8 +552,88 @@ Note: Available `:permission_group` is currently moderator and admin. 404 is ret
|
||||||
- Method `DELETE`
|
- Method `DELETE`
|
||||||
- Params:
|
- Params:
|
||||||
- `id`
|
- `id`
|
||||||
- Response:
|
- Response:
|
||||||
- On failure:
|
- On failure:
|
||||||
- 403 Forbidden `{"error": "error_msg"}`
|
- 403 Forbidden `{"error": "error_msg"}`
|
||||||
- 404 Not Found `"Not found"`
|
- 404 Not Found `"Not found"`
|
||||||
- On success: 200 OK `{}`
|
- On success: 200 OK `{}`
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/config`
|
||||||
|
### List config settings
|
||||||
|
- Method `GET`
|
||||||
|
- Params: none
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
configs: [
|
||||||
|
{
|
||||||
|
"key": string,
|
||||||
|
"value": string or {} or []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/config`
|
||||||
|
### Update config settings
|
||||||
|
Module name can be passed as string, which starts with `Pleroma`, e.g. `"Pleroma.Upload"`.
|
||||||
|
Atom or boolean value can be passed with `:` in the beginning, e.g. `":true"`, `":upload"`.
|
||||||
|
Integer with `i:`, e.g. `"i:150"`.
|
||||||
|
|
||||||
|
Compile time settings (need instance reboot):
|
||||||
|
- all settings by this keys:
|
||||||
|
- `:hackney_pools`
|
||||||
|
- `:chat`
|
||||||
|
- `Pleroma.Web.Endpoint`
|
||||||
|
- `Pleroma.Repo`
|
||||||
|
- part settings:
|
||||||
|
- `Pleroma.Captcha` -> `:seconds_valid`
|
||||||
|
- `Pleroma.Upload` -> `:proxy_remote`
|
||||||
|
- `:instance` -> `:upload_limit`
|
||||||
|
|
||||||
|
- Method `POST`
|
||||||
|
- Params:
|
||||||
|
- `configs` => [
|
||||||
|
- `key` (string)
|
||||||
|
- `value` (string, [], {})
|
||||||
|
- `delete` = true (optional, if parameter must be deleted)
|
||||||
|
]
|
||||||
|
|
||||||
|
- Request (example):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
configs: [
|
||||||
|
{
|
||||||
|
"key": "Pleroma.Upload",
|
||||||
|
"value": {
|
||||||
|
"uploader": "Pleroma.Uploaders.Local",
|
||||||
|
"filters": ["Pleroma.Upload.Filter.Dedupe"],
|
||||||
|
"link_name": ":true",
|
||||||
|
"proxy_remote": ":false",
|
||||||
|
"proxy_opts": {
|
||||||
|
"redirect_on_failure": ":false",
|
||||||
|
"max_body_length": "i:1048576",
|
||||||
|
"http": {
|
||||||
|
"follow_redirect": ":true",
|
||||||
|
"pool": ":upload"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
configs: [
|
||||||
|
{
|
||||||
|
"key": string,
|
||||||
|
"value": string or {} or []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -44,6 +44,7 @@ Has these additional fields under the `pleroma` object:
|
||||||
- `hide_followers`: boolean, true when the user has follower hiding enabled
|
- `hide_followers`: boolean, true when the user has follower hiding enabled
|
||||||
- `hide_follows`: boolean, true when the user has follow hiding enabled
|
- `hide_follows`: boolean, true when the user has follow hiding enabled
|
||||||
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
|
- `settings_store`: A generic map of settings for frontends. Opaque to the backend. Only returned in `verify_credentials` and `update_credentials`
|
||||||
|
- `chat_token`: The token needed for Pleroma chat. Only returned in `verify_credentials`
|
||||||
|
|
||||||
### Source
|
### Source
|
||||||
|
|
||||||
|
@ -84,6 +85,7 @@ Additional parameters can be added to the JSON body/Form data:
|
||||||
- `default_scope` - the scope returned under `privacy` key in Source subentity
|
- `default_scope` - the scope returned under `privacy` key in Source subentity
|
||||||
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
- `pleroma_settings_store` - Opaque user settings to be saved on the backend.
|
||||||
- `skip_thread_containment` - if true, skip filtering out broken threads
|
- `skip_thread_containment` - if true, skip filtering out broken threads
|
||||||
|
- `pleroma_background_image` - sets the background image of the user.
|
||||||
|
|
||||||
### Pleroma Settings Store
|
### Pleroma Settings Store
|
||||||
Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.
|
Pleroma has mechanism that allows frontends to save blobs of json for each user on the backend. This can be used to save frontend-specific settings for a user that the backend does not need to know about.
|
||||||
|
|
|
@ -114,6 +114,7 @@ config :pleroma, Pleroma.Emails.Mailer,
|
||||||
* `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database.
|
* `remote_post_retention_days`: The default amount of days to retain remote posts when pruning the database.
|
||||||
* `skip_thread_containment`: Skip filter out broken threads. The default is `false`.
|
* `skip_thread_containment`: Skip filter out broken threads. The default is `false`.
|
||||||
* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`.
|
* `limit_to_local_content`: Limit unauthenticated users to search for local statutes and users only. Possible values: `:unauthenticated`, `:all` and `false`. The default is `:unauthenticated`.
|
||||||
|
* `dynamic_configuration`: Allow transferring configuration to DB with the subsequent customization from Admin api.
|
||||||
|
|
||||||
|
|
||||||
## :logger
|
## :logger
|
||||||
|
|
68
lib/mix/tasks/pleroma/config.ex
Normal file
68
lib/mix/tasks/pleroma/config.ex
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
defmodule Mix.Tasks.Pleroma.Config do
|
||||||
|
use Mix.Task
|
||||||
|
alias Mix.Tasks.Pleroma.Common
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Web.AdminAPI.Config
|
||||||
|
@shortdoc "Manages the location of the config"
|
||||||
|
@moduledoc """
|
||||||
|
Manages the location of the config.
|
||||||
|
|
||||||
|
## Transfers config from file to DB.
|
||||||
|
|
||||||
|
mix pleroma.config migrate_to_db
|
||||||
|
|
||||||
|
## Transfers config from DB to file.
|
||||||
|
|
||||||
|
mix pleroma.config migrate_from_db ENV
|
||||||
|
"""
|
||||||
|
|
||||||
|
def run(["migrate_to_db"]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
if Pleroma.Config.get([:instance, :dynamic_configuration]) do
|
||||||
|
Application.get_all_env(:pleroma)
|
||||||
|
|> Enum.reject(fn {k, _v} -> k in [Pleroma.Repo, :env] end)
|
||||||
|
|> Enum.each(fn {k, v} ->
|
||||||
|
key = to_string(k) |> String.replace("Elixir.", "")
|
||||||
|
{:ok, _} = Config.update_or_create(%{key: key, value: v})
|
||||||
|
Mix.shell().info("#{key} is migrated.")
|
||||||
|
end)
|
||||||
|
|
||||||
|
Mix.shell().info("Settings migrated.")
|
||||||
|
else
|
||||||
|
Mix.shell().info(
|
||||||
|
"Migration is not allowed by config. You can change this behavior in instance settings."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["migrate_from_db", env]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
if Pleroma.Config.get([:instance, :dynamic_configuration]) do
|
||||||
|
config_path = "config/#{env}.migrated.secret.exs"
|
||||||
|
|
||||||
|
{:ok, file} = File.open(config_path, [:write])
|
||||||
|
|
||||||
|
Repo.all(Config)
|
||||||
|
|> Enum.each(fn config ->
|
||||||
|
mark = if String.starts_with?(config.key, "Pleroma."), do: ",", else: ":"
|
||||||
|
|
||||||
|
IO.write(
|
||||||
|
file,
|
||||||
|
"config :pleroma, #{config.key}#{mark} #{inspect(Config.from_binary(config.value))}\r\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
{:ok, _} = Repo.delete(config)
|
||||||
|
Mix.shell().info("#{config.key} deleted from DB.")
|
||||||
|
end)
|
||||||
|
|
||||||
|
File.close(file)
|
||||||
|
System.cmd("mix", ["format", config_path])
|
||||||
|
else
|
||||||
|
Mix.shell().info(
|
||||||
|
"Migration is not allowed by config. You can change this behavior in instance settings."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -55,15 +55,13 @@ defmodule Mix.Tasks.Pleroma.Emoji do
|
||||||
are extracted).
|
are extracted).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@default_manifest Pleroma.Config.get!([:emoji, :default_manifest])
|
|
||||||
|
|
||||||
def run(["ls-packs" | args]) do
|
def run(["ls-packs" | args]) do
|
||||||
Application.ensure_all_started(:hackney)
|
Application.ensure_all_started(:hackney)
|
||||||
|
|
||||||
{options, [], []} = parse_global_opts(args)
|
{options, [], []} = parse_global_opts(args)
|
||||||
|
|
||||||
manifest =
|
manifest =
|
||||||
fetch_manifest(if options[:manifest], do: options[:manifest], else: @default_manifest)
|
fetch_manifest(if options[:manifest], do: options[:manifest], else: default_manifest())
|
||||||
|
|
||||||
Enum.each(manifest, fn {name, info} ->
|
Enum.each(manifest, fn {name, info} ->
|
||||||
to_print = [
|
to_print = [
|
||||||
|
@ -88,7 +86,7 @@ def run(["get-packs" | args]) do
|
||||||
|
|
||||||
{options, pack_names, []} = parse_global_opts(args)
|
{options, pack_names, []} = parse_global_opts(args)
|
||||||
|
|
||||||
manifest_url = if options[:manifest], do: options[:manifest], else: @default_manifest
|
manifest_url = if options[:manifest], do: options[:manifest], else: default_manifest()
|
||||||
|
|
||||||
manifest = fetch_manifest(manifest_url)
|
manifest = fetch_manifest(manifest_url)
|
||||||
|
|
||||||
|
@ -298,4 +296,6 @@ defp client do
|
||||||
|
|
||||||
Tesla.client(middleware)
|
Tesla.client(middleware)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp default_manifest, do: Pleroma.Config.get!([:emoji, :default_manifest])
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,6 +30,7 @@ defmodule Mix.Tasks.Pleroma.Instance do
|
||||||
- `--dbuser DBUSER` - the user (aka role) to use for the database connection
|
- `--dbuser DBUSER` - the user (aka role) to use for the database connection
|
||||||
- `--dbpass DBPASS` - the password to use for the database connection
|
- `--dbpass DBPASS` - the password to use for the database connection
|
||||||
- `--indexable Y/N` - Allow/disallow indexing site by search engines
|
- `--indexable Y/N` - Allow/disallow indexing site by search engines
|
||||||
|
- `--db-configurable Y/N` - Allow/disallow configuring instance from admin part
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def run(["gen" | rest]) do
|
def run(["gen" | rest]) do
|
||||||
|
@ -48,7 +49,8 @@ def run(["gen" | rest]) do
|
||||||
dbname: :string,
|
dbname: :string,
|
||||||
dbuser: :string,
|
dbuser: :string,
|
||||||
dbpass: :string,
|
dbpass: :string,
|
||||||
indexable: :string
|
indexable: :string,
|
||||||
|
db_configurable: :string
|
||||||
],
|
],
|
||||||
aliases: [
|
aliases: [
|
||||||
o: :output,
|
o: :output,
|
||||||
|
@ -101,6 +103,14 @@ def run(["gen" | rest]) do
|
||||||
"y"
|
"y"
|
||||||
) === "y"
|
) === "y"
|
||||||
|
|
||||||
|
db_configurable? =
|
||||||
|
Common.get_option(
|
||||||
|
options,
|
||||||
|
:db_configurable,
|
||||||
|
"Do you want to be able to configure instance from admin part? (y/n)",
|
||||||
|
"y"
|
||||||
|
) === "y"
|
||||||
|
|
||||||
dbhost =
|
dbhost =
|
||||||
Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
|
Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
|
||||||
|
|
||||||
|
@ -144,7 +154,8 @@ def run(["gen" | rest]) do
|
||||||
secret: secret,
|
secret: secret,
|
||||||
signing_salt: signing_salt,
|
signing_salt: signing_salt,
|
||||||
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
||||||
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
|
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false),
|
||||||
|
db_configurable?: db_configurable?
|
||||||
)
|
)
|
||||||
|
|
||||||
result_psql =
|
result_psql =
|
||||||
|
|
|
@ -16,7 +16,8 @@ config :pleroma, :instance,
|
||||||
notify_email: "<%= notify_email %>",
|
notify_email: "<%= notify_email %>",
|
||||||
limit: 5000,
|
limit: 5000,
|
||||||
registrations_open: true,
|
registrations_open: true,
|
||||||
dedupe_media: false
|
dedupe_media: false,
|
||||||
|
dynamic_configuration: <%= db_configurable? %>
|
||||||
|
|
||||||
config :pleroma, :media_proxy,
|
config :pleroma, :media_proxy,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
|
|
@ -31,6 +31,7 @@ def start(_type, _args) do
|
||||||
[
|
[
|
||||||
# Start the Ecto repository
|
# Start the Ecto repository
|
||||||
%{id: Pleroma.Repo, start: {Pleroma.Repo, :start_link, []}, type: :supervisor},
|
%{id: Pleroma.Repo, start: {Pleroma.Repo, :start_link, []}, type: :supervisor},
|
||||||
|
%{id: Pleroma.Config.TransferTask, start: {Pleroma.Config.TransferTask, :start_link, []}},
|
||||||
%{id: Pleroma.Emoji, start: {Pleroma.Emoji, :start_link, []}},
|
%{id: Pleroma.Emoji, start: {Pleroma.Emoji, :start_link, []}},
|
||||||
%{id: Pleroma.Captcha, start: {Pleroma.Captcha, :start_link, []}},
|
%{id: Pleroma.Captcha, start: {Pleroma.Captcha, :start_link, []}},
|
||||||
%{
|
%{
|
||||||
|
@ -174,7 +175,6 @@ defp setup_instrumenters do
|
||||||
Pleroma.Repo.Instrumenter.setup()
|
Pleroma.Repo.Instrumenter.setup()
|
||||||
end
|
end
|
||||||
|
|
||||||
Prometheus.Registry.register_collector(:prometheus_process_collector)
|
|
||||||
Pleroma.Web.Endpoint.MetricsExporter.setup()
|
Pleroma.Web.Endpoint.MetricsExporter.setup()
|
||||||
Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
|
Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
|
||||||
Pleroma.Web.Endpoint.Instrumenter.setup()
|
Pleroma.Web.Endpoint.Instrumenter.setup()
|
||||||
|
@ -187,7 +187,7 @@ def enabled_hackney_pools do
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end ++
|
end ++
|
||||||
if Pleroma.Config.get([Pleroma.Uploader, :proxy_remote]) do
|
if Pleroma.Config.get([Pleroma.Upload, :proxy_remote]) do
|
||||||
[:upload]
|
[:upload]
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
|
|
42
lib/pleroma/config/transfer_task.ex
Normal file
42
lib/pleroma/config/transfer_task.ex
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
defmodule Pleroma.Config.TransferTask do
|
||||||
|
use Task
|
||||||
|
alias Pleroma.Web.AdminAPI.Config
|
||||||
|
|
||||||
|
def start_link do
|
||||||
|
load_and_update_env()
|
||||||
|
if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Pleroma.Repo)
|
||||||
|
:ignore
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_and_update_env do
|
||||||
|
if Pleroma.Config.get([:instance, :dynamic_configuration]) and
|
||||||
|
Ecto.Adapters.SQL.table_exists?(Pleroma.Repo, "config") do
|
||||||
|
Pleroma.Repo.all(Config)
|
||||||
|
|> Enum.each(&update_env(&1))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp update_env(setting) do
|
||||||
|
try do
|
||||||
|
key =
|
||||||
|
if String.starts_with?(setting.key, "Pleroma.") do
|
||||||
|
"Elixir." <> setting.key
|
||||||
|
else
|
||||||
|
setting.key
|
||||||
|
end
|
||||||
|
|
||||||
|
Application.put_env(
|
||||||
|
:pleroma,
|
||||||
|
String.to_existing_atom(key),
|
||||||
|
Config.from_binary(setting.value)
|
||||||
|
)
|
||||||
|
rescue
|
||||||
|
e ->
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
Logger.warn(
|
||||||
|
"updating env causes error, key: #{inspect(setting.key)}, error: #{inspect(e)}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -22,7 +22,6 @@ defmodule Pleroma.Emoji do
|
||||||
|
|
||||||
@ets __MODULE__.Ets
|
@ets __MODULE__.Ets
|
||||||
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
|
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
|
||||||
@groups Pleroma.Config.get([:emoji, :groups])
|
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def start_link do
|
def start_link do
|
||||||
|
@ -87,6 +86,8 @@ defp load do
|
||||||
"emoji"
|
"emoji"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
emoji_groups = Pleroma.Config.get([:emoji, :groups])
|
||||||
|
|
||||||
case File.ls(emoji_dir_path) do
|
case File.ls(emoji_dir_path) do
|
||||||
{:error, :enoent} ->
|
{:error, :enoent} ->
|
||||||
# The custom emoji directory doesn't exist,
|
# The custom emoji directory doesn't exist,
|
||||||
|
@ -118,7 +119,7 @@ defp load do
|
||||||
emojis =
|
emojis =
|
||||||
Enum.flat_map(
|
Enum.flat_map(
|
||||||
packs,
|
packs,
|
||||||
fn pack -> load_pack(Path.join(emoji_dir_path, pack)) end
|
fn pack -> load_pack(Path.join(emoji_dir_path, pack), emoji_groups) end
|
||||||
)
|
)
|
||||||
|
|
||||||
true = :ets.insert(@ets, emojis)
|
true = :ets.insert(@ets, emojis)
|
||||||
|
@ -129,9 +130,9 @@ defp load do
|
||||||
shortcode_globs = Pleroma.Config.get([:emoji, :shortcode_globs], [])
|
shortcode_globs = Pleroma.Config.get([:emoji, :shortcode_globs], [])
|
||||||
|
|
||||||
emojis =
|
emojis =
|
||||||
(load_from_file("config/emoji.txt") ++
|
(load_from_file("config/emoji.txt", emoji_groups) ++
|
||||||
load_from_file("config/custom_emoji.txt") ++
|
load_from_file("config/custom_emoji.txt", emoji_groups) ++
|
||||||
load_from_globs(shortcode_globs))
|
load_from_globs(shortcode_globs, emoji_groups))
|
||||||
|> Enum.reject(fn value -> value == nil end)
|
|> Enum.reject(fn value -> value == nil end)
|
||||||
|
|
||||||
true = :ets.insert(@ets, emojis)
|
true = :ets.insert(@ets, emojis)
|
||||||
|
@ -139,13 +140,13 @@ defp load do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
defp load_pack(pack_dir) do
|
defp load_pack(pack_dir, emoji_groups) do
|
||||||
pack_name = Path.basename(pack_dir)
|
pack_name = Path.basename(pack_dir)
|
||||||
|
|
||||||
emoji_txt = Path.join(pack_dir, "emoji.txt")
|
emoji_txt = Path.join(pack_dir, "emoji.txt")
|
||||||
|
|
||||||
if File.exists?(emoji_txt) do
|
if File.exists?(emoji_txt) do
|
||||||
load_from_file(emoji_txt)
|
load_from_file(emoji_txt, emoji_groups)
|
||||||
else
|
else
|
||||||
Logger.info(
|
Logger.info(
|
||||||
"No emoji.txt found for pack \"#{pack_name}\", assuming all .png files are emoji"
|
"No emoji.txt found for pack \"#{pack_name}\", assuming all .png files are emoji"
|
||||||
|
@ -155,7 +156,7 @@ defp load_pack(pack_dir) do
|
||||||
|> Enum.map(fn {shortcode, rel_file} ->
|
|> Enum.map(fn {shortcode, rel_file} ->
|
||||||
filename = Path.join("/emoji/#{pack_name}", rel_file)
|
filename = Path.join("/emoji/#{pack_name}", rel_file)
|
||||||
|
|
||||||
{shortcode, filename, [to_string(match_extra(@groups, filename))]}
|
{shortcode, filename, [to_string(match_extra(emoji_groups, filename))]}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -184,21 +185,21 @@ def find_all_emoji(dir, exts) do
|
||||||
|> Enum.filter(fn f -> Path.extname(f) in exts end)
|
|> Enum.filter(fn f -> Path.extname(f) in exts end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp load_from_file(file) do
|
defp load_from_file(file, emoji_groups) do
|
||||||
if File.exists?(file) do
|
if File.exists?(file) do
|
||||||
load_from_file_stream(File.stream!(file))
|
load_from_file_stream(File.stream!(file), emoji_groups)
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp load_from_file_stream(stream) do
|
defp load_from_file_stream(stream, emoji_groups) do
|
||||||
stream
|
stream
|
||||||
|> Stream.map(&String.trim/1)
|
|> Stream.map(&String.trim/1)
|
||||||
|> Stream.map(fn line ->
|
|> Stream.map(fn line ->
|
||||||
case String.split(line, ~r/,\s*/) do
|
case String.split(line, ~r/,\s*/) do
|
||||||
[name, file] ->
|
[name, file] ->
|
||||||
{name, file, [to_string(match_extra(@groups, file))]}
|
{name, file, [to_string(match_extra(emoji_groups, file))]}
|
||||||
|
|
||||||
[name, file | tags] ->
|
[name, file | tags] ->
|
||||||
{name, file, tags}
|
{name, file, tags}
|
||||||
|
@ -210,7 +211,7 @@ defp load_from_file_stream(stream) do
|
||||||
|> Enum.to_list()
|
|> Enum.to_list()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp load_from_globs(globs) do
|
defp load_from_globs(globs, emoji_groups) do
|
||||||
static_path = Path.join(:code.priv_dir(:pleroma), "static")
|
static_path = Path.join(:code.priv_dir(:pleroma), "static")
|
||||||
|
|
||||||
paths =
|
paths =
|
||||||
|
@ -221,7 +222,7 @@ defp load_from_globs(globs) do
|
||||||
|> Enum.concat()
|
|> Enum.concat()
|
||||||
|
|
||||||
Enum.map(paths, fn path ->
|
Enum.map(paths, fn path ->
|
||||||
tag = match_extra(@groups, Path.join("/", Path.relative_to(path, static_path)))
|
tag = match_extra(emoji_groups, Path.join("/", Path.relative_to(path, static_path)))
|
||||||
shortcode = Path.basename(path, Path.extname(path))
|
shortcode = Path.basename(path, Path.extname(path))
|
||||||
external_path = Path.join("/", Path.relative_to(path, static_path))
|
external_path = Path.join("/", Path.relative_to(path, static_path))
|
||||||
{shortcode, external_path, [to_string(tag)]}
|
{shortcode, external_path, [to_string(tag)]}
|
||||||
|
|
27
lib/pleroma/helpers/uri_helper.ex
Normal file
27
lib/pleroma/helpers/uri_helper.ex
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Helpers.UriHelper do
|
||||||
|
def append_uri_params(uri, appended_params) do
|
||||||
|
uri = URI.parse(uri)
|
||||||
|
appended_params = for {k, v} <- appended_params, into: %{}, do: {to_string(k), v}
|
||||||
|
existing_params = URI.query_decoder(uri.query || "") |> Enum.into(%{})
|
||||||
|
updated_params_keys = Enum.uniq(Map.keys(existing_params) ++ Map.keys(appended_params))
|
||||||
|
|
||||||
|
updated_params =
|
||||||
|
for k <- updated_params_keys, do: {k, appended_params[k] || existing_params[k]}
|
||||||
|
|
||||||
|
uri
|
||||||
|
|> Map.put(:query, URI.encode_query(updated_params))
|
||||||
|
|> URI.to_string()
|
||||||
|
end
|
||||||
|
|
||||||
|
def append_param_if_present(%{} = params, param_name, param_value) do
|
||||||
|
if param_value do
|
||||||
|
Map.put(params, param_name, param_value)
|
||||||
|
else
|
||||||
|
params
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -89,7 +89,7 @@ def extract_first_external_url(object, content) do
|
||||||
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||||
result =
|
result =
|
||||||
content
|
content
|
||||||
|> Floki.filter_out("a.mention")
|
|> Floki.filter_out("a.mention,a.hashtag")
|
||||||
|> Floki.attribute("a", "href")
|
|> Floki.attribute("a", "href")
|
||||||
|> Enum.at(0)
|
|> Enum.at(0)
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ def set_consistently_unreachable(url_or_host),
|
||||||
|
|
||||||
def reachability_datetime_threshold do
|
def reachability_datetime_threshold do
|
||||||
federation_reachability_timeout_days =
|
federation_reachability_timeout_days =
|
||||||
Pleroma.Config.get(:instance)[:federation_reachability_timeout_days] || 0
|
Pleroma.Config.get([:instance, :federation_reachability_timeout_days], 0)
|
||||||
|
|
||||||
if federation_reachability_timeout_days > 0 do
|
if federation_reachability_timeout_days > 0 do
|
||||||
NaiveDateTime.add(
|
NaiveDateTime.add(
|
||||||
|
|
|
@ -13,6 +13,8 @@ defmodule Pleroma.Notification do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Web.Push
|
||||||
|
alias Pleroma.Web.Streamer
|
||||||
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
@ -145,8 +147,9 @@ def create_notification(%Activity{} = activity, %User{} = user) do
|
||||||
unless skip?(activity, user) do
|
unless skip?(activity, user) do
|
||||||
notification = %Notification{user_id: user.id, activity: activity}
|
notification = %Notification{user_id: user.id, activity: activity}
|
||||||
{:ok, notification} = Repo.insert(notification)
|
{:ok, notification} = Repo.insert(notification)
|
||||||
Pleroma.Web.Streamer.stream("user", notification)
|
Streamer.stream("user", notification)
|
||||||
Pleroma.Web.Push.send(notification)
|
Streamer.stream("user:notification", notification)
|
||||||
|
Push.send(notification)
|
||||||
notification
|
notification
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Object.Containment do
|
defmodule Pleroma.Object.Containment do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
This module contains some useful functions for containing objects to specific
|
This module contains some useful functions for containing objects to specific
|
||||||
|
|
|
@ -85,6 +85,9 @@ def fetch_and_contain_remote_object_from_id(id) do
|
||||||
:ok <- Containment.contain_origin_from_id(id, data) do
|
:ok <- Containment.contain_origin_from_id(id, data) do
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
else
|
else
|
||||||
|
{:ok, %{status: code}} when code in [404, 410] ->
|
||||||
|
{:error, "Object has been deleted"}
|
||||||
|
|
||||||
e ->
|
e ->
|
||||||
{:error, e}
|
{:error, e}
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,13 +14,20 @@ defmodule Pleroma.Plugs.RateLimiter do
|
||||||
|
|
||||||
It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
|
It is also possible to have different limits for unauthenticated and authenticated users: the keyword value must be a list of two tuples where the first one is a config for unauthenticated users and the second one is for authenticated.
|
||||||
|
|
||||||
|
To disable a limiter set its value to `nil`.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
config :pleroma, :rate_limit,
|
config :pleroma, :rate_limit,
|
||||||
one: {1000, 10},
|
one: {1000, 10},
|
||||||
two: [{10_000, 10}, {10_000, 50}]
|
two: [{10_000, 10}, {10_000, 50}],
|
||||||
|
foobar: nil
|
||||||
|
|
||||||
Here we have two limiters: `one` which is not over 10req/1s and `two` which has two limits 10req/10s for unauthenticated users and 50req/10s for authenticated users.
|
Here we have three limiters:
|
||||||
|
|
||||||
|
* `one` which is not over 10req/1s
|
||||||
|
* `two` which has two limits: 10req/10s for unauthenticated users and 50req/10s for authenticated users
|
||||||
|
* `foobar` which is disabled
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ def call(%{request_path: <<"/", @path, "/", file::binary>>} = conn, opts) do
|
||||||
conn
|
conn
|
||||||
end
|
end
|
||||||
|
|
||||||
config = Pleroma.Config.get([Pleroma.Upload])
|
config = Pleroma.Config.get(Pleroma.Upload)
|
||||||
|
|
||||||
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),
|
||||||
|
|
|
@ -146,7 +146,7 @@ defp request(method, url, headers, hackney_opts) do
|
||||||
Logger.debug("#{__MODULE__} #{method} #{url} #{inspect(headers)}")
|
Logger.debug("#{__MODULE__} #{method} #{url} #{inspect(headers)}")
|
||||||
method = method |> String.downcase() |> String.to_existing_atom()
|
method = method |> String.downcase() |> String.to_existing_atom()
|
||||||
|
|
||||||
case :hackney.request(method, url, headers, "", hackney_opts) do
|
case hackney().request(method, url, headers, "", hackney_opts) do
|
||||||
{:ok, code, headers, client} when code in @valid_resp_codes ->
|
{:ok, code, headers, client} when code in @valid_resp_codes ->
|
||||||
{:ok, code, downcase_headers(headers), client}
|
{:ok, code, downcase_headers(headers), client}
|
||||||
|
|
||||||
|
@ -196,7 +196,7 @@ defp chunk_reply(conn, client, opts, sent_so_far, duration) do
|
||||||
duration,
|
duration,
|
||||||
Keyword.get(opts, :max_read_duration, @max_read_duration)
|
Keyword.get(opts, :max_read_duration, @max_read_duration)
|
||||||
),
|
),
|
||||||
{:ok, data} <- :hackney.stream_body(client),
|
{:ok, data} <- hackney().stream_body(client),
|
||||||
{:ok, duration} <- increase_read_duration(duration),
|
{:ok, duration} <- increase_read_duration(duration),
|
||||||
sent_so_far = sent_so_far + byte_size(data),
|
sent_so_far = sent_so_far + byte_size(data),
|
||||||
:ok <- body_size_constraint(sent_so_far, Keyword.get(opts, :max_body_size)),
|
:ok <- body_size_constraint(sent_so_far, Keyword.get(opts, :max_body_size)),
|
||||||
|
@ -377,4 +377,6 @@ defp increase_read_duration({previous_duration, started})
|
||||||
defp increase_read_duration(_) do
|
defp increase_read_duration(_) do
|
||||||
{:ok, :no_duration_limit, :no_duration_limit}
|
{:ok, :no_duration_limit, :no_duration_limit}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp hackney, do: Pleroma.Config.get(:hackney, :hackney)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1036,9 +1036,7 @@ def html_filter_policy(%User{info: %{no_rich_text: true}}) do
|
||||||
Pleroma.HTML.Scrubber.TwitterText
|
Pleroma.HTML.Scrubber.TwitterText
|
||||||
end
|
end
|
||||||
|
|
||||||
@default_scrubbers Pleroma.Config.get([:markup, :scrub_policy])
|
def html_filter_policy(_), do: Pleroma.Config.get([:markup, :scrub_policy])
|
||||||
|
|
||||||
def html_filter_policy(_), do: @default_scrubbers
|
|
||||||
|
|
||||||
def fetch_by_ap_id(ap_id) do
|
def fetch_by_ap_id(ap_id) do
|
||||||
ap_try = ActivityPub.make_user_from_ap_id(ap_id)
|
ap_try = ActivityPub.make_user_from_ap_id(ap_id)
|
||||||
|
|
|
@ -7,45 +7,69 @@ defmodule Pleroma.User.Search do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
def search(query, opts \\ []) do
|
@similarity_threshold 0.25
|
||||||
|
@limit 20
|
||||||
|
|
||||||
|
def search(query_string, opts \\ []) do
|
||||||
resolve = Keyword.get(opts, :resolve, false)
|
resolve = Keyword.get(opts, :resolve, false)
|
||||||
|
following = Keyword.get(opts, :following, false)
|
||||||
|
result_limit = Keyword.get(opts, :limit, @limit)
|
||||||
|
offset = Keyword.get(opts, :offset, 0)
|
||||||
|
|
||||||
for_user = Keyword.get(opts, :for_user)
|
for_user = Keyword.get(opts, :for_user)
|
||||||
|
|
||||||
# Strip the beginning @ off if there is a query
|
# Strip the beginning @ off if there is a query
|
||||||
query = String.trim_leading(query, "@")
|
query_string = String.trim_leading(query_string, "@")
|
||||||
|
|
||||||
maybe_resolve(resolve, for_user, query)
|
maybe_resolve(resolve, for_user, query_string)
|
||||||
|
|
||||||
{:ok, results} =
|
{:ok, results} =
|
||||||
Repo.transaction(fn ->
|
Repo.transaction(fn ->
|
||||||
Ecto.Adapters.SQL.query(Repo, "select set_limit(0.25)", [])
|
Ecto.Adapters.SQL.query(
|
||||||
|
Repo,
|
||||||
|
"select set_limit(#{@similarity_threshold})",
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
query
|
query_string
|
||||||
|> search_query(for_user)
|
|> search_query(for_user, following)
|
||||||
|
|> paginate(result_limit, offset)
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end)
|
end)
|
||||||
|
|
||||||
results
|
results
|
||||||
end
|
end
|
||||||
|
|
||||||
defp search_query(query, for_user) do
|
defp search_query(query_string, for_user, following) do
|
||||||
query
|
for_user
|
||||||
|> union_query()
|
|> base_query(following)
|
||||||
|
|> search_subqueries(query_string)
|
||||||
|
|> union_subqueries
|
||||||
|> distinct_query()
|
|> distinct_query()
|
||||||
|> boost_search_rank_query(for_user)
|
|> boost_search_rank_query(for_user)
|
||||||
|> subquery()
|
|> subquery()
|
||||||
|> order_by(desc: :search_rank)
|
|> order_by(desc: :search_rank)
|
||||||
|> limit(20)
|
|
||||||
|> maybe_restrict_local(for_user)
|
|> maybe_restrict_local(for_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp union_query(query) do
|
defp base_query(_user, false), do: User
|
||||||
fts_subquery = fts_search_subquery(query)
|
defp base_query(user, true), do: User.get_followers_query(user)
|
||||||
trigram_subquery = trigram_search_subquery(query)
|
|
||||||
|
|
||||||
|
defp paginate(query, limit, offset) do
|
||||||
|
from(q in query, limit: ^limit, offset: ^offset)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp union_subqueries({fts_subquery, trigram_subquery}) do
|
||||||
from(s in trigram_subquery, union_all: ^fts_subquery)
|
from(s in trigram_subquery, union_all: ^fts_subquery)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp search_subqueries(base_query, query_string) do
|
||||||
|
{
|
||||||
|
fts_search_subquery(base_query, query_string),
|
||||||
|
trigram_search_subquery(base_query, query_string)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
defp distinct_query(q) do
|
defp distinct_query(q) do
|
||||||
from(s in subquery(q), order_by: s.search_type, distinct: s.id)
|
from(s in subquery(q), order_by: s.search_type, distinct: s.id)
|
||||||
end
|
end
|
||||||
|
@ -102,7 +126,8 @@ defp boost_search_rank_query(query, for_user) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp fts_search_subquery(term, query \\ User) do
|
@spec fts_search_subquery(User.t() | Ecto.Query.t(), String.t()) :: Ecto.Query.t()
|
||||||
|
defp fts_search_subquery(query, term) do
|
||||||
processed_query =
|
processed_query =
|
||||||
term
|
term
|
||||||
|> String.replace(~r/\W+/, " ")
|
|> String.replace(~r/\W+/, " ")
|
||||||
|
@ -144,9 +169,10 @@ defp fts_search_subquery(term, query \\ User) do
|
||||||
|> User.restrict_deactivated()
|
|> User.restrict_deactivated()
|
||||||
end
|
end
|
||||||
|
|
||||||
defp trigram_search_subquery(term) do
|
@spec trigram_search_subquery(User.t() | Ecto.Query.t(), String.t()) :: Ecto.Query.t()
|
||||||
|
defp trigram_search_subquery(query, term) do
|
||||||
from(
|
from(
|
||||||
u in User,
|
u in query,
|
||||||
select_merge: %{
|
select_merge: %{
|
||||||
# ^1 gives 'Postgrex expected a binary, got 1' for some weird reason
|
# ^1 gives 'Postgrex expected a binary, got 1' for some weird reason
|
||||||
search_type: fragment("?", 1),
|
search_type: fragment("?", 1),
|
||||||
|
|
|
@ -88,7 +88,7 @@ defp should_federate?(inbox, public) do
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
inbox_info = URI.parse(inbox)
|
inbox_info = URI.parse(inbox)
|
||||||
!Enum.member?(Pleroma.Config.get([:instance, :quarantined_instances], []), inbox_info.host)
|
!Enum.member?(Config.get([:instance, :quarantined_instances], []), inbox_info.host)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
alias Pleroma.Web.AdminAPI.AccountView
|
alias Pleroma.Web.AdminAPI.AccountView
|
||||||
|
alias Pleroma.Web.AdminAPI.Config
|
||||||
|
alias Pleroma.Web.AdminAPI.ConfigView
|
||||||
alias Pleroma.Web.AdminAPI.ReportView
|
alias Pleroma.Web.AdminAPI.ReportView
|
||||||
alias Pleroma.Web.AdminAPI.Search
|
alias Pleroma.Web.AdminAPI.Search
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
@ -362,6 +364,41 @@ def status_delete(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def config_show(conn, _params) do
|
||||||
|
configs = Pleroma.Repo.all(Config)
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(ConfigView)
|
||||||
|
|> render("index.json", %{configs: configs})
|
||||||
|
end
|
||||||
|
|
||||||
|
def config_update(conn, %{"configs" => configs}) do
|
||||||
|
updated =
|
||||||
|
if Pleroma.Config.get([:instance, :dynamic_configuration]) do
|
||||||
|
updated =
|
||||||
|
Enum.map(configs, fn
|
||||||
|
%{"key" => key, "value" => value} ->
|
||||||
|
{:ok, config} = Config.update_or_create(%{key: key, value: value})
|
||||||
|
config
|
||||||
|
|
||||||
|
%{"key" => key, "delete" => "true"} ->
|
||||||
|
{:ok, _} = Config.delete(key)
|
||||||
|
nil
|
||||||
|
end)
|
||||||
|
|> Enum.reject(&is_nil(&1))
|
||||||
|
|
||||||
|
Pleroma.Config.TransferTask.load_and_update_env()
|
||||||
|
Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env)])
|
||||||
|
updated
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
conn
|
||||||
|
|> put_view(ConfigView)
|
||||||
|
|> render("index.json", %{configs: updated})
|
||||||
|
end
|
||||||
|
|
||||||
def errors(conn, {:error, :not_found}) do
|
def errors(conn, {:error, :not_found}) do
|
||||||
conn
|
conn
|
||||||
|> put_status(404)
|
|> put_status(404)
|
||||||
|
|
144
lib/pleroma/web/admin_api/config.ex
Normal file
144
lib/pleroma/web/admin_api/config.ex
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.AdminAPI.Config do
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
alias __MODULE__
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
|
schema "config" do
|
||||||
|
field(:key, :string)
|
||||||
|
field(:value, :binary)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec get_by_key(String.t()) :: Config.t() | nil
|
||||||
|
def get_by_key(key), do: Repo.get_by(Config, key: key)
|
||||||
|
|
||||||
|
@spec changeset(Config.t(), map()) :: Changeset.t()
|
||||||
|
def changeset(config, params \\ %{}) do
|
||||||
|
config
|
||||||
|
|> cast(params, [:key, :value])
|
||||||
|
|> validate_required([:key, :value])
|
||||||
|
|> unique_constraint(:key)
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
|
||||||
|
def create(%{key: key, value: value}) do
|
||||||
|
%Config{}
|
||||||
|
|> changeset(%{key: key, value: transform(value)})
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update(Config.t(), map()) :: {:ok, Config} | {:error, Changeset.t()}
|
||||||
|
def update(%Config{} = config, %{value: value}) do
|
||||||
|
config
|
||||||
|
|> change(value: transform(value))
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec update_or_create(map()) :: {:ok, Config.t()} | {:error, Changeset.t()}
|
||||||
|
def update_or_create(%{key: key} = params) do
|
||||||
|
with %Config{} = config <- Config.get_by_key(key) do
|
||||||
|
Config.update(config, params)
|
||||||
|
else
|
||||||
|
nil -> Config.create(params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec delete(String.t()) :: {:ok, Config.t()} | {:error, Changeset.t()}
|
||||||
|
def delete(key) do
|
||||||
|
with %Config{} = config <- Config.get_by_key(key) do
|
||||||
|
Repo.delete(config)
|
||||||
|
else
|
||||||
|
nil -> {:error, "Config with key #{key} not found"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec from_binary(binary()) :: term()
|
||||||
|
def from_binary(value), do: :erlang.binary_to_term(value)
|
||||||
|
|
||||||
|
@spec from_binary_to_map(binary()) :: any()
|
||||||
|
def from_binary_to_map(binary) do
|
||||||
|
from_binary(binary)
|
||||||
|
|> do_convert()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_convert([{k, v}] = value) when is_list(value) and length(value) == 1,
|
||||||
|
do: %{k => do_convert(v)}
|
||||||
|
|
||||||
|
defp do_convert(values) when is_list(values), do: for(val <- values, do: do_convert(val))
|
||||||
|
|
||||||
|
defp do_convert({k, v} = value) when is_tuple(value),
|
||||||
|
do: %{k => do_convert(v)}
|
||||||
|
|
||||||
|
defp do_convert(value) when is_binary(value) or is_atom(value) or is_map(value),
|
||||||
|
do: value
|
||||||
|
|
||||||
|
@spec transform(any()) :: binary()
|
||||||
|
def transform(entity) when is_map(entity) do
|
||||||
|
tuples =
|
||||||
|
for {k, v} <- entity,
|
||||||
|
into: [],
|
||||||
|
do: {if(is_atom(k), do: k, else: String.to_atom(k)), do_transform(v)}
|
||||||
|
|
||||||
|
Enum.reject(tuples, fn {_k, v} -> is_nil(v) end)
|
||||||
|
|> Enum.sort()
|
||||||
|
|> :erlang.term_to_binary()
|
||||||
|
end
|
||||||
|
|
||||||
|
def transform(entity) when is_list(entity) do
|
||||||
|
list = Enum.map(entity, &do_transform(&1))
|
||||||
|
:erlang.term_to_binary(list)
|
||||||
|
end
|
||||||
|
|
||||||
|
def transform(entity), do: :erlang.term_to_binary(entity)
|
||||||
|
|
||||||
|
defp do_transform(%Regex{} = value) when is_map(value), do: value
|
||||||
|
|
||||||
|
defp do_transform(value) when is_map(value) do
|
||||||
|
values =
|
||||||
|
for {key, val} <- value,
|
||||||
|
into: [],
|
||||||
|
do: {String.to_atom(key), do_transform(val)}
|
||||||
|
|
||||||
|
Enum.sort(values)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_transform(value) when is_list(value) do
|
||||||
|
Enum.map(value, &do_transform(&1))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_transform(entity) when is_list(entity) and length(entity) == 1, do: hd(entity)
|
||||||
|
|
||||||
|
defp do_transform(value) when is_binary(value) do
|
||||||
|
value = String.trim(value)
|
||||||
|
|
||||||
|
case String.length(value) do
|
||||||
|
0 ->
|
||||||
|
nil
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
cond do
|
||||||
|
String.starts_with?(value, "Pleroma") ->
|
||||||
|
String.to_existing_atom("Elixir." <> value)
|
||||||
|
|
||||||
|
String.starts_with?(value, ":") ->
|
||||||
|
String.replace(value, ":", "") |> String.to_existing_atom()
|
||||||
|
|
||||||
|
String.starts_with?(value, "i:") ->
|
||||||
|
String.replace(value, "i:", "") |> String.to_integer()
|
||||||
|
|
||||||
|
true ->
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_transform(value), do: value
|
||||||
|
end
|
16
lib/pleroma/web/admin_api/views/config_view.ex
Normal file
16
lib/pleroma/web/admin_api/views/config_view.ex
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
defmodule Pleroma.Web.AdminAPI.ConfigView do
|
||||||
|
use Pleroma.Web, :view
|
||||||
|
|
||||||
|
def render("index.json", %{configs: configs}) do
|
||||||
|
%{
|
||||||
|
configs: render_many(configs, __MODULE__, "show.json", as: :config)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def render("show.json", %{config: config}) do
|
||||||
|
%{
|
||||||
|
key: config.key,
|
||||||
|
value: Pleroma.Web.AdminAPI.Config.from_binary_to_map(config.value)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -15,4 +15,22 @@ def json_response(conn, status, json) do
|
||||||
|> put_status(status)
|
|> put_status(status)
|
||||||
|> json(json)
|
|> json(json)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec fetch_integer_param(map(), String.t(), integer() | nil) :: integer() | nil
|
||||||
|
def fetch_integer_param(params, name, default \\ nil) do
|
||||||
|
params
|
||||||
|
|> Map.get(name, default)
|
||||||
|
|> param_to_integer(default)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp param_to_integer(val, _) when is_integer(val), do: val
|
||||||
|
|
||||||
|
defp param_to_integer(val, default) when is_binary(val) do
|
||||||
|
case Integer.parse(val) do
|
||||||
|
{res, _} -> res
|
||||||
|
_ -> default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp param_to_integer(_, default), do: default
|
||||||
end
|
end
|
||||||
|
|
|
@ -91,7 +91,7 @@ defmodule Pleroma.Web.Endpoint do
|
||||||
Plug.Session,
|
Plug.Session,
|
||||||
store: :cookie,
|
store: :cookie,
|
||||||
key: cookie_name,
|
key: cookie_name,
|
||||||
signing_salt: {Pleroma.Config, :get, [[__MODULE__, :signing_salt], "CqaoopA2"]},
|
signing_salt: Pleroma.Config.get([__MODULE__, :signing_salt], "CqaoopA2"),
|
||||||
http_only: true,
|
http_only: true,
|
||||||
secure: secure_cookies,
|
secure: secure_cookies,
|
||||||
extra: extra
|
extra: extra
|
||||||
|
|
|
@ -136,6 +136,14 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||||
_ -> :error
|
_ -> :error
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|> add_if_present(params, "pleroma_background_image", :background, fn value ->
|
||||||
|
with %Plug.Upload{} <- value,
|
||||||
|
{:ok, object} <- ActivityPub.upload(value, type: :background) do
|
||||||
|
{:ok, object.data}
|
||||||
|
else
|
||||||
|
_ -> :error
|
||||||
|
end
|
||||||
|
end)
|
||||||
|> Map.put(:emoji, user_info_emojis)
|
|> Map.put(:emoji, user_info_emojis)
|
||||||
|
|
||||||
info_cng = User.Info.profile_update(user.info, info_params)
|
info_cng = User.Info.profile_update(user.info, info_params)
|
||||||
|
@ -160,8 +168,15 @@ def update_credentials(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
def verify_credentials(%{assigns: %{user: user}} = conn, _) do
|
||||||
|
chat_token = Phoenix.Token.sign(conn, "user socket", user.id)
|
||||||
|
|
||||||
account =
|
account =
|
||||||
AccountView.render("account.json", %{user: user, for: user, with_pleroma_settings: true})
|
AccountView.render("account.json", %{
|
||||||
|
user: user,
|
||||||
|
for: user,
|
||||||
|
with_pleroma_settings: true,
|
||||||
|
with_chat_token: chat_token
|
||||||
|
})
|
||||||
|
|
||||||
json(conn, account)
|
json(conn, account)
|
||||||
end
|
end
|
||||||
|
@ -439,12 +454,26 @@ def get_poll(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp get_cached_vote_or_vote(user, object, choices) do
|
||||||
|
idempotency_key = "polls:#{user.id}:#{object.data["id"]}"
|
||||||
|
|
||||||
|
{_, res} =
|
||||||
|
Cachex.fetch(:idempotency_cache, idempotency_key, fn _ ->
|
||||||
|
case CommonAPI.vote(user, object, choices) do
|
||||||
|
{:error, _message} = res -> {:ignore, res}
|
||||||
|
res -> {:commit, res}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
def poll_vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choices}) do
|
def poll_vote(%{assigns: %{user: user}} = conn, %{"id" => id, "choices" => choices}) do
|
||||||
with %Object{} = object <- Object.get_by_id(id),
|
with %Object{} = object <- Object.get_by_id(id),
|
||||||
true <- object.data["type"] == "Question",
|
true <- object.data["type"] == "Question",
|
||||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||||
true <- Visibility.visible_for_user?(activity, user),
|
true <- Visibility.visible_for_user?(activity, user),
|
||||||
{:ok, _activities, object} <- CommonAPI.vote(user, object, choices) do
|
{:ok, _activities, object} <- get_cached_vote_or_vote(user, object, choices) do
|
||||||
conn
|
conn
|
||||||
|> put_view(StatusView)
|
|> put_view(StatusView)
|
||||||
|> try_render("poll.json", %{object: object, for: user})
|
|> try_render("poll.json", %{object: object, for: user})
|
||||||
|
@ -1118,58 +1147,6 @@ def unsubscribe(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
|
||||||
accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
|
|
||||||
statuses = Activity.search(user, query)
|
|
||||||
tags_path = Web.base_url() <> "/tag/"
|
|
||||||
|
|
||||||
tags =
|
|
||||||
query
|
|
||||||
|> String.split()
|
|
||||||
|> Enum.uniq()
|
|
||||||
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
|
||||||
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
|
|
||||||
|> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end)
|
|
||||||
|
|
||||||
res = %{
|
|
||||||
"accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
|
|
||||||
"statuses" =>
|
|
||||||
StatusView.render("index.json", activities: statuses, for: user, as: :activity),
|
|
||||||
"hashtags" => tags
|
|
||||||
}
|
|
||||||
|
|
||||||
json(conn, res)
|
|
||||||
end
|
|
||||||
|
|
||||||
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
|
||||||
accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
|
|
||||||
statuses = Activity.search(user, query)
|
|
||||||
|
|
||||||
tags =
|
|
||||||
query
|
|
||||||
|> String.split()
|
|
||||||
|> Enum.uniq()
|
|
||||||
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
|
||||||
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
|
|
||||||
|
|
||||||
res = %{
|
|
||||||
"accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
|
|
||||||
"statuses" =>
|
|
||||||
StatusView.render("index.json", activities: statuses, for: user, as: :activity),
|
|
||||||
"hashtags" => tags
|
|
||||||
}
|
|
||||||
|
|
||||||
json(conn, res)
|
|
||||||
end
|
|
||||||
|
|
||||||
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
|
||||||
accounts = User.search(query, resolve: params["resolve"] == "true", for_user: user)
|
|
||||||
|
|
||||||
res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
|
|
||||||
|
|
||||||
json(conn, res)
|
|
||||||
end
|
|
||||||
|
|
||||||
def favourites(%{assigns: %{user: user}} = conn, params) do
|
def favourites(%{assigns: %{user: user}} = conn, params) do
|
||||||
params =
|
params =
|
||||||
params
|
params
|
||||||
|
|
79
lib/pleroma/web/mastodon_api/search_controller.ex
Normal file
79
lib/pleroma/web/mastodon_api/search_controller.ex
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.SearchController do
|
||||||
|
use Pleroma.Web, :controller
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web
|
||||||
|
alias Pleroma.Web.MastodonAPI.AccountView
|
||||||
|
alias Pleroma.Web.MastodonAPI.StatusView
|
||||||
|
|
||||||
|
alias Pleroma.Web.ControllerHelper
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
plug(Pleroma.Plugs.RateLimiter, :search when action in [:search, :search2, :account_search])
|
||||||
|
|
||||||
|
def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||||
|
accounts = User.search(query, search_options(params, user))
|
||||||
|
statuses = Activity.search(user, query)
|
||||||
|
tags_path = Web.base_url() <> "/tag/"
|
||||||
|
|
||||||
|
tags =
|
||||||
|
query
|
||||||
|
|> String.split()
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
||||||
|
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
|
||||||
|
|> Enum.map(fn tag -> %{name: tag, url: tags_path <> tag} end)
|
||||||
|
|
||||||
|
res = %{
|
||||||
|
"accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
|
||||||
|
"statuses" =>
|
||||||
|
StatusView.render("index.json", activities: statuses, for: user, as: :activity),
|
||||||
|
"hashtags" => tags
|
||||||
|
}
|
||||||
|
|
||||||
|
json(conn, res)
|
||||||
|
end
|
||||||
|
|
||||||
|
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||||
|
accounts = User.search(query, search_options(params, user))
|
||||||
|
statuses = Activity.search(user, query)
|
||||||
|
|
||||||
|
tags =
|
||||||
|
query
|
||||||
|
|> String.split()
|
||||||
|
|> Enum.uniq()
|
||||||
|
|> Enum.filter(fn tag -> String.starts_with?(tag, "#") end)
|
||||||
|
|> Enum.map(fn tag -> String.slice(tag, 1..-1) end)
|
||||||
|
|
||||||
|
res = %{
|
||||||
|
"accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user),
|
||||||
|
"statuses" =>
|
||||||
|
StatusView.render("index.json", activities: statuses, for: user, as: :activity),
|
||||||
|
"hashtags" => tags
|
||||||
|
}
|
||||||
|
|
||||||
|
json(conn, res)
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
|
||||||
|
accounts = User.search(query, search_options(params, user))
|
||||||
|
res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
|
||||||
|
|
||||||
|
json(conn, res)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp search_options(params, user) do
|
||||||
|
[
|
||||||
|
resolve: params["resolve"] == "true",
|
||||||
|
following: params["following"] == "true",
|
||||||
|
limit: ControllerHelper.fetch_integer_param(params, "limit"),
|
||||||
|
offset: ControllerHelper.fetch_integer_param(params, "offset"),
|
||||||
|
for_user: user
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
|
@ -125,13 +125,15 @@ defp do_render("account.json", %{user: user} = opts) do
|
||||||
hide_follows: user.info.hide_follows,
|
hide_follows: user.info.hide_follows,
|
||||||
hide_favorites: user.info.hide_favorites,
|
hide_favorites: user.info.hide_favorites,
|
||||||
relationship: relationship,
|
relationship: relationship,
|
||||||
skip_thread_containment: user.info.skip_thread_containment
|
skip_thread_containment: user.info.skip_thread_containment,
|
||||||
|
background_image: image_url(user.info.background) |> MediaProxy.url()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|> maybe_put_role(user, opts[:for])
|
|> maybe_put_role(user, opts[:for])
|
||||||
|> maybe_put_settings(user, opts[:for], user_info)
|
|> maybe_put_settings(user, opts[:for], user_info)
|
||||||
|> maybe_put_notification_settings(user, opts[:for])
|
|> maybe_put_notification_settings(user, opts[:for])
|
||||||
|> maybe_put_settings_store(user, opts[:for], opts)
|
|> maybe_put_settings_store(user, opts[:for], opts)
|
||||||
|
|> maybe_put_chat_token(user, opts[:for], opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp username_from_nickname(string) when is_binary(string) do
|
defp username_from_nickname(string) when is_binary(string) do
|
||||||
|
@ -163,6 +165,15 @@ defp maybe_put_settings_store(data, %User{info: info, id: id}, %User{id: id}, %{
|
||||||
|
|
||||||
defp maybe_put_settings_store(data, _, _, _), do: data
|
defp maybe_put_settings_store(data, _, _, _), do: data
|
||||||
|
|
||||||
|
defp maybe_put_chat_token(data, %User{id: id}, %User{id: id}, %{
|
||||||
|
with_chat_token: token
|
||||||
|
}) do
|
||||||
|
data
|
||||||
|
|> Kernel.put_in([:pleroma, :chat_token], token)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp maybe_put_chat_token(data, _, _, _), do: data
|
||||||
|
|
||||||
defp maybe_put_role(data, %User{info: %{show_role: true}} = user, _) do
|
defp maybe_put_role(data, %User{info: %{show_role: true}} = user, _) do
|
||||||
data
|
data
|
||||||
|> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin)
|
|> Kernel.put_in([:pleroma, :is_admin], user.info.is_admin)
|
||||||
|
@ -182,4 +193,7 @@ defp maybe_put_notification_settings(data, %User{id: user_id} = user, %User{id:
|
||||||
end
|
end
|
||||||
|
|
||||||
defp maybe_put_notification_settings(data, _, _), do: data
|
defp maybe_put_notification_settings(data, _, _), do: data
|
||||||
|
|
||||||
|
defp image_url(%{"url" => [%{"href" => href} | _]}), do: href
|
||||||
|
defp image_url(_), do: nil
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,6 +17,7 @@ defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do
|
||||||
"public:media",
|
"public:media",
|
||||||
"public:local:media",
|
"public:local:media",
|
||||||
"user",
|
"user",
|
||||||
|
"user:notification",
|
||||||
"direct",
|
"direct",
|
||||||
"list",
|
"list",
|
||||||
"hashtag"
|
"hashtag"
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
defmodule Pleroma.Web.OAuth.OAuthController do
|
defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
use Pleroma.Web, :controller
|
use Pleroma.Web, :controller
|
||||||
|
|
||||||
|
alias Pleroma.Helpers.UriHelper
|
||||||
alias Pleroma.Registration
|
alias Pleroma.Registration
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -26,34 +27,25 @@ defmodule Pleroma.Web.OAuth.OAuthController do
|
||||||
|
|
||||||
action_fallback(Pleroma.Web.OAuth.FallbackController)
|
action_fallback(Pleroma.Web.OAuth.FallbackController)
|
||||||
|
|
||||||
|
@oob_token_redirect_uri "urn:ietf:wg:oauth:2.0:oob"
|
||||||
|
|
||||||
# Note: this definition is only called from error-handling methods with `conn.params` as 2nd arg
|
# Note: this definition is only called from error-handling methods with `conn.params` as 2nd arg
|
||||||
def authorize(conn, %{"authorization" => _} = params) do
|
def authorize(%Plug.Conn{} = conn, %{"authorization" => _} = params) do
|
||||||
{auth_attrs, params} = Map.pop(params, "authorization")
|
{auth_attrs, params} = Map.pop(params, "authorization")
|
||||||
authorize(conn, Map.merge(params, auth_attrs))
|
authorize(conn, Map.merge(params, auth_attrs))
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize(%{assigns: %{token: %Token{} = token}} = conn, params) do
|
def authorize(%Plug.Conn{assigns: %{token: %Token{}}} = conn, params) do
|
||||||
if ControllerHelper.truthy_param?(params["force_login"]) do
|
if ControllerHelper.truthy_param?(params["force_login"]) do
|
||||||
do_authorize(conn, params)
|
do_authorize(conn, params)
|
||||||
else
|
else
|
||||||
redirect_uri =
|
handle_existing_authorization(conn, params)
|
||||||
if is_binary(params["redirect_uri"]) do
|
|
||||||
params["redirect_uri"]
|
|
||||||
else
|
|
||||||
app = Repo.preload(token, :app).app
|
|
||||||
|
|
||||||
app.redirect_uris
|
|
||||||
|> String.split()
|
|
||||||
|> Enum.at(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect(conn, external: redirect_uri(conn, redirect_uri))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize(conn, params), do: do_authorize(conn, params)
|
def authorize(%Plug.Conn{} = conn, params), do: do_authorize(conn, params)
|
||||||
|
|
||||||
defp do_authorize(conn, params) do
|
defp do_authorize(%Plug.Conn{} = conn, params) do
|
||||||
app = Repo.get_by(App, client_id: params["client_id"])
|
app = Repo.get_by(App, client_id: params["client_id"])
|
||||||
available_scopes = (app && app.scopes) || []
|
available_scopes = (app && app.scopes) || []
|
||||||
scopes = Scopes.fetch_scopes(params, available_scopes)
|
scopes = Scopes.fetch_scopes(params, available_scopes)
|
||||||
|
@ -70,8 +62,33 @@ defp do_authorize(conn, params) do
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp handle_existing_authorization(
|
||||||
|
%Plug.Conn{assigns: %{token: %Token{} = token}} = conn,
|
||||||
|
params
|
||||||
|
) do
|
||||||
|
token = Repo.preload(token, :app)
|
||||||
|
|
||||||
|
redirect_uri =
|
||||||
|
if is_binary(params["redirect_uri"]) do
|
||||||
|
params["redirect_uri"]
|
||||||
|
else
|
||||||
|
default_redirect_uri(token.app)
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_uri = redirect_uri(conn, redirect_uri)
|
||||||
|
|
||||||
|
if redirect_uri == @oob_token_redirect_uri do
|
||||||
|
render(conn, "oob_token_exists.html", %{token: token})
|
||||||
|
else
|
||||||
|
url_params = %{access_token: token.token}
|
||||||
|
url_params = UriHelper.append_param_if_present(url_params, :state, params["state"])
|
||||||
|
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
||||||
|
redirect(conn, external: url)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def create_authorization(
|
def create_authorization(
|
||||||
conn,
|
%Plug.Conn{} = conn,
|
||||||
%{"authorization" => _} = params,
|
%{"authorization" => _} = params,
|
||||||
opts \\ []
|
opts \\ []
|
||||||
) do
|
) do
|
||||||
|
@ -83,35 +100,23 @@ def create_authorization(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_create_authorization(conn, auth, %{
|
def after_create_authorization(%Plug.Conn{} = conn, %Authorization{} = auth, %{
|
||||||
"authorization" => %{"redirect_uri" => redirect_uri} = auth_attrs
|
"authorization" => %{"redirect_uri" => redirect_uri} = auth_attrs
|
||||||
}) do
|
}) do
|
||||||
redirect_uri = redirect_uri(conn, redirect_uri)
|
redirect_uri = redirect_uri(conn, redirect_uri)
|
||||||
|
|
||||||
if redirect_uri == "urn:ietf:wg:oauth:2.0:oob" do
|
if redirect_uri == @oob_token_redirect_uri do
|
||||||
render(conn, "results.html", %{
|
render(conn, "oob_authorization_created.html", %{auth: auth})
|
||||||
auth: auth
|
|
||||||
})
|
|
||||||
else
|
else
|
||||||
connector = if String.contains?(redirect_uri, "?"), do: "&", else: "?"
|
url_params = %{code: auth.token}
|
||||||
url = "#{redirect_uri}#{connector}"
|
url_params = UriHelper.append_param_if_present(url_params, :state, auth_attrs["state"])
|
||||||
url_params = %{:code => auth.token}
|
url = UriHelper.append_uri_params(redirect_uri, url_params)
|
||||||
|
|
||||||
url_params =
|
|
||||||
if auth_attrs["state"] do
|
|
||||||
Map.put(url_params, :state, auth_attrs["state"])
|
|
||||||
else
|
|
||||||
url_params
|
|
||||||
end
|
|
||||||
|
|
||||||
url = "#{url}#{Plug.Conn.Query.encode(url_params)}"
|
|
||||||
|
|
||||||
redirect(conn, external: url)
|
redirect(conn, external: url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_create_authorization_error(
|
defp handle_create_authorization_error(
|
||||||
conn,
|
%Plug.Conn{} = conn,
|
||||||
{:error, scopes_issue},
|
{:error, scopes_issue},
|
||||||
%{"authorization" => _} = params
|
%{"authorization" => _} = params
|
||||||
)
|
)
|
||||||
|
@ -125,7 +130,7 @@ defp handle_create_authorization_error(
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_create_authorization_error(
|
defp handle_create_authorization_error(
|
||||||
conn,
|
%Plug.Conn{} = conn,
|
||||||
{:auth_active, false},
|
{:auth_active, false},
|
||||||
%{"authorization" => _} = params
|
%{"authorization" => _} = params
|
||||||
) do
|
) do
|
||||||
|
@ -137,13 +142,13 @@ defp handle_create_authorization_error(
|
||||||
|> authorize(params)
|
|> authorize(params)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_create_authorization_error(conn, error, %{"authorization" => _}) do
|
defp handle_create_authorization_error(%Plug.Conn{} = conn, error, %{"authorization" => _}) do
|
||||||
Authenticator.handle_error(conn, error)
|
Authenticator.handle_error(conn, error)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Renew access_token with refresh_token"
|
@doc "Renew access_token with refresh_token"
|
||||||
def token_exchange(
|
def token_exchange(
|
||||||
conn,
|
%Plug.Conn{} = conn,
|
||||||
%{"grant_type" => "refresh_token", "refresh_token" => token} = _params
|
%{"grant_type" => "refresh_token", "refresh_token" => token} = _params
|
||||||
) do
|
) do
|
||||||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||||
|
@ -159,7 +164,7 @@ def token_exchange(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do
|
def token_exchange(%Plug.Conn{} = conn, %{"grant_type" => "authorization_code"} = params) do
|
||||||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||||
fixed_token = Token.Utils.fix_padding(params["code"]),
|
fixed_token = Token.Utils.fix_padding(params["code"]),
|
||||||
{:ok, auth} <- Authorization.get_by_token(app, fixed_token),
|
{:ok, auth} <- Authorization.get_by_token(app, fixed_token),
|
||||||
|
@ -176,7 +181,7 @@ def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def token_exchange(
|
def token_exchange(
|
||||||
conn,
|
%Plug.Conn{} = conn,
|
||||||
%{"grant_type" => "password"} = params
|
%{"grant_type" => "password"} = params
|
||||||
) do
|
) do
|
||||||
with {:ok, %User{} = user} <- Authenticator.get_user(conn),
|
with {:ok, %User{} = user} <- Authenticator.get_user(conn),
|
||||||
|
@ -207,7 +212,7 @@ def token_exchange(
|
||||||
end
|
end
|
||||||
|
|
||||||
def token_exchange(
|
def token_exchange(
|
||||||
conn,
|
%Plug.Conn{} = conn,
|
||||||
%{"grant_type" => "password", "name" => name, "password" => _password} = params
|
%{"grant_type" => "password", "name" => name, "password" => _password} = params
|
||||||
) do
|
) do
|
||||||
params =
|
params =
|
||||||
|
@ -218,7 +223,7 @@ def token_exchange(
|
||||||
token_exchange(conn, params)
|
token_exchange(conn, params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def token_exchange(conn, %{"grant_type" => "client_credentials"} = _params) do
|
def token_exchange(%Plug.Conn{} = conn, %{"grant_type" => "client_credentials"} = _params) do
|
||||||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||||
{:ok, auth} <- Authorization.create_authorization(app, %User{}),
|
{:ok, auth} <- Authorization.create_authorization(app, %User{}),
|
||||||
{:ok, token} <- Token.exchange_token(app, auth) do
|
{:ok, token} <- Token.exchange_token(app, auth) do
|
||||||
|
@ -231,9 +236,9 @@ def token_exchange(conn, %{"grant_type" => "client_credentials"} = _params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
# Bad request
|
# Bad request
|
||||||
def token_exchange(conn, params), do: bad_request(conn, params)
|
def token_exchange(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
|
||||||
|
|
||||||
def token_revoke(conn, %{"token" => _token} = params) do
|
def token_revoke(%Plug.Conn{} = conn, %{"token" => _token} = params) do
|
||||||
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
with {:ok, app} <- Token.Utils.fetch_app(conn),
|
||||||
{:ok, _token} <- RevokeToken.revoke(app, params) do
|
{:ok, _token} <- RevokeToken.revoke(app, params) do
|
||||||
json(conn, %{})
|
json(conn, %{})
|
||||||
|
@ -244,17 +249,20 @@ def token_revoke(conn, %{"token" => _token} = params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def token_revoke(conn, params), do: bad_request(conn, params)
|
def token_revoke(%Plug.Conn{} = conn, params), do: bad_request(conn, params)
|
||||||
|
|
||||||
# Response for bad request
|
# Response for bad request
|
||||||
defp bad_request(conn, _) do
|
defp bad_request(%Plug.Conn{} = conn, _) do
|
||||||
conn
|
conn
|
||||||
|> put_status(500)
|
|> put_status(500)
|
||||||
|> json(%{error: "Bad request"})
|
|> json(%{error: "Bad request"})
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Prepares OAuth request to provider for Ueberauth"
|
@doc "Prepares OAuth request to provider for Ueberauth"
|
||||||
def prepare_request(conn, %{"provider" => provider, "authorization" => auth_attrs}) do
|
def prepare_request(%Plug.Conn{} = conn, %{
|
||||||
|
"provider" => provider,
|
||||||
|
"authorization" => auth_attrs
|
||||||
|
}) do
|
||||||
scope =
|
scope =
|
||||||
auth_attrs
|
auth_attrs
|
||||||
|> Scopes.fetch_scopes([])
|
|> Scopes.fetch_scopes([])
|
||||||
|
@ -275,7 +283,7 @@ def prepare_request(conn, %{"provider" => provider, "authorization" => auth_attr
|
||||||
redirect(conn, to: o_auth_path(conn, :request, provider, params))
|
redirect(conn, to: o_auth_path(conn, :request, provider, params))
|
||||||
end
|
end
|
||||||
|
|
||||||
def request(conn, params) do
|
def request(%Plug.Conn{} = conn, params) do
|
||||||
message =
|
message =
|
||||||
if params["provider"] do
|
if params["provider"] do
|
||||||
"Unsupported OAuth provider: #{params["provider"]}."
|
"Unsupported OAuth provider: #{params["provider"]}."
|
||||||
|
@ -288,7 +296,7 @@ def request(conn, params) do
|
||||||
|> redirect(to: "/")
|
|> redirect(to: "/")
|
||||||
end
|
end
|
||||||
|
|
||||||
def callback(%{assigns: %{ueberauth_failure: failure}} = conn, params) do
|
def callback(%Plug.Conn{assigns: %{ueberauth_failure: failure}} = conn, params) do
|
||||||
params = callback_params(params)
|
params = callback_params(params)
|
||||||
messages = for e <- Map.get(failure, :errors, []), do: e.message
|
messages = for e <- Map.get(failure, :errors, []), do: e.message
|
||||||
message = Enum.join(messages, "; ")
|
message = Enum.join(messages, "; ")
|
||||||
|
@ -298,7 +306,7 @@ def callback(%{assigns: %{ueberauth_failure: failure}} = conn, params) do
|
||||||
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
|
|> redirect(external: redirect_uri(conn, params["redirect_uri"]))
|
||||||
end
|
end
|
||||||
|
|
||||||
def callback(conn, params) do
|
def callback(%Plug.Conn{} = conn, params) do
|
||||||
params = callback_params(params)
|
params = callback_params(params)
|
||||||
|
|
||||||
with {:ok, registration} <- Authenticator.get_registration(conn) do
|
with {:ok, registration} <- Authenticator.get_registration(conn) do
|
||||||
|
@ -333,7 +341,7 @@ defp callback_params(%{"state" => state} = params) do
|
||||||
Map.merge(params, Jason.decode!(state))
|
Map.merge(params, Jason.decode!(state))
|
||||||
end
|
end
|
||||||
|
|
||||||
def registration_details(conn, %{"authorization" => auth_attrs}) do
|
def registration_details(%Plug.Conn{} = conn, %{"authorization" => auth_attrs}) do
|
||||||
render(conn, "register.html", %{
|
render(conn, "register.html", %{
|
||||||
client_id: auth_attrs["client_id"],
|
client_id: auth_attrs["client_id"],
|
||||||
redirect_uri: auth_attrs["redirect_uri"],
|
redirect_uri: auth_attrs["redirect_uri"],
|
||||||
|
@ -344,7 +352,7 @@ def registration_details(conn, %{"authorization" => auth_attrs}) do
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
def register(conn, %{"authorization" => _, "op" => "connect"} = params) do
|
def register(%Plug.Conn{} = conn, %{"authorization" => _, "op" => "connect"} = params) do
|
||||||
with registration_id when not is_nil(registration_id) <- get_session_registration_id(conn),
|
with registration_id when not is_nil(registration_id) <- get_session_registration_id(conn),
|
||||||
%Registration{} = registration <- Repo.get(Registration, registration_id),
|
%Registration{} = registration <- Repo.get(Registration, registration_id),
|
||||||
{_, {:ok, auth}} <-
|
{_, {:ok, auth}} <-
|
||||||
|
@ -363,7 +371,7 @@ def register(conn, %{"authorization" => _, "op" => "connect"} = params) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def register(conn, %{"authorization" => _, "op" => "register"} = params) do
|
def register(%Plug.Conn{} = conn, %{"authorization" => _, "op" => "register"} = params) do
|
||||||
with registration_id when not is_nil(registration_id) <- get_session_registration_id(conn),
|
with registration_id when not is_nil(registration_id) <- get_session_registration_id(conn),
|
||||||
%Registration{} = registration <- Repo.get(Registration, registration_id),
|
%Registration{} = registration <- Repo.get(Registration, registration_id),
|
||||||
{:ok, user} <- Authenticator.create_from_registration(conn, registration) do
|
{:ok, user} <- Authenticator.create_from_registration(conn, registration) do
|
||||||
|
@ -399,7 +407,7 @@ def register(conn, %{"authorization" => _, "op" => "register"} = params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defp do_create_authorization(
|
defp do_create_authorization(
|
||||||
conn,
|
%Plug.Conn{} = conn,
|
||||||
%{
|
%{
|
||||||
"authorization" =>
|
"authorization" =>
|
||||||
%{
|
%{
|
||||||
|
@ -420,13 +428,13 @@ defp do_create_authorization(
|
||||||
end
|
end
|
||||||
|
|
||||||
# Special case: Local MastodonFE
|
# Special case: Local MastodonFE
|
||||||
defp redirect_uri(conn, "."), do: mastodon_api_url(conn, :login)
|
defp redirect_uri(%Plug.Conn{} = conn, "."), do: mastodon_api_url(conn, :login)
|
||||||
|
|
||||||
defp redirect_uri(_conn, redirect_uri), do: redirect_uri
|
defp redirect_uri(%Plug.Conn{}, redirect_uri), do: redirect_uri
|
||||||
|
|
||||||
defp get_session_registration_id(conn), do: get_session(conn, :registration_id)
|
defp get_session_registration_id(%Plug.Conn{} = conn), do: get_session(conn, :registration_id)
|
||||||
|
|
||||||
defp put_session_registration_id(conn, registration_id),
|
defp put_session_registration_id(%Plug.Conn{} = conn, registration_id),
|
||||||
do: put_session(conn, :registration_id, registration_id)
|
do: put_session(conn, :registration_id, registration_id)
|
||||||
|
|
||||||
@spec validate_scopes(App.t(), map()) ::
|
@spec validate_scopes(App.t(), map()) ::
|
||||||
|
@ -436,4 +444,10 @@ defp validate_scopes(app, params) do
|
||||||
|> Scopes.fetch_scopes(app.scopes)
|
|> Scopes.fetch_scopes(app.scopes)
|
||||||
|> Scopes.validates(app.scopes)
|
|> Scopes.validates(app.scopes)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp default_redirect_uri(%App{} = app) do
|
||||||
|
app.redirect_uris
|
||||||
|
|> String.split()
|
||||||
|
|> Enum.at(0)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,7 +14,6 @@ defmodule Pleroma.Web.OAuth.Token do
|
||||||
alias Pleroma.Web.OAuth.Token
|
alias Pleroma.Web.OAuth.Token
|
||||||
alias Pleroma.Web.OAuth.Token.Query
|
alias Pleroma.Web.OAuth.Token.Query
|
||||||
|
|
||||||
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
|
||||||
schema "oauth_tokens" do
|
schema "oauth_tokens" do
|
||||||
|
@ -78,7 +77,7 @@ defp put_refresh_token(changeset, attrs) do
|
||||||
|
|
||||||
defp put_valid_until(changeset, attrs) do
|
defp put_valid_until(changeset, attrs) do
|
||||||
expires_in =
|
expires_in =
|
||||||
Map.get(attrs, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), @expires_in))
|
Map.get(attrs, :valid_until, NaiveDateTime.add(NaiveDateTime.utc_now(), expires_in()))
|
||||||
|
|
||||||
changeset
|
changeset
|
||||||
|> change(%{valid_until: expires_in})
|
|> change(%{valid_until: expires_in})
|
||||||
|
@ -123,4 +122,6 @@ def is_expired?(%__MODULE__{valid_until: valid_until}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_expired?(_), do: false
|
def is_expired?(_), do: false
|
||||||
|
|
||||||
|
defp expires_in, do: Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,15 +4,13 @@ defmodule Pleroma.Web.OAuth.Token.Response do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.OAuth.Token.Utils
|
alias Pleroma.Web.OAuth.Token.Utils
|
||||||
|
|
||||||
@expires_in Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def build(%User{} = user, token, opts \\ %{}) do
|
def build(%User{} = user, token, opts \\ %{}) do
|
||||||
%{
|
%{
|
||||||
token_type: "Bearer",
|
token_type: "Bearer",
|
||||||
access_token: token.token,
|
access_token: token.token,
|
||||||
refresh_token: token.refresh_token,
|
refresh_token: token.refresh_token,
|
||||||
expires_in: @expires_in,
|
expires_in: expires_in(),
|
||||||
scope: Enum.join(token.scopes, " "),
|
scope: Enum.join(token.scopes, " "),
|
||||||
me: user.ap_id
|
me: user.ap_id
|
||||||
}
|
}
|
||||||
|
@ -25,8 +23,10 @@ def build_for_client_credentials(token) do
|
||||||
access_token: token.token,
|
access_token: token.token,
|
||||||
refresh_token: token.refresh_token,
|
refresh_token: token.refresh_token,
|
||||||
created_at: Utils.format_created_at(token),
|
created_at: Utils.format_created_at(token),
|
||||||
expires_in: @expires_in,
|
expires_in: expires_in(),
|
||||||
scope: Enum.join(token.scopes, " ")
|
scope: Enum.join(token.scopes, " ")
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp expires_in, do: Pleroma.Config.get([:oauth2, :token_expires_in], 600)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do
|
defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do
|
||||||
def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do
|
def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do
|
||||||
with elements = [_ | _] <- get_elements(html, key_name, prefix),
|
meta_data =
|
||||||
meta_data =
|
html
|
||||||
Enum.reduce(elements, data, fn el, acc ->
|
|> get_elements(key_name, prefix)
|
||||||
attributes = normalize_attributes(el, prefix, key_name, value_name)
|
|> Enum.reduce(data, fn el, acc ->
|
||||||
|
attributes = normalize_attributes(el, prefix, key_name, value_name)
|
||||||
|
|
||||||
Map.merge(acc, attributes)
|
Map.merge(acc, attributes)
|
||||||
end) do
|
end)
|
||||||
{:ok, meta_data}
|
|> maybe_put_title(html)
|
||||||
|
|
||||||
|
if Enum.empty?(meta_data) do
|
||||||
|
{:error, error_message}
|
||||||
else
|
else
|
||||||
_e -> {:error, error_message}
|
{:ok, meta_data}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -27,4 +31,17 @@ defp normalize_attributes(html_node, prefix, key_name, value_name) do
|
||||||
|
|
||||||
%{String.to_atom(data[key_name]) => data[value_name]}
|
%{String.to_atom(data[key_name]) => data[value_name]}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp maybe_put_title(%{title: _} = meta, _), do: meta
|
||||||
|
|
||||||
|
defp maybe_put_title(meta, html) do
|
||||||
|
case get_page_title(html) do
|
||||||
|
"" -> meta
|
||||||
|
title -> Map.put_new(meta, :title, title)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_page_title(html) do
|
||||||
|
Floki.find(html, "title") |> Floki.text()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -202,6 +202,9 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
put("/statuses/:id", AdminAPIController, :status_update)
|
put("/statuses/:id", AdminAPIController, :status_update)
|
||||||
delete("/statuses/:id", AdminAPIController, :status_delete)
|
delete("/statuses/:id", AdminAPIController, :status_delete)
|
||||||
|
|
||||||
|
get("/config", AdminAPIController, :config_show)
|
||||||
|
post("/config", AdminAPIController, :config_update)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web.TwitterAPI do
|
scope "/", Pleroma.Web.TwitterAPI do
|
||||||
|
@ -412,7 +415,7 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
get("/trends", MastodonAPIController, :empty_array)
|
get("/trends", MastodonAPIController, :empty_array)
|
||||||
|
|
||||||
get("/accounts/search", MastodonAPIController, :account_search)
|
get("/accounts/search", SearchController, :account_search)
|
||||||
|
|
||||||
scope [] do
|
scope [] do
|
||||||
pipe_through(:oauth_read_or_public)
|
pipe_through(:oauth_read_or_public)
|
||||||
|
@ -431,7 +434,7 @@ defmodule Pleroma.Web.Router do
|
||||||
get("/accounts/:id/following", MastodonAPIController, :following)
|
get("/accounts/:id/following", MastodonAPIController, :following)
|
||||||
get("/accounts/:id", MastodonAPIController, :user)
|
get("/accounts/:id", MastodonAPIController, :user)
|
||||||
|
|
||||||
get("/search", MastodonAPIController, :search)
|
get("/search", SearchController, :search)
|
||||||
|
|
||||||
get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites)
|
get("/pleroma/accounts/:id/favourites", MastodonAPIController, :user_favourites)
|
||||||
end
|
end
|
||||||
|
@ -439,7 +442,7 @@ defmodule Pleroma.Web.Router do
|
||||||
|
|
||||||
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
scope "/api/v2", Pleroma.Web.MastodonAPI do
|
||||||
pipe_through([:api, :oauth_read_or_public])
|
pipe_through([:api, :oauth_read_or_public])
|
||||||
get("/search", MastodonAPIController, :search2)
|
get("/search", SearchController, :search2)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/api", Pleroma.Web do
|
scope "/api", Pleroma.Web do
|
||||||
|
@ -604,12 +607,6 @@ defmodule Pleroma.Web.Router do
|
||||||
post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming)
|
post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/", Pleroma.Web do
|
|
||||||
pipe_through(:oembed)
|
|
||||||
|
|
||||||
get("/oembed", OEmbed.OEmbedController, :url)
|
|
||||||
end
|
|
||||||
|
|
||||||
pipeline :activitypub do
|
pipeline :activitypub do
|
||||||
plug(:accepts, ["activity+json", "json"])
|
plug(:accepts, ["activity+json", "json"])
|
||||||
plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
|
plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
|
||||||
|
|
|
@ -110,23 +110,18 @@ def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do
|
||||||
{:noreply, topics}
|
{:noreply, topics}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_cast(%{action: :stream, topic: "user", item: %Notification{} = item}, topics) do
|
def handle_cast(
|
||||||
topic = "user:#{item.user_id}"
|
%{action: :stream, topic: topic, item: %Notification{} = item},
|
||||||
|
topics
|
||||||
Enum.each(topics[topic] || [], fn socket ->
|
)
|
||||||
json =
|
when topic in ["user", "user:notification"] do
|
||||||
%{
|
topics
|
||||||
event: "notification",
|
|> Map.get("#{topic}:#{item.user_id}", [])
|
||||||
payload:
|
|> Enum.each(fn socket ->
|
||||||
NotificationView.render("show.json", %{
|
send(
|
||||||
notification: item,
|
socket.transport_pid,
|
||||||
for: socket.assigns["user"]
|
{:text, represent_notification(socket.assigns[:user], item)}
|
||||||
})
|
)
|
||||||
|> Jason.encode!()
|
|
||||||
}
|
|
||||||
|> Jason.encode!()
|
|
||||||
|
|
||||||
send(socket.transport_pid, {:text, json})
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
{:noreply, topics}
|
{:noreply, topics}
|
||||||
|
@ -216,6 +211,20 @@ def represent_conversation(%Participation{} = participation) do
|
||||||
|> Jason.encode!()
|
|> Jason.encode!()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@spec represent_notification(User.t(), Notification.t()) :: binary()
|
||||||
|
defp represent_notification(%User{} = user, %Notification{} = notify) do
|
||||||
|
%{
|
||||||
|
event: "notification",
|
||||||
|
payload:
|
||||||
|
NotificationView.render(
|
||||||
|
"show.json",
|
||||||
|
%{notification: notify, for: user}
|
||||||
|
)
|
||||||
|
|> Jason.encode!()
|
||||||
|
}
|
||||||
|
|> Jason.encode!()
|
||||||
|
end
|
||||||
|
|
||||||
def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do
|
def push_to_socket(topics, topic, %Activity{data: %{"type" => "Announce"}} = item) do
|
||||||
Enum.each(topics[topic] || [], fn socket ->
|
Enum.each(topics[topic] || [], fn socket ->
|
||||||
# Get the current user so we have up-to-date blocks etc.
|
# Get the current user so we have up-to-date blocks etc.
|
||||||
|
@ -274,7 +283,7 @@ def push_to_socket(topics, topic, item) do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp internal_topic(topic, socket) when topic in ~w[user direct] do
|
defp internal_topic(topic, socket) when topic in ~w[user user:notification direct] do
|
||||||
"#{topic}:#{socket.assigns[:user].id}"
|
"#{topic}:#{socket.assigns[:user].id}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
<h1>Authorization exists</h1>
|
||||||
|
<h2>Access token is <%= @token.token %></h2>
|
19
mix.exs
19
mix.exs
|
@ -85,7 +85,7 @@ defp oauth_deps do
|
||||||
# Type `mix help deps` for examples and options.
|
# Type `mix help deps` for examples and options.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:phoenix, "~> 1.4.1"},
|
{:phoenix, "~> 1.4.8"},
|
||||||
{:plug_cowboy, "~> 2.0"},
|
{:plug_cowboy, "~> 2.0"},
|
||||||
{:phoenix_pubsub, "~> 1.1"},
|
{:phoenix_pubsub, "~> 1.1"},
|
||||||
{:phoenix_ecto, "~> 4.0"},
|
{:phoenix_ecto, "~> 4.0"},
|
||||||
|
@ -136,7 +136,6 @@ defp deps do
|
||||||
{:prometheus_plugs, "~> 1.1"},
|
{:prometheus_plugs, "~> 1.1"},
|
||||||
{:prometheus_phoenix, "~> 1.2"},
|
{:prometheus_phoenix, "~> 1.2"},
|
||||||
{:prometheus_ecto, "~> 1.4"},
|
{:prometheus_ecto, "~> 1.4"},
|
||||||
{:prometheus_process_collector, "~> 1.4"},
|
|
||||||
{:recon, github: "ferd/recon", tag: "2.4.0"},
|
{:recon, github: "ferd/recon", tag: "2.4.0"},
|
||||||
{:quack, "~> 0.1.1"},
|
{:quack, "~> 0.1.1"},
|
||||||
{:benchee, "~> 1.0"},
|
{:benchee, "~> 1.0"},
|
||||||
|
@ -177,7 +176,9 @@ defp version(version) do
|
||||||
ahead <- String.replace(describe, tag, "") do
|
ahead <- String.replace(describe, tag, "") do
|
||||||
{String.replace_prefix(tag, "v", ""), if(ahead != "", do: String.trim(ahead))}
|
{String.replace_prefix(tag, "v", ""), if(ahead != "", do: String.trim(ahead))}
|
||||||
else
|
else
|
||||||
_ -> {nil, nil}
|
_ ->
|
||||||
|
{commit_hash, 0} = System.cmd("git", ["rev-parse", "--short", "HEAD"])
|
||||||
|
{nil, "-0-g" <> String.trim(commit_hash)}
|
||||||
end
|
end
|
||||||
|
|
||||||
if git_tag && version != git_tag do
|
if git_tag && version != git_tag do
|
||||||
|
@ -204,7 +205,17 @@ defp version(version) do
|
||||||
string -> "+" <> string
|
string -> "+" <> string
|
||||||
end).()
|
end).()
|
||||||
|
|
||||||
[version, git_pre_release, build]
|
branch_name =
|
||||||
|
with {branch_name, 0} <- System.cmd("git", ["rev-parse", "--abbrev-ref", "HEAD"]),
|
||||||
|
true <- branch_name != "master" do
|
||||||
|
branch_name =
|
||||||
|
String.trim(branch_name)
|
||||||
|
|> String.replace(~r/\W+/, "-")
|
||||||
|
|
||||||
|
"-" <> branch_name
|
||||||
|
end
|
||||||
|
|
||||||
|
[version, git_pre_release, branch_name, build]
|
||||||
|> Enum.filter(fn string -> string && string != "" end)
|
|> Enum.filter(fn string -> string && string != "" end)
|
||||||
|> Enum.join()
|
|> Enum.join()
|
||||||
end
|
end
|
||||||
|
|
10
mix.lock
10
mix.lock
|
@ -12,8 +12,8 @@
|
||||||
"comeonin": {:hex, :comeonin, "4.1.1", "c7304fc29b45b897b34142a91122bc72757bc0c295e9e824999d5179ffc08416", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
|
"comeonin": {:hex, :comeonin, "4.1.1", "c7304fc29b45b897b34142a91122bc72757bc0c295e9e824999d5179ffc08416", [:mix], [{:argon2_elixir, "~> 1.2", [hex: :argon2_elixir, repo: "hexpm", optional: true]}, {:bcrypt_elixir, "~> 0.12.1 or ~> 1.0", [hex: :bcrypt_elixir, repo: "hexpm", optional: true]}, {:pbkdf2_elixir, "~> 0.12", [hex: :pbkdf2_elixir, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
|
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
|
||||||
"cors_plug": {:hex, :cors_plug, "1.5.2", "72df63c87e4f94112f458ce9d25800900cc88608c1078f0e4faddf20933eda6e", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"cors_plug": {:hex, :cors_plug, "1.5.2", "72df63c87e4f94112f458ce9d25800900cc88608c1078f0e4faddf20933eda6e", [:mix], [{:plug, "~> 1.3 or ~> 1.4 or ~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"cowboy": {:hex, :cowboy, "2.6.1", "f2e06f757c337b3b311f9437e6e072b678fcd71545a7b2865bdaa154d078593f", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
"cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"cowlib": {:hex, :cowlib, "2.7.0", "3ef16e77562f9855a2605900cedb15c1462d76fb1be6a32fc3ae91973ee543d2", [:rebar3], [], "hexpm"},
|
"cowlib": {:hex, :cowlib, "2.7.3", "a7ffcd0917e6d50b4d5fb28e9e2085a0ceb3c97dea310505f7460ff5ed764ce9", [:rebar3], [], "hexpm"},
|
||||||
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
|
"crypt": {:git, "https://github.com/msantos/crypt", "1f2b58927ab57e72910191a7ebaeff984382a1d3", [ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"]},
|
||||||
"db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
|
"db_connection": {:hex, :db_connection, "2.0.6", "bde2f85d047969c5b5800cb8f4b3ed6316c8cb11487afedac4aa5f93fd39abfa", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
|
@ -55,13 +55,13 @@
|
||||||
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
|
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
|
||||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||||
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.3", "6706a148809a29c306062862c803406e88f048277f6e85b68faf73291e820b84", [:mix], [], "hexpm"},
|
"pbkdf2_elixir": {:hex, :pbkdf2_elixir, "0.12.3", "6706a148809a29c306062862c803406e88f048277f6e85b68faf73291e820b84", [:mix], [], "hexpm"},
|
||||||
"phoenix": {:hex, :phoenix, "1.4.1", "801f9d632808657f1f7c657c8bbe624caaf2ba91429123ebe3801598aea4c3d9", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}], "hexpm"},
|
"phoenix": {:hex, :phoenix, "1.4.8", "c72dc3adeb49c70eb963a0ea24f7a064ec1588e651e84e1b7ad5ed8253c0b4a2", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"phoenix_html": {:hex, :phoenix_html, "2.13.1", "fa8f034b5328e2dfa0e4131b5569379003f34bc1fafdaa84985b0b9d2f12e68b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"phoenix_html": {:hex, :phoenix_html, "2.13.1", "fa8f034b5328e2dfa0e4131b5569379003f34bc1fafdaa84985b0b9d2f12e68b", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.1", "6668d787e602981f24f17a5fbb69cc98f8ab085114ebfac6cc36e10a90c8e93c", [:mix], [], "hexpm"},
|
"phoenix_pubsub": {:hex, :phoenix_pubsub, "1.1.2", "496c303bdf1b2e98a9d26e89af5bba3ab487ba3a3735f74bf1f4064d2a845a3e", [:mix], [], "hexpm"},
|
||||||
"pleroma_job_queue": {:hex, :pleroma_job_queue, "0.2.0", "879e660aa1cebe8dc6f0aaaa6aa48b4875e89cd961d4a585fd128e0773b31a18", [:mix], [], "hexpm"},
|
"pleroma_job_queue": {:hex, :pleroma_job_queue, "0.2.0", "879e660aa1cebe8dc6f0aaaa6aa48b4875e89cd961d4a585fd128e0773b31a18", [:mix], [], "hexpm"},
|
||||||
"plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
"plug": {:hex, :plug, "1.8.2", "0bcce1daa420f189a6491f3940cc77ea7fb1919761175c9c3b59800d897440fc", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm"},
|
||||||
"plug_cowboy": {:hex, :plug_cowboy, "2.0.1", "d798f8ee5acc86b7d42dbe4450b8b0dadf665ce588236eb0a751a132417a980e", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"plug_cowboy": {:hex, :plug_cowboy, "2.0.2", "6055f16868cc4882b24b6e1d63d2bada94fb4978413377a3b32ac16c18dffba2", [:mix], [{:cowboy, "~> 2.5", [hex: :cowboy, repo: "hexpm", optional: false]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
"plug_crypto": {:hex, :plug_crypto, "1.0.0", "18e49317d3fa343f24620ed22795ec29d4a5e602d52d1513ccea0b07d8ea7d4d", [:mix], [], "hexpm"},
|
||||||
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
"plug_static_index_html": {:hex, :plug_static_index_html, "1.0.0", "840123d4d3975585133485ea86af73cb2600afd7f2a976f9f5fd8b3808e636a0", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
||||||
|
|
13
priv/repo/migrations/20190518032627_create_config.exs
Normal file
13
priv/repo/migrations/20190518032627_create_config.exs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateConfig do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:config) do
|
||||||
|
add(:key, :string)
|
||||||
|
add(:value, :binary)
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create(unique_index(:config, :key))
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# XXX: This should be removed when elixir's releases get custom command support
|
# XXX: This should be removed when elixir's releases get custom command support
|
||||||
if [ -z "$1" ] || [ "$1" == "help" ]; then
|
if [ -z "$1" ] || [ "$1" = "help" ]; then
|
||||||
echo "Usage: $(basename "$0") COMMAND [ARGS]
|
echo "Usage: $(basename "$0") COMMAND [ARGS]
|
||||||
|
|
||||||
The known commands are:
|
The known commands are:
|
||||||
|
@ -15,5 +15,5 @@ if [ -z "$1" ] || [ "$1" == "help" ]; then
|
||||||
else
|
else
|
||||||
SCRIPT=$(readlink -f "$0")
|
SCRIPT=$(readlink -f "$0")
|
||||||
SCRIPTPATH=$(dirname "$SCRIPT")
|
SCRIPTPATH=$(dirname "$SCRIPT")
|
||||||
$SCRIPTPATH/pleroma eval 'Pleroma.ReleaseTasks.run("'"$*"'")'
|
"$SCRIPTPATH"/pleroma eval 'Pleroma.ReleaseTasks.run("'"$*"'")'
|
||||||
fi
|
fi
|
||||||
|
|
35
test/config/transfer_task_test.exs
Normal file
35
test/config/transfer_task_test.exs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
defmodule Pleroma.Config.TransferTaskTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
setup do
|
||||||
|
dynamic = Pleroma.Config.get([:instance, :dynamic_configuration])
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
Pleroma.Config.put([:instance, :dynamic_configuration], dynamic)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "transfer config values from db to env" do
|
||||||
|
refute Application.get_env(:pleroma, :test_key)
|
||||||
|
Pleroma.Web.AdminAPI.Config.create(%{key: "test_key", value: [live: 2, com: 3]})
|
||||||
|
|
||||||
|
Pleroma.Config.TransferTask.start_link()
|
||||||
|
|
||||||
|
assert Application.get_env(:pleroma, :test_key) == [live: 2, com: 3]
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
Application.delete_env(:pleroma, :test_key)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "non existing atom" do
|
||||||
|
Pleroma.Web.AdminAPI.Config.create(%{key: "undefined_atom_key", value: [live: 2, com: 3]})
|
||||||
|
|
||||||
|
assert ExUnit.CaptureLog.capture_log(fn ->
|
||||||
|
Pleroma.Config.TransferTask.start_link()
|
||||||
|
end) =~
|
||||||
|
"updating env causes error, key: \"undefined_atom_key\", error: %ArgumentError{message: \"argument error\"}"
|
||||||
|
end
|
||||||
|
end
|
12
test/fixtures/rich_media/ogp-missing-title.html
vendored
Normal file
12
test/fixtures/rich_media/ogp-missing-title.html
vendored
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<html prefix="og: http://ogp.me/ns#">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>The Rock (1996)</title>
|
||||||
|
<meta property="og:type" content="video.movie" />
|
||||||
|
<meta property="og:url" content="http://www.imdb.com/title/tt0117500/" />
|
||||||
|
<meta property="og:image" content="http://ia.media-imdb.com/images/rock.jpg" />
|
||||||
|
<meta property="og:description"
|
||||||
|
content="Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
</html>
|
|
@ -4,8 +4,12 @@
|
||||||
|
|
||||||
defmodule Pleroma.HTMLTest do
|
defmodule Pleroma.HTMLTest do
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
@html_sample """
|
@html_sample """
|
||||||
<b>this is in bold</b>
|
<b>this is in bold</b>
|
||||||
<p>this is a paragraph</p>
|
<p>this is a paragraph</p>
|
||||||
|
@ -160,4 +164,53 @@ test "filters invalid microformats markup" do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "extract_first_external_url" do
|
||||||
|
test "extracts the url" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" =>
|
||||||
|
"I think I just found the best github repo https://github.com/komeiji-satori/Dress"
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
{:ok, url} = HTML.extract_first_external_url(object, object.data["content"])
|
||||||
|
assert url == "https://github.com/komeiji-satori/Dress"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "skips mentions" do
|
||||||
|
user = insert(:user)
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" =>
|
||||||
|
"@#{other_user.nickname} install misskey! https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md"
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
{:ok, url} = HTML.extract_first_external_url(object, object.data["content"])
|
||||||
|
|
||||||
|
assert url == "https://github.com/syuilo/misskey/blob/develop/docs/setup.en.md"
|
||||||
|
|
||||||
|
refute url == other_user.ap_id
|
||||||
|
end
|
||||||
|
|
||||||
|
test "skips hashtags" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" =>
|
||||||
|
"#cofe https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
|
||||||
|
})
|
||||||
|
|
||||||
|
object = Object.normalize(activity)
|
||||||
|
{:ok, url} = HTML.extract_first_external_url(object, object.data["content"])
|
||||||
|
|
||||||
|
assert url == "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=72255140"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -97,5 +97,15 @@ test "receives well formatted events" do
|
||||||
test "accepts valid tokens", state do
|
test "accepts valid tokens", state do
|
||||||
assert {:ok, _} = start_socket("?stream=user&access_token=#{state.token.token}")
|
assert {:ok, _} = start_socket("?stream=user&access_token=#{state.token.token}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "accepts the 'user' stream", %{token: token} = _state do
|
||||||
|
assert {:ok, _} = start_socket("?stream=user&access_token=#{token.token}")
|
||||||
|
assert {:error, {403, "Forbidden"}} = start_socket("?stream=user")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "accepts the 'user:notification' stream", %{token: token} = _state do
|
||||||
|
assert {:ok, _} = start_socket("?stream=user:notification&access_token=#{token.token}")
|
||||||
|
assert {:error, {403, "Forbidden"}} = start_socket("?stream=user:notification")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@ defmodule Pleroma.NotificationTest do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
alias Pleroma.Web.CommonAPI
|
alias Pleroma.Web.CommonAPI
|
||||||
|
alias Pleroma.Web.Streamer
|
||||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||||
import Pleroma.Factory
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
@ -44,13 +45,42 @@ test "it creates a notification for subscribed users" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "create_notification" do
|
describe "create_notification" do
|
||||||
|
setup do
|
||||||
|
GenServer.start(Streamer, %{}, name: Streamer)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
if pid = Process.whereis(Streamer) do
|
||||||
|
Process.exit(pid, :kill)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it creates a notification for user and send to the 'user' and the 'user:notification' stream" do
|
||||||
|
user = insert(:user)
|
||||||
|
task = Task.async(fn -> assert_receive {:text, _}, 4_000 end)
|
||||||
|
task_user_notification = Task.async(fn -> assert_receive {:text, _}, 4_000 end)
|
||||||
|
Streamer.add_socket("user", %{transport_pid: task.pid, assigns: %{user: user}})
|
||||||
|
|
||||||
|
Streamer.add_socket(
|
||||||
|
"user:notification",
|
||||||
|
%{transport_pid: task_user_notification.pid, assigns: %{user: user}}
|
||||||
|
)
|
||||||
|
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
|
||||||
|
notify = Notification.create_notification(activity, user)
|
||||||
|
assert notify.user_id == user.id
|
||||||
|
Task.await(task)
|
||||||
|
Task.await(task_user_notification)
|
||||||
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for user if the user blocks the activity author" do
|
test "it doesn't create a notification for user if the user blocks the activity author" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
author = User.get_cached_by_ap_id(activity.data["actor"])
|
author = User.get_cached_by_ap_id(activity.data["actor"])
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, user} = User.block(user, author)
|
{:ok, user} = User.block(user, author)
|
||||||
|
|
||||||
assert nil == Notification.create_notification(activity, user)
|
refute Notification.create_notification(activity, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notificatin for the user if the user mutes the activity author" do
|
test "it doesn't create a notificatin for the user if the user mutes the activity author" do
|
||||||
|
@ -60,7 +90,7 @@ test "it doesn't create a notificatin for the user if the user mutes the activit
|
||||||
muter = Repo.get(User, muter.id)
|
muter = Repo.get(User, muter.id)
|
||||||
{:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
|
{:ok, activity} = CommonAPI.post(muted, %{"status" => "Hi @#{muter.nickname}"})
|
||||||
|
|
||||||
assert nil == Notification.create_notification(activity, muter)
|
refute Notification.create_notification(activity, muter)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for an activity from a muted thread" do
|
test "it doesn't create a notification for an activity from a muted thread" do
|
||||||
|
@ -75,7 +105,7 @@ test "it doesn't create a notification for an activity from a muted thread" do
|
||||||
"in_reply_to_status_id" => activity.id
|
"in_reply_to_status_id" => activity.id
|
||||||
})
|
})
|
||||||
|
|
||||||
assert nil == Notification.create_notification(activity, muter)
|
refute Notification.create_notification(activity, muter)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it disables notifications from followers" do
|
test "it disables notifications from followers" do
|
||||||
|
@ -83,14 +113,14 @@ test "it disables notifications from followers" do
|
||||||
followed = insert(:user, info: %{notification_settings: %{"followers" => false}})
|
followed = insert(:user, info: %{notification_settings: %{"followers" => false}})
|
||||||
User.follow(follower, followed)
|
User.follow(follower, followed)
|
||||||
{:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
|
{:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
|
||||||
assert nil == Notification.create_notification(activity, followed)
|
refute Notification.create_notification(activity, followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it disables notifications from non-followers" do
|
test "it disables notifications from non-followers" do
|
||||||
follower = insert(:user)
|
follower = insert(:user)
|
||||||
followed = insert(:user, info: %{notification_settings: %{"non_followers" => false}})
|
followed = insert(:user, info: %{notification_settings: %{"non_followers" => false}})
|
||||||
{:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
|
{:ok, activity} = CommonAPI.post(follower, %{"status" => "hey @#{followed.nickname}"})
|
||||||
assert nil == Notification.create_notification(activity, followed)
|
refute Notification.create_notification(activity, followed)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it disables notifications from people the user follows" do
|
test "it disables notifications from people the user follows" do
|
||||||
|
@ -99,21 +129,21 @@ test "it disables notifications from people the user follows" do
|
||||||
User.follow(follower, followed)
|
User.follow(follower, followed)
|
||||||
follower = Repo.get(User, follower.id)
|
follower = Repo.get(User, follower.id)
|
||||||
{:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
|
{:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
|
||||||
assert nil == Notification.create_notification(activity, follower)
|
refute Notification.create_notification(activity, follower)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it disables notifications from people the user does not follow" do
|
test "it disables notifications from people the user does not follow" do
|
||||||
follower = insert(:user, info: %{notification_settings: %{"non_follows" => false}})
|
follower = insert(:user, info: %{notification_settings: %{"non_follows" => false}})
|
||||||
followed = insert(:user)
|
followed = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
|
{:ok, activity} = CommonAPI.post(followed, %{"status" => "hey @#{follower.nickname}"})
|
||||||
assert nil == Notification.create_notification(activity, follower)
|
refute Notification.create_notification(activity, follower)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for user if he is the activity author" do
|
test "it doesn't create a notification for user if he is the activity author" do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
author = User.get_cached_by_ap_id(activity.data["actor"])
|
author = User.get_cached_by_ap_id(activity.data["actor"])
|
||||||
|
|
||||||
assert nil == Notification.create_notification(activity, author)
|
refute Notification.create_notification(activity, author)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for follow-unfollow-follow chains" do
|
test "it doesn't create a notification for follow-unfollow-follow chains" do
|
||||||
|
@ -123,7 +153,7 @@ test "it doesn't create a notification for follow-unfollow-follow chains" do
|
||||||
Notification.create_notification(activity, followed_user)
|
Notification.create_notification(activity, followed_user)
|
||||||
TwitterAPI.unfollow(user, %{"user_id" => followed_user.id})
|
TwitterAPI.unfollow(user, %{"user_id" => followed_user.id})
|
||||||
{:ok, _, _, activity_dupe} = TwitterAPI.follow(user, %{"user_id" => followed_user.id})
|
{:ok, _, _, activity_dupe} = TwitterAPI.follow(user, %{"user_id" => followed_user.id})
|
||||||
assert nil == Notification.create_notification(activity_dupe, followed_user)
|
refute Notification.create_notification(activity_dupe, followed_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for like-unlike-like chains" do
|
test "it doesn't create a notification for like-unlike-like chains" do
|
||||||
|
@ -134,7 +164,7 @@ test "it doesn't create a notification for like-unlike-like chains" do
|
||||||
Notification.create_notification(fav_status, liked_user)
|
Notification.create_notification(fav_status, liked_user)
|
||||||
TwitterAPI.unfav(user, status.id)
|
TwitterAPI.unfav(user, status.id)
|
||||||
{:ok, dupe} = TwitterAPI.fav(user, status.id)
|
{:ok, dupe} = TwitterAPI.fav(user, status.id)
|
||||||
assert nil == Notification.create_notification(dupe, liked_user)
|
refute Notification.create_notification(dupe, liked_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create a notification for repeat-unrepeat-repeat chains" do
|
test "it doesn't create a notification for repeat-unrepeat-repeat chains" do
|
||||||
|
@ -150,7 +180,7 @@ test "it doesn't create a notification for repeat-unrepeat-repeat chains" do
|
||||||
Notification.create_notification(retweeted_activity, retweeted_user)
|
Notification.create_notification(retweeted_activity, retweeted_user)
|
||||||
TwitterAPI.unrepeat(user, status.id)
|
TwitterAPI.unrepeat(user, status.id)
|
||||||
{:ok, dupe} = TwitterAPI.repeat(user, status.id)
|
{:ok, dupe} = TwitterAPI.repeat(user, status.id)
|
||||||
assert nil == Notification.create_notification(dupe, retweeted_user)
|
refute Notification.create_notification(dupe, retweeted_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "it doesn't create duplicate notifications for follow+subscribed users" do
|
test "it doesn't create duplicate notifications for follow+subscribed users" do
|
||||||
|
|
|
@ -7,7 +7,17 @@ defmodule Pleroma.Object.FetcherTest do
|
||||||
import Tesla.Mock
|
import Tesla.Mock
|
||||||
|
|
||||||
setup do
|
setup do
|
||||||
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
mock(fn
|
||||||
|
%{method: :get, url: "https://mastodon.example.org/users/userisgone"} ->
|
||||||
|
%Tesla.Env{status: 410}
|
||||||
|
|
||||||
|
%{method: :get, url: "https://mastodon.example.org/users/userisgone404"} ->
|
||||||
|
%Tesla.Env{status: 404}
|
||||||
|
|
||||||
|
env ->
|
||||||
|
apply(HttpRequestMock, :request, [env])
|
||||||
|
end)
|
||||||
|
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -81,10 +91,24 @@ test "it can fetch peertube videos" do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "all objects with fake directions are rejected by the object fetcher" do
|
test "all objects with fake directions are rejected by the object fetcher" do
|
||||||
{:error, _} =
|
assert {:error, _} =
|
||||||
Fetcher.fetch_and_contain_remote_object_from_id(
|
Fetcher.fetch_and_contain_remote_object_from_id(
|
||||||
"https://info.pleroma.site/activity4.json"
|
"https://info.pleroma.site/activity4.json"
|
||||||
)
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle HTTP 410 Gone response" do
|
||||||
|
assert {:error, "Object has been deleted"} ==
|
||||||
|
Fetcher.fetch_and_contain_remote_object_from_id(
|
||||||
|
"https://mastodon.example.org/users/userisgone"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "handle HTTP 404 response" do
|
||||||
|
assert {:error, "Object has been deleted"} ==
|
||||||
|
Fetcher.fetch_and_contain_remote_object_from_id(
|
||||||
|
"https://mastodon.example.org/users/userisgone404"
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -310,4 +310,17 @@ def registration_factory do
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def config_factory do
|
||||||
|
%Pleroma.Web.AdminAPI.Config{
|
||||||
|
key: sequence(:key, &"some_key_#{&1}"),
|
||||||
|
value:
|
||||||
|
sequence(
|
||||||
|
:value,
|
||||||
|
fn key ->
|
||||||
|
:erlang.term_to_binary(%{another_key: "#{key}somevalue", another: "#{key}somevalue"})
|
||||||
|
end
|
||||||
|
)
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
54
test/tasks/config_test.exs
Normal file
54
test/tasks/config_test.exs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
defmodule Mix.Tasks.Pleroma.ConfigTest do
|
||||||
|
use Pleroma.DataCase
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.Web.AdminAPI.Config
|
||||||
|
|
||||||
|
setup_all do
|
||||||
|
Mix.shell(Mix.Shell.Process)
|
||||||
|
temp_file = "config/temp.migrated.secret.exs"
|
||||||
|
|
||||||
|
dynamic = Pleroma.Config.get([:instance, :dynamic_configuration])
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
Mix.shell(Mix.Shell.IO)
|
||||||
|
Application.delete_env(:pleroma, :first_setting)
|
||||||
|
Application.delete_env(:pleroma, :second_setting)
|
||||||
|
Pleroma.Config.put([:instance, :dynamic_configuration], dynamic)
|
||||||
|
:ok = File.rm(temp_file)
|
||||||
|
end)
|
||||||
|
|
||||||
|
{:ok, temp_file: temp_file}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "settings are migrated to db" do
|
||||||
|
assert Repo.all(Config) == []
|
||||||
|
|
||||||
|
Application.put_env(:pleroma, :first_setting, key: "value", key2: [Pleroma.Repo])
|
||||||
|
Application.put_env(:pleroma, :second_setting, key: "value2", key2: [Pleroma.Activity])
|
||||||
|
|
||||||
|
Mix.Tasks.Pleroma.Config.run(["migrate_to_db"])
|
||||||
|
|
||||||
|
first_db = Config.get_by_key("first_setting")
|
||||||
|
second_db = Config.get_by_key("second_setting")
|
||||||
|
refute Config.get_by_key("Pleroma.Repo")
|
||||||
|
|
||||||
|
assert Config.from_binary(first_db.value) == [key: "value", key2: [Pleroma.Repo]]
|
||||||
|
assert Config.from_binary(second_db.value) == [key: "value2", key2: [Pleroma.Activity]]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "settings are migrated to file and deleted from db", %{temp_file: temp_file} do
|
||||||
|
Config.create(%{key: "setting_first", value: [key: "value", key2: [Pleroma.Activity]]})
|
||||||
|
Config.create(%{key: "setting_second", value: [key: "valu2", key2: [Pleroma.Repo]]})
|
||||||
|
|
||||||
|
Mix.Tasks.Pleroma.Config.run(["migrate_from_db", "temp"])
|
||||||
|
|
||||||
|
assert Repo.all(Config) == []
|
||||||
|
assert File.exists?(temp_file)
|
||||||
|
{:ok, file} = File.read(temp_file)
|
||||||
|
|
||||||
|
assert file =~ "config :pleroma, setting_first:"
|
||||||
|
assert file =~ "config :pleroma, setting_second:"
|
||||||
|
end
|
||||||
|
end
|
|
@ -36,6 +36,8 @@ test "running gen" do
|
||||||
"--dbpass",
|
"--dbpass",
|
||||||
"dbpass",
|
"dbpass",
|
||||||
"--indexable",
|
"--indexable",
|
||||||
|
"y",
|
||||||
|
"--db-configurable",
|
||||||
"y"
|
"y"
|
||||||
])
|
])
|
||||||
end
|
end
|
||||||
|
@ -53,6 +55,7 @@ test "running gen" do
|
||||||
assert generated_config =~ "database: \"dbname\""
|
assert generated_config =~ "database: \"dbname\""
|
||||||
assert generated_config =~ "username: \"dbuser\""
|
assert generated_config =~ "username: \"dbuser\""
|
||||||
assert generated_config =~ "password: \"dbpass\""
|
assert generated_config =~ "password: \"dbpass\""
|
||||||
|
assert generated_config =~ "dynamic_configuration: true"
|
||||||
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
|
assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1011,6 +1011,18 @@ test "User.delete() plugs any possible zombie objects" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "User.search" do
|
describe "User.search" do
|
||||||
|
test "accepts limit parameter" do
|
||||||
|
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
|
||||||
|
assert length(User.search("john", limit: 3)) == 3
|
||||||
|
assert length(User.search("john")) == 5
|
||||||
|
end
|
||||||
|
|
||||||
|
test "accepts offset parameter" do
|
||||||
|
Enum.each(0..4, &insert(:user, %{nickname: "john#{&1}"}))
|
||||||
|
assert length(User.search("john", limit: 3)) == 3
|
||||||
|
assert length(User.search("john", limit: 3, offset: 3)) == 2
|
||||||
|
end
|
||||||
|
|
||||||
test "finds a user by full or partial nickname" do
|
test "finds a user by full or partial nickname" do
|
||||||
user = insert(:user, %{nickname: "john"})
|
user = insert(:user, %{nickname: "john"})
|
||||||
|
|
||||||
|
@ -1077,6 +1089,24 @@ test "finds users, boosting ranks of friends and followers" do
|
||||||
Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
|
Enum.map(User.search("doe", resolve: false, for_user: u1), & &1.id) == []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "finds followers of user by partial name" do
|
||||||
|
u1 = insert(:user)
|
||||||
|
u2 = insert(:user, %{name: "Jimi"})
|
||||||
|
follower_jimi = insert(:user, %{name: "Jimi Hendrix"})
|
||||||
|
follower_lizz = insert(:user, %{name: "Lizz Wright"})
|
||||||
|
friend = insert(:user, %{name: "Jimi"})
|
||||||
|
|
||||||
|
{:ok, follower_jimi} = User.follow(follower_jimi, u1)
|
||||||
|
{:ok, _follower_lizz} = User.follow(follower_lizz, u2)
|
||||||
|
{:ok, u1} = User.follow(u1, friend)
|
||||||
|
|
||||||
|
assert Enum.map(User.search("jimi", following: true, for_user: u1), & &1.id) == [
|
||||||
|
follower_jimi.id
|
||||||
|
]
|
||||||
|
|
||||||
|
assert User.search("lizz", following: true, for_user: u1) == []
|
||||||
|
end
|
||||||
|
|
||||||
test "find local and remote users for authenticated users" do
|
test "find local and remote users for authenticated users" do
|
||||||
u1 = insert(:user, %{name: "lain"})
|
u1 = insert(:user, %{name: "lain"})
|
||||||
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
|
u2 = insert(:user, %{name: "ebn", nickname: "lain@mastodon.social", local: false})
|
||||||
|
|
|
@ -1292,4 +1292,176 @@ test "returns error when status is not exist", %{conn: conn} do
|
||||||
assert json_response(conn, :bad_request) == "Could not delete"
|
assert json_response(conn, :bad_request) == "Could not delete"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "GET /api/pleroma/admin/config" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
|
%{conn: assign(conn, :user, admin)}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "without any settings in db", %{conn: conn} do
|
||||||
|
conn = get(conn, "/api/pleroma/admin/config")
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{"configs" => []}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with settings in db", %{conn: conn} do
|
||||||
|
config1 = insert(:config)
|
||||||
|
config2 = insert(:config)
|
||||||
|
|
||||||
|
conn = get(conn, "/api/pleroma/admin/config")
|
||||||
|
|
||||||
|
%{
|
||||||
|
"configs" => [
|
||||||
|
%{
|
||||||
|
"key" => key1,
|
||||||
|
"value" => _
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"key" => key2,
|
||||||
|
"value" => _
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert key1 == config1.key
|
||||||
|
assert key2 == config2.key
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/admin/config" do
|
||||||
|
setup %{conn: conn} do
|
||||||
|
admin = insert(:user, info: %{is_admin: true})
|
||||||
|
|
||||||
|
temp_file = "config/test.migrated.secret.exs"
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
Application.delete_env(:pleroma, :key1)
|
||||||
|
Application.delete_env(:pleroma, :key2)
|
||||||
|
Application.delete_env(:pleroma, :key3)
|
||||||
|
Application.delete_env(:pleroma, :key4)
|
||||||
|
Application.delete_env(:pleroma, :keyaa1)
|
||||||
|
Application.delete_env(:pleroma, :keyaa2)
|
||||||
|
:ok = File.rm(temp_file)
|
||||||
|
end)
|
||||||
|
|
||||||
|
dynamic = Pleroma.Config.get([:instance, :dynamic_configuration])
|
||||||
|
|
||||||
|
Pleroma.Config.put([:instance, :dynamic_configuration], true)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
Pleroma.Config.put([:instance, :dynamic_configuration], dynamic)
|
||||||
|
end)
|
||||||
|
|
||||||
|
%{conn: assign(conn, :user, admin)}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create new config setting in db", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
post(conn, "/api/pleroma/admin/config", %{
|
||||||
|
configs: [
|
||||||
|
%{key: "key1", value: "value1"},
|
||||||
|
%{
|
||||||
|
key: "key2",
|
||||||
|
value: %{
|
||||||
|
"nested_1" => "nested_value1",
|
||||||
|
"nested_2" => [
|
||||||
|
%{"nested_22" => "nested_value222"},
|
||||||
|
%{"nested_33" => %{"nested_44" => "nested_444"}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: "key3",
|
||||||
|
value: [
|
||||||
|
%{"nested_3" => ":nested_3", "nested_33" => "nested_33"},
|
||||||
|
%{"nested_4" => ":true"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
key: "key4",
|
||||||
|
value: %{"nested_5" => ":upload", "endpoint" => "https://example.com"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"configs" => [
|
||||||
|
%{
|
||||||
|
"key" => "key1",
|
||||||
|
"value" => "value1"
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"key" => "key2",
|
||||||
|
"value" => [
|
||||||
|
%{"nested_1" => "nested_value1"},
|
||||||
|
%{
|
||||||
|
"nested_2" => [
|
||||||
|
%{"nested_22" => "nested_value222"},
|
||||||
|
%{"nested_33" => %{"nested_44" => "nested_444"}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"key" => "key3",
|
||||||
|
"value" => [
|
||||||
|
[%{"nested_3" => "nested_3"}, %{"nested_33" => "nested_33"}],
|
||||||
|
%{"nested_4" => true}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
%{
|
||||||
|
"key" => "key4",
|
||||||
|
"value" => [%{"endpoint" => "https://example.com"}, %{"nested_5" => "upload"}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Application.get_env(:pleroma, :key1) == "value1"
|
||||||
|
|
||||||
|
assert Application.get_env(:pleroma, :key2) == [
|
||||||
|
nested_1: "nested_value1",
|
||||||
|
nested_2: [
|
||||||
|
[nested_22: "nested_value222"],
|
||||||
|
[nested_33: [nested_44: "nested_444"]]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
assert Application.get_env(:pleroma, :key3) == [
|
||||||
|
[nested_3: :nested_3, nested_33: "nested_33"],
|
||||||
|
[nested_4: true]
|
||||||
|
]
|
||||||
|
|
||||||
|
assert Application.get_env(:pleroma, :key4) == [
|
||||||
|
endpoint: "https://example.com",
|
||||||
|
nested_5: :upload
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update config setting & delete", %{conn: conn} do
|
||||||
|
config1 = insert(:config, key: "keyaa1")
|
||||||
|
config2 = insert(:config, key: "keyaa2")
|
||||||
|
|
||||||
|
conn =
|
||||||
|
post(conn, "/api/pleroma/admin/config", %{
|
||||||
|
configs: [
|
||||||
|
%{key: config1.key, value: "another_value"},
|
||||||
|
%{key: config2.key, delete: "true"}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{
|
||||||
|
"configs" => [
|
||||||
|
%{
|
||||||
|
"key" => config1.key,
|
||||||
|
"value" => "another_value"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
assert Application.get_env(:pleroma, :keyaa1) == "another_value"
|
||||||
|
refute Application.get_env(:pleroma, :keyaa2)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
183
test/web/admin_api/config_test.exs
Normal file
183
test/web/admin_api/config_test.exs
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
defmodule Pleroma.Web.AdminAPI.ConfigTest do
|
||||||
|
use Pleroma.DataCase, async: true
|
||||||
|
import Pleroma.Factory
|
||||||
|
alias Pleroma.Web.AdminAPI.Config
|
||||||
|
|
||||||
|
test "get_by_key/1" do
|
||||||
|
config = insert(:config)
|
||||||
|
insert(:config)
|
||||||
|
|
||||||
|
assert config == Config.get_by_key(config.key)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create/1" do
|
||||||
|
{:ok, config} = Config.create(%{key: "some_key", value: "some_value"})
|
||||||
|
assert config == Config.get_by_key("some_key")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update/1" do
|
||||||
|
config = insert(:config)
|
||||||
|
{:ok, updated} = Config.update(config, %{value: "some_value"})
|
||||||
|
loaded = Config.get_by_key(config.key)
|
||||||
|
assert loaded == updated
|
||||||
|
end
|
||||||
|
|
||||||
|
test "update_or_create/1" do
|
||||||
|
config = insert(:config)
|
||||||
|
key2 = "another_key"
|
||||||
|
|
||||||
|
params = [
|
||||||
|
%{key: key2, value: "another_value"},
|
||||||
|
%{key: config.key, value: "new_value"}
|
||||||
|
]
|
||||||
|
|
||||||
|
assert Repo.all(Config) |> length() == 1
|
||||||
|
|
||||||
|
Enum.each(params, &Config.update_or_create(&1))
|
||||||
|
|
||||||
|
assert Repo.all(Config) |> length() == 2
|
||||||
|
|
||||||
|
config1 = Config.get_by_key(config.key)
|
||||||
|
config2 = Config.get_by_key(key2)
|
||||||
|
|
||||||
|
assert config1.value == Config.transform("new_value")
|
||||||
|
assert config2.value == Config.transform("another_value")
|
||||||
|
end
|
||||||
|
|
||||||
|
test "delete/1" do
|
||||||
|
config = insert(:config)
|
||||||
|
{:ok, _} = Config.delete(config.key)
|
||||||
|
refute Config.get_by_key(config.key)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "transform/1" do
|
||||||
|
test "string" do
|
||||||
|
binary = Config.transform("value as string")
|
||||||
|
assert binary == :erlang.term_to_binary("value as string")
|
||||||
|
assert Config.from_binary(binary) == "value as string"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "list of modules" do
|
||||||
|
binary = Config.transform(["Pleroma.Repo", "Pleroma.Activity"])
|
||||||
|
assert binary == :erlang.term_to_binary([Pleroma.Repo, Pleroma.Activity])
|
||||||
|
assert Config.from_binary(binary) == [Pleroma.Repo, Pleroma.Activity]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "list of strings" do
|
||||||
|
binary = Config.transform(["string1", "string2"])
|
||||||
|
assert binary == :erlang.term_to_binary(["string1", "string2"])
|
||||||
|
assert Config.from_binary(binary) == ["string1", "string2"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "map" do
|
||||||
|
binary =
|
||||||
|
Config.transform(%{
|
||||||
|
"types" => "Pleroma.PostgresTypes",
|
||||||
|
"telemetry_event" => ["Pleroma.Repo.Instrumenter"],
|
||||||
|
"migration_lock" => ""
|
||||||
|
})
|
||||||
|
|
||||||
|
assert binary ==
|
||||||
|
:erlang.term_to_binary(
|
||||||
|
telemetry_event: [Pleroma.Repo.Instrumenter],
|
||||||
|
types: Pleroma.PostgresTypes
|
||||||
|
)
|
||||||
|
|
||||||
|
assert Config.from_binary(binary) == [
|
||||||
|
telemetry_event: [Pleroma.Repo.Instrumenter],
|
||||||
|
types: Pleroma.PostgresTypes
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "complex map with nested integers, lists and atoms" do
|
||||||
|
binary =
|
||||||
|
Config.transform(%{
|
||||||
|
"uploader" => "Pleroma.Uploaders.Local",
|
||||||
|
"filters" => ["Pleroma.Upload.Filter.Dedupe"],
|
||||||
|
"link_name" => ":true",
|
||||||
|
"proxy_remote" => ":false",
|
||||||
|
"proxy_opts" => %{
|
||||||
|
"redirect_on_failure" => ":false",
|
||||||
|
"max_body_length" => "i:1048576",
|
||||||
|
"http" => %{
|
||||||
|
"follow_redirect" => ":true",
|
||||||
|
"pool" => ":upload"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert binary ==
|
||||||
|
:erlang.term_to_binary(
|
||||||
|
filters: [Pleroma.Upload.Filter.Dedupe],
|
||||||
|
link_name: true,
|
||||||
|
proxy_opts: [
|
||||||
|
http: [
|
||||||
|
follow_redirect: true,
|
||||||
|
pool: :upload
|
||||||
|
],
|
||||||
|
max_body_length: 1_048_576,
|
||||||
|
redirect_on_failure: false
|
||||||
|
],
|
||||||
|
proxy_remote: false,
|
||||||
|
uploader: Pleroma.Uploaders.Local
|
||||||
|
)
|
||||||
|
|
||||||
|
assert Config.from_binary(binary) ==
|
||||||
|
[
|
||||||
|
filters: [Pleroma.Upload.Filter.Dedupe],
|
||||||
|
link_name: true,
|
||||||
|
proxy_opts: [
|
||||||
|
http: [
|
||||||
|
follow_redirect: true,
|
||||||
|
pool: :upload
|
||||||
|
],
|
||||||
|
max_body_length: 1_048_576,
|
||||||
|
redirect_on_failure: false
|
||||||
|
],
|
||||||
|
proxy_remote: false,
|
||||||
|
uploader: Pleroma.Uploaders.Local
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "keyword" do
|
||||||
|
binary =
|
||||||
|
Config.transform(%{
|
||||||
|
"level" => ":warn",
|
||||||
|
"meta" => [":all"],
|
||||||
|
"webhook_url" => "https://hooks.slack.com/services/YOUR-KEY-HERE"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert binary ==
|
||||||
|
:erlang.term_to_binary(
|
||||||
|
level: :warn,
|
||||||
|
meta: [:all],
|
||||||
|
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert Config.from_binary(binary) == [
|
||||||
|
level: :warn,
|
||||||
|
meta: [:all],
|
||||||
|
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "complex map with sigil" do
|
||||||
|
binary =
|
||||||
|
Config.transform(%{
|
||||||
|
federated_timeline_removal: [],
|
||||||
|
reject: [~r/comp[lL][aA][iI][nN]er/],
|
||||||
|
replace: []
|
||||||
|
})
|
||||||
|
|
||||||
|
assert binary ==
|
||||||
|
:erlang.term_to_binary(
|
||||||
|
federated_timeline_removal: [],
|
||||||
|
reject: [~r/comp[lL][aA][iI][nN]er/],
|
||||||
|
replace: []
|
||||||
|
)
|
||||||
|
|
||||||
|
assert Config.from_binary(binary) ==
|
||||||
|
[federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -19,9 +19,18 @@ test "Represent a user account" do
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
background_image = %{
|
||||||
|
"url" => [%{"href" => "https://example.com/images/asuka_hospital.png"}]
|
||||||
|
}
|
||||||
|
|
||||||
user =
|
user =
|
||||||
insert(:user, %{
|
insert(:user, %{
|
||||||
info: %{note_count: 5, follower_count: 3, source_data: source_data},
|
info: %{
|
||||||
|
note_count: 5,
|
||||||
|
follower_count: 3,
|
||||||
|
source_data: source_data,
|
||||||
|
background: background_image
|
||||||
|
},
|
||||||
nickname: "shp@shitposter.club",
|
nickname: "shp@shitposter.club",
|
||||||
name: ":karjalanpiirakka: shp",
|
name: ":karjalanpiirakka: shp",
|
||||||
bio: "<script src=\"invalid-html\"></script><span>valid html</span>",
|
bio: "<script src=\"invalid-html\"></script><span>valid html</span>",
|
||||||
|
@ -60,6 +69,7 @@ test "Represent a user account" do
|
||||||
pleroma: %{}
|
pleroma: %{}
|
||||||
},
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
|
background_image: "https://example.com/images/asuka_hospital.png",
|
||||||
confirmation_pending: false,
|
confirmation_pending: false,
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
|
@ -126,6 +136,7 @@ test "Represent a Service(bot) account" do
|
||||||
pleroma: %{}
|
pleroma: %{}
|
||||||
},
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
|
background_image: nil,
|
||||||
confirmation_pending: false,
|
confirmation_pending: false,
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
|
@ -216,6 +227,7 @@ test "represent an embedded relationship" do
|
||||||
pleroma: %{}
|
pleroma: %{}
|
||||||
},
|
},
|
||||||
pleroma: %{
|
pleroma: %{
|
||||||
|
background_image: nil,
|
||||||
confirmation_pending: false,
|
confirmation_pending: false,
|
||||||
tags: [],
|
tags: [],
|
||||||
is_admin: false,
|
is_admin: false,
|
||||||
|
|
|
@ -0,0 +1,304 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.MastodonAPIController.UpdateCredentialsTest do
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
import Pleroma.Factory
|
||||||
|
|
||||||
|
describe "updating credentials" do
|
||||||
|
test "sets user settings in a generic way", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
res_conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
"pleroma_settings_store" => %{
|
||||||
|
pleroma_fe: %{
|
||||||
|
theme: "bla"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert user = json_response(res_conn, 200)
|
||||||
|
assert user["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}}
|
||||||
|
|
||||||
|
user = Repo.get(User, user["id"])
|
||||||
|
|
||||||
|
res_conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
"pleroma_settings_store" => %{
|
||||||
|
masto_fe: %{
|
||||||
|
theme: "bla"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert user = json_response(res_conn, 200)
|
||||||
|
|
||||||
|
assert user["pleroma"]["settings_store"] ==
|
||||||
|
%{
|
||||||
|
"pleroma_fe" => %{"theme" => "bla"},
|
||||||
|
"masto_fe" => %{"theme" => "bla"}
|
||||||
|
}
|
||||||
|
|
||||||
|
user = Repo.get(User, user["id"])
|
||||||
|
|
||||||
|
res_conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
"pleroma_settings_store" => %{
|
||||||
|
masto_fe: %{
|
||||||
|
theme: "blub"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert user = json_response(res_conn, 200)
|
||||||
|
|
||||||
|
assert user["pleroma"]["settings_store"] ==
|
||||||
|
%{
|
||||||
|
"pleroma_fe" => %{"theme" => "bla"},
|
||||||
|
"masto_fe" => %{"theme" => "blub"}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's bio", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
user2 = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
"note" => "I drink #cofe with @#{user2.nickname}"
|
||||||
|
})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert user["note"] ==
|
||||||
|
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe" rel="tag">#cofe</a> with <span class="h-card"><a data-user=") <>
|
||||||
|
user2.id <>
|
||||||
|
~s(" class="u-url mention" href=") <>
|
||||||
|
user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's locking status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["locked"] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's default scope", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["source"]["privacy"] == "cofe"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's hide_followers status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["pleroma"]["hide_followers"] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's skip_thread_containment option", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
response =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
assert response["pleroma"]["skip_thread_containment"] == true
|
||||||
|
assert refresh_record(user).info.skip_thread_containment
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's hide_follows status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["pleroma"]["hide_follows"] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's hide_favorites status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["pleroma"]["hide_favorites"] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's show_role status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["source"]["pleroma"]["show_role"] == false
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's no_rich_text status", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["source"]["pleroma"]["no_rich_text"] == true
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's name", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
assert user["display_name"] == "markorepairs"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's avatar", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
new_avatar = %Plug.Upload{
|
||||||
|
content_type: "image/jpg",
|
||||||
|
path: Path.absname("test/fixtures/image.jpg"),
|
||||||
|
filename: "an_image.jpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
|
||||||
|
|
||||||
|
assert user_response = json_response(conn, 200)
|
||||||
|
assert user_response["avatar"] != User.avatar_url(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's banner", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
new_header = %Plug.Upload{
|
||||||
|
content_type: "image/jpg",
|
||||||
|
path: Path.absname("test/fixtures/image.jpg"),
|
||||||
|
filename: "an_image.jpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
|
||||||
|
|
||||||
|
assert user_response = json_response(conn, 200)
|
||||||
|
assert user_response["header"] != User.banner_url(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates the user's background", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
new_header = %Plug.Upload{
|
||||||
|
content_type: "image/jpg",
|
||||||
|
path: Path.absname("test/fixtures/image.jpg"),
|
||||||
|
filename: "an_image.jpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
"pleroma_background_image" => new_header
|
||||||
|
})
|
||||||
|
|
||||||
|
assert user_response = json_response(conn, 200)
|
||||||
|
assert user_response["pleroma"]["background_image"]
|
||||||
|
end
|
||||||
|
|
||||||
|
test "requires 'write' permission", %{conn: conn} do
|
||||||
|
token1 = insert(:oauth_token, scopes: ["read"])
|
||||||
|
token2 = insert(:oauth_token, scopes: ["write", "follow"])
|
||||||
|
|
||||||
|
for token <- [token1, token2] do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_req_header("authorization", "Bearer #{token.token}")
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{})
|
||||||
|
|
||||||
|
if token == token1 do
|
||||||
|
assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
|
||||||
|
else
|
||||||
|
assert json_response(conn, 200)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "updates profile emojos", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
note = "*sips :blank:*"
|
||||||
|
name = "I am :firefox:"
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> patch("/api/v1/accounts/update_credentials", %{
|
||||||
|
"note" => note,
|
||||||
|
"display_name" => name
|
||||||
|
})
|
||||||
|
|
||||||
|
assert json_response(conn, 200)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/accounts/#{user.id}")
|
||||||
|
|
||||||
|
assert user = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert user["note"] == note
|
||||||
|
assert user["display_name"] == name
|
||||||
|
assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -542,7 +542,10 @@ test "verify_credentials", %{conn: conn} do
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> get("/api/v1/accounts/verify_credentials")
|
|> get("/api/v1/accounts/verify_credentials")
|
||||||
|
|
||||||
assert %{"id" => id, "source" => %{"privacy" => "public"}} = json_response(conn, 200)
|
response = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert %{"id" => id, "source" => %{"privacy" => "public"}} = response
|
||||||
|
assert response["pleroma"]["chat_token"]
|
||||||
assert id == to_string(user.id)
|
assert id == to_string(user.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2134,116 +2137,6 @@ test "unimplemented follow_requests, blocks, domain blocks" do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "account search", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
|
||||||
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
|
|
||||||
|
|
||||||
results =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/accounts/search", %{"q" => "shp"})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
result_ids = for result <- results, do: result["acct"]
|
|
||||||
|
|
||||||
assert user_two.nickname in result_ids
|
|
||||||
assert user_three.nickname in result_ids
|
|
||||||
|
|
||||||
results =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/accounts/search", %{"q" => "2hu"})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
result_ids = for result <- results, do: result["acct"]
|
|
||||||
|
|
||||||
assert user_three.nickname in result_ids
|
|
||||||
end
|
|
||||||
|
|
||||||
test "search", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
|
||||||
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
|
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
|
|
||||||
|
|
||||||
{:ok, _activity} =
|
|
||||||
CommonAPI.post(user, %{
|
|
||||||
"status" => "This is about 2hu, but private",
|
|
||||||
"visibility" => "private"
|
|
||||||
})
|
|
||||||
|
|
||||||
{:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/search", %{"q" => "2hu"})
|
|
||||||
|
|
||||||
assert results = json_response(conn, 200)
|
|
||||||
|
|
||||||
[account | _] = results["accounts"]
|
|
||||||
assert account["id"] == to_string(user_three.id)
|
|
||||||
|
|
||||||
assert results["hashtags"] == []
|
|
||||||
|
|
||||||
[status] = results["statuses"]
|
|
||||||
assert status["id"] == to_string(activity.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "search fetches remote statuses", %{conn: conn} do
|
|
||||||
capture_log(fn ->
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
|
|
||||||
|
|
||||||
assert results = json_response(conn, 200)
|
|
||||||
|
|
||||||
[status] = results["statuses"]
|
|
||||||
assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "search doesn't show statuses that it shouldn't", %{conn: conn} do
|
|
||||||
{:ok, activity} =
|
|
||||||
CommonAPI.post(insert(:user), %{
|
|
||||||
"status" => "This is about 2hu, but private",
|
|
||||||
"visibility" => "private"
|
|
||||||
})
|
|
||||||
|
|
||||||
capture_log(fn ->
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
|
|
||||||
|
|
||||||
assert results = json_response(conn, 200)
|
|
||||||
|
|
||||||
[] = results["statuses"]
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "search fetches remote accounts", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
|
|
||||||
|
|
||||||
assert results = json_response(conn, 200)
|
|
||||||
[account] = results["accounts"]
|
|
||||||
assert account["acct"] == "shp@social.heldscal.la"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
|
|
||||||
|
|
||||||
assert results = json_response(conn, 200)
|
|
||||||
assert [] == results["accounts"]
|
|
||||||
end
|
|
||||||
|
|
||||||
test "returns the favorites of a user", %{conn: conn} do
|
test "returns the favorites of a user", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
@ -2484,278 +2377,6 @@ test "hides favorites for new users by default", %{conn: conn, current_user: cur
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "updating credentials" do
|
|
||||||
test "sets user settings in a generic way", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
res_conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{
|
|
||||||
"pleroma_settings_store" => %{
|
|
||||||
pleroma_fe: %{
|
|
||||||
theme: "bla"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
assert user = json_response(res_conn, 200)
|
|
||||||
assert user["pleroma"]["settings_store"] == %{"pleroma_fe" => %{"theme" => "bla"}}
|
|
||||||
|
|
||||||
user = Repo.get(User, user["id"])
|
|
||||||
|
|
||||||
res_conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{
|
|
||||||
"pleroma_settings_store" => %{
|
|
||||||
masto_fe: %{
|
|
||||||
theme: "bla"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
assert user = json_response(res_conn, 200)
|
|
||||||
|
|
||||||
assert user["pleroma"]["settings_store"] ==
|
|
||||||
%{
|
|
||||||
"pleroma_fe" => %{"theme" => "bla"},
|
|
||||||
"masto_fe" => %{"theme" => "bla"}
|
|
||||||
}
|
|
||||||
|
|
||||||
user = Repo.get(User, user["id"])
|
|
||||||
|
|
||||||
res_conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{
|
|
||||||
"pleroma_settings_store" => %{
|
|
||||||
masto_fe: %{
|
|
||||||
theme: "blub"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
assert user = json_response(res_conn, 200)
|
|
||||||
|
|
||||||
assert user["pleroma"]["settings_store"] ==
|
|
||||||
%{
|
|
||||||
"pleroma_fe" => %{"theme" => "bla"},
|
|
||||||
"masto_fe" => %{"theme" => "blub"}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's bio", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
user2 = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{
|
|
||||||
"note" => "I drink #cofe with @#{user2.nickname}"
|
|
||||||
})
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
|
|
||||||
assert user["note"] ==
|
|
||||||
~s(I drink <a class="hashtag" data-tag="cofe" href="http://localhost:4001/tag/cofe" rel="tag">#cofe</a> with <span class="h-card"><a data-user=") <>
|
|
||||||
user2.id <>
|
|
||||||
~s(" class="u-url mention" href=") <>
|
|
||||||
user2.ap_id <> ~s(">@<span>) <> user2.nickname <> ~s(</span></a></span>)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's locking status", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{locked: "true"})
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
assert user["locked"] == true
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's default scope", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{default_scope: "cofe"})
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
assert user["source"]["privacy"] == "cofe"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's hide_followers status", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{hide_followers: "true"})
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
assert user["pleroma"]["hide_followers"] == true
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's skip_thread_containment option", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
response =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{skip_thread_containment: "true"})
|
|
||||||
|> json_response(200)
|
|
||||||
|
|
||||||
assert response["pleroma"]["skip_thread_containment"] == true
|
|
||||||
assert refresh_record(user).info.skip_thread_containment
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's hide_follows status", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{hide_follows: "true"})
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
assert user["pleroma"]["hide_follows"] == true
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's hide_favorites status", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{hide_favorites: "true"})
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
assert user["pleroma"]["hide_favorites"] == true
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's show_role status", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{show_role: "false"})
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
assert user["source"]["pleroma"]["show_role"] == false
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's no_rich_text status", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{no_rich_text: "true"})
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
assert user["source"]["pleroma"]["no_rich_text"] == true
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's name", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
assert user["display_name"] == "markorepairs"
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's avatar", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
new_avatar = %Plug.Upload{
|
|
||||||
content_type: "image/jpg",
|
|
||||||
path: Path.absname("test/fixtures/image.jpg"),
|
|
||||||
filename: "an_image.jpg"
|
|
||||||
}
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
|
|
||||||
|
|
||||||
assert user_response = json_response(conn, 200)
|
|
||||||
assert user_response["avatar"] != User.avatar_url(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates the user's banner", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
new_header = %Plug.Upload{
|
|
||||||
content_type: "image/jpg",
|
|
||||||
path: Path.absname("test/fixtures/image.jpg"),
|
|
||||||
filename: "an_image.jpg"
|
|
||||||
}
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
|
|
||||||
|
|
||||||
assert user_response = json_response(conn, 200)
|
|
||||||
assert user_response["header"] != User.banner_url(user)
|
|
||||||
end
|
|
||||||
|
|
||||||
test "requires 'write' permission", %{conn: conn} do
|
|
||||||
token1 = insert(:oauth_token, scopes: ["read"])
|
|
||||||
token2 = insert(:oauth_token, scopes: ["write", "follow"])
|
|
||||||
|
|
||||||
for token <- [token1, token2] do
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> put_req_header("authorization", "Bearer #{token.token}")
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{})
|
|
||||||
|
|
||||||
if token == token1 do
|
|
||||||
assert %{"error" => "Insufficient permissions: write."} == json_response(conn, 403)
|
|
||||||
else
|
|
||||||
assert json_response(conn, 200)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "updates profile emojos", %{conn: conn} do
|
|
||||||
user = insert(:user)
|
|
||||||
|
|
||||||
note = "*sips :blank:*"
|
|
||||||
name = "I am :firefox:"
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> assign(:user, user)
|
|
||||||
|> patch("/api/v1/accounts/update_credentials", %{
|
|
||||||
"note" => note,
|
|
||||||
"display_name" => name
|
|
||||||
})
|
|
||||||
|
|
||||||
assert json_response(conn, 200)
|
|
||||||
|
|
||||||
conn =
|
|
||||||
conn
|
|
||||||
|> get("/api/v1/accounts/#{user.id}")
|
|
||||||
|
|
||||||
assert user = json_response(conn, 200)
|
|
||||||
|
|
||||||
assert user["note"] == note
|
|
||||||
assert user["display_name"] == name
|
|
||||||
assert [%{"shortcode" => "blank"}, %{"shortcode" => "firefox"}] = user["emojis"]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
test "get instance information", %{conn: conn} do
|
test "get instance information", %{conn: conn} do
|
||||||
conn = get(conn, "/api/v1/instance")
|
conn = get(conn, "/api/v1/instance")
|
||||||
assert result = json_response(conn, 200)
|
assert result = json_response(conn, 200)
|
||||||
|
|
128
test/web/mastodon_api/search_controller_test.exs
Normal file
128
test/web/mastodon_api/search_controller_test.exs
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Web.MastodonAPI.SearchControllerTest do
|
||||||
|
use Pleroma.Web.ConnCase
|
||||||
|
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
|
import Pleroma.Factory
|
||||||
|
import ExUnit.CaptureLog
|
||||||
|
import Tesla.Mock
|
||||||
|
|
||||||
|
setup do
|
||||||
|
mock(fn env -> apply(HttpRequestMock, :request, [env]) end)
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
test "account search", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
||||||
|
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
|
||||||
|
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/search", %{"q" => "shp"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
result_ids = for result <- results, do: result["acct"]
|
||||||
|
|
||||||
|
assert user_two.nickname in result_ids
|
||||||
|
assert user_three.nickname in result_ids
|
||||||
|
|
||||||
|
results =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/search", %{"q" => "2hu"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
result_ids = for result <- results, do: result["acct"]
|
||||||
|
|
||||||
|
assert user_three.nickname in result_ids
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
||||||
|
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
|
||||||
|
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
|
||||||
|
|
||||||
|
{:ok, _activity} =
|
||||||
|
CommonAPI.post(user, %{
|
||||||
|
"status" => "This is about 2hu, but private",
|
||||||
|
"visibility" => "private"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/search", %{"q" => "2hu"})
|
||||||
|
|
||||||
|
assert results = json_response(conn, 200)
|
||||||
|
|
||||||
|
[account | _] = results["accounts"]
|
||||||
|
assert account["id"] == to_string(user_three.id)
|
||||||
|
|
||||||
|
assert results["hashtags"] == []
|
||||||
|
|
||||||
|
[status] = results["statuses"]
|
||||||
|
assert status["id"] == to_string(activity.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search fetches remote statuses", %{conn: conn} do
|
||||||
|
capture_log(fn ->
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
|
||||||
|
|
||||||
|
assert results = json_response(conn, 200)
|
||||||
|
|
||||||
|
[status] = results["statuses"]
|
||||||
|
assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search doesn't show statuses that it shouldn't", %{conn: conn} do
|
||||||
|
{:ok, activity} =
|
||||||
|
CommonAPI.post(insert(:user), %{
|
||||||
|
"status" => "This is about 2hu, but private",
|
||||||
|
"visibility" => "private"
|
||||||
|
})
|
||||||
|
|
||||||
|
capture_log(fn ->
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/search", %{"q" => Object.normalize(activity).data["id"]})
|
||||||
|
|
||||||
|
assert results = json_response(conn, 200)
|
||||||
|
|
||||||
|
[] = results["statuses"]
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search fetches remote accounts", %{conn: conn} do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
|
||||||
|
|
||||||
|
assert results = json_response(conn, 200)
|
||||||
|
[account] = results["accounts"]
|
||||||
|
assert account["acct"] == "shp@social.heldscal.la"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "search doesn't fetch remote accounts if resolve is false", %{conn: conn} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "false"})
|
||||||
|
|
||||||
|
assert results = json_response(conn, 200)
|
||||||
|
assert [] == results["accounts"]
|
||||||
|
end
|
||||||
|
end
|
|
@ -408,7 +408,11 @@ test "renders authentication page if user is already authenticated but `force_lo
|
||||||
assert html_response(conn, 200) =~ ~s(type="submit")
|
assert html_response(conn, 200) =~ ~s(type="submit")
|
||||||
end
|
end
|
||||||
|
|
||||||
test "redirects to app if user is already authenticated", %{app: app, conn: conn} do
|
test "with existing authentication and non-OOB `redirect_uri`, redirects to app with `token` and `state` params",
|
||||||
|
%{
|
||||||
|
app: app,
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
token = insert(:oauth_token, app_id: app.id)
|
token = insert(:oauth_token, app_id: app.id)
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
|
@ -420,11 +424,36 @@ test "redirects to app if user is already authenticated", %{app: app, conn: conn
|
||||||
"response_type" => "code",
|
"response_type" => "code",
|
||||||
"client_id" => app.client_id,
|
"client_id" => app.client_id,
|
||||||
"redirect_uri" => app.redirect_uris,
|
"redirect_uri" => app.redirect_uris,
|
||||||
|
"state" => "specific_client_state",
|
||||||
"scope" => "read"
|
"scope" => "read"
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
assert redirected_to(conn) == "https://redirect.url"
|
assert URI.decode(redirected_to(conn)) ==
|
||||||
|
"https://redirect.url?access_token=#{token.token}&state=specific_client_state"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with existing authentication and OOB `redirect_uri`, redirects to app with `token` and `state` params",
|
||||||
|
%{
|
||||||
|
app: app,
|
||||||
|
conn: conn
|
||||||
|
} do
|
||||||
|
token = insert(:oauth_token, app_id: app.id)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> put_session(:oauth_token, token.token)
|
||||||
|
|> get(
|
||||||
|
"/oauth/authorize",
|
||||||
|
%{
|
||||||
|
"response_type" => "code",
|
||||||
|
"client_id" => app.client_id,
|
||||||
|
"redirect_uri" => "urn:ietf:wg:oauth:2.0:oob",
|
||||||
|
"scope" => "read"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
assert html_response(conn, 200) =~ "Authorization exists"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,15 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
|
||||||
} ->
|
} ->
|
||||||
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
|
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/ogp.html")}
|
||||||
|
|
||||||
|
%{
|
||||||
|
method: :get,
|
||||||
|
url: "http://example.com/ogp-missing-title"
|
||||||
|
} ->
|
||||||
|
%Tesla.Env{
|
||||||
|
status: 200,
|
||||||
|
body: File.read!("test/fixtures/rich_media/ogp-missing-title.html")
|
||||||
|
}
|
||||||
|
|
||||||
%{
|
%{
|
||||||
method: :get,
|
method: :get,
|
||||||
url: "http://example.com/twitter-card"
|
url: "http://example.com/twitter-card"
|
||||||
|
@ -51,6 +60,19 @@ test "parses ogp" do
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "falls back to <title> when ogp:title is missing" do
|
||||||
|
assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/ogp-missing-title") ==
|
||||||
|
{:ok,
|
||||||
|
%{
|
||||||
|
image: "http://ia.media-imdb.com/images/rock.jpg",
|
||||||
|
title: "The Rock (1996)",
|
||||||
|
description:
|
||||||
|
"Directed by Michael Bay. With Sean Connery, Nicolas Cage, Ed Harris, John Spencer.",
|
||||||
|
type: "video.movie",
|
||||||
|
url: "http://www.imdb.com/title/tt0117500/"
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
test "parses twitter card" do
|
test "parses twitter card" do
|
||||||
assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/twitter-card") ==
|
assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/twitter-card") ==
|
||||||
{:ok,
|
{:ok,
|
||||||
|
|
|
@ -21,6 +21,52 @@ defmodule Pleroma.Web.StreamerTest do
|
||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "user streams" do
|
||||||
|
setup do
|
||||||
|
GenServer.start(Streamer, %{}, name: Streamer)
|
||||||
|
|
||||||
|
on_exit(fn ->
|
||||||
|
if pid = Process.whereis(Streamer) do
|
||||||
|
Process.exit(pid, :kill)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
user = insert(:user)
|
||||||
|
notify = insert(:notification, user: user, activity: build(:note_activity))
|
||||||
|
{:ok, %{user: user, notify: notify}}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it sends notify to in the 'user' stream", %{user: user, notify: notify} do
|
||||||
|
task =
|
||||||
|
Task.async(fn ->
|
||||||
|
assert_receive {:text, _}, 4_000
|
||||||
|
end)
|
||||||
|
|
||||||
|
Streamer.add_socket(
|
||||||
|
"user",
|
||||||
|
%{transport_pid: task.pid, assigns: %{user: user}}
|
||||||
|
)
|
||||||
|
|
||||||
|
Streamer.stream("user", notify)
|
||||||
|
Task.await(task)
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it sends notify to in the 'user:notification' stream", %{user: user, notify: notify} do
|
||||||
|
task =
|
||||||
|
Task.async(fn ->
|
||||||
|
assert_receive {:text, _}, 4_000
|
||||||
|
end)
|
||||||
|
|
||||||
|
Streamer.add_socket(
|
||||||
|
"user:notification",
|
||||||
|
%{transport_pid: task.pid, assigns: %{user: user}}
|
||||||
|
)
|
||||||
|
|
||||||
|
Streamer.stream("user:notification", notify)
|
||||||
|
Task.await(task)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
test "it sends to public" do
|
test "it sends to public" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user)
|
other_user = insert(:user)
|
||||||
|
|
Loading…
Reference in a new issue