diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 30f90d044..3b6877039 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -196,6 +196,7 @@ amd64: variables: &release-variables MIX_ENV: prod before_script: &before-release + - apt install cmake -y - echo "import Mix.Config" > config/prod.secret.exs - mix local.hex --force - mix local.rebar --force @@ -214,7 +215,7 @@ amd64-musl: cache: *release-cache variables: *release-variables before_script: &before-release-musl - - apk add git gcc g++ musl-dev make + - apk add git gcc g++ musl-dev make cmake - echo "import Mix.Config" > config/prod.secret.exs - mix local.hex --force - mix local.rebar --force diff --git a/CHANGELOG.md b/CHANGELOG.md index a8e80eb3c..d0ae2981c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Configuration: `:media_proxy, whitelist` format changed to host with scheme (e.g. `http://example.com` instead of `example.com`). Domain format is deprecated. - **Breaking:** Configuration: `:instance, welcome_user_nickname` moved to `:welcome, :direct_message, :sender_nickname`, `:instance, :welcome_message` moved to `:welcome, :direct_message, :message`. Old config namespace is deprecated. - **Breaking:** LDAP: Fallback to local database authentication has been removed for security reasons and lack of a mechanism to ensure the passwords are synchronized when LDAP passwords are updated. +- **Breaking** Changed defaults for `:restrict_unauthenticated` so that when `:instance, :public` is set to `false` then all `:restrict_unauthenticated` items be effectively set to `true`. If you'd like to allow unauthenticated access to specific API endpoints on a private instance, please explicitly set `:restrict_unauthenticated` to non-default value in `config/prod.secret.exs`.
API Changes @@ -105,6 +106,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fix edge case where MediaProxy truncates media, usually caused when Caddy is serving content for the other Federated instance. - Emoji Packs could not be listed when instance was set to `public: false` - Fix whole_word always returning false on filter get requests +- Migrations not working on OTP releases if the database was connected over ssl ## [Unreleased (patch)] diff --git a/Dockerfile b/Dockerfile index 0f4fcd0bb..aa50e27ec 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ COPY . . ENV MIX_ENV=prod -RUN apk add git gcc g++ musl-dev make &&\ +RUN apk add git gcc g++ musl-dev make cmake &&\ echo "import Mix.Config" > config/prod.secret.exs &&\ mix local.hex --force &&\ mix local.rebar --force &&\ diff --git a/config/config.exs b/config/config.exs index dfc4e1629..ab4508ccf 100644 --- a/config/config.exs +++ b/config/config.exs @@ -737,10 +737,12 @@ timeout: 300_000 ] +private_instance? = :if_instance_is_private + config :pleroma, :restrict_unauthenticated, - timelines: %{local: false, federated: false}, - profiles: %{local: false, remote: false}, - activities: %{local: false, remote: false} + timelines: %{local: private_instance?, federated: private_instance?}, + profiles: %{local: private_instance?, remote: private_instance?}, + activities: %{local: private_instance?, remote: private_instance?} config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false diff --git a/config/description.exs b/config/description.exs index d823812fb..e27abf40f 100644 --- a/config/description.exs +++ b/config/description.exs @@ -194,7 +194,7 @@ type: [:string, {:list, :string}, {:list, :tuple}], description: "List of actions for the mogrify command. It's possible to add self-written settings as string. " <> - "For example `[\"auto-orient\", \"strip\", {\"resize\", \"3840x1080>\"}]` string will be parsed into list of the settings.", + "For example `auto-orient, strip, {\"resize\", \"3840x1080>\"}` value will be parsed into valid list of the settings.", suggestions: [ "strip", "auto-orient", @@ -951,7 +951,7 @@ }, %{ key: :instance_thumbnail, - type: :string, + type: {:string, :image}, description: "The instance thumbnail can be any image that represents your instance and is used by some apps or services when they display information about your instance.", suggestions: ["/instance/thumbnail.jpeg"] @@ -964,25 +964,25 @@ ] }, %{ - group: :welcome, + group: :pleroma, + key: :welcome, type: :group, description: "Welcome messages settings", children: [ %{ - group: :direct_message, - type: :group, + key: :direct_message, + type: :keyword, descpiption: "Direct message settings", children: [ %{ key: :enabled, type: :boolean, - description: "Enables sends direct message for new user after registration" + description: "Enables sending a direct message to newly registered users" }, %{ key: :message, type: :string, - description: - "A message that will be sent to a newly registered users as a direct message", + description: "A message that will be sent to newly registered users", suggestions: [ "Hi, @username! Welcome on board!" ] @@ -990,7 +990,7 @@ %{ key: :sender_nickname, type: :string, - description: "The nickname of the local user that sends the welcome message", + description: "The nickname of the local user that sends a welcome message", suggestions: [ "lain" ] @@ -998,20 +998,20 @@ ] }, %{ - group: :chat_message, - type: :group, + key: :chat_message, + type: :keyword, descpiption: "Chat message settings", children: [ %{ key: :enabled, type: :boolean, - description: "Enables sends chat message for new user after registration" + description: "Enables sending a chat message to newly registered users" }, %{ key: :message, type: :string, description: - "A message that will be sent to a newly registered users as a chat message", + "A message that will be sent to newly registered users as a chat message", suggestions: [ "Hello, welcome on board!" ] @@ -1019,7 +1019,7 @@ %{ key: :sender_nickname, type: :string, - description: "The nickname of the local user that sends the welcome message", + description: "The nickname of the local user that sends a welcome chat message", suggestions: [ "lain" ] @@ -1027,20 +1027,20 @@ ] }, %{ - group: :email, - type: :group, + key: :email, + type: :keyword, descpiption: "Email message settings", children: [ %{ key: :enabled, type: :boolean, - description: "Enables sends direct message for new user after registration" + description: "Enables sending an email to newly registered users" }, %{ key: :sender, type: [:string, :tuple], description: - "The email address or tuple with `{nickname, email}` that will use as sender to the welcome email.", + "Email address and/or nickname that will be used to send the welcome email.", suggestions: [ {"Pleroma App", "welcome@pleroma.app"} ] @@ -1049,21 +1049,21 @@ key: :subject, type: :string, description: - "The subject of welcome email. Can be use EEX template with `user` and `instance_name` variables.", + "Subject of the welcome email. EEX template with user and instance_name variables can be used.", suggestions: ["Welcome to <%= instance_name%>"] }, %{ key: :html, type: :string, description: - "The html content of welcome email. Can be use EEX template with `user` and `instance_name` variables.", + "HTML content of the welcome email. EEX template with user and instance_name variables can be used.", suggestions: ["

Hello <%= user.name%>. Welcome to <%= instance_name%>

"] }, %{ key: :text, type: :string, description: - "The text content of welcome email. Can be use EEX template with `user` and `instance_name` variables.", + "Text content of the welcome email. EEX template with user and instance_name variables can be used.", suggestions: ["Hello <%= user.name%>. \n Welcome to <%= instance_name%>\n"] } ] @@ -1236,7 +1236,7 @@ }, %{ key: :background, - type: :string, + type: {:string, :image}, description: "URL of the background, unless viewing a user profile with a background that is set", suggestions: ["/images/city.jpg"] @@ -1293,7 +1293,7 @@ }, %{ key: :logo, - type: :string, + type: {:string, :image}, description: "URL of the logo, defaults to Pleroma's logo", suggestions: ["/static/logo.png"] }, @@ -1325,7 +1325,7 @@ %{ key: :nsfwCensorImage, label: "NSFW Censor Image", - type: :string, + type: {:string, :image}, description: "URL of the image to use for hiding NSFW media attachments in the timeline", suggestions: ["/static/img/nsfw.74818f9.png"] @@ -1451,7 +1451,7 @@ }, %{ key: :default_user_avatar, - type: :string, + type: {:string, :image}, description: "URL of the default user avatar", suggestions: ["/images/avi.png"] } @@ -1810,12 +1810,12 @@ %{ key: :enabled, type: :boolean, - description: "Enables invalidate media cache" + description: "Enables media cache object invalidation." }, %{ key: :provider, type: :module, - description: "Module which will be used to cache purge.", + description: "Module which will be used to purge objects from the cache.", suggestions: [ Pleroma.Web.MediaProxy.Invalidation.Script, Pleroma.Web.MediaProxy.Invalidation.Http @@ -2642,7 +2642,7 @@ children: [ %{ key: :logo, - type: :string, + type: {:string, :image}, description: "A path to a custom logo. Set it to `nil` to use the default Pleroma logo.", suggestions: ["some/path/logo.png"] }, @@ -3299,13 +3299,13 @@ group: :pleroma, key: :connections_pool, type: :group, - description: "Advanced settings for `gun` connections pool", + description: "Advanced settings for `Gun` connections pool", children: [ %{ key: :connection_acquisition_wait, type: :integer, description: - "Timeout to acquire a connection from pool.The total max time is this value multiplied by the number of retries. Default: 250ms.", + "Timeout to acquire a connection from pool. The total max time is this value multiplied by the number of retries. Default: 250ms.", suggestions: [250] }, %{ @@ -3340,7 +3340,7 @@ group: :pleroma, key: :pools, type: :group, - description: "Advanced settings for `gun` workers pools", + description: "Advanced settings for `Gun` workers pools", children: Enum.map([:federation, :media, :upload, :default], fn pool_name -> %{ @@ -3369,7 +3369,7 @@ group: :pleroma, key: :hackney_pools, type: :group, - description: "Advanced settings for `hackney` connections pools", + description: "Advanced settings for `Hackney` connections pools", children: [ %{ key: :federation, @@ -3433,6 +3433,7 @@ %{ group: :pleroma, key: :restrict_unauthenticated, + label: "Restrict Unauthenticated", type: :group, description: "Disallow viewing timelines, user profiles and statuses for unauthenticated users.", @@ -3555,13 +3556,17 @@ children: [ %{ key: "name", + label: "Name", type: :string, - description: "Name of the installed primary frontend" + description: + "Name of the installed primary frontend. Valid config must include both `Name` and `Reference` values." }, %{ key: "ref", + label: "Reference", type: :string, - description: "reference of the installed primary frontend to be used" + description: + "Reference of the installed primary frontend to be used. Valid config must include both `Name` and `Reference` values." } ] } diff --git a/docs/API/admin_api.md b/docs/API/admin_api.md index 4b143e4ee..05e63b528 100644 --- a/docs/API/admin_api.md +++ b/docs/API/admin_api.md @@ -1266,11 +1266,14 @@ Loads json generated from `config/descriptions.exs`. - Params: - *optional* `page`: **integer** page number - *optional* `page_size`: **integer** number of log entries per page (default is `50`) +- *optional* `query`: **string** search term - Response: ``` json { + "page_size": integer, + "count": integer, "urls": [ "http://example.com/media/a688346.jpg", "http://example.com/media/fb1f4d.jpg" @@ -1290,12 +1293,7 @@ Loads json generated from `config/descriptions.exs`. - Response: ``` json -{ - "urls": [ - "http://example.com/media/a688346.jpg", - "http://example.com/media/fb1f4d.jpg" - ] -} +{ } ``` @@ -1311,11 +1309,6 @@ Loads json generated from `config/descriptions.exs`. - Response: ``` json -{ - "urls": [ - "http://example.com/media/a688346.jpg", - "http://example.com/media/fb1f4d.jpg" - ] -} +{ } ``` diff --git a/docs/administration/CLI_tasks/config.md b/docs/administration/CLI_tasks/config.md index cc32bf859..0923004b5 100644 --- a/docs/administration/CLI_tasks/config.md +++ b/docs/administration/CLI_tasks/config.md @@ -11,14 +11,17 @@ config :pleroma, configurable_from_database: true ``` -```sh tab="OTP" - ./bin/pleroma_ctl config migrate_to_db -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.config migrate_to_db -``` + ```sh + ./bin/pleroma_ctl config migrate_to_db + ``` +=== "From Source" + + ```sh + mix pleroma.config migrate_to_db + ``` ## Transfer config from DB to `config/env.exported_from_db.secret.exs` @@ -31,10 +34,12 @@ mix pleroma.config migrate_to_db To delete transfered settings from database optional flag `-d` can be used. `` is `prod` by default. -```sh tab="OTP" - ./bin/pleroma_ctl config migrate_from_db [--env=] [-d] -``` +=== "OTP" + ```sh + ./bin/pleroma_ctl config migrate_from_db [--env=] [-d] + ``` -```sh tab="From Source" -mix pleroma.config migrate_from_db [--env=] [-d] -``` +=== "From Source" + ```sh + mix pleroma.config migrate_from_db [--env=] [-d] + ``` diff --git a/docs/administration/CLI_tasks/database.md b/docs/administration/CLI_tasks/database.md index 64dd66c0c..6dca83167 100644 --- a/docs/administration/CLI_tasks/database.md +++ b/docs/administration/CLI_tasks/database.md @@ -9,13 +9,18 @@ Replaces embedded objects with references to them in the `objects` table. Only needs to be ran once if the instance was created before Pleroma 1.0.5. The reason why this is not a migration is because it could significantly increase the database size after being ran, however after this `VACUUM FULL` will be able to reclaim about 20% (really depends on what is in the database, your mileage may vary) of the db size before the migration. -```sh tab="OTP" -./bin/pleroma_ctl database remove_embedded_objects [option ...] -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl database remove_embedded_objects [option ...] + ``` + +=== "From Source" + + ```sh + mix pleroma.database remove_embedded_objects [option ...] + ``` -```sh tab="From Source" -mix pleroma.database remove_embedded_objects [option ...] -``` ### Options - `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references @@ -27,13 +32,17 @@ This will prune remote posts older than 90 days (configurable with [`config :ple !!! danger The disk space will only be reclaimed after `VACUUM FULL`. You may run out of disk space during the execution of the task or vacuuming if you don't have about 1/3rds of the database size free. -```sh tab="OTP" -./bin/pleroma_ctl database prune_objects [option ...] -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.database prune_objects [option ...] -``` + ```sh + ./bin/pleroma_ctl database prune_objects [option ...] + ``` + +=== "From Source" + + ```sh + mix pleroma.database prune_objects [option ...] + ``` ### Options - `--vacuum` - run `VACUUM FULL` after the objects are pruned @@ -42,33 +51,45 @@ mix pleroma.database prune_objects [option ...] Can be safely re-run -```sh tab="OTP" -./bin/pleroma_ctl database bump_all_conversations -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.database bump_all_conversations -``` + ```sh + ./bin/pleroma_ctl database bump_all_conversations + ``` + +=== "From Source" + + ```sh + mix pleroma.database bump_all_conversations + ``` ## Remove duplicated items from following and update followers count for all users -```sh tab="OTP" -./bin/pleroma_ctl database update_users_following_followers_counts -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.database update_users_following_followers_counts -``` + ```sh + ./bin/pleroma_ctl database update_users_following_followers_counts + ``` + +=== "From Source" + + ```sh + mix pleroma.database update_users_following_followers_counts + ``` ## Fix the pre-existing "likes" collections for all objects -```sh tab="OTP" -./bin/pleroma_ctl database fix_likes_collections -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.database fix_likes_collections -``` + ```sh + ./bin/pleroma_ctl database fix_likes_collections + ``` + +=== "From Source" + + ```sh + mix pleroma.database fix_likes_collections + ``` ## Vacuum the database @@ -76,13 +97,17 @@ mix pleroma.database fix_likes_collections Running an `analyze` vacuum job can improve performance by updating statistics used by the query planner. **It is safe to cancel this.** -```sh tab="OTP" -./bin/pleroma_ctl database vacuum analyze -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.database vacuum analyze -``` + ```sh + ./bin/pleroma_ctl database vacuum analyze + ``` + +=== "From Source" + + ```sh + mix pleroma.database vacuum analyze + ``` ### Full @@ -91,20 +116,28 @@ and more compact files with an optimized layout. This process will take a long t it builds the files side-by-side the existing database files. It can make your database faster and use less disk space, but should only be run if necessary. **It is safe to cancel this.** -```sh tab="OTP" -./bin/pleroma_ctl database vacuum full -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.database vacuum full -``` + ```sh + ./bin/pleroma_ctl database vacuum full + ``` + +=== "From Source" + + ```sh + mix pleroma.database vacuum full + ``` ## Add expiration to all local statuses -```sh tab="OTP" -./bin/pleroma_ctl database ensure_expiration -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.database ensure_expiration -``` + ```sh + ./bin/pleroma_ctl database ensure_expiration + ``` + +=== "From Source" + + ```sh + mix pleroma.database ensure_expiration + ``` diff --git a/docs/administration/CLI_tasks/digest.md b/docs/administration/CLI_tasks/digest.md index 2eb31379e..a590581e3 100644 --- a/docs/administration/CLI_tasks/digest.md +++ b/docs/administration/CLI_tasks/digest.md @@ -4,22 +4,30 @@ ## Send digest email since given date (user registration date by default) ignoring user activity status. -```sh tab="OTP" - ./bin/pleroma_ctl digest test [since_date] -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.digest test [since_date] -``` + ```sh + ./bin/pleroma_ctl digest test [since_date] + ``` + +=== "From Source" + + ```sh + mix pleroma.digest test [since_date] + ``` Example: -```sh tab="OTP" -./bin/pleroma_ctl digest test donaldtheduck 2019-05-20 -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.digest test donaldtheduck 2019-05-20 -``` + ```sh + ./bin/pleroma_ctl digest test donaldtheduck 2019-05-20 + ``` + +=== "From Source" + + ```sh + mix pleroma.digest test donaldtheduck 2019-05-20 + ``` diff --git a/docs/administration/CLI_tasks/email.md b/docs/administration/CLI_tasks/email.md index 7b7a8457a..00d2e74f8 100644 --- a/docs/administration/CLI_tasks/email.md +++ b/docs/administration/CLI_tasks/email.md @@ -4,21 +4,29 @@ ## Send test email (instance email by default) -```sh tab="OTP" - ./bin/pleroma_ctl email test [--to ] -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.email test [--to ] -``` + ```sh + ./bin/pleroma_ctl email test [--to ] + ``` + +=== "From Source" + + ```sh + mix pleroma.email test [--to ] + ``` Example: -```sh tab="OTP" -./bin/pleroma_ctl email test --to root@example.org -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.email test --to root@example.org -``` + ```sh + ./bin/pleroma_ctl email test --to root@example.org + ``` + +=== "From Source" + + ```sh + mix pleroma.email test --to root@example.org + ``` diff --git a/docs/administration/CLI_tasks/emoji.md b/docs/administration/CLI_tasks/emoji.md index ddcb7e62c..e3d1b210e 100644 --- a/docs/administration/CLI_tasks/emoji.md +++ b/docs/administration/CLI_tasks/emoji.md @@ -4,13 +4,15 @@ ## Lists emoji packs and metadata specified in the manifest -```sh tab="OTP" -./bin/pleroma_ctl emoji ls-packs [option ...] -``` +=== "OTP" + ```sh + ./bin/pleroma_ctl emoji ls-packs [option ...] + ``` -```sh tab="From Source" -mix pleroma.emoji ls-packs [option ...] -``` +=== "From Source" + ```sh + mix pleroma.emoji ls-packs [option ...] + ``` ### Options @@ -18,26 +20,30 @@ mix pleroma.emoji ls-packs [option ...] ## Fetch, verify and install the specified packs from the manifest into `STATIC-DIR/emoji/PACK-NAME` -```sh tab="OTP" -./bin/pleroma_ctl emoji get-packs [option ...] -``` +=== "OTP" + ```sh + ./bin/pleroma_ctl emoji get-packs [option ...] + ``` -```sh tab="From Source" -mix pleroma.emoji get-packs [option ...] -``` +=== "From Source" + ```sh + mix pleroma.emoji get-packs [option ...] + ``` ### Options - `-m, --manifest PATH/URL` - same as [`ls-packs`](#ls-packs) ## Create a new manifest entry and a file list from the specified remote pack file -```sh tab="OTP" -./bin/pleroma_ctl emoji gen-pack PACK-URL -``` +=== "OTP" + ```sh + ./bin/pleroma_ctl emoji gen-pack PACK-URL + ``` -```sh tab="From Source" -mix pleroma.emoji gen-pack PACK-URL -``` +=== "From Source" + ```sh + mix pleroma.emoji gen-pack PACK-URL + ``` Currently, only .zip archives are recognized as remote pack files and packs are therefore assumed to be zip archives. This command is intended to run interactively and will first ask you some basic questions about the pack, then download the remote file and generate an SHA256 checksum for it, then generate an emoji file list for you. @@ -47,8 +53,9 @@ Currently, only .zip archives are recognized as remote pack files and packs are ## Reload emoji packs -```sh tab="OTP" -./bin/pleroma_ctl emoji reload -``` +=== "OTP" + ```sh + ./bin/pleroma_ctl emoji reload + ``` This command only works with OTP releases. diff --git a/docs/administration/CLI_tasks/instance.md b/docs/administration/CLI_tasks/instance.md index 52e264bb1..989ecc55d 100644 --- a/docs/administration/CLI_tasks/instance.md +++ b/docs/administration/CLI_tasks/instance.md @@ -3,13 +3,17 @@ {! backend/administration/CLI_tasks/general_cli_task_info.include !} ## Generate a new configuration file -```sh tab="OTP" - ./bin/pleroma_ctl instance gen [option ...] -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.instance gen [option ...] -``` + ```sh + ./bin/pleroma_ctl instance gen [option ...] + ``` + +=== "From Source" + + ```sh + mix pleroma.instance gen [option ...] + ``` If any of the options are left unspecified, you will be prompted interactively. diff --git a/docs/administration/CLI_tasks/oauth_app.md b/docs/administration/CLI_tasks/oauth_app.md index 4d6bfc25a..f0568491e 100644 --- a/docs/administration/CLI_tasks/oauth_app.md +++ b/docs/administration/CLI_tasks/oauth_app.md @@ -7,10 +7,14 @@ Optional params: * `-s SCOPES` - scopes for app, e.g. `read,write,follow,push`. -```sh tab="OTP" - ./bin/pleroma_ctl app create -n APP_NAME -r REDIRECT_URI -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.app create -n APP_NAME -r REDIRECT_URI -``` \ No newline at end of file + ```sh + ./bin/pleroma_ctl app create -n APP_NAME -r REDIRECT_URI + ``` + +=== "From Source" + + ```sh + mix pleroma.app create -n APP_NAME -r REDIRECT_URI + ``` \ No newline at end of file diff --git a/docs/administration/CLI_tasks/relay.md b/docs/administration/CLI_tasks/relay.md index c4f078f4d..bdd7e8be4 100644 --- a/docs/administration/CLI_tasks/relay.md +++ b/docs/administration/CLI_tasks/relay.md @@ -4,30 +4,42 @@ ## Follow a relay -```sh tab="OTP" -./bin/pleroma_ctl relay follow -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.relay follow -``` + ```sh + ./bin/pleroma_ctl relay follow + ``` + +=== "From Source" + + ```sh + mix pleroma.relay follow + ``` ## Unfollow a remote relay -```sh tab="OTP" -./bin/pleroma_ctl relay unfollow -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.relay unfollow -``` + ```sh + ./bin/pleroma_ctl relay unfollow + ``` + +=== "From Source" + + ```sh + mix pleroma.relay unfollow + ``` ## List relay subscriptions -```sh tab="OTP" -./bin/pleroma_ctl relay list -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.relay list -``` + ```sh + ./bin/pleroma_ctl relay list + ``` + +=== "From Source" + + ```sh + mix pleroma.relay list + ``` diff --git a/docs/administration/CLI_tasks/robots_txt.md b/docs/administration/CLI_tasks/robots_txt.md new file mode 100644 index 000000000..7eeedf571 --- /dev/null +++ b/docs/administration/CLI_tasks/robots_txt.md @@ -0,0 +1,21 @@ +# Managing robots.txt + +{! backend/administration/CLI_tasks/general_cli_task_info.include !} + +## Generate a new robots.txt file and add it to the static directory + +The `robots.txt` that ships by default is permissive. It allows well-behaved search engines to index all of your instance's URIs. + +If you want to generate a restrictive `robots.txt`, you can run the following mix task. The generated `robots.txt` will be written in your instance [static directory](../../../configuration/static_dir/). + +=== "OTP" + + ```sh + ./bin/pleroma_ctl robots_txt disallow_all + ``` + +=== "From Source" + + ```sh + mix pleroma.robots_txt disallow_all + ``` diff --git a/docs/administration/CLI_tasks/uploads.md b/docs/administration/CLI_tasks/uploads.md index 6a15d22f6..8585ec76b 100644 --- a/docs/administration/CLI_tasks/uploads.md +++ b/docs/administration/CLI_tasks/uploads.md @@ -3,13 +3,17 @@ {! backend/administration/CLI_tasks/general_cli_task_info.include !} ## Migrate uploads from local to remote storage -```sh tab="OTP" - ./bin/pleroma_ctl uploads migrate_local [option ...] -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.uploads migrate_local [option ...] -``` + ```sh + ./bin/pleroma_ctl uploads migrate_local [option ...] + ``` + +=== "From Source" + + ```sh + mix pleroma.uploads migrate_local [option ...] + ``` ### Options - `--delete` - delete local uploads after migrating them to the target uploader diff --git a/docs/administration/CLI_tasks/user.md b/docs/administration/CLI_tasks/user.md index 3b4c421a7..3e7f028ba 100644 --- a/docs/administration/CLI_tasks/user.md +++ b/docs/administration/CLI_tasks/user.md @@ -4,13 +4,17 @@ ## Create a user -```sh tab="OTP" -./bin/pleroma_ctl user new [option ...] -``` +=== "OTP" -```sh tab="From Source" -mix pleroma.user new [option ...] -``` + ```sh + ./bin/pleroma_ctl user new [option ...] + ``` + +=== "From Source" + + ```sh + mix pleroma.user new [option ...] + ``` ### Options @@ -22,23 +26,33 @@ mix pleroma.user new [option ...] - `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions ## List local users -```sh tab="OTP" - ./bin/pleroma_ctl user list -``` -```sh tab="From Source" -mix pleroma.user list -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user list + ``` + +=== "From Source" + + ```sh + mix pleroma.user list + ``` ## Generate an invite link -```sh tab="OTP" - ./bin/pleroma_ctl user invite [option ...] -``` -```sh tab="From Source" -mix pleroma.user invite [option ...] -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user invite [option ...] + ``` + +=== "From Source" + + ```sh + mix pleroma.user invite [option ...] + ``` ### Options @@ -46,113 +60,168 @@ mix pleroma.user invite [option ...] - `--max-use NUMBER` - maximum numbers of token uses ## List generated invites -```sh tab="OTP" - ./bin/pleroma_ctl user invites -``` -```sh tab="From Source" -mix pleroma.user invites -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user invites + ``` + +=== "From Source" + + ```sh + mix pleroma.user invites + ``` ## Revoke invite -```sh tab="OTP" - ./bin/pleroma_ctl user revoke_invite -``` -```sh tab="From Source" -mix pleroma.user revoke_invite -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user revoke_invite + ``` + +=== "From Source" + + ```sh + mix pleroma.user revoke_invite + ``` ## Delete a user -```sh tab="OTP" - ./bin/pleroma_ctl user rm -``` -```sh tab="From Source" -mix pleroma.user rm -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user rm + ``` + +=== "From Source" + + ```sh + mix pleroma.user rm + ``` ## Delete user's posts and interactions -```sh tab="OTP" - ./bin/pleroma_ctl user delete_activities -``` -```sh tab="From Source" -mix pleroma.user delete_activities -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user delete_activities + ``` + +=== "From Source" + + ```sh + mix pleroma.user delete_activities + ``` ## Sign user out from all applications (delete user's OAuth tokens and authorizations) -```sh tab="OTP" - ./bin/pleroma_ctl user sign_out -``` -```sh tab="From Source" -mix pleroma.user sign_out -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user sign_out + ``` + +=== "From Source" + + ```sh + mix pleroma.user sign_out + ``` ## Deactivate or activate a user -```sh tab="OTP" - ./bin/pleroma_ctl user toggle_activated -``` -```sh tab="From Source" -mix pleroma.user toggle_activated -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user toggle_activated + ``` + +=== "From Source" + + ```sh + mix pleroma.user toggle_activated + ``` ## Deactivate a user and unsubscribes local users from the user -```sh tab="OTP" - ./bin/pleroma_ctl user deactivate NICKNAME -``` -```sh tab="From Source" -mix pleroma.user deactivate NICKNAME -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user deactivate NICKNAME + ``` + +=== "From Source" + + ```sh + mix pleroma.user deactivate NICKNAME + ``` ## Deactivate all accounts from an instance and unsubscribe local users on it -```sh tab="OTP" - ./bin/pleroma_ctl user deactivate_all_from_instance -``` -```sh tab="From Source" -mix pleroma.user deactivate_all_from_instance -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user deactivate_all_from_instance + ``` + +=== "From Source" + + ```sh + mix pleroma.user deactivate_all_from_instance + ``` ## Create a password reset link for user -```sh tab="OTP" - ./bin/pleroma_ctl user reset_password -``` -```sh tab="From Source" -mix pleroma.user reset_password -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user reset_password + ``` + +=== "From Source" + + ```sh + mix pleroma.user reset_password + ``` ## Disable Multi Factor Authentication (MFA/2FA) for a user -```sh tab="OTP" - ./bin/pleroma_ctl user reset_mfa -``` -```sh tab="From Source" -mix pleroma.user reset_mfa -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user reset_mfa + ``` + +=== "From Source" + + ```sh + mix pleroma.user reset_mfa + ``` ## Set the value of the given user's settings -```sh tab="OTP" - ./bin/pleroma_ctl user set [option ...] -``` -```sh tab="From Source" -mix pleroma.user set [option ...] -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user set [option ...] + ``` + +=== "From Source" + + ```sh + mix pleroma.user set [option ...] + ``` ### Options - `--locked`/`--no-locked` - whether the user should be locked @@ -160,30 +229,45 @@ mix pleroma.user set [option ...] - `--admin`/`--no-admin` - whether the user should be an admin ## Add tags to a user -```sh tab="OTP" - ./bin/pleroma_ctl user tag -``` -```sh tab="From Source" -mix pleroma.user tag -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user tag + ``` + +=== "From Source" + + ```sh + mix pleroma.user tag + ``` ## Delete tags from a user -```sh tab="OTP" - ./bin/pleroma_ctl user untag -``` -```sh tab="From Source" -mix pleroma.user untag -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user untag + ``` + +=== "From Source" + + ```sh + mix pleroma.user untag + ``` ## Toggle confirmation status of the user -```sh tab="OTP" - ./bin/pleroma_ctl user toggle_confirmed -``` -```sh tab="From Source" -mix pleroma.user toggle_confirmed -``` +=== "OTP" + + ```sh + ./bin/pleroma_ctl user toggle_confirmed + ``` + +=== "From Source" + + ```sh + mix pleroma.user toggle_confirmed + ``` diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index ca587af8e..e68b6c6dc 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -38,8 +38,8 @@ To add configuration to your config file, you can copy it from the base config. * `federation_incoming_replies_max_depth`: Max. depth of reply-to activities fetching on incoming federation, to prevent out-of-memory situations while fetching very long threads. If set to `nil`, threads of any depth will be fetched. Lower this value if you experience out-of-memory crashes. * `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it. * `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance. -* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. See also: `restrict_unauthenticated`. -* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send. +* `public`: Makes the client API in authenticated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network. Note that there is a dependent setting restricting or allowing unauthenticated access to specific resources, see `restrict_unauthenticated` for more details. +* `quarantined_instances`: List of ActivityPub instances where private (DMs, followers-only) activities will not be send. * `managed_config`: Whenether the config for pleroma-fe is configured in [:frontend_configurations](#frontend_configurations) or in ``static/config.json``. * `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML). * `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with @@ -114,6 +114,7 @@ To add configuration to your config file, you can copy it from the base config. * `Pleroma.Web.ActivityPub.MRF.MentionPolicy`: Drops posts mentioning configurable users. (See [`:mrf_mention`](#mrf_mention)). * `Pleroma.Web.ActivityPub.MRF.VocabularyPolicy`: Restricts activities to a configured set of vocabulary. (See [`:mrf_vocabulary`](#mrf_vocabulary)). * `Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy`: Rejects or delists posts based on their age when received. (See [`:mrf_object_age`](#mrf_object_age)). + * `Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy`: Sets a default expiration on all posts made by users of the local instance. Requires `Pleroma.ActivityExpiration` to be enabled for processing the scheduled delections. * `transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo). * `transparency_exclusions`: Exclude specific instance names from MRF transparency. The use of the exclusions feature will be disclosed in nodeinfo as a boolean value. @@ -220,6 +221,8 @@ config :pleroma, :mrf_user_allowlist, %{ ## Pleroma.ActivityExpiration +Enables the worker which processes posts scheduled for deletion. Pinned posts are exempt from expiration. + * `enabled`: whether expired activities will be sent to the job queue to be deleted ## Frontends @@ -1048,6 +1051,8 @@ Restrict access for unauthenticated users to timelines (public and federated), u * `local` * `remote` +Note: when `:instance, :public` is set to `false`, all `:restrict_unauthenticated` items be effectively set to `true` by default. If you'd like to allow unauthenticated access to specific API endpoints on a private instance, please explicitly set `:restrict_unauthenticated` to non-default value in `config/prod.secret.exs`. + Note: setting `restrict_unauthenticated/timelines/local` to `true` has no practical sense if `restrict_unauthenticated/timelines/federated` is set to `false` (since local public activities will still be delivered to unauthenticated users as part of federated timeline). ## Pleroma.Web.ApiSpec.CastAndValidate diff --git a/docs/configuration/static_dir.md b/docs/configuration/static_dir.md index 5fb38c3de..8ac07b725 100644 --- a/docs/configuration/static_dir.md +++ b/docs/configuration/static_dir.md @@ -1,45 +1,61 @@ # Static Directory -Static frontend files are shipped in `priv/static/` and tracked by version control in this repository. If you want to overwrite or update these without the possibility of merge conflicts, you can write your custom versions to `instance/static/`. +Static frontend files are shipped with pleroma. If you want to overwrite or update these without problems during upgrades, you can write your custom versions to the static directory. -``` -config :pleroma, :instance, - static_dir: "instance/static/", -``` +You can find the location of the static directory in the [configuration](../cheatsheet/#instance). -For example, edit `instance/static/instance/panel.html` . +=== "OTP" + + ```elixir + config :pleroma, :instance, + static_dir: "/var/lib/pleroma/static/" + ``` + +=== "From Source" + + ```elixir + config :pleroma, :instance, + static_dir: "instance/static/" + ``` Alternatively, you can overwrite this value in your configuration to use a different static instance directory. -This document is written assuming `instance/static/`. +This document is written using `$static_dir` as the value of the `config :pleroma, :instance, static_dir` setting. -Or, if you want to manage your custom file in git repository, basically remove the `instance/` entry from `.gitignore`. +If you use a From Source installation and want to manage your custom files in the git repository, you can remove the `instance/` entry from `.gitignore`. ## robots.txt -By default, the `robots.txt` that ships in `priv/static/` is permissive. It allows well-behaved search engines to index all of your instance's URIs. +There's a mix tasks to [generate a new robot.txt](../../administration/CLI_tasks/robots_txt/). -If you want to generate a restrictive `robots.txt`, you can run the following mix task. The generated `robots.txt` will be written in your instance static directory. +For more complex things, you can write your own robots.txt to `$static_dir/robots.txt`. + +E.g. if you want to block all crawlers except for [fediverse.network](https://fediverse.network/about) you can use ``` -mix pleroma.robots_txt disallow_all +User-Agent: * +Disallow: / + +User-Agent: crawler-us-il-1.fediverse.network +Allow: / + +User-Agent: makhnovtchina.random.sh +Allow: / ``` ## Thumbnail -Put on `instance/static/instance/thumbnail.jpeg` with your selfie or other neat picture. It will appear in [Pleroma Instances](http://distsn.org/pleroma-instances.html). +Add `$static_dir/instance/thumbnail.jpeg` with your selfie or other neat picture. It will be available on `http://your-domain.tld/instance/thumbnail.jpeg` and can be used by external applications. ## Instance-specific panel -![instance-specific panel demo](/uploads/296b19ec806b130e0b49b16bfe29ce8a/image.png) - -Create and Edit your file on `instance/static/instance/panel.html`. +Create and Edit your file at `$static_dir/instance/panel.html`. ## Background -You can change the background of your Pleroma instance by uploading it to `instance/static/`, and then changing `background` in `config/prod.secret.exs` accordingly. +You can change the background of your Pleroma instance by uploading it to `$static_dir/`, and then changing `background` in [your configuration](../cheatsheet/#frontend_configurations) accordingly. -If you put `instance/static/images/background.jpg` +E.g. if you put `$static_dir/images/background.jpg` ``` config :pleroma, :frontend_configurations, @@ -50,12 +66,14 @@ config :pleroma, :frontend_configurations, ## Logo -![logo modification demo](/uploads/c70b14de60fa74245e7f0dcfa695ebff/image.png) +!!! important + Note the extra `static` folder for the default logo.png location -If you want to give a brand to your instance, You can change the logo of your instance by uploading it to `instance/static/`. +If you want to give a brand to your instance, You can change the logo of your instance by uploading it to the static directory `$static_dir/static/logo.png`. -Alternatively, you can specify the path with config. -If you put `instance/static/static/mylogo-file.png` +Alternatively, you can specify the path to your logo in [your configuration](../cheatsheet/#frontend_configurations). + +E.g. if you put `$static_dir/static/mylogo-file.png` ``` config :pleroma, :frontend_configurations, @@ -66,4 +84,7 @@ config :pleroma, :frontend_configurations, ## Terms of Service -Terms of Service will be shown to all users on the registration page. It's the best place where to write down the rules for your instance. You can modify the rules by changing `instance/static/static/terms-of-service.html`. +!!! important + Note the extra `static` folder for the terms-of-service.html + +Terms of Service will be shown to all users on the registration page. It's the best place where to write down the rules for your instance. You can modify the rules by adding and changing `$static_dir/static/terms-of-service.html`. diff --git a/docs/installation/freebsd_en.md b/docs/installation/freebsd_en.md new file mode 100644 index 000000000..130d68766 --- /dev/null +++ b/docs/installation/freebsd_en.md @@ -0,0 +1,210 @@ +# Installing on FreeBSD + +This document was written for FreeBSD 12.1, but should be work on future releases. + +## Required software + +This assumes the target system has `pkg(8)`. + +``` +# pkg install elixir postgresql12-server postgresql12-client postgresql12-contrib git-lite sudo nginx gmake acme.sh +``` + +Copy the rc.d scripts to the right directory: + +Setup the required services to automatically start at boot, using `sysrc(8)`. + +``` +# sysrc nginx_enable=YES +# sysrc postgresql_enable=YES +``` + +## Initialize postgres + +``` +# service postgresql initdb +# service postgresql start +``` + +## Configuring Pleroma + +Create a user for Pleroma: + +``` +# pw add user pleroma -m +# echo 'export LC_ALL="en_US.UTF-8"' >> /home/pleroma/.profile +# su -l pleroma +``` + +Clone the repository: + +``` +$ cd $HOME # Should be the same as /home/pleroma +$ git clone -b stable https://git.pleroma.social/pleroma/pleroma.git +``` + +Configure Pleroma. Note that you need a domain name at this point: + +``` +$ cd /home/pleroma/pleroma +$ mix deps.get # Enter "y" when asked to install Hex +$ mix pleroma.instance gen # You will be asked a few questions here. +$ cp config/generated_config.exs config/prod.secret.exs +``` + +Since Postgres is configured, we can now initialize the database. There should +now be a file in `config/setup_db.psql` that makes this easier. Edit it, and +*change the password* to a password of your choice. Make sure it is secure, since +it'll be protecting your database. As root, you can now initialize the database: + +``` +# cd /home/pleroma/pleroma +# sudo -Hu postgres -g postgres psql -f config/setup_db.psql +``` + +Postgres allows connections from all users without a password by default. To +fix this, edit `/var/db/postgres/data12/pg_hba.conf`. Change every `trust` to +`password`. + +Once this is done, restart Postgres with: +``` +# service postgresql restart +``` + +Run the database migrations. + +Back as the pleroma user, run the following to implement any database migrations. + +``` +# su -l pleroma +$ cd /home/pleroma/pleroma +$ MIX_ENV=prod mix ecto.migrate +``` + +You will need to do this whenever you update with `git pull`: + +## Configuring acme.sh + +We'll be using acme.sh in Stateless Mode for TLS certificate renewal. + +First, as root, allow the user `acme` to have access to the acme log file, as follows: + +``` +# touch /var/log/acme.sh.log +# chown acme:acme /var/log/acme.sh.log +# chmod 600 /var/log/acme.sh.log +``` + +Next, obtain your account fingerprint: + +``` +# sudo -Hu acme -g acme acme.sh --register-account +``` + +You need to add the following to your nginx configuration for the server +running on port 80: + +``` + location ~ ^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)$ { + default_type text/plain; + return 200 "$1.6fXAG9VyG0IahirPEU2ZerUtItW2DHzDzD9wZaEKpqd"; + } +``` + +Replace the string after after `$1.` with your fingerprint. + +Start nginx: + +``` +# service nginx start +``` + +It should now be possible to issue a cert (replace `example.com` +with your domain name): + +``` +# sudo -Hu acme -g acme acme.sh --issue -d example.com --stateless +``` + +Let's add auto-renewal to `/etc/crontab` +(replace `example.com` with your domain): + +``` +/usr/local/bin/sudo -Hu acme -g acme /usr/local/sbin/acme.sh -r -d example.com --stateless +``` + +### Configuring nginx + +FreeBSD's default nginx configuration does not contain an include directive, which is +typically used for multiple sites. Therefore, you will need to first create the required +directory as follows: + + +``` +# mkdir -p /usr/local/etc/nginx/sites-available +``` + +Next, add an `include` directive to `/usr/local/etc/nginx/nginx.conf`, within the `http {}` +block, as follows: + + +``` +http { +... + include /usr/local/etc/nginx/sites-available/*; +} +``` + +As root, copy `/home/pleroma/pleroma/installation/pleroma.nginx` to +`/usr/local/etc/nginx/sites-available/pleroma.nginx`. + +Edit the defaults of `/usr/local/etc/nginx/sites-available/pleroma.nginx`: + +* Change `ssl_trusted_certificate` to `/var/db/acme/certs/example.tld/example.tld.cer`. +* Change `ssl_certificate` to `/var/db/acme/certs/example.tld/fullchain.cer`. +* Change `ssl_certificate_key` to `/var/db/acme/certs/example.tld/example.tld.key`. +* Change all references of `example.tld` to your instance's domain name. + +## Creating a startup script for Pleroma + +Pleroma will need to compile when it initially starts, which typically takes a longer +period of time. Therefore, it is good practice to initially run pleroma from the +command-line before utilizing the rc.d script. That is done as follows: + +``` +# su -l pleroma +$ cd $HOME/pleroma +$ MIX_ENV=prod mix phx.server +``` + +Copy the startup script to the correct location and make sure it's executable: + +``` +# cp /home/pleroma/pleroma/installation/freebsd/rc.d/pleroma /usr/local/etc/rc.d/pleroma +# chmod +x /usr/local/etc/rc.d/pleroma +``` + +Update the `/etc/rc.conf` and start pleroma with the following commands: + +``` +# sysrc pleroma_enable=YES +# service pleroma start +``` + +#### Create your first user + +If your instance is up and running, you can create your first user with administrative rights with the following task: + +```shell +sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new --admin +``` +## Conclusion + +Restart nginx with `# service nginx restart` and you should be up and running. + +Make sure your time is in sync, or other instances will receive your posts with +incorrect timestamps. You should have ntpd running. + +## Questions + +Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**. diff --git a/docs/installation/migrating_from_source_otp_en.md b/docs/installation/migrating_from_source_otp_en.md index 31c2f1294..d303a6daf 100644 --- a/docs/installation/migrating_from_source_otp_en.md +++ b/docs/installation/migrating_from_source_otp_en.md @@ -8,13 +8,15 @@ You will be running commands as root. If you aren't root already, please elevate The system needs to have `curl` and `unzip` installed for downloading and unpacking release builds. -```sh tab="Alpine" -apk add curl unzip -``` +=== "Alpine" + ```sh + apk add curl unzip + ``` -```sh tab="Debian/Ubuntu" -apt install curl unzip -``` +=== "Debian/Ubuntu" + ```sh + apt install curl unzip + ``` ## Moving content out of the application directory When using OTP releases the application directory changes with every version so it would be a bother to keep content there (and also dangerous unless `--no-rm` option is used when updating). Fortunately almost all paths in Pleroma are configurable, so it is possible to move them out of there. @@ -110,27 +112,29 @@ OTP releases have different service files than from-source installs so they need **Warning:** The service files assume pleroma user's home directory is `/opt/pleroma`, please make sure all paths fit your installation. -```sh tab="Alpine" -# Copy the service into a proper directory -cp -f ~pleroma/installation/init.d/pleroma /etc/init.d/pleroma +=== "Alpine" + ```sh + # Copy the service into a proper directory + cp -f ~pleroma/installation/init.d/pleroma /etc/init.d/pleroma -# Start pleroma -rc-service pleroma start -``` + # Start pleroma + rc-service pleroma start + ``` -```sh tab="Debian/Ubuntu" -# Copy the service into a proper directory -cp ~pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service +=== "Debian/Ubuntu" + ```sh + # Copy the service into a proper directory + cp ~pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service -# Reload service files -systemctl daemon-reload + # Reload service files + systemctl daemon-reload -# Reenable pleroma to start on boot -systemctl reenable pleroma + # Reenable pleroma to start on boot + systemctl reenable pleroma -# Start pleroma -systemctl start pleroma -``` + # Start pleroma + systemctl start pleroma + ``` ## Running mix tasks Refer to [Running mix tasks](otp_en.md#running-mix-tasks) section from OTP release installation guide. diff --git a/docs/installation/otp_en.md b/docs/installation/otp_en.md index e4f822d1c..b7e3bb2ac 100644 --- a/docs/installation/otp_en.md +++ b/docs/installation/otp_en.md @@ -28,15 +28,17 @@ Other than things bundled in the OTP release Pleroma depends on: * nginx (could be swapped with another reverse proxy but this guide covers only it) * certbot (for Let's Encrypt certificates, could be swapped with another ACME client, but this guide covers only it) -```sh tab="Alpine" -echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories -apk update -apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot -``` +=== "Alpine" + ``` + echo "http://nl.alpinelinux.org/alpine/latest-stable/community" >> /etc/apk/repositories + apk update + apk add curl unzip ncurses postgresql postgresql-contrib nginx certbot + ``` -```sh tab="Debian/Ubuntu" -apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot -``` +=== "Debian/Ubuntu" + ``` + apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot + ``` ## Setup ### Configuring PostgreSQL @@ -47,31 +49,35 @@ apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot RUM indexes are an alternative indexing scheme that is not included in PostgreSQL by default. You can read more about them on the [Configuration page](../configuration/cheatsheet.md#rum-indexing-for-full-text-search). They are completely optional and most of the time are not worth it, especially if you are running a single user instance (unless you absolutely need ordered search results). -```sh tab="Alpine" -apk add git build-base postgresql-dev -git clone https://github.com/postgrespro/rum /tmp/rum -cd /tmp/rum -make USE_PGXS=1 -make USE_PGXS=1 install -cd -rm -r /tmp/rum -``` +=== "Alpine" + ``` + apk add git build-base postgresql-dev + git clone https://github.com/postgrespro/rum /tmp/rum + cd /tmp/rum + make USE_PGXS=1 + make USE_PGXS=1 install + cd + rm -r /tmp/rum + ``` -```sh tab="Debian/Ubuntu" -# Available only on Buster/19.04 -apt install postgresql-11-rum -``` +=== "Debian/Ubuntu" + ``` + # Available only on Buster/19.04 + apt install postgresql-11-rum + ``` #### (Optional) Performance configuration It is encouraged to check [Optimizing your PostgreSQL performance](../configuration/postgresql.md) document, for tips on PostgreSQL tuning. -```sh tab="Alpine" -rc-service postgresql restart -``` +=== "Alpine" + ``` + rc-service postgresql restart + ``` -```sh tab="Debian/Ubuntu" -systemctl restart postgresql -``` +=== "Debian/Ubuntu" + ``` + systemctl restart postgresql + ``` If you are using PostgreSQL 12 or higher, add this to your Ecto database configuration @@ -151,14 +157,16 @@ certbot certonly --standalone --preferred-challenges http -d yourinstance.tld The location of nginx configs is dependent on the distro -```sh tab="Alpine" -cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf -``` +=== "Alpine" + ``` + cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf + ``` -```sh tab="Debian/Ubuntu" -cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.conf -ln -s /etc/nginx/sites-available/pleroma.conf /etc/nginx/sites-enabled/pleroma.conf -``` +=== "Debian/Ubuntu" + ``` + cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.conf + ln -s /etc/nginx/sites-available/pleroma.conf /etc/nginx/sites-enabled/pleroma.conf + ``` If your distro does not have either of those you can append `include /etc/nginx/pleroma.conf` to the end of the http section in /etc/nginx/nginx.conf and ```sh @@ -175,35 +183,39 @@ nginx -t ``` #### Start nginx -```sh tab="Alpine" -rc-service nginx start -``` +=== "Alpine" + ``` + rc-service nginx start + ``` -```sh tab="Debian/Ubuntu" -systemctl start nginx -``` +=== "Debian/Ubuntu" + ``` + systemctl start nginx + ``` At this point if you open your (sub)domain in a browser you should see a 502 error, that's because Pleroma is not started yet. ### Setting up a system service -```sh tab="Alpine" -# Copy the service into a proper directory -cp /opt/pleroma/installation/init.d/pleroma /etc/init.d/pleroma +=== "Alpine" + ``` + # Copy the service into a proper directory + cp /opt/pleroma/installation/init.d/pleroma /etc/init.d/pleroma -# Start pleroma and enable it on boot -rc-service pleroma start -rc-update add pleroma -``` + # Start pleroma and enable it on boot + rc-service pleroma start + rc-update add pleroma + ``` -```sh tab="Debian/Ubuntu" -# Copy the service into a proper directory -cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service +=== "Debian/Ubuntu" + ``` + # Copy the service into a proper directory + cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service -# Start pleroma and enable it on boot -systemctl start pleroma -systemctl enable pleroma -``` + # Start pleroma and enable it on boot + systemctl start pleroma + systemctl enable pleroma + ``` If everything worked, you should see Pleroma-FE when visiting your domain. If that didn't happen, try reviewing the installation steps, starting Pleroma in the foreground and seeing if there are any errrors. @@ -223,43 +235,45 @@ $EDITOR path-to-nginx-config nginx -t ``` -```sh tab="Alpine" -# Restart nginx -rc-service nginx restart +=== "Alpine" + ``` + # Restart nginx + rc-service nginx restart -# Start the cron daemon and make it start on boot -rc-service crond start -rc-update add crond + # Start the cron daemon and make it start on boot + rc-service crond start + rc-update add crond -# Ensure the webroot menthod and post hook is working -certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook 'rc-service nginx reload' + # Ensure the webroot menthod and post hook is working + certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook 'rc-service nginx reload' -# Add it to the daily cron -echo '#!/bin/sh -certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "rc-service nginx reload" -' > /etc/periodic/daily/renew-pleroma-cert -chmod +x /etc/periodic/daily/renew-pleroma-cert + # Add it to the daily cron + echo '#!/bin/sh + certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "rc-service nginx reload" + ' > /etc/periodic/daily/renew-pleroma-cert + chmod +x /etc/periodic/daily/renew-pleroma-cert -# If everything worked the output should contain /etc/cron.daily/renew-pleroma-cert -run-parts --test /etc/periodic/daily -``` + # If everything worked the output should contain /etc/cron.daily/renew-pleroma-cert + run-parts --test /etc/periodic/daily + ``` -```sh tab="Debian/Ubuntu" -# Restart nginx -systemctl restart nginx +=== "Debian/Ubuntu" + ``` + # Restart nginx + systemctl restart nginx -# Ensure the webroot menthod and post hook is working -certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook 'systemctl reload nginx' + # Ensure the webroot menthod and post hook is working + certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --dry-run --post-hook 'systemctl reload nginx' -# Add it to the daily cron -echo '#!/bin/sh -certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "systemctl reload nginx" -' > /etc/cron.daily/renew-pleroma-cert -chmod +x /etc/cron.daily/renew-pleroma-cert + # Add it to the daily cron + echo '#!/bin/sh + certbot renew --cert-name yourinstance.tld --webroot -w /var/lib/letsencrypt/ --post-hook "systemctl reload nginx" + ' > /etc/cron.daily/renew-pleroma-cert + chmod +x /etc/cron.daily/renew-pleroma-cert -# If everything worked the output should contain /etc/cron.daily/renew-pleroma-cert -run-parts --test /etc/cron.daily -``` + # If everything worked the output should contain /etc/cron.daily/renew-pleroma-cert + run-parts --test /etc/cron.daily + ``` ## Create your first user and set as admin ```sh diff --git a/installation/freebsd/rc.d/pleroma b/installation/freebsd/rc.d/pleroma new file mode 100755 index 000000000..1e41e57e6 --- /dev/null +++ b/installation/freebsd/rc.d/pleroma @@ -0,0 +1,28 @@ +#!/bin/sh +# REQUIRE: DAEMON postgresql +# PROVIDE: pleroma + +# sudo -u pleroma MIX_ENV=prod elixir --erl \"-detached\" -S mix phx.server + +. /etc/rc.subr + +name="pleroma" +desc="Pleroma Social Media Platform" +rcvar=${name}_enable +command="/usr/local/bin/elixir" +command_args="--erl \"-detached\" -S /usr/local/bin/mix phx.server" +pidfile="/dev/null" + +pleroma_user="pleroma" +pleroma_home="/home/pleroma" +pleroma_chdir="${pleroma_home}/pleroma" +pleroma_env="HOME=${pleroma_home} MIX_ENV=prod" + +check_pidfile() +{ + pid=$(pgrep beam.smp$) + echo -n "${pid}" +} + +load_rc_config ${name} +run_rc_command "$1" diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index d57e59b11..7d8f00b08 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -134,13 +134,23 @@ def run(["ensure_expiration"]) do Pleroma.Activity |> join(:left, [a], u in assoc(a, :expiration)) + |> join(:inner, [a, _u], o in Object, + on: + fragment( + "(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')", + o.data, + a.data, + a.data + ) + ) |> where(local: true) |> where([a, u], is_nil(u)) + |> where([a], fragment("(? ->> 'type'::text) = 'Create'", a.data)) + |> where([_a, _u, o], fragment("?->>'type' = 'Note'", o.data)) |> Pleroma.RepoStreamer.chunk_stream(100) |> Stream.each(fn activities -> Enum.each(activities, fn activity -> expires_at = Timex.shift(activity.inserted_at, days: days) - Pleroma.ActivityExpiration.create(activity, expires_at, false) end) end) diff --git a/lib/mix/tasks/pleroma/ecto/migrate.ex b/lib/mix/tasks/pleroma/ecto/migrate.ex index bc8ed29fb..e903bd171 100644 --- a/lib/mix/tasks/pleroma/ecto/migrate.ex +++ b/lib/mix/tasks/pleroma/ecto/migrate.ex @@ -41,6 +41,10 @@ def run(args \\ []) do load_pleroma() {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) + if Application.get_env(:pleroma, Pleroma.Repo)[:ssl] do + Application.ensure_all_started(:ssl) + end + opts = if opts[:to] || opts[:step] || opts[:all], do: opts, diff --git a/lib/mix/tasks/pleroma/ecto/rollback.ex b/lib/mix/tasks/pleroma/ecto/rollback.ex index f43bd0b98..3dba952cb 100644 --- a/lib/mix/tasks/pleroma/ecto/rollback.ex +++ b/lib/mix/tasks/pleroma/ecto/rollback.ex @@ -40,6 +40,10 @@ def run(args \\ []) do load_pleroma() {opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases) + if Application.get_env(:pleroma, Pleroma.Repo)[:ssl] do + Application.ensure_all_started(:ssl) + end + opts = if opts[:to] || opts[:step] || opts[:all], do: opts, diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index c3cea8d2a..97feebeaa 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -340,4 +340,10 @@ def direct_conversation_id(activity, for_user) do _ -> nil end end + + @spec pinned_by_actor?(Activity.t()) :: boolean() + def pinned_by_actor?(%Activity{} = activity) do + actor = user_actor(activity) + activity.id in actor.pinned_activities + end end diff --git a/lib/pleroma/activity_expiration.ex b/lib/pleroma/activity_expiration.ex index 7cc9668b3..955f0578e 100644 --- a/lib/pleroma/activity_expiration.ex +++ b/lib/pleroma/activity_expiration.ex @@ -46,7 +46,12 @@ def due_expirations(offset \\ 0) do ActivityExpiration |> where([exp], exp.scheduled_at < ^naive_datetime) + |> limit(50) + |> preload(:activity) |> Repo.all() + |> Enum.reject(fn %{activity: activity} -> + Activity.pinned_by_actor?(activity) + end) end def validate_scheduled_at(changeset, false), do: changeset diff --git a/lib/pleroma/config.ex b/lib/pleroma/config.ex index a8329cc1e..97f877595 100644 --- a/lib/pleroma/config.ex +++ b/lib/pleroma/config.ex @@ -81,6 +81,16 @@ def delete(key) do Application.delete_env(:pleroma, key) end + def restrict_unauthenticated_access?(resource, kind) do + setting = get([:restrict_unauthenticated, resource, kind]) + + if setting in [nil, :if_instance_is_private] do + !get!([:instance, :public]) + else + setting + end + end + def oauth_consumer_strategies, do: get([:auth, :oauth_consumer_strategies], []) def oauth_consumer_enabled?, do: oauth_consumer_strategies() != [] diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 3ff25118d..6fdbc8efd 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -125,8 +125,8 @@ def fetch_object_from_id(id, options \\ []) do defp prepare_activity_params(data) do %{ "type" => "Create", - "to" => data["to"], - "cc" => data["cc"], + "to" => data["to"] || [], + "cc" => data["cc"] || [], # Should we seriously keep this attributedTo thing? "actor" => data["actor"] || data["attributedTo"], "object" => data diff --git a/lib/pleroma/upload/filter/mogrifun.ex b/lib/pleroma/upload/filter/mogrifun.ex index a8503ac24..c8fa7b190 100644 --- a/lib/pleroma/upload/filter/mogrifun.ex +++ b/lib/pleroma/upload/filter/mogrifun.ex @@ -6,6 +6,10 @@ defmodule Pleroma.Upload.Filter.Mogrifun do @behaviour Pleroma.Upload.Filter alias Pleroma.Upload.Filter + @moduledoc """ + This module is just an example of an Upload filter. It's not supposed to be used in production. + """ + @filters [ {"implode", "1"}, {"-raise", "20"}, diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index d1436a688..a9820affa 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -311,10 +311,12 @@ def visible_for(%User{} = user, for_user) do def visible_for(_, _), do: :invisible - defp restrict_unauthenticated?(%User{local: local}) do - config_key = if local, do: :local, else: :remote + defp restrict_unauthenticated?(%User{local: true}) do + Config.restrict_unauthenticated_access?(:profiles, :local) + end - Config.get([:restrict_unauthenticated, :profiles, config_key], false) + defp restrict_unauthenticated?(%User{local: _}) do + Config.restrict_unauthenticated_access?(:profiles, :remote) end defp visible_account_status(user) do @@ -1581,6 +1583,49 @@ def update_notification_settings(%User{} = user, settings) do |> update_and_set_cache() end + @spec purge_user_changeset(User.t()) :: Changeset.t() + def purge_user_changeset(user) do + # "Right to be forgotten" + # https://gdpr.eu/right-to-be-forgotten/ + change(user, %{ + bio: nil, + raw_bio: nil, + email: nil, + name: nil, + password_hash: nil, + keys: nil, + public_key: nil, + avatar: %{}, + tags: [], + last_refreshed_at: nil, + last_digest_emailed_at: nil, + banner: %{}, + background: %{}, + note_count: 0, + follower_count: 0, + following_count: 0, + locked: false, + confirmation_pending: false, + password_reset_pending: false, + approval_pending: false, + registration_reason: nil, + confirmation_token: nil, + domain_blocks: [], + deactivated: true, + ap_enabled: false, + is_moderator: false, + is_admin: false, + mastofe_settings: nil, + mascot: nil, + emoji: %{}, + pleroma_settings_store: %{}, + fields: [], + raw_fields: [], + discoverable: false, + also_known_as: [] + }) + end + def delete(users) when is_list(users) do for user <- users, do: delete(user) end @@ -1608,7 +1653,7 @@ defp delete_or_deactivate(%User{local: true} = user) do _ -> user - |> change(%{deactivated: true, email: nil}) + |> purge_user_changeset() |> update_and_set_cache() end end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index fe62673dc..bde1fe708 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -285,32 +285,6 @@ def listen(%{to: to, actor: actor, context: context, object: object} = params) d end end - @spec accept(map()) :: {:ok, Activity.t()} | {:error, any()} - def accept(params) do - accept_or_reject("Accept", params) - end - - @spec reject(map()) :: {:ok, Activity.t()} | {:error, any()} - def reject(params) do - accept_or_reject("Reject", params) - end - - @spec accept_or_reject(String.t(), map()) :: {:ok, Activity.t()} | {:error, any()} - defp accept_or_reject(type, %{to: to, actor: actor, object: object} = params) do - local = Map.get(params, :local, true) - activity_id = Map.get(params, :activity_id, nil) - - data = - %{"to" => to, "type" => type, "actor" => actor.ap_id, "object" => object} - |> Maps.put_if_present("id", activity_id) - - with {:ok, activity} <- insert(data, local), - _ <- notify_and_stream(activity), - :ok <- maybe_federate(activity) do - {:ok, activity} - end - end - @spec unfollow(User.t(), User.t(), String.t() | nil, boolean()) :: {:ok, Activity.t()} | nil | {:error, any()} def unfollow(follower, followed, activity_id \\ nil, local \\ true) do diff --git a/lib/pleroma/web/activity_pub/builder.ex b/lib/pleroma/web/activity_pub/builder.ex index 1b4c421b8..f2392ce79 100644 --- a/lib/pleroma/web/activity_pub/builder.ex +++ b/lib/pleroma/web/activity_pub/builder.ex @@ -14,6 +14,28 @@ defmodule Pleroma.Web.ActivityPub.Builder do require Pleroma.Constants + def accept_or_reject(actor, activity, type) do + data = %{ + "id" => Utils.generate_activity_id(), + "actor" => actor.ap_id, + "type" => type, + "object" => activity.data["id"], + "to" => [activity.actor] + } + + {:ok, data, []} + end + + @spec reject(User.t(), Activity.t()) :: {:ok, map(), keyword()} + def reject(actor, rejected_activity) do + accept_or_reject(actor, rejected_activity, "Reject") + end + + @spec accept(User.t(), Activity.t()) :: {:ok, map(), keyword()} + def accept(actor, accepted_activity) do + accept_or_reject(actor, accepted_activity, "Accept") + end + @spec follow(User.t(), User.t()) :: {:ok, map(), keyword()} def follow(follower, followed) do data = %{ diff --git a/lib/pleroma/web/activity_pub/object_validator.ex b/lib/pleroma/web/activity_pub/object_validator.ex index e1114a44d..d770ce1be 100644 --- a/lib/pleroma/web/activity_pub/object_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validator.ex @@ -13,8 +13,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Object alias Pleroma.User + alias Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnnounceValidator alias Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator alias Pleroma.Web.ActivityPub.ObjectValidators.BlockValidator alias Pleroma.Web.ActivityPub.ObjectValidators.ChatMessageValidator alias Pleroma.Web.ActivityPub.ObjectValidators.CreateChatMessageValidator @@ -30,6 +32,17 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do @spec validate(map(), keyword()) :: {:ok, map(), keyword()} | {:error, any()} def validate(object, meta) + def validate(%{"type" => type} = object, meta) + when type in ~w[Accept Reject] do + with {:ok, object} <- + object + |> AcceptRejectValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) + {:ok, object, meta} + end + end + def validate(%{"type" => "Follow"} = object, meta) do with {:ok, object} <- object @@ -125,6 +138,16 @@ def validate(%{"type" => "Question"} = object, meta) do end end + def validate(%{"type" => "Audio"} = object, meta) do + with {:ok, object} <- + object + |> AudioValidator.cast_and_validate() + |> Ecto.Changeset.apply_action(:insert) do + object = stringify_keys(object) + {:ok, object, meta} + end + end + def validate(%{"type" => "Answer"} = object, meta) do with {:ok, object} <- object @@ -164,7 +187,7 @@ def validate( %{"type" => "Create", "object" => %{"type" => objtype} = object} = create_activity, meta ) - when objtype in ["Question", "Answer"] do + when objtype in ~w[Question Answer Audio] do with {:ok, object_data} <- cast_and_apply(object), meta = Keyword.put(meta, :object_data, object_data |> stringify_keys), {:ok, create_activity} <- @@ -198,6 +221,10 @@ def cast_and_apply(%{"type" => "Answer"} = object) do AnswerValidator.cast_and_apply(object) end + def cast_and_apply(%{"type" => "Audio"} = object) do + AudioValidator.cast_and_apply(object) + end + def cast_and_apply(o), do: {:error, {:validator_not_set, o}} # is_struct/1 isn't present in Elixir 1.8.x diff --git a/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex new file mode 100644 index 000000000..179beda58 --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/accept_reject_validator.ex @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptRejectValidator do + use Ecto.Schema + + alias Pleroma.Activity + alias Pleroma.EctoType.ActivityPub.ObjectValidators + + import Ecto.Changeset + import Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations + + @primary_key false + + embedded_schema do + field(:id, ObjectValidators.ObjectID, primary_key: true) + field(:type, :string) + field(:object, ObjectValidators.ObjectID) + field(:actor, ObjectValidators.ObjectID) + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) + end + + def cast_data(data) do + %__MODULE__{} + |> cast(data, __schema__(:fields)) + end + + def validate_data(cng) do + cng + |> validate_required([:id, :type, :actor, :to, :cc, :object]) + |> validate_inclusion(:type, ["Accept", "Reject"]) + |> validate_actor_presence() + |> validate_object_presence(allowed_types: ["Follow"]) + |> validate_accept_reject_rights() + end + + def cast_and_validate(data) do + data + |> cast_data + |> validate_data + end + + def validate_accept_reject_rights(cng) do + with object_id when is_binary(object_id) <- get_field(cng, :object), + %Activity{data: %{"object" => followed_actor}} <- Activity.get_by_ap_id(object_id), + true <- followed_actor == get_field(cng, :actor) do + cng + else + _e -> + cng + |> add_error(:actor, "can't accept or reject the given activity") + end + end +end diff --git a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex index 323367642..b9fbaf4f6 100644 --- a/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/answer_validator.ex @@ -15,16 +15,13 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.AnswerValidator do embedded_schema do field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, {:array, :string}, default: []) - field(:cc, {:array, :string}, default: []) - - # is this actually needed? - field(:bto, {:array, :string}, default: []) - field(:bcc, {:array, :string}, default: []) - + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) + field(:bto, ObjectValidators.Recipients, default: []) + field(:bcc, ObjectValidators.Recipients, default: []) field(:type, :string) field(:name, :string) - field(:inReplyTo, :string) + field(:inReplyTo, ObjectValidators.ObjectID) field(:attributedTo, ObjectValidators.ObjectID) # TODO: Remove actor on objects diff --git a/lib/pleroma/web/activity_pub/object_validators/audio_validator.ex b/lib/pleroma/web/activity_pub/object_validators/audio_validator.ex new file mode 100644 index 000000000..5d9bf345f --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/audio_validator.ex @@ -0,0 +1,92 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AudioValidator do + use Ecto.Schema + + alias Pleroma.EctoType.ActivityPub.ObjectValidators + alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations + + import Ecto.Changeset + + @primary_key false + @derive Jason.Encoder + + embedded_schema do + field(:id, ObjectValidators.ObjectID, primary_key: true) + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) + field(:bto, ObjectValidators.Recipients, default: []) + field(:bcc, ObjectValidators.Recipients, default: []) + # TODO: Write type + field(:tag, {:array, :map}, default: []) + field(:type, :string) + field(:content, :string) + field(:context, :string) + + # TODO: Remove actor on objects + field(:actor, ObjectValidators.ObjectID) + + field(:attributedTo, ObjectValidators.ObjectID) + field(:summary, :string) + field(:published, ObjectValidators.DateTime) + # TODO: Write type + field(:emoji, :map, default: %{}) + field(:sensitive, :boolean, default: false) + embeds_many(:attachment, AttachmentValidator) + field(:replies_count, :integer, default: 0) + field(:like_count, :integer, default: 0) + field(:announcement_count, :integer, default: 0) + field(:inReplyTo, :string) + field(:uri, ObjectValidators.Uri) + # short identifier for PleromaFE to group statuses by context + field(:context_id, :integer) + + field(:likes, {:array, :string}, default: []) + field(:announcements, {:array, :string}, default: []) + end + + def cast_and_apply(data) do + data + |> cast_data + |> apply_action(:insert) + end + + def cast_and_validate(data) do + data + |> cast_data() + |> validate_data() + end + + def cast_data(data) do + %__MODULE__{} + |> changeset(data) + end + + defp fix(data) do + data + |> CommonFixes.fix_defaults() + |> CommonFixes.fix_attribution() + end + + def changeset(struct, data) do + data = fix(data) + + struct + |> cast(data, __schema__(:fields) -- [:attachment]) + |> cast_embed(:attachment) + end + + def validate_data(data_cng) do + data_cng + |> validate_inclusion(:type, ["Audio"]) + |> validate_required([:id, :actor, :attributedTo, :type, :context]) + |> CommonValidations.validate_any_presence([:cc, :to]) + |> CommonValidations.validate_fields_match([:actor, :attributedTo]) + |> CommonValidations.validate_actor_presence() + |> CommonValidations.validate_host_match() + end +end diff --git a/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex new file mode 100644 index 000000000..721749de0 --- /dev/null +++ b/lib/pleroma/web/activity_pub/object_validators/common_fixes.ex @@ -0,0 +1,22 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes do + alias Pleroma.Web.ActivityPub.Utils + + # based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults + def fix_defaults(data) do + %{data: %{"id" => context}, id: context_id} = + Utils.create_context(data["context"] || data["conversation"]) + + data + |> Map.put_new("context", context) + |> Map.put_new("context_id", context_id) + end + + def fix_attribution(data) do + data + |> Map.put_new("actor", data["attributedTo"]) + end +end diff --git a/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex index 316bd0c07..9b9743c4a 100644 --- a/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/create_note_validator.ex @@ -16,11 +16,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CreateNoteValidator do field(:id, ObjectValidators.ObjectID, primary_key: true) field(:actor, ObjectValidators.ObjectID) field(:type, :string) - field(:to, {:array, :string}) - field(:cc, {:array, :string}) - field(:bto, {:array, :string}, default: []) - field(:bcc, {:array, :string}, default: []) - + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) + field(:bto, ObjectValidators.Recipients, default: []) + field(:bcc, ObjectValidators.Recipients, default: []) embeds_one(:object, NoteValidator) end diff --git a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex index a543af1f8..336c92d35 100644 --- a/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/emoji_react_validator.ex @@ -20,8 +20,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.EmojiReactValidator do field(:actor, ObjectValidators.ObjectID) field(:context, :string) field(:content, :string) - field(:to, {:array, :string}, default: []) - field(:cc, {:array, :string}, default: []) + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) end def cast_and_validate(data) do diff --git a/lib/pleroma/web/activity_pub/object_validators/note_validator.ex b/lib/pleroma/web/activity_pub/object_validators/note_validator.ex index a65fe2354..14ae29cb6 100644 --- a/lib/pleroma/web/activity_pub/object_validators/note_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/note_validator.ex @@ -13,10 +13,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do embedded_schema do field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, {:array, :string}, default: []) - field(:cc, {:array, :string}, default: []) - field(:bto, {:array, :string}, default: []) - field(:bcc, {:array, :string}, default: []) + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) + field(:bto, ObjectValidators.Recipients, default: []) + field(:bcc, ObjectValidators.Recipients, default: []) # TODO: Write type field(:tag, {:array, :map}, default: []) field(:type, :string) @@ -34,7 +34,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.NoteValidator do field(:replies_count, :integer, default: 0) field(:like_count, :integer, default: 0) field(:announcement_count, :integer, default: 0) - field(:inReplyTo, :string) + field(:inReplyTo, ObjectValidators.ObjectID) field(:uri, ObjectValidators.Uri) field(:likes, {:array, :string}, default: []) diff --git a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex index f47acf606..a7ca42b2f 100644 --- a/lib/pleroma/web/activity_pub/object_validators/question_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/question_validator.ex @@ -7,9 +7,9 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.Web.ActivityPub.ObjectValidators.AttachmentValidator + alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations alias Pleroma.Web.ActivityPub.ObjectValidators.QuestionOptionsValidator - alias Pleroma.Web.ActivityPub.Utils import Ecto.Changeset @@ -19,10 +19,10 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do # Extends from NoteValidator embedded_schema do field(:id, ObjectValidators.ObjectID, primary_key: true) - field(:to, {:array, :string}, default: []) - field(:cc, {:array, :string}, default: []) - field(:bto, {:array, :string}, default: []) - field(:bcc, {:array, :string}, default: []) + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) + field(:bto, ObjectValidators.Recipients, default: []) + field(:bcc, ObjectValidators.Recipients, default: []) # TODO: Write type field(:tag, {:array, :map}, default: []) field(:type, :string) @@ -42,7 +42,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do field(:replies_count, :integer, default: 0) field(:like_count, :integer, default: 0) field(:announcement_count, :integer, default: 0) - field(:inReplyTo, :string) + field(:inReplyTo, ObjectValidators.ObjectID) field(:uri, ObjectValidators.Uri) # short identifier for PleromaFE to group statuses by context field(:context_id, :integer) @@ -81,27 +81,11 @@ defp fix_closed(data) do end end - # based on Pleroma.Web.ActivityPub.Utils.lazy_put_objects_defaults - defp fix_defaults(data) do - %{data: %{"id" => context}, id: context_id} = - Utils.create_context(data["context"] || data["conversation"]) - - data - |> Map.put_new_lazy("published", &Utils.make_date/0) - |> Map.put_new("context", context) - |> Map.put_new("context_id", context_id) - end - - defp fix_attribution(data) do - data - |> Map.put_new("actor", data["attributedTo"]) - end - defp fix(data) do data - |> fix_attribution() + |> CommonFixes.fix_defaults() + |> CommonFixes.fix_attribution() |> fix_closed() - |> fix_defaults() end def changeset(struct, data) do @@ -117,7 +101,7 @@ def changeset(struct, data) do def validate_data(data_cng) do data_cng |> validate_inclusion(:type, ["Question"]) - |> validate_required([:id, :actor, :attributedTo, :type, :context]) + |> validate_required([:id, :actor, :attributedTo, :type, :context, :context_id]) |> CommonValidations.validate_any_presence([:cc, :to]) |> CommonValidations.validate_fields_match([:actor, :attributedTo]) |> CommonValidations.validate_actor_presence() diff --git a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex index e8d2d39c1..8cae94467 100644 --- a/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/undo_validator.ex @@ -18,8 +18,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UndoValidator do field(:type, :string) field(:object, ObjectValidators.ObjectID) field(:actor, ObjectValidators.ObjectID) - field(:to, {:array, :string}, default: []) - field(:cc, {:array, :string}, default: []) + field(:to, ObjectValidators.Recipients, default: []) + field(:cc, ObjectValidators.Recipients, default: []) end def cast_and_validate(data) do diff --git a/lib/pleroma/web/activity_pub/side_effects.ex b/lib/pleroma/web/activity_pub/side_effects.ex index 5104d38ee..3dc66c60b 100644 --- a/lib/pleroma/web/activity_pub/side_effects.ex +++ b/lib/pleroma/web/activity_pub/side_effects.ex @@ -16,14 +16,70 @@ defmodule Pleroma.Web.ActivityPub.SideEffects do alias Pleroma.Repo alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.ActivityPub.Builder alias Pleroma.Web.ActivityPub.Pipeline alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.Push alias Pleroma.Web.Streamer alias Pleroma.Workers.BackgroundWorker + require Logger + def handle(object, meta \\ []) + # Task this handles + # - Follows + # - Sends a notification + def handle( + %{ + data: %{ + "actor" => actor, + "type" => "Accept", + "object" => follow_activity_id + } + } = object, + meta + ) do + with %Activity{actor: follower_id} = follow_activity <- + Activity.get_by_ap_id(follow_activity_id), + %User{} = followed <- User.get_cached_by_ap_id(actor), + %User{} = follower <- User.get_cached_by_ap_id(follower_id), + {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), + {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do + Notification.update_notification_type(followed, follow_activity) + User.update_follower_count(followed) + User.update_following_count(follower) + end + + {:ok, object, meta} + end + + # Task this handles + # - Rejects all existing follow activities for this person + # - Updates the follow state + # - Dismisses notification + def handle( + %{ + data: %{ + "actor" => actor, + "type" => "Reject", + "object" => follow_activity_id + } + } = object, + meta + ) do + with %Activity{actor: follower_id} = follow_activity <- + Activity.get_by_ap_id(follow_activity_id), + %User{} = followed <- User.get_cached_by_ap_id(actor), + %User{} = follower <- User.get_cached_by_ap_id(follower_id), + {:ok, _follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject") do + FollowingRelationship.update(follower, followed, :follow_reject) + Notification.dismiss(follow_activity) + end + + {:ok, object, meta} + end + # Tasks this handle # - Follows if possible # - Sends a notification @@ -44,33 +100,13 @@ def handle( {_, {:ok, _}, _, _} <- {:following, User.follow(follower, followed, :follow_pending), follower, followed} do if followed.local && !followed.locked do - Utils.update_follow_state_for_all(object, "accept") - FollowingRelationship.update(follower, followed, :follow_accept) - User.update_follower_count(followed) - User.update_following_count(follower) - - %{ - to: [following_user], - actor: followed, - object: follow_id, - local: true - } - |> ActivityPub.accept() + {:ok, accept_data, _} = Builder.accept(followed, object) + {:ok, _activity, _} = Pipeline.common_pipeline(accept_data, local: true) end else - {:following, {:error, _}, follower, followed} -> - Utils.update_follow_state_for_all(object, "reject") - FollowingRelationship.update(follower, followed, :follow_reject) - - if followed.local do - %{ - to: [follower.ap_id], - actor: followed, - object: follow_id, - local: true - } - |> ActivityPub.reject() - end + {:following, {:error, _}, _follower, followed} -> + {:ok, reject_data, _} = Builder.reject(followed, object) + {:ok, _activity, _} = Pipeline.common_pipeline(reject_data, local: true) _ -> nil @@ -217,13 +253,15 @@ def handle(%{data: %{"type" => "EmojiReact"}} = object, meta) do # - Stream out the activity def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, meta) do deleted_object = - Object.normalize(deleted_object, false) || User.get_cached_by_ap_id(deleted_object) + Object.normalize(deleted_object, false) || + User.get_cached_by_ap_id(deleted_object) result = case deleted_object do %Object{} -> with {:ok, deleted_object, activity} <- Object.delete(deleted_object), - %User{} = user <- User.get_cached_by_ap_id(deleted_object.data["actor"]) do + {_, actor} when is_binary(actor) <- {:actor, deleted_object.data["actor"]}, + %User{} = user <- User.get_cached_by_ap_id(actor) do User.remove_pinnned_activity(user, activity) {:ok, user} = ActivityPub.decrease_note_count_if_public(user, deleted_object) @@ -237,6 +275,10 @@ def handle(%{data: %{"type" => "Delete", "object" => deleted_object}} = object, ActivityPub.stream_out(object) ActivityPub.stream_out_participations(deleted_object, user) :ok + else + {:actor, _} -> + Logger.error("The object doesn't have an actor: #{inspect(deleted_object)}") + :no_object_actor end %User{} -> @@ -298,7 +340,8 @@ def handle_object_creation(%{"type" => "Answer"} = object_map, meta) do end end - def handle_object_creation(%{"type" => "Question"} = object, meta) do + def handle_object_creation(%{"type" => objtype} = object, meta) + when objtype in ~w[Audio Question] do with {:ok, object, meta} <- Pipeline.common_pipeline(object, meta) do {:ok, object, meta} end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 2f04cc6ff..6be17e0ed 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -9,9 +9,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do alias Pleroma.Activity alias Pleroma.EarmarkRenderer alias Pleroma.EctoType.ActivityPub.ObjectValidators - alias Pleroma.FollowingRelationship alias Pleroma.Maps - alias Pleroma.Notification alias Pleroma.Object alias Pleroma.Object.Containment alias Pleroma.Repo @@ -391,32 +389,6 @@ defp fix_content(%{"mediaType" => "text/markdown", "content" => content} = objec defp fix_content(object), do: object - defp mastodon_follow_hack(%{"id" => id, "actor" => follower_id}, followed) do - with true <- id =~ "follows", - %User{local: true} = follower <- User.get_cached_by_ap_id(follower_id), - %Activity{} = activity <- Utils.fetch_latest_follow(follower, followed) do - {:ok, activity} - else - _ -> {:error, nil} - end - end - - defp mastodon_follow_hack(_, _), do: {:error, nil} - - defp get_follow_activity(follow_object, followed) do - with object_id when not is_nil(object_id) <- Utils.get_ap_id(follow_object), - {_, %Activity{} = activity} <- {:activity, Activity.get_by_ap_id(object_id)} do - {:ok, activity} - else - # Can't find the activity. This might a Mastodon 2.3 "Accept" - {:activity, nil} -> - mastodon_follow_hack(follow_object, followed) - - _ -> - {:error, nil} - end - end - # Reduce the object list to find the reported user. defp get_reported(objects) do Enum.reduce_while(objects, nil, fn ap_id, _ -> @@ -489,7 +461,7 @@ def handle_incoming( %{"type" => "Create", "object" => %{"type" => objtype} = object} = data, options ) - when objtype in ["Article", "Event", "Note", "Video", "Page", "Audio"] do + when objtype in ~w{Article Event Note Video Page} do actor = Containment.get_actor(data) with nil <- Activity.get_create_by_object_ap_id(object["id"]), @@ -551,60 +523,6 @@ def handle_incoming( end end - def handle_incoming( - %{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => id} = data, - _options - ) do - with actor <- Containment.get_actor(data), - {:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor), - {:ok, follow_activity} <- get_follow_activity(follow_object, followed), - {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), - %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept) do - User.update_follower_count(followed) - User.update_following_count(follower) - - Notification.update_notification_type(followed, follow_activity) - - ActivityPub.accept(%{ - to: follow_activity.data["to"], - type: "Accept", - actor: followed, - object: follow_activity.data["id"], - local: false, - activity_id: id - }) - else - _e -> - :error - end - end - - def handle_incoming( - %{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => id} = data, - _options - ) do - with actor <- Containment.get_actor(data), - {:ok, %User{} = followed} <- User.get_or_fetch_by_ap_id(actor), - {:ok, follow_activity} <- get_follow_activity(follow_object, followed), - {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), - %User{local: true} = follower <- User.get_cached_by_ap_id(follow_activity.data["actor"]), - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject), - {:ok, activity} <- - ActivityPub.reject(%{ - to: follow_activity.data["to"], - type: "Reject", - actor: followed, - object: follow_activity.data["id"], - local: false, - activity_id: id - }) do - {:ok, activity} - else - _e -> :error - end - end - @misskey_reactions %{ "like" => "👍", "love" => "❤️", @@ -637,7 +555,7 @@ def handle_incoming( %{"type" => "Create", "object" => %{"type" => objtype}} = data, _options ) - when objtype in ["Question", "Answer", "ChatMessage"] do + when objtype in ~w{Question Answer ChatMessage Audio} do with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do {:ok, activity} @@ -659,9 +577,10 @@ def handle_incoming( %{"type" => type} = data, _options ) - when type in ~w{Update Block Follow} do + when type in ~w{Update Block Follow Accept Reject} do with {:ok, %User{}} <- ObjectValidator.fetch_actor(data), - {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do + {:ok, activity, _} <- + Pipeline.common_pipeline(data, local: false) do {:ok, activity} end end @@ -670,7 +589,8 @@ def handle_incoming( %{"type" => "Delete"} = data, _options ) do - with {:ok, activity, _} <- Pipeline.common_pipeline(data, local: false) do + with {:ok, activity, _} <- + Pipeline.common_pipeline(data, local: false) do {:ok, activity} else {:error, {:validate_object, _}} = e -> diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 343f41caa..5c349bb7a 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -59,12 +59,9 @@ def visible_for_user?(%{data: %{"listMessage" => list_ap_id}} = activity, %User{ end def visible_for_user?(%{local: local} = activity, nil) do - cfg_key = - if local, - do: :local, - else: :remote + cfg_key = if local, do: :local, else: :remote - if Pleroma.Config.get([:restrict_unauthenticated, :activities, cfg_key]), + if Pleroma.Config.restrict_unauthenticated_access?(:activities, cfg_key), do: false, else: is_public?(activity) end diff --git a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex index e2759d59f..131e22d78 100644 --- a/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex +++ b/lib/pleroma/web/admin_api/controllers/media_proxy_cache_controller.ex @@ -26,29 +26,40 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheController do defdelegate open_api_operation(action), to: Spec.MediaProxyCacheOperation def index(%{assigns: %{user: _}} = conn, params) do - cursor = - :banned_urls_cache - |> :ets.table([{:traverse, {:select, Cachex.Query.create(true, :key)}}]) - |> :qlc.cursor() + entries = fetch_entries(params) + urls = paginate_entries(entries, params.page, params.page_size) - urls = - case params.page do - 1 -> - :qlc.next_answers(cursor, params.page_size) + render(conn, "index.json", + urls: urls, + page_size: params.page_size, + count: length(entries) + ) + end - _ -> - :qlc.next_answers(cursor, (params.page - 1) * params.page_size) - :qlc.next_answers(cursor, params.page_size) - end + defp fetch_entries(params) do + MediaProxy.cache_table() + |> Cachex.stream!(Cachex.Query.create(true, :key)) + |> filter_entries(params[:query]) + end - :qlc.delete_cursor(cursor) + defp filter_entries(stream, query) when is_binary(query) do + regex = ~r/#{query}/i - render(conn, "index.json", urls: urls) + stream + |> Enum.filter(fn url -> String.match?(url, regex) end) + |> Enum.to_list() + end + + defp filter_entries(stream, _), do: Enum.to_list(stream) + + defp paginate_entries(entries, page, page_size) do + offset = page_size * (page - 1) + Enum.slice(entries, offset, page_size) end def delete(%{assigns: %{user: _}, body_params: %{urls: urls}} = conn, _) do MediaProxy.remove_from_banned_urls(urls) - render(conn, "index.json", urls: urls) + json(conn, %{}) end def purge(%{assigns: %{user: _}, body_params: %{urls: urls, ban: ban}} = conn, _) do @@ -58,6 +69,6 @@ def purge(%{assigns: %{user: _}, body_params: %{urls: urls, ban: ban}} = conn, _ MediaProxy.put_in_banned_urls(urls) end - render(conn, "index.json", urls: urls) + json(conn, %{}) end end diff --git a/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex b/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex index c97400beb..a803bda0b 100644 --- a/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex +++ b/lib/pleroma/web/admin_api/views/media_proxy_cache_view.ex @@ -5,7 +5,11 @@ defmodule Pleroma.Web.AdminAPI.MediaProxyCacheView do use Pleroma.Web, :view - def render("index.json", %{urls: urls}) do - %{urls: urls} + def render("index.json", %{urls: urls, page_size: page_size, count: count}) do + %{ + urls: urls, + count: count, + page_size: page_size + } end end diff --git a/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex b/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex index 20d033f66..ab45d6633 100644 --- a/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex +++ b/lib/pleroma/web/api_spec/operations/admin/media_proxy_cache_operation.ex @@ -21,6 +21,12 @@ def index_operation do operationId: "AdminAPI.MediaProxyCacheController.index", security: [%{"oAuth" => ["read:media_proxy_caches"]}], parameters: [ + Operation.parameter( + :query, + :query, + %Schema{type: :string, default: nil}, + "Page" + ), Operation.parameter( :page, :query, @@ -36,7 +42,26 @@ def index_operation do | admin_api_params() ], responses: %{ - 200 => success_response() + 200 => + Operation.response( + "Array of banned MediaProxy URLs in Cachex", + "application/json", + %Schema{ + type: :object, + properties: %{ + count: %Schema{type: :integer}, + page_size: %Schema{type: :integer}, + urls: %Schema{ + type: :array, + items: %Schema{ + type: :string, + format: :uri, + description: "MediaProxy URLs" + } + } + } + } + ) } } end @@ -61,7 +86,7 @@ def delete_operation do required: true ), responses: %{ - 200 => success_response(), + 200 => empty_object_response(), 400 => Operation.response("Error", "application/json", ApiError) } } @@ -88,25 +113,9 @@ def purge_operation do required: true ), responses: %{ - 200 => success_response(), + 200 => empty_object_response(), 400 => Operation.response("Error", "application/json", ApiError) } } end - - defp success_response do - Operation.response("Array of banned MediaProxy URLs in Cachex", "application/json", %Schema{ - type: :object, - properties: %{ - urls: %Schema{ - type: :array, - items: %Schema{ - type: :string, - format: :uri, - description: "MediaProxy URLs" - } - } - } - }) - end end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index c08e0ffeb..a8141b28f 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -6,9 +6,7 @@ defmodule Pleroma.Web.CommonAPI do alias Pleroma.Activity alias Pleroma.ActivityExpiration alias Pleroma.Conversation.Participation - alias Pleroma.FollowingRelationship alias Pleroma.Formatter - alias Pleroma.Notification alias Pleroma.Object alias Pleroma.ThreadMute alias Pleroma.User @@ -122,33 +120,16 @@ def unfollow(follower, unfollowed) do def accept_follow_request(follower, followed) do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), - {:ok, follower} <- User.follow(follower, followed), - {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "accept"), - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_accept), - {:ok, _activity} <- - ActivityPub.accept(%{ - to: [follower.ap_id], - actor: followed, - object: follow_activity.data["id"], - type: "Accept" - }) do - Notification.update_notification_type(followed, follow_activity) + {:ok, accept_data, _} <- Builder.accept(followed, follow_activity), + {:ok, _activity, _} <- Pipeline.common_pipeline(accept_data, local: true) do {:ok, follower} end end def reject_follow_request(follower, followed) do with %Activity{} = follow_activity <- Utils.fetch_latest_follow(follower, followed), - {:ok, follow_activity} <- Utils.update_follow_state_for_all(follow_activity, "reject"), - {:ok, _relationship} <- FollowingRelationship.update(follower, followed, :follow_reject), - {:ok, _notifications} <- Notification.dismiss(follow_activity), - {:ok, _activity} <- - ActivityPub.reject(%{ - to: [follower.ap_id], - actor: followed, - object: follow_activity.data["id"], - type: "Reject" - }) do + {:ok, reject_data, _} <- Builder.reject(followed, follow_activity), + {:ok, _activity, _} <- Pipeline.common_pipeline(reject_data, local: true) do {:ok, follower} end end diff --git a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex index ab7b1d6aa..9244316ed 100644 --- a/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex +++ b/lib/pleroma/web/mastodon_api/controllers/timeline_controller.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.MastodonAPI.TimelineController do import Pleroma.Web.ControllerHelper, only: [add_link_headers: 2, add_link_headers: 3] + alias Pleroma.Config alias Pleroma.Pagination alias Pleroma.Plugs.EnsurePublicOrAuthenticatedPlug alias Pleroma.Plugs.OAuthScopesPlug @@ -89,11 +90,11 @@ def direct(%{assigns: %{user: user}} = conn, params) do end defp restrict_unauthenticated?(true = _local_only) do - Pleroma.Config.get([:restrict_unauthenticated, :timelines, :local]) + Config.restrict_unauthenticated_access?(:timelines, :local) end defp restrict_unauthenticated?(_) do - Pleroma.Config.get([:restrict_unauthenticated, :timelines, :federated]) + Config.restrict_unauthenticated_access?(:timelines, :federated) end # GET /api/v1/timelines/public diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex index 217c3f922..6695d49ce 100644 --- a/lib/pleroma/web/media_proxy/media_proxy.ex +++ b/lib/pleroma/web/media_proxy/media_proxy.ex @@ -9,28 +9,31 @@ defmodule Pleroma.Web.MediaProxy do alias Pleroma.Web.MediaProxy.Invalidation @base64_opts [padding: false] + @cache_table :banned_urls_cache + + def cache_table, do: @cache_table @spec in_banned_urls(String.t()) :: boolean() - def in_banned_urls(url), do: elem(Cachex.exists?(:banned_urls_cache, url(url)), 1) + def in_banned_urls(url), do: elem(Cachex.exists?(@cache_table, url(url)), 1) def remove_from_banned_urls(urls) when is_list(urls) do - Cachex.execute!(:banned_urls_cache, fn cache -> + Cachex.execute!(@cache_table, fn cache -> Enum.each(Invalidation.prepare_urls(urls), &Cachex.del(cache, &1)) end) end def remove_from_banned_urls(url) when is_binary(url) do - Cachex.del(:banned_urls_cache, url(url)) + Cachex.del(@cache_table, url(url)) end def put_in_banned_urls(urls) when is_list(urls) do - Cachex.execute!(:banned_urls_cache, fn cache -> + Cachex.execute!(@cache_table, fn cache -> Enum.each(Invalidation.prepare_urls(urls), &Cachex.put(cache, &1, true)) end) end def put_in_banned_urls(url) when is_binary(url) do - Cachex.put(:banned_urls_cache, url(url), true) + Cachex.put(@cache_table, url(url), true) end def url(url) when is_nil(url) or url == "", do: nil diff --git a/lib/pleroma/web/preload/timelines.ex b/lib/pleroma/web/preload/timelines.ex index 57de04051..b279a865d 100644 --- a/lib/pleroma/web/preload/timelines.ex +++ b/lib/pleroma/web/preload/timelines.ex @@ -16,7 +16,7 @@ def generate_terms(params) do end def build_public_tag(acc, params) do - if Pleroma.Config.get([:restrict_unauthenticated, :timelines, :federated], true) do + if Pleroma.Config.restrict_unauthenticated_access?(:timelines, :federated) do acc else Map.put(acc, @public_url, public_timeline(params)) diff --git a/lib/pleroma/workers/cron/clear_oauth_token_worker.ex b/lib/pleroma/workers/cron/clear_oauth_token_worker.ex index d41be4e87..276f47efc 100644 --- a/lib/pleroma/workers/cron/clear_oauth_token_worker.ex +++ b/lib/pleroma/workers/cron/clear_oauth_token_worker.ex @@ -16,8 +16,8 @@ defmodule Pleroma.Workers.Cron.ClearOauthTokenWorker do def perform(_job) do if Config.get([:oauth2, :clean_expired_tokens], false) do Token.delete_expired_tokens() - else - :ok end + + :ok end end diff --git a/lib/pleroma/workers/cron/digest_emails_worker.ex b/lib/pleroma/workers/cron/digest_emails_worker.ex index ee646229f..0c56f00fb 100644 --- a/lib/pleroma/workers/cron/digest_emails_worker.ex +++ b/lib/pleroma/workers/cron/digest_emails_worker.ex @@ -37,9 +37,9 @@ def perform(_job) do ) |> Repo.all() |> send_emails - else - :ok end + + :ok end def send_emails(users) do diff --git a/lib/pleroma/workers/cron/new_users_digest_worker.ex b/lib/pleroma/workers/cron/new_users_digest_worker.ex index abc8a5e95..8bbaed83d 100644 --- a/lib/pleroma/workers/cron/new_users_digest_worker.ex +++ b/lib/pleroma/workers/cron/new_users_digest_worker.ex @@ -55,11 +55,9 @@ def perform(_job) do |> Repo.all() |> Enum.map(&Pleroma.Emails.NewUsersDigestEmail.new_users(&1, users_and_statuses)) |> Enum.each(&Pleroma.Emails.Mailer.deliver/1) - else - :ok end - else - :ok end + + :ok end end diff --git a/lib/pleroma/workers/cron/purge_expired_activities_worker.ex b/lib/pleroma/workers/cron/purge_expired_activities_worker.ex index e926c5dc8..6549207fc 100644 --- a/lib/pleroma/workers/cron/purge_expired_activities_worker.ex +++ b/lib/pleroma/workers/cron/purge_expired_activities_worker.ex @@ -23,9 +23,9 @@ defmodule Pleroma.Workers.Cron.PurgeExpiredActivitiesWorker do def perform(_job) do if Config.get([ActivityExpiration, :enabled]) do Enum.each(ActivityExpiration.due_expirations(@interval), &delete_activity/1) - else - :ok end + after + :ok end def delete_activity(%ActivityExpiration{activity_id: activity_id}) do @@ -41,7 +41,7 @@ def delete_activity(%ActivityExpiration{activity_id: activity_id}) do {:user, _} -> Logger.error( - "#{__MODULE__} Couldn't delete expired activity: not found actorof ##{activity_id}" + "#{__MODULE__} Couldn't delete expired activity: not found actor of ##{activity_id}" ) end end diff --git a/lib/pleroma/workers/cron/stats_worker.ex b/lib/pleroma/workers/cron/stats_worker.ex index e54bd9a7f..6a79540bc 100644 --- a/lib/pleroma/workers/cron/stats_worker.ex +++ b/lib/pleroma/workers/cron/stats_worker.ex @@ -12,5 +12,6 @@ defmodule Pleroma.Workers.Cron.StatsWorker do @impl Oban.Worker def perform(_job) do Pleroma.Stats.do_collect() + :ok end end diff --git a/priv/repo/migrations/20200808173046_only_expire_creates.exs b/priv/repo/migrations/20200808173046_only_expire_creates.exs new file mode 100644 index 000000000..9df52956f --- /dev/null +++ b/priv/repo/migrations/20200808173046_only_expire_creates.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.OnlyExpireCreates do + use Ecto.Migration + + def up do + statement = """ + DELETE FROM + activity_expirations a_exp USING activities a, objects o + WHERE + a_exp.activity_id = a.id AND (o.data->>'id') = COALESCE(a.data->'object'->>'id', a.data->>'object') + AND (a.data->>'type' != 'Create' OR o.data->>'type' != 'Note'); + """ + + execute(statement) + end + + def down do + :ok + end +end diff --git a/priv/repo/migrations/20200811143147_ap_id_not_null.exs b/priv/repo/migrations/20200811143147_ap_id_not_null.exs new file mode 100644 index 000000000..df649c7ca --- /dev/null +++ b/priv/repo/migrations/20200811143147_ap_id_not_null.exs @@ -0,0 +1,19 @@ +defmodule Pleroma.Repo.Migrations.ApIdNotNull do + use Ecto.Migration + + require Logger + + def up do + Logger.warn( + "If this migration fails please open an issue at https://git.pleroma.social/pleroma/pleroma/-/issues/new \n" + ) + + alter table(:users) do + modify(:ap_id, :string, null: false) + end + end + + def down do + :ok + end +end diff --git a/priv/repo/migrations/20200817120935_add_invisible_index_to_users.exs b/priv/repo/migrations/20200817120935_add_invisible_index_to_users.exs new file mode 100644 index 000000000..2417d366e --- /dev/null +++ b/priv/repo/migrations/20200817120935_add_invisible_index_to_users.exs @@ -0,0 +1,7 @@ +defmodule Pleroma.Repo.Migrations.AddInvisibleIndexToUsers do + use Ecto.Migration + + def change do + create(index(:users, [:invisible])) + end +end diff --git a/priv/static/adminfe/app.01bdb34a.css b/priv/static/adminfe/app.61bb0915.css similarity index 77% rename from priv/static/adminfe/app.01bdb34a.css rename to priv/static/adminfe/app.61bb0915.css index 1b83a8a39..9d74d13dc 100644 Binary files a/priv/static/adminfe/app.01bdb34a.css and b/priv/static/adminfe/app.61bb0915.css differ diff --git a/priv/static/adminfe/chunk-0171.8dc0d9da.css b/priv/static/adminfe/chunk-0171.8dc0d9da.css new file mode 100644 index 000000000..824bddc85 Binary files /dev/null and b/priv/static/adminfe/chunk-0171.8dc0d9da.css differ diff --git a/priv/static/adminfe/chunk-0cbc.60bba79b.css b/priv/static/adminfe/chunk-0cbc.60bba79b.css deleted file mode 100644 index c6280f7ef..000000000 Binary files a/priv/static/adminfe/chunk-0cbc.60bba79b.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-176e.5d7d957b.css b/priv/static/adminfe/chunk-176e.4d21033f.css similarity index 100% rename from priv/static/adminfe/chunk-176e.5d7d957b.css rename to priv/static/adminfe/chunk-176e.4d21033f.css diff --git a/priv/static/adminfe/chunk-143c.43ada4fc.css b/priv/static/adminfe/chunk-2d97.7053ff89.css similarity index 53% rename from priv/static/adminfe/chunk-143c.43ada4fc.css rename to priv/static/adminfe/chunk-2d97.7053ff89.css index b580e0699..f6e28e1fb 100644 Binary files a/priv/static/adminfe/chunk-143c.43ada4fc.css and b/priv/static/adminfe/chunk-2d97.7053ff89.css differ diff --git a/priv/static/adminfe/chunk-40a4.2fe71f6c.css b/priv/static/adminfe/chunk-40a4.2fe71f6c.css new file mode 100644 index 000000000..83fefcb55 Binary files /dev/null and b/priv/static/adminfe/chunk-40a4.2fe71f6c.css differ diff --git a/priv/static/adminfe/chunk-43ca.af749c6c.css b/priv/static/adminfe/chunk-43ca.af749c6c.css deleted file mode 100644 index 504affb93..000000000 Binary files a/priv/static/adminfe/chunk-43ca.af749c6c.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-565e.33809ac8.css b/priv/static/adminfe/chunk-565e.33809ac8.css new file mode 100644 index 000000000..063b0b35d Binary files /dev/null and b/priv/static/adminfe/chunk-565e.33809ac8.css differ diff --git a/priv/static/adminfe/chunk-60a9.a80ec218.css b/priv/static/adminfe/chunk-60a9.a80ec218.css new file mode 100644 index 000000000..d45d79f4c Binary files /dev/null and b/priv/static/adminfe/chunk-60a9.a80ec218.css differ diff --git a/priv/static/adminfe/chunk-1609.408dae86.css b/priv/static/adminfe/chunk-654e.e105ec9c.css similarity index 100% rename from priv/static/adminfe/chunk-1609.408dae86.css rename to priv/static/adminfe/chunk-654e.e105ec9c.css diff --git a/priv/static/adminfe/chunk-070d.d2dd6533.css b/priv/static/adminfe/chunk-68ea.be16aa5f.css similarity index 100% rename from priv/static/adminfe/chunk-070d.d2dd6533.css rename to priv/static/adminfe/chunk-68ea.be16aa5f.css diff --git a/priv/static/adminfe/chunk-6e81.ca3b222f.css b/priv/static/adminfe/chunk-6e81.7f126ac7.css similarity index 100% rename from priv/static/adminfe/chunk-6e81.ca3b222f.css rename to priv/static/adminfe/chunk-6e81.7f126ac7.css diff --git a/priv/static/adminfe/chunk-5882.f65db7f2.css b/priv/static/adminfe/chunk-6e8c.f7407fd4.css similarity index 65% rename from priv/static/adminfe/chunk-5882.f65db7f2.css rename to priv/static/adminfe/chunk-6e8c.f7407fd4.css index b5e2a00b0..6936755b9 100644 Binary files a/priv/static/adminfe/chunk-5882.f65db7f2.css and b/priv/static/adminfe/chunk-6e8c.f7407fd4.css differ diff --git a/priv/static/adminfe/chunk-7506.f01f6c2a.css b/priv/static/adminfe/chunk-7503.c75b68df.css similarity index 100% rename from priv/static/adminfe/chunk-7506.f01f6c2a.css rename to priv/static/adminfe/chunk-7503.c75b68df.css diff --git a/priv/static/adminfe/chunk-7c6b.d9e7180a.css b/priv/static/adminfe/chunk-7c6b.4c8fa90a.css similarity index 100% rename from priv/static/adminfe/chunk-7c6b.d9e7180a.css rename to priv/static/adminfe/chunk-7c6b.4c8fa90a.css diff --git a/priv/static/adminfe/chunk-c5f4.b1112f18.css b/priv/static/adminfe/chunk-97e2.b21a8915.css similarity index 100% rename from priv/static/adminfe/chunk-c5f4.b1112f18.css rename to priv/static/adminfe/chunk-97e2.b21a8915.css diff --git a/priv/static/adminfe/chunk-4e7e.5afe1978.css b/priv/static/adminfe/chunk-9a72.3e577534.css similarity index 100% rename from priv/static/adminfe/chunk-4e7e.5afe1978.css rename to priv/static/adminfe/chunk-9a72.3e577534.css diff --git a/priv/static/adminfe/chunk-commons.7f6d2d11.css b/priv/static/adminfe/chunk-commons.67f053f7.css similarity index 100% rename from priv/static/adminfe/chunk-commons.7f6d2d11.css rename to priv/static/adminfe/chunk-commons.67f053f7.css diff --git a/priv/static/adminfe/chunk-e404.a56021ae.css b/priv/static/adminfe/chunk-e404.a56021ae.css deleted file mode 100644 index 7d8596ef6..000000000 Binary files a/priv/static/adminfe/chunk-e404.a56021ae.css and /dev/null differ diff --git a/priv/static/adminfe/chunk-libs.686b5876.css b/priv/static/adminfe/chunk-libs.5cf7f50a.css similarity index 100% rename from priv/static/adminfe/chunk-libs.686b5876.css rename to priv/static/adminfe/chunk-libs.5cf7f50a.css diff --git a/priv/static/adminfe/index.html b/priv/static/adminfe/index.html index 22b3143d2..5214cc94f 100644 --- a/priv/static/adminfe/index.html +++ b/priv/static/adminfe/index.html @@ -1 +1 @@ -Admin FE
\ No newline at end of file +Admin FE
\ No newline at end of file diff --git a/priv/static/adminfe/static/js/ZhIB.861df339.js b/priv/static/adminfe/static/js/ZhIB.861df339.js deleted file mode 100644 index aeec873c8..000000000 Binary files a/priv/static/adminfe/static/js/ZhIB.861df339.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/ZhIB.861df339.js.map b/priv/static/adminfe/static/js/ZhIB.861df339.js.map deleted file mode 100644 index ff11a2e71..000000000 Binary files a/priv/static/adminfe/static/js/ZhIB.861df339.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.86bfcdf3.js b/priv/static/adminfe/static/js/app.86bfcdf3.js new file mode 100644 index 000000000..083555948 Binary files /dev/null and b/priv/static/adminfe/static/js/app.86bfcdf3.js differ diff --git a/priv/static/adminfe/static/js/app.86bfcdf3.js.map b/priv/static/adminfe/static/js/app.86bfcdf3.js.map new file mode 100644 index 000000000..1e35d9d3d Binary files /dev/null and b/priv/static/adminfe/static/js/app.86bfcdf3.js.map differ diff --git a/priv/static/adminfe/static/js/app.f220ac13.js b/priv/static/adminfe/static/js/app.f220ac13.js deleted file mode 100644 index e5e1eda91..000000000 Binary files a/priv/static/adminfe/static/js/app.f220ac13.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/app.f220ac13.js.map b/priv/static/adminfe/static/js/app.f220ac13.js.map deleted file mode 100644 index 90c22121e..000000000 Binary files a/priv/static/adminfe/static/js/app.f220ac13.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0171.9ad03c0e.js b/priv/static/adminfe/static/js/chunk-0171.9ad03c0e.js new file mode 100644 index 000000000..070fe2201 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0171.9ad03c0e.js differ diff --git a/priv/static/adminfe/static/js/chunk-0171.9ad03c0e.js.map b/priv/static/adminfe/static/js/chunk-0171.9ad03c0e.js.map new file mode 100644 index 000000000..4696152ee Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-0171.9ad03c0e.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js b/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js deleted file mode 100644 index d29070b62..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js.map b/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js.map deleted file mode 100644 index 7c99d9d48..000000000 Binary files a/priv/static/adminfe/static/js/chunk-0cbc.2b0f8802.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-143c.fc1825bf.js b/priv/static/adminfe/static/js/chunk-143c.fc1825bf.js deleted file mode 100644 index 6fbc5b1ed..000000000 Binary files a/priv/static/adminfe/static/js/chunk-143c.fc1825bf.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-143c.fc1825bf.js.map b/priv/static/adminfe/static/js/chunk-143c.fc1825bf.js.map deleted file mode 100644 index 425a7427a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-143c.fc1825bf.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-176e.c4995511.js b/priv/static/adminfe/static/js/chunk-176e.fe016b36.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-176e.c4995511.js rename to priv/static/adminfe/static/js/chunk-176e.fe016b36.js index 80474b904..eb57c5863 100644 Binary files a/priv/static/adminfe/static/js/chunk-176e.c4995511.js and b/priv/static/adminfe/static/js/chunk-176e.fe016b36.js differ diff --git a/priv/static/adminfe/static/js/chunk-176e.c4995511.js.map b/priv/static/adminfe/static/js/chunk-176e.fe016b36.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-176e.c4995511.js.map rename to priv/static/adminfe/static/js/chunk-176e.fe016b36.js.map index f0caa5f62..b3d84706b 100644 Binary files a/priv/static/adminfe/static/js/chunk-176e.c4995511.js.map and b/priv/static/adminfe/static/js/chunk-176e.fe016b36.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-2d97.931fa130.js b/priv/static/adminfe/static/js/chunk-2d97.931fa130.js new file mode 100644 index 000000000..d5ba28881 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-2d97.931fa130.js differ diff --git a/priv/static/adminfe/static/js/chunk-2d97.931fa130.js.map b/priv/static/adminfe/static/js/chunk-2d97.931fa130.js.map new file mode 100644 index 000000000..69c447abc Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-2d97.931fa130.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-40a4.e7e37fc4.js b/priv/static/adminfe/static/js/chunk-40a4.e7e37fc4.js new file mode 100644 index 000000000..7e3de73d2 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-40a4.e7e37fc4.js differ diff --git a/priv/static/adminfe/static/js/chunk-40a4.e7e37fc4.js.map b/priv/static/adminfe/static/js/chunk-40a4.e7e37fc4.js.map new file mode 100644 index 000000000..935c150cc Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-40a4.e7e37fc4.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js b/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js deleted file mode 100644 index f9fcbc288..000000000 Binary files a/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js.map b/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js.map deleted file mode 100644 index 3c71ad178..000000000 Binary files a/priv/static/adminfe/static/js/chunk-43ca.aceb457c.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-565e.32b3b7b0.js b/priv/static/adminfe/static/js/chunk-565e.32b3b7b0.js new file mode 100644 index 000000000..b72017611 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-565e.32b3b7b0.js differ diff --git a/priv/static/adminfe/static/js/chunk-565e.32b3b7b0.js.map b/priv/static/adminfe/static/js/chunk-565e.32b3b7b0.js.map new file mode 100644 index 000000000..a2bc8a3cd Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-565e.32b3b7b0.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-5882.7cbc4c1b.js b/priv/static/adminfe/static/js/chunk-5882.7cbc4c1b.js deleted file mode 100644 index a29b6daab..000000000 Binary files a/priv/static/adminfe/static/js/chunk-5882.7cbc4c1b.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-5882.7cbc4c1b.js.map b/priv/static/adminfe/static/js/chunk-5882.7cbc4c1b.js.map deleted file mode 100644 index d1aa2037f..000000000 Binary files a/priv/static/adminfe/static/js/chunk-5882.7cbc4c1b.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-60a9.15f68a0f.js b/priv/static/adminfe/static/js/chunk-60a9.15f68a0f.js new file mode 100644 index 000000000..7b3e2e46c Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-60a9.15f68a0f.js differ diff --git a/priv/static/adminfe/static/js/chunk-60a9.15f68a0f.js.map b/priv/static/adminfe/static/js/chunk-60a9.15f68a0f.js.map new file mode 100644 index 000000000..a1bd1aa43 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-60a9.15f68a0f.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-1609.98da6b01.js b/priv/static/adminfe/static/js/chunk-654e.d523dfc3.js similarity index 98% rename from priv/static/adminfe/static/js/chunk-1609.98da6b01.js rename to priv/static/adminfe/static/js/chunk-654e.d523dfc3.js index 29dbad261..44c2c61c8 100644 Binary files a/priv/static/adminfe/static/js/chunk-1609.98da6b01.js and b/priv/static/adminfe/static/js/chunk-654e.d523dfc3.js differ diff --git a/priv/static/adminfe/static/js/chunk-1609.98da6b01.js.map b/priv/static/adminfe/static/js/chunk-654e.d523dfc3.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-1609.98da6b01.js.map rename to priv/static/adminfe/static/js/chunk-654e.d523dfc3.js.map index f287a503a..00f04d1d4 100644 Binary files a/priv/static/adminfe/static/js/chunk-1609.98da6b01.js.map and b/priv/static/adminfe/static/js/chunk-654e.d523dfc3.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-070d.7e10a520.js b/priv/static/adminfe/static/js/chunk-68ea.a283cad8.js similarity index 94% rename from priv/static/adminfe/static/js/chunk-070d.7e10a520.js rename to priv/static/adminfe/static/js/chunk-68ea.a283cad8.js index 8726dbcd3..bb7cbff96 100644 Binary files a/priv/static/adminfe/static/js/chunk-070d.7e10a520.js and b/priv/static/adminfe/static/js/chunk-68ea.a283cad8.js differ diff --git a/priv/static/adminfe/static/js/chunk-070d.7e10a520.js.map b/priv/static/adminfe/static/js/chunk-68ea.a283cad8.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-070d.7e10a520.js.map rename to priv/static/adminfe/static/js/chunk-68ea.a283cad8.js.map index 6b75a215e..201d8eaa9 100644 Binary files a/priv/static/adminfe/static/js/chunk-070d.7e10a520.js.map and b/priv/static/adminfe/static/js/chunk-68ea.a283cad8.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.6efb01f4.js b/priv/static/adminfe/static/js/chunk-6e81.b4ee7cf5.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-6e81.6efb01f4.js rename to priv/static/adminfe/static/js/chunk-6e81.b4ee7cf5.js index f40d31879..32ede5eff 100644 Binary files a/priv/static/adminfe/static/js/chunk-6e81.6efb01f4.js and b/priv/static/adminfe/static/js/chunk-6e81.b4ee7cf5.js differ diff --git a/priv/static/adminfe/static/js/chunk-6e81.6efb01f4.js.map b/priv/static/adminfe/static/js/chunk-6e81.b4ee7cf5.js.map similarity index 98% rename from priv/static/adminfe/static/js/chunk-6e81.6efb01f4.js.map rename to priv/static/adminfe/static/js/chunk-6e81.b4ee7cf5.js.map index 0390c3309..7301b6957 100644 Binary files a/priv/static/adminfe/static/js/chunk-6e81.6efb01f4.js.map and b/priv/static/adminfe/static/js/chunk-6e81.b4ee7cf5.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-6e8c.46fda72d.js b/priv/static/adminfe/static/js/chunk-6e8c.46fda72d.js new file mode 100644 index 000000000..f6175a4b5 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-6e8c.46fda72d.js differ diff --git a/priv/static/adminfe/static/js/chunk-6e8c.46fda72d.js.map b/priv/static/adminfe/static/js/chunk-6e8c.46fda72d.js.map new file mode 100644 index 000000000..159876ea9 Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-6e8c.46fda72d.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7503.ee7af549.js b/priv/static/adminfe/static/js/chunk-7503.ee7af549.js new file mode 100644 index 000000000..6126d904d Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7503.ee7af549.js differ diff --git a/priv/static/adminfe/static/js/chunk-7503.ee7af549.js.map b/priv/static/adminfe/static/js/chunk-7503.ee7af549.js.map new file mode 100644 index 000000000..cf893c61f Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-7503.ee7af549.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-7506.a3364e53.js b/priv/static/adminfe/static/js/chunk-7506.a3364e53.js deleted file mode 100644 index d4eaa356a..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7506.a3364e53.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7506.a3364e53.js.map b/priv/static/adminfe/static/js/chunk-7506.a3364e53.js.map deleted file mode 100644 index c8e9db8e0..000000000 Binary files a/priv/static/adminfe/static/js/chunk-7506.a3364e53.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.e63ae1da.js b/priv/static/adminfe/static/js/chunk-7c6b.7c4844a9.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-7c6b.e63ae1da.js rename to priv/static/adminfe/static/js/chunk-7c6b.7c4844a9.js index 27478ddb1..a349860a8 100644 Binary files a/priv/static/adminfe/static/js/chunk-7c6b.e63ae1da.js and b/priv/static/adminfe/static/js/chunk-7c6b.7c4844a9.js differ diff --git a/priv/static/adminfe/static/js/chunk-7c6b.e63ae1da.js.map b/priv/static/adminfe/static/js/chunk-7c6b.7c4844a9.js.map similarity index 98% rename from priv/static/adminfe/static/js/chunk-7c6b.e63ae1da.js.map rename to priv/static/adminfe/static/js/chunk-7c6b.7c4844a9.js.map index 2114a3c52..632e5750e 100644 Binary files a/priv/static/adminfe/static/js/chunk-7c6b.e63ae1da.js.map and b/priv/static/adminfe/static/js/chunk-7c6b.7c4844a9.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js b/priv/static/adminfe/static/js/chunk-97e2.5baa6e73.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js rename to priv/static/adminfe/static/js/chunk-97e2.5baa6e73.js index 2d5308031..a3b706d5d 100644 Binary files a/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js and b/priv/static/adminfe/static/js/chunk-97e2.5baa6e73.js differ diff --git a/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js.map b/priv/static/adminfe/static/js/chunk-97e2.5baa6e73.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js.map rename to priv/static/adminfe/static/js/chunk-97e2.5baa6e73.js.map index d5fc047ee..b7a392337 100644 Binary files a/priv/static/adminfe/static/js/chunk-c5f4.cf269f9b.js.map and b/priv/static/adminfe/static/js/chunk-97e2.5baa6e73.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-4e7e.91b5e73a.js b/priv/static/adminfe/static/js/chunk-9a72.7b2fc06e.js similarity index 97% rename from priv/static/adminfe/static/js/chunk-4e7e.91b5e73a.js rename to priv/static/adminfe/static/js/chunk-9a72.7b2fc06e.js index 0fdf0de50..0dc8e9b68 100644 Binary files a/priv/static/adminfe/static/js/chunk-4e7e.91b5e73a.js and b/priv/static/adminfe/static/js/chunk-9a72.7b2fc06e.js differ diff --git a/priv/static/adminfe/static/js/chunk-4e7e.91b5e73a.js.map b/priv/static/adminfe/static/js/chunk-9a72.7b2fc06e.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-4e7e.91b5e73a.js.map rename to priv/static/adminfe/static/js/chunk-9a72.7b2fc06e.js.map index 7a6751cf8..c351b689e 100644 Binary files a/priv/static/adminfe/static/js/chunk-4e7e.91b5e73a.js.map and b/priv/static/adminfe/static/js/chunk-9a72.7b2fc06e.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-commons.5a106955.js b/priv/static/adminfe/static/js/chunk-commons.38728553.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-commons.5a106955.js rename to priv/static/adminfe/static/js/chunk-commons.38728553.js index a6cf2ce52..0f2ffce9f 100644 Binary files a/priv/static/adminfe/static/js/chunk-commons.5a106955.js and b/priv/static/adminfe/static/js/chunk-commons.38728553.js differ diff --git a/priv/static/adminfe/static/js/chunk-commons.5a106955.js.map b/priv/static/adminfe/static/js/chunk-commons.38728553.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-commons.5a106955.js.map rename to priv/static/adminfe/static/js/chunk-commons.38728553.js.map index d924490e5..048f21e43 100644 Binary files a/priv/static/adminfe/static/js/chunk-commons.5a106955.js.map and b/priv/static/adminfe/static/js/chunk-commons.38728553.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-e404.554bc2e3.js b/priv/static/adminfe/static/js/chunk-e404.554bc2e3.js deleted file mode 100644 index 769e9f4f9..000000000 Binary files a/priv/static/adminfe/static/js/chunk-e404.554bc2e3.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-e404.554bc2e3.js.map b/priv/static/adminfe/static/js/chunk-e404.554bc2e3.js.map deleted file mode 100644 index e8214adbb..000000000 Binary files a/priv/static/adminfe/static/js/chunk-e404.554bc2e3.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.fba0efec.js b/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js similarity index 99% rename from priv/static/adminfe/static/js/chunk-elementUI.fba0efec.js rename to priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js index f106931f5..c76b0430b 100644 Binary files a/priv/static/adminfe/static/js/chunk-elementUI.fba0efec.js and b/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js differ diff --git a/priv/static/adminfe/static/js/chunk-elementUI.fba0efec.js.map b/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js.map similarity index 99% rename from priv/static/adminfe/static/js/chunk-elementUI.fba0efec.js.map rename to priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js.map index a1f726fde..fa9dc12f0 100644 Binary files a/priv/static/adminfe/static/js/chunk-elementUI.fba0efec.js.map and b/priv/static/adminfe/static/js/chunk-elementUI.2de79b84.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-libs.b8c453ab.js b/priv/static/adminfe/static/js/chunk-libs.76802be9.js similarity index 80% rename from priv/static/adminfe/static/js/chunk-libs.b8c453ab.js rename to priv/static/adminfe/static/js/chunk-libs.76802be9.js index 2af35eb62..984b5ad40 100644 Binary files a/priv/static/adminfe/static/js/chunk-libs.b8c453ab.js and b/priv/static/adminfe/static/js/chunk-libs.76802be9.js differ diff --git a/priv/static/adminfe/static/js/chunk-libs.76802be9.js.map b/priv/static/adminfe/static/js/chunk-libs.76802be9.js.map new file mode 100644 index 000000000..d4680796a Binary files /dev/null and b/priv/static/adminfe/static/js/chunk-libs.76802be9.js.map differ diff --git a/priv/static/adminfe/static/js/chunk-libs.b8c453ab.js.map b/priv/static/adminfe/static/js/chunk-libs.b8c453ab.js.map deleted file mode 100644 index 9c9d9acde..000000000 Binary files a/priv/static/adminfe/static/js/chunk-libs.b8c453ab.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.0a70a9f5.js b/priv/static/adminfe/static/js/runtime.0a70a9f5.js deleted file mode 100644 index a99d1d369..000000000 Binary files a/priv/static/adminfe/static/js/runtime.0a70a9f5.js and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.0a70a9f5.js.map b/priv/static/adminfe/static/js/runtime.0a70a9f5.js.map deleted file mode 100644 index 62e726b22..000000000 Binary files a/priv/static/adminfe/static/js/runtime.0a70a9f5.js.map and /dev/null differ diff --git a/priv/static/adminfe/static/js/runtime.ba9393f3.js b/priv/static/adminfe/static/js/runtime.ba9393f3.js new file mode 100644 index 000000000..c66462ab6 Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.ba9393f3.js differ diff --git a/priv/static/adminfe/static/js/runtime.ba9393f3.js.map b/priv/static/adminfe/static/js/runtime.ba9393f3.js.map new file mode 100644 index 000000000..c167edf90 Binary files /dev/null and b/priv/static/adminfe/static/js/runtime.ba9393f3.js.map differ diff --git a/priv/static/index.html b/priv/static/index.html index 2257dec35..7dd080b2d 100644 --- a/priv/static/index.html +++ b/priv/static/index.html @@ -1 +1 @@ -Pleroma
\ No newline at end of file +Pleroma
\ No newline at end of file diff --git a/priv/static/static/css/app.6dbc7dea4fc148c85860.css b/priv/static/static/css/app.77b1644622e3bae24b6b.css similarity index 99% rename from priv/static/static/css/app.6dbc7dea4fc148c85860.css rename to priv/static/static/css/app.77b1644622e3bae24b6b.css index 3927e3b77..8038882c0 100644 Binary files a/priv/static/static/css/app.6dbc7dea4fc148c85860.css and b/priv/static/static/css/app.77b1644622e3bae24b6b.css differ diff --git a/priv/static/static/css/app.6dbc7dea4fc148c85860.css.map b/priv/static/static/css/app.77b1644622e3bae24b6b.css.map similarity index 99% rename from priv/static/static/css/app.6dbc7dea4fc148c85860.css.map rename to priv/static/static/css/app.77b1644622e3bae24b6b.css.map index 963d5b3b8..4b042ef35 100644 --- a/priv/static/static/css/app.6dbc7dea4fc148c85860.css.map +++ b/priv/static/static/css/app.77b1644622e3bae24b6b.css.map @@ -1 +1 @@ -{"version":3,"sources":["webpack:///./src/components/tab_switcher/tab_switcher.scss","webpack:///./src/hocs/with_load_more/with_load_more.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;ACtOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"static/css/app.6dbc7dea4fc148c85860.css","sourcesContent":[".tab-switcher {\n display: -ms-flexbox;\n display: flex;\n}\n.tab-switcher .tab-icon {\n font-size: 2em;\n display: block;\n}\n.tab-switcher.top-tabs {\n -ms-flex-direction: column;\n flex-direction: column;\n}\n.tab-switcher.top-tabs > .tabs {\n width: 100%;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 5px;\n -ms-flex-direction: row;\n flex-direction: row;\n}\n.tab-switcher.top-tabs > .tabs::after, .tab-switcher.top-tabs > .tabs::before {\n content: \"\";\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher.top-tabs > .tabs .tab-wrapper {\n height: 28px;\n}\n.tab-switcher.top-tabs > .tabs .tab-wrapper:not(.active)::after {\n left: 0;\n right: 0;\n bottom: 0;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher.top-tabs > .tabs .tab {\n width: 100%;\n min-width: 1px;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n padding-bottom: 99px;\n margin-bottom: -93px;\n}\n.tab-switcher.top-tabs .contents.scrollable-tabs {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n}\n.tab-switcher.side-tabs {\n -ms-flex-direction: row;\n flex-direction: row;\n}\n@media all and (max-width: 800px) {\n .tab-switcher.side-tabs {\n overflow-x: auto;\n }\n}\n.tab-switcher.side-tabs > .contents {\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n}\n.tab-switcher.side-tabs > .tabs {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n overflow-y: auto;\n overflow-x: hidden;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n.tab-switcher.side-tabs > .tabs::after, .tab-switcher.side-tabs > .tabs::before {\n -ms-flex-negative: 0;\n flex-shrink: 0;\n -ms-flex-preferred-size: 0.5em;\n flex-basis: 0.5em;\n content: \"\";\n border-right: 1px solid;\n border-right-color: #222;\n border-right-color: var(--border, #222);\n}\n.tab-switcher.side-tabs > .tabs::after {\n -ms-flex-positive: 1;\n flex-grow: 1;\n}\n.tab-switcher.side-tabs > .tabs::before {\n -ms-flex-positive: 0;\n flex-grow: 0;\n}\n.tab-switcher.side-tabs > .tabs .tab-wrapper {\n min-width: 10em;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n@media all and (max-width: 800px) {\n .tab-switcher.side-tabs > .tabs .tab-wrapper {\n min-width: 1em;\n }\n}\n.tab-switcher.side-tabs > .tabs .tab-wrapper:not(.active)::after {\n top: 0;\n right: 0;\n bottom: 0;\n border-right: 1px solid;\n border-right-color: #222;\n border-right-color: var(--border, #222);\n}\n.tab-switcher.side-tabs > .tabs .tab-wrapper::before {\n -ms-flex: 0 0 6px;\n flex: 0 0 6px;\n content: \"\";\n border-right: 1px solid;\n border-right-color: #222;\n border-right-color: var(--border, #222);\n}\n.tab-switcher.side-tabs > .tabs .tab-wrapper:last-child .tab {\n margin-bottom: 0;\n}\n.tab-switcher.side-tabs > .tabs .tab {\n -ms-flex: 1;\n flex: 1;\n box-sizing: content-box;\n min-width: 10em;\n min-width: 1px;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n padding-left: 1em;\n padding-right: calc(1em + 200px);\n margin-right: -200px;\n margin-left: 1em;\n}\n@media all and (max-width: 800px) {\n .tab-switcher.side-tabs > .tabs .tab {\n padding-left: 0.25em;\n padding-right: calc(.25em + 200px);\n margin-right: calc(.25em - 200px);\n margin-left: 0.25em;\n }\n .tab-switcher.side-tabs > .tabs .tab .text {\n display: none;\n }\n}\n.tab-switcher .contents {\n -ms-flex: 1 0 auto;\n flex: 1 0 auto;\n min-height: 0px;\n}\n.tab-switcher .contents .hidden {\n display: none;\n}\n.tab-switcher .contents .full-height:not(.hidden) {\n height: 100%;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n.tab-switcher .contents .full-height:not(.hidden) > *:not(.mobile-label) {\n -ms-flex: 1;\n flex: 1;\n}\n.tab-switcher .contents.scrollable-tabs {\n overflow-y: auto;\n}\n.tab-switcher .tab {\n position: relative;\n white-space: nowrap;\n padding: 6px 1em;\n background-color: #182230;\n background-color: var(--tab, #182230);\n}\n.tab-switcher .tab, .tab-switcher .tab:active .tab-icon {\n color: #b9b9ba;\n color: var(--tabText, #b9b9ba);\n}\n.tab-switcher .tab:not(.active) {\n z-index: 4;\n}\n.tab-switcher .tab:not(.active):hover {\n z-index: 6;\n}\n.tab-switcher .tab.active {\n background: transparent;\n z-index: 5;\n color: #b9b9ba;\n color: var(--tabActiveText, #b9b9ba);\n}\n.tab-switcher .tab img {\n max-height: 26px;\n vertical-align: top;\n margin-top: -5px;\n}\n.tab-switcher .tabs {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n box-sizing: border-box;\n}\n.tab-switcher .tabs::after, .tab-switcher .tabs::before {\n display: block;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n}\n.tab-switcher .tab-wrapper {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n}\n.tab-switcher .tab-wrapper:not(.active)::after {\n content: \"\";\n position: absolute;\n z-index: 7;\n}\n.tab-switcher .mobile-label {\n padding-left: 0.3em;\n padding-bottom: 0.25em;\n margin-top: 0.5em;\n margin-left: 0.2em;\n margin-bottom: 0.25em;\n border-bottom: 1px solid var(--border, #222);\n}\n@media all and (min-width: 800px) {\n .tab-switcher .mobile-label {\n display: none;\n }\n}",".with-load-more-footer {\n padding: 10px;\n text-align: center;\n border-top: 1px solid;\n border-top-color: #222;\n border-top-color: var(--border, #222);\n}\n.with-load-more-footer .error {\n font-size: 14px;\n}\n.with-load-more-footer a {\n cursor: pointer;\n}"],"sourceRoot":""} \ No newline at end of file +{"version":3,"sources":["webpack:///./src/components/tab_switcher/tab_switcher.scss","webpack:///./src/hocs/with_load_more/with_load_more.scss"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C;ACtOA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,C","file":"static/css/app.77b1644622e3bae24b6b.css","sourcesContent":[".tab-switcher {\n display: -ms-flexbox;\n display: flex;\n}\n.tab-switcher .tab-icon {\n font-size: 2em;\n display: block;\n}\n.tab-switcher.top-tabs {\n -ms-flex-direction: column;\n flex-direction: column;\n}\n.tab-switcher.top-tabs > .tabs {\n width: 100%;\n overflow-y: hidden;\n overflow-x: auto;\n padding-top: 5px;\n -ms-flex-direction: row;\n flex-direction: row;\n}\n.tab-switcher.top-tabs > .tabs::after, .tab-switcher.top-tabs > .tabs::before {\n content: \"\";\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher.top-tabs > .tabs .tab-wrapper {\n height: 28px;\n}\n.tab-switcher.top-tabs > .tabs .tab-wrapper:not(.active)::after {\n left: 0;\n right: 0;\n bottom: 0;\n border-bottom: 1px solid;\n border-bottom-color: #222;\n border-bottom-color: var(--border, #222);\n}\n.tab-switcher.top-tabs > .tabs .tab {\n width: 100%;\n min-width: 1px;\n border-bottom-left-radius: 0;\n border-bottom-right-radius: 0;\n padding-bottom: 99px;\n margin-bottom: -93px;\n}\n.tab-switcher.top-tabs .contents.scrollable-tabs {\n -ms-flex-preferred-size: 0;\n flex-basis: 0;\n}\n.tab-switcher.side-tabs {\n -ms-flex-direction: row;\n flex-direction: row;\n}\n@media all and (max-width: 800px) {\n .tab-switcher.side-tabs {\n overflow-x: auto;\n }\n}\n.tab-switcher.side-tabs > .contents {\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n}\n.tab-switcher.side-tabs > .tabs {\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n overflow-y: auto;\n overflow-x: hidden;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n.tab-switcher.side-tabs > .tabs::after, .tab-switcher.side-tabs > .tabs::before {\n -ms-flex-negative: 0;\n flex-shrink: 0;\n -ms-flex-preferred-size: 0.5em;\n flex-basis: 0.5em;\n content: \"\";\n border-right: 1px solid;\n border-right-color: #222;\n border-right-color: var(--border, #222);\n}\n.tab-switcher.side-tabs > .tabs::after {\n -ms-flex-positive: 1;\n flex-grow: 1;\n}\n.tab-switcher.side-tabs > .tabs::before {\n -ms-flex-positive: 0;\n flex-grow: 0;\n}\n.tab-switcher.side-tabs > .tabs .tab-wrapper {\n min-width: 10em;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n@media all and (max-width: 800px) {\n .tab-switcher.side-tabs > .tabs .tab-wrapper {\n min-width: 1em;\n }\n}\n.tab-switcher.side-tabs > .tabs .tab-wrapper:not(.active)::after {\n top: 0;\n right: 0;\n bottom: 0;\n border-right: 1px solid;\n border-right-color: #222;\n border-right-color: var(--border, #222);\n}\n.tab-switcher.side-tabs > .tabs .tab-wrapper::before {\n -ms-flex: 0 0 6px;\n flex: 0 0 6px;\n content: \"\";\n border-right: 1px solid;\n border-right-color: #222;\n border-right-color: var(--border, #222);\n}\n.tab-switcher.side-tabs > .tabs .tab-wrapper:last-child .tab {\n margin-bottom: 0;\n}\n.tab-switcher.side-tabs > .tabs .tab {\n -ms-flex: 1;\n flex: 1;\n box-sizing: content-box;\n min-width: 10em;\n min-width: 1px;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n padding-left: 1em;\n padding-right: calc(1em + 200px);\n margin-right: -200px;\n margin-left: 1em;\n}\n@media all and (max-width: 800px) {\n .tab-switcher.side-tabs > .tabs .tab {\n padding-left: 0.25em;\n padding-right: calc(.25em + 200px);\n margin-right: calc(.25em - 200px);\n margin-left: 0.25em;\n }\n .tab-switcher.side-tabs > .tabs .tab .text {\n display: none;\n }\n}\n.tab-switcher .contents {\n -ms-flex: 1 0 auto;\n flex: 1 0 auto;\n min-height: 0px;\n}\n.tab-switcher .contents .hidden {\n display: none;\n}\n.tab-switcher .contents .full-height:not(.hidden) {\n height: 100%;\n display: -ms-flexbox;\n display: flex;\n -ms-flex-direction: column;\n flex-direction: column;\n}\n.tab-switcher .contents .full-height:not(.hidden) > *:not(.mobile-label) {\n -ms-flex: 1;\n flex: 1;\n}\n.tab-switcher .contents.scrollable-tabs {\n overflow-y: auto;\n}\n.tab-switcher .tab {\n position: relative;\n white-space: nowrap;\n padding: 6px 1em;\n background-color: #182230;\n background-color: var(--tab, #182230);\n}\n.tab-switcher .tab, .tab-switcher .tab:active .tab-icon {\n color: #b9b9ba;\n color: var(--tabText, #b9b9ba);\n}\n.tab-switcher .tab:not(.active) {\n z-index: 4;\n}\n.tab-switcher .tab:not(.active):hover {\n z-index: 6;\n}\n.tab-switcher .tab.active {\n background: transparent;\n z-index: 5;\n color: #b9b9ba;\n color: var(--tabActiveText, #b9b9ba);\n}\n.tab-switcher .tab img {\n max-height: 26px;\n vertical-align: top;\n margin-top: -5px;\n}\n.tab-switcher .tabs {\n display: -ms-flexbox;\n display: flex;\n position: relative;\n box-sizing: border-box;\n}\n.tab-switcher .tabs::after, .tab-switcher .tabs::before {\n display: block;\n -ms-flex: 1 1 auto;\n flex: 1 1 auto;\n}\n.tab-switcher .tab-wrapper {\n position: relative;\n display: -ms-flexbox;\n display: flex;\n -ms-flex: 0 0 auto;\n flex: 0 0 auto;\n}\n.tab-switcher .tab-wrapper:not(.active)::after {\n content: \"\";\n position: absolute;\n z-index: 7;\n}\n.tab-switcher .mobile-label {\n padding-left: 0.3em;\n padding-bottom: 0.25em;\n margin-top: 0.5em;\n margin-left: 0.2em;\n margin-bottom: 0.25em;\n border-bottom: 1px solid var(--border, #222);\n}\n@media all and (min-width: 800px) {\n .tab-switcher .mobile-label {\n display: none;\n }\n}",".with-load-more-footer {\n padding: 10px;\n text-align: center;\n border-top: 1px solid;\n border-top-color: #222;\n border-top-color: var(--border, #222);\n}\n.with-load-more-footer .error {\n font-size: 14px;\n}\n.with-load-more-footer a {\n cursor: pointer;\n}"],"sourceRoot":""} \ No newline at end of file diff --git a/priv/static/static/font/fontello.1594823398494.woff2 b/priv/static/static/font/fontello.1594823398494.woff2 deleted file mode 100644 index c61bf111a..000000000 Binary files a/priv/static/static/font/fontello.1594823398494.woff2 and /dev/null differ diff --git a/priv/static/static/font/fontello.1594823398494.eot b/priv/static/static/font/fontello.1597327457363.eot similarity index 99% rename from priv/static/static/font/fontello.1594823398494.eot rename to priv/static/static/font/fontello.1597327457363.eot index 12e6beabf..af2c39275 100644 Binary files a/priv/static/static/font/fontello.1594823398494.eot and b/priv/static/static/font/fontello.1597327457363.eot differ diff --git a/priv/static/static/font/fontello.1594823398494.svg b/priv/static/static/font/fontello.1597327457363.svg similarity index 100% rename from priv/static/static/font/fontello.1594823398494.svg rename to priv/static/static/font/fontello.1597327457363.svg diff --git a/priv/static/static/font/fontello.1594823398494.ttf b/priv/static/static/font/fontello.1597327457363.ttf similarity index 99% rename from priv/static/static/font/fontello.1594823398494.ttf rename to priv/static/static/font/fontello.1597327457363.ttf index 6f21845a8..1d5640d5d 100644 Binary files a/priv/static/static/font/fontello.1594823398494.ttf and b/priv/static/static/font/fontello.1597327457363.ttf differ diff --git a/priv/static/static/font/fontello.1594823398494.woff b/priv/static/static/font/fontello.1597327457363.woff similarity index 98% rename from priv/static/static/font/fontello.1594823398494.woff rename to priv/static/static/font/fontello.1597327457363.woff index a7cd098f4..c04735bf5 100644 Binary files a/priv/static/static/font/fontello.1594823398494.woff and b/priv/static/static/font/fontello.1597327457363.woff differ diff --git a/priv/static/static/font/fontello.1597327457363.woff2 b/priv/static/static/font/fontello.1597327457363.woff2 new file mode 100644 index 000000000..f53414761 Binary files /dev/null and b/priv/static/static/font/fontello.1597327457363.woff2 differ diff --git a/priv/static/static/fontello.1597327457363.css b/priv/static/static/fontello.1597327457363.css new file mode 100644 index 000000000..22d148873 Binary files /dev/null and b/priv/static/static/fontello.1597327457363.css differ diff --git a/priv/static/static/js/10.5ef4671883649cf93524.js b/priv/static/static/js/10.8c5b75840b696a152c7e.js similarity index 99% rename from priv/static/static/js/10.5ef4671883649cf93524.js rename to priv/static/static/js/10.8c5b75840b696a152c7e.js index 6819c854b..eb95d66d1 100644 Binary files a/priv/static/static/js/10.5ef4671883649cf93524.js and b/priv/static/static/js/10.8c5b75840b696a152c7e.js differ diff --git a/priv/static/static/js/10.5ef4671883649cf93524.js.map b/priv/static/static/js/10.8c5b75840b696a152c7e.js.map similarity index 56% rename from priv/static/static/js/10.5ef4671883649cf93524.js.map rename to priv/static/static/js/10.8c5b75840b696a152c7e.js.map index 95fa2207e..b390fbeaf 100644 Binary files a/priv/static/static/js/10.5ef4671883649cf93524.js.map and b/priv/static/static/js/10.8c5b75840b696a152c7e.js.map differ diff --git a/priv/static/static/js/11.c5b938b4349f87567338.js b/priv/static/static/js/11.bfcde1c26c4d54b84ee4.js similarity index 99% rename from priv/static/static/js/11.c5b938b4349f87567338.js rename to priv/static/static/js/11.bfcde1c26c4d54b84ee4.js index b97f69bf3..0dea63f5a 100644 Binary files a/priv/static/static/js/11.c5b938b4349f87567338.js and b/priv/static/static/js/11.bfcde1c26c4d54b84ee4.js differ diff --git a/priv/static/static/js/11.c5b938b4349f87567338.js.map b/priv/static/static/js/11.bfcde1c26c4d54b84ee4.js.map similarity index 56% rename from priv/static/static/js/11.c5b938b4349f87567338.js.map rename to priv/static/static/js/11.bfcde1c26c4d54b84ee4.js.map index 5ccf83b1d..2b2305773 100644 Binary files a/priv/static/static/js/11.c5b938b4349f87567338.js.map and b/priv/static/static/js/11.bfcde1c26c4d54b84ee4.js.map differ diff --git a/priv/static/static/js/12.76095ee23394e0ef65bb.js b/priv/static/static/js/12.76095ee23394e0ef65bb.js new file mode 100644 index 000000000..6c34e2da2 Binary files /dev/null and b/priv/static/static/js/12.76095ee23394e0ef65bb.js differ diff --git a/priv/static/static/js/12.ab82f9512fa85e78c114.js.map b/priv/static/static/js/12.76095ee23394e0ef65bb.js.map similarity index 56% rename from priv/static/static/js/12.ab82f9512fa85e78c114.js.map rename to priv/static/static/js/12.76095ee23394e0ef65bb.js.map index 23335ae23..e00137a2b 100644 Binary files a/priv/static/static/js/12.ab82f9512fa85e78c114.js.map and b/priv/static/static/js/12.76095ee23394e0ef65bb.js.map differ diff --git a/priv/static/static/js/12.ab82f9512fa85e78c114.js b/priv/static/static/js/12.ab82f9512fa85e78c114.js deleted file mode 100644 index 100d72b33..000000000 Binary files a/priv/static/static/js/12.ab82f9512fa85e78c114.js and /dev/null differ diff --git a/priv/static/static/js/13.40e59c5015d3307b94ad.js b/priv/static/static/js/13.40e59c5015d3307b94ad.js deleted file mode 100644 index 2088bb6b7..000000000 Binary files a/priv/static/static/js/13.40e59c5015d3307b94ad.js and /dev/null differ diff --git a/priv/static/static/js/13.957b04ac11d6cde66f5b.js b/priv/static/static/js/13.957b04ac11d6cde66f5b.js new file mode 100644 index 000000000..917b6a58b Binary files /dev/null and b/priv/static/static/js/13.957b04ac11d6cde66f5b.js differ diff --git a/priv/static/static/js/13.40e59c5015d3307b94ad.js.map b/priv/static/static/js/13.957b04ac11d6cde66f5b.js.map similarity index 56% rename from priv/static/static/js/13.40e59c5015d3307b94ad.js.map rename to priv/static/static/js/13.957b04ac11d6cde66f5b.js.map index 3931b5ef9..25434f73b 100644 Binary files a/priv/static/static/js/13.40e59c5015d3307b94ad.js.map and b/priv/static/static/js/13.957b04ac11d6cde66f5b.js.map differ diff --git a/priv/static/static/js/14.de791a47ee5249a526b1.js b/priv/static/static/js/14.aae5a904931591edfaa7.js similarity index 54% rename from priv/static/static/js/14.de791a47ee5249a526b1.js rename to priv/static/static/js/14.aae5a904931591edfaa7.js index 0e341275e..001914ad7 100644 Binary files a/priv/static/static/js/14.de791a47ee5249a526b1.js and b/priv/static/static/js/14.aae5a904931591edfaa7.js differ diff --git a/priv/static/static/js/14.aae5a904931591edfaa7.js.map b/priv/static/static/js/14.aae5a904931591edfaa7.js.map new file mode 100644 index 000000000..24719fee8 Binary files /dev/null and b/priv/static/static/js/14.aae5a904931591edfaa7.js.map differ diff --git a/priv/static/static/js/14.de791a47ee5249a526b1.js.map b/priv/static/static/js/14.de791a47ee5249a526b1.js.map deleted file mode 100644 index 4bef54546..000000000 Binary files a/priv/static/static/js/14.de791a47ee5249a526b1.js.map and /dev/null differ diff --git a/priv/static/static/js/15.139f5de3950adc3b66df.js b/priv/static/static/js/15.139f5de3950adc3b66df.js new file mode 100644 index 000000000..303e00130 Binary files /dev/null and b/priv/static/static/js/15.139f5de3950adc3b66df.js differ diff --git a/priv/static/static/js/15.139f5de3950adc3b66df.js.map b/priv/static/static/js/15.139f5de3950adc3b66df.js.map new file mode 100644 index 000000000..d5a3c800d Binary files /dev/null and b/priv/static/static/js/15.139f5de3950adc3b66df.js.map differ diff --git a/priv/static/static/js/15.e24854297ad682aec45a.js b/priv/static/static/js/15.e24854297ad682aec45a.js deleted file mode 100644 index 671370192..000000000 Binary files a/priv/static/static/js/15.e24854297ad682aec45a.js and /dev/null differ diff --git a/priv/static/static/js/15.e24854297ad682aec45a.js.map b/priv/static/static/js/15.e24854297ad682aec45a.js.map deleted file mode 100644 index 89789a542..000000000 Binary files a/priv/static/static/js/15.e24854297ad682aec45a.js.map and /dev/null differ diff --git a/priv/static/static/js/16.7b8466d62084c04f6671.js b/priv/static/static/js/16.7b8466d62084c04f6671.js new file mode 100644 index 000000000..587b41dd0 Binary files /dev/null and b/priv/static/static/js/16.7b8466d62084c04f6671.js differ diff --git a/priv/static/static/js/16.7b8466d62084c04f6671.js.map b/priv/static/static/js/16.7b8466d62084c04f6671.js.map new file mode 100644 index 000000000..22818639f Binary files /dev/null and b/priv/static/static/js/16.7b8466d62084c04f6671.js.map differ diff --git a/priv/static/static/js/16.b7b0e4b8227a50fcb9bb.js b/priv/static/static/js/16.b7b0e4b8227a50fcb9bb.js deleted file mode 100644 index 6a3ea9513..000000000 Binary files a/priv/static/static/js/16.b7b0e4b8227a50fcb9bb.js and /dev/null differ diff --git a/priv/static/static/js/16.b7b0e4b8227a50fcb9bb.js.map b/priv/static/static/js/16.b7b0e4b8227a50fcb9bb.js.map deleted file mode 100644 index fec45b087..000000000 Binary files a/priv/static/static/js/16.b7b0e4b8227a50fcb9bb.js.map and /dev/null differ diff --git a/priv/static/static/js/17.c98118b6bb84ee3b5b08.js.map b/priv/static/static/js/17.c98118b6bb84ee3b5b08.js.map deleted file mode 100644 index 0c20fc89b..000000000 Binary files a/priv/static/static/js/17.c98118b6bb84ee3b5b08.js.map and /dev/null differ diff --git a/priv/static/static/js/17.c98118b6bb84ee3b5b08.js b/priv/static/static/js/17.e8ec1f5666cb4e28784a.js similarity index 94% rename from priv/static/static/js/17.c98118b6bb84ee3b5b08.js rename to priv/static/static/js/17.e8ec1f5666cb4e28784a.js index c41f0b6b8..03a7d28e5 100644 Binary files a/priv/static/static/js/17.c98118b6bb84ee3b5b08.js and b/priv/static/static/js/17.e8ec1f5666cb4e28784a.js differ diff --git a/priv/static/static/js/17.e8ec1f5666cb4e28784a.js.map b/priv/static/static/js/17.e8ec1f5666cb4e28784a.js.map new file mode 100644 index 000000000..0fe92287e Binary files /dev/null and b/priv/static/static/js/17.e8ec1f5666cb4e28784a.js.map differ diff --git a/priv/static/static/js/18.89c20aa67a4dd067ea37.js.map b/priv/static/static/js/18.89c20aa67a4dd067ea37.js.map deleted file mode 100644 index 72cdf0e0e..000000000 Binary files a/priv/static/static/js/18.89c20aa67a4dd067ea37.js.map and /dev/null differ diff --git a/priv/static/static/js/18.89c20aa67a4dd067ea37.js b/priv/static/static/js/18.d32389579b85948022b8.js similarity index 57% rename from priv/static/static/js/18.89c20aa67a4dd067ea37.js rename to priv/static/static/js/18.d32389579b85948022b8.js index db1c78a49..477f6e06f 100644 Binary files a/priv/static/static/js/18.89c20aa67a4dd067ea37.js and b/priv/static/static/js/18.d32389579b85948022b8.js differ diff --git a/priv/static/static/js/18.d32389579b85948022b8.js.map b/priv/static/static/js/18.d32389579b85948022b8.js.map new file mode 100644 index 000000000..62fc5b84f Binary files /dev/null and b/priv/static/static/js/18.d32389579b85948022b8.js.map differ diff --git a/priv/static/static/js/19.6e13bad8131c4501c1c5.js b/priv/static/static/js/19.6e13bad8131c4501c1c5.js deleted file mode 100644 index 8b32827cc..000000000 Binary files a/priv/static/static/js/19.6e13bad8131c4501c1c5.js and /dev/null differ diff --git a/priv/static/static/js/19.6e13bad8131c4501c1c5.js.map b/priv/static/static/js/19.6e13bad8131c4501c1c5.js.map deleted file mode 100644 index 762d85e27..000000000 Binary files a/priv/static/static/js/19.6e13bad8131c4501c1c5.js.map and /dev/null differ diff --git a/priv/static/static/js/19.d180c594b843c17c80fa.js b/priv/static/static/js/19.d180c594b843c17c80fa.js new file mode 100644 index 000000000..c30dc75c2 Binary files /dev/null and b/priv/static/static/js/19.d180c594b843c17c80fa.js differ diff --git a/priv/static/static/js/19.d180c594b843c17c80fa.js.map b/priv/static/static/js/19.d180c594b843c17c80fa.js.map new file mode 100644 index 000000000..e90081dd9 Binary files /dev/null and b/priv/static/static/js/19.d180c594b843c17c80fa.js.map differ diff --git a/priv/static/static/js/2.5ecefab707beea40b7f0.js b/priv/static/static/js/2.5ecefab707beea40b7f0.js new file mode 100644 index 000000000..bf563c79f Binary files /dev/null and b/priv/static/static/js/2.5ecefab707beea40b7f0.js differ diff --git a/priv/static/static/js/2.5ecefab707beea40b7f0.js.map b/priv/static/static/js/2.5ecefab707beea40b7f0.js.map new file mode 100644 index 000000000..7452e1f6e Binary files /dev/null and b/priv/static/static/js/2.5ecefab707beea40b7f0.js.map differ diff --git a/priv/static/static/js/2.78a48aa26599b00c3b8d.js b/priv/static/static/js/2.78a48aa26599b00c3b8d.js deleted file mode 100644 index ecb27aa9c..000000000 Binary files a/priv/static/static/js/2.78a48aa26599b00c3b8d.js and /dev/null differ diff --git a/priv/static/static/js/2.78a48aa26599b00c3b8d.js.map b/priv/static/static/js/2.78a48aa26599b00c3b8d.js.map deleted file mode 100644 index 167cfa1c6..000000000 Binary files a/priv/static/static/js/2.78a48aa26599b00c3b8d.js.map and /dev/null differ diff --git a/priv/static/static/js/20.3615c3cea2e1c2707a4f.js b/priv/static/static/js/20.27e04f2209628de3092b.js similarity index 99% rename from priv/static/static/js/20.3615c3cea2e1c2707a4f.js rename to priv/static/static/js/20.27e04f2209628de3092b.js index 74f89016c..e41b60066 100644 Binary files a/priv/static/static/js/20.3615c3cea2e1c2707a4f.js and b/priv/static/static/js/20.27e04f2209628de3092b.js differ diff --git a/priv/static/static/js/20.27e04f2209628de3092b.js.map b/priv/static/static/js/20.27e04f2209628de3092b.js.map new file mode 100644 index 000000000..4009ef5b9 Binary files /dev/null and b/priv/static/static/js/20.27e04f2209628de3092b.js.map differ diff --git a/priv/static/static/js/20.3615c3cea2e1c2707a4f.js.map b/priv/static/static/js/20.3615c3cea2e1c2707a4f.js.map deleted file mode 100644 index acddecea7..000000000 Binary files a/priv/static/static/js/20.3615c3cea2e1c2707a4f.js.map and /dev/null differ diff --git a/priv/static/static/js/21.64dedfc646e13e6f7915.js b/priv/static/static/js/21.641aba6f96885c381070.js similarity index 99% rename from priv/static/static/js/21.64dedfc646e13e6f7915.js rename to priv/static/static/js/21.641aba6f96885c381070.js index 407e6665e..d80f64e11 100644 Binary files a/priv/static/static/js/21.64dedfc646e13e6f7915.js and b/priv/static/static/js/21.641aba6f96885c381070.js differ diff --git a/priv/static/static/js/21.641aba6f96885c381070.js.map b/priv/static/static/js/21.641aba6f96885c381070.js.map new file mode 100644 index 000000000..8f6253113 Binary files /dev/null and b/priv/static/static/js/21.641aba6f96885c381070.js.map differ diff --git a/priv/static/static/js/21.64dedfc646e13e6f7915.js.map b/priv/static/static/js/21.64dedfc646e13e6f7915.js.map deleted file mode 100644 index 8e3432668..000000000 Binary files a/priv/static/static/js/21.64dedfc646e13e6f7915.js.map and /dev/null differ diff --git a/priv/static/static/js/22.6fa63bc6a054b7638e9e.js.map b/priv/static/static/js/22.6fa63bc6a054b7638e9e.js.map deleted file mode 100644 index 1c556f040..000000000 Binary files a/priv/static/static/js/22.6fa63bc6a054b7638e9e.js.map and /dev/null differ diff --git a/priv/static/static/js/22.6fa63bc6a054b7638e9e.js b/priv/static/static/js/22.cbe4790c7601004ed96f.js similarity index 99% rename from priv/static/static/js/22.6fa63bc6a054b7638e9e.js rename to priv/static/static/js/22.cbe4790c7601004ed96f.js index 4a8740c99..0e9c6ab97 100644 Binary files a/priv/static/static/js/22.6fa63bc6a054b7638e9e.js and b/priv/static/static/js/22.cbe4790c7601004ed96f.js differ diff --git a/priv/static/static/js/22.cbe4790c7601004ed96f.js.map b/priv/static/static/js/22.cbe4790c7601004ed96f.js.map new file mode 100644 index 000000000..8de20817c Binary files /dev/null and b/priv/static/static/js/22.cbe4790c7601004ed96f.js.map differ diff --git a/priv/static/static/js/23.e0ddea2b6e049d221ee7.js b/priv/static/static/js/23.96b5bf8d37de3bf02a17.js similarity index 99% rename from priv/static/static/js/23.e0ddea2b6e049d221ee7.js rename to priv/static/static/js/23.96b5bf8d37de3bf02a17.js index 51fe36368..6a78c71fd 100644 Binary files a/priv/static/static/js/23.e0ddea2b6e049d221ee7.js and b/priv/static/static/js/23.96b5bf8d37de3bf02a17.js differ diff --git a/priv/static/static/js/23.96b5bf8d37de3bf02a17.js.map b/priv/static/static/js/23.96b5bf8d37de3bf02a17.js.map new file mode 100644 index 000000000..12929720a Binary files /dev/null and b/priv/static/static/js/23.96b5bf8d37de3bf02a17.js.map differ diff --git a/priv/static/static/js/23.e0ddea2b6e049d221ee7.js.map b/priv/static/static/js/23.e0ddea2b6e049d221ee7.js.map deleted file mode 100644 index 36bae2bf4..000000000 Binary files a/priv/static/static/js/23.e0ddea2b6e049d221ee7.js.map and /dev/null differ diff --git a/priv/static/static/js/24.38e3b9d44e9ee703ebf6.js.map b/priv/static/static/js/24.38e3b9d44e9ee703ebf6.js.map deleted file mode 100644 index 09f3c19d0..000000000 Binary files a/priv/static/static/js/24.38e3b9d44e9ee703ebf6.js.map and /dev/null differ diff --git a/priv/static/static/js/24.38e3b9d44e9ee703ebf6.js b/priv/static/static/js/24.5e5eea3542b0e17c6479.js similarity index 99% rename from priv/static/static/js/24.38e3b9d44e9ee703ebf6.js rename to priv/static/static/js/24.5e5eea3542b0e17c6479.js index e5abf0af6..45787dddd 100644 Binary files a/priv/static/static/js/24.38e3b9d44e9ee703ebf6.js and b/priv/static/static/js/24.5e5eea3542b0e17c6479.js differ diff --git a/priv/static/static/js/24.5e5eea3542b0e17c6479.js.map b/priv/static/static/js/24.5e5eea3542b0e17c6479.js.map new file mode 100644 index 000000000..1938ee57a Binary files /dev/null and b/priv/static/static/js/24.5e5eea3542b0e17c6479.js.map differ diff --git a/priv/static/static/js/25.696b41c0a8660e1f85af.js b/priv/static/static/js/25.696b41c0a8660e1f85af.js deleted file mode 100644 index b114890fc..000000000 Binary files a/priv/static/static/js/25.696b41c0a8660e1f85af.js and /dev/null differ diff --git a/priv/static/static/js/25.696b41c0a8660e1f85af.js.map b/priv/static/static/js/25.696b41c0a8660e1f85af.js.map deleted file mode 100644 index f6d208812..000000000 Binary files a/priv/static/static/js/25.696b41c0a8660e1f85af.js.map and /dev/null differ diff --git a/priv/static/static/js/25.dd8471a33b5a4d256564.js b/priv/static/static/js/25.dd8471a33b5a4d256564.js new file mode 100644 index 000000000..b30f01f9b Binary files /dev/null and b/priv/static/static/js/25.dd8471a33b5a4d256564.js differ diff --git a/priv/static/static/js/25.dd8471a33b5a4d256564.js.map b/priv/static/static/js/25.dd8471a33b5a4d256564.js.map new file mode 100644 index 000000000..e6a6bf3a0 Binary files /dev/null and b/priv/static/static/js/25.dd8471a33b5a4d256564.js.map differ diff --git a/priv/static/static/js/26.1168f22384be75dc5492.js.map b/priv/static/static/js/26.1168f22384be75dc5492.js.map deleted file mode 100644 index c9b0d8495..000000000 Binary files a/priv/static/static/js/26.1168f22384be75dc5492.js.map and /dev/null differ diff --git a/priv/static/static/js/26.1168f22384be75dc5492.js b/priv/static/static/js/26.91a9c2effdd1a423a79f.js similarity index 99% rename from priv/static/static/js/26.1168f22384be75dc5492.js rename to priv/static/static/js/26.91a9c2effdd1a423a79f.js index b77a4d30f..f30ff939a 100644 Binary files a/priv/static/static/js/26.1168f22384be75dc5492.js and b/priv/static/static/js/26.91a9c2effdd1a423a79f.js differ diff --git a/priv/static/static/js/26.91a9c2effdd1a423a79f.js.map b/priv/static/static/js/26.91a9c2effdd1a423a79f.js.map new file mode 100644 index 000000000..ae4781108 Binary files /dev/null and b/priv/static/static/js/26.91a9c2effdd1a423a79f.js.map differ diff --git a/priv/static/static/js/27.3c0cfbb2a898b35486dd.js.map b/priv/static/static/js/27.3c0cfbb2a898b35486dd.js.map deleted file mode 100644 index 0cc5f46b2..000000000 Binary files a/priv/static/static/js/27.3c0cfbb2a898b35486dd.js.map and /dev/null differ diff --git a/priv/static/static/js/27.3c0cfbb2a898b35486dd.js b/priv/static/static/js/27.949d608895f6e29a2fc2.js similarity index 94% rename from priv/static/static/js/27.3c0cfbb2a898b35486dd.js rename to priv/static/static/js/27.949d608895f6e29a2fc2.js index a0765356f..f735c1a04 100644 Binary files a/priv/static/static/js/27.3c0cfbb2a898b35486dd.js and b/priv/static/static/js/27.949d608895f6e29a2fc2.js differ diff --git a/priv/static/static/js/27.949d608895f6e29a2fc2.js.map b/priv/static/static/js/27.949d608895f6e29a2fc2.js.map new file mode 100644 index 000000000..9f75161dd Binary files /dev/null and b/priv/static/static/js/27.949d608895f6e29a2fc2.js.map differ diff --git a/priv/static/static/js/28.926c71d6f1813e177271.js b/priv/static/static/js/28.1e879ccb6222c26ee837.js similarity index 99% rename from priv/static/static/js/28.926c71d6f1813e177271.js rename to priv/static/static/js/28.1e879ccb6222c26ee837.js index 55cf840f2..64e286799 100644 Binary files a/priv/static/static/js/28.926c71d6f1813e177271.js and b/priv/static/static/js/28.1e879ccb6222c26ee837.js differ diff --git a/priv/static/static/js/28.1e879ccb6222c26ee837.js.map b/priv/static/static/js/28.1e879ccb6222c26ee837.js.map new file mode 100644 index 000000000..123aae91b Binary files /dev/null and b/priv/static/static/js/28.1e879ccb6222c26ee837.js.map differ diff --git a/priv/static/static/js/28.926c71d6f1813e177271.js.map b/priv/static/static/js/28.926c71d6f1813e177271.js.map deleted file mode 100644 index 1ae8f08cb..000000000 Binary files a/priv/static/static/js/28.926c71d6f1813e177271.js.map and /dev/null differ diff --git a/priv/static/static/js/29.187064ebed099ae45749.js.map b/priv/static/static/js/29.187064ebed099ae45749.js.map deleted file mode 100644 index b5dd63f96..000000000 Binary files a/priv/static/static/js/29.187064ebed099ae45749.js.map and /dev/null differ diff --git a/priv/static/static/js/29.187064ebed099ae45749.js b/priv/static/static/js/29.a0eb0eee98462dc00d86.js similarity index 99% rename from priv/static/static/js/29.187064ebed099ae45749.js rename to priv/static/static/js/29.a0eb0eee98462dc00d86.js index 6eaae0226..740e150ca 100644 Binary files a/priv/static/static/js/29.187064ebed099ae45749.js and b/priv/static/static/js/29.a0eb0eee98462dc00d86.js differ diff --git a/priv/static/static/js/29.a0eb0eee98462dc00d86.js.map b/priv/static/static/js/29.a0eb0eee98462dc00d86.js.map new file mode 100644 index 000000000..357679d53 Binary files /dev/null and b/priv/static/static/js/29.a0eb0eee98462dc00d86.js.map differ diff --git a/priv/static/static/js/3.56898c1005d9ba1b8d4a.js b/priv/static/static/js/3.44ee95fa34170fe38ef7.js similarity index 99% rename from priv/static/static/js/3.56898c1005d9ba1b8d4a.js rename to priv/static/static/js/3.44ee95fa34170fe38ef7.js index 6b20ecb04..ad2b9294c 100644 Binary files a/priv/static/static/js/3.56898c1005d9ba1b8d4a.js and b/priv/static/static/js/3.44ee95fa34170fe38ef7.js differ diff --git a/priv/static/static/js/3.56898c1005d9ba1b8d4a.js.map b/priv/static/static/js/3.44ee95fa34170fe38ef7.js.map similarity index 99% rename from priv/static/static/js/3.56898c1005d9ba1b8d4a.js.map rename to priv/static/static/js/3.44ee95fa34170fe38ef7.js.map index 594d9047b..7efe5d6a5 100644 Binary files a/priv/static/static/js/3.56898c1005d9ba1b8d4a.js.map and b/priv/static/static/js/3.44ee95fa34170fe38ef7.js.map differ diff --git a/priv/static/static/js/30.d78855ca19bf749be905.js b/priv/static/static/js/30.73f0507f6b66caa1b632.js similarity index 99% rename from priv/static/static/js/30.d78855ca19bf749be905.js rename to priv/static/static/js/30.73f0507f6b66caa1b632.js index 9202d19d3..0f1beeb58 100644 Binary files a/priv/static/static/js/30.d78855ca19bf749be905.js and b/priv/static/static/js/30.73f0507f6b66caa1b632.js differ diff --git a/priv/static/static/js/30.73f0507f6b66caa1b632.js.map b/priv/static/static/js/30.73f0507f6b66caa1b632.js.map new file mode 100644 index 000000000..e73f818cd Binary files /dev/null and b/priv/static/static/js/30.73f0507f6b66caa1b632.js.map differ diff --git a/priv/static/static/js/30.d78855ca19bf749be905.js.map b/priv/static/static/js/30.d78855ca19bf749be905.js.map deleted file mode 100644 index b9f39664d..000000000 Binary files a/priv/static/static/js/30.d78855ca19bf749be905.js.map and /dev/null differ diff --git a/priv/static/static/js/4.2d3bef896b463484e6eb.js b/priv/static/static/js/4.77639012e321d98c064c.js similarity index 80% rename from priv/static/static/js/4.2d3bef896b463484e6eb.js rename to priv/static/static/js/4.77639012e321d98c064c.js index 4a611feb4..e8d35a81d 100644 Binary files a/priv/static/static/js/4.2d3bef896b463484e6eb.js and b/priv/static/static/js/4.77639012e321d98c064c.js differ diff --git a/priv/static/static/js/4.2d3bef896b463484e6eb.js.map b/priv/static/static/js/4.77639012e321d98c064c.js.map similarity index 99% rename from priv/static/static/js/4.2d3bef896b463484e6eb.js.map rename to priv/static/static/js/4.77639012e321d98c064c.js.map index ebcc883e5..1a0373e08 100644 Binary files a/priv/static/static/js/4.2d3bef896b463484e6eb.js.map and b/priv/static/static/js/4.77639012e321d98c064c.js.map differ diff --git a/priv/static/static/js/5.84f3dce298bc720719c7.js b/priv/static/static/js/5.abcc811ac6e85e621b0d.js similarity index 98% rename from priv/static/static/js/5.84f3dce298bc720719c7.js rename to priv/static/static/js/5.abcc811ac6e85e621b0d.js index 242b2a525..1575d2a95 100644 Binary files a/priv/static/static/js/5.84f3dce298bc720719c7.js and b/priv/static/static/js/5.abcc811ac6e85e621b0d.js differ diff --git a/priv/static/static/js/5.84f3dce298bc720719c7.js.map b/priv/static/static/js/5.abcc811ac6e85e621b0d.js.map similarity index 57% rename from priv/static/static/js/5.84f3dce298bc720719c7.js.map rename to priv/static/static/js/5.abcc811ac6e85e621b0d.js.map index 4fcc32982..03251d1d8 100644 Binary files a/priv/static/static/js/5.84f3dce298bc720719c7.js.map and b/priv/static/static/js/5.abcc811ac6e85e621b0d.js.map differ diff --git a/priv/static/static/js/6.389907251866808cf2c4.js b/priv/static/static/js/6.389907251866808cf2c4.js new file mode 100644 index 000000000..def098eda Binary files /dev/null and b/priv/static/static/js/6.389907251866808cf2c4.js differ diff --git a/priv/static/static/js/6.b9497e1d403b901a664e.js.map b/priv/static/static/js/6.389907251866808cf2c4.js.map similarity index 57% rename from priv/static/static/js/6.b9497e1d403b901a664e.js.map rename to priv/static/static/js/6.389907251866808cf2c4.js.map index 5af0576c7..7b96d2998 100644 Binary files a/priv/static/static/js/6.b9497e1d403b901a664e.js.map and b/priv/static/static/js/6.389907251866808cf2c4.js.map differ diff --git a/priv/static/static/js/6.b9497e1d403b901a664e.js b/priv/static/static/js/6.b9497e1d403b901a664e.js deleted file mode 100644 index 8c66e9062..000000000 Binary files a/priv/static/static/js/6.b9497e1d403b901a664e.js and /dev/null differ diff --git a/priv/static/static/js/7.455b574116ce3f004ffb.js b/priv/static/static/js/7.33e3cc5c9abab3f21825.js similarity index 65% rename from priv/static/static/js/7.455b574116ce3f004ffb.js rename to priv/static/static/js/7.33e3cc5c9abab3f21825.js index 0e35f6904..6a4e332e9 100644 Binary files a/priv/static/static/js/7.455b574116ce3f004ffb.js and b/priv/static/static/js/7.33e3cc5c9abab3f21825.js differ diff --git a/priv/static/static/js/7.455b574116ce3f004ffb.js.map b/priv/static/static/js/7.33e3cc5c9abab3f21825.js.map similarity index 57% rename from priv/static/static/js/7.455b574116ce3f004ffb.js.map rename to priv/static/static/js/7.33e3cc5c9abab3f21825.js.map index 971b570e1..a04c36f4c 100644 Binary files a/priv/static/static/js/7.455b574116ce3f004ffb.js.map and b/priv/static/static/js/7.33e3cc5c9abab3f21825.js.map differ diff --git a/priv/static/static/js/8.5e0b07052c330e85bead.js b/priv/static/static/js/8.5e0b07052c330e85bead.js new file mode 100644 index 000000000..7fd0ec5a1 Binary files /dev/null and b/priv/static/static/js/8.5e0b07052c330e85bead.js differ diff --git a/priv/static/static/js/8.8db9f2dcc5ed429777f7.js.map b/priv/static/static/js/8.5e0b07052c330e85bead.js.map similarity index 57% rename from priv/static/static/js/8.8db9f2dcc5ed429777f7.js.map rename to priv/static/static/js/8.5e0b07052c330e85bead.js.map index e193375de..d324ed4b0 100644 Binary files a/priv/static/static/js/8.8db9f2dcc5ed429777f7.js.map and b/priv/static/static/js/8.5e0b07052c330e85bead.js.map differ diff --git a/priv/static/static/js/8.8db9f2dcc5ed429777f7.js b/priv/static/static/js/8.8db9f2dcc5ed429777f7.js deleted file mode 100644 index 79082fc7c..000000000 Binary files a/priv/static/static/js/8.8db9f2dcc5ed429777f7.js and /dev/null differ diff --git a/priv/static/static/js/9.da3973d058660aa9612f.js b/priv/static/static/js/9.da3973d058660aa9612f.js deleted file mode 100644 index 50d977f67..000000000 Binary files a/priv/static/static/js/9.da3973d058660aa9612f.js and /dev/null differ diff --git a/priv/static/static/js/9.da3973d058660aa9612f.js.map b/priv/static/static/js/9.da3973d058660aa9612f.js.map deleted file mode 100644 index 4c8f70599..000000000 Binary files a/priv/static/static/js/9.da3973d058660aa9612f.js.map and /dev/null differ diff --git a/priv/static/static/js/9.f8e3aa590f4a66aedc3f.js b/priv/static/static/js/9.f8e3aa590f4a66aedc3f.js new file mode 100644 index 000000000..353737ab0 Binary files /dev/null and b/priv/static/static/js/9.f8e3aa590f4a66aedc3f.js differ diff --git a/priv/static/static/js/9.f8e3aa590f4a66aedc3f.js.map b/priv/static/static/js/9.f8e3aa590f4a66aedc3f.js.map new file mode 100644 index 000000000..452afcc41 Binary files /dev/null and b/priv/static/static/js/9.f8e3aa590f4a66aedc3f.js.map differ diff --git a/priv/static/static/js/app.032cb80dafd1f208df1c.js b/priv/static/static/js/app.032cb80dafd1f208df1c.js new file mode 100644 index 000000000..c4b099811 Binary files /dev/null and b/priv/static/static/js/app.032cb80dafd1f208df1c.js differ diff --git a/priv/static/static/js/app.032cb80dafd1f208df1c.js.map b/priv/static/static/js/app.032cb80dafd1f208df1c.js.map new file mode 100644 index 000000000..397fbfbe8 Binary files /dev/null and b/priv/static/static/js/app.032cb80dafd1f208df1c.js.map differ diff --git a/priv/static/static/js/app.31bba9f1e242ff273dcb.js b/priv/static/static/js/app.31bba9f1e242ff273dcb.js deleted file mode 100644 index 22413689c..000000000 Binary files a/priv/static/static/js/app.31bba9f1e242ff273dcb.js and /dev/null differ diff --git a/priv/static/static/js/app.31bba9f1e242ff273dcb.js.map b/priv/static/static/js/app.31bba9f1e242ff273dcb.js.map deleted file mode 100644 index 8ff7a00c6..000000000 Binary files a/priv/static/static/js/app.31bba9f1e242ff273dcb.js.map and /dev/null differ diff --git a/priv/static/static/js/vendors~app.9e24ed238da5a8538f50.js b/priv/static/static/js/vendors~app.811c8482146cad566f7e.js similarity index 92% rename from priv/static/static/js/vendors~app.9e24ed238da5a8538f50.js rename to priv/static/static/js/vendors~app.811c8482146cad566f7e.js index 76c8a8dc1..c2114925d 100644 Binary files a/priv/static/static/js/vendors~app.9e24ed238da5a8538f50.js and b/priv/static/static/js/vendors~app.811c8482146cad566f7e.js differ diff --git a/priv/static/static/js/vendors~app.9e24ed238da5a8538f50.js.map b/priv/static/static/js/vendors~app.811c8482146cad566f7e.js.map similarity index 99% rename from priv/static/static/js/vendors~app.9e24ed238da5a8538f50.js.map rename to priv/static/static/js/vendors~app.811c8482146cad566f7e.js.map index f3c067c15..858078059 100644 Binary files a/priv/static/static/js/vendors~app.9e24ed238da5a8538f50.js.map and b/priv/static/static/js/vendors~app.811c8482146cad566f7e.js.map differ diff --git a/priv/static/sw-pleroma.js b/priv/static/sw-pleroma.js index 9b7d127fd..5aabeb744 100644 Binary files a/priv/static/sw-pleroma.js and b/priv/static/sw-pleroma.js differ diff --git a/priv/static/sw-pleroma.js.map b/priv/static/sw-pleroma.js.map index 5749809d5..20dac11d0 100644 Binary files a/priv/static/sw-pleroma.js.map and b/priv/static/sw-pleroma.js.map differ diff --git a/test/activity_expiration_test.exs b/test/activity_expiration_test.exs index d75c06cc7..f86d79826 100644 --- a/test/activity_expiration_test.exs +++ b/test/activity_expiration_test.exs @@ -11,7 +11,10 @@ defmodule Pleroma.ActivityExpirationTest do test "finds activities due to be deleted only" do activity = insert(:note_activity) - expiration_due = insert(:expiration_in_the_past, %{activity_id: activity.id}) + + expiration_due = + insert(:expiration_in_the_past, %{activity_id: activity.id}) |> Repo.preload(:activity) + activity2 = insert(:note_activity) insert(:expiration_in_the_future, %{activity_id: activity2.id}) diff --git a/test/user_test.exs b/test/user_test.exs index b47405895..3cf248659 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1417,7 +1417,6 @@ test "deactivates user when activation is not required", %{user: user} do test "delete/1 when approval is pending deletes the user" do user = insert(:user, approval_pending: true) - {:ok, user: user} {:ok, job} = User.delete(user) {:ok, _} = ObanHelpers.perform(job) @@ -1426,6 +1425,85 @@ test "delete/1 when approval is pending deletes the user" do refute User.get_by_id(user.id) end + test "delete/1 purges a user when they wouldn't be fully deleted" do + user = + insert(:user, %{ + bio: "eyy lmao", + name: "qqqqqqq", + password_hash: "pdfk2$1b3n159001", + keys: "RSA begin buplic key", + public_key: "--PRIVATE KEYE--", + avatar: %{"a" => "b"}, + tags: ["qqqqq"], + banner: %{"a" => "b"}, + background: %{"a" => "b"}, + note_count: 9, + follower_count: 9, + following_count: 9001, + locked: true, + confirmation_pending: true, + password_reset_pending: true, + approval_pending: true, + registration_reason: "ahhhhh", + confirmation_token: "qqqq", + domain_blocks: ["lain.com"], + deactivated: true, + ap_enabled: true, + is_moderator: true, + is_admin: true, + mastofe_settings: %{"a" => "b"}, + mascot: %{"a" => "b"}, + emoji: %{"a" => "b"}, + pleroma_settings_store: %{"q" => "x"}, + fields: [%{"gg" => "qq"}], + raw_fields: [%{"gg" => "qq"}], + discoverable: true, + also_known_as: ["https://lol.olo/users/loll"] + }) + + {:ok, job} = User.delete(user) + {:ok, _} = ObanHelpers.perform(job) + user = User.get_by_id(user.id) + + assert %User{ + bio: nil, + raw_bio: nil, + email: nil, + name: nil, + password_hash: nil, + keys: nil, + public_key: nil, + avatar: %{}, + tags: [], + last_refreshed_at: nil, + last_digest_emailed_at: nil, + banner: %{}, + background: %{}, + note_count: 0, + follower_count: 0, + following_count: 0, + locked: false, + confirmation_pending: false, + password_reset_pending: false, + approval_pending: false, + registration_reason: nil, + confirmation_token: nil, + domain_blocks: [], + deactivated: true, + ap_enabled: false, + is_moderator: false, + is_admin: false, + mastofe_settings: nil, + mascot: nil, + emoji: %{}, + pleroma_settings_store: %{}, + fields: [], + raw_fields: [], + discoverable: false, + also_known_as: [] + } = user + end + test "get_public_key_for_ap_id fetches a user that's not in the db" do assert {:ok, _key} = User.get_public_key_for_ap_id("http://mastodon.example.org/users/admin") end diff --git a/test/web/activity_pub/object_validators/accept_validation_test.exs b/test/web/activity_pub/object_validators/accept_validation_test.exs new file mode 100644 index 000000000..d6111ba41 --- /dev/null +++ b/test/web/activity_pub/object_validators/accept_validation_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.AcceptValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.Pipeline + + import Pleroma.Factory + + setup do + follower = insert(:user) + followed = insert(:user, local: false) + + {:ok, follow_data, _} = Builder.follow(follower, followed) + {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) + + {:ok, accept_data, _} = Builder.accept(followed, follow_activity) + + %{accept_data: accept_data, followed: followed} + end + + test "it validates a basic 'accept'", %{accept_data: accept_data} do + assert {:ok, _, _} = ObjectValidator.validate(accept_data, []) + end + + test "it fails when the actor doesn't exist", %{accept_data: accept_data} do + accept_data = + accept_data + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _} = ObjectValidator.validate(accept_data, []) + end + + test "it fails when the accepted activity doesn't exist", %{accept_data: accept_data} do + accept_data = + accept_data + |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") + + assert {:error, _} = ObjectValidator.validate(accept_data, []) + end + + test "for an accepted follow, it only validates if the actor of the accept is the followed actor", + %{accept_data: accept_data} do + stranger = insert(:user) + + accept_data = + accept_data + |> Map.put("actor", stranger.ap_id) + + assert {:error, _} = ObjectValidator.validate(accept_data, []) + end +end diff --git a/test/web/activity_pub/object_validators/reject_validation_test.exs b/test/web/activity_pub/object_validators/reject_validation_test.exs new file mode 100644 index 000000000..370bb6e5c --- /dev/null +++ b/test/web/activity_pub/object_validators/reject_validation_test.exs @@ -0,0 +1,56 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.ObjectValidators.RejectValidationTest do + use Pleroma.DataCase + + alias Pleroma.Web.ActivityPub.Builder + alias Pleroma.Web.ActivityPub.ObjectValidator + alias Pleroma.Web.ActivityPub.Pipeline + + import Pleroma.Factory + + setup do + follower = insert(:user) + followed = insert(:user, local: false) + + {:ok, follow_data, _} = Builder.follow(follower, followed) + {:ok, follow_activity, _} = Pipeline.common_pipeline(follow_data, local: true) + + {:ok, reject_data, _} = Builder.reject(followed, follow_activity) + + %{reject_data: reject_data, followed: followed} + end + + test "it validates a basic 'reject'", %{reject_data: reject_data} do + assert {:ok, _, _} = ObjectValidator.validate(reject_data, []) + end + + test "it fails when the actor doesn't exist", %{reject_data: reject_data} do + reject_data = + reject_data + |> Map.put("actor", "https://gensokyo.2hu/users/raymoo") + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end + + test "it fails when the rejected activity doesn't exist", %{reject_data: reject_data} do + reject_data = + reject_data + |> Map.put("object", "https://gensokyo.2hu/users/raymoo/follows/1") + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end + + test "for an rejected follow, it only validates if the actor of the reject is the followed actor", + %{reject_data: reject_data} do + stranger = insert(:user) + + reject_data = + reject_data + |> Map.put("actor", stranger.ap_id) + + assert {:error, _} = ObjectValidator.validate(reject_data, []) + end +end diff --git a/test/web/activity_pub/side_effects_test.exs b/test/web/activity_pub/side_effects_test.exs index 4a08eb7ee..9efbaad04 100644 --- a/test/web/activity_pub/side_effects_test.exs +++ b/test/web/activity_pub/side_effects_test.exs @@ -19,8 +19,9 @@ defmodule Pleroma.Web.ActivityPub.SideEffectsTest do alias Pleroma.Web.ActivityPub.SideEffects alias Pleroma.Web.CommonAPI - import Pleroma.Factory + import ExUnit.CaptureLog import Mock + import Pleroma.Factory describe "handle_after_transaction" do test "it streams out notifications and streams" do @@ -221,6 +222,22 @@ test "it handles user deletions", %{delete_user: delete, user: user} do assert User.get_cached_by_ap_id(user.ap_id).deactivated end + + test "it logs issues with objects deletion", %{ + delete: delete, + object: object + } do + {:ok, object} = + object + |> Object.change(%{data: Map.delete(object.data, "actor")}) + |> Repo.update() + + Object.invalid_object_cache(object) + + assert capture_log(fn -> + {:error, :no_object_actor} = SideEffects.handle(delete) + end) =~ "object doesn't have an actor" + end end describe "EmojiReact objects" do diff --git a/test/web/activity_pub/transmogrifier/accept_handling_test.exs b/test/web/activity_pub/transmogrifier/accept_handling_test.exs new file mode 100644 index 000000000..77d468f5c --- /dev/null +++ b/test/web/activity_pub/transmogrifier/accept_handling_test.exs @@ -0,0 +1,91 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.AcceptHandlingTest do + use Pleroma.DataCase + + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it works for incoming accepts which were pre-accepted" do + follower = insert(:user) + followed = insert(:user) + + {:ok, follower} = User.follow(follower, followed) + assert User.following?(follower, followed) == true + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) + + accept_data = + File.read!("test/fixtures/mastodon-accept-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + + object = + accept_data["object"] + |> Map.put("actor", follower.ap_id) + |> Map.put("id", follow_activity.data["id"]) + + accept_data = Map.put(accept_data, "object", object) + + {:ok, activity} = Transmogrifier.handle_incoming(accept_data) + refute activity.local + + assert activity.data["object"] == follow_activity.data["id"] + + assert activity.data["id"] == accept_data["id"] + + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, followed) == true + end + + test "it works for incoming accepts which are referenced by IRI only" do + follower = insert(:user) + followed = insert(:user, locked: true) + + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) + + accept_data = + File.read!("test/fixtures/mastodon-accept-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + |> Map.put("object", follow_activity.data["id"]) + + {:ok, activity} = Transmogrifier.handle_incoming(accept_data) + assert activity.data["object"] == follow_activity.data["id"] + + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, followed) == true + + follower = User.get_by_id(follower.id) + assert follower.following_count == 1 + + followed = User.get_by_id(followed.id) + assert followed.follower_count == 1 + end + + test "it fails for incoming accepts which cannot be correlated" do + follower = insert(:user) + followed = insert(:user, locked: true) + + accept_data = + File.read!("test/fixtures/mastodon-accept-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + + accept_data = + Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) + + {:error, _} = Transmogrifier.handle_incoming(accept_data) + + follower = User.get_cached_by_id(follower.id) + + refute User.following?(follower, followed) == true + end +end diff --git a/test/web/activity_pub/transmogrifier/audio_handling_test.exs b/test/web/activity_pub/transmogrifier/audio_handling_test.exs new file mode 100644 index 000000000..c74a9c45d --- /dev/null +++ b/test/web/activity_pub/transmogrifier/audio_handling_test.exs @@ -0,0 +1,45 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.AudioHandlingTest do + use Oban.Testing, repo: Pleroma.Repo + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.Object + alias Pleroma.Web.ActivityPub.Transmogrifier + + import Pleroma.Factory + + test "it works for incoming listens" do + _user = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + + data = %{ + "@context" => "https://www.w3.org/ns/activitystreams", + "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "cc" => [], + "type" => "Listen", + "id" => "http://mastodon.example.org/users/admin/listens/1234/activity", + "actor" => "http://mastodon.example.org/users/admin", + "object" => %{ + "type" => "Audio", + "id" => "http://mastodon.example.org/users/admin/listens/1234", + "attributedTo" => "http://mastodon.example.org/users/admin", + "title" => "lain radio episode 1", + "artist" => "lain", + "album" => "lain radio", + "length" => 180_000 + } + } + + {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) + + object = Object.normalize(activity) + + assert object.data["title"] == "lain radio episode 1" + assert object.data["artist"] == "lain" + assert object.data["album"] == "lain radio" + assert object.data["length"] == 180_000 + end +end diff --git a/test/web/activity_pub/transmogrifier/follow_handling_test.exs b/test/web/activity_pub/transmogrifier/follow_handling_test.exs index 17e764ca1..757d90941 100644 --- a/test/web/activity_pub/transmogrifier/follow_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/follow_handling_test.exs @@ -185,5 +185,24 @@ test "it works for incoming follow requests from hubzilla" do assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2" assert User.following?(User.get_cached_by_ap_id(data["actor"]), user) end + + test "it works for incoming follows to locked account" do + pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") + user = insert(:user, locked: true) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + + {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) + + assert data["type"] == "Follow" + assert data["object"] == user.ap_id + assert data["state"] == "pending" + assert data["actor"] == "http://mastodon.example.org/users/admin" + + assert [^pending_follower] = User.get_follow_requests(user) + end end end diff --git a/test/web/activity_pub/transmogrifier/reject_handling_test.exs b/test/web/activity_pub/transmogrifier/reject_handling_test.exs new file mode 100644 index 000000000..7592fbe1c --- /dev/null +++ b/test/web/activity_pub/transmogrifier/reject_handling_test.exs @@ -0,0 +1,67 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2020 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.Transmogrifier.RejectHandlingTest do + use Pleroma.DataCase + + alias Pleroma.Activity + alias Pleroma.User + alias Pleroma.Web.ActivityPub.Transmogrifier + alias Pleroma.Web.CommonAPI + + import Pleroma.Factory + + test "it fails for incoming rejects which cannot be correlated" do + follower = insert(:user) + followed = insert(:user, locked: true) + + accept_data = + File.read!("test/fixtures/mastodon-reject-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + + accept_data = + Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) + + {:error, _} = Transmogrifier.handle_incoming(accept_data) + + follower = User.get_cached_by_id(follower.id) + + refute User.following?(follower, followed) == true + end + + test "it works for incoming rejects which are referenced by IRI only" do + follower = insert(:user) + followed = insert(:user, locked: true) + + {:ok, follower} = User.follow(follower, followed) + {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) + + assert User.following?(follower, followed) == true + + reject_data = + File.read!("test/fixtures/mastodon-reject-activity.json") + |> Poison.decode!() + |> Map.put("actor", followed.ap_id) + |> Map.put("object", follow_activity.data["id"]) + + {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data) + + follower = User.get_cached_by_id(follower.id) + + assert User.following?(follower, followed) == false + end + + test "it rejects activities without a valid ID" do + user = insert(:user) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") + |> Poison.decode!() + |> Map.put("object", user.ap_id) + |> Map.put("id", "") + + :error = Transmogrifier.handle_incoming(data) + end +end diff --git a/test/web/activity_pub/transmogrifier/undo_handling_test.exs b/test/web/activity_pub/transmogrifier/undo_handling_test.exs index 01dd6c370..8683f7135 100644 --- a/test/web/activity_pub/transmogrifier/undo_handling_test.exs +++ b/test/web/activity_pub/transmogrifier/undo_handling_test.exs @@ -130,7 +130,7 @@ test "it works for incoming unannounces with an existing notice" do "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" end - test "it works for incomming unfollows with an existing follow" do + test "it works for incoming unfollows with an existing follow" do user = insert(:user) follow_data = diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 6dd9a3fec..3fa41b0c7 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -225,35 +225,6 @@ test "it works for incoming notices with hashtags" do assert Enum.at(object.data["tag"], 2) == "moo" end - test "it works for incoming listens" do - data = %{ - "@context" => "https://www.w3.org/ns/activitystreams", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "cc" => [], - "type" => "Listen", - "id" => "http://mastodon.example.org/users/admin/listens/1234/activity", - "actor" => "http://mastodon.example.org/users/admin", - "object" => %{ - "type" => "Audio", - "id" => "http://mastodon.example.org/users/admin/listens/1234", - "attributedTo" => "http://mastodon.example.org/users/admin", - "title" => "lain radio episode 1", - "artist" => "lain", - "album" => "lain radio", - "length" => 180_000 - } - } - - {:ok, %Activity{local: false} = activity} = Transmogrifier.handle_incoming(data) - - object = Object.normalize(activity) - - assert object.data["title"] == "lain radio episode 1" - assert object.data["artist"] == "lain" - assert object.data["album"] == "lain radio" - assert object.data["length"] == 180_000 - end - test "it works for incoming notices with contentMap" do data = File.read!("test/fixtures/mastodon-post-activity-contentmap.json") |> Poison.decode!() @@ -359,7 +330,7 @@ test "it strips internal reactions" do refute Map.has_key?(object_data, "reaction_count") end - test "it works for incomming unfollows with an existing follow" do + test "it works for incoming unfollows with an existing follow" do user = insert(:user) follow_data = @@ -384,204 +355,6 @@ test "it works for incomming unfollows with an existing follow" do refute User.following?(User.get_cached_by_ap_id(data["actor"]), user) end - test "it works for incoming follows to locked account" do - pending_follower = insert(:user, ap_id: "http://mastodon.example.org/users/admin") - user = insert(:user, locked: true) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - - {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - - assert data["type"] == "Follow" - assert data["object"] == user.ap_id - assert data["state"] == "pending" - assert data["actor"] == "http://mastodon.example.org/users/admin" - - assert [^pending_follower] = User.get_follow_requests(user) - end - - test "it works for incoming accepts which were pre-accepted" do - follower = insert(:user) - followed = insert(:user) - - {:ok, follower} = User.follow(follower, followed) - assert User.following?(follower, followed) == true - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - object = - accept_data["object"] - |> Map.put("actor", follower.ap_id) - |> Map.put("id", follow_activity.data["id"]) - - accept_data = Map.put(accept_data, "object", object) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - refute activity.local - - assert activity.data["object"] == follow_activity.data["id"] - - assert activity.data["id"] == accept_data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == true - end - - test "it works for incoming accepts which were orphaned" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - assert activity.data["object"] == follow_activity.data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == true - end - - test "it works for incoming accepts which are referenced by IRI only" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - |> Map.put("object", follow_activity.data["id"]) - - {:ok, activity} = Transmogrifier.handle_incoming(accept_data) - assert activity.data["object"] == follow_activity.data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == true - - follower = User.get_by_id(follower.id) - assert follower.following_count == 1 - - followed = User.get_by_id(followed.id) - assert followed.follower_count == 1 - end - - test "it fails for incoming accepts which cannot be correlated" do - follower = insert(:user) - followed = insert(:user, locked: true) - - accept_data = - File.read!("test/fixtures/mastodon-accept-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - :error = Transmogrifier.handle_incoming(accept_data) - - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, followed) == true - end - - test "it fails for incoming rejects which cannot be correlated" do - follower = insert(:user) - followed = insert(:user, locked: true) - - accept_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - accept_data = - Map.put(accept_data, "object", Map.put(accept_data["object"], "actor", follower.ap_id)) - - :error = Transmogrifier.handle_incoming(accept_data) - - follower = User.get_cached_by_id(follower.id) - - refute User.following?(follower, followed) == true - end - - test "it works for incoming rejects which are orphaned" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, follower} = User.follow(follower, followed) - {:ok, _, _, _follow_activity} = CommonAPI.follow(follower, followed) - - assert User.following?(follower, followed) == true - - reject_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - - reject_data = - Map.put(reject_data, "object", Map.put(reject_data["object"], "actor", follower.ap_id)) - - {:ok, activity} = Transmogrifier.handle_incoming(reject_data) - refute activity.local - assert activity.data["id"] == reject_data["id"] - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == false - end - - test "it works for incoming rejects which are referenced by IRI only" do - follower = insert(:user) - followed = insert(:user, locked: true) - - {:ok, follower} = User.follow(follower, followed) - {:ok, _, _, follow_activity} = CommonAPI.follow(follower, followed) - - assert User.following?(follower, followed) == true - - reject_data = - File.read!("test/fixtures/mastodon-reject-activity.json") - |> Poison.decode!() - |> Map.put("actor", followed.ap_id) - |> Map.put("object", follow_activity.data["id"]) - - {:ok, %Activity{data: _}} = Transmogrifier.handle_incoming(reject_data) - - follower = User.get_cached_by_id(follower.id) - - assert User.following?(follower, followed) == false - end - - test "it rejects activities without a valid ID" do - user = insert(:user) - - data = - File.read!("test/fixtures/mastodon-follow-activity.json") - |> Poison.decode!() - |> Map.put("object", user.ap_id) - |> Map.put("id", "") - - :error = Transmogrifier.handle_incoming(data) - end - test "skip converting the content when it is nil" do object_id = "https://peertube.social/videos/watch/278d2b7c-0f38-4aaa-afe6-9ecc0c4a34fe" diff --git a/test/web/admin_api/controllers/admin_api_controller_test.exs b/test/web/admin_api/controllers/admin_api_controller_test.exs index eca9272e0..2eb698807 100644 --- a/test/web/admin_api/controllers/admin_api_controller_test.exs +++ b/test/web/admin_api/controllers/admin_api_controller_test.exs @@ -155,11 +155,30 @@ test "GET /api/pleroma/admin/users/:nickname requires " <> describe "DELETE /api/pleroma/admin/users" do test "single user", %{admin: admin, conn: conn} do - user = insert(:user) clear_config([:instance, :federating], true) + user = + insert(:user, + avatar: %{"url" => [%{"href" => "https://someurl"}]}, + banner: %{"url" => [%{"href" => "https://somebanner"}]}, + bio: "Hello world!", + name: "A guy" + ) + + # Create some activities to check they got deleted later + follower = insert(:user) + {:ok, _} = CommonAPI.post(user, %{status: "test"}) + {:ok, _, _, _} = CommonAPI.follow(user, follower) + {:ok, _, _, _} = CommonAPI.follow(follower, user) + user = Repo.get(User, user.id) + assert user.note_count == 1 + assert user.follower_count == 1 + assert user.following_count == 1 + refute user.deactivated + with_mock Pleroma.Web.Federator, - publish: fn _ -> nil end do + publish: fn _ -> nil end, + perform: fn _, _ -> nil end do conn = conn |> put_req_header("accept", "application/json") @@ -176,6 +195,17 @@ test "single user", %{admin: admin, conn: conn} do assert json_response(conn, 200) == [user.nickname] + user = Repo.get(User, user.id) + assert user.deactivated + + assert user.avatar == %{} + assert user.banner == %{} + assert user.note_count == 0 + assert user.follower_count == 0 + assert user.following_count == 0 + assert user.bio == nil + assert user.name == nil + assert called(Pleroma.Web.Federator.publish(:_)) end end diff --git a/test/web/admin_api/controllers/config_controller_test.exs b/test/web/admin_api/controllers/config_controller_test.exs index 61bc9fd39..4e897455f 100644 --- a/test/web/admin_api/controllers/config_controller_test.exs +++ b/test/web/admin_api/controllers/config_controller_test.exs @@ -1342,6 +1342,75 @@ test "args for Pleroma.Upload.Filter.Mogrify with custom tuples", %{conn: conn} args: ["auto-orient", "strip", {"implode", "1"}, {"resize", "3840x1080>"}] ] end + + test "enables the welcome messages", %{conn: conn} do + clear_config([:welcome]) + + params = %{ + "group" => ":pleroma", + "key" => ":welcome", + "value" => [ + %{ + "tuple" => [ + ":direct_message", + [ + %{"tuple" => [":enabled", true]}, + %{"tuple" => [":message", "Welcome to Pleroma!"]}, + %{"tuple" => [":sender_nickname", "pleroma"]} + ] + ] + }, + %{ + "tuple" => [ + ":chat_message", + [ + %{"tuple" => [":enabled", true]}, + %{"tuple" => [":message", "Welcome to Pleroma!"]}, + %{"tuple" => [":sender_nickname", "pleroma"]} + ] + ] + }, + %{ + "tuple" => [ + ":email", + [ + %{"tuple" => [":enabled", true]}, + %{"tuple" => [":sender", %{"tuple" => ["pleroma@dev.dev", "Pleroma"]}]}, + %{"tuple" => [":subject", "Welcome to <%= instance_name %>!"]}, + %{"tuple" => [":html", "Welcome to <%= instance_name %>!"]}, + %{"tuple" => [":text", "Welcome to <%= instance_name %>!"]} + ] + ] + } + ] + } + + refute Pleroma.User.WelcomeEmail.enabled?() + refute Pleroma.User.WelcomeMessage.enabled?() + refute Pleroma.User.WelcomeChatMessage.enabled?() + + res = + assert conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/config", %{"configs" => [params]}) + |> json_response_and_validate_schema(200) + + assert Pleroma.User.WelcomeEmail.enabled?() + assert Pleroma.User.WelcomeMessage.enabled?() + assert Pleroma.User.WelcomeChatMessage.enabled?() + + assert res == %{ + "configs" => [ + %{ + "db" => [":direct_message", ":chat_message", ":email"], + "group" => ":pleroma", + "key" => ":welcome", + "value" => params["value"] + } + ], + "need_reboot" => false + } + end end describe "GET /api/pleroma/admin/config/descriptions" do diff --git a/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs b/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs index 5ab6cb78a..f243d1fb2 100644 --- a/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs +++ b/test/web/admin_api/controllers/media_proxy_cache_controller_test.exs @@ -48,6 +48,9 @@ test "shows banned MediaProxy URLs", %{conn: conn} do |> get("/api/pleroma/admin/media_proxy_caches?page_size=2") |> json_response_and_validate_schema(200) + assert response["page_size"] == 2 + assert response["count"] == 5 + assert response["urls"] == [ "http://localhost:4001/media/fb1f4d.jpg", "http://localhost:4001/media/a688346.jpg" @@ -63,6 +66,9 @@ test "shows banned MediaProxy URLs", %{conn: conn} do "http://localhost:4001/media/tb13f47.jpg" ] + assert response["page_size"] == 2 + assert response["count"] == 5 + response = conn |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&page=3") @@ -70,6 +76,30 @@ test "shows banned MediaProxy URLs", %{conn: conn} do assert response["urls"] == ["http://localhost:4001/media/wb1f46.jpg"] end + + test "search banned MediaProxy URLs", %{conn: conn} do + MediaProxy.put_in_banned_urls([ + "http://localhost:4001/media/a688346.jpg", + "http://localhost:4001/media/ff44b1f4d.jpg" + ]) + + MediaProxy.put_in_banned_urls("http://localhost:4001/media/gb1f44.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/tb13f47.jpg") + MediaProxy.put_in_banned_urls("http://localhost:4001/media/wb1f46.jpg") + + response = + conn + |> get("/api/pleroma/admin/media_proxy_caches?page_size=2&query=F44") + |> json_response_and_validate_schema(200) + + assert response["urls"] == [ + "http://localhost:4001/media/gb1f44.jpg", + "http://localhost:4001/media/ff44b1f4d.jpg" + ] + + assert response["page_size"] == 2 + assert response["count"] == 2 + end end describe "POST /api/pleroma/admin/media_proxy_caches/delete" do @@ -79,15 +109,13 @@ test "deleted MediaProxy URLs from banned", %{conn: conn} do "http://localhost:4001/media/fb1f4d.jpg" ]) - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/media_proxy_caches/delete", %{ - urls: ["http://localhost:4001/media/a688346.jpg"] - }) - |> json_response_and_validate_schema(200) + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/media_proxy_caches/delete", %{ + urls: ["http://localhost:4001/media/a688346.jpg"] + }) + |> json_response_and_validate_schema(200) - assert response["urls"] == ["http://localhost:4001/media/a688346.jpg"] refute MediaProxy.in_banned_urls("http://localhost:4001/media/a688346.jpg") assert MediaProxy.in_banned_urls("http://localhost:4001/media/fb1f4d.jpg") end @@ -106,13 +134,10 @@ test "perform invalidates cache of MediaProxy", %{conn: conn} do purge: fn _, _ -> {"ok", 0} end ]} ] do - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/media_proxy_caches/purge", %{urls: urls, ban: false}) - |> json_response_and_validate_schema(200) - - assert response["urls"] == urls + conn + |> put_req_header("content-type", "application/json") + |> post("/api/pleroma/admin/media_proxy_caches/purge", %{urls: urls, ban: false}) + |> json_response_and_validate_schema(200) refute MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") refute MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") @@ -126,16 +151,13 @@ test "perform invalidates cache of MediaProxy and adds url to banned", %{conn: c ] with_mocks [{MediaProxy.Invalidation.Script, [], [purge: fn _, _ -> {"ok", 0} end]}] do - response = - conn - |> put_req_header("content-type", "application/json") - |> post("/api/pleroma/admin/media_proxy_caches/purge", %{ - urls: urls, - ban: true - }) - |> json_response_and_validate_schema(200) - - assert response["urls"] == urls + conn + |> put_req_header("content-type", "application/json") + |> post( + "/api/pleroma/admin/media_proxy_caches/purge", + %{urls: urls, ban: true} + ) + |> json_response_and_validate_schema(200) assert MediaProxy.in_banned_urls("http://example.com/media/a688346.jpg") assert MediaProxy.in_banned_urls("http://example.com/media/fb1f4d.jpg") diff --git a/test/web/mastodon_api/controllers/timeline_controller_test.exs b/test/web/mastodon_api/controllers/timeline_controller_test.exs index 50e0d783d..71bac99f7 100644 --- a/test/web/mastodon_api/controllers/timeline_controller_test.exs +++ b/test/web/mastodon_api/controllers/timeline_controller_test.exs @@ -445,6 +445,23 @@ defp ensure_authenticated_access(base_uri) do assert length(json_response(res_conn, 200)) == 2 end + test "with default settings on private instances, returns 403 for unauthenticated users", %{ + conn: conn, + base_uri: base_uri, + error_response: error_response + } do + clear_config([:instance, :public], false) + clear_config([:restrict_unauthenticated, :timelines]) + + for local <- [true, false] do + res_conn = get(conn, "#{base_uri}?local=#{local}") + + assert json_response(res_conn, :unauthorized) == error_response + end + + ensure_authenticated_access(base_uri) + end + test "with `%{local: true, federated: true}`, returns 403 for unauthenticated users", %{ conn: conn, base_uri: base_uri, diff --git a/test/web/preload/timeline_test.exs b/test/web/preload/timeline_test.exs index fea95a6a4..3b1f2f1aa 100644 --- a/test/web/preload/timeline_test.exs +++ b/test/web/preload/timeline_test.exs @@ -12,16 +12,8 @@ defmodule Pleroma.Web.Preload.Providers.TimelineTest do @public_url "/api/v1/timelines/public" describe "unauthenticated timeliness when restricted" do - setup do - svd_config = Pleroma.Config.get([:restrict_unauthenticated, :timelines]) - Pleroma.Config.put([:restrict_unauthenticated, :timelines], %{local: true, federated: true}) - - on_exit(fn -> - Pleroma.Config.put([:restrict_unauthenticated, :timelines], svd_config) - end) - - :ok - end + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], true) + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], true) test "return nothing" do tl_data = Timelines.generate_terms(%{}) @@ -31,20 +23,10 @@ test "return nothing" do end describe "unauthenticated timeliness when unrestricted" do - setup do - svd_config = Pleroma.Config.get([:restrict_unauthenticated, :timelines]) + setup do: clear_config([:restrict_unauthenticated, :timelines, :local], false) + setup do: clear_config([:restrict_unauthenticated, :timelines, :federated], false) - Pleroma.Config.put([:restrict_unauthenticated, :timelines], %{ - local: false, - federated: false - }) - - on_exit(fn -> - Pleroma.Config.put([:restrict_unauthenticated, :timelines], svd_config) - end) - - {:ok, user: insert(:user)} - end + setup do: {:ok, user: insert(:user)} test "returns the timeline when not restricted" do assert Timelines.generate_terms(%{}) diff --git a/test/web/twitter_api/util_controller_test.exs b/test/web/twitter_api/util_controller_test.exs index 109c1e637..354d77b56 100644 --- a/test/web/twitter_api/util_controller_test.exs +++ b/test/web/twitter_api/util_controller_test.exs @@ -586,10 +586,16 @@ test "with proper permissions and wrong or missing password", %{conn: conn} do end end - test "with proper permissions and valid password", %{conn: conn} do + test "with proper permissions and valid password", %{conn: conn, user: user} do conn = post(conn, "/api/pleroma/delete_account", %{"password" => "test"}) - + ObanHelpers.perform_all() assert json_response(conn, 200) == %{"status" => "success"} + + user = User.get_by_id(user.id) + assert user.deactivated == true + assert user.name == nil + assert user.bio == nil + assert user.password_hash == nil end end end